Skip to content

Commit 4051bec

Browse files
authored
Ensure BuildSlice oparg to be either 2 or 3 (RustPython#6313)
* Force `BuildSlice` oparg to be either 2 or 3 * `compile_slice` to return `BuildSliceArgCount`
1 parent 6782fa2 commit 4051bec

File tree

3 files changed

+80
-24
lines changed

3 files changed

+80
-24
lines changed

crates/codegen/src/compile.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ use ruff_text_size::{Ranged, TextRange};
3535
use rustpython_compiler_core::{
3636
Mode, OneIndexed, PositionEncoding, SourceFile, SourceLocation,
3737
bytecode::{
38-
self, Arg as OpArgMarker, BinaryOperator, CodeObject, ComparisonOperator, ConstantData,
39-
Instruction, Invert, OpArg, OpArgType, UnpackExArgs,
38+
self, Arg as OpArgMarker, BinaryOperator, BuildSliceArgCount, CodeObject,
39+
ComparisonOperator, ConstantData, Instruction, Invert, OpArg, OpArgType, UnpackExArgs,
4040
},
4141
};
4242
use rustpython_wtf8::Wtf8Buf;
@@ -401,7 +401,7 @@ impl Compiler {
401401

402402
/// Compile a slice expression
403403
// = compiler_slice
404-
fn compile_slice(&mut self, s: &ExprSlice) -> CompileResult<u32> {
404+
fn compile_slice(&mut self, s: &ExprSlice) -> CompileResult<BuildSliceArgCount> {
405405
// Compile lower
406406
if let Some(lower) = &s.lower {
407407
self.compile_expression(lower)?;
@@ -416,13 +416,14 @@ impl Compiler {
416416
self.emit_load_const(ConstantData::None);
417417
}
418418

419-
// Compile step if present
420-
if let Some(step) = &s.step {
421-
self.compile_expression(step)?;
422-
Ok(3) // Three values on stack
423-
} else {
424-
Ok(2) // Two values on stack
425-
}
419+
Ok(match &s.step {
420+
Some(step) => {
421+
// Compile step if present
422+
self.compile_expression(step)?;
423+
BuildSliceArgCount::Three
424+
}
425+
None => BuildSliceArgCount::Two,
426+
})
426427
}
427428

428429
/// Compile a subscript expression
@@ -449,19 +450,19 @@ impl Compiler {
449450

450451
// Handle two-element slice (for Load/Store, not Del)
451452
if Self::is_two_element_slice(slice) && !matches!(ctx, ExprContext::Del) {
452-
let n = match slice {
453+
let argc = match slice {
453454
Expr::Slice(s) => self.compile_slice(s)?,
454455
_ => unreachable!("is_two_element_slice should only return true for Expr::Slice"),
455456
};
456457
match ctx {
457458
ExprContext::Load => {
458459
// CPython uses BINARY_SLICE
459-
emit!(self, Instruction::BuildSlice { step: n == 3 });
460+
emit!(self, Instruction::BuildSlice { argc });
460461
emit!(self, Instruction::Subscript);
461462
}
462463
ExprContext::Store => {
463464
// CPython uses STORE_SLICE
464-
emit!(self, Instruction::BuildSlice { step: n == 3 });
465+
emit!(self, Instruction::BuildSlice { argc });
465466
emit!(self, Instruction::StoreSubscript);
466467
}
467468
_ => unreachable!(),
@@ -4587,8 +4588,11 @@ impl Compiler {
45874588
if let Some(step) = step {
45884589
self.compile_expression(step)?;
45894590
}
4590-
let step = step.is_some();
4591-
emit!(self, Instruction::BuildSlice { step });
4591+
let argc = match step {
4592+
Some(_) => BuildSliceArgCount::Three,
4593+
None => BuildSliceArgCount::Two,
4594+
};
4595+
emit!(self, Instruction::BuildSlice { argc });
45924596
}
45934597
Expr::Yield(ExprYield { value, .. }) => {
45944598
if !self.ctx.in_func() {

crates/compiler-core/src/bytecode.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use itertools::Itertools;
1010
use malachite_bigint::BigInt;
1111
use num_complex::Complex64;
1212
use rustpython_wtf8::{Wtf8, Wtf8Buf};
13-
use std::{collections::BTreeSet, fmt, hash, marker::PhantomData, mem, ops::Deref};
13+
use std::{collections::BTreeSet, fmt, hash, marker::PhantomData, mem, num::NonZeroU8, ops::Deref};
1414

1515
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
1616
#[repr(i8)]
@@ -760,8 +760,7 @@ pub enum Instruction {
760760
index: Arg<u32>,
761761
},
762762
BuildSlice {
763-
/// whether build a slice with a third step argument
764-
step: Arg<bool>,
763+
argc: Arg<BuildSliceArgCount>,
765764
},
766765
ListAppend {
767766
i: Arg<u32>,
@@ -1151,6 +1150,48 @@ op_arg_enum!(
11511150
}
11521151
);
11531152

1153+
/// Specifies if a slice is built with either 2 or 3 arguments.
1154+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1155+
pub enum BuildSliceArgCount {
1156+
/// ```py
1157+
/// x[5:10]
1158+
/// ```
1159+
Two,
1160+
/// ```py
1161+
/// x[5:10:2]
1162+
/// ```
1163+
Three,
1164+
}
1165+
1166+
impl OpArgType for BuildSliceArgCount {
1167+
#[inline(always)]
1168+
fn from_op_arg(x: u32) -> Option<Self> {
1169+
Some(match x {
1170+
2 => Self::Two,
1171+
3 => Self::Three,
1172+
_ => return None,
1173+
})
1174+
}
1175+
1176+
#[inline(always)]
1177+
fn to_op_arg(self) -> u32 {
1178+
u32::from(self.argc().get())
1179+
}
1180+
}
1181+
1182+
impl BuildSliceArgCount {
1183+
/// Get the numeric value of `Self`.
1184+
#[must_use]
1185+
pub const fn argc(self) -> NonZeroU8 {
1186+
let inner = match self {
1187+
Self::Two => 2,
1188+
Self::Three => 3,
1189+
};
1190+
// Safety: `inner` can be either 2 or 3.
1191+
unsafe { NonZeroU8::new_unchecked(inner) }
1192+
}
1193+
}
1194+
11541195
#[derive(Copy, Clone)]
11551196
pub struct UnpackExArgs {
11561197
pub before: u8,
@@ -1547,7 +1588,11 @@ impl Instruction {
15471588
-(nargs as i32) + 1
15481589
}
15491590
DictUpdate { .. } => -1,
1550-
BuildSlice { step } => -2 - (step.get(arg) as i32) + 1,
1591+
BuildSlice { argc } => {
1592+
// push 1
1593+
// pops either 2/3
1594+
1 - (argc.get(arg).argc().get() as i32)
1595+
}
15511596
ListAppend { .. } | SetAdd { .. } => -1,
15521597
MapAdd { .. } => -2,
15531598
PrintExpr => -1,
@@ -1734,7 +1779,7 @@ impl Instruction {
17341779
BuildMap { size } => w!(BuildMap, size),
17351780
BuildMapForCall { size } => w!(BuildMapForCall, size),
17361781
DictUpdate { index } => w!(DictUpdate, index),
1737-
BuildSlice { step } => w!(BuildSlice, step),
1782+
BuildSlice { argc } => w!(BuildSlice, ?argc),
17381783
ListAppend { i } => w!(ListAppend, i),
17391784
SetAdd { i } => w!(SetAdd, i),
17401785
MapAdd { i } => w!(MapAdd, i),

crates/vm/src/frame.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -834,8 +834,8 @@ impl ExecutingFrame<'_> {
834834
dict.merge_object(source, vm)?;
835835
Ok(None)
836836
}
837-
bytecode::Instruction::BuildSlice { step } => {
838-
self.execute_build_slice(vm, step.get(arg))
837+
bytecode::Instruction::BuildSlice { argc } => {
838+
self.execute_build_slice(vm, argc.get(arg))
839839
}
840840
bytecode::Instruction::ListAppend { i } => {
841841
let item = self.pop_value();
@@ -1777,8 +1777,15 @@ impl ExecutingFrame<'_> {
17771777
Ok(None)
17781778
}
17791779

1780-
fn execute_build_slice(&mut self, vm: &VirtualMachine, step: bool) -> FrameResult {
1781-
let step = if step { Some(self.pop_value()) } else { None };
1780+
fn execute_build_slice(
1781+
&mut self,
1782+
vm: &VirtualMachine,
1783+
argc: bytecode::BuildSliceArgCount,
1784+
) -> FrameResult {
1785+
let step = match argc {
1786+
bytecode::BuildSliceArgCount::Two => None,
1787+
bytecode::BuildSliceArgCount::Three => Some(self.pop_value()),
1788+
};
17821789
let stop = self.pop_value();
17831790
let start = self.pop_value();
17841791

0 commit comments

Comments
 (0)