Skip to content

Commit 30f421b

Browse files
committed
typing TypeAlias
1 parent 8b6c78c commit 30f421b

File tree

5 files changed

+134
-123
lines changed

5 files changed

+134
-123
lines changed

Lib/test/test_typing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6649,8 +6649,6 @@ def manager():
66496649
self.assertIsInstance(cm, typing.ContextManager)
66506650
self.assertNotIsInstance(42, typing.ContextManager)
66516651

6652-
# TODO: RUSTPYTHON
6653-
@unittest.expectedFailure
66546652
def test_contextmanager_type_params(self):
66556653
cm1 = typing.ContextManager[int]
66566654
self.assertEqual(get_args(cm1), (int, bool | None))

compiler/codegen/src/compile.rs

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,33 +1067,41 @@ impl Compiler<'_> {
10671067

10681068
// For PEP 695 syntax, we need to compile type_params first
10691069
// so that they're available when compiling the value expression
1070+
// Push name first
1071+
self.emit_load_const(ConstantData::Str {
1072+
value: name_string.clone().into(),
1073+
});
1074+
10701075
if let Some(type_params) = type_params {
10711076
self.push_symbol_table();
10721077

1073-
// Compile type params first to define T1, T2, etc.
1078+
// Compile type params and push to stack
10741079
self.compile_type_params(type_params)?;
1075-
// Stack now has type_params tuple at top
1080+
// Stack now has [name, type_params_tuple]
10761081

10771082
// Compile value expression (can now see T1, T2)
10781083
self.compile_expression(value)?;
1079-
// Stack: [type_params_tuple, value]
1080-
1081-
// We need [value, type_params_tuple] for TypeAlias instruction
1082-
emit!(self, Instruction::Rotate2);
1084+
// Stack: [name, type_params_tuple, value]
10831085

10841086
self.pop_symbol_table();
10851087
} else {
1086-
// No type params - push value first, then None (not empty tuple)
1087-
self.compile_expression(value)?;
10881088
// Push None for type_params (matching CPython)
10891089
self.emit_load_const(ConstantData::None);
1090+
// Stack: [name, None]
1091+
1092+
// Compile value expression
1093+
self.compile_expression(value)?;
1094+
// Stack: [name, None, value]
10901095
}
10911096

1092-
// Push name last
1093-
self.emit_load_const(ConstantData::Str {
1094-
value: name_string.clone().into(),
1095-
});
1096-
emit!(self, Instruction::TypeAlias);
1097+
// Build tuple of 3 elements and call intrinsic
1098+
emit!(self, Instruction::BuildTuple { size: 3 });
1099+
emit!(
1100+
self,
1101+
Instruction::CallIntrinsic1 {
1102+
func: bytecode::IntrinsicFunction1::TypeAlias
1103+
}
1104+
);
10971105
self.store_name(&name_string)?;
10981106
}
10991107
Stmt::IpyEscapeCommand(_) => todo!(),
@@ -1246,12 +1254,22 @@ impl Compiler<'_> {
12461254
self.emit_load_const(ConstantData::Str {
12471255
value: name.as_str().into(),
12481256
});
1249-
emit!(self, Instruction::TypeVarWithBound);
1257+
emit!(
1258+
self,
1259+
Instruction::CallIntrinsic2 {
1260+
func: bytecode::IntrinsicFunction2::TypeVarWithBound
1261+
}
1262+
);
12501263
} else {
12511264
self.emit_load_const(ConstantData::Str {
12521265
value: name.as_str().into(),
12531266
});
1254-
emit!(self, Instruction::TypeVar);
1267+
emit!(
1268+
self,
1269+
Instruction::CallIntrinsic1 {
1270+
func: bytecode::IntrinsicFunction1::TypeVar
1271+
}
1272+
);
12551273
}
12561274

12571275
// Handle default value if present (PEP 695)
@@ -1274,7 +1292,12 @@ impl Compiler<'_> {
12741292
self.emit_load_const(ConstantData::Str {
12751293
value: name.as_str().into(),
12761294
});
1277-
emit!(self, Instruction::ParamSpec);
1295+
emit!(
1296+
self,
1297+
Instruction::CallIntrinsic1 {
1298+
func: bytecode::IntrinsicFunction1::ParamSpec
1299+
}
1300+
);
12781301

