fix: Dict::get

This commit is contained in:
Shunsuke Shibayama 2024-09-21 17:05:57 +09:00
parent f6145d01a5
commit 3366043a2d
9 changed files with 45 additions and 23 deletions

View file

@ -2359,14 +2359,13 @@ impl Context {
))); )));
dict_.register_builtin_const(FUNC_AS_RECORD, Visibility::BUILTIN_PUBLIC, None, as_record); dict_.register_builtin_const(FUNC_AS_RECORD, Visibility::BUILTIN_PUBLIC, None, as_record);
let Def = type_q(TY_DEFAULT); let Def = type_q(TY_DEFAULT);
let K = type_q(TY_K);
let V = type_q(TY_V);
let get_t = no_var_fn_met( let get_t = no_var_fn_met(
dict_t.clone(), dict! { K.clone() => V.clone() }.into(),
vec![kw(KW_KEY, T.clone())], vec![kw(KW_KEY, K.clone())],
vec![kw_default(KW_DEFAULT, Def.clone(), NoneType)], vec![kw_default(KW_DEFAULT, Def.clone(), NoneType)],
or( or(V.clone(), Def),
proj_call(D.clone(), FUNDAMENTAL_GETITEM, vec![ty_tp(T.clone())]),
Def,
),
) )
.quantify(); .quantify();
dict_.register_py_builtin(FUNC_GET, get_t, Some(FUNC_GET), 9); dict_.register_py_builtin(FUNC_GET, get_t, Some(FUNC_GET), 9);

View file

