Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3135,8 +3135,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)]
Expand Down Expand Up @@ -4127,7 +4135,7 @@ impl AssocItemKind {
| Self::Fn(box Fn { defaultness, .. })
| Self::Type(box TyAlias { defaultness, .. }) => defaultness,
Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => {
Defaultness::Final
Defaultness::Implicit
}
}
}
Expand Down
21 changes: 13 additions & 8 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,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, ..
}) => {
Expand Down Expand Up @@ -1074,13 +1074,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)
Expand Down Expand Up @@ -1108,7 +1112,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,
Expand Down Expand Up @@ -1137,7 +1142,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,
Expand Down Expand Up @@ -1285,15 +1291,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))),
}
}

Expand Down
78 changes: 66 additions & 12 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 });
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,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) {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/alloc_error_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/autodiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,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(),
Expand Down Expand Up @@ -850,7 +850,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 },
Expand Down Expand Up @@ -1072,7 +1072,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 {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/global_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/test_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,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 {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ impl<'a> ExtCtxt<'a> {
ty: Box<ast::Ty>,
rhs: ast::ConstItemRhs,
) -> Box<ast::Item> {
let defaultness = ast::Defaultness::Final;
let defaultness = ast::Defaultness::Implicit;
self.item(
span,
AttrVec::new(),
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,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
Expand Down
Loading
Loading