mirror of
https://github.com/erg-lang/erg.git
synced 2025-07-24 05:26:24 +00:00
feat: implement Structural
types
This commit is contained in:
parent
8903e84011
commit
c9dda183ab
16 changed files with 472 additions and 58 deletions
|
@ -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<Field, Type> {
|
||||
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,
|
||||
|
|
|
@ -210,6 +210,32 @@ pub fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<Value
|
|||
)))
|
||||
}
|
||||
|
||||
pub fn structural_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||
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<ValueObj> {
|
||||
let slf = ctx
|
||||
.convert_value_into_array(args.remove_left_or_key("Self").unwrap())
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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__";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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::<TyCheckResult<_>>()?;
|
||||
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::<TyCheckResult<_>>()?;
|
||||
Ok(TyParam::Record(rec))
|
||||
}
|
||||
TyParam::UnaryOp { op, val } => {
|
||||
let res = self.instantiate_tp(*val, tmp_tv_cache, loc)?;
|
||||
Ok(TyParam::unary(op, res))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -268,8 +268,10 @@ impl ASTLowerer {
|
|||
}
|
||||
|
||||
fn fake_lower_record(&self, rec: ast::Record) -> LowerResult<hir::Record> {
|
||||
match rec {
|
||||
ast::Record::Normal(rec) => {
|
||||
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)?;
|
||||
|
@ -278,14 +280,6 @@ impl ASTLowerer {
|
|||
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(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn fake_lower_set(&self, set: ast::Set) -> LowerResult<hir::Set> {
|
||||
match set {
|
||||
|
|
|
@ -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!(),
|
||||
},
|
||||
|
|
|
@ -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<TyParam>,
|
||||
}, // e.g. Ts.__getitem__(N)
|
||||
Structural(Box<Type>),
|
||||
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<Dict<Type, Type>> 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<Type> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<TyParam>),
|
||||
Set(Set<TyParam>),
|
||||
Dict(Dict<TyParam, TyParam>),
|
||||
Record(Dict<Field, TyParam>),
|
||||
Mono(Str),
|
||||
Proj {
|
||||
obj: Box<TyParam>,
|
||||
|
@ -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<TyParam> 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()),
|
||||
|
|
|
@ -164,6 +164,21 @@ impl IntersectionTypeObj {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct StructuralTypeObj {
|
||||
pub t: Type,
|
||||
pub base: Box<TypeObj>,
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -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<ConstExpr>);
|
||||
|
||||
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<ConstDef>,
|
||||
}
|
||||
|
||||
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<ConstDef>) -> 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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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))?;
|
||||
|
|
|
@ -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<ConstDef, ParseError> {
|
||||
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())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue