about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2024-09-05 13:45:26 +0200
committerFolkert de Vries <folkert@folkertdev.nl>2024-10-06 18:12:25 +0200
commit562ec5a6fb469a360b72aa8ba0ce149598cc7975 (patch)
treeef26a0c513a413e9a7994389cdfd5796e5a69b68 /compiler
parent1a9c1cbf367fb2b6f04a0a81d7fae56485e29e7f (diff)
downloadrust-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.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs62
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0787.md5
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs6
-rw-r--r--compiler/rustc_passes/messages.ftl12
-rw-r--r--compiler/rustc_passes/src/errors.rs8
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs47
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 });
-        }
     }
 }