Skip to content

Commit 73f8cdd

Browse files
committed
support #[cfg(...)] on arguments to the asm! macros
1 parent 4455c89 commit 73f8cdd

File tree

10 files changed

+421
-4
lines changed

10 files changed

+421
-4
lines changed

compiler/rustc_builtin_macros/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function
22
builtin_macros_alloc_must_statics = allocators must be statics
33
4+
builtin_macros_asm_attribute_not_supported =
5+
this attribute is not supported on assembly
6+
builtin_macros_asm_cfg =
7+
the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable
8+
49
builtin_macros_asm_clobber_abi = clobber_abi
510
builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs
611
builtin_macros_asm_clobber_outputs = generic outputs

compiler/rustc_builtin_macros/src/asm.rs

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,20 @@ use rustc_index::bit_set::GrowableBitSet;
1010
use rustc_parse::exp;
1111
use rustc_parse::parser::{ExpKeywordPair, Parser};
1212
use rustc_session::lint;
13-
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw};
13+
use rustc_session::parse::feature_err;
14+
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw, sym};
1415
use rustc_target::asm::InlineAsmArch;
1516
use smallvec::smallvec;
1617
use {rustc_ast as ast, rustc_parse_format as parse};
1718

18-
use crate::errors;
1919
use crate::util::{ExprToSpannedString, expr_to_spanned_string};
20+
use crate::{errors, fluent_generated as fluent};
2021

2122
/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
2223
/// not validated at all.
2324
pub struct AsmArg {
2425
pub kind: AsmArgKind,
26+
pub attributes: AsmAttrVec,
2527
pub span: Span,
2628
}
2729

@@ -52,6 +54,44 @@ struct ValidatedAsmArgs {
5254
pub options_spans: Vec<Span>,
5355
}
5456

57+
/// A parsed list of attributes that is not attached to any item.
58+
/// Used to check whether `asm!` arguments are configured out.
59+
pub struct AsmAttrVec(pub ast::AttrVec);
60+
61+
impl AsmAttrVec {
62+
fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> {
63+
let mut attributes = ast::AttrVec::new();
64+
while p.token == token::Pound {
65+
let attr = p.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)?;
66+
attributes.push(attr);
67+
}
68+
69+
Ok(Self(attributes))
70+
}
71+
}
72+
impl ast::HasAttrs for AsmAttrVec {
73+
// Follows `ast::Expr`.
74+
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
75+
76+
fn attrs(&self) -> &[rustc_ast::Attribute] {
77+
&self.0
78+
}
79+
80+
fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) {
81+
f(&mut self.0)
82+
}
83+
}
84+
85+
impl ast::HasTokens for AsmAttrVec {
86+
fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> {
87+
None
88+
}
89+
90+
fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyAttrTokenStream>> {
91+
None
92+
}
93+
}
94+
5595
/// Used for better error messages when operand types are used that are not
5696
/// supported by the current macro (e.g. `in` or `out` for `global_asm!`)
5797
///
@@ -167,8 +207,13 @@ pub fn parse_asm_args<'a>(
167207

168208
let mut args = Vec::new();
169209

210+
let attributes = AsmAttrVec::parse(p)?;
170211
let first_template = p.parse_expr()?;
171-
args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) });
212+
args.push(AsmArg {
213+
span: first_template.span,
214+
kind: AsmArgKind::Template(first_template),
215+
attributes,
216+
});
172217

173218
let mut allow_templates = true;
174219

@@ -188,6 +233,7 @@ pub fn parse_asm_args<'a>(
188233
break;
189234
}
190235

236+
let attributes = AsmAttrVec::parse(p)?;
191237
let span_start = p.token.span;
192238

193239
// Parse `clobber_abi`.
@@ -197,6 +243,7 @@ pub fn parse_asm_args<'a>(
197243
args.push(AsmArg {
198244
kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
199245
span: span_start.to(p.prev_token.span),
246+
attributes,
200247
});
201248

202249
continue;
@@ -209,6 +256,7 @@ pub fn parse_asm_args<'a>(
209256
args.push(AsmArg {
210257
kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
211258
span: span_start.to(p.prev_token.span),
259+
attributes,
212260
});
213261

214262
continue;
@@ -231,6 +279,7 @@ pub fn parse_asm_args<'a>(
231279
args.push(AsmArg {
232280
span: span_start.to(p.prev_token.span),
233281
kind: AsmArgKind::Operand(name, op),
282+
attributes,
234283
});
235284
} else if allow_templates {
236285
let template = p.parse_expr()?;
@@ -252,7 +301,11 @@ pub fn parse_asm_args<'a>(
252301
}
253302
}
254303

255-
args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) });
304+
args.push(AsmArg {
305+
span: template.span,
306+
kind: AsmArgKind::Template(template),
307+
attributes,
308+
});
256309
} else {
257310
p.unexpected_any()?
258311
}
@@ -278,6 +331,13 @@ fn validate_asm_args<'a>(
278331
) -> PResult<'a, ValidatedAsmArgs> {
279332
let dcx = ecx.dcx();
280333

