Skip to content

Commit

Permalink
Make E0379 diagnostic more helpful
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Jan 1, 2024
1 parent 36aece9 commit 9a4ac5e
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 16 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ ast_passes_trait_fn_const =
[true] trait impls
*[false] traits
} cannot be const
.const_context_label = this declares all functions implicitly const
.remove_const_sugg = remove the `const`
ast_passes_trait_object_single_bound = only a single explicit lifetime bound is permitted
Expand Down
44 changes: 30 additions & 14 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ struct AstValidator<'a> {
in_trait_impl: bool,

/// Are we inside a const trait defn or impl?
in_const_trait_or_impl: bool,
// FIXME(fmease): docs, var name
in_const_trait_or_impl: Option<Span>,

has_proc_macro_decls: bool,

Expand Down Expand Up @@ -85,14 +86,17 @@ impl<'a> AstValidator<'a> {
let old = mem::replace(&mut self.in_trait_impl, is_in);
let old_const = mem::replace(
&mut self.in_const_trait_or_impl,
matches!(constness, Some(Const::Yes(_))),
match constness {
Some(Const::Yes(span)) => Some(span),
_ => None,
},
);
f(self);
self.in_trait_impl = old;
self.in_const_trait_or_impl = old_const;
}

fn with_in_trait(&mut self, is_const: bool, f: impl FnOnce(&mut Self)) {
fn with_in_trait(&mut self, is_const: Option<Span>, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.in_const_trait_or_impl, is_const);
f(self);
self.in_const_trait_or_impl = old;
Expand Down Expand Up @@ -292,9 +296,18 @@ impl<'a> AstValidator<'a> {
}

fn check_trait_fn_not_const(&self, constness: Const) {
if let Const::Yes(span) = constness {
self.dcx().emit_err(errors::TraitFnConst { span, in_impl: self.in_trait_impl });
}
let Const::Yes(span) = constness else {
return;
};

// FIXME(const_trait_impl): If the trait or impl is not const and feature `const_trait_impl`
// is enabled, provide a structured suggestion to make the trait (impl) const.
self.dcx().emit_err(errors::TraitFnConst {
span,
in_impl: self.in_trait_impl,
const_context_label: self.in_const_trait_or_impl,
remove_const_sugg: self.in_const_trait_or_impl.map(|_| span),
});
}

fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
Expand Down Expand Up @@ -963,7 +976,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
}
ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => {
let is_const_trait = attr::contains_name(&item.attrs, sym::const_trait);
let is_const_trait =
attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
self.with_in_trait(is_const_trait, |this| {
if *is_auto == IsAuto::Yes {
// Auto traits cannot have generics, super traits nor contain items.
Expand All @@ -977,8 +991,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
// context for the supertraits.
this.visit_vis(&item.vis);
this.visit_ident(item.ident);
let disallowed =
(!is_const_trait).then(|| DisallowTildeConstContext::Trait(item.span));
let disallowed = is_const_trait
.is_none()
.then(|| DisallowTildeConstContext::Trait(item.span));
this.with_tilde_const(disallowed, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
Expand Down Expand Up @@ -1340,9 +1355,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
});
}

let tilde_const_allowed =
matches!(fk.header(), Some(FnHeader { constness: ast::Const::Yes(_), .. }))
|| matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)) if self.in_const_trait_or_impl);
let tilde_const_allowed = matches!(
fk.header(),
Some(FnHeader { constness: ast::Const::Yes(_), .. })
) || matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)) if self.in_const_trait_or_impl.is_some());

let disallowed = (!tilde_const_allowed).then(|| DisallowTildeConstContext::Fn(fk));
self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk));
Expand Down Expand Up @@ -1414,7 +1430,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

match &item.kind {
AssocItemKind::Fn(box Fn { sig, generics, body, .. })
if self.in_const_trait_or_impl
if self.in_const_trait_or_impl.is_some()
|| ctxt == AssocCtxt::Trait
|| matches!(sig.header.constness, Const::Yes(_)) =>
{
Expand Down Expand Up @@ -1548,7 +1564,7 @@ pub fn check_crate(
features,
extern_mod: None,
in_trait_impl: false,
in_const_trait_or_impl: false,
in_const_trait_or_impl: None,
has_proc_macro_decls: false,
outer_impl_trait: None,
disallow_tilde_const: Some(DisallowTildeConstContext::Item),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub struct TraitFnConst {
#[label]
pub span: Span,
pub in_impl: bool,
#[label(ast_passes_const_context_label)]
pub const_context_label: Option<Span>,
#[suggestion(ast_passes_remove_const_sugg, code = "", applicability = "machine-applicable")]
pub remove_const_sugg: Option<Span>,
}

#[derive(Diagnostic)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
error[E0379]: functions in traits cannot be declared const
--> $DIR/trait-fn-const.rs:8:5
|
LL | #[const_trait]
| -------------- this declares all functions implicitly const
LL | trait Trait {
LL | const fn fun();
| ^^^^^ functions in traits cannot be const
| ^^^^^
| |
| functions in traits cannot be const
| help: remove the `const`

error[E0379]: functions in trait impls cannot be declared const
--> $DIR/trait-fn-const.rs:12:5
|
LL | impl const Trait for () {
| ----- this declares all functions implicitly const
LL | const fn fun() {}
| ^^^^^ functions in trait impls cannot be const
| ^^^^^
| |
| functions in trait impls cannot be const
| help: remove the `const`

error: aborting due to 2 previous errors

Expand Down

0 comments on commit 9a4ac5e

Please sign in to comment.