From 9e75cbed1264d1af2dd29f44a657bdd49a59180a Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 17 Nov 2021 18:47:02 +0100 Subject: [PATCH 01/11] introduce new TypeError variant: ConstMismatchTooGeneric --- compiler/rustc_middle/src/ty/error.rs | 8 ++++++++ compiler/rustc_middle/src/ty/structural_impls.rs | 3 +++ 2 files changed, 11 insertions(+) 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), From 2569e599b044ece4b2874dff708719e35b53e61f Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 17 Nov 2021 18:48:57 +0100 Subject: [PATCH 02/11] make BinOp and UnOp 'displayable' --- compiler/rustc_middle/src/mir/mod.rs | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d2dd15aad1276..347ec477420bb 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2328,6 +2328,30 @@ 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, + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] @@ -2348,6 +2372,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::*; From 7bf3dc1660842e7be8ff54945103416a22644d17 Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 17 Nov 2021 18:50:46 +0100 Subject: [PATCH 03/11] allow AbstractConsts to be printed for diagnostics --- .../src/traits/const_evaluatable.rs | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0ea3a18ca34fa..9b2cbf0668a14 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,61 @@ 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(mut self, tcx: TyCtxt<'tcx>) -> Option { + // try to replace substs + while let abstract_const::Node::Leaf(ct) = self.root(tcx) { + match AbstractConst::from_const(tcx, ct) { + Ok(Some(act)) => self = act, + Ok(None) => break, + Err(_) => bug!("should be able to create AbstractConst here"), + } + } + + match self.root(tcx) { + abstract_const::Node::Leaf(ct) => match ct.val { + ty::ConstKind::Error(_) | ty::ConstKind::Infer(_) => return None, + ty::ConstKind::Param(c) => return Some(format!("{}", c)), + ty::ConstKind::Value(ConstValue::Scalar(scalar)) => match scalar.to_i64() { + Ok(s) => return Some(format!("{}", s)), + Err(_) => return None, + }, + _ => return None, + }, + abstract_const::Node::Binop(op, l, r) => { + let op = match op.try_as_string() { + Some(o) => o, + None => return None, + }; + + let left = self.subtree(l).try_print_with_replacing_substs(tcx); + debug!(?left); + + let right = self.subtree(r).try_print_with_replacing_substs(tcx); + debug!(?right); + + match (left, right) { + (Some(l), Some(r)) => { + return Some(format!("{} {} {}", l, op, r)); + } + _ => return None, + } + } + abstract_const::Node::UnaryOp(op, v) => { + match self.subtree(v).try_print_with_replacing_substs(tcx) { + Some(operand) => { + return Some(format!("{}{}", op, operand)); + } + None => return None, + } + } + _ => return None, + } + } } struct AbstractConstBuilder<'a, 'tcx> { From 4bcd67c9a767a0968939f6de85cbbc847b379a5a Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 17 Nov 2021 18:52:15 +0100 Subject: [PATCH 04/11] create suggestions for mismatched consts --- Cargo.lock | 6 ++--- .../src/traits/error_reporting/mod.rs | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 961a3ba9f19cf..47166a636a155 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,7 +276,7 @@ dependencies = [ [[package]] name = "cargo" -version = "0.60.0" +version = "0.59.0" dependencies = [ "anyhow", "atty", @@ -419,7 +419,7 @@ dependencies = [ [[package]] name = "cargo-util" -version = "0.1.2" +version = "0.1.1" dependencies = [ "anyhow", "core-foundation", @@ -768,7 +768,7 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] name = "crates-io" -version = "0.33.1" +version = "0.33.0" dependencies = [ "anyhow", "curl", 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..9b6a1ee42bdef 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}; @@ -1173,6 +1175,11 @@ trait InferCtxtPrivExt<'hir, 'tcx> { obligated_types: &mut Vec<&ty::TyS<'tcx>>, cause_code: &ObligationCauseCode<'tcx>, ) -> bool; + + fn try_create_suggestion_for_mismatched_const( + &self, + expected_found: ExpectedFound<&'tcx ty::Const<'tcx>>, + ) -> Option; } impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { @@ -1246,6 +1253,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, @@ -1489,6 +1501,16 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } } + 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, + } + } + fn report_similar_impl_candidates( &self, impl_candidates: Vec>, From 3b925830cfc0044f90a369b2c43ca7209544d5b7 Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 17 Nov 2021 18:54:29 +0100 Subject: [PATCH 05/11] try to create suggestions for mismatched consts in FulfillmentError::TooGeneric cases --- .../src/infer/error_reporting/mod.rs | 39 ++++++++++++++++--- compiler/rustc_infer/src/infer/mod.rs | 1 + .../src/traits/fulfill.rs | 27 ++++++++++++- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 9a76c05e4f620..2c810ae502d70 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -856,7 +856,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .regions() .map(|lifetime| { let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } + if s.is_empty() { + "'_".to_string() + } else { + s + } }) .collect::>() .join(", "); @@ -1175,7 +1179,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn lifetime_display(lifetime: Region<'_>) -> String { let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } + if s.is_empty() { + "'_".to_string() + } else { + s + } } // At one point we'd like to elide all lifetimes here, they are irrelevant for // all diagnostics that use this output @@ -1449,7 +1457,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 +1588,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 +1659,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 +1713,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 +1737,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 +1753,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 +1780,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 +2046,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 +2062,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_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 42e3f0db15e53..f4eb91c87ed02 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -27,6 +27,7 @@ use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::error_reporting::InferCtxtPrivExt; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -629,11 +630,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), )) } } From 3c9701b59984bb773cda95138aeab4df0d553a6b Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 17 Nov 2021 20:07:15 +0100 Subject: [PATCH 06/11] add tests --- .../const-generics/mismatched-const-errors.rs | 39 +++++ .../mismatched-const-errors.stderr | 138 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 src/test/ui/const-generics/mismatched-const-errors.rs create mode 100644 src/test/ui/const-generics/mismatched-const-errors.stderr 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..f257bd274e1d1 --- /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 [(); { M + 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 [(); { M + 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 [(); { M + 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:28:44 + | +LL | fn foo4(c : [usize; N]) -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { M + 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:28: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:34:30 + | +LL | fn foo5() -> HasTrait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { M + 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:34: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`. From e1b1b2f00b94437ca06d0b3aa5505c475a4c47f5 Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 18 Nov 2021 12:27:26 +0100 Subject: [PATCH 07/11] suggest correct anonymous constant in where bounds in NonConstEvaluatable error message --- .../src/traits/error_reporting/mod.rs | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) 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 9b6a1ee42bdef..e189a13efc72e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -804,6 +804,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, @@ -822,16 +824,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 } _ => { From bf67758be965d7e0b16f1ad8d8616c482013f8fc Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 18 Nov 2021 12:44:57 +0100 Subject: [PATCH 08/11] bless tests --- .../defaults/generic-expr-default.stderr | 2 +- ...ay-size-in-generic-struct-param.full.stderr | 2 +- .../mismatched-const-errors.stderr | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) 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.stderr b/src/test/ui/const-generics/mismatched-const-errors.stderr index f257bd274e1d1..9975586755642 100644 --- a/src/test/ui/const-generics/mismatched-const-errors.stderr +++ b/src/test/ui/const-generics/mismatched-const-errors.stderr @@ -4,7 +4,7 @@ error: unconstrained generic constant LL | fn foo1() -> HasTrait> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { M + 1 }]:` + = 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 | @@ -31,7 +31,7 @@ error: unconstrained generic constant LL | fn foo2() -> HasTrait> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { M + 1 }]:` + = 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 | @@ -58,7 +58,7 @@ error: unconstrained generic constant LL | fn foo3() -> HasTrait> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { M + 1 }]:` + = 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 | @@ -80,12 +80,12 @@ LL | fn foo3() -> HasTrait $DIR/mismatched-const-errors.rs:28:44 + --> $DIR/mismatched-const-errors.rs:27:44 | LL | fn foo4(c : [usize; N]) -> HasTrait> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { M + 1 }]:` + = 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 | @@ -98,7 +98,7 @@ LL | pub struct HasTrait(T); | ^^^^^ required by this bound in `HasTrait` error[E0308]: mismatched types - --> $DIR/mismatched-const-errors.rs:28:44 + --> $DIR/mismatched-const-errors.rs:27:44 | LL | fn foo4(c : [usize; N]) -> HasTrait> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `N`, found `{ N - 1 + 1 }` @@ -107,12 +107,12 @@ LL | fn foo4(c : [usize; N]) -> HasTrait $DIR/mismatched-const-errors.rs:34:30 + --> $DIR/mismatched-const-errors.rs:33:30 | LL | fn foo5() -> HasTrait> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { M + 1 }]:` + = 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 | @@ -125,7 +125,7 @@ LL | pub struct HasTrait(T); | ^^^^^ required by this bound in `HasTrait` error[E0308]: mismatched types - --> $DIR/mismatched-const-errors.rs:34:30 + --> $DIR/mismatched-const-errors.rs:33:30 | LL | fn foo5() -> HasTrait> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ 2 * N }`, found `{ N + N + 1 }` From bb8be4f985a4a745c427d78038d5dcf23ca63a13 Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 24 Nov 2021 13:25:22 +0100 Subject: [PATCH 09/11] implement precedence for anon const mismatch suggestions --- .../src/infer/error_reporting/mod.rs | 12 +- compiler/rustc_middle/src/mir/mod.rs | 24 +++ .../src/traits/const_evaluatable.rs | 165 ++++++++++++++++-- 3 files changed, 174 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 2c810ae502d70..b1db2ac2e74a9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -856,11 +856,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .regions() .map(|lifetime| { let s = lifetime.to_string(); - if s.is_empty() { - "'_".to_string() - } else { - s - } + if s.is_empty() { "'_".to_string() } else { s } }) .collect::>() .join(", "); @@ -1179,11 +1175,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn lifetime_display(lifetime: Region<'_>) -> String { let s = lifetime.to_string(); - if s.is_empty() { - "'_".to_string() - } else { - s - } + if s.is_empty() { "'_".to_string() } else { s } } // At one point we'd like to elide all lifetimes here, they are irrelevant for // all diagnostics that use this output diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 347ec477420bb..6852da9b50a1b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2352,6 +2352,30 @@ impl BinOp { 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)] diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 9b2cbf0668a14..ad9ffebd14754 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -231,54 +231,164 @@ impl<'tcx> AbstractConst<'tcx> { /// 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(mut self, tcx: TyCtxt<'tcx>) -> Option { + 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)) => self = act, + 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) => return Some(format!("{}", c)), + 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)) => match scalar.to_i64() { - Ok(s) => return Some(format!("{}", s)), + Ok(s) => { + let s = format!("{}", s); + + if applied_subst { + return Some(PrintStyle::Braces(None, s)); + } else { + return Some(PrintStyle::NoBraces(s)); + } + } Err(_) => return None, }, _ => return None, }, abstract_const::Node::Binop(op, l, r) => { - let op = match op.try_as_string() { + let op_str = match op.try_as_string() { Some(o) => o, None => return None, }; - let left = self.subtree(l).try_print_with_replacing_substs(tcx); + // 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).try_print_with_replacing_substs(tcx); + let right = self.subtree(r).recursive_try_print_with_replacing_substs(tcx); debug!(?right); - match (left, right) { - (Some(l), Some(r)) => { - return Some(format!("{} {} {}", l, op, r)); + 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 + } } - _ => return None, + 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(op, v) => { - match self.subtree(v).try_print_with_replacing_substs(tcx) { - Some(operand) => { - return Some(format!("{}{}", op, operand)); + 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)) } - None => return None, + s @ Some(PrintStyle::NoBraces(_)) => s, + None => None, } } - _ => return None, + _ => None, } } } @@ -551,6 +661,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>, From a262e3ed0c32b0fe3296e96b5960949b375153c1 Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 24 Nov 2021 13:25:48 +0100 Subject: [PATCH 10/11] add tests for precedence --- .../mismatched-const-types-precedence.rs | 41 +++++ .../mismatched-const-types-precedence.stderr | 160 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 src/test/ui/const-generics/mismatched-const-types-precedence.rs create mode 100644 src/test/ui/const-generics/mismatched-const-types-precedence.stderr 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`. From 7b72ba87bc731b3a0450cd5d8990cfa76039c622 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 24 Dec 2021 15:19:19 +0100 Subject: [PATCH 11/11] properly convert scalar to string --- Cargo.lock | 6 +- .../rustc_middle/src/mir/interpret/value.rs | 74 ++++++++++++++++++- .../src/traits/const_evaluatable.rs | 24 +++--- .../src/traits/error_reporting/mod.rs | 32 ++++---- .../src/traits/fulfill.rs | 1 - 5 files changed, 106 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47166a636a155..961a3ba9f19cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,7 +276,7 @@ dependencies = [ [[package]] name = "cargo" -version = "0.59.0" +version = "0.60.0" dependencies = [ "anyhow", "atty", @@ -419,7 +419,7 @@ dependencies = [ [[package]] name = "cargo-util" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", "core-foundation", @@ -768,7 +768,7 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] name = "crates-io" -version = "0.33.0" +version = "0.33.1" dependencies = [ "anyhow", "curl", 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_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index ad9ffebd14754..6103bf2198110 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -284,18 +284,22 @@ impl<'tcx> AbstractConst<'tcx> { return Some(PrintStyle::NoBraces(s)); } } - ty::ConstKind::Value(ConstValue::Scalar(scalar)) => match scalar.to_i64() { - Ok(s) => { - let s = format!("{}", s); - - 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, } - Err(_) => return None, - }, + } _ => return None, }, abstract_const::Node::Binop(op, l, r) => { 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 e189a13efc72e..c24cf4f47a1dc 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -88,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> { @@ -1091,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; @@ -1202,11 +1217,6 @@ trait InferCtxtPrivExt<'hir, 'tcx> { obligated_types: &mut Vec<&ty::TyS<'tcx>>, cause_code: &ObligationCauseCode<'tcx>, ) -> bool; - - fn try_create_suggestion_for_mismatched_const( - &self, - expected_found: ExpectedFound<&'tcx ty::Const<'tcx>>, - ) -> Option; } impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { @@ -1528,16 +1538,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } } - 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, - } - } - fn report_similar_impl_candidates( &self, impl_candidates: Vec>, diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index f4eb91c87ed02..673ec168b5ebb 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -27,7 +27,6 @@ use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; use crate::traits::error_reporting::InferCtxtExt as _; -use crate::traits::error_reporting::InferCtxtPrivExt; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _;