mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 12:14:43 +00:00
fix: dict type bug
This commit is contained in:
parent
3f61bc5c17
commit
144a05ec6b
7 changed files with 82 additions and 27 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue