mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 02:39:20 +00:00
fix(typechecker): quantified types unifying
This commit is contained in:
parent
448fe4e64c
commit
855d47f02c
7 changed files with 77 additions and 40 deletions
|
@ -206,7 +206,9 @@ impl Context {
|
|||
Some((Type::Never, Type::Obj)) => (Absolutely, true),
|
||||
_ => (Maybe, false),
|
||||
},
|
||||
(Type::Mono(n), Subr(_)) if &n[..] == "GenericCallable" => (Absolutely, true),
|
||||
(Type::Mono(n), Subr(_) | Quantified(_)) if &n[..] == "GenericCallable" => {
|
||||
(Absolutely, true)
|
||||
}
|
||||
(lhs, rhs) if lhs.is_simple_class() && rhs.is_simple_class() => (Absolutely, false),
|
||||
_ => (Maybe, false),
|
||||
}
|
||||
|
|
|
@ -91,16 +91,17 @@ impl Context {
|
|||
/// ```
|
||||
fn generalize_t_inner(&self, free_type: Type, variance: Variance, uninit: bool) -> Type {
|
||||
match free_type {
|
||||
FreeVar(fv) if fv.is_generalized() => Type::FreeVar(fv),
|
||||
FreeVar(fv) if fv.is_linked() => {
|
||||
let fv_mut = unsafe { fv.as_ptr().as_mut().unwrap() };
|
||||
self.generalize_t_inner(fv.crack().clone(), variance, uninit)
|
||||
/*let fv_mut = unsafe { fv.as_ptr().as_mut().unwrap() };
|
||||
if let FreeKind::Linked(t) = fv_mut {
|
||||
*t = self.generalize_t_inner(t.clone(), variance, uninit);
|
||||
} else {
|
||||
assume_unreachable!()
|
||||
}
|
||||
Type::FreeVar(fv)
|
||||
Type::FreeVar(fv)*/
|
||||
}
|
||||
FreeVar(fv) if fv.is_generalized() => Type::FreeVar(fv),
|
||||
// TODO: Polymorphic generalization
|
||||
FreeVar(fv) if fv.level().unwrap() > self.level => {
|
||||
if uninit {
|
||||
|
|
|
@ -32,14 +32,21 @@ use crate::error::{
|
|||
TyCheckErrors, TyCheckResult,
|
||||
};
|
||||
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
|
||||
use crate::AccessKind;
|
||||
use crate::{feature_error, hir};
|
||||
use crate::{unreachable_error, AccessKind};
|
||||
use RegistrationMode::*;
|
||||
use Visibility::*;
|
||||
|
||||
use super::instantiate::ParamKind;
|
||||
use super::{ContextKind, MethodInfo};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum SubstituteResult {
|
||||
Ok,
|
||||
__Call__(Type),
|
||||
Coerced(Type),
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub(crate) fn get_ctx_from_path(&self, path: &Path) -> Option<&Context> {
|
||||
self.mod_cache()
|
||||
|
@ -1047,7 +1054,7 @@ impl Context {
|
|||
instance: &Type,
|
||||
pos_args: &[hir::PosArg],
|
||||
kw_args: &[hir::KwArg],
|
||||
) -> TyCheckResult<Option<Type>> {
|
||||
) -> TyCheckResult<SubstituteResult> {
|
||||
match instance {
|
||||
Type::FreeVar(fv) if fv.is_linked() => {
|
||||
self.substitute_call(obj, attr_name, &fv.crack(), pos_args, kw_args)
|
||||
|
@ -1057,9 +1064,26 @@ impl Context {
|
|||
if !self.subtype_of(&sub, &mono("GenericCallable")) {
|
||||
return Err(self.not_callable_error(obj, attr_name, instance, None));
|
||||
}
|
||||
if sub != Never {
|
||||
instance.coerce();
|
||||
if instance.is_quantified() {
|
||||
let instance = self.instantiate(instance.clone(), obj)?;
|
||||
self.substitute_call(obj, attr_name, &instance, pos_args, kw_args)?;
|
||||
return Ok(SubstituteResult::Coerced(instance));
|
||||
} else {
|
||||
return self
|
||||
.substitute_call(obj, attr_name, instance, pos_args, kw_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(attr_name) = attr_name {
|
||||
feature_error!(TyCheckErrors, TyCheckError, self, attr_name.loc(), "")
|
||||
feature_error!(
|
||||
TyCheckErrors,
|
||||
TyCheckError,
|
||||
self,
|
||||
attr_name.loc(),
|
||||
"substitute_call for methods/type-var"
|
||||
)
|
||||
} else {
|
||||
let is_procedural = obj
|
||||
.show_acc()
|
||||
|
@ -1073,13 +1097,16 @@ impl Context {
|
|||
let ret_t = free_var(self.level, Constraint::new_type_of(Type));
|
||||
let non_default_params = pos_args.iter().map(|a| anon(a.expr.t())).collect();
|
||||
let subr_t = subr_t(kind, non_default_params, None, vec![], ret_t);
|
||||
self.occur(&subr_t, instance, obj)?;
|
||||
fv.link(&subr_t);
|
||||
Ok(None)
|
||||
Ok(SubstituteResult::Ok)
|
||||
}
|
||||
}
|
||||
Type::Refinement(refine) => {
|
||||
self.substitute_call(obj, attr_name, &refine.t, pos_args, kw_args)
|
||||
}
|
||||
// instance must be instantiated
|
||||
Type::Quantified(_) => unreachable_error!(TyCheckErrors, TyCheckError, self),
|
||||
Type::Subr(subr) => {
|
||||
let mut errs = TyCheckErrors::empty();
|
||||
let is_method = subr.self_t().is_some();
|
||||
|
@ -1235,7 +1262,7 @@ impl Context {
|
|||
/*if subr.has_qvar() {
|
||||
panic!("{subr} has qvar");
|
||||
}*/
|
||||
Ok(None)
|
||||
Ok(SubstituteResult::Ok)
|
||||
} else {
|
||||
Err(errs)
|
||||
}
|
||||
|
@ -1249,7 +1276,7 @@ impl Context {
|
|||
let instance =
|
||||
self.instantiate_t_inner(call_vi.t.clone(), &mut dummy, obj)?;
|
||||
self.substitute_call(obj, attr_name, &instance, pos_args, kw_args)?;
|
||||
return Ok(Some(instance));
|
||||
return Ok(SubstituteResult::__Call__(instance));
|
||||
}
|
||||
}
|
||||
let hint = if other == &ClassType {
|
||||
|
@ -1553,12 +1580,12 @@ impl Context {
|
|||
fmt_slice(pos_args),
|
||||
fmt_slice(kw_args)
|
||||
);
|
||||
let res = self.substitute_call(obj, attr_name, &instance, pos_args, kw_args)?;
|
||||
let instance = if let Some(__call__) = res {
|
||||
__call__
|
||||
} else {
|
||||
instance
|
||||
let instance = match self.substitute_call(obj, attr_name, &instance, pos_args, kw_args)? {
|
||||
SubstituteResult::Ok => instance,
|
||||
SubstituteResult::__Call__(__call__) => __call__,
|
||||
SubstituteResult::Coerced(coerced) => coerced,
|
||||
};
|
||||
debug_assert!(!instance.is_quantified());
|
||||
log!(info "Substituted:\ninstance: {instance}");
|
||||
let res = self.eval_t_params(instance, self.level, obj)?;
|
||||
log!(info "Params evaluated:\nres: {res}\n");
|
||||
|
|
|
@ -293,10 +293,23 @@ impl Context {
|
|||
) -> Result<Type, (TyCheckErrors, Type)> {
|
||||
let mut errs = TyCheckErrors::empty();
|
||||
// -> Result<Type, (Type, TyCheckErrors)> {
|
||||
let opt_decl_sig_t = self
|
||||
let opt_decl_sig_t = match self
|
||||
.rec_get_decl_info(&sig.ident, AccessKind::Name, &self.cfg.input, &self.name)
|
||||
.ok()
|
||||
.map(|vi| enum_unwrap!(vi.t, Type::Subr));
|
||||
.map(|vi| vi.t)
|
||||
{
|
||||
Some(Type::Subr(subr)) => Some(subr),
|
||||
Some(Type::FreeVar(fv)) if fv.is_unbound() => return Ok(Type::FreeVar(fv)),
|
||||
Some(other) => {
|
||||
let err = TyCheckError::unreachable(
|
||||
self.cfg.input.clone(),
|
||||
"instantiate_sub_sig_t",
|
||||
line!(),
|
||||
);
|
||||
return Err((TyCheckErrors::from(err), other));
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let mut tmp_tv_cache = self
|
||||
.instantiate_ty_bounds(&sig.bounds, PreRegister)
|
||||
.map_err(|errs| (errs, Type::Failure))?;
|
||||
|
@ -1447,26 +1460,17 @@ impl Context {
|
|||
|
||||
pub(crate) fn instantiate(&self, quantified: Type, callee: &hir::Expr) -> TyCheckResult<Type> {
|
||||
match quantified {
|
||||
FreeVar(fv) if fv.is_linked() => self.instantiate(fv.crack().clone(), callee),
|
||||
Quantified(quant) => {
|
||||
let mut tmp_tv_cache = TyVarCache::new(self.level, self);
|
||||
let t = self.instantiate_t_inner(*quant, &mut tmp_tv_cache, callee)?;
|
||||
match &t {
|
||||
Type::Subr(subr) => {
|
||||
if let Some(self_t) = subr.self_t() {
|
||||
self.sub_unify(
|
||||
callee.ref_t(),
|
||||
self_t,
|
||||
callee,
|
||||
Some(&Str::ever("self")),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
let ty = self.instantiate_t_inner(*quant, &mut tmp_tv_cache, callee)?;
|
||||
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") && t.has_qvar() {
|
||||
panic!("{t} has qvar")
|
||||
if cfg!(feature = "debug") && ty.has_qvar() {
|
||||
panic!("{ty} has qvar")
|
||||
}
|
||||
Ok(t)
|
||||
Ok(ty)
|
||||
}
|
||||
// HACK: {op: |T|(T -> T) | op == F} => ?T -> ?T
|
||||
Refinement(refine) if refine.t.is_quantified() => {
|
||||
|
|
|
@ -24,17 +24,19 @@ use Type::*;
|
|||
use ValueObj::{Inf, NegInf};
|
||||
|
||||
impl Context {
|
||||
// occur(X -> ?T, ?T) ==> Error
|
||||
// occur(?T, ?T -> X) ==> Error
|
||||
// occur(?T, Option(?T)) ==> Error
|
||||
// occur(?T, ?T.Output) ==> Error
|
||||
fn occur(
|
||||
/// occur(X -> ?T, ?T) ==> Error
|
||||
/// occur(?T, ?T -> X) ==> Error
|
||||
/// occur(?T, Option(?T)) ==> Error
|
||||
/// occur(?T, ?T.Output) ==> Error
|
||||
pub(crate) fn occur(
|
||||
&self,
|
||||
maybe_sub: &Type,
|
||||
maybe_sup: &Type,
|
||||
loc: &impl Locational,
|
||||
) -> TyCheckResult<()> {
|
||||
match (maybe_sub, maybe_sup) {
|
||||
(Type::FreeVar(fv), _) if fv.is_linked() => self.occur(&fv.crack(), maybe_sup, loc),
|
||||
(_, Type::FreeVar(fv)) if fv.is_linked() => self.occur(maybe_sub, &fv.crack(), loc),
|
||||
(Type::FreeVar(sub), Type::FreeVar(sup)) => {
|
||||
if sub.is_unbound() && sup.is_unbound() && sub == sup {
|
||||
Err(TyCheckErrors::from(TyCheckError::subtyping_error(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue