Skip to content

Provide additional context to errors involving const traits #144194

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
90 changes: 77 additions & 13 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! Concrete error types for all operations which may be invalid in a certain const context.
use hir::{ConstContext, LangItem};
use rustc_errors::Diag;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
Expand Down Expand Up @@ -211,6 +212,7 @@ fn build_error_for_const_call<'tcx>(

debug!(?call_kind);

let mut note = true;
let mut err = match call_kind {
CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
macro_rules! error {
Expand Down Expand Up @@ -355,20 +357,82 @@ fn build_error_for_const_call<'tcx>(
non_or_conditionally,
})
}
_ => ccx.dcx().create_err(errors::NonConstFnCall {
span,
def_descr: ccx.tcx.def_descr(callee),
def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
kind: ccx.const_kind(),
non_or_conditionally,
}),
_ => {
let def_descr = ccx.tcx.def_descr(callee);
let mut err = ccx.dcx().create_err(errors::NonConstFnCall {
span,
def_descr,
def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
kind: ccx.const_kind(),
non_or_conditionally,
});
let context_span = ccx.tcx.def_span(ccx.def_id());
err.span_label(context_span, format!(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this to a label only for this case, and keep it as a note for the rest? Having special logic changing it between a note and a label seems too special cased. I'd prefer if you reverted it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is pretty long for a label too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to point at the constant context in the output in some way, as it isn't always trivially obvious why a constant function was expected. As we already had a note talking about the const context I moved that same message over from the note to the label so we wouldn't need both.

It was

error[E0015]: cannot call non-const method `<T as Foo>::a` in constant functions
  --> const-super-trait.rs:10:7
   |
LL | const fn foo<T: ~const Bar>(x: &T) {
   | ---------------------------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
LL |     x.a();
   |       ^^^

vs

error[E0015]: cannot call non-const method `<T as Foo>::a` in constant functions
  --> const-super-trait.rs:10:7
   |
LL |     x.a();
   |       ^^^
   |
   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

We could instead have

error[E0015]: cannot call non-const method `<T as Foo>::a` in constant functions
  --> const-super-trait.rs:10:7
   |
LL | const fn foo<T: ~const Bar>(x: &T) {
   | ---------------------------------- required by this constant function
LL |     x.a();
   |       ^^^
   |
   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

I was just trying to keep the verbosity increase to a minimum.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to point at the constant context in the output in some way, as it isn't always trivially obvious why a constant function was expected.

I don't think the users need this to be a label. There's a tradeoff here with verbosity and I think this both complicates the implementation by having that note boolean and also splits the presentation of the diagnostic.

"calls in {}s are limited to constant functions, tuple structs and tuple variants",
ccx.const_kind(),
));
note = false;
let def_kind = ccx.tcx.def_kind(callee);
if let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn = def_kind {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callees are always AssocFns, why did you add DefKind::AssocTy | DefKind::AssocConst too?

let parent = ccx.tcx.parent(callee);
if let DefKind::Trait = ccx.tcx.def_kind(parent)
Comment on lines +376 to +378
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole check could be simplified if you looked at opt_associated_item and checked both the AssocKind and the AssocItemContainer, rather than using manual parenting logic.

&& !ccx.tcx.is_const_trait(parent)
{
let assoc_span = ccx.tcx.def_span(callee);
let assoc_name = ccx.tcx.item_name(callee);
let mut span: MultiSpan = ccx.tcx.def_span(parent).into();
span.push_span_label(assoc_span, format!("this {def_descr} is not const"));
let trait_descr = ccx.tcx.def_descr(parent);
let trait_span = ccx.tcx.def_span(parent);
let trait_name = ccx.tcx.item_name(parent);
span.push_span_label(trait_span, format!("this {trait_descr} is not const"));
err.span_note(
span,
format!(
"{def_descr} `{assoc_name}` is not const because {trait_descr} \
`{trait_name}` is not const",
),
);
if parent.is_local() && ccx.tcx.sess.is_nightly_build() {
if !ccx.tcx.features().const_trait_impl() {
err.help(
"add `#![feature(const_trait_impl)]` to the crate attributes to \
enable `#[const_trait]`",
);
}
let indentation = ccx
.tcx
.sess
.source_map()
.indentation_before(trait_span)
.unwrap_or_default();
err.span_suggestion_verbose(
trait_span.shrink_to_lo(),
format!("consider making trait `{trait_name}` const"),
format!("#[const_trait]\n{indentation}"),
Applicability::MachineApplicable,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not MachineApplicable, since it doesn't fix the bug completely.

);
} else if !ccx.tcx.sess.is_nightly_build() {
err.help("const traits are not yet supported on stable Rust");
}
}
} else if ccx.tcx.constness(callee) != hir::Constness::Const {
let name = ccx.tcx.item_name(callee);
err.span_note(
ccx.tcx.def_span(callee),
format!("{def_descr} `{name}` is not const"),
);
}
err
}
};

err.note(format!(
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
ccx.const_kind(),
));
if note {
err.note(format!(
"calls in {}s are limited to constant functions, tuple structs and tuple variants",
ccx.const_kind(),
));
}

err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,34 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
)) {
diag.downgrade_to_delayed_bug();
}
for candidate in self.find_similar_impl_candidates(trait_ref) {
let CandidateSimilarity::Exact { .. } = candidate.similarity else { continue };
let impl_did = candidate.impl_def_id;
let trait_did = candidate.trait_ref.def_id;
let impl_span = self.tcx.def_span(impl_did);
let trait_name = self.tcx.item_name(trait_did);

if self.tcx.is_const_trait(trait_did) && !self.tcx.is_const_trait_impl(impl_did) {
if let Some(impl_did) = impl_did.as_local()
&& let item = self.tcx.hir_expect_item(impl_did)
&& let hir::ItemKind::Impl(item) = item.kind
&& let Some(of_trait) = item.of_trait
{
// trait is const, impl is local and not const
diag.span_suggestion_verbose(
of_trait.path.span.shrink_to_lo(),
format!("make the `impl` of trait `{trait_name}` `const`"),
"const ".to_string(),
Applicability::MachineApplicable,
);
} else {
diag.span_note(
impl_span,
format!("trait `{trait_name}` is implemented but not `const`"),
);
}
}
}
diag
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,24 @@ LL | #[const_trait] trait Bar: ~const Foo {}
error[E0015]: cannot call non-const method `<T as Foo>::a` in constant functions
--> const-super-trait.rs:10:7
|
LL | const fn foo<T: ~const Bar>(x: &T) {
| ---------------------------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
LL | x.a();
| ^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
note: method `a` is not const because trait `Foo` is not const
--> const-super-trait.rs:3:1
|
LL | trait Foo {
| ^^^^^^^^^ this trait is not const
LL | fn a(&self);
| ------------ this method is not const
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable `#[const_trait]`
help: consider making trait `Foo` const
|
LL + #[const_trait]
LL | trait Foo {
|

error: aborting due to 6 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,23 @@ LL | #[const_trait] trait Bar: ~const Foo {}
error[E0015]: cannot call non-const method `<T as Foo>::a` in constant functions
--> const-super-trait.rs:10:7
|
LL | const fn foo<T: ~const Bar>(x: &T) {
| ---------------------------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
LL | x.a();
| ^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
note: method `a` is not const because trait `Foo` is not const
--> const-super-trait.rs:3:1
|
LL | trait Foo {
| ^^^^^^^^^ this trait is not const
LL | fn a(&self);
| ------------ this method is not const
help: consider making trait `Foo` const
|
LL + #[const_trait]
LL | trait Foo {
|

error: aborting due to 4 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,19 @@ note: `Bar` can't be used with `[const]` because it isn't `const`
error[E0015]: cannot call non-const method `<T as Foo>::a` in constant functions
--> const-super-trait.rs:10:7
|
9 | const fn foo<T: ~const Bar>(x: &T) {
| ---------------------------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
10 | x.a();
| ^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
note: method `a` is not const because trait `Foo` is not const
--> const-super-trait.rs:3:1
|
3 | trait Foo {
| ^^^^^^^^^ this trait is not const
4 | fn a(&self);
| ------------ this method is not const
= help: const traits are not yet supported on stable Rust

error: aborting due to 6 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,19 @@ note: `Bar` can't be used with `[const]` because it isn't `const`
error[E0015]: cannot call non-const method `<T as Foo>::a` in constant functions
--> const-super-trait.rs:10:7
|
9 | const fn foo<T: ~const Bar>(x: &T) {
| ---------------------------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
10 | x.a();
| ^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
note: method `a` is not const because trait `Foo` is not const
--> const-super-trait.rs:3:1
|
3 | trait Foo {
| ^^^^^^^^^ this trait is not const
4 | fn a(&self);
| ------------ this method is not const
= help: const traits are not yet supported on stable Rust

error: aborting due to 5 previous errors

Expand Down
8 changes: 6 additions & 2 deletions tests/ui/asm/non-const.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ error[E0015]: cannot call non-const function `non_const_fn` in constants
--> $DIR/non-const.rs:10:31
|
LL | global_asm!("/* {} */", const non_const_fn(0));
| ^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^ calls in constants are limited to constant functions, tuple structs and tuple variants
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
note: function `non_const_fn` is not const
--> $DIR/non-const.rs:8:1
|
LL | fn non_const_fn(x: i32) -> i32 { x }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Expand Down
7 changes: 5 additions & 2 deletions tests/ui/borrowck/issue-64453.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ error[E0015]: cannot call non-const function `format` in statics
--> $DIR/issue-64453.rs:4:31
|
LL | static settings_dir: String = format!("");
| ^^^^^^^^^^^
| --------------------------- ^^^^^^^^^^^
| |
| calls in statics are limited to constant functions, tuple structs and tuple variants
|
= note: calls in statics are limited to constant functions, tuple structs and tuple variants
note: function `format` is not const
--> $SRC_DIR/alloc/src/fmt.rs:LL:COL
= note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)

Expand Down
15 changes: 11 additions & 4 deletions tests/ui/const-generics/nested-type.full.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
error[E0015]: cannot call non-const associated function `Foo::{constant#0}::Foo::<17>::value` in constants
--> $DIR/nested-type.rs:15:5
|
LL | Foo::<17>::value()
| ^^^^^^^^^^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
LL | struct Foo<const N: [u8; {
| __________________________-
LL | | struct Foo<const N: usize>;
LL | |
LL | | impl<const N: usize> Foo<N> {
... |
LL | | Foo::<17>::value()
| | ^^^^^^^^^^^^^^^^^^
LL | |
LL | | }]>;
| |_- calls in constants are limited to constant functions, tuple structs and tuple variants

error: aborting due to 1 previous error

Expand Down
15 changes: 11 additions & 4 deletions tests/ui/const-generics/nested-type.min.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
error[E0015]: cannot call non-const associated function `Foo::{constant#0}::Foo::<17>::value` in constants
--> $DIR/nested-type.rs:15:5
|
LL | Foo::<17>::value()
| ^^^^^^^^^^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
LL | struct Foo<const N: [u8; {
| __________________________-
LL | | struct Foo<const N: usize>;
LL | |
LL | | impl<const N: usize> Foo<N> {
... |
LL | | Foo::<17>::value()
| | ^^^^^^^^^^^^^^^^^^
LL | |
LL | | }]>;
| |_- calls in constants are limited to constant functions, tuple structs and tuple variants

error: `[u8; {
struct Foo<const N: usize>;
Expand Down
8 changes: 6 additions & 2 deletions tests/ui/consts/const-call.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ error[E0015]: cannot call non-const function `f` in constants
--> $DIR/const-call.rs:6:17
|
LL | let _ = [0; f(2)];
| ^^^^
| ^^^^ calls in constants are limited to constant functions, tuple structs and tuple variants
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
note: function `f` is not const
--> $DIR/const-call.rs:1:1
|
LL | fn f(x: usize) -> usize {
| ^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Expand Down
5 changes: 4 additions & 1 deletion tests/ui/consts/const-eval/format.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ LL | println!("{:?}", 0);
error[E0015]: cannot call non-const function `_print` in constant functions
--> $DIR/format.rs:7:5
|
LL | const fn print() {
| ---------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
LL | println!("{:?}", 0);
| ^^^^^^^^^^^^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
note: function `_print` is not const
--> $SRC_DIR/std/src/io/stdio.rs:LL:COL
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0015]: cannot call non-const formatting macro in constant functions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
error[E0015]: cannot call non-const function `regular_in_block` in constant functions
--> $DIR/const-extern-fn-call-extern-fn.rs:7:9
|
LL | const extern "C" fn bar() {
| ------------------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
LL | unsafe {
LL | regular_in_block();
| ^^^^^^^^^^^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
note: function `regular_in_block` is not const
--> $DIR/const-extern-fn-call-extern-fn.rs:2:5
|
LL | fn regular_in_block();
| ^^^^^^^^^^^^^^^^^^^^^^

error[E0015]: cannot call non-const function `regular` in constant functions
--> $DIR/const-extern-fn-call-extern-fn.rs:16:9
|
LL | const extern "C" fn foo() {
| ------------------------- calls in constant functions are limited to constant functions, tuple structs and tuple variants
LL | unsafe {
LL | regular();
| ^^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
note: function `regular` is not const
--> $DIR/const-extern-fn-call-extern-fn.rs:12:1
|
LL | extern "C" fn regular() {}
| ^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Expand Down
Loading
Loading