From 144a05ec6b200c6484b3fce5236ba2832fbcf0d3 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sat, 28 Sep 2024 00:29:48 +0900 Subject: [PATCH] fix: dict type bug --- crates/erg_compiler/context/compare.rs | 1 + crates/erg_compiler/context/eval.rs | 25 ++++++++- .../context/initialize/const_func.rs | 16 +++--- crates/erg_compiler/context/inquire.rs | 51 ++++++++++++++----- crates/erg_compiler/context/unify.rs | 6 +-- crates/erg_compiler/ty/typaram.rs | 7 +++ tests/should_ok/collection.er | 3 ++ 7 files changed, 82 insertions(+), 27 deletions(-) diff --git a/crates/erg_compiler/context/compare.rs b/crates/erg_compiler/context/compare.rs index cc9526d20..d5ebb611e 100644 --- a/crates/erg_compiler/context/compare.rs +++ b/crates/erg_compiler/context/compare.rs @@ -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 { diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index fb9a9bc37..1b1ee5737 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -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, ()> { + 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, ()> { match ty { Type::FreeVar(fv) if fv.is_linked() => { diff --git a/crates/erg_compiler/context/initialize/const_func.rs b/crates/erg_compiler/context/initialize/const_func.rs index eec8979ee..c670e5e03 100644 --- a/crates/erg_compiler/context/initialize/const_func.rs +++ b/crates/erg_compiler/context/initialize/const_func.rs @@ -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 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 { +pub(crate) fn dict_concat(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 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 { +pub(crate) fn dict_diff(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 other = args diff --git a/crates/erg_compiler/context/inquire.rs b/crates/erg_compiler/context/inquire.rs index 4409ad757..91c7bef91 100644 --- a/crates/erg_compiler/context/inquire.rs +++ b/crates/erg_compiler/context/inquire.rs @@ -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() diff --git a/crates/erg_compiler/context/unify.rs b/crates/erg_compiler/context/unify.rs index f1baec45a..b9d3d5526 100644 --- a/crates/erg_compiler/context/unify.rs +++ b/crates/erg_compiler/context/unify.rs @@ -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) diff --git a/crates/erg_compiler/ty/typaram.rs b/crates/erg_compiler/ty/typaram.rs index 3ec3aca90..747621bed 100644 --- a/crates/erg_compiler/ty/typaram.rs +++ b/crates/erg_compiler/ty/typaram.rs @@ -688,6 +688,10 @@ impl TryFrom for Dict { 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 for Vec { 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::>()) + } _ => Err(()), } } diff --git a/tests/should_ok/collection.er b/tests/should_ok/collection.er index f01a97bc2..2ff20c082 100644 --- a/tests/should_ok/collection.er +++ b/tests/should_ok/collection.er @@ -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