2024-02-13 23:28:27 +00:00
|
|
|
use ast::token::IdentIsRaw;
|
2024-04-14 20:11:14 +00:00
|
|
|
use lint::BuiltinLintDiag;
|
2020-02-12 15:47:43 +00:00
|
|
|
use rustc_ast::ptr::P;
|
|
|
|
|
use rustc_ast::tokenstream::TokenStream;
|
2024-12-04 15:55:06 +11:00
|
|
|
use rustc_ast::{AsmMacro, token};
|
2023-05-02 16:42:36 +01:00
|
|
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
2025-04-30 01:06:38 +02:00
|
|
|
use rustc_errors::PResult;
|
2024-03-12 10:55:17 +08:00
|
|
|
use rustc_expand::base::*;
|
2023-05-02 16:42:36 +01:00
|
|
|
use rustc_index::bit_set::GrowableBitSet;
|
2024-12-04 15:55:06 +11:00
|
|
|
use rustc_parse::exp;
|
|
|
|
|
use rustc_parse::parser::{ExpKeywordPair, Parser};
|
2021-08-19 16:34:01 -04:00
|
|
|
use rustc_session::lint;
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-21 09:47:43 +11:00
|
|
|
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw};
|
2021-03-24 04:52:57 +00:00
|
|
|
use rustc_target::asm::InlineAsmArch;
|
2021-04-11 20:51:28 +01:00
|
|
|
use smallvec::smallvec;
|
2020-06-02 20:19:49 +03:00
|
|
|
use {rustc_ast as ast, rustc_parse_format as parse};
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2024-04-26 08:44:23 +10:00
|
|
|
use crate::errors;
|
2024-12-31 04:15:40 +00:00
|
|
|
use crate::util::{ExprToSpannedString, expr_to_spanned_string};
|
2020-02-12 15:47:43 +00:00
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
|
|
|
|
|
/// not validated at all.
|
2025-05-17 23:17:06 +02:00
|
|
|
pub struct AsmArg {
|
|
|
|
|
pub kind: AsmArgKind,
|
2025-04-29 22:43:46 +02:00
|
|
|
pub span: Span,
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
pub enum AsmArgKind {
|
2025-04-29 22:43:46 +02:00
|
|
|
Template(P<ast::Expr>),
|
|
|
|
|
Operand(Option<Symbol>, ast::InlineAsmOperand),
|
2025-05-17 23:27:33 +02:00
|
|
|
Options(Vec<AsmOption>),
|
2025-04-29 22:43:46 +02:00
|
|
|
ClobberAbi(Vec<(Symbol, Span)>),
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:27:33 +02:00
|
|
|
pub struct AsmOption {
|
|
|
|
|
pub symbol: Symbol,
|
|
|
|
|
pub span: Span,
|
|
|
|
|
// A bitset, with only the bit for this option's symbol set.
|
|
|
|
|
pub options: ast::InlineAsmOptions,
|
|
|
|
|
// Used when suggesting to remove an option.
|
|
|
|
|
pub span_with_comma: Span,
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
/// Validated assembly arguments, ready for macro expansion.
|
2025-05-17 23:17:06 +02:00
|
|
|
struct ValidatedAsmArgs {
|
2022-01-10 21:48:22 -05:00
|
|
|
pub templates: Vec<P<ast::Expr>>,
|
|
|
|
|
pub operands: Vec<(ast::InlineAsmOperand, Span)>,
|
2023-04-30 20:54:43 +01:00
|
|
|
named_args: FxIndexMap<Symbol, usize>,
|
2023-05-02 16:42:36 +01:00
|
|
|
reg_args: GrowableBitSet<usize>,
|
2022-01-10 21:48:22 -05:00
|
|
|
pub clobber_abis: Vec<(Symbol, Span)>,
|
2020-05-06 14:46:01 +01:00
|
|
|
options: ast::InlineAsmOptions,
|
2022-01-10 21:48:22 -05:00
|
|
|
pub options_spans: Vec<Span>,
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-28 15:11:14 +02:00
|
|
|
/// 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!`)
|
|
|
|
|
///
|
|
|
|
|
/// returns
|
|
|
|
|
///
|
|
|
|
|
/// - `Ok(true)` if the current token matches the keyword, and was expected
|
|
|
|
|
/// - `Ok(false)` if the current token does not match the keyword
|
|
|
|
|
/// - `Err(_)` if the current token matches the keyword, but was not expected
|
2024-08-04 16:42:37 +02:00
|
|
|
fn eat_operand_keyword<'a>(
|
|
|
|
|
p: &mut Parser<'a>,
|
2024-12-04 15:55:06 +11:00
|
|
|
exp: ExpKeywordPair,
|
2024-08-04 16:42:37 +02:00
|
|
|
asm_macro: AsmMacro,
|
|
|
|
|
) -> PResult<'a, bool> {
|
|
|
|
|
if matches!(asm_macro, AsmMacro::Asm) {
|
2024-12-04 15:55:06 +11:00
|
|
|
Ok(p.eat_keyword(exp))
|
2024-07-28 15:11:14 +02:00
|
|
|
} else {
|
|
|
|
|
let span = p.token.span;
|
2024-12-04 15:55:06 +11:00
|
|
|
if p.eat_keyword_noexpect(exp.kw) {
|
2024-07-28 15:11:14 +02:00
|
|
|
// in gets printed as `r#in` otherwise
|
2024-12-04 15:55:06 +11:00
|
|
|
let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() };
|
2024-08-04 16:42:37 +02:00
|
|
|
Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
|
|
|
|
|
span,
|
|
|
|
|
symbol,
|
|
|
|
|
macro_name: asm_macro.macro_name(),
|
|
|
|
|
}))
|
2024-07-28 15:11:14 +02:00
|
|
|
} else {
|
|
|
|
|
Ok(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-29 20:35:39 +02:00
|
|
|
fn parse_asm_operand<'a>(
|
|
|
|
|
p: &mut Parser<'a>,
|
|
|
|
|
asm_macro: AsmMacro,
|
|
|
|
|
) -> PResult<'a, Option<ast::InlineAsmOperand>> {
|
|
|
|
|
let dcx = p.dcx();
|
|
|
|
|
|
|
|
|
|
Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
|
|
|
|
|
let reg = parse_reg(p)?;
|
|
|
|
|
if p.eat_keyword(exp!(Underscore)) {
|
|
|
|
|
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
|
|
|
|
return Err(err);
|
|
|
|
|
}
|
|
|
|
|
let expr = p.parse_expr()?;
|
|
|
|
|
ast::InlineAsmOperand::In { reg, expr }
|
|
|
|
|
} else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
|
|
|
|
|
let reg = parse_reg(p)?;
|
|
|
|
|
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
|
|
|
|
ast::InlineAsmOperand::Out { reg, expr, late: false }
|
|
|
|
|
} else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
|
|
|
|
|
let reg = parse_reg(p)?;
|
|
|
|
|
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
|
|
|
|
ast::InlineAsmOperand::Out { reg, expr, late: true }
|
|
|
|
|
} else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
|
|
|
|
|
let reg = parse_reg(p)?;
|
|
|
|
|
if p.eat_keyword(exp!(Underscore)) {
|
|
|
|
|
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
|
|
|
|
return Err(err);
|
|
|
|
|
}
|
|
|
|
|
let expr = p.parse_expr()?;
|
|
|
|
|
if p.eat(exp!(FatArrow)) {
|
|
|
|
|
let out_expr =
|
|
|
|
|
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
|
|
|
|
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
|
|
|
|
|
} else {
|
|
|
|
|
ast::InlineAsmOperand::InOut { reg, expr, late: false }
|
|
|
|
|
}
|
|
|
|
|
} else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
|
|
|
|
|
let reg = parse_reg(p)?;
|
|
|
|
|
if p.eat_keyword(exp!(Underscore)) {
|
|
|
|
|
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
|
|
|
|
return Err(err);
|
|
|
|
|
}
|
|
|
|
|
let expr = p.parse_expr()?;
|
|
|
|
|
if p.eat(exp!(FatArrow)) {
|
|
|
|
|
let out_expr =
|
|
|
|
|
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
|
|
|
|
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
|
|
|
|
|
} else {
|
|
|
|
|
ast::InlineAsmOperand::InOut { reg, expr, late: true }
|
|
|
|
|
}
|
|
|
|
|
} else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
|
|
|
|
|
let block = p.parse_block()?;
|
|
|
|
|
ast::InlineAsmOperand::Label { block }
|
|
|
|
|
} else if p.eat_keyword(exp!(Const)) {
|
|
|
|
|
let anon_const = p.parse_expr_anon_const()?;
|
|
|
|
|
ast::InlineAsmOperand::Const { anon_const }
|
|
|
|
|
} else if p.eat_keyword(exp!(Sym)) {
|
|
|
|
|
let expr = p.parse_expr()?;
|
|
|
|
|
let ast::ExprKind::Path(qself, path) = &expr.kind else {
|
|
|
|
|
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
|
|
|
|
|
return Err(err);
|
|
|
|
|
};
|
|
|
|
|
let sym =
|
|
|
|
|
ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
|
|
|
|
|
ast::InlineAsmOperand::Sym { sym }
|
|
|
|
|
} else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
// Public for rustfmt.
|
2025-05-17 23:17:06 +02:00
|
|
|
pub fn parse_asm_args<'a>(
|
2021-12-16 18:40:07 -06:00
|
|
|
p: &mut Parser<'a>,
|
|
|
|
|
sp: Span,
|
2024-08-04 16:42:37 +02:00
|
|
|
asm_macro: AsmMacro,
|
2025-05-17 23:17:06 +02:00
|
|
|
) -> PResult<'a, Vec<AsmArg>> {
|
2024-06-18 09:43:28 +00:00
|
|
|
let dcx = p.dcx();
|
2020-02-12 15:47:43 +00:00
|
|
|
|
|
|
|
|
if p.token == token::Eof {
|
2023-12-18 11:15:13 +11:00
|
|
|
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
let mut args = Vec::new();
|
|
|
|
|
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
let first_template = p.parse_expr()?;
|
2025-05-17 23:17:06 +02:00
|
|
|
args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) });
|
2020-02-12 15:47:43 +00:00
|
|
|
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
let mut allow_templates = true;
|
2025-04-29 22:43:46 +02:00
|
|
|
|
2020-02-12 15:47:43 +00:00
|
|
|
while p.token != token::Eof {
|
2024-12-04 15:55:06 +11:00
|
|
|
if !p.eat(exp!(Comma)) {
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
if allow_templates {
|
|
|
|
|
// After a template string, we always expect *only* a comma...
|
2023-12-18 11:15:13 +11:00
|
|
|
return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
|
2020-02-12 15:47:43 +00:00
|
|
|
} else {
|
|
|
|
|
// ...after that delegate to `expect` to also include the other expected tokens.
|
2024-12-04 15:55:06 +11:00
|
|
|
return Err(p.expect(exp!(Comma)).err().unwrap());
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-04-29 22:43:46 +02:00
|
|
|
|
|
|
|
|
// Accept trailing commas.
|
2020-02-12 15:47:43 +00:00
|
|
|
if p.token == token::Eof {
|
|
|
|
|
break;
|
2025-04-29 22:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let span_start = p.token.span;
|
2020-02-12 15:47:43 +00:00
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
// Parse `clobber_abi`.
|
2024-12-04 15:55:06 +11:00
|
|
|
if p.eat_keyword(exp!(ClobberAbi)) {
|
2021-07-29 13:43:26 +02:00
|
|
|
allow_templates = false;
|
2025-04-29 22:43:46 +02:00
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
args.push(AsmArg {
|
|
|
|
|
kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
|
2025-04-29 22:43:46 +02:00
|
|
|
span: span_start.to(p.prev_token.span),
|
|
|
|
|
});
|
|
|
|
|
|
2021-07-29 13:43:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
// Parse `options`.
|
2024-12-04 15:55:06 +11:00
|
|
|
if p.eat_keyword(exp!(Options)) {
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
allow_templates = false;
|
2025-04-29 22:43:46 +02:00
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
args.push(AsmArg {
|
|
|
|
|
kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
|
2025-04-29 22:43:46 +02:00
|
|
|
span: span_start.to(p.prev_token.span),
|
|
|
|
|
});
|
|
|
|
|
|
2020-02-12 15:47:43 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
// Parse operand names.
|
2020-02-12 15:47:43 +00:00
|
|
|
let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
|
|
|
|
|
let (ident, _) = p.token.ident().unwrap();
|
|
|
|
|
p.bump();
|
2024-12-04 15:55:06 +11:00
|
|
|
p.expect(exp!(Eq))?;
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
allow_templates = false;
|
2020-02-12 15:47:43 +00:00
|
|
|
Some(ident.name)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
if let Some(op) = parse_asm_operand(p, asm_macro)? {
|
|
|
|
|
allow_templates = false;
|
|
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
args.push(AsmArg {
|
2025-04-29 22:43:46 +02:00
|
|
|
span: span_start.to(p.prev_token.span),
|
2025-05-17 23:17:06 +02:00
|
|
|
kind: AsmArgKind::Operand(name, op),
|
2025-04-29 22:43:46 +02:00
|
|
|
});
|
|
|
|
|
} else if allow_templates {
|
|
|
|
|
let template = p.parse_expr()?;
|
|
|
|
|
// If it can't possibly expand to a string, provide diagnostics here to include other
|
|
|
|
|
// things it could have been.
|
|
|
|
|
match template.kind {
|
|
|
|
|
ast::ExprKind::Lit(token_lit)
|
|
|
|
|
if matches!(
|
|
|
|
|
token_lit.kind,
|
|
|
|
|
token::LitKind::Str | token::LitKind::StrRaw(_)
|
|
|
|
|
) => {}
|
|
|
|
|
ast::ExprKind::MacCall(..) => {}
|
|
|
|
|
_ => {
|
|
|
|
|
let err = dcx.create_err(errors::AsmExpectedOther {
|
|
|
|
|
span: template.span,
|
|
|
|
|
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
|
|
|
|
|
});
|
|
|
|
|
return Err(err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) });
|
2025-04-29 22:43:46 +02:00
|
|
|
} else {
|
|
|
|
|
p.unexpected_any()?
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(args)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_args<'a>(
|
|
|
|
|
ecx: &ExtCtxt<'a>,
|
|
|
|
|
sp: Span,
|
|
|
|
|
tts: TokenStream,
|
|
|
|
|
asm_macro: AsmMacro,
|
2025-05-17 23:17:06 +02:00
|
|
|
) -> PResult<'a, ValidatedAsmArgs> {
|
|
|
|
|
let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?;
|
|
|
|
|
validate_asm_args(ecx, asm_macro, args)
|
2025-04-29 22:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
fn validate_asm_args<'a>(
|
2025-04-30 01:06:38 +02:00
|
|
|
ecx: &ExtCtxt<'a>,
|
2025-04-29 22:43:46 +02:00
|
|
|
asm_macro: AsmMacro,
|
2025-05-17 23:17:06 +02:00
|
|
|
args: Vec<AsmArg>,
|
|
|
|
|
) -> PResult<'a, ValidatedAsmArgs> {
|
2025-04-30 01:06:38 +02:00
|
|
|
let dcx = ecx.dcx();
|
|
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
let mut validated = ValidatedAsmArgs {
|
2025-04-29 22:43:46 +02:00
|
|
|
templates: vec![],
|
|
|
|
|
operands: vec![],
|
|
|
|
|
named_args: Default::default(),
|
|
|
|
|
reg_args: Default::default(),
|
|
|
|
|
clobber_abis: Vec::new(),
|
|
|
|
|
options: ast::InlineAsmOptions::empty(),
|
|
|
|
|
options_spans: vec![],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut allow_templates = true;
|
|
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
for arg in args {
|
2025-04-29 22:43:46 +02:00
|
|
|
match arg.kind {
|
2025-05-17 23:17:06 +02:00
|
|
|
AsmArgKind::Template(template) => {
|
2025-04-29 22:43:46 +02:00
|
|
|
// The error for the first template is delayed.
|
|
|
|
|
if !allow_templates {
|
|
|
|
|
match template.kind {
|
|
|
|
|
ast::ExprKind::Lit(token_lit)
|
|
|
|
|
if matches!(
|
|
|
|
|
token_lit.kind,
|
|
|
|
|
token::LitKind::Str | token::LitKind::StrRaw(_)
|
|
|
|
|
) => {}
|
|
|
|
|
ast::ExprKind::MacCall(..) => {}
|
|
|
|
|
_ => {
|
|
|
|
|
let err = dcx.create_err(errors::AsmExpectedOther {
|
|
|
|
|
span: template.span,
|
|
|
|
|
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
|
|
|
|
|
});
|
|
|
|
|
return Err(err);
|
|
|
|
|
}
|
2025-04-29 20:35:39 +02:00
|
|
|
}
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
2025-04-29 22:43:46 +02:00
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
validated.templates.push(template);
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
AsmArgKind::Operand(name, op) => {
|
2025-04-29 22:43:46 +02:00
|
|
|
allow_templates = false;
|
2020-02-12 15:47:43 +00:00
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_)));
|
|
|
|
|
let span = arg.span;
|
2025-05-17 23:17:06 +02:00
|
|
|
let slot = validated.operands.len();
|
|
|
|
|
validated.operands.push((op, span));
|
2025-04-29 20:25:59 +02:00
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
// Validate the order of named, positional & explicit register operands and
|
|
|
|
|
// clobber_abi/options. We do this at the end once we have the full span
|
|
|
|
|
// of the argument available.
|
2020-02-12 15:47:43 +00:00
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
if explicit_reg {
|
|
|
|
|
if name.is_some() {
|
|
|
|
|
dcx.emit_err(errors::AsmExplicitRegisterName { span });
|
|
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
validated.reg_args.insert(slot);
|
2025-04-29 22:43:46 +02:00
|
|
|
} else if let Some(name) = name {
|
2025-05-17 23:17:06 +02:00
|
|
|
if let Some(&prev) = validated.named_args.get(&name) {
|
2025-04-29 22:43:46 +02:00
|
|
|
dcx.emit_err(errors::AsmDuplicateArg {
|
|
|
|
|
span,
|
|
|
|
|
name,
|
2025-05-17 23:17:06 +02:00
|
|
|
prev: validated.operands[prev].1,
|
2025-04-29 22:43:46 +02:00
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
validated.named_args.insert(name, slot);
|
|
|
|
|
} else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() {
|
|
|
|
|
let named =
|
|
|
|
|
validated.named_args.values().map(|p| validated.operands[*p].1).collect();
|
|
|
|
|
let explicit =
|
|
|
|
|
validated.reg_args.iter().map(|p| validated.operands[p].1).collect();
|
2025-04-29 20:25:59 +02:00
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
AsmArgKind::Options(new_options) => {
|
2025-04-29 22:43:46 +02:00
|
|
|
allow_templates = false;
|
|
|
|
|
|
2025-05-17 23:27:33 +02:00
|
|
|
for asm_option in new_options {
|
|
|
|
|
let AsmOption { span, symbol, span_with_comma, options } = asm_option;
|
|
|
|
|
|
|
|
|
|
if !asm_macro.is_supported_option(options) {
|
2025-04-29 23:39:10 +02:00
|
|
|
// Tool-only output.
|
|
|
|
|
dcx.emit_err(errors::AsmUnsupportedOption {
|
2025-04-29 22:43:46 +02:00
|
|
|
span,
|
|
|
|
|
symbol,
|
2025-05-17 23:27:33 +02:00
|
|
|
span_with_comma,
|
2025-04-29 22:43:46 +02:00
|
|
|
macro_name: asm_macro.macro_name(),
|
|
|
|
|
});
|
2025-05-17 23:27:33 +02:00
|
|
|
} else if validated.options.contains(options) {
|
2025-04-29 22:43:46 +02:00
|
|
|
// Tool-only output.
|
2025-05-17 23:27:33 +02:00
|
|
|
dcx.emit_err(errors::AsmOptAlreadyprovided {
|
|
|
|
|
span,
|
|
|
|
|
symbol,
|
|
|
|
|
span_with_comma,
|
|
|
|
|
});
|
2025-04-29 22:43:46 +02:00
|
|
|
} else {
|
2025-05-17 23:27:33 +02:00
|
|
|
validated.options |= asm_option.options;
|
2025-04-29 22:43:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-04-30 21:45:46 +01:00
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
validated.options_spans.push(arg.span);
|
2025-04-29 22:43:46 +02:00
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
AsmArgKind::ClobberAbi(new_abis) => {
|
2025-04-29 22:43:46 +02:00
|
|
|
allow_templates = false;
|
|
|
|
|
|
|
|
|
|
match &new_abis[..] {
|
|
|
|
|
// This should have errored above during parsing.
|
|
|
|
|
[] => unreachable!(),
|
2025-05-17 23:17:06 +02:00
|
|
|
[(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)),
|
|
|
|
|
_ => validated.clobber_abis.extend(new_abis),
|
2025-04-29 22:43:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
if validated.options.contains(ast::InlineAsmOptions::NOMEM)
|
|
|
|
|
&& validated.options.contains(ast::InlineAsmOptions::READONLY)
|
2020-02-12 15:47:43 +00:00
|
|
|
{
|
2025-05-17 23:17:06 +02:00
|
|
|
let spans = validated.options_spans.clone();
|
2023-12-18 11:15:13 +11:00
|
|
|
dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
if validated.options.contains(ast::InlineAsmOptions::PURE)
|
|
|
|
|
&& validated.options.contains(ast::InlineAsmOptions::NORETURN)
|
2020-02-12 15:47:43 +00:00
|
|
|
{
|
2025-05-17 23:17:06 +02:00
|
|
|
let spans = validated.options_spans.clone();
|
2023-12-18 11:15:13 +11:00
|
|
|
dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
if validated.options.contains(ast::InlineAsmOptions::PURE)
|
|
|
|
|
&& !validated
|
|
|
|
|
.options
|
|
|
|
|
.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
|
2020-02-12 15:47:43 +00:00
|
|
|
{
|
2025-05-17 23:17:06 +02:00
|
|
|
let spans = validated.options_spans.clone();
|
2023-12-18 11:15:13 +11:00
|
|
|
dcx.emit_err(errors::AsmPureCombine { spans });
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut have_real_output = false;
|
|
|
|
|
let mut outputs_sp = vec![];
|
2021-07-29 13:43:26 +02:00
|
|
|
let mut regclass_outputs = vec![];
|
2023-12-26 04:44:22 +00:00
|
|
|
let mut labels_sp = vec![];
|
2025-05-17 23:17:06 +02:00
|
|
|
for (op, op_sp) in &validated.operands {
|
2020-02-12 15:47:43 +00:00
|
|
|
match op {
|
2021-07-29 13:43:26 +02:00
|
|
|
ast::InlineAsmOperand::Out { reg, expr, .. }
|
|
|
|
|
| ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
|
2020-02-12 15:47:43 +00:00
|
|
|
outputs_sp.push(*op_sp);
|
|
|
|
|
have_real_output |= expr.is_some();
|
2021-07-29 13:43:26 +02:00
|
|
|
if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
|
|
|
|
|
regclass_outputs.push(*op_sp);
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2021-07-29 13:43:26 +02:00
|
|
|
ast::InlineAsmOperand::InOut { reg, .. } => {
|
2020-02-12 15:47:43 +00:00
|
|
|
outputs_sp.push(*op_sp);
|
|
|
|
|
have_real_output = true;
|
2021-07-29 13:43:26 +02:00
|
|
|
if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
|
|
|
|
|
regclass_outputs.push(*op_sp);
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2023-12-26 04:44:22 +00:00
|
|
|
ast::InlineAsmOperand::Label { .. } => {
|
|
|
|
|
labels_sp.push(*op_sp);
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
|
|
|
|
|
dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() });
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
if validated.options.contains(ast::InlineAsmOptions::NORETURN)
|
2024-10-11 00:26:21 +01:00
|
|
|
&& !outputs_sp.is_empty()
|
|
|
|
|
&& labels_sp.is_empty()
|
|
|
|
|
{
|
2023-12-18 11:15:13 +11:00
|
|
|
let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
|
2020-02-12 15:47:43 +00:00
|
|
|
// Bail out now since this is likely to confuse MIR
|
|
|
|
|
return Err(err);
|
|
|
|
|
}
|
2025-05-17 23:17:06 +02:00
|
|
|
if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
|
2023-12-26 04:44:22 +00:00
|
|
|
dcx.emit_err(errors::AsmMayUnwind { labels_sp });
|
|
|
|
|
}
|
2021-10-14 03:23:09 -04:00
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
if !validated.clobber_abis.is_empty() {
|
2024-08-04 16:42:37 +02:00
|
|
|
match asm_macro {
|
|
|
|
|
AsmMacro::GlobalAsm | AsmMacro::NakedAsm => {
|
|
|
|
|
let err = dcx.create_err(errors::AsmUnsupportedClobberAbi {
|
2025-05-17 23:17:06 +02:00
|
|
|
spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
|
2024-08-04 16:42:37 +02:00
|
|
|
macro_name: asm_macro.macro_name(),
|
|
|
|
|
});
|
2021-07-29 13:43:26 +02:00
|
|
|
|
2024-08-04 16:42:37 +02:00
|
|
|
// Bail out now since this is likely to confuse later stages
|
|
|
|
|
return Err(err);
|
|
|
|
|
}
|
|
|
|
|
AsmMacro::Asm => {
|
|
|
|
|
if !regclass_outputs.is_empty() {
|
|
|
|
|
dcx.emit_err(errors::AsmClobberNoReg {
|
|
|
|
|
spans: regclass_outputs,
|
2025-05-17 23:17:06 +02:00
|
|
|
clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
|
2024-08-04 16:42:37 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-29 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
|
2025-05-17 23:17:06 +02:00
|
|
|
Ok(validated)
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:27:33 +02:00
|
|
|
fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
|
2024-12-04 15:55:06 +11:00
|
|
|
p.expect(exp!(OpenParen))?;
|
|
|
|
|
|
2025-05-17 23:27:33 +02:00
|
|
|
let mut asm_options = Vec::new();
|
2025-04-29 22:43:46 +02:00
|
|
|
|
2024-12-04 15:55:06 +11:00
|
|
|
while !p.eat(exp!(CloseParen)) {
|
|
|
|
|
const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
|
|
|
|
|
(exp!(Pure), ast::InlineAsmOptions::PURE),
|
|
|
|
|
(exp!(Nomem), ast::InlineAsmOptions::NOMEM),
|
|
|
|
|
(exp!(Readonly), ast::InlineAsmOptions::READONLY),
|
|
|
|
|
(exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS),
|
|
|
|
|
(exp!(Noreturn), ast::InlineAsmOptions::NORETURN),
|
|
|
|
|
(exp!(Nostack), ast::InlineAsmOptions::NOSTACK),
|
|
|
|
|
(exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND),
|
|
|
|
|
(exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX),
|
|
|
|
|
(exp!(Raw), ast::InlineAsmOptions::RAW),
|
2024-07-25 10:05:31 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
'blk: {
|
2025-05-17 23:27:33 +02:00
|
|
|
for (exp, options) in OPTIONS {
|
2025-04-29 22:43:46 +02:00
|
|
|
// Gives a more accurate list of expected next tokens.
|
2025-05-17 23:27:33 +02:00
|
|
|
let kw_matched = if asm_macro.is_supported_option(options) {
|
2024-12-04 15:55:06 +11:00
|
|
|
p.eat_keyword(exp)
|
2024-08-04 16:42:37 +02:00
|
|
|
} else {
|
2024-12-04 15:55:06 +11:00
|
|
|
p.eat_keyword_noexpect(exp.kw)
|
2024-08-04 16:42:37 +02:00
|
|
|
};
|
2024-07-26 00:08:22 +02:00
|
|
|
|
|
|
|
|
if kw_matched {
|
2025-04-29 22:43:46 +02:00
|
|
|
let span = p.prev_token.span;
|
2025-05-17 23:27:33 +02:00
|
|
|
let span_with_comma =
|
2025-04-29 22:43:46 +02:00
|
|
|
if p.token == token::Comma { span.to(p.token.span) } else { span };
|
|
|
|
|
|
2025-05-17 23:27:33 +02:00
|
|
|
asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
|
2024-07-25 10:05:31 +02:00
|
|
|
break 'blk;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
return p.unexpected_any();
|
2020-03-19 07:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
// Allow trailing commas.
|
2024-12-04 15:55:06 +11:00
|
|
|
if p.eat(exp!(CloseParen)) {
|
2020-03-19 07:41:43 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2024-12-04 15:55:06 +11:00
|
|
|
p.expect(exp!(Comma))?;
|
2020-03-19 07:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:27:33 +02:00
|
|
|
Ok(asm_options)
|
2020-03-19 07:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
|
2024-12-04 15:55:06 +11:00
|
|
|
p.expect(exp!(OpenParen))?;
|
2021-07-29 13:43:26 +02:00
|
|
|
|
2024-12-04 15:55:06 +11:00
|
|
|
if p.eat(exp!(CloseParen)) {
|
2024-06-18 09:43:28 +00:00
|
|
|
return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
|
2021-10-14 03:23:09 -04:00
|
|
|
}
|
2021-07-29 13:43:26 +02:00
|
|
|
|
2021-10-14 03:23:09 -04:00
|
|
|
let mut new_abis = Vec::new();
|
2024-12-04 15:55:06 +11:00
|
|
|
while !p.eat(exp!(CloseParen)) {
|
2021-10-14 03:23:09 -04:00
|
|
|
match p.parse_str_lit() {
|
|
|
|
|
Ok(str_lit) => {
|
|
|
|
|
new_abis.push((str_lit.symbol_unescaped, str_lit.span));
|
|
|
|
|
}
|
|
|
|
|
Err(opt_lit) => {
|
|
|
|
|
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
|
2024-06-25 18:04:21 +08:00
|
|
|
return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
|
2021-10-14 03:23:09 -04:00
|
|
|
}
|
|
|
|
|
};
|
2021-07-29 13:43:26 +02:00
|
|
|
|
2021-10-14 03:23:09 -04:00
|
|
|
// Allow trailing commas
|
2024-12-04 15:55:06 +11:00
|
|
|
if p.eat(exp!(CloseParen)) {
|
2021-10-14 03:23:09 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2024-12-04 15:55:06 +11:00
|
|
|
p.expect(exp!(Comma))?;
|
2021-10-14 03:23:09 -04:00
|
|
|
}
|
2021-07-29 13:43:26 +02:00
|
|
|
|
2025-04-29 22:43:46 +02:00
|
|
|
Ok(new_abis)
|
2021-07-29 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-29 20:25:59 +02:00
|
|
|
fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
|
2024-12-04 15:55:06 +11:00
|
|
|
p.expect(exp!(OpenParen))?;
|
2020-09-27 19:47:52 +03:00
|
|
|
let result = match p.token.uninterpolate().kind {
|
2024-02-13 23:28:27 +00:00
|
|
|
token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
|
2020-03-19 07:41:43 +00:00
|
|
|
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
|
|
|
|
|
ast::InlineAsmRegOrRegClass::Reg(symbol)
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
2023-12-18 21:14:02 +11:00
|
|
|
return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister {
|
2023-06-21 19:01:53 +08:00
|
|
|
span: p.token.span,
|
|
|
|
|
}));
|
2020-03-19 07:41:43 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
p.bump();
|
2024-12-04 15:55:06 +11:00
|
|
|
p.expect(exp!(CloseParen))?;
|
2020-03-19 07:41:43 +00:00
|
|
|
Ok(result)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-25 22:22:11 +01:00
|
|
|
fn expand_preparsed_asm(
|
|
|
|
|
ecx: &mut ExtCtxt<'_>,
|
2024-09-05 13:45:26 +02:00
|
|
|
asm_macro: AsmMacro,
|
2025-05-17 23:17:06 +02:00
|
|
|
args: ValidatedAsmArgs,
|
2024-03-12 10:55:17 +08:00
|
|
|
) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
let mut template = vec![];
|
2020-02-12 15:47:43 +00:00
|
|
|
// Register operands are implicitly used since they are not allowed to be
|
|
|
|
|
// referenced in the template string.
|
|
|
|
|
let mut used = vec![false; args.operands.len()];
|
2023-05-02 16:42:36 +01:00
|
|
|
for pos in args.reg_args.iter() {
|
|
|
|
|
used[pos] = true;
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2020-06-11 01:27:48 +01:00
|
|
|
let named_pos: FxHashMap<usize, Symbol> =
|
|
|
|
|
args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect();
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
let mut line_spans = Vec::with_capacity(args.templates.len());
|
|
|
|
|
let mut curarg = 0;
|
|
|
|
|
|
2021-08-19 16:34:01 -04:00
|
|
|
let mut template_strs = Vec::with_capacity(args.templates.len());
|
|
|
|
|
|
2022-07-12 22:54:47 +02:00
|
|
|
for (i, template_expr) in args.templates.into_iter().enumerate() {
|
|
|
|
|
if i != 0 {
|
2024-06-24 16:17:59 +01:00
|
|
|
template.push(ast::InlineAsmTemplatePiece::String("\n".into()));
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let msg = "asm template must be a string literal";
|
|
|
|
|
let template_sp = template_expr.span;
|
2024-09-27 09:49:15 +05:30
|
|
|
let template_is_mac_call = matches!(template_expr.kind, ast::ExprKind::MacCall(_));
|
2024-12-31 04:15:40 +00:00
|
|
|
let ExprToSpannedString {
|
|
|
|
|
symbol: template_str,
|
|
|
|
|
style: template_style,
|
|
|
|
|
span: template_span,
|
2024-12-31 05:03:22 +00:00
|
|
|
..
|
2024-12-31 04:15:40 +00:00
|
|
|
} = {
|
2024-03-12 10:55:17 +08:00
|
|
|
let ExpandResult::Ready(mac) = expr_to_spanned_string(ecx, template_expr, msg) else {
|
|
|
|
|
return ExpandResult::Retry(());
|
|
|
|
|
};
|
|
|
|
|
match mac {
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
Ok(template_part) => template_part,
|
|
|
|
|
Err(err) => {
|
2024-03-12 10:55:17 +08:00
|
|
|
return ExpandResult::Ready(Err(match err {
|
2024-02-25 22:22:11 +01:00
|
|
|
Ok((err, _)) => err.emit(),
|
|
|
|
|
Err(guar) => guar,
|
2024-03-12 10:55:17 +08:00
|
|
|
}));
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
2024-03-12 10:55:17 +08:00
|
|
|
}
|
|
|
|
|
};
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
|
|
|
|
|
let str_style = match template_style {
|
|
|
|
|
ast::StrStyle::Cooked => None,
|
|
|
|
|
ast::StrStyle::Raw(raw) => Some(raw as usize),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
|
2021-08-19 16:34:01 -04:00
|
|
|
template_strs.push((
|
|
|
|
|
template_str,
|
2022-11-16 21:58:58 +00:00
|
|
|
template_snippet.as_deref().map(Symbol::intern),
|
2021-08-19 16:34:01 -04:00
|
|
|
template_sp,
|
|
|
|
|
));
|
2021-12-15 14:39:23 +11:00
|
|
|
let template_str = template_str.as_str();
|
2021-02-20 01:17:18 -05:00
|
|
|
|
2021-03-24 04:52:57 +00:00
|
|
|
if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch {
|
|
|
|
|
let find_span = |needle: &str| -> Span {
|
|
|
|
|
if let Some(snippet) = &template_snippet {
|
|
|
|
|
if let Some(pos) = snippet.find(needle) {
|
|
|
|
|
let end = pos
|
2021-11-07 10:33:27 +01:00
|
|
|
+ snippet[pos..]
|
2021-03-24 04:52:57 +00:00
|
|
|
.find(|c| matches!(c, '\n' | ';' | '\\' | '"'))
|
|
|
|
|
.unwrap_or(snippet[pos..].len() - 1);
|
|
|
|
|
let inner = InnerSpan::new(pos, end);
|
|
|
|
|
return template_sp.from_inner(inner);
|
2021-02-20 01:17:18 -05:00
|
|
|
}
|
|
|
|
|
}
|
2021-03-24 04:52:57 +00:00
|
|
|
template_sp
|
|
|
|
|
};
|
2021-02-20 01:17:18 -05:00
|
|
|
|
2021-03-24 04:52:57 +00:00
|
|
|
if template_str.contains(".intel_syntax") {
|
2024-05-20 17:47:54 +00:00
|
|
|
ecx.psess().buffer_lint(
|
2021-03-24 04:52:57 +00:00
|
|
|
lint::builtin::BAD_ASM_STYLE,
|
|
|
|
|
find_span(".intel_syntax"),
|
2021-07-14 18:24:12 -05:00
|
|
|
ecx.current_expansion.lint_node_id,
|
2024-04-14 20:11:14 +00:00
|
|
|
BuiltinLintDiag::AvoidUsingIntelSyntax,
|
2021-03-24 04:52:57 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if template_str.contains(".att_syntax") {
|
2024-05-20 17:47:54 +00:00
|
|
|
ecx.psess().buffer_lint(
|
2021-03-24 04:52:57 +00:00
|
|
|
lint::builtin::BAD_ASM_STYLE,
|
|
|
|
|
find_span(".att_syntax"),
|
2021-07-14 18:24:12 -05:00
|
|
|
ecx.current_expansion.lint_node_id,
|
2024-04-14 20:11:14 +00:00
|
|
|
BuiltinLintDiag::AvoidUsingAttSyntax,
|
2021-03-24 04:52:57 +00:00
|
|
|
);
|
2021-02-20 01:17:18 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 16:25:44 +01:00
|
|
|
// Don't treat raw asm as a format string.
|
|
|
|
|
if args.options.contains(ast::InlineAsmOptions::RAW) {
|
2024-06-24 16:17:59 +01:00
|
|
|
template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string().into()));
|
2021-06-24 16:25:44 +01:00
|
|
|
let template_num_lines = 1 + template_str.matches('\n').count();
|
|
|
|
|
line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
let mut parser = parse::Parser::new(
|
|
|
|
|
template_str,
|
|
|
|
|
str_style,
|
|
|
|
|
template_snippet,
|
|
|
|
|
false,
|
|
|
|
|
parse::ParseMode::InlineAsm,
|
|
|
|
|
);
|
|
|
|
|
parser.curarg = curarg;
|
|
|
|
|
|
|
|
|
|
let mut unverified_pieces = Vec::new();
|
|
|
|
|
while let Some(piece) = parser.next() {
|
|
|
|
|
if !parser.errors.is_empty() {
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
unverified_pieces.push(piece);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !parser.errors.is_empty() {
|
|
|
|
|
let err = parser.errors.remove(0);
|
2024-09-27 09:49:15 +05:30
|
|
|
let err_sp = if template_is_mac_call {
|
|
|
|
|
// If the template is a macro call we can't reliably point to the error's
|
|
|
|
|
// span so just use the template's span as the error span (fixes #129503)
|
|
|
|
|
template_span
|
|
|
|
|
} else {
|
|
|
|
|
template_span.from_inner(InnerSpan::new(err.span.start, err.span.end))
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-16 16:04:03 +10:00
|
|
|
let msg = format!("invalid asm template string: {}", err.description);
|
2023-12-18 20:54:03 +11:00
|
|
|
let mut e = ecx.dcx().struct_span_err(err_sp, msg);
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
e.span_label(err_sp, err.label + " in asm template string");
|
|
|
|
|
if let Some(note) = err.note {
|
Restrict `From<S>` for `{D,Subd}iagnosticMessage`.
Currently a `{D,Subd}iagnosticMessage` can be created from any type that
impls `Into<String>`. That includes `&str`, `String`, and `Cow<'static,
str>`, which are reasonable. It also includes `&String`, which is pretty
weird, and results in many places making unnecessary allocations for
patterns like this:
```
self.fatal(&format!(...))
```
This creates a string with `format!`, takes a reference, passes the
reference to `fatal`, which does an `into()`, which clones the
reference, doing a second allocation. Two allocations for a single
string, bleh.
This commit changes the `From` impls so that you can only create a
`{D,Subd}iagnosticMessage` from `&str`, `String`, or `Cow<'static,
str>`. This requires changing all the places that currently create one
from a `&String`. Most of these are of the `&format!(...)` form
described above; each one removes an unnecessary static `&`, plus an
allocation when executed. There are also a few places where the existing
use of `&String` was more reasonable; these now just use `clone()` at
the call site.
As well as making the code nicer and more efficient, this is a step
towards possibly using `Cow<'static, str>` in
`{D,Subd}iagnosticMessage::{Str,Eager}`. That would require changing
the `From<&'a str>` impls to `From<&'static str>`, which is doable, but
I'm not yet sure if it's worthwhile.
2023-04-20 13:26:58 +10:00
|
|
|
e.note(note);
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
|
|
|
|
if let Some((label, span)) = err.secondary_label {
|
2022-04-29 18:48:58 +02:00
|
|
|
let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
e.span_label(err_sp, label);
|
2020-05-06 14:46:01 +01:00
|
|
|
}
|
2024-02-25 22:22:11 +01:00
|
|
|
let guar = e.emit();
|
2024-03-12 10:55:17 +08:00
|
|
|
return ExpandResult::Ready(Err(guar));
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curarg = parser.curarg;
|
|
|
|
|
|
2022-04-29 18:48:58 +02:00
|
|
|
let mut arg_spans = parser
|
|
|
|
|
.arg_places
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end)));
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
for piece in unverified_pieces {
|
|
|
|
|
match piece {
|
2025-01-23 10:16:08 +01:00
|
|
|
parse::Piece::Lit(s) => {
|
2024-06-24 16:17:59 +01:00
|
|
|
template.push(ast::InlineAsmTemplatePiece::String(s.to_string().into()))
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
|
|
|
|
parse::Piece::NextArgument(arg) => {
|
|
|
|
|
let span = arg_spans.next().unwrap_or(template_sp);
|
|
|
|
|
|
|
|
|
|
let operand_idx = match arg.position {
|
2022-07-31 15:11:00 +00:00
|
|
|
parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
if idx >= args.operands.len()
|
|
|
|
|
|| named_pos.contains_key(&idx)
|
2023-05-02 16:42:36 +01:00
|
|
|
|| args.reg_args.contains(idx)
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
{
|
2023-07-25 22:00:13 +02:00
|
|
|
let msg = format!("invalid reference to argument at index {idx}");
|
2023-12-18 20:54:03 +11:00
|
|
|
let mut err = ecx.dcx().struct_span_err(span, msg);
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
err.span_label(span, "from here");
|
|
|
|
|
|
|
|
|
|
let positional_args = args.operands.len()
|
|
|
|
|
- args.named_args.len()
|
|
|
|
|
- args.reg_args.len();
|
|
|
|
|
let positional = if positional_args != args.operands.len() {
|
|
|
|
|
"positional "
|
|
|
|
|
} else {
|
|
|
|
|
""
|
|
|
|
|
};
|
|
|
|
|
let msg = match positional_args {
|
2023-07-25 22:00:13 +02:00
|
|
|
0 => format!("no {positional}arguments were given"),
|
|
|
|
|
1 => format!("there is 1 {positional}argument"),
|
|
|
|
|
x => format!("there are {x} {positional}arguments"),
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
};
|
Restrict `From<S>` for `{D,Subd}iagnosticMessage`.
Currently a `{D,Subd}iagnosticMessage` can be created from any type that
impls `Into<String>`. That includes `&str`, `String`, and `Cow<'static,
str>`, which are reasonable. It also includes `&String`, which is pretty
weird, and results in many places making unnecessary allocations for
patterns like this:
```
self.fatal(&format!(...))
```
This creates a string with `format!`, takes a reference, passes the
reference to `fatal`, which does an `into()`, which clones the
reference, doing a second allocation. Two allocations for a single
string, bleh.
This commit changes the `From` impls so that you can only create a
`{D,Subd}iagnosticMessage` from `&str`, `String`, or `Cow<'static,
str>`. This requires changing all the places that currently create one
from a `&String`. Most of these are of the `&format!(...)` form
described above; each one removes an unnecessary static `&`, plus an
allocation when executed. There are also a few places where the existing
use of `&String` was more reasonable; these now just use `clone()` at
the call site.
As well as making the code nicer and more efficient, this is a step
towards possibly using `Cow<'static, str>` in
`{D,Subd}iagnosticMessage::{Str,Eager}`. That would require changing
the `From<&'a str>` impls to `From<&'static str>`, which is doable, but
I'm not yet sure if it's worthwhile.
2023-04-20 13:26:58 +10:00
|
|
|
err.note(msg);
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
|
|
|
|
|
if named_pos.contains_key(&idx) {
|
|
|
|
|
err.span_label(args.operands[idx].1, "named argument");
|
|
|
|
|
err.span_note(
|
|
|
|
|
args.operands[idx].1,
|
|
|
|
|
"named arguments cannot be referenced by position",
|
|
|
|
|
);
|
2023-05-02 16:42:36 +01:00
|
|
|
} else if args.reg_args.contains(idx) {
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
err.span_label(
|
|
|
|
|
args.operands[idx].1,
|
|
|
|
|
"explicit register argument",
|
|
|
|
|
);
|
|
|
|
|
err.span_note(
|
|
|
|
|
args.operands[idx].1,
|
|
|
|
|
"explicit register arguments cannot be used in the asm template",
|
|
|
|
|
);
|
2022-12-16 04:20:34 +00:00
|
|
|
err.span_help(
|
|
|
|
|
args.operands[idx].1,
|
|
|
|
|
"use the register name directly in the assembly code",
|
|
|
|
|
);
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
|
|
|
|
err.emit();
|
|
|
|
|
None
|
2020-02-12 15:47:43 +00:00
|
|
|
} else {
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
Some(idx)
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-07-31 15:11:00 +00:00
|
|
|
parse::ArgumentNamed(name) => {
|
2022-04-29 18:48:58 +02:00
|
|
|
match args.named_args.get(&Symbol::intern(name)) {
|
|
|
|
|
Some(&idx) => Some(idx),
|
|
|
|
|
None => {
|
2022-07-31 15:11:00 +00:00
|
|
|
let span = arg.position_span;
|
2023-12-18 20:54:03 +11:00
|
|
|
ecx.dcx()
|
2024-06-25 18:04:21 +08:00
|
|
|
.create_err(errors::AsmNoMatchedArgumentName {
|
|
|
|
|
name: name.to_owned(),
|
|
|
|
|
span: template_span
|
2023-12-18 20:54:03 +11:00
|
|
|
.from_inner(InnerSpan::new(span.start, span.end)),
|
2024-06-25 18:04:21 +08:00
|
|
|
})
|
2023-12-18 20:54:03 +11:00
|
|
|
.emit();
|
2022-04-29 18:48:58 +02:00
|
|
|
None
|
|
|
|
|
}
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
}
|
2022-04-29 18:48:58 +02:00
|
|
|
}
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut chars = arg.format.ty.chars();
|
|
|
|
|
let mut modifier = chars.next();
|
|
|
|
|
if chars.next().is_some() {
|
|
|
|
|
let span = arg
|
|
|
|
|
.format
|
|
|
|
|
.ty_span
|
2022-04-29 18:48:58 +02:00
|
|
|
.map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
.unwrap_or(template_sp);
|
2023-12-18 20:54:03 +11:00
|
|
|
ecx.dcx().emit_err(errors::AsmModifierInvalid { span });
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
modifier = None;
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
if let Some(operand_idx) = operand_idx {
|
|
|
|
|
used[operand_idx] = true;
|
|
|
|
|
template.push(ast::InlineAsmTemplatePiece::Placeholder {
|
|
|
|
|
operand_idx,
|
|
|
|
|
modifier,
|
|
|
|
|
span,
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
|
|
|
|
|
if parser.line_spans.is_empty() {
|
|
|
|
|
let template_num_lines = 1 + template_str.matches('\n').count();
|
|
|
|
|
line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
|
|
|
|
|
} else {
|
2022-04-29 18:48:58 +02:00
|
|
|
line_spans.extend(
|
|
|
|
|
parser
|
|
|
|
|
.line_spans
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
|
|
|
|
|
);
|
asm: Allow multiple template strings; interpret them as newline-separated
Allow the `asm!` macro to accept a series of template arguments, and
interpret them as if they were concatenated with a '\n' between them.
This allows writing an `asm!` where each line of assembly appears in a
separate template string argument.
This syntax makes it possible for rustfmt to reliably format and indent
each line of assembly, without risking changes to the inside of a
template string. It also avoids the complexity of having the user
carefully format and indent a multi-line string (including where to put
the surrounding quotes), and avoids the extra indentation and lines of a
call to `concat!`.
For example, rewriting the second example from the [blog post on the new
inline assembly
syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html)
using multiple template strings:
```rust
fn main() {
let mut bits = [0u8; 64];
for value in 0..=1024u64 {
let popcnt;
unsafe {
asm!(
" popcnt {popcnt}, {v}",
"2:",
" blsi rax, {v}",
" jz 1f",
" xor {v}, rax",
" tzcnt rax, rax",
" stosb",
" jmp 2b",
"1:",
v = inout(reg) value => _,
popcnt = out(reg) popcnt,
out("rax") _, // scratch
inout("rdi") bits.as_mut_ptr() => _,
);
}
println!("bits of {}: {:?}", value, &bits[0..popcnt]);
}
}
```
Note that all the template strings must appear before all other
arguments; you cannot, for instance, provide a series of template
strings intermixed with the corresponding operands.
In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.
Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.
2020-06-14 23:33:55 -07:00
|
|
|
};
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
|
2020-06-11 01:27:48 +01:00
|
|
|
let mut unused_operands = vec![];
|
|
|
|
|
let mut help_str = String::new();
|
|
|
|
|
for (idx, used) in used.into_iter().enumerate() {
|
|
|
|
|
if !used {
|
|
|
|
|
let msg = if let Some(sym) = named_pos.get(&idx) {
|
|
|
|
|
help_str.push_str(&format!(" {{{}}}", sym));
|
|
|
|
|
"named argument never used"
|
2020-02-12 15:47:43 +00:00
|
|
|
} else {
|
2020-06-11 01:27:48 +01:00
|
|
|
help_str.push_str(&format!(" {{{}}}", idx));
|
|
|
|
|
"argument never used"
|
|
|
|
|
};
|
|
|
|
|
unused_operands.push((args.operands[idx].1, msg));
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-07 12:41:49 +02:00
|
|
|
match unused_operands[..] {
|
|
|
|
|
[] => {}
|
|
|
|
|
[(sp, msg)] => {
|
2024-01-03 17:03:10 +11:00
|
|
|
ecx.dcx()
|
|
|
|
|
.struct_span_err(sp, msg)
|
2024-01-09 09:08:49 +11:00
|
|
|
.with_span_label(sp, msg)
|
|
|
|
|
.with_help(format!(
|
2024-01-03 17:03:10 +11:00
|
|
|
"if this argument is intentionally unused, \
|
|
|
|
|
consider using it in an asm comment: `\"/*{help_str} */\"`"
|
|
|
|
|
))
|
|
|
|
|
.emit();
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
_ => {
|
2023-12-18 20:54:03 +11:00
|
|
|
let mut err = ecx.dcx().struct_span_err(
|
2020-02-12 15:47:43 +00:00
|
|
|
unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
|
|
|
|
|
"multiple unused asm arguments",
|
|
|
|
|
);
|
|
|
|
|
for (sp, msg) in unused_operands {
|
|
|
|
|
err.span_label(sp, msg);
|
|
|
|
|
}
|
Restrict `From<S>` for `{D,Subd}iagnosticMessage`.
Currently a `{D,Subd}iagnosticMessage` can be created from any type that
impls `Into<String>`. That includes `&str`, `String`, and `Cow<'static,
str>`, which are reasonable. It also includes `&String`, which is pretty
weird, and results in many places making unnecessary allocations for
patterns like this:
```
self.fatal(&format!(...))
```
This creates a string with `format!`, takes a reference, passes the
reference to `fatal`, which does an `into()`, which clones the
reference, doing a second allocation. Two allocations for a single
string, bleh.
This commit changes the `From` impls so that you can only create a
`{D,Subd}iagnosticMessage` from `&str`, `String`, or `Cow<'static,
str>`. This requires changing all the places that currently create one
from a `&String`. Most of these are of the `&format!(...)` form
described above; each one removes an unnecessary static `&`, plus an
allocation when executed. There are also a few places where the existing
use of `&String` was more reasonable; these now just use `clone()` at
the call site.
As well as making the code nicer and more efficient, this is a step
towards possibly using `Cow<'static, str>` in
`{D,Subd}iagnosticMessage::{Str,Eager}`. That would require changing
the `From<&'a str>` impls to `From<&'static str>`, which is doable, but
I'm not yet sure if it's worthwhile.
2023-04-20 13:26:58 +10:00
|
|
|
err.help(format!(
|
2020-06-11 01:27:48 +01:00
|
|
|
"if these arguments are intentionally unused, \
|
2023-07-25 22:00:13 +02:00
|
|
|
consider using them in an asm comment: `\"/*{help_str} */\"`"
|
2020-06-11 01:27:48 +01:00
|
|
|
));
|
2020-02-12 15:47:43 +00:00
|
|
|
err.emit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 10:55:17 +08:00
|
|
|
ExpandResult::Ready(Ok(ast::InlineAsm {
|
2024-09-10 14:42:17 +02:00
|
|
|
asm_macro,
|
2021-07-29 13:43:26 +02:00
|
|
|
template,
|
2021-08-19 16:34:01 -04:00
|
|
|
template_strs: template_strs.into_boxed_slice(),
|
2021-07-29 13:43:26 +02:00
|
|
|
operands: args.operands,
|
2021-10-14 03:23:09 -04:00
|
|
|
clobber_abis: args.clobber_abis,
|
2021-07-29 13:43:26 +02:00
|
|
|
options: args.options,
|
|
|
|
|
line_spans,
|
2024-03-12 10:55:17 +08:00
|
|
|
}))
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
|
|
|
|
|
2021-12-20 22:05:17 -06:00
|
|
|
pub(super) fn expand_asm<'cx>(
|
2020-02-12 15:47:43 +00:00
|
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
|
|
|
sp: Span,
|
|
|
|
|
tts: TokenStream,
|
2024-03-12 10:55:17 +08:00
|
|
|
) -> MacroExpanderResult<'cx> {
|
2024-08-04 16:42:37 +02:00
|
|
|
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
|
2021-04-11 20:51:28 +01:00
|
|
|
Ok(args) => {
|
2024-09-10 14:42:17 +02:00
|
|
|
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
|
2024-03-12 10:55:17 +08:00
|
|
|
return ExpandResult::Retry(());
|
|
|
|
|
};
|
|
|
|
|
let expr = match mac {
|
2024-02-25 22:22:11 +01:00
|
|
|
Ok(inline_asm) => P(ast::Expr {
|
2021-04-11 20:51:28 +01:00
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
|
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
|
|
|
|
|
span: sp,
|
|
|
|
|
attrs: ast::AttrVec::new(),
|
|
|
|
|
tokens: None,
|
2024-02-25 22:22:11 +01:00
|
|
|
}),
|
|
|
|
|
Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
|
2021-04-11 20:51:28 +01:00
|
|
|
};
|
|
|
|
|
MacEager::expr(expr)
|
|
|
|
|
}
|
Make `DiagnosticBuilder::emit` consuming.
This works for most of its call sites. This is nice, because `emit` very
much makes sense as a consuming operation -- indeed,
`DiagnosticBuilderState` exists to ensure no diagnostic is emitted
twice, but it uses runtime checks.
For the small number of call sites where a consuming emit doesn't work,
the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will
be removed in subsequent commits.)
Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes
consuming, while `delay_as_bug_without_consuming` is added (which will
also be removed in subsequent commits.)
All this requires significant changes to `DiagnosticBuilder`'s chaining
methods. Currently `DiagnosticBuilder` method chaining uses a
non-consuming `&mut self -> &mut Self` style, which allows chaining to
be used when the chain ends in `emit()`, like so:
```
struct_err(msg).span(span).emit();
```
But it doesn't work when producing a `DiagnosticBuilder` value,
requiring this:
```
let mut err = self.struct_err(msg);
err.span(span);
err
```
This style of chaining won't work with consuming `emit` though. For
that, we need to use to a `self -> Self` style. That also would allow
`DiagnosticBuilder` production to be chained, e.g.:
```
self.struct_err(msg).span(span)
```
However, removing the `&mut self -> &mut Self` style would require that
individual modifications of a `DiagnosticBuilder` go from this:
```
err.span(span);
```
to this:
```
err = err.span(span);
```
There are *many* such places. I have a high tolerance for tedious
refactorings, but even I gave up after a long time trying to convert
them all.
Instead, this commit has it both ways: the existing `&mut self -> Self`
chaining methods are kept, and new `self -> Self` chaining methods are
added, all of which have a `_mv` suffix (short for "move"). Changes to
the existing `forward!` macro lets this happen with very little
additional boilerplate code. I chose to add the suffix to the new
chaining methods rather than the existing ones, because the number of
changes required is much smaller that way.
This doubled chainging is a bit clumsy, but I think it is worthwhile
because it allows a *lot* of good things to subsequently happen. In this
commit, there are many `mut` qualifiers removed in places where
diagnostics are emitted without being modified. In subsequent commits:
- chaining can be used more, making the code more concise;
- more use of chaining also permits the removal of redundant diagnostic
APIs like `struct_err_with_code`, which can be replaced easily with
`struct_err` + `code_mv`;
- `emit_without_diagnostic` can be removed, which simplifies a lot of
machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
|
|
|
Err(err) => {
|
2024-02-25 22:22:11 +01:00
|
|
|
let guar = err.emit();
|
|
|
|
|
DummyResult::any(sp, guar)
|
2021-04-11 20:51:28 +01:00
|
|
|
}
|
2024-03-12 10:55:17 +08:00
|
|
|
})
|
2021-04-11 20:51:28 +01:00
|
|
|
}
|
|
|
|
|
|
2024-09-09 12:47:40 +02:00
|
|
|
pub(super) fn expand_naked_asm<'cx>(
|
|
|
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
|
|
|
sp: Span,
|
|
|
|
|
tts: TokenStream,
|
|
|
|
|
) -> MacroExpanderResult<'cx> {
|
2024-09-05 13:45:26 +02:00
|
|
|
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
|
2024-09-09 12:47:40 +02:00
|
|
|
Ok(args) => {
|
2024-09-10 14:42:17 +02:00
|
|
|
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
|
|
|
|
|
else {
|
2024-09-09 12:47:40 +02:00
|
|
|
return ExpandResult::Retry(());
|
|
|
|
|
};
|
|
|
|
|
let expr = match mac {
|
2024-09-05 19:45:40 +02:00
|
|
|
Ok(inline_asm) => P(ast::Expr {
|
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
|
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
|
|
|
|
|
span: sp,
|
|
|
|
|
attrs: ast::AttrVec::new(),
|
|
|
|
|
tokens: None,
|
|
|
|
|
}),
|
2024-09-09 12:47:40 +02:00
|
|
|
Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
|
|
|
|
|
};
|
|
|
|
|
MacEager::expr(expr)
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
let guar = err.emit();
|
|
|
|
|
DummyResult::any(sp, guar)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-20 22:05:17 -06:00
|
|
|
pub(super) fn expand_global_asm<'cx>(
|
2021-04-11 20:51:28 +01:00
|
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
|
|
|
sp: Span,
|
|
|
|
|
tts: TokenStream,
|
2024-03-12 10:55:17 +08:00
|
|
|
) -> MacroExpanderResult<'cx> {
|
2024-08-04 16:42:37 +02:00
|
|
|
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
|
2024-03-12 10:55:17 +08:00
|
|
|
Ok(args) => {
|
2024-09-10 14:42:17 +02:00
|
|
|
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
|
|
|
|
|
else {
|
2024-03-12 10:55:17 +08:00
|
|
|
return ExpandResult::Retry(());
|
|
|
|
|
};
|
|
|
|
|
match mac {
|
|
|
|
|
Ok(inline_asm) => MacEager::items(smallvec![P(ast::Item {
|
|
|
|
|
attrs: ast::AttrVec::new(),
|
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
|
kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
|
|
|
|
|
vis: ast::Visibility {
|
|
|
|
|
span: sp.shrink_to_lo(),
|
|
|
|
|
kind: ast::VisibilityKind::Inherited,
|
|
|
|
|
tokens: None,
|
|
|
|
|
},
|
|
|
|
|
span: sp,
|
2021-04-11 20:51:28 +01:00
|
|
|
tokens: None,
|
2024-03-12 10:55:17 +08:00
|
|
|
})]),
|
|
|
|
|
Err(guar) => DummyResult::any(sp, guar),
|
|
|
|
|
}
|
|
|
|
|
}
|
Make `DiagnosticBuilder::emit` consuming.
This works for most of its call sites. This is nice, because `emit` very
much makes sense as a consuming operation -- indeed,
`DiagnosticBuilderState` exists to ensure no diagnostic is emitted
twice, but it uses runtime checks.
For the small number of call sites where a consuming emit doesn't work,
the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will
be removed in subsequent commits.)
Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes
consuming, while `delay_as_bug_without_consuming` is added (which will
also be removed in subsequent commits.)
All this requires significant changes to `DiagnosticBuilder`'s chaining
methods. Currently `DiagnosticBuilder` method chaining uses a
non-consuming `&mut self -> &mut Self` style, which allows chaining to
be used when the chain ends in `emit()`, like so:
```
struct_err(msg).span(span).emit();
```
But it doesn't work when producing a `DiagnosticBuilder` value,
requiring this:
```
let mut err = self.struct_err(msg);
err.span(span);
err
```
This style of chaining won't work with consuming `emit` though. For
that, we need to use to a `self -> Self` style. That also would allow
`DiagnosticBuilder` production to be chained, e.g.:
```
self.struct_err(msg).span(span)
```
However, removing the `&mut self -> &mut Self` style would require that
individual modifications of a `DiagnosticBuilder` go from this:
```
err.span(span);
```
to this:
```
err = err.span(span);
```
There are *many* such places. I have a high tolerance for tedious
refactorings, but even I gave up after a long time trying to convert
them all.
Instead, this commit has it both ways: the existing `&mut self -> Self`
chaining methods are kept, and new `self -> Self` chaining methods are
added, all of which have a `_mv` suffix (short for "move"). Changes to
the existing `forward!` macro lets this happen with very little
additional boilerplate code. I chose to add the suffix to the new
chaining methods rather than the existing ones, because the number of
changes required is much smaller that way.
This doubled chainging is a bit clumsy, but I think it is worthwhile
because it allows a *lot* of good things to subsequently happen. In this
commit, there are many `mut` qualifiers removed in places where
diagnostics are emitted without being modified. In subsequent commits:
- chaining can be used more, making the code more concise;
- more use of chaining also permits the removal of redundant diagnostic
APIs like `struct_err_with_code`, which can be replaced easily with
`struct_err` + `code_mv`;
- `emit_without_diagnostic` can be removed, which simplifies a lot of
machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
|
|
|
Err(err) => {
|
2024-02-25 22:22:11 +01:00
|
|
|
let guar = err.emit();
|
|
|
|
|
DummyResult::any(sp, guar)
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|
2024-03-12 10:55:17 +08:00
|
|
|
})
|
2020-02-12 15:47:43 +00:00
|
|
|
}
|