mirror of
https://github.com/erg-lang/erg.git
synced 2025-07-07 21:25:31 +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(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 {
|
||||
|
|
|
@ -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() => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue