Skip to content

Commit

Permalink
fix: dict typing bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Apr 27, 2023
1 parent 630d4d6 commit 245d9ee
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 39 deletions.
33 changes: 22 additions & 11 deletions crates/erg_compiler/context/compare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1023,17 +1023,7 @@ impl Context {
(Some(sub), Some(sup)) => bounded(sub.clone(), sup.clone()),
_ => self.simple_union(lhs, rhs),
},
(other, or @ Or(l, r)) | (or @ Or(l, r), other) => {
if &self.union(other, l) == l.as_ref() || &self.union(other, r) == r.as_ref() {
or.clone()
} else if &self.union(other, l) == other {
self.union(other, r)
} else if &self.union(other, r) == other {
self.union(other, l)
} else {
self.simple_union(lhs, rhs)
}
}
(other, or @ Or(_, _)) | (or @ Or(_, _), other) => self.union_add(or, other),
(t, Type::Never) | (Type::Never, t) => t.clone(),
// Array({1, 2}, 2), Array({3, 4}, 2) ==> Array({1, 2, 3, 4}, 2)
(
Expand Down Expand Up @@ -1094,6 +1084,27 @@ impl Context {
}
}

/// ```erg
/// union_add(Int or ?T(:> NoneType), Nat) == Int or ?T
/// union_add(Int or ?T(:> NoneType), Str) == Int or ?T or Str
/// ```
fn union_add(&self, union: &Type, elem: &Type) -> Type {
let union_ts = union.union_types();
let fixed = union_ts.into_iter().map(|t| {
if let Ok(free) = <&FreeTyVar>::try_from(&t) {
free.get_sub().unwrap_or(t)
} else {
t
}
});
for t in fixed {
if self.supertype_of(&t, elem) {
return union.clone();
}
}
or(union.clone(), elem.clone())
}

/// ```erg
/// simple_union(?T, ?U) == ?T or ?U
/// union(Set!(?T(<: Int), 3), Set(?U(<: Nat), 3)) == Set(?T, 3)
Expand Down
8 changes: 7 additions & 1 deletion crates/erg_compiler/context/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,12 @@ impl Context {
(TyParam::Value(lhs), TyParam::Value(rhs)) => {
self.eval_bin(op, lhs, rhs).map(TyParam::value)
}
(TyParam::Dict(l), TyParam::Dict(r)) if op == OpKind::Add => {
Ok(TyParam::Dict(l.concat(r)))
}
(TyParam::Array(l), TyParam::Array(r)) if op == OpKind::Add => {
Ok(TyParam::Array([l, r].concat()))
}
(TyParam::FreeVar(fv), r) if fv.is_linked() => {
self.eval_bin_tp(op, fv.crack().clone(), r)
}
Expand All @@ -854,7 +860,7 @@ impl Context {
(lhs @ TyParam::FreeVar(_), rhs) => Ok(TyParam::bin(op, lhs, rhs)),
(lhs, rhs @ TyParam::FreeVar(_)) => Ok(TyParam::bin(op, lhs, rhs)),
(e @ TyParam::Erased(_), _) | (_, e @ TyParam::Erased(_)) => Ok(e),
(l, r) => feature_error!(self, Location::Unknown, &format!("{l:?} {op} {r:?}"))
(l, r) => feature_error!(self, Location::Unknown, &format!("{l} {op} {r}"))
.map_err(Into::into),
}
}
Expand Down
10 changes: 4 additions & 6 deletions crates/erg_compiler/context/initialize/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2028,7 +2028,7 @@ impl Context {
);
array_mut_.register_trait(array_mut_t.clone(), array_mut_mutable);
/* Dict! */
let dict_mut_t = poly(MUT_DICT, vec![D]);
let dict_mut_t = poly(MUT_DICT, vec![D.clone()]);
let mut dict_mut =
Self::builtin_poly_class(MUT_DICT, vec![PS::named_nd(TY_D, mono(GENERIC_DICT))], 3);
dict_mut.register_superclass(dict_t.clone(), &dict_);
Expand All @@ -2037,12 +2037,10 @@ impl Context {
let insert_t = pr_met(
ref_mut(
dict_mut_t.clone(),
// TODO:
None,
/*Some(poly(
Some(poly(
MUT_DICT,
vec![D + dict!{ K.clone() => V.clone() }.into()],
)),*/
vec![D + dict! { K.clone() => V.clone() }.into()],
)),
),
vec![kw(KW_KEY, K), kw(KW_VALUE, V)],
None,
Expand Down
80 changes: 62 additions & 18 deletions crates/erg_compiler/context/initialize/const_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::context::Context;
use crate::feature_error;
use crate::ty::constructors::{and, mono, poly, tuple_t, ty_tp};
use crate::ty::value::{EvalValueError, EvalValueResult, GenTypeObj, TypeObj, ValueObj};
use crate::ty::{Type, ValueArgs};
use crate::ty::{TyParam, Type, ValueArgs};
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
use erg_common::style::{Color, StyledStr, StyledString, THEME};

Expand Down Expand Up @@ -263,27 +263,71 @@ pub(crate) fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValue
}
}

pub(crate) fn sub_vdict_get<'d>(
dict: &'d Dict<ValueObj, ValueObj>,
key: &ValueObj,
ctx: &Context,
) -> Option<&'d ValueObj> {
let mut matches = vec![];
for (k, v) in dict.iter() {
match (key, k) {
(ValueObj::Type(idx), ValueObj::Type(kt)) if ctx.subtype_of(idx.typ(), kt.typ()) => {
matches.push((idx, kt, v));
}
(idx, k) if idx == k => {
return Some(v);
}
_ => {}
}
}
for (idx, kt, v) in matches.into_iter() {
match ctx.sub_unify(idx.typ(), kt.typ(), &(), None) {
Ok(_) => {
return Some(v);
}
Err(_err) => {
erg_common::log!(err "{idx} <!: {kt} => {v}");
}
}
}
None
}

