From b38a54049ed89f00f0519b37d10ae2ae1e69b173 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Fri, 26 Nov 2021 23:10:46 +0100 Subject: [PATCH] Print a suggestion when comparing references to primitive types in constant functions --- .../src/transform/check_consts/check.rs | 6 +- .../src/transform/check_consts/ops.rs | 70 +++++++++++++++++-- src/test/ui/consts/issue-90870.fixed | 34 +++++++++ src/test/ui/consts/issue-90870.rs | 34 +++++++++ src/test/ui/consts/issue-90870.stderr | 36 ++++++++++ 5 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/consts/issue-90870.fixed create mode 100644 src/test/ui/consts/issue-90870.rs create mode 100644 src/test/ui/consts/issue-90870.stderr diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 4e3a8b64094..e7313201c03 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -812,7 +812,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location if let Some(trait_id) = tcx.trait_of_item(callee) { trace!("attempting to call a trait method"); if !self.tcx.features().const_trait_impl { - self.check_op(ops::FnCallNonConst); + self.check_op(ops::FnCallNonConst(Some((callee, substs)))); return; } @@ -868,7 +868,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst); + self.check_op(ops::FnCallNonConst(None)); return; } } @@ -937,7 +937,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst); + self.check_op(ops::FnCallNonConst(None)); return; } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 6391c886009..421c559474a 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -1,12 +1,14 @@ //! Concrete error types for all operations which may be invalid in a certain const context. -use rustc_errors::{struct_span_err, DiagnosticBuilder}; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::mir; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::{mir, ty::AssocKind}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{symbol::Ident, Span, Symbol}; +use rustc_span::{BytePos, Pos}; use super::ConstCx; @@ -72,17 +74,71 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder< /// A function call where the callee is not marked as `const`. #[derive(Debug)] -pub struct FnCallNonConst; -impl NonConstOp for FnCallNonConst { +pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>); +impl<'a> NonConstOp for FnCallNonConst<'a> { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - struct_span_err!( + let mut err = struct_span_err!( ccx.tcx.sess, span, E0015, "calls in {}s are limited to constant functions, \ tuple structs and tuple variants", ccx.const_kind(), - ) + ); + + if let FnCallNonConst(Some((callee, substs))) = *self { + if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() { + if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind( + ccx.tcx, + Ident::with_dummy_span(sym::eq), + AssocKind::Fn, + trait_def_id, + ) { + if callee == eq_item.def_id && substs.len() == 2 { + match (substs[0].unpack(), substs[1].unpack()) { + (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) + if self_ty == rhs_ty + && self_ty.is_ref() + && self_ty.peel_refs().is_primitive() => + { + let mut num_refs = 0; + let mut tmp_ty = self_ty; + while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { + num_refs += 1; + tmp_ty = inner_ty; + } + let deref = "*".repeat(num_refs); + + if let Ok(call_str) = + ccx.tcx.sess.source_map().span_to_snippet(span) + { + if let Some(eq_idx) = call_str.find("==") { + if let Some(rhs_idx) = call_str[(eq_idx + 2)..] + .find(|c: char| !c.is_whitespace()) + { + let rhs_pos = span.lo() + + BytePos::from_usize(eq_idx + 2 + rhs_idx); + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); + err.multipart_suggestion( + "consider dereferencing here", + vec![ + (span.shrink_to_lo(), deref.clone()), + (rhs_span, deref), + ], + Applicability::MachineApplicable, + ); + } + } + } + } + _ => {} + } + } + } + } + } + + err } } diff --git a/src/test/ui/consts/issue-90870.fixed b/src/test/ui/consts/issue-90870.fixed new file mode 100644 index 00000000000..e767effcdd0 --- /dev/null +++ b/src/test/ui/consts/issue-90870.fixed @@ -0,0 +1,34 @@ +// Regression test for issue #90870. + +// run-rustfix + +#![allow(dead_code)] + +const fn f(a: &u8, b: &u8) -> bool { + *a == *b + //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015] + //~| HELP: consider dereferencing here +} + +const fn g(a: &&&&i64, b: &&&&i64) -> bool { + ****a == ****b + //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015] + //~| HELP: consider dereferencing here +} + +const fn h(mut a: &[u8], mut b: &[u8]) -> bool { + while let ([l, at @ ..], [r, bt @ ..]) = (a, b) { + if *l == *r { + //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015] + //~| HELP: consider dereferencing here + a = at; + b = bt; + } else { + return false; + } + } + + a.is_empty() && b.is_empty() +} + +fn main() {} diff --git a/src/test/ui/consts/issue-90870.rs b/src/test/ui/consts/issue-90870.rs new file mode 100644 index 00000000000..35b3c8242aa --- /dev/null +++ b/src/test/ui/consts/issue-90870.rs @@ -0,0 +1,34 @@ +// Regression test for issue #90870. + +// run-rustfix + +#![allow(dead_code)] + +const fn f(a: &u8, b: &u8) -> bool { + a == b + //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015] + //~| HELP: consider dereferencing here +} + +const fn g(a: &&&&i64, b: &&&&i64) -> bool { + a == b + //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015] + //~| HELP: consider dereferencing here +} + +const fn h(mut a: &[u8], mut b: &[u8]) -> bool { + while let ([l, at @ ..], [r, bt @ ..]) = (a, b) { + if l == r { + //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015] + //~| HELP: consider dereferencing here + a = at; + b = bt; + } else { + return false; + } + } + + a.is_empty() && b.is_empty() +} + +fn main() {} diff --git a/src/test/ui/consts/issue-90870.stderr b/src/test/ui/consts/issue-90870.stderr new file mode 100644 index 00000000000..0e33e6ebe5a --- /dev/null +++ b/src/test/ui/consts/issue-90870.stderr @@ -0,0 +1,36 @@ +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-90870.rs:8:5 + | +LL | a == b + | ^^^^^^ + | +help: consider dereferencing here + | +LL | *a == *b + | + + + +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-90870.rs:14:5 + | +LL | a == b + | ^^^^^^ + | +help: consider dereferencing here + | +LL | ****a == ****b + | ++++ ++++ + +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-90870.rs:21:12 + | +LL | if l == r { + | ^^^^^^ + | +help: consider dereferencing here + | +LL | if *l == *r { + | + + + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0015`.