Skip to content

Print a suggestion when comparing references to primitive types in const fn #91272

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions compiler/rustc_const_eval/src/transform/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,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;
}

Expand Down Expand Up @@ -868,7 +868,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
}

if !nonconst_call_permission {
self.check_op(ops::FnCallNonConst);
self.check_op(ops::FnCallNonConst(None));
return;
}
}
Expand Down Expand Up @@ -937,7 +937,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
}

if !nonconst_call_permission {
self.check_op(ops::FnCallNonConst);
self.check_op(ops::FnCallNonConst(None));
return;
}
}
Expand Down
70 changes: 63 additions & 7 deletions compiler/rustc_const_eval/src/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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
}
}

Expand Down
34 changes: 34 additions & 0 deletions src/test/ui/consts/issue-90870.fixed
Original file line number Diff line number Diff line change
@@ -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() {}
34 changes: 34 additions & 0 deletions src/test/ui/consts/issue-90870.rs
Original file line number Diff line number Diff line change
@@ -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() {}
36 changes: 36 additions & 0 deletions src/test/ui/consts/issue-90870.stderr
Original file line number Diff line number Diff line change
@@ -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`.