fix(typechecker): type generalization & dereference bugs

* `Context::subtype_of` now has `allow_cast` param. If this is `false`, cast-aware comparisons are not performed.
This commit is contained in:
Shunsuke Shibayama 2023-02-21 17:04:52 +09:00
parent 3fea50f8bc
commit 4dcca2b06d
18 changed files with 689 additions and 247 deletions

View file

@ -724,6 +724,7 @@ impl Context {
lhs: TyParam,
rhs: TyParam,
) -> EvalResult<TyParam> {
let allow_cast = true;
match (lhs, rhs) {
(TyParam::Value(ValueObj::Mut(lhs)), TyParam::Value(rhs)) => self
.eval_bin(op, lhs.borrow().clone(), rhs)
@ -738,7 +739,8 @@ impl Context {
// _: Nat <= 10 => true
// TODO: maybe this is wrong, we should do the type-checking of `<=`
(TyParam::Erased(t), rhs)
if op.is_comparison() && self.supertype_of(&t, &self.get_tp_t(&rhs).unwrap()) =>
if op.is_comparison()
&& self.supertype_of(&t, &self.get_tp_t(&rhs).unwrap(), allow_cast) =>
{
Ok(TyParam::value(true))
}
@ -748,7 +750,8 @@ impl Context {
(_, TyParam::FreeVar(_)) if op.is_comparison() => Ok(TyParam::value(true)),
// 10 <= _: Nat => true
(lhs, TyParam::Erased(t))
if op.is_comparison() && self.supertype_of(&self.get_tp_t(&lhs).unwrap(), &t) =>
if op.is_comparison()
&& self.supertype_of(&self.get_tp_t(&lhs).unwrap(), &t, allow_cast) =>
{
Ok(TyParam::value(true))
}
@ -952,6 +955,7 @@ impl Context {
level: usize,
t_loc: &impl Locational,
) -> EvalResult<Type> {
let allow_cast = true;
// Currently Erg does not allow projection-types to be evaluated with type variables included.
// All type variables will be dereferenced or fail.
let (sub, opt_sup) = match lhs.clone() {
@ -993,12 +997,12 @@ impl Context {
for (class, methods) in ty_ctx.methods_list.iter() {
match (&class, &opt_sup) {
(ClassDefType::ImplTrait { impl_trait, .. }, Some(sup)) => {
if !self.supertype_of(impl_trait, sup) {
if !self.supertype_of(impl_trait, sup, allow_cast) {
continue;
}
}
(ClassDefType::ImplTrait { impl_trait, .. }, None) => {
if !self.supertype_of(impl_trait, &sub) {
if !self.supertype_of(impl_trait, &sub, allow_cast) {
continue;
}
}
@ -1121,6 +1125,7 @@ impl Context {
level: usize,
t_loc: &impl Locational,
) -> Option<Type> {
let allow_cast = true;
// e.g. sub: Int, opt_sup: Add(?T), rhs: Output, methods: Int.methods
// sub: [Int; 4], opt_sup: Add([Int; 2]), rhs: Output, methods: [T; N].methods
if let Ok(obj) = methods.get_const_local(&Token::symbol(rhs), &self.name) {
@ -1129,7 +1134,7 @@ impl Context {
// opt_sup: Add([Int; 2]), methods.impl_of(): Add([T; M])
match (&opt_sup, methods.impl_of()) {
(Some(sup), Some(trait_)) => {
if !self.supertype_of(&trait_, sup) {
if !self.supertype_of(&trait_, sup, allow_cast) {
return None;
}
}
@ -1522,13 +1527,14 @@ impl Context {
/// NOTE: If l and r are types, the Context is used to determine the type.
/// NOTE: lとrが型の場合はContextの方で判定する
pub(crate) fn shallow_eq_tp(&self, lhs: &TyParam, rhs: &TyParam) -> bool {
let allow_cast = true;
match (lhs, rhs) {
(TyParam::Type(l), _) if l.is_unbound_var() => {
self.subtype_of(&self.get_tp_t(rhs).unwrap(), &Type::Type)
self.subtype_of(&self.get_tp_t(rhs).unwrap(), &Type::Type, allow_cast)
}
(_, TyParam::Type(r)) if r.is_unbound_var() => {
let lhs = self.get_tp_t(lhs).unwrap();
self.subtype_of(&lhs, &Type::Type)
self.subtype_of(&lhs, &Type::Type, allow_cast)
}
(TyParam::Type(l), TyParam::Type(r)) => l == r,
(TyParam::Value(l), TyParam::Value(r)) => l == r,