Skip to content
Draft
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
30 changes: 29 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2066,6 +2066,19 @@ pub struct MacroDef {
pub body: Box<DelimArgs>,
/// `true` if macro was defined with `macro_rules`.
pub macro_rules: bool,

/// If this is a macro used for externally implementable items,
/// it refers to an extern item which is its "target". This requires
/// name resolution so can't just be an attribute, so we store it in this field.
pub eii_extern_target: Option<EiiExternTarget>,
}

#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
pub struct EiiExternTarget {
/// path to the extern item we're targetting
pub extern_item_path: Path,
pub impl_unsafe: bool,
pub span: Span,
}

#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
Expand Down Expand Up @@ -3714,6 +3727,21 @@ pub struct Fn {
pub contract: Option<Box<FnContract>>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
pub body: Option<Box<Block>>,

/// This function is an implementation of an externally implementable item (EII).
/// This means, there was an EII declared somewhere and this function is the
/// implementation that should be run when the declaration is called.
pub eii_impls: ThinVec<EiiImpl>,
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct EiiImpl {
pub node_id: NodeId,
pub eii_macro_path: Path,
pub impl_safety: Safety,
pub span: Span,
pub inner_span: Span,
pub is_default: bool,
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
Expand Down Expand Up @@ -4061,7 +4089,7 @@ mod size_asserts {
static_assert_size!(Block, 32);
static_assert_size!(Expr, 72);
static_assert_size!(ExprKind, 40);
static_assert_size!(Fn, 184);
static_assert_size!(Fn, 192);
static_assert_size!(ForeignItem, 80);
static_assert_size!(ForeignItemKind, 16);
static_assert_size!(GenericArg, 24);
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ macro_rules! common_visitor_and_walkers {
ThinVec<Box<Pat>>,
ThinVec<Box<Ty>>,
ThinVec<Box<TyPat>>,
ThinVec<EiiImpl>,
);

// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
Expand Down Expand Up @@ -485,6 +486,8 @@ macro_rules! common_visitor_and_walkers {
WhereEqPredicate,
WhereRegionPredicate,
YieldKind,
EiiExternTarget,
EiiImpl,
);

/// Each method of this trait is a hook to be potentially
Expand Down Expand Up @@ -914,13 +917,13 @@ macro_rules! common_visitor_and_walkers {
_ctxt,
// Visibility is visited as a part of the item.
_vis,
Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls },
) => {
let FnSig { header, decl, span } = sig;
visit_visitable!($($mut)? vis,
defaultness, ident, header, generics, decl,
contract, body, span, define_opaque
)
contract, body, span, define_opaque, eii_impls
);
}
FnKind::Closure(binder, coroutine_kind, decl, body) =>
visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),
Expand Down
113 changes: 109 additions & 4 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use rustc_abi::ExternAbi;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::attrs::{AttributeKind, EiiDecl};
use rustc_hir::def::{DefKind, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{
Expand All @@ -11,6 +11,7 @@ use rustc_hir::{
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
use smallvec::{SmallVec, smallvec};
Expand Down Expand Up @@ -135,17 +136,103 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}

fn generate_extra_attrs_for_item_kind(
&mut self,
id: NodeId,
i: &ItemKind,
) -> Vec<hir::Attribute> {
match i {
ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(),
ItemKind::Fn(box Fn { eii_impls, .. }) => {
vec![hir::Attribute::Parsed(AttributeKind::EiiImpls(
eii_impls
.iter()
.flat_map(
|EiiImpl {
node_id,
eii_macro_path,
impl_safety,
span,
inner_span,
is_default,
}| {
self.lower_path_simple_eii(*node_id, eii_macro_path).map(|did| {
hir::attrs::EiiImpl {
eii_macro: did,
span: self.lower_span(*span),
inner_span: self.lower_span(*inner_span),
impl_marked_unsafe: self
.lower_safety(*impl_safety, hir::Safety::Safe)
.is_unsafe(),
is_default: *is_default,
}
})
},
)
.collect(),
))]
}
ItemKind::MacroDef(
_,
MacroDef {
eii_extern_target: Some(EiiExternTarget { extern_item_path, impl_unsafe, span }),
..
},
) => self
.lower_path_simple_eii(id, extern_item_path)
.map(|did| {
vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(EiiDecl {
eii_extern_target: did,
impl_unsafe: *impl_unsafe,
span: self.lower_span(*span),
}))]
})
.unwrap_or_default(),
ItemKind::ExternCrate(..)
| ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(..)
| ItemKind::GlobalAsm(..)
| ItemKind::TyAlias(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Trait(..)
| ItemKind::TraitAlias(..)
| ItemKind::Impl(..)
| ItemKind::MacCall(..)
| ItemKind::MacroDef(..)
| ItemKind::Delegation(..)
| ItemKind::DelegationMac(..) => Vec::new(),
}
}

fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
let vis_span = self.lower_span(i.vis.span);
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_ast_item(i));

let extra_hir_attributes = self.generate_extra_attrs_for_item_kind(i.id, &i.kind);
let attrs = self.lower_attrs_with_extra(
hir_id,
&i.attrs,
i.span,
Target::from_ast_item(i),
&extra_hir_attributes,
);

