From b41c2a28705bb1b5d6c93d67f1eb24af210ea426 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 11:11:47 +0000 Subject: [PATCH] Forbid `const fn` within `const impl`s --- compiler/rustc_ast_passes/messages.ftl | 5 +++ .../rustc_ast_passes/src/ast_validation.rs | 37 +++++++++++++++---- compiler/rustc_ast_passes/src/errors.rs | 9 +++++ .../const-impl-inherent-double-const.fixed | 18 +++++++++ .../const-impl-inherent-double-const.rs | 18 +++++++++ .../const-impl-inherent-double-const.stderr | 11 ++++++ .../const-traits/span-bug-issue-121418.rs | 1 + .../const-traits/span-bug-issue-121418.stderr | 10 ++++- 8 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 tests/ui/traits/const-traits/const-impl-inherent-double-const.fixed create mode 100644 tests/ui/traits/const-traits/const-impl-inherent-double-const.rs create mode 100644 tests/ui/traits/const-traits/const-impl-inherent-double-const.stderr diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 1bfe4b07551f..3858bc80d431 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -204,6 +204,11 @@ ast_passes_generic_before_constraints = generic arguments must come before the f ast_passes_generic_default_trailing = generic parameters with a default must be trailing +ast_passes_impl_fn_const = + redundant `const` fn marker in const impl + .parent_constness = this declares all associated functions implicitly const + .label = remove the `const` + ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed .help = remove one of these features diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f8b138eebd1c..425ba407fd61 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -242,6 +242,20 @@ impl<'a> AstValidator<'a> { } } + fn check_impl_fn_not_const(&self, constness: Const, parent_constness: Const) { + let Const::Yes(span) = constness else { + return; + }; + + let span = self.sess.source_map().span_extend_while_whitespace(span); + + let Const::Yes(parent_constness) = parent_constness else { + return; + }; + + self.dcx().emit_err(errors::ImplFnConst { span, parent_constness }); + } + fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrImpl) { let Const::Yes(span) = constness else { return; @@ -1689,14 +1703,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ); } - if let Some(parent @ (TraitOrImpl::Trait { .. } | TraitOrImpl::TraitImpl { .. })) = - &self.outer_trait_or_trait_impl - { - self.visibility_not_permitted(&item.vis, errors::VisibilityNotPermittedNote::TraitImpl); - if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.check_trait_fn_not_const(sig.header.constness, parent); - self.check_async_fn_in_const_trait_or_impl(sig, parent); + match &self.outer_trait_or_trait_impl { + Some(parent @ (TraitOrImpl::Trait { .. } | TraitOrImpl::TraitImpl { .. })) => { + self.visibility_not_permitted( + &item.vis, + errors::VisibilityNotPermittedNote::TraitImpl, + ); + if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { + self.check_trait_fn_not_const(sig.header.constness, parent); + self.check_async_fn_in_const_trait_or_impl(sig, parent); + } } + Some(TraitOrImpl::Impl { constness }) => { + if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { + self.check_impl_fn_not_const(sig.header.constness, *constness); + } + } + None => {} } if let AssocItemKind::Const(ci) = &item.kind { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 20813d936c00..48fd6a7cdd5d 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -35,6 +35,15 @@ pub(crate) enum VisibilityNotPermittedNote { #[note(ast_passes_individual_foreign_items)] IndividualForeignItems, } +#[derive(Diagnostic)] +#[diag(ast_passes_impl_fn_const)] +pub(crate) struct ImplFnConst { + #[primary_span] + #[suggestion(ast_passes_label, code = "", applicability = "machine-applicable")] + pub span: Span, + #[label(ast_passes_parent_constness)] + pub parent_constness: Span, +} #[derive(Diagnostic)] #[diag(ast_passes_trait_fn_const, code = E0379)] diff --git a/tests/ui/traits/const-traits/const-impl-inherent-double-const.fixed b/tests/ui/traits/const-traits/const-impl-inherent-double-const.fixed new file mode 100644 index 000000000000..40a7a9154f1d --- /dev/null +++ b/tests/ui/traits/const-traits/const-impl-inherent-double-const.fixed @@ -0,0 +1,18 @@ +//@run-rustfix + +#![feature(const_trait_impl)] +struct Foo; + +const impl Foo { + fn bar() {} + fn baz() {} + //~^ ERROR: redundant `const` +} + +const _: () = { + Foo::bar(); + Foo::baz(); +}; + +fn main() { +} diff --git a/tests/ui/traits/const-traits/const-impl-inherent-double-const.rs b/tests/ui/traits/const-traits/const-impl-inherent-double-const.rs new file mode 100644 index 000000000000..b82c80576de0 --- /dev/null +++ b/tests/ui/traits/const-traits/const-impl-inherent-double-const.rs @@ -0,0 +1,18 @@ +//@run-rustfix + +#![feature(const_trait_impl)] +struct Foo; + +const impl Foo { + fn bar() {} + const fn baz() {} + //~^ ERROR: redundant `const` +} + +const _: () = { + Foo::bar(); + Foo::baz(); +}; + +fn main() { +} diff --git a/tests/ui/traits/const-traits/const-impl-inherent-double-const.stderr b/tests/ui/traits/const-traits/const-impl-inherent-double-const.stderr new file mode 100644 index 000000000000..dc7a70385f14 --- /dev/null +++ b/tests/ui/traits/const-traits/const-impl-inherent-double-const.stderr @@ -0,0 +1,11 @@ +error: redundant `const` fn marker in const impl + --> $DIR/const-impl-inherent-double-const.rs:8:5 + | +LL | const impl Foo { + | ----- this declares all associated functions implicitly const +LL | fn bar() {} +LL | const fn baz() {} + | ^^^^^^ help: remove the `const` + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/const-traits/span-bug-issue-121418.rs b/tests/ui/traits/const-traits/span-bug-issue-121418.rs index d46e0a1e6cb7..97d9c69616cf 100644 --- a/tests/ui/traits/const-traits/span-bug-issue-121418.rs +++ b/tests/ui/traits/const-traits/span-bug-issue-121418.rs @@ -7,6 +7,7 @@ impl const dyn T { pub const fn new() -> std::sync::Mutex {} //~^ ERROR mismatched types //~| ERROR cannot be known at compilation time + //~| ERROR redundant `const` fn marker in const impl } fn main() {} diff --git a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr index e772cfced8f4..88776f28ef6e 100644 --- a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr +++ b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr @@ -1,3 +1,11 @@ +error: redundant `const` fn marker in const impl + --> $DIR/span-bug-issue-121418.rs:7:9 + | +LL | impl const dyn T { + | ----- this declares all associated functions implicitly const +LL | pub const fn new() -> std::sync::Mutex {} + | ^^^^^^ help: remove the `const` + error[E0277]: the size for values of type `(dyn T + 'static)` cannot be known at compilation time --> $DIR/span-bug-issue-121418.rs:7:27 | @@ -20,7 +28,7 @@ LL | pub const fn new() -> std::sync::Mutex {} = note: expected struct `std::sync::Mutex<(dyn T + 'static)>` found unit type `()` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`.