Skip to content

Commit

Permalink
fix: dict type bug
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Sep 27, 2024
1 parent 3f61bc5 commit 144a05e
Showing 7 changed files with 82 additions and 27 deletions.
1 change: 1 addition & 0 deletions crates/erg_compiler/context/compare.rs
Original file line number Diff line number Diff line change
@@ -1715,6 +1715,7 @@ impl Context {
/// intersection(Int, Str) == Never
/// intersection(Obj, Int) == Int
/// intersection(Never, Int) == Never
/// intersection(Iterable(Int), List(Int)) == List(Int)
/// ```
pub(crate) fn intersection(&self, lhs: &Type, rhs: &Type) -> Type {
if lhs == rhs {
25 changes: 24 additions & 1 deletion crates/erg_compiler/context/eval.rs
Original file line number Diff line number Diff line change
@@ -3038,7 +3038,7 @@ impl Context {
self.convert_type_to_dict_type(t)
}
Type::Refinement(refine) => self.convert_type_to_dict_type(*refine.t),
Type::Poly { name, params } if &name[..] == "Dict" => {
Type::Poly { name, params } if &name[..] == "Dict" || &name[..] == "Dict!" => {
let dict = Dict::try_from(params[0].clone())?;
let mut new_dict = dict! {};
for (k, v) in dict.into_iter() {
@@ -3076,6 +3076,29 @@ impl Context {
}
}

pub(crate) fn convert_value_to_dict(
&self,
val: &ValueObj,
) -> Result<Dict<ValueObj, ValueObj>, ()> {
match val {
ValueObj::Dict(dic) => Ok(dic.clone()),
ValueObj::Type(ty) if ty.typ().is_dict() || ty.typ().is_dict_mut() => {
let Ok(dict) = self
.convert_type_to_dict_type(ty.typ().clone())
.map(|dict| {
dict.into_iter()
.map(|(k, v)| (ValueObj::builtin_type(k), ValueObj::builtin_type(v)))
.collect()
})
else {
return Err(());
};
Ok(dict)
}
_ => Err(()),
}
}

pub(crate) fn convert_type_to_tuple_type(&self, ty: Type) -> Result<Vec<Type>, ()> {
match ty {
Type::FreeVar(fv) if fv.is_linked() => {
16 changes: 8 additions & 8 deletions crates/erg_compiler/context/initialize/const_func.rs
Original file line number Diff line number Diff line change
@@ -339,7 +339,7 @@ pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueR
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let ValueObj::Dict(slf) = slf else {
let Ok(slf) = ctx.convert_value_to_dict(&slf) else {
return Err(type_mismatch("Dict", slf, "Self"));
};
let index = args
@@ -367,7 +367,7 @@ pub(crate) fn dict_keys(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<T
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let ValueObj::Dict(slf) = slf else {
let Ok(slf) = ctx.convert_value_to_dict(&slf) else {
return Err(type_mismatch("Dict", slf, "Self"));
};
let dict_type = slf
@@ -394,7 +394,7 @@ pub(crate) fn dict_values(mut args: ValueArgs, ctx: &Context) -> EvalValueResult
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let ValueObj::Dict(slf) = slf else {
let Ok(slf) = ctx.convert_value_to_dict(&slf) else {
return Err(type_mismatch("Dict", slf, "Self"));
};
let dict_type = slf
@@ -421,7 +421,7 @@ pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let ValueObj::Dict(slf) = slf else {
let Ok(slf) = ctx.convert_value_to_dict(&slf) else {
return Err(type_mismatch("Dict", slf, "Self"));
};
let dict_type = slf
@@ -451,11 +451,11 @@ pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<

/// If the key is duplicated, the value of the right dict is used.
/// `{Str: Int, Int: Float}.concat({Int: Str, Float: Bool}) == {Str: Int, Int: Str, Float: Bool}`
pub(crate) fn dict_concat(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<TyParam> {
pub(crate) fn dict_concat(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let ValueObj::Dict(slf) = slf else {
let Ok(slf) = ctx.convert_value_to_dict(&slf) else {
return Err(type_mismatch("Dict", slf, "Self"));
};
let other = args
@@ -467,11 +467,11 @@ pub(crate) fn dict_concat(mut args: ValueArgs, _ctx: &Context) -> EvalValueResul
Ok(ValueObj::Dict(slf.concat(other)).into())
}

pub(crate) fn dict_diff(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<TyParam> {
pub(crate) fn dict_diff(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let ValueObj::Dict(slf) = slf else {
let Ok(slf) = ctx.convert_value_to_dict(&slf) else {
return Err(type_mismatch("Dict", slf, "Self"));
};
let other = args
51 changes: 38 additions & 13 deletions crates/erg_compiler/context/inquire.rs
Original file line number Diff line number Diff line change
@@ -1362,18 +1362,21 @@ impl Context {
}
}
let mut checked = vec![];
for ctx in self
.get_nominal_super_type_ctxs(&obj.ref_t().lower_bounded())
.ok_or_else(|| {
TyCheckError::type_not_found(
self.cfg.input.clone(),
line!() as usize,
obj.loc(),
self.caused_by(),
obj.ref_t(),
)
})?
{
// FIXME: tests/should_ok/collection.er
let obj_t = if obj.ref_t().lower_bounded().is_dict() {
obj.t()
} else {
obj.ref_t().lower_bounded()
};
for ctx in self.get_nominal_super_type_ctxs(&obj_t).ok_or_else(|| {
TyCheckError::type_not_found(
self.cfg.input.clone(),
line!() as usize,
obj.loc(),
self.caused_by(),
obj.ref_t(),
)
})? {
checked.push(&ctx.typ);
if let Some(vi) = ctx.get_current_scope_non_param(&attr_name.name) {
self.validate_visibility(attr_name, vi, input, namespace)?;
@@ -4058,12 +4061,14 @@ impl Context {
/// Int.meta_type() == ClassType (<: Type)
/// Show.meta_type() == TraitType (<: Type)
/// [Int; 3].meta_type() == [ClassType; 3] (<: Type)
/// (Int, Str).meta_type() == (ClassType, ClassType) (<: Type)
/// {Str: Int}.meta_type() == {ClassType: ClassType} (<: Type)
/// Indexable(T).meta_type() == TraitType (<: Type)
/// NamedTuple({ .x = Int; .y = Str }).meta_type() == NamedTuple({ .x = ClassType; .y = ClassType })
/// ```
pub fn meta_type(&self, typ: &Type) -> Type {
match typ {
Type::Poly { name, params } if &name[..] == "List" || &name[..] == "Set" => poly(
Type::Poly { name, params } if typ.is_list() || typ.is_set() || typ.is_tuple() => poly(
name.clone(),
params
.iter()
@@ -4076,6 +4081,26 @@ impl Context {
})
.collect(),
),
Type::Poly { params, .. } if typ.is_dict() => self
.convert_tp_into_value(params[0].clone())
.map(|value| {
if let ValueObj::Dict(dict) = value {
let mut ty = dict! {};
for (k, v) in dict {
let Ok(k) = self.convert_value_into_type(k) else {
return Type;
};
let Ok(v) = self.convert_value_into_type(v) else {
return Type;
};
ty.insert(self.meta_type(&k), self.meta_type(&v));
}
Type::from(ty)
} else {
Type
}
})
.unwrap_or(Type),
NamedTuple(tuple) => NamedTuple(
tuple
.iter()
6 changes: 1 addition & 5 deletions crates/erg_compiler/context/unify.rs
Original file line number Diff line number Diff line change
@@ -1497,11 +1497,7 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
return Ok(());
}
let sub = mem::take(&mut sub);
let new_sup = if let Some(new_sup) = self.ctx.min(&sup, maybe_sup).either() {
new_sup.clone()
} else {
self.ctx.intersection(&sup, maybe_sup)
};
let new_sup = self.ctx.intersection(&sup, maybe_sup);
self.sub_unify(&sub, &new_sup)?;
// ?T(:> Int, <: Int) ==> ?T == Int
// ?T(:> List(Int, 3), <: List(?T, ?N)) ==> ?T == List(Int, 3)
7 changes: 7 additions & 0 deletions crates/erg_compiler/ty/typaram.rs
Original file line number Diff line number Diff line change
@@ -688,6 +688,10 @@ impl TryFrom<TyParam> for Dict<TyParam, TyParam> {
match tp {
TyParam::FreeVar(fv) if fv.is_linked() => Dict::try_from(fv.crack().clone()),
TyParam::Dict(tps) => Ok(tps),
TyParam::Value(ValueObj::Dict(dict)) => Ok(dict
.into_iter()
.map(|(k, v)| (TyParam::value(k), TyParam::value(v)))
.collect()),
_ => Err(()),
}
}
@@ -699,6 +703,9 @@ impl TryFrom<TyParam> for Vec<TyParam> {
match tp {
TyParam::FreeVar(fv) if fv.is_linked() => Vec::try_from(fv.crack().clone()),
TyParam::List(tps) => Ok(tps),
TyParam::Value(ValueObj::List(list)) => {
Ok(list.iter().cloned().map(TyParam::value).collect::<Vec<_>>())
}
_ => Err(()),
}
}
3 changes: 3 additions & 0 deletions tests/should_ok/collection.er
Original file line number Diff line number Diff line change
@@ -10,3 +10,6 @@ ab = if True: # x: {"a"} or {"b"}
do "a"
do "b"
_ = ["c", "d", ab] # OK

d2 = [{"a": 1}]
assert d2[0]["a"] == 1

0 comments on commit 144a05e

Please sign in to comment.