Skip to content

Commit bec29f5

Browse files
authored
Rollup merge of rust-lang#91272 - FabianWolff:issue-90870-const-fn-eq, r=wesleywiser
Print a suggestion when comparing references to primitive types in `const fn` Fixes rust-lang#90870.
2 parents c5c9494 + b38a540 commit bec29f5

File tree

5 files changed

+170
-10
lines changed

5 files changed

+170
-10
lines changed

compiler/rustc_const_eval/src/transform/check_consts/check.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
801801
if let Some(trait_id) = tcx.trait_of_item(callee) {
802802
trace!("attempting to call a trait method");
803803
if !self.tcx.features().const_trait_impl {
804-
self.check_op(ops::FnCallNonConst);
804+
self.check_op(ops::FnCallNonConst(Some((callee, substs))));
805805
return;
806806
}
807807

@@ -857,7 +857,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
857857
}
858858

859859
if !nonconst_call_permission {
860-
self.check_op(ops::FnCallNonConst);
860+
self.check_op(ops::FnCallNonConst(None));
861861
return;
862862
}
863863
}
@@ -926,7 +926,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
926926
}
927927

928928
if !nonconst_call_permission {
929-
self.check_op(ops::FnCallNonConst);
929+
self.check_op(ops::FnCallNonConst(None));
930930
return;
931931
}
932932
}

compiler/rustc_const_eval/src/transform/check_consts/ops.rs

+63-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
//! Concrete error types for all operations which may be invalid in a certain const context.
22
3-
use rustc_errors::{struct_span_err, DiagnosticBuilder};
3+
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
44
use rustc_hir as hir;
55
use rustc_hir::def_id::DefId;
6-
use rustc_middle::mir;
6+
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
7+
use rustc_middle::{mir, ty::AssocKind};
78
use rustc_session::parse::feature_err;
89
use rustc_span::symbol::sym;
9-
use rustc_span::{Span, Symbol};
10+
use rustc_span::{symbol::Ident, Span, Symbol};
11+
use rustc_span::{BytePos, Pos};
1012

1113
use super::ConstCx;
1214

@@ -72,17 +74,71 @@ impl NonConstOp for FnCallIndirect {
7274

7375
/// A function call where the callee is not marked as `const`.
7476
#[derive(Debug)]
75-
pub struct FnCallNonConst;
76-
impl NonConstOp for FnCallNonConst {
77+
pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>);
78+
impl<'a> NonConstOp for FnCallNonConst<'a> {
7779
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
78-
struct_span_err!(
80+
let mut err = struct_span_err!(
7981
ccx.tcx.sess,
8082
span,
8183
E0015,
8284
"calls in {}s are limited to constant functions, \
8385
tuple structs and tuple variants",
8486
ccx.const_kind(),
85-
)
87+
);
88+
89+
if let FnCallNonConst(Some((callee, substs))) = *self {
90+
if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() {
91+
if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind(
92+
ccx.tcx,
93+
Ident::with_dummy_span(sym::eq),
94+
AssocKind::Fn,
95+
trait_def_id,
96+
) {
97+
if callee == eq_item.def_id && substs.len() == 2 {
98+
match (substs[0].unpack(), substs[1].unpack()) {
99+
(GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
100+
if self_ty == rhs_ty
101+
&& self_ty.is_ref()
102+
&& self_ty.peel_refs().is_primitive() =>
103+
{
104+
let mut num_refs = 0;
105+
let mut tmp_ty = self_ty;
106+
while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
107+
num_refs += 1;
108+
tmp_ty = inner_ty;
109+
}
110+
let deref = "*".repeat(num_refs);
111+
112+
if let Ok(call_str) =
113+
ccx.tcx.sess.source_map().span_to_snippet(span)
114+
{
115+
if let Some(eq_idx) = call_str.find("==") {
116+
if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
117+
.find(|c: char| !c.is_whitespace())
118+
{
119+
let rhs_pos = span.lo()
120+
+ BytePos::from_usize(eq_idx + 2 + rhs_idx);
121+
let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
122+
err.multipart_suggestion(
123+
"consider dereferencing here",
124+
vec![
125+
(span.shrink_to_lo(), deref.clone()),
126+
(rhs_span, deref),
127+
],
128+
Applicability::MachineApplicable,
129+
);
130+
}
131+
}
132+
}
133+
}
134+
_ => {}
135+
}
136+
}
137+
}
138+
}
139+
}
140+
141+
err
86142
}
87143
}
88144

src/test/ui/consts/issue-90870.fixed

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Regression test for issue #90870.
2+
3+
// run-rustfix
4+
5+
#![allow(dead_code)]
6+
7+
const fn f(a: &u8, b: &u8) -> bool {
8+
*a == *b
9+
//~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
10+
//~| HELP: consider dereferencing here
11+
}
12+
13+
const fn g(a: &&&&i64, b: &&&&i64) -> bool {
14+
****a == ****b
15+
//~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
16+
//~| HELP: consider dereferencing here
17+
}
18+
19+
const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
20+
while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
21+
if *l == *r {
22+
//~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
23+
//~| HELP: consider dereferencing here
24+
a = at;
25+
b = bt;
26+
} else {
27+
return false;
28+
}
29+
}
30+
31+
a.is_empty() && b.is_empty()
32+
}
33+
34+
fn main() {}

src/test/ui/consts/issue-90870.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Regression test for issue #90870.
2+
3+
// run-rustfix
4+
5+
#![allow(dead_code)]
6+
7+
const fn f(a: &u8, b: &u8) -> bool {
8+
a == b
9+
//~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
10+
//~| HELP: consider dereferencing here
11+
}
12+
13+
const fn g(a: &&&&i64, b: &&&&i64) -> bool {
14+
a == b
15+
//~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
16+
//~| HELP: consider dereferencing here
17+
}
18+
19+
const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
20+
while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
21+
if l == r {
22+
//~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
23+
//~| HELP: consider dereferencing here
24+
a = at;
25+
b = bt;
26+
} else {
27+
return false;
28+
}
29+
}
30+
31+
a.is_empty() && b.is_empty()
32+
}
33+
34+
fn main() {}

src/test/ui/consts/issue-90870.stderr

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
2+
--> $DIR/issue-90870.rs:8:5
3+
|
4+
LL | a == b
5+
| ^^^^^^
6+
|
7+
help: consider dereferencing here
8+
|
9+
LL | *a == *b
10+
| + +
11+
12+
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
13+
--> $DIR/issue-90870.rs:14:5
14+
|
15+
LL | a == b
16+
| ^^^^^^
17+
|
18+
help: consider dereferencing here
19+
|
20+
LL | ****a == ****b
21+
| ++++ ++++
22+
23+
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
24+
--> $DIR/issue-90870.rs:21:12
25+
|
26+
LL | if l == r {
27+
| ^^^^^^
28+
|
29+
help: consider dereferencing here
30+
|
31+
LL | if *l == *r {
32+
| + +
33+
34+
error: aborting due to 3 previous errors
35+
36+
For more information about this error, try `rustc --explain E0015`.

0 commit comments

Comments
 (0)