Merge pull request #518 from erg-lang/fix_inf_rec

Fix infinite recursion bugs
This commit is contained in:
Shunsuke Shibayama 2024-09-01 18:23:58 +09:00 committed by GitHub
commit b5092e7890
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1886 additions and 535 deletions

View file

@ -7,7 +7,7 @@ use erg_common::dict::Dict;
use erg_common::set::Set;
use erg_common::style::colors::DEBUG_ERROR;
use erg_common::traits::StructuralEq;
use erg_common::{assume_unreachable, log};
use erg_common::{assume_unreachable, log, set_recursion_limit};
use erg_common::{Str, Triple};
use crate::context::eval::UndoableLinkedList;
@ -126,6 +126,7 @@ impl Context {
/// lhs :> rhs ?
pub(crate) fn supertype_of(&self, lhs: &Type, rhs: &Type) -> bool {
set_recursion_limit!(false, 128);
let res = match Self::cheap_supertype_of(lhs, rhs) {
(Absolutely, judge) => judge,
(Maybe, judge) => {
@ -1072,6 +1073,23 @@ impl Context {
(TyParam::UnsizedList(sup), TyParam::UnsizedList(sub)) => {
self.supertype_of_tp(sup, sub, variance)
}
(
TyParam::DataClass { name, fields },
TyParam::DataClass {
name: sub_name,
fields: sub_fields,
},
) => {
if name != sub_name || fields.len() != sub_fields.len() {
return false;
}
for (sup_tp, sub_tp) in fields.values().zip(sub_fields.values()) {
if !self.supertype_of_tp(sup_tp, sub_tp, variance) {
return false;
}
}
true
}
(TyParam::Type(sup), TyParam::Type(sub)) => match variance {
Variance::Contravariant => self.subtype_of(sup, sub),
Variance::Covariant => self.supertype_of(sup, sub),
@ -1465,6 +1483,7 @@ impl Context {
t
}
(t, Type::Never) | (Type::Never, t) => t.clone(),
// REVIEW: variance?
// List({1, 2}, 2), List({3, 4}, 2) ==> List({1, 2, 3, 4}, 2)
(
Type::Poly {
@ -1566,19 +1585,27 @@ impl Context {
/// ```
fn simple_union(&self, lhs: &Type, rhs: &Type) -> Type {
if let Ok(free) = <&FreeTyVar>::try_from(lhs) {
if !rhs.is_totally_unbound() && self.supertype_of(&free.get_sub().unwrap_or(Never), rhs)
free.dummy_link();
let res = if !rhs.is_totally_unbound()
&& self.supertype_of(&free.get_sub().unwrap_or(Never), rhs)
{
lhs.clone()
} else {
or(lhs.clone(), rhs.clone())
}
};
free.undo();
res
} else if let Ok(free) = <&FreeTyVar>::try_from(rhs) {
if !lhs.is_totally_unbound() && self.supertype_of(&free.get_sub().unwrap_or(Never), lhs)
free.dummy_link();
let res = if !lhs.is_totally_unbound()
&& self.supertype_of(&free.get_sub().unwrap_or(Never), lhs)
{
rhs.clone()
} else {
or(lhs.clone(), rhs.clone())
}
};
free.undo();
res
} else {
if lhs.is_totally_unbound() || rhs.is_totally_unbound() {
return or(lhs.clone(), rhs.clone());
@ -1677,6 +1704,29 @@ impl Context {
}
t
}
// REVIEW: variance?
// Array({1, 2, 3}) and Array({2, 3, 4}) == Array({2, 3})
(
Poly {
name: ln,
params: lps,
},
Poly {
name: rn,
params: rps,
},
) if ln == rn && self.is_class(lhs) => {
debug_assert_eq!(lps.len(), rps.len());
let mut new_params = vec![];
for (lp, rp) in lps.iter().zip(rps.iter()) {
if let Some(intersec) = self.intersection_tp(lp, rp) {
new_params.push(intersec);
} else {
return self.simple_intersection(lhs, rhs);
}
}
poly(ln.clone(), new_params)
}
(other, Refinement(refine)) | (Refinement(refine), other) => {
let other = other.clone().into_refinement();
let intersec = self.intersection_refinement(&other, refine);
@ -1689,6 +1739,50 @@ impl Context {
}
}
pub(crate) fn intersection_tp(&self, lhs: &TyParam, rhs: &TyParam) -> Option<TyParam> {
match (lhs, rhs) {
(TyParam::Value(ValueObj::Type(l)), TyParam::Value(ValueObj::Type(r))) => {
Some(TyParam::t(self.intersection(l.typ(), r.typ())))
}
(TyParam::Value(ValueObj::Type(l)), TyParam::Type(r)) => {
Some(TyParam::t(self.intersection(l.typ(), r)))
}
(TyParam::Type(l), TyParam::Value(ValueObj::Type(r))) => {
Some(TyParam::t(self.intersection(l, r.typ())))
}
(TyParam::Type(l), TyParam::Type(r)) => Some(TyParam::t(self.intersection(l, r))),
(TyParam::List(l), TyParam::List(r)) => {
let mut tps = vec![];
for (l, r) in l.iter().zip(r.iter()) {
if let Some(tp) = self.intersection_tp(l, r) {
tps.push(tp);
} else {
return None;
}
}
Some(TyParam::List(tps))
}
(fv @ TyParam::FreeVar(f), other) | (other, fv @ TyParam::FreeVar(f))
if f.is_unbound() =>
{
let fv_t = self.get_tp_t(fv).ok()?.derefine();
let other_t = self.get_tp_t(other).ok()?.derefine();
if self.same_type_of(&fv_t, &other_t) {
Some(other.clone())
} else {
None
}
}
(_, _) => {
if self.eq_tp(lhs, rhs) {
Some(lhs.clone())
} else {
None
}
}
}
}
/// ```erg
/// intersection_add(Nat and ?T(:> NoneType), Int) == Nat and ?T
/// intersection_add(Int and ?T(:> NoneType), Nat) == Nat and ?T

View file

@ -9,7 +9,7 @@ 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, Triple};
use erg_common::{dict, fmt_vec, fn_name, option_enum_unwrap, set, set_recursion_limit, Triple};
use erg_common::{ArcArray, Str};
use OpKind::*;
@ -37,7 +37,7 @@ use crate::error::{EvalError, EvalErrors, EvalResult, Failable, SingleEvalResult
use crate::varinfo::{AbsLocation, VarInfo};
use super::instantiate::TyVarCache;
use Type::{Failure, Never, Subr};
use Type::{Failure, Never};
macro_rules! feature_error {
($ctx: expr, $loc: expr, $name: expr) => {
@ -406,11 +406,17 @@ impl<'c> Substituter<'c> {
/// -> Iterable(Int)
/// ```
pub(crate) fn substitute_self(qt: &Type, subtype: &Type, ctx: &'c Context) -> Option<Self> {
#[allow(clippy::blocks_in_conditions)]
for t in qt.contained_ts() {
if t.is_qvar()
&& &t.qual_name()[..] == "Self"
&& t.get_super()
.is_some_and(|sup| ctx.supertype_of(&sup, subtype))
&& t.get_super().is_some_and(|sup| {
let fv = t.as_free().unwrap();
fv.dummy_link();
let res = ctx.supertype_of(&sup, subtype);
fv.undo();
res
})
{
let mut _self = Self::new(ctx);
t.undoable_link(subtype, &_self.undoable_linked);
@ -633,7 +639,7 @@ impl Context {
Ok(tp) => (tp, EvalErrors::empty()),
Err((tp, errs)) => (tp, errs),
};
match ValueObj::try_from(tp) {
match self.convert_tp_into_value(tp) {
Ok(val) => {
if errs.is_empty() {
Ok(val)
@ -641,7 +647,7 @@ impl Context {
Err((val, errs))
}
}
Err(()) => {
Err(_) => {
if errs.is_empty() {
Err((
ValueObj::Failure,
@ -1748,21 +1754,38 @@ impl Context {
fn eval_unary_val(&self, op: OpKind, val: ValueObj) -> EvalResult<ValueObj> {
match op {
Pos => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
Neg => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
Invert => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
Pos => match val {
ValueObj::Nat(_)
| ValueObj::Int(_)
| ValueObj::Float(_)
| ValueObj::Inf
| ValueObj::NegInf => Ok(val),
_ => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
},
Neg => match val {
ValueObj::Nat(n) => Ok(ValueObj::Int(-(n as i32))),
ValueObj::Int(i) => Ok(ValueObj::Int(-i)),
ValueObj::Float(f) => Ok(ValueObj::Float(-f)),
ValueObj::Inf => Ok(ValueObj::NegInf),
ValueObj::NegInf => Ok(ValueObj::Inf),
_ => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
},
Invert => match val {
ValueObj::Bool(b) => Ok(ValueObj::Bool(!b)),
_ => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
},
Not => match val {
ValueObj::Bool(b) => Ok(ValueObj::Bool(!b)),
ValueObj::Type(lhs) => Ok(self.eval_not_type(lhs)),
@ -1815,16 +1838,13 @@ impl Context {
pub(crate) fn eval_tp(&self, p: TyParam) -> Failable<TyParam> {
let mut errs = EvalErrors::empty();
let tp = match p {
TyParam::FreeVar(fv) if fv.is_linked() => {
let tp = fv.crack().clone();
match self.eval_tp(tp) {
Ok(tp) => tp,
Err((tp, es)) => {
errs.extend(es);
tp
}
TyParam::FreeVar(fv) if fv.is_linked() => match self.eval_tp(fv.unwrap_linked()) {
Ok(tp) => tp,
Err((tp, es)) => {
errs.extend(es);
tp
}
}
},
TyParam::FreeVar(_) => p,
TyParam::Mono(name) => match self
.rec_get_const_obj(&name)
@ -1968,15 +1988,17 @@ impl Context {
TyParam::erased(t)
}
},
TyParam::Value(ValueObj::Type(mut t)) => {
match t.try_map_t(|t| self.eval_t_params(t, self.level, &())) {
Ok(_) => {}
TyParam::Value(val) => {
match val
.clone()
.try_map_t(&mut |t| self.eval_t_params(t, self.level, &()))
{
Ok(val) => TyParam::Value(val),
Err((_t, es)) => {
errs.extend(es);
*t.typ_mut() = _t;
TyParam::Value(val)
}
}
TyParam::Value(ValueObj::Type(t))
}
TyParam::ProjCall { obj, attr, args } => {
match self.eval_proj_call(*obj, attr, args, &()) {
@ -1994,7 +2016,6 @@ impl Context {
return Err((TyParam::Failure, errs));
}
},
TyParam::Value(_) => p.clone(),
other => {
errs.push(EvalError::feature_error(
self.cfg.input.clone(),
@ -2046,11 +2067,11 @@ impl Context {
level: usize,
t_loc: &impl Locational,
) -> Failable<Type> {
set_recursion_limit!(Ok(Failure), 128);
let mut errs = EvalErrors::empty();
match substituted {
Type::FreeVar(fv) if fv.is_linked() => {
let t = fv.crack().clone();
self.eval_t_params(t, level, t_loc)
self.eval_t_params(fv.unwrap_linked(), level, t_loc)
}
Type::FreeVar(fv) if fv.constraint_is_sandwiched() => {
let (sub, sup) = fv.get_subsup().unwrap();
@ -2073,9 +2094,9 @@ impl Context {
*pt.typ_mut() = match self.eval_t_params(mem::take(pt.typ_mut()), level, t_loc)
{
Ok(t) => t,
Err((_, errs)) => {
// `mem::take` replaces the type with `Type::Failure`, so it can return as is
return Err((Subr(subr), errs));
Err((t, es)) => {
errs.extend(es);
t
}
};
}
@ -2083,19 +2104,28 @@ impl Context {
*var_args.typ_mut() =
match self.eval_t_params(mem::take(var_args.typ_mut()), level, t_loc) {
Ok(t) => t,
Err((_, errs)) => return Err((Subr(subr), errs)),
Err((t, es)) => {
errs.extend(es);
t
}
};
}
for pt in subr.default_params.iter_mut() {
*pt.typ_mut() = match self.eval_t_params(mem::take(pt.typ_mut()), level, t_loc)
{
Ok(t) => t,
Err((_, errs)) => return Err((Subr(subr), errs)),
Err((t, es)) => {
errs.extend(es);
t
}
};
if let Some(default) = pt.default_typ_mut() {
*default = match self.eval_t_params(mem::take(default), level, t_loc) {
Ok(t) => t,
Err((_, errs)) => return Err((Subr(subr), errs)),
Err((t, es)) => {
errs.extend(es);
t
}
};
}
}
@ -2103,31 +2133,37 @@ impl Context {
*kw_var_args.typ_mut() =
match self.eval_t_params(mem::take(kw_var_args.typ_mut()), level, t_loc) {
Ok(t) => t,
Err((_, errs)) => return Err((Subr(subr), errs)),
Err((t, es)) => {
errs.extend(es);
t
}
};
}
match self.eval_t_params(*subr.return_t, level, t_loc) {
Ok(return_t) => Ok(subr_t(
subr.kind,
subr.non_default_params,
subr.var_params.map(|v| *v),
subr.default_params,
subr.kw_var_params.map(|v| *v),
return_t,
)),
Err((_, errs)) => {
let subr = subr_t(
subr.kind,
subr.non_default_params,
subr.var_params.map(|v| *v),
subr.default_params,
subr.kw_var_params.map(|v| *v),
Failure,
);
Err((subr, errs))
let return_t = match self.eval_t_params(*subr.return_t, level, t_loc) {
Ok(return_t) => return_t,
Err((return_t, es)) => {
errs.extend(es);
return_t
}
};
let subr = subr_t(
subr.kind,
subr.non_default_params,
subr.var_params.map(|v| *v),
subr.default_params,
subr.kw_var_params.map(|v| *v),
return_t,
);
if errs.is_empty() {
Ok(subr)
} else {
Err((subr, errs))
}
}
Type::Quantified(quant) => match self.eval_t_params(*quant, level, t_loc) {
Ok(t) => Ok(t.quantify()),
Err((t, es)) => Err((t.quantify(), es)),
},
Type::Refinement(refine) => {
if refine.pred.variables().is_empty() {
let pred = match self.eval_pred(*refine.pred) {
@ -2157,27 +2193,33 @@ impl Context {
.map_err(|errs| (Failure, errs)),
Type::Ref(l) => match self.eval_t_params(*l, level, t_loc) {
Ok(t) => Ok(ref_(t)),
Err((_, errs)) => Err((ref_(Failure), errs)),
Err((t, errs)) => Err((ref_(t), errs)),
},
Type::RefMut { before, after } => {
let before = match self.eval_t_params(*before, level, t_loc) {
Ok(before) => before,
Err((_, errs)) => {
return Err((ref_mut(Failure, after.map(|x| *x)), errs));
Err((before, es)) => {
errs.extend(es);
before
}
};
let after = if let Some(after) = after {
let aft = match self.eval_t_params(*after, level, t_loc) {
Ok(aft) => aft,
Err((_, errs)) => {
return Err((ref_mut(before, Some(Failure)), errs));
Err((aft, es)) => {
errs.extend(es);
aft
}
};
Some(aft)
} else {
None
};
Ok(ref_mut(before, after))
if errs.is_empty() {
Ok(ref_mut(before, after))
} else {
Err((ref_mut(before, after), errs))
}
}
Type::Poly { name, mut params } => {
for p in params.iter_mut() {
@ -2208,82 +2250,115 @@ impl Context {
Type::And(l, r) => {
let l = match self.eval_t_params(*l, level, t_loc) {
Ok(l) => l,
Err((_, errs)) => {
return Err((Failure, errs));
Err((l, es)) => {
errs.extend(es);
l
}
};
let r = match self.eval_t_params(*r, level, t_loc) {
Ok(r) => r,
Err((_, errs)) => {
// L and Never == Never
return Err((Failure, errs));
Err((r, es)) => {
errs.extend(es);
r
}
};
Ok(self.intersection(&l, &r))
let intersec = self.intersection(&l, &r);
if errs.is_empty() {
Ok(intersec)
} else {
Err((intersec, errs))
}
}
Type::Or(l, r) => {
let l = match self.eval_t_params(*l, level, t_loc) {
Ok(l) => l,
Err((_, errs)) => {
return Err((Failure, errs));
Err((l, es)) => {
errs.extend(es);
l
}
};
let r = match self.eval_t_params(*r, level, t_loc) {
Ok(r) => r,
Err((_, errs)) => {
// L or Never == L
return Err((l, errs));
Err((r, es)) => {
errs.extend(es);
r
}
};
Ok(self.union(&l, &r))
let union = self.union(&l, &r);
if errs.is_empty() {
Ok(union)
} else {
Err((union, errs))
}
}
Type::Not(ty) => match self.eval_t_params(*ty, level, t_loc) {
Ok(ty) => Ok(self.complement(&ty)),
Err((_, errs)) => Err((Failure, errs)),
Err((ty, errs)) => Err((self.complement(&ty), errs)),
},
Type::Structural(typ) => match self.eval_t_params(*typ, level, t_loc) {
Ok(typ) => Ok(typ.structuralize()),
Err((t, errs)) => Err((t.structuralize(), errs)),
},
Type::Structural(typ) => {
let typ = self.eval_t_params(*typ, level, t_loc)?;
Ok(typ.structuralize())
}
Type::Record(rec) => {
let mut fields = dict! {};
for (name, tp) in rec.into_iter() {
fields.insert(name, self.eval_t_params(tp, level, t_loc)?);
for (name, ty) in rec.into_iter() {
match self.eval_t_params(ty, level, t_loc) {
Ok(ty) => {
fields.insert(name, ty);
}
Err((tp, es)) => {
fields.insert(name, tp);
errs.extend(es);
}
}
}
Ok(Type::Record(fields))
}
Type::NamedTuple(tuple) => {
let mut new_tuple = vec![];
for (name, tp) in tuple.into_iter() {
new_tuple.push((name, self.eval_t_params(tp, level, t_loc)?));
for (name, ty) in tuple.into_iter() {
match self.eval_t_params(ty, level, t_loc) {
Ok(ty) => new_tuple.push((name, ty)),
Err((ty, es)) => {
new_tuple.push((name, ty));
errs.extend(es);
}
}
}
Ok(Type::NamedTuple(new_tuple))
}
Type::Bounded { sub, sup } => {
let sub = match self.eval_t_params(*sub, level, t_loc) {
Ok(sub) => sub,
Err((_, errs)) => {
return Err((Failure, errs));
Err((sub, es)) => {
errs.extend(es);
sub
}
};
let sup = match self.eval_t_params(*sup, level, t_loc) {
Ok(sup) => sup,
Err((_, errs)) => {
return Err((Failure, errs));
Err((sup, es)) => {
errs.extend(es);
sup
}
};
Ok(bounded(sub, sup))
}
Type::Guard(grd) => {
let to = self.eval_t_params(*grd.to, level, t_loc)?;
Ok(guard(grd.namespace, grd.target, to))
if errs.is_empty() {
Ok(bounded(sub, sup))
} else {
Err((bounded(sub, sup), errs))
}
}
Type::Guard(grd) => match self.eval_t_params(*grd.to, level, t_loc) {
Ok(to) => Ok(guard(grd.namespace, grd.target, to)),
Err((to, es)) => Err((guard(grd.namespace, grd.target, to), es)),
},
other if other.is_monomorphic() => Ok(other),
other => feature_error!(self, t_loc.loc(), &format!("eval {other}"))
.map_err(|errs| (other, errs)),
}
}
/// This may do nothing (be careful with recursive calls).
/// lhs: mainly class
pub(crate) fn eval_proj(
&self,
@ -2476,6 +2551,7 @@ impl Context {
/// {x = Int; y = Int} => Type::Record({x = Int, y = Int})
/// {Str: Int} => Dict({Str: Int})
/// {1, 2} => {I: Int | I == 1 or I == 2 } (== {1, 2})
/// foo.T => Ok(foo.T) if T: Type else Err(foo.T)
/// ```
pub(crate) fn convert_tp_into_type(&self, tp: TyParam) -> Result<Type, TyParam> {
match tp {
@ -2544,22 +2620,46 @@ impl Context {
}
TyParam::Type(t) => Ok(t.as_ref().clone()),
TyParam::Mono(name) => Ok(Type::Mono(name)),
// REVIEW: should be checked?
TyParam::App { name, args } => Ok(Type::Poly { name, params: args }),
TyParam::Proj { obj, attr } => {
let lhs = self.convert_tp_into_type(*obj)?;
Ok(lhs.proj(attr))
TyParam::App { name, args } => {
if self.get_type_ctx(&name).is_none() {
Err(TyParam::App { name, args })
} else {
Ok(Type::Poly { name, params: args })
}
}
TyParam::Proj { obj, attr } => {
let lhs = self.convert_tp_into_type(*obj.clone())?;
let Some(ty_ctx) = self.get_nominal_type_ctx(&lhs) else {
return Err(TyParam::Proj { obj, attr });
};
if ty_ctx.rec_get_const_obj(&attr).is_some() {
Ok(lhs.proj(attr))
} else {
Err(TyParam::Proj { obj, attr })
}
}
TyParam::ProjCall { obj, attr, args } => {
let Some(ty_ctx) = self
.get_tp_t(&obj)
.ok()
.and_then(|t| self.get_nominal_type_ctx(&t))
else {
return Err(TyParam::ProjCall { obj, attr, args });
};
if ty_ctx.rec_get_const_obj(&attr).is_some() {
Ok(proj_call(*obj, attr, args))
} else {
Err(TyParam::ProjCall { obj, attr, args })
}
}
TyParam::ProjCall { obj, attr, args } => Ok(proj_call(*obj, attr, args)),
// TyParam::Erased(_t) => Ok(Type::Obj),
TyParam::Value(v) => self.convert_value_into_type(v).map_err(TyParam::Value),
TyParam::Erased(t) if t.is_type() => Ok(Type::Obj),
// TODO: Dict, Set
// TODO: DataClass, ...
other => Err(other),
}
}
#[allow(clippy::only_used_in_recursion)]
pub(crate) fn convert_tp_into_value(&self, tp: TyParam) -> Result<ValueObj, TyParam> {
match tp {
TyParam::FreeVar(fv) if fv.is_linked() => {
@ -2595,6 +2695,16 @@ impl Context {
}
Ok(ValueObj::Record(new))
}
TyParam::Dict(tps) => {
let mut vals = dict! {};
for (k, v) in tps {
vals.insert(
self.convert_tp_into_value(k)?,
self.convert_tp_into_value(v)?,
);
}
Ok(ValueObj::Dict(vals))
}
TyParam::Set(set) => {
let mut new = set! {};
for elem in set {
@ -2657,10 +2767,12 @@ impl Context {
}
}
// FIXME: Failable<Type>
pub(crate) fn convert_value_into_type(&self, val: ValueObj) -> Result<Type, ValueObj> {
match val {
ValueObj::Failure => Ok(Type::Failure),
ValueObj::Ellipsis => Ok(Type::Ellipsis),
ValueObj::NotImplemented => Ok(Type::NotImplementedType),
ValueObj::Type(t) => Ok(t.into_typ()),
ValueObj::Record(rec) => {
let mut fields = dict! {};
@ -2812,6 +2924,30 @@ impl Context {
}
Ok(new_dict)
}
Type::Proj { lhs, rhs } => {
let old = proj(*lhs.clone(), rhs.clone());
let eval = self.eval_proj(*lhs, rhs, self.level, &()).map_err(|_| ())?;
if eval != old {
self.convert_type_to_dict_type(eval)
} else {
Err(())
}
}
Type::ProjCall {
lhs,
attr_name,
args,
} => {
let old = proj_call(*lhs.clone(), attr_name.clone(), args.clone());
let eval = self
.eval_proj_call_t(*lhs, attr_name, args, self.level, &())
.map_err(|_| ())?;
if eval != old {
self.convert_type_to_dict_type(eval)
} else {
Err(())
}
}
_ => Err(()),
}
}
@ -2832,6 +2968,31 @@ impl Context {
}
Ok(tys)
}
Type::NamedTuple(tuple) => Ok(tuple.into_iter().map(|(_, ty)| ty).collect()),
Type::Proj { lhs, rhs } => {
let old = proj(*lhs.clone(), rhs.clone());
let eval = self.eval_proj(*lhs, rhs, self.level, &()).map_err(|_| ())?;
if eval != old {
self.convert_type_to_tuple_type(eval)
} else {
Err(())
}
}
Type::ProjCall {
lhs,
attr_name,
args,
} => {
let old = proj_call(*lhs.clone(), attr_name.clone(), args.clone());
let eval = self
.eval_proj_call_t(*lhs, attr_name, args, self.level, &())
.map_err(|_| ())?;
if eval != old {
self.convert_type_to_tuple_type(eval)
} else {
Err(())
}
}
_ => Err(()),
}
}
@ -2857,6 +3018,24 @@ impl Context {
};
Ok(vec![ValueObj::builtin_type(t); len])
}
Type::Proj { lhs, rhs } => {
let old = proj(*lhs.clone(), rhs.clone());
match self.eval_proj(*lhs, rhs, self.level, &()) {
Ok(eval) if eval != old => self.convert_type_to_list(eval),
_ => Err(old),
}
}
Type::ProjCall {
lhs,
attr_name,
args,
} => {
let old = proj_call(*lhs.clone(), attr_name.clone(), args.clone());
match self.eval_proj_call_t(*lhs, attr_name, args, self.level, &()) {
Ok(eval) if eval != old => self.convert_type_to_list(eval),
_ => Err(old),
}
}
_ => Err(ty),
}
}
@ -3018,7 +3197,7 @@ impl Context {
let Some(lhs) = lhs else {
return feature_error!(self, t_loc.loc(), "??");
};
if let Ok(value) = ValueObj::try_from(lhs.clone()) {
if let Ok(value) = self.convert_tp_into_value(lhs.clone()) {
pos_args.push(value);
} else if let Ok(value) = self.eval_tp_into_value(lhs.clone()) {
pos_args.push(value);
@ -3027,7 +3206,7 @@ impl Context {
}
}
for pos_arg in args.into_iter() {
if let Ok(value) = ValueObj::try_from(pos_arg.clone()) {
if let Ok(value) = self.convert_tp_into_value(pos_arg.clone()) {
pos_args.push(value);
} else if let Ok(value) = self.eval_tp_into_value(pos_arg.clone()) {
pos_args.push(value);
@ -3074,6 +3253,7 @@ impl Context {
})
}
/// This may do nothing (be careful with recursive calls)
pub(crate) fn eval_proj_call_t(
&self,
lhs: TyParam,

View file

@ -18,7 +18,7 @@ use crate::ty::{HasType, Predicate, SubrType, Type};
use crate::context::{Context, Variance};
use crate::error::{TyCheckError, TyCheckErrors, TyCheckResult};
use crate::{feature_error, hir, unreachable_error};
use crate::{feature_error, hir, mono_type_pattern, mono_value_pattern, unreachable_error};
use Type::*;
use Variance::*;
@ -45,9 +45,10 @@ 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::Value(val) => TyParam::Value(
val.map_t(&mut |t| self.generalize_t(t, uninit))
.map_tp(&mut |tp| self.generalize_tp(tp, uninit)),
),
TyParam::FreeVar(fv) if fv.is_generalized() => TyParam::FreeVar(fv),
TyParam::FreeVar(fv) if fv.is_linked() => {
let tp = fv.crack().clone();
@ -65,6 +66,9 @@ impl Generalizer {
.map(|tp| self.generalize_tp(tp, uninit))
.collect(),
),
TyParam::UnsizedList(tp) => {
TyParam::UnsizedList(Box::new(self.generalize_tp(*tp, uninit)))
}
TyParam::Tuple(tps) => TyParam::Tuple(
tps.into_iter()
.map(|tp| self.generalize_tp(tp, uninit))
@ -128,6 +132,14 @@ impl Generalizer {
let obj = self.generalize_tp(*obj, uninit);
TyParam::proj(obj, attr)
}
TyParam::ProjCall { obj, attr, args } => {
let obj = self.generalize_tp(*obj, uninit);
let args = args
.into_iter()
.map(|tp| self.generalize_tp(tp, uninit))
.collect();
TyParam::proj_call(obj, attr, args)
}
TyParam::Erased(t) => TyParam::erased(self.generalize_t(*t, uninit)),
TyParam::App { name, args } => {
let args = args
@ -145,13 +157,7 @@ impl Generalizer {
let val = self.generalize_tp(*val, uninit);
TyParam::unary(op, val)
}
other if other.has_no_unbound_var() => other,
other => {
if DEBUG_MODE {
todo!("{other:?}");
}
other
}
TyParam::Mono(_) | TyParam::Failure => free,
}
}
@ -203,6 +209,7 @@ impl Generalizer {
Type::FreeVar(fv)
}
}
FreeVar(_) => free_type,
Subr(mut subr) => {
self.variance = Contravariant;
let qnames = subr.essential_qnames();
@ -231,6 +238,10 @@ impl Generalizer {
return_t,
)
}
Quantified(quant) => {
log!(err "{quant}");
quant.quantify()
}
Record(rec) => {
let fields = rec
.into_iter()
@ -307,8 +318,14 @@ impl Generalizer {
let to = self.generalize_t(*grd.to, uninit);
guard(grd.namespace, grd.target, to)
}
// REVIEW: その他何でもそのまま通していいのか?
other => other,
Bounded { sub, sup } => {
let sub = self.generalize_t(*sub, uninit);
let sup = self.generalize_t(*sup, uninit);
bounded(sub, sup)
}
Int | Nat | Float | Ratio | Complex | Bool | Str | Never | Obj | Type | Error
| Code | Frame | NoneType | Inf | NegInf | NotImplementedType | Ellipsis
| ClassType | TraitType | Patch | Failure | Uninited | Mono(_) => free_type,
}
}
@ -328,9 +345,8 @@ impl Generalizer {
fn generalize_pred(&mut self, pred: Predicate, uninit: bool) -> Predicate {
match pred {
Predicate::Const(_) | Predicate::Failure => pred,
Predicate::Value(ValueObj::Type(mut typ)) => {
*typ.typ_mut() = self.generalize_t(mem::take(typ.typ_mut()), uninit);
Predicate::Value(ValueObj::Type(typ))
Predicate::Value(val) => {
Predicate::Value(val.map_t(&mut |t| self.generalize_t(t, uninit)))
}
Predicate::Call {
receiver,
@ -348,7 +364,6 @@ impl Generalizer {
let receiver = self.generalize_tp(receiver, uninit);
Predicate::attr(receiver, name)
}
Predicate::Value(_) => pred,
Predicate::GeneralEqual { lhs, rhs } => {
let lhs = self.generalize_pred(*lhs, uninit);
let rhs = self.generalize_pred(*rhs, uninit);
@ -454,7 +469,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
fn deref_value(&mut self, val: ValueObj) -> TyCheckResult<ValueObj> {
match val {
ValueObj::Type(mut t) => {
t.try_map_t(|t| self.deref_tyvar(t.clone()))?;
t.try_map_t(&mut |t| self.deref_tyvar(t.clone()))?;
Ok(ValueObj::Type(t))
}
ValueObj::List(vs) => {
@ -505,7 +520,8 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
})
}
ValueObj::UnsizedList(v) => Ok(ValueObj::UnsizedList(Box::new(self.deref_value(*v)?))),
_ => Ok(val),
ValueObj::Subr(subr) => Ok(ValueObj::Subr(subr)),
mono_value_pattern!() => Ok(val),
}
}
@ -530,6 +546,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
fv.update_type(t);
Ok(TyParam::FreeVar(fv))
}
TyParam::FreeVar(_) => Ok(tp),
TyParam::Type(t) => Ok(TyParam::t(self.deref_tyvar(*t)?)),
TyParam::Value(val) => self.deref_value(val).map(TyParam::Value),
TyParam::Erased(t) => Ok(TyParam::erased(self.deref_tyvar(*t)?)),
@ -562,6 +579,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
}
Ok(TyParam::List(new_tps))
}
TyParam::UnsizedList(tp) => Ok(TyParam::UnsizedList(Box::new(self.deref_tp(*tp)?))),
TyParam::Tuple(tps) => {
let mut new_tps = vec![];
for tp in tps {
@ -613,20 +631,20 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
let nd_params = lambda
.nd_params
.into_iter()
.map(|pt| pt.try_map_type(|t| self.deref_tyvar(t)))
.map(|pt| pt.try_map_type(&mut |t| self.deref_tyvar(t)))
.collect::<TyCheckResult<_>>()?;
let var_params = lambda
.var_params
.map(|pt| pt.try_map_type(|t| self.deref_tyvar(t)))
.map(|pt| pt.try_map_type(&mut |t| self.deref_tyvar(t)))
.transpose()?;
let d_params = lambda
.d_params
.into_iter()
.map(|pt| pt.try_map_type(|t| self.deref_tyvar(t)))
.map(|pt| pt.try_map_type(&mut |t| self.deref_tyvar(t)))
.collect::<TyCheckResult<_>>()?;
let kw_var_params = lambda
.kw_var_params
.map(|pt| pt.try_map_type(|t| self.deref_tyvar(t)))
.map(|pt| pt.try_map_type(&mut |t| self.deref_tyvar(t)))
.transpose()?;
let body = lambda
.body
@ -649,10 +667,22 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
attr,
})
}
TyParam::ProjCall { obj, attr, args } => {
let obj = self.deref_tp(*obj)?;
let mut new_args = vec![];
for arg in args.into_iter() {
new_args.push(self.deref_tp(arg)?);
}
Ok(TyParam::ProjCall {
obj: Box::new(obj),
attr,
args: new_args,
})
}
TyParam::Failure if self.level == 0 => Err(TyCheckErrors::from(
TyCheckError::dummy_infer_error(self.ctx.cfg.input.clone(), fn_name!(), line!()),
)),
t => Ok(t),
TyParam::Mono(_) | TyParam::Failure => Ok(tp),
}
}
@ -769,8 +799,12 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
let pred = self.deref_pred(*pred)?;
Ok(!pred)
}
Predicate::Attr { receiver, name } => {
let receiver = self.deref_tp(receiver)?;
Ok(Predicate::attr(receiver, name))
}
Predicate::Value(v) => self.deref_value(v).map(Predicate::Value),
_ => Ok(pred),
Predicate::Const(_) | Predicate::Failure => Ok(pred),
}
}
@ -895,6 +929,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
Ok(Type::FreeVar(fv))
}
}
FreeVar(_) => Ok(t),
Poly { name, mut params } => {
let typ = poly(&name, params.clone());
let ctx = self.ctx.get_nominal_type_ctx(&typ).ok_or_else(|| {
@ -1059,7 +1094,12 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
let to = self.deref_tyvar(*grd.to)?;
Ok(guard(grd.namespace, grd.target, to))
}
t => Ok(t),
Bounded { sub, sup } => {
let sub = self.deref_tyvar(*sub)?;
let sup = self.deref_tyvar(*sup)?;
Ok(bounded(sub, sup))
}
mono_type_pattern!() => Ok(t),
}
}

View file

@ -348,8 +348,7 @@ pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueR
Ok(v.into())
} else {
let index = if let ValueObj::Type(t) = &index {
let derefed = ctx.coerce(t.typ().clone(), &()).unwrap_or(t.typ().clone());
ValueObj::builtin_type(derefed)
ValueObj::builtin_type(ctx.readable_type(t.typ().clone()))
} else {
index
};

View file

@ -1854,7 +1854,10 @@ impl Context {
),
Type::FreeVar(fv) => {
if let Some(sub) = fv.get_sub() {
if !self.subtype_of(&sub, &mono("GenericCallable")) {
fv.dummy_link();
let subtype_of = self.subtype_of(&sub, &mono("GenericCallable"));
fv.undo();
if !subtype_of {
return Err(self.not_callable_error(obj, attr_name, instance, None));
}
if sub != Never {

View file

@ -4,12 +4,12 @@ use std::option::Option; // conflicting to Type::Option
use erg_common::consts::DEBUG_MODE;
use erg_common::dict::Dict;
use erg_common::enum_unwrap;
#[allow(unused)]
use erg_common::log;
use erg_common::set::Set;
use erg_common::traits::Locational;
use erg_common::Str;
use erg_common::{enum_unwrap, set_recursion_limit};
use erg_parser::ast::VarName;
use crate::ty::constructors::*;
@ -19,7 +19,7 @@ use crate::ty::ConstSubr;
use crate::ty::GuardType;
use crate::ty::ValueObj;
use crate::ty::{HasType, Predicate, Type};
use crate::{type_feature_error, unreachable_error};
use crate::{mono_type_pattern, unreachable_error};
use Type::*;
use crate::context::{Context, VarInfo};
@ -40,7 +40,6 @@ pub struct TyVarCache {
pub(crate) tyvar_instances: Dict<VarName, Type>,
pub(crate) typaram_instances: Dict<VarName, TyParam>,
pub(crate) var_infos: Dict<VarName, VarInfo>,
pub(crate) structural_inner: bool,
}
impl fmt::Display for TyVarCache {
@ -61,7 +60,6 @@ impl TyVarCache {
tyvar_instances: Dict::new(),
typaram_instances: Dict::new(),
var_infos: Dict::new(),
structural_inner: false,
}
}
@ -419,6 +417,8 @@ impl Context {
tmp_tv_cache.instantiate_constraint(constr, self, loc)?;
fv.update_constraint(new_constr, true);
}
} else {
todo!("{tp}");
}
Ok(tp)
} else if let Some(t) = tmp_tv_cache.get_tyvar(&name) {
@ -433,6 +433,8 @@ impl Context {
tmp_tv_cache.instantiate_constraint(constr, self, loc)?;
fv.update_constraint(new_constr, true);
}
} else {
todo!("{t}");
}
Ok(TyParam::t(t))
} else {
@ -517,20 +519,28 @@ impl Context {
let nd_params = lambda
.nd_params
.into_iter()
.map(|pt| pt.try_map_type(|t| self.instantiate_t_inner(t, tmp_tv_cache, loc)))
.map(|pt| {
pt.try_map_type(&mut |t| self.instantiate_t_inner(t, tmp_tv_cache, loc))
})
.collect::<TyCheckResult<_>>()?;
let var_params = lambda
.var_params
.map(|pt| pt.try_map_type(|t| self.instantiate_t_inner(t, tmp_tv_cache, loc)))
.map(|pt| {
pt.try_map_type(&mut |t| self.instantiate_t_inner(t, tmp_tv_cache, loc))
})
.transpose()?;
let d_params = lambda
.d_params
.into_iter()
.map(|pt| pt.try_map_type(|t| self.instantiate_t_inner(t, tmp_tv_cache, loc)))
.map(|pt| {
pt.try_map_type(&mut |t| self.instantiate_t_inner(t, tmp_tv_cache, loc))
})
.collect::<TyCheckResult<_>>()?;
let kw_var_params = lambda
.kw_var_params
.map(|pt| pt.try_map_type(|t| self.instantiate_t_inner(t, tmp_tv_cache, loc)))
.map(|pt| {
pt.try_map_type(&mut |t| self.instantiate_t_inner(t, tmp_tv_cache, loc))
})
.transpose()?;
let body = lambda
.body
@ -578,22 +588,18 @@ impl Context {
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
Ok(TyParam::t(t))
}
TyParam::Value(ValueObj::Type(t)) => {
let t = self.instantiate_t_inner(t.into_typ(), tmp_tv_cache, loc)?;
Ok(TyParam::t(t))
TyParam::Value(val) => {
// println!("592: {val} / {tmp_tv_cache}");
let val = val.try_map_t(&mut |t| self.instantiate_t_inner(t, tmp_tv_cache, loc))?;
// .try_map_tp(&mut |tp| self.instantiate_tp(tp, tmp_tv_cache, loc))?;
// println!("596: {val} / {tmp_tv_cache}");
Ok(TyParam::Value(val))
}
TyParam::Erased(t) => {
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
Ok(TyParam::Erased(Box::new(t)))
}
p @ (TyParam::Value(_) | TyParam::Mono(_) | TyParam::FreeVar(_)) => Ok(p),
other => {
type_feature_error!(
self,
loc.loc(),
&format!("instantiating type-parameter {other}")
)
}
p @ (TyParam::Mono(_) | TyParam::FreeVar(_) | TyParam::Failure) => Ok(p),
}
}
@ -670,7 +676,11 @@ impl Context {
let rhs = self.instantiate_pred(*rhs, tmp_tv_cache, loc)?;
Ok(Predicate::general_ne(lhs, rhs))
}
_ => Ok(pred),
Predicate::Attr { receiver, name } => {
let receiver = self.instantiate_tp(receiver, tmp_tv_cache, loc)?;
Ok(Predicate::attr(receiver, name))
}
Predicate::Const(_) | Predicate::Failure => Ok(pred),
}
}
@ -714,6 +724,10 @@ impl Context {
}
Ok(ValueObj::List(new.into()))
}
ValueObj::UnsizedList(lis) => {
let lis = self.instantiate_value(*lis, tmp_tv_cache, loc)?;
Ok(ValueObj::UnsizedList(Box::new(lis)))
}
ValueObj::Tuple(tup) => {
let mut new = vec![];
for v in tup.iter().cloned() {
@ -754,7 +768,18 @@ impl Context {
}
Ok(ValueObj::DataClass { name, fields: new })
}
_ => Ok(value),
ValueObj::Int(_)
| ValueObj::Nat(_)
| ValueObj::Float(_)
| ValueObj::Str(_)
| ValueObj::Bool(_)
| ValueObj::Code(_)
| ValueObj::None
| ValueObj::Ellipsis
| ValueObj::Inf
| ValueObj::NegInf
| ValueObj::NotImplemented
| ValueObj::Failure => Ok(value),
}
}
@ -765,6 +790,8 @@ impl Context {
tmp_tv_cache: &mut TyVarCache,
loc: &impl Locational,
) -> TyCheckResult<Type> {
// Structural types may have recursive structures
set_recursion_limit!(Ok(unbound), 128);
match unbound {
FreeVar(fv) if fv.is_linked() => {
let t = fv.crack().clone();
@ -776,8 +803,7 @@ impl Context {
let t = t.clone();
Ok(t)
} else if let Some(tp) = tmp_tv_cache.get_typaram(&name) {
if let TyParam::Type(t) = tp {
let t = *t.clone();
if let Ok(t) = self.convert_tp_into_type(tp.clone()) {
Ok(t)
} else {
todo!(
@ -797,8 +823,8 @@ impl Context {
if let Some(t) = tv_ctx.get_tyvar(&name) {
return Ok(t.clone());
} else if let Some(tp) = tv_ctx.get_typaram(&name) {
if let TyParam::Type(t) = tp {
return Ok(*t.clone());
if let Ok(t) = self.convert_tp_into_type(tp.clone()) {
return Ok(t);
} else {
todo!(
"typaram_insts: {}\ntyvar_insts:{}\n{tp}",
@ -902,16 +928,8 @@ impl Context {
Ok(poly(name, params))
}
Structural(t) => {
// avoid infinite recursion
if tmp_tv_cache.structural_inner {
Ok(t.structuralize())
} else {
if t.is_recursive() {
tmp_tv_cache.structural_inner = true;
}
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
Ok(t.structuralize())
}
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
Ok(t.structuralize())
}
FreeVar(fv) => {
if let Some((sub, sup)) = fv.get_subsup() {
@ -961,8 +979,15 @@ impl Context {
let sup = self.instantiate_t_inner(*sup, tmp_tv_cache, loc)?;
Ok(bounded(sub, sup))
}
other if other.is_monomorphic() => Ok(other),
other => type_feature_error!(self, loc.loc(), &format!("instantiating type {other}")),
Callable { param_ts, return_t } => {
let param_ts = param_ts
.into_iter()
.map(|t| self.instantiate_t_inner(t, tmp_tv_cache, loc))
.collect::<TyCheckResult<_>>()?;
let return_t = self.instantiate_t_inner(*return_t, tmp_tv_cache, loc)?;
Ok(callable(param_ts, return_t))
}
mono_type_pattern!() => Ok(unbound),
}
}
@ -983,7 +1008,7 @@ impl Context {
if let Some(self_t) = ty.self_t() {
self.sub_unify(callee.ref_t(), self_t, callee, Some(&Str::ever("self")))?;
}
if cfg!(feature = "debug") && ty.has_qvar() {
if DEBUG_MODE && ty.has_qvar() {
panic!("{ty} has qvar")
}
Ok(ty)
@ -1028,6 +1053,7 @@ impl Context {
log!(err "{subr} has qvar");
self.instantiate(Type::Subr(subr).quantify(), callee)
}
// There are no quantified types inside normal types (rank-0 types) due to the rank-1 restriction
// rank-1制限により、通常の型(rank-0型)の内側に量化型は存在しない
other => Ok(other),
}
@ -1047,7 +1073,7 @@ impl Context {
Quantified(quant) => {
let mut tmp_tv_cache = TyVarCache::new(self.level, self);
let ty = self.instantiate_t_inner(*quant, &mut tmp_tv_cache, &())?;
if cfg!(feature = "debug") && ty.has_qvar() {
if DEBUG_MODE && ty.has_qvar() {
panic!("{ty} has qvar")
}
Ok(ty)

View file

@ -1945,7 +1945,7 @@ impl Context {
TyParam::App { name, args } => Ok(poly(name, args)),
TyParam::Type(t) => Ok(*t),
TyParam::Value(value) => self.convert_value_into_type(value).or_else(|value| {
type_feature_error!(self, loc.loc(), &format!("instantiate `{value}` as type"))
type_feature_error!(self, loc.loc(), &format!("instantiate `{value}` as a type"))
.map_err(|e| (Type::Failure, e))
}),
TyParam::List(lis) => {
@ -2038,7 +2038,7 @@ impl Context {
_ => type_feature_error!(
self,
loc.loc(),
&format!("instantiate `{lhs} {op} {rhs}` as type")
&format!("instantiate `{lhs} {op} {rhs}` as a type")
)
.map_err(|es| {
errs.extend(es);
@ -2049,7 +2049,7 @@ impl Context {
{
#[allow(clippy::bind_instead_of_map)]
self.convert_tp_into_type(other).or_else(|tp| {
type_feature_error!(self, loc.loc(), &format!("instantiate `{tp}` as type"))
type_feature_error!(self, loc.loc(), &format!("instantiate `{tp}` as a type"))
.map_err(|e| (Type::Failure, e))
})
}
@ -2927,7 +2927,7 @@ impl Context {
})?;
let mut dummy = TyVarCache::new(self.level, self);
match self.instantiate_const_expr(&const_expr, None, &mut dummy, false) {
Ok(tp) => ValueObj::try_from(tp).map_err(|_| {
Ok(tp) => self.convert_tp_into_value(tp).map_err(|_| {
let err = TyCheckError::not_const_expr(
self.cfg.input.clone(),
line!() as usize,
@ -2936,7 +2936,7 @@ impl Context {
);
(ValueObj::Failure, TyCheckErrors::from(err))
}),
Err((tp, mut errs)) => match ValueObj::try_from(tp) {
Err((tp, mut errs)) => match self.convert_tp_into_value(tp) {
Ok(value) => Err((value, errs)),
Err(_) => {
let err = TyCheckError::not_const_expr(

View file

@ -292,6 +292,15 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
Ok(())
}
(ValueObj::Dict(sub), ValueObj::Dict(sup)) => {
if sub.len() == 1 && sup.len() == 1 {
let sub_key = sub.keys().next().unwrap();
let sup_key = sup.keys().next().unwrap();
self.sub_unify_value(sub_key, sup_key)?;
let sub_value = sub.values().next().unwrap();
let sup_value = sup.values().next().unwrap();
self.sub_unify_value(sub_value, sup_value)?;
return Ok(());
}
for (sub_k, sub_v) in sub.iter() {
if let Some(sup_v) = sup.get(sub_k) {
self.sub_unify_value(sub_v, sup_v)?;
@ -308,6 +317,22 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
}
Ok(())
}
(ValueObj::Set(sub), ValueObj::Set(sup)) => {
if sub.len() == 1 && sup.len() == 1 {
let sub = sub.iter().next().unwrap();
let sup = sup.iter().next().unwrap();
self.sub_unify_value(sub, sup)?;
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::feature_error(
self.ctx.cfg.input.clone(),
line!() as usize,
self.loc.loc(),
&format!("unifying {sub} and {sup}"),
self.ctx.caused_by(),
)))
}
}
(ValueObj::Record(sub), ValueObj::Record(sup)) => {
for (sub_k, sub_v) in sub.iter() {
if let Some(sup_v) = sup.get(sub_k) {
@ -929,8 +954,8 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
/// ```
fn sub_unify(&self, maybe_sub: &Type, maybe_sup: &Type) -> TyCheckResult<()> {
log!(info "trying {}sub_unify:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}", self.undoable.map_or("", |_| "undoable_"));
self.recursion_limit.fetch_sub(1, Ordering::SeqCst);
if self.recursion_limit.load(Ordering::SeqCst) == 0 {
if self.recursion_limit.fetch_sub(1, Ordering::SeqCst) == 0 {
self.recursion_limit.store(128, Ordering::SeqCst);
log!(err "recursion limit exceeded: {maybe_sub} / {maybe_sup}");
return Err(TyCheckError::recursion_limit(
self.ctx.cfg.input.clone(),
@ -1268,23 +1293,33 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
}
}
}
(FreeVar(sub_fv), Structural(sup)) if sub_fv.is_unbound() => {
if sub_fv.get_sub().is_none() {
(FreeVar(sub_fv), Structural(struct_sup)) if sub_fv.is_unbound() => {
let Some((sub, sup)) = sub_fv.get_subsup() else {
log!(err "{sub_fv} is not a type variable");
return Ok(());
}
};
sub_fv.dummy_link();
let sub_fields = self.ctx.fields(maybe_sub);
for (sup_field, sup_ty) in self.ctx.fields(sup) {
for (sup_field, sup_ty) in self.ctx.fields(struct_sup) {
if let Some((_, sub_ty)) = sub_fields.get_key_value(&sup_field) {
self.sub_unify(sub_ty, &sup_ty)?;
} else if !self.ctx.subtype_of(&sub_fv.get_sub().unwrap(), &Never) {
self.sub_unify(sub_ty, &sup_ty).map_err(|errs| {
sub_fv.undo();
errs
})?;
} else if !self.ctx.subtype_of(&sub, &Never) {
maybe_sub.coerce(self.undoable);
sub_fv.dummy_link();
return self.sub_unify(maybe_sub, maybe_sup);
} else {
// e.g. ?T / Structural({ .method = (self: ?T) -> Int })
sub_fv.update_super(|sup| self.ctx.intersection(&sup, maybe_sup));
let constr = Constraint::new_sandwiched(
sub.clone(),
self.ctx.intersection(&sup, maybe_sup),
);
sub_fv.update_constraint(constr, false);
}
}
sub_fv.undo();
}
(FreeVar(sub_fv), Ref(sup)) if sub_fv.is_unbound() => {
self.sub_unify(maybe_sub, sup)?;