Skip to content

add asm_cfg: #[cfg(...)] within asm! #140367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
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
Expand Down
147 changes: 124 additions & 23 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ 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};

pub struct AsmArgs {
pub templates: Vec<P<ast::Expr>>,
Expand Down Expand Up @@ -59,30 +60,113 @@ fn eat_operand_keyword<'a>(
}
}

/// A parsed list of attributes that is not attached to any item.
/// Used to check whether `asm!` arguments are configured out.
struct AsmAttrVec(ast::AttrVec);

impl AsmAttrVec {
fn parse<'a>(ecx: &ExtCtxt<'a>, p: &mut Parser<'a>) -> PResult<'a, Self> {
let span_start = p.token.span;

let mut attributes = ast::AttrVec::new();
loop {
if p.token != token::Pound {
break;
}

let attr = p.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)?;
attributes.push(attr);
}

for attr in attributes.iter() {
match attr.name() {
Some(sym::cfg | sym::cfg_attr) => {
if !ecx.ecfg.features.asm_cfg() {
let span = span_start.to(p.prev_token.span);
feature_err(ecx.sess, sym::asm_cfg, span, fluent::builtin_macros_asm_cfg)
.emit();
}
}
_ => {
ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() });
}
Comment on lines +90 to +92
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-cfg attributes are now accepted by the parser, but emit this error. Previously attributes were only parsed (and later rejected, unless #[feature(stmt_expr_attributes)] was enabled) on expressions. We now also always parse them on operands.

}
}

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
}
}

fn parse_args<'a>(
ecx: &ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
asm_macro: AsmMacro,
) -> PResult<'a, AsmArgs> {
let mut p = ecx.new_parser_from_tts(tts);
parse_asm_args(&mut p, sp, asm_macro)
parse_asm_args(ecx, &mut p, sp, asm_macro)
}

// Primarily public for rustfmt consumption.
// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm`
pub fn parse_asm_args<'a>(
ecx: &ExtCtxt<'a>,
p: &mut Parser<'a>,
sp: Span,
asm_macro: AsmMacro,
) -> PResult<'a, AsmArgs> {
let dcx = p.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,
};

if p.token == token::Eof {
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
}

let first_template = p.parse_expr()?;
let first_template = loop {
let attributes = AsmAttrVec::parse(ecx, p)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attributes are now parsed before the parse_expr below, and never attached to the expression. In theory that could interact with #[feature(stmt_expr_attributes)], but:

  • that feature is unstable
  • any attributes it parsed would have no effect

let is_configured_out =
ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(attributes).is_none();

let template = p.parse_expr()?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that we use parse_expr here mean that this does not work, and does not produce a great error message. This complains about an unexpected , after the 6, but the real problem is that this cannot possibly be or expand to a string literal.

asm!(
    #[cfg(false)]
    a = const 6,
    "nop",
);

Maybe we should specialize the parser to specifically only parse string literals and item macros?


if !is_configured_out {
break template;
}

if !p.eat(exp!(Comma)) {
// After a template string, we always expect *only* a comma...
return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
}
};

let mut args = AsmArgs {
templates: vec![first_template],
operands: vec![],
Expand All @@ -108,17 +192,26 @@ pub fn parse_asm_args<'a>(
break;
} // accept trailing commas

let attributes = AsmAttrVec::parse(ecx, p)?;
let is_configured_out =
ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(attributes).is_none();

// Parse clobber_abi
if p.eat_keyword(exp!(ClobberAbi)) {
parse_clobber_abi(p, &mut args)?;
allow_templates = false;
let new_abis = parse_clobber_abi(p)?;
if !is_configured_out {
args.clobber_abis.extend(new_abis);
allow_templates = false;
}
continue;
}

// Parse options
if p.eat_keyword(exp!(Options)) {
parse_options(p, &mut args, asm_macro)?;
allow_templates = false;
parse_options(p, &mut args, asm_macro, is_configured_out)?;
if !is_configured_out {
allow_templates = false;
}
continue;
}

Expand All @@ -129,7 +222,9 @@ pub fn parse_asm_args<'a>(
let (ident, _) = p.token.ident().unwrap();
p.bump();
p.expect(exp!(Eq))?;
allow_templates = false;
if !is_configured_out {
allow_templates = false;
}
Some(ident.name)
} else {
None
Expand Down Expand Up @@ -200,6 +295,11 @@ pub fn parse_asm_args<'a>(
ast::InlineAsmOperand::Sym { sym }
} else if allow_templates {
let template = p.parse_expr()?;

if is_configured_out {
continue;
}

// If it can't possibly expand to a string, provide diagnostics here to include other
// things it could have been.
match template.kind {
Expand All @@ -223,6 +323,10 @@ pub fn parse_asm_args<'a>(
p.unexpected_any()?
};

if is_configured_out {
continue;
}

allow_templates = false;
let span = span_start.to(p.prev_token.span);
let slot = args.operands.len();
Expand Down Expand Up @@ -386,6 +490,7 @@ fn parse_options<'a>(
p: &mut Parser<'a>,
args: &mut AsmArgs,
asm_macro: AsmMacro,
is_configured_out: bool,
) -> PResult<'a, ()> {
let span_start = p.prev_token.span;

Expand Down Expand Up @@ -413,7 +518,9 @@ fn parse_options<'a>(
};

if kw_matched {
try_set_option(p, args, asm_macro, exp.kw, option);
if !is_configured_out {
try_set_option(p, args, asm_macro, exp.kw, option);
}
break 'blk;
}
}
Expand All @@ -428,13 +535,15 @@ fn parse_options<'a>(
p.expect(exp!(Comma))?;
}

let new_span = span_start.to(p.prev_token.span);
args.options_spans.push(new_span);
if !is_configured_out {
let new_span = span_start.to(p.prev_token.span);
args.options_spans.push(new_span);
}

Ok(())
}

fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
let span_start = p.prev_token.span;

p.expect(exp!(OpenParen))?;
Expand Down Expand Up @@ -465,17 +574,9 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
let full_span = span_start.to(p.prev_token.span);

match &new_abis[..] {
// should have errored above during parsing
[] => unreachable!(),
[(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
abis => {
for (abi, span) in abis {
args.clobber_abis.push((*abi, *span));
}
}
[(abi, _span)] => Ok(vec![(*abi, full_span)]),
_ => Ok(new_abis),
}

Ok(())
}

fn parse_reg<'a>(
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ symbols! {
as_ref,
as_str,
asm,
asm_cfg,
asm_const,
asm_experimental_arch,
asm_experimental_reg,
Expand Down
Loading
Loading