let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind);
let item = hir::Item {
owner_id: hir_id.expect_owner(),
kind,
vis_span,
span: self.lower_span(i.span),
has_delayed_lints: !self.delayed_lints.is_empty(),
eii: find_attr!(
attrs,
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
),
};
self.arena.alloc(item)
}
Expand Down Expand Up @@ -433,7 +520,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
hir::ItemKind::TraitAlias(ident, generics, bounds)
}
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
ItemKind::MacroDef(ident, MacroDef { body, macro_rules, eii_extern_target: _ }) => {
let ident = self.lower_ident(*ident);
let body = Box::new(self.lower_delim_args(body));
let def_id = self.local_def_id(id);
Expand All @@ -444,7 +531,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
def_kind.descr(def_id.to_def_id())
);
};
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
let macro_def = self.arena.alloc(ast::MacroDef {
body,
macro_rules: *macro_rules,
eii_extern_target: None,
});
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
}
ItemKind::Delegation(box delegation) => {
Expand All @@ -463,6 +554,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}

fn lower_path_simple_eii(&mut self, id: NodeId, path: &Path) -> Option<DefId> {
let res = self.resolver.get_partial_res(id)?;
let Some(did) = res.expect_full_res().opt_def_id() else {
self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
return None;
};

Some(did)
}

fn lower_const_item(
&mut self,
ty: &Ty,
Expand Down Expand Up @@ -582,6 +683,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
vis_span,
span: this.lower_span(use_tree.span),
has_delayed_lints: !this.delayed_lints.is_empty(),
eii: find_attr!(
attrs,
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
),
};
hir::OwnerNode::Item(this.arena.alloc(item))
});
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -945,11 +945,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
target_span: Span,
target: Target,
) -> &'hir [hir::Attribute] {
if attrs.is_empty() {
self.lower_attrs_with_extra(id, attrs, target_span, target, &[])
}

fn lower_attrs_with_extra(
&mut self,
id: HirId,
attrs: &[Attribute],
target_span: Span,
target: Target,
extra_hir_attributes: &[hir::Attribute],
) -> &'hir [hir::Attribute] {
if attrs.is_empty() && extra_hir_attributes.is_empty() {
&[]
} else {
let lowered_attrs =
let mut lowered_attrs =
self.lower_attrs_vec(attrs, self.lower_span(target_span), id, target);
lowered_attrs.extend(extra_hir_attributes.iter().cloned());

assert_eq!(id.owner, self.current_hir_id_owner);
let ret = self.arena.alloc_from_iter(lowered_attrs);
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,11 +1084,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
contract: _,
body,
define_opaque: _,
eii_impls,
},
) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.check_defaultness(item.span, *defaultness);

for EiiImpl { eii_macro_path, .. } in eii_impls {
self.visit_path(eii_macro_path);
}

let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
self.dcx().emit_err(errors::FnWithoutBody {
Expand Down
26 changes: 26 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,17 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
sp: Span,
print_visibility: impl FnOnce(&mut Self),
) {
if let Some(eii_extern_target) = &macro_def.eii_extern_target {
self.word("#[eii_extern_target(");
self.print_path(&eii_extern_target.extern_item_path, false, 0);
if eii_extern_target.impl_unsafe {
self.word(",");
self.space();
self.word("unsafe");
}
self.word(")]");
self.hardbreak();
}
let (kw, has_bang) = if macro_def.macro_rules {
("macro_rules", true)
} else {
Expand Down Expand Up @@ -2141,6 +2152,15 @@ impl<'a> State<'a> {

fn print_meta_item(&mut self, item: &ast::MetaItem) {
let ib = self.ibox(INDENT_UNIT);

match item.unsafety {
ast::Safety::Unsafe(_) => {
self.word("unsafe");
self.popen();
}
ast::Safety::Default | ast::Safety::Safe(_) => {}
}

match &item.kind {
ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
ast::MetaItemKind::NameValue(value) => {
Expand All @@ -2156,6 +2176,12 @@ impl<'a> State<'a> {
self.pclose();
}
}

match item.unsafety {
ast::Safety::Unsafe(_) => self.pclose(),
ast::Safety::Default | ast::Safety::Safe(_) => {}
}

self.end(ib);
}

Expand Down
20 changes: 17 additions & 3 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use ast::StaticItem;
use itertools::{Itertools, Position};
use rustc_ast as ast;
use rustc_ast::ModKind;
use rustc_ast::{self as ast, EiiImpl, ModKind, Safety};
use rustc_span::Ident;

use crate::pp::BoxMarker;
Expand Down Expand Up @@ -675,10 +674,25 @@ impl<'a> State<'a> {
}

fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) {
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque } = func;
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impls } =
func;

self.print_define_opaques(define_opaque.as_deref());

for EiiImpl { eii_macro_path, impl_safety, .. } in eii_impls {
self.word("#[");
if let Safety::Unsafe(..) = impl_safety {
self.word("unsafe");
self.popen();
}
self.print_path(eii_macro_path, false, 0);
if let Safety::Unsafe(..) = impl_safety {
self.pclose();
}
self.word("]");
self.hardbreak();
}

let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));

self.print_visibility(vis);
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,10 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal
.other = for example, write `#[derive(Debug)]` for `Debug`

builtin_macros_unnameable_test_items = cannot test inner items

builtin_macros_eii_shared_macro_expected_function = `#[{$name}]` is only valid on functions
builtin_macros_eii_extern_target_expected_list = `#[eii_extern_target(...)]` expects a list of one or two elements
builtin_macros_eii_extern_target_expected_macro = `#[eii_extern_target(...)]` is only valid on macros
builtin_macros_eii_shared_macro_expected_max_one_argument = `#[{$name}]` expected no arguments or a single argument: `#[{$name}(default)]`
builtin_macros_eii_extern_target_expected_unsafe = expected this argument to be "unsafe"
.note = the second argument is optional
Loading
Loading