Skip to content
Closed
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 @@ -2648,8 +2648,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)]
pub enum Defaultness {
/// `default`
Default(Span),
Final,
/// 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,
/// `final`; per RFC 3678, only trait items may be *explicitly* marked final.
Final(Span),
}

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
Expand Down Expand Up @@ -3440,7 +3448,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
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,8 @@ fn visit_nonterminal<T: MutVisitor>(vis: &mut T, nt: &mut token::Nonterminal) {
fn visit_defaultness<T: MutVisitor>(vis: &mut T, defaultness: &mut Defaultness) {
match defaultness {
Defaultness::Default(span) => vis.visit_span(span),
Defaultness::Final => {}
Defaultness::Final(span) => vis.visit_span(span),
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 @@ -386,7 +386,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 polarity = match polarity {
ImplPolarity::Positive => ImplPolarity::Positive,
ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)),
Expand Down Expand Up @@ -785,7 +786,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_attrs(hir_id, &i.attrs);
let trait_item_def_id = hir_id.expect_owner();

let (generics, kind, has_default) = match &i.kind {
let (generics, kind, has_value) = match &i.kind {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
let (generics, kind) = self.lower_generics(
generics,
Expand Down Expand Up @@ -874,13 +875,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(i.ident),
generics,
kind,
span: self.lower_span(i.span),
defaultness: hir::Defaultness::Default { has_value: has_default },
defaultness,
};
self.arena.alloc(item)
}
Expand Down Expand Up @@ -920,7 +925,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 = self.lower_node_id(i.id);
self.lower_attrs(hir_id, &i.attrs);

Expand Down Expand Up @@ -1044,15 +1050,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(sp)),
}
}

Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ ast_passes_forbidden_default =
`default` is only allowed on items in trait impls
.label = `default` because of this

ast_passes_forbidden_final =
`final` is only allowed on associated functions in traits
.label = `final` because of this

ast_passes_forbidden_final_without_body =
`final` is only allowed on associated functions if they have a body
.label = `final` because of this

ast_passes_forbidden_non_lifetime_param =
only lifetime parameters can be used in this context

Expand Down
85 changes: 73 additions & 12 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ impl TraitOrTraitImpl {
}
}

enum AllowDefault {
Yes,
No,
}

impl AllowDefault {
fn only_if(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}

enum AllowFinal {
Yes,
No,
}

impl AllowFinal {
fn only_if(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}

struct AstValidator<'a> {
session: &'a Session,
features: &'a Features,
Expand Down Expand Up @@ -470,10 +492,38 @@ impl<'a> AstValidator<'a> {
}
}

fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
if let Defaultness::Default(def_span) = defaultness {
let span = self.session.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::Implicit => {}
Defaultness::Default(def_span) => match allow_default {
AllowDefault::Yes => {}
AllowDefault::No => {
let span = self.session.source_map().guess_head_span(span);
self.dcx().emit_err(errors::ForbiddenDefault { span, def_span });
}
},
Defaultness::Final(def_span) => match allow_final {
AllowFinal::Yes => {}
AllowFinal::No => {
let span = self.session.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.session.source_map().guess_head_span(item.span);
self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span });
}
}

Expand Down Expand Up @@ -1005,7 +1055,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
return; // Avoid visiting again.
}
ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => {
self.check_defaultness(item.span, *defaultness);
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);

if body.is_none() {
self.dcx().emit_err(errors::FnWithoutBody {
Expand Down Expand Up @@ -1152,7 +1202,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
}
ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
self.check_defaultness(item.span, *defaultness);
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
if expr.is_none() {
self.dcx().emit_err(errors::ConstWithoutBody {
span: item.span,
Expand All @@ -1176,7 +1226,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, where_clauses, 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 @@ -1205,7 +1255,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, sig, body, .. }) => {
self.check_defaultness(fi.span, *defaultness);
self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No);
self.check_foreign_fn_bodyless(fi.ident, body.as_deref());
self.check_foreign_fn_headerless(sig.header);
self.check_foreign_item_ascii_only(fi.ident);
Expand All @@ -1218,7 +1268,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(fi.ident, "type", ty.as_ref().map(|b| b.span));
self.check_type_no_bounds(bounds, "`extern` blocks");
self.check_foreign_ty_genericless(generics, where_clauses);
Expand Down Expand Up @@ -1490,9 +1540,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_nomangle_item_asciionly(item.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::only_if(ctxt == AssocCtxt::Impl),
// `final` is allowed on all associated *functions* in traits.
AllowFinal::only_if(
ctxt == AssocCtxt::Trait && matches!(item.kind, AssocItemKind::Fn(..)),
),
);

//
self.check_final_has_body(item, defaultness);

if ctxt == AssocCtxt::Impl {
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 @@ -124,6 +124,24 @@ pub(crate) struct ForbiddenDefault {
pub def_span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_forbidden_final)]
pub(crate) struct ForbiddenFinal {
#[primary_span]
pub span: Span,
#[label]
pub def_span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_forbidden_final_without_body)]
pub(crate) struct ForbiddenFinalWithoutBody {
#[primary_span]
pub span: Span,
#[label]
pub def_span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_assoc_const_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 @@ -545,6 +545,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
gate_all!(global_registration, "global registration is experimental");
gate_all!(return_type_notation, "return type notation is 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 @@ -46,7 +46,7 @@ impl<'a> State<'a> {
ty,
expr.as_deref(),
vis,
ast::Defaultness::Final,
ast::Defaultness::Implicit,
)
}
ast::ForeignItemKind::TyAlias(box ast::TyAlias {
Expand Down Expand Up @@ -181,7 +181,7 @@ impl<'a> State<'a> {
ty,
body.as_deref(),
&item.vis,
ast::Defaultness::Final,
ast::Defaultness::Implicit,
);
}
ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
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,
generics: Generics::default(),
body,
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 @@ -608,7 +608,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,
generics: Generics::default(),
where_clauses: ast::TyAliasWhereClauses::default(),
bounds: Vec::new(),
Expand Down Expand Up @@ -799,7 +799,7 @@ impl<'a> TraitDef<'a> {
ast::ItemKind::Impl(Box::new(ast::Impl {
safety: ast::Safety::Default,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
defaultness: ast::Defaultness::Implicit,
constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No },
generics: trait_generics,
of_trait: opt_trait_ref,
Expand Down Expand Up @@ -1026,7 +1026,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.
P(ast::AssocItem {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ pub(crate) fn expand_deriving_smart_ptr(
ast::ItemKind::Impl(Box::new(ast::Impl {
safety: ast::Safety::Default,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
defaultness: ast::Defaultness::Implicit,
constness: ast::Const::No,
generics,
of_trait: Some(trait_ref),
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 @@ -79,7 +79,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,
generics: Generics::default(),
body,
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 @@ -260,7 +260,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,
generics: ast::Generics::default(),
ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
// test::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 @@ -341,7 +341,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<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;
let main = ast::ItemKind::Fn(Box::new(ast::Fn {
defaultness,
sig,
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 @@ -642,7 +642,7 @@ impl<'a> ExtCtxt<'a> {
ty: P<ast::Ty>,
expr: P<ast::Expr>,
) -> P<ast::Item> {
let defaultness = ast::Defaultness::Final;
let defaultness = ast::Defaultness::Implicit;
self.item(
span,
name,
Expand Down
Loading