diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1bee7a72229a..5f22dce29dc6 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 d6ae14a7acfe..074501b8ebe4 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 6a23b42ae098..4144f3eb95e7 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/tests/ui/traits/final/overriding.rs b/tests/ui/traits/final/overriding.rs new file mode 100644 index 000000000000..f91451852ff0 --- /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 000000000000..b5598565072f --- /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 index 01ce6ea4750f..9bf659e12431 100644 --- a/tests/ui/traits/final/positions.rs +++ b/tests/ui/traits/final/positions.rs @@ -34,12 +34,15 @@ final impl Foo { 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 } diff --git a/tests/ui/traits/final/positions.stderr b/tests/ui/traits/final/positions.stderr index 0514f5e9f472..bcb300e49d1a 100644 --- a/tests/ui/traits/final/positions.stderr +++ b/tests/ui/traits/final/positions.stderr @@ -15,7 +15,7 @@ LL | final trait Trait { = note: only associated functions in traits can be `final` error: a static item cannot be `final` - --> $DIR/positions.rs:64:5 + --> $DIR/positions.rs:67:5 | LL | final static FOO_EXTERN: usize = 0; | ^^^^^ `final` because of this @@ -23,7 +23,7 @@ LL | final static FOO_EXTERN: usize = 0; = note: only associated functions in traits can be `final` error: an extern block cannot be `final` - --> $DIR/positions.rs:55:1 + --> $DIR/positions.rs:58:1 | LL | final unsafe extern "C" { | ^^^^^ `final` because of this @@ -79,7 +79,7 @@ LL | final fn method() {} | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:38:5 + --> $DIR/positions.rs:39:5 | LL | final type Foo = (); | -----^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | final type Foo = (); | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:41:5 + --> $DIR/positions.rs:43:5 | LL | final const FOO: usize = 1; | -----^^^^^^^^^^^^^^^^^^^^^^ @@ -95,7 +95,7 @@ LL | final const FOO: usize = 1; | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:46:1 + --> $DIR/positions.rs:49:1 | LL | final fn foo() {} | -----^^^^^^^^^ @@ -103,7 +103,7 @@ LL | final fn foo() {} | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:49:1 + --> $DIR/positions.rs:52:1 | LL | final type FooTy = (); | -----^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | final type FooTy = (); | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:52:1 + --> $DIR/positions.rs:55:1 | LL | final const FOO: usize = 0; | -----^^^^^^^^^^^^^^^^^^^^^^ @@ -119,7 +119,7 @@ LL | final const FOO: usize = 0; | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:58:5 + --> $DIR/positions.rs:61:5 | LL | final fn foo_extern(); | -----^^^^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | final fn foo_extern(); | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:61:5 + --> $DIR/positions.rs:64:5 | LL | final type FooExtern; | -----^^^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | final type FooExtern; | `final` because of this error: incorrect `static` inside `extern` block - --> $DIR/positions.rs:64:18 + --> $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 @@ -147,5 +147,41 @@ LL | final static FOO_EXTERN: usize = 0; | = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html -error: aborting due to 18 previous errors +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 000000000000..e756521701a4 --- /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(); +}