@@ -302,13 +302,7 @@ fn declarations_ty<'db>(
302
302
let declared_ty = if let Some ( second) = all_types. next ( ) {
303
303
let mut builder = UnionBuilder :: new ( db) . add ( first) ;
304
304
for other in [ second] . into_iter ( ) . chain ( all_types) {
305
- // Make sure not to emit spurious errors relating to `Type::Todo`,
306
- // since we only infer this type due to a limitation in our current model.
307
- //
308
- // `Unknown` is different here, since we might infer `Unknown`
309
- // for one of these due to a variable being defined in one possible
310
- // control-flow branch but not another one.
311
- if !first. is_equivalent_to ( db, other) && !first. is_todo ( ) && !other. is_todo ( ) {
305
+ if !first. is_equivalent_to ( db, other) {
312
306
conflicting. push ( other) ;
313
307
}
314
308
builder = builder. add ( other) ;
@@ -600,11 +594,16 @@ impl<'db> Type<'db> {
600
594
601
595
/// Return true if this type is a [subtype of] type `target`.
602
596
///
597
+ /// This method returns `false` if either `self` or `other` is not fully static.
598
+ ///
603
599
/// [subtype of]: https://typing.readthedocs.io/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
604
600
pub ( crate ) fn is_subtype_of ( self , db : & ' db dyn Db , target : Type < ' db > ) -> bool {
605
601
if self . is_equivalent_to ( db, target) {
606
602
return true ;
607
603
}
604
+ if !self . is_fully_static ( db) || !target. is_fully_static ( db) {
605
+ return false ;
606
+ }
608
607
match ( self , target) {
609
608
( Type :: Unknown | Type :: Any | Type :: Todo ( _) , _) => false ,
610
609
( _, Type :: Unknown | Type :: Any | Type :: Todo ( _) ) => false ,
@@ -764,8 +763,16 @@ impl<'db> Type<'db> {
764
763
}
765
764
}
766
765
767
- /// Return true if this type is equivalent to type `other`.
766
+ /// Return true if this type is [equivalent to] type `other`.
767
+ ///
768
+ /// This method returns `false` if either `self` or `other` is not fully static.
769
+ ///
770
+ /// [equivalent to]: https://typing.readthedocs.io/en/latest/spec/glossary.html#term-equivalent
768
771
pub ( crate ) fn is_equivalent_to ( self , db : & ' db dyn Db , other : Type < ' db > ) -> bool {
772
+ if !( self . is_fully_static ( db) && other. is_fully_static ( db) ) {
773
+ return false ;
774
+ }
775
+
769
776
// TODO equivalent but not identical structural types, differently-ordered unions and
770
777
// intersections, other cases?
771
778
@@ -776,7 +783,6 @@ impl<'db> Type<'db> {
776
783
// of `NoneType` and `NoDefaultType` in typeshed. This should not be required anymore once
777
784
// we understand `sys.version_info` branches.
778
785
self == other
779
- || matches ! ( ( self , other) , ( Type :: Todo ( _) , Type :: Todo ( _) ) )
780
786
|| matches ! ( ( self , other) ,
781
787
(
782
788
Type :: Instance ( InstanceType { class: self_class } ) ,
@@ -790,6 +796,17 @@ impl<'db> Type<'db> {
790
796
)
791
797
}
792
798
799
+ /// Returns true if both `self` and `other` are the same gradual form
800
+ /// (limited to `Any`, `Unknown`, or `Todo`).
801
+ pub ( crate ) fn is_same_gradual_form ( self , other : Type < ' db > ) -> bool {
802
+ matches ! (
803
+ ( self , other) ,
804
+ ( Type :: Unknown , Type :: Unknown )
805
+ | ( Type :: Any , Type :: Any )
806
+ | ( Type :: Todo ( _) , Type :: Todo ( _) )
807
+ )
808
+ }
809
+
793
810
/// Return true if this type and `other` have no common elements.
794
811
///
795
812
/// Note: This function aims to have no false positives, but might return
@@ -996,6 +1013,63 @@ impl<'db> Type<'db> {
996
1013
}
997
1014
}
998
1015
1016
+ /// Returns true if the type does not contain any gradual forms (as a sub-part).
1017
+ pub ( crate ) fn is_fully_static ( self , db : & ' db dyn Db ) -> bool {
1018
+ match self {
1019
+ Type :: Any | Type :: Unknown | Type :: Todo ( _) => false ,
1020
+ Type :: Never
1021
+ | Type :: FunctionLiteral ( ..)
1022
+ | Type :: ModuleLiteral ( ..)
1023
+ | Type :: IntLiteral ( _)
1024
+ | Type :: BooleanLiteral ( _)
1025
+ | Type :: StringLiteral ( _)
1026
+ | Type :: LiteralString
1027
+ | Type :: BytesLiteral ( _)
1028
+ | Type :: SliceLiteral ( _)
1029
+ | Type :: KnownInstance ( _) => true ,
1030
+ Type :: ClassLiteral ( _) | Type :: SubclassOf ( _) | Type :: Instance ( _) => {
1031
+ // TODO: Ideally, we would iterate over the MRO of the class, check if all
1032
+ // bases are fully static, and only return `true` if that is the case.
1033
+ //
1034
+ // This does not work yet, because we currently infer `Unknown` for some
1035
+ // generic base classes that we don't understand yet. For example, `str`
1036
+ // is defined as `class str(Sequence[str])` in typeshed and we currently
1037
+ // compute its MRO as `(str, Unknown, object)`. This would make us think
1038
+ // that `str` is a gradual type, which causes all sorts of downstream
1039
+ // issues because it does not participate in equivalence/subtyping etc.
1040
+ //
1041
+ // Another problem is that we run into problems if we eagerly query the
1042
+ // MRO of class literals here. I have not fully investigated this, but
1043
+ // iterating over the MRO alone, without even acting on it, causes us to
1044
+ // infer `Unknown` for many classes.
1045
+
1046
+ true
1047
+ }
1048
+ Type :: Union ( union) => union
1049
+ . elements ( db)
1050
+ . iter ( )
1051
+ . all ( |elem| elem. is_fully_static ( db) ) ,
1052
+ Type :: Intersection ( intersection) => {
1053
+ intersection
1054
+ . positive ( db)
1055
+ . iter ( )
1056
+ . all ( |elem| elem. is_fully_static ( db) )
1057
+ && intersection
1058
+ . negative ( db)
1059
+ . iter ( )
1060
+ . all ( |elem| elem. is_fully_static ( db) )
1061
+ }
1062
+ Type :: Tuple ( tuple) => tuple
1063
+ . elements ( db)
1064
+ . iter ( )
1065
+ . all ( |elem| elem. is_fully_static ( db) ) ,
1066
+ // TODO: Once we support them, make sure that we return `false` for other types
1067
+ // containing gradual forms such as `tuple[Any, ...]` or `Callable[..., str]`.
1068
+ // Conversely, make sure to return `true` for homogeneous tuples such as
1069
+ // `tuple[int, ...]`, once we add support for them.
1070
+ }
1071
+ }
1072
+
999
1073
/// Return true if there is just a single inhabitant for this type.
1000
1074
///
1001
1075
/// Note: This function aims to have no false positives, but might return `false`
@@ -3261,7 +3335,9 @@ pub(crate) mod tests {
3261
3335
}
3262
3336
3263
3337
#[ test_case( Ty :: BuiltinInstance ( "object" ) , Ty :: BuiltinInstance ( "int" ) ) ]
3338
+ #[ test_case( Ty :: Unknown , Ty :: Unknown ) ]
3264
3339
#[ test_case( Ty :: Unknown , Ty :: IntLiteral ( 1 ) ) ]
3340
+ #[ test_case( Ty :: Any , Ty :: Any ) ]
3265
3341
#[ test_case( Ty :: Any , Ty :: IntLiteral ( 1 ) ) ]
3266
3342
#[ test_case( Ty :: IntLiteral ( 1 ) , Ty :: Unknown ) ]
3267
3343
#[ test_case( Ty :: IntLiteral ( 1 ) , Ty :: Any ) ]
@@ -3369,6 +3445,18 @@ pub(crate) mod tests {
3369
3445
assert ! ( from. into_type( & db) . is_equivalent_to( & db, to. into_type( & db) ) ) ;
3370
3446
}
3371
3447
3448
+ #[ test_case( Ty :: Any , Ty :: Any ) ]
3449
+ #[ test_case( Ty :: Any , Ty :: None ) ]
3450
+ #[ test_case( Ty :: Unknown , Ty :: Unknown ) ]
3451
+ #[ test_case( Ty :: Todo , Ty :: Todo ) ]
3452
+ #[ test_case( Ty :: Union ( vec![ Ty :: IntLiteral ( 1 ) , Ty :: IntLiteral ( 2 ) ] ) , Ty :: Union ( vec![ Ty :: IntLiteral ( 1 ) , Ty :: IntLiteral ( 0 ) ] ) ) ]
3453
+ #[ test_case( Ty :: Union ( vec![ Ty :: IntLiteral ( 1 ) , Ty :: IntLiteral ( 2 ) ] ) , Ty :: Union ( vec![ Ty :: IntLiteral ( 1 ) , Ty :: IntLiteral ( 2 ) , Ty :: IntLiteral ( 3 ) ] ) ) ]
3454
+ fn is_not_equivalent_to ( from : Ty , to : Ty ) {
3455
+ let db = setup_db ( ) ;
3456
+
3457
+ assert ! ( !from. into_type( & db) . is_equivalent_to( & db, to. into_type( & db) ) ) ;
3458
+ }
3459
+
3372
3460
#[ test_case( Ty :: Never , Ty :: Never ) ]
3373
3461
#[ test_case( Ty :: Never , Ty :: None ) ]
3374
3462
#[ test_case( Ty :: Never , Ty :: BuiltinInstance ( "int" ) ) ]
@@ -3597,6 +3685,41 @@ pub(crate) mod tests {
3597
3685
assert ! ( !from. into_type( & db) . is_singleton( & db) ) ;
3598
3686
}
3599
3687
3688
+ #[ test_case( Ty :: Never ) ]
3689
+ #[ test_case( Ty :: None ) ]
3690
+ #[ test_case( Ty :: IntLiteral ( 1 ) ) ]
3691
+ #[ test_case( Ty :: BooleanLiteral ( true ) ) ]
3692
+ #[ test_case( Ty :: StringLiteral ( "abc" ) ) ]
3693
+ #[ test_case( Ty :: LiteralString ) ]
3694
+ #[ test_case( Ty :: BytesLiteral ( "abc" ) ) ]
3695
+ #[ test_case( Ty :: KnownClassInstance ( KnownClass :: Str ) ) ]
3696
+ #[ test_case( Ty :: KnownClassInstance ( KnownClass :: Object ) ) ]
3697
+ #[ test_case( Ty :: KnownClassInstance ( KnownClass :: Type ) ) ]
3698
+ #[ test_case( Ty :: BuiltinClassLiteral ( "str" ) ) ]
3699
+ #[ test_case( Ty :: TypingLiteral ) ]
3700
+ #[ test_case( Ty :: Union ( vec![ Ty :: KnownClassInstance ( KnownClass :: Str ) , Ty :: None ] ) ) ]
3701
+ #[ test_case( Ty :: Intersection { pos: vec![ Ty :: KnownClassInstance ( KnownClass :: Str ) ] , neg: vec![ Ty :: LiteralString ] } ) ]
3702
+ #[ test_case( Ty :: Tuple ( vec![ ] ) ) ]
3703
+ #[ test_case( Ty :: Tuple ( vec![ Ty :: KnownClassInstance ( KnownClass :: Int ) , Ty :: KnownClassInstance ( KnownClass :: Object ) ] ) ) ]
3704
+ fn is_fully_static ( from : Ty ) {
3705
+ let db = setup_db ( ) ;
3706
+
3707
+ assert ! ( from. into_type( & db) . is_fully_static( & db) ) ;
3708
+ }
3709
+
3710
+ #[ test_case( Ty :: Any ) ]
3711
+ #[ test_case( Ty :: Unknown ) ]
3712
+ #[ test_case( Ty :: Todo ) ]
3713
+ #[ test_case( Ty :: Union ( vec![ Ty :: Any , Ty :: KnownClassInstance ( KnownClass :: Str ) ] ) ) ]
3714
+ #[ test_case( Ty :: Union ( vec![ Ty :: KnownClassInstance ( KnownClass :: Str ) , Ty :: Unknown ] ) ) ]
3715
+ #[ test_case( Ty :: Intersection { pos: vec![ Ty :: Any ] , neg: vec![ Ty :: LiteralString ] } ) ]
3716
+ #[ test_case( Ty :: Tuple ( vec![ Ty :: KnownClassInstance ( KnownClass :: Int ) , Ty :: Any ] ) ) ]
3717
+ fn is_not_fully_static ( from : Ty ) {
3718
+ let db = setup_db ( ) ;
3719
+
3720
+ assert ! ( !from. into_type( & db) . is_fully_static( & db) ) ;
3721
+ }
3722
+
3600
3723
#[ test_case( Ty :: IntLiteral ( 1 ) ; "is_int_literal_truthy" ) ]
3601
3724
#[ test_case( Ty :: IntLiteral ( -1 ) ) ]
3602
3725
#[ test_case( Ty :: StringLiteral ( "foo" ) ) ]
@@ -3771,19 +3894,6 @@ pub(crate) mod tests {
3771
3894
let todo3 = todo_type ! ( ) ;
3772
3895
let todo4 = todo_type ! ( ) ;
3773
3896
3774
- assert ! ( todo1. is_equivalent_to( & db, todo2) ) ;
3775
- assert ! ( todo3. is_equivalent_to( & db, todo4) ) ;
3776
- assert ! ( todo1. is_equivalent_to( & db, todo3) ) ;
3777
-
3778
- assert ! ( todo1. is_subtype_of( & db, todo2) ) ;
3779
- assert ! ( todo2. is_subtype_of( & db, todo1) ) ;
3780
-
3781
- assert ! ( todo3. is_subtype_of( & db, todo4) ) ;
3782
- assert ! ( todo4. is_subtype_of( & db, todo3) ) ;
3783
-
3784
- assert ! ( todo1. is_subtype_of( & db, todo3) ) ;
3785
- assert ! ( todo3. is_subtype_of( & db, todo1) ) ;
3786
-
3787
3897
let int = KnownClass :: Int . to_instance ( & db) ;
3788
3898
3789
3899
assert ! ( int. is_assignable_to( & db, todo1) ) ;
0 commit comments