fix: dict type bug

This commit is contained in:
Shunsuke Shibayama 2024-09-28 00:29:48 +09:00
parent 3f61bc5c17
commit 144a05ec6b
7 changed files with 82 additions and 27 deletions

View file

@ -1715,6 +1715,7 @@ impl Context {
/// intersection(Int, Str) == Never /// intersection(Int, Str) == Never
/// intersection(Obj, Int) == Int /// intersection(Obj, Int) == Int
/// intersection(Never, Int) == Never /// intersection(Never, Int) == Never
/// intersection(Iterable(Int), List(Int)) == List(Int)
/// ``` /// ```
pub(crate) fn intersection(&self, lhs: &Type, rhs: &Type) -> Type { pub(crate) fn intersection(&self, lhs: &Type, rhs: &Type) -> Type {
if lhs == rhs { if lhs == rhs {

View file

@ -3038,7 +3038,7 @@ impl Context {
self.convert_type_to_dict_type(t) self.convert_type_to_dict_type(t)
} }
Type::Refinement(refine) => self.convert_type_to_dict_type(*refine.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 dict = Dict::try_from(params[0].clone())?;
let mut new_dict = dict! {}; let mut new_dict = dict! {};
for (k, v) in dict.into_iter() { 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>, ()> { pub(crate) fn convert_type_to_tuple_type(&self, ty: Type) -> Result<Vec<Type>, ()> {
match ty { match ty {
Type::FreeVar(fv) if fv.is_linked() => { Type::FreeVar(fv) if fv.is_linked() => {

View file

@ -339,7 +339,7 @@ pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueR
let slf = args let slf = args
.remove_left_or_key("Self") .remove_left_or_key("Self")
.ok_or_else(|| not_passed("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")); return Err(type_mismatch("Dict", slf, "Self"));
}; };
let index = args let index = args
@ -367,7 +367,7 @@ pub(crate) fn dict_keys(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<T
let slf = args let slf = args
.remove_left_or_key("Self") .remove_left_or_key("Self")
.ok_or_else(|| not_passed("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")); return Err(type_mismatch("Dict", slf, "Self"));
}; };
let dict_type = slf let dict_type = slf
@ -394,7 +394,7 @@ pub(crate) fn dict_values(mut args: ValueArgs, ctx: &Context) -> EvalValueResult
let slf = args let slf = args
.remove_left_or_key("Self") .remove_left_or_key("Self")
.ok_or_else(|| not_passed("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")); return Err(type_mismatch("Dict", slf, "Self"));
}; };
let dict_type = slf let dict_type = slf
@ -421,7 +421,7 @@ pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
let slf = args let slf = args
.remove_left_or_key("Self") .remove_left_or_key("Self")
.ok_or_else(|| not_passed("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")); return Err(type_mismatch("Dict", slf, "Self"));
}; };
let dict_type = slf 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. /// 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}` /// `{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 let slf = args
.remove_left_or_key("Self") .remove_left_or_key("Self")
.ok_or_else(|| not_passed("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")); return Err(type_mismatch("Dict", slf, "Self"));
}; };
let other = args 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()) 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 let slf = args
.remove_left_or_key("Self") .remove_left_or_key("Self")
.ok_or_else(|| not_passed("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")); return Err(type_mismatch("Dict", slf, "Self"));
}; };
let other = args let other = args

View file

@ -1362,9 +1362,13 @@ impl Context {
} }
} }
let mut checked = vec![]; let mut checked = vec![];
for ctx in self // FIXME: tests/should_ok/collection.er
.get_nominal_super_type_ctxs(&obj.ref_t().lower_bounded()) let obj_t = if obj.ref_t().lower_bounded().is_dict() {
.ok_or_else(|| { 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( TyCheckError::type_not_found(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
@ -1372,8 +1376,7 @@ impl Context {
self.caused_by(), self.caused_by(),
obj.ref_t(), obj.ref_t(),
) )
})? })? {
{
checked.push(&ctx.typ); checked.push(&ctx.typ);
if let Some(vi) = ctx.get_current_scope_non_param(&attr_name.name) { if let Some(vi) = ctx.get_current_scope_non_param(&attr_name.name) {
self.validate_visibility(attr_name, vi, input, namespace)?; self.validate_visibility(attr_name, vi, input, namespace)?;
@ -4058,12 +4061,14 @@ impl Context {
/// Int.meta_type() == ClassType (<: Type) /// Int.meta_type() == ClassType (<: Type)
/// Show.meta_type() == TraitType (<: Type) /// Show.meta_type() == TraitType (<: Type)
/// [Int; 3].meta_type() == [ClassType; 3] (<: 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) /// Indexable(T).meta_type() == TraitType (<: Type)
/// NamedTuple({ .x = Int; .y = Str }).meta_type() == NamedTuple({ .x = ClassType; .y = ClassType }) /// NamedTuple({ .x = Int; .y = Str }).meta_type() == NamedTuple({ .x = ClassType; .y = ClassType })
/// ``` /// ```
pub fn meta_type(&self, typ: &Type) -> Type { pub fn meta_type(&self, typ: &Type) -> Type {
match typ { 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(), name.clone(),
params params
.iter() .iter()
@ -4076,6 +4081,26 @@ impl Context {
}) })
.collect(), .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( NamedTuple(tuple) => NamedTuple(
tuple tuple
.iter() .iter()

View file

@ -1497,11 +1497,7 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
return Ok(()); return Ok(());
} }
let sub = mem::take(&mut sub); let sub = mem::take(&mut sub);
let new_sup = if let Some(new_sup) = self.ctx.min(&sup, maybe_sup).either() { let new_sup = self.ctx.intersection(&sup, maybe_sup);
new_sup.clone()
} else {
self.ctx.intersection(&sup, maybe_sup)
};
self.sub_unify(&sub, &new_sup)?; self.sub_unify(&sub, &new_sup)?;
// ?T(:> Int, <: Int) ==> ?T == Int // ?T(:> Int, <: Int) ==> ?T == Int
// ?T(:> List(Int, 3), <: List(?T, ?N)) ==> ?T == List(Int, 3) // ?T(:> List(Int, 3), <: List(?T, ?N)) ==> ?T == List(Int, 3)

View file

@ -688,6 +688,10 @@ impl TryFrom<TyParam> for Dict<TyParam, TyParam> {
match tp { match tp {
TyParam::FreeVar(fv) if fv.is_linked() => Dict::try_from(fv.crack().clone()), TyParam::FreeVar(fv) if fv.is_linked() => Dict::try_from(fv.crack().clone()),
TyParam::Dict(tps) => Ok(tps), 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(()), _ => Err(()),
} }
} }
@ -699,6 +703,9 @@ impl TryFrom<TyParam> for Vec<TyParam> {
match tp { match tp {
TyParam::FreeVar(fv) if fv.is_linked() => Vec::try_from(fv.crack().clone()), TyParam::FreeVar(fv) if fv.is_linked() => Vec::try_from(fv.crack().clone()),
TyParam::List(tps) => Ok(tps), TyParam::List(tps) => Ok(tps),
TyParam::Value(ValueObj::List(list)) => {
Ok(list.iter().cloned().map(TyParam::value).collect::<Vec<_>>())
}
_ => Err(()), _ => Err(()),
} }
} }

View file

@ -10,3 +10,6 @@ ab = if True: # x: {"a"} or {"b"}
do "a" do "a"
do "b" do "b"
_ = ["c", "d", ab] # OK _ = ["c", "d", ab] # OK
d2 = [{"a": 1}]
assert d2[0]["a"] == 1