Compare commits

...

6 Commits

Author SHA1 Message Date
Jeong YunWon
29d014a0e1 Pin malachite versions to avoid API incompatibility 2025-02-03 11:57:30 +09:00
Ashwin Naren
396a0ca563 Basic Match statements (#5485)
Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-01-25 23:14:15 +09:00
Jeong YunWon
a500178b3c update parser to fix match crash 2025-01-22 13:41:01 +09:00
Jeong YunWon
7d770f55fb more assertions in switch_to_block 2025-01-21 23:53:23 +09:00
Jeong, YunWon
db283a66e8 Merge pull request #5477 from youknowone/better-downcast-error
Add better panic for abnormal downcast error
2025-01-21 13:54:45 +09:00
Jeong YunWon
5ad7e97e05 Add better panic for abnormal downcast error 2025-01-16 00:57:09 +09:00
6 changed files with 228 additions and 60 deletions

72
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "Inflector"
@@ -307,12 +307,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -563,15 +557,23 @@ dependencies = [
[[package]]
name = "derive_more"
version = "0.99.18"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.77",
"unicode-xid",
]
[[package]]
@@ -1074,9 +1076,9 @@ dependencies = [
[[package]]
name = "libm"
version = "0.2.8"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "libredox"
@@ -1168,9 +1170,9 @@ dependencies = [
[[package]]
name = "malachite"
version = "0.4.16"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5616515d632967cd329b6f6db96be9a03ea0b3a49cdbc45b0016803dad8a77b7"
checksum = "4a6ecab92657eb234bfe98abd0b17920772c6b14ce69256950142e2eb36d000b"
dependencies = [
"malachite-base",
"malachite-nz",
@@ -1179,9 +1181,9 @@ dependencies = [
[[package]]
name = "malachite-base"
version = "0.4.16"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46059721011b0458b7bd6d9179be5d0b60294281c23320c207adceaecc54d13b"
checksum = "06f6d078bb4dad5b76f6a85737e6c9113667d199612a73eef375c56e75d5d4d5"
dependencies = [
"hashbrown",
"itertools 0.11.0",
@@ -1191,9 +1193,9 @@ dependencies = [
[[package]]
name = "malachite-bigint"
version = "0.2.0"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17703a19c80bbdd0b7919f0f104f3b0597f7de4fc4e90a477c15366a5ba03faa"
checksum = "63c7698e7abae9522edd41b54ae0395a9bd736ca5054b5fbe672316283a7b817"
dependencies = [
"derive_more",
"malachite",
@@ -1204,9 +1206,9 @@ dependencies = [
[[package]]
name = "malachite-nz"
version = "0.4.16"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1503b27e825cabd1c3d0ff1e95a39fb2ec9eab6fd3da6cfa41aec7091d273e78"
checksum = "ad61a09cde72dcdb8b2baaf3e71430c5bd94fb6753da89f17301a459f3e466db"
dependencies = [
"itertools 0.11.0",
"libm",
@@ -1215,9 +1217,9 @@ dependencies = [
[[package]]
name = "malachite-q"
version = "0.4.16"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a475503a70a3679dbe3b9b230a23622516742528ba614a7b2490f180ea9cb514"
checksum = "be4a9dfffb87667ae94e8320213d3f5419e3e37311bc6bf8f4d2ab9b44f3a535"
dependencies = [
"itertools 0.11.0",
"malachite-base",
@@ -1895,8 +1897,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cdaf8ee5c1473b993b398c174641d3aa9da847af36e8d5eb8291930b72f31a5"
source = "git+https://github.com/RustPython/Parser.git?rev=4588ea5c3e6327009640e7c9c89eb6fa9220358e#4588ea5c3e6327009640e7c9c89eb6fa9220358e"
dependencies = [
"is-macro",
"malachite-bigint",
@@ -2008,8 +2009,7 @@ dependencies = [
[[package]]
name = "rustpython-format"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0389039b132ad8e350552d771270ccd03186985696764bcee2239694e7839942"
source = "git+https://github.com/RustPython/Parser.git?rev=4588ea5c3e6327009640e7c9c89eb6fa9220358e#4588ea5c3e6327009640e7c9c89eb6fa9220358e"
dependencies = [
"bitflags 2.6.0",
"itertools 0.11.0",
@@ -2036,8 +2036,7 @@ dependencies = [
[[package]]
name = "rustpython-literal"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8304be3cae00232a1721a911033e55877ca3810215f66798e964a2d8d22281d"
source = "git+https://github.com/RustPython/Parser.git?rev=4588ea5c3e6327009640e7c9c89eb6fa9220358e#4588ea5c3e6327009640e7c9c89eb6fa9220358e"
dependencies = [
"hexf-parse",
"is-macro",
@@ -2049,8 +2048,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "868f724daac0caf9bd36d38caf45819905193a901e8f1c983345a68e18fb2abb"
source = "git+https://github.com/RustPython/Parser.git?rev=4588ea5c3e6327009640e7c9c89eb6fa9220358e#4588ea5c3e6327009640e7c9c89eb6fa9220358e"
dependencies = [
"anyhow",
"is-macro",
@@ -2073,8 +2071,7 @@ dependencies = [
[[package]]
name = "rustpython-parser-core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b6c12fa273825edc7bccd9a734f0ad5ba4b8a2f4da5ff7efe946f066d0f4ad"
source = "git+https://github.com/RustPython/Parser.git?rev=4588ea5c3e6327009640e7c9c89eb6fa9220358e#4588ea5c3e6327009640e7c9c89eb6fa9220358e"
dependencies = [
"is-macro",
"memchr",
@@ -2084,8 +2081,7 @@ dependencies = [
[[package]]
name = "rustpython-parser-vendored"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04fcea49a4630a3a5d940f4d514dc4f575ed63c14c3e3ed07146634aed7f67a6"
source = "git+https://github.com/RustPython/Parser.git?rev=4588ea5c3e6327009640e7c9c89eb6fa9220358e#4588ea5c3e6327009640e7c9c89eb6fa9220358e"
dependencies = [
"memchr",
"once_cell",
@@ -2859,6 +2855,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unicode_names2"
version = "1.3.0"

View File

@@ -122,16 +122,16 @@ rustpython-stdlib = { path = "stdlib", default-features = false, version = "0.4.
rustpython-sre_engine = { path = "vm/sre_engine", version = "0.4.0" }
rustpython-doc = { git = "https://github.com/RustPython/__doc__", tag = "0.3.0", version = "0.3.0" }
rustpython-literal = { version = "0.4.0" }
rustpython-parser-core = { version = "0.4.0" }
rustpython-parser = { version = "0.4.0" }
rustpython-ast = { version = "0.4.0" }
rustpython-format= { version = "0.4.0" }
# rustpython-literal = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "00d2f1d1a7522ef9c85c10dfa5f0bb7178dee655" }
# rustpython-parser-core = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "00d2f1d1a7522ef9c85c10dfa5f0bb7178dee655" }
# rustpython-parser = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "00d2f1d1a7522ef9c85c10dfa5f0bb7178dee655" }
# rustpython-ast = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "00d2f1d1a7522ef9c85c10dfa5f0bb7178dee655" }
# rustpython-format = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "00d2f1d1a7522ef9c85c10dfa5f0bb7178dee655" }
# rustpython-literal = { version = "0.4.0" }
# rustpython-parser-core = { version = "0.4.0" }
# rustpython-parser = { version = "0.4.0" }
# rustpython-ast = { version = "0.4.0" }
# rustpython-format= { version = "0.4.0" }
rustpython-literal = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-parser-core = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-parser = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-ast = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-format = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
# rustpython-literal = { path = "../RustPython-parser/literal" }
# rustpython-parser-core = { path = "../RustPython-parser/core" }
# rustpython-parser = { path = "../RustPython-parser/parser" }
@@ -157,9 +157,9 @@ junction = "1.0.0"
libc = "0.2.153"
log = "0.4.16"
nix = { version = "0.29", features = ["fs", "user", "process", "term", "time", "signal", "ioctl", "socket", "sched", "zerocopy", "dir", "hostname", "net", "poll"] }
malachite-bigint = "0.2.0"
malachite-q = "0.4.4"
malachite-base = "0.4.4"
malachite-bigint = "0.2.2"
malachite-q = "<=0.4.18"
malachite-base = "<=0.4.18"
memchr = "2.7.2"
num-complex = "0.4.0"
num-integer = "0.1.44"

View File

@@ -18,7 +18,10 @@ use num_complex::Complex64;
use num_traits::ToPrimitive;
use rustpython_ast::located::{self as located_ast, Located};
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};
@@ -211,6 +214,12 @@ macro_rules! emit {
};
}
struct PatternContext {
current_block: usize,
blocks: Vec<ir::BlockIdx>,
allow_irrefutable: bool,
}
impl Compiler {
fn new(opts: CompileOpts, source_path: String, code_name: String) -> Self {
let module_code = ir::CodeInfo {
@@ -1755,14 +1764,152 @@ impl Compiler {
Ok(())
}
fn compile_pattern_value(
&mut self,
value: &located_ast::PatternMatchValue,
_pattern_context: &mut PatternContext,
) -> CompileResult<()> {
self.compile_expression(&value.value)?;
emit!(
self,
Instruction::CompareOperation {
op: ComparisonOperator::Equal
}
);
Ok(())
}
fn compile_pattern_as(
&mut self,
as_pattern: &located_ast::PatternMatchAs,
pattern_context: &mut PatternContext,
) -> CompileResult<()> {
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);
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_inner(
&mut self,
pattern_type: &located_ast::Pattern,
pattern_context: &mut PatternContext,
) -> CompileResult<()> {
match &pattern_type {
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: &located_ast::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,
cases: &[located_ast::MatchCase],
pattern_context: &mut PatternContext,
) -> CompileResult<()> {
self.compile_expression(subject)?;
pattern_context.blocks = std::iter::repeat_with(|| self.new_block())
.take(cases.len() + 1)
.collect::<Vec<_>>();
let end_block = *pattern_context.blocks.last().unwrap();
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) {
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 {
emit!(self, Instruction::Duplicate);
}
self.compile_pattern(&m.pattern, pattern_context)?;
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:
let m = &cases.last().unwrap();
self.switch_to_block(*pattern_context.blocks.last().unwrap());
if cases.len() == 1 {
// No matches. Done with the subject:
emit!(self, Instruction::Pop);
} else {
// Show line coverage for default case (it doesn't create bytecode)
// emit!(self, Instruction::Nop);
}
self.compile_statements(&m.body)?;
}
self.switch_to_block(end_block);
let code = self.current_code_info();
pattern_context
.blocks
.iter()
.zip(pattern_context.blocks.iter().skip(1))
.for_each(|(a, b)| {
code.blocks[a.0 as usize].next = *b;
});
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 {
current_block: usize::MAX,
blocks: Vec::new(),
allow_irrefutable: false,
};
self.compile_match_inner(subject, cases, &mut pattern_context)?;
Ok(())
}
fn compile_chained_comparison(
@@ -3036,16 +3183,17 @@ impl Compiler {
fn switch_to_block(&mut self, block: ir::BlockIdx) {
let code = self.current_code_info();
let prev = code.current_block;
assert_ne!(prev, block, "recursive switching {prev:?} -> {block:?}");
assert_eq!(
code.blocks[block].next,
ir::BlockIdx::NULL,
"switching to completed block"
"switching {prev:?} -> {block:?} to completed block"
);
let prev_block = &mut code.blocks[prev.0 as usize];
assert_eq!(
prev_block.next.0,
u32::MAX,
"switching from block that's already got a next"
"switching {prev:?} -> {block:?} from block that's already got a next"
);
prev_block.next = block;
code.current_block = block;

View File

@@ -30,6 +30,8 @@ pub enum CodegenErrorType {
TooManyStarUnpack,
EmptyWithItems,
EmptyWithBody,
DuplicateStore(String),
InvalidMatchCase,
NotImplementedYet, // RustPython marker for unimplemented features
}
@@ -75,6 +77,12 @@ impl fmt::Display for CodegenErrorType {
EmptyWithBody => {
write!(f, "empty body on With")
}
DuplicateStore(s) => {
write!(f, "duplicate store {s}")
}
InvalidMatchCase => {
write!(f, "invalid match case")
}
NotImplementedYet => {
write!(f, "RustPython does not implement this feature yet")
}

View File

@@ -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 {

View File

@@ -365,7 +365,15 @@ impl VirtualMachine {
let actual_class = obj.class();
let actual_type = &*actual_class.name();
let expected_type = &*class.name();
let msg = format!("Expected {msg} '{expected_type}' but '{actual_type}' found");
let msg = format!("Expected {msg} '{expected_type}' but '{actual_type}' found.");
#[cfg(debug_assertions)]
let msg = if class.get_id() == actual_class.get_id() {
let mut msg = msg;
msg += " Did you forget to add `#[pyclass(with(Constructor))]`?";
msg
} else {
msg
};
self.new_exception_msg(error_type.to_owned(), msg)
}