and many bugs
This commit is contained in:
Shunsuke Shibayama 2023-04-22 23:43:03 +09:00
parent b444fcac2c
commit 3c40fc35e9
18 changed files with 365 additions and 495 deletions

View file

@ -280,25 +280,13 @@ impl Context {
}
if let Some((_, ty_ctx)) = self.get_nominal_type_ctx(rhs) {
for rhs_sup in ty_ctx.super_classes.iter() {
let rhs_sup = if rhs_sup.has_qvar() {
let rhs = match rhs {
Type::Ref(t) => t,
Type::RefMut { before, .. } => before,
other => other,
};
// let subst_ctx = SubstContext::new(rhs, self, Location::Unknown);
// subst_ctx.substitute(rhs_sup.clone()).unwrap()
rhs.clone()
} else {
rhs_sup.clone()
};
// Not `supertype_of` (only structures are compared)
match Self::cheap_supertype_of(lhs, &rhs_sup) {
match Self::cheap_supertype_of(lhs, rhs_sup) {
(Absolutely, true) => {
return (Absolutely, true);
}
(Maybe, _) => {
if self.structural_supertype_of(lhs, &rhs_sup) {
if self.structural_supertype_of(lhs, rhs_sup) {
return (Absolutely, true);
}
}
@ -661,10 +649,10 @@ impl Context {
// Not(Eq) :> Float == !(Eq :> Float) == true
(Not(_), Obj) => false,
(Not(l), rhs) => !self.supertype_of(l, rhs),
// RefMut are invariant
// Ref T :> RefMut T :> T
(Ref(l), Ref(r)) => self.supertype_of(l, r),
// TはすべてのRef(T)のメソッドを持つので、Ref(T)のサブタイプ
// REVIEW: RefMut is invariant, maybe
(Ref(l), RefMut { before: r, .. }) => self.supertype_of(l, r),
(RefMut { before: l, .. }, RefMut { before: r, .. }) => self.supertype_of(l, r),
(Ref(l), r) => self.supertype_of(l, r),
(RefMut { before: l, .. }, r) => self.supertype_of(l, r),
// `Eq(Set(T, N)) :> Set(T, N)` will be false, such cases are judged by nominal_supertype_of
@ -834,7 +822,7 @@ impl Context {
Ok(t) => t,
Err(err) => {
log!("supertype_of_tp: {err}");
return false;
Type::Obj
}
};
if variance == Variance::Contravariant {
@ -842,7 +830,7 @@ impl Context {
} else if variance == Variance::Covariant {
self.supertype_of(&fv_t, &rp_t)
} else {
self.same_type_of(&fv_t, &rp_t)
self.same_type_of(&fv_t, &rp_t) || self.same_type_of(&fv_t, &rp_t.derefine())
}
}
_ => self.eq_tp(lp, rp),

View file

@ -5,7 +5,6 @@ use erg_common::error::Location;
#[allow(unused)]
use erg_common::log;
use erg_common::set::Set;
use erg_common::shared::Shared;
use erg_common::traits::{Locational, Stream};
use erg_common::{dict, fmt_vec, fn_name, option_enum_unwrap, set};
use erg_common::{RcArray, Str};
@ -20,7 +19,7 @@ use crate::ty::constructors::{
array_t, dict_t, mono, poly, proj, proj_call, ref_, ref_mut, refinement, subr_t, tuple_t,
v_enum,
};
use crate::ty::free::{Constraint, FreeTyVar, HasLevel};
use crate::ty::free::{FreeTyVar, HasLevel};
use crate::ty::typaram::{OpKind, TyParam};
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{ConstSubr, HasType, Predicate, SubrKind, Type, UserConstSubr, ValueArgs};
@ -88,7 +87,6 @@ fn op_to_name(op: OpKind) -> &'static str {
OpKind::BitXor => "__bitxor__",
OpKind::Shl => "__shl__",
OpKind::Shr => "__shr__",
OpKind::Mutate => "__mutate__",
}
}
@ -115,7 +113,6 @@ impl Context {
TokenKind::BitOr => Ok(OpKind::BitOr),
TokenKind::Shl => Ok(OpKind::Shl),
TokenKind::Shr => Ok(OpKind::Shr),
TokenKind::Mutate => Ok(OpKind::Mutate),
TokenKind::PrePlus => Ok(OpKind::Pos),
TokenKind::PreMinus => Ok(OpKind::Neg),
TokenKind::PreBitNot => Ok(OpKind::Invert),
@ -817,9 +814,6 @@ impl Context {
rhs: TyParam,
) -> EvalResult<TyParam> {
match (lhs, rhs) {
(TyParam::Value(ValueObj::Mut(lhs)), TyParam::Value(rhs)) => self
.eval_bin(op, lhs.borrow().clone(), rhs)
.map(|v| TyParam::Value(ValueObj::Mut(Shared::new(v)))),
(TyParam::Value(lhs), TyParam::Value(rhs)) => {
self.eval_bin(op, lhs, rhs).map(TyParam::value)
}
@ -880,7 +874,6 @@ impl Context {
line!(),
))),
},
Mutate => Ok(ValueObj::Mut(Shared::new(val))),
_other => unreachable_error!(self),
}
}
@ -891,15 +884,7 @@ impl Context {
TyParam::FreeVar(fv) if fv.is_linked() => self.eval_unary_tp(op, fv.crack().clone()),
e @ TyParam::Erased(_) => Ok(e),
TyParam::FreeVar(fv) if fv.is_unbound() => {
let t = fv.get_type().unwrap();
if op == OpKind::Mutate {
let constr = Constraint::new_type_of(t.mutate());
fv.update_constraint(constr, false);
let tp = TyParam::FreeVar(fv);
Ok(tp)
} else {
feature_error!(self, Location::Unknown, &format!("{op} {fv}"))
}
feature_error!(self, Location::Unknown, &format!("{op} {fv}"))
}
other => feature_error!(self, Location::Unknown, &format!("{op} {other}")),
}
@ -1620,7 +1605,6 @@ impl Context {
pub(crate) fn get_tp_t(&self, p: &TyParam) -> EvalResult<Type> {
let p = self.eval_tp(p.clone())?;
match p {
TyParam::Value(ValueObj::Mut(v)) => Ok(v.borrow().class().mutate()),
TyParam::Value(v) => Ok(v_enum(set![v])),
TyParam::Erased(t) => Ok((*t).clone()),
TyParam::FreeVar(fv) if fv.is_linked() => self.get_tp_t(&fv.crack()),
@ -1655,10 +1639,6 @@ impl Context {
Ok(tuple_t(tps_t))
}
dict @ TyParam::Dict(_) => Ok(dict_t(dict)),
TyParam::UnaryOp { op, val } => match op {
OpKind::Mutate => Ok(self.get_tp_t(&val)?.mutate()),
_ => feature_error!(self, Location::Unknown, "??"),
},
TyParam::BinOp { op, lhs, rhs } => {
let op_name = op_to_name(op);
feature_error!(

View file

@ -665,10 +665,13 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
Ok(self.ctx.complement(&ty))
}
Type::Proj { lhs, rhs } => {
let lhs = self.deref_tyvar(*lhs)?;
let proj = self
.ctx
.eval_proj(lhs, rhs, self.ctx.level, self.loc)
.eval_proj(*lhs.clone(), rhs.clone(), self.ctx.level, self.loc)
.or_else(|_| {
let lhs = self.deref_tyvar(*lhs)?;
self.ctx.eval_proj(lhs, rhs, self.ctx.level, self.loc)
})
.unwrap_or(Failure);
Ok(proj)
}

View file

@ -1234,10 +1234,8 @@ impl Context {
.quantify();
array_.register_builtin_erg_impl(FUNC_PUSH, t, Immutable, Visibility::BUILTIN_PUBLIC);
// [T; N].MutType! = [T; !N] (neither [T!; N] nor [T; N]!)
let mut_type = ValueObj::builtin_class(poly(
MUT_ARRAY,
vec![TyParam::t(T.clone()), N.clone().mutate()],
));
let mut_type =
ValueObj::builtin_class(poly(MUT_ARRAY, vec![TyParam::t(T.clone()), N.clone()]));
array_.register_builtin_const(MUTABLE_MUT_TYPE, Visibility::BUILTIN_PUBLIC, mut_type);
let var = StrStruct::from(fresh_varname());
let input = refinement(
@ -1889,20 +1887,17 @@ impl Context {
file_mut.register_marker_trait(mono(MUT_FILE_LIKE));
file_mut.register_marker_trait(mono(CONTEXT_MANAGER));
/* Array! */
let N_MUT = mono_q_tp(TY_N, instanceof(mono(MUT_NAT)));
let array_mut_t = poly(MUT_ARRAY, vec![ty_tp(T.clone()), N_MUT.clone()]);
let mut array_mut_ = Self::builtin_poly_class(
MUT_ARRAY,
vec![PS::t_nd(TY_T), PS::named_nd(TY_N, mono(MUT_NAT))],
2,
);
let N = mono_q_tp(TY_N, instanceof(Nat));
let array_mut_t = poly(MUT_ARRAY, vec![ty_tp(T.clone()), N.clone()]);
let mut array_mut_ =
Self::builtin_poly_class(MUT_ARRAY, vec![PS::t_nd(TY_T), PS::named_nd(TY_N, Nat)], 2);
array_mut_.register_superclass(arr_t.clone(), &array_);
let t = pr_met(
ref_mut(
array_mut_t.clone(),
Some(poly(
MUT_ARRAY,
vec![ty_tp(T.clone()), N_MUT.clone() + value(1usize)],
vec![ty_tp(T.clone()), N.clone() + value(1usize)],
)),
),
vec![kw(KW_ELEM, T.clone())],
@ -1917,7 +1912,7 @@ impl Context {
array_mut_t.clone(),
Some(poly(
MUT_ARRAY,
vec![ty_tp(T.clone()), TyParam::erased(mono(MUT_NAT))],
vec![ty_tp(T.clone()), TyParam::erased(Nat)],
)),
),
vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))],
@ -1932,7 +1927,7 @@ impl Context {
array_mut_t.clone(),
Some(poly(
MUT_ARRAY,
vec![ty_tp(T.clone()), N_MUT.clone() + value(1usize)],
vec![ty_tp(T.clone()), N.clone() + value(1usize)],
)),
),
vec![kw(KW_INDEX, Nat), kw(KW_ELEM, T.clone())],
@ -1947,7 +1942,7 @@ impl Context {
array_mut_t.clone(),
Some(poly(
MUT_ARRAY,
vec![ty_tp(T.clone()), N_MUT.clone() - value(1usize)],
vec![ty_tp(T.clone()), N.clone() - value(1usize)],
)),
),
vec![kw(KW_X, T.clone())],
@ -1962,7 +1957,7 @@ impl Context {
array_mut_t.clone(),
Some(poly(
MUT_ARRAY,
vec![ty_tp(T.clone()), N_MUT.clone() - value(1usize)],
vec![ty_tp(T.clone()), N.clone() - value(1usize)],
)),
),
vec![],
@ -2049,21 +2044,15 @@ impl Context {
.quantify();
dict_mut.register_py_builtin(PROC_INSERT, insert_t, Some(FUNDAMENTAL_SETITEM), 12);
/* Set! */
let set_mut_t = poly(MUT_SET, vec![ty_tp(T.clone()), N_MUT]);
let mut set_mut_ = Self::builtin_poly_class(
MUT_SET,
vec![PS::t_nd(TY_T), PS::named_nd(TY_N, mono(MUT_NAT))],
2,
);
let set_mut_t = poly(MUT_SET, vec![ty_tp(T.clone()), N]);
let mut set_mut_ =
Self::builtin_poly_class(MUT_SET, vec![PS::t_nd(TY_T), PS::named_nd(TY_N, Nat)], 2);
set_mut_.register_superclass(set_t.clone(), &set_);
// `add!` will erase N
let t = pr_met(
ref_mut(
set_mut_t.clone(),
Some(poly(
MUT_SET,
vec![ty_tp(T.clone()), TyParam::erased(mono(MUT_NAT))],
)),
Some(poly(MUT_SET, vec![ty_tp(T.clone()), TyParam::erased(Nat)])),
),
vec![kw(KW_ELEM, T.clone())],
None,

View file

@ -455,6 +455,32 @@ impl Context {
Triple::None
}
pub(crate) fn rec_get_mut_var_info(
&mut self,
ident: &Identifier,
acc_kind: AccessKind,
) -> Option<&mut VarInfo> {
if let Some(vi) = self.get_current_scope_var(&ident.name) {
match self.validate_visibility(ident, vi, &self.cfg.input, self) {
Ok(()) => {
let vi = self.get_mut_current_scope_var(&ident.name).unwrap();
return Some(vi);
}
Err(_err) => {
if !acc_kind.is_local() {
return None;
}
}
}
}
if acc_kind.is_local() {
if let Some(parent) = self.get_mut_outer() {
return parent.rec_get_mut_var_info(ident, acc_kind);
}
}
None
}
pub(crate) fn rec_get_decl_info(
&self,
ident: &Identifier,
@ -1094,19 +1120,36 @@ impl Context {
/// Propagate mutable dependent types changes
/// 可変依存型の変更を伝搬させる
fn propagate(&self, t: &Type, callee: &hir::Expr) -> TyCheckResult<()> {
if let Type::Subr(subr) = t {
if let Some(after) = subr.self_t().and_then(|self_t| {
if let RefMut { after, .. } = self_t {
after.as_ref()
} else {
None
/// ```erg
/// v = ![] # Γ: { v: [Int; 0]! }
/// v.push! 1 # v: [Int; 0]! ~> [Int; 1]!; Γ: { v: [Int; 1]! }
/// v # : [Int; 1]!
/// ```
pub(crate) fn propagate(
&mut self,
subr_t: &mut Type,
receiver: &hir::Expr,
) -> TyCheckResult<()> {
if let Type::Subr(subr) = subr_t {
if let Some(self_t) = subr.mut_self_t() {
log!(info "Propagating:\n {self_t}");
if let RefMut {
after: Some(after), ..
} = self_t
{
log!(info "~> {after}\n");
*self_t = *after.clone();
if let hir::Expr::Accessor(hir::Accessor::Ident(ident)) = receiver {
if let Some(vi) = self.rec_get_mut_var_info(&ident.raw, AccessKind::Name) {
vi.t = self_t.clone();
}
}
}
}) {
self.reunify(callee.ref_t(), after, callee)?;
}
Ok(())
} else {
Ok(())
}
Ok(())
}
fn not_callable_error(
@ -1541,6 +1584,7 @@ impl Context {
TyCheckErrors::new(
errs.into_iter()
.map(|e| {
log!("err: {e}");
TyCheckError::type_mismatch_error(
self.cfg.input.clone(),
line!() as usize,
@ -1742,16 +1786,6 @@ impl Context {
.eval_t_params(instance, self.level, obj)
.map_err(|(t, errs)| (Some(VarInfo { t, ..found.clone() }), errs))?;
log!(info "Params evaluated:\nres: {res}\n");
self.propagate(&res, obj).map_err(|errs| {
(
Some(VarInfo {
t: res.clone(),
..found.clone()
}),
errs,
)
})?;
log!(info "Propagated:\nres: {res}\n");
let res = VarInfo { t: res, ..found };
Ok(res)
}
@ -2085,7 +2119,10 @@ impl Context {
/// include `typ` itself.
/// if `typ` is a refinement type, include the base type (refine.t)
pub(crate) fn _get_super_classes(&self, typ: &Type) -> Option<impl Iterator<Item = Type>> {
pub(crate) fn _get_super_classes(
&self,
typ: &Type,
) -> Option<impl Iterator<Item = Type> + Clone> {
self.get_nominal_type_ctx(typ).map(|(t, ctx)| {
let super_classes = ctx.super_classes.clone();
let derefined = typ.derefine();
@ -2097,6 +2134,28 @@ impl Context {
})
}
pub(crate) fn _get_super_types(
&self,
typ: &Type,
) -> Option<impl Iterator<Item = Type> + Clone> {
self.get_nominal_type_ctx(typ).map(|(t, ctx)| {
let super_classes = ctx.super_classes.clone();
let super_traits = ctx.super_traits.clone();
let derefined = typ.derefine();
if typ != &derefined {
vec![t.clone(), derefined]
.into_iter()
.chain(super_classes)
.chain(super_traits)
} else {
vec![t.clone()]
.into_iter()
.chain(super_classes)
.chain(super_traits)
}
})
}
// TODO: Never
pub(crate) fn get_nominal_type_ctx<'a>(
&'a self,

View file

@ -516,8 +516,16 @@ impl Context {
}
FreeVar(fv) => {
if let Some((sub, sup)) = fv.get_subsup() {
let sub = self.instantiate_t_inner(sub, tmp_tv_cache, loc)?;
let sup = self.instantiate_t_inner(sup, tmp_tv_cache, loc)?;
let sub = if sub.is_recursive() {
sub
} else {
self.instantiate_t_inner(sub, tmp_tv_cache, loc)?
};
let sup = if sup.is_recursive() {
sup
} else {
self.instantiate_t_inner(sup, tmp_tv_cache, loc)?
};
let new_constraint = Constraint::new_sandwiched(sub, sup);
fv.update_constraint(new_constraint, true);
} else if let Some(ty) = fv.get_type() {

View file

@ -652,9 +652,11 @@ impl Context {
let name = &sig.ident.name;
// FIXME: constでない関数
let t = self.get_current_scope_var(name).map(|vi| &vi.t).unwrap();
let non_default_params = t.non_default_params().unwrap();
debug_assert!(t.is_subr());
let empty = vec![];
let non_default_params = t.non_default_params().unwrap_or(&empty);
let var_args = t.var_params();
let default_params = t.default_params().unwrap();
let default_params = t.default_params().unwrap_or(&empty);
if let Some(spec_ret_t) = t.return_t() {
let unify_result = if let Some(t_spec) = sig.return_t_spec.as_ref() {
self.sub_unify(body_t, spec_ret_t, t_spec, None)

View file

@ -3,12 +3,10 @@ use std::mem;
use std::option::Option;
use erg_common::fresh::fresh_varname;
use erg_common::style::Stylize;
use erg_common::traits::Locational;
use erg_common::Str;
#[allow(unused_imports)]
use erg_common::{fmt_vec, log};
use erg_common::{fn_name, switch_lang};
use erg_common::{fmt_vec, fn_name, log};
use crate::ty::constructors::*;
use crate::ty::free::{Constraint, FreeKind, HasLevel, GENERIC_LEVEL};
@ -17,7 +15,7 @@ use crate::ty::value::ValueObj;
use crate::ty::{Predicate, SubrType, Type};
use crate::context::{Context, Variance};
use crate::error::{SingleTyCheckResult, TyCheckError, TyCheckErrors, TyCheckResult};
use crate::error::{TyCheckError, TyCheckErrors, TyCheckResult};
use crate::{feature_error, type_feature_error};
use Predicate as Pred;
@ -443,48 +441,6 @@ impl Context {
}
}
fn reunify_tp(
&self,
before: &TyParam,
after: &TyParam,
loc: &impl Locational,
) -> SingleTyCheckResult<()> {
match (before, after) {
(TyParam::Value(ValueObj::Mut(l)), TyParam::Value(ValueObj::Mut(r))) => {
*l.borrow_mut() = r.borrow().clone();
Ok(())
}
(TyParam::Value(ValueObj::Mut(l)), TyParam::Value(r)) => {
*l.borrow_mut() = r.clone();
Ok(())
}
(TyParam::Type(l), TyParam::Type(r)) => self.reunify(l, r, loc),
(TyParam::UnaryOp { op: lop, val: lval }, TyParam::UnaryOp { op: rop, val: rval })
if lop == rop =>
{
self.reunify_tp(lval, rval, loc)
}
(
TyParam::BinOp { op: lop, lhs, rhs },
TyParam::BinOp {
op: rop,
lhs: lhs2,
rhs: rhs2,
},
) if lop == rop => {
self.reunify_tp(lhs, lhs2, loc)?;
self.reunify_tp(rhs, rhs2, loc)
}
(TyParam::Lambda(_l), TyParam::Lambda(_r)) => {
todo!("{_l}/{_r}")
}
(l, r) if self.eq_tp(l, r) => Ok(()),
(l, r) => {
type_feature_error!(error self, loc.loc(), &format!("re-unifying {l} ~> {r}"))
}
}
}
/// predは正規化されているとする
fn sub_unify_pred(
&self,
@ -617,82 +573,6 @@ impl Context {
}
}
/// T: Array(Int, !0), U: Array(Int, !1)
/// reunify(T, U):
/// T: Array(Int, !1), U: Array(Int, !1)
pub(crate) fn reunify(
&self,
before_t: &Type,
after_t: &Type,
loc: &impl Locational,
) -> SingleTyCheckResult<()> {
match (before_t, after_t) {
(FreeVar(fv), r) if fv.is_linked() => self.reunify(&fv.crack(), r, loc),
(l, FreeVar(fv)) if fv.is_linked() => self.reunify(l, &fv.crack(), loc),
(Ref(l), Ref(r)) => self.reunify(l, r, loc),
(
RefMut {
before: lbefore,
after: lafter,
},
RefMut {
before: rbefore,
after: rafter,
},
) => {
self.reunify(lbefore, rbefore, loc)?;
match (lafter, rafter) {
(Some(lafter), Some(rafter)) => {
self.reunify(lafter, rafter, loc)?;
}
(None, None) => {}
_ => todo!(),
}
Ok(())
}
(Ref(l), r) => self.reunify(l, r, loc),
// REVIEW:
(RefMut { before, .. }, r) => self.reunify(before, r, loc),
(l, Ref(r)) => self.reunify(l, r, loc),
(l, RefMut { before, .. }) => self.reunify(l, before, loc),
(
Poly {
name: ln,
params: lps,
},
Poly {
name: rn,
params: rps,
},
) => {
if ln != rn {
let before_t = poly(ln.clone(), lps.clone());
return Err(TyCheckError::re_unification_error(
self.cfg.input.clone(),
line!() as usize,
&before_t,
after_t,
loc.loc(),
self.caused_by(),
));
}
for (l, r) in lps.iter().zip(rps.iter()) {
self.reunify_tp(l, r, loc)?;
}
Ok(())
}
(l, r) if self.same_type_of(l, r) => Ok(()),
(l, r) => Err(TyCheckError::re_unification_error(
self.cfg.input.clone(),
line!() as usize,
l,
r,
loc.loc(),
self.caused_by(),
)),
}
}
/// Assuming that `sub` is a subtype of `sup`, fill in the type variable to satisfy the assumption
///
/// When comparing arguments and parameter, the left side (`sub`) is the argument (found) and the right side (`sup`) is the parameter (expected)
@ -789,7 +669,24 @@ impl Context {
sup_fv.undo();
let intersec = self.intersection(&lsup, &rsup);
let new_constraint = if intersec != Type::Never {
Constraint::new_sandwiched(self.union(&lsub, &rsub), intersec)
let union = self.union(&lsub, &rsub);
if !lsub.has_union_type() && !rsub.has_union_type() && union.has_union_type() {
let (l, r) = union.union_pair().unwrap_or((lsub, rsub));
let unified = self.unify(&l, &r);
if unified.is_none() {
return Err(TyCheckErrors::from(
TyCheckError::implicit_widening_error(
self.cfg.input.clone(),
line!() as usize,
loc.loc(),
self.caused_by(),
maybe_sub,
maybe_sup,
),
));
}
}
Constraint::new_sandwiched(union, intersec)
} else {
return Err(TyCheckErrors::from(TyCheckError::subtyping_error(
self.cfg.input.clone(),
@ -873,34 +770,23 @@ impl Context {
// Expanding to an Or-type is prohibited by default
// This increases the quality of error reporting
// (Try commenting out this part and run tests/should_err/subtyping.er to see the error report changes on lines 29-30)
if !maybe_sub.is_union_type() && !sub.is_union_type() && new_sub.is_union_type()
if !maybe_sub.has_union_type()
&& !sub.has_union_type()
&& new_sub.has_union_type()
{
let (l, r) = new_sub.union_pair().unwrap();
if self.unify(&l, &r).is_none() {
let maybe_sub_ = maybe_sub
.to_string()
.with_color(erg_common::style::Color::Yellow);
let new_sub = new_sub
.to_string()
.with_color(erg_common::style::Color::Yellow);
let hint = switch_lang!(
"japanese" => format!("{maybe_sub_}から{new_sub}への暗黙の型拡大はデフォルトでは禁止されています。明示的に型指定してください"),
"simplified_chinese" => format!("隐式扩展{maybe_sub_}{new_sub}被默认禁止。请明确指定类型。"),
"traditional_chinese" => format!("隱式擴展{maybe_sub_}{new_sub}被默認禁止。請明確指定類型。"),
"english" => format!("Implicitly widening {maybe_sub_} to {new_sub} is prohibited by default. Consider specifying the type explicitly."),
);
return Err(TyCheckErrors::from(TyCheckError::type_mismatch_error(
self.cfg.input.clone(),
line!() as usize,
loc.loc(),
self.caused_by(),
"",
None,
maybe_sub,
maybe_sup,
None,
Some(hint),
)));
let (l, r) = new_sub.union_pair().unwrap_or((maybe_sub.clone(), sub));
let unified = self.unify(&l, &r);
if unified.is_none() {
return Err(TyCheckErrors::from(
TyCheckError::implicit_widening_error(
self.cfg.input.clone(),
line!() as usize,
loc.loc(),
self.caused_by(),
maybe_sub,
maybe_sup,
),
));
}
}
if sup.contains_union(&new_sub) {
@ -1272,11 +1158,16 @@ impl Context {
Self::undo_substitute_typarams(sub_def_t);
errs
})?;
for sup_trait in sub_ctx.super_traits.iter() {
let sub_trait_instance = self.instantiate_def_type(sup_trait)?;
if self.supertype_of(maybe_sup, sup_trait) {
let sups = if self.is_class(maybe_sup) {
sub_ctx.super_classes.iter()
} else {
sub_ctx.super_traits.iter()
};
for sup_ty in sups {
let sub_instance = self.instantiate_def_type(sup_ty)?;
if self.supertype_of(maybe_sup, sup_ty) {
for (l_maybe_sub, r_maybe_sup) in
sub_trait_instance.typarams().iter().zip(sup_params.iter())
sub_instance.typarams().iter().zip(sup_params.iter())
{
self.sub_unify_tp(l_maybe_sub, r_maybe_sup, None, loc, false)
.map_err(|errs| {
@ -1304,15 +1195,28 @@ impl Context {
///
/// Error if they can't unify without upcasting both types (derefining is allowed) or using the Or type
/// ```erg
/// unify(Int, Nat) == Ok(Int)
/// unify(Int, Str) == Err
/// unify({1.2}, Nat) == Ok(Float)
/// unify(Eq, Int) == Ok(Eq)
/// unify(Eq, Float) == Err
/// unify(Int, Nat) == Some(Int)
/// unify(Int, Str) == None
/// unify({1.2}, Nat) == Some(Float)
/// unify(Nat, Int!) == Some(Int)
/// unify(Eq, Int) == None
/// ```
pub fn unify(&self, lhs: &Type, rhs: &Type) -> Option<Type> {
let lhs = lhs.derefine();
let rhs = rhs.derefine();
self.max(&lhs, &rhs).cloned()
let l_sups = self._get_super_classes(lhs)?;
let r_sups = self._get_super_classes(rhs)?;
for l_sup in l_sups {
if self.supertype_of(&l_sup, &Obj) {
continue;
}
for r_sup in r_sups.clone() {
if self.supertype_of(&r_sup, &Obj) {
continue;
}
if let Some(t) = self.max(&l_sup, &r_sup) {
return Some(t.clone());
}
}
}
None
}
}

View file

@ -1217,4 +1217,38 @@ passed keyword args: {kw_args_len}"
caused_by,
)
}
pub fn implicit_widening_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
expect: &Type,
found: &Type,
) -> Self {
let maybe_sub_ = expect
.to_string()
.with_color(erg_common::style::Color::Yellow);
let new_sub = found
.to_string()
.with_color(erg_common::style::Color::Yellow);
let hint = switch_lang!(
"japanese" => format!("{maybe_sub_}から{new_sub}への暗黙の型拡大はデフォルトでは禁止されています。明示的に型指定してください"),
"simplified_chinese" => format!("隐式扩展{maybe_sub_}{new_sub}被默认禁止。请明确指定类型。"),
"traditional_chinese" => format!("隱式擴展{maybe_sub_}{new_sub}被默認禁止。請明確指定類型。"),
"english" => format!("Implicitly widening {maybe_sub_} to {new_sub} is prohibited by default. Consider specifying the type explicitly."),
);
Self::type_mismatch_error(
input,
errno,
loc,
caused_by,
"",
None,
expect,
found,
None,
Some(hint),
)
}
}

View file

@ -24,8 +24,7 @@ use crate::artifact::{CompleteArtifact, IncompleteArtifact};
use crate::context::instantiate::TyVarCache;
use crate::module::SharedCompilerResource;
use crate::ty::constructors::{
array_mut, array_t, free_var, func, guard, mono, poly, proc, refinement, set_mut, set_t, ty_tp,
v_enum,
array_t, free_var, func, guard, mono, poly, proc, refinement, set_t, ty_tp, v_enum,
};
use crate::ty::free::Constraint;
use crate::ty::typaram::TyParam;
@ -333,38 +332,10 @@ impl ASTLowerer {
fn gen_array_with_length_type(&self, elem: &hir::Expr, len: &ast::Expr) -> Type {
let maybe_len = self.module.context.eval_const_expr(len);
match maybe_len {
Ok(v @ ValueObj::Nat(_)) => {
if elem.ref_t().is_mut_type() {
poly(
"ArrayWithMutType!",
vec![TyParam::t(elem.t()), TyParam::Value(v)],
)
} else {
array_t(elem.t(), TyParam::Value(v))
}
}
Ok(v @ ValueObj::Mut(_)) if v.class() == mono("Nat!") => {
if elem.ref_t().is_mut_type() {
poly(
"ArrayWithMutTypeAndLength!",
vec![TyParam::t(elem.t()), TyParam::Value(v)],
)
} else {
array_mut(elem.t(), TyParam::Value(v))
}
}
Ok(v @ ValueObj::Nat(_)) => array_t(elem.t(), TyParam::Value(v)),
Ok(other) => todo!("{other} is not a Nat object"),
// REVIEW: is it ok to ignore the error?
Err(_e) => {
if elem.ref_t().is_mut_type() {
poly(
"ArrayWithMutType!",
vec![TyParam::t(elem.t()), TyParam::erased(Type::Nat)],
)
} else {
array_t(elem.t(), TyParam::erased(Type::Nat))
}
}
Err(_e) => array_t(elem.t(), TyParam::erased(Type::Nat)),
}
}
@ -510,38 +481,14 @@ impl ASTLowerer {
let maybe_len = self.module.context.eval_const_expr(len);
match maybe_len {
Ok(v @ ValueObj::Nat(_)) => {
if elem.ref_t().is_mut_type() {
poly(
"SetWithMutType!",
vec![TyParam::t(elem.t()), TyParam::Value(v)],
)
} else if self.module.context.subtype_of(&elem.t(), &Type::Type) {
if self.module.context.subtype_of(&elem.t(), &Type::Type) {
poly("SetType", vec![TyParam::t(elem.t()), TyParam::Value(v)])
} else {
set_t(elem.t(), TyParam::Value(v))
}
}
Ok(v @ ValueObj::Mut(_)) if v.class() == mono("Nat!") => {
if elem.ref_t().is_mut_type() {
poly(
"SetWithMutTypeAndLength!",
vec![TyParam::t(elem.t()), TyParam::Value(v)],
)
} else {
set_mut(elem.t(), TyParam::Value(v))
}
}
Ok(other) => todo!("{other} is not a Nat object"),
Err(_e) => {
if elem.ref_t().is_mut_type() {
poly(
"SetWithMutType!",
vec![TyParam::t(elem.t()), TyParam::erased(Type::Nat)],
)
} else {
set_t(elem.t(), TyParam::erased(Type::Nat))
}
}
Err(_e) => set_t(elem.t(), TyParam::erased(Type::Nat)),
}
}
@ -995,6 +942,9 @@ impl ASTLowerer {
vi.unwrap_or(VarInfo::ILLEGAL.clone())
}
};
if let Err(es) = self.module.context.propagate(&mut vi.t, &obj) {
errs.extend(es);
}
if let Some(guard) = guard {
debug_assert!(self
.module

View file

@ -473,6 +473,19 @@ impl SubrType {
})
}
pub fn mut_self_t(&mut self) -> Option<&mut Type> {
self.non_default_params.first_mut().and_then(|p| {
if p.name()
.map(|n| &n[..] == "self" || &n[..] == "Self")
.unwrap_or(false)
{
Some(p.typ_mut())
} else {
None
}
})
}
pub fn is_method(&self) -> bool {
self.self_t().is_some()
}
@ -1697,6 +1710,57 @@ impl Type {
}
}
pub fn has_union_type(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().has_union_type(),
Self::FreeVar(fv) if fv.constraint_is_sandwiched() => {
let (sub, sup) = fv.get_subsup().unwrap();
fv.dummy_link();
let res = sub.has_union_type() || sup.has_union_type();
fv.undo();
res
}
Self::Or(_, _) => true,
Self::Refinement(refine) => refine.t.has_union_type(),
Self::Ref(t) => t.has_union_type(),
Self::RefMut { before, after } => {
before.has_union_type()
|| after.as_ref().map(|t| t.has_union_type()).unwrap_or(false)
}
Self::And(lhs, rhs) => lhs.has_union_type() || rhs.has_union_type(),
Self::Not(ty) => ty.has_union_type(),
Self::Callable { param_ts, return_t } => {
param_ts.iter().any(|t| t.has_union_type()) || return_t.has_union_type()
}
Self::Subr(subr) => {
subr.non_default_params
.iter()
.any(|pt| pt.typ().has_union_type())
|| subr
.var_params
.as_ref()
.map(|pt| pt.typ().has_union_type())
.unwrap_or(false)
|| subr
.default_params
.iter()
.any(|pt| pt.typ().has_union_type())
|| subr.return_t.has_union_type()
}
Self::Record(r) => r.values().any(|t| t.has_union_type()),
Self::Quantified(quant) => quant.has_union_type(),
Self::Poly { params, .. } => params.iter().any(|p| p.has_union_type()),
Self::Proj { lhs, .. } => lhs.has_union_type(),
Self::ProjCall { lhs, args, .. } => {
lhs.has_union_type() || args.iter().any(|t| t.has_union_type())
}
Self::Structural(ty) => ty.has_union_type(),
Self::Guard(guard) => guard.to.has_union_type(),
Self::Bounded { sub, sup } => sub.has_union_type() || sup.has_union_type(),
_ => false,
}
}
pub fn is_refinement(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_refinement(),
@ -2385,7 +2449,7 @@ impl Type {
Self::Refinement(refine) => refine.t.has_unbound_var() || refine.pred.has_unbound_var(),
Self::Quantified(quant) => quant.has_unbound_var(),
Self::Poly { params, .. } => params.iter().any(|p| p.has_unbound_var()),
Self::Proj { lhs, .. } => lhs.has_no_unbound_var(),
Self::Proj { lhs, .. } => lhs.has_unbound_var(),
Self::ProjCall { lhs, args, .. } => {
lhs.has_unbound_var() || args.iter().any(|t| t.has_unbound_var())
}

View file

@ -48,7 +48,6 @@ pub enum OpKind {
BitXor,
Shl,
Shr,
Mutate,
}
impl fmt::Display for OpKind {
@ -79,7 +78,6 @@ impl fmt::Display for OpKind {
Self::BitXor => write!(f, "^^"),
Self::Shl => write!(f, "<<"),
Self::Shr => write!(f, ">>"),
Self::Mutate => write!(f, "!"),
}
}
}
@ -823,11 +821,6 @@ impl TyParam {
}
}
#[inline]
pub fn mutate(self) -> Self {
Self::unary(OpKind::Mutate, self)
}
#[inline]
pub fn bin(op: OpKind, lhs: TyParam, rhs: TyParam) -> Self {
Self::BinOp {
@ -1056,6 +1049,27 @@ impl TyParam {
!self.has_unbound_var()
}
pub fn has_union_type(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().has_union_type(),
Self::Type(t) => t.has_union_type(),
Self::Proj { obj, .. } => obj.has_union_type(),
Self::Array(ts) | Self::Tuple(ts) => ts.iter().any(|t| t.has_union_type()),
Self::Set(ts) => ts.iter().any(|t| t.has_union_type()),
Self::Dict(kv) => kv
.iter()
.any(|(k, v)| k.has_union_type() || v.has_union_type()),
Self::Record(rec) => rec.iter().any(|(_, v)| v.has_union_type()),
Self::Lambda(lambda) => lambda.body.iter().any(|t| t.has_union_type()),
Self::UnaryOp { val, .. } => val.has_union_type(),
Self::BinOp { lhs, rhs, .. } => lhs.has_union_type() || rhs.has_union_type(),
Self::App { args, .. } => args.iter().any(|p| p.has_union_type()),
Self::Erased(t) => t.has_union_type(),
Self::Value(ValueObj::Type(t)) => t.typ().has_union_type(),
_ => false,
}
}
pub fn has_upper_bound(&self) -> bool {
match self {
// TODO: 型によっては上限がある

View file

@ -4,7 +4,6 @@
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Neg;
use std::rc::Rc;
@ -15,7 +14,6 @@ use erg_common::fresh::fresh_varname;
use erg_common::python_util::PythonVersion;
use erg_common::serialize::*;
use erg_common::set::Set;
use erg_common::shared::Shared;
use erg_common::{dict, fmt_iter, impl_display_from_debug, log, switch_lang};
use erg_common::{RcArray, Str};
use erg_parser::ast::{ConstArgs, ConstExpr};
@ -25,7 +23,7 @@ use crate::context::eval::type_from_token_kind;
use self::value_set::inner_class;
use super::codeobj::CodeObj;
use super::constructors::{array_t, dict_t, mono, poly, refinement, set_t, tuple_t};
use super::constructors::{array_t, dict_t, refinement, set_t, tuple_t};
use super::typaram::TyParam;
use super::{ConstSubr, Field, HasType, Predicate, Type};
@ -455,7 +453,6 @@ pub enum ValueObj {
NotImplemented,
NegInf,
Inf,
Mut(Shared<ValueObj>),
#[default]
Illegal, // to avoid conversions with TryFrom
}
@ -538,7 +535,6 @@ impl fmt::Debug for ValueObj {
Self::NotImplemented => write!(f, "NotImplemented"),
Self::NegInf => write!(f, "-Inf"),
Self::Inf => write!(f, "Inf"),
Self::Mut(v) => write!(f, "<mut {}>", v.borrow()),
Self::Illegal => write!(f, "<illegal>"),
}
}
@ -605,7 +601,6 @@ impl Hash for ValueObj {
"literal".hash(state);
"Inf".hash(state)
}
Self::Mut(v) => v.borrow().hash(state),
Self::Illegal => {
"literal".hash(state);
"illegal".hash(state)
@ -686,7 +681,6 @@ impl TryFrom<&ValueObj> for f64 {
ValueObj::Inf => Ok(f64::INFINITY),
ValueObj::NegInf => Ok(f64::NEG_INFINITY),
ValueObj::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
ValueObj::Mut(v) => f64::try_from(&*v.borrow()).map_err(|_| ()),
_ => Err(()),
}
}
@ -715,7 +709,6 @@ impl TryFrom<ValueObj> for Type {
Ok(tuple_t(new_ts))
}
ValueObj::Subr(subr) => subr.as_type().ok_or(ValueObj::Subr(subr)),
ValueObj::Mut(v) => Type::try_from(v.borrow().clone()),
other => Err(other),
}
}
@ -757,14 +750,14 @@ impl HasType for ValueObj {
}
impl ValueObj {
pub fn builtin_class(t: Type) -> Self {
pub const fn builtin_class(t: Type) -> Self {
ValueObj::Type(TypeObj::Builtin {
t,
meta_t: Type::ClassType,
})
}
pub fn builtin_trait(t: Type) -> Self {
pub const fn builtin_trait(t: Type) -> Self {
ValueObj::Type(TypeObj::Builtin {
t,
meta_t: Type::TraitType,
@ -778,49 +771,35 @@ impl ValueObj {
})
}
pub fn gen_t(gen: GenTypeObj) -> Self {
pub const fn gen_t(gen: GenTypeObj) -> Self {
ValueObj::Type(TypeObj::Generated(gen))
}
// TODO: add Complex
pub fn is_num(&self) -> bool {
match self {
Self::Float(_) | Self::Int(_) | Self::Nat(_) | Self::Bool(_) => true,
Self::Mut(n) => n.borrow().is_num(),
_ => false,
}
pub const fn is_num(&self) -> bool {
matches!(
self,
Self::Float(_) | Self::Int(_) | Self::Nat(_) | Self::Bool(_)
)
}
pub fn is_float(&self) -> bool {
match self {
Self::Float(_) | Self::Int(_) | Self::Nat(_) | Self::Bool(_) => true,
Self::Mut(n) => n.borrow().is_float(),
_ => false,
}
pub const fn is_float(&self) -> bool {
matches!(
self,
Self::Float(_) | Self::Int(_) | Self::Nat(_) | Self::Bool(_)
)
}
pub fn is_int(&self) -> bool {
match self {
Self::Int(_) | Self::Nat(_) | Self::Bool(_) => true,
Self::Mut(n) => n.borrow().is_nat(),
_ => false,
}
pub const fn is_int(&self) -> bool {
matches!(self, Self::Int(_) | Self::Nat(_) | Self::Bool(_))
}
pub fn is_nat(&self) -> bool {
match self {
Self::Nat(_) | Self::Bool(_) => true,
Self::Mut(n) => n.borrow().is_nat(),
_ => false,
}
pub const fn is_nat(&self) -> bool {
matches!(self, Self::Nat(_) | Self::Bool(_))
}
pub fn is_bool(&self) -> bool {
match self {
Self::Bool(_) => true,
Self::Mut(n) => n.borrow().is_bool(),
_ => false,
}
pub const fn is_bool(&self) -> bool {
matches!(self, Self::Bool(_))
}
pub const fn is_str(&self) -> bool {
@ -831,10 +810,6 @@ impl ValueObj {
matches!(self, Self::Type(_))
}
pub const fn is_mut(&self) -> bool {
matches!(self, Self::Mut(_))
}
pub fn from_str(t: Type, content: Str) -> Option<Self> {
match t {
Type::Int => content.replace('_', "").parse::<i32>().ok().map(Self::Int),
@ -986,38 +961,6 @@ impl ValueObj {
Self::NotImplemented => Type::NotImplementedType,
Self::Inf => Type::Inf,
Self::NegInf => Type::NegInf,
Self::Mut(m) => match &*m.borrow() {
Self::Int(_) => mono("Int!"),
Self::Nat(_) => mono("Nat!"),
Self::Float(_) => mono("Float!"),
Self::Str(_) => mono("Str!"),
Self::Bool(_) => mono("Bool!"),
Self::Array(arr) => poly(
"Array!",
vec![
// REVIEW: Never?
TyParam::t(
arr.iter()
.next()
.map(|elem| elem.class())
.unwrap_or(Type::Never),
),
TyParam::value(arr.len()).mutate(),
],
),
Self::Dict(dict) => poly(
"Dict!",
vec![TyParam::Dict(
dict.iter()
.map(|(k, v)| (TyParam::value(k.clone()), TyParam::value(v.clone())))
.collect(),
)],
),
other => {
log!(err "{other} object cannot be mutated");
other.class()
}
},
Self::Illegal => Type::Failure,
}
}
@ -1036,8 +979,6 @@ impl ValueObj {
(Self::Inf, Self::NegInf) => Some(Ordering::Greater),
// REVIEW: 等しいとみなしてよいのか?
(Self::Inf, Self::Inf) | (Self::NegInf, Self::NegInf) => Some(Ordering::Equal),
(Self::Mut(m), other) => m.borrow().try_cmp(other),
(self_, Self::Mut(m)) => self_.try_cmp(&m.borrow()),
/* (Self::PlusEpsilon(l), r) => l.try_cmp(r)
.map(|o| if matches!(o, Ordering::Equal) { Ordering::Less } else { o }),
(l, Self::PlusEpsilon(r)) => l.try_cmp(r)
@ -1073,14 +1014,6 @@ impl ValueObj {
(inf @ (Self::Inf | Self::NegInf), _) | (_, inf @ (Self::Inf | Self::NegInf)) => {
Some(inf)
}
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_add(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_add(m.borrow().clone()),
_ => None,
}
}
@ -1102,14 +1035,6 @@ impl ValueObj {
{
Some(inf)
}
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_sub(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_sub(m.borrow().clone()),
_ => None,
}
}
@ -1129,14 +1054,6 @@ impl ValueObj {
(inf @ (Self::Inf | Self::NegInf), _) | (_, inf @ (Self::Inf | Self::NegInf)) => {
Some(inf)
}
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_mul(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_mul(m.borrow().clone()),
_ => None,
}
}
@ -1152,14 +1069,6 @@ impl ValueObj {
(Self::Nat(l), Self::Float(r)) => Some(Self::from(l as f64 / r)),
(Self::Float(l), Self::Int(r)) => Some(Self::from(l / r as f64)),
(Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 / r)),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_div(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_div(m.borrow().clone()),
// TODO: x/±Inf = 0
_ => None,
}
@ -1176,14 +1085,6 @@ impl ValueObj {
(Self::Nat(l), Self::Float(r)) => Some(Self::Float((l as f64 / r).floor())),
(Self::Float(l), Self::Int(r)) => Some(Self::Float((l / r as f64).floor())),
(Self::Int(l), Self::Float(r)) => Some(Self::Float((l as f64 / r).floor())),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_div(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_floordiv(m.borrow().clone()),
// TODO: x//±Inf = 0
_ => None,
}
@ -1200,14 +1101,6 @@ impl ValueObj {
(Self::Nat(l), Self::Float(r)) => Some(Self::from(l as f64 > r)),
(Self::Float(l), Self::Int(r)) => Some(Self::from(l > r as f64)),
(Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 > r)),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_gt(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_gt(m.borrow().clone()),
_ => None,
}
}
@ -1223,14 +1116,6 @@ impl ValueObj {
(Self::Nat(l), Self::Float(r)) => Some(Self::from(l as f64 >= r)),
(Self::Float(l), Self::Int(r)) => Some(Self::from(l >= r as f64)),
(Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 >= r)),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_ge(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_ge(m.borrow().clone()),
_ => None,
}
}
@ -1246,14 +1131,6 @@ impl ValueObj {
(Self::Nat(l), Self::Float(r)) => Some(Self::from((l as f64) < r)),
(Self::Float(l), Self::Int(r)) => Some(Self::from(l < r as f64)),
(Self::Int(l), Self::Float(r)) => Some(Self::from((l as f64) < r)),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_lt(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_lt(m.borrow().clone()),
_ => None,
}
}
@ -1269,14 +1146,6 @@ impl ValueObj {
(Self::Nat(l), Self::Float(r)) => Some(Self::from((l as f64) <= r)),
(Self::Float(l), Self::Int(r)) => Some(Self::from(l <= r as f64)),
(Self::Int(l), Self::Float(r)) => Some(Self::from((l as f64) <= r)),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_le(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_le(m.borrow().clone()),
_ => None,
}
}
@ -1295,14 +1164,6 @@ impl ValueObj {
(Self::Str(l), Self::Str(r)) => Some(Self::from(l == r)),
(Self::Bool(l), Self::Bool(r)) => Some(Self::from(l == r)),
(Self::Type(l), Self::Type(r)) => Some(Self::from(l == r)),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_eq(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_eq(m.borrow().clone()),
// TODO:
_ => None,
}
@ -1322,14 +1183,6 @@ impl ValueObj {
(Self::Str(l), Self::Str(r)) => Some(Self::from(l != r)),
(Self::Bool(l), Self::Bool(r)) => Some(Self::from(l != r)),
(Self::Type(l), Self::Type(r)) => Some(Self::from(l != r)),
(Self::Mut(m), other) => {
{
let ref_m = &mut *m.borrow_mut();
*ref_m = mem::take(ref_m).try_ne(other)?;
}
Some(Self::Mut(m))
}
(self_, Self::Mut(m)) => self_.try_ne(m.borrow().clone()),
_ => None,
}
}

3
tests/should_err/mut.er Normal file
View file

@ -0,0 +1,3 @@
n = !10
n.update! n -> n + 1 # OK
n.update! n -> n - 1 # ERR: n: Int

View file

@ -0,0 +1,7 @@
v = ![]
v.push! 1
w = ![]
w.push! "a"
_ = v.concat w # ERR

View file

@ -22,7 +22,7 @@ counter = !10
print! counter
w! do!(not(counter == 0)), do!:
print! "counter = \{counter}"
counter.update!(i -> i - 1)
counter.update!(i -> Nat(i - 1))
counter2 = !2
not_zero!() = not counter2 == 0

View file

@ -2,4 +2,6 @@ v = ![]
for! 0..<10, i =>
v.push! i
assert v[0] == 0
assert v == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
log sum v

View file

@ -322,11 +322,26 @@ fn exec_callable() -> Result<(), ()> {
expect_failure("tests/should_err/callable.er", 0, 5)
}
#[test]
fn exec_move() -> Result<(), ()> {
expect_failure("tests/should_err/move.er", 1, 1)
}
#[test]
fn exec_multiline_invalid_next() -> Result<(), ()> {
expect_failure("tests/should_err/multi_line_invalid_nest.er", 0, 1)
}
#[test]
fn exec_mut_err() -> Result<(), ()> {
expect_failure("tests/should_err/mut.er", 0, 1)
}
#[test]
fn exec_mut_array_err() -> Result<(), ()> {
expect_failure("tests/should_err/mut_array.er", 0, 1)
}
#[test]
fn exec_quantified_err() -> Result<(), ()> {
expect_failure("tests/should_err/quantified.er", 0, 3)
@ -346,8 +361,3 @@ fn exec_var_args_err() -> Result<(), ()> {
fn exec_visibility() -> Result<(), ()> {
expect_failure("tests/should_err/visibility.er", 2, 7)
}
#[test]
fn exec_move() -> Result<(), ()> {
expect_failure("tests/should_err/move.er", 1, 1)
}