From c9dda183abf0030d024859ca7fa9fe66d4e9a077 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Thu, 23 Feb 2023 01:37:54 +0900 Subject: [PATCH] feat: implement `Structural` types --- crates/erg_compiler/context/compare.rs | 36 +++++ .../context/initialize/const_func.rs | 26 ++++ .../erg_compiler/context/initialize/funcs.rs | 7 + crates/erg_compiler/context/initialize/mod.rs | 1 + crates/erg_compiler/context/inquire.rs | 6 +- crates/erg_compiler/context/instantiate.rs | 55 ++++++++ crates/erg_compiler/context/unify.rs | 11 ++ crates/erg_compiler/declare.rs | 26 ++-- crates/erg_compiler/lower.rs | 3 +- crates/erg_compiler/ty/mod.rs | 121 +++++++++++++---- crates/erg_compiler/ty/typaram.rs | 38 ++++++ crates/erg_compiler/ty/value.rs | 32 ++++- crates/erg_parser/ast.rs | 126 +++++++++++++++++- crates/erg_parser/desugar.rs | 6 + crates/erg_parser/parse.rs | 3 + crates/erg_parser/typespec.rs | 33 ++++- 16 files changed, 472 insertions(+), 58 deletions(-) diff --git a/crates/erg_compiler/context/compare.rs b/crates/erg_compiler/context/compare.rs index 8b10b173..3114c1fc 100644 --- a/crates/erg_compiler/context/compare.rs +++ b/crates/erg_compiler/context/compare.rs @@ -14,6 +14,7 @@ use crate::ty::{Predicate, RefinementType, SubrKind, SubrType, Type}; use Predicate as Pred; use erg_common::dict::Dict; +use erg_common::vis::Field; use erg_common::{assume_unreachable, log}; use TyParamOrdering::*; use Type::*; @@ -716,10 +717,45 @@ impl Context { } false } + (Structural(l), Structural(r)) => self.structural_supertype_of(l, r, allow_cast), + (Structural(l), r) => { + log!(err "{l}/{r}"); + let r_fields = self.fields(r); + log!(err "{r_fields}"); + for (l_field, l_ty) in self.fields(l) { + log!(err "{l_field} = {l_ty}"); + if let Some((r_field, r_ty)) = r_fields.get_key_value(&l_field) { + if r_field.vis != l_field.vis || !self.supertype_of(&l_ty, r_ty, allow_cast) + { + return false; + } + } else { + return false; + } + } + true + } (_l, _r) => false, } } + pub fn fields(&self, t: &Type) -> Dict { + match t { + Type::FreeVar(fv) if fv.is_linked() => self.fields(&fv.crack()), + Type::Record(fields) => fields.clone(), + other => { + let (_, ctx) = self + .get_nominal_type_ctx(other) + .unwrap_or_else(|| panic!("{other} is not found")); + ctx.locals + .iter() + .chain(ctx.decls.iter()) + .map(|(name, vi)| (Field::new(vi.vis, name.inspect().clone()), vi.t.clone())) + .collect() + } + } + } + pub(crate) fn poly_supertype_of( &self, typ: &Type, diff --git a/crates/erg_compiler/context/initialize/const_func.rs b/crates/erg_compiler/context/initialize/const_func.rs index 76fbff9f..78fd3337 100644 --- a/crates/erg_compiler/context/initialize/const_func.rs +++ b/crates/erg_compiler/context/initialize/const_func.rs @@ -210,6 +210,32 @@ pub fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult EvalValueResult { + let type_ = args.remove_left_or_key("Type").ok_or_else(|| { + ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!("{BASE_ERR} is not passed"), + line!() as usize, + ErrorKind::KeyError, + Location::Unknown, + ) + })?; + let Some(base) = type_.as_type() else { + let type_ = StyledString::new(format!("{type_}"), Some(ERR), None); + return Err(ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!( + "non-type object {type_} is passed to {BASE_WARN}", + ), + line!() as usize, + ErrorKind::TypeError, + Location::Unknown, + ).into()); + }; + let t = base.typ().clone().structuralize(); + Ok(ValueObj::gen_t(GenTypeObj::structural(t, base))) +} + pub fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult { let slf = ctx .convert_value_into_array(args.remove_left_or_key("Self").unwrap()) diff --git a/crates/erg_compiler/context/initialize/funcs.rs b/crates/erg_compiler/context/initialize/funcs.rs index d684b875..64085d71 100644 --- a/crates/erg_compiler/context/initialize/funcs.rs +++ b/crates/erg_compiler/context/initialize/funcs.rs @@ -411,6 +411,13 @@ impl Context { None, )); self.register_builtin_const(SUBSUME, vis, ValueObj::Subr(subsume)); + let structural = ConstSubr::Builtin(BuiltinConstSubr::new( + STRUCTURAL, + structural_func, + func1(Type, Type), + None, + )); + self.register_builtin_const(STRUCTURAL, vis, ValueObj::Subr(structural)); // decorators let inheritable_t = func1(ClassType, ClassType); let inheritable = ConstSubr::Builtin(BuiltinConstSubr::new( diff --git a/crates/erg_compiler/context/initialize/mod.rs b/crates/erg_compiler/context/initialize/mod.rs index f387c03d..25b9e2c6 100644 --- a/crates/erg_compiler/context/initialize/mod.rs +++ b/crates/erg_compiler/context/initialize/mod.rs @@ -300,6 +300,7 @@ const INHERIT: &str = "Inherit"; const INHERITABLE: &str = "Inheritable"; const DEL: &str = "Del"; const PATCH: &str = "Patch"; +const STRUCTURAL: &str = "Structural"; const OP_IN: &str = "__in__"; const OP_NOT_IN: &str = "__notin__"; diff --git a/crates/erg_compiler/context/inquire.rs b/crates/erg_compiler/context/inquire.rs index c4c955d0..43560553 100644 --- a/crates/erg_compiler/context/inquire.rs +++ b/crates/erg_compiler/context/inquire.rs @@ -628,18 +628,18 @@ impl Context { ); Ok(vi) } else { - let t = Type::Record(record.clone()); Err(TyCheckError::no_attr_error( self.cfg.input.clone(), line!() as usize, ident.loc(), namespace.into(), - &t, + t, ident.inspect(), - self.get_similar_attr(&t, ident.inspect()), + self.get_similar_attr(t, ident.inspect()), )) } } + Type::Structural(t) => self.get_attr_info_from_attributive(t, ident, namespace), other => { if let Some(v) = self.rec_get_const_obj(&other.local_name()) { match v { diff --git a/crates/erg_compiler/context/instantiate.rs b/crates/erg_compiler/context/instantiate.rs index 0e0ad2da..6fcd73a1 100644 --- a/crates/erg_compiler/context/instantiate.rs +++ b/crates/erg_compiler/context/instantiate.rs @@ -7,6 +7,7 @@ use erg_common::dict::Dict; use erg_common::log; use erg_common::set::Set; use erg_common::traits::{Locational, Stream}; +use erg_common::vis::Field; use erg_common::Str; use erg_common::{assume_unreachable, dict, enum_unwrap, set, try_map_mut}; @@ -589,6 +590,21 @@ impl Context { let t = self.instantiate_const_expr_as_type(&first.expr, None, tmp_tv_cache)?; Ok(ref_mut(t, None)) } + "Structural" => { + let mut args = simple.args.pos_args(); + let Some(first) = args.next() else { + return Err(TyCheckErrors::from(TyCheckError::args_missing_error( + self.cfg.input.clone(), + line!() as usize, + simple.args.loc(), + "Structural", + self.caused_by(), + vec![Str::from("Type")], + ))); + }; + let t = self.instantiate_const_expr_as_type(&first.expr, None, tmp_tv_cache)?; + Ok(t.structuralize()) + } "Self" => self.rec_get_self_t().ok_or_else(|| { TyCheckErrors::from(TyCheckError::unreachable( self.cfg.input.clone(), @@ -763,6 +779,19 @@ impl Context { } Ok(TyParam::Tuple(tp_tuple)) } + ast::ConstExpr::Record(rec) => { + let mut tp_rec = dict! {}; + for attr in rec.attrs.iter() { + let field = Field::new(attr.ident.vis(), attr.ident.inspect().clone()); + let val = self.instantiate_const_expr( + attr.body.block.get(0).unwrap(), + None, + tmp_tv_cache, + )?; + tp_rec.insert(field, val); + } + Ok(TyParam::Record(tp_rec)) + } ast::ConstExpr::BinOp(bin) => { let Some(op) = token_kind_to_op_kind(bin.op.kind) else { return type_feature_error!( @@ -819,6 +848,22 @@ impl Context { .unwrap_or(Type::Never); Ok(tp_enum(t, set)) } + TyParam::Tuple(ts) => { + let mut tps = vec![]; + for tp in ts { + let t = self.instantiate_tp_as_type(tp, loc)?; + tps.push(t); + } + Ok(tuple_t(tps)) + } + TyParam::Record(rec) => { + let mut rec_t = dict! {}; + for (field, tp) in rec { + let t = self.instantiate_tp_as_type(tp, loc)?; + rec_t.insert(field, t); + } + Ok(Type::Record(rec_t)) + } other => { type_feature_error!(self, loc.loc(), &format!("instantiate `{other}` as type")) } @@ -1245,6 +1290,16 @@ impl Context { .collect::>()?; Ok(TyParam::Tuple(tup)) } + TyParam::Record(rec) => { + let rec = rec + .into_iter() + .map(|(k, v)| { + let v = self.instantiate_tp(v, tmp_tv_cache, loc)?; + Ok((k, v)) + }) + .collect::>()?; + Ok(TyParam::Record(rec)) + } TyParam::UnaryOp { op, val } => { let res = self.instantiate_tp(*val, tmp_tv_cache, loc)?; Ok(TyParam::unary(op, res)) diff --git a/crates/erg_compiler/context/unify.rs b/crates/erg_compiler/context/unify.rs index fe332aae..ad7cd921 100644 --- a/crates/erg_compiler/context/unify.rs +++ b/crates/erg_compiler/context/unify.rs @@ -845,6 +845,17 @@ impl Context { Ok(()) } } + (sub, Type::Structural(sup)) => { + let sub_fields = self.fields(sub); + for (sup_field, sup_ty) in self.fields(sup) { + if let Some((_, sub_ty)) = sub_fields.get_key_value(&sup_field) { + self.sub_unify(sub_ty, &sup_ty, loc, param_name)?; + } else { + unreachable!() + } + } + Ok(()) + } ( _, Type::Poly { diff --git a/crates/erg_compiler/declare.rs b/crates/erg_compiler/declare.rs index d7430081..ab18b44f 100644 --- a/crates/erg_compiler/declare.rs +++ b/crates/erg_compiler/declare.rs @@ -268,23 +268,17 @@ impl ASTLowerer { } fn fake_lower_record(&self, rec: ast::Record) -> LowerResult { - match rec { - ast::Record::Normal(rec) => { - let mut elems = Vec::new(); - for elem in rec.attrs.into_iter() { - let elem = self.fake_lower_def(elem)?; - elems.push(elem); - } - let attrs = hir::RecordAttrs::new(elems); - Ok(hir::Record::new(rec.l_brace, rec.r_brace, attrs)) - } - other => Err(LowerErrors::from(LowerError::declare_error( - self.cfg().input.clone(), - line!() as usize, - other.loc(), - self.module.context.caused_by(), - ))), + let rec = match rec { + ast::Record::Normal(rec) => rec, + ast::Record::Mixed(_mixed) => unreachable!(), + }; + let mut elems = Vec::new(); + for elem in rec.attrs.into_iter() { + let elem = self.fake_lower_def(elem)?; + elems.push(elem); } + let attrs = hir::RecordAttrs::new(elems); + Ok(hir::Record::new(rec.l_brace, rec.r_brace, attrs)) } fn fake_lower_set(&self, set: ast::Set) -> LowerResult { diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index d8da44f5..704c3042 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -1975,11 +1975,12 @@ impl ASTLowerer { match expr { acc @ hir::Expr::Accessor(_) => Some(acc), hir::Expr::Call(mut call) => match call.obj.show_acc().as_ref().map(|s| &s[..]) { - Some("Class") => call.args.remove_left_or_key("Requirement"), + Some("Class" | "Trait") => call.args.remove_left_or_key("Requirement"), Some("Inherit") => call.args.remove_left_or_key("Super"), Some("Inheritable") => { Self::get_require_or_sup_or_base(call.args.remove_left_or_key("Class").unwrap()) } + Some("Structural") => call.args.remove_left_or_key("Type"), Some("Patch") => call.args.remove_left_or_key("Base"), _ => todo!(), }, diff --git a/crates/erg_compiler/ty/mod.rs b/crates/erg_compiler/ty/mod.rs index c2ba5d37..f3c1b141 100644 --- a/crates/erg_compiler/ty/mod.rs +++ b/crates/erg_compiler/ty/mod.rs @@ -13,7 +13,7 @@ pub mod typaram; pub mod value; use std::fmt; -use std::ops::{Range, RangeInclusive}; +use std::ops::{BitAnd, BitOr, Deref, Not, Range, RangeInclusive}; use std::path::PathBuf; use constructors::dict_t; @@ -705,6 +705,7 @@ pub enum Type { attr_name: Str, args: Vec, }, // e.g. Ts.__getitem__(N) + Structural(Box), FreeVar(FreeTyVar), // a reference to the type of other expression, see docs/compiler/inference.md Failure, // indicates a failure of type inference and behaves as `Never`. /// used to represent `TyParam` is not initialized (see `erg_compiler::context::instantiate_tp`) @@ -749,14 +750,18 @@ impl PartialEq for Type { (Self::Subr(l), Self::Subr(r)) => l == r, ( Self::Callable { - param_ts: _lps, - return_t: _lr, + param_ts: lps, + return_t: lr, }, Self::Callable { - param_ts: _rps, - return_t: _rr, + param_ts: rps, + return_t: rr, }, - ) => todo!(), + ) => { + lps.len() != rps.len() + && lps.iter().zip(rps.iter()).all(|(l, r)| l == r) + && (lr == rr) + } (Self::Record(lhs), Self::Record(rhs)) => { for (l_field, l_t) in lhs.iter() { if let Some(r_t) = rhs.get(l_field) { @@ -804,6 +809,7 @@ impl PartialEq for Type { args: ra, }, ) => lhs == r && attr_name == rn && args == ra, + (Self::Structural(l), Self::Structural(r)) => l == r, (Self::FreeVar(fv), other) if fv.is_linked() => &*fv.crack() == other, (_self, Self::FreeVar(fv)) if fv.is_linked() => _self == &*fv.crack(), (Self::FreeVar(l), Self::FreeVar(r)) => l == r, @@ -929,6 +935,11 @@ impl LimitedDisplay for Type { } write!(f, ")") } + Self::Structural(ty) => { + write!(f, "Structural(")?; + ty.limited_fmt(f, limit - 1)?; + write!(f, ")") + } _ => write!(f, "{}", self.qual_name()), } } @@ -1000,6 +1011,27 @@ impl From> for Type { } } +impl BitAnd for Type { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self::And(Box::new(self), Box::new(rhs)) + } +} + +impl BitOr for Type { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + Self::Or(Box::new(self), Box::new(rhs)) + } +} + +impl Not for Type { + type Output = Self; + fn not(self) -> Self::Output { + Self::Not(Box::new(self)) + } +} + fn get_t_from_tp(tp: &TyParam) -> Option { match tp { TyParam::FreeVar(fv) if fv.is_linked() => get_t_from_tp(&fv.crack()), @@ -1129,6 +1161,7 @@ impl HasLevel for Type { Some(min) } } + Self::Structural(ty) => ty.level(), Self::Quantified(quant) => quant.level(), _ => None, } @@ -1189,6 +1222,13 @@ impl HasLevel for Type { pred.set_level(level); } } + Self::ProjCall { lhs, args, .. } => { + lhs.set_level(level); + for arg in args.iter() { + arg.set_level(level); + } + } + Self::Structural(ty) => ty.set_level(level), _ => {} } } @@ -1284,6 +1324,7 @@ impl StructuralEq for Type { .zip(args2.iter()) .all(|(a, b)| a.structural_eq(b)) } + (Self::Structural(l), Self::Structural(r)) => l.structural_eq(r), // TODO: commutative (Self::And(l, r), Self::And(l2, r2)) => l.structural_eq(l2) && r.structural_eq(r2), (Self::Or(l, r), Self::Or(l2, r2)) => l.structural_eq(l2) && r.structural_eq(r2), @@ -1334,6 +1375,10 @@ impl Type { } } + pub fn structuralize(self) -> Self { + Self::Structural(Box::new(self)) + } + pub fn is_simple_class(&self) -> bool { match self { Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_simple_class(), @@ -1507,6 +1552,7 @@ impl Type { Self::Subr(subr) => subr.contains_tvar(name), // TODO: preds Self::Refinement(refine) => refine.t.contains_tvar(name), + Self::Structural(ty) => ty.contains_tvar(name), _ => false, } } @@ -1614,6 +1660,7 @@ impl Type { }, Self::Proj { .. } => Str::ever("Proj"), Self::ProjCall { .. } => Str::ever("ProjCall"), + Self::Structural(_) => Str::ever("Structural"), Self::Failure => Str::ever("Failure"), Self::Uninited => Str::ever("Uninited"), } @@ -1809,6 +1856,7 @@ impl Type { Self::ProjCall { lhs, args, .. } => lhs .qvars() .concat(args.iter().fold(set! {}, |acc, tp| acc.concat(tp.qvars()))), + Self::Structural(ty) => ty.qvars(), _ => set! {}, } } @@ -1866,6 +1914,7 @@ impl Type { Self::ProjCall { lhs, args, .. } => { lhs.has_qvar() || args.iter().any(|tp| tp.has_qvar()) } + Self::Structural(ty) => ty.has_qvar(), _ => false, } } @@ -1917,6 +1966,10 @@ impl Type { Self::Quantified(quant) => quant.has_unbound_var(), Self::Poly { params, .. } => params.iter().any(|p| p.has_unbound_var()), Self::Proj { lhs, .. } => lhs.has_no_unbound_var(), + Self::ProjCall { lhs, args, .. } => { + lhs.has_no_unbound_var() && args.iter().all(|t| t.has_no_unbound_var()) + } + Self::Structural(ty) => ty.has_unbound_var(), _ => false, } } @@ -1941,6 +1994,9 @@ impl Type { ), Self::Callable { param_ts, .. } => Some(param_ts.len() + 1), Self::Poly { params, .. } => Some(params.len()), + Self::Proj { lhs, .. } => lhs.typarams_len(), + Self::ProjCall { args, .. } => Some(1 + args.len()), + Self::Structural(ty) => ty.typarams_len(), _ => None, } } @@ -1977,6 +2033,11 @@ impl Type { Self::Quantified(quant) => quant.typarams(), Self::Callable { param_ts: _, .. } => todo!(), Self::Poly { params, .. } => params.clone(), + Self::Proj { lhs, .. } => lhs.typarams(), + Self::ProjCall { lhs, args, .. } => { + [vec![*lhs.clone()], args.deref().to_vec()].concat() + } + Self::Structural(ty) => ty.typarams(), _ => vec![], } } @@ -2135,17 +2196,11 @@ impl Type { before: Box::new(before.derefine()), after: after.as_ref().map(|t| Box::new(t.derefine())), }, - Self::And(l, r) => { - let l = l.derefine(); - let r = r.derefine(); - Self::And(Box::new(l), Box::new(r)) - } - Self::Or(l, r) => { - let l = l.derefine(); - let r = r.derefine(); - Self::Or(Box::new(l), Box::new(r)) - } - Self::Not(ty) => Self::Not(Box::new(ty.derefine())), + Self::And(l, r) => l.derefine() & r.derefine(), + Self::Or(l, r) => l.derefine() | r.derefine(), + Self::Not(ty) => !ty.derefine(), + Self::Proj { lhs, rhs } => lhs.derefine().proj(rhs.clone()), + Self::Structural(ty) => ty.derefine().structuralize(), other => other.clone(), } } @@ -2218,17 +2273,9 @@ impl Type { before: Box::new(before._replace(target, to)), after: after.map(|t| Box::new(t._replace(target, to))), }, - Self::And(l, r) => { - let l = l._replace(target, to); - let r = r._replace(target, to); - Self::And(Box::new(l), Box::new(r)) - } - Self::Or(l, r) => { - let l = l._replace(target, to); - let r = r._replace(target, to); - Self::Or(Box::new(l), Box::new(r)) - } - Self::Not(ty) => Self::Not(Box::new(ty._replace(target, to))), + Self::And(l, r) => l._replace(target, to) & r._replace(target, to), + Self::Or(l, r) => l._replace(target, to) | r._replace(target, to), + Self::Not(ty) => !ty._replace(target, to), Self::Proj { lhs, rhs } => lhs._replace(target, to).proj(rhs), Self::ProjCall { lhs, @@ -2238,6 +2285,7 @@ impl Type { let args = args.into_iter().map(|tp| tp.replace(target, to)).collect(); lhs.replace(target, to).proj_call(attr_name, args) } + Self::Structural(ty) => ty._replace(target, to).structuralize(), other => other, } } @@ -2264,6 +2312,23 @@ impl Type { Self::Subr(subr) } Self::Proj { lhs, rhs } => lhs.normalize().proj(rhs), + Self::ProjCall { + lhs, + attr_name, + args, + } => { + let args = args.into_iter().map(|tp| tp.normalize()).collect(); + lhs.normalize().proj_call(attr_name, args) + } + Self::Ref(t) => Self::Ref(Box::new(t.normalize())), + Self::RefMut { before, after } => Self::RefMut { + before: Box::new(before.normalize()), + after: after.map(|t| Box::new(t.normalize())), + }, + Self::And(l, r) => l.normalize() & r.normalize(), + Self::Or(l, r) => l.normalize() | r.normalize(), + Self::Not(ty) => !ty.normalize(), + Self::Structural(ty) => ty.normalize().structuralize(), other => other, } } diff --git a/crates/erg_compiler/ty/typaram.rs b/crates/erg_compiler/ty/typaram.rs index afbf2041..9e0fd769 100644 --- a/crates/erg_compiler/ty/typaram.rs +++ b/crates/erg_compiler/ty/typaram.rs @@ -7,6 +7,7 @@ use erg_common::dict::Dict; use erg_common::set; use erg_common::set::Set; use erg_common::traits::{LimitedDisplay, StructuralEq}; +use erg_common::vis::Field; use erg_common::Str; use erg_common::{dict, log}; @@ -146,6 +147,7 @@ pub enum TyParam { Tuple(Vec), Set(Set), Dict(Dict), + Record(Dict), Mono(Str), Proj { obj: Box, @@ -177,6 +179,7 @@ impl PartialEq for TyParam { (Self::Array(l), Self::Array(r)) => l == r, (Self::Tuple(l), Self::Tuple(r)) => l == r, (Self::Dict(l), Self::Dict(r)) => l == r, + (Self::Record(l), Self::Record(r)) => l == r, (Self::Set(l), Self::Set(r)) => l == r, (Self::Mono(l), Self::Mono(r)) => l == r, ( @@ -296,6 +299,7 @@ impl LimitedDisplay for TyParam { write!(f, "}}") } Self::Dict(dict) => write!(f, "{dict}"), + Self::Record(rec) => write!(f, "{rec}"), Self::Tuple(tuple) => { write!(f, "(")?; for (i, t) in tuple.iter().enumerate() { @@ -453,6 +457,13 @@ impl TryFrom for ValueObj { } Ok(ValueObj::Dict(vals)) } + TyParam::Record(rec) => { + let mut vals = dict! {}; + for (k, v) in rec { + vals.insert(k, ValueObj::try_from(v)?); + } + Ok(ValueObj::Record(vals)) + } TyParam::FreeVar(fv) if fv.is_linked() => ValueObj::try_from(fv.crack().clone()), TyParam::Type(t) => Ok(ValueObj::builtin_t(*t)), TyParam::Value(v) => Ok(v), @@ -530,6 +541,10 @@ impl HasLevel for TyParam { .min(v.level().unwrap_or(GENERIC_LEVEL)) }) .min(), + Self::Record(rec) => rec + .iter() + .map(|(_, v)| v.level().unwrap_or(GENERIC_LEVEL)) + .min(), Self::Set(tps) => tps.iter().filter_map(|tp| tp.level()).min(), Self::Proj { obj, .. } => obj.level(), Self::App { args, .. } => args.iter().filter_map(|tp| tp.level()).min(), @@ -549,6 +564,11 @@ impl HasLevel for TyParam { v.set_level(level); } } + Self::Record(rec) => { + for (_, v) in rec.iter() { + v.set_level(level); + } + } Self::Array(tps) => { for tp in tps { tp.set_level(level); @@ -600,6 +620,18 @@ impl StructuralEq for TyParam { } true } + (Self::Record(l), Self::Record(r)) => { + for (l_field, l_val) in l.iter() { + if let Some((r_field, r_val)) = r.get_key_value(l_field) { + if l_field.vis != r_field.vis || !l_val.structural_eq(r_val) { + return false; + } + } else { + return false; + } + } + true + } (Self::Set(l), Self::Set(r)) => { for l_val in l.iter() { if r.get_by(l_val, |l, r| l.structural_eq(r)).is_none() { @@ -813,6 +845,9 @@ impl TyParam { Self::Dict(ts) => ts.iter().fold(set! {}, |acc, (k, v)| { acc.concat(k.qvars().concat(v.qvars())) }), + Self::Record(rec) => rec + .iter() + .fold(set! {}, |acc, (_, v)| acc.concat(v.qvars())), Self::UnaryOp { val, .. } => val.qvars(), Self::BinOp { lhs, rhs, .. } => lhs.qvars().concat(rhs.qvars()), Self::App { args, .. } => args.iter().fold(set! {}, |acc, p| acc.concat(p.qvars())), @@ -836,6 +871,7 @@ impl TyParam { Self::Array(ts) | Self::Tuple(ts) => ts.iter().any(|t| t.has_qvar()), Self::Set(ts) => ts.iter().any(|t| t.has_qvar()), Self::Dict(ts) => ts.iter().any(|(k, v)| k.has_qvar() || v.has_qvar()), + Self::Record(rec) => rec.iter().any(|(_, tp)| tp.has_qvar()), Self::UnaryOp { val, .. } => val.has_qvar(), Self::BinOp { lhs, rhs, .. } => lhs.has_qvar() || rhs.has_qvar(), Self::App { args, .. } => args.iter().any(|p| p.has_qvar()), @@ -858,6 +894,7 @@ impl TyParam { Self::Dict(ts) => ts .iter() .any(|(k, v)| k.contains_var(name) || v.contains_var(name)), + Self::Record(rec) => rec.iter().any(|(_, tp)| tp.contains_var(name)), Self::UnaryOp { val, .. } => val.contains_var(name), Self::BinOp { lhs, rhs, .. } => lhs.contains_var(name) || rhs.contains_var(name), Self::App { args, .. } => args.iter().any(|p| p.contains_var(name)), @@ -885,6 +922,7 @@ impl TyParam { Self::Dict(kv) => kv .iter() .any(|(k, v)| k.has_unbound_var() || v.has_unbound_var()), + Self::Record(rec) => rec.iter().any(|(_, v)| v.has_unbound_var()), Self::UnaryOp { val, .. } => val.has_unbound_var(), Self::BinOp { lhs, rhs, .. } => lhs.has_unbound_var() || rhs.has_unbound_var(), Self::App { args, .. } => args.iter().any(|p| p.has_unbound_var()), diff --git a/crates/erg_compiler/ty/value.rs b/crates/erg_compiler/ty/value.rs index c9050c09..c6ad8e8e 100644 --- a/crates/erg_compiler/ty/value.rs +++ b/crates/erg_compiler/ty/value.rs @@ -164,6 +164,21 @@ impl IntersectionTypeObj { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct StructuralTypeObj { + pub t: Type, + pub base: Box, +} + +impl StructuralTypeObj { + pub fn new(t: Type, base: TypeObj) -> Self { + Self { + t, + base: Box::new(base), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct PatchObj { pub t: Type, @@ -187,7 +202,7 @@ pub enum GenTypeObj { Subclass(InheritedTypeObj), Trait(TraitTypeObj), Subtrait(SubsumedTypeObj), - StructuralTrait(TraitTypeObj), + Structural(StructuralTypeObj), Union(UnionTypeObj), Intersection(IntersectionTypeObj), Patch(PatchObj), @@ -238,13 +253,17 @@ impl GenTypeObj { GenTypeObj::Intersection(IntersectionTypeObj::new(t, lhs, rhs)) } + pub fn structural(t: Type, type_: TypeObj) -> Self { + GenTypeObj::Structural(StructuralTypeObj::new(t, type_)) + } + pub fn base_or_sup(&self) -> Option<&TypeObj> { match self { Self::Class(class) => class.base.as_ref().map(AsRef::as_ref), Self::Subclass(subclass) => Some(subclass.sup.as_ref()), Self::Trait(trait_) => Some(trait_.requires.as_ref()), Self::Subtrait(subtrait) => Some(subtrait.sup.as_ref()), - Self::StructuralTrait(trait_) => Some(trait_.requires.as_ref()), + Self::Structural(type_) => Some(type_.base.as_ref()), Self::Patch(patch) => Some(patch.base.as_ref()), _ => None, } @@ -281,8 +300,9 @@ impl GenTypeObj { pub fn meta_type(&self) -> Type { match self { Self::Class(_) | Self::Subclass(_) => Type::ClassType, - Self::Trait(_) | Self::Subtrait(_) | Self::StructuralTrait(_) => Type::TraitType, + Self::Trait(_) | Self::Subtrait(_) => Type::TraitType, Self::Patch(_) => Type::Patch, + Self::Structural(_) => Type::Type, _ => Type::Type, } } @@ -293,7 +313,7 @@ impl GenTypeObj { Self::Subclass(subclass) => &subclass.t, Self::Trait(trait_) => &trait_.t, Self::Subtrait(subtrait) => &subtrait.t, - Self::StructuralTrait(trait_) => &trait_.t, + Self::Structural(struct_) => &struct_.t, Self::Union(union_) => &union_.t, Self::Intersection(intersection) => &intersection.t, Self::Patch(patch) => &patch.t, @@ -306,7 +326,7 @@ impl GenTypeObj { Self::Subclass(subclass) => &mut subclass.t, Self::Trait(trait_) => &mut trait_.t, Self::Subtrait(subtrait) => &mut subtrait.t, - Self::StructuralTrait(trait_) => &mut trait_.t, + Self::Structural(struct_) => &mut struct_.t, Self::Union(union_) => &mut union_.t, Self::Intersection(intersection) => &mut intersection.t, Self::Patch(patch) => &mut patch.t, @@ -319,7 +339,7 @@ impl GenTypeObj { Self::Subclass(subclass) => subclass.t, Self::Trait(trait_) => trait_.t, Self::Subtrait(subtrait) => subtrait.t, - Self::StructuralTrait(trait_) => trait_.t, + Self::Structural(struct_) => struct_.t, Self::Union(union_) => union_.t, Self::Intersection(intersection) => intersection.t, Self::Patch(patch) => patch.t, diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index 8d6e06dd..89676fa2 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -884,6 +884,14 @@ impl Record { }) } + pub fn new_normal(l_brace: Token, r_brace: Token, attrs: RecordAttrs) -> Self { + Self::Normal(NormalRecord { + l_brace, + r_brace, + attrs, + }) + } + pub fn empty(l_brace: Token, r_brace: Token) -> Self { Self::Normal(NormalRecord { l_brace, @@ -1528,6 +1536,118 @@ impl ConstTuple { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstBlock(Vec); + +impl NestedDisplay for ConstBlock { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + fmt_lines(self.0.iter(), f, _level) + } +} + +impl_display_from_nested!(ConstBlock); + +impl Locational for ConstBlock { + fn loc(&self) -> Location { + if self.0.is_empty() { + Location::Unknown + } else { + Location::concat(self.0.first().unwrap(), self.0.last().unwrap()) + } + } +} + +impl_stream!(ConstBlock, ConstExpr); + +impl ConstBlock { + pub fn downcast(self) -> Block { + Block::new(self.0.into_iter().map(|e| e.downcast()).collect()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstDefBody { + pub op: Token, + pub block: ConstBlock, + pub id: DefId, +} + +impl_locational!(ConstDefBody, lossy op, block); + +impl ConstDefBody { + pub const fn new(op: Token, block: ConstBlock, id: DefId) -> Self { + Self { op, block, id } + } + + pub fn downcast(self) -> DefBody { + DefBody::new(self.op, self.block.downcast(), self.id) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstDef { + pub ident: ConstIdentifier, + pub body: ConstDefBody, +} + +impl NestedDisplay for ConstDef { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{} = {}", self.ident, self.body.block) + } +} + +impl_display_from_nested!(ConstDef); +impl_locational!(ConstDef, ident, body); + +impl ConstDef { + pub const fn new(ident: ConstIdentifier, body: ConstDefBody) -> Self { + Self { ident, body } + } + + pub fn downcast(self) -> Def { + Def::new(Signature::new_var(self.ident), self.body.downcast()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstRecord { + pub l_brace: Token, + pub r_brace: Token, + pub attrs: Vec, +} + +impl NestedDisplay for ConstRecord { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, _level: usize) -> std::fmt::Result { + write!(f, "{{{}}}", fmt_vec_split_with(&self.attrs, "; ")) + } +} + +impl Locational for ConstRecord { + fn loc(&self) -> Location { + Location::concat(&self.l_brace, &self.r_brace) + } +} + +impl_display_from_nested!(ConstRecord); + +impl ConstRecord { + pub const fn new(l_brace: Token, r_brace: Token, attrs: Vec) -> Self { + Self { + l_brace, + r_brace, + attrs, + } + } + + pub fn downcast(self) -> Record { + Record::Normal(NormalRecord::new( + self.l_brace, + self.r_brace, + self.attrs.into_iter().map(|d| d.downcast()).collect(), + )) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ConstBinOp { pub op: Token, @@ -1633,13 +1753,15 @@ pub enum ConstExpr { Set(ConstSet), Dict(ConstDict), Tuple(ConstTuple), + Record(ConstRecord), + Def(ConstDef), BinOp(ConstBinOp), UnaryOp(ConstUnaryOp), } -impl_nested_display_for_chunk_enum!(ConstExpr; Lit, Accessor, App, Array, Set, Dict, Tuple, BinOp, UnaryOp, Erased, Set); +impl_nested_display_for_chunk_enum!(ConstExpr; Lit, Accessor, App, Array, Set, Dict, Tuple, Record, BinOp, UnaryOp, Def, Erased, Set); impl_display_from_nested!(ConstExpr); -impl_locational_for_enum!(ConstExpr; Lit, Accessor, App, Array, Set Dict, Tuple, BinOp, UnaryOp, Erased, Set); +impl_locational_for_enum!(ConstExpr; Lit, Accessor, App, Array, Set, Dict, Tuple, Record, BinOp, UnaryOp, Def, Erased, Set); impl ConstExpr { pub fn need_to_be_closed(&self) -> bool { diff --git a/crates/erg_parser/desugar.rs b/crates/erg_parser/desugar.rs index 03ee36e8..e5daec67 100644 --- a/crates/erg_parser/desugar.rs +++ b/crates/erg_parser/desugar.rs @@ -51,6 +51,12 @@ impl Desugarer { module } + pub fn desugar_simple_expr(expr: Expr) -> Expr { + let expr = Self::rec_desugar_shortened_record(expr); + let expr = Self::rec_desugar_lambda_pattern(&mut Desugarer {}, expr); + Self::rec_desugar_acc(expr) + } + fn desugar_all_chunks(module: Module, desugar: impl Fn(Expr) -> Expr) -> Module { module.into_iter().map(desugar).collect() } diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index 14681c9e..407ff97d 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -17,6 +17,7 @@ use erg_common::{ }; use crate::ast::*; +use crate::desugar::Desugarer; use crate::error::{ParseError, ParseErrors, ParseResult, ParserRunnerError, ParserRunnerErrors}; use crate::lex::Lexer; use crate::token::{Token, TokenCategory, TokenKind, TokenStream}; @@ -1169,6 +1170,7 @@ impl Parser { let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let t_spec_as_expr = self .try_reduce_expr(false, false, false, false) + .map(Desugarer::desugar_simple_expr) .map_err(|_| self.stack_dec(fn_name!()))?; let t_spec = Self::expr_to_type_spec(t_spec_as_expr.clone()) .map_err(|e| self.errs.push(e))?; @@ -1440,6 +1442,7 @@ impl Parser { let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let t_spec_as_expr = self .try_reduce_expr(false, in_type_args, in_brace, false) + .map(Desugarer::desugar_simple_expr) .map_err(|_| self.stack_dec(fn_name!()))?; let t_spec = Self::expr_to_type_spec(t_spec_as_expr.clone()) .map_err(|e| self.errs.push(e))?; diff --git a/crates/erg_parser/typespec.rs b/crates/erg_parser/typespec.rs index 5f1eea41..4ed77a9d 100644 --- a/crates/erg_parser/typespec.rs +++ b/crates/erg_parser/typespec.rs @@ -109,10 +109,29 @@ impl Parser { let args = ConstArgs::pos_only(const_pos_args, paren); Ok(ConstExpr::App(ConstApp::new(acc, args))) } - // TODO: App, Record, + Expr::Def(def) => Self::validate_const_def(def).map(ConstExpr::Def), + Expr::Record(rec) => { + let rec = match rec { + Record::Normal(rec) => rec, + Record::Mixed(_) => unreachable!(), + }; + let mut const_fields = vec![]; + for attr in rec.attrs.into_iter() { + const_fields.push(Self::validate_const_def(attr)?); + } + Ok(ConstExpr::Record(ConstRecord::new( + rec.l_brace, + rec.r_brace, + const_fields, + ))) + } + // TODO: Lambda, ... other => Err(ParseError::syntax_error( line!() as usize, - other.loc(), + { + erg_common::log!(err "{other}"); + other.loc() + }, switch_lang!( "japanese" => "この式はコンパイル時計算できないため、型引数には使用できません", "simplified_chinese" => "此表达式在编译时不可计算,因此不能用作类型参数", @@ -124,6 +143,16 @@ impl Parser { } } + fn validate_const_def(def: Def) -> Result { + let mut block = vec![]; + for expr in def.body.block.into_iter() { + let const_expr = Self::validate_const_expr(expr)?; + block.push(const_expr); + } + let body = ConstDefBody::new(def.body.op, ConstBlock::new(block), def.body.id); + Ok(ConstDef::new(def.sig.ident().unwrap().clone(), body)) + } + fn ident_to_type_spec(ident: Identifier) -> SimpleTypeSpec { SimpleTypeSpec::new(ident, ConstArgs::empty()) }