fix: some bugs

Fixed:

* argument `_` of anonymous function disappears
* els crashes
* incomplete `.update!` codegen
This commit is contained in:
Shunsuke Shibayama 2023-02-13 15:06:50 +09:00
parent da7ff0a258
commit 2574de3a68
7 changed files with 121 additions and 50 deletions

View file

@ -38,7 +38,9 @@ impl<Checker: BuildRunnable> Server<Checker> {
.filter(|warn| warn.core.main_message.ends_with("is not used"))
.collect::<Vec<_>>();
for warn in warns {
let range = util::loc_to_range(warn.core.loc).unwrap();
let Some(range) = util::loc_to_range(warn.core.loc) else {
continue;
};
match visitor.get_min_expr(range) {
Some(Expr::Def(def)) => {
let Some(mut range) = util::loc_to_range(def.loc()) else {

View file

@ -555,7 +555,8 @@ impl<'a> HIRVisitor<'a> {
}
fn get_lambda_info(&self, lambda: &Lambda, token: &Token) -> Option<VarInfo> {
self.get_block_info(&lambda.body, token)
self.get_params_info(&lambda.params, token)
.or_else(|| self.get_block_info(&lambda.body, token))
}
fn get_array_info(&self, arr: &Array, token: &Token) -> Option<VarInfo> {
@ -583,7 +584,7 @@ impl<'a> HIRVisitor<'a> {
fn get_record_info(&self, record: &Record, token: &Token) -> Option<VarInfo> {
for field in record.attrs.iter() {
if let Some(expr) = self.get_block_info(&field.body.block, token) {
if let Some(expr) = self.get_def_info(field, token) {
return Some(expr);
}
}

View file

@ -142,7 +142,12 @@ impl<Checker: BuildRunnable> Server<Checker> {
def_loc: &AbsLocation,
) -> ELSResult<()> {
if let Some(module) = def_loc.module.as_ref() {
let mut def_pos = util::loc_to_range(def_loc.loc).unwrap().start;
let mut def_pos = match util::loc_to_range(def_loc.loc) {
Some(range) => range.start,
None => {
return Ok(());
}
};
def_pos.line = def_pos.line.saturating_sub(1);
let def_uri = util::normalize_url(Url::from_file_path(module).unwrap());
let mut default_code_block = "".to_string();

View file

@ -2195,12 +2195,17 @@ impl PyCodeGenerator {
}
/// X.update! x -> x + 1
/// X = (x -> x + 1)(X)
/// X = X + 1
/// => X = mutate_operator((x -> x + 1)(X))
/// TODO: should be `X = X + 1` in the above case
fn emit_call_update_311(&mut self, obj: Expr, mut args: Args) {
log!(info "entered {}", fn_name!());
let acc = enum_unwrap!(obj, Expr::Accessor);
let func = args.remove_left_or_key("f").unwrap();
if !self.mutate_op_loaded {
self.load_mutate_op();
}
self.emit_push_null();
self.emit_load_name_instr(Identifier::private("#mutate_operator"));
self.emit_push_null();
self.emit_expr(func);
self.emit_acc(acc.clone());
@ -2208,23 +2213,32 @@ impl PyCodeGenerator {
// (1 (subroutine) + argc) input objects -> 1 return object
// self.stack_dec_n((1 + 1) - 1);
self.stack_dec();
self.emit_precall_and_call(1);
self.stack_dec();
self.store_acc(acc);
self.emit_load_const(ValueObj::None);
}
/// X.update! x -> x + 1
/// X = (x -> x + 1)(X)
/// X = mutate_operator((x -> x + 1)(X))
/// X = X + 1
fn emit_call_update_310(&mut self, obj: Expr, mut args: Args) {
log!(info "entered {}", fn_name!());
let acc = enum_unwrap!(obj, Expr::Accessor);
let func = args.remove_left_or_key("f").unwrap();
if !self.mutate_op_loaded {
self.load_mutate_op();
}
self.emit_load_name_instr(Identifier::private("#mutate_operator"));
self.emit_expr(func);
self.emit_acc(acc.clone());
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(1);
// (1 (subroutine) + argc) input objects -> 1 return object
self.stack_dec_n((1 + 1) - 1);
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(1);
self.stack_dec();
self.store_acc(acc);
self.emit_load_const(ValueObj::None);
}

View file

@ -261,13 +261,16 @@ impl Context {
// Literal patterns will be desugared to discard patterns
ast::ParamPattern::Lit(_) => unreachable!(),
ast::ParamPattern::Discard(token) => {
let spec_t = self.instantiate_param_sig_t(
let (spec_t, errs) = match self.instantiate_param_sig_t(
&sig.raw,
opt_decl_t,
&mut TyVarCache::new(self.level, self),
Normal,
kind,
)?;
) {
Ok(ty) => (ty, TyCheckErrors::empty()),
Err(errs) => (Type::Failure, errs),
};
let def_id = DefId(get_hash(&(&self.name, "_")));
let kind = VarKind::parameter(def_id, DefaultInfo::NonDefault);
let vi = VarInfo::new(
@ -282,7 +285,11 @@ impl Context {
);
sig.vi = vi.clone();
self.params.push((Some(VarName::from_static("_")), vi));
Ok(())
if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
}
ast::ParamPattern::VarName(name) => {
if self
@ -299,13 +306,16 @@ impl Context {
} else {
// ok, not defined
let mut dummy_tv_cache = TyVarCache::new(self.level, self);
let spec_t = self.instantiate_param_sig_t(
let (spec_t, mut errs) = match self.instantiate_param_sig_t(
&sig.raw,
opt_decl_t,
&mut dummy_tv_cache,
Normal,
kind,
)?;
) {
Ok(ty) => (ty, TyCheckErrors::empty()),
Err(errs) => (Type::Failure, errs),
};
let spec_t = if is_var_params {
unknown_len_array_t(spec_t)
} else {
@ -313,7 +323,11 @@ impl Context {
};
if &name.inspect()[..] == "self" {
if let Some(self_t) = self.rec_get_self_t() {
self.sub_unify(&spec_t, &self_t, name.loc(), Some(name.inspect()))?;
if let Err(es) =
self.sub_unify(&spec_t, &self_t, name.loc(), Some(name.inspect()))
{
errs.extend(es);
}
} else {
log!(err "self_t is None");
}
@ -336,7 +350,11 @@ impl Context {
}
sig.vi = vi.clone();
self.params.push((Some(name.clone()), vi));
Ok(())
if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
}
}
ast::ParamPattern::Ref(name) => {
@ -354,21 +372,26 @@ impl Context {
} else {
// ok, not defined
let mut dummy_tv_cache = TyVarCache::new(self.level, self);
let spec_t = self.instantiate_param_sig_t(
let (spec_t, mut errs) = match self.instantiate_param_sig_t(
&sig.raw,
opt_decl_t,
&mut dummy_tv_cache,
Normal,
kind,
)?;
) {
Ok(ty) => (ty, TyCheckErrors::empty()),
Err(errs) => (Type::Failure, errs),
};
if &name.inspect()[..] == "self" {
if let Some(self_t) = self.rec_get_self_t() {
self.sub_unify(
if let Err(es) = self.sub_unify(
&spec_t,
&ref_(self_t),
name.loc(),
Some(name.inspect()),
)?;
) {
errs.extend(es);
}
} else {
log!(err "self_t is None");
}
@ -386,7 +409,11 @@ impl Context {
);
sig.vi = vi.clone();
self.params.push((Some(name.clone()), vi));
Ok(())
if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
}
}
ast::ParamPattern::RefMut(name) => {
@ -404,21 +431,26 @@ impl Context {
} else {
// ok, not defined
let mut dummy_tv_cache = TyVarCache::new(self.level, self);
let spec_t = self.instantiate_param_sig_t(
let (spec_t, mut errs) = match self.instantiate_param_sig_t(
&sig.raw,
opt_decl_t,
&mut dummy_tv_cache,
Normal,
kind,
)?;
) {
Ok(ty) => (ty, TyCheckErrors::empty()),
Err(errs) => (Type::Failure, errs),
};
if &name.inspect()[..] == "self" {
if let Some(self_t) = self.rec_get_self_t() {
self.sub_unify(
if let Err(es) = self.sub_unify(
&spec_t,
&ref_mut(self_t, None),
name.loc(),
Some(name.inspect()),
)?;
) {
errs.extend(es);
}
} else {
log!(err "self_t is None");
}
@ -436,7 +468,11 @@ impl Context {
);
sig.vi = vi.clone();
self.params.push((Some(name.clone()), vi));
Ok(())
if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
}
}
other => {
@ -1045,7 +1081,7 @@ impl Context {
if let Some(sup) =
self.rec_get_const_obj(&gen.base_or_sup().unwrap().typ().local_name())
{
let sup = enum_unwrap!(sup, ValueObj::Type);
let ValueObj::Type(sup) = sup else { todo!("{sup}") };
let param_t = match sup {
TypeObj::Builtin(t) => t,
TypeObj::Generated(t) => t.base_or_sup().unwrap().typ(),
@ -1086,7 +1122,7 @@ impl Context {
2,
self.level,
);
let req = enum_unwrap!(gen.base_or_sup().unwrap(), TypeObj::Builtin:(Type::Record:(_)));
let Some(TypeObj::Builtin(Type::Record(req))) = gen.base_or_sup() else { todo!("{gen}") };
for (field, t) in req.iter() {
let muty = if field.is_const() {
Mutability::Const
@ -1162,7 +1198,7 @@ impl Context {
}
GenTypeObj::Patch(_) => {
if gen.typ().is_monomorphic() {
let base = enum_unwrap!(gen.base_or_sup().unwrap(), TypeObj::Builtin);
let Some(TypeObj::Builtin(base)) = gen.base_or_sup() else { todo!("{gen}") };
let ctx = Self::mono_patch(
gen.typ().qual_name(),
base.clone(),
@ -1356,7 +1392,7 @@ impl Context {
}
fn import_erg_mod(&self, mod_name: &Literal) -> CompileResult<PathBuf> {
let __name__ = enum_unwrap!(mod_name.value.clone(), ValueObj::Str);
let ValueObj::Str(__name__) = mod_name.value.clone() else { todo!("{mod_name}") };
let mod_cache = self.mod_cache().unwrap();
let py_mod_cache = self.py_mod_cache().unwrap();
let path = match Self::resolve_real_path(&self.cfg, Path::new(&__name__[..])) {
@ -1514,7 +1550,7 @@ impl Context {
}
fn import_py_mod(&self, mod_name: &Literal) -> CompileResult<PathBuf> {
let __name__ = enum_unwrap!(mod_name.value.clone(), ValueObj::Str);
let ValueObj::Str(__name__) = mod_name.value.clone() else { todo!("{mod_name}") };
let py_mod_cache = self.py_mod_cache().unwrap();
let path = self.get_path(mod_name, __name__)?;
if py_mod_cache.get(&path).is_some() {
@ -1586,12 +1622,7 @@ impl Context {
RegistrationMode::Normal,
false,
)?;
let lhs = enum_unwrap!(
call.args.get_mut_left_or_key("pred").unwrap(),
hir::Expr::BinOp
)
.lhs
.as_mut();
let Some(hir::Expr::BinOp(hir::BinOp { lhs, .. })) = call.args.get_mut_left_or_key("pred") else { todo!("{}", call.args) };
match (
self.supertype_of(lhs.ref_t(), &cast_to),
self.subtype_of(lhs.ref_t(), &cast_to),
@ -1602,7 +1633,7 @@ impl Context {
(false, true) => Ok(()), // TODO: warn (needless)
// assert x in Nat (x: Int)
(true, false) => {
if let hir::Expr::Accessor(ref acc) = lhs {
if let hir::Expr::Accessor(ref acc) = lhs.as_ref() {
self.change_var_type(acc, cast_to.clone())?;
}
match lhs.ref_t() {

View file

@ -1017,28 +1017,45 @@ impl ASTLowerer {
}
errs
})?;
// suppress warns of lambda types, e.g. `(x: Int, y: Int) -> Int`
if self.module.context.subtype_of(body.ref_t(), &Type::Type) {
for param in params.non_defaults.iter() {
self.inc_ref(&param.vi, param);
}
if let Some(var_param) = params.var_params.as_deref() {
self.inc_ref(&var_param.vi, var_param);
}
for default in params.defaults.iter() {
self.inc_ref(&default.sig.vi, &default.sig);
}
}
let (non_default_params, default_params): (Vec<_>, Vec<_>) = self
.module
.context
.params
.iter()
.partition(|(_, v)| !v.kind.has_default());
let non_default_params = non_default_params
.into_iter()
// necessary when `py_compatible` feature is enabled
.filter(|(name, _)| {
params
.non_defaults
.iter()
.any(|nd| nd.name() == name.as_ref())
})
.partition(|(_, vi)| !vi.kind.has_default());
#[cfg(not(feature = "py_compatible"))]
let non_default_params = non_default_params.into_iter();
#[cfg(feature = "py_compatible")]
let non_default_params = non_default_params.into_iter().filter(|(name, _)| {
params
.non_defaults
.iter()
.any(|nd| nd.name() == name.as_ref())
});
let non_default_param_tys = non_default_params
.map(|(name, vi)| {
ParamTy::pos(name.as_ref().map(|n| n.inspect().clone()), vi.t.clone())
})
.collect();
#[cfg(not(feature = "py_compatible"))]
let default_params = default_params.into_iter();
#[cfg(feature = "py_compatible")]
let default_params = default_params
.into_iter()
.filter(|(name, _)| params.defaults.iter().any(|d| d.name() == name.as_ref()))
.filter(|(name, _)| params.defaults.iter().any(|d| d.name() == name.as_ref()));
let default_param_tys = default_params
.map(|(name, vi)| ParamTy::kw(name.as_ref().unwrap().inspect().clone(), vi.t.clone()))
.collect();
if in_statement {
@ -1062,9 +1079,9 @@ impl ASTLowerer {
self.pop_append_errs();
}
let ty = if is_procedural {
proc(non_default_params, None, default_params, body.t())
proc(non_default_param_tys, None, default_param_tys, body.t())
} else {
func(non_default_params, None, default_params, body.t())
func(non_default_param_tys, None, default_param_tys, body.t())
};
let t = if ty.has_qvar() { ty.quantify() } else { ty };
Ok(hir::Lambda::new(id, params, lambda.op, body, t))

View file

@ -1,4 +1,5 @@
i = !1
i = !0
i.update! _ -> 1
i.update! i -> i * 3
assert i == 3