mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-03 10:23:20 +00:00
fix: access violation bug caused by sub_unify
add structural method types inferring
This commit is contained in:
parent
57588c78f7
commit
ebf41b514e
2 changed files with 119 additions and 82 deletions
|
@ -19,7 +19,7 @@ use erg_common::{
|
|||
use erg_parser::ast::{self, Identifier, VarName};
|
||||
use erg_parser::token::Token;
|
||||
|
||||
use crate::ty::constructors::{anon, free_var, func, mono, poly, proc, proj, ref_, subr_t};
|
||||
use crate::ty::constructors::{anon, fn_met, free_var, func, mono, poly, proc, proj, ref_, subr_t};
|
||||
use crate::ty::free::Constraint;
|
||||
use crate::ty::typaram::TyParam;
|
||||
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
|
||||
|
@ -675,6 +675,7 @@ impl Context {
|
|||
|
||||
/// get type from given attributive type (Record).
|
||||
/// not ModuleType or ClassType etc.
|
||||
/// if `t == Never`, returns `VarInfo::ILLEGAL`
|
||||
fn get_attr_info_from_attributive(
|
||||
&self,
|
||||
t: &Type,
|
||||
|
@ -728,6 +729,8 @@ impl Context {
|
|||
&self,
|
||||
obj: &hir::Expr,
|
||||
attr_name: &Option<Identifier>,
|
||||
pos_args: &[hir::PosArg],
|
||||
kw_args: &[hir::KwArg],
|
||||
input: &Input,
|
||||
namespace: &Context,
|
||||
) -> SingleTyCheckResult<VarInfo> {
|
||||
|
@ -745,7 +748,7 @@ impl Context {
|
|||
});
|
||||
}
|
||||
if let Some(attr_name) = attr_name.as_ref() {
|
||||
self.search_method_info(obj, attr_name, input, namespace)
|
||||
self.search_method_info(obj, attr_name, pos_args, kw_args, input, namespace)
|
||||
} else {
|
||||
Ok(VarInfo {
|
||||
t: obj.t(),
|
||||
|
@ -766,6 +769,8 @@ impl Context {
|
|||
&self,
|
||||
obj: &hir::Expr,
|
||||
attr_name: &Identifier,
|
||||
pos_args: &[hir::PosArg],
|
||||
kw_args: &[hir::KwArg],
|
||||
input: &Input,
|
||||
namespace: &Context,
|
||||
) -> SingleTyCheckResult<VarInfo> {
|
||||
|
@ -887,6 +892,47 @@ impl Context {
|
|||
let coerced = self
|
||||
.deref_tyvar(obj.t(), Variance::Covariant, &set! {}, obj)
|
||||
.map_err(|mut errs| errs.remove(0))?;
|
||||
// search_method_info(?T, aaa, pos_args: [1, 2]) == None
|
||||
// => ?T(<: Structural({ .aaa = (self: ?T, ?U, ?V) -> ?W }))
|
||||
if coerced == Never && cfg!(feature = "py_compatible") && self.in_subr() {
|
||||
let nd_params = pos_args
|
||||
.iter()
|
||||
.map(|_| ParamTy::Pos(free_var(self.level, Constraint::new_type_of(Type))))
|
||||
.collect::<Vec<_>>();
|
||||
let d_params = kw_args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
ParamTy::kw(
|
||||
arg.keyword.inspect().clone(),
|
||||
free_var(self.level, Constraint::new_type_of(Type)),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let return_t = free_var(self.level, Constraint::new_type_of(Type));
|
||||
let subr_t = fn_met(obj.t(), nd_params, None, d_params, return_t);
|
||||
if let Type::FreeVar(fv) = obj.ref_t() {
|
||||
if fv.get_sub().is_some() {
|
||||
let vis = self.instantiate_vis_modifier(&attr_name.vis).unwrap();
|
||||
let structural = Type::Record(
|
||||
dict! { Field::new(vis, attr_name.inspect().clone()) => subr_t.clone() },
|
||||
)
|
||||
.structuralize();
|
||||
fv.update_super(|_| structural);
|
||||
}
|
||||
}
|
||||
let muty = Mutability::from(&attr_name.inspect()[..]);
|
||||
let vi = VarInfo::new(
|
||||
subr_t,
|
||||
muty,
|
||||
Visibility::DUMMY_PUBLIC,
|
||||
VarKind::Builtin,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
AbsLocation::unknown(),
|
||||
);
|
||||
return Ok(vi);
|
||||
}
|
||||
if &coerced == obj.ref_t() {
|
||||
Err(TyCheckError::no_attr_error(
|
||||
self.cfg.input.clone(),
|
||||
|
@ -899,7 +945,7 @@ impl Context {
|
|||
))
|
||||
} else {
|
||||
obj.ref_t().coerce();
|
||||
self.search_method_info(obj, attr_name, input, namespace)
|
||||
self.search_method_info(obj, attr_name, pos_args, kw_args, input, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1644,7 +1690,7 @@ impl Context {
|
|||
}
|
||||
}
|
||||
let found = self
|
||||
.search_callee_info(obj, attr_name, input, namespace)
|
||||
.search_callee_info(obj, attr_name, pos_args, kw_args, input, namespace)
|
||||
.map_err(|err| (None, TyCheckErrors::from(err)))?;
|
||||
log!(
|
||||
"Found:\ncallee: {obj}{}\nfound: {found}",
|
||||
|
|
|
@ -687,47 +687,40 @@ impl Context {
|
|||
Ok(())
|
||||
}
|
||||
(_, Type::FreeVar(rfv)) if rfv.is_unbound() => {
|
||||
// NOTE: cannot `borrow_mut` because of cycle reference
|
||||
let rfv_ref = unsafe { rfv.as_ptr().as_mut().unwrap() };
|
||||
match rfv_ref {
|
||||
FreeKind::NamedUnbound { constraint, .. }
|
||||
| FreeKind::Unbound { constraint, .. } => match constraint {
|
||||
// * sub_unify(Nat, ?E(<: Eq(?E)))
|
||||
// sub !<: l => OK (sub will widen)
|
||||
// sup !:> l => Error
|
||||
// * sub_unify(Str, ?T(:> _, <: Int)): (/* Error */)
|
||||
// * sub_unify(Ratio, ?T(:> _, <: Int)): (/* Error */)
|
||||
// sub = max(l, sub) if max exists
|
||||
// * sub_unify(Nat, ?T(:> Int, <: _)): (/* OK */)
|
||||
// * sub_unify(Int, ?T(:> Nat, <: Obj)): (?T(:> Int, <: Obj))
|
||||
// * sub_unify(Nat, ?T(:> Never, <: Add(?R))): (?T(:> Nat, <: Add(?R))
|
||||
// sub = union(l, sub) if max does not exist
|
||||
// * sub_unify(Str, ?T(:> Int, <: Obj)): (?T(:> Str or Int, <: Obj))
|
||||
// * sub_unify({0}, ?T(:> {1}, <: Nat)): (?T(:> {0, 1}, <: Nat))
|
||||
// * sub_unify(Bool, ?T(<: Bool or Y)): (?T == Bool)
|
||||
// * sub_unify(Float, ?T(<: Structural{ .imag = ?U })) ==> ?U == Float
|
||||
Constraint::Sandwiched { sub, sup } => {
|
||||
if sup.is_structural() {
|
||||
self.sub_unify(maybe_sub, sup, loc, param_name)?;
|
||||
}
|
||||
let new_sub = self.union(maybe_sub, sub);
|
||||
if sup.contains_union(&new_sub) {
|
||||
rfv.link(&new_sub); // Bool <: ?T <: Bool or Y ==> ?T == Bool
|
||||
} else {
|
||||
*constraint = Constraint::new_sandwiched(new_sub, mem::take(sup));
|
||||
}
|
||||
}
|
||||
// sub_unify(Nat, ?T(: Type)): (/* ?T(:> Nat) */)
|
||||
Constraint::TypeOf(ty) => {
|
||||
if self.supertype_of(&Type, ty) {
|
||||
*constraint = Constraint::new_supertype_of(maybe_sub.clone());
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Constraint::Uninited => unreachable!(),
|
||||
},
|
||||
_ => {}
|
||||
// * sub_unify(Nat, ?E(<: Eq(?E)))
|
||||
// sub !<: l => OK (sub will widen)
|
||||
// sup !:> l => Error
|
||||
// * sub_unify(Str, ?T(:> _, <: Int)): (/* Error */)
|
||||
// * sub_unify(Ratio, ?T(:> _, <: Int)): (/* Error */)
|
||||
// sub = max(l, sub) if max exists
|
||||
// * sub_unify(Nat, ?T(:> Int, <: _)): (/* OK */)
|
||||
// * sub_unify(Int, ?T(:> Nat, <: Obj)): (?T(:> Int, <: Obj))
|
||||
// * sub_unify(Nat, ?T(:> Never, <: Add(?R))): (?T(:> Nat, <: Add(?R))
|
||||
// sub = union(l, sub) if max does not exist
|
||||
// * sub_unify(Str, ?T(:> Int, <: Obj)): (?T(:> Str or Int, <: Obj))
|
||||
// * sub_unify({0}, ?T(:> {1}, <: Nat)): (?T(:> {0, 1}, <: Nat))
|
||||
// * sub_unify(Bool, ?T(<: Bool or Y)): (?T == Bool)
|
||||
// * sub_unify(Float, ?T(<: Structural{ .imag = ?U })) ==> ?U == Float
|
||||
if let Some((sub, mut sup)) = rfv.get_subsup() {
|
||||
if sup.is_structural() {
|
||||
self.sub_unify(maybe_sub, &sup, loc, param_name)?;
|
||||
}
|
||||
let new_sub = self.union(maybe_sub, &sub);
|
||||
if sup.contains_union(&new_sub) {
|
||||
rfv.link(&new_sub); // Bool <: ?T <: Bool or Y ==> ?T == Bool
|
||||
} else {
|
||||
let constr = Constraint::new_sandwiched(new_sub, mem::take(&mut sup));
|
||||
rfv.update_constraint(constr, true);
|
||||
}
|
||||
}
|
||||
// sub_unify(Nat, ?T(: Type)): (/* ?T(:> Nat) */)
|
||||
else if let Some(ty) = rfv.get_type() {
|
||||
if self.supertype_of(&Type, &ty) {
|
||||
let constr = Constraint::new_supertype_of(maybe_sub.clone());
|
||||
rfv.update_constraint(constr, true);
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -750,43 +743,41 @@ impl Context {
|
|||
self.sub_unify(maybe_sub, t, loc, param_name)
|
||||
}
|
||||
(Type::FreeVar(lfv), _) if lfv.is_unbound() => {
|
||||
let lfv_ref = unsafe { lfv.as_ptr().as_mut().unwrap() };
|
||||
match lfv_ref {
|
||||
FreeKind::NamedUnbound { constraint, .. }
|
||||
| FreeKind::Unbound { constraint, .. } => match constraint {
|
||||
// sub !<: r => Error
|
||||
// * sub_unify(?T(:> Int, <: _), Nat): (/* Error */)
|
||||
// * sub_unify(?T(:> Nat, <: _), Str): (/* Error */)
|
||||
// sup !:> r => Error
|
||||
// * sub_unify(?T(:> _, <: Str), Int): (/* Error */)
|
||||
// * sub_unify(?T(:> _, <: Int), Nat): (/* Error */)
|
||||
// sub <: r, sup :> r => sup = min(sup, r) if min exists
|
||||
// * sub_unify(?T(:> Never, <: Nat), Int): (/* OK */)
|
||||
// * sub_unify(?T(:> Nat, <: Obj), Int): (?T(:> Nat, <: Int))
|
||||
// sup = intersection(sup, r) if min does not exist
|
||||
// * sub_unify(?T(<: {1}), {0}): (* ?T == Never *)
|
||||
// * sub_unify(?T(<: Eq and Ord), Show): (?T(<: Eq and Ord and Show))
|
||||
Constraint::Sandwiched { sub, sup } => {
|
||||
// REVIEW: correct?
|
||||
if let Some(new_sup) = self.min(sup, maybe_sup) {
|
||||
*constraint =
|
||||
Constraint::new_sandwiched(mem::take(sub), new_sup.clone());
|
||||
} else {
|
||||
let new_sup = self.intersection(sup, maybe_sup);
|
||||
*constraint = Constraint::new_sandwiched(mem::take(sub), new_sup);
|
||||
}
|
||||
}
|
||||
// sub_unify(?T(: Type), Int): (?T(<: Int))
|
||||
Constraint::TypeOf(ty) => {
|
||||
if self.supertype_of(&Type, ty) {
|
||||
*constraint = Constraint::new_subtype_of(maybe_sup.clone());
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Constraint::Uninited => unreachable!(),
|
||||
},
|
||||
_ => {}
|
||||
// sub !<: r => Error
|
||||
// * sub_unify(?T(:> Int, <: _), Nat): (/* Error */)
|
||||
// * sub_unify(?T(:> Nat, <: _), Str): (/* Error */)
|
||||
// sup !:> r => Error
|
||||
// * sub_unify(?T(:> _, <: Str), Int): (/* Error */)
|
||||
// * sub_unify(?T(:> _, <: Int), Nat): (/* Error */)
|
||||
// sub <: r, sup :> r => sup = min(sup, r) if min exists
|
||||
// * sub_unify(?T(:> Never, <: Nat), Int): (/* OK */)
|
||||
// * sub_unify(?T(:> Nat, <: Obj), Int): (?T(:> Nat, <: Int))
|
||||
// sup = intersection(sup, r) if min does not exist
|
||||
// * sub_unify(?T(<: {1}), {0}): (* ?T == Never *)
|
||||
// * sub_unify(?T(<: Eq and Ord), Show): (?T(<: Eq and Ord and Show))
|
||||
if let Some((mut sub, sup)) = lfv.get_subsup() {
|
||||
if sup.is_structural() {
|
||||
return Ok(());
|
||||
}
|
||||
// REVIEW: correct?
|
||||
if let Some(new_sup) = self.min(&sup, maybe_sup) {
|
||||
let constr =
|
||||
Constraint::new_sandwiched(mem::take(&mut sub), new_sup.clone());
|
||||
lfv.update_constraint(constr, true);
|
||||
} else {
|
||||
let new_sup = self.intersection(&sup, maybe_sup);
|
||||
let constr = Constraint::new_sandwiched(mem::take(&mut sub), new_sup);
|
||||
lfv.update_constraint(constr, true);
|
||||
}
|
||||
}
|
||||
// sub_unify(?T(: Type), Int): (?T(<: Int))
|
||||
else if let Some(ty) = lfv.get_type() {
|
||||
if self.supertype_of(&Type, &ty) {
|
||||
let constr = Constraint::new_subtype_of(maybe_sup.clone());
|
||||
lfv.update_constraint(constr, true);
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue