support #[cfg(...)] on arguments to the asm! macros
This commit is contained in:
parent
6eef33bb39
commit
e3bbbeeafd
10 changed files with 421 additions and 4 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -795,6 +795,13 @@ pub(crate) struct AsmRequiresTemplate {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -475,6 +475,7 @@ symbols! {
|
|||
as_ref,
|
||||
as_str,
|
||||
asm,
|
||||
asm_cfg,
|
||||
asm_const,
|
||||
asm_experimental_arch,
|
||||
asm_experimental_reg,
|
||||
|
|
|
|||
56
tests/ui/asm/cfg-parse-error.rs
Normal file
56
tests/ui/asm/cfg-parse-error.rs
Normal file
|
|
@ -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
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
36
tests/ui/asm/cfg-parse-error.stderr
Normal file
36
tests/ui/asm/cfg-parse-error.stderr
Normal file
|
|
@ -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
|
||||
|
||||
125
tests/ui/asm/cfg.rs
Normal file
125
tests/ui/asm/cfg.rs
Normal file
|
|
@ -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);
|
||||
}
|
||||
48
tests/ui/feature-gates/feature-gate-asm_cfg.rs
Normal file
48
tests/ui/feature-gates/feature-gate-asm_cfg.rs
Normal file
|
|
@ -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"
|
||||
)
|
||||
};
|
||||
}
|
||||
57
tests/ui/feature-gates/feature-gate-asm_cfg.stderr
Normal file
57
tests/ui/feature-gates/feature-gate-asm_cfg.stderr
Normal file
|
|
@ -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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue