Skip to content

Commit

Permalink
Merge pull request #449 from erg-lang/named_tuple
Browse files Browse the repository at this point in the history
Add `NamedTuple`
  • Loading branch information
mtshiba authored Aug 17, 2023
2 parents 5ef1288 + 5ac0012 commit 359329e
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 3 deletions.
12 changes: 12 additions & 0 deletions crates/erg_compiler/context/compare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ impl Context {
None
};
for rhs_sup in get_types(ty_ctx) {
let _subs = Substituter::substitute_self(rhs_sup, rhs, self);
// Not `supertype_of` (only structures are compared)
match Self::cheap_supertype_of(lhs, rhs_sup) {
(Absolutely, true) => {
Expand Down Expand Up @@ -507,6 +508,15 @@ impl Context {
}
true
}
(NamedTuple(lhs), NamedTuple(rhs)) => {
for ((l_k, l_t), (r_k, r_t)) in lhs.iter().zip(rhs.iter()) {
if (l_k.vis.is_public() && r_k.vis.is_private()) || !self.supertype_of(l_t, r_t)
{
return false;
}
}
true
}
(Type, Record(rec)) => {
for (_, t) in rec.iter() {
if !self.supertype_of(&Type, t) {
Expand All @@ -516,6 +526,7 @@ impl Context {
true
}
(Bool, Guard { .. }) => true,
(Mono(n), NamedTuple(_)) => &n[..] == "GenericNamedTuple" || &n[..] == "GenericTuple",
(Type, Subr(subr)) => self.supertype_of(&Type, &subr.return_t),
(Type, Poly { name, params }) if &name[..] == "Array" || &name[..] == "Set" => {
let elem_t = self.convert_tp_into_type(params[0].clone()).unwrap();
Expand Down Expand Up @@ -758,6 +769,7 @@ impl Context {
match t {
Type::FreeVar(fv) if fv.is_linked() => self.fields(&fv.crack()),
Type::Record(fields) => fields.clone(),
Type::NamedTuple(fields) => fields.iter().cloned().collect(),
Type::Refinement(refine) => self.fields(&refine.t),
Type::Structural(t) => self.fields(t),
other => {
Expand Down
18 changes: 18 additions & 0 deletions crates/erg_compiler/context/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,24 @@ impl<'c> Substituter<'c> {
_ => {}
}
}

/// ```erg
/// substitute_self(Iterable('Self), Int)
/// -> Iterable(Int)
/// ```
pub(crate) fn substitute_self(qt: &Type, subtype: &Type, ctx: &'c Context) -> Option<Self> {
for t in qt.contained_ts() {
if t.is_qvar()
&& &t.qual_name()[..] == "Self"
&& t.get_super()
.is_some_and(|sup| ctx.supertype_of(&sup, subtype))
{
t.undoable_link(subtype);
return Some(Self::new(ctx, qt.clone(), subtype.clone()));
}
}
None
}
}

impl Context {
Expand Down
13 changes: 13 additions & 0 deletions crates/erg_compiler/context/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ impl Generalizer {
.collect();
Type::Record(fields)
}
NamedTuple(rec) => {
let fields = rec
.into_iter()
.map(|(name, t)| (name, self.generalize_t(t, uninit)))
.collect();
Type::NamedTuple(fields)
}
Callable { .. } => todo!(),
Ref(t) => ref_(self.generalize_t(*t, uninit)),
RefMut { before, after } => {
Expand Down Expand Up @@ -672,6 +679,12 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
}
Ok(Type::Record(rec))
}
Type::NamedTuple(mut rec) => {
for (_, t) in rec.iter_mut() {
*t = self.deref_tyvar(mem::take(t))?;
}
Ok(Type::NamedTuple(rec))
}
Type::Refinement(refine) => {
let t = self.deref_tyvar(*refine.t)?;
// TODO: deref_predicate
Expand Down
72 changes: 72 additions & 0 deletions crates/erg_compiler/context/initialize/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,71 @@ impl Context {
/* record */
let mut record = Self::builtin_mono_class(RECORD, 2);
record.register_superclass(Obj, &obj);
/* GenericNamedTuple */
let mut generic_named_tuple = Self::builtin_mono_class(GENERIC_NAMED_TUPLE, 2);
generic_named_tuple.register_superclass(mono(GENERIC_TUPLE), &generic_tuple);
let Slf = mono_q("Self", subtypeof(mono(GENERIC_NAMED_TUPLE)));
let input_t = tp_enum(Nat, set! {N.clone()});
let return_t = proj_call(ty_tp(Slf.clone()), FUNDAMENTAL_GETITEM, vec![N.clone()]);
let named_tuple_getitem =
fn1_met(Slf.clone(), input_t.clone(), return_t.clone()).quantify();
let mut named_tuple_indexable = Self::builtin_methods(
Some(poly(INDEXABLE, vec![ty_tp(input_t), ty_tp(return_t)])),
2,
);
named_tuple_indexable.register_builtin_py_impl(
FUNDAMENTAL_TUPLE_GETITEM,
named_tuple_getitem.clone(),
Const,
Visibility::BUILTIN_PUBLIC,
Some(FUNDAMENTAL_GETITEM),
);
generic_named_tuple.register_trait(mono(GENERIC_NAMED_TUPLE), named_tuple_indexable);
let get_item = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
FUNDAMENTAL_GETITEM,
__named_tuple_getitem__,
named_tuple_getitem,
None,
)));
generic_named_tuple.register_builtin_const(
FUNDAMENTAL_GETITEM,
Visibility::BUILTIN_PUBLIC,
get_item,
);
let mut named_tuple_iterable = Self::builtin_methods(
Some(poly(
ITERABLE,
vec![ty_tp(proj_call(ty_tp(Slf.clone()), FUNC_UNION, vec![]))],
)),
2,
);
let named_tuple_iterator = poly(
TUPLE_ITERATOR,
vec![ty_tp(proj_call(ty_tp(Slf.clone()), FUNC_UNION, vec![]))],
);
let t = fn0_met(Slf.clone(), named_tuple_iterator.clone()).quantify();
named_tuple_iterable.register_builtin_py_impl(
FUNC_ITER,
t,
Immutable,
Visibility::BUILTIN_PUBLIC,
Some(FUNDAMENTAL_ITER),
);
named_tuple_iterable.register_builtin_const(
ITERATOR,
vis.clone(),
ValueObj::builtin_class(named_tuple_iterator),
);
generic_named_tuple.register_trait(mono(GENERIC_NAMED_TUPLE), named_tuple_iterable);
// union: (self: NamedTuple({...})) -> Type
let named_tuple_union_t = fn0_met(Slf, Type).quantify();
let union = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
FUNC_UNION,
named_tuple_union,
named_tuple_union_t,
None,
)));
generic_named_tuple.register_builtin_const(FUNC_UNION, Visibility::BUILTIN_PUBLIC, union);
/* Or (true or type) */
let or_t = poly(OR, vec![ty_tp(L), ty_tp(R)]);
let mut or = Self::builtin_poly_class(OR, vec![PS::t_nd(TY_L), PS::t_nd(TY_R)], 2);
Expand Down Expand Up @@ -2471,6 +2536,13 @@ impl Context {
);
self.register_builtin_type(_tuple_t, tuple_, vis.clone(), Const, Some(FUNC_TUPLE));
self.register_builtin_type(mono(RECORD), record, vis.clone(), Const, Some(RECORD));
self.register_builtin_type(
mono(GENERIC_NAMED_TUPLE),
generic_named_tuple,
vis.clone(),
Const,
Some(GENERIC_NAMED_TUPLE),
);
self.register_builtin_type(or_t, or, vis.clone(), Const, Some(UNION));
self.register_builtin_type(
mono(STR_ITERATOR),
Expand Down
49 changes: 49 additions & 0 deletions crates/erg_compiler/context/initialize/const_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,52 @@ pub(crate) fn __range_getitem__(mut args: ValueArgs, _ctx: &Context) -> EvalValu
.into())
}
}

pub(crate) fn __named_tuple_getitem__(
mut args: ValueArgs,
ctx: &Context,
) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let fields = match ctx.convert_value_into_type(slf) {
Ok(Type::NamedTuple(fields)) => fields,
Ok(other) => {
return Err(type_mismatch("NamedTuple", other, "Self"));
}
Err(val) => {
return Err(type_mismatch("NamedTuple", val, "Self"));
}
};
let index = args
.remove_left_or_key("Index")
.ok_or_else(|| not_passed("Index"))?;
let Ok(index) = usize::try_from(&index) else {
return Err(type_mismatch("Nat", index, "Index"));
};
if let Some((_, t)) = fields.get(index) {
Ok(TyParam::t(t.clone()))
} else {
Err(no_key(Type::NamedTuple(fields), index))
}
}

