Skip to content

Commit fef660e

Browse files
authored
more PEP695 (RustPython#5917)
* compile_class_body * type.__orig_bases__ regression of test_all_exported_names * rework type_params scope * refactor compile_class_def
1 parent 3ef0cfc commit fef660e

4 files changed

Lines changed: 177 additions & 87 deletions

File tree

Lib/test/test_descr.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5124,8 +5124,6 @@ def test_iter_keys(self):
51245124
self.assertEqual(keys, ['__dict__', '__doc__', '__module__',
51255125
'__weakref__', 'meth'])
51265126

5127-
# TODO: RUSTPYTHON
5128-
@unittest.expectedFailure
51295127
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
51305128
'trace function introduces __local__')
51315129
def test_iter_values(self):

Lib/test/test_typing.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6934,8 +6934,6 @@ class Y(Generic[T], NamedTuple):
69346934
with self.assertRaises(TypeError):
69356935
G[int, str]
69366936

6937-
# TODO: RUSTPYTHON
6938-
@unittest.expectedFailure
69396937
def test_generic_pep695(self):
69406938
class X[T](NamedTuple):
69416939
x: T
@@ -7560,8 +7558,6 @@ class FooBarGeneric(BarGeneric[int]):
75607558
{'a': typing.Optional[T], 'b': int, 'c': str}
75617559
)
75627560

7563-
# TODO: RUSTPYTHON
7564-
@unittest.expectedFailure
75657561
def test_pep695_generic_typeddict(self):
75667562
class A[T](TypedDict):
75677563
a: T

compiler/codegen/src/compile.rs

Lines changed: 171 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,66 +2069,56 @@ impl Compiler<'_> {
20692069
false
20702070
}
20712071