12791302
// Handle default value if present (PEP 695)
12801303
if let Some(default_expr) = default {
@@ -1296,7 +1319,12 @@ impl Compiler<'_> {
12961319
self.emit_load_const(ConstantData::Str {
12971320
value: name.as_str().into(),
12981321
});
1299-
emit!(self, Instruction::TypeVarTuple);
1322+
emit!(
1323+
self,
1324+
Instruction::CallIntrinsic1 {
1325+
func: bytecode::IntrinsicFunction1::TypeVarTuple
1326+
}
1327+
);
13001328

13011329
// Handle default value if present (PEP 695)
13021330
if let Some(default_expr) = default {

compiler/core/src/bytecode.rs

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -382,27 +382,13 @@ op_arg_enum!(
382382
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
383383
#[repr(u8)]
384384
pub enum IntrinsicFunction1 {
385-
/// Import * special case
386-
// ImportStar = 0,
387-
/// Set stop iteration value
388-
// StopAsyncIteration = 1,
389-
/// Unary operators
390-
// UnaryPositive = 2,
391-
// UnaryNegative = 3,
392-
// UnaryNot = 4,
393-
// UnaryInvert = 5,
394-
/// Exit init subclass
395-
// ExitInitCheck = 6,
396-
/// Create a new list from an iterator
397-
// ListToTupleForCall = 7,
398385
/// Type parameter related
399-
// TypeVar = 8,
400-
// TypeVarTuple = 9,
401-
// ParamSpec = 10,
386+
TypeVar = 7,
387+
ParamSpec = 8,
388+
TypeVarTuple = 9,
402389
/// Generic subscript for PEP 695
403390
SubscriptGeneric = 10,
404-
// TypeAlias = 12,
405-
// TypeParams = 13,
391+
TypeAlias = 11,
406392
}
407393
);
408394

@@ -412,8 +398,8 @@ op_arg_enum!(
412398
#[repr(u8)]
413399
pub enum IntrinsicFunction2 {
414400
// PrepReraiseS tar = 1,
415-
// TypeVarWithBound = 2,
416-
// TypeVarWithConstraints = 3,
401+
TypeVarWithBound = 2,
402+
TypeVarWithConstraint = 3,
417403
SetFunctionTypeParams = 4,
418404
/// Set default value for type parameter (PEP 695)
419405
SetTypeparamDefault = 5,
@@ -668,16 +654,10 @@ pub enum Instruction {
668654
MatchKeys,
669655
MatchClass(Arg<u32>),
670656
ExtendedArg,
671-
TypeVar,
672-
TypeVarWithBound,
673-
TypeVarWithConstraint,
674-
TypeAlias,
675-
TypeVarTuple,
676-
ParamSpec,
677657
// If you add a new instruction here, be sure to keep LAST_INSTRUCTION updated
678658
}
679659
// This must be kept up to date to avoid marshaling errors
680-
const LAST_INSTRUCTION: Instruction = Instruction::ParamSpec;
660+
const LAST_INSTRUCTION: Instruction = Instruction::ExtendedArg;
681661
const _: () = assert!(mem::size_of::<Instruction>() == 1);
682662

683663
impl From<Instruction> for u8 {
@@ -1380,12 +1360,6 @@ impl Instruction {
13801360
MatchKeys => -1,
13811361
MatchClass(_) => -2,
13821362
ExtendedArg => 0,
1383-
TypeVar => 0,
1384-
TypeVarWithBound => -1,
1385-
TypeVarWithConstraint => -1,
1386-
TypeAlias => -2,
1387-
ParamSpec => 0,
1388-
TypeVarTuple => 0,
13891363
}
13901364
}
13911365

@@ -1565,12 +1539,6 @@ impl Instruction {
15651539
MatchKeys => w!(MatchKeys),
15661540
MatchClass(arg) => w!(MatchClass, arg),
15671541
ExtendedArg => w!(ExtendedArg, Arg::<u32>::marker()),
1568-
TypeVar => w!(TypeVar),
1569-
TypeVarWithBound => w!(TypeVarWithBound),
1570-
TypeVarWithConstraint => w!(TypeVarWithConstraint),
1571-
TypeAlias => w!(TypeAlias),
1572-
ParamSpec => w!(ParamSpec),
1573-
TypeVarTuple => w!(TypeVarTuple),
15741542
}
15751543
}
15761544
}

vm/src/frame.rs

Lines changed: 61 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,71 +1255,6 @@ impl ExecutingFrame<'_> {
12551255
*extend_arg = true;
12561256
Ok(None)
12571257
}
1258-
bytecode::Instruction::TypeVar => {
1259-
let type_name = self.pop_value();
1260-
let type_var: PyObjectRef =
1261-
typing::TypeVar::new(vm, type_name.clone(), vm.ctx.none(), vm.ctx.none())
1262-
.into_ref(&vm.ctx)
1263-
.into();
1264-
self.push_value(type_var);
1265-
Ok(None)
1266-
}
1267-
bytecode::Instruction::TypeVarWithBound => {
1268-
let type_name = self.pop_value();
1269-
let bound = self.pop_value();
1270-
let type_var: PyObjectRef =
1271-
typing::TypeVar::new(vm, type_name.clone(), bound, vm.ctx.none())
1272-
.into_ref(&vm.ctx)
1273-
.into();
1274-
self.push_value(type_var);
1275-
Ok(None)
1276-
}
1277-
bytecode::Instruction::TypeVarWithConstraint => {
1278-
let type_name = self.pop_value();
1279-
let constraint = self.pop_value();
1280-
let type_var: PyObjectRef =
1281-
typing::TypeVar::new(vm, type_name.clone(), vm.ctx.none(), constraint)
1282-
.into_ref(&vm.ctx)
1283-
.into();
1284-
self.push_value(type_var);
1285-
Ok(None)
1286-
}
1287-
bytecode::Instruction::TypeAlias => {
1288-
let name = self.pop_value();
1289-
let type_params_obj = self.pop_value();
1290-
1291-
// CPython allows None or tuple for type_params
1292-
let type_params: PyTupleRef = if vm.is_none(&type_params_obj) {
1293-
// If None, use empty tuple (matching CPython's behavior)
1294-
vm.ctx.empty_tuple.clone()
1295-
} else {
1296-
type_params_obj
1297-
.downcast()
1298-
.map_err(|_| vm.new_type_error("Type params must be a tuple."))?
1299-
};
1300-
1301-
let value = self.pop_value();
1302-
let type_alias = typing::TypeAliasType::new(name, type_params, value);
1303-
self.push_value(type_alias.into_ref(&vm.ctx).into());
1304-
Ok(None)
1305-
}
1306-
bytecode::Instruction::ParamSpec => {
1307-
let param_spec_name = self.pop_value();
1308-
let param_spec: PyObjectRef = typing::ParamSpec::new(param_spec_name.clone(), vm)
1309-
.into_ref(&vm.ctx)
1310-
.into();
1311-
self.push_value(param_spec);
1312-
Ok(None)
1313-
}
1314-
bytecode::Instruction::TypeVarTuple => {
1315-
let type_var_tuple_name = self.pop_value();
1316-
let type_var_tuple: PyObjectRef =
1317-
typing::TypeVarTuple::new(type_var_tuple_name.clone(), vm)
1318-
.into_ref(&vm.ctx)
1319-
.into();
1320-
self.push_value(type_var_tuple);
1321-
Ok(None)
1322-
}
13231258
bytecode::Instruction::MatchMapping => {
13241259
// Pop the subject from stack
13251260
let subject = self.pop_value();
@@ -2272,6 +2207,53 @@ impl ExecutingFrame<'_> {
22722207
// Used for PEP 695: Generic[*type_params]
22732208
crate::builtins::genericalias::subscript_generic(arg, vm)
22742209
}
2210+
bytecode::IntrinsicFunction1::TypeVar => {
2211+
let type_var: PyObjectRef =
2212+
typing::TypeVar::new(vm, arg.clone(), vm.ctx.none(), vm.ctx.none())
2213+
.into_ref(&vm.ctx)
2214+
.into();
2215+
Ok(type_var)
2216+
}
2217+
bytecode::IntrinsicFunction1::ParamSpec => {
2218+
let param_spec: PyObjectRef = typing::ParamSpec::new(arg.clone(), vm)
2219+
.into_ref(&vm.ctx)
2220+
.into();
2221+
Ok(param_spec)
2222+
}
2223+
bytecode::IntrinsicFunction1::TypeVarTuple => {
2224+
let type_var_tuple: PyObjectRef = typing::TypeVarTuple::new(arg.clone(), vm)
2225+
.into_ref(&vm.ctx)
2226+
.into();
2227+
Ok(type_var_tuple)
2228+
}
2229+
bytecode::IntrinsicFunction1::TypeAlias => {
2230+
// TypeAlias receives a tuple of (name, type_params, value)
2231+
let tuple: PyTupleRef = arg
2232+
.downcast()
2233+
.map_err(|_| vm.new_type_error("TypeAlias expects a tuple argument"))?;
2234+
2235+
if tuple.len() != 3 {
2236+
return Err(vm.new_type_error(format!(
2237+
"TypeAlias expects exactly 3 arguments, got {}",
2238+
tuple.len()
2239+
)));
2240+
}
2241+
2242+
let name = tuple.as_slice()[0].clone();
2243+
let type_params_obj = tuple.as_slice()[1].clone();
2244+
let value = tuple.as_slice()[2].clone();
2245+
2246+
let type_params: PyTupleRef = if vm.is_none(&type_params_obj) {
2247+
vm.ctx.empty_tuple.clone()
2248+
} else {
2249+
type_params_obj
2250+
.downcast()
2251+
.map_err(|_| vm.new_type_error("Type params must be a tuple."))?
2252+
};
2253+
2254+
let type_alias = typing::TypeAliasType::new(name, type_params, value);
2255+
Ok(type_alias.into_ref(&vm.ctx).into())
2256+
}
22752257
}
22762258
}
22772259

