Conversation
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/compiler-core/src/bytecode/instruction.rs (1)
416-463: RustPython cannot deserialize CPython 3.14 bytecode using opcodes 121‑127 or 226‑233.CPython 3.14 assigns opcodes throughout the entire range 0–255. Specifically, values 121–127 are assigned to
STORE_FAST_LOAD_FASTthroughFAST_FUNCTION, and values 226–233 are assigned toBUILD_MAP__RETURN_VALUEthroughMATCH_CLASS__RETURN_VALUE. The currentTryFrom<u8>implementation rejects these ranges, causing valid CPython 3.14 bytecode to fail deserialization. These gaps must be filled with the corresponding CPython 3.14 opcodes to maintain compatibility.
🤖 Fix all issues with AI agents
In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 17-199: The GetAwaitable opcode in the Instruction enum is missing
the required oparg ("where") per CPython 3.14; update the
Instruction::GetAwaitable variant to include an oparg field (e.g. GetAwaitable {
oparg: Arg<u32> } or a small enum type Arg<GetAwaitableWhere>) so
decoding/encoding and semantics can read 0/1/2 contexts correctly; search for
Instruction::GetAwaitable and adjust any places that construct, pattern-match,
encode/decode, or format Instruction to handle the new oparg field (and add a
small GetAwaitableWhere type if you prefer typed values instead of raw u32).
In `@crates/vm/src/version.rs`:
- Around line 131-132: The constant PYC_MAGIC_NUMBER is set incorrectly to 2996;
update the value of pub const PYC_MAGIC_NUMBER in version.rs to 3627 so it
matches CPython 3.14's magic number (as defined in CPython's
pycore_magic_number) to ensure .pyc files from CPython 3.14 are read correctly.
🧹 Nitpick comments (1)
crates/compiler-core/src/bytecode/instruction.rs (1)
872-1011: Add disassembly formatting for newly added opcodes to avoid placeholder output.Several new variants (e.g.,
LoadCommonConstant,LoadSmallInt,LoadSpecial,LoadFastBorrow,BinarySlice,StoreSlice) currently fall through toRUSTPYTHON_PLACEHOLDER, makingdisoutput harder to read. Consider adding explicit formatters.♻️ Suggested disassembly cases
@@ - Self::BinarySubscr => w!(BINARY_SUBSCR), + Self::BinarySubscr => w!(BINARY_SUBSCR), + Self::BinarySlice => w!(BINARY_SLICE), @@ - Self::LoadBuildClass => w!(LOAD_BUILD_CLASS), - Self::LoadFromDictOrDeref(i) => w!(LOAD_FROM_DICT_OR_DEREF, cell_name = i), - Self::LoadConst { idx } => fmt_const("LOAD_CONST", arg, f, idx), + Self::LoadBuildClass => w!(LOAD_BUILD_CLASS), + Self::LoadCommonConstant { idx } => w!(LOAD_COMMON_CONSTANT, idx), + Self::LoadSmallInt { idx } => w!(LOAD_SMALL_INT, idx), + Self::LoadSpecial { arg } => w!(LOAD_SPECIAL, arg), + Self::LoadFromDictOrDeref(i) => w!(LOAD_FROM_DICT_OR_DEREF, cell_name = i), + Self::LoadConst { idx } => fmt_const("LOAD_CONST", arg, f, idx), Self::LoadDeref(idx) => w!(LOAD_DEREF, cell_name = idx), Self::LoadFast(idx) => w!(LOAD_FAST, varname = idx), Self::LoadFastAndClear(idx) => w!(LOAD_FAST_AND_CLEAR, varname = idx), + Self::LoadFastBorrow(idx) => w!(LOAD_FAST_BORROW, varname = idx), @@ - Self::StoreSubscr => w!(STORE_SUBSCR), + Self::StoreSubscr => w!(STORE_SUBSCR), + Self::StoreSlice => w!(STORE_SLICE),
| /// Currently aligned with CPython 3.14. | ||
| /// | ||
| /// ## See also | ||
| /// - [CPython opcode IDs](https://github.com/python/cpython/blob/627894459a84be3488a1789919679c997056a03c/Include/opcode_ids.h) | ||
| /// - [CPython opcode IDs](https://github.com/python/cpython/blob/v3.14.2/Include/opcode_ids.h) | ||
| #[derive(Clone, Copy, Debug)] | ||
| #[repr(u8)] | ||
| pub enum Instruction { | ||
| // ==================== No-argument instructions (opcode < 44) ==================== | ||
| Cache = 0, // Placeholder | ||
| BeforeAsyncWith = 1, | ||
| BeforeWith = 2, | ||
| // No-argument instructions (opcode < HAVE_ARGUMENT=44) | ||
| Cache = 0, // Placeholder | ||
| BinarySlice = 1, // Placeholder | ||
| BuildTemplate = 2, | ||
| BinaryOpInplaceAddUnicode = 3, // Placeholder | ||
| BinarySlice = 4, // Placeholder | ||
| BinarySubscr = 5, | ||
| CheckEgMatch = 6, | ||
| CheckExcMatch = 7, | ||
| CleanupThrow = 8, | ||
| DeleteSubscr = 9, | ||
| EndAsyncFor = 10, | ||
| EndFor = 11, // Placeholder | ||
| EndSend = 12, | ||
| ExitInitCheck = 13, // Placeholder | ||
| FormatSimple = 14, | ||
| FormatWithSpec = 15, | ||
| GetAIter = 16, | ||
| CallFunctionEx = 4, | ||
| CheckEgMatch = 5, | ||
| CheckExcMatch = 6, | ||
| CleanupThrow = 7, | ||
| DeleteSubscr = 8, | ||
| EndFor = 9, // Placeholder | ||
| EndSend = 10, | ||
| ExitInitCheck = 11, // Placeholder | ||
| FormatSimple = 12, | ||
| FormatWithSpec = 13, | ||
| GetAIter = 14, | ||
| GetANext = 15, | ||
| GetIter = 16, | ||
| Reserved = 17, | ||
| GetANext = 18, | ||
| GetIter = 19, | ||
| GetLen = 20, | ||
| GetYieldFromIter = 21, | ||
| InterpreterExit = 22, // Placeholder | ||
| LoadAssertionError = 23, // Placeholder | ||
| LoadBuildClass = 24, | ||
| LoadLocals = 25, // Placeholder | ||
| MakeFunction = 26, | ||
| MatchKeys = 27, | ||
| MatchMapping = 28, | ||
| MatchSequence = 29, | ||
| Nop = 30, | ||
| PopExcept = 31, | ||
| PopTop = 32, | ||
| PushExcInfo = 33, | ||
| PushNull = 34, | ||
| ReturnGenerator = 35, // Placeholder | ||
| ReturnValue = 36, | ||
| SetupAnnotations = 37, | ||
| StoreSlice = 38, // Placeholder | ||
| StoreSubscr = 39, | ||
| ToBool = 40, | ||
| UnaryInvert = 41, | ||
| UnaryNegative = 42, | ||
| UnaryNot = 43, | ||
| WithExceptStart = 44, | ||
| // ==================== With-argument instructions (opcode > 44) ==================== | ||
| GetLen = 18, | ||
| GetYieldFromIter = 19, | ||
| InterpreterExit = 20, // Placeholder | ||
| LoadBuildClass = 21, | ||
| LoadLocals = 22, // Placeholder | ||
| MakeFunction = 23, | ||
| MatchKeys = 24, | ||
| MatchMapping = 25, | ||
| MatchSequence = 26, | ||
| Nop = 27, | ||
| NotTaken = 28, // Placeholder | ||
| PopExcept = 29, | ||
| PopIter = 30, // Placeholder | ||
| PopTop = 31, | ||
| PushExcInfo = 32, | ||
| PushNull = 33, | ||
| ReturnGenerator = 34, // Placeholder | ||
| ReturnValue = 35, | ||
| SetupAnnotations = 36, | ||
| StoreSlice = 37, // Placeholder | ||
| StoreSubscr = 38, | ||
| ToBool = 39, | ||
| UnaryInvert = 40, | ||
| UnaryNegative = 41, | ||
| UnaryNot = 42, | ||
| WithExceptStart = 43, | ||
| // CPython 3.14 opcodes with arguments (44-120) | ||
| BinaryOp { | ||
| op: Arg<BinaryOperator>, | ||
| } = 44, | ||
| /// Build an Interpolation from value, expression string, and optional format_spec on stack. | ||
| /// | ||
| /// oparg encoding: (conversion << 2) | has_format_spec | ||
| /// - has_format_spec (bit 0): if 1, format_spec is on stack | ||
| /// - conversion (bits 2+): 0=None, 1=Str, 2=Repr, 3=Ascii | ||
| /// | ||
| /// Stack: [value, expression_str, format_spec?] -> [interpolation] | ||
| BuildInterpolation { | ||
| oparg: Arg<u32>, | ||
| } = 45, | ||
| BuildConstKeyMap { | ||
| size: Arg<u32>, | ||
| } = 46, // Placeholder | ||
| BuildList { | ||
| size: Arg<u32>, | ||
| } = 47, | ||
| } = 46, | ||
| BuildMap { | ||
| size: Arg<u32>, | ||
| } = 48, | ||
| } = 47, | ||
| BuildSet { | ||
| size: Arg<u32>, | ||
| } = 49, | ||
| } = 48, | ||
| BuildSlice { | ||
| argc: Arg<BuildSliceArgCount>, | ||
| } = 50, | ||
| } = 49, | ||
| BuildString { | ||
| size: Arg<u32>, | ||
| } = 51, | ||
| } = 50, | ||
| BuildTuple { | ||
| size: Arg<u32>, | ||
| } = 52, | ||
| } = 51, | ||
| Call { | ||
| nargs: Arg<u32>, | ||
| } = 53, | ||
| CallFunctionEx = 54, | ||
| } = 52, | ||
| CallIntrinsic1 { | ||
| func: Arg<IntrinsicFunction1>, | ||
| } = 55, | ||
| } = 53, | ||
| CallIntrinsic2 { | ||
| func: Arg<IntrinsicFunction2>, | ||
| } = 56, | ||
| } = 54, | ||
| CallKw { | ||
| nargs: Arg<u32>, | ||
| } = 57, | ||
| } = 55, | ||
| CompareOp { | ||
| op: Arg<ComparisonOperator>, | ||
| } = 58, | ||
| ContainsOp(Arg<Invert>) = 59, | ||
| } = 56, | ||
| ContainsOp(Arg<Invert>) = 57, | ||
| ConvertValue { | ||
| oparg: Arg<ConvertValueOparg>, | ||
| } = 60, | ||
| } = 58, | ||
| Copy { | ||
| index: Arg<u32>, | ||
| } = 61, | ||
| } = 59, | ||
| CopyFreeVars { | ||
| count: Arg<u32>, | ||
| } = 62, // Placeholder | ||
| } = 60, // Placeholder | ||
| DeleteAttr { | ||
| idx: Arg<NameIdx>, | ||
| } = 63, | ||
| DeleteDeref(Arg<NameIdx>) = 64, | ||
| DeleteFast(Arg<NameIdx>) = 65, | ||
| DeleteGlobal(Arg<NameIdx>) = 66, | ||
| DeleteName(Arg<NameIdx>) = 67, | ||
| } = 61, | ||
| DeleteDeref(Arg<NameIdx>) = 62, | ||
| DeleteFast(Arg<NameIdx>) = 63, | ||
| DeleteGlobal(Arg<NameIdx>) = 64, | ||
| DeleteName(Arg<NameIdx>) = 65, | ||
| DictMerge { | ||
| index: Arg<u32>, | ||
| } = 68, // Placeholder | ||
| } = 66, | ||
| DictUpdate { | ||
| index: Arg<u32>, | ||
| } = 69, | ||
| EnterExecutor = 70, // Placeholder | ||
| ExtendedArg = 71, | ||
| } = 67, | ||
| EndAsyncFor = 68, | ||
| ExtendedArg = 69, | ||
| ForIter { | ||
| target: Arg<Label>, | ||
| } = 72, | ||
| GetAwaitable = 73, // TODO: Make this instruction to hold an oparg | ||
| } = 70, | ||
| GetAwaitable = 71, // TODO: Make this instruction to hold an oparg | ||
| ImportFrom { | ||
| idx: Arg<NameIdx>, | ||
| } = 74, | ||
| } = 72, | ||
| ImportName { | ||
| idx: Arg<NameIdx>, | ||
| } = 75, | ||
| IsOp(Arg<Invert>) = 76, | ||
| } = 73, | ||
| IsOp(Arg<Invert>) = 74, | ||
| JumpBackward { | ||
| target: Arg<Label>, | ||
| } = 77, | ||
| } = 75, | ||
| JumpBackwardNoInterrupt { | ||
| target: Arg<Label>, | ||
| } = 78, // Placeholder | ||
| } = 76, // Placeholder | ||
| JumpForward { | ||
| target: Arg<Label>, | ||
| } = 79, | ||
| } = 77, | ||
| ListAppend { | ||
| i: Arg<u32>, | ||
| } = 80, | ||
| } = 78, | ||
| ListExtend { | ||
| i: Arg<u32>, | ||
| } = 81, // Placeholder | ||
| } = 79, | ||
| LoadAttr { | ||
| idx: Arg<NameIdx>, | ||
| } = 82, | ||
| } = 80, | ||
| LoadCommonConstant { | ||
| idx: Arg<u32>, | ||
| } = 81, // Placeholder | ||
| LoadConst { | ||
| idx: Arg<u32>, | ||
| } = 83, | ||
| LoadDeref(Arg<NameIdx>) = 84, | ||
| LoadFast(Arg<NameIdx>) = 85, | ||
| LoadFastAndClear(Arg<NameIdx>) = 86, | ||
| LoadFastCheck(Arg<NameIdx>) = 87, // Placeholder | ||
| } = 82, | ||
| LoadDeref(Arg<NameIdx>) = 83, | ||
| LoadFast(Arg<NameIdx>) = 84, | ||
| LoadFastAndClear(Arg<NameIdx>) = 85, | ||
| LoadFastBorrow(Arg<NameIdx>) = 86, // Placeholder | ||
| LoadFastBorrowLoadFastBorrow { | ||
| arg: Arg<u32>, | ||
| } = 87, // Placeholder | ||
| LoadFastCheck(Arg<NameIdx>) = 88, // Placeholder | ||
| LoadFastLoadFast { | ||
| arg: Arg<u32>, | ||
| } = 88, // Placeholder | ||
| LoadFromDictOrDeref(Arg<NameIdx>) = 89, | ||
| LoadFromDictOrGlobals(Arg<NameIdx>) = 90, // Placeholder | ||
| LoadGlobal(Arg<NameIdx>) = 91, | ||
| LoadName(Arg<NameIdx>) = 92, | ||
| } = 89, // Placeholder | ||
| LoadFromDictOrDeref(Arg<NameIdx>) = 90, | ||
| LoadFromDictOrGlobals(Arg<NameIdx>) = 91, // Placeholder | ||
| LoadGlobal(Arg<NameIdx>) = 92, | ||
| LoadName(Arg<NameIdx>) = 93, | ||
| LoadSmallInt { | ||
| idx: Arg<u32>, | ||
| } = 94, // Placeholder | ||
| LoadSpecial { | ||
| arg: Arg<u32>, | ||
| } = 95, // Placeholder | ||
| LoadSuperAttr { | ||
| arg: Arg<u32>, | ||
| } = 93, | ||
| MakeCell(Arg<NameIdx>) = 94, // Placeholder | ||
| } = 96, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
CPython 3.14 GET_AWAITABLE opcode oparg specification
💡 Result:
In CPython 3.14 the GET_AWAITABLE opcode takes an oparg "where" that indicates the context where the awaitable is produced:
- 0: general (normal await).
- 1: after a call to aenter (i.e. awaiting the result of aenter).
- 2: after a call to aexit (i.e. awaiting the result of aexit).
(Documentation shows the same "where" operand meaning as in earlier versions.) [1][2]
Sources: Python dis documentation (3.14, 3.11). [1] https://docs.python.org/3.14/library/dis.html [2] https://docs.python.org/3.11/library/dis.html
Fix GetAwaitable to include oparg field for CPython 3.14 compatibility.
CPython 3.14's GET_AWAITABLE opcode requires an oparg ("where") that indicates the context (0=general, 1=after aenter, 2=after aexit). The current variant lacks this field, which will cause incorrect decoding and incomplete execution semantics. The TODO comment acknowledges this gap—implement the fix to match CPython's specification.
🤖 Prompt for AI Agents
In `@crates/compiler-core/src/bytecode/instruction.rs` around lines 17 - 199, The
GetAwaitable opcode in the Instruction enum is missing the required oparg
("where") per CPython 3.14; update the Instruction::GetAwaitable variant to
include an oparg field (e.g. GetAwaitable { oparg: Arg<u32> } or a small enum
type Arg<GetAwaitableWhere>) so decoding/encoding and semantics can read 0/1/2
contexts correctly; search for Instruction::GetAwaitable and adjust any places
that construct, pattern-match, encode/decode, or format Instruction to handle
the new oparg field (and add a small GetAwaitableWhere type if you prefer typed
values instead of raw u32).
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
CPython 3.14 pyc magic number
💡 Result:
CPython 3.14 pyc magic number: 3627 (4-byte magic token = b'\x2b\x0e\x0d\x0a'). [1][2]
Sources:
- Include/internal/pycore_magic_number.h (PYC_MAGIC_NUMBER = 3627). [1]
- Explanation of pyc magic/token format (little-endian 2-byte number + CR LF). [2]
Change PYC_MAGIC_NUMBER to 3627 to match CPython 3.14.
Line 132 sets the value to 2996, but CPython 3.14's magic number is 3627 (defined in Include/internal/pycore_magic_number.h). Using the incorrect value will cause RustPython to misread .pyc files from CPython 3.14.
🤖 Prompt for AI Agents
In `@crates/vm/src/version.rs` around lines 131 - 132, The constant
PYC_MAGIC_NUMBER is set incorrectly to 2996; update the value of pub const
PYC_MAGIC_NUMBER in version.rs to 3627 so it matches CPython 3.14's magic number
(as defined in CPython's pycore_magic_number) to ensure .pyc files from CPython
3.14 are read correctly.

Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.