Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Lib/_opcode_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,6 @@
'BEFORE_WITH': 213,
'BINARY_SUBSCR': 214,
'BUILD_CONST_KEY_MAP': 215,
'JUMP_IF_NOT_EXC_MATCH': 220,
'SET_EXC_INFO': 223,
'INSTRUMENTED_END_FOR': 234,
'INSTRUMENTED_POP_ITER': 235,
'INSTRUMENTED_END_SEND': 236,
Expand Down
21 changes: 12 additions & 9 deletions crates/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2925,12 +2925,19 @@ impl Compiler {
// If we gave a typ,
// check if this handler can handle the exception:
if let Some(exc_type) = type_ {
// Duplicate exception for test:
emit!(self, Instruction::Copy { index: 1_u32 });

// Check exception type:
// Stack: [prev_exc, exc]
self.compile_expression(exc_type)?;
emit!(self, Instruction::JumpIfNotExcMatch(next_handler));
// Stack: [prev_exc, exc, type]
emit!(self, Instruction::CheckExcMatch);
// Stack: [prev_exc, exc, bool]
emit!(
self,
Instruction::PopJumpIfFalse {
target: next_handler
}
);
// Stack: [prev_exc, exc]

// We have a match, store in name (except x as y)
if let Some(alias) = name {
Expand Down Expand Up @@ -3308,13 +3315,9 @@ impl Compiler {

// Handler matched
// Stack: [prev_exc, orig, list, new_rest, match]
// Note: CheckEgMatch already sets the matched exception as current exception
let handler_except_block = self.new_block();

// Set matched exception as current exception (for __context__ in handler body)
// This ensures that exceptions raised in the handler get the matched part
// as their __context__, not the original full exception group
emit!(self, Instruction::SetExcInfo);

// Store match to name or pop
if let Some(alias) = name {
self.store_name(alias.as_str())?;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 0 additions & 9 deletions crates/compiler-core/src/bytecode/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,6 @@ pub enum Instruction {
BuildConstKeyMap {
size: Arg<u32>,
} = 215, // Placeholder
JumpIfNotExcMatch(Arg<Label>) = 220,
SetExcInfo = 223,
// CPython 3.14 RESUME (128)
Resume {
arg: Arg<u32>,
Expand Down Expand Up @@ -418,8 +416,6 @@ impl TryFrom<u8> for Instruction {
u8::from(Self::BuildConstKeyMap {
size: Arg::marker(),
}),
u8::from(Self::JumpIfNotExcMatch(Arg::marker())),
u8::from(Self::SetExcInfo),
];

if (cpython_start..=cpython_end).contains(&value)
Expand All @@ -443,7 +439,6 @@ impl InstructionMetadata for Instruction {
Self::JumpBackward { target: l }
| Self::JumpBackwardNoInterrupt { target: l }
| Self::JumpForward { target: l }
| Self::JumpIfNotExcMatch(l)
| Self::PopJumpIfTrue { target: l }
| Self::PopJumpIfFalse { target: l }
| Self::ForIter { target: l }
Expand Down Expand Up @@ -534,7 +529,6 @@ impl InstructionMetadata for Instruction {
}
Self::IsOp(_) => -1,
Self::ContainsOp(_) => -1,
Self::JumpIfNotExcMatch(_) => -2,
Self::ReturnValue => -1,
Self::Resume { .. } => 0,
Self::YieldValue { .. } => 0,
Expand All @@ -544,7 +538,6 @@ impl InstructionMetadata for Instruction {
Self::EndSend => -1,
// CLEANUP_THROW: (sub_iter, last_sent_val, exc) -> (None, value) = 3 pop, 2 push = -1
Self::CleanupThrow => -1,
Self::SetExcInfo => 0,
Self::PushExcInfo => 1, // [exc] -> [prev_exc, exc]
Self::CheckExcMatch => 0, // [exc, type] -> [exc, bool] (pops type, pushes bool)
Self::Reraise { .. } => 0, // Exception raised, stack effect doesn't matter
Expand Down Expand Up @@ -858,7 +851,6 @@ impl InstructionMetadata for Instruction {
Self::JumpBackward { target } => w!(JUMP_BACKWARD, target),
Self::JumpBackwardNoInterrupt { target } => w!(JUMP_BACKWARD_NO_INTERRUPT, target),
Self::JumpForward { target } => w!(JUMP_FORWARD, target),
Self::JumpIfNotExcMatch(target) => w!(JUMP_IF_NOT_EXC_MATCH, target),
Self::ListAppend { i } => w!(LIST_APPEND, i),
Self::ListExtend { i } => w!(LIST_EXTEND, i),
Self::LoadAttr { idx } => {
Expand Down Expand Up @@ -912,7 +904,6 @@ impl InstructionMetadata for Instruction {
Self::ReturnValue => w!(RETURN_VALUE),
Self::Send { target } => w!(SEND, target),
Self::SetAdd { i } => w!(SET_ADD, i),
Self::SetExcInfo => w!(SET_EXC_INFO),
Self::SetFunctionAttribute { attr } => w!(SET_FUNCTION_ATTRIBUTE, ?attr),
Self::SetupAnnotations => w!(SETUP_ANNOTATIONS),
Self::SetUpdate { i } => w!(SET_UPDATE, i),
Expand Down
1 change: 0 additions & 1 deletion crates/stdlib/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ mod opcode {
Self::try_from(opcode).map(|op| op.inner()),
Ok(AnyInstruction::Real(
Instruction::ForIter { .. }
| Instruction::JumpIfNotExcMatch(_)
| Instruction::PopJumpIfFalse { .. }
| Instruction::PopJumpIfTrue { .. }
| Instruction::Send { .. }
Expand Down
59 changes: 26 additions & 33 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,15 @@ impl ExecutingFrame<'_> {
let exc_value = self.pop_value();
let (rest, matched) =
crate::exceptions::exception_group_match(&exc_value, &match_type, vm)?;

// Set matched exception as current exception (if not None)
// This mirrors CPython's PyErr_SetHandledException(match_o) in CHECK_EG_MATCH
if !vm.is_none(&matched)
&& let Some(exc) = matched.downcast_ref::<PyBaseException>()
{
vm.set_exception(Some(exc.to_owned()));
}

self.push_value(rest);
self.push_value(matched);
Ok(None)
Expand Down Expand Up @@ -1119,29 +1128,6 @@ impl ExecutingFrame<'_> {
self.push_value(vm.ctx.new_bool(value).into());
Ok(None)
}
Instruction::JumpIfNotExcMatch(target) => {
let b = self.pop_value();
let a = self.pop_value();
if let Some(tuple_of_exceptions) = b.downcast_ref::<PyTuple>() {
for exception in tuple_of_exceptions {
if !exception
.is_subclass(vm.ctx.exceptions.base_exception_type.into(), vm)?
{
return Err(vm.new_type_error(
"catching classes that do not inherit from BaseException is not allowed",
));
}
}
} else if !b.is_subclass(vm.ctx.exceptions.base_exception_type.into(), vm)? {
return Err(vm.new_type_error(
"catching classes that do not inherit from BaseException is not allowed",
));
}

let value = a.is_instance(&b, vm)?;
self.push_value(vm.ctx.new_bool(value).into());
self.pop_jump_if(vm, target.get(arg), false)
}
Instruction::JumpForward { target } => {
self.jump(target.get(arg));
Ok(None)
Expand Down Expand Up @@ -1569,15 +1555,6 @@ impl ExecutingFrame<'_> {
}
Ok(None)
}
Instruction::SetExcInfo => {
// Set the current exception to TOS (for except* handlers)
// This updates sys.exc_info() so bare 'raise' will reraise the matched exception
let exc = self.top_value();
if let Some(exc) = exc.downcast_ref::<PyBaseException>() {
vm.set_exception(Some(exc.to_owned()));
}
Ok(None)
}
Instruction::PushExcInfo => {
// Stack: [exc] -> [prev_exc, exc]
let exc = self.pop_value();
Expand All @@ -1600,7 +1577,23 @@ impl ExecutingFrame<'_> {
let exc_type = self.pop_value();
let exc = self.top_value();

// Validate that exc_type is valid for exception matching
// Validate that exc_type inherits from BaseException
if let Some(tuple_of_exceptions) = exc_type.downcast_ref::<PyTuple>() {
for exception in tuple_of_exceptions {
if !exception
.is_subclass(vm.ctx.exceptions.base_exception_type.into(), vm)?
{
return Err(vm.new_type_error(
"catching classes that do not inherit from BaseException is not allowed",
));
}
}
} else if !exc_type.is_subclass(vm.ctx.exceptions.base_exception_type.into(), vm)? {
return Err(vm.new_type_error(
"catching classes that do not inherit from BaseException is not allowed",
));
}

let result = exc.is_instance(&exc_type, vm)?;
self.push_value(vm.ctx.new_bool(result).into());
Ok(None)
Expand Down
Loading