Rollup merge of #151783 - mu001999-contrib:impl/final-method, r=fee1-dead
Implement RFC 3678: Final trait methods Tracking: https://github.com/rust-lang/rust/issues/131179 This PR is based on rust-lang/rust#130802, with some minor changes and conflict resolution. Futhermore, this PR excludes final methods from the vtable of a dyn Trait. And some excerpt from the original PR description: > Implements the surface part of https://github.com/rust-lang/rfcs/pull/3678. > > I'm using the word "method" in the title, but in the diagnostics and the feature gate I used "associated function", since that's more accurate. cc @joshtriplett
This commit is contained in:
commit
1367126837
57 changed files with 755 additions and 117 deletions
|
|
@ -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)]
|
||||
|
|
@ -4140,7 +4148,7 @@ impl AssocItemKind {
|
|||
| Self::Fn(box Fn { defaultness, .. })
|
||||
| Self::Type(box TyAlias { defaultness, .. }) => defaultness,
|
||||
Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => {
|
||||
Defaultness::Final
|
||||
Defaultness::Implicit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -1088,13 +1088,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)
|
||||
|
|
@ -1122,7 +1126,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,
|
||||
|
|
@ -1151,7 +1156,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,
|
||||
|
|
@ -1304,15 +1310,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
&self,
|
||||
d: Defaultness,
|
||||
has_value: bool,
|
||||
implicit: impl FnOnce() -> hir::Defaultness,
|
||||
) -> (hir::Defaultness, Option<Span>) {
|
||||
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))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<AssocItemKind>, 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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1190,7 +1234,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);
|
||||
|
|
@ -1360,7 +1404,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
});
|
||||
}
|
||||
ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => {
|
||||
self.check_defaultness(item.span, *defaultness);
|
||||
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
|
||||
if !rhs_kind.has_expr() {
|
||||
self.dcx().emit_err(errors::ConstWithoutBody {
|
||||
span: item.span,
|
||||
|
|
@ -1398,7 +1442,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,
|
||||
|
|
@ -1428,7 +1472,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);
|
||||
|
|
@ -1448,7 +1492,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);
|
||||
|
|
@ -1707,9 +1751,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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -580,6 +580,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) {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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"))),
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box<ast::Item> {
|
|||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -729,7 +729,7 @@ impl<'a> ExtCtxt<'a> {
|
|||
ty: Box<ast::Ty>,
|
||||
rhs_kind: ast::ConstItemRhsKind,
|
||||
) -> Box<ast::Item> {
|
||||
let defaultness = ast::Defaultness::Final;
|
||||
let defaultness = ast::Defaultness::Implicit;
|
||||
self.item(
|
||||
span,
|
||||
AttrVec::new(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -3787,6 +3787,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 `::`")]
|
||||
|
|
|
|||
|
|
@ -206,14 +206,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 => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,8 +239,8 @@ impl<'a> Parser<'a> {
|
|||
fn_parse_mode: FnParseMode,
|
||||
case: Case,
|
||||
) -> PResult<'a, Option<ItemKind>> {
|
||||
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()?
|
||||
|
|
@ -1005,8 +1015,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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1130,7 +1143,7 @@ impl<'a> Parser<'a> {
|
|||
}) => {
|
||||
self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span });
|
||||
AssocItemKind::Const(Box::new(ConstItem {
|
||||
defaultness: Defaultness::Final,
|
||||
defaultness: Defaultness::Implicit,
|
||||
ident,
|
||||
generics: Generics::default(),
|
||||
ty,
|
||||
|
|
|
|||
|
|
@ -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) };
|
||||
|
|
|
|||
|
|
@ -1096,6 +1096,7 @@ symbols! {
|
|||
fields,
|
||||
file,
|
||||
file_options,
|
||||
final_associated_functions,
|
||||
flags,
|
||||
float,
|
||||
float_to_int_unchecked,
|
||||
|
|
|
|||
|
|
@ -314,6 +314,11 @@ pub fn dyn_compatibility_violations_for_assoc_item(
|
|||
trait_def_id: DefId,
|
||||
item: ty::AssocItem,
|
||||
) -> Vec<DynCompatibilityViolation> {
|
||||
// `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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue