Skip to content

Commit c1e29cb

Browse files
committed
disallow asm! in #[naked] functions
also disallow the `noreturn` option, and infer `naked_asm!` as `!`
1 parent 6c40f8f commit c1e29cb

30 files changed

+255
-263
lines changed

compiler/rustc_ast/src/ast.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -2272,7 +2272,7 @@ impl InlineAsmOptions {
22722272
pub const COUNT: usize = Self::all().bits().count_ones() as usize;
22732273

22742274
pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
2275-
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
2275+
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
22762276

22772277
pub fn human_readable_names(&self) -> Vec<&'static str> {
22782278
let mut options = vec![];
@@ -2418,11 +2418,40 @@ impl InlineAsmOperand {
24182418
}
24192419
}
24202420

2421+
#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic)]
2422+
pub enum AsmMacro {
2423+
/// The `asm!` macro
2424+
Asm,
2425+
/// The `global_asm!` macro
2426+
GlobalAsm,
2427+
/// The `naked_asm!` macro
2428+
NakedAsm,
2429+
}
2430+
2431+
impl AsmMacro {
2432+
pub const fn macro_name(&self) -> &'static str {
2433+
match self {
2434+
AsmMacro::Asm => "asm",
2435+
AsmMacro::GlobalAsm => "global_asm",
2436+
AsmMacro::NakedAsm => "naked_asm",
2437+
}
2438+
}
2439+
2440+
pub const fn is_supported_option(&self, option: InlineAsmOptions) -> bool {
2441+
match self {
2442+
AsmMacro::Asm => true,
2443+
AsmMacro::GlobalAsm => InlineAsmOptions::GLOBAL_OPTIONS.contains(option),
2444+
AsmMacro::NakedAsm => InlineAsmOptions::NAKED_OPTIONS.contains(option),
2445+
}
2446+
}
2447+
}
2448+
24212449
/// Inline assembly.
24222450
///
24232451
/// E.g., `asm!("NOP");`.
24242452
#[derive(Clone, Encodable, Decodable, Debug)]
24252453
pub struct InlineAsm {
2454+
pub asm_macro: AsmMacro,
24262455
pub template: Vec<InlineAsmTemplatePiece>,
24272456
pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>,
24282457
pub operands: Vec<(InlineAsmOperand, Span)>,

compiler/rustc_ast/src/mut_visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,7 @@ fn walk_anon_const<T: MutVisitor>(vis: &mut T, AnonConst { id, value }: &mut Ano
13881388
fn walk_inline_asm<T: MutVisitor>(vis: &mut T, asm: &mut InlineAsm) {
13891389
// FIXME: Visit spans inside all this currently ignored stuff.
13901390
let InlineAsm {
1391+
asm_macro: _,
13911392
template: _,
13921393
template_strs: _,
13931394
operands,

compiler/rustc_ast/src/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@ pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonCo
976976

977977
pub fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) -> V::Result {
978978
let InlineAsm {
979+
asm_macro: _,
979980
template: _,
980981
template_strs: _,
981982
operands,

compiler/rustc_ast_lowering/src/asm.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
474474
);
475475
let line_spans =
476476
self.arena.alloc_from_iter(asm.line_spans.iter().map(|span| self.lower_span(*span)));
477-
let hir_asm =
478-
hir::InlineAsm { template, template_strs, operands, options: asm.options, line_spans };
477+
let hir_asm = hir::InlineAsm {
478+
asm_macro: asm.asm_macro,
479+
template,
480+
template_strs,
481+
operands,
482+
options: asm.options,
483+
line_spans,
484+
};
479485
self.arena.alloc(hir_asm)
480486
}
481487
}

compiler/rustc_builtin_macros/src/asm.rs

+8-32
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use lint::BuiltinLintDiag;
33
use rustc_ast::ptr::P;
44
use rustc_ast::token::{self, Delimiter};
55
use rustc_ast::tokenstream::TokenStream;
6+
use rustc_ast::AsmMacro;
67
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
78
use rustc_errors::PResult;
89
use rustc_expand::base::*;
@@ -59,35 +60,6 @@ fn eat_operand_keyword<'a>(
5960
}
6061
}
6162