pub(crate) fn sub_tpdict_get<'d>(
dict: &'d Dict<TyParam, TyParam>,
key: &TyParam,
ctx: &Context,
) -> Option<&'d TyParam> {
let mut matches = vec![];
for (k, v) in dict.iter() {
match (<&Type>::try_from(key), <&Type>::try_from(k)) {
(Ok(idx), Ok(kt)) if ctx.subtype_of(idx, kt) => {
matches.push((idx, kt, v));
}
(_, _) if key == k => {
return Some(v);
}
_ => {}
}
}
for (idx, kt, v) in matches.into_iter() {
match ctx.sub_unify(idx, kt, &(), None) {
Ok(_) => {
return Some(v);
}
Err(_err) => {
erg_common::log!(err "{idx} <!: {kt} => {v}");
}
}
}
None
}

pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = args.remove_left_or_key("Self").unwrap();
let slf = enum_unwrap!(slf, ValueObj::Dict);
let index = args.remove_left_or_key("Index").unwrap();
if let Some(v) = slf.get(&index).or_else(|| {
for (k, v) in slf.iter() {
match (&index, k) {
(ValueObj::Type(idx), ValueObj::Type(kt)) => {
if ctx.subtype_of(idx.typ(), kt.typ()) {
return Some(v);
}
}
(idx, k) => {
if idx == k {
return Some(v);
}
}
}
}
None
}) {
if let Some(v) = slf.get(&index).or_else(|| sub_vdict_get(&slf, &index, ctx)) {
Ok(v.clone())
} else {
let index = if let ValueObj::Type(t) = &index {
Expand Down
5 changes: 4 additions & 1 deletion crates/erg_compiler/context/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use Predicate as Pred;
use Type::*;
use ValueObj::{Inf, NegInf};

use super::initialize::const_func::sub_tpdict_get;

impl Context {
/// ```erg
/// occur(?T, ?T) ==> OK
Expand Down Expand Up @@ -444,9 +446,10 @@ impl Context {
}
(TyParam::Dict(ls), TyParam::Dict(rs)) => {
for (lk, lv) in ls.iter() {
if let Some(rv) = rs.get(lk) {
if let Some(rv) = rs.get(lk).or_else(|| sub_tpdict_get(rs, lk, self)) {
self.sub_unify_tp(lv, rv, _variance, loc, allow_divergence)?;
} else {
log!(err "{rs} does not have key {lk}");
// TODO:
return Err(TyCheckErrors::from(TyCheckError::unreachable(
self.cfg.input.clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/erg_compiler/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2389,7 +2389,7 @@ impl Type {
pub fn has_qvar(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().has_qvar(),
Self::FreeVar(fv) if fv.is_generalized() => true,
Self::FreeVar(fv) if fv.is_unbound() && fv.is_generalized() => true,
Self::FreeVar(fv) => {
if let Some((sub, sup)) = fv.get_subsup() {
fv.dummy_link();
Expand Down
2 changes: 1 addition & 1 deletion crates/erg_compiler/ty/typaram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ impl TyParam {

pub fn has_qvar(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_generalized() => true,
Self::FreeVar(fv) if fv.is_unbound() && fv.is_generalized() => true,
Self::FreeVar(fv) if fv.is_linked() => fv.crack().has_qvar(),
Self::Type(t) => t.has_qvar(),
Self::Proj { obj, .. } => obj.has_qvar(),
Expand Down
5 changes: 5 additions & 0 deletions crates/erg_compiler/ty/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,11 @@ impl ValueObj {
(Self::Nat(l), Self::Float(r)) => Some(Self::Float(l as f64 - r)),
(Self::Float(l), Self::Int(r)) => Some(Self::Float(l - r as f64)),
(Self::Str(l), Self::Str(r)) => Some(Self::Str(Str::from(format!("{l}{r}")))),
(Self::Array(l), Self::Array(r)) => {
let arr = Rc::from([l, r].concat());
Some(Self::Array(arr))
}
(Self::Dict(l), Self::Dict(r)) => Some(Self::Dict(l.concat(r))),
(inf @ (Self::Inf | Self::NegInf), _) | (_, inf @ (Self::Inf | Self::NegInf)) => {
Some(inf)
}
Expand Down
7 changes: 7 additions & 0 deletions tests/should_err/mut_dict.er
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
d = {"a": 1}
dict = !d

dict.insert! "b", 2
_ = dict.get("a") == "a" # ERR
_ = dict.get("b") == "a" # ERR
_ = dict.get("c") # ERR
1 change: 1 addition & 0 deletions tests/should_ok/mut_dict.er
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dict = !d

dict.insert! "b", 2
assert dict.get("a") == 1
assert dict.get("b") == 2
5 changes: 5 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,11 @@ fn exec_mut_array_err() -> Result<(), ()> {
expect_failure("tests/should_err/mut_array.er", 0, 4)
}

#[test]
fn exec_mut_dict_err() -> Result<(), ()> {
expect_failure("tests/should_err/mut_dict.er", 0, 3)
}

#[test]
fn exec_quantified_err() -> Result<(), ()> {
expect_failure("tests/should_err/quantified.er", 0, 3)
Expand Down

0 comments on commit 245d9ee

Please sign in to comment.