Skip to content

Commit 5fcdb52

Browse files
Analysis for externally implementable statics
1 parent 2836226 commit 5fcdb52

9 files changed

Lines changed: 303 additions & 103 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,8 @@ pub(crate) struct RustcEiiForeignItemParser;
715715
impl<S: Stage> NoArgsAttributeParser<S> for RustcEiiForeignItemParser {
716716
const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
717717
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
718-
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
718+
const ALLOWED_TARGETS: AllowedTargets =
719+
AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
719720
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
720721
}
721722

compiler/rustc_builtin_macros/src/eii.rs

Lines changed: 100 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use thin_vec::{ThinVec, thin_vec};
1010

1111
use crate::errors::{
1212
EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe,
13-
EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction,
14-
EiiSharedMacroInStatementPosition,
13+
EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition,
14+
EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault,
1515
};
1616

1717
/// ```rust
@@ -73,44 +73,63 @@ fn eii_(
7373
});
7474
return vec![orig_item];
7575
} else {
76-
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
76+
ecx.dcx().emit_err(EiiSharedMacroTarget {
7777
span: eii_attr_span,
7878
name: path_to_string(&meta_item.path),
7979
});
8080
return vec![orig_item];
8181
};
8282

83-
let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } =
84-
item.as_ref()
85-
else {
86-
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
87-
span: eii_attr_span,
88-
name: path_to_string(&meta_item.path),
89-
});
90-
return vec![Annotatable::Item(item)];
83+
let ast::Item { attrs, id: _, span: _, vis, kind, tokens: _ } = item.as_ref();
84+
let (item_span, foreign_item_name) = match kind {
85+
ItemKind::Fn(func) => (func.sig.span, func.ident),
86+
ItemKind::Static(stat) => {
87+
// Statics with a default are not supported yet
88+
if let Some(stat_body) = &stat.expr {
89+
ecx.dcx().emit_err(EiiStaticDefault {
90+
span: stat_body.span,
91+
name: path_to_string(&meta_item.path),
92+
});
93+
return vec![];
94+
}
95+
// Statics must have an explicit name for the eii
96+
if meta_item.is_word() {
97+
ecx.dcx().emit_err(EiiStaticArgumentRequired {
98+
span: eii_attr_span,
99+
name: path_to_string(&meta_item.path),
100+
});
101+
return vec![];
102+
}
103+
(item.span, stat.ident)
104+
}
105+
_ => {
106+
ecx.dcx().emit_err(EiiSharedMacroTarget {
107+
span: eii_attr_span,
108+
name: path_to_string(&meta_item.path),
109+
});
110+
return vec![Annotatable::Item(item)];
111+
}
91112
};
113+
92114
// only clone what we need
93115
let attrs = attrs.clone();
94-
let func = (**func).clone();
95116
let vis = vis.clone();
96117

97118
let attrs_from_decl =
98119
filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path);
99120