62-
// Public for rustfmt consumption.
63-
#[derive(Copy, Clone)]
64-
pub enum AsmMacro {
65-
/// The `asm!` macro
66-
Asm,
67-
/// The `global_asm!` macro
68-
GlobalAsm,
69-
/// The `naked_asm!` macro
70-
NakedAsm,
71-
}
72-
73-
impl AsmMacro {
74-
const fn macro_name(&self) -> &'static str {
75-
match self {
76-
AsmMacro::Asm => "asm",
77-
AsmMacro::GlobalAsm => "global_asm",
78-
AsmMacro::NakedAsm => "naked_asm",
79-
}
80-
}
81-
82-
const fn is_supported_option(&self, option: ast::InlineAsmOptions) -> bool {
83-
match self {
84-
AsmMacro::Asm => true,
85-
AsmMacro::GlobalAsm => ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option),
86-
AsmMacro::NakedAsm => ast::InlineAsmOptions::NAKED_OPTIONS.contains(option),
87-
}
88-
}
89-
}
90-
9163
fn parse_args<'a>(
9264
ecx: &ExtCtxt<'a>,
9365
sp: Span,
@@ -530,6 +502,7 @@ fn parse_reg<'a>(
530502

531503
fn expand_preparsed_asm(
532504
ecx: &mut ExtCtxt<'_>,
505+
asm_macro: AsmMacro,
533506
args: AsmArgs,
534507
) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
535508
let mut template = vec![];
@@ -820,6 +793,7 @@ fn expand_preparsed_asm(
820793
}
821794