/// `NamedTuple({ .x = Int; .y = Str }).union() == Int or Str`
pub(crate) fn named_tuple_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let fields = match ctx.convert_value_into_type(slf) {
Ok(Type::NamedTuple(fields)) => fields,
Ok(other) => {
return Err(type_mismatch("NamedTuple", other, "Self"));
}
Err(val) => {
return Err(type_mismatch("NamedTuple", val, "Self"));
}
};
let union = fields
.iter()
.fold(Type::Never, |union, (_, t)| ctx.union(&union, t));
Ok(ValueObj::builtin_type(union).into())
}
1 change: 1 addition & 0 deletions crates/erg_compiler/context/initialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ const GENERIC_TUPLE: &str = "GenericTuple";
const TUPLE: &str = "Tuple";
const TUPLE_ITERATOR: &str = "TupleIterator";
const RECORD: &str = "Record";
const GENERIC_NAMED_TUPLE: &str = "GenericNamedTuple";
const OR: &str = "Or";
const RANGE_ITERATOR: &str = "RangeIterator";
const ENUMERATE: &str = "Enumerate";
Expand Down
36 changes: 36 additions & 0 deletions crates/erg_compiler/context/inquire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,27 @@ impl Context {
Triple::None
}
}
Type::NamedTuple(tuple) => {
if let Some((field, attr_t)) = tuple.iter().find(|(f, _)| &f.symbol == ident.inspect()) {
let muty = Mutability::from(&ident.inspect()[..]);
let vi = VarInfo::new(
attr_t.clone(),
muty,
Visibility::new(field.vis.clone(), Str::ever("<dummy>")),
VarKind::Builtin,
None,
None,
None,
AbsLocation::unknown(),
);
if let Err(err) = self.validate_visibility(ident, &vi, &self.cfg.input, self) {
return Triple::Err(err);
}
Triple::Ok(vi)
} else {
Triple::None
}
}
Type::Structural(t) => self.get_attr_info_from_attributive(t, ident),
_other => Triple::None,
}
Expand Down Expand Up @@ -2397,6 +2418,12 @@ impl Context {
.unwrap_or(self)
.rec_local_get_mono_type("Record");
}
Type::NamedTuple(_) => {
return self
.get_builtins()
.unwrap_or(self)
.rec_local_get_mono_type("GenericNamedTuple");
}
Type::Or(_l, _r) => {
if let Some(ctx) = self.get_nominal_type_ctx(&poly("Or", vec![])) {
return Some(ctx);
Expand Down Expand Up @@ -3076,10 +3103,13 @@ impl Context {
}

// TODO:
/// ```erg
/// Int.meta_type() == ClassType (<: Type)
/// Show.meta_type() == TraitType (<: Type)
/// [Int; 3].meta_type() == [ClassType; 3] (<: 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[..] == "Array" || &name[..] == "Set" => poly(
Expand All @@ -3095,6 +3125,12 @@ impl Context {
})
.collect(),
),
NamedTuple(tuple) => NamedTuple(
tuple
.iter()
.map(|(name, tp)| (name.clone(), self.meta_type(tp)))
.collect(),
),
_ => Type,
}
}
Expand Down
6 changes: 6 additions & 0 deletions crates/erg_compiler/context/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,12 @@ impl Context {
}
Ok(Type::Record(dict))
}
NamedTuple(mut tup) => {
for (_, v) in tup.iter_mut() {
*v = self.instantiate_t_inner(mem::take(v), tmp_tv_cache, loc)?;
}
Ok(Type::NamedTuple(tup))
}
Ref(t) => {
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
Ok(ref_(t))
Expand Down
46 changes: 45 additions & 1 deletion crates/erg_compiler/context/instantiate_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use ast::{
TypeBoundSpecs, TypeSpec,
};
use erg_parser::ast::{
self, ConstArray, ConstSet, Identifier, VarName, VisModifierSpec, VisRestriction,
self, ConstArray, ConstExpr, ConstSet, Identifier, VarName, VisModifierSpec, VisRestriction,
};
use erg_parser::token::TokenKind;
use erg_parser::Parser;
Expand Down Expand Up @@ -671,6 +671,50 @@ impl Context {
)?;
Ok(t.structuralize())
}
"NamedTuple" => {
let mut args = poly_spec.args.pos_args();
let Some(first) = args.next() else {
return Err(TyCheckErrors::from(TyCheckError::args_missing_error(
self.cfg.input.clone(),
line!() as usize,
poly_spec.args.loc(),
"NamedTuple",
self.caused_by(),
vec![Str::from("Fields")],
)));
};
let ConstExpr::Record(fields) = &first.expr else {
return Err(TyCheckErrors::from(TyCheckError::type_mismatch_error(
self.cfg.input.clone(),
line!() as usize,
first.expr.loc(),
self.caused_by(),
"NamedTuple",
None,
&mono("Record"),
&self.instantiate_const_expr_as_type(
&first.expr,
None,
tmp_tv_cache,
not_found_is_qvar,
)?,
None,
None,
)));
};
let mut ts = vec![];
for def in fields.attrs.iter() {
let t = self.instantiate_const_expr_as_type(
&def.body.block[0],
None,
tmp_tv_cache,
not_found_is_qvar,
)?;
let vis = self.instantiate_vis_modifier(&def.ident.vis)?;
ts.push((Field::new(vis, def.ident.inspect().clone()), t));
}
Ok(Type::NamedTuple(ts))
}
other => {
let Some((typ, ctx)) = self.get_type(&Str::rc(other)) else {
return Err(TyCheckErrors::from(TyCheckError::no_type_error(
Expand Down
5 changes: 5 additions & 0 deletions crates/erg_compiler/context/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,11 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
}
}
}
(NamedTuple(sub_tup), NamedTuple(sup_tup)) => {
for ((_, lt), (_, rt)) in sub_tup.iter().zip(sup_tup.iter()) {
self.sub_unify(lt, rt)?;
}
}
(Subr(sub_subr), Subr(sup_subr)) => {
sub_subr
.non_default_params
Expand Down
Loading

0 comments on commit 359329e

Please sign in to comment.