diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index b7274db029f6e..642b0337ae3a2 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -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 diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 39ea684087e51..9736d95cce706 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -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, has_proc_macro_decls: bool, @@ -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, 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; @@ -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) { @@ -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. @@ -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) @@ -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)); @@ -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(_)) => { @@ -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), diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ebaa7ccecc916..c4b23935e3314 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -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, + #[suggestion(ast_passes_remove_const_sugg, code = "", applicability = "machine-applicable")] + pub remove_const_sugg: Option, } #[derive(Diagnostic)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr index 0835881e59634..2036b50beaf04 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr @@ -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