822795
ExpandResult::Ready(Ok(ast::InlineAsm {
796+
asm_macro,
823797
template,
824798
template_strs: template_strs.into_boxed_slice(),
825799
operands: args.operands,
@@ -836,7 +810,7 @@ pub(super) fn expand_asm<'cx>(
836810
) -> MacroExpanderResult<'cx> {
837811
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
838812
Ok(args) => {
839-
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else {
813+
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
840814
return ExpandResult::Retry(());
841815
};
842816
let expr = match mac {
@@ -865,7 +839,8 @@ pub(super) fn expand_global_asm<'cx>(
865839
) -> MacroExpanderResult<'cx> {
866840
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
867841
Ok(args) => {
868-
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else {
842+
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
843+
else {
869844
return ExpandResult::Retry(());
870845
};
871846
match mac {
@@ -899,7 +874,8 @@ pub(super) fn expand_naked_asm<'cx>(
899874
) -> MacroExpanderResult<'cx> {
900875
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
901876
Ok(args) => {
902-
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else {
877+
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
878+
else {
903879
return ExpandResult::Retry(());
904880
};
905881
let expr = match mac {

compiler/rustc_error_codes/src/error_codes/E0787.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ pub extern "C" fn f() -> u32 {
1111
}
1212
```
1313

14-
The naked functions must be defined using a single inline assembly
15-
block.
14+
The naked function must be defined using a single `naked_asm!` assembly block.
1615

1716
The execution must never fall through past the end of the assembly
18-
code so the block must use `noreturn` option. The asm block can also
17+
code, so it must either return or diverge. The asm block can also
1918
use `att_syntax` and `raw` options, but others options are not allowed.
2019

2120
The asm block must not contain any operands other than `const` and

compiler/rustc_hir/src/hir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2914,6 +2914,7 @@ impl<'hir> InlineAsmOperand<'hir> {
29142914

29152915
#[derive(Debug, Clone, Copy, HashStable_Generic)]
29162916
pub struct InlineAsm<'hir> {
2917+
pub asm_macro: ast::AsmMacro,
29172918
pub template: &'hir [InlineAsmTemplatePiece],
29182919
pub template_strs: &'hir [(Symbol, Option<Symbol>, Span)],
29192920
pub operands: &'hir [(InlineAsmOperand<'hir>, Span)],

compiler/rustc_hir_typeck/src/expr.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -3307,7 +3307,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33073307
}
33083308

33093309
fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> {
3310-
let mut diverge = asm.options.contains(ast::InlineAsmOptions::NORETURN);
3310+
let mut diverge = match asm.asm_macro {
3311+
rustc_ast::AsmMacro::Asm => asm.options.contains(ast::InlineAsmOptions::NORETURN),
3312+
rustc_ast::AsmMacro::GlobalAsm => true,
3313+
rustc_ast::AsmMacro::NakedAsm => true,
3314+
};
33113315

33123316
for (op, _op_sp) in asm.operands {
33133317
match op {

compiler/rustc_passes/messages.ftl

+6-6
Original file line numberDiff line numberDiff line change
@@ -485,9 +485,9 @@ passes_must_use_no_effect =
485485
`#[must_use]` has no effect when applied to {$article} {$target}
486486
487487
passes_naked_functions_asm_block =
488-
naked functions must contain a single asm block
489-
.label_multiple_asm = multiple asm blocks are unsupported in naked functions
490-
.label_non_asm = non-asm is unsupported in naked functions
488+
naked functions must contain a single `naked_asm!` invocation
489+
.label_multiple_asm = multiple `naked_asm!` invocations are not allowed in naked functions
490+
.label_non_asm = not allowed in naked functions
491491
492492
passes_naked_functions_asm_options =
493493
asm options unsupported in naked functions: {$unsupported_options}
@@ -497,9 +497,9 @@ passes_naked_functions_incompatible_attribute =
497497
.label = the `{$attr}` attribute is incompatible with `#[naked]`
498498
.naked_attribute = function marked with `#[naked]` here
499499
500-
passes_naked_functions_must_use_noreturn =
501-
asm in naked functions must use `noreturn` option
502-
.suggestion = consider specifying that the asm block is responsible for returning from the function
500+
passes_naked_functions_must_naked_asm =
501+
the `asm!` macro is not allowed in naked functions
502+
.suggestion = consider using the `naked_asm!` macro instead
503503
504504
passes_naked_functions_operands =
505505
only `const` and `sym` operands are supported in naked functions

compiler/rustc_passes/src/errors.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1202,12 +1202,12 @@ pub(crate) struct NakedFunctionsAsmOptions {
12021202
}
12031203

12041204
#[derive(Diagnostic)]
1205-
#[diag(passes_naked_functions_must_use_noreturn, code = E0787)]
1206-
pub(crate) struct NakedFunctionsMustUseNoreturn {
1205+
#[diag(passes_naked_functions_must_naked_asm, code = E0787)]
1206+
pub(crate) struct NakedFunctionsMustNakedAsm {
12071207
#[primary_span]
12081208
pub span: Span,
1209-
#[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")]
1210-
pub last_span: Span,
1209+
#[suggestion(code = "naked_asm!", applicability = "machine-applicable")]
1210+
pub macro_span: Span,
12111211
}
12121212

12131213
#[derive(Diagnostic)]

compiler/rustc_passes/src/naked_functions.rs

+29-21
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ use rustc_middle::query::Providers;
1010
use rustc_middle::ty::TyCtxt;
1111
use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
1212
use rustc_span::symbol::sym;
13-
use rustc_span::Span;
13+
use rustc_span::{BytePos, Span};
1414
use rustc_target::spec::abi::Abi;
1515

1616
use crate::errors::{
17-
NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, NakedFunctionsMustUseNoreturn,
17+
NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, NakedFunctionsMustNakedAsm,
1818
NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, UndefinedNakedFunctionAbi,
1919
};
2020

@@ -29,8 +29,7 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
2929
continue;
3030
}
3131

32-
let naked = tcx.has_attr(def_id, sym::naked);
33-
if !naked {
32+
if !tcx.has_attr(def_id, sym::naked) {
3433
continue;
3534
}
3635

@@ -117,21 +116,29 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
117116
fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
118117
let mut this = CheckInlineAssembly { tcx, items: Vec::new() };
119118
this.visit_body(body);
120-
if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] {
119+
if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] {
121120
// Ok.
122121
} else {
123122
let mut must_show_error = false;
124-
let mut has_asm = false;
123+
let mut has_naked_asm = false;
125124
let mut has_err = false;
126125
let mut multiple_asms = vec![];
127126
let mut non_asms = vec![];
128127
for &(kind, span) in &this.items {
129128
match kind {
130-
ItemKind::Asm if has_asm => {
129+
ItemKind::NakedAsm if has_naked_asm => {
131130
must_show_error = true;
132131
multiple_asms.push(span);
133132
}
134-
ItemKind::Asm => has_asm = true,
133+
ItemKind::NakedAsm => has_naked_asm = true,
134+
ItemKind::InlineAsm => {
135+
has_err = true;
136+
137+
// the span that contains the `asm!` call,
138+
// so tooling can replace it with `naked_asm!`
139+
let macro_span = span.with_hi(span.lo() + BytePos("asm!".len() as u32));
140+
tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span, macro_span });
141+
}
135142
ItemKind::NonAsm => {
136143
must_show_error = true;
137144
non_asms.push(span);
@@ -160,7 +167,8 @@ struct CheckInlineAssembly<'tcx> {
160167

161168
#[derive(Copy, Clone)]
162169
enum ItemKind {
163-
Asm,
170+
NakedAsm,
171+
InlineAsm,
164172
NonAsm,
165173
Err,
166174
}
@@ -201,8 +209,18 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
201209
}
202210

203211
ExprKind::InlineAsm(asm) => {
204-
self.items.push((ItemKind::Asm, span));
205-
self.check_inline_asm(asm, span);
212+
match asm.asm_macro {
213+
rustc_ast::AsmMacro::Asm => {
214+
self.items.push((ItemKind::InlineAsm, span));
215+
}
216+
rustc_ast::AsmMacro::NakedAsm => {
217+
self.items.push((ItemKind::NakedAsm, span));
218+
self.check_inline_asm(asm, span);
219+
}
220+
rustc_ast::AsmMacro::GlobalAsm => {
221+
// not allowed in this position
222+
}
223+
}
206224
}
207225

208226
ExprKind::DropTemps(..) | ExprKind::Block(..) => {
@@ -246,16 +264,6 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
246264
.join(", "),
247265
});
248266
}
249-
250-
if !asm.options.contains(InlineAsmOptions::NORETURN) {
251-
let last_span = asm
252-
.operands
253-
.last()
254-
.map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1)
255-
.shrink_to_hi();
256-
257-
self.tcx.dcx().emit_err(NakedFunctionsMustUseNoreturn { span, last_span });
258-
}
259267
}
260268
}
261269

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use rustc_ast::ast;
2-
use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs, AsmMacro};
2+
use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs};
33

44
use crate::rewrite::RewriteContext;
55

66
#[allow(dead_code)]
77
pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<AsmArgs> {
88
let ts = mac.args.tokens.clone();
99
let mut parser = super::build_parser(context, ts);
10-
parse_asm_args(&mut parser, mac.span(), AsmMacro::Asm).ok()
10+
parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok()
1111
}

tests/codegen/naked-fn/aligned.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#![crate_type = "lib"]
66
#![feature(naked_functions, fn_align)]
7-
use std::arch::asm;
7+
use std::arch::naked_asm;
88

99
// CHECK: Function Attrs: naked
1010
// CHECK-NEXT: define{{.*}}void @naked_empty()
@@ -16,5 +16,5 @@ pub unsafe extern "C" fn naked_empty() {
1616
// CHECK-NEXT: start:
1717
// CHECK-NEXT: call void asm
1818
// CHECK-NEXT: unreachable
19-
asm!("ret", options(noreturn));
19+
naked_asm!("ret");
2020
}

0 commit comments

Comments
 (0)