-
Notifications
You must be signed in to change notification settings - Fork 13.3k
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
base: master
Are you sure you want to change the base?
Changes from all commits
ead0a60
b4fb021
f8e2002
56a9de8
3e00278
c7b63c8
5cfd71a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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>>, | ||
|
@@ -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() }); | ||
} | ||
} | ||
} | ||
|
||
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)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. attributes are now parsed before the
|
||
let is_configured_out = | ||
ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(attributes).is_none(); | ||
|
||
let template = p.parse_expr()?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fact that we use 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![], | ||
|
@@ -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; | ||
} | ||
|
||
|
@@ -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 | ||
|
@@ -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 { | ||
|
@@ -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(); | ||
|
@@ -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; | ||
|
||
|
@@ -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; | ||
} | ||
} | ||
|
@@ -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))?; | ||
|
@@ -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>( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -472,6 +472,7 @@ symbols! { | |
as_ref, | ||
as_str, | ||
asm, | ||
asm_cfg, | ||
asm_const, | ||
asm_experimental_arch, | ||
asm_experimental_reg, | ||
|
There was a problem hiding this comment.
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.