mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 10:49:54 +00:00
Merge pull request #447 from erg-lang/shape
Enhance compile-time verification capabilities
This commit is contained in:
commit
3c88aee6be
35 changed files with 1705 additions and 688 deletions
|
@ -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),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(¶ms[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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)],
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>>,
|
||||
}
|
||||
|
||||
|
|
|
@ -11,3 +11,5 @@ abs_ x = x.abs()
|
|||
|
||||
Norm = Trait { .norm = (self: Self) -> Nat }
|
||||
norm x = x.norm()
|
||||
|
||||
a = [1, 2] + [3, 4]
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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, ()> {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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型として宣言する、副作用のあるサブルーチンでも関数として宣言する、などができてしまいます。
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue