From d26909c31710c38af4c40abdae32bc8d8cdcbe66 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Fri, 25 Nov 2022 17:20:39 +0900 Subject: [PATCH] Improve type mismatch messages --- compiler/erg_compiler/context/eval.rs | 4 +-- compiler/erg_compiler/context/hint.rs | 45 +++++++++++++++--------- compiler/erg_compiler/context/inquire.rs | 10 +++--- compiler/erg_compiler/context/tyvar.rs | 2 +- compiler/erg_compiler/error.rs | 5 +-- compiler/erg_compiler/lower.rs | 2 +- compiler/erg_compiler/ty/mod.rs | 9 +++++ 7 files changed, 50 insertions(+), 27 deletions(-) diff --git a/compiler/erg_compiler/context/eval.rs b/compiler/erg_compiler/context/eval.rs index 2f8c945c..df67fb95 100644 --- a/compiler/erg_compiler/context/eval.rs +++ b/compiler/erg_compiler/context/eval.rs @@ -874,7 +874,7 @@ impl Context { &proj, t_loc, self.caused_by(), - self.get_no_candidate_hint(&proj), + Self::get_no_candidate_hint(&proj), ))) } } @@ -1169,7 +1169,7 @@ impl Context { &proj, t_loc, self.caused_by(), - self.get_no_candidate_hint(&proj), + Self::get_no_candidate_hint(&proj), ))) } } diff --git a/compiler/erg_compiler/context/hint.rs b/compiler/erg_compiler/context/hint.rs index 63a459e1..9ebc06b2 100644 --- a/compiler/erg_compiler/context/hint.rs +++ b/compiler/erg_compiler/context/hint.rs @@ -1,7 +1,7 @@ use erg_common::enum_unwrap; use crate::ty::typaram::TyParam; -use crate::ty::Type; +use crate::ty::{HasType, Type}; use crate::context::Context; @@ -11,8 +11,9 @@ enum Sequence { Backward, } +// TODO: these should not be in Context impl Context { - fn readable_type(&self, typ: &Type) -> Type { + fn readable_type(typ: &Type) -> Type { match typ { Type::FreeVar(fv) if fv.constraint_is_sandwiched() => { let (sub, sup) = fv.get_subsup().unwrap(); @@ -25,7 +26,20 @@ impl Context { } } - pub(crate) fn get_type_mismatch_hint(&self, expected: &Type, found: &Type) -> Option { + /// TODO: custom types + fn get_verb_and_preposition(trait_: &Type) -> Option<(&str, &str, Sequence)> { + match &trait_.qual_name()[..] { + "Add" => Some(("add", "and", Sequence::Forward)), + "Sub" => Some(("subtract", "from", Sequence::Backward)), + "Mul" => Some(("multiply", "and", Sequence::Forward)), + "Div" => Some(("divide", "by", Sequence::Forward)), + "Eq" => Some(("compare", "and", Sequence::Forward)), + "Ord" => Some(("compare", "and", Sequence::Forward)), + _ => None, + } + } + + pub(crate) fn get_type_mismatch_hint(expected: &Type, found: &Type) -> Option { let expected = if let Type::FreeVar(fv) = expected { if fv.is_linked() { fv.crack().clone() @@ -38,27 +52,26 @@ impl Context { }; match (&expected.qual_name()[..], &found.qual_name()[..]) { ("Eq", "Float") => Some(String::from("Float has no equivalence relation defined. you should use `l - r <= Float.EPSILON` instead of `l == r`.")), - _ => None, + _ => { + let (verb, preposition, _sequence) = Self::get_verb_and_preposition(&expected)?; + found.union_types() + .map(|(t1, t2)| format!("cannot {verb} {t1} {preposition} {t2}")) + .or_else(|| { + let ts = expected.inner_ts(); + Some(format!("cannot {verb} {found} {preposition} {}", ts[0])) + }) + } } } - pub(crate) fn get_no_candidate_hint(&self, proj: &Type) -> Option { + pub(crate) fn get_no_candidate_hint(proj: &Type) -> Option { match proj { Type::Proj { lhs, rhs: _ } => { if let Type::FreeVar(fv) = lhs.as_ref() { let (sub, sup) = fv.get_subsup()?; - // TODO: automating - let (verb, preposition, sequence) = match &sup.qual_name()[..] { - "Add" => Some(("add", "and", Sequence::Forward)), - "Sub" => Some(("subtract", "from", Sequence::Backward)), - "Mul" => Some(("multiply", "and", Sequence::Forward)), - "Div" => Some(("divide", "by", Sequence::Forward)), - "Eq" => Some(("compare", "and", Sequence::Forward)), - "Ord" => Some(("compare", "and", Sequence::Forward)), - _ => None, - }?; + let (verb, preposition, sequence) = Self::get_verb_and_preposition(&sup)?; let sup = enum_unwrap!(sup.typarams().remove(0), TyParam::Type); - let sup = self.readable_type(&sup); + let sup = Self::readable_type(&sup); let (l, r) = if sequence == Sequence::Forward { (sub, sup) } else { diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index cfeb532f..218e345f 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -57,7 +57,7 @@ impl Context { &spec_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), ))); } Ok(()) @@ -256,7 +256,7 @@ impl Context { &mono("LambdaFunc"), t, self.get_candidates(t), - self.get_type_mismatch_hint(&mono("LambdaFunc"), t), + Self::get_type_mismatch_hint(&mono("LambdaFunc"), t), ))); } } @@ -1075,7 +1075,7 @@ impl Context { param_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), ) }) .collect(), @@ -1130,7 +1130,7 @@ impl Context { param_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), ) }) .collect(), @@ -1187,7 +1187,7 @@ impl Context { pt.typ(), 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), ) }) .collect(), diff --git a/compiler/erg_compiler/context/tyvar.rs b/compiler/erg_compiler/context/tyvar.rs index 78ae22c8..1de77ad1 100644 --- a/compiler/erg_compiler/context/tyvar.rs +++ b/compiler/erg_compiler/context/tyvar.rs @@ -1285,7 +1285,7 @@ impl Context { maybe_sup, 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), ))); } match (maybe_sub, maybe_sup) { diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index 256e8a23..f27f9a33 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -16,9 +16,9 @@ use erg_common::{ use erg_parser::error::{ParserRunnerError, ParserRunnerErrors}; -use crate::ty::{Predicate, Type}; - +use crate::context::Context; use crate::hir::{Expr, Identifier, Signature}; +use crate::ty::{Predicate, Type}; pub fn ordinal_num(n: usize) -> String { match n.to_string().chars().last().unwrap() { @@ -972,6 +972,7 @@ passed keyword args: {kw_args_len}" caused_by: String, hint: Option, ) -> Self { + let hint = hint.or_else(|| Context::get_type_mismatch_hint(trait_, class)); Self::new( ErrorCore::new( vec![SubMessage::ambiguous_new(loc, vec![], hint)], diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 281c1be2..eaa51f51 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -168,7 +168,7 @@ impl ASTLowerer { expect, found, self.ctx.get_candidates(found), - self.ctx.get_type_mismatch_hint(expect, found), + Context::get_type_mismatch_hint(expect, found), ) }) } diff --git a/compiler/erg_compiler/ty/mod.rs b/compiler/erg_compiler/ty/mod.rs index af1cb6bd..09817a6e 100644 --- a/compiler/erg_compiler/ty/mod.rs +++ b/compiler/erg_compiler/ty/mod.rs @@ -1779,6 +1779,15 @@ impl Type { } } + pub fn union_types(&self) -> Option<(Type, Type)> { + match self { + Type::FreeVar(fv) if fv.is_linked() => fv.crack().union_types(), + Type::Refinement(refine) => refine.t.union_types(), + Type::Or(t1, t2) => Some((*t1.clone(), *t2.clone())), + _ => None, + } + } + /// assert!((A or B).contains_union(B)) pub fn contains_union(&self, typ: &Type) -> bool { match self {