diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 9a76c05e4f620..b1db2ac2e74a9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1449,7 +1449,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { swap_secondary_and_primary: bool, ) { let span = cause.span(self.tcx); - debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr); // For some types of errors, expected-found does not make // sense, so just ignore the values we were given. @@ -1581,11 +1580,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - debug!("note_type_err(diag={:?})", diag); enum Mismatch<'a> { Variable(ty::error::ExpectedFound>), Fixed(&'static str), } + let (expected_found, exp_found, is_simple_error) = match values { None => (None, Mismatch::Fixed("type"), false), Some(values) => { @@ -1652,6 +1651,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } }; + if let Some((expected, found)) = expected_found { let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( @@ -1705,6 +1705,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { (TypeError::ObjectUnsafeCoercion(_), _) => { diag.note_unsuccessful_coercion(found, expected); } + (TypeError::ConstMismatchTooGeneric(_, Some(sugg)), _) => { + if !is_simple_error { + let found = + DiagnosticStyledString::highlighted(format!("{}", sugg.clone())); + + diag.note_expected_found( + &expected_label, + expected, + &"type".to_string(), + found, + ); + } + } (_, _) => { debug!( "note_type_err: exp_found={:?}, expected={:?} found={:?}", @@ -1716,10 +1729,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } } + let exp_found = match exp_found { Mismatch::Variable(exp_found) => Some(exp_found), Mismatch::Fixed(_) => None, }; + let exp_found = match terr { // `terr` has more accurate type information than `exp_found` in match expressions. ty::error::TypeError::Sorts(terr) @@ -1730,6 +1745,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => exp_found, }; debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code()); + if let Some(exp_found) = exp_found { let should_suggest_fixes = if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() @@ -1756,6 +1772,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.hir().opt_local_def_id(cause.body_id).unwrap_or_else(|| { self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id }) }); + self.check_and_note_conflicting_crates(diag, terr); self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id()); @@ -2021,10 +2038,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) -> DiagnosticBuilder<'tcx> { use crate::traits::ObligationCauseCode::MatchExpressionArm; - debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); - let span = trace.cause.span(self.tcx); let failure_code = trace.cause.as_failure_code(terr); + let mut diag = match failure_code { FailureCode::Error0038(did) => { let violations = self.tcx.object_safety_violations(did); @@ -2038,9 +2054,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } FailureCode::Error0308(failure_str) => { let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str); + if let ValuePairs::Types(ty::error::ExpectedFound { expected, found }) = trace.values { + debug!(?expected, ?found); + // If a tuple of length one was expected and the found expression has // parentheses around it, perhaps the user meant to write `(expr,)` to // build a tuple (issue #86100) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 04e04e297cdd2..5d0dfd192d229 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1484,6 +1484,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.report_and_explain_type_error(trace, &err) } + #[instrument(skip(self), level = "debug")] pub fn report_mismatched_consts( &self, cause: &ObligationCause<'tcx>, diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index cc31d8c2c1879..d61e8efa59720 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -8,7 +8,7 @@ use rustc_apfloat::{ use rustc_macros::HashStable; use rustc_target::abi::{HasDataLayout, Size}; -use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt}; +use crate::ty::{self, Lift, ParamEnv, ScalarInt, Ty, TyCtxt}; use super::{ AllocId, AllocRange, Allocation, InterpResult, Pointer, PointerArithmetic, Provenance, @@ -456,6 +456,78 @@ impl<'tcx, Tag: Provenance> Scalar { // Going through `u64` to check size and truncation. Ok(Double::from_bits(self.to_u64()?.into())) } + + #[inline] + pub fn try_to_string(self, t: Ty<'tcx>) -> Option { + #[cfg(target_pointer_width = "32")] + fn usize_to_string(scalar: Scalar) -> Option { + scalar.to_u32().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + + #[cfg(target_pointer_width = "64")] + fn usize_to_string(scalar: Scalar) -> Option { + scalar.to_u64().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + + #[cfg(target_pointer_width = "32")] + fn isize_to_string(scalar: Scalar) -> Option { + scalar.to_i32().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + + #[cfg(target_pointer_width = "64")] + fn isize_to_string(scalar: Scalar) -> Option { + scalar.to_i64().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + + #[cfg(target_pointer_width = "128")] + fn isize_to_string(scalar: Scalar) -> Option { + scalar.to_i128().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + + #[cfg(target_pointer_width = "128")] + fn usize_to_string(scalar: Scalar) -> Option { + scalar.to_u128().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + + match self { + Scalar::Int(_) => match t.kind() { + ty::Int(ty::IntTy::Isize) => isize_to_string(self), + ty::Int(ty::IntTy::I8) => { + self.to_i8().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Int(ty::IntTy::I16) => { + self.to_i16().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Int(ty::IntTy::I32) => { + self.to_i32().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Int(ty::IntTy::I64) => { + self.to_i64().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Int(ty::IntTy::I128) => { + self.to_i128().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Uint(ty::UintTy::Usize) => usize_to_string(self), + ty::Uint(ty::UintTy::U8) => { + self.to_u8().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Uint(ty::UintTy::U16) => { + self.to_u16().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Uint(ty::UintTy::U32) => { + self.to_u32().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Uint(ty::UintTy::U64) => { + self.to_u64().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + ty::Uint(ty::UintTy::U128) => { + self.to_u128().map_or_else(|_| None, |v| Some(format!("{}", v))) + } + _ => None, + }, + Scalar::Ptr(_, _) => Some(format!("{}", self)), + } + } } #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d2dd15aad1276..6852da9b50a1b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2328,6 +2328,54 @@ impl BinOp { use self::BinOp::*; matches!(self, Add | Sub | Mul | Shl | Shr) } + + pub fn try_as_string(self) -> Option { + use self::BinOp::*; + + match self { + Add => Some("+".to_string()), + Sub => Some("-".to_string()), + Mul => Some("*".to_string()), + Div => Some("/".to_string()), + Rem => Some("%".to_string()), + BitXor => Some("^".to_string()), + BitAnd => Some("&".to_string()), + BitOr => Some("|".to_string()), + Shl => Some("<<".to_string()), + Shr => Some(">>".to_string()), + Eq => Some("=".to_string()), + Lt => Some("<".to_string()), + Le => Some("<=".to_string()), + Ne => Some("!=".to_string()), + Ge => Some(">=".to_string()), + Gt => Some(">".to_string()), + Offset => None, + } + } + + pub fn get_precedence(self) -> usize { + use self::BinOp::*; + + match self { + Add => 7, + Sub => 7, + Mul => 8, + Div => 8, + Rem => 8, + BitXor => 2, + BitAnd => 3, + BitOr => 1, + Shl => 6, + Shr => 6, + Eq => 4, + Lt => 5, + Le => 5, + Ne => 4, + Ge => 5, + Gt => 5, + Offset => 0, + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] @@ -2348,6 +2396,15 @@ pub enum UnOp { Neg, } +impl fmt::Display for UnOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + UnOp::Not => write!(f, "!"), + UnOp::Neg => write!(f, "-"), + } + } +} + impl<'tcx> Debug for Rvalue<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::Rvalue::*; diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index df6e739dc2011..fbbc969dd9a60 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -67,6 +67,7 @@ pub enum TypeError<'tcx> { ), ObjectUnsafeCoercion(DefId), ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), + ConstMismatchTooGeneric(ExpectedFound<&'tcx ty::Const<'tcx>>, Option), IntrinsicCast, /// Safe `#[target_feature]` functions are not assignable to safe function pointers. @@ -201,6 +202,12 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { ConstMismatch(ref values) => { write!(f, "expected `{}`, found `{}`", values.expected, values.found) } + ConstMismatchTooGeneric(ref values, ref suggestion) => match suggestion { + Some(sugg) => { + write!(f, "expected `{}`, found `{}`", values.expected, sugg) + } + None => write!(f, "expected `{}`, found `{}`", values.expected, values.found), + }, IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"), TargetFeatureCast(_) => write!( f, @@ -233,6 +240,7 @@ impl<'tcx> TypeError<'tcx> { | ProjectionMismatched(_) | ExistentialMismatch(_) | ConstMismatch(_) + | ConstMismatchTooGeneric(_, _) | IntrinsicCast | ObjectUnsafeCoercion(_) => true, } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 98b1a8b4d7631..f998d5e0716c5 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -625,6 +625,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { Sorts(x) => return tcx.lift(x).map(Sorts), ExistentialMismatch(x) => return tcx.lift(x).map(ExistentialMismatch), ConstMismatch(x) => return tcx.lift(x).map(ConstMismatch), + ConstMismatchTooGeneric(x, s) => { + return tcx.lift(x).map(|x| ConstMismatchTooGeneric(x, s)); + } IntrinsicCast => IntrinsicCast, TargetFeatureCast(x) => TargetFeatureCast(x), ObjectUnsafeCoercion(x) => return tcx.lift(x).map(ObjectUnsafeCoercion), diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0ea3a18ca34fa..6103bf2198110 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -13,7 +13,7 @@ use rustc_hir::def::DefKind; use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::mir; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::thir; use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable}; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -33,7 +33,8 @@ pub fn is_const_evaluatable<'cx, 'tcx>( param_env: ty::ParamEnv<'tcx>, span: Span, ) -> Result<(), NotConstEvaluatable> { - debug!("is_const_evaluatable({:?})", uv); + debug!("param_env: {:#?}", param_env); + if infcx.tcx.features().generic_const_exprs { let tcx = infcx.tcx; match AbstractConst::new(tcx, uv)? { @@ -225,6 +226,175 @@ impl<'tcx> AbstractConst<'tcx> { Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, } } + + /// Used for diagnostics only. + /// Tries to create a String of an `AbstractConst` while recursively applying substs. This + /// will fail for `AbstractConst`s that contain `Leaf`s including inference variables or errors, + /// and any non-Leaf `Node`s other than `BinOp` and `UnOp`. + pub(crate) fn try_print_with_replacing_substs(self, tcx: TyCtxt<'tcx>) -> Option { + self.recursive_try_print_with_replacing_substs(tcx).map(|s| s.to_string()) + } + + // Recursively applies substs to leaves. Also ensures that the strings corresponding + // to nodes in the `AbstractConst` tree are wrapped in curly braces after a substitution + // was applied. E.g in `{ 3 * M}` where we have substs `{ N + 2 }` for M, we want to + // make sure that the node corresponding to `M` is wrapped in curly braces: `{ 3 * { N +2 }}` + #[instrument(skip(tcx), level = "debug")] + fn recursive_try_print_with_replacing_substs( + mut self, + tcx: TyCtxt<'tcx>, + ) -> Option { + let mut applied_subst = false; + + // try to replace substs + while let abstract_const::Node::Leaf(ct) = self.root(tcx) { + match AbstractConst::from_const(tcx, ct) { + Ok(Some(act)) => { + applied_subst = true; + self = act; + } + Ok(None) => break, + Err(_) => bug!("should be able to create AbstractConst here"), + } + } + + debug!("after applying substs: {:#?}", self); + debug!("root: {:?}", self.root(tcx)); + debug!(?applied_subst); + + // How we infer precedence of operations: + // We use `PrintStyle::Braces(op_precedence, str)` to indicate that resulting + // string of recursive call may need to be wrapped in curly braces. If the + // precedence of the op of a node is larger than `op_precedence` of the returned + // subnode we need to use curly braces. + // If the precedence isn't larger we still send `PrintStyle::Braces` upwards + // since the string that includes the node which requires precedence might + // still require a higher-level node string to be wrapped in curly braces, e.g + // in `{ 3 + {M + K}}` where `M` is substituted by `N + 2` we want the result string + // to be `{ M + { N + 2 + K}}`. + match self.root(tcx) { + abstract_const::Node::Leaf(ct) => match ct.val { + ty::ConstKind::Error(_) | ty::ConstKind::Infer(_) => return None, + ty::ConstKind::Param(c) => { + let s = format!("{}", c); + + if applied_subst { + return Some(PrintStyle::Braces(None, s)); + } else { + return Some(PrintStyle::NoBraces(s)); + } + } + ty::ConstKind::Value(ConstValue::Scalar(scalar)) => { + debug!("ct.ty: {:?}", ct.ty); + let test_string = scalar.try_to_string(ct.ty); + debug!(?test_string); + + match scalar.try_to_string(ct.ty) { + Some(s) => { + if applied_subst { + return Some(PrintStyle::Braces(None, s)); + } else { + return Some(PrintStyle::NoBraces(s)); + } + } + None => return None, + } + } + _ => return None, + }, + abstract_const::Node::Binop(op, l, r) => { + let op_str = match op.try_as_string() { + Some(o) => o, + None => return None, + }; + + // To decide whether we need to propagate PrintStyle::CurlyBraces upwards + // due to substitutions in subnodes of Binop + let mut use_curly_braces_due_to_subnodes = false; + + let left = self.subtree(l).recursive_try_print_with_replacing_substs(tcx); + debug!(?left); + + let right = self.subtree(r).recursive_try_print_with_replacing_substs(tcx); + debug!(?right); + + let left_str = match left { + Some(PrintStyle::Braces(opt_l_prec, l_str)) => { + use_curly_braces_due_to_subnodes = true; + + if let Some(l_prec) = opt_l_prec { + if op.get_precedence() > l_prec { + // Already applied curly braces for subnode, no need to + // propagate PrintStyle::CurlyBraces upwards anymore + // for this node + use_curly_braces_due_to_subnodes = false; + + // Include curly braces for l_str + format!("{{ {} }}", l_str) + } else { + l_str + } + } else { + l_str + } + } + Some(PrintStyle::NoBraces(l_str)) => l_str, + None => return None, + }; + + let right_str = match right { + Some(PrintStyle::Braces(opt_r_prec, r_str)) => { + use_curly_braces_due_to_subnodes = true; + + if let Some(r_prec) = opt_r_prec { + if op.get_precedence() > r_prec { + // Already applied curly braces for subnode, no need to + // propagate PrintStyle::CurlyBraces upwards anymore + // for this node + use_curly_braces_due_to_subnodes = false; + + // Include curly braces for l_str + format!("{{ {} }}", r_str) + } else { + r_str + } + } else { + r_str + } + } + Some(PrintStyle::NoBraces(r_str)) => r_str, + None => return None, + }; + + let binop_str = format!("{} {} {}", left_str, op_str, right_str); + + // We propagate the need for curly braces upwards in the following cases: + // 1. We applied a substitution for current root + // 2. We applied a substitution in a subnode and did not apply curly braces + // to that subnode string + let current_op_prec = op.get_precedence(); + debug!(?current_op_prec); + debug!(?applied_subst); + debug!(?use_curly_braces_due_to_subnodes); + + if applied_subst || use_curly_braces_due_to_subnodes { + Some(PrintStyle::Braces(Some(current_op_prec), binop_str)) + } else { + Some(PrintStyle::NoBraces(binop_str)) + } + } + abstract_const::Node::UnaryOp(_, v) => { + match self.subtree(v).recursive_try_print_with_replacing_substs(tcx) { + Some(PrintStyle::Braces(opt_prec, operand_str)) => { + Some(PrintStyle::Braces(opt_prec, operand_str)) + } + s @ Some(PrintStyle::NoBraces(_)) => s, + None => None, + } + } + _ => None, + } + } } struct AbstractConstBuilder<'a, 'tcx> { @@ -495,6 +665,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { } } +/// Used in diagnostics for creating const mismatch suggestions. Indicates precedence +/// that needs to be applied to nodes +#[derive(Debug)] +enum PrintStyle { + /// String needs to possibly be wrapped in curly braces if precedence of operation + /// requires it + Braces(Option, String), + + /// String does not need to be wrapped in curly braces + NoBraces(String), +} + +impl PrintStyle { + pub(crate) fn to_string(self) -> String { + match self { + Self::Braces(_, s) => s, + Self::NoBraces(s) => s, + } + } +} + /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. pub(super) fn thir_abstract_const<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index b5c5724f56edc..c24cf4f47a1dc 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -11,6 +11,7 @@ use super::{ use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{self, InferCtxt, TyCtxtInferExt}; +use crate::traits::const_evaluatable::AbstractConst; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; @@ -19,6 +20,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; +//use rustc_middle::mir::interpret::ConstValue; use rustc_middle::thir::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences}; @@ -86,6 +88,11 @@ pub trait InferCtxtExt<'tcx> { found_args: Vec, is_closure: bool, ) -> DiagnosticBuilder<'tcx>; + + fn try_create_suggestion_for_mismatched_const( + &self, + expected_found: ExpectedFound<&'tcx ty::Const<'tcx>>, + ) -> Option; } impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { @@ -802,6 +809,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { + debug!("NotConstEvaluatable::MentionsParam error"); + if !self.tcx.features().generic_const_exprs { let mut err = self.tcx.sess.struct_span_err( span, @@ -820,16 +829,41 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { match obligation.predicate.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { + debug!(?uv); + let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); - let const_span = self.tcx.def_span(uv.def.did); - match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(&format!( + + let anon_const_sugg = match AbstractConst::new(self.tcx, uv) { + Ok(Some(a)) => a.try_print_with_replacing_substs(self.tcx).map_or( + { + let const_span = self.tcx.def_span(uv.def.did); + self.tcx + .sess + .source_map() + .span_to_snippet(const_span) + .map_or(None, Some) + }, + |s| Some(format!("{{ {} }}", s)), + ), + _ => { + let const_span = self.tcx.def_span(uv.def.did); + self.tcx + .sess + .source_map() + .span_to_snippet(const_span) + .map_or(None, Some) + } + }; + + match anon_const_sugg { + Some(snippet) => err.help(&format!( "try adding a `where` bound using this expression: `where [(); {}]:`", snippet )), - _ => err.help("consider adding a `where` bound using this expression"), + None => err.help("consider adding a `where` bound using this expression"), }; + err } _ => { @@ -1062,9 +1096,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err } + + fn try_create_suggestion_for_mismatched_const( + &self, + expected_found: ExpectedFound<&'tcx ty::Const<'tcx>>, + ) -> Option { + match AbstractConst::from_const(self.tcx, expected_found.found) { + Ok(Some(f_abstract)) => f_abstract.try_print_with_replacing_substs(self.tcx), + _ => None, + } + } } -trait InferCtxtPrivExt<'hir, 'tcx> { +pub trait InferCtxtPrivExt<'hir, 'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; @@ -1246,6 +1290,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .emit(); } FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { + debug!( + "expected: {:?}, found: {:?}", + expected_found.expected, expected_found.found + ); + self.report_mismatched_consts( &error.obligation.cause, expected_found.expected, diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 42e3f0db15e53..673ec168b5ebb 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -629,11 +629,35 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { if c1.has_infer_types_or_consts() || c2.has_infer_types_or_consts() { ProcessResult::Unchanged } else { + debug!( + "ConstEquate error found, expected {:?}, found {:?}", + c1, c2 + ); + // Two different constants using generic parameters ~> error. let expected_found = ExpectedFound::new(true, c1, c2); + + let suggestion = self + .selcx + .infcx() + .try_create_suggestion_for_mismatched_const(expected_found) + .map(|sugg| { + let found_is_uneval = match expected_found.found.val { + ty::ConstKind::Unevaluated(_) => true, + _ => false, + }; + + if found_is_uneval { + format!("{{ {} }}", sugg) + } else { + sugg + } + }); + debug!(?suggestion); + ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError( expected_found, - TypeError::ConstMismatch(expected_found), + TypeError::ConstMismatchTooGeneric(expected_found, suggestion), )) } } diff --git a/src/test/ui/const-generics/defaults/generic-expr-default.stderr b/src/test/ui/const-generics/defaults/generic-expr-default.stderr index ada1498d1c80b..14c998cf01ac4 100644 --- a/src/test/ui/const-generics/defaults/generic-expr-default.stderr +++ b/src/test/ui/const-generics/defaults/generic-expr-default.stderr @@ -4,7 +4,7 @@ error: unconstrained generic constant LL | pub fn needs_evaluatable_bound() -> Foo { | ^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:` + = help: try adding a `where` bound using this expression: `where [(); { N1 + 1 }]:` error: unconstrained generic constant --> $DIR/generic-expr-default.rs:14:58 diff --git a/src/test/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr b/src/test/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr index 041232e869079..6ae6e3652c0e6 100644 --- a/src/test/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr @@ -4,7 +4,7 @@ error: unconstrained generic constant LL | struct ArithArrayLen([u32; 0 + N]); | ^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); 0 + N]:` + = help: try adding a `where` bound using this expression: `where [(); { 0 + N }]:` error: overly complex generic constant --> $DIR/array-size-in-generic-struct-param.rs:19:15 diff --git a/src/test/ui/const-generics/mismatched-const-errors.rs b/src/test/ui/const-generics/mismatched-const-errors.rs new file mode 100644 index 0000000000000..549b06e665f18 --- /dev/null +++ b/src/test/ui/const-generics/mismatched-const-errors.rs @@ -0,0 +1,39 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +pub trait Trait {} +struct HasCastInTraitImpl; +impl Trait for HasCastInTraitImpl {} +pub struct HasTrait(T); + +fn foo1() -> HasTrait> { + //~^ ERROR mismatched types + //~| ERROR unconstrained generic constant + loop {} +} + +fn foo2() -> HasTrait> { + //~^ ERROR mismatched types + //~| ERROR unconstrained generic constant + loop {} +} + +fn foo3() -> HasTrait> { + //~^ ERROR mismatched types + //~| ERROR unconstrained generic constant + loop {} +} + +fn foo4(c : [usize; N]) -> HasTrait> { + //~^ ERROR mismatched types + //~| ERROR unconstrained generic constant + loop {} +} + +fn foo5() -> HasTrait> { + //~^ ERROR mismatched types + //~| ERROR unconstrained generic constant + loop {} +} + +fn main() {} diff --git a/src/test/ui/const-generics/mismatched-const-errors.stderr b/src/test/ui/const-generics/mismatched-const-errors.stderr new file mode 100644 index 0000000000000..9975586755642 --- /dev/null +++ b/src/test/ui/const-generics/mismatched-const-errors.stderr @@ -0,0 +1,138 @@ +error: unconstrained generic constant + --> $DIR/mismatched-const-errors.rs:9:30 + | +LL | fn foo1() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { N + 2 + 1 }]:` +note: required because of the requirements on the impl of `Trait` for `HasCastInTraitImpl<{ N + 2}, N>` + --> $DIR/mismatched-const-errors.rs:6:22 + | +LL | impl Trait for HasCastInTraitImpl {} + | ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `HasTrait` + --> $DIR/mismatched-const-errors.rs:7:24 + | +LL | pub struct HasTrait(T); + | ^^^^^ required by this bound in `HasTrait` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-errors.rs:9:30 + | +LL | fn foo1() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `N`, found `{ N + 2 + 1 }` + | + = note: expected type `N` + found type `{ N + 2 + 1 }` + +error: unconstrained generic constant + --> $DIR/mismatched-const-errors.rs:15:30 + | +LL | fn foo2() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { N + 1 + 1 }]:` +note: required because of the requirements on the impl of `Trait` for `HasCastInTraitImpl<{ N + 1}, { N + 1 }>` + --> $DIR/mismatched-const-errors.rs:6:22 + | +LL | impl Trait for HasCastInTraitImpl {} + | ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `HasTrait` + --> $DIR/mismatched-const-errors.rs:7:24 + | +LL | pub struct HasTrait(T); + | ^^^^^ required by this bound in `HasTrait` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-errors.rs:15:30 + | +LL | fn foo2() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N + 1 }`, found `{ N + 1 + 1 }` + | + = note: expected type `{ N + 1 }` + found type `{ N + 1 + 1 }` + +error: unconstrained generic constant + --> $DIR/mismatched-const-errors.rs:21:30 + | +LL | fn foo3() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { N + 1 + 1 }]:` +note: required because of the requirements on the impl of `Trait` for `HasCastInTraitImpl<{ N + 1}, { N - 1}>` + --> $DIR/mismatched-const-errors.rs:6:22 + | +LL | impl Trait for HasCastInTraitImpl {} + | ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `HasTrait` + --> $DIR/mismatched-const-errors.rs:7:24 + | +LL | pub struct HasTrait(T); + | ^^^^^ required by this bound in `HasTrait` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-errors.rs:21:30 + | +LL | fn foo3() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N - 1}`, found `{ N + 1 + 1 }` + | + = note: expected type `{ N - 1}` + found type `{ N + 1 + 1 }` + +error: unconstrained generic constant + --> $DIR/mismatched-const-errors.rs:27:44 + | +LL | fn foo4(c : [usize; N]) -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { N - 1 + 1 }]:` +note: required because of the requirements on the impl of `Trait` for `HasCastInTraitImpl<{ N - 1}, N>` + --> $DIR/mismatched-const-errors.rs:6:22 + | +LL | impl Trait for HasCastInTraitImpl {} + | ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `HasTrait` + --> $DIR/mismatched-const-errors.rs:7:24 + | +LL | pub struct HasTrait(T); + | ^^^^^ required by this bound in `HasTrait` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-errors.rs:27:44 + | +LL | fn foo4(c : [usize; N]) -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `N`, found `{ N - 1 + 1 }` + | + = note: expected type `N` + found type `{ N - 1 + 1 }` + +error: unconstrained generic constant + --> $DIR/mismatched-const-errors.rs:33:30 + | +LL | fn foo5() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { N + N + 1 }]:` +note: required because of the requirements on the impl of `Trait` for `HasCastInTraitImpl<{ N + N }, { 2 * N }>` + --> $DIR/mismatched-const-errors.rs:6:22 + | +LL | impl Trait for HasCastInTraitImpl {} + | ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `HasTrait` + --> $DIR/mismatched-const-errors.rs:7:24 + | +LL | pub struct HasTrait(T); + | ^^^^^ required by this bound in `HasTrait` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-errors.rs:33:30 + | +LL | fn foo5() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ 2 * N }`, found `{ N + N + 1 }` + | + = note: expected type `{ 2 * N }` + found type `{ N + N + 1 }` + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/mismatched-const-types-precedence.rs b/src/test/ui/const-generics/mismatched-const-types-precedence.rs new file mode 100644 index 0000000000000..dcd9de28c789c --- /dev/null +++ b/src/test/ui/const-generics/mismatched-const-types-precedence.rs @@ -0,0 +1,41 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + + +fn bar(a: [u32; 2 + N], b: [u32; K]) { + let _: [u32; 3 * 2 + N + K] = foo::<{ 2 + N }, K>(a, b); + //~^ ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR mismatched types +} + +fn foo(a: [u32; M], b: [u32; K]) -> [u32; 3 * {M + K}] {} +//~^ ERROR mismatched types + + +fn bar2(a: [u32; 2 + N], b: [u32; K]) { + let _: [u32; 3 * 2 + N * K] = foo2::<{ 2 + N }, K>(a, b); + //~^ ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR mismatched types +} + +fn foo2(a: [u32; M], b: [u32; K]) -> [u32; 3 * M * K] {} +//~^ ERROR mismatched types + + +fn bar3(a: [u32; 2 + N], b: [u32; K + L]) { + let _: [u32; 3 * 2 + N * K + L] = foo3::<{ 2 + N }, K, L>(a, b); + //~^ ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR mismatched types +} + +fn foo3(a: [u32; M], b: [u32; K + L]) + -> [u32; 3 * M * {K + L}] {} +//~^ ERROR mismatched types + +fn main() {} diff --git a/src/test/ui/const-generics/mismatched-const-types-precedence.stderr b/src/test/ui/const-generics/mismatched-const-types-precedence.stderr new file mode 100644 index 0000000000000..4dd99bc173019 --- /dev/null +++ b/src/test/ui/const-generics/mismatched-const-types-precedence.stderr @@ -0,0 +1,160 @@ +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:6:33 + | +LL | let _: [u32; 3 * 2 + N + K] = foo::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * { 2 + N + K } }]:` +note: required by a bound in `foo` + --> $DIR/mismatched-const-types-precedence.rs:13:75 + | +LL | fn foo(a: [u32; M], b: [u32; K]) -> [u32; 3 * {M + K}] {} + | ^^^^^^^^^^^ required by this bound in `foo` + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:6:10 + | +LL | let _: [u32; 3 * 2 + N + K] = foo::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * 2 + N + K }]:` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-types-precedence.rs:6:33 + | +LL | let _: [u32; 3 * 2 + N + K] = foo::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3 * 2 + N + K`, found `{ 3 * { 2 + N + K } }` + | + = note: expected type `3 * 2 + N + K` + found type `{ 3 * { 2 + N + K } }` + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:6:33 + | +LL | let _: [u32; 3 * 2 + N + K] = foo::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * { 2 + N + K } }]:` +note: required by a bound in `foo` + --> $DIR/mismatched-const-types-precedence.rs:13:75 + | +LL | fn foo(a: [u32; M], b: [u32; K]) -> [u32; 3 * {M + K}] {} + | ^^^^^^^^^^^ required by this bound in `foo` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-types-precedence.rs:13:69 + | +LL | fn foo(a: [u32; M], b: [u32; K]) -> [u32; 3 * {M + K}] {} + | --- ^^^^^^^^^^^^^^^^^^ expected array `[u32; _]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:18:33 + | +LL | let _: [u32; 3 * 2 + N * K] = foo2::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * { 2 + N } * K }]:` +note: required by a bound in `foo2` + --> $DIR/mismatched-const-types-precedence.rs:25:76 + | +LL | fn foo2(a: [u32; M], b: [u32; K]) -> [u32; 3 * M * K] {} + | ^^^^^^^^^ required by this bound in `foo2` + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:18:10 + | +LL | let _: [u32; 3 * 2 + N * K] = foo2::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * 2 + N * K }]:` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-types-precedence.rs:18:33 + | +LL | let _: [u32; 3 * 2 + N * K] = foo2::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3 * 2 + N * K`, found `{ 3 * { 2 + N } * K }` + | + = note: expected type `3 * 2 + N * K` + found type `{ 3 * { 2 + N } * K }` + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:18:33 + | +LL | let _: [u32; 3 * 2 + N * K] = foo2::<{ 2 + N }, K>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * { 2 + N } * K }]:` +note: required by a bound in `foo2` + --> $DIR/mismatched-const-types-precedence.rs:25:76 + | +LL | fn foo2(a: [u32; M], b: [u32; K]) -> [u32; 3 * M * K] {} + | ^^^^^^^^^ required by this bound in `foo2` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-types-precedence.rs:25:70 + | +LL | fn foo2(a: [u32; M], b: [u32; K]) -> [u32; 3 * M * K] {} + | ---- ^^^^^^^^^^^^^^^^ expected array `[u32; _]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:30:37 + | +LL | let _: [u32; 3 * 2 + N * K + L] = foo3::<{ 2 + N }, K, L>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * { 2 + N } * K + L }]:` +note: required by a bound in `foo3` + --> $DIR/mismatched-const-types-precedence.rs:38:12 + | +LL | fn foo3(a: [u32; M], b: [u32; K + L]) + | ---- required by a bound in this +LL | -> [u32; 3 * M * {K + L}] {} + | ^^^^^^^^^^^^^^^ required by this bound in `foo3` + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:30:10 + | +LL | let _: [u32; 3 * 2 + N * K + L] = foo3::<{ 2 + N }, K, L>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * 2 + N * K + L }]:` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-types-precedence.rs:30:37 + | +LL | let _: [u32; 3 * 2 + N * K + L] = foo3::<{ 2 + N }, K, L>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3 * 2 + N * K + L`, found `{ 3 * { 2 + N } * K + L }` + | + = note: expected type `3 * 2 + N * K + L` + found type `{ 3 * { 2 + N } * K + L }` + +error: unconstrained generic constant + --> $DIR/mismatched-const-types-precedence.rs:30:37 + | +LL | let _: [u32; 3 * 2 + N * K + L] = foo3::<{ 2 + N }, K, L>(a, b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { 3 * { 2 + N } * K + L }]:` +note: required by a bound in `foo3` + --> $DIR/mismatched-const-types-precedence.rs:38:12 + | +LL | fn foo3(a: [u32; M], b: [u32; K + L]) + | ---- required by a bound in this +LL | -> [u32; 3 * M * {K + L}] {} + | ^^^^^^^^^^^^^^^ required by this bound in `foo3` + +error[E0308]: mismatched types + --> $DIR/mismatched-const-types-precedence.rs:38:6 + | +LL | fn foo3(a: [u32; M], b: [u32; K + L]) + | ---- implicitly returns `()` as its body has no tail or `return` expression +LL | -> [u32; 3 * M * {K + L}] {} + | ^^^^^^^^^^^^^^^^^^^^^^ expected array `[u32; _]`, found `()` + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0308`.