Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

improve error message when global_asm! uses asm! options #128207

Merged
merged 5 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2265,6 +2265,11 @@ bitflags::bitflags! {
}

impl InlineAsmOptions {
pub const COUNT: usize = Self::all().bits().count_ones() as usize;

pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);

pub fn human_readable_names(&self) -> Vec<&'static str> {
let mut options = vec![];

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ 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_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

builtin_macros_invalid_crate_attribute = invalid crate attribute

builtin_macros_multiple_default_attrs = multiple `#[default]` attributes
Expand Down
65 changes: 43 additions & 22 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@ fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
}

/// Report an invalid option error.
///
/// This function must be called immediately after the option token is parsed.
/// Otherwise, the suggestion will be incorrect.
fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
// Tool-only output
let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
p.dcx().emit_err(errors::GlobalAsmUnsupportedOption { span, symbol, full_span });
}

/// Try to set the provided option in the provided `AsmArgs`.
/// If it is already set, report a duplicate option error.
///
Expand All @@ -318,13 +328,16 @@ fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
fn try_set_option<'a>(
p: &Parser<'a>,
args: &mut AsmArgs,
is_global_asm: bool,
symbol: Symbol,
option: ast::InlineAsmOptions,
) {
if !args.options.contains(option) {
args.options |= option;
} else {
if is_global_asm && !ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) {
err_unsupported_option(p, symbol, p.prev_token.span);
} else if args.options.contains(option) {
err_duplicate_option(p, symbol, p.prev_token.span);
} else {
args.options |= option;
}
}

Expand All @@ -338,25 +351,33 @@ fn parse_options<'a>(
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;

