mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
compile_type_param_bound_or_default (#6005)
This commit is contained in:
1 parent
ff35dcd95a
commit
4e0c1aa83d
2 files changed
+152
-39
No files matched your search
@@ -1652,6 +1652,49 @@ impl Compiler<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile type parameter bound or default in a separate scope and return closure
|
||||
fn compile_type_param_bound_or_default(
|
||||
&mut self,
|
||||
expr: &Expr,
|
||||
name: &str,
|
||||
allow_starred: bool,
|
||||
) -> CompileResult<()> {
|
||||
// Push the next symbol table onto the stack
|
||||
self.push_symbol_table();
|
||||
|
||||
// Get the current symbol table
|
||||
let key = self.symbol_table_stack.len() - 1;
|
||||
let lineno = expr.range().start().to_u32();
|
||||
|
||||
// Enter scope with the type parameter name
|
||||
self.enter_scope(name, CompilerScope::TypeParams, key, lineno)?;
|
||||
|
||||
// Compile the expression
|
||||
if allow_starred && matches!(expr, Expr::Starred(_)) {
|
||||
if let Expr::Starred(starred) = expr {
|
||||
self.compile_expression(&starred.value)?;
|
||||
emit!(self, Instruction::UnpackSequence { size: 1 });
|
||||
}
|
||||
} else {
|
||||
self.compile_expression(expr)?;
|
||||
}
|
||||
|
||||
// Return value
|
||||
emit!(self, Instruction::ReturnValue);
|
||||
|
||||
// Exit scope and create closure
|
||||
let code = self.exit_scope();
|
||||
// Note: exit_scope already calls pop_symbol_table, so we don't need to call it again
|
||||
|
||||
// Create type params function with closure
|
||||
self.make_closure(code, bytecode::MakeFunctionFlags::empty())?;
|
||||
|
||||
// Call the function immediately
|
||||
emit!(self, Instruction::CallFunctionPositional { nargs: 0 });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Store each type parameter so it is accessible to the current scope, and leave a tuple of
|
||||
/// all the type parameters on the stack.
|
||||
fn compile_type_params(&mut self, type_params: &TypeParams) -> CompileResult<()> {
|
||||
@@ -1664,21 +1707,25 @@ impl Compiler<'_> {
|
||||
default,
|
||||
..
|
||||
}) => {
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: name.as_str().into(),
|
||||
});
|
||||
|
||||
if let Some(expr) = &bound {
|
||||
self.compile_expression(expr)?;
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: name.as_str().into(),
|
||||
});
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic2 {
|
||||
func: bytecode::IntrinsicFunction2::TypeVarWithBound
|
||||
}
|
||||
);
|
||||
let scope_name = if expr.is_tuple_expr() {
|
||||
format!("<TypeVar constraint of {name}>")
|
||||
} else {
|
||||
format!("<TypeVar bound of {name}>")
|
||||
};
|
||||
self.compile_type_param_bound_or_default(expr, &scope_name, false)?;
|
||||
|
||||
let intrinsic = if expr.is_tuple_expr() {
|
||||
bytecode::IntrinsicFunction2::TypeVarWithConstraint
|
||||
} else {
|
||||
bytecode::IntrinsicFunction2::TypeVarWithBound
|
||||
};
|
||||
emit!(self, Instruction::CallIntrinsic2 { func: intrinsic });
|
||||
} else {
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: name.as_str().into(),
|
||||
});
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic1 {
|
||||
@@ -1689,9 +1736,8 @@ impl Compiler<'_> {
|
||||
|
||||
// Handle default value if present (PEP 695)
|
||||
if let Some(default_expr) = default {
|
||||
// Compile the default expression
|
||||
self.compile_expression(default_expr)?;
|
||||
|
||||
let scope_name = format!("<TypeVar default of {name}>");
|
||||
self.compile_type_param_bound_or_default(default_expr, &scope_name, false)?;
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic2 {
|
||||
@@ -1716,9 +1762,8 @@ impl Compiler<'_> {
|
||||
|
||||
// Handle default value if present (PEP 695)
|
||||
if let Some(default_expr) = default {
|
||||
// Compile the default expression
|
||||
self.compile_expression(default_expr)?;
|
||||
|
||||
let scope_name = format!("<ParamSpec default of {name}>");
|
||||
self.compile_type_param_bound_or_default(default_expr, &scope_name, false)?;
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic2 {
|
||||
@@ -1743,10 +1788,9 @@ impl Compiler<'_> {
|
||||
|
||||
// Handle default value if present (PEP 695)
|
||||
if let Some(default_expr) = default {
|
||||
// Compile the default expression
|
||||
self.compile_expression(default_expr)?;
|
||||
|
||||
// Handle starred expression (*default)
|
||||
// TypeVarTuple allows starred expressions
|
||||
let scope_name = format!("<TypeVarTuple default of {name}>");
|
||||
self.compile_type_param_bound_or_default(default_expr, &scope_name, true)?;
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic2 {
|
||||
|
||||
@@ -54,6 +54,9 @@ pub struct SymbolTable {
|
||||
|
||||
/// Whether this class scope needs an implicit __classdict__ cell
|
||||
pub needs_classdict: bool,
|
||||
|
||||
/// Whether this type param scope can see the parent class scope
|
||||
pub can_see_class_scope: bool,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
@@ -68,6 +71,7 @@ impl SymbolTable {
|
||||
varnames: Vec::new(),
|
||||
needs_class_closure: false,
|
||||
needs_classdict: false,
|
||||
can_see_class_scope: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,6 +669,31 @@ impl SymbolTableBuilder<'_> {
|
||||
self.current_varnames.clear();
|
||||
}
|
||||
|
||||
fn enter_type_param_block(&mut self, name: &str, line_number: u32) -> SymbolTableResult {
|
||||
// Check if we're in a class scope
|
||||
let in_class = self
|
||||
.tables
|
||||
.last()
|
||||
.is_some_and(|t| t.typ == CompilerScope::Class);
|
||||
|
||||
self.enter_scope(name, CompilerScope::TypeParams, line_number);
|
||||
|
||||
// If we're in a class, mark that this type param scope can see the class scope
|
||||
if let Some(table) = self.tables.last_mut() {
|
||||
table.can_see_class_scope = in_class;
|
||||
|
||||
// Add __classdict__ as a USE symbol in type param scope if in class
|
||||
if in_class {
|
||||
self.register_name("__classdict__", SymbolUsage::Used, TextRange::default())?;
|
||||
}
|
||||
}
|
||||
|
||||
// Register .type_params as a SET symbol (it will be converted to cell variable later)
|
||||
self.register_name(".type_params", SymbolUsage::Assigned, TextRange::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pop symbol table and add to sub table of parent table.
|
||||
fn leave_scope(&mut self) {
|
||||
let mut table = self.tables.pop().unwrap();
|
||||
@@ -746,12 +775,10 @@ impl SymbolTableBuilder<'_> {
|
||||
self.scan_annotation(expression)?;
|
||||
}
|
||||
if let Some(type_params) = type_params {
|
||||
self.enter_scope(
|
||||
self.enter_type_param_block(
|
||||
&format!("<generic parameters of {}>", name.as_str()),
|
||||
CompilerScope::TypeParams,
|
||||
// FIXME: line no
|
||||
self.line_index_start(*range),
|
||||
);
|
||||
self.line_index_start(type_params.range),
|
||||
)?;
|
||||
self.scan_type_params(type_params)?;
|
||||
}
|
||||
self.enter_scope_with_parameters(
|
||||
@@ -774,11 +801,10 @@ impl SymbolTableBuilder<'_> {
|
||||
range,
|
||||
}) => {
|
||||
if let Some(type_params) = type_params {
|
||||
self.enter_scope(
|
||||
self.enter_type_param_block(
|
||||
&format!("<generic parameters of {}>", name.as_str()),
|
||||
CompilerScope::TypeParams,
|
||||
self.line_index_start(type_params.range),
|
||||
);
|
||||
)?;
|
||||
self.scan_type_params(type_params)?;
|
||||
}
|
||||
self.enter_scope(
|
||||
@@ -965,12 +991,10 @@ impl SymbolTableBuilder<'_> {
|
||||
..
|
||||
}) => {
|
||||
if let Some(type_params) = type_params {
|
||||
self.enter_scope(
|
||||
// &name.to_string(),
|
||||
self.enter_type_param_block(
|
||||
"TypeAlias",
|
||||
CompilerScope::TypeParams,
|
||||
self.line_index_start(type_params.range),
|
||||
);
|
||||
)?;
|
||||
self.scan_type_params(type_params)?;
|
||||
self.scan_expression(value, ExpressionContext::Load)?;
|
||||
self.leave_scope();
|
||||
@@ -1344,6 +1368,26 @@ impl SymbolTableBuilder<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Scan type parameter bound or default in a separate scope
|
||||
// = symtable_visit_type_param_bound_or_default
|
||||
fn scan_type_param_bound_or_default(&mut self, expr: &Expr, name: &str) -> SymbolTableResult {
|
||||
// Enter a new TypeParams scope for the bound/default expression
|
||||
// This allows the expression to access outer scope symbols
|
||||
let line_number = self.line_index_start(expr.range());
|
||||
self.enter_scope(name, CompilerScope::TypeParams, line_number);
|
||||
|
||||
// Note: In CPython, can_see_class_scope is preserved in the new scope
|
||||
// In RustPython, this is handled through the scope hierarchy
|
||||
|
||||
// Scan the expression in this new scope
|
||||
let result = self.scan_expression(expr, ExpressionContext::Load);
|
||||
|
||||
// Exit the scope
|
||||
self.leave_scope();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn scan_type_params(&mut self, type_params: &TypeParams) -> SymbolTableResult {
|
||||
// Register .type_params as a type parameter (automatically becomes cell variable)
|
||||
self.register_name(".type_params", SymbolUsage::TypeParam, type_params.range)?;
|
||||
@@ -1355,26 +1399,51 @@ impl SymbolTableBuilder<'_> {
|
||||
name,
|
||||
bound,
|
||||
range: type_var_range,
|
||||
..
|
||||
default,
|
||||
}) => {
|
||||
self.register_name(name.as_str(), SymbolUsage::TypeParam, *type_var_range)?;
|
||||
|
||||
// Process bound in a separate scope
|
||||
if let Some(binding) = bound {
|
||||
self.scan_expression(binding, ExpressionContext::Load)?;
|
||||
let scope_name = if binding.is_tuple_expr() {
|
||||
format!("<TypeVar constraint of {name}>")
|
||||
} else {
|
||||
format!("<TypeVar bound of {name}>")
|
||||
};
|
||||
self.scan_type_param_bound_or_default(binding, &scope_name)?;
|
||||
}
|
||||
|
||||
// Process default in a separate scope
|
||||
if let Some(default_value) = default {
|
||||
let scope_name = format!("<TypeVar default of {name}>");
|
||||
self.scan_type_param_bound_or_default(default_value, &scope_name)?;
|
||||
}
|
||||
}
|
||||
TypeParam::ParamSpec(TypeParamParamSpec {
|
||||
name,
|
||||
range: param_spec_range,
|
||||
..
|
||||
default,
|
||||
}) => {
|
||||
self.register_name(name, SymbolUsage::TypeParam, *param_spec_range)?;
|
||||
|
||||
// Process default in a separate scope
|
||||
if let Some(default_value) = default {
|
||||
let scope_name = format!("<ParamSpec default of {name}>");
|
||||
self.scan_type_param_bound_or_default(default_value, &scope_name)?;
|
||||
}
|
||||
}
|
||||
TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
|
||||
name,
|
||||
range: type_var_tuple_range,
|
||||
..
|
||||
default,
|
||||
}) => {
|
||||
self.register_name(name, SymbolUsage::TypeParam, *type_var_tuple_range)?;
|
||||
|
||||
// Process default in a separate scope
|
||||
if let Some(default_value) = default {
|
||||
let scope_name = format!("<TypeVarTuple default of {name}>");
|
||||
self.scan_type_param_bound_or_default(default_value, &scope_name)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user