Skip to content

Commit

Permalink
fix: access violation bug caused by sub_unify
Browse files Browse the repository at this point in the history
add structural method types inferring
  • Loading branch information
mtshiba committed Mar 20, 2023
1 parent 57588c7 commit ebf41b5
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 82 deletions.
54 changes: 50 additions & 4 deletions crates/erg_compiler/context/inquire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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> {
Expand All @@ -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(),
Expand All @@ -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> {
Expand Down Expand Up @@ -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(),
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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}",
Expand Down
147 changes: 69 additions & 78 deletions crates/erg_compiler/context/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand All @@ -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(())
}
Expand Down

0 comments on commit ebf41b5

Please sign in to comment.