Skip to content

Commit 02d7db1

Browse files
committed
Auto merge of #146348 - jdonszelmann:eiiv3, r=lcnr,oli-obk
Externally implementable items Supersedes #140010 Tracking issue: #125418 Getting started: ```rust #![feature(eii)] #[eii(eii1)] pub fn decl1(x: u64) // body optional (it's the default) { println!("default {x}"); } // in another crate, maybe #[eii1] pub fn decl2(x: u64) { println!("explicit {x}"); } fn main() { decl1(4); } ``` - tiny perf regression, underlying issue makes multiple things in the compiler slow, not just EII, planning to solve those separately. - No codegen_gcc support, they don't have bindings for weak symbols yet but could - No windows support yet for weak definitions This PR merges the implementation of EII for just llvm + not windows, doesn't yet contain like a new panic handler implementation or alloc handler. With this implementation, it would support implementing the panic handler in terms of EII already since it requires no default implementation so no weak symbols The PR has been open in various forms for about a year now, but I feel that having some implementation merged to build upon
2 parents 4ad239f + cbd6653 commit 02d7db1

File tree

131 files changed

+3300
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+3300
-53
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,19 @@ pub struct MacroDef {
20902090
pub body: Box<DelimArgs>,
20912091
/// `true` if macro was defined with `macro_rules`.
20922092
pub macro_rules: bool,
2093+
2094+
/// If this is a macro used for externally implementable items,
2095+
/// it refers to an extern item which is its "target". This requires
2096+
/// name resolution so can't just be an attribute, so we store it in this field.
2097+
pub eii_extern_target: Option<EiiExternTarget>,
2098+
}
2099+
2100+
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
2101+
pub struct EiiExternTarget {
2102+
/// path to the extern item we're targetting
2103+
pub extern_item_path: Path,
2104+
pub impl_unsafe: bool,
2105+
pub span: Span,
20932106
}
20942107

20952108
#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
@@ -3729,6 +3742,21 @@ pub struct Fn {
37293742
pub contract: Option<Box<FnContract>>,
37303743
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
37313744
pub body: Option<Box<Block>>,
3745+
3746+
/// This function is an implementation of an externally implementable item (EII).
3747+
/// This means, there was an EII declared somewhere and this function is the
3748+
/// implementation that should be run when the declaration is called.
3749+
pub eii_impls: ThinVec<EiiImpl>,
3750+
}
3751+
3752+
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
3753+
pub struct EiiImpl {
3754+
pub node_id: NodeId,
3755+
pub eii_macro_path: Path,
3756+
pub impl_safety: Safety,
3757+
pub span: Span,
3758+
pub inner_span: Span,
3759+
pub is_default: bool,
37323760
}
37333761

37343762
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
@@ -4095,7 +4123,7 @@ mod size_asserts {
40954123
static_assert_size!(Block, 32);
40964124
static_assert_size!(Expr, 72);
40974125
static_assert_size!(ExprKind, 40);
4098-
static_assert_size!(Fn, 184);
4126+
static_assert_size!(Fn, 192);
40994127
static_assert_size!(ForeignItem, 80);
41004128
static_assert_size!(ForeignItemKind, 16);
41014129
static_assert_size!(GenericArg, 24);

compiler/rustc_ast/src/visit.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ macro_rules! common_visitor_and_walkers {
393393
ThinVec<Pat>,
394394
ThinVec<Box<Ty>>,
395395
ThinVec<TyPat>,
396+
ThinVec<EiiImpl>,
396397
);
397398

398399
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
@@ -485,6 +486,8 @@ macro_rules! common_visitor_and_walkers {
485486
WhereEqPredicate,
486487
WhereRegionPredicate,
487488
YieldKind,
489+
EiiExternTarget,
490+
EiiImpl,
488491
);
489492

490493
/// Each method of this trait is a hook to be potentially
@@ -919,13 +922,13 @@ macro_rules! common_visitor_and_walkers {
919922
_ctxt,
920923
// Visibility is visited as a part of the item.
921924
_vis,
922-
Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
925+
Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls },
923926
) => {
924927
let FnSig { header, decl, span } = sig;
925928
visit_visitable!($($mut)? vis,
926929
defaultness, ident, header, generics, decl,
927-
contract, body, span, define_opaque
928-
)
930+
contract, body, span, define_opaque, eii_impls
931+
);
929932
}
930933
FnKind::Closure(binder, coroutine_kind, decl, body) =>
931934
visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustc_abi::ExternAbi;
22
use rustc_ast::visit::AssocCtxt;
33
use rustc_ast::*;
44
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
5-
use rustc_hir::attrs::AttributeKind;
5+
use rustc_hir::attrs::{AttributeKind, EiiDecl};
66
use rustc_hir::def::{DefKind, PerNS, Res};
77
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
88
use rustc_hir::{
@@ -11,6 +11,7 @@ use rustc_hir::{
1111
use rustc_index::{IndexSlice, IndexVec};
1212
use rustc_middle::span_bug;
1313
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
14+
use rustc_span::def_id::DefId;
1415
use rustc_span::edit_distance::find_best_match_for_name;
1516
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
1617
use smallvec::{SmallVec, smallvec};
@@ -133,17 +134,103 @@ impl<'hir> LoweringContext<'_, 'hir> {
133134
}
134135
}
135136

137+
fn generate_extra_attrs_for_item_kind(
138+
&mut self,
139+
id: NodeId,
140+
i: &ItemKind,
141+
) -> Vec<hir::Attribute> {
142+
match i {
143+
ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(),
144+
ItemKind::Fn(box Fn { eii_impls, .. }) => {
145+
vec![hir::Attribute::Parsed(AttributeKind::EiiImpls(
146+
eii_impls
147+
.iter()
148+
.flat_map(
149+
|EiiImpl {
150+
node_id,
151+
eii_macro_path,
152+
impl_safety,
153+
span,
154+
inner_span,
155+
is_default,
156+
}| {
157+
self.lower_path_simple_eii(*node_id, eii_macro_path).map(|did| {
158+
hir::attrs::EiiImpl {
159+
eii_macro: did,
160+
span: self.lower_span(*span),
161+
inner_span: self.lower_span(*inner_span),
162+
impl_marked_unsafe: self
163+
.lower_safety(*impl_safety, hir::Safety::Safe)
164+
.is_unsafe(),
165+
is_default: *is_default,
166+
}
167+
})
168+
},
169+
)
170+
.collect(),
171+
))]
172+
}
173+
ItemKind::MacroDef(
174+
_,
175+
MacroDef {
176+
eii_extern_target: Some(EiiExternTarget { extern_item_path, impl_unsafe, span }),
177+
..
178+
},
179+
) => self
180+
.lower_path_simple_eii(id, extern_item_path)
181+
.map(|did| {
182+
vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(EiiDecl {
183+
eii_extern_target: did,
184+
impl_unsafe: *impl_unsafe,
185+
span: self.lower_span(*span),
186+
}))]
187+
})
188+
.unwrap_or_default(),
189+
ItemKind::ExternCrate(..)
190+
| ItemKind::Use(..)
191+
| ItemKind::Static(..)
192+
| ItemKind::Const(..)
193+
| ItemKind::Mod(..)
194+
| ItemKind::ForeignMod(..)
195+
| ItemKind::GlobalAsm(..)
196+
| ItemKind::TyAlias(..)
197+
| ItemKind::Enum(..)
198+
| ItemKind::Struct(..)
199+
| ItemKind::Union(..)
200+
| ItemKind::Trait(..)
201+
| ItemKind::TraitAlias(..)
202+
| ItemKind::Impl(..)
203+
| ItemKind::MacCall(..)
204+
| ItemKind::MacroDef(..)
205+
| ItemKind::Delegation(..)
206+
| ItemKind::DelegationMac(..) => Vec::new(),
207+
}
208+
}
209+
136210
fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
137211
let vis_span = self.lower_span(i.vis.span);
138212
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
139-
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_ast_item(i));
213+
214+
let extra_hir_attributes = self.generate_extra_attrs_for_item_kind(i.id, &i.kind);
215+
let attrs = self.lower_attrs_with_extra(
216+
hir_id,
217+
&i.attrs,
218+
i.span,
219+
Target::from_ast_item(i),
220+
&extra_hir_attributes,
221+
);
222+
140223
let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind);
141224
let item = hir::Item {
142225
owner_id: hir_id.expect_owner(),
143226
kind,
144227
vis_span,
145228
span: self.lower_span(i.span),
146229
has_delayed_lints: !self.delayed_lints.is_empty(),
230+
eii: find_attr!(
231+
attrs,
232+
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
233+
),
147234
};
148235
self.arena.alloc(item)
149236
}
@@ -435,7 +522,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
435522
);
436523
hir::ItemKind::TraitAlias(constness, ident, generics, bounds)
437524
}
438-
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
525+
ItemKind::MacroDef(ident, MacroDef { body, macro_rules, eii_extern_target: _ }) => {
439526
let ident = self.lower_ident(*ident);
440527
let body = Box::new(self.lower_delim_args(body));
441528
let def_id = self.local_def_id(id);
@@ -446,7 +533,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
446533
def_kind.descr(def_id.to_def_id())
447534
);
448535
};
449-
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
536+
let macro_def = self.arena.alloc(ast::MacroDef {
537+
body,
538+
macro_rules: *macro_rules,
539+
eii_extern_target: None,
540+
});
450541
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
451542
}
452543
ItemKind::Delegation(box delegation) => {
@@ -465,6 +556,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
465556
}
466557
}
467558

559+
fn lower_path_simple_eii(&mut self, id: NodeId, path: &Path) -> Option<DefId> {
560+
let res = self.resolver.get_partial_res(id)?;
561+
let Some(did) = res.expect_full_res().opt_def_id() else {
562+
self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
563+
return None;
564+
};
565+
566+
Some(did)
567+
}
568+
468569
#[instrument(level = "debug", skip(self))]
469570
fn lower_use_tree(
470571
&mut self,
@@ -573,6 +674,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
573674
vis_span,
574675
span: this.lower_span(use_tree.span),
575676
has_delayed_lints: !this.delayed_lints.is_empty(),
677+
eii: find_attr!(
678+
attrs,
679+
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
680+
),
576681
};
577682
hir::OwnerNode::Item(this.arena.alloc(item))
578683
});

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -958,11 +958,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
958958
target_span: Span,
959959
target: Target,
960960
) -> &'hir [hir::Attribute] {
961-
if attrs.is_empty() {
961+
self.lower_attrs_with_extra(id, attrs, target_span, target, &[])
962+
}
963+
964+
fn lower_attrs_with_extra(
965+
&mut self,
966+
id: HirId,
967+
attrs: &[Attribute],
968+
target_span: Span,
969+
target: Target,
970+
extra_hir_attributes: &[hir::Attribute],
971+
) -> &'hir [hir::Attribute] {
972+
if attrs.is_empty() && extra_hir_attributes.is_empty() {
962973
&[]
963974
} else {
964-
let lowered_attrs =
975+
let mut lowered_attrs =
965976
self.lower_attrs_vec(attrs, self.lower_span(target_span), id, target);
977+
lowered_attrs.extend(extra_hir_attributes.iter().cloned());
966978

967979
assert_eq!(id.owner, self.current_hir_id_owner);
968980
let ret = self.arena.alloc_from_iter(lowered_attrs);

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,11 +1171,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11711171
contract: _,
11721172
body,
11731173
define_opaque: _,
1174+
eii_impls,
11741175
},
11751176
) => {
11761177
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
11771178
self.check_defaultness(item.span, *defaultness);
11781179

1180+
for EiiImpl { eii_macro_path, .. } in eii_impls {
1181+
self.visit_path(eii_macro_path);
1182+
}
1183+
11791184
let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
11801185
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
11811186
self.dcx().emit_err(errors::FnWithoutBody {

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,17 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
852852
sp: Span,
853853
print_visibility: impl FnOnce(&mut Self),
854854
) {
855+
if let Some(eii_extern_target) = &macro_def.eii_extern_target {
856+
self.word("#[eii_extern_target(");
857+
self.print_path(&eii_extern_target.extern_item_path, false, 0);
858+
if eii_extern_target.impl_unsafe {
859+
self.word(",");
860+
self.space();
861+
self.word("unsafe");
862+
}
863+
self.word(")]");
864+
self.hardbreak();
865+
}
855866
let (kw, has_bang) = if macro_def.macro_rules {
856867
("macro_rules", true)
857868
} else {
@@ -2148,6 +2159,15 @@ impl<'a> State<'a> {
21482159

21492160
fn print_meta_item(&mut self, item: &ast::MetaItem) {
21502161
let ib = self.ibox(INDENT_UNIT);
2162+
2163+
match item.unsafety {
2164+
ast::Safety::Unsafe(_) => {
2165+
self.word("unsafe");
2166+
self.popen();
2167+
}
2168+
ast::Safety::Default | ast::Safety::Safe(_) => {}
2169+
}
2170+
21512171
match &item.kind {
21522172
ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
21532173
ast::MetaItemKind::NameValue(value) => {
@@ -2163,6 +2183,12 @@ impl<'a> State<'a> {
21632183
self.pclose();
21642184
}
21652185
}
2186+
2187+
match item.unsafety {
2188+
ast::Safety::Unsafe(_) => self.pclose(),
2189+
ast::Safety::Default | ast::Safety::Safe(_) => {}
2190+
}
2191+
21662192
self.end(ib);
21672193
}
21682194

compiler/rustc_ast_pretty/src/pprust/state/item.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use ast::StaticItem;
22
use itertools::{Itertools, Position};
3-
use rustc_ast::{self as ast, ModKind, TraitAlias};
3+
use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias};
44
use rustc_span::Ident;
55

66
use crate::pp::BoxMarker;
@@ -671,10 +671,25 @@ impl<'a> State<'a> {
671671
}
672672

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

676677
self.print_define_opaques(define_opaque.as_deref());
677678

679+
for EiiImpl { eii_macro_path, impl_safety, .. } in eii_impls {
680+
self.word("#[");
681+
if let Safety::Unsafe(..) = impl_safety {
682+
self.word("unsafe");
683+
self.popen();
684+
}
685+
self.print_path(eii_macro_path, false, 0);
686+
if let Safety::Unsafe(..) = impl_safety {
687+
self.pclose();
688+
}
689+
self.word("]");
690+
self.hardbreak();
691+
}
692+
678693
let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));
679694

680695
self.print_visibility(vis);

compiler/rustc_builtin_macros/messages.ftl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
152152
153153
builtin_macros_duplicate_macro_attribute = duplicated attribute
154154
155+
builtin_macros_eii_extern_target_expected_list = `#[eii_extern_target(...)]` expects a list of one or two elements
156+
builtin_macros_eii_extern_target_expected_macro = `#[eii_extern_target(...)]` is only valid on macros
157+
builtin_macros_eii_extern_target_expected_unsafe = expected this argument to be "unsafe"
158+
.note = the second argument is optional
159+
160+
builtin_macros_eii_only_once = `#[{$name}]` can only be specified once
161+
.note = specified again here
162+
163+
builtin_macros_eii_shared_macro_expected_function = `#[{$name}]` is only valid on functions
164+
builtin_macros_eii_shared_macro_expected_max_one_argument = `#[{$name}]` expected no arguments or a single argument: `#[{$name}(default)]`
165+
155166
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
156167
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
157168
.cargo_typo = there is a similar Cargo environment variable: `{$suggested_var}`

0 commit comments

Comments
 (0)