100-
let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else {
121+
let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else {
101122
// we don't need to wrap in Annotatable::Stmt conditionally since
102123
// EII can't be used on items in statement position
103124
return vec![Annotatable::Item(item)];
104125
};
105126

106-
// span of the declaring item without attributes
107-
let item_span = func.sig.span;
108-
let foreign_item_name = func.ident;
109-
110127
let mut module_items = Vec::new();
111128

112-
if func.body.is_some() {
113-
module_items.push(generate_default_impl(
129+
if let ItemKind::Fn(func) = kind
130+
&& func.body.is_some()
131+
{
132+
module_items.push(generate_default_func_impl(
114133
ecx,
115134
&func,
116135
impl_unsafe,
@@ -125,7 +144,7 @@ fn eii_(
125144
ecx,
126145
eii_attr_span,
127146
item_span,
128-
func,
147+
kind,
129148
vis,
130149
&attrs_from_decl,
131150
));
@@ -148,11 +167,11 @@ fn eii_(
148167
/// declaration of the EII.
149168
fn name_for_impl_macro(
150169
ecx: &mut ExtCtxt<'_>,
151-
func: &ast::Fn,
170+
item_ident: Ident,
152171
meta_item: &MetaItem,
153172
) -> Result<Ident, ErrorGuaranteed> {
154173
if meta_item.is_word() {
155-
Ok(func.ident)
174+
Ok(item_ident)
156175
} else if let Some([first]) = meta_item.meta_item_list()
157176
&& let Some(m) = first.meta_item()
158177
&& m.path.segments.len() == 1
@@ -190,7 +209,7 @@ fn filter_attrs_for_multiple_eii_attr(
190209
.collect()
191210
}
192211

193-
fn generate_default_impl(
212+
fn generate_default_func_impl(
194213
ecx: &mut ExtCtxt<'_>,
195214
func: &ast::Fn,
196215
impl_unsafe: bool,
@@ -257,7 +276,7 @@ fn generate_foreign_item(
257276
ecx: &mut ExtCtxt<'_>,
258277
eii_attr_span: Span,
259278
item_span: Span,
260-
mut func: ast::Fn,
279+
item_kind: &ItemKind,
261280
vis: Visibility,
262281
attrs_from_decl: &[Attribute],
263282
) -> Box<ast::Item> {
@@ -268,30 +287,21 @@ fn generate_foreign_item(
268287
// This attribute makes sure that we later know that this foreign item's symbol should not be.
269288
foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span));
270289

271-
let abi = match func.sig.header.ext {
272-
// extern "X" fn => extern "X" {}
273-
ast::Extern::Explicit(lit, _) => Some(lit),
274-
// extern fn => extern {}
275-
ast::Extern::Implicit(_) => None,
276-
// fn => extern "Rust" {}
277-
ast::Extern::None => Some(ast::StrLit {
278-
symbol: sym::Rust,
279-
suffix: None,
280-
symbol_unescaped: sym::Rust,
281-
style: ast::StrStyle::Cooked,
282-
span: eii_attr_span,
283-
}),
290+
// We set the abi to the default "rust" abi, which can be overridden by `generate_foreign_func`,
291+
// if a specific abi was specified on the EII function
292+
let mut abi = Some(ast::StrLit {
293+
symbol: sym::Rust,
294+
suffix: None,
295+
symbol_unescaped: sym::Rust,
296+
style: ast::StrStyle::Cooked,
297+
span: eii_attr_span,
298+
});
299+
let foreign_kind = match item_kind {
300+
ItemKind::Fn(func) => generate_foreign_func(func.clone(), &mut abi),
301+
ItemKind::Static(stat) => generate_foreign_static(stat.clone()),
302+
_ => unreachable!("Target was checked earlier"),
284303
};
285304

286-
// ABI has been moved to the extern {} block, so we remove it from the fn item.
287-
func.sig.header.ext = ast::Extern::None;
288-
func.body = None;
289-
290-
// And mark safe functions explicitly as `safe fn`.
291-
if func.sig.header.safety == ast::Safety::Default {
292-
func.sig.header.safety = ast::Safety::Safe(func.sig.span);
293-
}
294-
295305
ecx.item(
296306
eii_attr_span,
297307
ThinVec::new(),
@@ -304,13 +314,46 @@ fn generate_foreign_item(
304314
id: ast::DUMMY_NODE_ID,
305315
span: item_span,
306316
vis,
307-
kind: ast::ForeignItemKind::Fn(Box::new(func.clone())),
317+
kind: foreign_kind,
308318
tokens: None,
309319
})]),
310320
}),
311321
)
312322
}
313323

324+
fn generate_foreign_func(
325+
mut func: Box<ast::Fn>,
326+
abi: &mut Option<ast::StrLit>,
327+
) -> ast::ForeignItemKind {
328+
match func.sig.header.ext {
329+
// extern "X" fn => extern "X" {}
330+
ast::Extern::Explicit(lit, _) => *abi = Some(lit),
331+
// extern fn => extern {}
332+
ast::Extern::Implicit(_) => *abi = None,
333+
// no abi was specified, so we keep the default
334+
ast::Extern::None => {}
335+
};
336+
337+
// ABI has been moved to the extern {} block, so we remove it from the fn item.
338+
func.sig.header.ext = ast::Extern::None;
339+
func.body = None;
340+
341+
// And mark safe functions explicitly as `safe fn`.
342+
if func.sig.header.safety == ast::Safety::Default {
343+
func.sig.header.safety = ast::Safety::Safe(func.sig.span);
344+
}
345+
346+
ast::ForeignItemKind::Fn(func)
347+
}
348+
349+
fn generate_foreign_static(mut stat: Box<ast::StaticItem>) -> ast::ForeignItemKind {
350+
if stat.safety == ast::Safety::Default {
351+
stat.safety = ast::Safety::Safe(stat.ident.span);
352+
}
353+
354+
ast::ForeignItemKind::Static(stat)
355+
}
356+
314357
/// Generate a stub macro (a bit like in core) that will roughly look like:
315358
///
316359
/// ```rust, ignore, example
@@ -453,19 +496,18 @@ pub(crate) fn eii_shared_macro(
453496
{
454497
item
455498
} else {
456-
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
457-
span,
458-
name: path_to_string(&meta_item.path),
459-
});
499+
ecx.dcx().emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
460500
return vec![item];
461501
};
462502

463-
let ItemKind::Fn(f) = &mut i.kind else {
464-
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
465-
span,
466-
name: path_to_string(&meta_item.path),
467-
});
468-
return vec![item];
503+
let eii_impls = match &mut i.kind {
504+
ItemKind::Fn(func) => &mut func.eii_impls,
505+
ItemKind::Static(stat) => &mut stat.eii_impls,
506+
_ => {
507+
ecx.dcx()
508+
.emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
509+
return vec![item];
510+
}
469511
};
470512

471513
let is_default = if meta_item.is_word() {
@@ -483,7 +525,7 @@ pub(crate) fn eii_shared_macro(
483525
return vec![item];
484526
};
485527

486-
f.eii_impls.push(EiiImpl {
528+
eii_impls.push(EiiImpl {
487529
node_id: DUMMY_NODE_ID,
488530
inner_span: meta_item.path.span,
489531
eii_macro_path: meta_item.path.clone(),

compiler/rustc_builtin_macros/src/errors.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,8 +1107,24 @@ pub(crate) struct EiiExternTargetExpectedUnsafe {
11071107
}
11081108

11091109
#[derive(Diagnostic)]
1110-
#[diag("`#[{$name}]` is only valid on functions")]
1111-
pub(crate) struct EiiSharedMacroExpectedFunction {
1110+
#[diag("`#[{$name}]` is only valid on functions and statics")]
1111+
pub(crate) struct EiiSharedMacroTarget {
1112+
#[primary_span]
1113+
pub span: Span,
1114+
pub name: String,
1115+
}
1116+
1117+
#[derive(Diagnostic)]
1118+
#[diag("`#[{$name}]` cannot be used on statics with a value")]
1119+
pub(crate) struct EiiStaticDefault {
1120+
#[primary_span]
1121+
pub span: Span,
1122+
pub name: String,
1123+
}
1124+
1125+
#[derive(Diagnostic)]
1126+
#[diag("`#[{$name}]` requires the name as an explicit argument when used on a static")]
1127+
pub(crate) struct EiiStaticArgumentRequired {
11121128
#[primary_span]
11131129
pub span: Span,
11141130
pub name: String,

0 commit comments

Comments
 (0)