From 9fbd359184ea0d7dc4906df03958877a6659e6e1 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sat, 18 Jan 2025 22:51:25 -0800 Subject: [PATCH 01/27] initial stub implementation of match statements --- compiler/codegen/src/compile.rs | 166 +++++++++++++++++++++++++++- compiler/codegen/src/symboltable.rs | 12 +- 2 files changed, 170 insertions(+), 8 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 6d3a1eb6d6..0b4f5e6b06 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -23,6 +23,8 @@ use rustpython_compiler_core::{ }; use rustpython_parser_core::source_code::{LineNumber, SourceLocation}; use std::borrow::Cow; +use rustpython_ast::{Pattern, PatternMatchSingleton, PatternMatchValue}; +use rustpython_compiler_core::bytecode::ComparisonOperator; type CompileResult = Result; @@ -211,6 +213,36 @@ macro_rules! emit { }; } +struct PatternContext { + // // A list of strings corresponding to name captures. It is used to track: + // // - Repeated name assignments in the same pattern. + // // - Different name assignments in alternatives. + // // - The order of name assignments in alternatives. + // PyObject *stores; + // // If 0, any name captures against our subject will raise. + // int allow_irrefutable; + // // An array of blocks to jump to on failure. Jumping to fail_pop[i] will pop + // // i items off of the stack. The end result looks like this (with each block + // // falling through to the next): + // // fail_pop[4]: POP_TOP + // // fail_pop[3]: POP_TOP + // // fail_pop[2]: POP_TOP + // // fail_pop[1]: POP_TOP + // // fail_pop[0]: NOP + // jump_target_label *fail_pop; + // // The current length of fail_pop. + // Py_ssize_t fail_pop_size; + // // The number of items on top of the stack that need to *stay* on top of the + // // stack. Variable captures go beneath these. All of them will be popped on + // // failure. + // Py_ssize_t on_top; + pub stores: Vec<()>, + pub allow_irrefutable: bool, + pub fail_pop: Vec, + pub fail_pop_size: usize, + pub on_top: usize, +} + impl Compiler { fn new(opts: CompileOpts, source_path: String, code_name: String) -> Self { let module_code = ir::CodeInfo { @@ -1754,15 +1786,143 @@ impl Compiler { Ok(()) } + // static int + // codegen_pattern_value(compiler *c, pattern_ty p, pattern_context *pc) + // { + // assert(p->kind == MatchValue_kind); + // expr_ty value = p->v.MatchValue.value; + // if (!MATCH_VALUE_EXPR(value)) { + // const char *e = "patterns may only match literals and attribute lookups"; + // return _PyCompile_Error(c, LOC(p), e); + // } + // VISIT(c, expr, value); + // ADDOP_COMPARE(c, LOC(p), Eq); + // ADDOP(c, LOC(p), TO_BOOL); + // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); + // return SUCCESS; + // } + fn codegen_pattern_value(&mut self, value: PatternMatchValue, pattern_context: &mut PatternContext) -> CompileResult<()> { + self.compile_expression(value.value.as_ref())?; + emit!(self, Instruction::CompareOperation { op: ComparisonOperator::Equal }); + emit!(self, Instruction::ToBool); + self.jump_to_fail_pop(pattern_context, bytecode::JumpIfFalse)?; + Ok(()) + } + + // static int + // codegen_pattern_singleton(compiler *c, pattern_ty p, pattern_context *pc) + // { + // assert(p->kind == MatchSingleton_kind); + // ADDOP_LOAD_CONST(c, LOC(p), p->v.MatchSingleton.value); + // ADDOP_COMPARE(c, LOC(p), Is); + // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); + // return SUCCESS; + // } + fn codegen_pattern_singleton(&mut self, singleton: PatternMatchSingleton, pattern_context: &mut PatternContext) -> CompileResult<()> { + self.emit_load_const(ConstantData::from(singleton.value)); + emit!(self, Instruction::CompareOperation { op: ComparisonOperator::Is }); + self.jump_to_fail_pop(pattern_context, bytecode::JumpIfFalse)?; + Ok(()) + } + + fn codegen_pattern(&mut self, pattern_type: Pattern, pattern_context: &mut PatternContext) -> CompileResult<()> { + match pattern_type { + Pattern::MatchValue(value) => self.codegen_pattern_value(value, pattern_context), + Pattern::MatchSingleton(singleton) => self.codegen_pattern_singleton(singleton, pattern_context), + Pattern::MatchSequence(sequence) => self.codegen_pattern_sequence(sequence, pattern_context), + Pattern::MatchMapping(mapping) => self.codegen_pattern_mapping(mapping, pattern_context), + Pattern::MatchClass(class) => self.codegen_pattern_class(class, pattern_context), + Pattern::MatchStar(star) => self.codegen_pattern_star(star, pattern_context), + Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(as_pattern, pattern_context), + Pattern::MatchOr(or_pattern) => self.codegen_pattern_or(or_pattern, pattern_context), + } + } + + fn compile_match_inner(&mut self, + subject: &located_ast::Expr, + cases: &[located_ast::MatchCase], + pattern_context: &mut PatternContext) -> CompileResult<()> { + self.compile_expression(subject)?; + todo!(); + // NEW_JUMP_TARGET_LABEL(c, end); + + let match_case_type = cases.last().expect("cases is not empty"); + let has_default = match_case_type.pattern.is_match_star() && 1 < cases; + // for (Py_ssize_t i = 0; i < cases - has_default; i++) { + for i in 0..cases.len() - (has_default as usize) { + // m = asdl_seq_GET(s->v.Match.cases, i); + let m = &cases[i]; + // Only copy the subject if we're *not* on the last case: + if i != cases - has_default as usize - 1 { + // ADDOP_I(c, LOC(m->pattern), COPY, 1); + emit!(cases, Instruction::Duplicate); + } + // TODO: redundant + pattern_context.stores = Vec::new(); + // Irrefutable cases must be either guarded, last, or both: + pattern_context.allow_irrefutable = m.guard.is_some() || i == cases - 1; + // pc->fail_pop = NULL; + pattern_context.fail_pop_size = 0; + pattern_context.on_top = 0; + self.codegen_pattern(m.pattern, pattern_context)?; + assert!(!pattern_context.on_top); + // It's a match! Store all of the captured names (they're on the stack). + let nstores = pattern_context.stores.len(); + for n in 0..nstores { + let name = &pattern_context.stores[n]; + // TODO: below + // if (codegen_nameop(c, LOC(m->pattern), name, Store) < 0) { + // Py_DECREF(pc->stores); + // return ERROR; + // } + } + // TODO: below + // Py_DECREF(pc->stores); + if i != cases - has_default - 1 { + // TODO: Below + // ADDOP(c, LOC(m->pattern), POP_TOP); + } + // VISIT_SEQ(c, stmt, m->body); + // ADDOP_JUMP(c, NO_LOCATION, JUMP, end); + // If the pattern fails to match, we want the line number of the + // cleanup to be associated with the failed pattern, not the last line + // of the body + // RETURN_IF_ERROR(emit_and_reset_fail_pop(c, LOC(m->pattern), pc)); + } + if has_default { + // A trailing "case _" is common, and lets us save a bit of redundant + // pushing and popping in the loop above: + let m = &cases.last().unwrap(); + if cases.len() == 1 { + // No matches. Done with the subject: + // TODO: Below + // ADDOP(c, LOC(m->pattern), POP_TOP); + } else { + // Show line coverage for default case (it doesn't create bytecode) + // TODO: Below + // ADDOP(c, LOC(m->pattern), NOP); + // emit!(cases, Instruction::Nop); + } + self.compile_statement(&m.body)?; + } + // USE_LABEL(c, end); + Ok(()) + } fn compile_match( &mut self, subject: &located_ast::Expr, cases: &[located_ast::MatchCase], ) -> CompileResult<()> { - eprintln!("match subject: {subject:?}"); - eprintln!("match cases: {cases:?}"); - Err(self.error(CodegenErrorType::NotImplementedYet)) + let mut pattern_context = PatternContext { + stores: Vec::new(), + allow_irrefutable: false, + fail_pop: Vec::new(), + fail_pop_size: 0, + on_top: 0, + }; + self.compile_match_inner(subject, cases, &mut pattern_context)?; + Ok(()) } fn compile_chained_comparison( diff --git a/compiler/codegen/src/symboltable.rs b/compiler/codegen/src/symboltable.rs index 8522c82037..bbb134facf 100644 --- a/compiler/codegen/src/symboltable.rs +++ b/compiler/codegen/src/symboltable.rs @@ -886,11 +886,13 @@ impl SymbolTableBuilder { self.scan_statements(orelse)?; self.scan_statements(finalbody)?; } - Stmt::Match(StmtMatch { subject, .. }) => { - return Err(SymbolTableError { - error: "match expression is not implemented yet".to_owned(), - location: Some(subject.location()), - }); + Stmt::Match(StmtMatch { subject, cases, .. }) => { + self.scan_expression(subject, ExpressionContext::Load)?; + for case in cases { + // TODO: below + // self.scan_pattern(&case.pattern, ExpressionContext::Load)?; + self.scan_statements(&case.body)?; + } } Stmt::Raise(StmtRaise { exc, cause, .. }) => { if let Some(expression) = exc { From bfb40911ebe85859207fc6b41fb17c55c86626b9 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 12:53:49 -0800 Subject: [PATCH 02/27] formatting --- compiler/codegen/src/compile.rs | 58 +++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 0b4f5e6b06..2958892682 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -17,14 +17,14 @@ use itertools::Itertools; use num_complex::Complex64; use num_traits::ToPrimitive; use rustpython_ast::located::{self as located_ast, Located}; +use rustpython_ast::{Pattern, PatternMatchSingleton, PatternMatchValue}; +use rustpython_compiler_core::bytecode::ComparisonOperator; use rustpython_compiler_core::{ bytecode::{self, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, OpArg, OpArgType}, Mode, }; use rustpython_parser_core::source_code::{LineNumber, SourceLocation}; use std::borrow::Cow; -use rustpython_ast::{Pattern, PatternMatchSingleton, PatternMatchValue}; -use rustpython_compiler_core::bytecode::ComparisonOperator; type CompileResult = Result; @@ -1801,9 +1801,18 @@ impl Compiler { // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); // return SUCCESS; // } - fn codegen_pattern_value(&mut self, value: PatternMatchValue, pattern_context: &mut PatternContext) -> CompileResult<()> { + fn codegen_pattern_value( + &mut self, + value: PatternMatchValue, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { self.compile_expression(value.value.as_ref())?; - emit!(self, Instruction::CompareOperation { op: ComparisonOperator::Equal }); + emit!( + self, + Instruction::CompareOperation { + op: ComparisonOperator::Equal + } + ); emit!(self, Instruction::ToBool); self.jump_to_fail_pop(pattern_context, bytecode::JumpIfFalse)?; Ok(()) @@ -1818,19 +1827,38 @@ impl Compiler { // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); // return SUCCESS; // } - fn codegen_pattern_singleton(&mut self, singleton: PatternMatchSingleton, pattern_context: &mut PatternContext) -> CompileResult<()> { + fn codegen_pattern_singleton( + &mut self, + singleton: PatternMatchSingleton, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { self.emit_load_const(ConstantData::from(singleton.value)); - emit!(self, Instruction::CompareOperation { op: ComparisonOperator::Is }); + emit!( + self, + Instruction::CompareOperation { + op: ComparisonOperator::Is + } + ); self.jump_to_fail_pop(pattern_context, bytecode::JumpIfFalse)?; Ok(()) } - fn codegen_pattern(&mut self, pattern_type: Pattern, pattern_context: &mut PatternContext) -> CompileResult<()> { + fn codegen_pattern( + &mut self, + pattern_type: Pattern, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { match pattern_type { Pattern::MatchValue(value) => self.codegen_pattern_value(value, pattern_context), - Pattern::MatchSingleton(singleton) => self.codegen_pattern_singleton(singleton, pattern_context), - Pattern::MatchSequence(sequence) => self.codegen_pattern_sequence(sequence, pattern_context), - Pattern::MatchMapping(mapping) => self.codegen_pattern_mapping(mapping, pattern_context), + Pattern::MatchSingleton(singleton) => { + self.codegen_pattern_singleton(singleton, pattern_context) + } + Pattern::MatchSequence(sequence) => { + self.codegen_pattern_sequence(sequence, pattern_context) + } + Pattern::MatchMapping(mapping) => { + self.codegen_pattern_mapping(mapping, pattern_context) + } Pattern::MatchClass(class) => self.codegen_pattern_class(class, pattern_context), Pattern::MatchStar(star) => self.codegen_pattern_star(star, pattern_context), Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(as_pattern, pattern_context), @@ -1838,10 +1866,12 @@ impl Compiler { } } - fn compile_match_inner(&mut self, - subject: &located_ast::Expr, - cases: &[located_ast::MatchCase], - pattern_context: &mut PatternContext) -> CompileResult<()> { + fn compile_match_inner( + &mut self, + subject: &located_ast::Expr, + cases: &[located_ast::MatchCase], + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { self.compile_expression(subject)?; todo!(); // NEW_JUMP_TARGET_LABEL(c, end); From c0213269a23cbd6ba8806bfc96ee9eb9d3dbd100 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 17:49:33 -0800 Subject: [PATCH 03/27] solved compilation errors --- compiler/codegen/src/compile.rs | 145 ++++++++++++++++++++++---------- 1 file changed, 99 insertions(+), 46 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 2958892682..80bb7e4854 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -18,13 +18,14 @@ use num_complex::Complex64; use num_traits::ToPrimitive; use rustpython_ast::located::{self as located_ast, Located}; use rustpython_ast::{Pattern, PatternMatchSingleton, PatternMatchValue}; -use rustpython_compiler_core::bytecode::ComparisonOperator; +use rustpython_compiler_core::bytecode::{Arg, ComparisonOperator, Label}; use rustpython_compiler_core::{ bytecode::{self, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, OpArg, OpArgType}, Mode, }; -use rustpython_parser_core::source_code::{LineNumber, SourceLocation}; +use rustpython_parser_core::source_code::{LineNumber, SourceLocation, SourceRange}; use std::borrow::Cow; +use crate::ir::BlockIdx; type CompileResult = Result; @@ -236,9 +237,9 @@ struct PatternContext { // // stack. Variable captures go beneath these. All of them will be popped on // // failure. // Py_ssize_t on_top; - pub stores: Vec<()>, + pub stores: Vec, pub allow_irrefutable: bool, - pub fail_pop: Vec, + pub fail_pop: Vec, pub fail_pop_size: usize, pub on_top: usize, } @@ -1786,6 +1787,59 @@ impl Compiler { Ok(()) } + + // static int + // ensure_fail_pop(compiler *c, pattern_context *pc, Py_ssize_t n) + // { + // Py_ssize_t size = n + 1; + // if (size <= pc->fail_pop_size) { + // return SUCCESS; + // } + // Py_ssize_t needed = sizeof(jump_target_label) * size; + // jump_target_label *resized = PyMem_Realloc(pc->fail_pop, needed); + // if (resized == NULL) { + // PyErr_NoMemory(); + // return ERROR; + // } + // pc->fail_pop = resized; + // while (pc->fail_pop_size < size) { + // NEW_JUMP_TARGET_LABEL(c, new_block); + // pc->fail_pop[pc->fail_pop_size++] = new_block; + // } + // return SUCCESS; + // } + + fn ensure_fail_pop(&mut self, pattern_context: &mut PatternContext, n: usize) -> CompileResult<()> { + let size = n + 1; + if size <= pattern_context.fail_pop_size { + return Ok(()) + } + let reserve = size.saturating_sub(pattern_context.fail_pop.len()); + pattern_context.fail_pop.reserve(reserve); + while pattern_context.fail_pop_size < size { + let label = self.new_block(); + pattern_context.fail_pop.push(label); + } + Ok(()) + } + + // static int + // jump_to_fail_pop(compiler *c, location loc, + // pattern_context *pc, int op) + // { + // // Pop any items on the top of the stack, plus any objects we were going to + // // capture on success: + // Py_ssize_t pops = pc->on_top + PyList_GET_SIZE(pc->stores); + // RETURN_IF_ERROR(ensure_fail_pop(c, pc, pops)); + // ADDOP_JUMP(c, loc, op, pc->fail_pop[pops]); + // return SUCCESS; + // } + fn jump_to_fail_pop(&mut self, pattern_context: &mut PatternContext) -> CompileResult<()> { + let pops = pattern_context.on_top + pattern_context.stores.len(); + self.ensure_fail_pop(pattern_context, pops)?; + self.switch_to_block(pattern_context.fail_pop[pops]); + Ok(()) + } // static int // codegen_pattern_value(compiler *c, pattern_ty p, pattern_context *pc) // { @@ -1803,18 +1857,18 @@ impl Compiler { // } fn codegen_pattern_value( &mut self, - value: PatternMatchValue, + value: &PatternMatchValue, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - self.compile_expression(value.value.as_ref())?; + self.compile_expression(&value.value)?; emit!( self, Instruction::CompareOperation { op: ComparisonOperator::Equal } ); - emit!(self, Instruction::ToBool); - self.jump_to_fail_pop(pattern_context, bytecode::JumpIfFalse)?; + // emit!(self, Instruction::ToBool); + self.jump_to_fail_pop(pattern_context)?; Ok(()) } @@ -1829,40 +1883,42 @@ impl Compiler { // } fn codegen_pattern_singleton( &mut self, - singleton: PatternMatchSingleton, + singleton: &PatternMatchSingleton, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - self.emit_load_const(ConstantData::from(singleton.value)); + todo!(); + // self.emit_load_const(ConstantData::from(singleton.value)); emit!( self, Instruction::CompareOperation { - op: ComparisonOperator::Is + // TODO: ComparisonOperator::Is doesn't exist, this will have to do for now + op: ComparisonOperator::Equal } ); - self.jump_to_fail_pop(pattern_context, bytecode::JumpIfFalse)?; + self.jump_to_fail_pop(pattern_context)?; Ok(()) } fn codegen_pattern( &mut self, - pattern_type: Pattern, + pattern_type: &Pattern, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - match pattern_type { - Pattern::MatchValue(value) => self.codegen_pattern_value(value, pattern_context), + match &pattern_type { + Pattern::MatchValue(value) => self.codegen_pattern_value(&value, pattern_context), Pattern::MatchSingleton(singleton) => { - self.codegen_pattern_singleton(singleton, pattern_context) + self.codegen_pattern_singleton(&singleton, pattern_context) } Pattern::MatchSequence(sequence) => { - self.codegen_pattern_sequence(sequence, pattern_context) + todo!() } Pattern::MatchMapping(mapping) => { - self.codegen_pattern_mapping(mapping, pattern_context) + todo!() } - Pattern::MatchClass(class) => self.codegen_pattern_class(class, pattern_context), - Pattern::MatchStar(star) => self.codegen_pattern_star(star, pattern_context), - Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(as_pattern, pattern_context), - Pattern::MatchOr(or_pattern) => self.codegen_pattern_or(or_pattern, pattern_context), + Pattern::MatchClass(class) => todo!(), + Pattern::MatchStar(star) => todo!(), + Pattern::MatchAs(as_pattern) => todo!(), + Pattern::MatchOr(or_pattern) => todo!(), } } @@ -1873,51 +1929,46 @@ impl Compiler { pattern_context: &mut PatternContext, ) -> CompileResult<()> { self.compile_expression(subject)?; - todo!(); - // NEW_JUMP_TARGET_LABEL(c, end); + // Block at the end of the switch statement that we jump to after finishing a branch + let end = self.new_block(); let match_case_type = cases.last().expect("cases is not empty"); - let has_default = match_case_type.pattern.is_match_star() && 1 < cases; + let has_default = match_case_type.pattern.is_match_star() && 1 < cases.len(); // for (Py_ssize_t i = 0; i < cases - has_default; i++) { for i in 0..cases.len() - (has_default as usize) { // m = asdl_seq_GET(s->v.Match.cases, i); let m = &cases[i]; // Only copy the subject if we're *not* on the last case: - if i != cases - has_default as usize - 1 { + if i != cases.len() - has_default as usize - 1 { // ADDOP_I(c, LOC(m->pattern), COPY, 1); - emit!(cases, Instruction::Duplicate); + emit!(self, Instruction::Duplicate); } // TODO: redundant pattern_context.stores = Vec::new(); // Irrefutable cases must be either guarded, last, or both: - pattern_context.allow_irrefutable = m.guard.is_some() || i == cases - 1; + pattern_context.allow_irrefutable = m.guard.is_some() || i == cases.len() - 1; // pc->fail_pop = NULL; pattern_context.fail_pop_size = 0; pattern_context.on_top = 0; - self.codegen_pattern(m.pattern, pattern_context)?; - assert!(!pattern_context.on_top); + self.codegen_pattern(&m.pattern, pattern_context)?; + assert_eq!(pattern_context.on_top, 0); // It's a match! Store all of the captured names (they're on the stack). let nstores = pattern_context.stores.len(); for n in 0..nstores { let name = &pattern_context.stores[n]; - // TODO: below - // if (codegen_nameop(c, LOC(m->pattern), name, Store) < 0) { - // Py_DECREF(pc->stores); - // return ERROR; - // } - } - // TODO: below - // Py_DECREF(pc->stores); - if i != cases - has_default - 1 { - // TODO: Below + // codegen_nameop(c, LOC(m->pattern), name, Store) < 0) + self.compile_name(name, NameUsage::Store)?; + } + if i != cases.len() - (has_default as usize) - 1 { // ADDOP(c, LOC(m->pattern), POP_TOP); + emit!(self, Instruction::Pop); } // VISIT_SEQ(c, stmt, m->body); + self.compile_statements(&m.body)?; // ADDOP_JUMP(c, NO_LOCATION, JUMP, end); - // If the pattern fails to match, we want the line number of the - // cleanup to be associated with the failed pattern, not the last line - // of the body - // RETURN_IF_ERROR(emit_and_reset_fail_pop(c, LOC(m->pattern), pc)); + emit!(self, Instruction::Jump { + target: end + }); } if has_default { // A trailing "case _" is common, and lets us save a bit of redundant @@ -1927,15 +1978,17 @@ impl Compiler { // No matches. Done with the subject: // TODO: Below // ADDOP(c, LOC(m->pattern), POP_TOP); + emit!(self, Instruction::Pop); } else { + todo!("Noop instruction"); // Show line coverage for default case (it doesn't create bytecode) // TODO: Below // ADDOP(c, LOC(m->pattern), NOP); // emit!(cases, Instruction::Nop); } - self.compile_statement(&m.body)?; + self.compile_statements(&m.body)?; } - // USE_LABEL(c, end); + self.switch_to_block(end); Ok(()) } From 6c7a543b03394c42ec9eac8e2b046cb3bfa33b06 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 20:00:44 -0800 Subject: [PATCH 04/27] almost working --- compiler/codegen/src/compile.rs | 36 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 80bb7e4854..566725ec5d 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -18,7 +18,7 @@ use num_complex::Complex64; use num_traits::ToPrimitive; use rustpython_ast::located::{self as located_ast, Located}; use rustpython_ast::{Pattern, PatternMatchSingleton, PatternMatchValue}; -use rustpython_compiler_core::bytecode::{Arg, ComparisonOperator, Label}; +use rustpython_compiler_core::bytecode::ComparisonOperator; use rustpython_compiler_core::{ bytecode::{self, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, OpArg, OpArgType}, Mode, @@ -240,7 +240,6 @@ struct PatternContext { pub stores: Vec, pub allow_irrefutable: bool, pub fail_pop: Vec, - pub fail_pop_size: usize, pub on_top: usize, } @@ -1811,12 +1810,12 @@ impl Compiler { fn ensure_fail_pop(&mut self, pattern_context: &mut PatternContext, n: usize) -> CompileResult<()> { let size = n + 1; - if size <= pattern_context.fail_pop_size { + if size <= pattern_context.fail_pop.len() { return Ok(()) } let reserve = size.saturating_sub(pattern_context.fail_pop.len()); pattern_context.fail_pop.reserve(reserve); - while pattern_context.fail_pop_size < size { + while pattern_context.fail_pop.len() < size { let label = self.new_block(); pattern_context.fail_pop.push(label); } @@ -1909,16 +1908,16 @@ impl Compiler { Pattern::MatchSingleton(singleton) => { self.codegen_pattern_singleton(&singleton, pattern_context) } - Pattern::MatchSequence(sequence) => { + Pattern::MatchSequence(_sequence) => { todo!() } - Pattern::MatchMapping(mapping) => { + Pattern::MatchMapping(_mapping) => { todo!() } - Pattern::MatchClass(class) => todo!(), - Pattern::MatchStar(star) => todo!(), - Pattern::MatchAs(as_pattern) => todo!(), - Pattern::MatchOr(or_pattern) => todo!(), + Pattern::MatchClass(_class) => todo!(), + Pattern::MatchStar(_star) => todo!(), + Pattern::MatchAs(_as_pattern) => todo!(), + Pattern::MatchOr(_or_pattern) => todo!(), } } @@ -1930,7 +1929,7 @@ impl Compiler { ) -> CompileResult<()> { self.compile_expression(subject)?; // Block at the end of the switch statement that we jump to after finishing a branch - let end = self.new_block(); + let end_block = self.new_block(); let match_case_type = cases.last().expect("cases is not empty"); let has_default = match_case_type.pattern.is_match_star() && 1 < cases.len(); @@ -1948,7 +1947,6 @@ impl Compiler { // Irrefutable cases must be either guarded, last, or both: pattern_context.allow_irrefutable = m.guard.is_some() || i == cases.len() - 1; // pc->fail_pop = NULL; - pattern_context.fail_pop_size = 0; pattern_context.on_top = 0; self.codegen_pattern(&m.pattern, pattern_context)?; assert_eq!(pattern_context.on_top, 0); @@ -1959,6 +1957,14 @@ impl Compiler { // codegen_nameop(c, LOC(m->pattern), name, Store) < 0) self.compile_name(name, NameUsage::Store)?; } + // if (m->guard) { + // RETURN_IF_ERROR(ensure_fail_pop(c, pc, 0)); + // RETURN_IF_ERROR(codegen_jump_if(c, LOC(m->pattern), m->guard, pc->fail_pop[0], 0)); + // } + if let Some(guard) = &m.guard { + self.ensure_fail_pop(pattern_context, 0)?; + self.compile_jump_if(guard, false, pattern_context.fail_pop[0])?; + } if i != cases.len() - (has_default as usize) - 1 { // ADDOP(c, LOC(m->pattern), POP_TOP); emit!(self, Instruction::Pop); @@ -1967,7 +1973,7 @@ impl Compiler { self.compile_statements(&m.body)?; // ADDOP_JUMP(c, NO_LOCATION, JUMP, end); emit!(self, Instruction::Jump { - target: end + target: end_block }); } if has_default { @@ -1976,7 +1982,6 @@ impl Compiler { let m = &cases.last().unwrap(); if cases.len() == 1 { // No matches. Done with the subject: - // TODO: Below // ADDOP(c, LOC(m->pattern), POP_TOP); emit!(self, Instruction::Pop); } else { @@ -1988,7 +1993,7 @@ impl Compiler { } self.compile_statements(&m.body)?; } - self.switch_to_block(end); + self.switch_to_block(end_block); Ok(()) } @@ -2001,7 +2006,6 @@ impl Compiler { stores: Vec::new(), allow_irrefutable: false, fail_pop: Vec::new(), - fail_pop_size: 0, on_top: 0, }; self.compile_match_inner(subject, cases, &mut pattern_context)?; From 8617786c8638e79ae5261d56d299e823fbfc21ca Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 20:07:09 -0800 Subject: [PATCH 05/27] formatting --- compiler/codegen/src/compile.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 566725ec5d..802f1d341c 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -7,6 +7,7 @@ #![deny(clippy::cast_possible_truncation)] +use crate::ir::BlockIdx; use crate::{ error::{CodegenError, CodegenErrorType}, ir, @@ -25,7 +26,6 @@ use rustpython_compiler_core::{ }; use rustpython_parser_core::source_code::{LineNumber, SourceLocation, SourceRange}; use std::borrow::Cow; -use crate::ir::BlockIdx; type CompileResult = Result; @@ -1808,10 +1808,14 @@ impl Compiler { // return SUCCESS; // } - fn ensure_fail_pop(&mut self, pattern_context: &mut PatternContext, n: usize) -> CompileResult<()> { + fn ensure_fail_pop( + &mut self, + pattern_context: &mut PatternContext, + n: usize, + ) -> CompileResult<()> { let size = n + 1; if size <= pattern_context.fail_pop.len() { - return Ok(()) + return Ok(()); } let reserve = size.saturating_sub(pattern_context.fail_pop.len()); pattern_context.fail_pop.reserve(reserve); @@ -1972,9 +1976,7 @@ impl Compiler { // VISIT_SEQ(c, stmt, m->body); self.compile_statements(&m.body)?; // ADDOP_JUMP(c, NO_LOCATION, JUMP, end); - emit!(self, Instruction::Jump { - target: end_block - }); + emit!(self, Instruction::Jump { target: end_block }); } if has_default { // A trailing "case _" is common, and lets us save a bit of redundant From a01240fe9125c0c7ee48c4f5c8b2ad9bcdac7c42 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 22:34:31 -0800 Subject: [PATCH 06/27] as and * support for switch case --- compiler/codegen/src/compile.rs | 120 +++++++++++++++++++++++++++++++- compiler/codegen/src/error.rs | 8 +++ 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 802f1d341c..f176b89e4c 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -18,7 +18,9 @@ use itertools::Itertools; use num_complex::Complex64; use num_traits::ToPrimitive; use rustpython_ast::located::{self as located_ast, Located}; -use rustpython_ast::{Pattern, PatternMatchSingleton, PatternMatchValue}; +use rustpython_ast::{ + Pattern, PatternMatchAs, PatternMatchSingleton, PatternMatchStar, PatternMatchValue, +}; use rustpython_compiler_core::bytecode::ComparisonOperator; use rustpython_compiler_core::{ bytecode::{self, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, OpArg, OpArgType}, @@ -1902,6 +1904,118 @@ impl Compiler { Ok(()) } + // static int + // codegen_pattern_helper_store_name(compiler *c, location loc, + // identifier n, pattern_context *pc) + // { + // if (n == NULL) { + // ADDOP(c, loc, POP_TOP); + // return SUCCESS; + // } + // // Can't assign to the same name twice: + // int duplicate = PySequence_Contains(pc->stores, n); + // RETURN_IF_ERROR(duplicate); + // if (duplicate) { + // return codegen_error_duplicate_store(c, loc, n); + // } + // // Rotate this object underneath any items we need to preserve: + // Py_ssize_t rotations = pc->on_top + PyList_GET_SIZE(pc->stores) + 1; + // RETURN_IF_ERROR(codegen_pattern_helper_rotate(c, loc, rotations)); + // RETURN_IF_ERROR(PyList_Append(pc->stores, n)); + // return SUCCESS; + // } + fn codegen_pattern_helper_store_name( + &mut self, + loc: SourceLocation, + n: Option<&str>, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { + if n.is_none() { + emit!(self, Instruction::Pop); + return Ok(()); + } + let n = n.unwrap(); + let duplicate = pattern_context.stores.contains(&n.to_string()); + if duplicate { + return Err(self.error(CodegenErrorType::DuplicateStore(n.to_string()))); + } + let rotations = pattern_context.on_top + pattern_context.stores.len() + 1; + self.codegen_pattern_helper_rotate(loc, rotations)?; + pattern_context.stores.push(n.to_string()); + Ok(()) + } + + fn codegen_pattern_star( + &mut self, + star: &PatternMatchStar, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { + // codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchStar.name, pc)); + self.codegen_pattern_helper_store_name( + star.location(), + star.name.as_deref(), + pattern_context, + )?; + Ok(()) + } + + // static int + // codegen_pattern_as(compiler *c, pattern_ty p, pattern_context *pc) + // { + // assert(p->kind == MatchAs_kind); + // if (p->v.MatchAs.pattern == NULL) { + // // An irrefutable match: + // if (!pc->allow_irrefutable) { + // if (p->v.MatchAs.name) { + // const char *e = "name capture %R makes remaining patterns unreachable"; + // return _PyCompile_Error(c, LOC(p), e, p->v.MatchAs.name); + // } + // const char *e = "wildcard makes remaining patterns unreachable"; + // return _PyCompile_Error(c, LOC(p), e); + // } + // return codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchAs.name, pc); + // } + // // Need to make a copy for (possibly) storing later: + // pc->on_top++; + // ADDOP_I(c, LOC(p), COPY, 1); + // RETURN_IF_ERROR(codegen_pattern(c, p->v.MatchAs.pattern, pc)); + // // Success! Store it: + // pc->on_top--; + // RETURN_IF_ERROR(codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchAs.name, pc)); + // return SUCCESS; + // } + fn codegen_pattern_as( + &mut self, + as_pattern: &PatternMatchAs, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { + if as_pattern.pattern.is_none() { + // An irrefutable match: + if !pattern_context.allow_irrefutable { + if let Some(name) = &as_pattern.name { + return Err(self.error(CodegenErrorType::InvalidMatchCase)); + } else { + return Err(self.error(CodegenErrorType::InvalidMatchCase)); + } + } + return self.codegen_pattern_helper_store_name( + as_pattern.location(), + as_pattern.name.as_deref(), + pattern_context, + ); + } + pattern_context.on_top += 1; + emit!(self, Instruction::Duplicate); + self.codegen_pattern(as_pattern.pattern.as_ref().unwrap(), pattern_context)?; + pattern_context.on_top -= 1; + self.codegen_pattern_helper_store_name( + as_pattern.location(), + as_pattern.name.as_deref(), + pattern_context, + )?; + Ok(()) + } + fn codegen_pattern( &mut self, pattern_type: &Pattern, @@ -1919,8 +2033,8 @@ impl Compiler { todo!() } Pattern::MatchClass(_class) => todo!(), - Pattern::MatchStar(_star) => todo!(), - Pattern::MatchAs(_as_pattern) => todo!(), + Pattern::MatchStar(star) => self.codegen_pattern_star(&star, pattern_context), + Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(&as_pattern, pattern_context), Pattern::MatchOr(_or_pattern) => todo!(), } } diff --git a/compiler/codegen/src/error.rs b/compiler/codegen/src/error.rs index 017f735105..5a8f8df0f1 100644 --- a/compiler/codegen/src/error.rs +++ b/compiler/codegen/src/error.rs @@ -31,6 +31,8 @@ pub enum CodegenErrorType { EmptyWithItems, EmptyWithBody, NotImplementedYet, // RustPython marker for unimplemented features + DuplicateStore(String), + InvalidMatchCase, } impl std::error::Error for CodegenErrorType {} @@ -78,6 +80,12 @@ impl fmt::Display for CodegenErrorType { NotImplementedYet => { write!(f, "RustPython does not implement this feature yet") } + DuplicateStore(s) => { + write!(f, "duplicate store {s}") + } + InvalidMatchCase => { + write!(f, "invalid match case") + } } } } From d8c4cff0e21299702fa82775f9b978be6d5d35dd Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 22:36:12 -0800 Subject: [PATCH 07/27] replace todos with errors --- compiler/codegen/src/compile.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index f176b89e4c..d9c2e45ead 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -2027,15 +2027,15 @@ impl Compiler { self.codegen_pattern_singleton(&singleton, pattern_context) } Pattern::MatchSequence(_sequence) => { - todo!() + Err(self.error(CodegenErrorType::NotImplementedYet)) } Pattern::MatchMapping(_mapping) => { - todo!() + Err(self.error(CodegenErrorType::NotImplementedYet)) } - Pattern::MatchClass(_class) => todo!(), + Pattern::MatchClass(_class) => Err(self.error(CodegenErrorType::NotImplementedYet)), Pattern::MatchStar(star) => self.codegen_pattern_star(&star, pattern_context), Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(&as_pattern, pattern_context), - Pattern::MatchOr(_or_pattern) => todo!(), + Pattern::MatchOr(_or_pattern) => Err(self.error(CodegenErrorType::NotImplementedYet)), } } From aaccadbc4b6971dec6d21bd1ebbb2388eb60cd68 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 22:37:46 -0800 Subject: [PATCH 08/27] fix compile --- compiler/codegen/src/compile.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index d9c2e45ead..4fea625e20 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1940,7 +1940,8 @@ impl Compiler { return Err(self.error(CodegenErrorType::DuplicateStore(n.to_string()))); } let rotations = pattern_context.on_top + pattern_context.stores.len() + 1; - self.codegen_pattern_helper_rotate(loc, rotations)?; + todo!("below"); + // self.codegen_pattern_helper_rotate(loc, rotations)?; pattern_context.stores.push(n.to_string()); Ok(()) } From f9d2418c63575c6fad59bb293e8794a68c5ece45 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 22:42:43 -0800 Subject: [PATCH 09/27] added Noop instruction --- compiler/core/src/bytecode.rs | 6 ++++++ vm/src/frame.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/compiler/core/src/bytecode.rs b/compiler/core/src/bytecode.rs index c8dbc63744..befee9d1b4 100644 --- a/compiler/core/src/bytecode.rs +++ b/compiler/core/src/bytecode.rs @@ -370,6 +370,10 @@ pub type NameIdx = u32; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum Instruction { + /// No-op, don't do anything at all + /// + /// Equivalent to `NOP` in cpython bytecode + Noop, /// Importing by name ImportName { idx: Arg, @@ -1177,6 +1181,7 @@ impl Instruction { /// pub fn stack_effect(&self, arg: OpArg, jump: bool) -> i32 { match self { + Noop => 0, ImportName { .. } | ImportNameless => -1, ImportStar => -1, ImportFrom { .. } => 1, @@ -1361,6 +1366,7 @@ impl Instruction { }; match self { + Noop => w!(Noop), ImportName { idx } => w!(ImportName, name = idx), ImportNameless => w!(ImportNameless), ImportStar => w!(ImportStar), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 8fc7e171b3..e5fcba0006 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -517,6 +517,7 @@ impl ExecutingFrame<'_> { } match instruction { + bytecode::Instruction::Noop => Ok(None), bytecode::Instruction::LoadConst { idx } => { self.push_value(self.code.constants[idx.get(arg) as usize].clone().into()); Ok(None) From 5839dfdffbb1a37e965b47af4e467011c5c545b9 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 22:43:22 -0800 Subject: [PATCH 10/27] finished default for switch case --- compiler/codegen/src/compile.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 4fea625e20..becf265272 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -2102,11 +2102,9 @@ impl Compiler { // ADDOP(c, LOC(m->pattern), POP_TOP); emit!(self, Instruction::Pop); } else { - todo!("Noop instruction"); // Show line coverage for default case (it doesn't create bytecode) - // TODO: Below // ADDOP(c, LOC(m->pattern), NOP); - // emit!(cases, Instruction::Nop); + emit!(cases, Instruction::Noop); } self.compile_statements(&m.body)?; } From 40e380ee98666f7888e43367ba3ed45ee8bcf6fc Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 22:50:23 -0800 Subject: [PATCH 11/27] fix compile --- compiler/codegen/src/compile.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index becf265272..aa454de5a1 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -2103,8 +2103,7 @@ impl Compiler { emit!(self, Instruction::Pop); } else { // Show line coverage for default case (it doesn't create bytecode) - // ADDOP(c, LOC(m->pattern), NOP); - emit!(cases, Instruction::Noop); + emit!(self, Instruction::Noop); } self.compile_statements(&m.body)?; } From 8af2c42447961082272e5327e4e39724dc8007ed Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 19 Jan 2025 22:50:54 -0800 Subject: [PATCH 12/27] formatting --- compiler/codegen/src/compile.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index aa454de5a1..579d74796b 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -2030,9 +2030,7 @@ impl Compiler { Pattern::MatchSequence(_sequence) => { Err(self.error(CodegenErrorType::NotImplementedYet)) } - Pattern::MatchMapping(_mapping) => { - Err(self.error(CodegenErrorType::NotImplementedYet)) - } + Pattern::MatchMapping(_mapping) => Err(self.error(CodegenErrorType::NotImplementedYet)), Pattern::MatchClass(_class) => Err(self.error(CodegenErrorType::NotImplementedYet)), Pattern::MatchStar(star) => self.codegen_pattern_star(&star, pattern_context), Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(&as_pattern, pattern_context), From 0774cbdd536b68e9b96c3cbac4c75bbbf6180d89 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 20 Jan 2025 10:41:56 -0800 Subject: [PATCH 13/27] more implementation --- compiler/codegen/src/compile.rs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 579d74796b..59175a8218 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1904,6 +1904,20 @@ impl Compiler { Ok(()) } + fn codegen_pattern_helper_rotate( + &mut self, + count: usize, + ) -> CompileResult<()> { + let mut count = count; + while 1 < count { + todo!("below"); + // ADDOP_I(c, loc, SWAP, count--); + // emit!(self, Instruction::Swap { count: count as u32 }); + count -= 1; + } + Ok(()) + } + // static int // codegen_pattern_helper_store_name(compiler *c, location loc, // identifier n, pattern_context *pc) @@ -1926,7 +1940,6 @@ impl Compiler { // } fn codegen_pattern_helper_store_name( &mut self, - loc: SourceLocation, n: Option<&str>, pattern_context: &mut PatternContext, ) -> CompileResult<()> { @@ -1940,8 +1953,7 @@ impl Compiler { return Err(self.error(CodegenErrorType::DuplicateStore(n.to_string()))); } let rotations = pattern_context.on_top + pattern_context.stores.len() + 1; - todo!("below"); - // self.codegen_pattern_helper_rotate(loc, rotations)?; + self.codegen_pattern_helper_rotate(rotations)?; pattern_context.stores.push(n.to_string()); Ok(()) } @@ -1953,7 +1965,6 @@ impl Compiler { ) -> CompileResult<()> { // codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchStar.name, pc)); self.codegen_pattern_helper_store_name( - star.location(), star.name.as_deref(), pattern_context, )?; @@ -1993,6 +2004,7 @@ impl Compiler { if as_pattern.pattern.is_none() { // An irrefutable match: if !pattern_context.allow_irrefutable { + // TODO: better error message if let Some(name) = &as_pattern.name { return Err(self.error(CodegenErrorType::InvalidMatchCase)); } else { @@ -2000,7 +2012,6 @@ impl Compiler { } } return self.codegen_pattern_helper_store_name( - as_pattern.location(), as_pattern.name.as_deref(), pattern_context, ); @@ -2010,7 +2021,6 @@ impl Compiler { self.codegen_pattern(as_pattern.pattern.as_ref().unwrap(), pattern_context)?; pattern_context.on_top -= 1; self.codegen_pattern_helper_store_name( - as_pattern.location(), as_pattern.name.as_deref(), pattern_context, )?; @@ -2045,8 +2055,9 @@ impl Compiler { pattern_context: &mut PatternContext, ) -> CompileResult<()> { self.compile_expression(subject)?; - // Block at the end of the switch statement that we jump to after finishing a branch - let end_block = self.new_block(); + // Blocks at the end of the switch statement that we jump to after finishing a branch + // TODO: optimize, we can reuse the same block for all cases + let mut end_block = self.new_block(); let match_case_type = cases.last().expect("cases is not empty"); let has_default = match_case_type.pattern.is_match_star() && 1 < cases.len(); @@ -2105,6 +2116,7 @@ impl Compiler { } self.compile_statements(&m.body)?; } + self.switch_to_block(end_block); Ok(()) } @@ -3401,6 +3413,10 @@ impl Compiler { ir::BlockIdx::NULL, "switching {prev:?} -> {block:?} to completed block" ); + println!("{}", prev.0); + for (count, b) in code.blocks.iter().enumerate() { + println!("{count}: {} {}", b.next.0, b.instructions.len()); + } let prev_block = &mut code.blocks[prev.0 as usize]; assert_eq!( prev_block.next.0, From cd0f1cf63b99b54fa522c8c449e42ea445c1a09a Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 20 Jan 2025 19:31:56 -0800 Subject: [PATCH 14/27] rename codegen_* to compile_* Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 48 ++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 59175a8218..6117898609 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1860,7 +1860,7 @@ impl Compiler { // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); // return SUCCESS; // } - fn codegen_pattern_value( + fn compile_pattern_value( &mut self, value: &PatternMatchValue, pattern_context: &mut PatternContext, @@ -1886,7 +1886,7 @@ impl Compiler { // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); // return SUCCESS; // } - fn codegen_pattern_singleton( + fn compile_pattern_singleton( &mut self, singleton: &PatternMatchSingleton, pattern_context: &mut PatternContext, @@ -1904,7 +1904,7 @@ impl Compiler { Ok(()) } - fn codegen_pattern_helper_rotate( + fn compile_pattern_helper_rotate( &mut self, count: usize, ) -> CompileResult<()> { @@ -1938,7 +1938,7 @@ impl Compiler { // RETURN_IF_ERROR(PyList_Append(pc->stores, n)); // return SUCCESS; // } - fn codegen_pattern_helper_store_name( + fn compile_pattern_helper_store_name( &mut self, n: Option<&str>, pattern_context: &mut PatternContext, @@ -1953,18 +1953,18 @@ impl Compiler { return Err(self.error(CodegenErrorType::DuplicateStore(n.to_string()))); } let rotations = pattern_context.on_top + pattern_context.stores.len() + 1; - self.codegen_pattern_helper_rotate(rotations)?; + self.compile_pattern_helper_rotate(rotations)?; pattern_context.stores.push(n.to_string()); Ok(()) } - fn codegen_pattern_star( + fn compile_pattern_star( &mut self, star: &PatternMatchStar, pattern_context: &mut PatternContext, ) -> CompileResult<()> { // codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchStar.name, pc)); - self.codegen_pattern_helper_store_name( + self.compile_pattern_helper_store_name( star.name.as_deref(), pattern_context, )?; @@ -2011,38 +2011,38 @@ impl Compiler { return Err(self.error(CodegenErrorType::InvalidMatchCase)); } } - return self.codegen_pattern_helper_store_name( + return self.compile_pattern_helper_store_name( as_pattern.name.as_deref(), pattern_context, ); } pattern_context.on_top += 1; emit!(self, Instruction::Duplicate); - self.codegen_pattern(as_pattern.pattern.as_ref().unwrap(), pattern_context)?; + self.compile_pattern(as_pattern.pattern.as_ref().unwrap(), pattern_context)?; pattern_context.on_top -= 1; - self.codegen_pattern_helper_store_name( + self.compile_pattern_helper_store_name( as_pattern.name.as_deref(), pattern_context, )?; Ok(()) } - fn codegen_pattern( + fn compile_pattern( &mut self, pattern_type: &Pattern, pattern_context: &mut PatternContext, ) -> CompileResult<()> { match &pattern_type { - Pattern::MatchValue(value) => self.codegen_pattern_value(&value, pattern_context), + Pattern::MatchValue(value) => self.compile_pattern_value(&value, pattern_context), Pattern::MatchSingleton(singleton) => { - self.codegen_pattern_singleton(&singleton, pattern_context) + self.compile_pattern_singleton(&singleton, pattern_context) } Pattern::MatchSequence(_sequence) => { Err(self.error(CodegenErrorType::NotImplementedYet)) } Pattern::MatchMapping(_mapping) => Err(self.error(CodegenErrorType::NotImplementedYet)), Pattern::MatchClass(_class) => Err(self.error(CodegenErrorType::NotImplementedYet)), - Pattern::MatchStar(star) => self.codegen_pattern_star(&star, pattern_context), + Pattern::MatchStar(star) => self.compile_pattern_star(&star, pattern_context), Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(&as_pattern, pattern_context), Pattern::MatchOr(_or_pattern) => Err(self.error(CodegenErrorType::NotImplementedYet)), } @@ -2076,7 +2076,7 @@ impl Compiler { pattern_context.allow_irrefutable = m.guard.is_some() || i == cases.len() - 1; // pc->fail_pop = NULL; pattern_context.on_top = 0; - self.codegen_pattern(&m.pattern, pattern_context)?; + self.compile_pattern(&m.pattern, pattern_context)?; assert_eq!(pattern_context.on_top, 0); // It's a match! Store all of the captured names (they're on the stack). let nstores = pattern_context.stores.len(); @@ -3727,4 +3727,22 @@ for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): " )); } + + #[test] + fn test_match() { + assert_dis_snapshot!(compile_exec( + r#"\ +v = "one" +match v: + case "one": + v = "two" + case "two": + v = "three" + case "three": + v = "one" + case _: + v = "one" +"# + )); + } } From 223fbe4ae7eb1c7ffb20488fec9bbf339fd16ca4 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 20 Jan 2025 22:04:43 -0800 Subject: [PATCH 15/27] implement SWAP instruction Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 44 +++++++++++++++------------------ compiler/core/src/bytecode.rs | 3 +++ vm/src/frame.rs | 5 ++++ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 6117898609..57e7ba5aae 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -217,31 +217,29 @@ macro_rules! emit { } struct PatternContext { - // // A list of strings corresponding to name captures. It is used to track: - // // - Repeated name assignments in the same pattern. - // // - Different name assignments in alternatives. - // // - The order of name assignments in alternatives. - // PyObject *stores; - // // If 0, any name captures against our subject will raise. - // int allow_irrefutable; - // // An array of blocks to jump to on failure. Jumping to fail_pop[i] will pop - // // i items off of the stack. The end result looks like this (with each block - // // falling through to the next): - // // fail_pop[4]: POP_TOP - // // fail_pop[3]: POP_TOP - // // fail_pop[2]: POP_TOP - // // fail_pop[1]: POP_TOP - // // fail_pop[0]: NOP - // jump_target_label *fail_pop; // // The current length of fail_pop. // Py_ssize_t fail_pop_size; - // // The number of items on top of the stack that need to *stay* on top of the - // // stack. Variable captures go beneath these. All of them will be popped on - // // failure. - // Py_ssize_t on_top; + + /// Py_ssize_t on_top; + /// A list of strings corresponding to name captures. It is used to track: + /// - Repeated name assignments in the same pattern. + /// - Different name assignments in alternatives. + /// - The order of name assignments in alternatives. pub stores: Vec, + /// If 0, any name captures against our subject will raise. pub allow_irrefutable: bool, + /// An array of blocks to jump to on failure. Jumping to fail_pop[i] will pop + /// i items off of the stack. The end result looks like this (with each block + /// falling through to the next): + /// fail_pop[4]: POP_TOP + /// fail_pop[3]: POP_TOP + /// fail_pop[2]: POP_TOP + /// fail_pop[1]: POP_TOP + /// fail_pop[0]: NOP pub fail_pop: Vec, + /// The number of items on top of the stack that need to *stay* on top of the + /// stack. Variable captures go beneath these. All of them will be popped on + /// failure. pub on_top: usize, } @@ -2055,9 +2053,8 @@ impl Compiler { pattern_context: &mut PatternContext, ) -> CompileResult<()> { self.compile_expression(subject)?; - // Blocks at the end of the switch statement that we jump to after finishing a branch - // TODO: optimize, we can reuse the same block for all cases - let mut end_block = self.new_block(); + // Block at the end of the switch statement that we jump to after finishing a branch + let end_block = self.new_block(); let match_case_type = cases.last().expect("cases is not empty"); let has_default = match_case_type.pattern.is_match_star() && 1 < cases.len(); @@ -2117,7 +2114,6 @@ impl Compiler { self.compile_statements(&m.body)?; } - self.switch_to_block(end_block); Ok(()) } diff --git a/compiler/core/src/bytecode.rs b/compiler/core/src/bytecode.rs index befee9d1b4..1403cd93c9 100644 --- a/compiler/core/src/bytecode.rs +++ b/compiler/core/src/bytecode.rs @@ -432,6 +432,7 @@ pub enum Instruction { op: Arg, }, Pop, + Swap(Arg), Rotate2, Rotate3, Duplicate, @@ -1202,6 +1203,7 @@ impl Instruction { | TestOperation { .. } | CompareOperation { .. } => -1, Pop => -1, + Swap(_) => 0, Rotate2 | Rotate3 => 0, Duplicate => 1, Duplicate2 => 2, @@ -1398,6 +1400,7 @@ impl Instruction { TestOperation { op } => w!(TestOperation, ?op), CompareOperation { op } => w!(CompareOperation, ?op), Pop => w!(Pop), + Swap(a) => w!(Swap, a), Rotate2 => w!(Rotate2), Rotate3 => w!(Rotate3), Duplicate => w!(Duplicate), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index e5fcba0006..55012b9dba 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -673,6 +673,11 @@ impl ExecutingFrame<'_> { self.pop_value(); Ok(None) } + bytecode::Instruction::Swap(i) => { + let len = self.state.stack.len(); + self.state.stack.swap(len - 1, len - 1 - i.get(arg) as usize); + Ok(None) + } bytecode::Instruction::Duplicate => { // Duplicate top of stack let value = self.top_value(); From 4ef2a506f981e0b4c5f950051dbae47a7a9e1980 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 21 Jan 2025 15:35:35 -0800 Subject: [PATCH 16/27] applied fix Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 57e7ba5aae..2744066759 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1840,7 +1840,7 @@ impl Compiler { fn jump_to_fail_pop(&mut self, pattern_context: &mut PatternContext) -> CompileResult<()> { let pops = pattern_context.on_top + pattern_context.stores.len(); self.ensure_fail_pop(pattern_context, pops)?; - self.switch_to_block(pattern_context.fail_pop[pops]); + // self.switch_to_block(pattern_context.fail_pop[pops]); Ok(()) } // static int @@ -2054,7 +2054,11 @@ impl Compiler { ) -> CompileResult<()> { self.compile_expression(subject)?; // Block at the end of the switch statement that we jump to after finishing a branch - let end_block = self.new_block(); + let pattern_blocks = std::iter::repeat_with(|| self.new_block()) + .take(cases.len() + 1) + .collect::>(); + eprintln!("created pattern_blocks: {:?} - {:?}(end block)", pattern_blocks.first().unwrap(), pattern_blocks.last().unwrap()); + let end_block = *pattern_blocks.last().unwrap(); let match_case_type = cases.last().expect("cases is not empty"); let has_default = match_case_type.pattern.is_match_star() && 1 < cases.len(); @@ -2088,7 +2092,7 @@ impl Compiler { // } if let Some(guard) = &m.guard { self.ensure_fail_pop(pattern_context, 0)?; - self.compile_jump_if(guard, false, pattern_context.fail_pop[0])?; + self.compile_jump_if(guard, false, pattern_blocks[i + 1])?; } if i != cases.len() - (has_default as usize) - 1 { // ADDOP(c, LOC(m->pattern), POP_TOP); @@ -2103,6 +2107,7 @@ impl Compiler { // A trailing "case _" is common, and lets us save a bit of redundant // pushing and popping in the loop above: let m = &cases.last().unwrap(); + self.switch_to_block(*pattern_blocks.last().unwrap()); if cases.len() == 1 { // No matches. Done with the subject: // ADDOP(c, LOC(m->pattern), POP_TOP); @@ -3419,6 +3424,7 @@ impl Compiler { u32::MAX, "switching {prev:?} -> {block:?} from block that's already got a next" ); + eprintln!("switch_to_block {prev:?} -> {block:?}"); prev_block.next = block; code.current_block = block; } From aa6a0409666b427a1364e8d61b78cd3527562e55 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Tue, 21 Jan 2025 16:11:14 -0800 Subject: [PATCH 17/27] enabled IR debug and attempted to fix codegen Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 18 ++++++++++++++++-- compiler/codegen/src/ir.rs | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 2744066759..8af6b47278 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -2054,7 +2054,7 @@ impl Compiler { ) -> CompileResult<()> { self.compile_expression(subject)?; // Block at the end of the switch statement that we jump to after finishing a branch - let pattern_blocks = std::iter::repeat_with(|| self.new_block()) + let mut pattern_blocks = std::iter::repeat_with(|| self.new_block()) .take(cases.len() + 1) .collect::>(); eprintln!("created pattern_blocks: {:?} - {:?}(end block)", pattern_blocks.first().unwrap(), pattern_blocks.last().unwrap()); @@ -2118,7 +2118,21 @@ impl Compiler { } self.compile_statements(&m.body)?; } - + let block = self.new_block(); + pattern_blocks.push(block); + let code = self.current_code_info(); + let _ = pattern_blocks.iter() + .zip(pattern_blocks.iter().skip(1)) + .for_each(|(a, b)| { + eprintln!("linking: {} -> {}", a.0, b.0); + code.blocks[a.0 as usize].next = *b; + }); + self.switch_to_block(*pattern_blocks.last().unwrap()); + let code = self.current_code_info(); + for block in pattern_blocks { + let b = &code.blocks[block.0 as usize]; + eprintln!("block: {} -> {}", block.0, b.next.0); + } Ok(()) } diff --git a/compiler/codegen/src/ir.rs b/compiler/codegen/src/ir.rs index 9f1a86e51d..95cb2780b0 100644 --- a/compiler/codegen/src/ir.rs +++ b/compiler/codegen/src/ir.rs @@ -227,7 +227,7 @@ impl CodeInfo { let mut start_depths = vec![u32::MAX; self.blocks.len()]; start_depths[0] = 0; stack.push(BlockIdx(0)); - const DEBUG: bool = false; + const DEBUG: bool = true; 'process_blocks: while let Some(block) = stack.pop() { let mut depth = start_depths[block.idx()]; if DEBUG { From 822d5655ff2dc534c47653ec87caae670fb47b3d Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Tue, 21 Jan 2025 23:47:15 -0800 Subject: [PATCH 18/27] formatting --- compiler/codegen/src/compile.rs | 35 +++++++--------- ...on_codegen__compile__tests__match.snap.new | 42 +++++++++++++++++++ vm/src/frame.rs | 4 +- 3 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 8af6b47278..6f4f7d1f75 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -219,7 +219,6 @@ macro_rules! emit { struct PatternContext { // // The current length of fail_pop. // Py_ssize_t fail_pop_size; - /// Py_ssize_t on_top; /// A list of strings corresponding to name captures. It is used to track: /// - Repeated name assignments in the same pattern. @@ -1902,10 +1901,7 @@ impl Compiler { Ok(()) } - fn compile_pattern_helper_rotate( - &mut self, - count: usize, - ) -> CompileResult<()> { + fn compile_pattern_helper_rotate(&mut self, count: usize) -> CompileResult<()> { let mut count = count; while 1 < count { todo!("below"); @@ -1962,10 +1958,7 @@ impl Compiler { pattern_context: &mut PatternContext, ) -> CompileResult<()> { // codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchStar.name, pc)); - self.compile_pattern_helper_store_name( - star.name.as_deref(), - pattern_context, - )?; + self.compile_pattern_helper_store_name(star.name.as_deref(), pattern_context)?; Ok(()) } @@ -2009,19 +2002,14 @@ impl Compiler { return Err(self.error(CodegenErrorType::InvalidMatchCase)); } } - return self.compile_pattern_helper_store_name( - as_pattern.name.as_deref(), - pattern_context, - ); + return self + .compile_pattern_helper_store_name(as_pattern.name.as_deref(), pattern_context); } pattern_context.on_top += 1; emit!(self, Instruction::Duplicate); self.compile_pattern(as_pattern.pattern.as_ref().unwrap(), pattern_context)?; pattern_context.on_top -= 1; - self.compile_pattern_helper_store_name( - as_pattern.name.as_deref(), - pattern_context, - )?; + self.compile_pattern_helper_store_name(as_pattern.name.as_deref(), pattern_context)?; Ok(()) } @@ -2057,7 +2045,11 @@ impl Compiler { let mut pattern_blocks = std::iter::repeat_with(|| self.new_block()) .take(cases.len() + 1) .collect::>(); - eprintln!("created pattern_blocks: {:?} - {:?}(end block)", pattern_blocks.first().unwrap(), pattern_blocks.last().unwrap()); + eprintln!( + "created pattern_blocks: {:?} - {:?}(end block)", + pattern_blocks.first().unwrap(), + pattern_blocks.last().unwrap() + ); let end_block = *pattern_blocks.last().unwrap(); let match_case_type = cases.last().expect("cases is not empty"); @@ -2121,12 +2113,13 @@ impl Compiler { let block = self.new_block(); pattern_blocks.push(block); let code = self.current_code_info(); - let _ = pattern_blocks.iter() + let _ = pattern_blocks + .iter() .zip(pattern_blocks.iter().skip(1)) .for_each(|(a, b)| { eprintln!("linking: {} -> {}", a.0, b.0); code.blocks[a.0 as usize].next = *b; - }); + }); self.switch_to_block(*pattern_blocks.last().unwrap()); let code = self.current_code_info(); for block in pattern_blocks { @@ -3759,6 +3752,6 @@ match v: case _: v = "one" "# - )); + )); } } diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new b/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new new file mode 100644 index 0000000000..ce26d7050c --- /dev/null +++ b/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new @@ -0,0 +1,42 @@ +--- +source: compiler/codegen/src/compile.rs +assertion_line: 3749 +expression: "compile_exec(r#\"\\\nv = \"one\"\nmatch v:\n case \"one\":\n v = \"two\"\n case \"two\":\n v = \"three\"\n case \"three\":\n v = \"one\"\n case _:\n v = \"one\"\n\"#)" +--- + 2 >> 0 LoadConst ("one") + 1 StoreLocal (0, v) + + 3 2 LoadNameAny (0, v) + 3 Duplicate + + 4 4 LoadConst ("one") + 5 CompareOperation (Equal) + 6 Pop + + 5 7 LoadConst ("two") + 8 StoreLocal (0, v) + 9 Jump (0) + 10 Duplicate + + 6 11 LoadConst ("two") + 12 CompareOperation (Equal) + 13 Pop + + 7 14 LoadConst ("three") + 15 StoreLocal (0, v) + 16 Jump (0) + 17 Duplicate + + 8 18 LoadConst ("three") + 19 CompareOperation (Equal) + 20 Pop + + 9 21 LoadConst ("one") + 22 StoreLocal (0, v) + 23 Jump (0) + 24 Pop + + 11 25 LoadConst ("one") + 26 StoreLocal (0, v) + 27 Jump (0) + 28 ReturnConst (None) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 55012b9dba..5d3d95543f 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -675,7 +675,9 @@ impl ExecutingFrame<'_> { } bytecode::Instruction::Swap(i) => { let len = self.state.stack.len(); - self.state.stack.swap(len - 1, len - 1 - i.get(arg) as usize); + self.state + .stack + .swap(len - 1, len - 1 - i.get(arg) as usize); Ok(None) } bytecode::Instruction::Duplicate => { From ad164c0c5e4ae464bb9b890f8914b591229f7c1a Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 22 Jan 2025 12:22:04 -0800 Subject: [PATCH 19/27] basic match statement working Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 297 ++---------------- compiler/codegen/src/ir.rs | 2 +- ...on_codegen__compile__tests__match.snap.new | 84 +++-- 3 files changed, 86 insertions(+), 297 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 6f4f7d1f75..5d772156fb 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -217,29 +217,7 @@ macro_rules! emit { } struct PatternContext { - // // The current length of fail_pop. - // Py_ssize_t fail_pop_size; - /// Py_ssize_t on_top; - /// A list of strings corresponding to name captures. It is used to track: - /// - Repeated name assignments in the same pattern. - /// - Different name assignments in alternatives. - /// - The order of name assignments in alternatives. - pub stores: Vec, - /// If 0, any name captures against our subject will raise. - pub allow_irrefutable: bool, - /// An array of blocks to jump to on failure. Jumping to fail_pop[i] will pop - /// i items off of the stack. The end result looks like this (with each block - /// falling through to the next): - /// fail_pop[4]: POP_TOP - /// fail_pop[3]: POP_TOP - /// fail_pop[2]: POP_TOP - /// fail_pop[1]: POP_TOP - /// fail_pop[0]: NOP - pub fail_pop: Vec, - /// The number of items on top of the stack that need to *stay* on top of the - /// stack. Variable captures go beneath these. All of them will be popped on - /// failure. - pub on_top: usize, + blocks: Vec } impl Compiler { @@ -1786,77 +1764,6 @@ impl Compiler { Ok(()) } - // static int - // ensure_fail_pop(compiler *c, pattern_context *pc, Py_ssize_t n) - // { - // Py_ssize_t size = n + 1; - // if (size <= pc->fail_pop_size) { - // return SUCCESS; - // } - // Py_ssize_t needed = sizeof(jump_target_label) * size; - // jump_target_label *resized = PyMem_Realloc(pc->fail_pop, needed); - // if (resized == NULL) { - // PyErr_NoMemory(); - // return ERROR; - // } - // pc->fail_pop = resized; - // while (pc->fail_pop_size < size) { - // NEW_JUMP_TARGET_LABEL(c, new_block); - // pc->fail_pop[pc->fail_pop_size++] = new_block; - // } - // return SUCCESS; - // } - - fn ensure_fail_pop( - &mut self, - pattern_context: &mut PatternContext, - n: usize, - ) -> CompileResult<()> { - let size = n + 1; - if size <= pattern_context.fail_pop.len() { - return Ok(()); - } - let reserve = size.saturating_sub(pattern_context.fail_pop.len()); - pattern_context.fail_pop.reserve(reserve); - while pattern_context.fail_pop.len() < size { - let label = self.new_block(); - pattern_context.fail_pop.push(label); - } - Ok(()) - } - - // static int - // jump_to_fail_pop(compiler *c, location loc, - // pattern_context *pc, int op) - // { - // // Pop any items on the top of the stack, plus any objects we were going to - // // capture on success: - // Py_ssize_t pops = pc->on_top + PyList_GET_SIZE(pc->stores); - // RETURN_IF_ERROR(ensure_fail_pop(c, pc, pops)); - // ADDOP_JUMP(c, loc, op, pc->fail_pop[pops]); - // return SUCCESS; - // } - fn jump_to_fail_pop(&mut self, pattern_context: &mut PatternContext) -> CompileResult<()> { - let pops = pattern_context.on_top + pattern_context.stores.len(); - self.ensure_fail_pop(pattern_context, pops)?; - // self.switch_to_block(pattern_context.fail_pop[pops]); - Ok(()) - } - // static int - // codegen_pattern_value(compiler *c, pattern_ty p, pattern_context *pc) - // { - // assert(p->kind == MatchValue_kind); - // expr_ty value = p->v.MatchValue.value; - // if (!MATCH_VALUE_EXPR(value)) { - // const char *e = "patterns may only match literals and attribute lookups"; - // return _PyCompile_Error(c, LOC(p), e); - // } - // VISIT(c, expr, value); - // ADDOP_COMPARE(c, LOC(p), Eq); - // ADDOP(c, LOC(p), TO_BOOL); - // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); - // return SUCCESS; - // } fn compile_pattern_value( &mut self, value: &PatternMatchValue, @@ -1869,8 +1776,12 @@ impl Compiler { op: ComparisonOperator::Equal } ); - // emit!(self, Instruction::ToBool); - self.jump_to_fail_pop(pattern_context)?; + emit!( + self, + Instruction::JumpIfFalse { + target: *pattern_context.blocks.last().unwrap() + } + ); Ok(()) } @@ -1889,67 +1800,6 @@ impl Compiler { pattern_context: &mut PatternContext, ) -> CompileResult<()> { todo!(); - // self.emit_load_const(ConstantData::from(singleton.value)); - emit!( - self, - Instruction::CompareOperation { - // TODO: ComparisonOperator::Is doesn't exist, this will have to do for now - op: ComparisonOperator::Equal - } - ); - self.jump_to_fail_pop(pattern_context)?; - Ok(()) - } - - fn compile_pattern_helper_rotate(&mut self, count: usize) -> CompileResult<()> { - let mut count = count; - while 1 < count { - todo!("below"); - // ADDOP_I(c, loc, SWAP, count--); - // emit!(self, Instruction::Swap { count: count as u32 }); - count -= 1; - } - Ok(()) - } - - // static int - // codegen_pattern_helper_store_name(compiler *c, location loc, - // identifier n, pattern_context *pc) - // { - // if (n == NULL) { - // ADDOP(c, loc, POP_TOP); - // return SUCCESS; - // } - // // Can't assign to the same name twice: - // int duplicate = PySequence_Contains(pc->stores, n); - // RETURN_IF_ERROR(duplicate); - // if (duplicate) { - // return codegen_error_duplicate_store(c, loc, n); - // } - // // Rotate this object underneath any items we need to preserve: - // Py_ssize_t rotations = pc->on_top + PyList_GET_SIZE(pc->stores) + 1; - // RETURN_IF_ERROR(codegen_pattern_helper_rotate(c, loc, rotations)); - // RETURN_IF_ERROR(PyList_Append(pc->stores, n)); - // return SUCCESS; - // } - fn compile_pattern_helper_store_name( - &mut self, - n: Option<&str>, - pattern_context: &mut PatternContext, - ) -> CompileResult<()> { - if n.is_none() { - emit!(self, Instruction::Pop); - return Ok(()); - } - let n = n.unwrap(); - let duplicate = pattern_context.stores.contains(&n.to_string()); - if duplicate { - return Err(self.error(CodegenErrorType::DuplicateStore(n.to_string()))); - } - let rotations = pattern_context.on_top + pattern_context.stores.len() + 1; - self.compile_pattern_helper_rotate(rotations)?; - pattern_context.stores.push(n.to_string()); - Ok(()) } fn compile_pattern_star( @@ -1957,60 +1807,15 @@ impl Compiler { star: &PatternMatchStar, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - // codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchStar.name, pc)); - self.compile_pattern_helper_store_name(star.name.as_deref(), pattern_context)?; - Ok(()) + todo!() } - // static int - // codegen_pattern_as(compiler *c, pattern_ty p, pattern_context *pc) - // { - // assert(p->kind == MatchAs_kind); - // if (p->v.MatchAs.pattern == NULL) { - // // An irrefutable match: - // if (!pc->allow_irrefutable) { - // if (p->v.MatchAs.name) { - // const char *e = "name capture %R makes remaining patterns unreachable"; - // return _PyCompile_Error(c, LOC(p), e, p->v.MatchAs.name); - // } - // const char *e = "wildcard makes remaining patterns unreachable"; - // return _PyCompile_Error(c, LOC(p), e); - // } - // return codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchAs.name, pc); - // } - // // Need to make a copy for (possibly) storing later: - // pc->on_top++; - // ADDOP_I(c, LOC(p), COPY, 1); - // RETURN_IF_ERROR(codegen_pattern(c, p->v.MatchAs.pattern, pc)); - // // Success! Store it: - // pc->on_top--; - // RETURN_IF_ERROR(codegen_pattern_helper_store_name(c, LOC(p), p->v.MatchAs.name, pc)); - // return SUCCESS; - // } fn codegen_pattern_as( &mut self, as_pattern: &PatternMatchAs, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - if as_pattern.pattern.is_none() { - // An irrefutable match: - if !pattern_context.allow_irrefutable { - // TODO: better error message - if let Some(name) = &as_pattern.name { - return Err(self.error(CodegenErrorType::InvalidMatchCase)); - } else { - return Err(self.error(CodegenErrorType::InvalidMatchCase)); - } - } - return self - .compile_pattern_helper_store_name(as_pattern.name.as_deref(), pattern_context); - } - pattern_context.on_top += 1; - emit!(self, Instruction::Duplicate); - self.compile_pattern(as_pattern.pattern.as_ref().unwrap(), pattern_context)?; - pattern_context.on_top -= 1; - self.compile_pattern_helper_store_name(as_pattern.name.as_deref(), pattern_context)?; - Ok(()) + todo!() } fn compile_pattern( @@ -2041,65 +1846,29 @@ impl Compiler { pattern_context: &mut PatternContext, ) -> CompileResult<()> { self.compile_expression(subject)?; - // Block at the end of the switch statement that we jump to after finishing a branch - let mut pattern_blocks = std::iter::repeat_with(|| self.new_block()) + pattern_context.blocks = std::iter::repeat_with(|| self.new_block()) .take(cases.len() + 1) .collect::>(); - eprintln!( - "created pattern_blocks: {:?} - {:?}(end block)", - pattern_blocks.first().unwrap(), - pattern_blocks.last().unwrap() - ); - let end_block = *pattern_blocks.last().unwrap(); + let end_block = *pattern_context.blocks.last().unwrap(); let match_case_type = cases.last().expect("cases is not empty"); let has_default = match_case_type.pattern.is_match_star() && 1 < cases.len(); - // for (Py_ssize_t i = 0; i < cases - has_default; i++) { for i in 0..cases.len() - (has_default as usize) { - // m = asdl_seq_GET(s->v.Match.cases, i); + self.switch_to_block(pattern_context.blocks[i]); let m = &cases[i]; // Only copy the subject if we're *not* on the last case: if i != cases.len() - has_default as usize - 1 { - // ADDOP_I(c, LOC(m->pattern), COPY, 1); emit!(self, Instruction::Duplicate); } - // TODO: redundant - pattern_context.stores = Vec::new(); - // Irrefutable cases must be either guarded, last, or both: - pattern_context.allow_irrefutable = m.guard.is_some() || i == cases.len() - 1; - // pc->fail_pop = NULL; - pattern_context.on_top = 0; self.compile_pattern(&m.pattern, pattern_context)?; - assert_eq!(pattern_context.on_top, 0); - // It's a match! Store all of the captured names (they're on the stack). - let nstores = pattern_context.stores.len(); - for n in 0..nstores { - let name = &pattern_context.stores[n]; - // codegen_nameop(c, LOC(m->pattern), name, Store) < 0) - self.compile_name(name, NameUsage::Store)?; - } - // if (m->guard) { - // RETURN_IF_ERROR(ensure_fail_pop(c, pc, 0)); - // RETURN_IF_ERROR(codegen_jump_if(c, LOC(m->pattern), m->guard, pc->fail_pop[0], 0)); - // } - if let Some(guard) = &m.guard { - self.ensure_fail_pop(pattern_context, 0)?; - self.compile_jump_if(guard, false, pattern_blocks[i + 1])?; - } - if i != cases.len() - (has_default as usize) - 1 { - // ADDOP(c, LOC(m->pattern), POP_TOP); - emit!(self, Instruction::Pop); - } - // VISIT_SEQ(c, stmt, m->body); self.compile_statements(&m.body)?; - // ADDOP_JUMP(c, NO_LOCATION, JUMP, end); emit!(self, Instruction::Jump { target: end_block }); } if has_default { // A trailing "case _" is common, and lets us save a bit of redundant // pushing and popping in the loop above: let m = &cases.last().unwrap(); - self.switch_to_block(*pattern_blocks.last().unwrap()); + self.switch_to_block(*pattern_context.blocks.last().unwrap()); if cases.len() == 1 { // No matches. Done with the subject: // ADDOP(c, LOC(m->pattern), POP_TOP); @@ -2110,22 +1879,16 @@ impl Compiler { } self.compile_statements(&m.body)?; } - let block = self.new_block(); - pattern_blocks.push(block); + + self.switch_to_block(end_block); + let code = self.current_code_info(); - let _ = pattern_blocks + let _ = pattern_context.blocks .iter() - .zip(pattern_blocks.iter().skip(1)) + .zip(pattern_context.blocks.iter().skip(1)) .for_each(|(a, b)| { - eprintln!("linking: {} -> {}", a.0, b.0); code.blocks[a.0 as usize].next = *b; }); - self.switch_to_block(*pattern_blocks.last().unwrap()); - let code = self.current_code_info(); - for block in pattern_blocks { - let b = &code.blocks[block.0 as usize]; - eprintln!("block: {} -> {}", block.0, b.next.0); - } Ok(()) } @@ -2135,10 +1898,7 @@ impl Compiler { cases: &[located_ast::MatchCase], ) -> CompileResult<()> { let mut pattern_context = PatternContext { - stores: Vec::new(), - allow_irrefutable: false, - fail_pop: Vec::new(), - on_top: 0, + blocks: Vec::new(), }; self.compile_match_inner(subject, cases, &mut pattern_context)?; Ok(()) @@ -3421,17 +3181,12 @@ impl Compiler { ir::BlockIdx::NULL, "switching {prev:?} -> {block:?} to completed block" ); - println!("{}", prev.0); - for (count, b) in code.blocks.iter().enumerate() { - println!("{count}: {} {}", b.next.0, b.instructions.len()); - } let prev_block = &mut code.blocks[prev.0 as usize]; assert_eq!( prev_block.next.0, u32::MAX, "switching {prev:?} -> {block:?} from block that's already got a next" ); - eprintln!("switch_to_block {prev:?} -> {block:?}"); prev_block.next = block; code.current_block = block; } @@ -3742,6 +3497,16 @@ for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): assert_dis_snapshot!(compile_exec( r#"\ v = "one" + +if v == "one": + v = "two" +elif v == "two": + v = "three" +elif v == "three": + v = "one" +else: + v = "one" + match v: case "one": v = "two" @@ -3749,8 +3514,8 @@ match v: v = "three" case "three": v = "one" - case _: - v = "one" +# case _: +# v = "one" "# )); } diff --git a/compiler/codegen/src/ir.rs b/compiler/codegen/src/ir.rs index 95cb2780b0..9f1a86e51d 100644 --- a/compiler/codegen/src/ir.rs +++ b/compiler/codegen/src/ir.rs @@ -227,7 +227,7 @@ impl CodeInfo { let mut start_depths = vec![u32::MAX; self.blocks.len()]; start_depths[0] = 0; stack.push(BlockIdx(0)); - const DEBUG: bool = true; + const DEBUG: bool = false; 'process_blocks: while let Some(block) = stack.pop() { let mut depth = start_depths[block.idx()]; if DEBUG { diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new b/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new index ce26d7050c..4285f2528b 100644 --- a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new +++ b/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new @@ -1,42 +1,66 @@ --- source: compiler/codegen/src/compile.rs -assertion_line: 3749 -expression: "compile_exec(r#\"\\\nv = \"one\"\nmatch v:\n case \"one\":\n v = \"two\"\n case \"two\":\n v = \"three\"\n case \"three\":\n v = \"one\"\n case _:\n v = \"one\"\n\"#)" +assertion_line: 3509 +expression: "compile_exec(r#\"\\\nv = \"one\"\n\nif v == \"one\":\n v = \"two\"\nelif v == \"two\":\n v = \"three\"\nelif v == \"three\":\n v = \"one\"\nelse:\n v = \"one\"\n\nmatch v:\n case \"one\":\n v = \"two\"\n case \"two\":\n v = \"three\"\n case \"three\":\n v = \"one\"\n# case _:\n# v = \"one\"\n\"#)" --- - 2 >> 0 LoadConst ("one") + 2 0 LoadConst ("one") 1 StoreLocal (0, v) - 3 2 LoadNameAny (0, v) - 3 Duplicate + 4 2 LoadNameAny (0, v) + 3 LoadConst ("one") + 4 CompareOperation (Equal) + 5 JumpIfFalse (9) - 4 4 LoadConst ("one") - 5 CompareOperation (Equal) - 6 Pop + 5 6 LoadConst ("two") + 7 StoreLocal (0, v) + 8 Jump (25) - 5 7 LoadConst ("two") - 8 StoreLocal (0, v) - 9 Jump (0) - 10 Duplicate + 6 >> 9 LoadNameAny (0, v) + 10 LoadConst ("two") + 11 CompareOperation (Equal) + 12 JumpIfFalse (16) - 6 11 LoadConst ("two") - 12 CompareOperation (Equal) - 13 Pop + 7 13 LoadConst ("three") + 14 StoreLocal (0, v) + 15 Jump (25) - 7 14 LoadConst ("three") - 15 StoreLocal (0, v) - 16 Jump (0) - 17 Duplicate + 8 >> 16 LoadNameAny (0, v) + 17 LoadConst ("three") + 18 CompareOperation (Equal) + 19 JumpIfFalse (23) - 8 18 LoadConst ("three") - 19 CompareOperation (Equal) - 20 Pop + 9 20 LoadConst ("one") + 21 StoreLocal (0, v) + 22 Jump (25) - 9 21 LoadConst ("one") - 22 StoreLocal (0, v) - 23 Jump (0) - 24 Pop + 11 >> 23 LoadConst ("one") + 24 StoreLocal (0, v) - 11 25 LoadConst ("one") - 26 StoreLocal (0, v) - 27 Jump (0) - 28 ReturnConst (None) + 13 >> 25 LoadNameAny (0, v) + 26 Duplicate + + 14 27 LoadConst ("one") + 28 CompareOperation (Equal) + 29 JumpIfFalse (46) + + 15 30 LoadConst ("two") + 31 StoreLocal (0, v) + 32 Jump (46) + 33 Duplicate + + 16 34 LoadConst ("two") + 35 CompareOperation (Equal) + 36 JumpIfFalse (46) + + 17 37 LoadConst ("three") + 38 StoreLocal (0, v) + 39 Jump (46) + + 18 40 LoadConst ("three") + 41 CompareOperation (Equal) + 42 JumpIfFalse (46) + + 19 43 LoadConst ("one") + 44 StoreLocal (0, v) + 45 Jump (46) + >> 46 ReturnConst (None) From e955ede8a4224e080fd0cbbb86502ad8b553ce31 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 22 Jan 2025 12:26:31 -0800 Subject: [PATCH 20/27] basic match statement working Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 5d772156fb..0a5ca668b3 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -3498,15 +3498,6 @@ for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): r#"\ v = "one" -if v == "one": - v = "two" -elif v == "two": - v = "three" -elif v == "three": - v = "one" -else: - v = "one" - match v: case "one": v = "two" From 46498985b0fd80fc118a0477117283a80717e3a8 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 22 Jan 2025 20:29:26 -0800 Subject: [PATCH 21/27] working as pattern (default works too by extension) Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 162 +++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 34 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 0a5ca668b3..cc442c2386 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -18,9 +18,7 @@ use itertools::Itertools; use num_complex::Complex64; use num_traits::ToPrimitive; use rustpython_ast::located::{self as located_ast, Located}; -use rustpython_ast::{ - Pattern, PatternMatchAs, PatternMatchSingleton, PatternMatchStar, PatternMatchValue, -}; +use rustpython_ast::{Pattern, PatternMatchAs, PatternMatchOr, PatternMatchSequence, PatternMatchSingleton, PatternMatchStar, PatternMatchValue}; use rustpython_compiler_core::bytecode::ComparisonOperator; use rustpython_compiler_core::{ bytecode::{self, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, OpArg, OpArgType}, @@ -217,7 +215,9 @@ macro_rules! emit { } struct PatternContext { - blocks: Vec + current_block: usize, + blocks: Vec, + allow_irrefutable: bool, } impl Compiler { @@ -1776,12 +1776,6 @@ impl Compiler { op: ComparisonOperator::Equal } ); - emit!( - self, - Instruction::JumpIfFalse { - target: *pattern_context.blocks.last().unwrap() - } - ); Ok(()) } @@ -1799,7 +1793,38 @@ impl Compiler { singleton: &PatternMatchSingleton, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - todo!(); + todo!("Pattern::MatchSingleton"); + } + + fn compile_pattern_sequence( + &mut self, + sequence: &PatternMatchSequence, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { + let patterns = &sequence.patterns; + // let size = patterns.len(); + let mut star = -1; + let mut only_wildcard = 1; + let mut star_wildcard = 0; + // Find a starred name, if it exists. There may be at most one: + // for i in 0..patterns.len() { + // let pattern = &patterns[i]; + // if pattern.is_match_star() { + // if star >= 0 { + // return Err(self.error_loc( + // CodegenErrorType::MultipleStarredNamesInSequencePattern, + // pattern.location(), + // )); + // } + // star_wildcard = pattern.is_wildcard_star(); + // only_wildcard = star_wildcard; + // star = i; + // continue; + // } + // only_wildcard &= pattern.is_wildcard(); + // } + todo!("Pattern::MatchSequence"); + Ok(()) } fn compile_pattern_star( @@ -1807,38 +1832,104 @@ impl Compiler { star: &PatternMatchStar, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - todo!() + todo!("Pattern::MatchStar"); } - fn codegen_pattern_as( + fn compile_pattern_as( &mut self, as_pattern: &PatternMatchAs, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - todo!() + if as_pattern.pattern.is_none() { + if !pattern_context.allow_irrefutable { + // TODO: better error message + // TODO: Fix this error message + // if let Some(name) = as_pattern.name.as_ref() { + // return Err(self.error_loc( + // CodegenErrorType::InvalidMatchCase, + // as_pattern.location() + // )); + // } + return Err(self.error_loc( + CodegenErrorType::InvalidMatchCase, + as_pattern.location(), + )); + } + } + // Need to make a copy for (possibly) storing later: + emit!(self, Instruction::Duplicate); + if let Some(pattern) = &as_pattern.pattern { + self.compile_pattern_inner(pattern, pattern_context)?; + } + if let Some(name) = as_pattern.name.as_ref() { + self.store_name(name.as_str())?; + } else { + emit!(self, Instruction::Pop); + } + Ok(()) } - fn compile_pattern( + fn compile_pattern_or( &mut self, - pattern_type: &Pattern, + or_pattern: &PatternMatchOr, pattern_context: &mut PatternContext, + ) -> CompileResult<()> { + todo!("Pattern::MatchOr"); + let true_block = self.new_block(); + // Only check as many patterns as necessary, jumping over everything if needed + for pattern in &or_pattern.patterns { + self.compile_pattern_inner(pattern, pattern_context)?; + emit!( + self, + Instruction::JumpIfTrue { + target: true_block + } + ); + } + self.switch_to_block(true_block); + Ok(()) + } + + fn compile_pattern_inner( + &mut self, + pattern_type: &Pattern, + pattern_context: &mut PatternContext ) -> CompileResult<()> { match &pattern_type { Pattern::MatchValue(value) => self.compile_pattern_value(&value, pattern_context), Pattern::MatchSingleton(singleton) => { self.compile_pattern_singleton(&singleton, pattern_context) } - Pattern::MatchSequence(_sequence) => { - Err(self.error(CodegenErrorType::NotImplementedYet)) + Pattern::MatchSequence(sequence) => { + self.compile_pattern_sequence(&sequence, pattern_context) } - Pattern::MatchMapping(_mapping) => Err(self.error(CodegenErrorType::NotImplementedYet)), - Pattern::MatchClass(_class) => Err(self.error(CodegenErrorType::NotImplementedYet)), + Pattern::MatchMapping(_mapping) => { + todo!("Pattern::MatchMapping"); + }, + Pattern::MatchClass(_class) => { + todo!("Pattern::MatchClass"); + }, Pattern::MatchStar(star) => self.compile_pattern_star(&star, pattern_context), - Pattern::MatchAs(as_pattern) => self.codegen_pattern_as(&as_pattern, pattern_context), - Pattern::MatchOr(_or_pattern) => Err(self.error(CodegenErrorType::NotImplementedYet)), + Pattern::MatchAs(as_pattern) => self.compile_pattern_as(&as_pattern, pattern_context), + Pattern::MatchOr(or_pattern) => self.compile_pattern_or(&or_pattern, pattern_context), } } + fn compile_pattern( + &mut self, + pattern_type: &Pattern, + pattern_context: &mut PatternContext, + ) -> CompileResult<()> { + self.compile_pattern_inner(pattern_type, pattern_context)?; + emit!( + self, + Instruction::JumpIfFalse { + target: pattern_context.blocks[pattern_context.current_block + 1] + } + ); + Ok(()) + } + fn compile_match_inner( &mut self, subject: &located_ast::Expr, @@ -1852,9 +1943,13 @@ impl Compiler { let end_block = *pattern_context.blocks.last().unwrap(); let match_case_type = cases.last().expect("cases is not empty"); - let has_default = match_case_type.pattern.is_match_star() && 1 < cases.len(); + // TODO: doesn't have to be a default case + // let has_default = match_case_type.pattern.is_match_as() && 1 < cases.len(); + let has_default = false; for i in 0..cases.len() - (has_default as usize) { self.switch_to_block(pattern_context.blocks[i]); + pattern_context.current_block = i; + pattern_context.allow_irrefutable = cases[i].guard.is_some() || i == cases.len() - 1; let m = &cases[i]; // Only copy the subject if we're *not* on the last case: if i != cases.len() - has_default as usize - 1 { @@ -1871,7 +1966,6 @@ impl Compiler { self.switch_to_block(*pattern_context.blocks.last().unwrap()); if cases.len() == 1 { // No matches. Done with the subject: - // ADDOP(c, LOC(m->pattern), POP_TOP); emit!(self, Instruction::Pop); } else { // Show line coverage for default case (it doesn't create bytecode) @@ -1898,7 +1992,9 @@ impl Compiler { cases: &[located_ast::MatchCase], ) -> CompileResult<()> { let mut pattern_context = PatternContext { + current_block: usize::MAX, blocks: Vec::new(), + allow_irrefutable: false, }; self.compile_match_inner(subject, cases, &mut pattern_context)?; Ok(()) @@ -3496,17 +3592,15 @@ for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): fn test_match() { assert_dis_snapshot!(compile_exec( r#"\ -v = "one" - +v = 5 match v: - case "one": - v = "two" - case "two": - v = "three" - case "three": - v = "one" -# case _: -# v = "one" + case 1: + print("v is 1") + case 2: + print("v is 2") + case _: + print("v is something else") + "# )); } From 493fc6e303a386df4522d5c9d846ce325591d91f Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 22 Jan 2025 20:30:19 -0800 Subject: [PATCH 22/27] fix rust tests Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index cc442c2386..7777150c69 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -3587,21 +3587,4 @@ for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): " )); } - - #[test] - fn test_match() { - assert_dis_snapshot!(compile_exec( - r#"\ -v = 5 -match v: - case 1: - print("v is 1") - case 2: - print("v is 2") - case _: - print("v is something else") - -"# - )); - } } From fe7f8a3e0efd011ab4fbecec7940abbbe9b92556 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 23 Jan 2025 00:08:10 -0800 Subject: [PATCH 23/27] cleanup Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 125 +++--------------- ...on_codegen__compile__tests__match.snap.new | 66 --------- compiler/core/src/bytecode.rs | 8 +- vm/src/frame.rs | 4 +- 4 files changed, 24 insertions(+), 179 deletions(-) delete mode 100644 compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 7777150c69..3923ab2dfb 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -18,7 +18,7 @@ use itertools::Itertools; use num_complex::Complex64; use num_traits::ToPrimitive; use rustpython_ast::located::{self as located_ast, Located}; -use rustpython_ast::{Pattern, PatternMatchAs, PatternMatchOr, PatternMatchSequence, PatternMatchSingleton, PatternMatchStar, PatternMatchValue}; +use rustpython_ast::{Pattern, PatternMatchAs, PatternMatchValue}; use rustpython_compiler_core::bytecode::ComparisonOperator; use rustpython_compiler_core::{ bytecode::{self, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, OpArg, OpArgType}, @@ -1767,7 +1767,7 @@ impl Compiler { fn compile_pattern_value( &mut self, value: &PatternMatchValue, - pattern_context: &mut PatternContext, + _pattern_context: &mut PatternContext, ) -> CompileResult<()> { self.compile_expression(&value.value)?; emit!( @@ -1779,62 +1779,6 @@ impl Compiler { Ok(()) } - // static int - // codegen_pattern_singleton(compiler *c, pattern_ty p, pattern_context *pc) - // { - // assert(p->kind == MatchSingleton_kind); - // ADDOP_LOAD_CONST(c, LOC(p), p->v.MatchSingleton.value); - // ADDOP_COMPARE(c, LOC(p), Is); - // RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE)); - // return SUCCESS; - // } - fn compile_pattern_singleton( - &mut self, - singleton: &PatternMatchSingleton, - pattern_context: &mut PatternContext, - ) -> CompileResult<()> { - todo!("Pattern::MatchSingleton"); - } - - fn compile_pattern_sequence( - &mut self, - sequence: &PatternMatchSequence, - pattern_context: &mut PatternContext, - ) -> CompileResult<()> { - let patterns = &sequence.patterns; - // let size = patterns.len(); - let mut star = -1; - let mut only_wildcard = 1; - let mut star_wildcard = 0; - // Find a starred name, if it exists. There may be at most one: - // for i in 0..patterns.len() { - // let pattern = &patterns[i]; - // if pattern.is_match_star() { - // if star >= 0 { - // return Err(self.error_loc( - // CodegenErrorType::MultipleStarredNamesInSequencePattern, - // pattern.location(), - // )); - // } - // star_wildcard = pattern.is_wildcard_star(); - // only_wildcard = star_wildcard; - // star = i; - // continue; - // } - // only_wildcard &= pattern.is_wildcard(); - // } - todo!("Pattern::MatchSequence"); - Ok(()) - } - - fn compile_pattern_star( - &mut self, - star: &PatternMatchStar, - pattern_context: &mut PatternContext, - ) -> CompileResult<()> { - todo!("Pattern::MatchStar"); - } - fn compile_pattern_as( &mut self, as_pattern: &PatternMatchAs, @@ -1843,17 +1787,14 @@ impl Compiler { if as_pattern.pattern.is_none() { if !pattern_context.allow_irrefutable { // TODO: better error message - // TODO: Fix this error message - // if let Some(name) = as_pattern.name.as_ref() { - // return Err(self.error_loc( - // CodegenErrorType::InvalidMatchCase, - // as_pattern.location() - // )); - // } - return Err(self.error_loc( - CodegenErrorType::InvalidMatchCase, - as_pattern.location(), - )); + if let Some(_name) = as_pattern.name.as_ref() { + return Err( + self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location()) + ); + } + return Err( + self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location()) + ); } } // Need to make a copy for (possibly) storing later: @@ -1869,49 +1810,15 @@ impl Compiler { Ok(()) } - fn compile_pattern_or( - &mut self, - or_pattern: &PatternMatchOr, - pattern_context: &mut PatternContext, - ) -> CompileResult<()> { - todo!("Pattern::MatchOr"); - let true_block = self.new_block(); - // Only check as many patterns as necessary, jumping over everything if needed - for pattern in &or_pattern.patterns { - self.compile_pattern_inner(pattern, pattern_context)?; - emit!( - self, - Instruction::JumpIfTrue { - target: true_block - } - ); - } - self.switch_to_block(true_block); - Ok(()) - } - fn compile_pattern_inner( &mut self, pattern_type: &Pattern, - pattern_context: &mut PatternContext + pattern_context: &mut PatternContext, ) -> CompileResult<()> { match &pattern_type { Pattern::MatchValue(value) => self.compile_pattern_value(&value, pattern_context), - Pattern::MatchSingleton(singleton) => { - self.compile_pattern_singleton(&singleton, pattern_context) - } - Pattern::MatchSequence(sequence) => { - self.compile_pattern_sequence(&sequence, pattern_context) - } - Pattern::MatchMapping(_mapping) => { - todo!("Pattern::MatchMapping"); - }, - Pattern::MatchClass(_class) => { - todo!("Pattern::MatchClass"); - }, - Pattern::MatchStar(star) => self.compile_pattern_star(&star, pattern_context), Pattern::MatchAs(as_pattern) => self.compile_pattern_as(&as_pattern, pattern_context), - Pattern::MatchOr(or_pattern) => self.compile_pattern_or(&or_pattern, pattern_context), + _ => Err(self.error(CodegenErrorType::NotImplementedYet)), } } @@ -1942,8 +1849,8 @@ impl Compiler { .collect::>(); let end_block = *pattern_context.blocks.last().unwrap(); - let match_case_type = cases.last().expect("cases is not empty"); - // TODO: doesn't have to be a default case + let _match_case_type = cases.last().expect("cases is not empty"); + // TODO: get proper check for default case // let has_default = match_case_type.pattern.is_match_as() && 1 < cases.len(); let has_default = false; for i in 0..cases.len() - (has_default as usize) { @@ -1959,6 +1866,7 @@ impl Compiler { self.compile_statements(&m.body)?; emit!(self, Instruction::Jump { target: end_block }); } + // TODO: below code is not called and does not work if has_default { // A trailing "case _" is common, and lets us save a bit of redundant // pushing and popping in the loop above: @@ -1977,7 +1885,8 @@ impl Compiler { self.switch_to_block(end_block); let code = self.current_code_info(); - let _ = pattern_context.blocks + let _ = pattern_context + .blocks .iter() .zip(pattern_context.blocks.iter().skip(1)) .for_each(|(a, b)| { diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new b/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new deleted file mode 100644 index 4285f2528b..0000000000 --- a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__match.snap.new +++ /dev/null @@ -1,66 +0,0 @@ ---- -source: compiler/codegen/src/compile.rs -assertion_line: 3509 -expression: "compile_exec(r#\"\\\nv = \"one\"\n\nif v == \"one\":\n v = \"two\"\nelif v == \"two\":\n v = \"three\"\nelif v == \"three\":\n v = \"one\"\nelse:\n v = \"one\"\n\nmatch v:\n case \"one\":\n v = \"two\"\n case \"two\":\n v = \"three\"\n case \"three\":\n v = \"one\"\n# case _:\n# v = \"one\"\n\"#)" ---- - 2 0 LoadConst ("one") - 1 StoreLocal (0, v) - - 4 2 LoadNameAny (0, v) - 3 LoadConst ("one") - 4 CompareOperation (Equal) - 5 JumpIfFalse (9) - - 5 6 LoadConst ("two") - 7 StoreLocal (0, v) - 8 Jump (25) - - 6 >> 9 LoadNameAny (0, v) - 10 LoadConst ("two") - 11 CompareOperation (Equal) - 12 JumpIfFalse (16) - - 7 13 LoadConst ("three") - 14 StoreLocal (0, v) - 15 Jump (25) - - 8 >> 16 LoadNameAny (0, v) - 17 LoadConst ("three") - 18 CompareOperation (Equal) - 19 JumpIfFalse (23) - - 9 20 LoadConst ("one") - 21 StoreLocal (0, v) - 22 Jump (25) - - 11 >> 23 LoadConst ("one") - 24 StoreLocal (0, v) - - 13 >> 25 LoadNameAny (0, v) - 26 Duplicate - - 14 27 LoadConst ("one") - 28 CompareOperation (Equal) - 29 JumpIfFalse (46) - - 15 30 LoadConst ("two") - 31 StoreLocal (0, v) - 32 Jump (46) - 33 Duplicate - - 16 34 LoadConst ("two") - 35 CompareOperation (Equal) - 36 JumpIfFalse (46) - - 17 37 LoadConst ("three") - 38 StoreLocal (0, v) - 39 Jump (46) - - 18 40 LoadConst ("three") - 41 CompareOperation (Equal) - 42 JumpIfFalse (46) - - 19 43 LoadConst ("one") - 44 StoreLocal (0, v) - 45 Jump (46) - >> 46 ReturnConst (None) diff --git a/compiler/core/src/bytecode.rs b/compiler/core/src/bytecode.rs index 1403cd93c9..78721791d8 100644 --- a/compiler/core/src/bytecode.rs +++ b/compiler/core/src/bytecode.rs @@ -432,7 +432,9 @@ pub enum Instruction { op: Arg, }, Pop, - Swap(Arg), + Swap { + index: Arg, + }, Rotate2, Rotate3, Duplicate, @@ -1203,7 +1205,7 @@ impl Instruction { | TestOperation { .. } | CompareOperation { .. } => -1, Pop => -1, - Swap(_) => 0, + Swap { .. } => 0, Rotate2 | Rotate3 => 0, Duplicate => 1, Duplicate2 => 2, @@ -1400,7 +1402,7 @@ impl Instruction { TestOperation { op } => w!(TestOperation, ?op), CompareOperation { op } => w!(CompareOperation, ?op), Pop => w!(Pop), - Swap(a) => w!(Swap, a), + Swap { index } => w!(Swap, index), Rotate2 => w!(Rotate2), Rotate3 => w!(Rotate3), Duplicate => w!(Duplicate), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 5d3d95543f..f575640e61 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -673,11 +673,11 @@ impl ExecutingFrame<'_> { self.pop_value(); Ok(None) } - bytecode::Instruction::Swap(i) => { + bytecode::Instruction::Swap { index } => { let len = self.state.stack.len(); self.state .stack - .swap(len - 1, len - 1 - i.get(arg) as usize); + .swap(len - 1, len - 1 - index.get(arg) as usize); Ok(None) } bytecode::Instruction::Duplicate => { From 805757a2e819e2ded24084fcb24261bec3112a66 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 23 Jan 2025 00:15:15 -0800 Subject: [PATCH 24/27] fix clippy errors Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 3923ab2dfb..4b7868b372 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1784,18 +1784,16 @@ impl Compiler { as_pattern: &PatternMatchAs, pattern_context: &mut PatternContext, ) -> CompileResult<()> { - if as_pattern.pattern.is_none() { - if !pattern_context.allow_irrefutable { - // TODO: better error message - if let Some(_name) = as_pattern.name.as_ref() { - return Err( - self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location()) - ); - } + if as_pattern.pattern.is_none() && !pattern_context.allow_irrefutable { + // TODO: better error message + if let Some(_name) = as_pattern.name.as_ref() { return Err( self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location()) ); } + return Err( + self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location()) + ); } // Need to make a copy for (possibly) storing later: emit!(self, Instruction::Duplicate); @@ -1816,8 +1814,8 @@ impl Compiler { pattern_context: &mut PatternContext, ) -> CompileResult<()> { match &pattern_type { - Pattern::MatchValue(value) => self.compile_pattern_value(&value, pattern_context), - Pattern::MatchAs(as_pattern) => self.compile_pattern_as(&as_pattern, pattern_context), + Pattern::MatchValue(value) => self.compile_pattern_value(value, pattern_context), + Pattern::MatchAs(as_pattern) => self.compile_pattern_as(as_pattern, pattern_context), _ => Err(self.error(CodegenErrorType::NotImplementedYet)), } } @@ -1885,7 +1883,7 @@ impl Compiler { self.switch_to_block(end_block); let code = self.current_code_info(); - let _ = pattern_context + pattern_context .blocks .iter() .zip(pattern_context.blocks.iter().skip(1)) From b176f9c1887519758de0051366a01e41e10bb388 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 23 Jan 2025 00:17:14 -0800 Subject: [PATCH 25/27] formatting Signed-off-by: Ashwin Naren --- compiler/codegen/src/compile.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 4b7868b372..e30318c80b 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1791,9 +1791,7 @@ impl Compiler { self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location()) ); } - return Err( - self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location()) - ); + return Err(self.error_loc(CodegenErrorType::InvalidMatchCase, as_pattern.location())); } // Need to make a copy for (possibly) storing later: emit!(self, Instruction::Duplicate); From 8e78c6d59c083d67e7ba122cb7447892c7d928a3 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 25 Jan 2025 22:29:20 +0900 Subject: [PATCH 26/27] trivial edits --- compiler/codegen/src/compile.rs | 35 ++++++++++++++++++++------------- compiler/codegen/src/error.rs | 8 ++++---- compiler/core/src/bytecode.rs | 6 +++--- vm/src/frame.rs | 2 +- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index e30318c80b..fbe1e97b86 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -7,7 +7,6 @@ #![deny(clippy::cast_possible_truncation)] -use crate::ir::BlockIdx; use crate::{ error::{CodegenError, CodegenErrorType}, ir, @@ -18,13 +17,14 @@ use itertools::Itertools; use num_complex::Complex64; use num_traits::ToPrimitive; use rustpython_ast::located::{self as located_ast, Located}; -use rustpython_ast::{Pattern, PatternMatchAs, PatternMatchValue}; -use rustpython_compiler_core::bytecode::ComparisonOperator; use rustpython_compiler_core::{ - bytecode::{self, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, OpArg, OpArgType}, + bytecode::{ + self, Arg as OpArgMarker, CodeObject, ComparisonOperator, ConstantData, Instruction, OpArg, + OpArgType, + }, Mode, }; -use rustpython_parser_core::source_code::{LineNumber, SourceLocation, SourceRange}; +use rustpython_parser_core::source_code::{LineNumber, SourceLocation}; use std::borrow::Cow; type CompileResult = Result; @@ -216,7 +216,7 @@ macro_rules! emit { struct PatternContext { current_block: usize, - blocks: Vec, + blocks: Vec, allow_irrefutable: bool, } @@ -1766,7 +1766,7 @@ impl Compiler { fn compile_pattern_value( &mut self, - value: &PatternMatchValue, + value: &located_ast::PatternMatchValue, _pattern_context: &mut PatternContext, ) -> CompileResult<()> { self.compile_expression(&value.value)?; @@ -1781,7 +1781,7 @@ impl Compiler { fn compile_pattern_as( &mut self, - as_pattern: &PatternMatchAs, + as_pattern: &located_ast::PatternMatchAs, pattern_context: &mut PatternContext, ) -> CompileResult<()> { if as_pattern.pattern.is_none() && !pattern_context.allow_irrefutable { @@ -1808,19 +1808,26 @@ impl Compiler { fn compile_pattern_inner( &mut self, - pattern_type: &Pattern, + pattern_type: &located_ast::Pattern, pattern_context: &mut PatternContext, ) -> CompileResult<()> { match &pattern_type { - Pattern::MatchValue(value) => self.compile_pattern_value(value, pattern_context), - Pattern::MatchAs(as_pattern) => self.compile_pattern_as(as_pattern, pattern_context), - _ => Err(self.error(CodegenErrorType::NotImplementedYet)), + located_ast::Pattern::MatchValue(value) => { + self.compile_pattern_value(value, pattern_context) + } + located_ast::Pattern::MatchAs(as_pattern) => { + self.compile_pattern_as(as_pattern, pattern_context) + } + _ => { + eprintln!("not implemented pattern type: {pattern_type:?}"); + Err(self.error(CodegenErrorType::NotImplementedYet)) + } } } fn compile_pattern( &mut self, - pattern_type: &Pattern, + pattern_type: &located_ast::Pattern, pattern_context: &mut PatternContext, ) -> CompileResult<()> { self.compile_pattern_inner(pattern_type, pattern_context)?; @@ -1873,7 +1880,7 @@ impl Compiler { emit!(self, Instruction::Pop); } else { // Show line coverage for default case (it doesn't create bytecode) - emit!(self, Instruction::Noop); + emit!(self, Instruction::Nop); } self.compile_statements(&m.body)?; } diff --git a/compiler/codegen/src/error.rs b/compiler/codegen/src/error.rs index 5a8f8df0f1..27333992df 100644 --- a/compiler/codegen/src/error.rs +++ b/compiler/codegen/src/error.rs @@ -30,9 +30,9 @@ pub enum CodegenErrorType { TooManyStarUnpack, EmptyWithItems, EmptyWithBody, - NotImplementedYet, // RustPython marker for unimplemented features DuplicateStore(String), InvalidMatchCase, + NotImplementedYet, // RustPython marker for unimplemented features } impl std::error::Error for CodegenErrorType {} @@ -77,15 +77,15 @@ impl fmt::Display for CodegenErrorType { EmptyWithBody => { write!(f, "empty body on With") } - NotImplementedYet => { - write!(f, "RustPython does not implement this feature yet") - } DuplicateStore(s) => { write!(f, "duplicate store {s}") } InvalidMatchCase => { write!(f, "invalid match case") } + NotImplementedYet => { + write!(f, "RustPython does not implement this feature yet") + } } } } diff --git a/compiler/core/src/bytecode.rs b/compiler/core/src/bytecode.rs index 78721791d8..95b2e7c54b 100644 --- a/compiler/core/src/bytecode.rs +++ b/compiler/core/src/bytecode.rs @@ -373,7 +373,7 @@ pub enum Instruction { /// No-op, don't do anything at all /// /// Equivalent to `NOP` in cpython bytecode - Noop, + Nop, /// Importing by name ImportName { idx: Arg, @@ -1184,7 +1184,7 @@ impl Instruction { /// pub fn stack_effect(&self, arg: OpArg, jump: bool) -> i32 { match self { - Noop => 0, + Nop => 0, ImportName { .. } | ImportNameless => -1, ImportStar => -1, ImportFrom { .. } => 1, @@ -1370,7 +1370,7 @@ impl Instruction { }; match self { - Noop => w!(Noop), + Nop => w!(Nop), ImportName { idx } => w!(ImportName, name = idx), ImportNameless => w!(ImportNameless), ImportStar => w!(ImportStar), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index f575640e61..d44a7d83d7 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -517,7 +517,7 @@ impl ExecutingFrame<'_> { } match instruction { - bytecode::Instruction::Noop => Ok(None), + bytecode::Instruction::Nop => Ok(None), bytecode::Instruction::LoadConst { idx } => { self.push_value(self.code.constants[idx.get(arg) as usize].clone().into()); Ok(None) From 4beef2c2c1c6c70acd179733735ca86d8fe4053b Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 25 Jan 2025 22:54:54 +0900 Subject: [PATCH 27/27] Remove currently unused Nop,Swap op --- compiler/codegen/src/compile.rs | 2 +- compiler/core/src/bytecode.rs | 11 ----------- vm/src/frame.rs | 8 -------- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index fbe1e97b86..b75e977b5d 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1880,7 +1880,7 @@ impl Compiler { emit!(self, Instruction::Pop); } else { // Show line coverage for default case (it doesn't create bytecode) - emit!(self, Instruction::Nop); + // emit!(self, Instruction::Nop); } self.compile_statements(&m.body)?; } diff --git a/compiler/core/src/bytecode.rs b/compiler/core/src/bytecode.rs index 95b2e7c54b..c8dbc63744 100644 --- a/compiler/core/src/bytecode.rs +++ b/compiler/core/src/bytecode.rs @@ -370,10 +370,6 @@ pub type NameIdx = u32; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum Instruction { - /// No-op, don't do anything at all - /// - /// Equivalent to `NOP` in cpython bytecode - Nop, /// Importing by name ImportName { idx: Arg, @@ -432,9 +428,6 @@ pub enum Instruction { op: Arg, }, Pop, - Swap { - index: Arg, - }, Rotate2, Rotate3, Duplicate, @@ -1184,7 +1177,6 @@ impl Instruction { /// pub fn stack_effect(&self, arg: OpArg, jump: bool) -> i32 { match self { - Nop => 0, ImportName { .. } | ImportNameless => -1, ImportStar => -1, ImportFrom { .. } => 1, @@ -1205,7 +1197,6 @@ impl Instruction { | TestOperation { .. } | CompareOperation { .. } => -1, Pop => -1, - Swap { .. } => 0, Rotate2 | Rotate3 => 0, Duplicate => 1, Duplicate2 => 2, @@ -1370,7 +1361,6 @@ impl Instruction { }; match self { - Nop => w!(Nop), ImportName { idx } => w!(ImportName, name = idx), ImportNameless => w!(ImportNameless), ImportStar => w!(ImportStar), @@ -1402,7 +1392,6 @@ impl Instruction { TestOperation { op } => w!(TestOperation, ?op), CompareOperation { op } => w!(CompareOperation, ?op), Pop => w!(Pop), - Swap { index } => w!(Swap, index), Rotate2 => w!(Rotate2), Rotate3 => w!(Rotate3), Duplicate => w!(Duplicate), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index d44a7d83d7..8fc7e171b3 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -517,7 +517,6 @@ impl ExecutingFrame<'_> { } match instruction { - bytecode::Instruction::Nop => Ok(None), bytecode::Instruction::LoadConst { idx } => { self.push_value(self.code.constants[idx.get(arg) as usize].clone().into()); Ok(None) @@ -673,13 +672,6 @@ impl ExecutingFrame<'_> { self.pop_value(); Ok(None) } - bytecode::Instruction::Swap { index } => { - let len = self.state.stack.len(); - self.state - .stack - .swap(len - 1, len - 1 - index.get(arg) as usize); - Ok(None) - } bytecode::Instruction::Duplicate => { // Duplicate top of stack let value = self.top_value();