diff --git a/compiler/erg_compiler/context/eval.rs b/compiler/erg_compiler/context/eval.rs index 4953d81e..5903c5b7 100644 --- a/compiler/erg_compiler/context/eval.rs +++ b/compiler/erg_compiler/context/eval.rs @@ -593,11 +593,13 @@ impl Context { } } } + let proj = mono_proj(*lhs, rhs); Err(EvalError::no_candidate_error( line!() as usize, - &mono_proj(*lhs, rhs), + &proj, t_loc, self.caused_by(), + self.get_no_candidate_hint(&proj), )) } Type::Ref(l) => Ok(ref_(self.eval_t_params(*l, level, t_loc)?)), diff --git a/compiler/erg_compiler/context/hint.rs b/compiler/erg_compiler/context/hint.rs index 1bdb0e71..fa0d659b 100644 --- a/compiler/erg_compiler/context/hint.rs +++ b/compiler/erg_compiler/context/hint.rs @@ -1,10 +1,31 @@ use erg_common::astr::AtomicStr; +use erg_common::enum_unwrap; +use erg_type::typaram::TyParam; use erg_type::Type; use crate::context::Context; +#[derive(PartialEq, Eq)] +enum Sequence { + Forward, + Backward, +} + impl Context { + fn readable_type(&self, typ: &Type) -> Type { + match typ { + Type::FreeVar(fv) if fv.constraint_is_sandwiched() => { + let (sub, sup) = fv.get_bound_types().unwrap(); + if sup == Type::Obj { + return sub; + } + Type::FreeVar(fv.clone()) + } + other => other.clone(), + } + } + pub(crate) fn get_type_mismatch_hint( &self, expected: &Type, @@ -25,4 +46,35 @@ impl Context { _ => None, } } + + pub(crate) fn get_no_candidate_hint(&self, proj: &Type) -> Option { + match proj { + Type::MonoProj { lhs, rhs: _ } => { + if let Type::FreeVar(fv) = lhs.as_ref() { + let (sub, sup) = fv.get_bound_types()?; + // TODO: automating + let (verb, preposition, sequence) = match &sup.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)), + _ => None, + }?; + let sup = enum_unwrap!(sup.typarams().remove(0), TyParam::Type); + let sup = self.readable_type(&sup); + let (l, r) = if sequence == Sequence::Forward { + (sub, sup) + } else { + (sup, sub) + }; + Some(AtomicStr::from(format!( + "cannot {verb} {l} {preposition} {r}" + ))) + } else { + None + } + } + _ => None, + } + } } diff --git a/compiler/erg_compiler/context/initialize/mod.rs b/compiler/erg_compiler/context/initialize/mod.rs index e4972c5f..9601c60d 100644 --- a/compiler/erg_compiler/context/initialize/mod.rs +++ b/compiler/erg_compiler/context/initialize/mod.rs @@ -1594,7 +1594,7 @@ impl Context { op_t, set! { static_instance("R", Type), - subtypeof(l, poly("Mul", params)) + subtypeof(l, poly("Div", params)) }, ); self.register_builtin_impl("__div__", op_t, Const, Private); diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index 51fd768b..d9dde557 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -1145,6 +1145,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" proj: &Type, loc: Location, caused_by: AtomicStr, + hint: Option, ) -> Self { Self::new( ErrorCore::new( @@ -1157,7 +1158,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" "traditional_chinese" => format!("{proj}沒有候選項"), "english" => format!("no candidate for {proj}"), ), - None, + hint, ), caused_by, )