Merge branch 'main' into codegen-bug

This commit is contained in:
Shunsuke Shibayama 2023-07-23 23:27:56 +09:00
commit 4053e1646f
38 changed files with 901 additions and 221 deletions

View file

@ -54,7 +54,7 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest] os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- run: rustup update stable - run: rustup update stable
- run: cargo build --all --all-targets --verbose - run: cargo build --all --all-targets --verbose

View file

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
use erg_common::pathutil::NormalizedPathBuf;
use erg_common::traits::{Locational, Stream}; use erg_common::traits::{Locational, Stream};
use erg_compiler::artifact::IncompleteArtifact; use erg_compiler::artifact::IncompleteArtifact;
use erg_compiler::erg_parser::parse::Parsable; use erg_compiler::erg_parser::parse::Parsable;
@ -146,26 +147,26 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
/// self is __included__ /// self is __included__
pub fn dependencies_of(&self, uri: &NormalizedUrl) -> Vec<NormalizedUrl> { pub fn dependencies_of(&self, uri: &NormalizedUrl) -> Vec<NormalizedUrl> {
let graph = self.get_graph().unwrap(); let graph = self.get_graph().unwrap();
let path = util::uri_to_path(uri); let path = NormalizedPathBuf::from(util::uri_to_path(uri));
graph.sort().unwrap(); graph.sort().unwrap();
let self_node = graph.get_node(&path).unwrap(); let self_node = graph.get_node(&path).unwrap();
graph graph
.ref_inner() .ref_inner()
.iter() .iter()
.filter(|node| node.id == path || self_node.depends_on(&node.id)) .filter(|node| node.id == path || self_node.depends_on(&node.id))
.map(|node| NormalizedUrl::new(Url::from_file_path(&node.id).unwrap())) .map(|node| NormalizedUrl::new(Url::from_file_path(node.id.to_path_buf()).unwrap()))
.collect() .collect()
} }
/// self is __not included__ /// self is __not included__
pub fn dependents_of(&self, uri: &NormalizedUrl) -> Vec<NormalizedUrl> { pub fn dependents_of(&self, uri: &NormalizedUrl) -> Vec<NormalizedUrl> {
let graph = self.get_graph().unwrap(); let graph = self.get_graph().unwrap();
let path = util::uri_to_path(uri); let path = NormalizedPathBuf::from(util::uri_to_path(uri));
graph graph
.ref_inner() .ref_inner()
.iter() .iter()
.filter(|node| node.depends_on(&path)) .filter(|node| node.depends_on(&path))
.map(|node| NormalizedUrl::new(Url::from_file_path(&node.id).unwrap())) .map(|node| NormalizedUrl::new(Url::from_file_path(node.id.to_path_buf()).unwrap()))
.collect() .collect()
} }
} }

View file