2072-
fn compile_class_def(
2072+
/// Compile the class body into a code object
2073+
/// This is similar to CPython's compiler_class_body
2074+
fn compile_class_body(
20732075
&mut self,
20742076
name: &str,
20752077
body: &[Stmt],
2076-
decorator_list: &[Decorator],
20772078
type_params: Option<&TypeParams>,
2078-
arguments: Option<&Arguments>,
2079-
) -> CompileResult<()> {
2080-
self.prepare_decorators(decorator_list)?;
2081-
2082-
let prev_ctx = self.ctx;
2083-
self.ctx = CompileContext {
2084-
func: FunctionContext::NoFunction,
2085-
in_class: true,
2086-
loop_data: None,
2087-
};
2088-
2089-
// If there are type params, we need to push a special symbol table just for them
2090-
if let Some(type_params) = type_params {
2091-
self.push_symbol_table();
2092-
// Save current private name to restore later
2093-
let saved_private = self.code_stack.last().and_then(|info| info.private.clone());
2094-
// Compile type parameters and store as .type_params
2095-
self.compile_type_params(type_params)?;
2096-
// Restore private name after type param scope
2097-
if let Some(private) = saved_private {
2098-
self.code_stack.last_mut().unwrap().private = Some(private);
2099-
}
2100-
let dot_type_params = self.name(".type_params");
2101-
emit!(self, Instruction::StoreLocal(dot_type_params));
2102-
}
2103-
2104-
self.push_output(bytecode::CodeFlags::empty(), 0, 0, 0, name.to_owned());
2079+
firstlineno: u32,
2080+
) -> CompileResult<CodeObject> {
2081+
// 1. Enter class scope
2082+
// Use enter_scope instead of push_output to match CPython
2083+
let key = self.symbol_table_stack.len();
2084+
self.push_symbol_table();
2085+
self.enter_scope(name, SymbolTableType::Class, key, firstlineno)?;
21052086

21062087
// Set qualname using the new method
21072088
let qualname = self.set_qualname();
21082089

21092090
// For class scopes, set u_private to the class name for name mangling
21102091
self.code_stack.last_mut().unwrap().private = Some(name.to_owned());
21112092

2093+
// 2. Set up class namespace
21122094
let (doc_str, body) = split_doc(body, &self.opts);
21132095

2096+
// Load (global) __name__ and store as __module__
21142097
let dunder_name = self.name("__name__");
21152098
emit!(self, Instruction::LoadGlobal(dunder_name));
21162099
let dunder_module = self.name("__module__");
21172100
emit!(self, Instruction::StoreLocal(dunder_module));
2101+
2102+
// Store __qualname__
21182103
self.emit_load_const(ConstantData::Str {
21192104
value: qualname.into(),
21202105
});
21212106
let qualname_name = self.name("__qualname__");
21222107
emit!(self, Instruction::StoreLocal(qualname_name));
2108+
2109+
// Store __doc__
21232110
self.load_docstring(doc_str);
21242111
let doc = self.name("__doc__");
21252112
emit!(self, Instruction::StoreLocal(doc));
2126-
// setup annotations
2127-
if Self::find_ann(body) {
2128-
emit!(self, Instruction::SetupAnnotation);
2129-
}
21302113

2131-
// Set __type_params__ from .type_params if we have type parameters (PEP 695)
2114+
// Store __firstlineno__ (new in Python 3.12+)
2115+
self.emit_load_const(ConstantData::Integer {
2116+
value: BigInt::from(firstlineno),
2117+
});
2118+
let firstlineno_name = self.name("__firstlineno__");
2119+
emit!(self, Instruction::StoreLocal(firstlineno_name));
2120+
2121+
// Set __type_params__ if we have type parameters
21322122
if type_params.is_some() {
21332123
// Load .type_params from enclosing scope
21342124
let dot_type_params = self.name(".type_params");
@@ -2139,8 +2129,15 @@ impl Compiler<'_> {
21392129
emit!(self, Instruction::StoreLocal(dunder_type_params));
21402130
}
21412131

2132+
// Setup annotations if needed
2133+
if Self::find_ann(body) {
2134+
emit!(self, Instruction::SetupAnnotation);
2135+
}
2136+
2137+
// 3. Compile the class body
21422138
self.compile_statements(body)?;
21432139

2140+
// 4. Handle __classcell__ if needed
21442141
let classcell_idx = self
21452142
.code_stack
21462143
.last_mut()
@@ -2159,65 +2156,167 @@ impl Compiler<'_> {
21592156
self.emit_load_const(ConstantData::None);
21602157
}
21612158

2159+
// Return the class namespace
21622160
self.emit_return_value();
21632161

2164-
let code = self.exit_scope();
2165-
self.ctx = prev_ctx;
2162+
// Exit scope and return the code object
2163+
Ok(self.exit_scope())
2164+
}
2165+
2166+
fn compile_class_def(
2167+
&mut self,
2168+
name: &str,
2169+
body: &[Stmt],
2170+
decorator_list: &[Decorator],
2171+
type_params: Option<&TypeParams>,
2172+
arguments: Option<&Arguments>,
2173+
) -> CompileResult<()> {
2174+
self.prepare_decorators(decorator_list)?;
21662175

2167-
emit!(self, Instruction::LoadBuildClass);
2176+
let is_generic = type_params.is_some();
2177+
let firstlineno = self.get_source_line_number().get().to_u32();
21682178

2169-
let mut func_flags = bytecode::MakeFunctionFlags::empty();
2179+
// Step 1: If generic, enter type params scope and compile type params
2180+
if is_generic {
2181+
let type_params_name = format!("<generic parameters of {name}>");
2182+
self.push_output(
2183+
bytecode::CodeFlags::IS_OPTIMIZED | bytecode::CodeFlags::NEW_LOCALS,
2184+
0,
2185+
0,
2186+
0,
2187+
type_params_name,
2188+
);
21702189

2171-
// Prepare generic type parameters:
2172-
if type_params.is_some() {
2173-
// Load .type_params from the type params scope
2190+
// Set private name for name mangling
2191+
self.code_stack.last_mut().unwrap().private = Some(name.to_owned());
2192+
2193+
// Compile type parameters and store as .type_params
2194+
self.compile_type_params(type_params.unwrap())?;
21742195
let dot_type_params = self.name(".type_params");
2175-
emit!(self, Instruction::LoadNameAny(dot_type_params));
2176-
func_flags |= bytecode::MakeFunctionFlags::TYPE_PARAMS;
2196+
emit!(self, Instruction::StoreLocal(dot_type_params));
21772197
}
21782198

2179-
if self.build_closure(&code) {
2180-
func_flags |= bytecode::MakeFunctionFlags::CLOSURE;
2181-
}
2199+
// Step 2: Compile class body (always done, whether generic or not)
2200+
let prev_ctx = self.ctx;
2201+
self.ctx = CompileContext {
2202+
func: FunctionContext::NoFunction,
2203+
in_class: true,
2204+
loop_data: None,
2205+
};
2206+
let class_code = self.compile_class_body(name, body, type_params, firstlineno)?;
2207+
self.ctx = prev_ctx;
21822208

2183-
self.emit_load_const(ConstantData::Code {
2184-
code: Box::new(code),
2185-
});
2186-
self.emit_load_const(ConstantData::Str { value: name.into() });
2209+
// Step 3: Generate the rest of the code for the call
2210+
if is_generic {
2211+
// Still in type params scope
2212+
let dot_type_params = self.name(".type_params");
2213+
let dot_generic_base = self.name(".generic_base");
21872214

2188-
// Turn code object into function object:
2189-
emit!(self, Instruction::MakeFunction(func_flags));
2215+
// Create .generic_base
2216+
emit!(self, Instruction::LoadNameAny(dot_type_params));
2217+
emit!(
2218+
self,
2219+
Instruction::CallIntrinsic1 {
2220+
func: bytecode::IntrinsicFunction1::SubscriptGeneric
2221+
}
2222+
);
2223+
emit!(self, Instruction::StoreLocal(dot_generic_base));
21902224

2191-
self.emit_load_const(ConstantData::Str { value: name.into() });
2225+
// Generate class creation code
2226+
emit!(self, Instruction::LoadBuildClass);
21922227

2193-
// For PEP 695 classes: handle Generic base creation
2194-
if type_params.is_some() {
2195-
if let Some(arguments) = arguments {
2196-
// Has explicit bases - use them as is, don't add Generic
2197-
// CPython doesn't add Generic when explicit bases are present
2198-
let call = self.compile_call_inner(2, arguments)?;
2199-
self.compile_normal_call(call);
2228+
// Set up the class function with type params
2229+
let mut func_flags = bytecode::MakeFunctionFlags::empty();
2230+
emit!(self, Instruction::LoadNameAny(dot_type_params));
2231+
func_flags |= bytecode::MakeFunctionFlags::TYPE_PARAMS;
2232+
2233+
if self.build_closure(&class_code) {
2234+
func_flags |= bytecode::MakeFunctionFlags::CLOSURE;
2235+
}
2236+
2237+
self.emit_load_const(ConstantData::Code {
2238+
code: Box::new(class_code),
2239+
});
2240+
self.emit_load_const(ConstantData::Str { value: name.into() });
2241+
emit!(self, Instruction::MakeFunction(func_flags));
2242+
self.emit_load_const(ConstantData::Str { value: name.into() });
2243+
2244+
// Compile original bases
2245+
let base_count = if let Some(arguments) = arguments {
2246+
for arg in &arguments.args {
2247+
self.compile_expression(arg)?;
2248+
}
2249+
arguments.args.len()
22002250
} else {
2201-
// No explicit bases, add Generic[*type_params] as the only base
2202-
// Stack currently: [function, class_name]
2251+
0
2252+
};
2253+
2254+
// Load .generic_base as the last base
2255+
emit!(self, Instruction::LoadNameAny(dot_generic_base));
22032256

2204-
// Load .type_params for creating Generic base
2205-
let dot_type_params = self.name(".type_params");
2206-
emit!(self, Instruction::LoadNameAny(dot_type_params));
2257+
let nargs = 2 + u32::try_from(base_count).expect("too many base classes") + 1; // function, name, bases..., generic_base
22072258

2208-
// Call INTRINSIC_SUBSCRIPT_GENERIC to create Generic[*type_params]
2259+
// Handle keyword arguments
2260+
if let Some(arguments) = arguments
2261+
&& !arguments.keywords.is_empty()
2262+
{
2263+
for keyword in &arguments.keywords {
2264+
if let Some(name) = &keyword.arg {
2265+
self.emit_load_const(ConstantData::Str {
2266+
value: name.as_str().into(),
2267+
});
2268+
}
2269+
self.compile_expression(&keyword.value)?;
2270+
}
22092271
emit!(
22102272
self,
2211-
Instruction::CallIntrinsic1 {
2212-
func: bytecode::IntrinsicFunction1::SubscriptGeneric
2273+
Instruction::CallFunctionKeyword {
2274+
nargs: nargs
2275+
+ u32::try_from(arguments.keywords.len())
2276+
.expect("too many keyword arguments")
22132277
}
22142278
);
2279+
} else {
2280+
emit!(self, Instruction::CallFunctionPositional { nargs });
2281+
}
2282+
2283+
// Return the created class
2284+
self.emit_return_value();
22152285

2216-
// Call __build_class__ with 3 positional args: function, class_name, Generic[T]
2217-
emit!(self, Instruction::CallFunctionPositional { nargs: 3 });
2286+
// Exit type params scope and wrap in function
2287+
let type_params_code = self.exit_scope();
2288+
2289+
// Execute the type params function
2290+
if self.build_closure(&type_params_code) {
2291+
// Should not need closure
22182292
}
2293+
self.emit_load_const(ConstantData::Code {
2294+
code: Box::new(type_params_code),
2295+
});
2296+
self.emit_load_const(ConstantData::Str {
2297+
value: format!("<generic parameters of {name}>").into(),
2298+
});
2299+
emit!(
2300+
self,
2301+
Instruction::MakeFunction(bytecode::MakeFunctionFlags::empty())
2302+
);
2303+
emit!(self, Instruction::CallFunctionPositional { nargs: 0 });
22192304
} else {
2220-
// No type params, normal compilation
2305+
// Non-generic class: standard path
2306+
emit!(self, Instruction::LoadBuildClass);
2307+
2308+
let mut func_flags = bytecode::MakeFunctionFlags::empty();
2309+
if self.build_closure(&class_code) {
2310+
func_flags |= bytecode::MakeFunctionFlags::CLOSURE;
2311+
}
2312+
2313+
self.emit_load_const(ConstantData::Code {
2314+
code: Box::new(class_code),
2315+
});
2316+
self.emit_load_const(ConstantData::Str { value: name.into() });
2317+
emit!(self, Instruction::MakeFunction(func_flags));
2318+
self.emit_load_const(ConstantData::Str { value: name.into() });
2319+
22212320
let call = if let Some(arguments) = arguments {
22222321
self.compile_call_inner(2, arguments)?
22232322
} else {
@@ -2226,13 +2325,8 @@ impl Compiler<'_> {
22262325
self.compile_normal_call(call);
22272326
}
22282327

2229-
// Pop the special type params symbol table
2230-
if type_params.is_some() {
2231-
self.pop_symbol_table();
2232-
}
2233-
2328+
// Step 4: Apply decorators and store (common to both paths)
22342329
self.apply_decorators(decorator_list);
2235-
22362330
self.store_name(name)
22372331
}
22382332

vm/src/builtins/genericalias.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,19 +617,21 @@ impl Iterable for PyGenericAlias {
617617
/// This is used for PEP 695 classes to create Generic[T] from type parameters
618618
// _Py_subscript_generic
619619
pub fn subscript_generic(type_params: PyObjectRef, vm: &VirtualMachine) -> PyResult {
620-
// Get typing.Generic type
620+
// Get typing module and _GenericAlias
621621
let typing_module = vm.import("typing", 0)?;
622622
let generic_type = typing_module.get_attr("Generic", vm)?;
623-
let generic_type = PyTypeRef::try_from_object(vm, generic_type)?;
624623

625-
// Create GenericAlias: Generic[type_params]
624+
// Call typing._GenericAlias(Generic, type_params)
625+
let generic_alias_class = typing_module.get_attr("_GenericAlias", vm)?;
626+
626627
let args = if let Ok(tuple) = type_params.try_to_ref::<PyTuple>(vm) {
627628
tuple.to_owned()
628629
} else {
629630
PyTuple::new_ref(vec![type_params], &vm.ctx)
630631
};
631632

632-
Ok(PyGenericAlias::new(generic_type, args, false, vm).into_pyobject(vm))
633+
// Create _GenericAlias instance
634+
generic_alias_class.call((generic_type, args.to_pyobject(vm)), vm)
633635
}
634636

635637
pub fn init(context: &Context) {

0 commit comments

Comments
 (0)