fix(typechecker): quantified types unifying

This commit is contained in:
Shunsuke Shibayama 2023-02-19 02:00:54 +09:00
parent 448fe4e64c
commit 855d47f02c
7 changed files with 77 additions and 40 deletions

View file

@ -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),
}

View file

@ -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 {

View file

@ -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");

View file

@ -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() => {

View file

@ -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(