diff options
| author | Folkert de Vries <folkert@folkertdev.nl> | 2025-04-30 11:23:09 +0200 |
|---|---|---|
| committer | Folkert de Vries <folkert@folkertdev.nl> | 2025-05-27 09:44:04 +0200 |
| commit | e3bbbeeafd159b9cb7b000950420a20d8910fd5e (patch) | |
| tree | 89532d311a99463ff3f7c0ff20d14a088f22b134 | |
| parent | 6eef33bb399cabfab16aa4e0825895f5f32f4e26 (diff) | |
| download | rust-e3bbbeeafd159b9cb7b000950420a20d8910fd5e.tar.gz rust-e3bbbeeafd159b9cb7b000950420a20d8910fd5e.zip | |
support `#[cfg(...)]` on arguments to the `asm!` macros
| -rw-r--r-- | compiler/rustc_builtin_macros/messages.ftl | 5 | ||||
| -rw-r--r-- | compiler/rustc_builtin_macros/src/asm.rs | 88 | ||||
| -rw-r--r-- | compiler/rustc_builtin_macros/src/errors.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_feature/src/unstable.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 | ||||
| -rw-r--r-- | tests/ui/asm/cfg-parse-error.rs | 56 | ||||
| -rw-r--r-- | tests/ui/asm/cfg-parse-error.stderr | 36 | ||||
| -rw-r--r-- | tests/ui/asm/cfg.rs | 125 | ||||
| -rw-r--r-- | tests/ui/feature-gates/feature-gate-asm_cfg.rs | 48 | ||||
| -rw-r--r-- | tests/ui/feature-gates/feature-gate-asm_cfg.stderr | 57 |
10 files changed, 421 insertions, 4 deletions
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 73be954cefd..9e0fe255e99 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -1,6 +1,11 @@ builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function builtin_macros_alloc_must_statics = allocators must be statics +builtin_macros_asm_attribute_not_supported = + this attribute is not supported on assembly +builtin_macros_asm_cfg = + the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + builtin_macros_asm_clobber_abi = clobber_abi builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs builtin_macros_asm_clobber_outputs = generic outputs diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 62ee71fecc2..593d9ddfdf8 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -10,18 +10,20 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_parse::exp; use rustc_parse::parser::{ExpKeywordPair, Parser}; use rustc_session::lint; -use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw}; +use rustc_session::parse::feature_err; +use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw, sym}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; use {rustc_ast as ast, rustc_parse_format as parse}; -use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; +use crate::{errors, fluent_generated as fluent}; /// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise /// not validated at all. pub struct AsmArg { pub kind: AsmArgKind, + pub attributes: AsmAttrVec, pub span: Span, } @@ -52,6 +54,44 @@ struct ValidatedAsmArgs { pub options_spans: Vec<Span>, } +/// A parsed list of attributes that is not attached to any item. +/// Used to check whether `asm!` arguments are configured out. +pub struct AsmAttrVec(pub ast::AttrVec); + +impl AsmAttrVec { + fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> { + let mut attributes = ast::AttrVec::new(); + while p.token == token::Pound { + let attr = p.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)?; + attributes.push(attr); + } + + Ok(Self(attributes)) + } +} +impl ast::HasAttrs for AsmAttrVec { + // Follows `ast::Expr`. + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + + fn attrs(&self) -> &[rustc_ast::Attribute] { + &self.0 + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) { + f(&mut self.0) + } +} + +impl ast::HasTokens for AsmAttrVec { + fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> { + None + } + + fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyAttrTokenStream>> { + None + } +} + /// Used for better error messages when operand types are used that are not /// supported by the current macro (e.g. `in` or `out` for `global_asm!`) /// @@ -167,8 +207,13 @@ pub fn parse_asm_args<'a>( let mut args = Vec::new(); + let attributes = AsmAttrVec::parse(p)?; let first_template = p.parse_expr()?; - args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) }); + args.push(AsmArg { + span: first_template.span, + kind: AsmArgKind::Template(first_template), + attributes, + }); let mut allow_templates = true; @@ -188,6 +233,7 @@ pub fn parse_asm_args<'a>( break; } + let attributes = AsmAttrVec::parse(p)?; let span_start = p.token.span; // Parse `clobber_abi`. @@ -197,6 +243,7 @@ pub fn parse_asm_args<'a>( args.push(AsmArg { kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), span: span_start.to(p.prev_token.span), + attributes, }); continue; @@ -209,6 +256,7 @@ pub fn parse_asm_args<'a>( args.push(AsmArg { kind: AsmArgKind::Options(parse_options(p, asm_macro)?), span: span_start.to(p.prev_token.span), + attributes, }); continue; @@ -231,6 +279,7 @@ pub fn parse_asm_args<'a>( args.push(AsmArg { span: span_start.to(p.prev_token.span), kind: AsmArgKind::Operand(name, op), + attributes, }); } else if allow_templates { let template = p.parse_expr()?; @@ -252,7 +301,11 @@ pub fn parse_asm_args<'a>( } } - args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) }); + args.push(AsmArg { + span: template.span, + kind: AsmArgKind::Template(template), + attributes, + }); } else { p.unexpected_any()? } @@ -278,6 +331,13 @@ fn validate_asm_args<'a>( ) -> PResult<'a, ValidatedAsmArgs> { let dcx = ecx.dcx(); + let strip_unconfigured = rustc_expand::config::StripUnconfigured { + sess: ecx.sess, + features: Some(ecx.ecfg.features), + config_tokens: false, + lint_node_id: ecx.current_expansion.lint_node_id, + }; + let mut validated = ValidatedAsmArgs { templates: vec![], operands: vec![], @@ -291,6 +351,26 @@ fn validate_asm_args<'a>( let mut allow_templates = true; for arg in args { + for attr in arg.attributes.0.iter() { + match attr.name() { + Some(sym::cfg | sym::cfg_attr) => { + if !ecx.ecfg.features.asm_cfg() { + let span = attr.span(); + feature_err(ecx.sess, sym::asm_cfg, span, fluent::builtin_macros_asm_cfg) + .emit(); + } + } + _ => { + ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() }); + } + } + } + + // Skip arguments that are configured out. + if ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(arg.attributes).is_none() { + continue; + } + match arg.kind { AsmArgKind::Template(template) => { // The error for the first template is delayed. diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b28f7d312d9..73e8fed321c 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -796,6 +796,13 @@ pub(crate) struct AsmRequiresTemplate { } #[derive(Diagnostic)] +#[diag(builtin_macros_asm_attribute_not_supported)] +pub(crate) struct AsmAttributeNotSupported { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] #[diag(builtin_macros_asm_expected_comma)] pub(crate) struct AsmExpectedComma { #[primary_span] diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 6cdcf451f37..3e408a03111 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -371,6 +371,8 @@ declare_features! ( (unstable, arbitrary_self_types, "1.23.0", Some(44874)), /// Allows inherent and trait methods with arbitrary self types that are raw pointers. (unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)), + /// Allows #[cfg(...)] on inline assembly templates and operands. + (unstable, asm_cfg, "CURRENT_RUSTC_VERSION", Some(140364)), /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Enables experimental register support in inline assembly. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fbe3b4ca6f5..391d820faad 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -475,6 +475,7 @@ symbols! { as_ref, as_str, asm, + asm_cfg, asm_const, asm_experimental_arch, asm_experimental_reg, diff --git a/tests/ui/asm/cfg-parse-error.rs b/tests/ui/asm/cfg-parse-error.rs new file mode 100644 index 00000000000..c66a627ca94 --- /dev/null +++ b/tests/ui/asm/cfg-parse-error.rs @@ -0,0 +1,56 @@ +//@ needs-asm-support +#![feature(asm_cfg)] + +use std::arch::asm; + +fn main() { + unsafe { + asm!( + "", + #[cfg(false)] + clobber_abi("C"), + #[cfg(false)] + options(att_syntax), + #[cfg(false)] + a = out(reg) x, + "", + //~^ ERROR expected one of `clobber_abi`, `const` + ); + asm!( + #[cfg(false)] + "", + #[cfg(false)] + const { + 5 + }, + "", //~ ERROR expected one of `clobber_abi`, `const` + ); + + asm!( + #[cfg_attr(true, cfg(false))] + const { + 5 + }, + "", + ); + + // This is not accepted because `a = out(reg) x` is not a valid expression. + asm!( + #[cfg(false)] + a = out(reg) x, //~ ERROR expected token: `,` + "", + ); + + // For now, any non-cfg attributes are rejected + asm!( + #[rustfmt::skip] //~ ERROR this attribute is not supported on assembly + "", + ); + + // For now, any non-cfg attributes are rejected + asm!( + #![rustfmt::skip] //~ ERROR an inner attribute is not permitted in this context + "", + ); + } +} diff --git a/tests/ui/asm/cfg-parse-error.stderr b/tests/ui/asm/cfg-parse-error.stderr new file mode 100644 index 00000000000..19c76adee63 --- /dev/null +++ b/tests/ui/asm/cfg-parse-error.stderr @@ -0,0 +1,36 @@ +error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` + --> $DIR/cfg-parse-error.rs:16:13 + | +LL | a = out(reg) x, + | - expected one of 10 possible tokens +LL | "", + | ^^ unexpected token + +error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` + --> $DIR/cfg-parse-error.rs:26:13 + | +LL | }, + | - expected one of 10 possible tokens +LL | "", + | ^^ unexpected token + +error: expected token: `,` + --> $DIR/cfg-parse-error.rs:40:26 + | +LL | a = out(reg) x, + | ^ expected `,` + +error: this attribute is not supported on assembly + --> $DIR/cfg-parse-error.rs:46:13 + | +LL | #[rustfmt::skip] + | ^^^^^^^^^^^^^^^^ + +error: this attribute is not supported on assembly + --> $DIR/cfg-parse-error.rs:52:13 + | +LL | #![rustfmt::skip] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/asm/cfg.rs b/tests/ui/asm/cfg.rs new file mode 100644 index 00000000000..bcf86340b9d --- /dev/null +++ b/tests/ui/asm/cfg.rs @@ -0,0 +1,125 @@ +// Check that `cfg` and `cfg_attr` work as expected. +// +//@ revisions: reva revb +//@ only-x86_64 +//@ run-pass +#![feature(asm_cfg, cfg_select)] + +use std::arch::{asm, naked_asm}; + +#[unsafe(naked)] +extern "C" fn ignore_const_operand() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg(revb)] + "mov rax, {a}", + "ret", + #[cfg(revb)] + a = const 10, + ) +} + +#[unsafe(naked)] +extern "C" fn ignore_const_operand_cfg_attr() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg_attr(true, cfg(revb))] + "mov rax, {a}", + "ret", + #[cfg_attr(true, cfg(revb))] + a = const 10, + ) +} + +#[unsafe(naked)] +extern "C" fn const_operand() -> u64 { + naked_asm!( + "mov rax, {a}", + "ret", + #[cfg(reva)] + a = const 5, + #[cfg(revb)] + a = const 10, + ) +} + +fn options() { + // Without the cfg, this throws an error that the `att_syntax` option is provided twice. + unsafe { + asm!( + "nop", + #[cfg(false)] + options(att_syntax), + options(att_syntax) + ) + } +} + +fn clobber_abi() { + // Without the cfg, this throws an error that the "C" abi is provided twice. + unsafe { + asm!( + "nop", + #[cfg(false)] + clobber_abi("C"), + clobber_abi("C"), + ); + } +} + +#[unsafe(naked)] +extern "C" fn first_template() -> u64 { + naked_asm!( + #[cfg(reva)] + "mov rax, 5", + #[cfg(revb)] + "mov rax, 10", + "ret", + ) +} + +#[unsafe(naked)] +extern "C" fn true_and_false() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg(true)] + #[cfg(false)] + "mov rax, 10", + "ret", + ) +} + +#[unsafe(naked)] +extern "C" fn false_and_true() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg(false)] + #[cfg(true)] + "mov rax, 10", + "ret", + ) +} + +pub fn main() { + std::cfg_select! { + reva => { + assert_eq!(const_operand(), 5); + assert_eq!(ignore_const_operand_cfg_attr(), 5); + assert_eq!(ignore_const_operand(), 5); + assert_eq!(first_template(), 5); + + } + revb => { + assert_eq!(const_operand(), 10); + assert_eq!(ignore_const_operand_cfg_attr(), 10); + assert_eq!(ignore_const_operand(), 10); + assert_eq!(first_template(), 10); + + } + } + options(); + clobber_abi(); + + assert_eq!(true_and_false(), 5); + assert_eq!(false_and_true(), 5); +} diff --git a/tests/ui/feature-gates/feature-gate-asm_cfg.rs b/tests/ui/feature-gates/feature-gate-asm_cfg.rs new file mode 100644 index 00000000000..ef8bf75b692 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_cfg.rs @@ -0,0 +1,48 @@ +//@ only-x86_64 +#![crate_type = "lib"] + +use std::arch::{asm, global_asm, naked_asm}; + +global_asm!( + "nop", + #[cfg(false)] + //~^ ERROR the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + "nop" +); + +#[unsafe(naked)] +#[no_mangle] +extern "C" fn naked() { + naked_asm!( + "mov rax, 5", + #[cfg(false)] + //~^ ERROR the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + "mov rax, {a}", + "ret", + #[cfg(false)] + //~^ ERROR the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + a = const 10, + ) +} + +fn asm() { + unsafe { + asm!( + "nop", + #[cfg(false)] + //~^ ERROR the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + clobber_abi("C"), + clobber_abi("C"), //~ ERROR `C` ABI specified multiple times + ); + } +} + +fn bad_attribute() { + unsafe { + asm!( + #[inline] + //~^ ERROR this attribute is not supported on assembly + "nop" + ) + }; +} diff --git a/tests/ui/feature-gates/feature-gate-asm_cfg.stderr b/tests/ui/feature-gates/feature-gate-asm_cfg.stderr new file mode 100644 index 00000000000..e92d1e8c487 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_cfg.stderr @@ -0,0 +1,57 @@ +error[E0658]: the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + --> $DIR/feature-gate-asm_cfg.rs:8:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + | + = note: see issue #140364 <https://github.com/rust-lang/rust/issues/140364> for more information + = help: add `#![feature(asm_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + --> $DIR/feature-gate-asm_cfg.rs:18:9 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + | + = note: see issue #140364 <https://github.com/rust-lang/rust/issues/140364> for more information + = help: add `#![feature(asm_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + --> $DIR/feature-gate-asm_cfg.rs:22:9 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + | + = note: see issue #140364 <https://github.com/rust-lang/rust/issues/140364> for more information + = help: add `#![feature(asm_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + --> $DIR/feature-gate-asm_cfg.rs:32:13 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + | + = note: see issue #140364 <https://github.com/rust-lang/rust/issues/140364> for more information + = help: add `#![feature(asm_cfg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: this attribute is not supported on assembly + --> $DIR/feature-gate-asm_cfg.rs:43:13 + | +LL | #[inline] + | ^^^^^^^^^ + +error: `C` ABI specified multiple times + --> $DIR/feature-gate-asm_cfg.rs:35:13 + | +LL | clobber_abi("C"), + | ---------------- previously specified here +LL | clobber_abi("C"), + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. |
