fix: array type inferring

This commit is contained in:
Shunsuke Shibayama 2023-08-06 22:51:13 +09:00
parent 142db9b697
commit f4e1d494a4
15 changed files with 276 additions and 69 deletions

View file

@ -274,7 +274,7 @@ impl Context {
if let Err(err) = self.overwrite_typarams(typ, rhs) {
Self::undo_substitute_typarams(typ);
if DEBUG_MODE {
panic!("err: {err}");
panic!("{typ} / {rhs}: err: {err}");
}
}
}

View file

@ -398,16 +398,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![];
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)))
}
fn eval_const_array(&self, arr: &Array) -> EvalResult<ValueObj> {
match arr {
Array::Normal(arr) => self.eval_const_normal_array(arr),
_ => Err(EvalErrors::from(EvalError::not_const_expr(
self.cfg.input.clone(),
line!() as usize,
@ -1847,7 +1849,13 @@ impl Context {
if st.has_no_unbound_var() && qt.has_no_unbound_var() {
return Ok(());
}
if let Err(errs) = self.sub_unify(&st, &qt, &(), None) {
let qt = if qt.has_undoable_linked_var() {
let mut tv_cache = TyVarCache::new(self.level, self);
self.detach(qt, &mut tv_cache)
} else {
qt
};
if let Err(errs) = self.undoable_sub_unify(&st, &qt, &(), None) {
log!(err "{errs}");
}
Ok(())
@ -1913,7 +1921,13 @@ impl Context {
if !st.is_unbound_var() || !st.is_generalized() {
self.overwrite_typarams(&qt, &st)?;
}
if let Err(errs) = self.sub_unify(&st, &qt, &(), None) {
let qt = if qt.has_undoable_linked_var() {
let mut tv_cache = TyVarCache::new(self.level, self);
self.detach(qt, &mut tv_cache)
} else {
qt
};
if let Err(errs) = self.undoable_sub_unify(&st, &qt, &(), None) {
log!(err "{errs}");
}
Ok(())

View file

@ -4,7 +4,7 @@ use erg_common::consts::DEBUG_MODE;
use erg_common::set::Set;
use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_common::{dict, fn_name, set};
use erg_common::{dict, fn_name, get_hash, set};
#[allow(unused_imports)]
use erg_common::{fmt_vec, log};
@ -912,6 +912,8 @@ impl Context {
}
fn poly_class_trait_impl_exists(&self, class: &Type, trait_: &Type) -> bool {
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_)
@ -920,10 +922,22 @@ impl Context {
{
Self::undo_substitute_typarams(&imp.sub_type);
Self::undo_substitute_typarams(&imp.sup_trait);
if class_hash != get_hash(&class) {
class.undo();
}
if trait_hash != get_hash(&trait_) {
trait_.undo();
}
return true;
}
Self::undo_substitute_typarams(&imp.sub_type);
Self::undo_substitute_typarams(&imp.sup_trait);
if class_hash != get_hash(&class) {
class.undo();
}
if trait_hash != get_hash(&trait_) {
trait_.undo();
}
}
false
}

View file

@ -94,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)`.

View file

@ -836,13 +836,21 @@ impl Context {
self.instantiate_acc(acc, erased_idx, tmp_tv_cache, not_found_is_qvar)
}
ast::ConstExpr::App(app) => {
let ast::ConstAccessor::Local(ident) = &app.acc else {
return type_feature_error!(self, app.loc(), "instantiating const callee");
};
let &ctx = self
.get_singular_ctxs_by_ident(ident, self)?
.first()
.unwrap_or(&self);
let obj = self.instantiate_const_expr(
&app.obj,
erased_idx,
tmp_tv_cache,
not_found_is_qvar,
)?;
let ctx = self
.get_singular_ctxs(&app.obj.clone().downgrade(), self)
.ok()
.and_then(|ctxs| ctxs.first().copied())
.or_else(|| {
let typ = self.get_tp_t(&obj).ok()?;
self.get_nominal_type_ctx(&typ).map(|(_, ctx)| ctx)
})
.unwrap_or(self);
let mut args = vec![];
for (i, arg) in app.args.pos_args().enumerate() {
let arg_t = self.instantiate_const_expr(
@ -853,9 +861,14 @@ impl Context {
)?;
args.push(arg_t);
}
if ctx.kind.is_type() && !ctx.params.is_empty() {
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))
}
}
@ -1240,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) => {
@ -1248,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
@ -1299,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!(
@ -1568,7 +1582,20 @@ impl Context {
mode,
not_found_is_qvar,
)?;
let pred = self.instantiate_pred(&refine.pred, tmp_tv_cache)?;
let name = VarName::new(refine.var.clone());
let tp = TyParam::named_free_var(
refine.var.inspect().clone(),
self.level,
Constraint::new_type_of(t.clone()),
);
tmp_tv_cache.push_or_init_typaram(&name, &tp, self);
let pred = self
.instantiate_pred(&refine.pred, tmp_tv_cache)
.map_err(|err| {
tmp_tv_cache.remove(name.inspect());
err
})?;
tmp_tv_cache.remove(name.inspect());
let refine =
Type::Refinement(RefinementType::new(refine.var.inspect().clone(), t, pred));
Ok(refine)

View file

@ -20,6 +20,7 @@ use std::option::Option; // conflicting to Type::Option
use std::path::{Path, PathBuf};
use erg_common::config::ErgConfig;
use erg_common::consts::DEBUG_MODE;
use erg_common::consts::PYTHON_MODE;
use erg_common::dict::Dict;
use erg_common::error::Location;
@ -140,13 +141,20 @@ impl ClassDefType {
ClassDefType::ImplTrait { class, impl_trait }
}
pub fn class(&self) -> &Type {
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,
@ -1130,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);
@ -1143,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() {
@ -1257,6 +1267,32 @@ impl Context {
.last()
.and_then(|caller| ControlKind::try_from(&caller[..]).ok())
}
pub(crate) fn check_types(&self) {
if DEBUG_MODE {
for (_, (t, ctx)) in self.poly_types.iter() {
if t.has_undoable_linked_var() {
panic!("{t} has undoable linked vars");
}
ctx.check_types();
}
for (typ, methods) in self.methods_list.iter() {
if typ.get_class().has_undoable_linked_var() {
panic!("{typ} has undoable linked vars");
}
if typ
.get_impl_trait()
.is_some_and(|t| t.has_undoable_linked_var())
{
panic!("{typ} has undoable linked vars");
}
methods.check_types();
}
if let Some(outer) = self.get_outer() {
outer.check_types();
}
}
}
}
#[derive(Debug, Clone, Default)]

View file

@ -313,7 +313,7 @@ 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.replace_constraint(new_constraint, self.undoable, false);
}
} else {
maybe_sub.link(sup_tp, self.undoable);
@ -353,7 +353,7 @@ 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.replace_constraint(new_constraint, self.undoable, false);
}
} else {
maybe_sup.link(sub_tp, self.undoable);
@ -589,12 +589,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.replace_constraint(new_constr, self.undoable, false);
}
Ok(())
}
@ -777,20 +777,20 @@ 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_sub.replace_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_sup.replace_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_sup.replace_constraint(new_constraint, self.undoable, false);
maybe_sub.link(maybe_sup, self.undoable);
} else {
sub_fv.update_constraint(new_constraint, false);
maybe_sup.replace_constraint(new_constraint, self.undoable, false);
maybe_sup.link(maybe_sub, self.undoable);
}
}
@ -869,7 +869,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
maybe_sup.link(&union, self.undoable);
} else {
let new_constraint = Constraint::new_sandwiched(union, intersec);
sup_fv.update_constraint(new_constraint, false);
maybe_sup.replace_constraint(new_constraint, self.undoable, false);
}
}
// (Int or ?T) <: (?U or Int)
@ -955,14 +955,14 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
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.replace_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.replace_constraint(constr, self.undoable, true);
} else {
todo!("{maybe_sub} <: {maybe_sup}")
}
@ -1022,14 +1022,14 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
maybe_sub.link(&sub, self.undoable);
} else {
let constr = Constraint::new_sandwiched(sub, new_sup);
sub_fv.update_constraint(constr, true);
maybe_sub.replace_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.replace_constraint(constr, self.undoable, true);
} else {
todo!("{maybe_sub} <: {maybe_sup}")
}
@ -1458,4 +1458,15 @@ 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)
}
}

View file

@ -10,10 +10,10 @@ 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, mono_q_tp, poly, v_enum};
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};
@ -264,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(

View file

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

View file

@ -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;
@ -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(

View file

@ -849,7 +849,7 @@ 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.

View file

@ -43,7 +43,7 @@ pub use value::ValueObj;
use value::ValueObj::{Inf, NegInf};
pub use vis::*;
use self::constructors::{bounded, proj_call, 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;
@ -3194,6 +3194,46 @@ impl Type {
}
}
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 replace_constraint(
&self,
new_constraint: Constraint,
undoable: bool,
in_instantiation: bool,
) {
if undoable {
self.undoable_update_constraint(new_constraint);
} else {
self.update_constraint(new_constraint, in_instantiation);
}
}
fn inc_undo_count(&self) {
match self {
Self::FreeVar(fv) => fv.inc_undo_count(),

View file

@ -877,11 +877,15 @@ impl TyParam {
}
}
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))
}
@ -1297,6 +1301,43 @@ impl TyParam {
}
}
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 replace_constraint(
&self,
new_constraint: Constraint,
undoable: bool,
in_instantiation: bool,
) {
if undoable {
self.undoable_update_constraint(new_constraint);
} else {
self.update_constraint(new_constraint, in_instantiation);
}
}
fn inc_undo_count(&self) {
match self {
Self::FreeVar(fv) => fv.inc_undo_count(),

View file

@ -2022,14 +2022,20 @@ 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, ")")
}
}
@ -2038,20 +2044,31 @@ 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())
}
}
}

View file

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