feat: eval const call/lambda in refinement types

rename: `Shape` -> `HasShape`
This commit is contained in:
Shunsuke Shibayama 2024-01-29 18:10:26 +09:00
parent d59a4e82d3
commit d1fa616aea
11 changed files with 252 additions and 70 deletions

View file

@ -20,8 +20,8 @@ use erg_parser::desugar::Desugarer;
use erg_parser::token::{Token, TokenKind};
use crate::ty::constructors::{
array_t, bounded, closed_range, dict_t, mono, mono_q, named_free_var, poly, proj, proj_call,
ref_, ref_mut, refinement, set_t, subr_t, subtypeof, tp_enum, try_v_enum, tuple_t,
array_t, bounded, closed_range, dict_t, func, mono, mono_q, named_free_var, poly, proj,
proj_call, ref_, ref_mut, refinement, set_t, subr_t, subtypeof, tp_enum, try_v_enum, tuple_t,
unknown_len_array_t, v_enum,
};
use crate::ty::free::HasLevel;
@ -34,7 +34,7 @@ use crate::ty::{
use crate::context::instantiate_spec::ParamKind;
use crate::context::{ClassDefType, Context, ContextKind, RegistrationMode};
use crate::error::{EvalError, EvalErrors, EvalResult, SingleEvalResult};
use crate::varinfo::VarInfo;
use crate::varinfo::{AbsLocation, VarInfo};
use super::instantiate::TyVarCache;
use Type::{Failure, Never, Subr};
@ -485,10 +485,10 @@ impl Context {
}
fn eval_const_ident(&self, ident: &Identifier) -> EvalResult<ValueObj> {
if let Some(val) = self.rec_get_const_obj(ident.inspect()) {
Ok(val.clone())
} else if let Some(val) = self.get_value_from_tv_cache(ident) {
if let Some(val) = self.get_value_from_tv_cache(ident) {
Ok(val)
} else if let Some(val) = self.rec_get_const_obj(ident.inspect()) {
Ok(val.clone())
} else if self.kind.is_subr() {
feature_error!(self, ident.loc(), "const parameters")
} else if ident.is_const() {
@ -982,6 +982,35 @@ impl Context {
self.shared.clone(),
self.clone(),
);
for non_default in non_default_params.iter() {
let name = non_default
.name()
.map(|name| VarName::from_str(name.clone()));
let vi = VarInfo::nd_parameter(
non_default.typ().clone(),
AbsLocation::unknown(),
lambda_ctx.name.clone(),
);
lambda_ctx.params.push((name, vi));
}
if let Some(var_param) = var_params.as_ref() {
let name = var_param.name().map(|name| VarName::from_str(name.clone()));
let vi = VarInfo::nd_parameter(
var_param.typ().clone(),
AbsLocation::unknown(),
lambda_ctx.name.clone(),
);
lambda_ctx.params.push((name, vi));
}
for default in default_params.iter() {
let name = default.name().map(|name| VarName::from_str(name.clone()));
let vi = VarInfo::d_parameter(
default.typ().clone(),
AbsLocation::unknown(),
lambda_ctx.name.clone(),
);
lambda_ctx.params.push((name, vi));
}
let return_t = v_enum(set! {lambda_ctx.eval_const_block(&lambda.body)?});
let sig_t = subr_t(
SubrKind::from(lambda.op.kind),
@ -1444,7 +1473,7 @@ impl Context {
Ok(TyParam::app(name, args))
}
} else {
log!(err "eval_app({name}({}))", fmt_vec(&args));
log!(err "failed: eval_app({name}({}))", fmt_vec(&args));
Ok(TyParam::app(name, args))
}
}
@ -1531,13 +1560,15 @@ impl Context {
}
}
fn _eval_tp_into_value(&self, tp: TyParam) -> EvalResult<ValueObj> {
fn eval_tp_into_value(&self, tp: TyParam) -> EvalResult<ValueObj> {
self.eval_tp(tp).and_then(|tp| {
self.convert_tp_into_value(tp).map_err(|_| {
EvalErrors::from(EvalError::unreachable(
self.convert_tp_into_value(tp).map_err(|err| {
EvalErrors::from(EvalError::feature_error(
self.cfg.input.clone(),
fn_name!(),
line!(),
line!() as usize,
Location::Unknown,
&format!("convert {err} into a value"),
self.caused_by(),
))
})
})
@ -2014,6 +2045,37 @@ impl Context {
}
Ok(ValueObj::Set(new))
}
TyParam::App { name, args } => {
let mut new = vec![];
for elem in args.iter() {
let elem = self.convert_tp_into_value(elem.clone())?;
new.push(elem);
}
let Some(ValueObj::Subr(subr)) = self.rec_get_const_obj(&name) else {
return Err(TyParam::App { name, args });
};
let new = ValueArgs::pos_only(new);
match self.call(subr.clone(), new, Location::Unknown) {
Ok(TyParam::Value(val)) => Ok(val),
_ => Err(TyParam::App { name, args }),
}
}
TyParam::Lambda(lambda) => {
let name = Str::from("<lambda>");
let params = lambda.const_.sig.params;
let block = lambda.const_.body;
let sig_t = func(
lambda.nd_params,
lambda.var_params,
lambda.d_params,
lambda.kw_var_params,
// TODO:
Type::Obj,
);
Ok(ValueObj::Subr(ConstSubr::User(UserConstSubr::new(
name, params, block, sig_t,
))))
}
other => self.convert_tp_into_type(other).map(ValueObj::builtin_type),
}
}
@ -2372,6 +2434,38 @@ impl Context {
}
}
fn convert_args(
&self,
lhs: Option<TyParam>,
subr: &ConstSubr,
args: Vec<TyParam>,
t_loc: &impl Locational,
) -> EvalResult<ValueArgs> {
let mut pos_args = vec![];
if subr.sig_t().is_method() {
let Some(lhs) = lhs else {
return feature_error!(self, t_loc.loc(), "??");
};
if let Ok(value) = ValueObj::try_from(lhs.clone()) {
pos_args.push(value);
} else if let Ok(value) = self.eval_tp_into_value(lhs) {
pos_args.push(value);
} else {
return feature_error!(self, t_loc.loc(), "??");
}
}
for pos_arg in args.into_iter() {
if let Ok(value) = ValueObj::try_from(pos_arg.clone()) {
pos_args.push(value);
} else if let Ok(value) = self.eval_tp_into_value(pos_arg) {
pos_args.push(value);
} else {
return feature_error!(self, t_loc.loc(), "??");
}
}
Ok(ValueArgs::new(pos_args, dict! {}))
}
fn do_proj_call(
&self,
obj: ValueObj,
@ -2380,28 +2474,7 @@ impl Context {
t_loc: &impl Locational,
) -> EvalResult<TyParam> {
if let ValueObj::Subr(subr) = obj {
let mut pos_args = vec![];
if subr.sig_t().is_method() {
match ValueObj::try_from(lhs) {
Ok(value) => {
pos_args.push(value);
}
Err(_) => {
return feature_error!(self, t_loc.loc(), "??");
}
}
}
for pos_arg in args.into_iter() {
match ValueObj::try_from(pos_arg) {
Ok(value) => {
pos_args.push(value);
}
Err(_) => {
return feature_error!(self, t_loc.loc(), "??");
}
}
}
let args = ValueArgs::new(pos_args, dict! {});
let args = self.convert_args(Some(lhs), &subr, args, t_loc)?;
let tp = self.call(subr, args, t_loc.loc())?;
Ok(tp)
} else {
@ -2578,6 +2651,35 @@ impl Context {
}
}
pub(crate) fn eval_call(
&self,
lhs: TyParam,
args: Vec<TyParam>,
t_loc: &impl Locational,
) -> EvalResult<TyParam> {
match lhs {
/*TyParam::Lambda(lambda) => {
todo!("{lambda}")
}*/
TyParam::Value(ValueObj::Subr(subr)) => {
let args = self.convert_args(None, &subr, args, t_loc)?;
self.call(subr, args, t_loc.loc())
}
other => Err(EvalErrors::from(EvalError::type_mismatch_error(
self.cfg.input.clone(),
line!() as usize,
t_loc.loc(),
self.caused_by(),
&other.qual_name().unwrap_or(Str::from("_")),
None,
&mono("Callable"),
&self.get_tp_t(&other).ok().unwrap_or(Type::Obj),
None,
None,
))),
}
}
pub(crate) fn bool_eval_pred(&self, p: Predicate) -> EvalResult<bool> {
let evaled = self.eval_pred(p)?;
Ok(matches!(evaled, Predicate::Value(ValueObj::Bool(true))))
@ -2596,12 +2698,12 @@ impl Context {
for arg in args {
new_args.push(self.eval_tp(arg)?);
}
let t = if let Some(name) = name {
let tp = if let Some(name) = name {
self.eval_proj_call(receiver, name, new_args, &())?
} else {
return feature_error!(self, Location::Unknown, "eval_pred: Predicate::Call");
self.eval_call(receiver, new_args, &())?
};
if let TyParam::Value(v) = t {
if let Ok(v) = self.convert_tp_into_value(tp) {
Ok(Predicate::Value(v))
} else {
feature_error!(self, Location::Unknown, "eval_pred: Predicate::Call")

View file

@ -676,7 +676,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
self.ctx
.eval_proj_call(receiver.clone(), name.clone(), new_args.clone(), &())
} else {
return Ok(Predicate::call(receiver, name, new_args));
self.ctx.eval_call(receiver.clone(), new_args.clone(), &())
};
match evaled {
Ok(TyParam::Value(value)) => Ok(Predicate::Value(value)),

View file

@ -1516,7 +1516,7 @@ impl Context {
.register_marker_trait(
self,
poly(
SHAPE,
HAS_SHAPE,
vec![ty_tp(arr_t.clone()).proj_call(FUNC_SHAPE.into(), vec![])],
),
)

View file

@ -98,7 +98,7 @@ const COLLECTION: &str = "Collection";
const INDEXABLE: &str = "Indexable";
const MAPPING: &str = "Mapping";
const MUTABLE_MAPPING: &str = "Mapping!";
const SHAPE: &str = "Shape";
const HAS_SHAPE: &str = "HasShape";
const EQ: &str = "Eq";
const HASH: &str = "Hash";
const EQ_HASH: &str = "EqHash";

View file

@ -356,10 +356,10 @@ impl Context {
Visibility::BUILTIN_PUBLIC,
Some(FUNDAMENTAL_EXIT),
);
/* Shape */
/* HasShape */
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);
let has_shape = Self::builtin_poly_trait(HAS_SHAPE, params.clone(), 2);
/* Num */
let R = mono_q(TY_R, instanceof(Type));
let params = vec![PS::t(TY_R, false, WithDefault)];
@ -551,7 +551,13 @@ impl Context {
Const,
None,
);
self.register_builtin_type(poly(SHAPE, vec![S]), shape, vis.clone(), Const, None);
self.register_builtin_type(
poly(HAS_SHAPE, vec![S]),
has_shape,
vis.clone(),
Const,
None,
);
self.register_builtin_type(poly(ADD, ty_params.clone()), add, vis.clone(), Const, None);
self.register_builtin_type(poly(SUB, ty_params.clone()), sub, vis.clone(), Const, None);
self.register_builtin_type(poly(MUL, ty_params.clone()), mul, vis.clone(), Const, None);

View file

@ -158,9 +158,14 @@ impl TyVarCache {
self.already_appeared.insert(name);
}
pub(crate) fn push_or_init_tyvar(&mut self, name: &VarName, tv: &Type, ctx: &Context) {
pub(crate) fn push_or_init_tyvar(
&mut self,
name: &VarName,
tv: &Type,
ctx: &Context,
) -> TyCheckResult<()> {
if name.inspect() == "_" {
return;
return Ok(());
}
if let Some(inst) = self.tyvar_instances.get(name) {
self.update_tyvar(inst, tv, ctx);
@ -178,6 +183,7 @@ impl TyVarCache {
ctx.index().register(name.inspect().clone(), &vi);
self.var_infos.insert(name.clone(), vi);
}
Ok(())
}
pub(crate) fn dummy_push_or_init_tyvar(&mut self, name: &VarName, tv: &Type, ctx: &Context) {
@ -235,9 +241,24 @@ impl TyVarCache {
}
}
pub(crate) fn push_or_init_typaram(&mut self, name: &VarName, tp: &TyParam, ctx: &Context) {
pub(crate) fn push_or_init_typaram(
&mut self,
name: &VarName,
tp: &TyParam,
ctx: &Context,
) -> TyCheckResult<()> {
if name.inspect() == "_" {
return;
return Ok(());
}
if ctx.rec_get_const_obj(name.inspect()).is_some() {
return Err(TyCheckError::reassign_error(
ctx.cfg.input.clone(),
line!() as usize,
name.loc(),
ctx.caused_by(),
name.inspect(),
)
.into());
}
// FIXME:
if let Some(inst) = self.typaram_instances.get(name) {
@ -255,6 +276,7 @@ impl TyVarCache {
self.var_infos.insert(name.clone(), vi);
self.typaram_instances.insert(name.clone(), tp.clone());
}
Ok(())
}
pub(crate) fn push_refine_var(&mut self, name: &VarName, t: Type, ctx: &Context) {

View file

@ -22,6 +22,7 @@ use crate::ty::value::ValueObj;
use crate::ty::{constructors::*, Predicate, RefinementType, VisibilityModifier};
use crate::ty::{Field, HasType, ParamTy, SubrKind, SubrType, Type};
use crate::type_feature_error;
use crate::varinfo::{AbsLocation, VarInfo};
use TyParamOrdering::*;
use Type::*;
@ -125,8 +126,7 @@ impl Context {
// TODO: other than type `Type`
let constr = Constraint::new_type_of(Type);
let tv = named_free_var(name.inspect().clone(), self.level, constr);
tv_cache.push_or_init_tyvar(name, &tv, self);
Ok(())
tv_cache.push_or_init_tyvar(name, &tv, self)
}
TypeBoundSpec::NonDefault { lhs, spec } => {
let constr = match spec.op.kind {
@ -147,10 +147,10 @@ impl Context {
};
if constr.get_sub_sup().is_none() {
let tp = TyParam::named_free_var(lhs.inspect().clone(), self.level, constr);
tv_cache.push_or_init_typaram(lhs, &tp, self);
tv_cache.push_or_init_typaram(lhs, &tp, self)?;
} else {
let tv = named_free_var(lhs.inspect().clone(), self.level, constr);
tv_cache.push_or_init_tyvar(lhs, &tv, self);
tv_cache.push_or_init_tyvar(lhs, &tv, self)?;
}
Ok(())
}
@ -642,7 +642,7 @@ impl Context {
Ok(ctx.typ.clone())
} else if not_found_is_qvar {
let tyvar = named_free_var(Str::rc(other), self.level, Constraint::Uninited);
tmp_tv_cache.push_or_init_tyvar(&ident.name, &tyvar, self);
tmp_tv_cache.push_or_init_tyvar(&ident.name, &tyvar, self)?;
Ok(tyvar)
} else if let Some(decl_t) = opt_decl_t {
Ok(decl_t.typ().clone())
@ -836,7 +836,7 @@ impl Context {
Constraint::Uninited,
);
let varname = VarName::from_str(name);
tmp_tv_cache.push_or_init_typaram(&varname, &tp, self);
tmp_tv_cache.push_or_init_typaram(&varname, &tp, self)?;
Ok(tp)
} else {
Err(e)
@ -946,9 +946,15 @@ impl Context {
}
if not_found_is_qvar {
let tyvar = named_free_var(name.inspect().clone(), self.level, Constraint::Uninited);
tmp_tv_cache.push_or_init_tyvar(&name.name, &tyvar, self);
tmp_tv_cache.push_or_init_tyvar(&name.name, &tyvar, self)?;
return Ok(TyParam::t(tyvar));
}
if name.is_const() {
if let Some((_, vi)) = self.get_var_info(name.inspect()) {
self.inc_ref(name.inspect(), vi, name, self);
return Ok(TyParam::mono(name.inspect()));
}
}
Err(TyCheckErrors::from(TyCheckError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
@ -1227,10 +1233,50 @@ impl Context {
} else {
None
};
let mut lambda_ctx = Context::instant(
Str::ever("<lambda>"),
self.cfg.clone(),
0,
self.shared.clone(),
self.clone(),
);
for non_default in nd_params.iter() {
let name = non_default
.name()
.map(|name| VarName::from_str(name.clone()));
let vi = VarInfo::nd_parameter(
non_default.typ().clone(),
AbsLocation::unknown(),
lambda_ctx.name.clone(),
);
lambda_ctx.params.push((name, vi));
}
if let Some(var_param) = var_params.as_ref() {
let name = var_param.name().map(|name| VarName::from_str(name.clone()));
let vi = VarInfo::nd_parameter(
var_param.typ().clone(),
AbsLocation::unknown(),
lambda_ctx.name.clone(),
);
lambda_ctx.params.push((name, vi));
}
for default in d_params.iter() {
let name = default.name().map(|name| VarName::from_str(name.clone()));
let vi = VarInfo::d_parameter(
default.typ().clone(),
AbsLocation::unknown(),
lambda_ctx.name.clone(),
);
lambda_ctx.params.push((name, vi));
}
let mut body = vec![];
for expr in lambda.body.iter() {
let param =
self.instantiate_const_expr(expr, None, tmp_tv_cache, not_found_is_qvar)?;
let param = lambda_ctx.instantiate_const_expr(
expr,
None,
tmp_tv_cache,
not_found_is_qvar,
)?;
body.push(param);
}
tmp_tv_cache.purge(&_tmp_tv_cache);

View file

@ -1465,11 +1465,11 @@ impl Context {
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = &arg.expr {
if self.subtype_of(arg_t, &Type::Type) {
if let Ok(tv) = self.convert_tp_into_type(tp.clone()) {
tv_ctx.push_or_init_tyvar(&ident.name, &tv, self);
let _ = tv_ctx.push_or_init_tyvar(&ident.name, &tv, self);
continue;
}
}
tv_ctx.push_or_init_typaram(&ident.name, tp, self);
let _ = tv_ctx.push_or_init_typaram(&ident.name, tp, self);
}
}
}

View file

@ -366,10 +366,12 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
maybe_sub.link(sup_tp, self.undoable);
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::unreachable(
Err(TyCheckErrors::from(TyCheckError::feature_error(
self.ctx.cfg.input.clone(),
fn_name!(),
line!(),
line!() as usize,
self.loc.loc(),
&format!("unifying {sub_fv} and {sup_tp}"),
self.ctx.caused_by(),
)))
}
}
@ -412,10 +414,12 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
// self.sub_unify(&tp_t, &fv_t)
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::unreachable(
Err(TyCheckErrors::from(TyCheckError::feature_error(
self.ctx.cfg.input.clone(),
fn_name!(),
line!(),
line!() as usize,
self.loc.loc(),
&format!("unifying {sub_tp} and {sup_fv}"),
self.ctx.caused_by(),
)))
}
}
@ -523,10 +527,12 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
} else {
log!(err "{sup} does not have key {sub_k}");
// TODO:
return Err(TyCheckErrors::from(TyCheckError::unreachable(
return Err(TyCheckErrors::from(TyCheckError::feature_error(
self.ctx.cfg.input.clone(),
fn_name!(),
line!(),
line!() as usize,
self.loc.loc(),
&format!("unifying {sub} and {sup}"),
self.ctx.caused_by(),
)));
}
}

View file

@ -69,7 +69,7 @@
.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, S: [Nat; _]|(object: Iterable(T) and Shape(S),) -> .NDArray(T, S)
.array: |T, S: [Nat; _]|(object: Iterable(T) and HasShape(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

View file

@ -1785,13 +1785,13 @@ impl NestedDisplay for Call {
}
impl TryFrom<Expr> for Call {
type Error = Expr;
type Error = ();
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),
_ => Err(()),
}
}
}