mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 18:58:30 +00:00
fix: dict type bug
This commit is contained in:
parent
b238de378d
commit
41537f2aba
9 changed files with 208 additions and 70 deletions
|
@ -9,7 +9,7 @@ use erg_common::log;
|
|||
use erg_common::set::Set;
|
||||
use erg_common::shared::Shared;
|
||||
use erg_common::traits::{Locational, Stream};
|
||||
use erg_common::{dict, fmt_vec, fn_name, option_enum_unwrap, set, set_recursion_limit, Triple};
|
||||
use erg_common::{dict, fmt_vec, fn_name, set, set_recursion_limit, Triple};
|
||||
use erg_common::{ArcArray, Str};
|
||||
use OpKind::*;
|
||||
|
||||
|
@ -667,10 +667,52 @@ impl Context {
|
|||
}
|
||||
|
||||
fn tp_eval_const_call(&self, call: &Call) -> Failable<TyParam> {
|
||||
if let Expr::Accessor(acc) = call.obj.as_ref() {
|
||||
match acc {
|
||||
Accessor::Ident(ident) => {
|
||||
let obj = self.rec_get_const_obj(ident.inspect()).ok_or_else(|| {
|
||||
if let Some(attr) = &call.attr_name {
|
||||
let obj = self
|
||||
.eval_const_expr(&call.obj)
|
||||
.map_err(|(val, errs)| (TyParam::value(val), errs))?;
|
||||
let callee = self
|
||||
.eval_attr(obj.clone(), attr)
|
||||
.map_err(|err| (TyParam::Failure, err.into()))?;
|
||||
let ValueObj::Subr(subr) = callee else {
|
||||
return Err((
|
||||
TyParam::Failure,
|
||||
EvalError::type_mismatch_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
Location::concat(call.obj.as_ref(), attr),
|
||||
self.caused_by(),
|
||||
attr.inspect(),
|
||||
None,
|
||||
&mono("Subroutine"),
|
||||
&callee.t(),
|
||||
self.get_candidates(&callee.t()),
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
};
|
||||
let (mut args, mut errs) = match self.eval_args(&call.args) {
|
||||
Ok(args) => (args, EvalErrors::empty()),
|
||||
Err((args, es)) => (args, es),
|
||||
};
|
||||
args.pos_args.insert(0, obj);
|
||||
let tp = match self.call(subr.clone(), args, call.loc()) {
|
||||
Ok(tp) => tp,
|
||||
Err((tp, es)) => {
|
||||
errs.extend(es);
|
||||
tp
|
||||
}
|
||||
};
|
||||
if errs.is_empty() {
|
||||
Ok(tp)
|
||||
} else {
|
||||
Err((tp, errs))
|
||||
}
|
||||
} else {
|
||||
match call.obj.as_ref() {
|
||||
Expr::Accessor(Accessor::Ident(ident)) => {
|
||||
let callee = self.rec_get_const_obj(ident.inspect()).ok_or_else(|| {
|
||||
(
|
||||
TyParam::Failure,
|
||||
EvalError::not_comptime_fn_error(
|
||||
|
@ -685,31 +727,29 @@ impl Context {
|
|||
)
|
||||
})?;
|
||||
// TODO: __call__
|
||||
let subr = option_enum_unwrap!(obj, ValueObj::Subr)
|
||||
.ok_or_else(|| {
|
||||
(
|
||||
TyParam::Failure,
|
||||
EvalError::type_mismatch_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
ident.loc(),
|
||||
self.caused_by(),
|
||||
ident.inspect(),
|
||||
None,
|
||||
&mono("Subroutine"),
|
||||
&obj.t(),
|
||||
self.get_candidates(&obj.t()),
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
let ValueObj::Subr(subr) = callee else {
|
||||
return Err((
|
||||
TyParam::Failure,
|
||||
EvalError::type_mismatch_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
ident.loc(),
|
||||
self.caused_by(),
|
||||
ident.inspect(),
|
||||
None,
|
||||
&mono("Subroutine"),
|
||||
&callee.t(),
|
||||
self.get_candidates(&callee.t()),
|
||||
None,
|
||||
)
|
||||
})?
|
||||
.clone();
|
||||
.into(),
|
||||
));
|
||||
};
|
||||
let (args, mut errs) = match self.eval_args(&call.args) {
|
||||
Ok(args) => (args, EvalErrors::empty()),
|
||||
Err((args, es)) => (args, es),
|
||||
};
|
||||
let tp = match self.call(subr, args, call.loc()) {
|
||||
let tp = match self.call(subr.clone(), args, call.loc()) {
|
||||
Ok(tp) => tp,
|
||||
Err((tp, es)) => {
|
||||
errs.extend(es);
|
||||
|
@ -723,7 +763,7 @@ impl Context {
|
|||
}
|
||||
}
|
||||
// TODO: eval attr
|
||||
Accessor::Attr(_attr) => Err((
|
||||
Expr::Accessor(Accessor::Attr(_attr)) => Err((
|
||||
TyParam::Failure,
|
||||
EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
|
@ -733,7 +773,7 @@ impl Context {
|
|||
)),
|
||||
)),
|
||||
// TODO: eval type app
|
||||
Accessor::TypeApp(_type_app) => Err((
|
||||
Expr::Accessor(Accessor::TypeApp(_type_app)) => Err((
|
||||
TyParam::Failure,
|
||||
EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
|
@ -753,16 +793,6 @@ impl Context {
|
|||
)),
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err((
|
||||
TyParam::Failure,
|
||||
EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
call.loc(),
|
||||
self.caused_by(),
|
||||
)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1698,11 +1698,17 @@ impl Context {
|
|||
t_singleton(unknown_len_list_t(T.clone())),
|
||||
)
|
||||
.quantify();
|
||||
generic_list.register_builtin_erg_impl(
|
||||
let list_constructor = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
|
||||
FUNDAMENTAL_GETITEM,
|
||||
list_constructor,
|
||||
t_getitem.clone(),
|
||||
None,
|
||||
)));
|
||||
generic_list.register_builtin_const(
|
||||
FUNDAMENTAL_GETITEM,
|
||||
t_getitem,
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
Some(t_getitem),
|
||||
list_constructor,
|
||||
);
|
||||
}
|
||||
let mut list_hash = Self::builtin_methods(Some(mono(HASH)), 1);
|
||||
|
@ -2249,11 +2255,17 @@ impl Context {
|
|||
t_singleton(Type::from(dict! { T.clone() => U.clone() })),
|
||||
)
|
||||
.quantify();
|
||||
generic_dict.register_builtin_erg_impl(
|
||||
let dict_constructor = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
|
||||
FUNDAMENTAL_GETITEM,
|
||||
dict_constructor,
|
||||
t_getitem.clone(),
|
||||
None,
|
||||
)));
|
||||
generic_dict.register_builtin_const(
|
||||
FUNDAMENTAL_GETITEM,
|
||||
t_getitem,
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
Some(t_getitem),
|
||||
dict_constructor,
|
||||
);
|
||||
}
|
||||
let dic_t = poly(DICT, vec![D.clone()]);
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::context::eval::UndoableLinkedList;
|
|||
use crate::context::initialize::closed_range;
|
||||
use crate::context::Context;
|
||||
use crate::feature_error;
|
||||
use crate::ty::constructors::{and, mono, tuple_t, v_enum};
|
||||
use crate::ty::constructors::{and, dict_mut, list_mut, mono, tuple_t, v_enum};
|
||||
use crate::ty::value::{EvalValueError, EvalValueResult, GenTypeObj, TypeObj, ValueObj};
|
||||
use crate::ty::{Field, TyParam, Type, ValueArgs};
|
||||
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
|
||||
|
@ -483,6 +483,47 @@ pub(crate) fn dict_diff(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<T
|
|||
Ok(ValueObj::Dict(slf.diff(&other)).into())
|
||||
}
|
||||
|
||||
pub(crate) fn list_constructor(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
|
||||
let _cls = args
|
||||
.remove_left_or_key("Cls")
|
||||
.ok_or_else(|| not_passed("Cls"))?;
|
||||
let elem = args
|
||||
.remove_left_or_key("elem")
|
||||
.ok_or_else(|| not_passed("elem"))?;
|
||||
let Ok(elem_t) = ctx.convert_value_into_type(elem.clone()) else {
|
||||
return Err(type_mismatch("Type", elem, "elem"));
|
||||
};
|
||||
let len = args
|
||||
.remove_left_or_key("len")
|
||||
.map(TyParam::value)
|
||||
.unwrap_or(TyParam::erased(Type::Nat));
|
||||
Ok(ValueObj::builtin_class(list_mut(elem_t, len)).into())
|
||||
}
|
||||
|
||||
pub(crate) fn dict_constructor(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
|
||||
let _cls = args
|
||||
.remove_left_or_key("Cls")
|
||||
.ok_or_else(|| not_passed("Cls"))?;
|
||||
let key_value = args
|
||||
.remove_left_or_key("key_value")
|
||||
.ok_or_else(|| not_passed("key_value"))?;
|
||||
let (key_t, value_t) = match key_value {
|
||||
ValueObj::Tuple(ts) | ValueObj::List(ts) => {
|
||||
let key = ts.first().ok_or_else(|| not_passed("key"))?;
|
||||
let value = ts.get(1).ok_or_else(|| not_passed("value"))?;
|
||||
let Ok(key_t) = ctx.convert_value_into_type(key.clone()) else {
|
||||
return Err(type_mismatch("Type", key, "key"));
|
||||
};
|
||||
let Ok(value_t) = ctx.convert_value_into_type(value.clone()) else {
|
||||
return Err(type_mismatch("Type", value, "value"));
|
||||
};
|
||||
(key_t, value_t)
|
||||
}
|
||||
_ => return Err(type_mismatch("Tuple", key_value, "key_value")),
|
||||
};
|
||||
Ok(ValueObj::builtin_class(dict_mut(dict! { key_t => value_t }.into())).into())
|
||||
}
|
||||
|
||||
/// `[Int, Str].union() == Int or Str`
|
||||
pub(crate) fn list_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
|
||||
let slf = args
|
||||
|
|
|
@ -127,6 +127,7 @@ impl Context {
|
|||
None,
|
||||
NoneType,
|
||||
)
|
||||
.quantify()
|
||||
} else {
|
||||
nd_proc(
|
||||
vec![
|
||||
|
|
|
@ -9,7 +9,7 @@ use erg_common::fresh::FRESH_GEN;
|
|||
use erg_common::traits::Locational;
|
||||
use erg_common::Str;
|
||||
#[allow(unused_imports)]
|
||||
use erg_common::{fmt_vec, fn_name, log};
|
||||
use erg_common::{dict, fmt_vec, fn_name, log};
|
||||
|
||||
use crate::context::eval::Substituter;
|
||||
use crate::context::instantiate::TyVarCache;
|
||||
|
@ -2075,6 +2075,47 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
|
|||
return Some(self.ctx.union_refinement(lhs, rhs).into());
|
||||
}
|
||||
}
|
||||
(
|
||||
Poly {
|
||||
name: ln,
|
||||
params: lps,
|
||||
},
|
||||
Poly {
|
||||
name: rn,
|
||||
params: rps,
|
||||
},
|
||||
) if ln == rn && (lhs.is_dict() || lhs.is_dict_mut()) => {
|
||||
let Ok(ValueObj::Dict(l_dict)) = self.ctx.convert_tp_into_value(lps[0].clone())
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let Ok(ValueObj::Dict(r_dict)) = self.ctx.convert_tp_into_value(rps[0].clone())
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if l_dict.len() == 1 && r_dict.len() == 1 {
|
||||
let l_key = self
|
||||
.ctx
|
||||
.convert_value_into_type(l_dict.keys().next()?.clone())
|
||||
.ok()?;
|
||||
let r_key = self
|
||||
.ctx
|
||||
.convert_value_into_type(r_dict.keys().next()?.clone())
|
||||
.ok()?;
|
||||
let l_value = self
|
||||
.ctx
|
||||
.convert_value_into_type(l_dict.values().next()?.clone())
|
||||
.ok()?;
|
||||
let r_value = self
|
||||
.ctx
|
||||
.convert_value_into_type(r_dict.values().next()?.clone())
|
||||
.ok()?;
|
||||
let unified_key = self.unify(&l_key, &r_key)?;
|
||||
let unified_value = self.unify(&l_value, &r_value)?;
|
||||
let unified_dict = TyParam::t(dict! { unified_key => unified_value }.into());
|
||||
return Some(poly(ln.clone(), vec![unified_dict]));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let l_sups = self.ctx.get_super_classes(lhs)?;
|
||||
|
@ -2092,16 +2133,26 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
|
|||
continue;
|
||||
};
|
||||
let mut tv_cache = TyVarCache::new(self.ctx.level, self.ctx);
|
||||
let l_sup = self.ctx.detach(l_sup.clone(), &mut tv_cache);
|
||||
let detached_l_sup = self.ctx.detach(l_sup.clone(), &mut tv_cache);
|
||||
drop(l_substituter);
|
||||
let Ok(r_substituter) = Substituter::substitute_typarams(self.ctx, &r_sup, rhs)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let mut tv_cache = TyVarCache::new(self.ctx.level, self.ctx);
|
||||
let r_sup = self.ctx.detach(r_sup.clone(), &mut tv_cache);
|
||||
let detached_r_sup = self.ctx.detach(r_sup.clone(), &mut tv_cache);
|
||||
drop(r_substituter);
|
||||
if let Some(t) = self.ctx.max(&l_sup, &r_sup).either() {
|
||||
if let Some(t) = self.ctx.max(&detached_l_sup, &detached_r_sup).either() {
|
||||
for l_tp in l_sup.typarams() {
|
||||
if l_tp.has_qvar() && t.contains_tp(&l_tp) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
for r_tp in r_sup.typarams() {
|
||||
if r_tp.has_qvar() && t.contains_tp(&r_tp) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
return Some(t.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2562,6 +2562,11 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
/// ```erg
|
||||
/// Int.union_size() == 1
|
||||
/// (Int or Str).union_size() == 2
|
||||
/// K(Int or Str, Int or Str or NoneType) == 3
|
||||
/// ```
|
||||
pub fn union_size(&self) -> usize {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().union_size(),
|
||||
|
@ -3119,6 +3124,10 @@ impl Type {
|
|||
|| self.contains_value(&ValueObj::Failure)
|
||||
}
|
||||
|
||||
pub fn has_refinement(&self) -> bool {
|
||||
self.is_refinement() || self.has_type_satisfies(|t| t.is_refinement())
|
||||
}
|
||||
|
||||
pub fn is_recursive(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_recursive(),
|
||||
|
@ -4088,6 +4097,11 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
/// ```erg
|
||||
/// {1, 2, 3}.derifine() == Nat
|
||||
/// List({1, 2, 3}).derifine() == List(Nat)
|
||||
/// ?T(:> {1, 2, 3}).derifine() == ?T'(:> Nat)
|
||||
/// ```
|
||||
pub fn derefine(&self) -> Type {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().derefine(),
|
||||
|
@ -4112,13 +4126,7 @@ impl Type {
|
|||
Self::Poly { name, params } => {
|
||||
let params = params
|
||||
.iter()
|
||||
.map(|tp| match tp {
|
||||
TyParam::Value(ValueObj::Type(t)) => {
|
||||
TyParam::Value(ValueObj::Type(t.clone().mapped_t(|t| t.derefine())))
|
||||
}
|
||||
TyParam::Type(t) => TyParam::t(t.derefine()),
|
||||
other => other.clone(),
|
||||
})
|
||||
.map(|tp| tp.clone().map_t(&mut |t| t.derefine()))
|
||||
.collect();
|
||||
Self::Poly {
|
||||
name: name.clone(),
|
||||
|
@ -4149,22 +4157,10 @@ impl Type {
|
|||
attr_name,
|
||||
args,
|
||||
} => {
|
||||
let lhs = match lhs.as_ref() {
|
||||
TyParam::Value(ValueObj::Type(t)) => {
|
||||
TyParam::Value(ValueObj::Type(t.clone().mapped_t(|t| t.derefine())))
|
||||
}
|
||||
TyParam::Type(t) => TyParam::t(t.derefine()),
|
||||
other => other.clone(),
|
||||
};
|
||||
let lhs = lhs.clone().map_t(&mut |t| t.derefine());
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
TyParam::Value(ValueObj::Type(t)) => {
|
||||
TyParam::Value(ValueObj::Type(t.clone().mapped_t(|t| t.derefine())))
|
||||
}
|
||||
TyParam::Type(t) => TyParam::t(t.derefine()),
|
||||
other => other.clone(),
|
||||
})
|
||||
.map(|arg| arg.clone().map_t(&mut |t| t.derefine()))
|
||||
.collect();
|
||||
proj_call(lhs, attr_name.clone(), args)
|
||||
}
|
||||
|
|
|
@ -5,3 +5,7 @@ for! {"a": 1, "b": 2}.keys(), i =>
|
|||
|
||||
dic as {Nat: Int} = {1: -1}
|
||||
_ = dic[-1] # ERR
|
||||
|
||||
l as List! {Str: Int} = ![{"a": 1}]
|
||||
l.push!({"b": 2}) # OK
|
||||
l.push!({1: "a"}) # ERR
|
||||
|
|
|
@ -13,3 +13,6 @@ assert rec.a == 1 and rec.b == 2
|
|||
gdic as {Obj: Obj} = dic
|
||||
assert "a" in gdic
|
||||
assert not 1 in gdic
|
||||
|
||||
l as List! {Str: Int} = ![{"a": 1}]
|
||||
l.push!({"b": 2}) # OK
|
||||
|
|
|
@ -599,7 +599,7 @@ fn exec_dependent_err() -> Result<(), ()> {
|
|||
|
||||
#[test]
|
||||
fn exec_dict_err() -> Result<(), ()> {
|
||||
expect_compile_failure("tests/should_err/dict.er", 0, 3)
|
||||
expect_compile_failure("tests/should_err/dict.er", 0, 4)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue