diff options
| author | Folkert de Vries <folkert@folkertdev.nl> | 2024-09-05 13:45:26 +0200 |
|---|---|---|
| committer | Folkert de Vries <folkert@folkertdev.nl> | 2024-10-06 18:12:25 +0200 |
| commit | 562ec5a6fb469a360b72aa8ba0ce149598cc7975 (patch) | |
| tree | ef26a0c513a413e9a7994389cdfd5796e5a69b68 /compiler | |
| parent | 1a9c1cbf367fb2b6f04a0a81d7fae56485e29e7f (diff) | |
| download | rust-562ec5a6fb469a360b72aa8ba0ce149598cc7975.tar.gz rust-562ec5a6fb469a360b72aa8ba0ce149598cc7975.zip | |
disallow `asm!` in `#[naked]` functions
also disallow the `noreturn` option, and infer `naked_asm!` as `!`
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_ast/src/ast.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_builtin_macros/src/asm.rs | 62 | ||||
| -rw-r--r-- | compiler/rustc_error_codes/src/error_codes/E0787.md | 5 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/expr.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_passes/messages.ftl | 12 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/errors.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/naked_functions.rs | 47 |
7 files changed, 66 insertions, 94 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 37f429cce44..cb715213176 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2278,7 +2278,7 @@ 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 const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW); pub fn human_readable_names(&self) -> Vec<&'static str> { let mut options = vec![]; @@ -2434,6 +2434,24 @@ pub enum AsmMacro { NakedAsm, } +impl AsmMacro { + pub const fn macro_name(&self) -> &'static str { + match self { + AsmMacro::Asm => "asm", + AsmMacro::GlobalAsm => "global_asm", + AsmMacro::NakedAsm => "naked_asm", + } + } + + pub const fn is_supported_option(&self, option: InlineAsmOptions) -> bool { + match self { + AsmMacro::Asm => true, + AsmMacro::GlobalAsm => InlineAsmOptions::GLOBAL_OPTIONS.contains(option), + AsmMacro::NakedAsm => InlineAsmOptions::NAKED_OPTIONS.contains(option), + } + } +} + /// Inline assembly. /// /// E.g., `asm!("NOP");`. diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 2019c42e296..515ac17f70a 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -60,35 +60,6 @@ fn eat_operand_keyword<'a>( } } -// Public for rustfmt consumption. -#[derive(Copy, Clone)] -pub enum AsmMacro { - /// The `asm!` macro - Asm, - /// The `global_asm!` macro - GlobalAsm, - /// The `naked_asm!` macro - NakedAsm, -} - -impl AsmMacro { - const fn macro_name(&self) -> &'static str { - match self { - AsmMacro::Asm => "asm", - AsmMacro::GlobalAsm => "global_asm", - AsmMacro::NakedAsm => "naked_asm", - } - } - - const fn is_supported_option(&self, option: ast::InlineAsmOptions) -> bool { - match self { - AsmMacro::Asm => true, - AsmMacro::GlobalAsm => ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option), - AsmMacro::NakedAsm => ast::InlineAsmOptions::NAKED_OPTIONS.contains(option), - } - } -} - fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -529,7 +500,7 @@ fn parse_reg<'a>( fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, - asm_macro: ast::AsmMacro, + asm_macro: AsmMacro, args: AsmArgs, ) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> { let mut template = vec![]; @@ -872,7 +843,7 @@ pub(super) fn expand_naked_asm<'cx>( sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(match parse_args(ecx, sp, tts, false) { + ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) { Ok(args) => { let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args) else { @@ -940,32 +911,3 @@ pub(super) fn expand_global_asm<'cx>( } }) } - -pub(super) fn expand_naked_asm<'cx>( - ecx: &'cx mut ExtCtxt<'_>, - sp: Span, - tts: TokenStream, -) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) { - Ok(args) => { - let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else { - return ExpandResult::Retry(()); - }; - let expr = match mac { - 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, - }), - Err(guar) => DummyResult::raw_expr(sp, Some(guar)), - }; - MacEager::expr(expr) - } - Err(err) => { - let guar = err.emit(); - DummyResult::any(sp, guar) - } - }) -} diff --git a/compiler/rustc_error_codes/src/error_codes/E0787.md b/compiler/rustc_error_codes/src/error_codes/E0787.md index cee50829270..f5c5faa066b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0787.md +++ b/compiler/rustc_error_codes/src/error_codes/E0787.md @@ -11,11 +11,10 @@ pub extern "C" fn f() -> u32 { } ``` -The naked functions must be defined using a single inline assembly -block. +The naked function must be defined using a single `naked_asm!` assembly block. The execution must never fall through past the end of the assembly -code so the block must use `noreturn` option. The asm block can also +code, so it must either return or diverge. The asm block can also use `att_syntax` and `raw` options, but others options are not allowed. The asm block must not contain any operands other than `const` and diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2d718295374..5c2f39f879c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3507,7 +3507,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { - let mut diverge = asm.options.contains(ast::InlineAsmOptions::NORETURN); + let mut diverge = match asm.asm_macro { + rustc_ast::AsmMacro::Asm => asm.options.contains(ast::InlineAsmOptions::NORETURN), + rustc_ast::AsmMacro::GlobalAsm => true, + rustc_ast::AsmMacro::NakedAsm => true, + }; for (op, _op_sp) in asm.operands { match op { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index f2682acf8aa..1b1d1b1fb72 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -488,9 +488,9 @@ passes_naked_asm_outside_naked_fn = the `naked_asm!` macro can only be used in functions marked with `#[naked]` passes_naked_functions_asm_block = - naked functions must contain a single asm block - .label_multiple_asm = multiple asm blocks are unsupported in naked functions - .label_non_asm = non-asm is unsupported in naked functions + naked functions must contain a single `naked_asm!` invocation + .label_multiple_asm = multiple `naked_asm!` invocations are not allowed in naked functions + .label_non_asm = not allowed in naked functions passes_naked_functions_asm_options = asm options unsupported in naked functions: {$unsupported_options} @@ -500,9 +500,9 @@ passes_naked_functions_incompatible_attribute = .label = the `{$attr}` attribute is incompatible with `#[naked]` .naked_attribute = function marked with `#[naked]` here -passes_naked_functions_must_use_noreturn = - asm in naked functions must use `noreturn` option - .suggestion = consider specifying that the asm block is responsible for returning from the function +passes_naked_functions_must_naked_asm = + the `asm!` macro is not allowed in naked functions + .suggestion = consider using the `naked_asm!` macro instead passes_naked_functions_operands = only `const` and `sym` operands are supported in naked functions diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 29a087bf759..c46768ace53 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1202,12 +1202,12 @@ pub(crate) struct NakedFunctionsAsmOptions { } #[derive(Diagnostic)] -#[diag(passes_naked_functions_must_use_noreturn, code = E0787)] -pub(crate) struct NakedFunctionsMustUseNoreturn { +#[diag(passes_naked_functions_must_naked_asm, code = E0787)] +pub(crate) struct NakedFunctionsMustNakedAsm { #[primary_span] pub span: Span, - #[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")] - pub last_span: Span, + #[suggestion(code = "naked_asm!", applicability = "machine-applicable")] + pub macro_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 8d3f7e0231f..6046c728430 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -10,13 +10,13 @@ use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{BytePos, Span}; use rustc_target::spec::abi::Abi; use crate::errors::{ NakedAsmOutsideNakedFn, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, - NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, + NakedFunctionsMustNakedAsm, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, UndefinedNakedFunctionAbi, }; @@ -121,21 +121,29 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) { let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; this.visit_body(body); - if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] { + if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] { // Ok. } else { let mut must_show_error = false; - let mut has_asm = false; + let mut has_naked_asm = false; let mut has_err = false; let mut multiple_asms = vec![]; let mut non_asms = vec![]; for &(kind, span) in &this.items { match kind { - ItemKind::Asm if has_asm => { + ItemKind::NakedAsm if has_naked_asm => { must_show_error = true; multiple_asms.push(span); } - ItemKind::Asm => has_asm = true, + ItemKind::NakedAsm => has_naked_asm = true, + ItemKind::InlineAsm => { + has_err = true; + + // the span that contains the `asm!` call, + // so tooling can replace it with `naked_asm!` + let macro_span = span.with_hi(span.lo() + BytePos("asm!".len() as u32)); + tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span, macro_span }); + } ItemKind::NonAsm => { must_show_error = true; non_asms.push(span); @@ -164,7 +172,8 @@ struct CheckInlineAssembly<'tcx> { #[derive(Copy, Clone)] enum ItemKind { - Asm, + NakedAsm, + InlineAsm, NonAsm, Err, } @@ -205,8 +214,18 @@ impl<'tcx> CheckInlineAssembly<'tcx> { } ExprKind::InlineAsm(asm) => { - self.items.push((ItemKind::Asm, span)); - self.check_inline_asm(asm, span); + match asm.asm_macro { + rustc_ast::AsmMacro::Asm => { + self.items.push((ItemKind::InlineAsm, span)); + } + rustc_ast::AsmMacro::NakedAsm => { + self.items.push((ItemKind::NakedAsm, span)); + self.check_inline_asm(asm, span); + } + rustc_ast::AsmMacro::GlobalAsm => { + // not allowed in this position + } + } } ExprKind::DropTemps(..) | ExprKind::Block(..) => { @@ -250,16 +269,6 @@ impl<'tcx> CheckInlineAssembly<'tcx> { .join(", "), }); } - - if !asm.options.contains(InlineAsmOptions::NORETURN) { - let last_span = asm - .operands - .last() - .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1) - .shrink_to_hi(); - - self.tcx.dcx().emit_err(NakedFunctionsMustUseNoreturn { span, last_span }); - } } } |
