Skip to content

Commit c3b56bf

Browse files
committed
Typeparams scope
1 parent a1b3520 commit c3b56bf

File tree

2 files changed

+117
-28
lines changed

2 files changed

+117
-28
lines changed

compiler/codegen/src/compile.rs

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,49 @@ impl Compiler<'_> {
16521652
}
16531653
}
16541654

1655+
/// Compile type parameter bound or default in a separate scope and return closure
1656+
fn compile_type_param_bound_or_default(
1657+
&mut self,
1658+
expr: &Expr,
1659+
name: &str,
1660+
allow_starred: bool,
1661+
) -> CompileResult<()> {
1662+
// Push the next symbol table onto the stack
1663+
self.push_symbol_table();
1664+
1665+
// Get the current symbol table
1666+
let key = self.symbol_table_stack.len() - 1;
1667+
let lineno = expr.range().start().to_u32();
1668+
1669+
// Enter scope with the type parameter name
1670+
self.enter_scope(name, CompilerScope::TypeParams, key, lineno)?;
1671+
1672+
// Compile the expression
1673+
if allow_starred && matches!(expr, Expr::Starred(_)) {
1674+
if let Expr::Starred(starred) = expr {
1675+
self.compile_expression(&starred.value)?;
1676+
emit!(self, Instruction::UnpackSequence { size: 1 });
1677+
}
1678+
} else {
1679+
self.compile_expression(expr)?;
1680+
}
1681+
1682+
// Return value
1683+
emit!(self, Instruction::ReturnValue);
1684+
1685+
// Exit scope and create closure
1686+
let code = self.exit_scope();
1687+
// Note: exit_scope already calls pop_symbol_table, so we don't need to call it again
1688+
1689+
// Create type params function with closure
1690+
self.make_closure(code, bytecode::MakeFunctionFlags::empty())?;
1691+
1692+
// Call the function immediately
1693+
emit!(self, Instruction::CallFunctionPositional { nargs: 0 });
1694+
1695+
Ok(())
1696+
}
1697+
16551698
/// Store each type parameter so it is accessible to the current scope, and leave a tuple of
16561699
/// all the type parameters on the stack.
16571700
fn compile_type_params(&mut self, type_params: &TypeParams) -> CompileResult<()> {
@@ -1664,21 +1707,25 @@ impl Compiler<'_> {
16641707
default,
16651708
..
16661709
}) => {
1710+
self.emit_load_const(ConstantData::Str {
1711+
value: name.as_str().into(),
1712+
});
1713+
16671714
if let Some(expr) = &bound {
1668-
self.compile_expression(expr)?;
1669-
self.emit_load_const(ConstantData::Str {
1670-
value: name.as_str().into(),
1671-
});
1672-
emit!(
1673-
self,
1674-
Instruction::CallIntrinsic2 {
1675-
func: bytecode::IntrinsicFunction2::TypeVarWithBound
1676-
}
1677-
);
1715+
let scope_name = if expr.is_tuple_expr() {
1716+
format!("<TypeVar constraint of {name}>")
1717+
} else {
1718+
format!("<TypeVar bound of {name}>")
1719+
};
1720+
self.compile_type_param_bound_or_default(expr, &scope_name, false)?;
1721+
1722+
let intrinsic = if expr.is_tuple_expr() {
1723+
bytecode::IntrinsicFunction2::TypeVarWithConstraint
1724+
} else {
1725+
bytecode::IntrinsicFunction2::TypeVarWithBound
1726+
};
1727+
emit!(self, Instruction::CallIntrinsic2 { func: intrinsic });
16781728
} else {
1679-
self.emit_load_const(ConstantData::Str {
1680-
value: name.as_str().into(),
1681-
});
16821729
emit!(
16831730
self,
16841731
Instruction::CallIntrinsic1 {
@@ -1689,9 +1736,8 @@ impl Compiler<'_> {
16891736

16901737
// Handle default value if present (PEP 695)
16911738
if let Some(default_expr) = default {
1692-
// Compile the default expression
1693-
self.compile_expression(default_expr)?;
1694-
1739+
let scope_name = format!("<TypeVar default of {name}>");
1740+
self.compile_type_param_bound_or_default(default_expr, &scope_name, false)?;
16951741
emit!(
16961742
self,
16971743
Instruction::CallIntrinsic2 {
@@ -1716,9 +1762,8 @@ impl Compiler<'_> {
17161762

17171763
// Handle default value if present (PEP 695)
17181764
if let Some(default_expr) = default {
1719-
// Compile the default expression
1720-
self.compile_expression(default_expr)?;
1721-
1765+
let scope_name = format!("<ParamSpec default of {name}>");
1766+
self.compile_type_param_bound_or_default(default_expr, &scope_name, false)?;
17221767
emit!(
17231768
self,
17241769
Instruction::CallIntrinsic2 {
@@ -1743,10 +1788,9 @@ impl Compiler<'_> {
17431788

17441789
// Handle default value if present (PEP 695)
17451790
if let Some(default_expr) = default {
1746-
// Compile the default expression
1747-
self.compile_expression(default_expr)?;
1748-
1749-
// Handle starred expression (*default)
1791+
// TypeVarTuple allows starred expressions
1792+
let scope_name = format!("<TypeVarTuple default of {name}>");
1793+
self.compile_type_param_bound_or_default(default_expr, &scope_name, true)?;
17501794
emit!(
17511795
self,
17521796
Instruction::CallIntrinsic2 {

compiler/codegen/src/symboltable.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ impl SymbolTableBuilder<'_> {
674674
let in_class = self
675675
.tables
676676
.last()
677-
.map_or(false, |t| t.typ == CompilerScope::Class);
677+
.is_some_and(|t| t.typ == CompilerScope::Class);
678678

679679
self.enter_scope(name, CompilerScope::TypeParams, line_number);
680680

@@ -1368,6 +1368,26 @@ impl SymbolTableBuilder<'_> {
13681368
Ok(())
13691369
}
13701370

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+
13711391
fn scan_type_params(&mut self, type_params: &TypeParams) -> SymbolTableResult {
13721392
// Register .type_params as a type parameter (automatically becomes cell variable)
13731393
self.register_name(".type_params", SymbolUsage::TypeParam, type_params.range)?;
@@ -1379,26 +1399,51 @@ impl SymbolTableBuilder<'_> {
13791399
name,
13801400
bound,
13811401
range: type_var_range,
1382-
..
1402+
default,
13831403
}) => {
13841404
self.register_name(name.as_str(), SymbolUsage::TypeParam, *type_var_range)?;
1405+
1406+
// Process bound in a separate scope
13851407
if let Some(binding) = bound {
1386-
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)?;
13871420
}
13881421
}
13891422
TypeParam::ParamSpec(TypeParamParamSpec {
13901423
name,
13911424
range: param_spec_range,
1392-
..
1425+
default,
13931426
}) => {
13941427
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+
}
13951434
}
13961435
TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
13971436
name,
13981437
range: type_var_tuple_range,
1399-
..
1438+
default,
14001439
}) => {
14011440
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+
}
14021447
}
14031448
}
14041449
}

0 commit comments

Comments
 (0)