@ -239,21 +239,21 @@ pub(crate) fn sub_vdict_get<'d>(
) -> Option<&'d ValueObj> { ) -> Option<&'d ValueObj> {
let mut matches = vec![]; let mut matches = vec![];
for (k, v) in dict.iter() { for (k, v) in dict.iter() {
match (key, k) { if key == k {
(ValueObj::Type(idx), ValueObj::Type(kt)) return Some(v);
if ctx.subtype_of(&idx.typ().lower_bounded(), &kt.typ().lower_bounded()) => }
match (ctx.convert_value_into_type(key.clone()), ctx.convert_value_into_type(k.clone())) {
(Ok(idx), Ok(kt))
if ctx.subtype_of(&idx.lower_bounded(), &kt.lower_bounded()) /*|| dict.len() == 1*/ =>
{ {
matches.push((idx, kt, v)); matches.push((idx, kt, v));
} }
(idx, k) if idx == k => {
return Some(v);
}
_ => {} _ => {}
} }
} }
for (idx, kt, v) in matches.into_iter() { for (idx, kt, v) in matches.into_iter() {
let list = UndoableLinkedList::new(); let list = UndoableLinkedList::new();
match ctx.undoable_sub_unify(idx.typ(), kt.typ(), &(), &list, None) { match ctx.undoable_sub_unify(&idx, &kt, &(), &list, None) {
Ok(_) => { Ok(_) => {
return Some(v); return Some(v);
} }
@ -272,21 +272,24 @@ pub(crate) fn sub_tpdict_get<'d>(
) -> Option<&'d TyParam> { ) -> Option<&'d TyParam> {
let mut matches = vec![]; let mut matches = vec![];
for (k, v) in dict.iter() { for (k, v) in dict.iter() {
match (<&Type>::try_from(key), <&Type>::try_from(k)) { if key == k {
return Some(v);
}
match (
ctx.convert_tp_into_type(key.clone()),
ctx.convert_tp_into_type(k.clone()),
) {
(Ok(idx), Ok(kt)) (Ok(idx), Ok(kt))
if ctx.subtype_of(&idx.lower_bounded(), &kt.lower_bounded()) || dict.len() == 1 => if ctx.subtype_of(&idx.lower_bounded(), &kt.lower_bounded()) || dict.len() == 1 =>
{ {
matches.push((idx, kt, v)); matches.push((idx, kt, v));
} }
(_, _) if key == k => {
return Some(v);
}
_ => {} _ => {}
} }
} }
for (idx, kt, v) in matches.into_iter() { for (idx, kt, v) in matches.into_iter() {
let list = UndoableLinkedList::new(); let list = UndoableLinkedList::new();
match ctx.undoable_sub_unify(idx, kt, &(), &list, None) { match ctx.undoable_sub_unify(&idx, &kt, &(), &list, None) {
Ok(_) => { Ok(_) => {
return Some(v); return Some(v);
} }

View file

@ -1077,8 +1077,9 @@ impl Context {
); );
let E = mono_q(TY_E, subtypeof(mono(EQ))); let E = mono_q(TY_E, subtypeof(mono(EQ)));
let E2 = mono_q(TY_E, subtypeof(mono(IRREGULAR_EQ))); let E2 = mono_q(TY_E, subtypeof(mono(IRREGULAR_EQ)));
let op_t = bin_op(E.clone(), E, Bool).quantify() let op_t = (bin_op(E.clone(), E, Bool).quantify()
& bin_op(E2.clone(), E2.clone(), E2.proj(OUTPUT)).quantify(); & bin_op(E2.clone(), E2.clone(), E2.proj(OUTPUT)).quantify())
.with_default_intersec_index(0);
self.register_builtin_py_impl( self.register_builtin_py_impl(
OP_EQ, OP_EQ,
op_t.clone(), op_t.clone(),

View file

@ -1221,7 +1221,17 @@ impl<A: ASTBuildable> GenericASTLowerer<A> {
Some(guard(namespace, target, to)) Some(guard(namespace, target, to))
} }
TokenKind::Symbol if &op.content[..] == "isinstance" => { TokenKind::Symbol if &op.content[..] == "isinstance" => {
let to = self.module.context.expr_to_type(rhs.clone()).ok()?; // isinstance(x, (T, U)) => x: T or U
let to = if let ast::Expr::Tuple(ast::Tuple::Normal(tys)) = rhs {
tys.elems.pos_args.iter().fold(Type::Never, |acc, ex| {
let Ok(ty) = self.module.context.expr_to_type(ex.expr.clone()) else {
return acc;
};
self.module.context.union(&acc, &ty)
})
} else {
self.module.context.expr_to_type(rhs.clone()).ok()?
};
Some(guard(namespace, target, to)) Some(guard(namespace, target, to))
} }
TokenKind::IsOp | TokenKind::DblEq => { TokenKind::IsOp | TokenKind::DblEq => {

View file

@ -4,4 +4,6 @@ dict = !d
dict.insert! "b", 2 dict.insert! "b", 2
_ = dict.get("a") == "a" # ERR _ = dict.get("a") == "a" # ERR
_ = dict.get("b") == "a" # ERR _ = dict.get("b") == "a" # ERR
_ = dict.get("c") # ERR _ = dict.get("c") # OK
_ = dict["b"] # OK
_ = dict["c"] # ERR

View file

@ -13,3 +13,6 @@ _: {I: Int | (I < 5 or I != 3) and I != 4} = 4 # ERR
check _: {S: Str | S.replace("abc", "") == ""} = None check _: {S: Str | S.replace("abc", "") == ""} = None
check "abcd" # ERR check "abcd" # ERR
dic as Dict({{111}: {222}}) = {111: 222}
_ = dic[333] # ERR

View file

@ -6,5 +6,6 @@ for! {"a": 1, "b": 2}.values(), i =>
dic = { "a": 1, "b": 2 } dic = { "a": 1, "b": 2 }
assert dic.concat({ "c": 3 }) == { "a": 1, "b": 2, "c": 3 } assert dic.concat({ "c": 3 }) == { "a": 1, "b": 2, "c": 3 }
assert dic.diff({ "a": 1 }) == { "b": 2 } assert dic.diff({ "a": 1 }) == { "b": 2 }
assert dic.get("a"+"b", 3) == 3
rec = dic.as_record() rec = dic.as_record()
assert rec.a == 1 and rec.b == 2 assert rec.a == 1 and rec.b == 2

View file

@ -6,3 +6,6 @@ _: {I: Int | I < 5 or I != 3 and I != 4} = 4
check _: {S: Str | S.replace("abc", "") == ""} = None check _: {S: Str | S.replace("abc", "") == ""} = None
check "abc" check "abc"
dic as Dict({{111}: {222}}) = {111: 222}
_: {222} = dic[111]

View file

@ -766,7 +766,7 @@ fn exec_recursive_fn_err() -> Result<(), ()> {
#[test] #[test]
fn exec_refinement_err() -> Result<(), ()> { fn exec_refinement_err() -> Result<(), ()> {
expect_failure("tests/should_err/refinement.er", 0, 9) expect_failure("tests/should_err/refinement.er", 0, 10)
} }
#[test] #[test]