mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-29 04:24:43 +00:00
Improve: show candidates for unification of projection-types
This commit is contained in:
parent
e04b5bfb8b
commit
bc6eb37e39
11 changed files with 147 additions and 28 deletions
|
@ -155,6 +155,45 @@ macro_rules! fmt_option {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! fmt_option_map {
|
||||||
|
($ex: expr, $f: expr $(,)*) => {
|
||||||
|
if let Some(x) = &$ex {
|
||||||
|
format!("{}", $f(x))
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($ex: expr $(,)*, else $els: expr, $f: expr $(,)*) => {
|
||||||
|
if let Some(x) = &$ex {
|
||||||
|
format!("{}", $f(x))
|
||||||
|
} else {
|
||||||
|
$els.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(pre $prefix: expr, $ex: expr, $f: expr $(,)*) => {
|
||||||
|
if let Some(x) = &$ex {
|
||||||
|
format!("{}{}", $prefix, $f(x))
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($ex: expr, post $postfix: expr, $f: expr $(,)*) => {
|
||||||
|
if let Some(x) = &$ex {
|
||||||
|
format!("{}{}", $f(x), $postfix)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($prefix: expr, $ex: expr, $postfix: expr, $f: expr $(,)*) => {
|
||||||
|
if let Some(x) = &$ex {
|
||||||
|
format!("{}{}{}", $prefix, $f(x), $postfix)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! switch_unreachable {
|
macro_rules! switch_unreachable {
|
||||||
() => {{
|
() => {{
|
||||||
|
|
|
@ -203,3 +203,10 @@ impl<T: Hash + Ord> Set<T> {
|
||||||
self.iter().min_by(|x, y| x.cmp(y))
|
self.iter().min_by(|x, y| x.cmp(y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Hash + fmt::Display> Set<T> {
|
||||||
|
pub fn folded_display(&self) -> String {
|
||||||
|
self.iter()
|
||||||
|
.fold("".to_string(), |acc, x| acc + &x.to_string() + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -267,6 +267,7 @@ impl Context {
|
||||||
ident.inspect(),
|
ident.inspect(),
|
||||||
&mono("Subroutine"),
|
&mono("Subroutine"),
|
||||||
&obj.t(),
|
&obj.t(),
|
||||||
|
self.get_candidates(&obj.t()),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
|
|
|
@ -1264,7 +1264,7 @@ impl Context {
|
||||||
);
|
);
|
||||||
array_mut_.register_builtin_impl("push!", t, Immutable, Public);
|
array_mut_.register_builtin_impl("push!", t, Immutable, Public);
|
||||||
let t = pr_met(
|
let t = pr_met(
|
||||||
array_t.clone(),
|
array_mut_t.clone(),
|
||||||
vec![param_t(
|
vec![param_t(
|
||||||
"f",
|
"f",
|
||||||
nd_func(vec![anon(mono_q("T"))], None, mono_q("T")),
|
nd_func(vec![anon(mono_q("T"))], None, mono_q("T")),
|
||||||
|
@ -1275,9 +1275,9 @@ impl Context {
|
||||||
);
|
);
|
||||||
let t = quant(
|
let t = quant(
|
||||||
t,
|
t,
|
||||||
set! {static_instance("N", Nat), static_instance("T", mono("Mutable"))},
|
set! {static_instance("T", Type), static_instance("N", mono("Nat!"))},
|
||||||
);
|
);
|
||||||
array_mut_.register_builtin_impl("map!", t, Immutable, Public);
|
array_mut_.register_builtin_impl("strict_map!", t, Immutable, Public);
|
||||||
let f_t = param_t(
|
let f_t = param_t(
|
||||||
"f",
|
"f",
|
||||||
func(
|
func(
|
||||||
|
@ -1313,7 +1313,12 @@ impl Context {
|
||||||
poly("Eq", vec![ty_tp(range_t.clone())]),
|
poly("Eq", vec![ty_tp(range_t.clone())]),
|
||||||
range_eq,
|
range_eq,
|
||||||
);
|
);
|
||||||
|
let mut proc = Self::mono_class("Procedure", Self::TOP_LEVEL);
|
||||||
|
proc.register_superclass(Obj, &obj);
|
||||||
|
// TODO: lambda
|
||||||
|
proc.register_marker_trait(mono("Named"));
|
||||||
let mut func = Self::mono_class("Function", Self::TOP_LEVEL);
|
let mut func = Self::mono_class("Function", Self::TOP_LEVEL);
|
||||||
|
func.register_superclass(mono("Procedure"), &proc);
|
||||||
func.register_superclass(Obj, &obj);
|
func.register_superclass(Obj, &obj);
|
||||||
// TODO: lambda
|
// TODO: lambda
|
||||||
func.register_marker_trait(mono("Named"));
|
func.register_marker_trait(mono("Named"));
|
||||||
|
@ -1405,6 +1410,7 @@ impl Context {
|
||||||
self.register_builtin_type(array_mut_t, array_mut_, Const);
|
self.register_builtin_type(array_mut_t, array_mut_, Const);
|
||||||
self.register_builtin_type(range_t, range, Const);
|
self.register_builtin_type(range_t, range, Const);
|
||||||
self.register_builtin_type(mono("Tuple"), tuple_, Const);
|
self.register_builtin_type(mono("Tuple"), tuple_, Const);
|
||||||
|
self.register_builtin_type(mono("Procedure"), proc, Const);
|
||||||
self.register_builtin_type(mono("Function"), func, Const);
|
self.register_builtin_type(mono("Function"), func, Const);
|
||||||
self.register_builtin_type(mono("QuantifiedFunction"), qfunc, Const);
|
self.register_builtin_type(mono("QuantifiedFunction"), qfunc, Const);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use erg_type::constructors::{func, mono, mono_proj, poly, ref_, ref_mut, refinem
|
||||||
use erg_type::free::Constraint;
|
use erg_type::free::Constraint;
|
||||||
use erg_type::typaram::TyParam;
|
use erg_type::typaram::TyParam;
|
||||||
use erg_type::value::{GenTypeObj, TypeObj, ValueObj};
|
use erg_type::value::{GenTypeObj, TypeObj, ValueObj};
|
||||||
use erg_type::{HasType, ParamTy, TyBound, Type};
|
use erg_type::{HasType, ParamTy, SubrKind, SubrType, TyBound, Type};
|
||||||
|
|
||||||
use crate::context::instantiate::ConstTemplate;
|
use crate::context::instantiate::ConstTemplate;
|
||||||
use crate::context::{Context, ContextKind, RegistrationMode, TraitInstance, Variance};
|
use crate::context::{Context, ContextKind, RegistrationMode, TraitInstance, Variance};
|
||||||
|
@ -49,6 +49,7 @@ impl Context {
|
||||||
ident.inspect(),
|
ident.inspect(),
|
||||||
&spec_t,
|
&spec_t,
|
||||||
body_t,
|
body_t,
|
||||||
|
self.get_candidates(body_t),
|
||||||
self.get_type_mismatch_hint(&spec_t, body_t),
|
self.get_type_mismatch_hint(&spec_t, body_t),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -131,6 +132,7 @@ impl Context {
|
||||||
"match",
|
"match",
|
||||||
&mono("LambdaFunc"),
|
&mono("LambdaFunc"),
|
||||||
t,
|
t,
|
||||||
|
self.get_candidates(t),
|
||||||
self.get_type_mismatch_hint(&mono("LambdaFunc"), t),
|
self.get_type_mismatch_hint(&mono("LambdaFunc"), t),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -776,6 +778,7 @@ impl Context {
|
||||||
&(obj.to_string() + &method_name.to_string()),
|
&(obj.to_string() + &method_name.to_string()),
|
||||||
&mono("Callable"),
|
&mono("Callable"),
|
||||||
other,
|
other,
|
||||||
|
self.get_candidates(other),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
@ -786,6 +789,7 @@ impl Context {
|
||||||
&obj.to_string(),
|
&obj.to_string(),
|
||||||
&mono("Callable"),
|
&mono("Callable"),
|
||||||
other,
|
other,
|
||||||
|
self.get_candidates(other),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -816,6 +820,7 @@ impl Context {
|
||||||
&name[..],
|
&name[..],
|
||||||
param_t,
|
param_t,
|
||||||
arg_t,
|
arg_t,
|
||||||
|
self.get_candidates(arg_t),
|
||||||
self.get_type_mismatch_hint(param_t, arg_t),
|
self.get_type_mismatch_hint(param_t, arg_t),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
@ -857,6 +862,7 @@ impl Context {
|
||||||
&name[..],
|
&name[..],
|
||||||
param_t,
|
param_t,
|
||||||
arg_t,
|
arg_t,
|
||||||
|
self.get_candidates(arg_t),
|
||||||
self.get_type_mismatch_hint(param_t, arg_t),
|
self.get_type_mismatch_hint(param_t, arg_t),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -900,6 +906,7 @@ impl Context {
|
||||||
&name[..],
|
&name[..],
|
||||||
pt.typ(),
|
pt.typ(),
|
||||||
arg_t,
|
arg_t,
|
||||||
|
self.get_candidates(arg_t),
|
||||||
self.get_type_mismatch_hint(pt.typ(), arg_t),
|
self.get_type_mismatch_hint(pt.typ(), arg_t),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
@ -1209,6 +1216,18 @@ impl Context {
|
||||||
return Some(res);
|
return Some(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Type::Subr(_subr) => match _subr.kind {
|
||||||
|
SubrKind::Func => {
|
||||||
|
if let Some(res) = self.get_nominal_type_ctx(&mono("Function")) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SubrKind::Proc => {
|
||||||
|
if let Some(res) = self.get_nominal_type_ctx(&mono("Procedure")) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Type::Poly { name, params: _ } => {
|
Type::Poly { name, params: _ } => {
|
||||||
if let Some((t, ctx)) = self.rec_get_poly_type(name) {
|
if let Some((t, ctx)) = self.rec_get_poly_type(name) {
|
||||||
return Some((t, ctx));
|
return Some((t, ctx));
|
||||||
|
@ -1462,15 +1481,68 @@ impl Context {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: params, polymorphic types
|
||||||
|
pub(crate) fn get_candidates(&self, t: &Type) -> Option<Set<Type>> {
|
||||||
|
match t {
|
||||||
|
Type::MonoProj { lhs, rhs } => Some(self.get_proj_candidates(lhs, rhs)),
|
||||||
|
Type::Subr(subr) => {
|
||||||
|
let candidates = self.get_candidates(&subr.return_t)?;
|
||||||
|
Some(
|
||||||
|
candidates
|
||||||
|
.into_iter()
|
||||||
|
.map(|ret_t| {
|
||||||
|
let subr = SubrType::new(
|
||||||
|
subr.kind,
|
||||||
|
subr.non_default_params.clone(),
|
||||||
|
subr.var_params.as_ref().map(|p| *p.clone()),
|
||||||
|
subr.default_params.clone(),
|
||||||
|
ret_t,
|
||||||
|
);
|
||||||
|
Type::Subr(subr)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_proj_candidates(&self, lhs: &Type, rhs: &Str) -> Set<Type> {
|
||||||
|
match lhs {
|
||||||
|
Type::FreeVar(fv) => {
|
||||||
|
if let Some(sup) = fv.get_sup() {
|
||||||
|
let insts = self.rec_get_trait_impls(&sup.name());
|
||||||
|
let candidates = insts.into_iter().filter_map(move |inst| {
|
||||||
|
if self.supertype_of(&inst.sup_trait, &sup) {
|
||||||
|
Some(
|
||||||
|
self.eval_t_params(mono_proj(inst.sub_type, rhs), self.level)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return candidates.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
todo!("{lhs}.{rhs}")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn is_class(&self, typ: &Type) -> bool {
|
pub(crate) fn is_class(&self, typ: &Type) -> bool {
|
||||||
match typ {
|
match typ {
|
||||||
Type::And(_l, _r) => false,
|
Type::And(_l, _r) => false,
|
||||||
Type::Or(l, r) => self.is_class(l) && self.is_class(r),
|
Type::Or(l, r) => self.is_class(l) && self.is_class(r),
|
||||||
|
Type::MonoProj { lhs, rhs } => self
|
||||||
|
.get_proj_candidates(lhs, rhs)
|
||||||
|
.iter()
|
||||||
|
.all(|t| self.is_class(t)),
|
||||||
_ => {
|
_ => {
|
||||||
if let Some((_, ctx)) = self.get_nominal_type_ctx(typ) {
|
if let Some((_, ctx)) = self.get_nominal_type_ctx(typ) {
|
||||||
ctx.kind.is_class()
|
ctx.kind.is_class()
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!("is_class({typ})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1479,11 +1551,15 @@ impl Context {
|
||||||
pub(crate) fn is_trait(&self, typ: &Type) -> bool {
|
pub(crate) fn is_trait(&self, typ: &Type) -> bool {
|
||||||
match typ {
|
match typ {
|
||||||
Type::And(l, r) | Type::Or(l, r) => self.is_trait(l) && self.is_trait(r),
|
Type::And(l, r) | Type::Or(l, r) => self.is_trait(l) && self.is_trait(r),
|
||||||
|
Type::MonoProj { lhs, rhs } => self
|
||||||
|
.get_proj_candidates(lhs, rhs)
|
||||||
|
.iter()
|
||||||
|
.all(|t| self.is_trait(t)),
|
||||||
_ => {
|
_ => {
|
||||||
if let Some((_, ctx)) = self.get_nominal_type_ctx(typ) {
|
if let Some((_, ctx)) = self.get_nominal_type_ctx(typ) {
|
||||||
ctx.kind.is_trait()
|
ctx.kind.is_trait()
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!("is_trait({typ})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -328,7 +328,7 @@ impl TyVarContext {
|
||||||
if let TyParam::Type(t) = t {
|
if let TyParam::Type(t) = t {
|
||||||
t.as_ref()
|
t.as_ref()
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!("{t}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -735,6 +735,7 @@ impl Context {
|
||||||
"import::name",
|
"import::name",
|
||||||
&Str,
|
&Str,
|
||||||
mod_name.ref_t(),
|
mod_name.ref_t(),
|
||||||
|
self.get_candidates(mod_name.ref_t()),
|
||||||
self.get_type_mismatch_hint(&Str, mod_name.ref_t()),
|
self.get_type_mismatch_hint(&Str, mod_name.ref_t()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -803,6 +803,7 @@ impl Context {
|
||||||
param_name.unwrap_or(&Str::ever("_")),
|
param_name.unwrap_or(&Str::ever("_")),
|
||||||
maybe_sup,
|
maybe_sup,
|
||||||
maybe_sub,
|
maybe_sub,
|
||||||
|
self.get_candidates(maybe_sub),
|
||||||
self.get_type_mismatch_hint(maybe_sup, maybe_sub),
|
self.get_type_mismatch_hint(maybe_sup, maybe_sub),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ use std::ops::Add;
|
||||||
use erg_common::color::{GREEN, RED, RESET, YELLOW};
|
use erg_common::color::{GREEN, RED, RESET, YELLOW};
|
||||||
use erg_common::config::Input;
|
use erg_common::config::Input;
|
||||||
use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay};
|
use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay};
|
||||||
|
use erg_common::set::Set;
|
||||||
use erg_common::traits::{Locational, Stream};
|
use erg_common::traits::{Locational, Stream};
|
||||||
use erg_common::vis::Visibility;
|
use erg_common::vis::Visibility;
|
||||||
use erg_common::{fmt_iter, fmt_vec, impl_stream_for_wrapper, switch_lang, Str};
|
use erg_common::{fmt_iter, fmt_option_map, fmt_vec, impl_stream_for_wrapper, switch_lang, Str};
|
||||||
|
|
||||||
use erg_parser::error::{ParserRunnerError, ParserRunnerErrors};
|
use erg_parser::error::{ParserRunnerError, ParserRunnerErrors};
|
||||||
|
|
||||||
|
@ -534,6 +535,7 @@ impl TyCheckError {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn type_mismatch_error(
|
pub fn type_mismatch_error(
|
||||||
errno: usize,
|
errno: usize,
|
||||||
loc: Location,
|
loc: Location,
|
||||||
|
@ -541,6 +543,7 @@ impl TyCheckError {
|
||||||
name: &str,
|
name: &str,
|
||||||
expect: &Type,
|
expect: &Type,
|
||||||
found: &Type,
|
found: &Type,
|
||||||
|
candidates: Option<Set<Type>>,
|
||||||
hint: Option<Str>,
|
hint: Option<Str>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
|
@ -549,10 +552,10 @@ impl TyCheckError {
|
||||||
TypeError,
|
TypeError,
|
||||||
loc,
|
loc,
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => format!("{YELLOW}{name}{RESET}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}"),
|
"japanese" => format!("{YELLOW}{name}{RESET}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "与えられた型の単一化候補:\n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||||
"simplified_chinese" => format!("{YELLOW}{name}{RESET}的类型不匹配:\n预期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}"),
|
"simplified_chinese" => format!("{YELLOW}{name}{RESET}的类型不匹配:\n预期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一类型的统一候选: \n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||||
"traditional_chinese" => format!("{YELLOW}{name}{RESET}的類型不匹配:\n預期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}"),
|
"traditional_chinese" => format!("{YELLOW}{name}{RESET}的類型不匹配:\n預期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一類型的統一候選\n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||||
"english" => format!("the type of {YELLOW}{name}{RESET} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
|
"english" => format!("the type of {YELLOW}{name}{RESET} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "unification candidates of a given type:\n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||||
),
|
),
|
||||||
hint,
|
hint,
|
||||||
),
|
),
|
||||||
|
|
|
@ -175,6 +175,7 @@ impl ASTLowerer {
|
||||||
name,
|
name,
|
||||||
expect,
|
expect,
|
||||||
found,
|
found,
|
||||||
|
self.ctx.get_candidates(found),
|
||||||
self.ctx.get_type_mismatch_hint(expect, found),
|
self.ctx.get_type_mismatch_hint(expect, found),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -600,22 +600,6 @@ impl<T: Clone + HasLevel> Free<T> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constraint_is_supertypeof(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
&*self.borrow(),
|
|
||||||
FreeKind::Unbound { constraint, .. }
|
|
||||||
| FreeKind::NamedUnbound { constraint, .. } if constraint.get_sub().is_some()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn constraint_is_subtypeof(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
&*self.borrow(),
|
|
||||||
FreeKind::Unbound { constraint, .. }
|
|
||||||
| FreeKind::NamedUnbound { constraint, .. } if constraint.get_super().is_some()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn constraint_is_sandwiched(&self) -> bool {
|
pub fn constraint_is_sandwiched(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
&*self.borrow(),
|
&*self.borrow(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue