diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fea87c2360f13..830b52ffa3f44 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3131,8 +3131,16 @@ pub enum Const { /// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532). #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)] pub enum Defaultness { + /// Item is unmarked. Implicitly determined based off of position. + /// For impls, this is `final`; for traits, this is `default`. + /// + /// If you're expanding an item in a built-in macro or parsing an item + /// by hand, you probably want to use this. + Implicit, + /// `default` Default(Span), - Final, + /// `final`; per RFC 3678, only trait items may be *explicitly* marked final. + Final(Span), } #[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)] @@ -4123,7 +4131,7 @@ impl AssocItemKind { | Self::Fn(box Fn { defaultness, .. }) | Self::Type(box TyAlias { defaultness, .. }) => defaultness, Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => { - Defaultness::Final + Defaultness::Implicit } } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 12eaa99143e90..5a798267efb90 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -939,7 +939,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let trait_item_def_id = hir_id.expect_owner(); - let (ident, generics, kind, has_default) = match &i.kind { + let (ident, generics, kind, has_value) = match &i.kind { AssocItemKind::Const(box ConstItem { ident, generics, ty, rhs, define_opaque, .. }) => { @@ -1080,13 +1080,17 @@ impl<'hir> LoweringContext<'_, 'hir> { } }; + let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value, || { + hir::Defaultness::Default { has_value } + }); + let item = hir::TraitItem { owner_id: trait_item_def_id, ident: self.lower_ident(ident), generics, kind, span: self.lower_span(i.span), - defaultness: hir::Defaultness::Default { has_value: has_default }, + defaultness, has_delayed_lints: !self.delayed_lints.is_empty(), }; self.arena.alloc(item) @@ -1114,7 +1118,8 @@ impl<'hir> LoweringContext<'_, 'hir> { // `defaultness.has_value()` is never called for an `impl`, always `true` in order // to not cause an assertion failure inside the `lower_defaultness` function. let has_val = true; - let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val); + let (defaultness, defaultness_span) = + self.lower_defaultness(defaultness, has_val, || hir::Defaultness::Final); let modifiers = TraitBoundModifiers { constness: BoundConstness::Never, asyncness: BoundAsyncness::Normal, @@ -1143,7 +1148,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> &'hir hir::ImplItem<'hir> { // Since `default impl` is not yet implemented, this is always true in impls. let has_value = true; - let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); + let (defaultness, _) = + self.lower_defaultness(i.kind.defaultness(), has_value, || hir::Defaultness::Final); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let attrs = self.lower_attrs( hir_id, @@ -1291,15 +1297,14 @@ impl<'hir> LoweringContext<'_, 'hir> { &self, d: Defaultness, has_value: bool, + implicit: impl FnOnce() -> hir::Defaultness, ) -> (hir::Defaultness, Option) { match d { + Defaultness::Implicit => (implicit(), None), Defaultness::Default(sp) => { (hir::Defaultness::Default { has_value }, Some(self.lower_span(sp))) } - Defaultness::Final => { - assert!(has_value); - (hir::Defaultness::Final, None) - } + Defaultness::Final(sp) => (hir::Defaultness::Final, Some(self.lower_span(sp))), } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 2457e0a777e44..3d8c7f3210395 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -65,6 +65,28 @@ impl TraitOrImpl { } } +enum AllowDefault { + Yes, + No, +} + +impl AllowDefault { + fn when(b: bool) -> Self { + if b { Self::Yes } else { Self::No } + } +} + +enum AllowFinal { + Yes, + No, +} + +impl AllowFinal { + fn when(b: bool) -> Self { + if b { Self::Yes } else { Self::No } + } +} + struct AstValidator<'a> { sess: &'a Session, features: &'a Features, @@ -563,10 +585,32 @@ impl<'a> AstValidator<'a> { } } - fn check_defaultness(&self, span: Span, defaultness: Defaultness) { - if let Defaultness::Default(def_span) = defaultness { - let span = self.sess.source_map().guess_head_span(span); - self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + fn check_defaultness( + &self, + span: Span, + defaultness: Defaultness, + allow_default: AllowDefault, + allow_final: AllowFinal, + ) { + match defaultness { + Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => { + let span = self.sess.source_map().guess_head_span(span); + self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + } + Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => { + let span = self.sess.source_map().guess_head_span(span); + self.dcx().emit_err(errors::ForbiddenFinal { span, def_span }); + } + _ => (), + } + } + + fn check_final_has_body(&self, item: &Item, defaultness: Defaultness) { + if let AssocItemKind::Fn(box Fn { body: None, .. }) = &item.kind + && let Defaultness::Final(def_span) = defaultness + { + let span = self.sess.source_map().guess_head_span(item.span); + self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span }); } } @@ -1192,7 +1236,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }, ) => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); for EiiImpl { eii_macro_path, .. } in eii_impls { self.visit_path(eii_macro_path); @@ -1362,7 +1406,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } ItemKind::Const(box ConstItem { defaultness, ident, rhs, .. }) => { - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if rhs.is_none() { self.dcx().emit_err(errors::ConstWithoutBody { span: item.span, @@ -1400,7 +1444,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ItemKind::TyAlias( ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. }, ) => { - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if ty.is_none() { self.dcx().emit_err(errors::TyAliasWithoutBody { span: item.span, @@ -1430,7 +1474,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => { - self.check_defaultness(fi.span, *defaultness); + self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); self.check_foreign_fn_bodyless(*ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); self.check_foreign_item_ascii_only(*ident); @@ -1450,7 +1494,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ty, .. }) => { - self.check_defaultness(fi.span, *defaultness); + self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); self.check_foreign_ty_genericless(generics, after_where_clause); @@ -1709,9 +1753,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_nomangle_item_asciionly(ident, item.span); } - if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() { - self.check_defaultness(item.span, item.kind.defaultness()); - } + let defaultness = item.kind.defaultness(); + self.check_defaultness( + item.span, + defaultness, + // `default` is allowed on all associated items in impls. + AllowDefault::when(matches!(ctxt, AssocCtxt::Impl { .. })), + // `final` is allowed on all associated *functions* in traits. + AllowFinal::when( + ctxt == AssocCtxt::Trait && matches!(item.kind, AssocItemKind::Fn(..)), + ), + ); + + self.check_final_has_body(item, defaultness); if let AssocCtxt::Impl { .. } = ctxt { match &item.kind { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index bda04129e5795..ee5b1b6f30931 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -159,6 +159,24 @@ pub(crate) struct ForbiddenDefault { pub def_span: Span, } +#[derive(Diagnostic)] +#[diag("`final` is only allowed on associated functions in traits")] +pub(crate) struct ForbiddenFinal { + #[primary_span] + pub span: Span, + #[label("`final` because of this")] + pub def_span: Span, +} + +#[derive(Diagnostic)] +#[diag("`final` is only allowed on associated functions if they have a body")] +pub(crate) struct ForbiddenFinalWithoutBody { + #[primary_span] + pub span: Span, + #[label("`final` because of this")] + pub def_span: Span, +} + #[derive(Diagnostic)] #[diag("associated constant in `impl` without body")] pub(crate) struct AssocConstWithoutBody { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index ce9001d659318..b0af7b2a29b5f 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -540,6 +540,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(frontmatter, "frontmatters are experimental"); gate_all!(coroutines, "coroutine syntax is experimental"); gate_all!(const_block_items, "const block items are experimental"); + gate_all!(final_associated_functions, "`final` on trait functions is experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 68054b06e39ff..8463b73d59d90 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -51,7 +51,7 @@ impl<'a> State<'a> { expr.as_deref(), vis, *safety, - ast::Defaultness::Final, + ast::Defaultness::Implicit, define_opaque.as_deref(), ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { @@ -201,7 +201,7 @@ impl<'a> State<'a> { body.as_deref(), &item.vis, ast::Safety::Default, - ast::Defaultness::Final, + ast::Defaultness::Implicit, define_opaque.as_deref(), ); } diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 12ccc4a6de043..25be432f3fae3 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -83,7 +83,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span let body = Some(cx.block_expr(call)); let kind = ItemKind::Fn(Box::new(Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig, ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span), generics: Generics::default(), diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 95191b82ff3ff..264f797a78250 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -334,7 +334,7 @@ mod llvm_enzyme { // The first element of it is the name of the function to be generated let d_fn = Box::new(ast::Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig: d_sig, ident: first_ident(&meta_item_vec[0]), generics, diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 8984e51c08447..1d9551f93a14c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -136,7 +136,7 @@ pub(crate) fn expand_deriving_coerce_pointee( of_trait: Some(Box::new(ast::TraitImplHeader { safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: ast::Const::No, @@ -159,7 +159,7 @@ pub(crate) fn expand_deriving_coerce_pointee( of_trait: Some(Box::new(ast::TraitImplHeader { safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: ast::Const::No, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3fe5e89ef06db..5362bcde1aad8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -614,7 +614,7 @@ impl<'a> TraitDef<'a> { }, attrs: ast::AttrVec::new(), kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, ident, generics: Generics::default(), after_where_clause: ast::WhereClause::default(), @@ -851,7 +851,7 @@ impl<'a> TraitDef<'a> { of_trait: Some(Box::new(ast::TraitImplHeader { safety: self.safety, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, @@ -1073,7 +1073,7 @@ impl<'a> MethodDef<'a> { let trait_lo_sp = span.shrink_to_lo(); let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span }; - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; // Create the method. Box::new(ast::AssocItem { diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index bb031dafdcfd5..680db2b289585 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -77,7 +77,7 @@ impl AllocFnFactory<'_, '_> { let sig = FnSig { decl, header, span: self.span }; let body = Some(self.cx.block_expr(result)); let kind = ItemKind::Fn(Box::new(Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig, ident: Ident::from_str_and_span(&global_fn_name(method.name), self.span), generics: Generics::default(), diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index d5f774865a9e1..c71a403d9b17e 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -283,7 +283,7 @@ pub(crate) fn expand_test_or_bench( // const $ident: test::TestDescAndFn = ast::ItemKind::Const( ast::ConstItem { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, ident: Ident::new(fn_.ident.name, sp), generics: ast::Generics::default(), ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 8d6969b0ca125..8b89d06c9ec61 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -329,7 +329,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; // Honor the reexport_test_harness_main attribute let main_ident = match cx.reexport_test_harness_main { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e5c06889f3e06..e5058b102057c 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -729,7 +729,7 @@ impl<'a> ExtCtxt<'a> { ty: Box, rhs: ast::ConstItemRhs, ) -> Box { - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; self.item( span, AttrVec::new(), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a941eb1f459ed..de0f941d9375a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -494,6 +494,8 @@ declare_features! ( (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. (unstable, ffi_pure, "1.45.0", Some(58329)), + /// Allows marking trait functions as `final` to prevent overriding impls + (unstable, final_associated_functions, "CURRENT_RUSTC_VERSION", Some(1)), /// Controlling the behavior of fmt::Debug (unstable, fmt_debug, "1.82.0", Some(129709)), /// Allows using `#[align(...)]` on function items diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e0bfae9617ff1..9866b7ef06dfb 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1175,13 +1175,35 @@ pub(super) fn check_specialization_validity<'tcx>( if let Err(parent_impl) = result { if !tcx.is_impl_trait_in_trait(impl_item) { - report_forbidden_specialization(tcx, impl_item, parent_impl); + let span = tcx.def_span(impl_item); + let ident = tcx.item_ident(impl_item); + + let err = match tcx.span_of_impl(parent_impl) { + Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, + Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, + }; + + tcx.dcx().emit_err(err); } else { tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default")); } } } +fn check_overriding_final_trait_item<'tcx>( + tcx: TyCtxt<'tcx>, + trait_item: ty::AssocItem, + impl_item: ty::AssocItem, +) { + if trait_item.defaultness(tcx).is_final() { + tcx.dcx().emit_err(errors::OverridingFinalTraitFunction { + impl_span: tcx.def_span(impl_item.def_id), + trait_span: tcx.def_span(trait_item.def_id), + ident: tcx.item_ident(impl_item.def_id), + }); + } +} + fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, impl_id: LocalDefId, @@ -1259,6 +1281,8 @@ fn check_impl_items_against_trait<'tcx>( impl_id.to_def_id(), impl_item, ); + + check_overriding_final_trait_item(tcx, ty_trait_item, ty_impl_item); } if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index d6ae14a7acfef..074501b8ebe47 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -197,18 +197,6 @@ pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDef } } -fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) { - let span = tcx.def_span(impl_item); - let ident = tcx.item_ident(impl_item); - - let err = match tcx.span_of_impl(parent_impl) { - Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, - Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, - }; - - tcx.dcx().emit_err(err); -} - fn missing_items_err( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6a23b42ae0981..4144f3eb95e7d 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -892,6 +892,16 @@ pub(crate) enum ImplNotMarkedDefault { #[diag("this item cannot be used as its where bounds are not satisfied for the `Self` type")] pub(crate) struct UselessImplItem; +#[derive(Diagnostic)] +#[diag("cannot override `{$ident}` because it already has a `final` definition in the trait")] +pub(crate) struct OverridingFinalTraitFunction { + #[primary_span] + pub impl_span: Span, + #[note("`{$ident}` is marked final here")] + pub trait_span: Span, + pub ident: Ident, +} + #[derive(Diagnostic)] #[diag("not all trait items implemented, missing: `{$missing_items_msg}`", code = E0046)] pub(crate) struct MissingTraitItem { diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index b54782ec592cb..a5b09e61bd767 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3777,6 +3777,17 @@ pub(crate) struct RecoverImportAsUse { pub token_name: String, } +#[derive(Diagnostic)] +#[diag("{$article} {$descr} cannot be `final`")] +#[note("only associated functions in traits can be `final`")] +pub(crate) struct InappropriateFinal { + #[primary_span] + #[label("`final` because of this")] + pub span: Span, + pub article: &'static str, + pub descr: &'static str, +} + #[derive(Diagnostic)] #[diag("expected `::`, found `:`")] #[note("import paths are delimited using `::`")] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9ac9bd5c0e466..c91a1a45e42cd 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -208,14 +208,24 @@ impl<'a> Parser<'a> { }) } - /// Error in-case `default` was parsed in an in-appropriate context. + /// Error in-case `default`/`final` was parsed in an in-appropriate context. fn error_on_unconsumed_default(&self, def: Defaultness, kind: &ItemKind) { - if let Defaultness::Default(span) = def { - self.dcx().emit_err(errors::InappropriateDefault { - span, - article: kind.article(), - descr: kind.descr(), - }); + match def { + Defaultness::Default(span) => { + self.dcx().emit_err(errors::InappropriateDefault { + span, + article: kind.article(), + descr: kind.descr(), + }); + } + Defaultness::Final(span) => { + self.dcx().emit_err(errors::InappropriateFinal { + span, + article: kind.article(), + descr: kind.descr(), + }); + } + Defaultness::Implicit => (), } } @@ -231,8 +241,8 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, case: Case, ) -> PResult<'a, Option> { - let check_pub = def == &Defaultness::Final; - let mut def_ = || mem::replace(def, Defaultness::Final); + let check_pub = def == &Defaultness::Implicit; + let mut def_ = || mem::replace(def, Defaultness::Implicit); let info = if !self.is_use_closure() && self.eat_keyword_case(exp!(Use), case) { self.parse_use_item()? @@ -989,8 +999,11 @@ impl<'a> Parser<'a> { { self.bump(); // `default` Defaultness::Default(self.prev_token_uninterpolated_span()) + } else if self.eat_keyword(exp!(Final)) { + self.psess.gated_spans.gate(sym::final_associated_functions, self.prev_token.span); + Defaultness::Final(self.prev_token_uninterpolated_span()) } else { - Defaultness::Final + Defaultness::Implicit } } @@ -1115,7 +1128,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); let rhs = expr.map(ConstItemRhs::Body); AssocItemKind::Const(Box::new(ConstItem { - defaultness: Defaultness::Final, + defaultness: Defaultness::Implicit, ident, generics: Generics::default(), ty, diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 567b1be5e5d98..2d1db430ac27c 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -91,6 +91,7 @@ pub enum TokenType { KwElse, KwEnum, KwExtern, + KwFinal, KwFn, KwFor, KwGen, @@ -233,6 +234,7 @@ impl TokenType { KwExtern, KwFn, KwFor, + KwFinal, KwGen, KwIf, KwImpl, @@ -309,6 +311,7 @@ impl TokenType { TokenType::KwExtern => Some(kw::Extern), TokenType::KwFn => Some(kw::Fn), TokenType::KwFor => Some(kw::For), + TokenType::KwFinal => Some(kw::Final), TokenType::KwGen => Some(kw::Gen), TokenType::KwIf => Some(kw::If), TokenType::KwImpl => Some(kw::Impl), @@ -524,6 +527,7 @@ macro_rules! exp { (Extern) => { exp!(@kw, Extern, KwExtern) }; (Fn) => { exp!(@kw, Fn, KwFn) }; (For) => { exp!(@kw, For, KwFor) }; + (Final) => { exp!(@kw, Final, KwFinal) }; (Gen) => { exp!(@kw, Gen, KwGen) }; (If) => { exp!(@kw, If, KwIf) }; (Impl) => { exp!(@kw, Impl, KwImpl) }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6aa2eae556e25..abc70c77e29a1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1088,6 +1088,7 @@ symbols! { field_init_shorthand, file, file_options, + final_associated_functions, flags, float, float_to_int_unchecked, diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 512973e452870..489b2d160a51c 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -314,6 +314,11 @@ pub fn dyn_compatibility_violations_for_assoc_item( trait_def_id: DefId, item: ty::AssocItem, ) -> Vec { + // `final` assoc functions don't prevent a trait from being dyn-compatible + if tcx.defaultness(item.def_id).is_final() { + return Vec::new(); + } + // Any item that has a `Self: Sized` requisite is otherwise exempt from the regulations. if tcx.generics_require_sized_self(item.def_id) { return Vec::new(); diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 584c8e2a27c82..a124f67209df0 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -210,6 +210,11 @@ fn own_existential_vtable_entries_iter( debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); let def_id = trait_method.def_id; + // Final methods should not be included in the vtable. + if trait_method.defaultness(tcx).is_final() { + return None; + } + // Some methods cannot be called on an object; skip those. if !is_vtable_safe_method(tcx, trait_def_id, trait_method) { debug!("own_existential_vtable_entry: not vtable safe"); diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index cbe15eb54787f..a014098e7ed55 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -237,6 +237,12 @@ fn resolve_associated_item<'tcx>( } traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => { let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args); + + // `final` methods should be called directly. + if tcx.defaultness(trait_item_id).is_final() { + return Ok(Some(ty::Instance::new_raw(trait_item_id, rcvr_args))); + } + if trait_ref.has_non_region_infer() || trait_ref.has_non_region_param() { // We only resolve totally substituted vtable entries. None diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 753fba3ed4825..8771edabf97bc 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1222,11 +1222,11 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body)); - MethodItem(m, None) + MethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents)); - RequiredMethodItem(m) + RequiredMethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) } hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); @@ -1271,7 +1271,7 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final, hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness, }; - MethodItem(m, Some(defaultness)) + MethodItem(m, Defaultness::from_impl_item(defaultness)) } hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); @@ -1353,18 +1353,20 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } } - let provided = match assoc_item.container { - ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, - ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), + let defaultness = assoc_item.defaultness(tcx); + let (provided, defaultness) = match assoc_item.container { + ty::AssocContainer::Trait => { + (defaultness.has_value(), Defaultness::from_trait_item(defaultness)) + } + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { + (true, Defaultness::from_impl_item(defaultness)) + } }; + if provided { - let defaultness = match assoc_item.container { - ty::AssocContainer::TraitImpl(_) => Some(assoc_item.defaultness(tcx)), - ty::AssocContainer::InherentImpl | ty::AssocContainer::Trait => None, - }; MethodItem(item, defaultness) } else { - RequiredMethodItem(item) + RequiredMethodItem(item, defaultness) } } ty::AssocKind::Type { .. } => { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c145929534d97..47a1199e7fe46 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -61,6 +61,29 @@ pub(crate) enum ItemId { Blanket { impl_id: DefId, for_: DefId }, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) enum Defaultness { + Implicit, + Default, + Final, +} + +impl Defaultness { + pub(crate) fn from_trait_item(defaultness: hir::Defaultness) -> Self { + match defaultness { + hir::Defaultness::Default { .. } => Self::Implicit, + hir::Defaultness::Final => Self::Final, + } + } + + pub(crate) fn from_impl_item(defaultness: hir::Defaultness) -> Self { + match defaultness { + hir::Defaultness::Default { .. } => Self::Default, + hir::Defaultness::Final => Self::Implicit, + } + } +} + impl ItemId { #[inline] pub(crate) fn is_local(self) -> bool { @@ -703,12 +726,12 @@ impl Item { ItemType::from(self) } - pub(crate) fn is_default(&self) -> bool { + pub(crate) fn defaultness(&self) -> Option { match self.kind { - ItemKind::MethodItem(_, Some(defaultness)) => { - defaultness.has_value() && !defaultness.is_final() + ItemKind::MethodItem(_, defaultness) | ItemKind::RequiredMethodItem(_, defaultness) => { + Some(defaultness) } - _ => false, + _ => None, } } @@ -770,8 +793,8 @@ impl Item { } } ItemKind::FunctionItem(_) - | ItemKind::MethodItem(_, _) - | ItemKind::RequiredMethodItem(_) => { + | ItemKind::MethodItem(..) + | ItemKind::RequiredMethodItem(..) => { let def_id = self.def_id().unwrap(); build_fn_header(def_id, tcx, tcx.asyncness(def_id)) } @@ -855,11 +878,11 @@ pub(crate) enum ItemKind { TraitAliasItem(TraitAlias), ImplItem(Box), /// A required method in a trait declaration meaning it's only a function signature. - RequiredMethodItem(Box), + RequiredMethodItem(Box, Defaultness), /// A method in a trait impl or a provided method in a trait declaration. /// /// Compared to [RequiredMethodItem], it also contains a method body. - MethodItem(Box, Option), + MethodItem(Box, Defaultness), StructFieldItem(Type), VariantItem(Variant), /// `fn`s from an extern block @@ -917,8 +940,8 @@ impl ItemKind { | StaticItem(_) | ConstantItem(_) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(_, _) | ForeignStaticItem(_, _) diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index ee5f260615db5..c970fdbbc93af 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -82,8 +82,8 @@ pub(crate) trait DocFolder: Sized { | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(..) | ForeignStaticItem(..) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f38a21bd1ff36..7646375cbf7a3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1566,10 +1566,6 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { }) } -pub(crate) fn print_default_space(v: bool) -> &'static str { - if v { "default " } else { "" } -} - fn print_generic_arg(generic_arg: &clean::GenericArg, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match generic_arg { clean::GenericArg::Lifetime(lt) => f.write_str(print_lifetime(lt)), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 86e8167dc3cda..c03a3e8980e11 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -66,7 +66,7 @@ use tracing::{debug, info}; pub(crate) use self::context::*; pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; pub(crate) use self::write_shared::*; -use crate::clean::{self, ItemId, RenderedLink}; +use crate::clean::{self, Defaultness, ItemId, RenderedLink}; use crate::display::{Joined as _, MaybeDisplay as _}; use crate::error::Error; use crate::formats::Impl; @@ -75,8 +75,8 @@ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::format::{ Ending, HrefError, HrefInfo, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space, - print_constness_with_space, print_default_space, print_generic_bounds, print_generics, - print_impl, print_path, print_type, print_where_clause, visibility_print_with_space, + print_constness_with_space, print_generic_bounds, print_generics, print_impl, print_path, + print_type, print_where_clause, visibility_print_with_space, }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, @@ -1109,7 +1109,11 @@ fn assoc_method( let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); let name = meth.name.as_ref().unwrap(); let vis = visibility_print_with_space(meth, cx).to_string(); - let defaultness = print_default_space(meth.is_default()); + let defaultness = match meth.defaultness().expect("Expected assoc method to have defaultness") { + Defaultness::Implicit => "", + Defaultness::Final => "final ", + Defaultness::Default => "default ", + }; // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove // this condition. let constness = match render_mode { @@ -1260,7 +1264,7 @@ fn render_assoc_item( ) -> impl fmt::Display { fmt::from_fn(move |f| match &item.kind { clean::StrippedItem(..) => Ok(()), - clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => { + clean::RequiredMethodItem(m, _) | clean::MethodItem(m, _) => { assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f) } clean::RequiredAssocConstItem(generics, ty) => assoc_const( @@ -1585,7 +1589,7 @@ fn render_deref_methods( fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool { let self_type_opt = match item.kind { clean::MethodItem(ref method, _) => method.decl.receiver_type(), - clean::RequiredMethodItem(ref method) => method.decl.receiver_type(), + clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(), _ => None, }; @@ -1855,7 +1859,7 @@ fn render_impl( deprecation_class = ""; } match &item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => { + clean::MethodItem(..) | clean::RequiredMethodItem(..) => { // Only render when the method is not static or we allow static methods if render_method_item { let id = cx.derive_id(format!("{item_type}.{name}")); @@ -2033,7 +2037,9 @@ fn render_impl( if !impl_.is_negative_trait_impl() { for impl_item in &impl_.items { match impl_item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item), + clean::MethodItem(..) | clean::RequiredMethodItem(..) => { + methods.push(impl_item) + } clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => { assoc_types.push(impl_item) } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 30b534003da17..766109b65beaa 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1968,7 +1968,7 @@ pub(crate) fn get_function_type_for_search( clean::ForeignFunctionItem(ref f, _) | clean::FunctionItem(ref f) | clean::MethodItem(ref f, _) - | clean::RequiredMethodItem(ref f) => { + | clean::RequiredMethodItem(ref f, _) => { get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache) } clean::ConstantItem(ref c) => make_nullary_fn(&c.type_), diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 2edf7891be400..599d7b10005d9 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -296,7 +296,7 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum MethodItem(m, _) => { ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) } - RequiredMethodItem(m) => { + RequiredMethodItem(m, _) => { ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 4d31409afe825..9f6bf009a0540 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -35,8 +35,8 @@ pub(crate) trait DocVisitor<'a>: Sized { | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(..) | ForeignStaticItem(..) 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 cf8716398efb3..481b6cd373093 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -813,7 +813,9 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { matches!( (l, r), - (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) + (Defaultness::Implicit, Defaultness::Implicit) + | (Defaultness::Default(_), Defaultness::Default(_)) + | (Defaultness::Final(_), Defaultness::Final(_)) ) } diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index c9e32492c50b0..016d3fbe94c8e 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -319,12 +319,13 @@ impl<'a> FnSig<'a> { method_sig: &'a ast::FnSig, generics: &'a ast::Generics, visibility: &'a ast::Visibility, + defaultness: ast::Defaultness, ) -> FnSig<'a> { FnSig { safety: method_sig.header.safety, coroutine_kind: Cow::Borrowed(&method_sig.header.coroutine_kind), constness: method_sig.header.constness, - defaultness: ast::Defaultness::Final, + defaultness, ext: method_sig.header.ext, decl: &*method_sig.decl, generics, @@ -339,9 +340,7 @@ impl<'a> FnSig<'a> { ) -> FnSig<'a> { match *fn_kind { visit::FnKind::Fn(visit::FnCtxt::Assoc(..), vis, ast::Fn { sig, generics, .. }) => { - let mut fn_sig = FnSig::from_method_sig(sig, generics, vis); - fn_sig.defaultness = defaultness; - fn_sig + FnSig::from_method_sig(sig, generics, vis, defaultness) } visit::FnKind::Fn(_, vis, ast::Fn { sig, generics, .. }) => FnSig { decl, @@ -459,6 +458,7 @@ impl<'a> FmtVisitor<'a> { sig: &ast::FnSig, vis: &ast::Visibility, generics: &ast::Generics, + defaultness: ast::Defaultness, span: Span, ) -> RewriteResult { // Drop semicolon or it will be interpreted as comment. @@ -469,7 +469,7 @@ impl<'a> FmtVisitor<'a> { &context, indent, ident, - &FnSig::from_method_sig(sig, generics, vis), + &FnSig::from_method_sig(sig, generics, vis, defaultness), span, FnBraceStyle::None, )?; @@ -3475,7 +3475,7 @@ impl Rewrite for ast::ForeignItem { context, shape.indent, ident, - &FnSig::from_method_sig(sig, generics, &self.vis), + &FnSig::from_method_sig(sig, generics, &self.vis, defaultness), span, FnBraceStyle::None, ) diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 3a2975024a33b..b676803379f7c 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -102,8 +102,9 @@ pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str { #[inline] pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str { match defaultness { + ast::Defaultness::Implicit => "", ast::Defaultness::Default(..) => "default ", - ast::Defaultness::Final => "", + ast::Defaultness::Final(..) => "final ", } } diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 8abcba121e58b..742caefc7cc55 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -583,7 +583,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { let indent = self.block_indent; let rewrite = self - .rewrite_required_fn(indent, ident, sig, &item.vis, generics, item.span) + .rewrite_required_fn( + indent, + ident, + sig, + &item.vis, + generics, + defaultness, + item.span, + ) .ok(); self.push_rewrite(item.span, rewrite); } @@ -686,7 +694,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { let indent = self.block_indent; let rewrite = self - .rewrite_required_fn(indent, fn_kind.ident, sig, &ai.vis, generics, ai.span) + .rewrite_required_fn( + indent, + fn_kind.ident, + sig, + &ai.vis, + generics, + defaultness, + ai.span, + ) .ok(); self.push_rewrite(ai.span, rewrite); } diff --git a/src/tools/rustfmt/tests/target/final-kw.rs b/src/tools/rustfmt/tests/target/final-kw.rs new file mode 100644 index 0000000000000..d68b6908d76a7 --- /dev/null +++ b/src/tools/rustfmt/tests/target/final-kw.rs @@ -0,0 +1,5 @@ +trait Foo { + final fn final_() {} + + fn not_final() {} +} diff --git a/tests/rustdoc-html/final-trait-method.rs b/tests/rustdoc-html/final-trait-method.rs new file mode 100644 index 0000000000000..016578a8b0e74 --- /dev/null +++ b/tests/rustdoc-html/final-trait-method.rs @@ -0,0 +1,22 @@ +#![feature(final_associated_functions)] + +//@ has final_trait_method/trait.Item.html +pub trait Item { + //@ has - '//*[@id="method.foo"]' 'final fn foo()' + //@ !has - '//*[@id="method.foo"]' 'default fn foo()' + final fn foo() {} + + //@ has - '//*[@id="method.bar"]' 'fn bar()' + //@ !has - '//*[@id="method.bar"]' 'default fn bar()' + //@ !has - '//*[@id="method.bar"]' 'final fn bar()' + fn bar() {} +} + +//@ has final_trait_method/struct.Foo.html +pub struct Foo; +impl Item for Foo { + //@ has - '//*[@id="method.bar"]' 'fn bar()' + //@ !has - '//*[@id="method.bar"]' 'final fn bar()' + //@ !has - '//*[@id="method.bar"]' 'default fn bar()' + fn bar() {} +} diff --git a/tests/ui/coroutine/gen_fn.none.stderr b/tests/ui/coroutine/gen_fn.none.stderr index 590210641aed4..8a5f865413168 100644 --- a/tests/ui/coroutine/gen_fn.none.stderr +++ b/tests/ui/coroutine/gen_fn.none.stderr @@ -1,8 +1,8 @@ -error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` +error: expected one of `#`, `async`, `const`, `default`, `extern`, `final`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} - | ^^^ expected one of 10 possible tokens + | ^^^ expected one of 11 possible tokens error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index 2f50d5db9acf6..78301cd2832c7 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -2,7 +2,7 @@ //@[e2024] edition: 2024 gen fn foo() {} -//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` +//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `final`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` //[e2024]~^^ ERROR: gen blocks are experimental fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-final-associated-functions.rs b/tests/ui/feature-gates/feature-gate-final-associated-functions.rs new file mode 100644 index 0000000000000..bdac2710d58e1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-final-associated-functions.rs @@ -0,0 +1,6 @@ +trait Foo { + final fn bar() {} + //~^ ERROR `final` on trait functions is experimental +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-final-associated-functions.stderr b/tests/ui/feature-gates/feature-gate-final-associated-functions.stderr new file mode 100644 index 0000000000000..b3731acd1d905 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-final-associated-functions.stderr @@ -0,0 +1,13 @@ +error[E0658]: `final` on trait functions is experimental + --> $DIR/feature-gate-final-associated-functions.rs:2:5 + | +LL | final fn bar() {} + | ^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(final_associated_functions)]` 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 + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/duplicate-visibility.rs b/tests/ui/parser/duplicate-visibility.rs index f0ee60873da01..d35624981c744 100644 --- a/tests/ui/parser/duplicate-visibility.rs +++ b/tests/ui/parser/duplicate-visibility.rs @@ -2,8 +2,8 @@ fn main() {} extern "C" { //~ NOTE while parsing this item list starting here pub pub fn foo(); - //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` - //~| NOTE expected one of 9 possible tokens + //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `final`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` + //~| NOTE expected one of 10 possible tokens //~| HELP there is already a visibility modifier, remove one //~| NOTE explicit visibility first seen here } //~ NOTE the item list ends here diff --git a/tests/ui/parser/duplicate-visibility.stderr b/tests/ui/parser/duplicate-visibility.stderr index 0d1421ee7f4e4..e00ebe6a8cf6d 100644 --- a/tests/ui/parser/duplicate-visibility.stderr +++ b/tests/ui/parser/duplicate-visibility.stderr @@ -1,4 +1,4 @@ -error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` +error: expected one of `(`, `async`, `const`, `default`, `extern`, `final`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` --> $DIR/duplicate-visibility.rs:4:9 | LL | extern "C" { @@ -6,7 +6,7 @@ LL | extern "C" { LL | pub pub fn foo(); | ^^^ | | - | expected one of 9 possible tokens + | expected one of 10 possible tokens | help: there is already a visibility modifier, remove one ... LL | } diff --git a/tests/ui/parser/misspelled-keywords/pub-fn.stderr b/tests/ui/parser/misspelled-keywords/pub-fn.stderr index 1123c652c0ee7..9b895f32c91e1 100644 --- a/tests/ui/parser/misspelled-keywords/pub-fn.stderr +++ b/tests/ui/parser/misspelled-keywords/pub-fn.stderr @@ -1,8 +1,8 @@ -error: expected one of `#`, `async`, `auto`, `const`, `default`, `enum`, `extern`, `fn`, `gen`, `impl`, `macro_rules`, `macro`, `mod`, `pub`, `safe`, `static`, `struct`, `trait`, `type`, `unsafe`, or `use`, found `puB` +error: expected one of `#`, `async`, `auto`, `const`, `default`, `enum`, `extern`, `final`, `fn`, `gen`, `impl`, `macro_rules`, `macro`, `mod`, `pub`, `safe`, `static`, `struct`, `trait`, `type`, `unsafe`, or `use`, found `puB` --> $DIR/pub-fn.rs:1:1 | LL | puB fn code() {} - | ^^^ expected one of 21 possible tokens + | ^^^ expected one of 22 possible tokens | help: write keyword `pub` in lowercase | diff --git a/tests/ui/traits/final/dyn-compat.rs b/tests/ui/traits/final/dyn-compat.rs new file mode 100644 index 0000000000000..d600058820c20 --- /dev/null +++ b/tests/ui/traits/final/dyn-compat.rs @@ -0,0 +1,40 @@ +//@ run-pass + +#![feature(final_associated_functions)] + +trait FinalNoReceiver { + final fn no_receiver() {} +} + +trait FinalGeneric { + final fn generic(&self, _value: T) {} +} + +trait FinalSelfParam { + final fn self_param(&self, _other: &Self) {} +} + +trait FinalSelfReturn { + final fn self_return(&self) -> &Self { + self + } +} + +struct S; + +impl FinalNoReceiver for S {} +impl FinalGeneric for S {} +impl FinalSelfParam for S {} +impl FinalSelfReturn for S {} + +fn main() { + let s = S; + ::no_receiver(); + let obj_generic: &dyn FinalGeneric = &s; + let obj_param: &dyn FinalSelfParam = &s; + let obj_return: &dyn FinalSelfReturn = &s; + obj_generic.generic(1u8); + obj_param.self_param(obj_param); + let _ = obj_return.self_return(); + let _: &dyn FinalNoReceiver = &s; +} diff --git a/tests/ui/traits/final/final-kw.gated.stderr b/tests/ui/traits/final/final-kw.gated.stderr new file mode 100644 index 0000000000000..a7967ebf08e2a --- /dev/null +++ b/tests/ui/traits/final/final-kw.gated.stderr @@ -0,0 +1,13 @@ +error[E0658]: `final` on trait functions is experimental + --> $DIR/final-kw.rs:5:5 + | +LL | final fn foo() {} + | ^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(final_associated_functions)]` 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 + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/final/final-kw.rs b/tests/ui/traits/final/final-kw.rs new file mode 100644 index 0000000000000..ed675dea6608d --- /dev/null +++ b/tests/ui/traits/final/final-kw.rs @@ -0,0 +1,9 @@ +//@ revisions: ungated gated + +#[cfg(ungated)] +trait Trait { + final fn foo() {} + //~^ ERROR `final` on trait functions is experimental +} + +fn main() {} diff --git a/tests/ui/traits/final/final-kw.ungated.stderr b/tests/ui/traits/final/final-kw.ungated.stderr new file mode 100644 index 0000000000000..a7967ebf08e2a --- /dev/null +++ b/tests/ui/traits/final/final-kw.ungated.stderr @@ -0,0 +1,13 @@ +error[E0658]: `final` on trait functions is experimental + --> $DIR/final-kw.rs:5:5 + | +LL | final fn foo() {} + | ^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(final_associated_functions)]` 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 + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/final/final-must-have-body.rs b/tests/ui/traits/final/final-must-have-body.rs new file mode 100644 index 0000000000000..57ed26c3e78db --- /dev/null +++ b/tests/ui/traits/final/final-must-have-body.rs @@ -0,0 +1,8 @@ +#![feature(final_associated_functions)] + +trait Foo { + final fn method(); + //~^ ERROR `final` is only allowed on associated functions if they have a body +} + +fn main() {} diff --git a/tests/ui/traits/final/final-must-have-body.stderr b/tests/ui/traits/final/final-must-have-body.stderr new file mode 100644 index 0000000000000..e4f1ffb701e86 --- /dev/null +++ b/tests/ui/traits/final/final-must-have-body.stderr @@ -0,0 +1,10 @@ +error: `final` is only allowed on associated functions if they have a body + --> $DIR/final-must-have-body.rs:4:5 + | +LL | final fn method(); + | -----^^^^^^^^^^^^^ + | | + | `final` because of this + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/final/overriding.rs b/tests/ui/traits/final/overriding.rs new file mode 100644 index 0000000000000..f91451852ff06 --- /dev/null +++ b/tests/ui/traits/final/overriding.rs @@ -0,0 +1,12 @@ +#![feature(final_associated_functions)] + +trait Foo { + final fn method() {} +} + +impl Foo for () { + fn method() {} + //~^ ERROR cannot override `method` because it already has a `final` definition in the trait +} + +fn main() {} diff --git a/tests/ui/traits/final/overriding.stderr b/tests/ui/traits/final/overriding.stderr new file mode 100644 index 0000000000000..b5598565072fc --- /dev/null +++ b/tests/ui/traits/final/overriding.stderr @@ -0,0 +1,14 @@ +error: cannot override `method` because it already has a `final` definition in the trait + --> $DIR/overriding.rs:8:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ + | +note: `method` is marked final here + --> $DIR/overriding.rs:4:5 + | +LL | final fn method() {} + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/final/positions.rs b/tests/ui/traits/final/positions.rs new file mode 100644 index 0000000000000..9bf659e12431e --- /dev/null +++ b/tests/ui/traits/final/positions.rs @@ -0,0 +1,72 @@ +#![feature(final_associated_functions)] + +// Just for exercising the syntax positions +#![feature(associated_type_defaults, extern_types, inherent_associated_types)] +#![allow(incomplete_features)] + +final struct Foo {} +//~^ ERROR a struct cannot be `final` + +final trait Trait { +//~^ ERROR a trait cannot be `final` + + final fn method() {} + // OK! + + final type Foo = (); + //~^ ERROR `final` is only allowed on associated functions in traits + + final const FOO: usize = 1; + //~^ ERROR `final` is only allowed on associated functions in traits +} + +final impl Foo { + final fn method() {} + //~^ ERROR `final` is only allowed on associated functions in traits + + final type Foo = (); + //~^ ERROR `final` is only allowed on associated functions in traits + + final const FOO: usize = 1; + //~^ ERROR `final` is only allowed on associated functions in traits +} + +final impl Trait for Foo { + final fn method() {} + //~^ ERROR `final` is only allowed on associated functions in traits + //~^^ ERROR cannot override `method` because it already has a `final` definition in the trait + + final type Foo = (); + //~^ ERROR `final` is only allowed on associated functions in traits + //~^^ ERROR cannot override `Foo` because it already has a `final` definition in the trait + + final const FOO: usize = 1; + //~^ ERROR `final` is only allowed on associated functions in traits + //~^^ ERROR cannot override `FOO` because it already has a `final` definition in the trait +} + + +final fn foo() {} +//~^ ERROR `final` is only allowed on associated functions in traits + +final type FooTy = (); +//~^ ERROR `final` is only allowed on associated functions in traits + +final const FOO: usize = 0; +//~^ ERROR `final` is only allowed on associated functions in traits + +final unsafe extern "C" { +//~^ ERROR an extern block cannot be `final` + + final fn foo_extern(); + //~^ ERROR `final` is only allowed on associated functions in traits + + final type FooExtern; + //~^ ERROR `final` is only allowed on associated functions in traits + + final static FOO_EXTERN: usize = 0; + //~^ ERROR a static item cannot be `final` + //~| ERROR incorrect `static` inside `extern` block +} + +fn main() {} diff --git a/tests/ui/traits/final/positions.stderr b/tests/ui/traits/final/positions.stderr new file mode 100644 index 0000000000000..bcb300e49d1a9 --- /dev/null +++ b/tests/ui/traits/final/positions.stderr @@ -0,0 +1,187 @@ +error: a struct cannot be `final` + --> $DIR/positions.rs:7:1 + | +LL | final struct Foo {} + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: a trait cannot be `final` + --> $DIR/positions.rs:10:1 + | +LL | final trait Trait { + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: a static item cannot be `final` + --> $DIR/positions.rs:67:5 + | +LL | final static FOO_EXTERN: usize = 0; + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: an extern block cannot be `final` + --> $DIR/positions.rs:58:1 + | +LL | final unsafe extern "C" { + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:16:5 + | +LL | final type Foo = (); + | -----^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:19:5 + | +LL | final const FOO: usize = 1; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:24:5 + | +LL | final fn method() {} + | -----^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:27:5 + | +LL | final type Foo = (); + | -----^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:30:5 + | +LL | final const FOO: usize = 1; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:35:5 + | +LL | final fn method() {} + | -----^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:39:5 + | +LL | final type Foo = (); + | -----^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:43:5 + | +LL | final const FOO: usize = 1; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:49:1 + | +LL | final fn foo() {} + | -----^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:52:1 + | +LL | final type FooTy = (); + | -----^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:55:1 + | +LL | final const FOO: usize = 0; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:61:5 + | +LL | final fn foo_extern(); + | -----^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:64:5 + | +LL | final type FooExtern; + | -----^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: incorrect `static` inside `extern` block + --> $DIR/positions.rs:67:18 + | +LL | final unsafe extern "C" { + | ----------------------- `extern` blocks define existing foreign statics and statics inside of them cannot have a body +... +LL | final static FOO_EXTERN: usize = 0; + | ^^^^^^^^^^ - the invalid body + | | + | cannot have a body + | + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: cannot override `method` because it already has a `final` definition in the trait + --> $DIR/positions.rs:35:5 + | +LL | final fn method() {} + | ^^^^^^^^^^^^^^^^^ + | +note: `method` is marked final here + --> $DIR/positions.rs:13:5 + | +LL | final fn method() {} + | ^^^^^^^^^^^^^^^^^ + +error: cannot override `Foo` because it already has a `final` definition in the trait + --> $DIR/positions.rs:39:5 + | +LL | final type Foo = (); + | ^^^^^^^^^^^^^^ + | +note: `Foo` is marked final here + --> $DIR/positions.rs:16:5 + | +LL | final type Foo = (); + | ^^^^^^^^^^^^^^ + +error: cannot override `FOO` because it already has a `final` definition in the trait + --> $DIR/positions.rs:43:5 + | +LL | final const FOO: usize = 1; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: `FOO` is marked final here + --> $DIR/positions.rs:19:5 + | +LL | final const FOO: usize = 1; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 21 previous errors + diff --git a/tests/ui/traits/final/works.rs b/tests/ui/traits/final/works.rs new file mode 100644 index 0000000000000..e756521701a45 --- /dev/null +++ b/tests/ui/traits/final/works.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(final_associated_functions)] + +trait Foo { + final fn bar(&self) {} +} + +impl Foo for () {} + +fn main() { + ().bar(); +}