mirror of
https://github.com/erg-lang/erg.git
synced 2025-10-03 05:54:33 +00:00
fix: array type inferring
This commit is contained in:
parent
142db9b697
commit
f4e1d494a4
15 changed files with 276 additions and 69 deletions
|
@ -274,7 +274,7 @@ impl Context {
|
||||||
if let Err(err) = self.overwrite_typarams(typ, rhs) {
|
if let Err(err) = self.overwrite_typarams(typ, rhs) {
|
||||||
Self::undo_substitute_typarams(typ);
|
Self::undo_substitute_typarams(typ);
|
||||||
if DEBUG_MODE {
|
if DEBUG_MODE {
|
||||||
panic!("err: {err}");
|
panic!("{typ} / {rhs}: err: {err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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![];
|
let mut elems = vec![];
|
||||||
match arr {
|
|
||||||
Array::Normal(arr) => {
|
|
||||||
for elem in arr.elems.pos_args().iter() {
|
for elem in arr.elems.pos_args().iter() {
|
||||||
let elem = self.eval_const_expr(&elem.expr)?;
|
let elem = self.eval_const_expr(&elem.expr)?;
|
||||||
elems.push(elem);
|
elems.push(elem);
|
||||||
}
|
}
|
||||||
Ok(ValueObj::Array(ArcArray::from(elems)))
|
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(
|
_ => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
|
@ -1847,7 +1849,13 @@ impl Context {
|
||||||
if st.has_no_unbound_var() && qt.has_no_unbound_var() {
|
if st.has_no_unbound_var() && qt.has_no_unbound_var() {
|
||||||
return Ok(());
|
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}");
|
log!(err "{errs}");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1913,7 +1921,13 @@ impl Context {
|
||||||
if !st.is_unbound_var() || !st.is_generalized() {
|
if !st.is_unbound_var() || !st.is_generalized() {
|
||||||
self.overwrite_typarams(&qt, &st)?;
|
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}");
|
log!(err "{errs}");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -4,7 +4,7 @@ use erg_common::consts::DEBUG_MODE;
|
||||||
use erg_common::set::Set;
|
use erg_common::set::Set;
|
||||||
use erg_common::traits::{Locational, Stream};
|
use erg_common::traits::{Locational, Stream};
|
||||||
use erg_common::Str;
|
use erg_common::Str;
|
||||||
use erg_common::{dict, fn_name, set};
|
use erg_common::{dict, fn_name, get_hash, set};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use erg_common::{fmt_vec, log};
|
use erg_common::{fmt_vec, log};
|
||||||
|
|
||||||
|
@ -912,6 +912,8 @@ impl Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poly_class_trait_impl_exists(&self, class: &Type, trait_: &Type) -> bool {
|
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() {
|
for imp in self.get_trait_impls(trait_).into_iter() {
|
||||||
self.substitute_typarams(&imp.sub_type, class).unwrap_or(());
|
self.substitute_typarams(&imp.sub_type, class).unwrap_or(());
|
||||||
self.substitute_typarams(&imp.sup_trait, trait_)
|
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.sub_type);
|
||||||
Self::undo_substitute_typarams(&imp.sup_trait);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
Self::undo_substitute_typarams(&imp.sub_type);
|
Self::undo_substitute_typarams(&imp.sub_type);
|
||||||
Self::undo_substitute_typarams(&imp.sup_trait);
|
Self::undo_substitute_typarams(&imp.sup_trait);
|
||||||
|
if class_hash != get_hash(&class) {
|
||||||
|
class.undo();
|
||||||
|
}
|
||||||
|
if trait_hash != get_hash(&trait_) {
|
||||||
|
trait_.undo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`).
|
/// 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)`.
|
/// TODO: This warning is currently disabled because it raises a false warning in cases like `|T|(x: T) -> (y: T) -> (x, y)`.
|
||||||
|
|
|
@ -836,13 +836,21 @@ impl Context {
|
||||||
self.instantiate_acc(acc, erased_idx, tmp_tv_cache, not_found_is_qvar)
|
self.instantiate_acc(acc, erased_idx, tmp_tv_cache, not_found_is_qvar)
|
||||||
}
|
}
|
||||||
ast::ConstExpr::App(app) => {
|
ast::ConstExpr::App(app) => {
|
||||||
let ast::ConstAccessor::Local(ident) = &app.acc else {
|
let obj = self.instantiate_const_expr(
|
||||||
return type_feature_error!(self, app.loc(), "instantiating const callee");
|
&app.obj,
|
||||||
};
|
erased_idx,
|
||||||
let &ctx = self
|
tmp_tv_cache,
|
||||||
.get_singular_ctxs_by_ident(ident, self)?
|
not_found_is_qvar,
|
||||||
.first()
|
)?;
|
||||||
.unwrap_or(&self);
|
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![];
|
let mut args = vec![];
|
||||||
for (i, arg) in app.args.pos_args().enumerate() {
|
for (i, arg) in app.args.pos_args().enumerate() {
|
||||||
let arg_t = self.instantiate_const_expr(
|
let arg_t = self.instantiate_const_expr(
|
||||||
|
@ -853,9 +861,14 @@ impl Context {
|
||||||
)?;
|
)?;
|
||||||
args.push(arg_t);
|
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)))
|
Ok(TyParam::t(poly(ctx.name.clone(), args)))
|
||||||
} else {
|
} 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))
|
Ok(TyParam::app(ident.inspect().clone(), args))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1240,7 +1253,7 @@ impl Context {
|
||||||
fn instantiate_pred(
|
fn instantiate_pred(
|
||||||
&self,
|
&self,
|
||||||
expr: &ast::ConstExpr,
|
expr: &ast::ConstExpr,
|
||||||
_tmp_tv_cache: &mut TyVarCache,
|
tmp_tv_cache: &mut TyVarCache,
|
||||||
) -> TyCheckResult<Predicate> {
|
) -> TyCheckResult<Predicate> {
|
||||||
match expr {
|
match expr {
|
||||||
ast::ConstExpr::Lit(lit) => {
|
ast::ConstExpr::Lit(lit) => {
|
||||||
|
@ -1248,11 +1261,12 @@ impl Context {
|
||||||
Ok(Predicate::Value(value))
|
Ok(Predicate::Value(value))
|
||||||
}
|
}
|
||||||
ast::ConstExpr::Accessor(ast::ConstAccessor::Local(local)) => {
|
ast::ConstExpr::Accessor(ast::ConstAccessor::Local(local)) => {
|
||||||
|
self.inc_ref_local(local, self, tmp_tv_cache);
|
||||||
Ok(Predicate::Const(local.inspect().clone()))
|
Ok(Predicate::Const(local.inspect().clone()))
|
||||||
}
|
}
|
||||||
ast::ConstExpr::BinOp(bin) => {
|
ast::ConstExpr::BinOp(bin) => {
|
||||||
let lhs = self.instantiate_pred(&bin.lhs, _tmp_tv_cache)?;
|
let lhs = self.instantiate_pred(&bin.lhs, tmp_tv_cache)?;
|
||||||
let rhs = self.instantiate_pred(&bin.rhs, _tmp_tv_cache)?;
|
let rhs = self.instantiate_pred(&bin.rhs, tmp_tv_cache)?;
|
||||||
match bin.op.kind {
|
match bin.op.kind {
|
||||||
TokenKind::DblEq
|
TokenKind::DblEq
|
||||||
| TokenKind::NotEq
|
| TokenKind::NotEq
|
||||||
|
@ -1299,7 +1313,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ConstExpr::UnaryOp(unop) => {
|
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 {
|
match unop.op.kind {
|
||||||
TokenKind::PreBitNot => Ok(!pred),
|
TokenKind::PreBitNot => Ok(!pred),
|
||||||
_ => type_feature_error!(
|
_ => type_feature_error!(
|
||||||
|
@ -1568,7 +1582,20 @@ impl Context {
|
||||||
mode,
|
mode,
|
||||||
not_found_is_qvar,
|
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 =
|
let refine =
|
||||||
Type::Refinement(RefinementType::new(refine.var.inspect().clone(), t, pred));
|
Type::Refinement(RefinementType::new(refine.var.inspect().clone(), t, pred));
|
||||||
Ok(refine)
|
Ok(refine)
|
||||||
|
|
|
@ -20,6 +20,7 @@ use std::option::Option; // conflicting to Type::Option
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use erg_common::config::ErgConfig;
|
use erg_common::config::ErgConfig;
|
||||||
|
use erg_common::consts::DEBUG_MODE;
|
||||||
use erg_common::consts::PYTHON_MODE;
|
use erg_common::consts::PYTHON_MODE;
|
||||||
use erg_common::dict::Dict;
|
use erg_common::dict::Dict;
|
||||||
use erg_common::error::Location;
|
use erg_common::error::Location;
|
||||||
|
@ -140,13 +141,20 @@ impl ClassDefType {
|
||||||
ClassDefType::ImplTrait { class, impl_trait }
|
ClassDefType::ImplTrait { class, impl_trait }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn class(&self) -> &Type {
|
pub fn get_class(&self) -> &Type {
|
||||||
match self {
|
match self {
|
||||||
ClassDefType::Simple(class) => class,
|
ClassDefType::Simple(class) => class,
|
||||||
ClassDefType::ImplTrait { 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 {
|
pub fn is_class_of(&self, t: &Type) -> bool {
|
||||||
match self {
|
match self {
|
||||||
ClassDefType::Simple(class) => class == t,
|
ClassDefType::Simple(class) => class == t,
|
||||||
|
@ -1130,6 +1138,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Context {
|
pub fn pop(&mut self) -> Context {
|
||||||
|
self.check_types();
|
||||||
if let Some(parent) = self.outer.as_mut() {
|
if let Some(parent) = self.outer.as_mut() {
|
||||||
let parent = mem::take(parent);
|
let parent = mem::take(parent);
|
||||||
let ctx = mem::take(self);
|
let ctx = mem::take(self);
|
||||||
|
@ -1143,6 +1152,7 @@ impl Context {
|
||||||
|
|
||||||
/// unlike `pop`, `outer` must be `None`.
|
/// unlike `pop`, `outer` must be `None`.
|
||||||
pub fn pop_mod(&mut self) -> Option<Context> {
|
pub fn pop_mod(&mut self) -> Option<Context> {
|
||||||
|
self.check_types();
|
||||||
if self.outer.is_some() {
|
if self.outer.is_some() {
|
||||||
log!(err "not in the top-level context");
|
log!(err "not in the top-level context");
|
||||||
if self.kind.is_module() {
|
if self.kind.is_module() {
|
||||||
|
@ -1257,6 +1267,32 @@ impl Context {
|
||||||
.last()
|
.last()
|
||||||
.and_then(|caller| ControlKind::try_from(&caller[..]).ok())
|
.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)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
|
@ -313,7 +313,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
|
||||||
.is_sub_constraint_of(&sub_fv.constraint().unwrap(), &new_constraint)
|
.is_sub_constraint_of(&sub_fv.constraint().unwrap(), &new_constraint)
|
||||||
|| sub_fv.constraint().unwrap().get_type() == Some(&Type)
|
|| 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 {
|
} else {
|
||||||
maybe_sub.link(sup_tp, self.undoable);
|
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)
|
.is_sub_constraint_of(&sup_fv.constraint().unwrap(), &new_constraint)
|
||||||
|| sup_fv.constraint().unwrap().get_type() == Some(&Type)
|
|| 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 {
|
} else {
|
||||||
maybe_sup.link(sub_tp, self.undoable);
|
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<()> {
|
fn coerce_greater_than(&self, target: &TyParam, value: &TyParam) -> TyCheckResult<()> {
|
||||||
match target {
|
match target {
|
||||||
TyParam::FreeVar(fv) => {
|
TyParam::FreeVar(_fv) => {
|
||||||
if let Ok(evaled) = self.ctx.eval_tp(value.clone()) {
|
if let Ok(evaled) = self.ctx.eval_tp(value.clone()) {
|
||||||
let pred = Predicate::ge(FRESH_GEN.fresh_varname(), evaled);
|
let pred = Predicate::ge(FRESH_GEN.fresh_varname(), evaled);
|
||||||
let new_type = self.ctx.type_from_pred(pred);
|
let new_type = self.ctx.type_from_pred(pred);
|
||||||
let new_constr = Constraint::new_type_of(Type::from(new_type));
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -777,20 +777,20 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
|
||||||
.cmp(&sup_fv.level().unwrap_or(GENERIC_LEVEL))
|
.cmp(&sup_fv.level().unwrap_or(GENERIC_LEVEL))
|
||||||
{
|
{
|
||||||
std::cmp::Ordering::Less => {
|
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);
|
maybe_sup.link(maybe_sub, self.undoable);
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Greater => {
|
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);
|
maybe_sub.link(maybe_sup, self.undoable);
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Equal => {
|
std::cmp::Ordering::Equal => {
|
||||||
// choose named one
|
// choose named one
|
||||||
if sup_fv.is_named_unbound() {
|
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);
|
maybe_sub.link(maybe_sup, self.undoable);
|
||||||
} else {
|
} else {
|
||||||
sub_fv.update_constraint(new_constraint, false);
|
maybe_sup.replace_constraint(new_constraint, self.undoable, false);
|
||||||
maybe_sup.link(maybe_sub, self.undoable);
|
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);
|
maybe_sup.link(&union, self.undoable);
|
||||||
} else {
|
} else {
|
||||||
let new_constraint = Constraint::new_sandwiched(union, intersec);
|
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)
|
// (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
|
maybe_sup.link(&new_sub, self.undoable); // Bool <: ?T <: Bool or Y ==> ?T == Bool
|
||||||
} else {
|
} else {
|
||||||
let constr = Constraint::new_sandwiched(new_sub, mem::take(&mut sup));
|
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) */)
|
// sub_unify(Nat, ?T(: Type)): (/* ?T(:> Nat) */)
|
||||||
else if let Some(ty) = sup_fv.get_type() {
|
else if let Some(ty) = sup_fv.get_type() {
|
||||||
if self.ctx.supertype_of(&Type, &ty) {
|
if self.ctx.supertype_of(&Type, &ty) {
|
||||||
let constr = Constraint::new_supertype_of(maybe_sub.clone());
|
let constr = Constraint::new_supertype_of(maybe_sub.clone());
|
||||||
sup_fv.update_constraint(constr, true);
|
maybe_sup.replace_constraint(constr, self.undoable, true);
|
||||||
} else {
|
} else {
|
||||||
todo!("{maybe_sub} <: {maybe_sup}")
|
todo!("{maybe_sub} <: {maybe_sup}")
|
||||||
}
|
}
|
||||||
|
@ -1022,14 +1022,14 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> {
|
||||||
maybe_sub.link(&sub, self.undoable);
|
maybe_sub.link(&sub, self.undoable);
|
||||||
} else {
|
} else {
|
||||||
let constr = Constraint::new_sandwiched(sub, new_sup);
|
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))
|
// sub_unify(?T(: Type), Int): (?T(<: Int))
|
||||||
else if let Some(ty) = sub_fv.get_type() {
|
else if let Some(ty) = sub_fv.get_type() {
|
||||||
if self.ctx.supertype_of(&Type, &ty) {
|
if self.ctx.supertype_of(&Type, &ty) {
|
||||||
let constr = Constraint::new_subtype_of(maybe_sup.clone());
|
let constr = Constraint::new_subtype_of(maybe_sup.clone());
|
||||||
sub_fv.update_constraint(constr, true);
|
maybe_sub.replace_constraint(constr, self.undoable, true);
|
||||||
} else {
|
} else {
|
||||||
todo!("{maybe_sub} <: {maybe_sup}")
|
todo!("{maybe_sub} <: {maybe_sup}")
|
||||||
}
|
}
|
||||||
|
@ -1458,4 +1458,15 @@ impl Context {
|
||||||
let unifier = Unifier::new(self, loc, false, param_name.cloned());
|
let unifier = Unifier::new(self, loc, false, param_name.cloned());
|
||||||
unifier.sub_unify(maybe_sub, maybe_sup)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@ use erg_parser::desugar::Desugarer;
|
||||||
use crate::context::instantiate::TyVarCache;
|
use crate::context::instantiate::TyVarCache;
|
||||||
use crate::context::{ClassDefType, Context, MethodPair, TraitImpl};
|
use crate::context::{ClassDefType, Context, MethodPair, TraitImpl};
|
||||||
use crate::lower::ASTLowerer;
|
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::free::{Constraint, HasLevel};
|
||||||
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
|
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::compile::AccessKind;
|
||||||
use crate::error::{LowerError, LowerErrors, LowerResult};
|
use crate::error::{LowerError, LowerErrors, LowerResult};
|
||||||
|
@ -264,11 +264,9 @@ impl ASTLowerer {
|
||||||
elems.push(hir::PosArg::new(elem));
|
elems.push(hir::PosArg::new(elem));
|
||||||
}
|
}
|
||||||
let elems = hir::Args::new(elems, None, vec![], None);
|
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(
|
Ok(hir::Array::Normal(hir::NormalArray::new(
|
||||||
arr.l_sqbr,
|
arr.l_sqbr, arr.r_sqbr, t, elems,
|
||||||
arr.r_sqbr,
|
|
||||||
Type::Failure,
|
|
||||||
elems,
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
other => Err(LowerErrors::from(LowerError::declare_error(
|
other => Err(LowerErrors::from(LowerError::declare_error(
|
||||||
|
|
|
@ -20,7 +20,7 @@ use erg_parser::ast::{
|
||||||
};
|
};
|
||||||
use erg_parser::token::{Token, TokenKind, DOT};
|
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::typaram::TyParam;
|
||||||
use crate::ty::value::{GenTypeObj, ValueObj};
|
use crate::ty::value::{GenTypeObj, ValueObj};
|
||||||
use crate::ty::{Field, HasType, Type, VisibilityModifier};
|
use crate::ty::{Field, HasType, Type, VisibilityModifier};
|
||||||
|
@ -774,8 +774,7 @@ impl_locational!(NormalArray, l_sqbr, elems, r_sqbr);
|
||||||
impl_t!(NormalArray);
|
impl_t!(NormalArray);
|
||||||
|
|
||||||
impl NormalArray {
|
impl NormalArray {
|
||||||
pub fn new(l_sqbr: Token, r_sqbr: Token, elem_t: Type, elems: Args) -> Self {
|
pub fn new(l_sqbr: Token, r_sqbr: Token, t: Type, elems: Args) -> Self {
|
||||||
let t = array_t(elem_t, TyParam::value(elems.len()));
|
|
||||||
Self {
|
Self {
|
||||||
l_sqbr,
|
l_sqbr,
|
||||||
r_sqbr,
|
r_sqbr,
|
||||||
|
|
|
@ -26,7 +26,7 @@ use crate::artifact::{CompleteArtifact, IncompleteArtifact};
|
||||||
use crate::context::instantiate::TyVarCache;
|
use crate::context::instantiate::TyVarCache;
|
||||||
use crate::module::SharedCompilerResource;
|
use crate::module::SharedCompilerResource;
|
||||||
use crate::ty::constructors::{
|
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::free::Constraint;
|
||||||
use crate::ty::typaram::TyParam;
|
use crate::ty::typaram::TyParam;
|
||||||
|
@ -290,6 +290,7 @@ impl ASTLowerer {
|
||||||
fn lower_normal_array(&mut self, array: ast::NormalArray) -> LowerResult<hir::NormalArray> {
|
fn lower_normal_array(&mut self, array: ast::NormalArray) -> LowerResult<hir::NormalArray> {
|
||||||
log!(info "entered {}({array})", fn_name!());
|
log!(info "entered {}({array})", fn_name!());
|
||||||
let mut new_array = vec![];
|
let mut new_array = vec![];
|
||||||
|
let eval_result = self.module.context.eval_const_normal_array(&array);
|
||||||
let (elems, ..) = array.elems.deconstruct();
|
let (elems, ..) = array.elems.deconstruct();
|
||||||
let mut union = Type::Never;
|
let mut union = Type::Never;
|
||||||
for elem in elems.into_iter() {
|
for elem in elems.into_iter() {
|
||||||
|
@ -330,12 +331,14 @@ impl ASTLowerer {
|
||||||
} else {
|
} else {
|
||||||
union
|
union
|
||||||
};
|
};
|
||||||
Ok(hir::NormalArray::new(
|
let elems = hir::Args::values(new_array, None);
|
||||||
array.l_sqbr,
|
let t = array_t(elem_t, TyParam::value(elems.len()));
|
||||||
array.r_sqbr,
|
let t = if let Ok(value) = eval_result {
|
||||||
elem_t,
|
singleton(t, TyParam::Value(value))
|
||||||
hir::Args::values(new_array, None),
|
} else {
|
||||||
))
|
t
|
||||||
|
};
|
||||||
|
Ok(hir::NormalArray::new(array.l_sqbr, array.r_sqbr, t, elems))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_array_with_length(
|
fn lower_array_with_length(
|
||||||
|
|
|
@ -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
|
/// interior-mut
|
||||||
/// SAFETY: use `Type/TyParam::link` instead of this.
|
/// SAFETY: use `Type/TyParam::link` instead of this.
|
||||||
/// This method may cause circular references.
|
/// This method may cause circular references.
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub use value::ValueObj;
|
||||||
use value::ValueObj::{Inf, NegInf};
|
use value::ValueObj::{Inf, NegInf};
|
||||||
pub use vis::*;
|
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 STR_OMIT_THRESHOLD: usize = 16;
|
||||||
pub const CONTAINER_OMIT_THRESHOLD: usize = 8;
|
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) {
|
fn inc_undo_count(&self) {
|
||||||
match self {
|
match self {
|
||||||
Self::FreeVar(fv) => fv.inc_undo_count(),
|
Self::FreeVar(fv) => fv.inc_undo_count(),
|
||||||
|
|
|
@ -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);
|
let constraint = Constraint::new_type_of(t);
|
||||||
Self::FreeVar(FreeTyParam::new_unbound(level, constraint))
|
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 {
|
pub fn named_free_var(name: Str, level: usize, constr: Constraint) -> Self {
|
||||||
Self::FreeVar(FreeTyParam::new_named_unbound(name, level, constr))
|
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) {
|
fn inc_undo_count(&self) {
|
||||||
match self {
|
match self {
|
||||||
Self::FreeVar(fv) => fv.inc_undo_count(),
|
Self::FreeVar(fv) => fv.inc_undo_count(),
|
||||||
|
|
|
@ -2022,14 +2022,20 @@ impl ConstUnaryOp {
|
||||||
/// ex. `Vec Int` of `Option Vec Int`
|
/// ex. `Vec Int` of `Option Vec Int`
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct ConstApp {
|
pub struct ConstApp {
|
||||||
pub acc: ConstAccessor,
|
pub obj: Box<ConstExpr>,
|
||||||
|
pub attr_name: Option<ConstIdentifier>,
|
||||||
pub args: ConstArgs,
|
pub args: ConstArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NestedDisplay for ConstApp {
|
impl NestedDisplay for ConstApp {
|
||||||
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result {
|
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result {
|
||||||
writeln!(f, "{}:", self.acc)?;
|
writeln!(f, "{}", self.obj)?;
|
||||||
self.args.fmt_nest(f, level + 1)
|
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 {
|
impl Locational for ConstApp {
|
||||||
fn loc(&self) -> Location {
|
fn loc(&self) -> Location {
|
||||||
if self.args.is_empty() {
|
if self.args.is_empty() {
|
||||||
self.acc.loc()
|
self.obj.loc()
|
||||||
} else {
|
} else {
|
||||||
Location::concat(&self.acc, &self.args)
|
Location::concat(self.obj.as_ref(), &self.args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstApp {
|
impl ConstApp {
|
||||||
pub const fn new(acc: ConstAccessor, args: ConstArgs) -> Self {
|
pub fn new(obj: ConstExpr, attr_name: Option<ConstIdentifier>, args: ConstArgs) -> Self {
|
||||||
Self { acc, args }
|
Self {
|
||||||
|
obj: Box::new(obj),
|
||||||
|
attr_name,
|
||||||
|
args,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downgrade(self) -> Call {
|
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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,13 +124,14 @@ impl Parser {
|
||||||
}
|
}
|
||||||
Expr::Call(call) => {
|
Expr::Call(call) => {
|
||||||
let obj = Self::validate_const_expr(*call.obj)?;
|
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(
|
return Err(ParseError::feature_error(
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
obj.loc(),
|
obj.loc(),
|
||||||
"complex const function call",
|
"complex const function call",
|
||||||
));
|
));
|
||||||
};
|
};*/
|
||||||
|
let attr_name = call.attr_name;
|
||||||
let (pos_args, _, _, paren) = call.args.deconstruct();
|
let (pos_args, _, _, paren) = call.args.deconstruct();
|
||||||
let mut const_pos_args = vec![];
|
let mut const_pos_args = vec![];
|
||||||
for elem in pos_args.into_iter() {
|
for elem in pos_args.into_iter() {
|
||||||
|
@ -138,7 +139,7 @@ impl Parser {
|
||||||
const_pos_args.push(ConstPosArg::new(const_expr));
|
const_pos_args.push(ConstPosArg::new(const_expr));
|
||||||
}
|
}
|
||||||
let args = ConstArgs::pos_only(const_pos_args, paren);
|
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::Def(def) => Self::validate_const_def(def).map(ConstExpr::Def),
|
||||||
Expr::Lambda(lambda) => {
|
Expr::Lambda(lambda) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue