feat: improve record type narrowing

This commit is contained in:
Shunsuke Shibayama 2024-03-24 18:18:09 +09:00
parent 35f55c62d3
commit ce5eafca9b
7 changed files with 62 additions and 4 deletions

View file

@ -1304,7 +1304,7 @@ impl Context {
/// union(Array(Int, 2), Array(Str, 2)) == Array(Int or Str, 2)
/// union(Array(Int, 2), Array(Str, 3)) == Array(Int, 2) or Array(Int, 3)
/// union({ .a = Int }, { .a = Str }) == { .a = Int or Str }
/// union({ .a = Int }, { .a = Int; .b = Int }) == { .a = Int }
/// union({ .a = Int }, { .a = Int; .b = Int }) == { .a = Int } or { .a = Int; .b = Int } # not to lost `b` information
/// union((A and B) or C) == (A or C) and (B or C)
/// ```
pub(crate) fn union(&self, lhs: &Type, rhs: &Type) -> Type {
@ -1326,7 +1326,7 @@ impl Context {
union
}
}
(Record(l), Record(r)) => {
(Record(l), Record(r)) if l.len() == r.len() && l.len() == 1 => {
let mut union = Dict::new();
for (l_k, l_v) in l.iter() {
if let Some((r_k, r_v)) = r.get_key_value(l_k) {

View file

@ -1030,6 +1030,36 @@ impl Context {
}
}
Type::Structural(t) => self.get_attr_info_from_attributive(t, ident, namespace),
// TODO: And
Type::Or(l, r) => {
let l_info = self.get_attr_info_from_attributive(l, ident, namespace);
let r_info = self.get_attr_info_from_attributive(r, ident, namespace);
match (l_info, r_info) {
(Triple::Ok(l), Triple::Ok(r)) => {
let res = self.union(&l.t, &r.t);
let vis = if l.vis.is_public() && r.vis.is_public() {
Visibility::DUMMY_PUBLIC
} else {
Visibility::DUMMY_PRIVATE
};
let vi = VarInfo::new(
res,
l.muty,
vis,
l.kind,
l.comptime_decos,
l.ctx,
l.py_name,
l.def_loc,
);
Triple::Ok(vi)
},
(Triple::Ok(_), Triple::Err(e))
| (Triple::Err(e), Triple::Ok(_)) => Triple::Err(e),
(Triple::Err(e1), Triple::Err(_e2)) => Triple::Err(e1),
_ => Triple::None,
}
}
_other => Triple::None,
}
}

View file

@ -2405,6 +2405,16 @@ impl Context {
if expr == target.as_ref() {
return Some(*guard.to.clone());
}
// { r.x in Int } => { r in Structural { .x = Int } }
else if let ast::Expr::Accessor(ast::Accessor::Attr(attr)) = target.as_ref() {
if attr.obj.as_ref() == expr {
let mut rec = Dict::new();
let vis = self.instantiate_vis_modifier(&attr.ident.vis).ok()?;
let field = Field::new(vis, attr.ident.inspect().clone());
rec.insert(field, *guard.to.clone());
return Some(Type::Record(rec).structuralize());
}
}
}
}
None