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]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
- run: rustup update stable
- run: cargo build --all --all-targets --verbose

View file

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

View file

@ -216,7 +216,11 @@ impl Input {
match &self.kind {
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
),
InputKind::REPL | InputKind::Pipe(_) => format!("stdin_{}", self.id),
@ -248,9 +252,11 @@ impl Input {
pub fn unescaped_file_stem(&self) -> &str {
match &self.kind {
InputKind::File(filename) => {
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_")
}
InputKind::File(filename) => filename
.file_stem()
.and_then(|f| f.to_str())
.unwrap_or("_")
.trim_end_matches(".d"),
InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string",
@ -260,9 +266,11 @@ impl Input {
pub fn unescaped_filename(&self) -> &str {
match &self.kind {
InputKind::File(filename) => {
filename.file_name().and_then(|f| f.to_str()).unwrap_or("_")
}
InputKind::File(filename) => filename
.file_name()
.and_then(|f| f.to_str())
.unwrap_or("_")
.trim_end_matches(".d"),
InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string",
@ -283,7 +291,9 @@ impl Input {
pub fn module_name(&self) -> String {
match &self.kind {
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())
} else {
filename.file_stem()
@ -291,6 +301,7 @@ impl Input {
file_stem
.and_then(|f| f.to_str())
.unwrap_or("_")
.trim_end_matches(".d")
.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) {
let file_path = erg_path().join("els.log");
let mut f = if file_path.exists() {
File::options().write(true).open(file_path).unwrap()
File::options().append(true).open(file_path).unwrap()
} else {
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();
}

View file

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

View file

@ -31,14 +31,14 @@ impl_u8_enum! {Opcode308;
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,
WITH_EXCEPT_START = 49,
// GET_LEN = 30,
// MATCH_MAPPING = 31,
// MATCH_SEQUENCE = 32,
// MATCH_KEYS = 33,
// PUSH_EXC_INFO = 35,
// CHECK_EXC_MATCH = 36,
// CHECK_EG_MATCH = 37,
// WITH_EXCEPT_START = 49,
GET_AITER = 50,
GET_ANEXT = 51,
BEFORE_ASYNC_WITH = 52,
@ -92,9 +92,10 @@ impl_u8_enum! {Opcode308;
POP_JUMP_IF_FALSE = 114,
POP_JUMP_IF_TRUE = 115,
LOAD_GLOBAL = 116,
IS_OP = 117,
CONTAINS_OP = 118,
RERAISE = 119,
// IS_OP = 117,
// CONTAINS_OP = 118,
// RERAISE = 119,
SETUP_FINALLY = 122,
LOAD_FAST = 124,
STORE_FAST = 125,
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.
/// Use this for dictionary keys, etc.
/// See also: `els::util::NormalizedUrl`
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct NormalizedPathBuf(PathBuf);
impl<P: Into<PathBuf>> From<P> for NormalizedPathBuf {
@ -41,7 +41,7 @@ impl Deref for NormalizedPathBuf {
impl NormalizedPathBuf {
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::opcode::{CommonOpcode, CompareOp};
use erg_common::opcode308::Opcode308;
use erg_common::opcode309::Opcode309;
use erg_common::opcode310::Opcode310;
use erg_common::opcode311::{BinOpCode, Opcode311};
use erg_common::option_enum_unwrap;
@ -1783,10 +1784,10 @@ impl PyCodeGenerator {
self.write_instr(Opcode308::JUMP_FORWARD); // jump to end
self.write_arg(0);
// else block
let idx_else_begin = if self.py_version.minor >= Some(11) {
self.lasti() - idx_pop_jump_if_false - 2
} else {
self.lasti()
let idx_else_begin = match self.py_version.minor {
Some(11) => self.lasti() - idx_pop_jump_if_false - 2,
Some(10) => self.lasti(),
_ => self.lasti(),
};
self.fill_jump(idx_pop_jump_if_false + 1, idx_else_begin - 2);
match args.remove(0) {
@ -1805,8 +1806,12 @@ impl PyCodeGenerator {
self.stack_dec();
}
} else {
self.write_instr(Opcode308::JUMP_FORWARD);
self.write_arg(1);
self.write_instr(Opcode311::JUMP_FORWARD);
let jump_to = match self.py_version.minor {
Some(11 | 10) => 1,
_ => 2,
};
self.write_arg(jump_to);
// no else block
let idx_end = if self.py_version.minor >= Some(11) {
self.lasti() - idx_pop_jump_if_false - 1
@ -1861,7 +1866,7 @@ impl PyCodeGenerator {
self.write_arg(idx_for_iter / 2);
}
Some(9 | 8 | 7) => {
self.write_instr(Opcode308::JUMP_ABSOLUTE);
self.write_instr(Opcode309::JUMP_ABSOLUTE);
self.write_arg(idx_for_iter);
}
_ => todo!("not supported Python version"),
@ -2077,7 +2082,7 @@ impl PyCodeGenerator {
self.write_arg(0);
self.write_instr(Opcode311::PUSH_EXC_INFO);
self.write_arg(0);
self.write_instr(Opcode308::WITH_EXCEPT_START);
self.write_instr(Opcode309::WITH_EXCEPT_START);
self.write_arg(0);
self.write_instr(Opcode311::POP_JUMP_FORWARD_IF_TRUE);
self.write_arg(4);
@ -2151,6 +2156,59 @@ impl PyCodeGenerator {
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) {
log!(info "entered {}", fn_name!());
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
@ -2161,7 +2219,7 @@ impl PyCodeGenerator {
let params = self.gen_param_names(&lambda.params);
self.emit_expr(expr);
let idx_setup_with = self.lasti();
self.write_instr(Opcode308::SETUP_WITH);
self.write_instr(Opcode309::SETUP_WITH);
self.write_arg(0);
// push __exit__, __enter__() to the stack
// self.stack_inc_n(2);
@ -2219,7 +2277,8 @@ impl PyCodeGenerator {
"with!" => match self.py_version.minor {
Some(11) => self.emit_with_instr_311(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"),
},
// "pyimport" | "py" are here
@ -2281,7 +2340,7 @@ impl PyCodeGenerator {
}
self.emit_expr(var_args.expr.clone());
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);
}
}
@ -2752,6 +2811,10 @@ impl PyCodeGenerator {
self.emit_load_name_instr(Identifier::public("Str"));
}
other => match &other.qual_name()[..] {
"Bytes" => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Bytes"));
}
"Array" => {
self.emit_push_null();
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::Set as AstSet;
use erg_parser::ast::*;
use erg_parser::desugar::Desugarer;
use erg_parser::token::{Token, TokenKind};
use crate::ty::constructors::{
@ -303,8 +304,28 @@ impl Context {
fn call(&self, subr: ConstSubr, args: ValueArgs, loc: Location) -> EvalResult<ValueObj> {
match subr {
ConstSubr::User(_user) => {
feature_error!(self, loc, "calling user-defined subroutines").map_err(Into::into)
ConstSubr::User(user) => {
// 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| {
if e.0.loc.is_unknown() {
@ -316,6 +337,16 @@ impl Context {
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> {
match record {
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 {
Type::Poly { name, params } if &name[..] == "Array" || &name[..] == "Array!" => {
let t = self
.convert_tp_into_type(params[0].clone())
.map_err(|_| ())?;
let Ok(t) = self.convert_tp_into_type(params[0].clone()) else {
return Err(poly(name, params));
};
let TyParam::Value(ValueObj::Nat(len)) = params[1] else { unreachable!() };
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 {
ValueObj::Array(arr) => Ok(arr.to_vec()),
ValueObj::Type(t) => self.convert_type_to_array(t.into_typ()),
_ => Err(()),
ValueObj::Type(t) => self
.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 ValueObj::Subr(subr) = obj {
let is_method = subr.sig_t().self_t().is_some();
let mut pos_args = vec![];
if is_method {
if subr.sig_t().is_method() {
match ValueObj::try_from(lhs) {
Ok(value) => {
pos_args.push(value);

View file

@ -41,6 +41,9 @@ impl Generalizer {
fn generalize_tp(&mut self, free: TyParam, uninit: bool) -> TyParam {
match free {
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_linked() => {
self.generalize_tp(fv.crack().clone(), uninit)
@ -122,7 +125,7 @@ impl Generalizer {
TyParam::unary(op, val)
}
other if other.has_no_unbound_var() => other,
other => todo!("{other}"),
other => todo!("{other:?}"),
}
}

View file

@ -897,7 +897,7 @@ impl Context {
Immutable,
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(
FUNDAMENTAL_GETITEM,
str_getitem_t,
@ -1181,7 +1181,7 @@ impl Context {
);
/* 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_marker_trait(self, poly(OUTPUT, vec![ty_tp(T.clone())]))
@ -1240,11 +1240,16 @@ impl Context {
Predicate::le(var, N.clone() - value(1usize)),
);
// __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()),
anon(input.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();
let get_item = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
FUNDAMENTAL_GETITEM,
@ -1330,6 +1335,12 @@ impl Context {
array_t(T.clone(), TyParam::erased(Nat)),
);
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 */
let mut generic_set = Self::builtin_mono_class(GENERIC_SET, 1);
generic_set.register_superclass(Obj, &obj);
@ -1534,6 +1545,29 @@ impl Context {
Str,
);
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 */
let mut generic_tuple = Self::builtin_mono_class(GENERIC_TUPLE, 1);
generic_tuple.register_superclass(Obj, &obj);
@ -2370,6 +2404,7 @@ impl Context {
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(
mono(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> {
let slf = ctx
.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);
if let Some(v) = slf.get(index as usize) {
Ok(v.clone())

View file

@ -220,6 +220,12 @@ impl Context {
)
.quantify();
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(
vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))],
None,
@ -393,6 +399,13 @@ impl Context {
vis.clone(),
Some(FUNC_ROUND),
);
self.register_builtin_py_impl(
FUNC_SLICE,
t_slice,
Immutable,
vis.clone(),
Some(FUNC_SLICE),
);
self.register_builtin_py_impl(
FUNC_SORTED,
t_sorted,

View file

@ -33,7 +33,10 @@ use crate::module::SharedCompilerResource;
use crate::ty::constructors::*;
use crate::ty::free::Constraint;
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 Mutability::*;
use ParamSpec as PS;
@ -281,6 +284,7 @@ const NAMED_FUNC: &str = "NamedFunc";
const FUNC: &str = "Func";
const QUANTIFIED: &str = "Quantified";
const QUANTIFIED_FUNC: &str = "QuantifiedFunc";
const SLICE: &str = "Slice";
const FUNC_OBJECT: &str = "object";
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_REPR: &str = "repr";
const FUNC_ROUND: &str = "round";
const FUNC_SLICE: &str = "slice";
const FUNC_SORTED: &str = "sorted";
const FUNC_SUM: &str = "sum";
const FUNC_IF: &str = "if";
@ -780,23 +785,53 @@ impl Context {
muty: Mutability,
py_name: Option<&'static str>,
) {
// FIXME: panic
if let Some((_, root_ctx)) = self.poly_types.get_mut(&t.local_name()) {
root_ctx.methods_list.push((ClassDefType::Simple(t), ctx));
} else {
let val = match ctx.kind {
let ret_val = match ctx.kind {
ContextKind::Class => ValueObj::builtin_class(t.clone()),
ContextKind::Trait => ValueObj::builtin_trait(t.clone()),
_ => ValueObj::builtin_type(t.clone()),
};
let qual_name = t.qual_name();
let name = VarName::from_str(t.local_name());
// e.g Array!: |T, N|(_: {T}, _: {N}) -> {Array!(T, N)}
let params = t
.typarams()
// e.g Array!: |T, N|(_: {T}, _:= {N}) -> {Array!(T, N)}
let nd_params = ctx
.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()
.map(|tp| ParamTy::Pos(tp_enum(self.get_tp_t(&tp).unwrap_or(Obj), set! { tp })))
.collect();
let meta_t = func(params, None, vec![], v_enum(set! { val.clone() })).quantify();
.take(lack)
.map(|pt| TyParam::erased(pt.typ().clone()));
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 {
self.locals.insert(
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.poly_types.insert(name, (t, ctx));
}

View file

@ -29,8 +29,8 @@ use Type::*;
use crate::context::instantiate_spec::ConstTemplate;
use crate::context::{Context, RegistrationMode, TraitImpl, TyVarCache, Variance};
use crate::error::{
binop_to_dname, readable_name, unaryop_to_dname, SingleTyCheckResult, TyCheckError,
TyCheckErrors, TyCheckResult,
binop_to_dname, ordinal_num, readable_name, unaryop_to_dname, SingleTyCheckResult,
TyCheckError, TyCheckErrors, TyCheckResult,
};
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
use crate::{feature_error, hir};
@ -831,10 +831,10 @@ impl Context {
if let Some(attr_name) = attr_name.as_ref() {
let mut vi =
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)
} 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 {
t,
..VarInfo::default()
@ -844,6 +844,7 @@ impl Context {
fn resolve_overload(
&self,
obj: &hir::Expr,
instance: Type,
pos_args: &[hir::PosArg],
kw_args: &[hir::KwArg],
@ -853,7 +854,7 @@ impl Context {
if intersecs.len() == 1 {
Ok(instance)
} else {
let input_t = subr_t(
let mut input_t = subr_t(
SubrKind::Proc,
pos_args
.iter()
@ -867,6 +868,18 @@ impl Context {
Obj,
);
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) {
return Ok(ty.clone());
}
@ -1486,7 +1499,12 @@ impl Context {
let missing_params = subr
.non_default_params
.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))
.collect::<Vec<_>>();
if !missing_params.is_empty() {
@ -1666,6 +1684,8 @@ impl Context {
} else {
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())
.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)
}

View file

@ -42,6 +42,7 @@ use crate::module::{
};
use crate::ty::value::ValueObj;
use crate::ty::GuardType;
use crate::ty::ParamTy;
use crate::ty::{Predicate, Type, Visibility, VisibilityModifier};
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
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 {
Self::new(
Some(name),
@ -252,6 +263,20 @@ impl ParamSpec {
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)]
@ -434,6 +459,7 @@ pub struct Context {
/// => locals: {"x": T, "y": T}
/// TODO: impl params desugaring and replace to `Dict`
pub(crate) params: Vec<(Option<VarName>, VarInfo)>,
pub(crate) params_spec: Vec<ParamSpec>,
pub(crate) locals: Dict<VarName, VarInfo>,
pub(crate) consts: Dict<VarName, ValueObj>,
// {"Nat": ctx, "Int": ctx, ...}
@ -589,7 +615,7 @@ impl Context {
level: usize,
) -> Self {
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)));
if let Some(name) = param.name {
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
@ -635,6 +661,7 @@ impl Context {
method_to_classes: Dict::default(),
method_impl_patches: Dict::default(),
params: params_,
params_spec: params,
decls: Dict::default(),
future_defined_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_parser::ast::{self, AscriptionKind, Identifier, VarName, AST};
use erg_parser::desugar::Desugarer;
use crate::context::instantiate::TyVarCache;
use crate::lower::ASTLowerer;
@ -332,7 +333,7 @@ impl ASTLowerer {
fn fake_lower_record(&self, rec: ast::Record) -> LowerResult<hir::Record> {
let rec = match 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();
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
.chmod!: (path: PathLike, mode: Nat) => NoneType
.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; _]
.mkdir!: (path: PathLike, mode := Nat) => NoneType
.putenv!: (key: Str, value: Str) => NoneType

View file

@ -1,4 +1,5 @@
from _erg_control import then__
from _erg_range import Range
class Array(list):
def dedup(self, same_bucket=None):
@ -24,3 +25,11 @@ class Array(list):
def __mul__(self, n):
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):
def try_new(*b): # -> Result[Nat]
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):
pass
@staticmethod
def from_slice(slice):
pass
def into_slice(self):
pass
def __getitem__(self, item):
res = self.start + item
if res in self:
@ -56,6 +63,13 @@ class RightOpenRange(Range):
def __contains__(self, item):
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`
class OpenRange(Range):
@ -68,6 +82,13 @@ class ClosedRange(Range):
def __contains__(self, item):
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:
def __init__(self, rng):

View file

@ -2,7 +2,6 @@ from _erg_result import Error
from _erg_int import Int
from _erg_control import then__
class Str(str):
def __instancecheck__(cls, obj):
return isinstance(obj, str)
@ -40,6 +39,15 @@ class Str(str):
def __mod__(self, other):
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
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::{OperationKind, TypeSpecWithOp, VarName, AST};
use erg_parser::build_ast::ASTBuilder;
use erg_parser::desugar::Desugarer;
use erg_parser::token::{Token, TokenKind};
use erg_parser::Parser;
@ -381,7 +382,9 @@ impl ASTLowerer {
log!(info "entered {}({record})", fn_name!());
match record {
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 {
let mut _self = self.clone();
_self.promises.path = path;
_self.promises.path = path.into();
_self
}

View file

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

View file

@ -68,7 +68,7 @@ impl Promise {
#[derive(Debug, Clone, Default)]
pub struct SharedPromises {
graph: SharedModuleGraph,
pub(crate) path: PathBuf,
pub(crate) path: NormalizedPathBuf,
promises: Shared<Dict<NormalizedPathBuf, Promise>>,
}
@ -86,7 +86,7 @@ impl SharedPromises {
pub fn new(graph: SharedModuleGraph, path: PathBuf) -> Self {
Self {
graph,
path,
path: NormalizedPathBuf::new(path),
promises: Shared::new(Dict::new()),
}
}
@ -125,6 +125,26 @@ impl SharedPromises {
Promise::Running { parent, handle };
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();
*self.promises.borrow_mut().get_mut(path).unwrap() = Promise::Finished;
res

View file

@ -8,7 +8,7 @@ use erg_common::impl_display_from_debug;
#[allow(unused_imports)]
use erg_common::log;
use erg_common::opcode::CommonOpcode;
use erg_common::opcode308::Opcode308;
use erg_common::opcode309::Opcode309;
use erg_common::opcode310::Opcode310;
use erg_common::opcode311::{BinOpCode, Opcode311};
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 {
match minor_ver {
7 | 8 => jump_abs_addr_308(Opcode308::from(op), idx, arg),
9 | 10 => jump_abs_addr_310(Opcode310::from(op), idx, arg),
7 | 8 | 9 => jump_abs_addr_309(Opcode309::from(op), idx, arg),
10 => jump_abs_addr_310(Opcode310::from(op), idx, arg),
11 => jump_abs_addr_311(Opcode311::from(op), idx, arg),
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 {
Opcode308::FOR_ITER | Opcode308::JUMP_FORWARD => idx + arg * 2 + 2,
Opcode308::JUMP_ABSOLUTE | Opcode308::POP_JUMP_IF_FALSE | Opcode308::POP_JUMP_IF_TRUE => {
arg * 2
Opcode309::FOR_ITER | Opcode309::JUMP_FORWARD => idx + arg + 2,
Opcode309::JUMP_ABSOLUTE | Opcode309::POP_JUMP_IF_FALSE | Opcode309::POP_JUMP_IF_TRUE => {
arg
}
Opcode308::SETUP_WITH => idx + arg * 2 + 2,
Opcode309::SETUP_WITH => idx + arg + 2,
_ => unreachable!(),
}
}
@ -541,10 +541,8 @@ impl CodeObj {
arg
};
match py_ver.and_then(|pv| pv.minor) {
Some(7) => self.read_instr_307(op, arg, idx, &mut instrs),
Some(8) => self.read_instr_308(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(7 | 8 | 9) => self.read_instr_309(op, arg, idx, &mut instrs),
Some(10) => self.read_instr_310(op, arg, idx, &mut instrs),
Some(11) => self.read_instr_311(op, arg, idx, &mut instrs),
_ => {}
}
@ -557,78 +555,34 @@ impl CodeObj {
instrs
}
fn read_instr_307(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) {
let op307 = Opcode308::from(*op);
let s_op = op307.to_string();
fn read_instr_309(&self, op: &u8, arg: usize, idx: usize, instrs: &mut String) {
let op309 = Opcode309::from(*op);
let s_op = op309.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 op307 {
Opcode308::STORE_DEREF | Opcode308::LOAD_DEREF => {
match op309 {
Opcode309::STORE_DEREF | Opcode309::LOAD_DEREF => {
write!(instrs, "{arg} ({})", self.freevars.get(arg).unwrap()).unwrap();
}
Opcode308::LOAD_CLOSURE => {
Opcode309::LOAD_CLOSURE => {
write!(instrs, "{arg} ({})", self.cellvars.get(arg).unwrap()).unwrap();
}
Opcode308::JUMP_ABSOLUTE => {
Opcode309::JUMP_ABSOLUTE => {
write!(instrs, "{arg} (to {})", arg).unwrap();
}
Opcode308::JUMP_FORWARD => {
Opcode309::JUMP_FORWARD => {
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();
}
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();
}
_ => {}
}
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 => {
Opcode309::BINARY_ADD
| Opcode309::BINARY_SUBTRACT
| Opcode309::BINARY_MULTIPLY
| Opcode309::BINARY_TRUE_DIVIDE => {
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::Str;
use erg_parser::ast::{ConstBlock, Params};
use erg_parser::ast::{Block, ConstBlock, Params};
use super::constructors::subr_t;
use super::value::{EvalValueResult, ValueObj};
use super::{Predicate, Type};
use super::{ParamTy, Predicate, TyParam, Type};
use crate::context::Context;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UserConstSubr {
name: Str,
params: Params,
block: ConstBlock,
sig_t: Type,
pub name: Str,
pub(crate) params: Params,
pub(crate) block: ConstBlock,
pub(crate) sig_t: Type,
}
impl UserConstSubr {
@ -30,6 +30,10 @@ impl UserConstSubr {
sig_t,
}
}
pub fn block(self) -> Block {
self.block.downgrade()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -38,6 +42,26 @@ pub struct ValueArgs {
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 {
pub const fn new(pos_args: Vec<ValueObj>, kw_args: Dict<Str, ValueObj>) -> Self {
ValueArgs { pos_args, kw_args }
@ -54,7 +78,7 @@ impl ValueArgs {
#[derive(Clone)]
pub struct BuiltinConstSubr {
name: &'static str,
name: Str,
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
sig_t: Type,
as_type: Option<Type>,
@ -91,14 +115,14 @@ impl fmt::Display for BuiltinConstSubr {
}
impl BuiltinConstSubr {
pub const fn new(
name: &'static str,
pub fn new<S: Into<Str>>(
name: S,
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
sig_t: Type,
as_type: Option<Type>,
) -> Self {
Self {
name,
name: name.into(),
subr,
sig_t,
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)]
pub enum ConstSubr {
User(UserConstSubr),
Builtin(BuiltinConstSubr),
Gen(GenConstSubr),
}
impl fmt::Display for ConstSubr {
@ -123,6 +227,7 @@ impl fmt::Display for ConstSubr {
write!(f, "<user-defined const subroutine '{}'>", subr.name)
}
ConstSubr::Builtin(subr) => write!(f, "{subr}"),
ConstSubr::Gen(subr) => write!(f, "{subr}"),
}
}
}
@ -132,10 +237,10 @@ impl ConstSubr {
match self {
ConstSubr::User(user) => &user.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> {
match self {
ConstSubr::User(user) => {
@ -170,6 +275,7 @@ impl ConstSubr {
None
}
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 CONTAINER_OMIT_THRESHOLD: usize = 8;
pub const DEFAULT_PARAMS_THRESHOLD: usize = 5;
/// cloneのコストがあるためなるべく.ref_tを使うようにすること
/// いくつかの構造体は直接Typeを保持していないので、その場合は.tを使う
@ -315,6 +316,10 @@ impl LimitedDisplay for SubrType {
var_params.typ().limited_fmt(f, limit - 1)?;
}
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() {
write!(f, ", ")?;
}
@ -1712,6 +1717,7 @@ impl Type {
Self::Quantified(t) => t.is_procedure(),
Self::Subr(subr) if subr.kind == SubrKind::Proc => true,
Self::Refinement(refine) => refine.t.is_procedure(),
Self::And(lhs, rhs) => lhs.is_procedure() && rhs.is_procedure(),
_ => false,
}
}
@ -1814,6 +1820,7 @@ impl Type {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_refinement(),
Self::Refinement(_) => true,
Self::And(l, r) => l.is_refinement() && r.is_refinement(),
_ => false,
}
}
@ -1855,6 +1862,7 @@ impl Type {
Self::Refinement(refine) => refine.t.is_method(),
Self::Subr(subr) => subr.is_method(),
Self::Quantified(quant) => quant.is_method(),
Self::And(l, r) => l.is_method() && r.is_method(),
_ => false,
}
}
@ -2266,6 +2274,11 @@ impl Type {
match self {
Type::FreeVar(fv) if fv.is_linked() => fv.crack().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) => {
let mut types = t1.intersection_types();
types.extend(t2.intersection_types());

View file

@ -375,22 +375,7 @@ impl Hash for TypeObj {
impl fmt::Display for TypeObj {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
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}")
}
}
}
self.limited_fmt(f, 10)
}
}
@ -401,7 +386,7 @@ impl LimitedDisplay for TypeObj {
if cfg!(feature = "debug") {
write!(f, "<type ")?;
t.limited_fmt(f, limit - 1)?;
write!(f, "{t}>")
write!(f, ">")
} else {
t.limited_fmt(f, limit - 1)
}
@ -410,7 +395,7 @@ impl LimitedDisplay for TypeObj {
if cfg!(feature = "debug") {
write!(f, "<user type ")?;
t.limited_fmt(f, limit - 1)?;
write!(f, "{t}>")
write!(f, ">")
} else {
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 {
let muty = if field.is_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 {
match self {
Self::Var(var) => var.is_const(),

View file

@ -261,16 +261,11 @@ impl Desugarer {
for chunk in def.body.block.into_iter() {
chunks.push(desugar(chunk));
}
if let Signature::Subr(mut subr) = def.sig {
let mut defaults = vec![];
for default in subr.params.defaults.into_iter() {
let default_val = desugar(default.default_val);
defaults.push(DefaultParamSignature {
default_val,
..default
});
if let Some(t_op) = def.sig.t_spec_op_mut() {
*t_op.t_spec_as_expr = desugar(*t_op.t_spec_as_expr.clone());
}
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);
}
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() {
chunks.push(desugar(chunk));
}
let mut defaults = vec![];
for default in lambda.sig.params.defaults.into_iter() {
let default_val = desugar(default.default_val);
defaults.push(DefaultParamSignature {
default_val,
..default
});
if let Some(t_op) = &mut lambda.sig.return_t_spec {
*t_op.t_spec_as_expr = desugar(*t_op.t_spec_as_expr.clone());
}
lambda.sig.params.defaults = defaults;
lambda.sig.params = Self::perform_desugar_params(desugar, lambda.sig.params);
let body = Block::new(chunks);
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 n = match n, (0 -> 0), (1 -> 1), n -> fib(n-1) + fib(n-2)`
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
.attrs
.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)
}
#[test]
fn exec_array_member() -> Result<(), ()> {
expect_success("tests/should_ok/array_member.er", 0)
}
#[test]
fn exec_class() -> Result<(), ()> {
expect_success("examples/class.er", 0)
@ -118,6 +123,11 @@ fn exec_import_cyclic() -> Result<(), ()> {
expect_success("tests/should_ok/cyclic/import.er", 0)
}
#[test]
fn exec_index() -> Result<(), ()> {
expect_success("tests/should_ok/index.er", 0)
}
#[test]
fn exec_inherit() -> Result<(), ()> {
expect_success("tests/should_ok/inherit.er", 0)
@ -308,6 +318,11 @@ fn exec_array_err() -> Result<(), ()> {
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]
fn exec_as() -> Result<(), ()> {
expect_failure("tests/should_err/as.er", 0, 6)