@@ -68,6 +68,11 @@ pub struct SymbolTable {
6868
6969 /// Whether `from __future__ import annotations` is active
7070 pub future_annotations : bool ,
71+
72+ /// Names of type parameters that should still be mangled in type param scopes.
73+ /// When Some, only names in this set are mangled; other names are left unmangled.
74+ /// Set on type param blocks for generic classes; inherited by non-class child scopes.
75+ pub mangled_names : Option < IndexSet < String > > ,
7176}
7277
7378impl SymbolTable {
@@ -88,6 +93,7 @@ impl SymbolTable {
8893 annotation_block : None ,
8994 has_conditional_annotations : false ,
9095 future_annotations : false ,
96+ mangled_names : None ,
9197 }
9298 }
9399
@@ -905,14 +911,26 @@ impl SymbolTableBuilder {
905911 )
906912 } )
907913 . unwrap_or ( false ) ;
908- let table = SymbolTable :: new ( name. to_owned ( ) , typ, line_number, is_nested) ;
914+ // Inherit mangled_names from parent for non-class scopes
915+ let inherited_mangled_names = self
916+ . tables
917+ . last ( )
918+ . and_then ( |t| t. mangled_names . clone ( ) )
919+ . filter ( |_| typ != CompilerScope :: Class ) ;
920+ let mut table = SymbolTable :: new ( name. to_owned ( ) , typ, line_number, is_nested) ;
921+ table. mangled_names = inherited_mangled_names;
909922 self . tables . push ( table) ;
910923 // Save parent's varnames and start fresh for the new scope
911924 self . varnames_stack
912925 . push ( core:: mem:: take ( & mut self . current_varnames ) ) ;
913926 }
914927
915- fn enter_type_param_block ( & mut self , name : & str , line_number : u32 ) -> SymbolTableResult {
928+ fn enter_type_param_block (
929+ & mut self ,
930+ name : & str ,
931+ line_number : u32 ,
932+ for_class : bool ,
933+ ) -> SymbolTableResult {
916934 // Check if we're in a class scope
917935 let in_class = self
918936 . tables
@@ -921,16 +939,21 @@ impl SymbolTableBuilder {
921939
922940 self . enter_scope ( name, CompilerScope :: TypeParams , line_number) ;
923941
924- // If we're in a class, mark that this type param scope can see the class scope
942+ // Set properties on the newly created type param scope
925943 if let Some ( table) = self . tables . last_mut ( ) {
926944 table. can_see_class_scope = in_class;
927-
928- // Add __classdict__ as a USE symbol in type param scope if in class
929- if in_class {
930- self . register_name ( "__classdict__" , SymbolUsage :: Used , TextRange :: default ( ) ) ? ;
945+ // For generic classes, create mangled_names set so that only
946+ // type parameter names get mangled (not bases or other expressions)
947+ if for_class {
948+ table . mangled_names = Some ( IndexSet :: default ( ) ) ;
931949 }
932950 }
933951
952+ // Add __classdict__ as a USE symbol in type param scope if in class
953+ if in_class {
954+ self . register_name ( "__classdict__" , SymbolUsage :: Used , TextRange :: default ( ) ) ?;
955+ }
956+
934957 // Register .type_params as a SET symbol (it will be converted to cell variable later)
935958 self . register_name ( ".type_params" , SymbolUsage :: Assigned , TextRange :: default ( ) ) ?;
936959
@@ -1202,12 +1225,20 @@ impl SymbolTableBuilder {
12021225 None
12031226 } ;
12041227
1228+ // For generic functions, scan defaults before entering type_param_block
1229+ // (defaults are evaluated in the enclosing scope, not the type param scope)
1230+ let has_type_params = type_params. is_some ( ) ;
1231+ if has_type_params {
1232+ self . scan_parameter_defaults ( parameters) ?;
1233+ }
1234+
12051235 // For generic functions, enter type_param block FIRST so that
12061236 // annotation scopes are nested inside and can see type parameters.
12071237 if let Some ( type_params) = type_params {
12081238 self . enter_type_param_block (
12091239 & format ! ( "<generic parameters of {}>" , name. as_str( ) ) ,
12101240 self . line_index_start ( type_params. range ) ,
1241+ false ,
12111242 ) ?;
12121243 self . scan_type_params ( type_params) ?;
12131244 }
@@ -1223,6 +1254,7 @@ impl SymbolTableBuilder {
12231254 self . line_index_start ( * range) ,
12241255 has_return_annotation,
12251256 * is_async,
1257+ has_type_params, // skip_defaults: already scanned above
12261258 ) ?;
12271259 self . scan_statements ( body) ?;
12281260 self . leave_scope ( ) ;
@@ -1244,11 +1276,16 @@ impl SymbolTableBuilder {
12441276 range,
12451277 node_index : _,
12461278 } ) => {
1279+ // Save class_name for the entire ClassDef processing
1280+ let prev_class = self . class_name . take ( ) ;
12471281 if let Some ( type_params) = type_params {
12481282 self . enter_type_param_block (
12491283 & format ! ( "<generic parameters of {}>" , name. as_str( ) ) ,
12501284 self . line_index_start ( type_params. range ) ,
1285+ true , // for_class: enable selective mangling
12511286 ) ?;
1287+ // Set class_name for mangling in type param scope
1288+ self . class_name = Some ( name. to_string ( ) ) ;
12521289 self . scan_type_params ( type_params) ?;
12531290 }
12541291 self . enter_scope (
@@ -1257,18 +1294,19 @@ impl SymbolTableBuilder {
12571294 self . line_index_start ( * range) ,
12581295 ) ;
12591296 // Reset in_conditional_block for new class scope
1260- // (each scope has its own conditional context)
12611297 let saved_in_conditional = self . in_conditional_block ;
12621298 self . in_conditional_block = false ;
1263- let prev_class = self . class_name . replace ( name. to_string ( ) ) ;
1299+ // Set class_name for mangling in class body
1300+ self . class_name = Some ( name. to_string ( ) ) ;
12641301 self . register_name ( "__module__" , SymbolUsage :: Assigned , * range) ?;
12651302 self . register_name ( "__qualname__" , SymbolUsage :: Assigned , * range) ?;
12661303 self . register_name ( "__doc__" , SymbolUsage :: Assigned , * range) ?;
12671304 self . register_name ( "__class__" , SymbolUsage :: Assigned , * range) ?;
12681305 self . scan_statements ( body) ?;
12691306 self . leave_scope ( ) ;
12701307 self . in_conditional_block = saved_in_conditional;
1271- self . class_name = prev_class;
1308+ // Bases/keywords are scanned in type_param scope (if generic)
1309+ // class_name is still set, so mangling works via mangled_names filtering
12721310 if let Some ( arguments) = arguments {
12731311 self . scan_expressions ( & arguments. args , ExpressionContext :: Load ) ?;
12741312 for keyword in & arguments. keywords {
@@ -1278,6 +1316,8 @@ impl SymbolTableBuilder {
12781316 if type_params. is_some ( ) {
12791317 self . leave_scope ( ) ;
12801318 }
1319+ // Restore class_name after all ClassDef processing
1320+ self . class_name = prev_class;
12811321 self . scan_decorators ( decorator_list, ExpressionContext :: Load ) ?;
12821322 self . register_ident ( name, SymbolUsage :: Assigned ) ?;
12831323 }
@@ -1506,6 +1546,7 @@ impl SymbolTableBuilder {
15061546 self . enter_type_param_block (
15071547 "TypeAlias" ,
15081548 self . line_index_start ( type_params. range ) ,
1549+ false ,
15091550 ) ?;
15101551 self . scan_type_params ( type_params) ?;
15111552 }
@@ -1833,6 +1874,7 @@ impl SymbolTableBuilder {
18331874 self . line_index_start ( expression. range ( ) ) ,
18341875 false , // lambdas have no return annotation
18351876 false , // lambdas are never async
1877+ false , // don't skip defaults
18361878 ) ?;
18371879 } else {
18381880 self . enter_scope (
@@ -2233,23 +2275,32 @@ impl SymbolTableBuilder {
22332275 Ok ( ( ) )
22342276 }
22352277
2278+ /// Scan default parameter values (evaluated in the enclosing scope)
2279+ fn scan_parameter_defaults ( & mut self , parameters : & ast:: Parameters ) -> SymbolTableResult {
2280+ for default in parameters
2281+ . posonlyargs
2282+ . iter ( )
2283+ . chain ( parameters. args . iter ( ) )
2284+ . chain ( parameters. kwonlyargs . iter ( ) )
2285+ . filter_map ( |arg| arg. default . as_ref ( ) )
2286+ {
2287+ self . scan_expression ( default, ExpressionContext :: Load ) ?;
2288+ }
2289+ Ok ( ( ) )
2290+ }
2291+
22362292 fn enter_scope_with_parameters (
22372293 & mut self ,
22382294 name : & str ,
22392295 parameters : & ast:: Parameters ,
22402296 line_number : u32 ,
22412297 has_return_annotation : bool ,
22422298 is_async : bool ,
2299+ skip_defaults : bool ,
22432300 ) -> SymbolTableResult {
2244- // Evaluate eventual default parameters:
2245- for default in parameters
2246- . posonlyargs
2247- . iter ( )
2248- . chain ( parameters. args . iter ( ) )
2249- . chain ( parameters. kwonlyargs . iter ( ) )
2250- . filter_map ( |arg| arg. default . as_ref ( ) )
2251- {
2252- self . scan_expression ( default, ExpressionContext :: Load ) ?; // not ExprContext?
2301+ // Evaluate eventual default parameters (unless already scanned before type_param_block):
2302+ if !skip_defaults {
2303+ self . scan_parameter_defaults ( parameters) ?;
22532304 }
22542305
22552306 // Annotations are scanned in outer scope:
@@ -2381,7 +2432,18 @@ impl SymbolTableBuilder {
23812432 let scope_depth = self . tables . len ( ) ;
23822433 let table = self . tables . last_mut ( ) . unwrap ( ) ;
23832434
2384- let name = mangle_name ( self . class_name . as_deref ( ) , name) ;
2435+ // Add type param names to mangled_names set for selective mangling
2436+ if matches ! ( role, SymbolUsage :: TypeParam )
2437+ && let Some ( ref mut set) = table. mangled_names
2438+ {
2439+ set. insert ( name. to_owned ( ) ) ;
2440+ }
2441+
2442+ let name = maybe_mangle_name (
2443+ self . class_name . as_deref ( ) ,
2444+ table. mangled_names . as_ref ( ) ,
2445+ name,
2446+ ) ;
23852447 // Some checks for the symbol that present on this scope level:
23862448 let symbol = if let Some ( symbol) = table. symbols . get_mut ( name. as_ref ( ) ) {
23872449 let flags = & symbol. flags ;
@@ -2582,3 +2644,19 @@ pub(crate) fn mangle_name<'a>(class_name: Option<&str>, name: &'a str) -> Cow<'a
25822644 ret. push_str ( name) ;
25832645 ret. into ( )
25842646}
2647+
2648+ /// Selective mangling for type parameter scopes around generic classes.
2649+ /// If `mangled_names` is Some, only mangle names that are in the set;
2650+ /// other names are left unmangled.
2651+ pub ( crate ) fn maybe_mangle_name < ' a > (
2652+ class_name : Option < & str > ,
2653+ mangled_names : Option < & IndexSet < String > > ,
2654+ name : & ' a str ,
2655+ ) -> Cow < ' a , str > {
2656+ if let Some ( set) = mangled_names
2657+ && !set. contains ( name)
2658+ {
2659+ return name. into ( ) ;
2660+ }
2661+ mangle_name ( class_name, name)
2662+ }
0 commit comments