mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-03 18:29:00 +00:00
Merge pull request #518 from erg-lang/fix_inf_rec
Fix infinite recursion bugs
This commit is contained in:
commit
b5092e7890
17 changed files with 1886 additions and 535 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue