From 427deb66e3df53b054ab043dab18aec8679b91c9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 09:39:28 +0000 Subject: [PATCH 1/7] Refactor impl block frontmatter checking in the parser --- compiler/rustc_parse/src/parser/item.rs | 65 +++++++++++++++++-------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 4bb0d05c4f37c..5cb8cccb29821 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -247,29 +247,22 @@ impl<'a> Parser<'a> { } else if self.check_keyword(exp!(Trait)) || self.check_trait_front_matter() { // TRAIT ITEM self.parse_item_trait(attrs, lo)? - } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { - // CONST ITEM - if self.token.is_keyword(kw::Impl) { - // recover from `const impl`, suggest `impl const` - self.recover_const_impl(const_span, attrs, def_())? - } else { - self.recover_const_mut(const_span); - self.recover_missing_kw_before_item()?; - let (ident, generics, ty, expr) = self.parse_const_item()?; - ItemKind::Const(Box::new(ConstItem { - defaultness: def_(), - ident, - generics, - ty, - expr, - define_opaque: None, - })) - } - } else if self.check_keyword(exp!(Impl)) - || self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Impl]) - { + } else if self.check_impl_frontmatter() { // IMPL ITEM self.parse_item_impl(attrs, def_())? + } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { + // CONST ITEM + self.recover_const_mut(const_span); + self.recover_missing_kw_before_item()?; + let (ident, generics, ty, expr) = self.parse_const_item()?; + ItemKind::Const(Box::new(ConstItem { + defaultness: def_(), + ident, + generics, + ty, + expr, + define_opaque: None, + })) } else if self.is_reuse_path_item() { self.parse_item_delegation()? } else if self.check_keyword(exp!(Mod)) @@ -566,6 +559,9 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemKind> { + if self.eat_keyword(exp!(Const)) { + return self.recover_const_impl(self.prev_token.span, attrs, defaultness); + } let safety = self.parse_safety(Case::Sensitive); self.expect_keyword(exp!(Impl))?; @@ -2594,6 +2590,33 @@ impl<'a> Parser<'a> { Ok(body) } + fn check_impl_frontmatter(&mut self) -> bool { + const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe]; + // In contrast to the loop below, this call inserts `impl` into the + // list of expected tokens shown in diagnostics. + if self.check_keyword(exp!(Impl)) { + return true; + } + let mut i = 0; + while i < ALL_QUALS.len() { + let action = self.look_ahead(i, |token| { + if token.is_keyword(kw::Impl) { + return Some(true); + } + if ALL_QUALS.iter().any(|&qual| token.is_keyword(qual)) { + // Ok, we found a legal keyword, keep looking for `impl` + return None; + } + Some(false) + }); + if let Some(ret) = action { + return ret; + } + i += 1; + } + self.is_keyword_ahead(i, &[kw::Impl]) + } + /// Is the current token the start of an `FnHeader` / not a valid parse? /// /// `check_pub` adds additional `pub` to the checks in case users place it From 18597d8c4e6fa457d2ae8bf448cca4f6a29b875b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 10:06:49 +0000 Subject: [PATCH 2/7] Temporarily allow `const impl` and `impl const` at the same time to migrate --- compiler/rustc_parse/src/parser/item.rs | 50 +++---------------- tests/ui/parser/issues/issue-81806.rs | 7 +-- tests/ui/parser/issues/issue-81806.stderr | 28 +++++++---- .../const-traits/const-impl-norecover.rs | 2 +- .../const-traits/const-impl-norecover.stderr | 10 ++-- .../const-traits/const-impl-recovery.rs | 6 ++- .../const-traits/const-impl-recovery.stderr | 26 ---------- 7 files changed, 40 insertions(+), 89 deletions(-) delete mode 100644 tests/ui/traits/const-traits/const-impl-recovery.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 5cb8cccb29821..3f66d3c3b6e1a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -559,9 +559,7 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemKind> { - if self.eat_keyword(exp!(Const)) { - return self.recover_const_impl(self.prev_token.span, attrs, defaultness); - } + let mut constness = self.parse_constness(Case::Sensitive); let safety = self.parse_safety(Case::Sensitive); self.expect_keyword(exp!(Impl))?; @@ -576,7 +574,11 @@ impl<'a> Parser<'a> { generics }; - let constness = self.parse_constness(Case::Sensitive); + if let Const::No = constness { + // FIXME(const_trait_impl): disallow `impl const Trait` + constness = self.parse_constness(Case::Sensitive); + } + if let Const::Yes(span) = constness { self.psess.gated_spans.gate(sym::const_trait_impl, span); } @@ -1345,46 +1347,6 @@ impl<'a> Parser<'a> { } } - /// Recover on `const impl` with `const` already eaten. - fn recover_const_impl( - &mut self, - const_span: Span, - attrs: &mut AttrVec, - defaultness: Defaultness, - ) -> PResult<'a, ItemKind> { - let impl_span = self.token.span; - let err = self.expected_ident_found_err(); - - // Only try to recover if this is implementing a trait for a type - let mut item_kind = match self.parse_item_impl(attrs, defaultness) { - Ok(item_kind) => item_kind, - Err(recovery_error) => { - // Recovery failed, raise the "expected identifier" error - recovery_error.cancel(); - return Err(err); - } - }; - - match &mut item_kind { - ItemKind::Impl(Impl { of_trait: Some(of_trait), .. }) => { - of_trait.constness = Const::Yes(const_span); - - let before_trait = of_trait.trait_ref.path.span.shrink_to_lo(); - let const_up_to_impl = const_span.with_hi(impl_span.lo()); - err.with_multipart_suggestion( - "you might have meant to write a const trait impl", - vec![(const_up_to_impl, "".to_owned()), (before_trait, "const ".to_owned())], - Applicability::MaybeIncorrect, - ) - .emit(); - } - ItemKind::Impl { .. } => return Err(err), - _ => unreachable!(), - } - - Ok(item_kind) - } - /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in /// `mutability`. /// diff --git a/tests/ui/parser/issues/issue-81806.rs b/tests/ui/parser/issues/issue-81806.rs index ca86788dff79c..bc2a97f58b851 100644 --- a/tests/ui/parser/issues/issue-81806.rs +++ b/tests/ui/parser/issues/issue-81806.rs @@ -1,5 +1,6 @@ -trait T { const -impl //~ ERROR: expected identifier, found keyword `impl` -} +trait T { +const //~ ERROR: const trait impls are experimental +impl +} //~ ERROR: expected type, found `}` fn main() {} diff --git a/tests/ui/parser/issues/issue-81806.stderr b/tests/ui/parser/issues/issue-81806.stderr index f1287b82fa57e..2f15a5081d48f 100644 --- a/tests/ui/parser/issues/issue-81806.stderr +++ b/tests/ui/parser/issues/issue-81806.stderr @@ -1,17 +1,25 @@ -error: expected identifier, found keyword `impl` - --> $DIR/issue-81806.rs:2:1 +error: expected type, found `}` + --> $DIR/issue-81806.rs:4:1 | -LL | trait T { const +LL | trait T { | - while parsing this item list starting here -LL | impl - | ^^^^ expected identifier, found keyword +... LL | } - | - the item list ends here + | ^ + | | + | expected type + | the item list ends here + +error[E0658]: const trait impls are experimental + --> $DIR/issue-81806.rs:2:1 | -help: escape `impl` to use it as an identifier +LL | const + | ^^^^^ | -LL | r#impl - | ++ + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/const-impl-norecover.rs b/tests/ui/traits/const-traits/const-impl-norecover.rs index bed4e9fd1e610..31dc3471ca6ca 100644 --- a/tests/ui/traits/const-traits/const-impl-norecover.rs +++ b/tests/ui/traits/const-traits/const-impl-norecover.rs @@ -2,7 +2,7 @@ struct Foo; -const impl Foo { //~ ERROR: expected identifier, found keyword +const impl Foo { //~ ERROR: inherent impls cannot be const fn bar() {} } diff --git a/tests/ui/traits/const-traits/const-impl-norecover.stderr b/tests/ui/traits/const-traits/const-impl-norecover.stderr index efa72463c5e9a..78eb220343c94 100644 --- a/tests/ui/traits/const-traits/const-impl-norecover.stderr +++ b/tests/ui/traits/const-traits/const-impl-norecover.stderr @@ -1,8 +1,12 @@ -error: expected identifier, found keyword `impl` - --> $DIR/const-impl-norecover.rs:5:7 +error: inherent impls cannot be const + --> $DIR/const-impl-norecover.rs:5:12 | LL | const impl Foo { - | ^^^^ expected identifier, found keyword + | ----- ^^^ inherent impl for this type + | | + | const because of this + | + = note: only trait implementations may be annotated with `const` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-impl-recovery.rs b/tests/ui/traits/const-traits/const-impl-recovery.rs index 837124db04e20..384a3b7774d7c 100644 --- a/tests/ui/traits/const-traits/const-impl-recovery.rs +++ b/tests/ui/traits/const-traits/const-impl-recovery.rs @@ -1,14 +1,16 @@ #![feature(const_trait_impl)] +//@ check-pass + #[const_trait] trait Foo {} -const impl Foo for i32 {} //~ ERROR: expected identifier, found keyword +const impl Foo for i32 {} #[const_trait] trait Bar {} -const impl Bar for T {} //~ ERROR: expected identifier, found keyword +const impl Bar for T {} const fn still_implements() {} diff --git a/tests/ui/traits/const-traits/const-impl-recovery.stderr b/tests/ui/traits/const-traits/const-impl-recovery.stderr deleted file mode 100644 index 7217fc8554356..0000000000000 --- a/tests/ui/traits/const-traits/const-impl-recovery.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: expected identifier, found keyword `impl` - --> $DIR/const-impl-recovery.rs:6:7 - | -LL | const impl Foo for i32 {} - | ^^^^ expected identifier, found keyword - | -help: you might have meant to write a const trait impl - | -LL - const impl Foo for i32 {} -LL + impl const Foo for i32 {} - | - -error: expected identifier, found keyword `impl` - --> $DIR/const-impl-recovery.rs:11:7 - | -LL | const impl Bar for T {} - | ^^^^ expected identifier, found keyword - | -help: you might have meant to write a const trait impl - | -LL - const impl Bar for T {} -LL + impl const Bar for T {} - | - -error: aborting due to 2 previous errors - From c61304a1c90c6bcdab53e8d6fbbd596c4ad1b110 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 31 Oct 2025 10:50:52 +0000 Subject: [PATCH 3/7] Give all impls a constness --- compiler/rustc_ast/src/ast.rs | 10 +++++----- compiler/rustc_ast/src/visit.rs | 6 +++--- compiler/rustc_ast_lowering/src/item.rs | 9 +++++---- .../rustc_ast_passes/src/ast_validation.rs | 11 +++-------- .../rustc_ast_pretty/src/pprust/state/item.rs | 14 +++++--------- .../src/deriving/coerce_pointee.rs | 4 ++-- .../src/deriving/generic/mod.rs | 6 +----- compiler/rustc_expand/src/expand.rs | 4 ++-- compiler/rustc_hir/src/hir.rs | 4 ++-- compiler/rustc_hir/src/intravisit.rs | 3 +-- compiler/rustc_hir/src/target.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 4 ++-- compiler/rustc_hir_pretty/src/lib.rs | 10 +++++++--- compiler/rustc_parse/src/parser/item.rs | 11 +++-------- compiler/rustc_passes/src/stability.rs | 12 +++++++++--- .../clippy/clippy_lints/src/derivable_impls.rs | 3 ++- .../clippy/clippy_utils/src/ast_utils/mod.rs | 4 +++- src/tools/rustfmt/src/items.rs | 5 ++++- tests/ui/stats/input-stats.stderr | 18 +++++++++--------- 19 files changed, 69 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8f44931826398..97a5271766443 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3687,6 +3687,7 @@ pub struct TyAlias { #[derive(Clone, Encodable, Decodable, Debug)] pub struct Impl { pub generics: Generics, + pub constness: Const, pub of_trait: Option>, pub self_ty: Box, pub items: ThinVec>, @@ -3696,7 +3697,6 @@ pub struct Impl { pub struct TraitImplHeader { pub defaultness: Defaultness, pub safety: Safety, - pub constness: Const, pub polarity: ImplPolarity, pub trait_ref: TraitRef, } @@ -4072,9 +4072,9 @@ mod size_asserts { static_assert_size!(GenericArg, 24); static_assert_size!(GenericBound, 88); static_assert_size!(Generics, 40); - static_assert_size!(Impl, 64); - static_assert_size!(Item, 136); - static_assert_size!(ItemKind, 72); + static_assert_size!(Impl, 80); + static_assert_size!(Item, 152); + static_assert_size!(ItemKind, 88); static_assert_size!(LitKind, 24); static_assert_size!(Local, 96); static_assert_size!(MetaItemLit, 40); @@ -4085,7 +4085,7 @@ mod size_asserts { static_assert_size!(PathSegment, 24); static_assert_size!(Stmt, 32); static_assert_size!(StmtKind, 16); - static_assert_size!(TraitImplHeader, 80); + static_assert_size!(TraitImplHeader, 72); static_assert_size!(Ty, 64); static_assert_size!(TyKind, 40); // tidy-alphabetical-end diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 99490be64175f..c069e2e4b4568 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -928,11 +928,11 @@ macro_rules! common_visitor_and_walkers { } impl_walkable!(|&$($mut)? $($lt)? self: Impl, vis: &mut V| { - let Impl { generics, of_trait, self_ty, items } = self; + let Impl { generics, of_trait, self_ty, items, constness: _ } = self; try_visit!(vis.visit_generics(generics)); if let Some(box of_trait) = of_trait { - let TraitImplHeader { defaultness, safety, constness, polarity, trait_ref } = of_trait; - visit_visitable!($($mut)? vis, defaultness, safety, constness, polarity, trait_ref); + let TraitImplHeader { defaultness, safety, polarity, trait_ref } = of_trait; + visit_visitable!($($mut)? vis, defaultness, safety, polarity, trait_ref); } try_visit!(vis.visit_ty(self_ty)); visit_visitable_with!($($mut)? vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() }); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 177f33c0d8f1c..309cda3adbd76 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -345,6 +345,7 @@ impl<'hir> LoweringContext<'_, 'hir> { of_trait, self_ty: ty, items: impl_items, + constness, }) => { // Lower the "impl header" first. This ordering is important // for in-band lifetimes! Consider `'a` here: @@ -378,11 +379,14 @@ impl<'hir> LoweringContext<'_, 'hir> { .arena .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item))); + let constness = self.lower_constness(*constness); + hir::ItemKind::Impl(hir::Impl { generics, of_trait, self_ty: lowered_ty, items: new_impl_items, + constness, }) } ItemKind::Trait(box Trait { @@ -970,9 +974,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, trait_impl_header: &TraitImplHeader, ) -> &'hir hir::TraitImplHeader<'hir> { - let TraitImplHeader { constness, safety, polarity, defaultness, ref trait_ref } = - *trait_impl_header; - let constness = self.lower_constness(constness); + let TraitImplHeader { safety, polarity, defaultness, ref trait_ref } = *trait_impl_header; let safety = self.lower_safety(safety, hir::Safety::Safe); let polarity = match polarity { ImplPolarity::Positive => ImplPolarity::Positive, @@ -995,7 +997,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ); self.arena.alloc(hir::TraitImplHeader { - constness, safety, polarity, defaultness, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 6b218f34363dd..0ec1108ecfd9b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1011,14 +1011,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match &item.kind { ItemKind::Impl(Impl { generics, + constness, of_trait: - Some(box TraitImplHeader { - safety, - polarity, - defaultness: _, - constness, - trait_ref: t, - }), + Some(box TraitImplHeader { safety, polarity, defaultness: _, trait_ref: t }), self_ty, items, }) => { @@ -1050,7 +1045,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true }); }); } - ItemKind::Impl(Impl { generics, of_trait: None, self_ty, items }) => { + ItemKind::Impl(Impl { generics, of_trait: None, self_ty, items, constness: _ }) => { self.visit_attrs_vis(&item.attrs, &item.vis); self.visibility_not_permitted( &item.vis, diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 294d03a83b161..45e84d853ebd9 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -305,7 +305,7 @@ impl<'a> State<'a> { let (cb, ib) = self.head(visibility_qualified(&item.vis, "union")); self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } - ast::ItemKind::Impl(ast::Impl { generics, of_trait, self_ty, items }) => { + ast::ItemKind::Impl(ast::Impl { generics, of_trait, self_ty, items, constness }) => { let (cb, ib) = self.head(""); self.print_visibility(&item.vis); @@ -321,17 +321,12 @@ impl<'a> State<'a> { }; if let Some(box of_trait) = of_trait { - let ast::TraitImplHeader { - defaultness, - safety, - constness, - polarity, - ref trait_ref, - } = *of_trait; + let ast::TraitImplHeader { defaultness, safety, polarity, ref trait_ref } = + *of_trait; self.print_defaultness(defaultness); self.print_safety(safety); impl_generics(self); - self.print_constness(constness); + self.print_constness(*constness); if let ast::ImplPolarity::Negative(_) = polarity { self.word("!"); } @@ -339,6 +334,7 @@ impl<'a> State<'a> { self.space(); self.word_space("for"); } else { + self.print_constness(*constness); impl_generics(self); } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 5b378de8bbddc..d6fdf088b0d19 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -137,9 +137,9 @@ pub(crate) fn expand_deriving_coerce_pointee( safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, - constness: ast::Const::No, trait_ref, })), + constness: ast::Const::No, self_ty: self_type.clone(), items: ThinVec::new(), }), @@ -160,9 +160,9 @@ pub(crate) fn expand_deriving_coerce_pointee( safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, - constness: ast::Const::No, trait_ref, })), + constness: ast::Const::No, self_ty: self_type.clone(), items: ThinVec::new(), }), diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 24a71ae943899..3e1a96742420f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -835,13 +835,9 @@ impl<'a> TraitDef<'a> { safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, - constness: if self.is_const { - ast::Const::Yes(DUMMY_SP) - } else { - ast::Const::No - }, trait_ref, })), + constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, self_ty: self_type, items: methods.into_iter().chain(associated_types).collect(), }), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 276490bc0c9d5..37bd5024dbd08 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2389,10 +2389,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { ) -> SmallVec<[Box; 1]> { match ctxt { AssocCtxt::Trait => self.flat_map_node(AstNodeWrapper::new(node, TraitItemTag)), - AssocCtxt::Impl { of_trait: false } => { + AssocCtxt::Impl { of_trait: false, .. } => { self.flat_map_node(AstNodeWrapper::new(node, ImplItemTag)) } - AssocCtxt::Impl { of_trait: true } => { + AssocCtxt::Impl { of_trait: true, .. } => { self.flat_map_node(AstNodeWrapper::new(node, TraitImplItemTag)) } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3272f34a54526..48d45608f1df5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4345,11 +4345,11 @@ pub struct Impl<'hir> { pub of_trait: Option<&'hir TraitImplHeader<'hir>>, pub self_ty: &'hir Ty<'hir>, pub items: &'hir [ImplItemId], + pub constness: Constness, } #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct TraitImplHeader<'hir> { - pub constness: Constness, pub safety: Safety, pub polarity: ImplPolarity, pub defaultness: Defaultness, @@ -4914,7 +4914,7 @@ mod size_asserts { static_assert_size!(GenericArg<'_>, 16); static_assert_size!(GenericBound<'_>, 64); static_assert_size!(Generics<'_>, 56); - static_assert_size!(Impl<'_>, 40); + static_assert_size!(Impl<'_>, 48); static_assert_size!(ImplItem<'_>, 88); static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 232178ee403a6..62c69386bda50 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -590,10 +590,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_enum_def(enum_definition)); } - ItemKind::Impl(Impl { generics, of_trait, self_ty, items }) => { + ItemKind::Impl(Impl { generics, of_trait, self_ty, items, constness: _ }) => { try_visit!(visitor.visit_generics(generics)); if let Some(TraitImplHeader { - constness: _, safety: _, polarity: _, defaultness: _, diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index dcac51b10b49d..87953321af3fc 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -240,7 +240,7 @@ impl Target { AssocItemKind::Const(_) => Target::AssocConst, AssocItemKind::Fn(f) => Target::Method(match assoc_ctxt { AssocCtxt::Trait => MethodKind::Trait { body: f.body.is_some() }, - AssocCtxt::Impl { of_trait } => { + AssocCtxt::Impl { of_trait, .. } => { if of_trait { MethodKind::TraitImpl } else { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 1386070e3d9b6..7f1766d344a6d 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1301,7 +1301,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader let selfty = tcx.type_of(def_id).instantiate_identity(); let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); - check_impl_constness(tcx, of_trait.constness, &of_trait.trait_ref); + check_impl_constness(tcx, impl_.constness, &of_trait.trait_ref); let trait_ref = icx.lowerer().lower_impl_trait_ref(&of_trait.trait_ref, selfty); @@ -1309,7 +1309,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader trait_ref: ty::EarlyBinder::bind(trait_ref), safety: of_trait.safety, polarity: polarity_of_impl(tcx, of_trait, is_rustc_reservation), - constness: of_trait.constness, + constness: impl_.constness, } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5bf449a97dcd2..4594a39050bd1 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -690,7 +690,7 @@ impl<'a> State<'a> { let (cb, ib) = self.head("union"); self.print_struct(ident.name, generics, struct_def, item.span, true, cb, ib); } - hir::ItemKind::Impl(hir::Impl { generics, of_trait, self_ty, items }) => { + hir::ItemKind::Impl(hir::Impl { generics, of_trait, self_ty, items, constness }) => { let (cb, ib) = self.head(""); let impl_generics = |this: &mut Self| { @@ -702,9 +702,13 @@ impl<'a> State<'a> { }; match of_trait { - None => impl_generics(self), + None => { + if let hir::Constness::Const = constness { + self.word_nbsp("const"); + } + impl_generics(self) + } Some(&hir::TraitImplHeader { - constness, safety, polarity, defaultness, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 3f66d3c3b6e1a..ffa194c36ef95 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -662,13 +662,8 @@ impl<'a> Parser<'a> { }; let trait_ref = TraitRef { path, ref_id: ty_first.id }; - let of_trait = Some(Box::new(TraitImplHeader { - defaultness, - safety, - constness, - polarity, - trait_ref, - })); + let of_trait = + Some(Box::new(TraitImplHeader { defaultness, safety, polarity, trait_ref })); (of_trait, ty_second) } None => { @@ -699,7 +694,7 @@ impl<'a> Parser<'a> { } }; - Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items })) + Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items, constness })) } fn parse_item_delegation(&mut self) -> PResult<'a, ItemKind> { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 2ee1bd0dfd1ec..a895187f351ed 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -589,7 +589,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), self_ty, items, .. }) => { + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(of_trait), + self_ty, + items, + constness, + .. + }) => { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir_attrs(item.hir_id()); @@ -652,7 +658,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } if features.const_trait_impl() - && let hir::Constness::Const = of_trait.constness + && let hir::Constness::Const = constness { let stable_or_implied_stable = match const_stab { None => true, @@ -696,7 +702,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - if let hir::Constness::Const = of_trait.constness + if let hir::Constness::Const = constness && let Some(def_id) = of_trait.trait_ref.trait_def_id() { // FIXME(const_trait_impl): Improve the span here. diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 06c2393e0a39f..6b8a6aec92fab 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -232,6 +232,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { of_trait: Some(of_trait), items: [child], self_ty, + constness, .. }) = item.kind && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) @@ -247,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { && !attrs.iter().any(|attr| attr.doc_str().is_some()) && cx.tcx.hir_attrs(impl_item_hir).is_empty() { - let is_const = of_trait.constness == hir::Constness::Const; + let is_const = constness == hir::Constness::Const; if adt_def.is_struct() { check_struct( cx, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index e797c96156045..716c51a128263 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -496,12 +496,14 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { of_trait: lot, self_ty: lst, items: li, + constness: lc, }), Impl(ast::Impl { generics: rg, of_trait: rot, self_ty: rst, items: ri, + constness: rc, }), ) => { eq_generics(lg, rg) @@ -509,7 +511,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default) && matches!(l.polarity, ImplPolarity::Positive) == matches!(r.polarity, ImplPolarity::Positive) && eq_defaultness(l.defaultness, r.defaultness) - && matches!(l.constness, ast::Const::No) == matches!(r.constness, ast::Const::No) + && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No) && eq_path(&l.trait_ref.path, &r.trait_ref.path) }) && eq_ty(lst, rst) diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index ecaa4670f61d2..7cae6d10a92f2 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -961,6 +961,7 @@ fn format_impl_ref_and_type( of_trait, self_ty, items: _, + constness, } = iimpl; let mut result = String::with_capacity(128); @@ -969,6 +970,8 @@ fn format_impl_ref_and_type( if let Some(of_trait) = of_trait.as_deref() { result.push_str(format_defaultness(of_trait.defaultness)); result.push_str(format_safety(of_trait.safety)); + } else { + result.push_str(format_constness_right(*constness)); } let shape = if context.config.style_edition() >= StyleEdition::Edition2024 { @@ -985,7 +988,7 @@ fn format_impl_ref_and_type( let trait_ref_overhead; if let Some(of_trait) = of_trait.as_deref() { - result.push_str(format_constness_right(of_trait.constness)); + result.push_str(format_constness_right(*constness)); let polarity_str = match of_trait.polarity { ast::ImplPolarity::Negative(_) => "!", ast::ImplPolarity::Positive => "", diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index ef5039485df62..f2fcb98cb6a95 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -2,14 +2,14 @@ ast-stats ================================================================ ast-stats POST EXPANSION AST STATS: input_stats ast-stats Name Accumulated Size Count Item Size ast-stats ---------------------------------------------------------------- -ast-stats Item 1_496 (NN.N%) 11 136 -ast-stats - Enum 136 (NN.N%) 1 -ast-stats - ExternCrate 136 (NN.N%) 1 -ast-stats - ForeignMod 136 (NN.N%) 1 -ast-stats - Impl 136 (NN.N%) 1 -ast-stats - Trait 136 (NN.N%) 1 -ast-stats - Fn 272 (NN.N%) 2 -ast-stats - Use 544 (NN.N%) 4 +ast-stats Item 1_672 (NN.N%) 11 152 +ast-stats - Enum 152 (NN.N%) 1 +ast-stats - ExternCrate 152 (NN.N%) 1 +ast-stats - ForeignMod 152 (NN.N%) 1 +ast-stats - Impl 152 (NN.N%) 1 +ast-stats - Trait 152 (NN.N%) 1 +ast-stats - Fn 304 (NN.N%) 2 +ast-stats - Use 608 (NN.N%) 4 ast-stats Ty 896 (NN.N%) 14 64 ast-stats - Ptr 64 (NN.N%) 1 ast-stats - Ref 64 (NN.N%) 1 @@ -57,7 +57,7 @@ ast-stats GenericArgs 40 (NN.N%) 1 40 ast-stats - AngleBracketed 40 (NN.N%) 1 ast-stats Crate 40 (NN.N%) 1 40 ast-stats ---------------------------------------------------------------- -ast-stats Total 7_440 129 +ast-stats Total 7_616 129 ast-stats ================================================================ hir-stats ================================================================ hir-stats HIR STATS: input_stats From f8ebb53429d83d6c6e758ed1c9ad495acfa10e00 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 10:13:38 +0000 Subject: [PATCH 4/7] Allow inherent const impl blocks --- .../rustc_ast_passes/src/ast_validation.rs | 115 ++++++++++-------- compiler/rustc_parse/src/parser/item.rs | 2 +- .../const-traits/const-impl-norecover.rs | 5 +- .../const-traits/const-impl-norecover.stderr | 13 +- tests/ui/traits/const-traits/inherent-impl.rs | 4 +- .../traits/const-traits/inherent-impl.stderr | 22 ---- .../const-traits/span-bug-issue-121418.rs | 1 - .../const-traits/span-bug-issue-121418.stderr | 16 +-- 8 files changed, 82 insertions(+), 96 deletions(-) delete mode 100644 tests/ui/traits/const-traits/inherent-impl.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0ec1108ecfd9b..aafa0549b18e6 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -47,15 +47,17 @@ enum SelfSemantic { No, } -enum TraitOrTraitImpl { +enum TraitOrImpl { Trait { span: Span, constness: Const }, TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span }, + Impl { constness: Const }, } -impl TraitOrTraitImpl { +impl TraitOrImpl { fn constness(&self) -> Option { match self { Self::Trait { constness: Const::Yes(span), .. } + | Self::Impl { constness: Const::Yes(span), .. } | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span), _ => None, } @@ -69,7 +71,7 @@ struct AstValidator<'a> { /// The span of the `extern` in an `extern { ... }` block, if any. extern_mod_span: Option, - outer_trait_or_trait_impl: Option, + outer_trait_or_trait_impl: Option, has_proc_macro_decls: bool, @@ -92,28 +94,12 @@ struct AstValidator<'a> { } impl<'a> AstValidator<'a> { - fn with_in_trait_impl( + fn with_in_trait_or_impl( &mut self, - trait_: Option<(Const, ImplPolarity, &'a TraitRef)>, + in_trait_or_impl: Option, f: impl FnOnce(&mut Self), ) { - let old = mem::replace( - &mut self.outer_trait_or_trait_impl, - trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl { - constness, - polarity, - trait_ref_span: trait_ref.path.span, - }), - ); - f(self); - self.outer_trait_or_trait_impl = old; - } - - fn with_in_trait(&mut self, span: Span, constness: Const, f: impl FnOnce(&mut Self)) { - let old = mem::replace( - &mut self.outer_trait_or_trait_impl, - Some(TraitOrTraitImpl::Trait { span, constness }), - ); + let old = mem::replace(&mut self.outer_trait_or_trait_impl, in_trait_or_impl); f(self); self.outer_trait_or_trait_impl = old; } @@ -246,14 +232,14 @@ impl<'a> AstValidator<'a> { } } - fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl) { + fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrImpl) { let Const::Yes(span) = constness else { return; }; let const_trait_impl = self.features.const_trait_impl(); let make_impl_const_sugg = if const_trait_impl - && let TraitOrTraitImpl::TraitImpl { + && let TraitOrImpl::TraitImpl { constness: Const::No, polarity: ImplPolarity::Positive, trait_ref_span, @@ -266,7 +252,7 @@ impl<'a> AstValidator<'a> { }; let make_trait_const_sugg = if const_trait_impl - && let TraitOrTraitImpl::Trait { span, constness: ast::Const::No } = parent + && let TraitOrImpl::Trait { span, constness: ast::Const::No } = parent { Some(span.shrink_to_lo()) } else { @@ -276,7 +262,7 @@ impl<'a> AstValidator<'a> { let parent_constness = parent.constness(); self.dcx().emit_err(errors::TraitFnConst { span, - in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }), + in_impl: matches!(parent, TraitOrImpl::TraitImpl { .. }), const_context_label: parent_constness, remove_const_sugg: ( self.sess.source_map().span_extend_while_whitespace(span), @@ -292,7 +278,7 @@ impl<'a> AstValidator<'a> { }); } - fn check_async_fn_in_const_trait_or_impl(&self, sig: &FnSig, parent: &TraitOrTraitImpl) { + fn check_async_fn_in_const_trait_or_impl(&self, sig: &FnSig, parent: &TraitOrImpl) { let Some(const_keyword) = parent.constness() else { return }; let Some(CoroutineKind::Async { span: async_keyword, .. }) = sig.header.coroutine_kind @@ -302,7 +288,7 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::AsyncFnInConstTraitOrTraitImpl { async_keyword, - in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }), + in_impl: matches!(parent, TraitOrImpl::TraitImpl { .. }), const_keyword, }); } @@ -1041,24 +1027,46 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_trait_ref(t); self.visit_ty(self_ty); - self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| { - walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true }); - }); + self.with_in_trait_or_impl( + Some(TraitOrImpl::TraitImpl { + constness: *constness, + polarity: *polarity, + trait_ref_span: t.path.span, + }), + |this| { + walk_list!( + this, + visit_assoc_item, + items, + AssocCtxt::Impl { of_trait: true } + ); + }, + ); } - ItemKind::Impl(Impl { generics, of_trait: None, self_ty, items, constness: _ }) => { + ItemKind::Impl(Impl { generics, of_trait: None, self_ty, items, constness }) => { self.visit_attrs_vis(&item.attrs, &item.vis); self.visibility_not_permitted( &item.vis, errors::VisibilityNotPermittedNote::IndividualImplItems, ); - self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| { - this.visit_generics(generics) - }); + let disallowed = matches!(constness, ast::Const::No) + .then(|| TildeConstReason::Impl { span: item.span }); + + self.with_tilde_const(disallowed, |this| this.visit_generics(generics)); + self.visit_ty(self_ty); - self.with_in_trait_impl(None, |this| { - walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: false }); - }); + self.with_in_trait_or_impl( + Some(TraitOrImpl::Impl { constness: *constness }), + |this| { + walk_list!( + this, + visit_assoc_item, + items, + AssocCtxt::Impl { of_trait: false } + ); + }, + ); } ItemKind::Fn( func @ box Fn { @@ -1183,9 +1191,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { this.visit_generics(generics); walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) }); - self.with_in_trait(item.span, constness, |this| { - walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait); - }); + self.with_in_trait_or_impl( + Some(TraitOrImpl::Trait { span: item.span, constness }), + |this| { + walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait); + }, + ); } ItemKind::TraitAlias(box TraitAlias { constness, generics, bounds, .. }) => { let disallowed = matches!(constness, ast::Const::No) @@ -1553,7 +1564,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { && self .outer_trait_or_trait_impl .as_ref() - .and_then(TraitOrTraitImpl::constness) + .and_then(TraitOrImpl::constness) .is_some(); let disallowed = (!tilde_const_allowed).then(|| match fk { @@ -1620,7 +1631,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ); } - if let Some(parent) = &self.outer_trait_or_trait_impl { + 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); @@ -1633,7 +1646,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } let parent_is_const = - self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrTraitImpl::constness).is_some(); + self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrImpl::constness).is_some(); match &item.kind { AssocItemKind::Fn(func) @@ -1647,19 +1660,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } AssocItemKind::Type(_) => { let disallowed = (!parent_is_const).then(|| match self.outer_trait_or_trait_impl { - Some(TraitOrTraitImpl::Trait { .. }) => { + Some(TraitOrImpl::Trait { .. }) => { TildeConstReason::TraitAssocTy { span: item.span } } - Some(TraitOrTraitImpl::TraitImpl { .. }) => { + Some(TraitOrImpl::TraitImpl { .. }) => { TildeConstReason::TraitImplAssocTy { span: item.span } } - None => TildeConstReason::InherentAssocTy { span: item.span }, + Some(TraitOrImpl::Impl { .. }) | None => { + TildeConstReason::InherentAssocTy { span: item.span } + } }); self.with_tilde_const(disallowed, |this| { - this.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)) + this.with_in_trait_or_impl(None, |this| { + visit::walk_assoc_item(this, item, ctxt) + }) }) } - _ => self.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)), + _ => self.with_in_trait_or_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)), } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index ffa194c36ef95..b843f3fe2ddbb 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -688,7 +688,7 @@ impl<'a> Parser<'a> { error("default", "default", def_span).emit(); } if let Const::Yes(span) = constness { - error("const", "const", span).emit(); + self.psess.gated_spans.gate(sym::const_trait_impl, span); } (None, self_ty) } diff --git a/tests/ui/traits/const-traits/const-impl-norecover.rs b/tests/ui/traits/const-traits/const-impl-norecover.rs index 31dc3471ca6ca..a1cc384186e17 100644 --- a/tests/ui/traits/const-traits/const-impl-norecover.rs +++ b/tests/ui/traits/const-traits/const-impl-norecover.rs @@ -2,10 +2,13 @@ struct Foo; -const impl Foo { //~ ERROR: inherent impls cannot be const +const impl Foo { fn bar() {} } +const _: () = Foo::bar(); +//~^ ERROR: cannot call non-const associated function `Foo::bar` in constants + fn main() { // shouldn't error here because we shouldn't have been able to recover above Foo::bar(); diff --git a/tests/ui/traits/const-traits/const-impl-norecover.stderr b/tests/ui/traits/const-traits/const-impl-norecover.stderr index 78eb220343c94..234d1204bf098 100644 --- a/tests/ui/traits/const-traits/const-impl-norecover.stderr +++ b/tests/ui/traits/const-traits/const-impl-norecover.stderr @@ -1,12 +1,11 @@ -error: inherent impls cannot be const - --> $DIR/const-impl-norecover.rs:5:12 +error[E0015]: cannot call non-const associated function `Foo::bar` in constants + --> $DIR/const-impl-norecover.rs:9:15 | -LL | const impl Foo { - | ----- ^^^ inherent impl for this type - | | - | const because of this +LL | const _: () = Foo::bar(); + | ^^^^^^^^^^ | - = note: only trait implementations may be annotated with `const` + = note: calls in constants are limited to constant functions, tuple structs and tuple variants error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/inherent-impl.rs b/tests/ui/traits/const-traits/inherent-impl.rs index 5ca4c5d7863d2..886cfb0005f23 100644 --- a/tests/ui/traits/const-traits/inherent-impl.rs +++ b/tests/ui/traits/const-traits/inherent-impl.rs @@ -1,13 +1,13 @@ #![feature(const_trait_impl)] #![allow(bare_trait_objects)] +//@ check-pass + struct S; trait T {} impl const S {} -//~^ ERROR inherent impls cannot be const impl const dyn T {} -//~^ ERROR inherent impls cannot be const fn main() {} diff --git a/tests/ui/traits/const-traits/inherent-impl.stderr b/tests/ui/traits/const-traits/inherent-impl.stderr deleted file mode 100644 index d828ecb6349e8..0000000000000 --- a/tests/ui/traits/const-traits/inherent-impl.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: inherent impls cannot be const - --> $DIR/inherent-impl.rs:7:12 - | -LL | impl const S {} - | ----- ^ inherent impl for this type - | | - | const because of this - | - = note: only trait implementations may be annotated with `const` - -error: inherent impls cannot be const - --> $DIR/inherent-impl.rs:10:12 - | -LL | impl const dyn T {} - | ----- ^^^^^ inherent impl for this type - | | - | const because of this - | - = note: only trait implementations may be annotated with `const` - -error: aborting due to 2 previous errors - 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 593180ac09461..d46e0a1e6cb7d 100644 --- a/tests/ui/traits/const-traits/span-bug-issue-121418.rs +++ b/tests/ui/traits/const-traits/span-bug-issue-121418.rs @@ -4,7 +4,6 @@ struct S; trait T {} impl const dyn T { - //~^ ERROR inherent impls cannot be const pub const fn new() -> std::sync::Mutex {} //~^ ERROR mismatched types //~| ERROR cannot be known at compilation time 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 0c8ca918a3e1c..e772cfced8f4a 100644 --- a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr +++ b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr @@ -1,15 +1,5 @@ -error: inherent impls cannot be const - --> $DIR/span-bug-issue-121418.rs:6:12 - | -LL | impl const dyn T { - | ----- ^^^^^ inherent impl for this type - | | - | const because of this - | - = note: only trait implementations may be annotated with `const` - error[E0277]: the size for values of type `(dyn T + 'static)` cannot be known at compilation time - --> $DIR/span-bug-issue-121418.rs:8:27 + --> $DIR/span-bug-issue-121418.rs:7:27 | LL | pub const fn new() -> std::sync::Mutex {} | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -20,7 +10,7 @@ note: required because it appears within the type `std::sync::Mutex<(dyn T + 'st = note: the return type of a function must have a statically known size error[E0308]: mismatched types - --> $DIR/span-bug-issue-121418.rs:8:27 + --> $DIR/span-bug-issue-121418.rs:7:27 | LL | pub const fn new() -> std::sync::Mutex {} | --- ^^^^^^^^^^^^^^^^^^^^^^^ expected `Mutex`, found `()` @@ -30,7 +20,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 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. From 2b4c98a7e49abaf8a448bc8cf93ac0cfe31d661e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 10:32:29 +0000 Subject: [PATCH 5/7] Treat inherent methods in const impl blocks as const --- .../src/const_eval/fn_queries.rs | 3 +++ .../const-impl-inherent-bounds.rs | 22 +++++++++++++++++++ .../const-impl-inherent-bounds.stderr | 9 ++++++++ .../const-traits/const-impl-inherent.rs | 15 +++++++++++++ .../const-traits/const-impl-norecover.rs | 15 ------------- .../const-traits/const-impl-norecover.stderr | 11 ---------- ... const-impl-trait-not-impl-const-trait.rs} | 0 7 files changed, 49 insertions(+), 26 deletions(-) create mode 100644 tests/ui/traits/const-traits/const-impl-inherent-bounds.rs create mode 100644 tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr create mode 100644 tests/ui/traits/const-traits/const-impl-inherent.rs delete mode 100644 tests/ui/traits/const-traits/const-impl-norecover.rs delete mode 100644 tests/ui/traits/const-traits/const-impl-norecover.stderr rename tests/ui/traits/const-traits/{const-impl-recovery.rs => const-impl-trait-not-impl-const-trait.rs} (100%) diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 530d8d4b1fa62..4d95b8fec5c8a 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -8,6 +8,9 @@ fn parent_impl_or_trait_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::C let parent_id = tcx.local_parent(def_id); match tcx.def_kind(parent_id) { DefKind::Impl { of_trait: true } => tcx.impl_trait_header(parent_id).constness, + DefKind::Impl { of_trait: false } => { + tcx.hir_node_by_def_id(parent_id).expect_item().expect_impl().constness + } DefKind::Trait => { if tcx.is_const_trait(parent_id.into()) { hir::Constness::Const diff --git a/tests/ui/traits/const-traits/const-impl-inherent-bounds.rs b/tests/ui/traits/const-traits/const-impl-inherent-bounds.rs new file mode 100644 index 0000000000000..a0f5adc78f70a --- /dev/null +++ b/tests/ui/traits/const-traits/const-impl-inherent-bounds.rs @@ -0,0 +1,22 @@ +#![feature(const_trait_impl)] + +struct Foo(T); + +const trait Trait { + fn method() {} +} + +const impl Trait for () {} + +const impl Foo { + fn bar() { + T::method(); + //~^ ERROR: the trait bound `T: [const] Trait` is not satisfied + } +} + +const _: () = Foo::<()>::bar(); + +fn main() { + Foo::<()>::bar(); +} diff --git a/tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr b/tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr new file mode 100644 index 0000000000000..eb5142993d558 --- /dev/null +++ b/tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `T: [const] Trait` is not satisfied + --> $DIR/const-impl-inherent-bounds.rs:13:9 + | +LL | T::method(); + | ^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-impl-inherent.rs b/tests/ui/traits/const-traits/const-impl-inherent.rs new file mode 100644 index 0000000000000..493a9dc619a1e --- /dev/null +++ b/tests/ui/traits/const-traits/const-impl-inherent.rs @@ -0,0 +1,15 @@ +#![feature(const_trait_impl)] + +//@ check-pass + +struct Foo; + +const impl Foo { + fn bar() {} +} + +const _: () = Foo::bar(); + +fn main() { + Foo::bar(); +} diff --git a/tests/ui/traits/const-traits/const-impl-norecover.rs b/tests/ui/traits/const-traits/const-impl-norecover.rs deleted file mode 100644 index a1cc384186e17..0000000000000 --- a/tests/ui/traits/const-traits/const-impl-norecover.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(const_trait_impl)] - -struct Foo; - -const impl Foo { - fn bar() {} -} - -const _: () = Foo::bar(); -//~^ ERROR: cannot call non-const associated function `Foo::bar` in constants - -fn main() { - // shouldn't error here because we shouldn't have been able to recover above - Foo::bar(); -} diff --git a/tests/ui/traits/const-traits/const-impl-norecover.stderr b/tests/ui/traits/const-traits/const-impl-norecover.stderr deleted file mode 100644 index 234d1204bf098..0000000000000 --- a/tests/ui/traits/const-traits/const-impl-norecover.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0015]: cannot call non-const associated function `Foo::bar` in constants - --> $DIR/const-impl-norecover.rs:9:15 - | -LL | const _: () = Foo::bar(); - | ^^^^^^^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/const-impl-recovery.rs b/tests/ui/traits/const-traits/const-impl-trait-not-impl-const-trait.rs similarity index 100% rename from tests/ui/traits/const-traits/const-impl-recovery.rs rename to tests/ui/traits/const-traits/const-impl-trait-not-impl-const-trait.rs From 3596a2523ce5bf280e2b6af4da0e69f361213af4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 10:52:54 +0000 Subject: [PATCH 6/7] Collect const_conditions for inherent impls --- compiler/rustc_const_eval/src/const_eval/fn_queries.rs | 5 ++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 6 +++++- compiler/rustc_middle/src/ty/mod.rs | 2 +- .../ui/traits/const-traits/const-impl-inherent-bounds.rs | 4 +++- .../const-traits/const-impl-inherent-bounds.stderr | 9 --------- 5 files changed, 11 insertions(+), 15 deletions(-) delete mode 100644 tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 4d95b8fec5c8a..3e11541aace97 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -8,9 +8,7 @@ fn parent_impl_or_trait_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::C let parent_id = tcx.local_parent(def_id); match tcx.def_kind(parent_id) { DefKind::Impl { of_trait: true } => tcx.impl_trait_header(parent_id).constness, - DefKind::Impl { of_trait: false } => { - tcx.hir_node_by_def_id(parent_id).expect_item().expect_impl().constness - } + DefKind::Impl { of_trait: false } => tcx.constness(parent_id), DefKind::Trait => { if tcx.is_const_trait(parent_id.into()) { hir::Constness::Const @@ -33,6 +31,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { hir::Constness::NotConst } hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness, + hir::Node::Item(i) if let hir::ItemKind::Impl(impl_) = i.kind => impl_.constness, _ => { if let Some(fn_kind) = node.fn_kind() { if fn_kind.constness() == hir::Constness::Const { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index f47e349ac391d..cb694982dbe26 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1315,7 +1315,11 @@ fn should_encode_fn_sig(def_kind: DefKind) -> bool { fn should_encode_constness(def_kind: DefKind) -> bool { match def_kind { - DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Ctor(_, CtorKind::Fn) => true, + DefKind::Fn + | DefKind::AssocFn + | DefKind::Closure + | DefKind::Ctor(_, CtorKind::Fn) + | DefKind::Impl { of_trait: false } => true, DefKind::Struct | DefKind::Union diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2b9079da1830b..c01d3d5338acf 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2151,6 +2151,7 @@ impl<'tcx> TyCtxt<'tcx> { header.constness == hir::Constness::Const && self.is_const_trait(header.trait_ref.skip_binder().def_id) } + DefKind::Impl { of_trait: false } => self.constness(def_id) == hir::Constness::Const, DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) => { self.constness(def_id) == hir::Constness::Const } @@ -2189,7 +2190,6 @@ impl<'tcx> TyCtxt<'tcx> { false } DefKind::Ctor(_, CtorKind::Const) - | DefKind::Impl { of_trait: false } | DefKind::Mod | DefKind::Struct | DefKind::Union diff --git a/tests/ui/traits/const-traits/const-impl-inherent-bounds.rs b/tests/ui/traits/const-traits/const-impl-inherent-bounds.rs index a0f5adc78f70a..0781da1cc0af2 100644 --- a/tests/ui/traits/const-traits/const-impl-inherent-bounds.rs +++ b/tests/ui/traits/const-traits/const-impl-inherent-bounds.rs @@ -1,4 +1,7 @@ #![feature(const_trait_impl)] +//! Test that we can actually use `[const] Trait` bounds written on the impl block + +//@ check-pass struct Foo(T); @@ -11,7 +14,6 @@ const impl Trait for () {} const impl Foo { fn bar() { T::method(); - //~^ ERROR: the trait bound `T: [const] Trait` is not satisfied } } diff --git a/tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr b/tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr deleted file mode 100644 index eb5142993d558..0000000000000 --- a/tests/ui/traits/const-traits/const-impl-inherent-bounds.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0277]: the trait bound `T: [const] Trait` is not satisfied - --> $DIR/const-impl-inherent-bounds.rs:13:9 - | -LL | T::method(); - | ^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. From d780ac6dba76fc9efd5638aeb2b5051e312e1edb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 11:11:47 +0000 Subject: [PATCH 7/7] Forbid `const fn` within `const impl`s --- compiler/rustc_ast_passes/messages.ftl | 5 +++ .../rustc_ast_passes/src/ast_validation.rs | 35 +++++++++++++++---- 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, 99 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 5e10c5a77d97d..42e36486b3817 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -200,6 +200,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 aafa0549b18e6..e8844f03bd0c9 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -232,6 +232,18 @@ impl<'a> AstValidator<'a> { } } + fn check_impl_fn_not_const(&self, constness: Const, parent_constness: Const) { + let Const::Yes(span) = constness else { + return; + }; + + 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; @@ -1631,14 +1643,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 fd75e999d1381..da78141defa64 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 0000000000000..7d356af58c80f --- /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 0000000000000..b82c80576de00 --- /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 0000000000000..a135ba0831d90 --- /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 d46e0a1e6cb7d..97d9c69616cf1 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 e772cfced8f4a..e86e36907c4c6 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`.