about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs70
-rw-r--r--src/test/ui/consts/issue-90870.fixed34
-rw-r--r--src/test/ui/consts/issue-90870.rs34
-rw-r--r--src/test/ui/consts/issue-90870.stderr36
5 files changed, 170 insertions, 10 deletions
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 274665ccd98..4a02e059537 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -801,7 +801,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
                 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;
                     }
 
@@ -857,7 +857,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
                             }
 
                             if !nonconst_call_permission {
-                                self.check_op(ops::FnCallNonConst);
+                                self.check_op(ops::FnCallNonConst(None));
                                 return;
                             }
                         }
@@ -926,7 +926,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
                     }
 
                     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 @@ impl NonConstOp for FnCallIndirect {
 
 /// 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`.