while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
if !is_global_asm && p.eat_keyword(sym::pure) {
try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
} else if !is_global_asm && p.eat_keyword(sym::nomem) {
try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM);
} else if !is_global_asm && p.eat_keyword(sym::readonly) {
try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY);
} else if !is_global_asm && p.eat_keyword(sym::preserves_flags) {
try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS);
} else if !is_global_asm && p.eat_keyword(sym::noreturn) {
try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN);
} else if !is_global_asm && p.eat_keyword(sym::nostack) {
try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK);
} else if !is_global_asm && p.eat_keyword(sym::may_unwind) {
try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::MAY_UNWIND);
} else if p.eat_keyword(sym::att_syntax) {
try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
} else if p.eat_keyword(kw::Raw) {
try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::RAW);
} else {
const OPTIONS: [(Symbol, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
(sym::pure, ast::InlineAsmOptions::PURE),
(sym::nomem, ast::InlineAsmOptions::NOMEM),
(sym::readonly, ast::InlineAsmOptions::READONLY),
(sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS),
(sym::noreturn, ast::InlineAsmOptions::NORETURN),
(sym::nostack, ast::InlineAsmOptions::NOSTACK),
(sym::may_unwind, ast::InlineAsmOptions::MAY_UNWIND),
(sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX),
(kw::Raw, ast::InlineAsmOptions::RAW),
];

'blk: {
for (symbol, option) in OPTIONS {
let kw_matched =
if !is_global_asm || ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) {
p.eat_keyword(symbol)
} else {
p.eat_keyword_noexpect(symbol)
};

if kw_matched {
try_set_option(p, args, is_global_asm, symbol, option);
break 'blk;
}
}

return p.unexpected();
}

Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,17 @@ pub(crate) struct AsmOptAlreadyprovided {
pub(crate) full_span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_global_asm_unsupported_option)]
pub(crate) struct GlobalAsmUnsupportedOption {
#[primary_span]
#[label]
pub(crate) span: Span,
pub(crate) symbol: Symbol,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
folkertdev marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) full_span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_test_runner_invalid)]
pub(crate) struct TestRunnerInvalid {
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ impl<'a> Parser<'a> {

/// If the next token is the given keyword, eats it and returns `true`.
/// Otherwise, returns `false`. An expectation is also added for diagnostics purposes.
// Public for rustfmt usage.
// Public for rustc_builtin_macros and rustfmt usage.
#[inline]
pub fn eat_keyword(&mut self, kw: Symbol) -> bool {
if self.check_keyword(kw) {
Expand Down Expand Up @@ -631,8 +631,11 @@ impl<'a> Parser<'a> {
false
}

/// If the next token is the given keyword, eats it and returns `true`.
/// Otherwise, returns `false`. No expectation is added.
// Public for rustc_builtin_macros usage.
#[inline]
fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool {
pub fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool {
if self.token.is_keyword(kw) {
self.bump();
true
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_passes/src/naked_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
self.tcx.dcx().emit_err(NakedFunctionsOperands { unsupported_operands });
}

let supported_options =
InlineAsmOptions::RAW | InlineAsmOptions::NORETURN | InlineAsmOptions::ATT_SYNTAX;
let unsupported_options = asm.options.difference(supported_options);

let unsupported_options = asm.options.difference(InlineAsmOptions::NAKED_OPTIONS);
if !unsupported_options.is_empty() {
self.tcx.dcx().emit_err(NakedFunctionsAsmOptions {
span,
Expand Down
12 changes: 6 additions & 6 deletions tests/ui/asm/aarch64/bad-options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ fn main() {
}

global_asm!("", options(nomem));
//~^ ERROR expected one of
//~^ ERROR the `nomem` option cannot be used with `global_asm!`
global_asm!("", options(readonly));
//~^ ERROR expected one of
//~^ ERROR the `readonly` option cannot be used with `global_asm!`
global_asm!("", options(noreturn));
//~^ ERROR expected one of
//~^ ERROR the `noreturn` option cannot be used with `global_asm!`
global_asm!("", options(pure));
//~^ ERROR expected one of
//~^ ERROR the `pure` option cannot be used with `global_asm!`
global_asm!("", options(nostack));
//~^ ERROR expected one of
//~^ ERROR the `nostack` option cannot be used with `global_asm!`
global_asm!("", options(preserves_flags));
//~^ ERROR expected one of
//~^ ERROR the `preserves_flags` option cannot be used with `global_asm!`
24 changes: 12 additions & 12 deletions tests/ui/asm/aarch64/bad-options.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -36,41 +36,41 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"));
| |
| generic outputs

error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: the `nomem` option cannot be used with `global_asm!`
--> $DIR/bad-options.rs:28:25
|
LL | global_asm!("", options(nomem));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly

error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
error: the `readonly` option cannot be used with `global_asm!`
--> $DIR/bad-options.rs:30:25
|
LL | global_asm!("", options(readonly));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^ the `readonly` option is not meaningful for global-scoped inline assembly

error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
error: the `noreturn` option cannot be used with `global_asm!`
--> $DIR/bad-options.rs:32:25
|
LL | global_asm!("", options(noreturn));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly

error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
error: the `pure` option cannot be used with `global_asm!`
--> $DIR/bad-options.rs:34:25
|
LL | global_asm!("", options(pure));
| ^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^ the `pure` option is not meaningful for global-scoped inline assembly

error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
error: the `nostack` option cannot be used with `global_asm!`
--> $DIR/bad-options.rs:36:25
|
LL | global_asm!("", options(nostack));
| ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^ the `nostack` option is not meaningful for global-scoped inline assembly

error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
error: the `preserves_flags` option cannot be used with `global_asm!`
--> $DIR/bad-options.rs:38:25
|
LL | global_asm!("", options(preserves_flags));
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^^^^^^^^ the `preserves_flags` option is not meaningful for global-scoped inline assembly

error: invalid ABI for `clobber_abi`
--> $DIR/bad-options.rs:20:18
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/asm/aarch64/parse-error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,10 @@ global_asm!("", options(FOO));
//~^ ERROR expected one of
global_asm!("", options(nomem FOO));
//~^ ERROR expected one of
//~| ERROR the `nomem` option cannot be used with `global_asm!`
global_asm!("", options(nomem, FOO));
//~^ ERROR expected one of
//~| ERROR the `nomem` option cannot be used with `global_asm!`
global_asm!("{}", options(), const FOO);
global_asm!("", clobber_abi(FOO));
//~^ ERROR expected string literal
Expand Down
48 changes: 30 additions & 18 deletions tests/ui/asm/aarch64/parse-error.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -218,92 +218,104 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
LL | global_asm!("", options(FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`

error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: the `nomem` option cannot be used with `global_asm!`
--> $DIR/parse-error.rs:99:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly

error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
--> $DIR/parse-error.rs:101:25
error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:99:31
|
LL | global_asm!("", options(nomem FOO));
| ^^^ expected one of `)` or `,`

error: the `nomem` option cannot be used with `global_asm!`
--> $DIR/parse-error.rs:102:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly

error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
--> $DIR/parse-error.rs:102:32
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^ expected one of `)`, `att_syntax`, or `raw`

error: expected string literal
--> $DIR/parse-error.rs:104:29
--> $DIR/parse-error.rs:106:29
|
LL | global_asm!("", clobber_abi(FOO));
| ^^^ not a string literal

error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:106:33
--> $DIR/parse-error.rs:108:33
|
LL | global_asm!("", clobber_abi("C" FOO));
| ^^^ expected one of `)` or `,`

error: expected string literal
--> $DIR/parse-error.rs:108:34
--> $DIR/parse-error.rs:110:34
|
LL | global_asm!("", clobber_abi("C", FOO));
| ^^^ not a string literal

error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:110:19
--> $DIR/parse-error.rs:112:19
|
LL | global_asm!("{}", clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^

error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:112:28
--> $DIR/parse-error.rs:114:28
|
LL | global_asm!("", options(), clobber_abi("C"));
| ^^^^^^^^^^^^^^^^

error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:114:30
--> $DIR/parse-error.rs:116:30
|
LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^

error: duplicate argument named `a`
--> $DIR/parse-error.rs:116:35
--> $DIR/parse-error.rs:118:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ------------- ^^^^^^^^^^^^^ duplicate argument
| |
| previously here

error: argument never used
--> $DIR/parse-error.rs:116:35
--> $DIR/parse-error.rs:118:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ^^^^^^^^^^^^^ argument never used
|
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`

error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
--> $DIR/parse-error.rs:119:28
--> $DIR/parse-error.rs:121:28
|
LL | global_asm!("", options(), "");
| ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`

error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:121:30
--> $DIR/parse-error.rs:123:30
|
LL | global_asm!("{}", const FOO, "{}", const FOO);
| ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`

error: asm template must be a string literal
--> $DIR/parse-error.rs:123:13
--> $DIR/parse-error.rs:125:13
|
LL | global_asm!(format!("{{{}}}", 0), const FOO);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)

error: asm template must be a string literal
--> $DIR/parse-error.rs:125:20
--> $DIR/parse-error.rs:127:20
|
LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
| ^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -398,6 +410,6 @@ help: consider using `const` instead of `let`
LL | const bar: /* Type */ = 0;
| ~~~~~ ++++++++++++

error: aborting due to 57 previous errors
error: aborting due to 59 previous errors

For more information about this error, try `rustc --explain E0435`.
10 changes: 7 additions & 3 deletions tests/ui/asm/parse-error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,15 @@ global_asm!("{}", const);
global_asm!("{}", const(reg) FOO);
//~^ ERROR expected one of
global_asm!("", options(FOO));
//~^ ERROR expected one of
//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
global_asm!("", options(FOO,));
//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
global_asm!("", options(nomem FOO));
//~^ ERROR expected one of
//~^ ERROR the `nomem` option cannot be used with `global_asm!`
//~| ERROR expected one of `)` or `,`, found `FOO`
global_asm!("", options(nomem, FOO));
//~^ ERROR expected one of
//~^ ERROR the `nomem` option cannot be used with `global_asm!`
//~| ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
global_asm!("{}", options(), const FOO);
global_asm!("", clobber_abi(FOO));
//~^ ERROR expected string literal
Expand Down
Loading
Loading