From bc6eb37e39b8d65d71939f0db40e55d12b786344 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Tue, 13 Sep 2022 15:34:27 +0900 Subject: [PATCH] Improve: show candidates for unification of projection-types --- compiler/erg_common/macros.rs | 39 +++++++++ compiler/erg_common/set.rs | 7 ++ compiler/erg_compiler/context/eval.rs | 1 + .../erg_compiler/context/initialize/mod.rs | 12 ++- compiler/erg_compiler/context/inquire.rs | 82 ++++++++++++++++++- compiler/erg_compiler/context/instantiate.rs | 2 +- compiler/erg_compiler/context/register.rs | 1 + compiler/erg_compiler/context/tyvar.rs | 1 + compiler/erg_compiler/error.rs | 13 +-- compiler/erg_compiler/lower.rs | 1 + compiler/erg_type/free.rs | 16 ---- 11 files changed, 147 insertions(+), 28 deletions(-) diff --git a/compiler/erg_common/macros.rs b/compiler/erg_common/macros.rs index a6b715b4..2b4632fe 100644 --- a/compiler/erg_common/macros.rs +++ b/compiler/erg_common/macros.rs @@ -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_rules! switch_unreachable { () => {{ diff --git a/compiler/erg_common/set.rs b/compiler/erg_common/set.rs index 08a132f0..315b9571 100644 --- a/compiler/erg_common/set.rs +++ b/compiler/erg_common/set.rs @@ -203,3 +203,10 @@ impl Set { self.iter().min_by(|x, y| x.cmp(y)) } } + +impl Set { + pub fn folded_display(&self) -> String { + self.iter() + .fold("".to_string(), |acc, x| acc + &x.to_string() + "\n") + } +} diff --git a/compiler/erg_compiler/context/eval.rs b/compiler/erg_compiler/context/eval.rs index 72d1e260..886e3557 100644 --- a/compiler/erg_compiler/context/eval.rs +++ b/compiler/erg_compiler/context/eval.rs @@ -267,6 +267,7 @@ impl Context { ident.inspect(), &mono("Subroutine"), &obj.t(), + self.get_candidates(&obj.t()), None, ) })? diff --git a/compiler/erg_compiler/context/initialize/mod.rs b/compiler/erg_compiler/context/initialize/mod.rs index dc68a487..e4972c5f 100644 --- a/compiler/erg_compiler/context/initialize/mod.rs +++ b/compiler/erg_compiler/context/initialize/mod.rs @@ -1264,7 +1264,7 @@ impl Context { ); array_mut_.register_builtin_impl("push!", t, Immutable, Public); let t = pr_met( - array_t.clone(), + array_mut_t.clone(), vec![param_t( "f", nd_func(vec![anon(mono_q("T"))], None, mono_q("T")), @@ -1275,9 +1275,9 @@ impl Context { ); let t = quant( 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( "f", func( @@ -1313,7 +1313,12 @@ impl Context { poly("Eq", vec![ty_tp(range_t.clone())]), 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); + func.register_superclass(mono("Procedure"), &proc); func.register_superclass(Obj, &obj); // TODO: lambda 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(range_t, range, 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("QuantifiedFunction"), qfunc, Const); } diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index 4c7f0697..eab76aa3 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -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::typaram::TyParam; 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::{Context, ContextKind, RegistrationMode, TraitInstance, Variance}; @@ -49,6 +49,7 @@ impl Context { ident.inspect(), &spec_t, body_t, + self.get_candidates(body_t), self.get_type_mismatch_hint(&spec_t, body_t), )); } @@ -131,6 +132,7 @@ impl Context { "match", &mono("LambdaFunc"), t, + self.get_candidates(t), self.get_type_mismatch_hint(&mono("LambdaFunc"), t), )); } @@ -776,6 +778,7 @@ impl Context { &(obj.to_string() + &method_name.to_string()), &mono("Callable"), other, + self.get_candidates(other), None, )) } else { @@ -786,6 +789,7 @@ impl Context { &obj.to_string(), &mono("Callable"), other, + self.get_candidates(other), None, )) } @@ -816,6 +820,7 @@ impl Context { &name[..], param_t, arg_t, + self.get_candidates(arg_t), self.get_type_mismatch_hint(param_t, arg_t), ) })?; @@ -857,6 +862,7 @@ impl Context { &name[..], param_t, arg_t, + self.get_candidates(arg_t), self.get_type_mismatch_hint(param_t, arg_t), ) }) @@ -900,6 +906,7 @@ impl Context { &name[..], pt.typ(), arg_t, + self.get_candidates(arg_t), self.get_type_mismatch_hint(pt.typ(), arg_t), ) })?; @@ -1209,6 +1216,18 @@ impl Context { 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: _ } => { if let Some((t, ctx)) = self.rec_get_poly_type(name) { return Some((t, ctx)); @@ -1462,15 +1481,68 @@ impl Context { None } + // TODO: params, polymorphic types + pub(crate) fn get_candidates(&self, t: &Type) -> Option> { + 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 { + 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 { match typ { Type::And(_l, _r) => false, 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) { ctx.kind.is_class() } else { - todo!() + todo!("is_class({typ})") } } } @@ -1479,11 +1551,15 @@ impl Context { pub(crate) fn is_trait(&self, typ: &Type) -> bool { match typ { 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) { ctx.kind.is_trait() } else { - todo!() + todo!("is_trait({typ})") } } } diff --git a/compiler/erg_compiler/context/instantiate.rs b/compiler/erg_compiler/context/instantiate.rs index 7385e0fc..d5a2804b 100644 --- a/compiler/erg_compiler/context/instantiate.rs +++ b/compiler/erg_compiler/context/instantiate.rs @@ -328,7 +328,7 @@ impl TyVarContext { if let TyParam::Type(t) = t { t.as_ref() } else { - todo!() + todo!("{t}") } }) }) diff --git a/compiler/erg_compiler/context/register.rs b/compiler/erg_compiler/context/register.rs index 5fd5dae4..56d70658 100644 --- a/compiler/erg_compiler/context/register.rs +++ b/compiler/erg_compiler/context/register.rs @@ -735,6 +735,7 @@ impl Context { "import::name", &Str, mod_name.ref_t(), + self.get_candidates(mod_name.ref_t()), self.get_type_mismatch_hint(&Str, mod_name.ref_t()), )); } diff --git a/compiler/erg_compiler/context/tyvar.rs b/compiler/erg_compiler/context/tyvar.rs index bc0b67e9..12406d6f 100644 --- a/compiler/erg_compiler/context/tyvar.rs +++ b/compiler/erg_compiler/context/tyvar.rs @@ -803,6 +803,7 @@ impl Context { param_name.unwrap_or(&Str::ever("_")), maybe_sup, maybe_sub, + self.get_candidates(maybe_sub), self.get_type_mismatch_hint(maybe_sup, maybe_sub), )); } diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index 5e7a5c00..d44290c5 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -4,9 +4,10 @@ use std::ops::Add; use erg_common::color::{GREEN, RED, RESET, YELLOW}; use erg_common::config::Input; use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; +use erg_common::set::Set; use erg_common::traits::{Locational, Stream}; 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}; @@ -534,6 +535,7 @@ impl TyCheckError { ) } + #[allow(clippy::too_many_arguments)] pub fn type_mismatch_error( errno: usize, loc: Location, @@ -541,6 +543,7 @@ impl TyCheckError { name: &str, expect: &Type, found: &Type, + candidates: Option>, hint: Option, ) -> Self { Self::new( @@ -549,10 +552,10 @@ impl TyCheckError { TypeError, loc, switch_lang!( - "japanese" => 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}"), - "traditional_chinese" => format!("{YELLOW}{name}{RESET}的類型不匹配:\n預期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}"), - "english" => format!("the type of {YELLOW}{name}{RESET} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {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| x.folded_display())), + "simplified_chinese" => format!("{YELLOW}{name}{RESET}的类型不匹配:\n预期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一类型的统一候选: \n", candidates, |x: &Set| x.folded_display())), + "traditional_chinese" => format!("{YELLOW}{name}{RESET}的類型不匹配:\n預期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一類型的統一候選\n", candidates, |x: &Set| x.folded_display())), + "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| x.folded_display())), ), hint, ), diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index a7649325..d3bdf656 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -175,6 +175,7 @@ impl ASTLowerer { name, expect, found, + self.ctx.get_candidates(found), self.ctx.get_type_mismatch_hint(expect, found), ) }) diff --git a/compiler/erg_type/free.rs b/compiler/erg_type/free.rs index 9d0e524c..6c42efc7 100644 --- a/compiler/erg_type/free.rs +++ b/compiler/erg_type/free.rs @@ -600,22 +600,6 @@ impl Free { ) } - 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 { matches!( &*self.borrow(),