334+
let strip_unconfigured = rustc_expand::config::StripUnconfigured {
335+
sess: ecx.sess,
336+
features: Some(ecx.ecfg.features),
337+
config_tokens: false,
338+
lint_node_id: ecx.current_expansion.lint_node_id,
339+
};
340+
281341
let mut validated = ValidatedAsmArgs {
282342
templates: vec![],
283343
operands: vec![],
@@ -291,6 +351,26 @@ fn validate_asm_args<'a>(
291351
let mut allow_templates = true;
292352

293353
for arg in args {
354+
for attr in arg.attributes.0.iter() {
355+
match attr.name() {
356+
Some(sym::cfg | sym::cfg_attr) => {
357+
if !ecx.ecfg.features.asm_cfg() {
358+
let span = attr.span();
359+
feature_err(ecx.sess, sym::asm_cfg, span, fluent::builtin_macros_asm_cfg)
360+
.emit();
361+
}
362+
}
363+
_ => {
364+
ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() });
365+
}
366+
}
367+
}
368+
369+
// Skip arguments that are configured out.
370+
if ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(arg.attributes).is_none() {
371+
continue;
372+
}
373+
294374
match arg.kind {
295375
AsmArgKind::Template(template) => {
296376
// The error for the first template is delayed.

compiler/rustc_builtin_macros/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,13 @@ pub(crate) struct AsmRequiresTemplate {
795795
pub(crate) span: Span,
796796
}
797797

798+
#[derive(Diagnostic)]
799+
#[diag(builtin_macros_asm_attribute_not_supported)]
800+
pub(crate) struct AsmAttributeNotSupported {
801+
#[primary_span]
802+
pub(crate) span: Span,
803+
}
804+
798805
#[derive(Diagnostic)]
799806
#[diag(builtin_macros_asm_expected_comma)]
800807
pub(crate) struct AsmExpectedComma {

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ declare_features! (
371371
(unstable, arbitrary_self_types, "1.23.0", Some(44874)),
372372
/// Allows inherent and trait methods with arbitrary self types that are raw pointers.
373373
(unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
374+
/// Allows #[cfg(...)] on inline assembly templates and operands.
375+
(unstable, asm_cfg, "CURRENT_RUSTC_VERSION", Some(140364)),
374376
/// Enables experimental inline assembly support for additional architectures.
375377
(unstable, asm_experimental_arch, "1.58.0", Some(93335)),
376378
/// Enables experimental register support in inline assembly.

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ symbols! {
475475
as_ref,
476476
as_str,
477477
asm,
478+
asm_cfg,
478479
asm_const,
479480
asm_experimental_arch,
480481
asm_experimental_reg,

tests/ui/asm/cfg-parse-error.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//@ needs-asm-support
2+
#![feature(asm_cfg)]
3+
4+
use std::arch::asm;
5+
6+
fn main() {
7+
unsafe {
8+
asm!(
9+
"",
10+
#[cfg(false)]
11+
clobber_abi("C"),
12+
#[cfg(false)]
13+
options(att_syntax),
14+
#[cfg(false)]
15+
a = out(reg) x,
16+
"",
17+
//~^ ERROR expected one of `clobber_abi`, `const`
18+
);
19+
asm!(
20+
#[cfg(false)]
21+
"",
22+
#[cfg(false)]
23+
const {
24+
5
25+
},
26+
"", //~ ERROR expected one of `clobber_abi`, `const`
27+
);
28+
29+
asm!(
30+
#[cfg_attr(true, cfg(false))]
31+
const {
32+
5
33+
},
34+
"",
35+
);
36+
37+
// This is not accepted because `a = out(reg) x` is not a valid expression.
38+
asm!(
39+
#[cfg(false)]
40+
a = out(reg) x, //~ ERROR expected token: `,`
41+
"",
42+
);
43+
44+
// For now, any non-cfg attributes are rejected
45+
asm!(
46+
#[rustfmt::skip] //~ ERROR this attribute is not supported on assembly
47+
"",
48+
);
49+
50+
// For now, any non-cfg attributes are rejected
51+
asm!(
52+
#![rustfmt::skip] //~ ERROR an inner attribute is not permitted in this context
53+
"",
54+
);
55+
}
56+
}

tests/ui/asm/cfg-parse-error.stderr

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
2+
--> $DIR/cfg-parse-error.rs:16:13
3+
|
4+
LL | a = out(reg) x,
5+
| - expected one of 10 possible tokens
6+
LL | "",
7+
| ^^ unexpected token
8+
9+
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
10+
--> $DIR/cfg-parse-error.rs:26:13
11+
|
12+
LL | },
13+
| - expected one of 10 possible tokens
14+
LL | "",
15+
| ^^ unexpected token
16+
17+
error: expected token: `,`
18+
--> $DIR/cfg-parse-error.rs:40:26
19+
|
20+
LL | a = out(reg) x,
21+
| ^ expected `,`
22+
23+
error: this attribute is not supported on assembly
24+
--> $DIR/cfg-parse-error.rs:46:13
25+
|
26+
LL | #[rustfmt::skip]
27+
| ^^^^^^^^^^^^^^^^
28+
29+
error: this attribute is not supported on assembly
30+
--> $DIR/cfg-parse-error.rs:52:13
31+
|
32+
LL | #![rustfmt::skip]
33+
| ^^^^^^^^^^^^^^^^^
34+
35+
error: aborting due to 5 previous errors
36+

0 commit comments

Comments
 (0)