@@ -55,9 +55,10 @@ use crate::types::function::{
5555 DataclassTransformerFlags , DataclassTransformerParams , FunctionSpans , FunctionType ,
5656 KnownFunction ,
5757} ;
58+ pub ( crate ) use crate :: types:: generics:: GenericContext ;
5859use crate :: types:: generics:: {
59- GenericContext , InferableTypeVars , PartialSpecialization , Specialization , bind_typevar,
60- typing_self , walk_generic_context,
60+ InferableTypeVars , PartialSpecialization , Specialization , bind_typevar, typing_self ,
61+ walk_generic_context,
6162} ;
6263use crate :: types:: infer:: infer_unpack_types;
6364use crate :: types:: mro:: { Mro , MroError , MroIterator } ;
@@ -7274,6 +7275,7 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
72747275 . collect ( ) ,
72757276 Type :: SubclassOf ( subclass_of_type) => subclass_of_type. variance_of ( db, typevar) ,
72767277 Type :: TypeIs ( type_is_type) => type_is_type. variance_of ( db, typevar) ,
7278+ Type :: KnownInstance ( known_instance) => known_instance. variance_of ( db, typevar) ,
72777279 Type :: Dynamic ( _)
72787280 | Type :: Never
72797281 | Type :: WrapperDescriptor ( _)
@@ -7288,7 +7290,6 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
72887290 | Type :: LiteralString
72897291 | Type :: BytesLiteral ( _)
72907292 | Type :: SpecialForm ( _)
7291- | Type :: KnownInstance ( _)
72927293 | Type :: AlwaysFalsy
72937294 | Type :: AlwaysTruthy
72947295 | Type :: BoundSuper ( _)
@@ -7495,6 +7496,17 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
74957496 }
74967497}
74977498
7499+ impl < ' db > VarianceInferable < ' db > for KnownInstanceType < ' db > {
7500+ fn variance_of ( self , db : & ' db dyn Db , typevar : BoundTypeVarInstance < ' db > ) -> TypeVarVariance {
7501+ match self {
7502+ KnownInstanceType :: TypeAliasType ( type_alias) => {
7503+ type_alias. raw_value_type ( db) . variance_of ( db, typevar)
7504+ }
7505+ _ => TypeVarVariance :: Bivariant ,
7506+ }
7507+ }
7508+ }
7509+
74987510impl < ' db > KnownInstanceType < ' db > {
74997511 fn normalized_impl ( self , db : & ' db dyn Db , visitor : & NormalizedVisitor < ' db > ) -> Self {
75007512 match self {
@@ -10693,14 +10705,10 @@ impl<'db> PEP695TypeAliasType<'db> {
1069310705 semantic_index ( db, scope. file ( db) ) . expect_single_definition ( type_alias_stmt_node)
1069410706 }
1069510707
10708+ /// The RHS type of a PEP-695 style type alias with specialization applied.
1069610709 #[ salsa:: tracked( cycle_fn=value_type_cycle_recover, cycle_initial=value_type_cycle_initial, heap_size=ruff_memory_usage:: heap_size) ]
1069710710 pub ( crate ) fn value_type ( self , db : & ' db dyn Db ) -> Type < ' db > {
10698- let scope = self . rhs_scope ( db) ;
10699- let module = parsed_module ( db, scope. file ( db) ) . load ( db) ;
10700- let type_alias_stmt_node = scope. node ( db) . expect_type_alias ( ) ;
10701- let definition = self . definition ( db) ;
10702- let value_type =
10703- definition_expression_type ( db, definition, & type_alias_stmt_node. node ( & module) . value ) ;
10711+ let value_type = self . raw_value_type ( db) ;
1070410712
1070510713 if let Some ( generic_context) = self . generic_context ( db) {
1070610714 let specialization = self
@@ -10713,6 +10721,25 @@ impl<'db> PEP695TypeAliasType<'db> {
1071310721 }
1071410722 }
1071510723
10724+ /// The RHS type of a PEP-695 style type alias with *no* specialization applied.
10725+ ///
10726+ /// ## Warning
10727+ ///
10728+ /// This uses the semantic index to find the definition of the type alias. This means that if the
10729+ /// calling query is not in the same file as this type alias is defined in, then this will create
10730+ /// a cross-module dependency directly on the full AST which will lead to cache
10731+ /// over-invalidation.
10732+ /// This method also calls the type inference functions, and since type aliases can have recursive structures,
10733+ /// we should be careful not to create infinite recursions in this method (or make it tracked if necessary).
10734+ pub ( crate ) fn raw_value_type ( self , db : & ' db dyn Db ) -> Type < ' db > {
10735+ let scope = self . rhs_scope ( db) ;
10736+ let module = parsed_module ( db, scope. file ( db) ) . load ( db) ;
10737+ let type_alias_stmt_node = scope. node ( db) . expect_type_alias ( ) ;
10738+ let definition = self . definition ( db) ;
10739+
10740+ definition_expression_type ( db, definition, & type_alias_stmt_node. node ( & module) . value )
10741+ }
10742+
1071610743 pub ( crate ) fn apply_specialization (
1071710744 self ,
1071810745 db : & ' db dyn Db ,
@@ -10892,6 +10919,13 @@ impl<'db> TypeAliasType<'db> {
1089210919 }
1089310920 }
1089410921
10922+ pub ( crate ) fn raw_value_type ( self , db : & ' db dyn Db ) -> Type < ' db > {
10923+ match self {
10924+ TypeAliasType :: PEP695 ( type_alias) => type_alias. raw_value_type ( db) ,
10925+ TypeAliasType :: ManualPEP695 ( type_alias) => type_alias. value ( db) ,
10926+ }
10927+ }
10928+
1089510929 pub ( crate ) fn as_pep_695_type_alias ( self ) -> Option < PEP695TypeAliasType < ' db > > {
1089610930 match self {
1089710931 TypeAliasType :: PEP695 ( type_alias) => Some ( type_alias) ,
@@ -11724,4 +11758,85 @@ pub(crate) mod tests {
1172411758 . build ( ) ;
1172511759 assert_eq ! ( intersection. display( & db) . to_string( ) , "Never" ) ;
1172611760 }
11761+
11762+ #[ test]
11763+ fn type_alias_variance ( ) {
11764+ use crate :: db:: tests:: TestDb ;
11765+ use crate :: place:: global_symbol;
11766+
11767+ fn get_type_alias < ' db > ( db : & ' db TestDb , name : & str ) -> PEP695TypeAliasType < ' db > {
11768+ let module = ruff_db:: files:: system_path_to_file ( db, "/src/a.py" ) . unwrap ( ) ;
11769+ let ty = global_symbol ( db, module, name) . place . expect_type ( ) ;
11770+ let Type :: KnownInstance ( KnownInstanceType :: TypeAliasType ( TypeAliasType :: PEP695 (
11771+ type_alias,
11772+ ) ) ) = ty
11773+ else {
11774+ panic ! ( "Expected `{name}` to be a type alias" ) ;
11775+ } ;
11776+ type_alias
11777+ }
11778+ fn get_bound_typevar < ' db > (
11779+ db : & ' db TestDb ,
11780+ type_alias : PEP695TypeAliasType < ' db > ,
11781+ ) -> BoundTypeVarInstance < ' db > {
11782+ let generic_context = type_alias. generic_context ( db) . unwrap ( ) ;
11783+ generic_context. variables ( db) . next ( ) . unwrap ( )
11784+ }
11785+
11786+ let mut db = setup_db ( ) ;
11787+ db. write_dedented (
11788+ "/src/a.py" ,
11789+ r#"
11790+ class Covariant[T]:
11791+ def get(self) -> T:
11792+ raise ValueError
11793+
11794+ class Contravariant[T]:
11795+ def set(self, value: T):
11796+ pass
11797+
11798+ class Invariant[T]:
11799+ def get(self) -> T:
11800+ raise ValueError
11801+ def set(self, value: T):
11802+ pass
11803+
11804+ class Bivariant[T]:
11805+ pass
11806+
11807+ type CovariantAlias[T] = Covariant[T]
11808+ type ContravariantAlias[T] = Contravariant[T]
11809+ type InvariantAlias[T] = Invariant[T]
11810+ type BivariantAlias[T] = Bivariant[T]
11811+ "# ,
11812+ )
11813+ . unwrap ( ) ;
11814+ let covariant = get_type_alias ( & db, "CovariantAlias" ) ;
11815+ assert_eq ! (
11816+ KnownInstanceType :: TypeAliasType ( TypeAliasType :: PEP695 ( covariant) )
11817+ . variance_of( & db, get_bound_typevar( & db, covariant) ) ,
11818+ TypeVarVariance :: Covariant
11819+ ) ;
11820+
11821+ let contravariant = get_type_alias ( & db, "ContravariantAlias" ) ;
11822+ assert_eq ! (
11823+ KnownInstanceType :: TypeAliasType ( TypeAliasType :: PEP695 ( contravariant) )
11824+ . variance_of( & db, get_bound_typevar( & db, contravariant) ) ,
11825+ TypeVarVariance :: Contravariant
11826+ ) ;
11827+
11828+ let invariant = get_type_alias ( & db, "InvariantAlias" ) ;
11829+ assert_eq ! (
11830+ KnownInstanceType :: TypeAliasType ( TypeAliasType :: PEP695 ( invariant) )
11831+ . variance_of( & db, get_bound_typevar( & db, invariant) ) ,
11832+ TypeVarVariance :: Invariant
11833+ ) ;
11834+
11835+ let bivariant = get_type_alias ( & db, "BivariantAlias" ) ;
11836+ assert_eq ! (
11837+ KnownInstanceType :: TypeAliasType ( TypeAliasType :: PEP695 ( bivariant) )
11838+ . variance_of( & db, get_bound_typevar( & db, bivariant) ) ,
11839+ TypeVarVariance :: Bivariant
11840+ ) ;
11841+ }
1172711842}
0 commit comments