@ -216,7 +216,11 @@ impl Input {
match &self.kind { match &self.kind {
InputKind::File(filename) => format!( InputKind::File(filename) => format!(
"{}_{}", "{}_{}",
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_"), filename
.file_stem()
.and_then(|f| f.to_str())
.unwrap_or("_")
.trim_end_matches(".d"),
self.id self.id
), ),
InputKind::REPL | InputKind::Pipe(_) => format!("stdin_{}", self.id), InputKind::REPL | InputKind::Pipe(_) => format!("stdin_{}", self.id),
@ -248,9 +252,11 @@ impl Input {
pub fn unescaped_file_stem(&self) -> &str { pub fn unescaped_file_stem(&self) -> &str {
match &self.kind { match &self.kind {
InputKind::File(filename) => { InputKind::File(filename) => filename
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_") .file_stem()
} .and_then(|f| f.to_str())
.unwrap_or("_")
.trim_end_matches(".d"),
InputKind::REPL | InputKind::Pipe(_) => "stdin", InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin", InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string", InputKind::Str(_) => "string",
@ -260,9 +266,11 @@ impl Input {
pub fn unescaped_filename(&self) -> &str { pub fn unescaped_filename(&self) -> &str {
match &self.kind { match &self.kind {
InputKind::File(filename) => { InputKind::File(filename) => filename
filename.file_name().and_then(|f| f.to_str()).unwrap_or("_") .file_name()
} .and_then(|f| f.to_str())
.unwrap_or("_")
.trim_end_matches(".d"),
InputKind::REPL | InputKind::Pipe(_) => "stdin", InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin", InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string", InputKind::Str(_) => "string",
@ -283,7 +291,9 @@ impl Input {
pub fn module_name(&self) -> String { pub fn module_name(&self) -> String {
match &self.kind { match &self.kind {
InputKind::File(filename) => { InputKind::File(filename) => {
let file_stem = if filename.file_stem() == Some(OsStr::new("__init__")) { let file_stem = if filename.file_stem() == Some(OsStr::new("__init__"))
|| filename.file_stem() == Some(OsStr::new("__init__.d"))
{
filename.parent().and_then(|p| p.file_stem()) filename.parent().and_then(|p| p.file_stem())
} else { } else {
filename.file_stem() filename.file_stem()
@ -291,6 +301,7 @@ impl Input {
file_stem file_stem
.and_then(|f| f.to_str()) .and_then(|f| f.to_str())
.unwrap_or("_") .unwrap_or("_")
.trim_end_matches(".d")
.to_string() .to_string()
} }
InputKind::REPL | InputKind::Pipe(_) => "<stdin>".to_string(), InputKind::REPL | InputKind::Pipe(_) => "<stdin>".to_string(),
@ -748,11 +759,11 @@ impl Output {
pub fn lsp_log(file: &str, line: u32, msg: &str) { pub fn lsp_log(file: &str, line: u32, msg: &str) {
let file_path = erg_path().join("els.log"); let file_path = erg_path().join("els.log");
let mut f = if file_path.exists() { let mut f = if file_path.exists() {
File::options().write(true).open(file_path).unwrap() File::options().append(true).open(file_path).unwrap()
} else { } else {
File::create(file_path).unwrap() File::create(file_path).unwrap()
}; };
f.write_all(format!("{file}@{line}: {msg}").as_bytes()) f.write_all(format!("{file}@{line}: {msg}\n").as_bytes())
.unwrap(); .unwrap();
} }

View file

@ -19,6 +19,7 @@ pub mod levenshtein;
pub mod macros; pub mod macros;
pub mod opcode; pub mod opcode;
pub mod opcode308; pub mod opcode308;
pub mod opcode309;
pub mod opcode310; pub mod opcode310;
pub mod opcode311; pub mod opcode311;
pub mod pathutil; pub mod pathutil;

View file

@ -31,14 +31,14 @@ impl_u8_enum! {Opcode308;
BINARY_TRUE_DIVIDE = 27, BINARY_TRUE_DIVIDE = 27,
INPLACE_FLOOR_DIVIDE = 28, INPLACE_FLOOR_DIVIDE = 28,
INPLACE_TRUE_DIVIDE = 29, INPLACE_TRUE_DIVIDE = 29,
GET_LEN = 30, // GET_LEN = 30,
MATCH_MAPPING = 31, // MATCH_MAPPING = 31,
MATCH_SEQUENCE = 32, // MATCH_SEQUENCE = 32,
MATCH_KEYS = 33, // MATCH_KEYS = 33,
PUSH_EXC_INFO = 35, // PUSH_EXC_INFO = 35,
CHECK_EXC_MATCH = 36, // CHECK_EXC_MATCH = 36,
CHECK_EG_MATCH = 37, // CHECK_EG_MATCH = 37,
WITH_EXCEPT_START = 49, // WITH_EXCEPT_START = 49,
GET_AITER = 50, GET_AITER = 50,
GET_ANEXT = 51, GET_ANEXT = 51,
BEFORE_ASYNC_WITH = 52, BEFORE_ASYNC_WITH = 52,
@ -92,9 +92,10 @@ impl_u8_enum! {Opcode308;
POP_JUMP_IF_FALSE = 114, POP_JUMP_IF_FALSE = 114,
POP_JUMP_IF_TRUE = 115, POP_JUMP_IF_TRUE = 115,
LOAD_GLOBAL = 116, LOAD_GLOBAL = 116,
IS_OP = 117, // IS_OP = 117,
CONTAINS_OP = 118, // CONTAINS_OP = 118,
RERAISE = 119, // RERAISE = 119,
SETUP_FINALLY = 122,
LOAD_FAST = 124, LOAD_FAST = 124,
STORE_FAST = 125, STORE_FAST = 125,
DELETE_FAST = 126, DELETE_FAST = 126,

View file

@ -0,0 +1,163 @@
//! defines `Opcode` (represents Python bytecode opcodes).
//!
//! Opcode(Pythonバイトコードオペコードを表す)を定義する
#![allow(dead_code)]
#![allow(non_camel_case_types)]
use crate::impl_u8_enum;
impl_u8_enum! {Opcode309;
POP_TOP = 1,
ROT_TWO = 2,
ROT_THREE = 3,
DUP_TOP = 4,
DUP_TOP2 = 5,
ROT_FOUR = 6,
NOP = 9,
UNARY_POSITIVE = 10,
UNARY_NEGATIVE = 11,
UNARY_NOT = 12,
UNARY_INVERT = 15,
BINARY_MATRIX_MULTIPLY = 16,
INPLACE_MATRIX_MULTIPLY = 17,
BINARY_POWER = 19,
BINARY_MULTIPLY = 20,
BINARY_MODULO = 22,
BINARY_ADD = 23,
BINARY_SUBTRACT = 24,
BINARY_SUBSCR = 25,
BINARY_FLOOR_DIVIDE = 26,
BINARY_TRUE_DIVIDE = 27,
INPLACE_FLOOR_DIVIDE = 28,
INPLACE_TRUE_DIVIDE = 29,
// GET_LEN = 30,
// MATCH_MAPPING = 31,
// MATCH_SEQUENCE = 32,
// MATCH_KEYS = 33,
// PUSH_EXC_INFO = 35,
// CHECK_EXC_MATCH = 36,
// CHECK_EG_MATCH = 37,
RERAISE = 48,
WITH_EXCEPT_START = 49,
GET_AITER = 50,
GET_ANEXT = 51,
BEFORE_ASYNC_WITH = 52,
// BEGIN_FINALLY = 53,
END_ASYNC_FOR = 54,
// TODO:
INPLACE_ADD = 55,
INPLACE_SUBTRACT = 56,
INPLACE_MULTIPLY = 57,
INPLACE_MODULO = 59,
STORE_SUBSCR = 60,
BINARY_AND = 64,
BINARY_XOR = 65,
BINARY_OR = 66,
GET_ITER = 68,
GET_YIELD_FROM_ITER = 69,
PRINT_EXPR = 70,
LOAD_BUILD_CLASS = 71,
// LOAD_ASSERTION_ERROR = 74,
// WITH_CLEANUP_START = 81,
WITH_CLEANUP_FINISH = 82,
RETURN_VALUE = 83,
IMPORT_STAR = 84,
SETUP_ANNOTATIONS = 85,
YIELD_VALUE = 86,
POP_BLOCK = 87,
// END_FINALLY = 88,
POP_EXCEPT = 89,
/* ↓ These opcodes take an arg */
STORE_NAME = 90,
DELETE_NAME = 91,
UNPACK_SEQUENCE = 92,
FOR_ITER = 93,
UNPACK_EX = 94,
STORE_ATTR = 95,
STORE_GLOBAL = 97,
LOAD_CONST = 100,
LOAD_NAME = 101,
BUILD_TUPLE = 102,
BUILD_LIST = 103,
BUILD_SET = 104,
BUILD_MAP = 105, // build a Dict object
LOAD_ATTR = 106,
COMPARE_OP = 107,
IMPORT_NAME = 108,
IMPORT_FROM = 109,
JUMP_FORWARD = 110,
JUMP_IF_FALSE_OR_POP = 111,
JUMP_IF_TRUE_OR_POP = 112,
JUMP_ABSOLUTE = 113,
POP_JUMP_IF_FALSE = 114,
POP_JUMP_IF_TRUE = 115,
LOAD_GLOBAL = 116,
IS_OP = 117,
CONTAINS_OP = 118,
LOAD_FAST = 124,
STORE_FAST = 125,
DELETE_FAST = 126,
RAISE_VARARGS = 130,
CALL_FUNCTION = 131,
MAKE_FUNCTION = 132,
LOAD_CLOSURE = 135,
LOAD_DEREF = 136,
STORE_DEREF = 137,
CALL_FUNCTION_KW = 141,
CALL_FUNCTION_EX = 142,
SETUP_WITH = 143,
EXTENDED_ARG = 144,
BUILD_TUPLE_UNPACK_WITH_CALL = 158,
LOAD_METHOD = 160,
CALL_METHOD = 161,
CALL_FINALLY = 162,
POP_FINALLY = 163,
// Erg-specific opcodes (must have a unary `ERG_`)
// Define in descending order from 219, 255
ERG_POP_NTH = 196,
ERG_PEEK_NTH = 197, // get ref to the arg-th element from TOS
ERG_INC = 198, // name += 1; arg: typecode
ERG_DEC = 199, // name -= 1
ERG_LOAD_FAST_IMMUT = 200,
ERG_STORE_FAST_IMMUT = 201,
ERG_MOVE_FAST = 202,
ERG_CLONE_FAST = 203,
ERG_COPY_FAST = 204,
ERG_REF_FAST = 205,
ERG_REF_MUT_FAST = 206,
ERG_MOVE_OUTER = 207,
ERG_CLONE_OUTER = 208,
ERG_COPY_OUTER = 209,
ERG_REF_OUTER = 210,
ERG_REF_MUT_OUTER = 211,
ERG_LESS_THAN = 212,
ERG_LESS_EQUAL = 213,
ERG_EQUAL = 214,
ERG_NOT_EQUAL = 215,
ERG_MAKE_SLOT = 216,
ERG_MAKE_TYPE = 217,
ERG_MAKE_PURE_FUNCTION = 218,
ERG_CALL_PURE_FUNCTION = 219,
/* ↑ These opcodes take an arg ↑ */
/* ↓ These opcodes take no arg ↓ */
// ... = 220,
ERG_LOAD_EMPTY_SLOT = 242,
ERG_LOAD_EMPTY_STR = 243,
ERG_LOAD_1_NAT = 244,
ERG_LOAD_1_INT = 245,
ERG_LOAD_1_REAL = 246,
ERG_LOAD_NONE = 247,
ERG_MUTATE = 248, // !x
// `[] =` (it doesn't cause any exceptions)
ERG_STORE_SUBSCR = 249,
// ... = 250,
// `= []` (it doesn't cause any exceptions)
ERG_BINARY_SUBSCR = 251,
ERG_BINARY_RANGE = 252,
// `/?` (rhs may be 0, it may cause a runtime panic)
ERG_TRY_BINARY_DIVIDE = 253,
// `/` (rhs could not be 0, it doesn't cause any exceptions)
ERG_BINARY_TRUE_DIVIDE = 254,
NOT_IMPLEMENTED = 255,
}

View file

@ -10,7 +10,7 @@ use crate::normalize_path;
/// `PathBuf` may give false equivalence decisions in non-case-sensitive file systems. /// `PathBuf` may give false equivalence decisions in non-case-sensitive file systems.
/// Use this for dictionary keys, etc. /// Use this for dictionary keys, etc.
/// See also: `els::util::NormalizedUrl` /// See also: `els::util::NormalizedUrl`
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct NormalizedPathBuf(PathBuf); pub struct NormalizedPathBuf(PathBuf);
impl<P: Into<PathBuf>> From<P> for NormalizedPathBuf { impl<P: Into<PathBuf>> From<P> for NormalizedPathBuf {
@ -41,7 +41,7 @@ impl Deref for NormalizedPathBuf {
impl NormalizedPathBuf { impl NormalizedPathBuf {
pub fn new(path: PathBuf) -> Self { pub fn new(path: PathBuf) -> Self {
NormalizedPathBuf(normalize_path(path)) NormalizedPathBuf(normalize_path(path.canonicalize().unwrap_or(path)))
} }
} }

View file

@ -14,6 +14,7 @@ use erg_common::fresh::SharedFreshNameGenerator;
use erg_common::io::Input; use erg_common::io::Input;
use erg_common::opcode::{CommonOpcode, CompareOp}; use erg_common::opcode::{CommonOpcode, CompareOp};
use erg_common::opcode308::Opcode308; use erg_common::opcode308::Opcode308;
use erg_common::opcode309::Opcode309;
use erg_common::opcode310::Opcode310; use erg_common::opcode310::Opcode310;
use erg_common::opcode311::{BinOpCode, Opcode311}; use erg_common::opcode311::{BinOpCode, Opcode311};
use erg_common::option_enum_unwrap; use erg_common::option_enum_unwrap;
@ -1783,10 +1784,10 @@ impl PyCodeGenerator {
self.write_instr(Opcode308::JUMP_FORWARD); // jump to end self.write_instr(Opcode308::JUMP_FORWARD); // jump to end
self.write_arg(0); self.write_arg(0);
// else block // else block
let idx_else_begin = if self.py_version.minor >= Some(11) { let idx_else_begin = match self.py_version.minor {
self.lasti() - idx_pop_jump_if_false - 2 Some(11) => self.lasti() - idx_pop_jump_if_false - 2,
} else { Some(10) => self.lasti(),
self.lasti() _ => self.lasti(),
}; };
self.fill_jump(idx_pop_jump_if_false + 1, idx_else_begin - 2); self.fill_jump(idx_pop_jump_if_false + 1, idx_else_begin - 2);
match args.remove(0) { match args.remove(0) {
@ -1805,8 +1806,12 @@ impl PyCodeGenerator {
self.stack_dec(); self.stack_dec();
} }
} else { } else {
self.write_instr(Opcode308::JUMP_FORWARD); self.write_instr(Opcode311::JUMP_FORWARD);
self.write_arg(1); let jump_to = match self.py_version.minor {
Some(11 | 10) => 1,
_ => 2,
};
self.write_arg(jump_to);
// no else block // no else block
let idx_end = if self.py_version.minor >= Some(11) { let idx_end = if self.py_version.minor >= Some(11) {
self.lasti() - idx_pop_jump_if_false - 1 self.lasti() - idx_pop_jump_if_false - 1
@ -1861,7 +1866,7 @@ impl PyCodeGenerator {
self.write_arg(idx_for_iter / 2); self.write_arg(idx_for_iter / 2);
} }
Some(9 | 8 | 7) => { Some(9 | 8 | 7) => {
self.write_instr(Opcode308::JUMP_ABSOLUTE); self.write_instr(Opcode309::JUMP_ABSOLUTE);
self.write_arg(idx_for_iter); self.write_arg(idx_for_iter);
} }
_ => todo!("not supported Python version"), _ => todo!("not supported Python version"),
@ -2077,7 +2082,7 @@ impl PyCodeGenerator {
self.write_arg(0); self.write_arg(0);
self.write_instr(Opcode311::PUSH_EXC_INFO); self.write_instr(Opcode311::PUSH_EXC_INFO);
self.write_arg(0); self.write_arg(0);
self.write_instr(Opcode308::WITH_EXCEPT_START); self.write_instr(Opcode309::WITH_EXCEPT_START);
self.write_arg(0); self.write_arg(0);
self.write_instr(Opcode311::POP_JUMP_FORWARD_IF_TRUE); self.write_instr(Opcode311::POP_JUMP_FORWARD_IF_TRUE);
self.write_arg(4); self.write_arg(4);
@ -2151,6 +2156,59 @@ impl PyCodeGenerator {
self.emit_load_name_instr(stash); self.emit_load_name_instr(stash);
} }
fn emit_with_instr_309(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
return self.deopt_instr(ControlKind::With, args);
}
let expr = args.remove(0);
let Expr::Lambda(lambda) = args.remove(0) else { unreachable!() };
let params = self.gen_param_names(&lambda.params);
self.emit_expr(expr);
let idx_setup_with = self.lasti();
self.write_instr(Opcode310::SETUP_WITH);
self.write_arg(0);
// push __exit__, __enter__() to the stack
self.stack_inc_n(2);
let lambda_line = lambda.body.last().unwrap().ln_begin().unwrap_or(0);
self.emit_with_block(lambda.body, params);
let stash = Identifier::private_with_line(self.fresh_gen.fresh_varname(), lambda_line);
self.emit_store_instr(stash.clone(), Name);
self.write_instr(POP_BLOCK);
self.write_arg(0);
self.emit_load_const(ValueObj::None);
self.write_instr(Opcode310::DUP_TOP);
self.write_arg(0);
self.stack_inc();
self.write_instr(Opcode310::DUP_TOP);
self.write_arg(0);
self.stack_inc();
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(3);
self.stack_dec_n((1 + 3) - 1);
self.emit_pop_top();
let idx_jump_forward = self.lasti();
self.write_instr(Opcode311::JUMP_FORWARD);
self.write_arg(0);
self.edit_code(idx_setup_with + 1, self.lasti() - idx_setup_with - 2);
self.write_instr(Opcode310::WITH_EXCEPT_START);
self.write_arg(0);
let idx_pop_jump_if_true = self.lasti();
self.write_instr(Opcode310::POP_JUMP_IF_TRUE);
self.write_arg(0);
self.write_instr(Opcode309::RERAISE);
self.write_arg(1);
self.edit_code(idx_pop_jump_if_true + 1, self.lasti());
// self.emit_pop_top();
// self.emit_pop_top();
self.emit_pop_top();
self.write_instr(Opcode310::POP_EXCEPT);
self.write_arg(0);
let idx_end = self.lasti();
self.edit_code(idx_jump_forward + 1, idx_end - idx_jump_forward - 2);
self.emit_load_name_instr(stash);
}
fn emit_with_instr_308(&mut self, mut args: Args) { fn emit_with_instr_308(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!()); log!(info "entered {}", fn_name!());
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) { if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
@ -2161,7 +2219,7 @@ impl PyCodeGenerator {
let params = self.gen_param_names(&lambda.params); let params = self.gen_param_names(&lambda.params);
self.emit_expr(expr); self.emit_expr(expr);
let idx_setup_with = self.lasti(); let idx_setup_with = self.lasti();
self.write_instr(Opcode308::SETUP_WITH); self.write_instr(Opcode309::SETUP_WITH);
self.write_arg(0); self.write_arg(0);
// push __exit__, __enter__() to the stack // push __exit__, __enter__() to the stack
// self.stack_inc_n(2); // self.stack_inc_n(2);
@ -2219,7 +2277,8 @@ impl PyCodeGenerator {
"with!" => match self.py_version.minor { "with!" => match self.py_version.minor {
Some(11) => self.emit_with_instr_311(args), Some(11) => self.emit_with_instr_311(args),
Some(10) => self.emit_with_instr_310(args), Some(10) => self.emit_with_instr_310(args),
Some(9 | 8 | 7) => self.emit_with_instr_308(args), Some(9) => self.emit_with_instr_309(args),
Some(8) => self.emit_with_instr_308(args),
_ => todo!("not supported Python version"), _ => todo!("not supported Python version"),
}, },
// "pyimport" | "py" are here // "pyimport" | "py" are here
@ -2281,7 +2340,7 @@ impl PyCodeGenerator {
} }
self.emit_expr(var_args.expr.clone()); self.emit_expr(var_args.expr.clone());
if pos_len > 0 { if pos_len > 0 {
self.write_instr(Opcode308::BUILD_TUPLE_UNPACK_WITH_CALL); self.write_instr(Opcode309::BUILD_TUPLE_UNPACK_WITH_CALL);
self.write_arg(2); self.write_arg(2);
} }
} }
@ -2752,6 +2811,10 @@ impl PyCodeGenerator {
self.emit_load_name_instr(Identifier::public("Str")); self.emit_load_name_instr(Identifier::public("Str"));
} }
other => match &other.qual_name()[..] { other => match &other.qual_name()[..] {
"Bytes" => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Bytes"));
}
"Array" => { "Array" => {
self.emit_push_null(); self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Array")); self.emit_load_name_instr(Identifier::public("Array"));

View file

@ -14,6 +14,7 @@ use OpKind::*;
use erg_parser::ast::Dict as AstDict; use erg_parser::ast::Dict as AstDict;
use erg_parser::ast::Set as AstSet; use erg_parser::ast::Set as AstSet;
use erg_parser::ast::*; use erg_parser::ast::*;
use erg_parser::desugar::Desugarer;
use erg_parser::token::{Token, TokenKind}; use erg_parser::token::{Token, TokenKind};
use crate::ty::constructors::{ use crate::ty::constructors::{
@ -303,8 +304,28 @@ impl Context {
fn call(&self, subr: ConstSubr, args: ValueArgs, loc: Location) -> EvalResult<ValueObj> { fn call(&self, subr: ConstSubr, args: ValueArgs, loc: Location) -> EvalResult<ValueObj> {
match subr { match subr {
ConstSubr::User(_user) => { ConstSubr::User(user) => {
feature_error!(self, loc, "calling user-defined subroutines").map_err(Into::into) // HACK: should avoid cloning
let mut subr_ctx = Context::instant(
user.name.clone(),
self.cfg.clone(),
2,
self.shared.clone(),
self.clone(),
);
// TODO: var_args
for (arg, sig) in args
.pos_args
.into_iter()
.zip(user.params.non_defaults.iter())
{
let name = VarName::from_str(sig.inspect().unwrap().clone());
subr_ctx.consts.insert(name, arg);
}
for (name, arg) in args.kw_args.into_iter() {
subr_ctx.consts.insert(VarName::from_str(name), arg);
}
subr_ctx.eval_const_block(&user.block())
} }
ConstSubr::Builtin(builtin) => builtin.call(args, self).map_err(|mut e| { ConstSubr::Builtin(builtin) => builtin.call(args, self).map_err(|mut e| {
if e.0.loc.is_unknown() { if e.0.loc.is_unknown() {
@ -316,6 +337,16 @@ impl Context {
self.caused_by(), self.caused_by(),
)) ))
}), }),
ConstSubr::Gen(gen) => gen.call(args, self).map_err(|mut e| {
if e.0.loc.is_unknown() {
e.0.loc = loc;
}
EvalErrors::from(EvalError::new(
*e.0,
self.cfg.input.clone(),
self.caused_by(),
))
}),
} }
} }
@ -429,7 +460,9 @@ impl Context {
fn eval_const_record(&self, record: &Record) -> EvalResult<ValueObj> { fn eval_const_record(&self, record: &Record) -> EvalResult<ValueObj> {
match record { match record {
Record::Normal(rec) => self.eval_const_normal_record(rec), Record::Normal(rec) => self.eval_const_normal_record(rec),
Record::Mixed(_rec) => unreachable_error!(self), // should be desugared Record::Mixed(mixed) => self.eval_const_normal_record(
&Desugarer::desugar_shortened_record_inner(mixed.clone()),
),
} }
} }
@ -1478,24 +1511,29 @@ impl Context {
} }
} }
fn convert_type_to_array(&self, ty: Type) -> Result<Vec<ValueObj>, ()> { fn convert_type_to_array(&self, ty: Type) -> Result<Vec<ValueObj>, Type> {
match ty { match ty {
Type::Poly { name, params } if &name[..] == "Array" || &name[..] == "Array!" => { Type::Poly { name, params } if &name[..] == "Array" || &name[..] == "Array!" => {
let t = self let Ok(t) = self.convert_tp_into_type(params[0].clone()) else {
.convert_tp_into_type(params[0].clone()) return Err(poly(name, params));
.map_err(|_| ())?; };
let TyParam::Value(ValueObj::Nat(len)) = params[1] else { unreachable!() }; let TyParam::Value(ValueObj::Nat(len)) = params[1] else { unreachable!() };
Ok(vec![ValueObj::builtin_type(t); len as usize]) Ok(vec![ValueObj::builtin_type(t); len as usize])
} }
_ => Err(()), _ => Err(ty),
} }
} }
pub(crate) fn convert_value_into_array(&self, val: ValueObj) -> Result<Vec<ValueObj>, ()> { pub(crate) fn convert_value_into_array(
&self,
val: ValueObj,
) -> Result<Vec<ValueObj>, ValueObj> {
match val { match val {
ValueObj::Array(arr) => Ok(arr.to_vec()), ValueObj::Array(arr) => Ok(arr.to_vec()),
ValueObj::Type(t) => self.convert_type_to_array(t.into_typ()), ValueObj::Type(t) => self
_ => Err(()), .convert_type_to_array(t.into_typ())
.map_err(ValueObj::builtin_type),
_ => Err(val),
} }
} }
@ -1784,9 +1822,8 @@ impl Context {
})? { })? {
if let Ok(obj) = ty_ctx.get_const_local(&Token::symbol(&attr_name), &self.name) { if let Ok(obj) = ty_ctx.get_const_local(&Token::symbol(&attr_name), &self.name) {
if let ValueObj::Subr(subr) = obj { if let ValueObj::Subr(subr) = obj {
let is_method = subr.sig_t().self_t().is_some();
let mut pos_args = vec![]; let mut pos_args = vec![];
if is_method { if subr.sig_t().is_method() {
match ValueObj::try_from(lhs) { match ValueObj::try_from(lhs) {
Ok(value) => { Ok(value) => {
pos_args.push(value); pos_args.push(value);

View file

@ -41,6 +41,9 @@ impl Generalizer {
fn generalize_tp(&mut self, free: TyParam, uninit: bool) -> TyParam { fn generalize_tp(&mut self, free: TyParam, uninit: bool) -> TyParam {
match free { match free {
TyParam::Type(t) => TyParam::t(self.generalize_t(*t, uninit)), TyParam::Type(t) => TyParam::t(self.generalize_t(*t, uninit)),
TyParam::Value(ValueObj::Type(t)) => {
TyParam::t(self.generalize_t(t.into_typ(), uninit))
}
TyParam::FreeVar(fv) if fv.is_generalized() => TyParam::FreeVar(fv), TyParam::FreeVar(fv) if fv.is_generalized() => TyParam::FreeVar(fv),
TyParam::FreeVar(fv) if fv.is_linked() => { TyParam::FreeVar(fv) if fv.is_linked() => {
self.generalize_tp(fv.crack().clone(), uninit) self.generalize_tp(fv.crack().clone(), uninit)
@ -122,7 +125,7 @@ impl Generalizer {
TyParam::unary(op, val) TyParam::unary(op, val)
} }
other if other.has_no_unbound_var() => other, other if other.has_no_unbound_var() => other,
other => todo!("{other}"), other => todo!("{other:?}"),
} }
} }

View file

@ -897,7 +897,7 @@ impl Context {
Immutable, Immutable,
Visibility::BUILTIN_PUBLIC, Visibility::BUILTIN_PUBLIC,
); );
let str_getitem_t = fn1_kw_met(Str, kw(KW_IDX, Nat), Str); let str_getitem_t = fn1_kw_met(Str, kw(KW_IDX, Nat | poly(RANGE, vec![ty_tp(Int)])), Str);
str_.register_builtin_erg_impl( str_.register_builtin_erg_impl(
FUNDAMENTAL_GETITEM, FUNDAMENTAL_GETITEM,
str_getitem_t, str_getitem_t,
@ -1181,7 +1181,7 @@ impl Context {
); );
/* Array */ /* Array */
let mut array_ = let mut array_ =
Self::builtin_poly_class(ARRAY, vec![PS::t_nd(TY_T), PS::named_nd(TY_N, Nat)], 10); Self::builtin_poly_class(ARRAY, vec![PS::t_nd(TY_T), PS::default(TY_N, Nat)], 10);
array_.register_superclass(mono(GENERIC_ARRAY), &generic_array); array_.register_superclass(mono(GENERIC_ARRAY), &generic_array);
array_ array_
.register_marker_trait(self, poly(OUTPUT, vec![ty_tp(T.clone())])) .register_marker_trait(self, poly(OUTPUT, vec![ty_tp(T.clone())]))
@ -1240,11 +1240,16 @@ impl Context {
Predicate::le(var, N.clone() - value(1usize)), Predicate::le(var, N.clone() - value(1usize)),
); );
// __getitem__: |T, N|(self: [T; N], _: {I: Nat | I <= N}) -> T // __getitem__: |T, N|(self: [T; N], _: {I: Nat | I <= N}) -> T
let array_getitem_t = fn1_kw_met( // and (self: [T; N], _: Range(Int)) -> [T; _]
let array_getitem_t = (fn1_kw_met(
array_t(T.clone(), N.clone()), array_t(T.clone(), N.clone()),
anon(input.clone()), anon(input.clone()),
T.clone(), T.clone(),
) ) & fn1_kw_met(
array_t(T.clone(), N.clone()),
anon(poly(RANGE, vec![ty_tp(Int)])),
unknown_len_array_t(T.clone()),
))
.quantify(); .quantify();
let get_item = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new( let get_item = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
FUNDAMENTAL_GETITEM, FUNDAMENTAL_GETITEM,
@ -1330,6 +1335,12 @@ impl Context {
array_t(T.clone(), TyParam::erased(Nat)), array_t(T.clone(), TyParam::erased(Nat)),
); );
array_.register_py_builtin(FUNC_DEDUP, t.quantify(), Some(FUNC_DEDUP), 28); array_.register_py_builtin(FUNC_DEDUP, t.quantify(), Some(FUNC_DEDUP), 28);
/* Slice */
let mut slice = Self::builtin_mono_class(SLICE, 3);
slice.register_superclass(Obj, &obj);
slice.register_builtin_erg_impl(KW_START, Int, Immutable, Visibility::BUILTIN_PUBLIC);
slice.register_builtin_erg_impl(KW_STOP, Int, Immutable, Visibility::BUILTIN_PUBLIC);
slice.register_builtin_erg_impl(KW_STEP, Int, Immutable, Visibility::BUILTIN_PUBLIC);
/* GenericSet */ /* GenericSet */
let mut generic_set = Self::builtin_mono_class(GENERIC_SET, 1); let mut generic_set = Self::builtin_mono_class(GENERIC_SET, 1);
generic_set.register_superclass(Obj, &obj); generic_set.register_superclass(Obj, &obj);
@ -1534,6 +1545,29 @@ impl Context {
Str, Str,
); );
bytes.register_py_builtin(FUNC_DECODE, decode_t, Some(FUNC_DECODE), 6); bytes.register_py_builtin(FUNC_DECODE, decode_t, Some(FUNC_DECODE), 6);
let bytes_getitem_t = fn1_kw_met(mono(BYTES), kw(KW_IDX, Nat), Int)
& fn1_kw_met(
mono(BYTES),
kw(KW_IDX, poly(RANGE, vec![ty_tp(Int)])),
mono(BYTES),
);
bytes.register_builtin_erg_impl(
FUNDAMENTAL_GETITEM,
bytes_getitem_t,
Immutable,
Visibility::BUILTIN_PUBLIC,
);
bytes
.register_marker_trait(self, poly(INDEXABLE, vec![ty_tp(Nat), ty_tp(Int)]))
.unwrap();
let mut bytes_eq = Self::builtin_methods(Some(mono(EQ)), 2);
bytes_eq.register_builtin_erg_impl(
OP_EQ,
fn1_met(mono(BYTES), mono(BYTES), Bool),
Const,
Visibility::BUILTIN_PUBLIC,
);
bytes.register_trait(mono(BYTES), bytes_eq);
/* GenericTuple */ /* GenericTuple */
let mut generic_tuple = Self::builtin_mono_class(GENERIC_TUPLE, 1); let mut generic_tuple = Self::builtin_mono_class(GENERIC_TUPLE, 1);
generic_tuple.register_superclass(Obj, &obj); generic_tuple.register_superclass(Obj, &obj);
@ -2370,6 +2404,7 @@ impl Context {
Some(ARRAY), Some(ARRAY),
); );
self.register_builtin_type(arr_t, array_, vis.clone(), Const, Some(ARRAY)); self.register_builtin_type(arr_t, array_, vis.clone(), Const, Some(ARRAY));
self.register_builtin_type(mono(SLICE), slice, vis.clone(), Const, Some(FUNC_SLICE));
self.register_builtin_type( self.register_builtin_type(
mono(GENERIC_SET), mono(GENERIC_SET),
generic_set, generic_set,

View file

@ -242,7 +242,7 @@ pub(crate) fn structural_func(mut args: ValueArgs, ctx: &Context) -> EvalValueRe
pub(crate) fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> { pub(crate) fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = ctx let slf = ctx
.convert_value_into_array(args.remove_left_or_key("Self").unwrap()) .convert_value_into_array(args.remove_left_or_key("Self").unwrap())
.unwrap(); .unwrap_or_else(|err| panic!("{err}, {args}"));
let index = enum_unwrap!(args.remove_left_or_key("Index").unwrap(), ValueObj::Nat); let index = enum_unwrap!(args.remove_left_or_key("Index").unwrap(), ValueObj::Nat);
if let Some(v) = slf.get(index as usize) { if let Some(v) = slf.get(index as usize) {
Ok(v.clone()) Ok(v.clone())

View file

@ -220,6 +220,12 @@ impl Context {
) )
.quantify(); .quantify();
let t_round = nd_func(vec![kw(KW_NUMBER, Float)], None, Int); let t_round = nd_func(vec![kw(KW_NUMBER, Float)], None, Int);
let t_slice = func(
vec![kw(KW_START, Int)],
None,
vec![kw(KW_STOP, Int), kw(KW_STEP, Int)],
mono(SLICE),
);
let t_sorted = nd_func( let t_sorted = nd_func(
vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))], vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))],
None, None,
@ -393,6 +399,13 @@ impl Context {
vis.clone(), vis.clone(),
Some(FUNC_ROUND), Some(FUNC_ROUND),
); );
self.register_builtin_py_impl(
FUNC_SLICE,
t_slice,
Immutable,
vis.clone(),
Some(FUNC_SLICE),
);
self.register_builtin_py_impl( self.register_builtin_py_impl(
FUNC_SORTED, FUNC_SORTED,
t_sorted, t_sorted,

View file

@ -33,7 +33,10 @@ use crate::module::SharedCompilerResource;
use crate::ty::constructors::*; use crate::ty::constructors::*;
use crate::ty::free::Constraint; use crate::ty::free::Constraint;
use crate::ty::value::ValueObj; use crate::ty::value::ValueObj;
use crate::ty::{BuiltinConstSubr, ConstSubr, ParamTy, Predicate, TyParam, Type, Visibility}; use crate::ty::{
BuiltinConstSubr, ClosureData, ConstSubr, GenConstSubr, ParamTy, Predicate, TyParam, Type,
Visibility,
};
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind}; use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
use Mutability::*; use Mutability::*;
use ParamSpec as PS; use ParamSpec as PS;
@ -281,6 +284,7 @@ const NAMED_FUNC: &str = "NamedFunc";
const FUNC: &str = "Func"; const FUNC: &str = "Func";
const QUANTIFIED: &str = "Quantified"; const QUANTIFIED: &str = "Quantified";
const QUANTIFIED_FUNC: &str = "QuantifiedFunc"; const QUANTIFIED_FUNC: &str = "QuantifiedFunc";
const SLICE: &str = "Slice";
const FUNC_OBJECT: &str = "object"; const FUNC_OBJECT: &str = "object";
const FUNC_INT: &str = "int"; const FUNC_INT: &str = "int";
const FUNC_INT__: &str = "int__"; const FUNC_INT__: &str = "int__";
@ -332,6 +336,7 @@ const FUNC_POW: &str = "pow";
const FUNC_QUIT: &str = "quit"; const FUNC_QUIT: &str = "quit";
const FUNC_REPR: &str = "repr"; const FUNC_REPR: &str = "repr";
const FUNC_ROUND: &str = "round"; const FUNC_ROUND: &str = "round";
const FUNC_SLICE: &str = "slice";
const FUNC_SORTED: &str = "sorted"; const FUNC_SORTED: &str = "sorted";
const FUNC_SUM: &str = "sum"; const FUNC_SUM: &str = "sum";
const FUNC_IF: &str = "if"; const FUNC_IF: &str = "if";
@ -780,23 +785,53 @@ impl Context {
muty: Mutability, muty: Mutability,
py_name: Option<&'static str>, py_name: Option<&'static str>,
) { ) {
// FIXME: panic
if let Some((_, root_ctx)) = self.poly_types.get_mut(&t.local_name()) { if let Some((_, root_ctx)) = self.poly_types.get_mut(&t.local_name()) {
root_ctx.methods_list.push((ClassDefType::Simple(t), ctx)); root_ctx.methods_list.push((ClassDefType::Simple(t), ctx));
} else { } else {
let val = match ctx.kind { let ret_val = match ctx.kind {
ContextKind::Class => ValueObj::builtin_class(t.clone()), ContextKind::Class => ValueObj::builtin_class(t.clone()),
ContextKind::Trait => ValueObj::builtin_trait(t.clone()), ContextKind::Trait => ValueObj::builtin_trait(t.clone()),
_ => ValueObj::builtin_type(t.clone()), _ => ValueObj::builtin_type(t.clone()),
}; };
let qual_name = t.qual_name();
let name = VarName::from_str(t.local_name()); let name = VarName::from_str(t.local_name());
// e.g Array!: |T, N|(_: {T}, _: {N}) -> {Array!(T, N)} // e.g Array!: |T, N|(_: {T}, _:= {N}) -> {Array!(T, N)}
let params = t let nd_params = ctx
.typarams() .params_spec
.iter()
.filter_map(|ps| (!ps.has_default()).then_some(ParamTy::from(ps)))
.collect::<Vec<_>>();
let d_params = ctx
.params_spec
.iter()
.filter_map(|ps| ps.has_default().then_some(ParamTy::from(ps)))
.collect::<Vec<_>>();
let meta_t = func(
nd_params.clone(),
None,
d_params.clone(),
v_enum(set! { ret_val }),
)
.quantify();
let subr = move |data: ClosureData, args, _ctx: &Context| {
let passed = Vec::<TyParam>::from(args);
let lack = data.nd_params.len() + data.d_params.len() - passed.len();
let erased = data
.d_params
.clone()
.into_iter() .into_iter()
.map(|tp| ParamTy::Pos(tp_enum(self.get_tp_t(&tp).unwrap_or(Obj), set! { tp }))) .take(lack)
.collect(); .map(|pt| TyParam::erased(pt.typ().clone()));
let meta_t = func(params, None, vec![], v_enum(set! { val.clone() })).quantify(); let params = passed.into_iter().chain(erased).collect::<Vec<_>>();
Ok(ValueObj::builtin_type(poly(data.qual_name, params)))
};
let subr = ConstSubr::Gen(GenConstSubr::new(
t.local_name(),
ClosureData::new(nd_params, d_params, qual_name),
subr,
meta_t.clone(),
Some(t.clone()),
));
if ERG_MODE { if ERG_MODE {
self.locals.insert( self.locals.insert(
name.clone(), name.clone(),
@ -812,7 +847,7 @@ impl Context {
), ),
); );
} }
self.consts.insert(name.clone(), val); self.consts.insert(name.clone(), ValueObj::Subr(subr));
self.register_methods(&t, &ctx); self.register_methods(&t, &ctx);
self.poly_types.insert(name, (t, ctx)); self.poly_types.insert(name, (t, ctx));
} }

View file

@ -29,8 +29,8 @@ use Type::*;
use crate::context::instantiate_spec::ConstTemplate; use crate::context::instantiate_spec::ConstTemplate;
use crate::context::{Context, RegistrationMode, TraitImpl, TyVarCache, Variance}; use crate::context::{Context, RegistrationMode, TraitImpl, TyVarCache, Variance};
use crate::error::{ use crate::error::{
binop_to_dname, readable_name, unaryop_to_dname, SingleTyCheckResult, TyCheckError, binop_to_dname, ordinal_num, readable_name, unaryop_to_dname, SingleTyCheckResult,
TyCheckErrors, TyCheckResult, TyCheckError, TyCheckErrors, TyCheckResult,
}; };
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind}; use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
use crate::{feature_error, hir}; use crate::{feature_error, hir};
@ -831,10 +831,10 @@ impl Context {
if let Some(attr_name) = attr_name.as_ref() { if let Some(attr_name) = attr_name.as_ref() {
let mut vi = let mut vi =
self.search_method_info(obj, attr_name, pos_args, kw_args, input, namespace)?; self.search_method_info(obj, attr_name, pos_args, kw_args, input, namespace)?;
vi.t = self.resolve_overload(vi.t, pos_args, kw_args, attr_name)?; vi.t = self.resolve_overload(obj, vi.t, pos_args, kw_args, attr_name)?;
Ok(vi) Ok(vi)
} else { } else {
let t = self.resolve_overload(obj.t(), pos_args, kw_args, obj)?; let t = self.resolve_overload(obj, obj.t(), pos_args, kw_args, obj)?;
Ok(VarInfo { Ok(VarInfo {
t, t,
..VarInfo::default() ..VarInfo::default()
@ -844,6 +844,7 @@ impl Context {
fn resolve_overload( fn resolve_overload(
&self, &self,
obj: &hir::Expr,
instance: Type, instance: Type,
pos_args: &[hir::PosArg], pos_args: &[hir::PosArg],
kw_args: &[hir::KwArg], kw_args: &[hir::KwArg],
@ -853,7 +854,7 @@ impl Context {
if intersecs.len() == 1 { if intersecs.len() == 1 {
Ok(instance) Ok(instance)
} else { } else {
let input_t = subr_t( let mut input_t = subr_t(
SubrKind::Proc, SubrKind::Proc,
pos_args pos_args
.iter() .iter()
@ -867,6 +868,18 @@ impl Context {
Obj, Obj,
); );
for ty in intersecs.iter() { for ty in intersecs.iter() {
match (ty.is_method(), input_t.is_method()) {
(true, false) => {
let Type::Subr(sub) = &mut input_t else { unreachable!() };
sub.non_default_params
.insert(0, ParamTy::kw(Str::ever("self"), obj.t()));
}
(false, true) => {
let Type::Subr(sub) = &mut input_t else { unreachable!() };
sub.non_default_params.remove(0);
}
_ => {}
}
if self.subtype_of(ty, &input_t) { if self.subtype_of(ty, &input_t) {
return Ok(ty.clone()); return Ok(ty.clone());
} }
@ -1486,7 +1499,12 @@ impl Context {
let missing_params = subr let missing_params = subr
.non_default_params .non_default_params
.iter() .iter()
.map(|pt| pt.name().cloned().unwrap_or(Str::ever("_"))) .enumerate()
.map(|(i, pt)| {
pt.name().cloned().unwrap_or_else(|| {
Str::from(format!("({} param)", ordinal_num(i + 1)))
})
})
.filter(|pt| !passed_params.contains(pt)) .filter(|pt| !passed_params.contains(pt))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !missing_params.is_empty() { if !missing_params.is_empty() {
@ -1666,6 +1684,8 @@ impl Context {
} else { } else {
passed_params.insert(name.clone()); passed_params.insert(name.clone());
} }
} else {
passed_params.insert(Str::from(format!("({} param)", ordinal_num(nth))));
} }
self.sub_unify(arg_t, param_t, arg, param.name()) self.sub_unify(arg_t, param_t, arg, param.name())
.map_err(|errs| { .map_err(|errs| {

View file

@ -602,7 +602,25 @@ impl Context {
)?; )?;
} }
} }
_ => unreachable!(), Type::And(l, r) => {
if let Some(self_t) = l.self_t() {
self.sub_unify(
callee.ref_t(),
self_t,
callee,
Some(&Str::ever("self")),
)?;
}
if let Some(self_t) = r.self_t() {
self.sub_unify(
callee.ref_t(),
self_t,
callee,
Some(&Str::ever("self")),
)?;
}
}
other => unreachable!("{other}"),
} }
Ok(t) Ok(t)
} }

View file

@ -42,6 +42,7 @@ use crate::module::{
}; };
use crate::ty::value::ValueObj; use crate::ty::value::ValueObj;
use crate::ty::GuardType; use crate::ty::GuardType;
use crate::ty::ParamTy;
use crate::ty::{Predicate, Type, Visibility, VisibilityModifier}; use crate::ty::{Predicate, Type, Visibility, VisibilityModifier};
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind}; use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
use Type::*; use Type::*;
@ -233,6 +234,16 @@ impl ParamSpec {
) )
} }
pub fn default<S: Into<Str>>(name: S, t: Type) -> Self {
Self::new(
Some(name),
t,
false,
DefaultInfo::WithDefault,
AbsLocation::unknown(),
)
}
pub fn t<S: Into<Str>>(name: S, is_var_params: bool, default: DefaultInfo) -> Self { pub fn t<S: Into<Str>>(name: S, is_var_params: bool, default: DefaultInfo) -> Self {
Self::new( Self::new(
Some(name), Some(name),
@ -252,6 +263,20 @@ impl ParamSpec {
AbsLocation::unknown(), AbsLocation::unknown(),
) )
} }
pub fn has_default(&self) -> bool {
self.default_info.has_default()
}
}
impl From<&ParamSpec> for ParamTy {
fn from(param: &ParamSpec) -> Self {
if let Some(name) = &param.name {
ParamTy::kw(name.clone(), param.t.clone())
} else {
ParamTy::Pos(param.t.clone())
}
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -434,6 +459,7 @@ pub struct Context {
/// => locals: {"x": T, "y": T} /// => locals: {"x": T, "y": T}
/// TODO: impl params desugaring and replace to `Dict` /// TODO: impl params desugaring and replace to `Dict`
pub(crate) params: Vec<(Option<VarName>, VarInfo)>, pub(crate) params: Vec<(Option<VarName>, VarInfo)>,
pub(crate) params_spec: Vec<ParamSpec>,
pub(crate) locals: Dict<VarName, VarInfo>, pub(crate) locals: Dict<VarName, VarInfo>,
pub(crate) consts: Dict<VarName, ValueObj>, pub(crate) consts: Dict<VarName, ValueObj>,
// {"Nat": ctx, "Int": ctx, ...} // {"Nat": ctx, "Int": ctx, ...}
@ -589,7 +615,7 @@ impl Context {
level: usize, level: usize,
) -> Self { ) -> Self {
let mut params_ = Vec::new(); let mut params_ = Vec::new();
for param in params.into_iter() { for param in params.clone().into_iter() {
let id = DefId(get_hash(&(&name, &param))); let id = DefId(get_hash(&(&name, &param)));
if let Some(name) = param.name { if let Some(name) = param.name {
let kind = VarKind::parameter(id, param.is_var_params, param.default_info); let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
@ -635,6 +661,7 @@ impl Context {
method_to_classes: Dict::default(), method_to_classes: Dict::default(),
method_impl_patches: Dict::default(), method_impl_patches: Dict::default(),
params: params_, params: params_,
params_spec: params,
decls: Dict::default(), decls: Dict::default(),
future_defined_locals: Dict::default(), future_defined_locals: Dict::default(),
deleted_locals: Dict::default(), deleted_locals: Dict::default(),

View file

@ -5,6 +5,7 @@ use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{enum_unwrap, fn_name, log, set, Str, Triple}; use erg_common::{enum_unwrap, fn_name, log, set, Str, Triple};
use erg_parser::ast::{self, AscriptionKind, Identifier, VarName, AST}; use erg_parser::ast::{self, AscriptionKind, Identifier, VarName, AST};
use erg_parser::desugar::Desugarer;
use crate::context::instantiate::TyVarCache; use crate::context::instantiate::TyVarCache;
use crate::lower::ASTLowerer; use crate::lower::ASTLowerer;
@ -332,7 +333,7 @@ impl ASTLowerer {
fn fake_lower_record(&self, rec: ast::Record) -> LowerResult<hir::Record> { fn fake_lower_record(&self, rec: ast::Record) -> LowerResult<hir::Record> {
let rec = match rec { let rec = match rec {
ast::Record::Normal(rec) => rec, ast::Record::Normal(rec) => rec,
ast::Record::Mixed(_mixed) => unreachable!(), ast::Record::Mixed(mixed) => Desugarer::desugar_shortened_record_inner(mixed),
}; };
let mut elems = Vec::new(); let mut elems = Vec::new();
for elem in rec.attrs.into_iter() { for elem in rec.attrs.into_iter() {

View file

@ -10,7 +10,7 @@ The name of the operating system dependent module imported. The following names
.chdir!: (path: PathLike, ) => NoneType .chdir!: (path: PathLike, ) => NoneType
.chmod!: (path: PathLike, mode: Nat) => NoneType .chmod!: (path: PathLike, mode: Nat) => NoneType
.getcwd!: () => Str .getcwd!: () => Str
.getenv!: (key: Str, default: Str or NoneType := NoneType) => Str .getenv!: |D <: Str or NoneType|(key: Str, default: D := NoneType) => Str or D
.listdir!: (path := PathLike,) => [Str; _] .listdir!: (path := PathLike,) => [Str; _]
.mkdir!: (path: PathLike, mode := Nat) => NoneType .mkdir!: (path: PathLike, mode := Nat) => NoneType
.putenv!: (key: Str, value: Str) => NoneType .putenv!: (key: Str, value: Str) => NoneType

View file

@ -1,4 +1,5 @@
from _erg_control import then__ from _erg_control import then__
from _erg_range import Range
class Array(list): class Array(list):
def dedup(self, same_bucket=None): def dedup(self, same_bucket=None):
@ -24,3 +25,11 @@ class Array(list):
def __mul__(self, n): def __mul__(self, n):
return then__(list.__mul__(self, n), Array) return then__(list.__mul__(self, n), Array)
def __getitem__(self, index_or_slice):
if isinstance(index_or_slice, slice):
return Array(list.__getitem__(self, index_or_slice))
elif isinstance(index_or_slice, Range):
return Array(list.__getitem__(self, index_or_slice.into_slice()))
else:
return list.__getitem__(self, index_or_slice)

View file

@ -1,3 +1,12 @@
class Bytes(bytes): class Bytes(bytes):
def try_new(*b): # -> Result[Nat] def try_new(*b): # -> Result[Nat]
return Bytes(bytes(*b)) return Bytes(bytes(*b))
def __getitem__(self, index_or_slice):
from _erg_range import Range
if isinstance(index_or_slice, slice):
return Bytes(bytes.__getitem__(self, index_or_slice))
elif isinstance(index_or_slice, Range):
return Bytes(bytes.__getitem__(self, index_or_slice.into_slice()))
else:
return bytes.__getitem__(self, index_or_slice)

View file

@ -12,6 +12,13 @@ class Range:
def __contains__(self, item): def __contains__(self, item):
pass pass
@staticmethod
def from_slice(slice):
pass
def into_slice(self):
pass
def __getitem__(self, item): def __getitem__(self, item):
res = self.start + item res = self.start + item
if res in self: if res in self:
@ -56,6 +63,13 @@ class RightOpenRange(Range):
def __contains__(self, item): def __contains__(self, item):
return self.start <= item < self.end return self.start <= item < self.end
@staticmethod
def from_slice(slice):
return Range(slice.start, slice.stop)
def into_slice(self):
return slice(self.start, self.end)
# represents `start<..<end` # represents `start<..<end`
class OpenRange(Range): class OpenRange(Range):
@ -68,6 +82,13 @@ class ClosedRange(Range):
def __contains__(self, item): def __contains__(self, item):
return self.start <= item <= self.end return self.start <= item <= self.end
@staticmethod
def from_slice(slice):
return Range(slice.start, slice.stop - 1)
def into_slice(self):
return slice(self.start, self.end + 1)
class RangeIterator: class RangeIterator:
def __init__(self, rng): def __init__(self, rng):

View file

@ -2,7 +2,6 @@ from _erg_result import Error
from _erg_int import Int from _erg_int import Int
from _erg_control import then__ from _erg_control import then__
class Str(str): class Str(str):
def __instancecheck__(cls, obj): def __instancecheck__(cls, obj):
return isinstance(obj, str) return isinstance(obj, str)
@ -40,6 +39,15 @@ class Str(str):
def __mod__(self, other): def __mod__(self, other):
return then__(str.__mod__(other, self), Str) return then__(str.__mod__(other, self), Str)
def __getitem__(self, index_or_slice):
from _erg_range import Range
if isinstance(index_or_slice, slice):
return Str(str.__getitem__(self, index_or_slice))
elif isinstance(index_or_slice, Range):
return Str(str.__getitem__(self, index_or_slice.into_slice()))
else:
return str.__getitem__(self, index_or_slice)
class StrMut: # Inherits Str class StrMut: # Inherits Str
value: Str value: Str

View file

@ -18,6 +18,7 @@ use erg_common::{fmt_option, fn_name, log, switch_lang, Str};
use erg_parser::ast::{self, AscriptionKind, VisModifierSpec}; use erg_parser::ast::{self, AscriptionKind, VisModifierSpec};
use erg_parser::ast::{OperationKind, TypeSpecWithOp, VarName, AST}; use erg_parser::ast::{OperationKind, TypeSpecWithOp, VarName, AST};
use erg_parser::build_ast::ASTBuilder; use erg_parser::build_ast::ASTBuilder;
use erg_parser::desugar::Desugarer;
use erg_parser::token::{Token, TokenKind}; use erg_parser::token::{Token, TokenKind};
use erg_parser::Parser; use erg_parser::Parser;
@ -381,7 +382,9 @@ impl ASTLowerer {
log!(info "entered {}({record})", fn_name!()); log!(info "entered {}({record})", fn_name!());
match record { match record {
ast::Record::Normal(rec) => self.lower_normal_record(rec), ast::Record::Normal(rec) => self.lower_normal_record(rec),
ast::Record::Mixed(_rec) => unreachable!(), // should be desugared ast::Record::Mixed(mixed) => {
self.lower_normal_record(Desugarer::desugar_shortened_record_inner(mixed))
}
} }
} }

View file

@ -52,7 +52,7 @@ impl SharedCompilerResource {
pub fn inherit(&self, path: PathBuf) -> Self { pub fn inherit(&self, path: PathBuf) -> Self {
let mut _self = self.clone(); let mut _self = self.clone();
_self.promises.path = path; _self.promises.path = path.into();
_self _self
} }

View file

@ -1,10 +1,11 @@
use std::fmt; use std::fmt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use erg_common::pathutil::NormalizedPathBuf;
use erg_common::set;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::shared::{MappedRwLockReadGuard, RwLockReadGuard, Shared}; use erg_common::shared::{MappedRwLockReadGuard, RwLockReadGuard, Shared};
use erg_common::tsort::{tsort, Graph, Node, TopoSortError}; use erg_common::tsort::{tsort, Graph, Node, TopoSortError};
use erg_common::{normalize_path, set};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IncRefError { pub enum IncRefError {
@ -18,7 +19,7 @@ impl IncRefError {
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ModuleGraph(Graph<PathBuf, ()>); pub struct ModuleGraph(Graph<NormalizedPathBuf, ()>);
impl fmt::Display for ModuleGraph { impl fmt::Display for ModuleGraph {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -35,7 +36,7 @@ impl fmt::Display for ModuleGraph {
} }
impl IntoIterator for ModuleGraph { impl IntoIterator for ModuleGraph {
type Item = Node<PathBuf, ()>; type Item = Node<NormalizedPathBuf, ()>;
type IntoIter = std::vec::IntoIter<Self::Item>; type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
@ -48,20 +49,45 @@ impl ModuleGraph {
Self(Graph::new()) Self(Graph::new())
} }
pub fn get_node(&self, path: &Path) -> Option<&Node<PathBuf, ()>> { pub fn get_node(&self, path: &Path) -> Option<&Node<NormalizedPathBuf, ()>> {
let path = normalize_path(path.to_path_buf()); let path = NormalizedPathBuf::new(path.to_path_buf());
self.0.iter().find(|n| n.id == path) self.0.iter().find(|n| n.id == path)
} }
fn parents(&self, path: &Path) -> Option<&Set<PathBuf>> { /// if `path` depends on `target`, returns `true`, else `false`.
let path = normalize_path(path.to_path_buf()); /// if `path` not found, returns `false`
pub fn depends_on(&self, path: &Path, target: &Path) -> bool {
let path = NormalizedPathBuf::new(path.to_path_buf());
let target = NormalizedPathBuf::new(target.to_path_buf());
self.0
.iter()
.find(|n| n.id == path)
.map(|n| n.depends_on.contains(&target))
.unwrap_or(false)
}
pub fn children(&self, path: &Path) -> Set<NormalizedPathBuf> {
let path = NormalizedPathBuf::new(path.to_path_buf());
self.0
.iter()
.filter(|n| n.depends_on.contains(&path))
.map(|n| n.id.clone())
.collect()
}
fn parents(&self, path: &Path) -> Option<&Set<NormalizedPathBuf>> {
let path = NormalizedPathBuf::new(path.to_path_buf());
self.0.iter().find(|n| n.id == path).map(|n| &n.depends_on) self.0.iter().find(|n| n.id == path).map(|n| &n.depends_on)
} }
pub fn ancestors(&self, path: &Path) -> Set<PathBuf> { /// ```erg
let path = normalize_path(path.to_path_buf()); /// # a.er
/// b = import "b"
/// ```
/// -> a: child, b: parent
pub fn ancestors(&self, path: &Path) -> Set<NormalizedPathBuf> {
let mut ancestors = set! {}; let mut ancestors = set! {};
if let Some(parents) = self.parents(&path) { if let Some(parents) = self.parents(path) {
for parent in parents.iter() { for parent in parents.iter() {
ancestors.insert(parent.clone()); ancestors.insert(parent.clone());
ancestors.extend(self.ancestors(parent)); ancestors.extend(self.ancestors(parent));
@ -71,7 +97,7 @@ impl ModuleGraph {
} }
pub fn add_node_if_none(&mut self, path: &Path) { pub fn add_node_if_none(&mut self, path: &Path) {
let path = normalize_path(path.to_path_buf()); let path = NormalizedPathBuf::new(path.to_path_buf());
if self.0.iter().all(|n| n.id != path) { if self.0.iter().all(|n| n.id != path) {
let node = Node::new(path, (), set! {}); let node = Node::new(path, (), set! {});
self.0.push(node); self.0.push(node);
@ -80,8 +106,8 @@ impl ModuleGraph {
/// returns Err (and do nothing) if this operation makes a cycle /// returns Err (and do nothing) if this operation makes a cycle
pub fn inc_ref(&mut self, referrer: &Path, depends_on: PathBuf) -> Result<(), IncRefError> { pub fn inc_ref(&mut self, referrer: &Path, depends_on: PathBuf) -> Result<(), IncRefError> {
let referrer = normalize_path(referrer.to_path_buf()); let referrer = NormalizedPathBuf::new(referrer.to_path_buf());
let depends_on = normalize_path(depends_on); let depends_on = NormalizedPathBuf::new(depends_on);
if self.ancestors(&depends_on).contains(&referrer) && referrer != depends_on { if self.ancestors(&depends_on).contains(&referrer) && referrer != depends_on {
return Err(IncRefError::CycleDetected); return Err(IncRefError::CycleDetected);
} }
@ -96,7 +122,7 @@ impl ModuleGraph {
Ok(()) Ok(())
} }
pub fn iter(&self) -> impl Iterator<Item = &Node<PathBuf, ()>> { pub fn iter(&self) -> impl Iterator<Item = &Node<NormalizedPathBuf, ()>> {
self.0.iter() self.0.iter()
} }
@ -112,13 +138,13 @@ impl ModuleGraph {
} }
pub fn remove(&mut self, path: &Path) { pub fn remove(&mut self, path: &Path) {
let path = normalize_path(path.to_path_buf()); let path = NormalizedPathBuf::new(path.to_path_buf());
self.0.retain(|n| n.id != path); self.0.retain(|n| n.id != path);
} }
pub fn rename_path(&mut self, old: &Path, new: PathBuf) { pub fn rename_path(&mut self, old: &Path, new: PathBuf) {
let old = normalize_path(old.to_path_buf()); let old = NormalizedPathBuf::new(old.to_path_buf());
let new = normalize_path(new); let new = NormalizedPathBuf::new(new);
for node in self.0.iter_mut() { for node in self.0.iter_mut() {
if node.id == old { if node.id == old {
node.id = new.clone(); node.id = new.clone();
@ -145,7 +171,7 @@ impl fmt::Display for SharedModuleGraph {
} }
impl IntoIterator for SharedModuleGraph { impl IntoIterator for SharedModuleGraph {
type Item = Node<PathBuf, ()>; type Item = Node<NormalizedPathBuf, ()>;
type IntoIter = std::vec::IntoIter<Self::Item>; type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
@ -158,7 +184,10 @@ impl SharedModuleGraph {
Self(Shared::new(ModuleGraph::new())) Self(Shared::new(ModuleGraph::new()))
} }
pub fn get_node(&self, path: &Path) -> Option<MappedRwLockReadGuard<Node<PathBuf, ()>>> { pub fn get_node(
&self,
path: &Path,
) -> Option<MappedRwLockReadGuard<Node<NormalizedPathBuf, ()>>> {
if self.0.borrow().get_node(path).is_some() { if self.0.borrow().get_node(path).is_some() {
Some(RwLockReadGuard::map(self.0.borrow(), |graph| { Some(RwLockReadGuard::map(self.0.borrow(), |graph| {
graph.get_node(path).unwrap() graph.get_node(path).unwrap()
@ -168,7 +197,15 @@ impl SharedModuleGraph {
} }
} }
pub fn ancestors(&self, path: &Path) -> Set<PathBuf> { pub fn depends_on(&self, path: &Path, target: &Path) -> bool {
self.0.borrow().depends_on(path, target)
}
pub fn children(&self, path: &Path) -> Set<NormalizedPathBuf> {
self.0.borrow().children(path)
}
pub fn ancestors(&self, path: &Path) -> Set<NormalizedPathBuf> {
self.0.borrow().ancestors(path) self.0.borrow().ancestors(path)
} }

View file

@ -68,7 +68,7 @@ impl Promise {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct SharedPromises { pub struct SharedPromises {
graph: SharedModuleGraph, graph: SharedModuleGraph,
pub(crate) path: PathBuf, pub(crate) path: NormalizedPathBuf,
promises: Shared<Dict<NormalizedPathBuf, Promise>>, promises: Shared<Dict<NormalizedPathBuf, Promise>>,
} }
@ -86,7 +86,7 @@ impl SharedPromises {
pub fn new(graph: SharedModuleGraph, path: PathBuf) -> Self { pub fn new(graph: SharedModuleGraph, path: PathBuf) -> Self {
Self { Self {
graph, graph,
path, path: NormalizedPathBuf::new(path),
promises: Shared::new(Dict::new()), promises: Shared::new(Dict::new()),
} }
} }
@ -125,6 +125,26 @@ impl SharedPromises {
Promise::Running { parent, handle }; Promise::Running { parent, handle };
return Ok(()); return Ok(());
} }
// Suppose A depends on B and C, and B depends on C.
// In this case, B must join C before A joins C. Otherwise, a deadlock will occur.
let children = self.graph.children(path);
for child in children.iter() {
if child == &self.path {
continue;
} else if self.graph.depends_on(&self.path, child) {
*self.promises.borrow_mut().get_mut(path).unwrap() =
Promise::Running { parent, handle };
while self
.promises
.borrow()
.get(path)
.is_some_and(|p| !p.is_finished())
{
std::thread::yield_now();
}
return Ok(());
}
}
let res = handle.join(); let res = handle.join();
*self.promises.borrow_mut().get_mut(path).unwrap() = Promise::Finished; *self.promises.borrow_mut().get_mut(path).unwrap() = Promise::Finished;
res res

View file

@ -8,7 +8,7 @@ use erg_common::impl_display_from_debug;
#[allow(unused_imports)] #[allow(unused_imports)]
use erg_common::log; use erg_common::log;
use erg_common::opcode::CommonOpcode; use erg_common::opcode::CommonOpcode;
use erg_common::opcode308::Opcode308; use erg_common::opcode309::Opcode309;
use erg_common::opcode310::Opcode310; use erg_common::opcode310::Opcode310;
use erg_common::opcode311::{BinOpCode, Opcode311}; use erg_common::opcode311::{BinOpCode, Opcode311};
use erg_common::python_util::{env_magic_number, PythonVersion}; use erg_common::python_util::{env_magic_number, PythonVersion};
@ -36,20 +36,20 @@ pub fn consts_into_bytes(consts: Vec<ValueObj>, python_ver: PythonVersion) -> Ve
pub fn jump_abs_addr(minor_ver: u8, op: u8, idx: usize, arg: usize) -> usize { pub fn jump_abs_addr(minor_ver: u8, op: u8, idx: usize, arg: usize) -> usize {
match minor_ver { match minor_ver {
7 | 8 => jump_abs_addr_308(Opcode308::from(op), idx, arg), 7 | 8 | 9 => jump_abs_addr_309(Opcode309::from(op), idx, arg),
9 | 10 => jump_abs_addr_310(Opcode310::from(op), idx, arg), 10 => jump_abs_addr_310(Opcode310::from(op), idx, arg),
11 => jump_abs_addr_311(Opcode311::from(op), idx, arg), 11 => jump_abs_addr_311(Opcode311::from(op), idx, arg),
n => todo!("unsupported version: {n}"), n => todo!("unsupported version: {n}"),
} }
} }
fn jump_abs_addr_308(op: Opcode308, idx: usize, arg: usize) -> usize { fn jump_abs_addr_309(op: Opcode309, idx: usize, arg: usize) -> usize {
match op { match op {
Opcode308::FOR_ITER | Opcode308::JUMP_FORWARD => idx + arg * 2 + 2, Opcode309::FOR_ITER | Opcode309::JUMP_FORWARD => idx + arg + 2,
Opcode308::JUMP_ABSOLUTE | Opcode308::POP_JUMP_IF_FALSE | Opcode308::POP_JUMP_IF_TRUE => { Opcode309::JUMP_ABSOLUTE | Opcode309::POP_JUMP_IF_FALSE | Opcode309::POP_JUMP_IF_TRUE => {
arg * 2 arg
} }
Opcode308::SETUP_WITH => idx + arg * 2 + 2, Opcode309::SETUP_WITH => idx + arg + 2,
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -541,10 +541,8 @@ impl CodeObj {
arg arg
}; };
match py_ver.and_then(|pv| pv.minor) { match py_ver.and_then(|pv| pv.minor) {
Some(7) => self.read_instr_307(op, arg, idx, &mut instrs), Some(7 | 8 | 9) => self.read_instr_309(op, arg, idx, &mut instrs),
Some(8) => self.read_instr_308(op, arg, idx, &mut instrs), Some(10) => self.read_instr_310(op, arg, idx, &mut instrs),
// Some(9) => self.read_instr_309(op, arg, idx, &mut instrs),
Some(9 | 10) => self.read_instr_310(op, arg, idx, &mut instrs),
Some(11) => self.read_instr_311(op, arg, idx, &mut instrs), Some(11) => self.read_instr_311(op, arg, idx, &mut instrs),
_ => {} _ => {}
} }
@ -557,78 +555,34 @@ impl CodeObj {
instrs instrs
} }
fn read_instr_307(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) { fn read_instr_309(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) {
let op307 = Opcode308::from(*op); let op309 = Opcode309::from(*op);
let s_op = op307.to_string(); let s_op = op309.to_string();
write!(instrs, "{idx:>15} {s_op:<25}").unwrap(); write!(instrs, "{idx:>15} {s_op:<25}").unwrap();
if let Ok(op) = CommonOpcode::try_from(*op) { if let Ok(op) = CommonOpcode::try_from(*op) {
self.dump_additional_info(op, arg, idx, instrs); self.dump_additional_info(op, arg, idx, instrs);
} }
match op307 { match op309 {
Opcode308::STORE_DEREF | Opcode308::LOAD_DEREF => { Opcode309::STORE_DEREF | Opcode309::LOAD_DEREF => {
write!(instrs, "{arg} ({})", self.freevars.get(arg).unwrap()).unwrap(); write!(instrs, "{arg} ({})", self.freevars.get(arg).unwrap()).unwrap();
} }
Opcode308::LOAD_CLOSURE => { Opcode309::LOAD_CLOSURE => {
write!(instrs, "{arg} ({})", self.cellvars.get(arg).unwrap()).unwrap(); write!(instrs, "{arg} ({})", self.cellvars.get(arg).unwrap()).unwrap();
} }
Opcode308::JUMP_ABSOLUTE => { Opcode309::JUMP_ABSOLUTE => {
write!(instrs, "{arg} (to {})", arg).unwrap(); write!(instrs, "{arg} (to {})", arg).unwrap();
} }
Opcode308::JUMP_FORWARD => { Opcode309::JUMP_FORWARD => {
write!(instrs, "{arg} (to {})", idx + arg + 2).unwrap(); write!(instrs, "{arg} (to {})", idx + arg + 2).unwrap();
} }
Opcode308::POP_JUMP_IF_FALSE | Opcode308::POP_JUMP_IF_TRUE => { // REVIEW: *2?
Opcode309::POP_JUMP_IF_FALSE | Opcode309::POP_JUMP_IF_TRUE => {
write!(instrs, "{arg} (to {})", arg).unwrap(); write!(instrs, "{arg} (to {})", arg).unwrap();
} }
Opcode308::CALL_FUNCTION Opcode309::BINARY_ADD
| Opcode308::CALL_FUNCTION_EX | Opcode309::BINARY_SUBTRACT
| Opcode308::CALL_FUNCTION_KW => { | Opcode309::BINARY_MULTIPLY
write!(instrs, "{arg}").unwrap(); | Opcode309::BINARY_TRUE_DIVIDE => {
}
Opcode308::BINARY_ADD
| Opcode308::BINARY_SUBTRACT
| Opcode308::BINARY_MULTIPLY
| Opcode308::BINARY_TRUE_DIVIDE => {
write!(instrs, "{arg} ({:?})", TypePair::from(arg as u8)).unwrap();
}
_ => {}
}
instrs.push('\n');
}
fn read_instr_308(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) {
let op308 = Opcode308::from(*op);
let s_op = op308.to_string();
write!(instrs, "{idx:>15} {s_op:<25}").unwrap();
if let Ok(op) = CommonOpcode::try_from(*op) {
self.dump_additional_info(op, arg, idx, instrs);
}
match op308 {
Opcode308::STORE_DEREF | Opcode308::LOAD_DEREF => {
write!(instrs, "{arg} ({})", self.freevars.get(arg).unwrap()).unwrap();
}
Opcode308::LOAD_CLOSURE => {
write!(instrs, "{arg} ({})", self.cellvars.get(arg).unwrap()).unwrap();
}
Opcode308::JUMP_ABSOLUTE => {
write!(instrs, "{arg} (to {})", arg * 2).unwrap();
}
Opcode308::JUMP_FORWARD => {
write!(instrs, "{arg} (to {})", idx + arg * 2 + 2).unwrap();
}
// REVIEW: *2?
Opcode308::POP_JUMP_IF_FALSE | Opcode308::POP_JUMP_IF_TRUE => {
write!(instrs, "{arg} (to {})", arg * 2).unwrap();
}
Opcode308::CALL_FUNCTION
| Opcode308::CALL_FUNCTION_EX
| Opcode308::CALL_FUNCTION_KW => {
write!(instrs, "{arg}").unwrap();
}
Opcode308::BINARY_ADD
| Opcode308::BINARY_SUBTRACT
| Opcode308::BINARY_MULTIPLY
| Opcode308::BINARY_TRUE_DIVIDE => {
write!(instrs, "{arg} ({:?})", TypePair::from(arg as u8)).unwrap(); write!(instrs, "{arg} ({:?})", TypePair::from(arg as u8)).unwrap();
} }
_ => {} _ => {}

View file

@ -5,20 +5,20 @@ use erg_common::dict::Dict;
use erg_common::log; use erg_common::log;
use erg_common::Str; use erg_common::Str;
use erg_parser::ast::{ConstBlock, Params}; use erg_parser::ast::{Block, ConstBlock, Params};
use super::constructors::subr_t; use super::constructors::subr_t;
use super::value::{EvalValueResult, ValueObj}; use super::value::{EvalValueResult, ValueObj};
use super::{Predicate, Type}; use super::{ParamTy, Predicate, TyParam, Type};
use crate::context::Context; use crate::context::Context;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UserConstSubr { pub struct UserConstSubr {
name: Str, pub name: Str,
params: Params, pub(crate) params: Params,
block: ConstBlock, pub(crate) block: ConstBlock,
sig_t: Type, pub(crate) sig_t: Type,
} }
impl UserConstSubr { impl UserConstSubr {
@ -30,6 +30,10 @@ impl UserConstSubr {
sig_t, sig_t,
} }
} }
pub fn block(self) -> Block {
self.block.downgrade()
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -38,6 +42,26 @@ pub struct ValueArgs {
pub kw_args: Dict<Str, ValueObj>, pub kw_args: Dict<Str, ValueObj>,
} }
impl fmt::Display for ValueArgs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut args = Vec::new();
for arg in &self.pos_args {
args.push(arg.to_string());
}
for (key, arg) in self.kw_args.iter() {
args.push(format!("{key} := {arg}"));
}
write!(f, "({})", args.join(", "))
}
}
impl From<ValueArgs> for Vec<TyParam> {
fn from(args: ValueArgs) -> Self {
// TODO: kw_args
args.pos_args.into_iter().map(TyParam::Value).collect()
}
}
impl ValueArgs { impl ValueArgs {
pub const fn new(pos_args: Vec<ValueObj>, kw_args: Dict<Str, ValueObj>) -> Self { pub const fn new(pos_args: Vec<ValueObj>, kw_args: Dict<Str, ValueObj>) -> Self {
ValueArgs { pos_args, kw_args } ValueArgs { pos_args, kw_args }
@ -54,7 +78,7 @@ impl ValueArgs {
#[derive(Clone)] #[derive(Clone)]
pub struct BuiltinConstSubr { pub struct BuiltinConstSubr {
name: &'static str, name: Str,
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>, subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
sig_t: Type, sig_t: Type,
as_type: Option<Type>, as_type: Option<Type>,
@ -91,14 +115,14 @@ impl fmt::Display for BuiltinConstSubr {
} }
impl BuiltinConstSubr { impl BuiltinConstSubr {
pub const fn new( pub fn new<S: Into<Str>>(
name: &'static str, name: S,
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>, subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
sig_t: Type, sig_t: Type,
as_type: Option<Type>, as_type: Option<Type>,
) -> Self { ) -> Self {
Self { Self {
name, name: name.into(),
subr, subr,
sig_t, sig_t,
as_type, as_type,
@ -110,10 +134,90 @@ impl BuiltinConstSubr {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ClosureData {
pub(crate) nd_params: Vec<ParamTy>,
pub(crate) d_params: Vec<ParamTy>,
pub(crate) qual_name: Str,
}
impl ClosureData {
pub const fn new(nd_params: Vec<ParamTy>, d_params: Vec<ParamTy>, qual_name: Str) -> Self {
Self {
nd_params,
d_params,
qual_name,
}
}
}
#[allow(clippy::type_complexity)]
#[derive(Clone)]
pub struct GenConstSubr {
name: Str,
data: ClosureData,
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<ValueObj>,
sig_t: Type,
as_type: Option<Type>,
}
impl std::fmt::Debug for GenConstSubr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BuiltinConstSubr")
.field("name", &self.name)
.field("sig_t", &self.sig_t)
.field("as_type", &self.as_type)
.finish()
}
}
impl PartialEq for GenConstSubr {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for GenConstSubr {}
impl std::hash::Hash for GenConstSubr {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl fmt::Display for GenConstSubr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<const subroutine '{}'>", self.name)
}
}
impl GenConstSubr {
pub fn new<S: Into<Str>>(
name: S,
data: ClosureData,
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<ValueObj>,
sig_t: Type,
as_type: Option<Type>,
) -> Self {
Self {
name: name.into(),
data,
subr,
sig_t,
as_type,
}
}
pub fn call(&self, args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
(self.subr)(self.data.clone(), args, ctx)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ConstSubr { pub enum ConstSubr {
User(UserConstSubr), User(UserConstSubr),
Builtin(BuiltinConstSubr), Builtin(BuiltinConstSubr),
Gen(GenConstSubr),
} }
impl fmt::Display for ConstSubr { impl fmt::Display for ConstSubr {
@ -123,6 +227,7 @@ impl fmt::Display for ConstSubr {
write!(f, "<user-defined const subroutine '{}'>", subr.name) write!(f, "<user-defined const subroutine '{}'>", subr.name)
} }
ConstSubr::Builtin(subr) => write!(f, "{subr}"), ConstSubr::Builtin(subr) => write!(f, "{subr}"),
ConstSubr::Gen(subr) => write!(f, "{subr}"),
} }
} }
} }
@ -132,10 +237,10 @@ impl ConstSubr {
match self { match self {
ConstSubr::User(user) => &user.sig_t, ConstSubr::User(user) => &user.sig_t,
ConstSubr::Builtin(builtin) => &builtin.sig_t, ConstSubr::Builtin(builtin) => &builtin.sig_t,
ConstSubr::Gen(gen) => &gen.sig_t,
} }
} }
/// ConstSubr{sig_t: Int -> {Int}, ..}.as_type() == Int -> Int
pub fn as_type(&self, ctx: &Context) -> Option<Type> { pub fn as_type(&self, ctx: &Context) -> Option<Type> {
match self { match self {
ConstSubr::User(user) => { ConstSubr::User(user) => {
@ -170,6 +275,7 @@ impl ConstSubr {
None None
} }
ConstSubr::Builtin(builtin) => builtin.as_type.clone(), ConstSubr::Builtin(builtin) => builtin.as_type.clone(),
ConstSubr::Gen(gen) => gen.as_type.clone(),
} }
} }
} }

View file

@ -47,6 +47,7 @@ use self::constructors::subr_t;
pub const STR_OMIT_THRESHOLD: usize = 16; pub const STR_OMIT_THRESHOLD: usize = 16;
pub const CONTAINER_OMIT_THRESHOLD: usize = 8; pub const CONTAINER_OMIT_THRESHOLD: usize = 8;
pub const DEFAULT_PARAMS_THRESHOLD: usize = 5;
/// cloneのコストがあるためなるべく.ref_tを使うようにすること /// cloneのコストがあるためなるべく.ref_tを使うようにすること
/// いくつかの構造体は直接Typeを保持していないので、その場合は.tを使う /// いくつかの構造体は直接Typeを保持していないので、その場合は.tを使う
@ -315,6 +316,10 @@ impl LimitedDisplay for SubrType {
var_params.typ().limited_fmt(f, limit - 1)?; var_params.typ().limited_fmt(f, limit - 1)?;
} }
for (i, pt) in self.default_params.iter().enumerate() { for (i, pt) in self.default_params.iter().enumerate() {
if limit.is_positive() && i >= DEFAULT_PARAMS_THRESHOLD {
write!(f, ", ...")?;
break;
}
if i > 0 || !self.non_default_params.is_empty() || self.var_params.is_some() { if i > 0 || !self.non_default_params.is_empty() || self.var_params.is_some() {
write!(f, ", ")?; write!(f, ", ")?;
} }
@ -1712,6 +1717,7 @@ impl Type {
Self::Quantified(t) => t.is_procedure(), Self::Quantified(t) => t.is_procedure(),
Self::Subr(subr) if subr.kind == SubrKind::Proc => true, Self::Subr(subr) if subr.kind == SubrKind::Proc => true,
Self::Refinement(refine) => refine.t.is_procedure(), Self::Refinement(refine) => refine.t.is_procedure(),
Self::And(lhs, rhs) => lhs.is_procedure() && rhs.is_procedure(),
_ => false, _ => false,
} }
} }
@ -1814,6 +1820,7 @@ impl Type {
match self { match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_refinement(), Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_refinement(),
Self::Refinement(_) => true, Self::Refinement(_) => true,
Self::And(l, r) => l.is_refinement() && r.is_refinement(),
_ => false, _ => false,
} }
} }
@ -1855,6 +1862,7 @@ impl Type {
Self::Refinement(refine) => refine.t.is_method(), Self::Refinement(refine) => refine.t.is_method(),
Self::Subr(subr) => subr.is_method(), Self::Subr(subr) => subr.is_method(),
Self::Quantified(quant) => quant.is_method(), Self::Quantified(quant) => quant.is_method(),
Self::And(l, r) => l.is_method() && r.is_method(),
_ => false, _ => false,
} }
} }
@ -2266,6 +2274,11 @@ impl Type {
match self { match self {
Type::FreeVar(fv) if fv.is_linked() => fv.crack().intersection_types(), Type::FreeVar(fv) if fv.is_linked() => fv.crack().intersection_types(),
Type::Refinement(refine) => refine.t.intersection_types(), Type::Refinement(refine) => refine.t.intersection_types(),
Type::Quantified(tys) => tys
.intersection_types()
.into_iter()
.map(|t| t.quantify())
.collect(),
Type::And(t1, t2) => { Type::And(t1, t2) => {
let mut types = t1.intersection_types(); let mut types = t1.intersection_types();
types.extend(t2.intersection_types()); types.extend(t2.intersection_types());

View file

@ -375,22 +375,7 @@ impl Hash for TypeObj {
impl fmt::Display for TypeObj { impl fmt::Display for TypeObj {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { self.limited_fmt(f, 10)
TypeObj::Builtin { t, .. } => {
if cfg!(feature = "debug") {
write!(f, "<type {t}>")
} else {
write!(f, "{t}")
}
}
TypeObj::Generated(t) => {
if cfg!(feature = "debug") {
write!(f, "<user type {t}>")
} else {
write!(f, "{t}")
}
}
}
} }
} }
@ -401,7 +386,7 @@ impl LimitedDisplay for TypeObj {
if cfg!(feature = "debug") { if cfg!(feature = "debug") {
write!(f, "<type ")?; write!(f, "<type ")?;
t.limited_fmt(f, limit - 1)?; t.limited_fmt(f, limit - 1)?;
write!(f, "{t}>") write!(f, ">")
} else { } else {
t.limited_fmt(f, limit - 1) t.limited_fmt(f, limit - 1)
} }
@ -410,7 +395,7 @@ impl LimitedDisplay for TypeObj {
if cfg!(feature = "debug") { if cfg!(feature = "debug") {
write!(f, "<user type ")?; write!(f, "<user type ")?;
t.limited_fmt(f, limit - 1)?; t.limited_fmt(f, limit - 1)?;
write!(f, "{t}>") write!(f, ">")
} else { } else {
t.limited_fmt(f, limit - 1) t.limited_fmt(f, limit - 1)
} }

View file

@ -312,6 +312,24 @@ impl VarInfo {
) )
} }
pub fn d_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
let kind = VarKind::Parameter {
def_id: DefId(0),
var: false,
default: DefaultInfo::WithDefault,
};
Self::new(
t,
Immutable,
Visibility::private(namespace),
kind,
None,
None,
None,
def_loc,
)
}
pub fn instance_attr(field: Field, t: Type, impl_of: Option<Type>, namespace: Str) -> Self { pub fn instance_attr(field: Field, t: Type, impl_of: Option<Type>, namespace: Str) -> Self {
let muty = if field.is_const() { let muty = if field.is_const() {
Mutability::Const Mutability::Const

View file

@ -4159,6 +4159,13 @@ impl Signature {
} }
} }
pub fn t_spec_op_mut(&mut self) -> Option<&mut TypeSpecWithOp> {
match self {
Self::Var(v) => v.t_spec.as_mut(),
Self::Subr(c) => c.return_t_spec.as_mut(),
}
}
pub fn is_const(&self) -> bool { pub fn is_const(&self) -> bool {
match self { match self {
Self::Var(var) => var.is_const(), Self::Var(var) => var.is_const(),

View file

@ -261,16 +261,11 @@ impl Desugarer {
for chunk in def.body.block.into_iter() { for chunk in def.body.block.into_iter() {
chunks.push(desugar(chunk)); chunks.push(desugar(chunk));
} }
if let Signature::Subr(mut subr) = def.sig { if let Some(t_op) = def.sig.t_spec_op_mut() {
let mut defaults = vec![]; *t_op.t_spec_as_expr = desugar(*t_op.t_spec_as_expr.clone());
for default in subr.params.defaults.into_iter() {
let default_val = desugar(default.default_val);
defaults.push(DefaultParamSignature {
default_val,
..default
});
} }
subr.params.defaults = defaults; if let Signature::Subr(mut subr) = def.sig {
subr.params = Self::perform_desugar_params(desugar, subr.params);
def.sig = Signature::Subr(subr); def.sig = Signature::Subr(subr);
} }
let body = DefBody::new(def.body.op, Block::new(chunks), def.body.id); let body = DefBody::new(def.body.op, Block::new(chunks), def.body.id);
@ -304,15 +299,10 @@ impl Desugarer {
for chunk in lambda.body.into_iter() { for chunk in lambda.body.into_iter() {
chunks.push(desugar(chunk)); chunks.push(desugar(chunk));
} }
let mut defaults = vec![]; if let Some(t_op) = &mut lambda.sig.return_t_spec {
for default in lambda.sig.params.defaults.into_iter() { *t_op.t_spec_as_expr = desugar(*t_op.t_spec_as_expr.clone());
let default_val = desugar(default.default_val);
defaults.push(DefaultParamSignature {
default_val,
..default
});
} }
lambda.sig.params.defaults = defaults; lambda.sig.params = Self::perform_desugar_params(desugar, lambda.sig.params);
let body = Block::new(chunks); let body = Block::new(chunks);
Expr::Lambda(Lambda::new(lambda.sig, lambda.op, body, lambda.id)) Expr::Lambda(Lambda::new(lambda.sig, lambda.op, body, lambda.id))
} }
@ -370,6 +360,36 @@ impl Desugarer {
} }
} }
fn perform_desugar_params(mut desugar: impl FnMut(Expr) -> Expr, mut params: Params) -> Params {
let mut non_defaults = vec![];
for mut non_default in params.non_defaults.into_iter() {
non_default.t_spec = non_default.t_spec.map(|t_spec| {
TypeSpecWithOp::new(t_spec.op, t_spec.t_spec, desugar(*t_spec.t_spec_as_expr))
});
non_defaults.push(non_default);
}
params.var_params = params.var_params.map(|mut var_params| {
var_params.t_spec = var_params.t_spec.map(|t_spec| {
TypeSpecWithOp::new(t_spec.op, t_spec.t_spec, desugar(*t_spec.t_spec_as_expr))
});
var_params
});
let mut defaults = vec![];
for mut default in params.defaults.into_iter() {
let default_val = desugar(default.default_val);
default.sig.t_spec = default.sig.t_spec.map(|t_spec| {
TypeSpecWithOp::new(t_spec.op, t_spec.t_spec, desugar(*t_spec.t_spec_as_expr))
});
defaults.push(DefaultParamSignature {
default_val,
..default
});
}
params.non_defaults = non_defaults;
params.defaults = defaults;
params
}
/// `fib 0 = 0; fib 1 = 1; fib n = fib(n-1) + fib(n-2)` /// `fib 0 = 0; fib 1 = 1; fib n = fib(n-1) + fib(n-2)`
/// -> `fib n = match n, (0 -> 0), (1 -> 1), n -> fib(n-1) + fib(n-2)` /// -> `fib n = match n, (0 -> 0), (1 -> 1), n -> fib(n-1) + fib(n-2)`
fn desugar_multiple_pattern_def(&self, module: Module) -> Module { fn desugar_multiple_pattern_def(&self, module: Module) -> Module {
@ -856,7 +876,7 @@ impl Desugarer {
} }
} }
pub(crate) fn desugar_shortened_record_inner(record: MixedRecord) -> NormalRecord { pub fn desugar_shortened_record_inner(record: MixedRecord) -> NormalRecord {
let attrs = record let attrs = record
.attrs .attrs
.into_iter() .into_iter()

View file

@ -0,0 +1,12 @@
C = Class { .a = Array(Int) }
_ = C.new { .a = [1] } # OK
_ = C.new { .a = ["a"] } # ERR
D = Class { .a = Array(Int, 1) }
_ = D.new { .a = [1] } # OK
d = D.new { .a = [1, 2] } # OK
assert d.a[0] == "a" # ERR
E = Class { .a = Array(Int, 2) }
_ = E.new { .a = [1, 2] } # OK
_ = E.new { .a = [1] } # ERR

View file

@ -0,0 +1,3 @@
D = Class { .a = Array(Int, 1) }
d = D.new { .a = [1] }
assert d.a[0] == 1

11
tests/should_ok/index.er Normal file
View file

@ -0,0 +1,11 @@
a = [1, 2, 3]
assert a[0] == 1
assert a[1..2] == [2, 3]
s = "abcd"
assert s[0] == "a"
assert s[1..2] == "bc"
b = bytes("abcd", "utf-8")
assert b[0] == 97
assert b[1..2] == bytes("bc", "utf-8")

View file

@ -17,6 +17,11 @@ fn exec_array() -> Result<(), ()> {
expect_success("tests/should_ok/array.er", 0) expect_success("tests/should_ok/array.er", 0)
} }
#[test]
fn exec_array_member() -> Result<(), ()> {
expect_success("tests/should_ok/array_member.er", 0)
}
#[test] #[test]
fn exec_class() -> Result<(), ()> { fn exec_class() -> Result<(), ()> {
expect_success("examples/class.er", 0) expect_success("examples/class.er", 0)
@ -118,6 +123,11 @@ fn exec_import_cyclic() -> Result<(), ()> {
expect_success("tests/should_ok/cyclic/import.er", 0) expect_success("tests/should_ok/cyclic/import.er", 0)
} }
#[test]
fn exec_index() -> Result<(), ()> {
expect_success("tests/should_ok/index.er", 0)
}
#[test] #[test]
fn exec_inherit() -> Result<(), ()> { fn exec_inherit() -> Result<(), ()> {
expect_success("tests/should_ok/inherit.er", 0) expect_success("tests/should_ok/inherit.er", 0)
@ -308,6 +318,11 @@ fn exec_array_err() -> Result<(), ()> {
expect_failure("examples/array.er", 0, 1) expect_failure("examples/array.er", 0, 1)
} }
#[test]
fn exec_array_member_err() -> Result<(), ()> {
expect_failure("tests/should_err/array_member.er", 0, 3)
}
#[test] #[test]
fn exec_as() -> Result<(), ()> { fn exec_as() -> Result<(), ()> {
expect_failure("tests/should_err/as.er", 0, 6) expect_failure("tests/should_err/as.er", 0, 6)