Print a suggestion when comparing references to primitive types in constant functions

This commit is contained in:
Fabian Wolff 2021-11-26 23:10:46 +01:00
parent dd549dcab4
commit b38a54049e
5 changed files with 170 additions and 10 deletions

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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`.