Merge pull request #447 from erg-lang/shape

Enhance compile-time verification capabilities
This commit is contained in:
Shunsuke Shibayama 2023-08-10 08:11:40 +09:00 committed by GitHub
commit 3c88aee6be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1705 additions and 688 deletions

View file

@ -1438,6 +1438,7 @@ impl PyCodeGenerator {
_ => {
CompileError::feature_error(
self.cfg.input.clone(),
line!() as usize,
unary.op.loc(),
&unary.op.inspect().clone(),
String::from(unary.op.content),
@ -1550,6 +1551,7 @@ impl PyCodeGenerator {
_ => {
CompileError::feature_error(
self.cfg.input.clone(),
line!() as usize,
binop.loc(),
&binop.inspect().clone(),
String::from(binop.content),
@ -1624,6 +1626,7 @@ impl PyCodeGenerator {
_ => {
CompileError::feature_error(
self.cfg.input.clone(),
line!() as usize,
binop.loc(),
&binop.inspect().clone(),
String::from(binop.content),

View file

@ -30,6 +30,7 @@ pub enum Credibility {
use Credibility::*;
use super::eval::Substituter;
use super::ContextKind;
impl Context {
@ -153,8 +154,8 @@ impl Context {
}
match (lhs, rhs) {
(Obj, _) | (_, Never | Failure) => (Absolutely, true),
(_, Obj) if lhs.is_simple_class() => (Absolutely, false),
(Never | Failure, _) if rhs.is_simple_class() => (Absolutely, false),
(_, Obj) if lhs.is_mono_value_class() => (Absolutely, false),
(Never | Failure, _) if rhs.is_mono_value_class() => (Absolutely, false),
(Complex | Float | Ratio | Int | Nat | Bool, Bool)
| (Complex | Float | Ratio | Int | Nat, Nat)
| (Complex | Float | Ratio | Int, Int)
@ -202,7 +203,9 @@ impl Context {
_ => (Maybe, false),
},
(Mono(n), Subr(_) | Quantified(_)) if &n[..] == "GenericCallable" => (Absolutely, true),
(lhs, rhs) if lhs.is_simple_class() && rhs.is_simple_class() => (Absolutely, false),
(lhs, rhs) if lhs.is_mono_value_class() && rhs.is_mono_value_class() => {
(Absolutely, false)
}
_ => (Maybe, false),
}
}
@ -252,40 +255,63 @@ impl Context {
None
}
fn classes_supertype_of(&self, lhs: &Type, rhs: &Type) -> (Credibility, bool) {
if !self.is_class(lhs) || !self.is_class(rhs) {
return (Maybe, false);
}
fn _nominal_subtype_of<'c>(
&'c self,
lhs: &Type,
rhs: &Type,
get_types: impl FnOnce(&'c Context) -> &'c [Type],
) -> (Credibility, bool) {
if let Some((typ, ty_ctx)) = self.get_nominal_type_ctx(rhs) {
if typ.has_qvar() {
if let Err(err) = self.substitute_typarams(typ, rhs) {
Self::undo_substitute_typarams(typ);
if DEBUG_MODE {
panic!("{typ} / {rhs}: err: {err}");
let substitute = typ.has_qvar();
let overwrite = typ.has_undoable_linked_var();
let _substituter = if overwrite {
match Substituter::overwrite_typarams(self, typ, rhs) {
Ok(subs) => Some(subs),
Err(err) => {
if DEBUG_MODE {
panic!("{typ} / {rhs}: err: {err}");
}
None
}
}
}
for rhs_sup in ty_ctx.super_classes.iter() {
} else if substitute {
match Substituter::substitute_typarams(self, typ, rhs) {
Ok(subs) => Some(subs),
Err(err) => {
if DEBUG_MODE {
panic!("{typ} / {rhs}: err: {err}");
}
None
}
}
} else {
None
};
for rhs_sup in get_types(ty_ctx) {
// Not `supertype_of` (only structures are compared)
match Self::cheap_supertype_of(lhs, rhs_sup) {
(Absolutely, true) => {
Self::undo_substitute_typarams(typ);
return (Absolutely, true);
}
(Maybe, _) => {
if self.structural_supertype_of(lhs, rhs_sup) {
Self::undo_substitute_typarams(typ);
return (Absolutely, true);
}
}
_ => {}
}
}
Self::undo_substitute_typarams(typ);
}
(Maybe, false)
}
fn classes_supertype_of(&self, lhs: &Type, rhs: &Type) -> (Credibility, bool) {
if !self.is_class(lhs) || !self.is_class(rhs) {
return (Maybe, false);
}
self._nominal_subtype_of(lhs, rhs, |ty_ctx| &ty_ctx.super_classes)
}
// e.g. Eq(Nat) :> Nat
// Nat.super_traits = [Add(Nat), Eq(Nat), Sub(Float), ...]
// e.g. Eq :> ?L or ?R (if ?L <: Eq and ?R <: Eq)
@ -293,41 +319,7 @@ impl Context {
if !self.is_trait(lhs) {
return (Maybe, false);
}
if let Some((typ, rhs_ctx)) = self.get_nominal_type_ctx(rhs) {
if typ.has_qvar() {
if let Err(err) = self.substitute_typarams(typ, rhs) {
Self::undo_substitute_typarams(typ);
if DEBUG_MODE {
panic!("err: {err}");
}
}
} else if typ.has_undoable_linked_var() {
if let Err(err) = self.overwrite_typarams(typ, rhs) {
Self::undo_substitute_typarams(typ);
if DEBUG_MODE {
panic!("err: {err}");
}
}
}
for rhs_sup in rhs_ctx.super_traits.iter() {
// Not `supertype_of` (only structures are compared)
match Self::cheap_supertype_of(lhs, rhs_sup) {
(Absolutely, true) => {
Self::undo_substitute_typarams(typ);
return (Absolutely, true);
}
(Maybe, _) => {
if self.structural_supertype_of(lhs, rhs_sup) {
Self::undo_substitute_typarams(typ);
return (Absolutely, true);
}
}
_ => {}
}
}
Self::undo_substitute_typarams(typ);
}
(Maybe, false)
self._nominal_subtype_of(lhs, rhs, |ty_ctx| &ty_ctx.super_traits)
}
/// lhs :> rhs?
@ -426,7 +418,7 @@ impl Context {
},
_,
) => {
if let Ok(evaled) = self.eval_proj_call(
if let Ok(evaled) = self.eval_proj_call_t(
*l.clone(),
attr_name.clone(),
args.clone(),
@ -447,7 +439,7 @@ impl Context {
args,
},
) => {
if let Ok(evaled) = self.eval_proj_call(
if let Ok(evaled) = self.eval_proj_call_t(
*r.clone(),
attr_name.clone(),
args.clone(),
@ -590,13 +582,13 @@ impl Context {
}
self.is_super_pred_of(&l.pred, &r.pred)
}
(Nat, re @ Refinement(_)) => {
let nat = Type::Refinement(Nat.into_refinement());
self.structural_supertype_of(&nat, re)
(Nat | Bool, re @ Refinement(_)) => {
let refine = Type::Refinement(lhs.clone().into_refinement());
self.structural_supertype_of(&refine, re)
}
(re @ Refinement(_), Nat) => {
let nat = Type::Refinement(Nat.into_refinement());
self.structural_supertype_of(re, &nat)
(re @ Refinement(_), Nat | Bool) => {
let refine = Type::Refinement(rhs.clone().into_refinement());
self.structural_supertype_of(re, &refine)
}
(Structural(_), Refinement(refine)) => self.supertype_of(lhs, &refine.t),
// Int :> {I: Int | ...} == true
@ -606,21 +598,24 @@ impl Context {
// => true
// Bool :> {1} == true
// Bool :> {2} == false
// [2, 3]: {A: Array(Nat) | A.prod() == 6}
(l, Refinement(r)) => {
// Type / {S: Set(Str) | S == {"a", "b"}}
if let Predicate::Equal { rhs, .. } = r.pred.as_ref() {
if let Pred::Equal { rhs, .. } = r.pred.as_ref() {
if self.subtype_of(l, &Type) && self.convert_tp_into_type(rhs.clone()).is_ok() {
return true;
}
}
if self.supertype_of(l, &r.t) {
return true;
} else if self.subtype_of(l, &r.t) {
return false;
}
let l = l.derefine();
if self.supertype_of(&l, &r.t) {
return true;
}
let l = Type::Refinement(l.into_refinement());
let l = Type::Refinement(l.clone().into_refinement());
self.structural_supertype_of(&l, rhs)
}
// ({I: Int | True} :> Int) == true, ({N: Nat | ...} :> Int) == false, ({I: Int | I >= 0} :> Int) == false
@ -801,7 +796,7 @@ impl Context {
rparams: &[TyParam],
) -> bool {
log!(
"poly_supertype_of: {}, {}, {}",
"poly_supertype_of: {}\nlps: {}\nrps: {}",
typ.qual_name(),
erg_common::fmt_vec(lparams),
erg_common::fmt_vec(rparams)
@ -810,7 +805,12 @@ impl Context {
.get_nominal_type_ctx(typ)
.unwrap_or_else(|| panic!("{typ} is not found"));
let variances = ctx.type_params_variance();
debug_assert_eq!(lparams.len(), variances.len());
debug_assert_eq!(
lparams.len(),
variances.len(),
"{} / {variances:?}",
erg_common::fmt_vec(lparams)
);
lparams
.iter()
.zip(rparams.iter())
@ -918,6 +918,26 @@ impl Context {
self.eq_tp(sup_p, sub_p)
}
}
(TyParam::ProjCall { obj, attr, args }, _) => {
if let Ok(evaled) =
self.eval_proj_call(obj.as_ref().clone(), attr.clone(), args.clone(), &())
{
if sup_p != &evaled {
return self.supertype_of_tp(&evaled, sub_p, variance);
}
}
false
}
(_, TyParam::ProjCall { obj, attr, args }) => {
if let Ok(evaled) =
self.eval_proj_call(obj.as_ref().clone(), attr.clone(), args.clone(), &())
{
if sub_p != &evaled {
return self.supertype_of_tp(sup_p, &evaled, variance);
}
}
false
}
_ => {
match (
self.convert_tp_into_type(sup_p.clone()),
@ -1235,7 +1255,11 @@ impl Context {
}
}
fn union_refinement(&self, lhs: &RefinementType, rhs: &RefinementType) -> RefinementType {
pub(crate) fn union_refinement(
&self,
lhs: &RefinementType,
rhs: &RefinementType,
) -> RefinementType {
// TODO: warn if lhs.t !:> rhs.t && rhs.t !:> lhs.t
let union = self.union(&lhs.t, &rhs.t);
let name = lhs.var.clone();

View file

@ -1,4 +1,5 @@
use std::mem;
use std::ops::Drop;
use erg_common::consts::DEBUG_MODE;
use erg_common::dict::Dict;
@ -91,6 +92,289 @@ fn op_to_name(op: OpKind) -> &'static str {
}
}
#[derive(Debug)]
pub struct Substituter<'c> {
ctx: &'c Context,
qt: Type,
#[allow(unused)]
st: Type,
child: Option<Box<Substituter<'c>>>,
}
impl Drop for Substituter<'_> {
fn drop(&mut self) {
Self::undo_substitute_typarams(&self.qt);
}
}
impl<'c> Substituter<'c> {
fn new(ctx: &'c Context, qt: Type, st: Type) -> Self {
Self {
ctx,
qt,
st,
child: None,
}
}
/// e.g.
/// ```erg
/// qt: Array(T, N), st: Array(Int, 3)
/// ```
/// invalid (no effect):
/// ```erg
/// qt: Iterable(T), st: Array(Int, 3)
/// qt: Array(T, N), st: Array!(Int, 3) # TODO
/// ```
/// use `undo_substitute_typarams` after executing this method
pub(crate) fn substitute_typarams(
ctx: &'c Context,
qt: &Type,
st: &Type,
) -> EvalResult<Option<Self>> {
let qtps = qt.typarams();
let stps = st.typarams();
if qt.qual_name() != st.qual_name() || qtps.len() != stps.len() {
if let Some(sub) = st.get_sub() {
return Self::substitute_typarams(ctx, qt, &sub);
}
log!(err "{qt} / {st}");
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
return Ok(None); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
}
let mut self_ = Self::new(ctx, qt.clone(), st.clone());
let mut errs = EvalErrors::empty();
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
if let Err(err) = self_.substitute_typaram(qtp, stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(Some(self_))
}
}
pub(crate) fn overwrite_typarams(
ctx: &'c Context,
qt: &Type,
st: &Type,
) -> EvalResult<Option<Self>> {
let qtps = qt.typarams();
let stps = st.typarams();
if qt.qual_name() != st.qual_name() || qtps.len() != stps.len() {
if let Some(sub) = st.get_sub() {
return Self::overwrite_typarams(ctx, qt, &sub);
}
log!(err "{qt} / {st}");
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
return Ok(None); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
}
let mut self_ = Self::new(ctx, qt.clone(), st.clone());
let mut errs = EvalErrors::empty();
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
if let Err(err) = self_.overwrite_typaram(qtp, stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(Some(self_))
}
}
fn substitute_typaram(&mut self, qtp: TyParam, stp: TyParam) -> EvalResult<()> {
match qtp {
TyParam::FreeVar(ref fv) if fv.is_generalized() => {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
/*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}*/
Ok(())
}
TyParam::Type(qt) => self.substitute_type(*qt, stp),
TyParam::Value(ValueObj::Type(qt)) => self.substitute_type(qt.into_typ(), stp),
TyParam::App { name: _, args } => {
let tps = stp.typarams();
debug_assert_eq!(args.len(), tps.len());
let mut errs = EvalErrors::empty();
for (qtp, stp) in args.iter().zip(tps.into_iter()) {
if let Err(err) = self.substitute_typaram(qtp.clone(), stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(())
}
}
_ => Ok(()),
}
}
fn substitute_type(&mut self, qt: Type, stp: TyParam) -> EvalResult<()> {
let st = self.ctx.convert_tp_into_type(stp).map_err(|tp| {
EvalError::not_a_type_error(
self.ctx.cfg.input.clone(),
line!() as usize,
().loc(),
self.ctx.caused_by(),
&tp.to_string(),
)
})?;
if !qt.is_undoable_linked_var()
&& qt.is_generalized()
&& qt.is_free_var()
&& (!st.is_unbound_var() || !st.is_generalized())
{
qt.undoable_link(&st);
} else if qt.is_undoable_linked_var()
&& (!st.is_unbound_var() || !st.is_generalized())
&& qt != st
{
// e.g. Array(T, N) <: Add(Array(T, M))
// Array((Int), (3)) <: Add(Array((Int), (4))): OK
// Array((Int), (3)) <: Add(Array((Str), (4))): NG
if let Some(union) = self.ctx.unify(&qt, &st) {
qt.undoable_link(&union);
} else {
return Err(EvalError::unification_error(
self.ctx.cfg.input.clone(),
line!() as usize,
&qt,
&st,
().loc(),
self.ctx.caused_by(),
)
.into());
}
}
if !st.is_unbound_var() || !st.is_generalized() {
self.child = Self::substitute_typarams(self.ctx, &qt, &st)?.map(Box::new);
}
if st.has_no_unbound_var() && qt.has_no_unbound_var() {
return Ok(());
}
let qt = if qt.has_undoable_linked_var() {
let mut tv_cache = TyVarCache::new(self.ctx.level, self.ctx);
self.ctx.detach(qt, &mut tv_cache)
} else {
qt
};
if let Err(errs) = self.ctx.undoable_sub_unify(&st, &qt, &(), None) {
log!(err "{errs}");
}
Ok(())
}
fn overwrite_typaram(&mut self, qtp: TyParam, stp: TyParam) -> EvalResult<()> {
match qtp {
TyParam::FreeVar(ref fv) if fv.is_undoable_linked() => {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
/*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}*/
Ok(())
}
// NOTE: Rarely, double overwriting occurs.
// Whether this could be a problem is under consideration.
// e.g. `T` of Array(T, N) <: Add(T, M)
TyParam::FreeVar(ref fv) if fv.is_generalized() => {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
/*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}*/
Ok(())
}
TyParam::Type(qt) => self.overwrite_type(*qt, stp),
TyParam::Value(ValueObj::Type(qt)) => self.overwrite_type(qt.into_typ(), stp),
TyParam::App { name: _, args } => {
let tps = stp.typarams();
debug_assert_eq!(args.len(), tps.len());
let mut errs = EvalErrors::empty();
for (qtp, stp) in args.iter().zip(tps.into_iter()) {
if let Err(err) = self.overwrite_typaram(qtp.clone(), stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(())
}
}
_ => Ok(()),
}
}
fn overwrite_type(&mut self, qt: Type, stp: TyParam) -> EvalResult<()> {
let st = self.ctx.convert_tp_into_type(stp).map_err(|tp| {
EvalError::not_a_type_error(
self.ctx.cfg.input.clone(),
line!() as usize,
().loc(),
self.ctx.caused_by(),
&tp.to_string(),
)
})?;
if qt.is_undoable_linked_var() && (!st.is_unbound_var() || !st.is_generalized()) {
qt.undoable_link(&st);
}
if !st.is_unbound_var() || !st.is_generalized() {
self.child = Self::overwrite_typarams(self.ctx, &qt, &st)?.map(Box::new);
}
let qt = if qt.has_undoable_linked_var() {
let mut tv_cache = TyVarCache::new(self.ctx.level, self.ctx);
self.ctx.detach(qt, &mut tv_cache)
} else {
qt
};
if let Err(errs) = self.ctx.undoable_sub_unify(&st, &qt, &(), None) {
log!(err "{errs}");
}
Ok(())
}
pub(crate) fn undo_substitute_typarams(substituted_q: &Type) {
for tp in substituted_q.typarams().into_iter() {
Self::undo_substitute_typaram(tp);
}
}
fn undo_substitute_typaram(substituted_q_tp: TyParam) {
match substituted_q_tp {
TyParam::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(),
TyParam::Type(t) if t.is_free_var() => {
let Ok(subst) = <&FreeTyVar>::try_from(t.as_ref()) else { unreachable!() };
if subst.is_undoable_linked() {
subst.undo();
}
}
TyParam::Type(t) => {
Self::undo_substitute_typarams(&t);
}
TyParam::Value(ValueObj::Type(t)) => {
Self::undo_substitute_typarams(t.typ());
}
TyParam::App { args, .. } => {
for arg in args.into_iter() {
Self::undo_substitute_typaram(arg);
}
}
_ => {}
}
}
}
impl Context {
fn try_get_op_kind_from_token(&self, token: &Token) -> EvalResult<OpKind> {
match token.kind {
@ -244,6 +528,18 @@ impl Context {
}
fn eval_const_call(&self, call: &Call) -> EvalResult<ValueObj> {
let tp = self.tp_eval_const_call(call)?;
ValueObj::try_from(tp).map_err(|_| {
EvalErrors::from(EvalError::not_const_expr(
self.cfg.input.clone(),
line!() as usize,
call.loc(),
self.caused_by(),
))
})
}
fn tp_eval_const_call(&self, call: &Call) -> EvalResult<TyParam> {
if let Expr::Accessor(acc) = call.obj.as_ref() {
match acc {
Accessor::Ident(ident) => {
@ -302,7 +598,7 @@ impl Context {
}
}
fn call(&self, subr: ConstSubr, args: ValueArgs, loc: Location) -> EvalResult<ValueObj> {
fn call(&self, subr: ConstSubr, args: ValueArgs, loc: Location) -> EvalResult<TyParam> {
match subr {
ConstSubr::User(user) => {
// HACK: should avoid cloning
@ -325,7 +621,7 @@ impl Context {
for (name, arg) in args.kw_args.into_iter() {
subr_ctx.consts.insert(VarName::from_str(name), arg);
}
subr_ctx.eval_const_block(&user.block())
subr_ctx.eval_const_block(&user.block()).map(TyParam::value)
}
ConstSubr::Builtin(builtin) => builtin.call(args, self).map_err(|mut e| {
if e.0.loc.is_unknown() {
@ -386,16 +682,18 @@ impl Context {
}
}
fn eval_const_array(&self, arr: &Array) -> EvalResult<ValueObj> {
pub(crate) fn eval_const_normal_array(&self, arr: &NormalArray) -> EvalResult<ValueObj> {
let mut elems = vec![];
for elem in arr.elems.pos_args().iter() {
let elem = self.eval_const_expr(&elem.expr)?;
elems.push(elem);
}
Ok(ValueObj::Array(ArcArray::from(elems)))
}
fn eval_const_array(&self, arr: &Array) -> EvalResult<ValueObj> {
match arr {
Array::Normal(arr) => {
for elem in arr.elems.pos_args().iter() {
let elem = self.eval_const_expr(&elem.expr)?;
elems.push(elem);
}
Ok(ValueObj::Array(ArcArray::from(elems)))
}
Array::Normal(arr) => self.eval_const_normal_array(arr),
_ => Err(EvalErrors::from(EvalError::not_const_expr(
self.cfg.input.clone(),
line!() as usize,
@ -1050,6 +1348,7 @@ impl Context {
})?;
Ok(TyParam::Value(ValueObj::Type(t)))
}
TyParam::ProjCall { obj, attr, args } => self.eval_proj_call(*obj, attr, args, &()),
TyParam::Value(_) => Ok(p.clone()),
_other => feature_error!(self, Location::Unknown, "???"),
}
@ -1145,7 +1444,7 @@ impl Context {
attr_name,
args,
} => self
.eval_proj_call(*lhs, attr_name, args, level, t_loc)
.eval_proj_call_t(*lhs, attr_name, args, level, t_loc)
.map_err(|errs| (Failure, errs)),
Type::Ref(l) => match self.eval_t_params(*l, level, t_loc) {
Ok(t) => Ok(ref_(t)),
@ -1305,7 +1604,7 @@ impl Context {
let (sub, sup) = fv.get_subsup().unwrap();
if self.is_trait(&sup) && !self.trait_impl_exists(&sub, &sup) {
// link to `Never` to prevent double errors from being reported
lhs.link(&Never);
lhs.destructive_link(&Never);
let sub = if cfg!(feature = "debug") {
sub
} else {
@ -1405,6 +1704,7 @@ impl Context {
let lhs = self.convert_tp_into_type(*obj)?;
Ok(lhs.proj(attr))
}
TyParam::ProjCall { obj, attr, args } => Ok(proj_call(*obj, attr, args)),
// TyParam::Erased(_t) => Ok(Type::Obj),
TyParam::Value(v) => self.convert_value_into_type(v).map_err(TyParam::Value),
// TODO: Dict, Set
@ -1412,15 +1712,22 @@ impl Context {
}
}
#[allow(clippy::only_used_in_recursion)]
pub(crate) fn convert_tp_into_value(&self, tp: TyParam) -> Result<ValueObj, TyParam> {
match tp {
TyParam::FreeVar(fv) if fv.is_linked() => {
self.convert_tp_into_value(fv.crack().clone())
}
TyParam::Value(v) => Ok(v),
other => Err(other),
other => self.convert_tp_into_type(other).map(ValueObj::builtin_type),
}
}
pub(crate) fn convert_singular_type_into_value(&self, typ: Type) -> Result<ValueObj, Type> {
match typ {
Type::FreeVar(fv) if fv.is_linked() => {
self.convert_singular_type_into_value(fv.crack().clone())
}
Type::Refinement(ref refine) => {
if let Predicate::Equal { rhs, .. } = refine.pred.as_ref() {
self.convert_tp_into_value(rhs.clone()).map_err(|_| typ)
@ -1537,6 +1844,10 @@ impl Context {
fn _convert_type_to_dict_type(&self, ty: Type) -> Result<Dict<Type, Type>, ()> {
match ty {
Type::FreeVar(fv) if fv.is_linked() => {
self._convert_type_to_dict_type(fv.crack().clone())
}
Type::Refinement(refine) => self._convert_type_to_dict_type(*refine.t),
Type::Poly { name, params } if &name[..] == "Dict" => {
let dict = Dict::try_from(params[0].clone())?;
let mut new_dict = dict! {};
@ -1551,14 +1862,23 @@ impl Context {
}
}
fn convert_type_to_array(&self, ty: Type) -> Result<Vec<ValueObj>, Type> {
pub(crate) fn convert_type_to_array(&self, ty: Type) -> Result<Vec<ValueObj>, Type> {
match ty {
Type::FreeVar(fv) if fv.is_linked() => self.convert_type_to_array(fv.crack().clone()),
Type::Refinement(refine) => self.convert_type_to_array(*refine.t),
Type::Poly { name, params } if &name[..] == "Array" || &name[..] == "Array!" => {
let Ok(t) = self.convert_tp_into_type(params[0].clone()) else {
log!(err "cannot convert to type: {}", params[0]);
return Err(poly(name, params));
};
let TyParam::Value(ValueObj::Nat(len)) = params[1] else { unreachable!() };
Ok(vec![ValueObj::builtin_type(t); len as usize])
let Ok(len) = usize::try_from(&params[1]) else {
log!(err "cannot convert to usize: {}", params[1]);
if DEBUG_MODE {
panic!("cannot convert to usize: {}", params[1]);
}
return Err(poly(name, params));
};
Ok(vec![ValueObj::builtin_type(t); len])
}
_ => Err(ty),
}
@ -1605,40 +1925,22 @@ impl Context {
if let ValueObj::Type(quant_projected_t) = obj {
let projected_t = quant_projected_t.into_typ();
let (quant_sub, _) = self.get_type(&sub.qual_name()).unwrap();
if let Some(sup) = opt_sup {
if let Some(quant_sup) = methods.impl_of() {
// T -> Int, M -> 2
self.substitute_typarams(&quant_sup, sup)
.map_err(|errs| {
Self::undo_substitute_typarams(&quant_sup);
errs
})
.ok()?;
}
}
let _sup_subs = if let Some((sup, quant_sup)) = opt_sup.zip(methods.impl_of()) {
// T -> Int, M -> 2
Substituter::substitute_typarams(self, &quant_sup, sup).ok()?
} else {
None
};
// T -> Int, N -> 4
self.substitute_typarams(quant_sub, sub)
.map_err(|errs| {
Self::undo_substitute_typarams(quant_sub);
errs
})
.ok()?;
let _sub_subs = Substituter::substitute_typarams(self, quant_sub, sub).ok()?;
// [T; M+N] -> [Int; 4+2] -> [Int; 6]
let res = self.eval_t_params(projected_t, level, t_loc).ok();
if let Some(t) = res {
let mut tv_cache = TyVarCache::new(self.level, self);
let t = self.detach(t, &mut tv_cache);
// Int -> T, 2 -> M, 4 -> N
Self::undo_substitute_typarams(quant_sub);
if let Some(quant_sup) = methods.impl_of() {
Self::undo_substitute_typarams(&quant_sup);
}
return Some(t);
}
Self::undo_substitute_typarams(quant_sub);
if let Some(quant_sup) = methods.impl_of() {
Self::undo_substitute_typarams(&quant_sup);
}
} else {
log!(err "{obj}");
if DEBUG_MODE {
@ -1698,157 +2000,13 @@ impl Context {
}
}
/// e.g.
/// ```erg
/// qt: Array(T, N), st: Array(Int, 3)
/// ```
/// invalid (no effect):
/// ```erg
/// qt: Iterable(T), st: Array(Int, 3)
/// qt: Array(T, N), st: Array!(Int, 3) # TODO
/// ```
/// use `undo_substitute_typarams` after executing this method
pub(crate) fn substitute_typarams(&self, qt: &Type, st: &Type) -> EvalResult<()> {
let qtps = qt.typarams();
let stps = st.typarams();
if qt.qual_name() != st.qual_name() || qtps.len() != stps.len() {
if let Some(sub) = st.get_sub() {
return self.substitute_typarams(qt, &sub);
}
log!(err "{qt} / {st}");
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
return Ok(()); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
}
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
self.substitute_typaram(qtp, stp)?;
}
Ok(())
}
pub(crate) fn overwrite_typarams(&self, qt: &Type, st: &Type) -> EvalResult<()> {
let qtps = qt.typarams();
let stps = st.typarams();
if qt.qual_name() != st.qual_name() || qtps.len() != stps.len() {
log!(err "{qt} / {st}");
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
return Ok(()); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
}
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
self.overwrite_typaram(qtp, stp)?;
}
Ok(())
}
fn substitute_typaram(&self, qtp: TyParam, stp: TyParam) -> EvalResult<()> {
match qtp {
TyParam::FreeVar(ref fv) if fv.is_generalized() => {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}
Ok(())
}
TyParam::Type(qt) => self.substitute_type(stp, *qt),
TyParam::Value(ValueObj::Type(qt)) => self.substitute_type(stp, qt.into_typ()),
_ => Ok(()),
}
}
fn substitute_type(&self, stp: TyParam, qt: Type) -> EvalResult<()> {
let st = self.convert_tp_into_type(stp).map_err(|tp| {
EvalError::not_a_type_error(
self.cfg.input.clone(),
line!() as usize,
().loc(),
self.caused_by(),
&tp.to_string(),
)
})?;
if qt.is_generalized() && qt.is_free_var() && (!st.is_unbound_var() || !st.is_generalized())
{
qt.undoable_link(&st);
}
if !st.is_unbound_var() || !st.is_generalized() {
self.substitute_typarams(&qt, &st)?;
}
if st.has_no_unbound_var() && qt.has_no_unbound_var() {
return Ok(());
}
if let Err(errs) = self.sub_unify(&st, &qt, &(), None) {
log!(err "{errs}");
}
Ok(())
}
fn overwrite_typaram(&self, qtp: TyParam, stp: TyParam) -> EvalResult<()> {
match qtp {
TyParam::FreeVar(ref fv) if fv.is_undoable_linked() => {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}
Ok(())
}
TyParam::Type(qt) => self.overwrite_type(stp, *qt),
TyParam::Value(ValueObj::Type(qt)) => self.overwrite_type(stp, qt.into_typ()),
_ => Ok(()),
}
}
fn overwrite_type(&self, stp: TyParam, qt: Type) -> EvalResult<()> {
let st = self.convert_tp_into_type(stp).map_err(|tp| {
EvalError::not_a_type_error(
self.cfg.input.clone(),
line!() as usize,
().loc(),
self.caused_by(),
&tp.to_string(),
)
})?;
if qt.has_undoable_linked_var() && (!st.is_unbound_var() || !st.is_generalized()) {
qt.undoable_link(&st);
}
if !st.is_unbound_var() || !st.is_generalized() {
self.overwrite_typarams(&qt, &st)?;
}
if let Err(errs) = self.sub_unify(&st, &qt, &(), None) {
log!(err "{errs}");
}
Ok(())
}
pub(crate) fn undo_substitute_typarams(substituted_q: &Type) {
for tp in substituted_q.typarams().into_iter() {
match tp {
TyParam::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(),
TyParam::Type(t) if t.is_free_var() => {
let Ok(subst) = <&FreeTyVar>::try_from(t.as_ref()) else { unreachable!() };
if subst.is_undoable_linked() {
subst.undo();
}
}
TyParam::Type(t) => {
Self::undo_substitute_typarams(&t);
}
TyParam::Value(ValueObj::Type(t)) => {
Self::undo_substitute_typarams(t.typ());
}
_ => {}
}
}
}
fn do_proj_call(
&self,
obj: ValueObj,
lhs: TyParam,
args: Vec<TyParam>,
t_loc: &impl Locational,
) -> EvalResult<Type> {
) -> EvalResult<TyParam> {
if let ValueObj::Subr(subr) = obj {
let mut pos_args = vec![];
if subr.sig_t().is_method() {
@ -1872,17 +2030,34 @@ impl Context {
}
}
let args = ValueArgs::new(pos_args, dict! {});
let t = self.call(subr, args, t_loc.loc())?;
let t = self
.convert_value_into_type(t)
.unwrap_or_else(|value| todo!("Type::try_from {value}"));
Ok(t)
let tp = self.call(subr, args, t_loc.loc())?;
Ok(tp)
} else {
feature_error!(self, t_loc.loc(), "??")
feature_error!(self, t_loc.loc(), "do_proj_call: ??")
}
}
pub(crate) fn eval_proj_call(
fn do_proj_call_t(
&self,
obj: ValueObj,
lhs: TyParam,
args: Vec<TyParam>,
t_loc: &impl Locational,
) -> EvalResult<Type> {
let tp = self.do_proj_call(obj, lhs, args, t_loc)?;
self.convert_tp_into_type(tp).map_err(|e| {
EvalError::feature_error(
self.cfg.input.clone(),
line!() as usize,
t_loc.loc(),
&format!("converting {e} to a type"),
self.caused_by(),
)
.into()
})
}
pub(crate) fn eval_proj_call_t(
&self,
lhs: TyParam,
attr_name: Str,
@ -1901,11 +2076,11 @@ impl Context {
)
})? {
if let Ok(obj) = ty_ctx.get_const_local(&Token::symbol(&attr_name), &self.name) {
return self.do_proj_call(obj, lhs, args, t_loc);
return self.do_proj_call_t(obj, lhs, args, t_loc);
}
for (_class, methods) in ty_ctx.methods_list.iter() {
if let Ok(obj) = methods.get_const_local(&Token::symbol(&attr_name), &self.name) {
return self.do_proj_call(obj, lhs, args, t_loc);
return self.do_proj_call_t(obj, lhs, args, t_loc);
}
}
}
@ -1913,7 +2088,7 @@ impl Context {
if let Some((sub, sup)) = fv.get_subsup() {
if self.is_trait(&sup) && !self.trait_impl_exists(&sub, &sup) {
// to prevent double error reporting
lhs.link(&TyParam::t(Never));
lhs.destructive_link(&TyParam::t(Never));
let sub = if cfg!(feature = "debug") {
sub
} else {
@ -1960,6 +2135,77 @@ impl Context {
}
}
pub(crate) fn eval_proj_call(
&self,
lhs: TyParam,
attr_name: Str,
args: Vec<TyParam>,
t_loc: &impl Locational,
) -> EvalResult<TyParam> {
let t = self.get_tp_t(&lhs)?;
for ty_ctx in self.get_nominal_super_type_ctxs(&t).ok_or_else(|| {
EvalError::type_not_found(
self.cfg.input.clone(),
line!() as usize,
t_loc.loc(),
self.caused_by(),
&t,
)
})? {
if let Ok(obj) = ty_ctx.get_const_local(&Token::symbol(&attr_name), &self.name) {
return self.do_proj_call(obj, lhs, args, t_loc);
}
for (_class, methods) in ty_ctx.methods_list.iter() {
if let Ok(obj) = methods.get_const_local(&Token::symbol(&attr_name), &self.name) {
return self.do_proj_call(obj, lhs, args, t_loc);
}
}
}
if let TyParam::FreeVar(fv) = &lhs {
if let Some((sub, sup)) = fv.get_subsup() {
if self.is_trait(&sup) && !self.trait_impl_exists(&sub, &sup) {
// to prevent double error reporting
lhs.destructive_link(&TyParam::t(Never));
let sub = if cfg!(feature = "debug") {
sub
} else {
self.readable_type(sub)
};
let sup = if cfg!(feature = "debug") {
sup
} else {
self.readable_type(sup)
};
return Err(EvalErrors::from(EvalError::no_trait_impl_error(
self.cfg.input.clone(),
line!() as usize,
&sub,
&sup,
t_loc.loc(),
self.caused_by(),
self.get_simple_type_mismatch_hint(&sup, &sub),
)));
}
}
}
// if the target can't be found in the supertype, the type will be dereferenced.
// In many cases, it is still better to determine the type variable than if the target is not found.
let coerced = self.coerce_tp(lhs.clone(), t_loc)?;
if lhs != coerced {
self.eval_proj_call(coerced, attr_name, args, t_loc)
} else {
let proj = proj_call(lhs, attr_name, args);
Err(EvalErrors::from(EvalError::no_candidate_error(
self.cfg.input.clone(),
line!() as usize,
&proj,
t_loc.loc(),
self.caused_by(),
self.get_no_candidate_hint(&proj),
)))
}
}
pub(crate) fn eval_pred(&self, p: Predicate) -> EvalResult<Predicate> {
match p {
Predicate::Value(_) | Predicate::Const(_) => Ok(p),
@ -2087,6 +2333,11 @@ impl Context {
)
}
},
TyParam::ProjCall { obj, attr, args } => {
let tp = self.eval_proj_call(*obj, attr, args, &())?;
let ty = self.get_tp_t(&tp).unwrap_or(Type::Obj).derefine();
Ok(tp_enum(ty, set![tp]))
}
other => feature_error!(
self,
Location::Unknown,

View file

@ -4,7 +4,7 @@ use erg_common::consts::DEBUG_MODE;
use erg_common::set::Set;
use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_common::{dict, fn_name, set};
use erg_common::{dict, fn_name, get_hash, set};
#[allow(unused_imports)]
use erg_common::{fmt_vec, log};
@ -21,6 +21,8 @@ use crate::{feature_error, hir};
use Type::*;
use Variance::*;
use super::eval::Substituter;
pub struct Generalizer {
level: usize,
variance: Variance,
@ -151,7 +153,7 @@ impl Generalizer {
if sub == sup {
let t = self.generalize_t(sub, uninit);
let res = FreeVar(fv);
res.link(&t);
res.destructive_link(&t);
res
} else if sup != Obj
&& !self.qnames.contains(&fv.unbound_name().unwrap())
@ -551,7 +553,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
Ok(ty)
}
Err(errs) => {
Type::FreeVar(fv).link(&Never);
Type::FreeVar(fv).destructive_link(&Never);
Err(errs)
}
}
@ -693,7 +695,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
}
let proj = self
.ctx
.eval_proj_call(lhs, attr_name, new_args, self.ctx.level, self.loc)
.eval_proj_call_t(lhs, attr_name, new_args, self.ctx.level, self.loc)
.unwrap_or(Failure);
Ok(proj)
}
@ -912,22 +914,29 @@ impl Context {
}
fn poly_class_trait_impl_exists(&self, class: &Type, trait_: &Type) -> bool {
let mut super_exists = false;
let class_hash = get_hash(&class);
let trait_hash = get_hash(&trait_);
for imp in self.get_trait_impls(trait_).into_iter() {
self.substitute_typarams(&imp.sub_type, class).unwrap_or(());
self.substitute_typarams(&imp.sup_trait, trait_)
.unwrap_or(());
let _sub_subs = Substituter::substitute_typarams(self, &imp.sub_type, class).ok();
let _sup_subs = Substituter::substitute_typarams(self, &imp.sup_trait, trait_).ok();
if self.supertype_of(&imp.sub_type, class) && self.supertype_of(&imp.sup_trait, trait_)
{
super_exists = true;
Self::undo_substitute_typarams(&imp.sub_type);
Self::undo_substitute_typarams(&imp.sup_trait);
break;
if class_hash != get_hash(&class) {
class.undo();
}
if trait_hash != get_hash(&trait_) {
trait_.undo();
}
return true;
}
if class_hash != get_hash(&class) {
class.undo();
}
if trait_hash != get_hash(&trait_) {
trait_.undo();
}
Self::undo_substitute_typarams(&imp.sub_type);
Self::undo_substitute_typarams(&imp.sup_trait);
}
super_exists
false
}
fn check_trait_impl(

View file

@ -1270,6 +1270,15 @@ impl Context {
array_
.register_marker_trait(self, poly(INDEXABLE, vec![ty_tp(input), ty_tp(T.clone())]))
.unwrap();
array_
.register_marker_trait(
self,
poly(
SHAPE,
vec![ty_tp(arr_t.clone()).proj_call(FUNC_SHAPE.into(), vec![])],
),
)
.unwrap();
let mut array_sized = Self::builtin_methods(Some(mono(SIZED)), 2);
array_sized.register_builtin_erg_impl(
FUNDAMENTAL_LEN,
@ -1281,12 +1290,25 @@ impl Context {
// union: (self: [Type; _]) -> Type
let array_union_t = fn0_met(array_t(Type, TyParam::erased(Nat)), Type).quantify();
let union = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
UNION_FUNC,
FUNC_UNION,
array_union,
array_union_t,
None,
)));
array_.register_builtin_const(UNION_FUNC, Visibility::BUILTIN_PUBLIC, union);
array_.register_builtin_const(FUNC_UNION, Visibility::BUILTIN_PUBLIC, union);
// shape: (self: [Type; _]) -> [Nat; _]
let array_shape_t = fn0_met(
array_t(Type, TyParam::erased(Nat)),
unknown_len_array_t(Nat),
)
.quantify();
let shape = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
FUNC_SHAPE,
array_shape,
array_shape_t,
None,
)));
array_.register_builtin_const(FUNC_SHAPE, Visibility::BUILTIN_PUBLIC, shape);
let mut array_eq = Self::builtin_methods(Some(mono(EQ)), 2);
array_eq.register_builtin_erg_impl(
OP_EQ,
@ -1604,7 +1626,7 @@ impl Context {
// __Tuple_getitem__: (self: Tuple(Ts), _: {N}) -> Ts[N]
let input_t = tp_enum(Nat, set! {N.clone()});
let return_t = proj_call(Ts.clone(), FUNDAMENTAL_GETITEM, vec![N.clone()]);
let union_t = proj_call(Ts.clone(), UNION_FUNC, vec![]);
let union_t = proj_call(Ts.clone(), FUNC_UNION, vec![]);
let tuple_getitem_t =
fn1_met(_tuple_t.clone(), input_t.clone(), return_t.clone()).quantify();
tuple_.register_builtin_py_impl(
@ -1629,13 +1651,13 @@ impl Context {
let mut tuple_iterable = Self::builtin_methods(
Some(poly(
ITERABLE,
vec![ty_tp(proj_call(Ts.clone(), UNION_FUNC, vec![]))],
vec![ty_tp(proj_call(Ts.clone(), FUNC_UNION, vec![]))],
)),
2,
);
let tuple_iterator = poly(
TUPLE_ITERATOR,
vec![ty_tp(proj_call(Ts, UNION_FUNC, vec![]))],
vec![ty_tp(proj_call(Ts, FUNC_UNION, vec![]))],
);
// Tuple(Ts) -> TupleIterator(Ts.union())
let t = fn0_met(_tuple_t.clone(), tuple_iterator.clone()).quantify();
@ -1797,7 +1819,7 @@ impl Context {
Some(SYMMETRIC_DIFFERENCE),
3,
);
frozenset.register_py_builtin(UNION_FUNC, bin_t, Some(UNION_FUNC), 3);
frozenset.register_py_builtin(FUNC_UNION, bin_t, Some(FUNC_UNION), 3);
let memview_t = mono(MEMORYVIEW);
let mut memoryview = Self::builtin_mono_class(MEMORYVIEW, 2);
memoryview.register_superclass(Obj, &obj);

View file

@ -1,7 +1,10 @@
use std::fmt::Display;
use std::mem;
use erg_common::dict::Dict;
use erg_common::enum_unwrap;
#[allow(unused_imports)]
use erg_common::log;
use crate::context::Context;
use crate::feature_error;
@ -16,16 +19,45 @@ use super::{DICT_ITEMS, DICT_KEYS, DICT_VALUES};
const ERR: Color = THEME.colors.error;
const WARN: Color = THEME.colors.warning;
const SUP_ERR: StyledStr = StyledStr::new("Super", Some(ERR), None);
const SUP_WARN: StyledStr = StyledStr::new("Super", Some(WARN), None);
const CLASS_ERR: StyledStr = StyledStr::new("Class", Some(ERR), None);
const REQ_ERR: StyledStr = StyledStr::new("Requirement", Some(ERR), None);
const REQ_WARN: StyledStr = StyledStr::new("Requirement", Some(WARN), None);
const BASE_ERR: StyledStr = StyledStr::new("Base", Some(ERR), None);
const BASE_WARN: StyledStr = StyledStr::new("Base", Some(WARN), None);
fn not_passed(t: impl Display) -> EvalValueError {
let text = t.to_string();
let param = StyledStr::new(&text, Some(ERR), None);
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{param} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
.into()
}
fn no_key(slf: impl Display, key: impl Display) -> EvalValueError {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{slf} has no key {key}"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
.into()
}
fn type_mismatch(expected: impl Display, got: impl Display, param: &str) -> EvalValueError {
let got = StyledString::new(format!("{got}"), Some(ERR), None);
let param = StyledStr::new(param, Some(WARN), None);
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("non-{expected} object {got} is passed to {param}"),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
)
.into()
}
/// Base := Type or NoneType, Impl := Type -> ClassType
pub(crate) fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
pub(crate) fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let base = args.remove_left_or_key("Base");
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type(ctx).unwrap());
@ -33,69 +65,37 @@ pub(crate) fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
match base {
Some(value) => {
if let Some(base) = value.as_type(ctx) {
Ok(ValueObj::gen_t(GenTypeObj::class(t, Some(base), impls)))
Ok(ValueObj::gen_t(GenTypeObj::class(t, Some(base), impls)).into())
} else {
let base = StyledString::new(format!("{value}"), Some(ERR), None);
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("non-type object {base} is passed to {BASE_WARN}",),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
)
.into())
Err(type_mismatch("type", value, "Base"))
}
}
None => Ok(ValueObj::gen_t(GenTypeObj::class(t, None, impls))),
None => Ok(ValueObj::gen_t(GenTypeObj::class(t, None, impls)).into()),
}
}
/// Super: ClassType, Impl := Type, Additional := Type -> ClassType
pub(crate) fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
let sup = StyledStr::new("Super", Some(ERR), None);
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{sup} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
pub(crate) fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let sup = args
.remove_left_or_key("Super")
.ok_or_else(|| not_passed("Super"))?;
let Some(sup) = sup.as_type(ctx) else {
let sup_ty = StyledString::new(format!("{sup}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-class object {sup_ty} is passed to {SUP_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
return Err(type_mismatch("class", sup, "Super"));
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type(ctx).unwrap());
let additional = args.remove_left_or_key("Additional");
let additional = additional.map(|v| v.as_type(ctx).unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::inherited(
t, sup, impls, additional,
)))
Ok(ValueObj::gen_t(GenTypeObj::inherited(t, sup, impls, additional)).into())
}
/// Class: ClassType -> ClassType (with `InheritableType`)
/// This function is used by the compiler to mark a class as inheritable and does nothing in terms of actual operation.
pub(crate) fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<ValueObj> {
let class = args.remove_left_or_key("Class").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{CLASS_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
pub(crate) fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<TyParam> {
let class = args
.remove_left_or_key("Class")
.ok_or_else(|| not_passed("Class"))?;
match class {
ValueObj::Type(TypeObj::Generated(mut gen)) => {
if let Some(typ) = gen.impls_mut() {
@ -111,7 +111,7 @@ pub(crate) fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValue
}
}
}
Ok(ValueObj::Type(TypeObj::Generated(gen)))
Ok(ValueObj::Type(TypeObj::Generated(gen)).into())
}
other => feature_error!(
EvalValueError,
@ -123,129 +123,73 @@ pub(crate) fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValue
}
/// Base: Type, Impl := Type -> TraitType
pub(crate) fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let req = args.remove_left_or_key("Requirement").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{REQ_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
pub(crate) fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let req = args
.remove_left_or_key("Requirement")
.ok_or_else(|| not_passed("Requirement"))?;
let Some(req) = req.as_type(ctx) else {
let req = StyledString::new(format!("{req}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-type object {req} is passed to {REQ_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
return Err(type_mismatch("type", req, "Requirement"));
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type(ctx).unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::trait_(t, req, impls)))
Ok(ValueObj::gen_t(GenTypeObj::trait_(t, req, impls)).into())
}
/// Base: Type, Impl := Type -> Patch
pub(crate) fn patch_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let base = args.remove_left_or_key("Base").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{BASE_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
pub(crate) fn patch_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let base = args
.remove_left_or_key("Base")
.ok_or_else(|| not_passed("Base"))?;
let Some(base) = base.as_type(ctx) else {
let base = StyledString::new(format!("{base}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-type object {base} is passed to {BASE_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
return Err(type_mismatch("type", base, "Base"));
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type(ctx).unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::patch(t, base, impls)))
Ok(ValueObj::gen_t(GenTypeObj::patch(t, base, impls)).into())
}
/// Super: TraitType, Impl := Type, Additional := Type -> TraitType
pub(crate) fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{SUP_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
pub(crate) fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let sup = args
.remove_left_or_key("Super")
.ok_or_else(|| not_passed("Super"))?;
let Some(sup) = sup.as_type(ctx) else {
let sup = StyledString::new(format!("{sup}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-trait object {sup} is passed to {SUP_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
return Err(type_mismatch("trait", sup, "Super"));
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type(ctx).unwrap());
let additional = args.remove_left_or_key("Additional");
let additional = additional.map(|v| v.as_type(ctx).unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::subsumed(
t, sup, impls, additional,
)))
Ok(ValueObj::gen_t(GenTypeObj::subsumed(t, sup, impls, additional)).into())
}
pub(crate) 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,
)
})?;
pub(crate) fn structural_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let type_ = args
.remove_left_or_key("Type")
.ok_or_else(|| not_passed("Type"))?;
let Some(base) = type_.as_type(ctx) 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());
return Err(type_mismatch("type", type_, "Type"));
};
let t = base.typ().clone().structuralize();
Ok(ValueObj::gen_t(GenTypeObj::structural(t, base)))
Ok(ValueObj::gen_t(GenTypeObj::structural(t, base)).into())
}
pub(crate) fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
pub(crate) fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = ctx
.convert_value_into_array(args.remove_left_or_key("Self").unwrap())
.convert_value_into_array(slf)
.unwrap_or_else(|err| panic!("{err}, {args}"));
let index = enum_unwrap!(args.remove_left_or_key("Index").unwrap(), ValueObj::Nat);
let index = args
.remove_left_or_key("Index")
.ok_or_else(|| not_passed("Index"))?;
let index = enum_unwrap!(index, ValueObj::Nat);
if let Some(v) = slf.get(index as usize) {
Ok(v.clone())
Ok(v.clone().into())
} else {
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
@ -327,12 +271,16 @@ pub(crate) fn sub_tpdict_get<'d>(
None
}
pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = args.remove_left_or_key("Self").unwrap();
pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = enum_unwrap!(slf, ValueObj::Dict);
let index = args.remove_left_or_key("Index").unwrap();
let index = args
.remove_left_or_key("Index")
.ok_or_else(|| not_passed("Index"))?;
if let Some(v) = slf.get(&index).or_else(|| sub_vdict_get(&slf, &index, ctx)) {
Ok(v.clone())
Ok(v.clone().into())
} else {
let index = if let ValueObj::Type(t) = &index {
let derefed = ctx.coerce(t.typ().clone(), &()).unwrap_or(t.typ().clone());
@ -340,20 +288,15 @@ pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueR
} else {
index
};
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{slf} has no key {index}"),
line!() as usize,
ErrorKind::IndexError,
Location::Unknown,
)
.into())
Err(no_key(slf, index))
}
}
/// `{Str: Int, Int: Float}.keys() == DictKeys(Str or Int)`
pub(crate) fn dict_keys(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = args.remove_left_or_key("Self").unwrap();
pub(crate) fn dict_keys(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = enum_unwrap!(slf, ValueObj::Dict);
let slf = slf
.into_iter()
@ -368,12 +311,14 @@ pub(crate) fn dict_keys(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<V
.keys()
.fold(Type::Never, |union, t| ctx.union(&union, t));
let keys = poly(DICT_KEYS, vec![ty_tp(union)]);
Ok(ValueObj::builtin_class(keys))
Ok(ValueObj::builtin_class(keys).into())
}
/// `{Str: Int, Int: Float}.values() == DictValues(Int or Float)`
pub(crate) fn dict_values(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = args.remove_left_or_key("Self").unwrap();
pub(crate) fn dict_values(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = enum_unwrap!(slf, ValueObj::Dict);
let slf = slf
.into_iter()
@ -388,12 +333,14 @@ pub(crate) fn dict_values(mut args: ValueArgs, ctx: &Context) -> EvalValueResult
.values()
.fold(Type::Never, |union, t| ctx.union(&union, t));
let values = poly(DICT_VALUES, vec![ty_tp(union)]);
Ok(ValueObj::builtin_class(values))
Ok(ValueObj::builtin_class(values).into())
}
/// `{Str: Int, Int: Float}.items() == DictItems((Str, Int) or (Int, Float))`
pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = args.remove_left_or_key("Self").unwrap();
pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = enum_unwrap!(slf, ValueObj::Dict);
let slf = slf
.into_iter()
@ -408,12 +355,14 @@ pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
ctx.union(&union, &tuple_t(vec![k.clone(), v.clone()]))
});
let items = poly(DICT_ITEMS, vec![ty_tp(union)]);
Ok(ValueObj::builtin_class(items))
Ok(ValueObj::builtin_class(items).into())
}
/// `[Int, Str].union() == Int or Str`
pub(crate) fn array_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = args.remove_left_or_key("Self").unwrap();
pub(crate) fn array_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = enum_unwrap!(slf, ValueObj::Array);
let slf = slf
.iter()
@ -422,22 +371,74 @@ pub(crate) fn array_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult
let union = slf
.iter()
.fold(Type::Never, |union, t| ctx.union(&union, t));
Ok(ValueObj::builtin_type(union))
Ok(ValueObj::builtin_type(union).into())
}
pub(crate) fn __range_getitem__(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<ValueObj> {
fn _arr_shape(arr: ValueObj, ctx: &Context) -> Result<Vec<TyParam>, String> {
let mut shape = vec![];
let mut arr = arr;
loop {
match arr {
ValueObj::Array(a) => {
shape.push(ValueObj::from(a.len()).into());
match a.get(0) {
Some(arr_ @ (ValueObj::Array(_) | ValueObj::Type(_))) => {
arr = arr_.clone();
}
_ => {
break;
}
}
}
ValueObj::Type(ref t) if &t.typ().qual_name()[..] == "Array" => {
let mut tps = t.typ().typarams();
let elem = ctx.convert_tp_into_type(tps.remove(0)).unwrap();
let len = tps.remove(0);
shape.push(len);
arr = ValueObj::builtin_type(elem);
}
_ => {
break;
}
}
}
Ok(shape)
}
/// ```erg
/// Array(Int, 2).shape() == [2,]
/// Array(Array(Int, 2), N).shape() == [N, 2]
/// [1, 2].shape() == [2,]
/// [[1, 2], [3, 4], [5, 6]].shape() == [3, 2]
/// ```
pub(crate) fn array_shape(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let arr = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let res = _arr_shape(arr, ctx).unwrap();
let arr = TyParam::Array(res);
Ok(arr)
}
pub(crate) fn __range_getitem__(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<TyParam> {
let (_name, fields) = enum_unwrap!(
args.remove_left_or_key("Self").unwrap(),
args.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?,
ValueObj::DataClass { name, fields }
);
let index = enum_unwrap!(args.remove_left_or_key("Index").unwrap(), ValueObj::Nat);
let start = fields.get("start").unwrap();
let index = args
.remove_left_or_key("Index")
.ok_or_else(|| not_passed("Index"))?;
let index = enum_unwrap!(index, ValueObj::Nat);
let start = fields
.get("start")
.ok_or_else(|| no_key(&fields, "start"))?;
let start = *enum_unwrap!(start, ValueObj::Nat);
let end = fields.get("end").unwrap();
let end = fields.get("end").ok_or_else(|| no_key(&fields, "end"))?;
let end = *enum_unwrap!(end, ValueObj::Nat);
// FIXME <= if inclusive
if start + index < end {
Ok(ValueObj::Nat(start + index))
Ok(ValueObj::Nat(start + index).into())
} else {
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],

View file

@ -96,6 +96,7 @@ const COLLECTION: &str = "Collection";
const INDEXABLE: &str = "Indexable";
const MAPPING: &str = "Mapping";
const MUTABLE_MAPPING: &str = "Mapping!";
const SHAPE: &str = "Shape";
const EQ: &str = "Eq";
const ORD: &str = "Ord";
const TO_STR: &str = "to_str";
@ -140,7 +141,7 @@ const COMPLEX: &str = "Complex";
const INT: &str = "Int";
const MUT_INT: &str = "Int!";
const RATIO: &str = "Ratio";
const MUT_RATIO: &str = "Raltio!";
const MUT_RATIO: &str = "Ratio!";
const FUNC_ABS: &str = "abs";
const FUNC_SUCC: &str = "succ";
const FUNC_PRED: &str = "pred";
@ -244,7 +245,8 @@ const ISSUBSET: &str = "issubset";
const ISSUPERSET: &str = "issuperset";
const SYMMETRIC_DIFFERENCE: &str = "symmetric_difference";
const MEMORYVIEW: &str = "MemoryView";
const UNION_FUNC: &str = "union";
const FUNC_UNION: &str = "union";
const FUNC_SHAPE: &str = "shape";
const FUNC_INC: &str = "inc";
const PROC_INC: &str = "inc!";
const FUNC_DEC: &str = "dec";
@ -446,6 +448,7 @@ const TY_TS: &str = "Ts";
const TY_I: &str = "I";
const TY_P: &str = "P";
const TY_R: &str = "R";
const TY_S: &str = "S";
const TY_U: &str = "U";
const TY_L: &str = "L";
const TY_N: &str = "N";
@ -823,7 +826,7 @@ impl Context {
.take(lack)
.map(|pt| TyParam::erased(pt.typ().clone()));
let params = passed.into_iter().chain(erased).collect::<Vec<_>>();
Ok(ValueObj::builtin_type(poly(data.qual_name, params)))
Ok(TyParam::t(poly(data.qual_name, params)))
};
let subr = ConstSubr::Gen(GenConstSubr::new(
t.local_name(),

View file

@ -346,10 +346,14 @@ impl Context {
Visibility::BUILTIN_PUBLIC,
Some(FUNDAMENTAL_EXIT),
);
/* Shape */
let S = mono_q_tp(TY_S, instanceof(unknown_len_array_t(Nat)));
let params = vec![PS::named_nd("S", unknown_len_array_t(Nat))];
let shape = Self::builtin_poly_trait(SHAPE, params.clone(), 2);
/* Num */
let R = mono_q(TY_R, instanceof(Type));
let params = vec![PS::t(TY_R, false, WithDefault)];
let ty_params = vec![ty_tp(R.clone())];
/* Num */
/* Add */
let mut add = Self::builtin_poly_trait(ADD, params.clone(), 2);
// Covariant with `R` (independent of the type of __add__)
@ -541,6 +545,7 @@ impl Context {
Const,
None,
);
self.register_builtin_type(poly(SHAPE, vec![S]), shape, vis.clone(), Const, None);
self.register_builtin_type(poly(ADD, ty_params.clone()), add, vis.clone(), Const, None);
self.register_builtin_type(poly(SUB, ty_params.clone()), sub, vis.clone(), Const, None);
self.register_builtin_type(poly(MUL, ty_params.clone()), mul, vis.clone(), Const, None);

View file

@ -192,6 +192,7 @@ impl Context {
}
Ok(ctxs)
}
hir::Expr::TypeAsc(tasc) => self.get_singular_ctxs_by_hir_expr(&tasc.expr, namespace),
// TODO: change error
_ => Err(TyCheckError::no_var_error(
self.cfg.input.clone(),
@ -231,11 +232,21 @@ impl Context {
})
}
#[allow(unused)]
pub(crate) fn get_mut_singular_ctxs_by_ident(
&mut self,
ident: &ast::Identifier,
namespace: &Str,
) -> SingleTyCheckResult<&mut Context> {
self.get_mut_singular_ctxs_and_t_by_ident(ident, namespace)
.map(|(_, ctx)| ctx)
}
pub(crate) fn get_mut_singular_ctxs_and_t_by_ident(
&mut self,
ident: &ast::Identifier,
namespace: &Str,
) -> SingleTyCheckResult<(&Type, &mut Context)> {
let err = TyCheckError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
@ -244,9 +255,7 @@ impl Context {
ident.inspect(),
self.get_similar_name(ident.inspect()),
);
self.rec_get_mut_type(ident.inspect())
.map(|(_, ctx)| ctx)
.ok_or(err)
self.rec_get_mut_type(ident.inspect()).ok_or(err)
}
pub(crate) fn get_singular_ctxs(
@ -267,6 +276,44 @@ impl Context {
}
Ok(ctxs)
}
ast::Expr::Accessor(ast::Accessor::TypeApp(tapp)) => {
self.get_singular_ctxs(&tapp.obj, namespace)
}
ast::Expr::Call(call) => self.get_singular_ctxs(&call.obj, namespace),
ast::Expr::TypeAscription(tasc) => self.get_singular_ctxs(&tasc.expr, namespace),
_ => Err(TyCheckError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
obj.loc(),
self.caused_by(),
&obj.to_string(),
None,
)),
}
}
pub(crate) fn get_mut_singular_ctx_and_t(
&mut self,
obj: &ast::Expr,
namespace: &Str,
) -> SingleTyCheckResult<(&Type, &mut Context)> {
match obj {
ast::Expr::Accessor(ast::Accessor::Ident(ident)) => {
self.get_mut_singular_ctxs_and_t_by_ident(ident, namespace)
}
ast::Expr::Accessor(ast::Accessor::Attr(attr)) => {
// REVIEW: 両方singularとは限らない?
let ctx = self.get_mut_singular_ctx(&attr.obj, namespace)?;
let attr = ast::Expr::Accessor(ast::Accessor::Ident(attr.ident.clone()));
ctx.get_mut_singular_ctx_and_t(&attr, namespace)
}
ast::Expr::Accessor(ast::Accessor::TypeApp(tapp)) => {
self.get_mut_singular_ctx_and_t(&tapp.obj, namespace)
}
ast::Expr::Call(call) => self.get_mut_singular_ctx_and_t(&call.obj, namespace),
ast::Expr::TypeAscription(tasc) => {
self.get_mut_singular_ctx_and_t(&tasc.expr, namespace)
}
_ => Err(TyCheckError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
@ -283,25 +330,8 @@ impl Context {
obj: &ast::Expr,
namespace: &Str,
) -> SingleTyCheckResult<&mut Context> {
match obj {
ast::Expr::Accessor(ast::Accessor::Ident(ident)) => {
self.get_mut_singular_ctxs_by_ident(ident, namespace)
}
ast::Expr::Accessor(ast::Accessor::Attr(attr)) => {
// REVIEW: 両方singularとは限らない?
let ctx = self.get_mut_singular_ctx(&attr.obj, namespace)?;
let attr = ast::Expr::Accessor(ast::Accessor::Ident(attr.ident.clone()));
ctx.get_mut_singular_ctx(&attr, namespace)
}
_ => Err(TyCheckError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
obj.loc(),
self.caused_by(),
&obj.to_string(),
None,
)),
}
self.get_mut_singular_ctx_and_t(obj, namespace)
.map(|(_, ctx)| ctx)
}
fn get_match_call_t(
@ -1356,7 +1386,7 @@ impl Context {
let non_default_params = pos_args.iter().map(|a| anon(a.expr.t())).collect();
let subr_t = subr_t(kind, non_default_params, None, vec![], ret_t);
self.occur(&subr_t, instance, obj)?;
instance.link(&subr_t);
instance.destructive_link(&subr_t);
Ok(SubstituteResult::Ok)
}
}
@ -1917,7 +1947,10 @@ impl Context {
log!(info "Substituted:\ninstance: {instance}");
let res = self
.eval_t_params(instance, self.level, obj)
.map_err(|(t, errs)| (Some(VarInfo { t, ..found.clone() }), errs))?;
.map_err(|(t, errs)| {
log!(err "failed to eval: {t}");
(Some(VarInfo { t, ..found.clone() }), errs)
})?;
debug_assert!(res.has_no_qvar(), "{res} has qvar");
log!(info "Params evaluated:\nres: {res}\n");
let res = VarInfo { t: res, ..found };
@ -2395,7 +2428,7 @@ impl Context {
attr_name,
args,
} => {
if let Ok(typ) = self.eval_proj_call(
if let Ok(typ) = self.eval_proj_call_t(
*lhs.clone(),
attr_name.clone(),
args.clone(),
@ -3042,9 +3075,10 @@ impl Context {
/// Int.meta_type() == ClassType (<: Type)
/// Show.meta_type() == TraitType (<: Type)
/// [Int; 3].meta_type() == [ClassType; 3] (<: Type)
/// Indexable(T).meta_type() == TraitType (<: Type)
pub fn meta_type(&self, typ: &Type) -> Type {
match typ {
Type::Poly { name, params } => poly(
Type::Poly { name, params } if &name[..] == "Array" || &name[..] == "Set" => poly(
name.clone(),
params
.iter()

View file

@ -12,6 +12,8 @@ use erg_common::Str;
use erg_parser::ast::VarName;
use crate::ty::constructors::*;
use crate::ty::free::FreeTyParam;
use crate::ty::free::GENERIC_LEVEL;
use crate::ty::free::{Constraint, HasLevel};
use crate::ty::typaram::{TyParam, TyParamLambda};
use crate::ty::ValueObj;
@ -92,6 +94,12 @@ impl TyVarCache {
}
}
pub fn remove(&mut self, name: &str) {
self.tyvar_instances.remove(name);
self.typaram_instances.remove(name);
self.var_infos.remove(name);
}
/// Warn when a type does not need to be a type variable, such as `|T| T -> Int` (it should be `Obj -> Int`).
///
/// TODO: This warning is currently disabled because it raises a false warning in cases like `|T|(x: T) -> (y: T) -> (x, y)`.
@ -161,7 +169,7 @@ impl TyVarCache {
if let Ok(inst) = <&Type>::try_from(inst) {
self.update_tyvar(inst, tv, ctx);
} else if let TyParam::FreeVar(_fv) = inst {
inst.link(&TyParam::t(tv.clone()));
inst.destructive_link(&TyParam::t(tv.clone()));
} else {
unreachable!()
}
@ -180,7 +188,7 @@ impl TyVarCache {
if let Ok(inst) = <&Type>::try_from(inst) {
self.update_tyvar(inst, tv, ctx);
} else if let TyParam::FreeVar(_fv) = inst {
inst.link(&TyParam::t(tv.clone()));
inst.destructive_link(&TyParam::t(tv.clone()));
} else {
unreachable!()
}
@ -194,9 +202,9 @@ impl TyVarCache {
// T<inst> is uninitialized
// T<inst>.link(T<tv>);
// T <: Eq(T <: Eq(T <: ...))
let free_inst = enum_unwrap!(inst, Type::FreeVar);
let Type::FreeVar(free_inst) = inst else { todo!("{inst}") };
if free_inst.constraint_is_uninited() {
inst.link(tv);
inst.destructive_link(tv);
} else {
// inst: ?T(<: Int) => old_sub: Never, old_sup: Int
// tv: ?T(:> Nat) => new_sub: Nat, new_sup: Obj
@ -205,7 +213,7 @@ impl TyVarCache {
// tv: ?T(:> Nat)
// => ?T(:> Nat or Str)
let (old_sub, old_sup) = free_inst.get_subsup().unwrap();
let tv = enum_unwrap!(tv, Type::FreeVar);
let Type::FreeVar(tv) = tv else { todo!("{tv}") };
let (new_sub, new_sup) = tv.get_subsup().unwrap();
let new_constraint = Constraint::new_sandwiched(
ctx.union(&old_sub, &new_sub),
@ -255,12 +263,22 @@ impl TyVarCache {
}
fn update_typaram(&self, inst: &TyParam, tp: &TyParam, ctx: &Context) {
let free_inst = enum_unwrap!(inst, TyParam::FreeVar);
if inst.level() == Some(GENERIC_LEVEL) {
log!(err "{inst} is fixed");
return;
}
let Ok(free_inst) = <&FreeTyParam>::try_from(inst) else {
if let (Ok(inst), Ok(t)) = (<&Type>::try_from(inst), <&Type>::try_from(tp)) {
return self.update_tyvar(inst, t, ctx);
} else {
todo!("{inst}");
}
};
if free_inst.constraint_is_uninited() {
inst.link(tp);
inst.destructive_link(tp);
} else {
let old_type = free_inst.get_type().unwrap();
let tv = enum_unwrap!(tp, TyParam::FreeVar);
let Ok(tv) = <&FreeTyParam>::try_from(tp) else { todo!("{tp}") };
let new_type = tv.get_type().unwrap();
let new_constraint = Constraint::new_type_of(ctx.intersection(&old_type, &new_type));
free_inst.update_constraint(new_constraint, true);
@ -437,6 +455,18 @@ impl Context {
}
Ok(TyParam::app(name, new_args))
}
TyParam::ProjCall { obj, attr, args } => {
let obj = self.instantiate_tp(*obj, tmp_tv_cache, loc)?;
let mut new_args = Vec::with_capacity(args.len());
for arg in args {
new_args.push(self.instantiate_tp(arg, tmp_tv_cache, loc)?);
}
Ok(TyParam::proj_call(obj, attr, new_args))
}
TyParam::Proj { obj, attr } => {
let obj = self.instantiate_tp(*obj, tmp_tv_cache, loc)?;
Ok(TyParam::proj(obj, attr))
}
TyParam::Type(t) => {
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
Ok(TyParam::t(t))

View file

@ -836,13 +836,21 @@ impl Context {
self.instantiate_acc(acc, erased_idx, tmp_tv_cache, not_found_is_qvar)
}
ast::ConstExpr::App(app) => {
let ast::ConstAccessor::Local(ident) = &app.acc else {
return type_feature_error!(self, app.loc(), "instantiating const callee");
};
let &ctx = self
.get_singular_ctxs_by_ident(ident, self)?
.first()
.unwrap_or(&self);
let obj = self.instantiate_const_expr(
&app.obj,
erased_idx,
tmp_tv_cache,
not_found_is_qvar,
)?;
let ctx = self
.get_singular_ctxs(&app.obj.clone().downgrade(), self)
.ok()
.and_then(|ctxs| ctxs.first().copied())
.or_else(|| {
let typ = self.get_tp_t(&obj).ok()?;
self.get_nominal_type_ctx(&typ).map(|(_, ctx)| ctx)
})
.unwrap_or(self);
let mut args = vec![];
for (i, arg) in app.args.pos_args().enumerate() {
let arg_t = self.instantiate_const_expr(
@ -853,7 +861,16 @@ impl Context {
)?;
args.push(arg_t);
}
Ok(TyParam::app(ident.inspect().clone(), args))
if let Some(attr_name) = app.attr_name.as_ref() {
Ok(obj.proj_call(attr_name.inspect().clone(), args))
} else if ctx.kind.is_type() && !ctx.params.is_empty() {
Ok(TyParam::t(poly(ctx.name.clone(), args)))
} else {
let ast::ConstExpr::Accessor(ast::ConstAccessor::Local(ident)) = app.obj.as_ref() else {
return type_feature_error!(self, app.loc(), "instantiating const callee");
};
Ok(TyParam::app(ident.inspect().clone(), args))
}
}
ast::ConstExpr::Array(ConstArray::Normal(array)) => {
let mut tp_arr = vec![];
@ -1236,7 +1253,7 @@ impl Context {
fn instantiate_pred(
&self,
expr: &ast::ConstExpr,
_tmp_tv_cache: &mut TyVarCache,
tmp_tv_cache: &mut TyVarCache,
) -> TyCheckResult<Predicate> {
match expr {
ast::ConstExpr::Lit(lit) => {
@ -1244,11 +1261,12 @@ impl Context {
Ok(Predicate::Value(value))
}
ast::ConstExpr::Accessor(ast::ConstAccessor::Local(local)) => {
self.inc_ref_local(local, self, tmp_tv_cache);
Ok(Predicate::Const(local.inspect().clone()))
}
ast::ConstExpr::BinOp(bin) => {
let lhs = self.instantiate_pred(&bin.lhs, _tmp_tv_cache)?;
let rhs = self.instantiate_pred(&bin.rhs, _tmp_tv_cache)?;
let lhs = self.instantiate_pred(&bin.lhs, tmp_tv_cache)?;
let rhs = self.instantiate_pred(&bin.rhs, tmp_tv_cache)?;
match bin.op.kind {
TokenKind::DblEq
| TokenKind::NotEq
@ -1295,7 +1313,7 @@ impl Context {
}
}
ast::ConstExpr::UnaryOp(unop) => {
let pred = self.instantiate_pred(&unop.expr, _tmp_tv_cache)?;
let pred = self.instantiate_pred(&unop.expr, tmp_tv_cache)?;
match unop.op.kind {
TokenKind::PreBitNot => Ok(!pred),
_ => type_feature_error!(
@ -1461,7 +1479,7 @@ impl Context {
)?);
}
let ty = new_set.iter().fold(Type::Never, |t, tp| {
self.union(&t, &self.get_tp_t(tp).unwrap())
self.union(&t, &self.get_tp_t(tp).unwrap().derefine())
});
Ok(tp_enum(ty, new_set))
}
@ -1564,7 +1582,20 @@ impl Context {
mode,
not_found_is_qvar,
)?;
let pred = self.instantiate_pred(&refine.pred, tmp_tv_cache)?;
let name = VarName::new(refine.var.clone());
let tp = TyParam::named_free_var(
refine.var.inspect().clone(),
self.level,
Constraint::new_type_of(t.clone()),
);
tmp_tv_cache.push_or_init_typaram(&name, &tp, self);
let pred = self
.instantiate_pred(&refine.pred, tmp_tv_cache)
.map_err(|err| {
tmp_tv_cache.remove(name.inspect());
err
})?;
tmp_tv_cache.remove(name.inspect());
let refine =
Type::Refinement(RefinementType::new(refine.var.inspect().clone(), t, pred));
Ok(refine)

View file

@ -20,6 +20,7 @@ use std::option::Option; // conflicting to Type::Option
use std::path::{Path, PathBuf};
use erg_common::config::ErgConfig;
use erg_common::consts::DEBUG_MODE;
use erg_common::consts::PYTHON_MODE;
use erg_common::dict::Dict;
use erg_common::error::Location;
@ -139,6 +140,34 @@ impl ClassDefType {
pub const fn impl_trait(class: Type, impl_trait: Type) -> Self {
ClassDefType::ImplTrait { class, impl_trait }
}
pub fn get_class(&self) -> &Type {
match self {
ClassDefType::Simple(class) => class,
ClassDefType::ImplTrait { class, .. } => class,
}
}
pub fn get_impl_trait(&self) -> Option<&Type> {
match self {
ClassDefType::Simple(_) => None,
ClassDefType::ImplTrait { impl_trait, .. } => Some(impl_trait),
}
}
pub fn is_class_of(&self, t: &Type) -> bool {
match self {
ClassDefType::Simple(class) => class == t,
ClassDefType::ImplTrait { class, .. } => class == t,
}
}
pub fn is_impl_of(&self, trait_: &Type) -> bool {
match self {
ClassDefType::ImplTrait { impl_trait, .. } => impl_trait == trait_,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -1010,7 +1039,7 @@ impl Context {
if let Some(outer) = self.get_outer() {
outer.path()
} else if self.kind == ContextKind::Module {
self.name.clone()
self.name.replace(".__init__", "").into()
} else {
BUILTINS.clone()
}
@ -1109,6 +1138,7 @@ impl Context {
}
pub fn pop(&mut self) -> Context {
self.check_types();
if let Some(parent) = self.outer.as_mut() {
let parent = mem::take(parent);
let ctx = mem::take(self);
@ -1122,6 +1152,7 @@ impl Context {
/// unlike `pop`, `outer` must be `None`.
pub fn pop_mod(&mut self) -> Option<Context> {
self.check_types();
if self.outer.is_some() {
log!(err "not in the top-level context");
if self.kind.is_module() {
@ -1236,6 +1267,32 @@ impl Context {
.last()
.and_then(|caller| ControlKind::try_from(&caller[..]).ok())
}
pub(crate) fn check_types(&self) {
if DEBUG_MODE {
for (_, (t, ctx)) in self.poly_types.iter() {
if t.has_undoable_linked_var() {
panic!("{t} has undoable linked vars");
}
ctx.check_types();
}
for (typ, methods) in self.methods_list.iter() {
if typ.get_class().has_undoable_linked_var() {
panic!("{typ} has undoable linked vars");
}
if typ
.get_impl_trait()
.is_some_and(|t| t.has_undoable_linked_var())
{
panic!("{typ} has undoable linked vars");
}
methods.check_types();
}
if let Some(outer) = self.get_outer() {
outer.check_types();
}
}
}
}
#[derive(Debug, Clone, Default)]

View file

@ -978,7 +978,7 @@ impl Context {
return res;
};
if let Some(_fv) = vi.t.as_free() {
vi.t.link(&typ);
vi.t.destructive_link(&typ);
}
res
}
@ -1308,7 +1308,10 @@ impl Context {
.typ()
.typarams()
.into_iter()
.map(|tp| ParamSpec::t_nd(tp.qual_name().unwrap_or(Str::ever("_"))))
.map(|tp| {
let name = tp.qual_name().unwrap_or(Str::ever("_"));
ParamSpec::named_nd(name, self.get_tp_t(&tp).unwrap_or(Type::Obj))
})
.collect();
let mut ctx = Self::poly_class(
gen.typ().qual_name(),
@ -2376,7 +2379,6 @@ impl Context {
tmp_tv_cache: &TyVarCache,
) -> bool {
for arg in poly.args.pos_args() {
log!(err "{}", arg.expr);
self.inc_ref_expr(&arg.expr.clone().downgrade(), namespace, tmp_tv_cache);
}
if let Some(arg) = poly.args.var_args.as_ref() {
@ -2388,7 +2390,7 @@ impl Context {
self.inc_ref_acc(&poly.acc.clone().downgrade(), namespace, tmp_tv_cache)
}
fn inc_ref_local(
pub(crate) fn inc_ref_local(
&self,
local: &ConstIdentifier,
namespace: &Context,

View file

@ -8,6 +8,7 @@ use erg_common::Str;
#[allow(unused_imports)]
use erg_common::{fmt_vec, fn_name, log};
use crate::context::eval::Substituter;
use crate::ty::constructors::*;
use crate::ty::free::{Constraint, FreeKind, HasLevel, GENERIC_LEVEL};
use crate::ty::typaram::{OpKind, TyParam};
@ -27,7 +28,6 @@ use super::initialize::const_func::sub_tpdict_get;
pub struct Unifier<'c, 'l, L: Locational> {
ctx: &'c Context,
loc: &'l L,
#[allow(unused)]
undoable: bool,
param_name: Option<Str>,
}
@ -288,10 +288,10 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
{
if sub_fv.level().unwrap() > sup_fv.level().unwrap() {
if !sub_fv.is_generalized() {
maybe_sub.link(maybe_sup);
maybe_sub.link(maybe_sup, self.undoable);
}
} else if !sup_fv.is_generalized() {
maybe_sup.link(maybe_sub);
maybe_sup.link(maybe_sub, self.undoable);
}
Ok(())
}
@ -314,10 +314,10 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
.is_sub_constraint_of(&sub_fv.constraint().unwrap(), &new_constraint)
|| sub_fv.constraint().unwrap().get_type() == Some(&Type)
{
sub_fv.update_constraint(new_constraint, false);
maybe_sub.update_constraint(new_constraint, self.undoable, false);
}
} else {
maybe_sub.link(sup_tp);
maybe_sub.link(sup_tp, self.undoable);
}
Ok(())
} else if allow_divergence
@ -325,7 +325,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
|| self.ctx.eq_tp(sup_tp, &TyParam::value(NegInf)))
&& self.ctx.subtype_of(&fv_t, &mono("Num"))
{
maybe_sub.link(sup_tp);
maybe_sub.link(sup_tp, self.undoable);
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::unreachable(
@ -354,10 +354,10 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
.is_sub_constraint_of(&sup_fv.constraint().unwrap(), &new_constraint)
|| sup_fv.constraint().unwrap().get_type() == Some(&Type)
{
sup_fv.update_constraint(new_constraint, false);
maybe_sup.update_constraint(new_constraint, self.undoable, false);
}
} else {
maybe_sup.link(sub_tp);
maybe_sup.link(sub_tp, self.undoable);
}
Ok(())
} else if allow_divergence
@ -365,7 +365,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
|| self.ctx.eq_tp(sub_tp, &TyParam::value(NegInf)))
&& self.ctx.subtype_of(&fv_t, &mono("Num"))
{
maybe_sup.link(sub_tp);
maybe_sup.link(sub_tp, self.undoable);
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::unreachable(
@ -391,9 +391,6 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
self.sub_unify_tp(lhs, lhs2, _variance, allow_divergence)?;
self.sub_unify_tp(rhs, rhs2, _variance, allow_divergence)
}
(TyParam::Lambda(_l), TyParam::Lambda(_r)) => {
todo!("{_l}/{_r}")
}
(sub, TyParam::Erased(t)) => {
let sub_t = self.ctx.get_tp_t(sub)?;
if self.ctx.subtype_of(&sub_t, t) {
@ -477,6 +474,24 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
}
Ok(())
}
(
TyParam::ProjCall { obj, attr, args },
TyParam::ProjCall {
obj: o2,
attr: a2,
args: args2,
},
) => {
if attr == a2 {
self.sub_unify_tp(obj, o2, _variance, allow_divergence)?;
for (l, r) in args.iter().zip(args2.iter()) {
self.sub_unify_tp(l, r, _variance, allow_divergence)?;
}
Ok(())
} else {
todo!()
}
}
(l, r) => {
log!(err "{l} / {r}");
type_feature_error!(self.ctx, self.loc.loc(), &format!("unifying {l} and {r}"))
@ -575,12 +590,12 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
fn coerce_greater_than(&self, target: &TyParam, value: &TyParam) -> TyCheckResult<()> {
match target {
TyParam::FreeVar(fv) => {
TyParam::FreeVar(_fv) => {
if let Ok(evaled) = self.ctx.eval_tp(value.clone()) {
let pred = Predicate::ge(FRESH_GEN.fresh_varname(), evaled);
let new_type = self.ctx.type_from_pred(pred);
let new_constr = Constraint::new_type_of(Type::from(new_type));
fv.update_constraint(new_constr, false);
target.update_constraint(new_constr, self.undoable, false);
}
Ok(())
}
@ -620,10 +635,12 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
// In this case, there is no new information to be gained
// この場合、特に新しく得られる情報はない
if maybe_sub == &Type::Never || maybe_sup == &Type::Obj || maybe_sup == maybe_sub {
log!(info "no-op:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}");
return Ok(());
}
// API definition was failed and inspection is useless after this
if maybe_sub == &Type::Failure || maybe_sup == &Type::Failure {
log!(info "no-op:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}");
return Ok(());
}
self.occur(maybe_sub, maybe_sup).map_err(|err| {
@ -646,6 +663,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
self.ctx.get_simple_type_mismatch_hint(maybe_sup, maybe_sub),
)));
} else if maybe_sub.has_no_unbound_var() && maybe_sup.has_no_unbound_var() {
log!(info "no-op:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}");
return Ok(());
}
match (maybe_sub, maybe_sup) {
@ -666,6 +684,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
if sub_fv.constraint_is_sandwiched() && sup_fv.constraint_is_sandwiched() =>
{
if sub_fv.is_generalized() || sup_fv.is_generalized() {
log!(info "generalized:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}");
return Ok(());
}
let (lsub, lsup) = sub_fv.get_subsup().unwrap();
@ -733,21 +752,21 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
.cmp(&sup_fv.level().unwrap_or(GENERIC_LEVEL))
{
std::cmp::Ordering::Less => {
maybe_sub.link(&union);
maybe_sup.link(maybe_sub);
maybe_sub.link(&union, self.undoable);
maybe_sup.link(maybe_sub, self.undoable);
}
std::cmp::Ordering::Greater => {
maybe_sup.link(&union);
maybe_sub.link(maybe_sup);
maybe_sup.link(&union, self.undoable);
maybe_sub.link(maybe_sup, self.undoable);
}
std::cmp::Ordering::Equal => {
// choose named one
if sup_fv.is_named_unbound() {
maybe_sup.link(&union);
maybe_sub.link(maybe_sup);
maybe_sup.link(&union, self.undoable);
maybe_sub.link(maybe_sup, self.undoable);
} else {
maybe_sub.link(&union);
maybe_sup.link(maybe_sub);
maybe_sub.link(&union, self.undoable);
maybe_sup.link(maybe_sub, self.undoable);
}
}
}
@ -759,21 +778,21 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
.cmp(&sup_fv.level().unwrap_or(GENERIC_LEVEL))
{
std::cmp::Ordering::Less => {
sub_fv.update_constraint(new_constraint, false);
maybe_sup.link(maybe_sub);
maybe_sub.update_constraint(new_constraint, self.undoable, false);
maybe_sup.link(maybe_sub, self.undoable);
}
std::cmp::Ordering::Greater => {
sup_fv.update_constraint(new_constraint, false);
maybe_sub.link(maybe_sup);
maybe_sup.update_constraint(new_constraint, self.undoable, false);
maybe_sub.link(maybe_sup, self.undoable);
}
std::cmp::Ordering::Equal => {
// choose named one
if sup_fv.is_named_unbound() {
sup_fv.update_constraint(new_constraint, false);
maybe_sub.link(maybe_sup);
maybe_sup.update_constraint(new_constraint, self.undoable, false);
maybe_sub.link(maybe_sup, self.undoable);
} else {
sub_fv.update_constraint(new_constraint, false);
maybe_sup.link(maybe_sub);
maybe_sup.update_constraint(new_constraint, self.undoable, false);
maybe_sup.link(maybe_sub, self.undoable);
}
}
}
@ -848,10 +867,10 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
// self.sub_unify(&intersec, &lsup, loc, param_name)?;
// self.sub_unify(&lsub, &union, loc, param_name)?;
if union == intersec {
maybe_sup.link(&union);
maybe_sup.link(&union, self.undoable);
} else {
let new_constraint = Constraint::new_sandwiched(union, intersec);
sup_fv.update_constraint(new_constraint, false);
maybe_sup.update_constraint(new_constraint, self.undoable, false);
}
}
// (Int or ?T) <: (?U or Int)
@ -934,17 +953,17 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
}
}
if sup.contains_union(&new_sub) {
maybe_sup.link(&new_sub); // Bool <: ?T <: Bool or Y ==> ?T == Bool
maybe_sup.link(&new_sub, self.undoable); // Bool <: ?T <: Bool or Y ==> ?T == Bool
} else {
let constr = Constraint::new_sandwiched(new_sub, mem::take(&mut sup));
sup_fv.update_constraint(constr, true);
maybe_sup.update_constraint(constr, self.undoable, true);
}
}
// sub_unify(Nat, ?T(: Type)): (/* ?T(:> Nat) */)
else if let Some(ty) = sup_fv.get_type() {
if self.ctx.supertype_of(&Type, &ty) {
let constr = Constraint::new_supertype_of(maybe_sub.clone());
sup_fv.update_constraint(constr, true);
maybe_sup.update_constraint(constr, self.undoable, true);
} else {
todo!("{maybe_sub} <: {maybe_sup}")
}
@ -1001,17 +1020,17 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
&& !new_sup.is_unbound_var()
&& !sub.is_unbound_var()
{
maybe_sub.link(&sub);
maybe_sub.link(&sub, self.undoable);
} else {
let constr = Constraint::new_sandwiched(sub, new_sup);
sub_fv.update_constraint(constr, true);
maybe_sub.update_constraint(constr, self.undoable, true);
}
}
// sub_unify(?T(: Type), Int): (?T(<: Int))
else if let Some(ty) = sub_fv.get_type() {
if self.ctx.supertype_of(&Type, &ty) {
let constr = Constraint::new_subtype_of(maybe_sup.clone());
sub_fv.update_constraint(constr, true);
maybe_sub.update_constraint(constr, self.undoable, true);
} else {
todo!("{maybe_sub} <: {maybe_sup}")
}
@ -1314,13 +1333,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
// maybe_sub: Zip(Int, Str)
// sub_def_t: Zip(T, U) ==> Zip(Int, Str)
// super_traits: [Iterable((T, U)), ...] ==> [Iterable((Int, Str)), ...]
// TODO: user-defined types substitution
self.ctx
.substitute_typarams(sub_def_t, maybe_sub)
.map_err(|errs| {
Context::undo_substitute_typarams(sub_def_t);
errs
})?;
let _substituter = Substituter::substitute_typarams(self.ctx, sub_def_t, maybe_sub)?;
let sups = if self.ctx.is_class(maybe_sup) {
sub_ctx.super_classes.iter()
} else {
@ -1339,10 +1352,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
}
}
if let Some(sup_ty) = min_compatible {
let sub_instance = self.ctx.instantiate_def_type(sup_ty).map_err(|errs| {
Context::undo_substitute_typarams(sub_def_t);
errs
})?;
let sub_instance = self.ctx.instantiate_def_type(sup_ty)?;
let variances = self
.ctx
.get_nominal_type_ctx(&sub_instance)
@ -1354,16 +1364,12 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
.zip(sup_params.iter())
.zip(variances)
{
self.sub_unify_tp(l_maybe_sub, r_maybe_sup, variance, false)
.map_err(|errs| {
Context::undo_substitute_typarams(sub_def_t);
errs
})?;
self.sub_unify_tp(l_maybe_sub, r_maybe_sup, variance, false)?;
}
Context::undo_substitute_typarams(sub_def_t);
return Ok(());
} else {
log!(err "no compatible supertype found: {maybe_sub} <: {maybe_sup}");
}
Context::undo_substitute_typarams(sub_def_t);
}
Err(TyCheckErrors::from(TyCheckError::unification_error(
self.ctx.cfg.input.clone(),
@ -1386,6 +1392,18 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
/// unify(Eq, Int) == None
/// ```
fn unify(&self, lhs: &Type, rhs: &Type) -> Option<Type> {
#[allow(clippy::single_match)]
match (lhs, rhs) {
(Type::FreeVar(fv), _) if fv.is_linked() => return self.unify(&fv.crack(), rhs),
(_, Type::FreeVar(fv)) if fv.is_linked() => return self.unify(lhs, &fv.crack()),
(Type::Refinement(lhs), Type::Refinement(rhs)) => {
if let Some(_union) = self.unify(&lhs.t, &rhs.t) {
return Some(self.ctx.union_refinement(lhs, rhs).into());
} else {
}
}
_ => {}
}
let l_sups = self.ctx.get_super_classes(lhs)?;
let r_sups = self.ctx.get_super_classes(rhs)?;
for l_sup in l_sups {
@ -1438,4 +1456,20 @@ impl Context {
let unifier = Unifier::new(self, loc, false, param_name.cloned());
unifier.sub_unify(maybe_sub, maybe_sup)
}
pub(crate) fn undoable_sub_unify(
&self,
maybe_sub: &Type,
maybe_sup: &Type,
loc: &impl Locational,
param_name: Option<&Str>,
) -> TyCheckResult<()> {
let unifier = Unifier::new(self, loc, true, param_name.cloned());
unifier.sub_unify(maybe_sub, maybe_sup)
}
pub(crate) fn unify(&self, lhs: &Type, rhs: &Type) -> Option<Type> {
let unifier = Unifier::new(self, &(), false, None);
unifier.unify(lhs, rhs)
}
}

View file

@ -4,15 +4,16 @@ use erg_common::consts::PYTHON_MODE;
use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{enum_unwrap, fn_name, log, set, Str, Triple};
use erg_parser::ast::{self, AscriptionKind, Identifier, VarName, AST};
use erg_parser::ast::{self, AscriptionKind, Identifier, TypeAppArgsKind, VarName, AST};
use erg_parser::desugar::Desugarer;
use crate::context::instantiate::TyVarCache;
use crate::context::{ClassDefType, Context, MethodPair, TraitImpl};
use crate::lower::ASTLowerer;
use crate::ty::constructors::{mono, poly, ty_tp, type_q, v_enum};
use crate::ty::free::HasLevel;
use crate::ty::constructors::{array_t, mono, mono_q_tp, poly, v_enum};
use crate::ty::free::{Constraint, HasLevel};
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{HasType, Type, Visibility};
use crate::ty::{HasType, TyParam, Type, Visibility};
use crate::compile::AccessKind;
use crate::error::{LowerError, LowerErrors, LowerResult};
@ -155,6 +156,7 @@ impl ASTLowerer {
}
fn fake_lower_acc(&self, acc: ast::Accessor) -> LowerResult<hir::Accessor> {
// TypeApp is lowered in `fake_lower_expr`
match acc {
ast::Accessor::Ident(ident) => {
// to resolve `py_name`
@ -262,11 +264,9 @@ impl ASTLowerer {
elems.push(hir::PosArg::new(elem));
}
let elems = hir::Args::new(elems, None, vec![], None);
let t = array_t(Type::Failure, TyParam::value(elems.len()));
Ok(hir::Array::Normal(hir::NormalArray::new(
arr.l_sqbr,
arr.r_sqbr,
Type::Failure,
elems,
arr.l_sqbr, arr.r_sqbr, t, elems,
)))
}
other => Err(LowerErrors::from(LowerError::declare_error(
@ -509,6 +509,7 @@ impl ASTLowerer {
ast::Expr::Record(rec) => Ok(hir::Expr::Record(self.fake_lower_record(rec)?)),
ast::Expr::Set(set) => Ok(hir::Expr::Set(self.fake_lower_set(set)?)),
ast::Expr::Dict(dict) => Ok(hir::Expr::Dict(self.fake_lower_dict(dict)?)),
ast::Expr::Accessor(ast::Accessor::TypeApp(tapp)) => self.fake_lower_expr(*tapp.obj),
ast::Expr::Accessor(acc) => Ok(hir::Expr::Accessor(self.fake_lower_acc(acc)?)),
ast::Expr::Call(call) => Ok(hir::Expr::Call(self.fake_lower_call(call)?)),
ast::Expr::Lambda(lambda) => Ok(hir::Expr::Lambda(self.fake_lower_lambda(lambda)?)),
@ -525,6 +526,18 @@ impl ASTLowerer {
}
}
fn get_tv_ctx(&self, ident: &ast::Identifier, args: &ast::Args) -> TyVarCache {
let mut tv_ctx = TyVarCache::new(self.module.context.level, &self.module.context);
if let Some((t, _)) = self.module.context.get_type(ident.inspect()) {
for (tp, arg) in t.typarams().iter().zip(args.pos_args()) {
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = &arg.expr {
tv_ctx.push_or_init_typaram(&ident.name, tp, &self.module.context);
}
}
}
tv_ctx
}
fn declare_ident(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
log!(info "entered {}({})", fn_name!(), tasc);
let kind = tasc.kind();
@ -567,20 +580,98 @@ impl ASTLowerer {
}
ast::Expr::Accessor(ast::Accessor::Attr(attr)) => {
let py_name = Str::rc(attr.ident.inspect().trim_end_matches('!'));
let mut tv_cache = if let Ok(call) = ast::Call::try_from(*attr.obj.clone()) {
let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = *call.obj else {
return feature_error!(
LowerErrors,
LowerError,
&self.module.context,
call.obj.loc(),
"complex polymorphic type declaration"
);
};
self.get_tv_ctx(&ident, &call.args)
} else {
TyVarCache::new(self.module.context.level, &self.module.context)
};
let t = self
.module
.context
.instantiate_typespec(&tasc.t_spec.t_spec)?;
let ctx = self
.module
.context
.get_mut_singular_ctx(attr.obj.as_ref(), &self.module.context.name.clone())?;
ctx.assign_var_sig(
.instantiate_typespec_with_tv_cache(&tasc.t_spec.t_spec, &mut tv_cache)?;
let impl_trait = if let ast::Expr::Accessor(ast::Accessor::TypeApp(tapp)) =
attr.obj.as_ref()
{
match &tapp.type_args.args {
TypeAppArgsKind::SubtypeOf(typ) => {
let trait_ = self
.module
.context
.instantiate_typespec_with_tv_cache(&typ.t_spec, &mut tv_cache)?;
Some(trait_)
}
TypeAppArgsKind::Args(args) => {
log!(err "{args}");
None
}
}
} else {
None
};
let (class, ctx) = self.module.context.get_mut_singular_ctx_and_t(
attr.obj.as_ref(),
&self.module.context.name.clone(),
)?;
let class = class.clone();
let ctx = if let Some(impl_trait) = impl_trait {
match ctx
.methods_list
.iter_mut()
.find(|(class, _ctx)| class.is_impl_of(&impl_trait))
{
Some((_, impl_ctx)) => impl_ctx,
None => {
let impl_ctx = Context::methods(
Some(impl_trait.clone()),
self.cfg.copy(),
ctx.shared.clone(),
0,
ctx.level,
);
ctx.super_traits.push(impl_trait.clone());
if let Some(mut impls) =
ctx.trait_impls().get_mut(&impl_trait.qual_name())
{
impls.insert(TraitImpl::new(class.clone(), impl_trait.clone()));
}
ctx.methods_list.push((
ClassDefType::impl_trait(class.clone(), impl_trait),
impl_ctx,
));
&mut ctx.methods_list.iter_mut().last().unwrap().1
}
}
} else {
ctx
};
let vi = ctx.assign_var_sig(
&ast::VarSignature::new(ast::VarPattern::Ident(attr.ident.clone()), None),
&t,
ast::DefId(0),
Some(py_name.clone()),
)?;
if let Some(types) = self
.module
.context
.method_to_classes
.get_mut(attr.ident.inspect())
{
types.push(MethodPair::new(class, vi.clone()));
} else {
self.module.context.method_to_classes.insert(
attr.ident.inspect().clone(),
vec![MethodPair::new(class, vi.clone())],
);
}
let obj = self.fake_lower_expr(*attr.obj)?;
let muty = Mutability::from(&attr.ident.inspect()[..]);
let vis = self
@ -614,14 +705,7 @@ impl ASTLowerer {
);
};
let py_name = Str::rc(ident.inspect().trim_end_matches('!'));
let mut tv_cache = TyVarCache::new(self.module.context.level, &self.module.context);
if let Some((t, _)) = self.module.context.get_type(ident.inspect()) {
for (tp, arg) in t.typarams().iter().zip(call.args.pos_args()) {
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = &arg.expr {
tv_cache.push_or_init_typaram(&ident.name, tp, &self.module.context);
}
}
}
let mut tv_cache = self.get_tv_ctx(&ident, &call.args);
let t = self
.module
.context
@ -703,7 +787,10 @@ impl ASTLowerer {
let params = subr
.non_default_params
.iter()
.map(|p| ty_tp(type_q(p.name().unwrap_or(&Str::ever("_")))))
.map(|p| {
let c = Constraint::new_type_of(p.typ().clone());
mono_q_tp(p.name().unwrap_or(&Str::ever("_")), c)
})
.collect();
let t = poly(format!("{}{ident}", self.module.context.path()), params);
let ty_obj = GenTypeObj::class(t.clone(), None, None);

View file

@ -422,10 +422,10 @@ impl LowerError {
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("{typ}という型は定義されていません"),
"japanese" => format!("{typ}という型が見つかりませんでした"),
"simplified_chinese" => format!("{typ}未定义"),
"traditional_chinese" => format!("{typ}未定義"),
"english" => format!("Type {typ} is not defined"),
"english" => format!("Type {typ} is not found"),
),
errno,
NameError,

View file

@ -43,6 +43,7 @@ macro_rules! feature_error {
($Strcs: ident, $Strc: ident, $ctx: expr, $loc: expr, $name: expr) => {
Err($Strcs::from($Strc::feature_error(
$ctx.cfg.input.clone(),
line!() as usize,
$loc,
$name,
$ctx.caused_by(),
@ -295,7 +296,13 @@ caused from: {fn_name}"),
)
}
pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: String) -> Self {
pub fn feature_error(
input: Input,
errno: usize,
loc: Location,
name: &str,
caused_by: String,
) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
@ -305,7 +312,7 @@ caused from: {fn_name}"),
"traditional_chinese" => format!("此功能({name})尚未實現"),
"english" => format!("this feature({name}) is not implemented yet"),
),
0,
errno,
FeatureError,
loc,
),

View file

@ -20,7 +20,7 @@ use erg_parser::ast::{
};
use erg_parser::token::{Token, TokenKind, DOT};
use crate::ty::constructors::{array_t, dict_t, set_t, tuple_t};
use crate::ty::constructors::{dict_t, set_t, tuple_t};
use crate::ty::typaram::TyParam;
use crate::ty::value::{GenTypeObj, ValueObj};
use crate::ty::{Field, HasType, Type, VisibilityModifier};
@ -774,8 +774,7 @@ impl_locational!(NormalArray, l_sqbr, elems, r_sqbr);
impl_t!(NormalArray);
impl NormalArray {
pub fn new(l_sqbr: Token, r_sqbr: Token, elem_t: Type, elems: Args) -> Self {
let t = array_t(elem_t, TyParam::value(elems.len()));
pub fn new(l_sqbr: Token, r_sqbr: Token, t: Type, elems: Args) -> Self {
Self {
l_sqbr,
r_sqbr,

View file

@ -1,22 +1,28 @@
# TODO: dependent (static shaped)
.NDArray = 'ndarray': (T: Type) -> ClassType
.NDArray(T) <: Output T
.NDArray(_) <: Num
.NDArray = 'ndarray': (T: Type, Shape: [Nat; _]) -> ClassType
.NDArray(T, _) <: Output T
.NDArray(_, _) <: Num
.NDArray(T, S)|<: Add .NDArray(T, S)|.
Output: {.NDArray(T, S)}
__add__: (self: .NDArray(T, S), other: .NDArray(T, S)) -> .NDArray(T, S)
.NDArray.
shape: [Nat; _]
ndim: Nat
dtype: Type
size: Nat
reshape: |T, Old: [Nat; _], S: [Nat; _]|(
self: .NDArray(T, Old),
shape: {S},
) -> .NDArray(T, S)
.nan: Float
.Nan: Float
.abs: |T|(object: .NDArray(T),) -> .NDArray(T)
.add: |T|(object: .NDArray(T), other: .NDArray(T)) -> .NDArray(T)
.add: |T, S: [Nat; _]|(object: .NDArray(T, S), other: .NDArray(T, S)) -> .NDArray(T, S)
.all: |T <: Num|(object: .NDArray(T),) -> Bool
.any: |T <: Num|(object: .NDArray(T),) -> Bool
.arange: |T <: Num|(start: T, stop := T, step := T) -> .NDArray(T)
.array: |T|(object: Iterable(T),) -> .NDArray(T)
.array: |T, S: [Nat; _]|(object: Iterable(T) and Shape(S),) -> .NDArray(T, S)
.linspace: |T <: Num|(start: T, stop: T, num := Nat, endpoint := Bool, retstep := Bool, dtype := Type, axis := Nat) -> .NDArray(T)
.max: |T <: Num|(object: .NDArray(T),) -> T
.mean: |T <: Num|(object: .NDArray(T),) -> T
@ -27,3 +33,5 @@
.sum: |T|(object: .NDArray(T),) -> T
.sqrt: |T|(object: .NDArray(T),) -> .NDArray(T)
.transpose: |T|(object: .NDArray(T), axes := [Nat; _]) -> .NDArray(T)
.zeros: (|N: Nat|(shape: {N}, dtype := Type) -> .NDArray(Nat, [N])) \
and (|S: [Nat; _]|(shape: {S}, dtype := Type) -> .NDArray(Nat, S))

View file

@ -5,8 +5,8 @@ use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_parser::ast::{
Accessor, ClassAttr, ClassDef, Expr, Identifier, Methods, Module, PatchDef, PreDeclTypeSpec,
TypeAscription, TypeSpec, AST,
Accessor, ClassAttr, ClassDef, Expr, Identifier, Methods, Module, PatchDef, TypeAscription,
TypeSpec, AST,
};
use crate::error::{TyCheckError, TyCheckErrors};
@ -77,13 +77,12 @@ impl ASTLinker {
}
}
match &methods.class {
TypeSpec::PreDeclTy(PreDeclTypeSpec::Mono(ident)) => {
self.link_methods(ident.inspect().clone(), &mut new, methods, mode)
TypeSpec::PreDeclTy(predecl) => {
self.link_methods(predecl.ident().into(), &mut new, methods, mode)
}
TypeSpec::TypeApp { spec, .. } => {
if let TypeSpec::PreDeclTy(PreDeclTypeSpec::Mono(ident)) = spec.as_ref()
{
self.link_methods(ident.inspect().clone(), &mut new, methods, mode)
if let Some(ident) = spec.ident() {
self.link_methods(ident.into(), &mut new, methods, mode)
} else {
let similar_name = self
.def_root_pos_map

View file

@ -26,7 +26,7 @@ use crate::artifact::{CompleteArtifact, IncompleteArtifact};
use crate::context::instantiate::TyVarCache;
use crate::module::SharedCompilerResource;
use crate::ty::constructors::{
array_t, free_var, func, guard, mono, poly, proc, refinement, set_t, ty_tp, v_enum,
array_t, free_var, func, guard, mono, poly, proc, refinement, set_t, singleton, ty_tp, v_enum,
};
use crate::ty::free::Constraint;
use crate::ty::typaram::TyParam;
@ -72,7 +72,7 @@ pub fn expr_to_variable(expr: &ast::Expr) -> Option<Variable> {
/// Checks & infers types of an AST, and convert (lower) it into a HIR
#[derive(Debug)]
pub struct ASTLowerer {
cfg: ErgConfig,
pub(crate) cfg: ErgConfig,
pub(crate) module: ModuleContext,
pub(crate) errs: LowerErrors,
pub(crate) warns: LowerWarnings,
@ -290,6 +290,7 @@ impl ASTLowerer {
fn lower_normal_array(&mut self, array: ast::NormalArray) -> LowerResult<hir::NormalArray> {
log!(info "entered {}({array})", fn_name!());
let mut new_array = vec![];
let eval_result = self.module.context.eval_const_normal_array(&array);
let (elems, ..) = array.elems.deconstruct();
let mut union = Type::Never;
for elem in elems.into_iter() {
@ -330,12 +331,14 @@ impl ASTLowerer {
} else {
union
};
Ok(hir::NormalArray::new(
array.l_sqbr,
array.r_sqbr,
elem_t,
hir::Args::values(new_array, None),
))
let elems = hir::Args::values(new_array, None);
let t = array_t(elem_t, TyParam::value(elems.len()));
let t = if let Ok(value) = eval_result {
singleton(t, TyParam::Value(value))
} else {
t
};
Ok(hir::NormalArray::new(array.l_sqbr, array.r_sqbr, t, elems))
}
fn lower_array_with_length(
@ -1741,10 +1744,10 @@ impl ASTLowerer {
} else {
self.check_override(&class, None);
}
if let Err(err) = self.check_trait_impl(impl_trait, &class) {
if let Err(err) = self.check_trait_impl(impl_trait.clone(), &class) {
self.errs.push(err);
}
self.check_collision_and_push(class);
self.check_collision_and_push(class, impl_trait.map(|(t, _)| t));
}
let class = self.module.context.gen_type(&hir_def.sig.ident().raw);
let Some((_, class_ctx)) = self.module.context.get_nominal_type_ctx(&class) else {
@ -2180,7 +2183,7 @@ impl ASTLowerer {
(unverified_names, errors)
}
fn check_collision_and_push(&mut self, class: Type) {
fn check_collision_and_push(&mut self, class: Type, impl_trait: Option<Type>) {
let methods = self.module.context.pop();
let Some((_, class_root)) = self
.module
@ -2214,9 +2217,12 @@ impl ASTLowerer {
}
}
}
class_root
.methods_list
.push((ClassDefType::Simple(class), methods));
let typ = if let Some(impl_trait) = impl_trait {
ClassDefType::impl_trait(class, impl_trait)
} else {
ClassDefType::Simple(class)
};
class_root.methods_list.push((typ, methods));
}
fn push_patch(&mut self) {

View file

@ -16,6 +16,8 @@ use crate::context::TraitImpl;
/// (Erg links all scripts defined in erg and outputs them to a single pyc file).
#[derive(Debug, Default)]
pub struct TraitImpls {
/// * key: trait qualified name
/// * value: set of trait impls
cache: Dict<Str, Set<TraitImpl>>,
}

View file

@ -11,3 +11,5 @@ abs_ x = x.abs()
Norm = Trait { .norm = (self: Self) -> Nat }
norm x = x.norm()
a = [1, 2] + [3, 4]

View file

@ -1,6 +1,7 @@
use erg_common::config::ErgConfig;
use erg_common::error::MultiErrorDisplay;
use erg_common::io::Output;
use erg_common::set;
use erg_common::spawn::exec_new_thread;
use erg_common::traits::Runnable;
@ -9,7 +10,8 @@ use erg_compiler::error::CompileErrors;
use erg_compiler::lower::ASTLowerer;
use erg_compiler::ty::constructors::{
func0, func1, func2, kw, mono, nd_func, nd_proc, or, poly, proc1, subtype_q, ty_tp, type_q,
array_t, func0, func1, func2, kw, mono, nd_func, nd_proc, or, poly, proc1, subtype_q, ty_tp,
type_q, v_enum,
};
use erg_compiler::ty::Type::*;
@ -64,6 +66,11 @@ fn _test_infer_types() -> Result<(), ()> {
module.context.assert_var_type("abs_", &abs_t)?;
let norm_t = func1(mono("<module>::Norm"), Nat);
module.context.assert_var_type("norm", &norm_t)?;
let a_t = array_t(
v_enum(set! {1.into(), 2.into(), 3.into(), 4.into()}),
4.into(),
);
module.context.assert_var_type("a", &a_t)?;
Ok(())
}

View file

@ -79,7 +79,7 @@ impl ValueArgs {
#[derive(Clone)]
pub struct BuiltinConstSubr {
name: Str,
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
subr: fn(ValueArgs, &Context) -> EvalValueResult<TyParam>,
sig_t: Type,
as_type: Option<Type>,
}
@ -117,7 +117,7 @@ impl fmt::Display for BuiltinConstSubr {
impl BuiltinConstSubr {
pub fn new<S: Into<Str>>(
name: S,
subr: fn(ValueArgs, &Context) -> EvalValueResult<ValueObj>,
subr: fn(ValueArgs, &Context) -> EvalValueResult<TyParam>,
sig_t: Type,
as_type: Option<Type>,
) -> Self {
@ -129,7 +129,7 @@ impl BuiltinConstSubr {
}
}
pub fn call(&self, args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
pub fn call(&self, args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
(self.subr)(args, ctx)
}
}
@ -156,7 +156,7 @@ impl ClosureData {
pub struct GenConstSubr {
name: Str,
data: ClosureData,
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<ValueObj>,
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<TyParam>,
sig_t: Type,
as_type: Option<Type>,
}
@ -195,7 +195,7 @@ impl GenConstSubr {
pub fn new<S: Into<Str>>(
name: S,
data: ClosureData,
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<ValueObj>,
subr: fn(ClosureData, ValueArgs, &Context) -> EvalValueResult<TyParam>,
sig_t: Type,
as_type: Option<Type>,
) -> Self {
@ -208,7 +208,7 @@ impl GenConstSubr {
}
}
pub fn call(&self, args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
pub fn call(&self, args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
(self.subr)(self.data.clone(), args, ctx)
}
}

View file

@ -46,7 +46,7 @@ pub trait HasLevel {
///
/// __NOTE__: you should use `Free::get_type/get_subsup` instead of deconstructing the constraint by `match`.
/// Constraints may contain cycles, in which case using `match` to get the contents will cause memory destructions.
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Constraint {
// : Type --> (:> Never, <: Obj)
// :> Sub --> (:> Sub, <: Obj)
@ -67,12 +67,6 @@ impl fmt::Display for Constraint {
}
}
impl fmt::Debug for Constraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.limited_fmt(f, 10)
}
}
impl LimitedDisplay for Constraint {
fn limited_fmt<W: std::fmt::Write>(&self, f: &mut W, limit: isize) -> fmt::Result {
if limit == 0 {
@ -233,7 +227,7 @@ impl Constraint {
pub trait CanbeFree {
fn unbound_name(&self) -> Option<Str>;
fn constraint(&self) -> Option<Constraint>;
fn update_constraint(&self, constraint: Constraint, in_instantiation: bool);
fn destructive_update_constraint(&self, constraint: Constraint, in_instantiation: bool);
}
impl<T: CanbeFree + Send + Clone> Free<T> {
@ -252,6 +246,7 @@ pub enum FreeKind<T> {
UndoableLinked {
t: T,
previous: Box<FreeKind<T>>,
count: usize,
},
Unbound {
id: Id,
@ -488,6 +483,29 @@ impl<T> FreeKind<T> {
pub const fn is_undoable_linked(&self) -> bool {
matches!(self, Self::UndoableLinked { .. })
}
pub fn undo_count(&self) -> usize {
match self {
Self::UndoableLinked { count, .. } => *count,
_ => 0,
}
}
pub fn inc_undo_count(&mut self) {
#[allow(clippy::single_match)]
match self {
Self::UndoableLinked { count, .. } => *count += 1,
_ => {}
}
}
pub fn dec_undo_count(&mut self) {
#[allow(clippy::single_match)]
match self {
Self::UndoableLinked { count, .. } => *count -= 1,
_ => {}
}
}
}
#[derive(Debug, Clone)]
@ -820,6 +838,7 @@ impl<T: Send + Sync + 'static + Clone> Free<T> {
self.borrow().is_unnamed_unbound()
}
/// interior-mut
#[track_caller]
pub fn replace(&self, to: FreeKind<T>) {
// prevent linking to self
@ -830,7 +849,8 @@ impl<T: Send + Sync + 'static + Clone> Free<T> {
}
}
impl<T: Clone + fmt::Debug + Send + Sync + 'static> Free<T> {
impl<T: Clone + Send + Sync + 'static> Free<T> {
/// interior-mut
/// SAFETY: use `Type/TyParam::link` instead of this.
/// This method may cause circular references.
#[track_caller]
@ -842,6 +862,7 @@ impl<T: Clone + fmt::Debug + Send + Sync + 'static> Free<T> {
self.borrow_mut().replace(to.clone());
}
/// interior-mut
#[track_caller]
pub(super) fn undoable_link(&self, to: &T) {
if self.is_linked() && addr_eq!(*self.crack(), *to) {
@ -851,18 +872,36 @@ impl<T: Clone + fmt::Debug + Send + Sync + 'static> Free<T> {
let new = FreeKind::UndoableLinked {
t: to.clone(),
previous: Box::new(prev),
count: 0,
};
*self.borrow_mut() = new;
}
/// interior-mut
pub fn undo(&self) {
let prev = match &*self.borrow() {
FreeKind::UndoableLinked { previous, .. } => *previous.clone(),
let prev = match &mut *self.borrow_mut() {
FreeKind::UndoableLinked {
previous, count, ..
} => {
if *count > 0 {
*count -= 1;
return;
}
*previous.clone()
}
_other => panic!("cannot undo"),
};
self.replace(prev);
}
pub fn undo_stack_size(&self) -> usize {
self.borrow().undo_count()
}
pub fn inc_undo_count(&self) {
self.borrow_mut().inc_undo_count();
}
pub fn unwrap_unbound(self) -> (Option<Str>, usize, Constraint) {
match self.clone_inner() {
FreeKind::Linked(_) | FreeKind::UndoableLinked { .. } => panic!("the value is linked"),
@ -942,6 +981,7 @@ impl<T: Clone + fmt::Debug + Send + Sync + 'static> Free<T> {
}
impl<T: Default + Clone + fmt::Debug + Send + Sync + 'static> Free<T> {
/// interior-mut
#[track_caller]
pub fn dummy_link(&self) {
self.undoable_link(&T::default());
@ -991,8 +1031,12 @@ impl<T: CanbeFree + Send + Clone> Free<T> {
self.constraint().map(|c| c.is_uninited()).unwrap_or(false)
}
/// interior-mut
/// if `in_inst_or_gen` is true, constraint will be updated forcibly
pub fn update_constraint(&self, new_constraint: Constraint, in_inst_or_gen: bool) {
if new_constraint.get_type() == Some(&Type::Never) {
panic!();
}
match &mut *self.borrow_mut() {
FreeKind::Unbound {
lev, constraint, ..
@ -1010,11 +1054,12 @@ impl<T: CanbeFree + Send + Clone> Free<T> {
*constraint = new_constraint;
}
FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => {
t.update_constraint(new_constraint, in_inst_or_gen);
t.destructive_update_constraint(new_constraint, in_inst_or_gen);
}
}
}
/// interior-mut
pub fn update_sub<F>(&self, f: F)
where
F: FnOnce(Type) -> Type,
@ -1024,6 +1069,7 @@ impl<T: CanbeFree + Send + Clone> Free<T> {
self.update_constraint(new_constraint, true);
}
/// interior-mut
pub fn update_super<F>(&self, f: F)
where
F: FnOnce(Type) -> Type,

View file

@ -43,7 +43,7 @@ pub use value::ValueObj;
use value::ValueObj::{Inf, NegInf};
pub use vis::*;
use self::constructors::subr_t;
use self::constructors::{bounded, free_var, named_free_var, proj_call, subr_t};
pub const STR_OMIT_THRESHOLD: usize = 16;
pub const CONTAINER_OMIT_THRESHOLD: usize = 8;
@ -594,7 +594,7 @@ impl LimitedDisplay for RefinementType {
return write!(f, "...");
}
let first_subj = self.pred.ors().iter().next().and_then(|p| p.subject());
let is_simple_type = self.t.is_simple_class();
let is_simple_type = self.t.is_value_class();
let is_simple_preds = self
.pred
.ors()
@ -1210,7 +1210,7 @@ impl CanbeFree for Type {
}
}
fn update_constraint(&self, new_constraint: Constraint, in_instantiation: bool) {
fn destructive_update_constraint(&self, new_constraint: Constraint, in_instantiation: bool) {
if let Some(fv) = self.as_free() {
fv.update_constraint(new_constraint, in_instantiation);
}
@ -1534,7 +1534,7 @@ impl StructuralEq for Type {
}
(Self::FreeVar(fv), Self::FreeVar(fv2)) => fv.structural_eq(fv2),
(Self::Refinement(refine), Self::Refinement(refine2)) => {
refine.t.structural_eq(&refine2.t) && refine.pred == refine2.pred
refine.t.structural_eq(&refine2.t) && refine.pred.structural_eq(&refine2.pred)
}
(Self::Record(rec), Self::Record(rec2)) => {
for (k, v) in rec.iter() {
@ -1684,9 +1684,9 @@ impl Type {
Self::Structural(Box::new(self))
}
pub fn is_simple_class(&self) -> bool {
pub fn is_mono_value_class(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_simple_class(),
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_mono_value_class(),
Self::Obj
| Self::Int
| Self::Nat
@ -1712,6 +1712,23 @@ impl Type {
}
}
/// value class := mono value object class | (Array | Set)(value class)
pub fn is_value_class(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_value_class(),
Self::Refinement(refine) => refine.t.is_value_class(),
Self::Poly { name, params } => {
if &name[..] == "Array" || &name[..] == "Set" {
let elem_t = <&Type>::try_from(params.first().unwrap()).unwrap();
elem_t.is_value_class()
} else {
false
}
}
_ => self.is_mono_value_class(),
}
}
/// Procedure
pub fn is_procedure(&self) -> bool {
match self {
@ -2428,7 +2445,7 @@ impl Type {
Type::FreeVar(fv) if fv.is_unbound() => {
let (sub, _sup) = fv.get_subsup().unwrap();
sub.coerce();
self.link(&sub);
self.destructive_link(&sub);
}
Type::And(l, r) | Type::Or(l, r) => {
l.coerce();
@ -2542,6 +2559,14 @@ impl Type {
}
}
pub fn is_undoable_linked_var(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_undoable_linked() => true,
Self::FreeVar(fv) if fv.is_linked() => fv.crack().has_undoable_linked_var(),
_ => false,
}
}
pub fn has_undoable_linked_var(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_undoable_linked() => true,
@ -2908,7 +2933,7 @@ impl Type {
other => other.clone(),
})
.collect();
lhs.proj_call(attr_name.clone(), args)
proj_call(lhs, attr_name.clone(), args)
}
Self::Structural(ty) => ty.derefine().structuralize(),
Self::Guard(guard) => {
@ -3047,7 +3072,7 @@ impl Type {
args,
} => {
let args = args.into_iter().map(|tp| tp.replace(target, to)).collect();
lhs.replace(target, to).proj_call(attr_name, args)
proj_call(lhs.replace(target, to), attr_name, args)
}
Self::Structural(ty) => ty._replace(target, to).structuralize(),
Self::Guard(guard) => Self::Guard(GuardType::new(
@ -3090,7 +3115,7 @@ impl Type {
args,
} => {
let args = args.into_iter().map(|tp| tp.normalize()).collect();
lhs.normalize().proj_call(attr_name, args)
proj_call(lhs.normalize(), attr_name, args)
}
Self::Ref(t) => Self::Ref(Box::new(t.normalize())),
Self::RefMut { before, after } => Self::RefMut {
@ -3131,19 +3156,27 @@ impl Type {
}
}
pub(crate) fn link(&self, to: &Type) {
/// interior-mut
pub(crate) fn destructive_link(&self, to: &Type) {
if self.addr_eq(to) {
return;
}
if self.level() == Some(GENERIC_LEVEL) {
panic!("{self} is fixed");
}
match self {
Self::FreeVar(fv) => fv.link(to),
Self::Refinement(refine) => refine.t.link(to),
Self::Refinement(refine) => refine.t.destructive_link(to),
_ => panic!("{self} is not a free variable"),
}
}
/// interior-mut
///
/// `inc/dec_undo_count` due to the number of `substitute_typarams/undo_typarams` must be matched
pub(crate) fn undoable_link(&self, to: &Type) {
if self.addr_eq(to) {
self.inc_undo_count();
return;
}
match self {
@ -3152,6 +3185,74 @@ impl Type {
_ => panic!("{self} is not a free variable"),
}
}
pub(crate) fn link(&self, to: &Type, undoable: bool) {
if undoable {
self.undoable_link(to);
} else {
self.destructive_link(to);
}
}
pub(crate) fn undo(&self) {
match self {
Self::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(),
Self::FreeVar(fv) if fv.constraint_is_sandwiched() => {
let (sub, sup) = fv.get_subsup().unwrap();
sub.undo();
sup.undo();
}
Self::Poly { params, .. } => {
for param in params {
param.undo();
}
}
_ => {}
}
}
pub(crate) fn undoable_update_constraint(&self, new_constraint: Constraint) {
let level = self.level().unwrap();
let new = if let Some(name) = self.unbound_name() {
named_free_var(name, level, new_constraint)
} else {
free_var(level, new_constraint)
};
self.undoable_link(&new);
}
pub(crate) fn update_constraint(
&self,
new_constraint: Constraint,
undoable: bool,
in_instantiation: bool,
) {
if undoable {
self.undoable_update_constraint(new_constraint);
} else {
self.destructive_update_constraint(new_constraint, in_instantiation);
}
}
fn inc_undo_count(&self) {
match self {
Self::FreeVar(fv) => fv.inc_undo_count(),
Self::Refinement(refine) => refine.t.inc_undo_count(),
_ => panic!("{self} is not a free variable"),
}
}
pub fn into_bounded(&self) -> Type {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().clone().into_bounded(),
Self::FreeVar(fv) if fv.constraint_is_sandwiched() => {
let (sub, sup) = fv.get_subsup().unwrap();
bounded(sub, sup)
}
Self::Refinement(refine) => refine.t.as_ref().clone().into_bounded(),
_ => self.clone(),
}
}
}
pub struct ReplaceTable<'t> {

View file

@ -4,7 +4,7 @@ use std::ops::{BitAnd, BitOr, Not};
#[allow(unused_imports)]
use erg_common::log;
use erg_common::set::Set;
use erg_common::traits::LimitedDisplay;
use erg_common::traits::{LimitedDisplay, StructuralEq};
use erg_common::{set, Str};
use super::free::{Constraint, HasLevel};
@ -102,6 +102,50 @@ impl LimitedDisplay for Predicate {
}
}
impl StructuralEq for Predicate {
fn structural_eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Equal { rhs, .. }, Self::Equal { rhs: r2, .. })
| (Self::NotEqual { rhs, .. }, Self::NotEqual { rhs: r2, .. })
| (Self::GreaterEqual { rhs, .. }, Self::GreaterEqual { rhs: r2, .. })
| (Self::LessEqual { rhs, .. }, Self::LessEqual { rhs: r2, .. }) => {
rhs.structural_eq(r2)
}
(Self::Or(_, _), Self::Or(_, _)) => {
let self_ors = self.ors();
let other_ors = other.ors();
if self_ors.len() != other_ors.len() {
return false;
}
for l_val in self_ors.iter() {
if other_ors.get_by(l_val, |l, r| l.structural_eq(r)).is_none() {
return false;
}
}
true
}
(Self::And(_, _), Self::And(_, _)) => {
let self_ands = self.ands();
let other_ands = other.ands();
if self_ands.len() != other_ands.len() {
return false;
}
for l_val in self_ands.iter() {
if other_ands
.get_by(l_val, |l, r| l.structural_eq(r))
.is_none()
{
return false;
}
}
true
}
(Self::Not(p1), Self::Not(p2)) => p1.structural_eq(p2),
_ => self == other,
}
}
}
impl HasLevel for Predicate {
fn level(&self) -> Option<usize> {
match self {

View file

@ -3,7 +3,6 @@ use std::fmt;
use std::ops::{Add, Div, Mul, Neg, Range, RangeInclusive, Sub};
use std::sync::Arc;
use erg_common::consts::DEBUG_MODE;
use erg_common::dict::Dict;
use erg_common::set::Set;
use erg_common::traits::{LimitedDisplay, StructuralEq};
@ -214,6 +213,11 @@ pub enum TyParam {
obj: Box<TyParam>,
attr: Str,
},
ProjCall {
obj: Box<TyParam>,
attr: Str,
args: Vec<TyParam>,
},
App {
name: Str,
args: Vec<TyParam>,
@ -343,6 +347,20 @@ impl LimitedDisplay for TyParam {
write!(f, ".")?;
write!(f, "{attr}")
}
Self::ProjCall { obj, attr, args } => {
obj.limited_fmt(f, limit - 1)?;
write!(f, ".")?;
write!(f, "{attr}")?;
write!(f, "(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
arg.limited_fmt(f, limit - 1)?;
}
write!(f, ")")?;
Ok(())
}
Self::Array(arr) => {
write!(f, "[")?;
for (i, t) in arr.iter().enumerate() {
@ -443,16 +461,17 @@ impl CanbeFree for TyParam {
}
}
fn update_constraint(&self, new_constraint: Constraint, in_instantiation: bool) {
fn destructive_update_constraint(&self, new_constraint: Constraint, in_instantiation: bool) {
match self {
Self::FreeVar(fv) => {
fv.update_constraint(new_constraint, in_instantiation);
}
Self::Type(t) => {
t.update_constraint(new_constraint, in_instantiation);
t.destructive_update_constraint(new_constraint, in_instantiation);
}
Self::Value(ValueObj::Type(ty)) => {
ty.typ().update_constraint(new_constraint, in_instantiation);
ty.typ()
.destructive_update_constraint(new_constraint, in_instantiation);
}
_ => {}
}
@ -557,6 +576,16 @@ impl<'t> TryFrom<&'t TyParam> for &'t FreeTyParam {
}
}
impl<'t> TryFrom<&'t TyParam> for &'t FreeTyVar {
type Error = ();
fn try_from(t: &'t TyParam) -> Result<&'t FreeTyVar, ()> {
match t {
TyParam::Type(ty) => <&FreeTyVar>::try_from(ty.as_ref()),
_ => Err(()),
}
}
}
impl TryFrom<TyParam> for ValueObj {
type Error = ();
fn try_from(tp: TyParam) -> Result<Self, ()> {
@ -603,7 +632,7 @@ impl TryFrom<TyParam> for ValueObj {
TyParam::Type(t) => Ok(ValueObj::builtin_type(*t)),
TyParam::Value(v) => Ok(v),
_ => {
log!(err "Expected value, got {:?}", tp);
log!(err "Expected value, got {tp} ({tp:?})");
Err(())
}
}
@ -647,6 +676,17 @@ impl<'a> TryFrom<&'a TyParam> for &'a Type {
}
}
impl TryFrom<&TyParam> for usize {
type Error = ();
fn try_from(tp: &TyParam) -> Result<Self, ()> {
match tp {
TyParam::FreeVar(fv) if fv.is_linked() => usize::try_from(&*fv.crack()),
TyParam::Value(v) => usize::try_from(v),
_ => Err(()),
}
}
}
impl HasLevel for TyParam {
fn level(&self) -> Option<Level> {
match self {
@ -757,6 +797,9 @@ impl StructuralEq for TyParam {
true
}
(Self::Set(l), Self::Set(r)) => {
if l.len() != r.len() {
return false;
}
for l_val in l.iter() {
if r.get_by(l_val, |l, r| l.structural_eq(r)).is_none() {
return false;
@ -829,19 +872,23 @@ impl TyParam {
}
}
pub fn proj_call(self, attr_name: Str, args: Vec<TyParam>) -> Type {
Type::ProjCall {
lhs: Box::new(self),
attr_name,
pub fn proj_call(self, attr: Str, args: Vec<TyParam>) -> Self {
Self::ProjCall {
obj: Box::new(self),
attr,
args,
}
}
pub fn free_var(level: usize, t: Type) -> Self {
pub fn free_instance(level: usize, t: Type) -> Self {
let constraint = Constraint::new_type_of(t);
Self::FreeVar(FreeTyParam::new_unbound(level, constraint))
}
pub fn free_var(level: usize, constr: Constraint) -> Self {
Self::FreeVar(FreeTyParam::new_unbound(level, constr))
}
pub fn named_free_var(name: Str, level: usize, constr: Constraint) -> Self {
Self::FreeVar(FreeTyParam::new_named_unbound(name, level, constr))
}
@ -1217,24 +1264,24 @@ impl TyParam {
}
}
pub(crate) fn link(&self, to: &TyParam) {
/// interior-mut
pub(crate) fn destructive_link(&self, to: &TyParam) {
if self.addr_eq(to) {
return;
}
if self.level() == Some(GENERIC_LEVEL) {
panic!("{self} is fixed");
}
match self {
Self::FreeVar(fv) => fv.link(to),
_ => panic!("{self} is not a free variable"),
}
}
/// interior-mut
pub(crate) fn undoable_link(&self, to: &TyParam) {
if self.addr_eq(to) {
return;
}
if to.contains_tp(self) {
if DEBUG_MODE {
panic!("cyclic: {self} / {to}");
}
self.inc_undo_count();
return;
}
match self {
@ -1242,6 +1289,68 @@ impl TyParam {
_ => panic!("{self} is not a free variable"),
}
}
pub(crate) fn link(&self, to: &TyParam, undoable: bool) {
if undoable {
self.undoable_link(to);
} else {
self.destructive_link(to);
}
}
pub(crate) fn undo(&self) {
match self {
Self::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(),
Self::Type(t) => t.undo(),
Self::Value(ValueObj::Type(t)) => t.typ().undo(),
Self::App { args, .. } => {
for arg in args {
arg.undo();
}
}
_ => {}
}
}
pub(crate) fn undoable_update_constraint(&self, new_constraint: Constraint) {
let level = self.level().unwrap();
let new = if let Some(name) = self.unbound_name() {
Self::named_free_var(name, level, new_constraint)
} else {
Self::free_var(level, new_constraint)
};
self.undoable_link(&new);
}
pub(crate) fn update_constraint(
&self,
new_constraint: Constraint,
undoable: bool,
in_instantiation: bool,
) {
if undoable {
self.undoable_update_constraint(new_constraint);
} else {
self.destructive_update_constraint(new_constraint, in_instantiation);
}
}
fn inc_undo_count(&self) {
match self {
Self::FreeVar(fv) => fv.inc_undo_count(),
_ => panic!("{self} is not a free variable"),
}
}
pub fn typarams(&self) -> Vec<TyParam> {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().typarams(),
Self::Type(t) => t.typarams(),
Self::Value(ValueObj::Type(t)) => t.typ().typarams(),
Self::App { args, .. } => args.clone(),
_ => vec![],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

View file

@ -832,6 +832,19 @@ impl TryFrom<&ValueObj> for f64 {
}
}
impl TryFrom<&ValueObj> for usize {
type Error = ();
fn try_from(val: &ValueObj) -> Result<usize, Self::Error> {
match val {
ValueObj::Int(i) => usize::try_from(*i).map_err(|_| ()),
ValueObj::Nat(n) => usize::try_from(*n).map_err(|_| ()),
ValueObj::Float(f) => Ok(*f as usize),
ValueObj::Bool(b) => Ok(if *b { 1 } else { 0 }),
_ => Err(()),
}
}
}
impl<'a> TryFrom<&'a ValueObj> for &'a Type {
type Error = ();
fn try_from(val: &'a ValueObj) -> Result<Self, ()> {

View file

@ -204,8 +204,8 @@ impl fmt::Display for VarInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"VarInfo{{t: {}, muty: {:?}, vis: {:?}, kind: {:?}, py_name: {:?}}}",
self.t, self.muty, self.vis, self.kind, self.py_name,
"VarInfo{{t: {}, muty: {:?}, vis: {:?}, kind: {:?}, py_name: {:?}, def_loc: {} }}",
self.t, self.muty, self.vis, self.kind, self.py_name, self.def_loc
)
}
}

View file

@ -1227,6 +1227,18 @@ impl NestedDisplay for Call {
}
}
impl TryFrom<Expr> for Call {
type Error = Expr;
fn try_from(expr: Expr) -> Result<Self, Self::Error> {
match expr {
Expr::Call(call) => Ok(call),
Expr::TypeAscription(tasc) => Self::try_from(*tasc.expr),
Expr::Accessor(Accessor::TypeApp(tapp)) => Self::try_from(*tapp.obj),
_ => Err(expr),
}
}
}
impl_display_from_nested!(Call);
impl Locational for Call {
@ -2010,34 +2022,53 @@ impl ConstUnaryOp {
/// ex. `Vec Int` of `Option Vec Int`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ConstApp {
pub acc: ConstAccessor,
pub obj: Box<ConstExpr>,
pub attr_name: Option<ConstIdentifier>,
pub args: ConstArgs,
}
impl NestedDisplay for ConstApp {
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result {
writeln!(f, "{}:", self.acc)?;
self.args.fmt_nest(f, level + 1)
writeln!(f, "{}", self.obj)?;
if let Some(attr_name) = &self.attr_name {
writeln!(f, "{}", attr_name)?;
}
writeln!(f, "(")?;
self.args.fmt_nest(f, level + 1)?;
writeln!(f, ")")
}
}
impl_display_from_nested!(ConstApp);
impl Locational for ConstApp {
fn loc(&self) -> Location {
if self.args.is_empty() {
self.acc.loc()
self.obj.loc()
} else {
Location::concat(&self.acc, &self.args)
Location::concat(self.obj.as_ref(), &self.args)
}
}
}
impl ConstApp {
pub const fn new(acc: ConstAccessor, args: ConstArgs) -> Self {
Self { acc, args }
pub fn new(obj: ConstExpr, attr_name: Option<ConstIdentifier>, args: ConstArgs) -> Self {
Self {
obj: Box::new(obj),
attr_name,
args,
}
}
pub fn downgrade(self) -> Call {
Expr::Accessor(self.acc.downgrade()).call(self.args.downgrade())
if let Some(attr_name) = self.attr_name {
self.obj
.downgrade()
.attr_expr(attr_name)
.call(self.args.downgrade())
} else {
self.obj.downgrade().call(self.args.downgrade())
}
}
}
@ -2317,6 +2348,10 @@ impl PolyTypeSpec {
pub const fn new(acc: ConstAccessor, args: ConstArgs) -> Self {
Self { acc, args }
}
pub fn ident(&self) -> String {
self.acc.to_string()
}
}
// OK:
@ -2383,6 +2418,15 @@ impl PreDeclTypeSpec {
pub fn poly(acc: ConstAccessor, args: ConstArgs) -> Self {
Self::Poly(PolyTypeSpec::new(acc, args))
}
pub fn ident(&self) -> String {
match self {
Self::Mono(name) => name.inspect().to_string(),
Self::Poly(poly) => poly.ident(),
Self::Attr { namespace, t } => format!("{namespace}{t}"),
other => todo!("{other}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -2770,6 +2814,14 @@ impl TypeSpec {
pub fn poly(acc: ConstAccessor, args: ConstArgs) -> Self {
Self::PreDeclTy(PreDeclTypeSpec::Poly(PolyTypeSpec::new(acc, args)))
}
pub fn ident(&self) -> Option<String> {
match self {
Self::PreDeclTy(predecl) => Some(predecl.ident()),
Self::TypeApp { spec, .. } => spec.ident(),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -124,13 +124,14 @@ impl Parser {
}
Expr::Call(call) => {
let obj = Self::validate_const_expr(*call.obj)?;
let ConstExpr::Accessor(acc) = obj else {
/*let ConstExpr::Accessor(acc) = obj else {
return Err(ParseError::feature_error(
line!() as usize,
obj.loc(),
"complex const function call",
));
};
};*/
let attr_name = call.attr_name;
let (pos_args, _, _, paren) = call.args.deconstruct();
let mut const_pos_args = vec![];
for elem in pos_args.into_iter() {
@ -138,7 +139,7 @@ impl Parser {
const_pos_args.push(ConstPosArg::new(const_expr));
}
let args = ConstArgs::pos_only(const_pos_args, paren);
Ok(ConstExpr::App(ConstApp::new(acc, args)))
Ok(ConstExpr::App(ConstApp::new(obj, attr_name, args)))
}
Expr::Def(def) => Self::validate_const_def(def).map(ConstExpr::Def),
Expr::Lambda(lambda) => {

View file

@ -61,6 +61,8 @@ class C:
No syntax other than declarations and definitions (aliasing) are allowed in ``d.er``.
If an identifier on the Python side is not a valid identifier in Erg, it can be escaped by enclosing it in single quotes (`'`).
## Overloading
A special type that can be used only with Python typing is the overloaded type. This is a type that can accept multiple types.
@ -95,6 +97,18 @@ f: ((Int, Int) -> Str) and (Int -> Int)
f: (Int -> Str) and (Int -> Int)
```
## Declaration of Trait Implementation
To implement a trait and declare trait members for a class, write the following (taken from [type declarations for numpy.NDArray](https://github.com/erg-lang/erg/blob/main/crates/erg_compiler/lib/external/numpy.d/__init__.d.er)).
```erg
.NDArray = 'ndarray': (T: Type, Shape: [Nat; _]) -> ClassType
...
.NDArray(T, S)|<: Add .NDArray(T, S)|.
Output: {.NDArray(T, S)}
__add__: (self: .NDArray(T, S), other: .NDArray(T, S)) -> .NDArray(T, S)
```
## Notes
Currently, Erg unconditionally trusts the contents of type declarations. In other words, you can declare a variable of type `Str` even if it is actually a variable of type `Int`, or declare a subroutine as a function even if it has side effects, etc.

View file

@ -64,6 +64,8 @@ class C:
`d.er`内では宣言と定義(エイリアシング)以外の構文は使えません。
Python側での識別子がErgでは有効な識別子ではない場合、シングルクォーテーション(`'`)で囲むことでエスケープできます。
## オーバーロード
Pythonの型付けだけで使える特殊な型として、オーバーロード型があります。これは、複数の型を受け取ることができる型です。
@ -98,6 +100,18 @@ f: ((Int, Int) -> Str) and (Int -> Int)
f: (Int -> Str) and (Int -> Int)
```
## トレイト実装宣言
クラスに対してトレイトの実装とトレイトメンバーの宣言を行う場合、以下のように記述します([numpy.NDArrayの型宣言](https://github.com/erg-lang/erg/blob/main/crates/erg_compiler/lib/external/numpy.d/__init__.d.er)より抜粋)。
```erg
.NDArray = 'ndarray': (T: Type, Shape: [Nat; _]) -> ClassType
...
.NDArray(T, S)|<: Add .NDArray(T, S)|.
Output: {.NDArray(T, S)}
__add__: (self: .NDArray(T, S), other: .NDArray(T, S)) -> .NDArray(T, S)
```
## 注意点
現在のところ、Ergはこの型宣言の内容を無条件に信用します。すなわち、実際にはInt型の変数でもStr型として宣言する、副作用のあるサブルーチンでも関数として宣言する、などができてしまいます。