@@ -54,6 +54,9 @@ pub struct SymbolTable {
54
54
55
55
/// Whether this class scope needs an implicit __classdict__ cell
56
56
pub needs_classdict : bool ,
57
+
58
+ /// Whether this type param scope can see the parent class scope
59
+ pub can_see_class_scope : bool ,
57
60
}
58
61
59
62
impl SymbolTable {
@@ -68,6 +71,7 @@ impl SymbolTable {
68
71
varnames : Vec :: new ( ) ,
69
72
needs_class_closure : false ,
70
73
needs_classdict : false ,
74
+ can_see_class_scope : false ,
71
75
}
72
76
}
73
77
@@ -665,6 +669,31 @@ impl SymbolTableBuilder<'_> {
665
669
self . current_varnames . clear ( ) ;
666
670
}
667
671
672
+ fn enter_type_param_block ( & mut self , name : & str , line_number : u32 ) -> SymbolTableResult {
673
+ // Check if we're in a class scope
674
+ let in_class = self
675
+ . tables
676
+ . last ( )
677
+ . is_some_and ( |t| t. typ == CompilerScope :: Class ) ;
678
+
679
+ self . enter_scope ( name, CompilerScope :: TypeParams , line_number) ;
680
+
681
+ // If we're in a class, mark that this type param scope can see the class scope
682
+ if let Some ( table) = self . tables . last_mut ( ) {
683
+ table. can_see_class_scope = in_class;
684
+
685
+ // Add __classdict__ as a USE symbol in type param scope if in class
686
+ if in_class {
687
+ self . register_name ( "__classdict__" , SymbolUsage :: Used , TextRange :: default ( ) ) ?;
688
+ }
689
+ }
690
+
691
+ // Register .type_params as a SET symbol (it will be converted to cell variable later)
692
+ self . register_name ( ".type_params" , SymbolUsage :: Assigned , TextRange :: default ( ) ) ?;
693
+
694
+ Ok ( ( ) )
695
+ }
696
+
668
697
/// Pop symbol table and add to sub table of parent table.
669
698
fn leave_scope ( & mut self ) {
670
699
let mut table = self . tables . pop ( ) . unwrap ( ) ;
@@ -746,12 +775,10 @@ impl SymbolTableBuilder<'_> {
746
775
self . scan_annotation ( expression) ?;
747
776
}
748
777
if let Some ( type_params) = type_params {
749
- self . enter_scope (
778
+ self . enter_type_param_block (
750
779
& format ! ( "<generic parameters of {}>" , name. as_str( ) ) ,
751
- CompilerScope :: TypeParams ,
752
- // FIXME: line no
753
- self . line_index_start ( * range) ,
754
- ) ;
780
+ self . line_index_start ( type_params. range ) ,
781
+ ) ?;
755
782
self . scan_type_params ( type_params) ?;
756
783
}
757
784
self . enter_scope_with_parameters (
@@ -774,11 +801,10 @@ impl SymbolTableBuilder<'_> {
774
801
range,
775
802
} ) => {
776
803
if let Some ( type_params) = type_params {
777
- self . enter_scope (
804
+ self . enter_type_param_block (
778
805
& format ! ( "<generic parameters of {}>" , name. as_str( ) ) ,
779
- CompilerScope :: TypeParams ,
780
806
self . line_index_start ( type_params. range ) ,
781
- ) ;
807
+ ) ? ;
782
808
self . scan_type_params ( type_params) ?;
783
809
}
784
810
self . enter_scope (
@@ -965,12 +991,10 @@ impl SymbolTableBuilder<'_> {
965
991
..
966
992
} ) => {
967
993
if let Some ( type_params) = type_params {
968
- self . enter_scope (
969
- // &name.to_string(),
994
+ self . enter_type_param_block (
970
995
"TypeAlias" ,
971
- CompilerScope :: TypeParams ,
972
996
self . line_index_start ( type_params. range ) ,
973
- ) ;
997
+ ) ? ;
974
998
self . scan_type_params ( type_params) ?;
975
999
self . scan_expression ( value, ExpressionContext :: Load ) ?;
976
1000
self . leave_scope ( ) ;
@@ -1344,6 +1368,26 @@ impl SymbolTableBuilder<'_> {
1344
1368
Ok ( ( ) )
1345
1369
}
1346
1370
1371
+ /// Scan type parameter bound or default in a separate scope
1372
+ // = symtable_visit_type_param_bound_or_default
1373
+ fn scan_type_param_bound_or_default ( & mut self , expr : & Expr , name : & str ) -> SymbolTableResult {
1374
+ // Enter a new TypeParams scope for the bound/default expression
1375
+ // This allows the expression to access outer scope symbols
1376
+ let line_number = self . line_index_start ( expr. range ( ) ) ;
1377
+ self . enter_scope ( name, CompilerScope :: TypeParams , line_number) ;
1378
+
1379
+ // Note: In CPython, can_see_class_scope is preserved in the new scope
1380
+ // In RustPython, this is handled through the scope hierarchy
1381
+
1382
+ // Scan the expression in this new scope
1383
+ let result = self . scan_expression ( expr, ExpressionContext :: Load ) ;
1384
+
1385
+ // Exit the scope
1386
+ self . leave_scope ( ) ;
1387
+
1388
+ result
1389
+ }
1390
+
1347
1391
fn scan_type_params ( & mut self , type_params : & TypeParams ) -> SymbolTableResult {
1348
1392
// Register .type_params as a type parameter (automatically becomes cell variable)
1349
1393
self . register_name ( ".type_params" , SymbolUsage :: TypeParam , type_params. range ) ?;
@@ -1355,26 +1399,51 @@ impl SymbolTableBuilder<'_> {
1355
1399
name,
1356
1400
bound,
1357
1401
range : type_var_range,
1358
- ..
1402
+ default ,
1359
1403
} ) => {
1360
1404
self . register_name ( name. as_str ( ) , SymbolUsage :: TypeParam , * type_var_range) ?;
1405
+
1406
+ // Process bound in a separate scope
1361
1407
if let Some ( binding) = bound {
1362
- self . scan_expression ( binding, ExpressionContext :: Load ) ?;
1408
+ let scope_name = if binding. is_tuple_expr ( ) {
1409
+ format ! ( "<TypeVar constraint of {name}>" )
1410
+ } else {
1411
+ format ! ( "<TypeVar bound of {name}>" )
1412
+ } ;
1413
+ self . scan_type_param_bound_or_default ( binding, & scope_name) ?;
1414
+ }
1415
+
1416
+ // Process default in a separate scope
1417
+ if let Some ( default_value) = default {
1418
+ let scope_name = format ! ( "<TypeVar default of {name}>" ) ;
1419
+ self . scan_type_param_bound_or_default ( default_value, & scope_name) ?;
1363
1420
}
1364
1421
}
1365
1422
TypeParam :: ParamSpec ( TypeParamParamSpec {
1366
1423
name,
1367
1424
range : param_spec_range,
1368
- ..
1425
+ default ,
1369
1426
} ) => {
1370
1427
self . register_name ( name, SymbolUsage :: TypeParam , * param_spec_range) ?;
1428
+
1429
+ // Process default in a separate scope
1430
+ if let Some ( default_value) = default {
1431
+ let scope_name = format ! ( "<ParamSpec default of {name}>" ) ;
1432
+ self . scan_type_param_bound_or_default ( default_value, & scope_name) ?;
1433
+ }
1371
1434
}
1372
1435
TypeParam :: TypeVarTuple ( TypeParamTypeVarTuple {
1373
1436
name,
1374
1437
range : type_var_tuple_range,
1375
- ..
1438
+ default ,
1376
1439
} ) => {
1377
1440
self . register_name ( name, SymbolUsage :: TypeParam , * type_var_tuple_range) ?;
1441
+
1442
+ // Process default in a separate scope
1443
+ if let Some ( default_value) = default {
1444
+ let scope_name = format ! ( "<TypeVarTuple default of {name}>" ) ;
1445
+ self . scan_type_param_bound_or_default ( default_value, & scope_name) ?;
1446
+ }
1378
1447
}
1379
1448
}
1380
1449
}
0 commit comments