Skip to content

Commit

Permalink
Rollup merge of rust-lang#128305 - folkertdev:asm-parser-unsupported-…
Browse files Browse the repository at this point in the history
…operand, r=Amanieu

improve error message when `global_asm!` uses `asm!` operands

follow-up to rust-lang#128207

what was

```
error: expected expression, found keyword `in`
 --> src/lib.rs:1:31
  |
1 | core::arch::global_asm!("{}", in(reg));
  |                               ^^ expected expression
```

becomes

```
error: the `in` operand cannot be used with `global_asm!`
  --> $DIR/parse-error.rs:150:19
   |
LL | global_asm!("{}", in(reg));
   |                   ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it
```

the span of the error is just the keyword, which means that we can't create a machine-applicable suggestion here. The alternative would be to attempt to parse the full operand, but then if there are syntax errors in the operand those would  be presented to the user, even though the parser already knows that the output won't be valid. Also that would require more complexity in the parser.

So I think this is a nice improvement at very low cost.
  • Loading branch information
matthiaskrgr authored Aug 4, 2024
2 parents 8f63e9f + 571f7b6 commit e98f00b
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 15 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ builtin_macros_format_use_positional = consider using a positional formatting ar
builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!`
builtin_macros_global_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `global_asm!`
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
builtin_macros_global_asm_unsupported_option = the `{$symbol}` option cannot be used with `global_asm!`
.label = the `{$symbol}` option is not meaningful for global-scoped inline assembly
.suggestion = remove this option
Expand Down
39 changes: 31 additions & 8 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@ pub struct AsmArgs {
pub options_spans: Vec<Span>,
}

/// 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
fn eat_operand_keyword<'a>(p: &mut Parser<'a>, symbol: Symbol, expect: bool) -> PResult<'a, bool> {
if expect {
Ok(p.eat_keyword(symbol))
} else {
let span = p.token.span;
if p.eat_keyword_noexpect(symbol) {
// in gets printed as `r#in` otherwise
let symbol = if symbol == kw::In { "in" } else { symbol.as_str() };
Err(p.dcx().create_err(errors::GlobalAsmUnsupportedOperand { span, symbol }))
} else {
Ok(false)
}
}
}

fn parse_args<'a>(
ecx: &ExtCtxt<'a>,
sp: Span,
Expand Down Expand Up @@ -105,23 +128,23 @@ pub fn parse_asm_args<'a>(
};

let mut explicit_reg = false;
let op = if !is_global_asm && p.eat_keyword(kw::In) {
let op = if eat_operand_keyword(p, kw::In, !is_global_asm)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::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 !is_global_asm && p.eat_keyword(sym::out) {
} else if eat_operand_keyword(p, sym::out, !is_global_asm)? {
let reg = parse_reg(p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if !is_global_asm && p.eat_keyword(sym::lateout) {
} else if eat_operand_keyword(p, sym::lateout, !is_global_asm)? {
let reg = parse_reg(p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if !is_global_asm && p.eat_keyword(sym::inout) {
} else if eat_operand_keyword(p, sym::inout, !is_global_asm)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
Expand All @@ -135,7 +158,7 @@ pub fn parse_asm_args<'a>(
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if !is_global_asm && p.eat_keyword(sym::inlateout) {
} else if eat_operand_keyword(p, sym::inlateout, !is_global_asm)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
Expand All @@ -149,6 +172,9 @@ pub fn parse_asm_args<'a>(
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: true }
}
} else if eat_operand_keyword(p, sym::label, !is_global_asm)? {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if p.eat_keyword(kw::Const) {
let anon_const = p.parse_expr_anon_const()?;
ast::InlineAsmOperand::Const { anon_const }
Expand All @@ -164,9 +190,6 @@ pub fn parse_asm_args<'a>(
path: path.clone(),
};
ast::InlineAsmOperand::Sym { sym }
} else if !is_global_asm && p.eat_keyword(sym::label) {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if allow_templates {
let template = p.parse_expr()?;
// If it can't possibly expand to a string, provide diagnostics here to include other
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,15 @@ pub(crate) struct GlobalAsmUnsupportedOption {
pub(crate) full_span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_global_asm_unsupported_operand)]
pub(crate) struct GlobalAsmUnsupportedOperand<'a> {
#[primary_span]
#[label]
pub(crate) span: Span,
pub(crate) symbol: &'a str,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_test_runner_invalid)]
pub(crate) struct TestRunnerInvalid {
Expand Down
15 changes: 13 additions & 2 deletions tests/ui/asm/parse-error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,16 @@ global_asm!(format!("{{{}}}", 0), const FOO);
//~^ ERROR asm template must be a string literal
global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
//~^ ERROR asm template must be a string literal
global_asm!("{}", label {});
//~^ ERROR expected operand, options, or additional template string

global_asm!("{}", in(reg));
//~^ ERROR the `in` operand cannot be used with `global_asm!`
global_asm!("{}", out(reg));
//~^ ERROR the `out` operand cannot be used with `global_asm!`
global_asm!("{}", lateout(reg));
//~^ ERROR the `lateout` operand cannot be used with `global_asm!`
global_asm!("{}", inout(reg));
//~^ ERROR the `inout` operand cannot be used with `global_asm!`
global_asm!("{}", inlateout(reg));
//~^ ERROR the `inlateout` operand cannot be used with `global_asm!`
global_asm!("{}", label(reg));
//~^ ERROR the `label` operand cannot be used with `global_asm!`
40 changes: 35 additions & 5 deletions tests/ui/asm/parse-error.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,41 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
|
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)

error: expected operand, options, or additional template string
--> $DIR/parse-error.rs:149:19
error: the `in` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:150:19
|
LL | global_asm!("{}", in(reg));
| ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it

error: the `out` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:152:19
|
LL | global_asm!("{}", out(reg));
| ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it

error: the `lateout` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:154:19
|
LL | global_asm!("{}", lateout(reg));
| ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it

error: the `inout` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:156:19
|
LL | global_asm!("{}", inout(reg));
| ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it

error: the `inlateout` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:158:19
|
LL | global_asm!("{}", inlateout(reg));
| ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it

error: the `label` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:160:19
|
LL | global_asm!("{}", label {});
| ^^^^^^^^ expected operand, options, or additional template string
LL | global_asm!("{}", label(reg));
| ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it

error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:39:37
Expand Down Expand Up @@ -441,6 +471,6 @@ help: consider using `const` instead of `let`
LL | const bar: /* Type */ = 0;
| ~~~~~ ++++++++++++

error: aborting due to 67 previous errors
error: aborting due to 72 previous errors

For more information about this error, try `rustc --explain E0435`.

0 comments on commit e98f00b

Please sign in to comment.