@@ -2292,6 +2274,20 @@ impl ExecutingFrame<'_> {
22922274
arg1.set_attr("__type_params__", arg2, vm)?;
22932275
Ok(arg1)
22942276
}
2277+
bytecode::IntrinsicFunction2::TypeVarWithBound => {
2278+
let type_var: PyObjectRef =
2279+
typing::TypeVar::new(vm, arg1.clone(), arg2, vm.ctx.none())
2280+
.into_ref(&vm.ctx)
2281+
.into();
2282+
Ok(type_var)
2283+
}
2284+
bytecode::IntrinsicFunction2::TypeVarWithConstraint => {
2285+
let type_var: PyObjectRef =
2286+
typing::TypeVar::new(vm, arg1.clone(), vm.ctx.none(), arg2)
2287+
.into_ref(&vm.ctx)
2288+
.into();
2289+
Ok(type_var)
2290+
}
22952291
}
22962292
}
22972293

vm/src/stdlib/typing.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,27 @@ pub(crate) mod decl {
113113
value,
114114
}
115115
}
116+
117+
#[pygetset]
118+
fn __name__(&self) -> PyObjectRef {
119+
self.name.clone()
120+
}
121+
122+
#[pygetset]
123+
fn __value__(&self) -> PyObjectRef {
124+
self.value.clone()
125+
}
126+
127+
#[pygetset]
128+
fn __type_params__(&self) -> PyTupleRef {
129+
self.type_params.clone()
130+
}
131+
132+
#[pymethod(name = "__repr__")]
133+
fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
134+
let name = self.name.str(vm)?;
135+
Ok(name.as_str().to_owned())
136+
}
116137
}
117138

118139
// impl AsMapping for Generic {

0 commit comments

Comments
 (0)