diff options
| author | bors <bors@rust-lang.org> | 2020-12-07 22:47:20 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2020-12-07 22:47:20 +0000 |
| commit | bda05cc471f8cf4a3c45f587f87d7b01a8343c3c (patch) | |
| tree | 598e1d1abfd047276d82817c8d2c5e15208c87a1 | |
| parent | 3d6705aa5abffe94c83bf09af8c3ba3c599845fc (diff) | |
| parent | 8065dabd171f7807b47a461b17f443df47a0cad5 (diff) | |
| download | rust-bda05cc471f8cf4a3c45f587f87d7b01a8343c3c.tar.gz rust-bda05cc471f8cf4a3c45f587f87d7b01a8343c3c.zip | |
Auto merge of #79653 - tmiasko:naked-functions, r=Amanieu
Validate naked functions definitions Validate that naked functions are defined in terms of a single inline assembly block that uses only `const` and `sym` operands and has `noreturn` option. Implemented as future incompatibility lint with intention to migrate it into hard error. When it becomes a hard error it will ensure that naked functions are either unsafe or contain an unsafe block around the inline assembly. It will guarantee that naked functions do not reference functions parameters (obsoleting part of existing checks from #79411). It will limit the definitions of naked functions to what can be reliably supported. It will also reject naked functions implemented using legacy LLVM style assembly since it cannot satisfy those conditions. https://github.com/rust-lang/rfcs/pull/2774 https://github.com/rust-lang/rfcs/pull/2972
23 files changed, 773 insertions, 139 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index e0e78a4d609..6ad6e664316 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1307,7 +1307,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) } } }; - Some(op) + Some((op, *op_sp)) }) .collect(); @@ -1326,7 +1326,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } = *p { let op_sp = asm.operands[operand_idx].1; - match &operands[operand_idx] { + match &operands[operand_idx].0 { hir::InlineAsmOperand::In { reg, .. } | hir::InlineAsmOperand::Out { reg, .. } | hir::InlineAsmOperand::InOut { reg, .. } @@ -1385,8 +1385,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut used_input_regs = FxHashMap::default(); let mut used_output_regs = FxHashMap::default(); let mut required_features: Vec<&str> = vec![]; - for (idx, op) in operands.iter().enumerate() { - let op_sp = asm.operands[idx].1; + for (idx, &(ref op, op_sp)) in operands.iter().enumerate() { if let Some(reg) = op.reg() { // Make sure we don't accidentally carry features from the // previous iteration. @@ -1458,8 +1457,7 @@ impl<'hir> LoweringContext<'_, 'hir> { skip = true; let idx2 = *o.get(); - let op2 = &operands[idx2]; - let op_sp2 = asm.operands[idx2].1; + let &(ref op2, op_sp2) = &operands[idx2]; let reg2 = match op2.reg() { Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r, _ => unreachable!(), diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 44dfdcfccab..c7dc66b70fe 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -14,7 +14,7 @@ macro_rules! arena_types { // HIR types [few] hir_krate: rustc_hir::Crate<$tcx>, [] arm: rustc_hir::Arm<$tcx>, - [] asm_operand: rustc_hir::InlineAsmOperand<$tcx>, + [] asm_operand: (rustc_hir::InlineAsmOperand<$tcx>, Span), [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_ast::Attribute, [] block: rustc_hir::Block<$tcx>, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 44dc6673564..280e863d474 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2143,7 +2143,7 @@ impl<'hir> InlineAsmOperand<'hir> { #[derive(Debug, HashStable_Generic)] pub struct InlineAsm<'hir> { pub template: &'hir [InlineAsmTemplatePiece], - pub operands: &'hir [InlineAsmOperand<'hir>], + pub operands: &'hir [(InlineAsmOperand<'hir>, Span)], pub options: InlineAsmOptions, pub line_spans: &'hir [Span], } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 3e8fc689acf..3c330c5d6c5 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1191,7 +1191,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) walk_list!(visitor, visit_expr, optional_expression); } ExprKind::InlineAsm(ref asm) => { - for op in asm.operands { + for (op, _op_sp) in asm.operands { match op { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 25b09d76295..597c55b4bd7 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1462,7 +1462,7 @@ impl<'a> State<'a> { let mut args = vec![]; args.push(AsmArg::Template(ast::InlineAsmTemplatePiece::to_string(&a.template))); - args.extend(a.operands.iter().map(|o| AsmArg::Operand(o))); + args.extend(a.operands.iter().map(|(o, _)| AsmArg::Operand(o))); if !a.options.is_empty() { args.push(AsmArg::Options(a.options)); } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index fa82dce0ae2..a9358c9610a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2742,6 +2742,50 @@ declare_lint! { "detects deprecation attributes with no effect", } +declare_lint! { + /// The `unsupported_naked_functions` lint detects naked function + /// definitions that are unsupported but were previously accepted. + /// + /// ### Example + /// + /// ```rust + /// #![feature(naked_functions)] + /// + /// #[naked] + /// pub fn f() -> u32 { + /// 42 + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The naked functions must be defined using a single inline 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 + /// use `att_syntax` option, but other options are not allowed. + /// + /// The asm block must not contain any operands other than `const` and + /// `sym`. Additionally, naked function should specify a non-Rust ABI. + /// + /// While other definitions of naked functions were previously accepted, + /// they are unsupported and might not work reliably. This is a + /// [future-incompatible] lint that will transition into hard error in + /// the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub UNSUPPORTED_NAKED_FUNCTIONS, + Warn, + "unsupported naked function definitions", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #32408 <https://github.com/rust-lang/rust/issues/32408>", + edition: None, + }; +} + declare_tool_lint! { pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL, Deny, @@ -2832,6 +2876,7 @@ declare_lint_pass! { UNINHABITED_STATIC, FUNCTION_ITEM_REFERENCES, USELESS_DEPRECATED, + UNSUPPORTED_NAKED_FUNCTIONS, ] } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index e404afeb698..e934d8ed9da 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -408,7 +408,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( operands: asm .operands .iter() - .map(|op| { + .map(|(op, _op_sp)| { match *op { hir::InlineAsmOperand::In { reg, ref expr } => { InlineAsmOperand::In { reg, expr: expr.to_ref() } diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 956be925be8..711e8e87c6c 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -347,7 +347,7 @@ impl ExprVisitor<'tcx> { } fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { - for (idx, op) in asm.operands.iter().enumerate() { + for (idx, (op, _op_sp)) in asm.operands.iter().enumerate() { match *op { hir::InlineAsmOperand::In { reg, ref expr } => { self.check_asm_operand_type(idx, reg, expr, asm.template, None); diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index debb873beb9..a64c6fa319c 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1174,7 +1174,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { }; // Do a first pass for writing outputs only - for op in asm.operands.iter().rev() { + for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Const { .. } @@ -1197,7 +1197,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Then do a second pass for inputs let mut succ = succ; - for op in asm.operands.iter().rev() { + for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr, .. } @@ -1454,7 +1454,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { } hir::ExprKind::InlineAsm(ref asm) => { - for op in asm.operands { + for (op, _op_sp) in asm.operands { match op { hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 6ef45cdd391..5b50ef8627b 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -1,10 +1,16 @@ +//! Checks validity of naked functions. + +use rustc_ast::InlineAsmOptions; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{ErasedMap, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_session::lint::builtin::UNSUPPORTED_NAKED_FUNCTIONS; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_target::spec::abi::Abi; fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { tcx.hir().visit_item_likes_in_module( @@ -33,27 +39,52 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { fk: FnKind<'v>, _fd: &'tcx hir::FnDecl<'tcx>, body_id: hir::BodyId, - _span: Span, - _hir_id: hir::HirId, + span: Span, + hir_id: HirId, ) { + let ident_span; + let fn_header; + match fk { - // Rejected during attribute check. Do not validate further. - FnKind::Closure(..) => return, - FnKind::ItemFn(..) | FnKind::Method(..) => {} + FnKind::Closure(..) => { + // Closures with a naked attribute are rejected during attribute + // check. Don't validate them any further. + return; + } + FnKind::ItemFn(ident, _, ref header, ..) => { + ident_span = ident.span; + fn_header = header; + } + + FnKind::Method(ident, ref sig, ..) => { + ident_span = ident.span; + fn_header = &sig.header; + } } let naked = fk.attrs().iter().any(|attr| attr.has_name(sym::naked)); if naked { let body = self.tcx.hir().body(body_id); - check_params(self.tcx, body); - check_body(self.tcx, body); + check_abi(self.tcx, hir_id, fn_header.abi, ident_span); + check_no_patterns(self.tcx, body.params); + check_no_parameters_use(self.tcx, body); + check_asm(self.tcx, hir_id, body, span); } } } +/// Checks that function uses non-Rust ABI. +fn check_abi(tcx: TyCtxt<'_>, hir_id: HirId, abi: Abi, fn_ident_span: Span) { + if abi == Abi::Rust { + tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_ident_span, |lint| { + lint.build("Rust ABI is unsupported in naked functions").emit(); + }); + } +} + /// Checks that parameters don't use patterns. Mirrors the checks for function declarations. -fn check_params(tcx: TyCtxt<'_>, body: &hir::Body<'_>) { - for param in body.params { +fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) { + for param in params { match param.pat.kind { hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {} @@ -69,23 +100,23 @@ fn check_params(tcx: TyCtxt<'_>, body: &hir::Body<'_>) { } } -/// Checks that function parameters aren't referenced in the function body. -fn check_body<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) { +/// Checks that function parameters aren't used in the function body. +fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) { let mut params = hir::HirIdSet::default(); for param in body.params { param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| { params.insert(hir_id); }); } - CheckBody { tcx, params }.visit_body(body); + CheckParameters { tcx, params }.visit_body(body); } -struct CheckBody<'tcx> { +struct CheckParameters<'tcx> { tcx: TyCtxt<'tcx>, params: hir::HirIdSet, } -impl<'tcx> Visitor<'tcx> for CheckBody<'tcx> { +impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { @@ -103,11 +134,189 @@ impl<'tcx> Visitor<'tcx> for CheckBody<'tcx> { .sess .struct_span_err( expr.span, - "use of parameters not allowed inside naked functions", + "referencing function parameters is not allowed in naked functions", ) + .help("follow the calling convention in asm block to use parameters") .emit(); + return; } } hir::intravisit::walk_expr(self, expr); } } + +/// Checks that function body contains a single inline assembly block. +fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId, body: &'tcx hir::Body<'tcx>, fn_span: Span) { + let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; + this.visit_body(body); + if let &[(ItemKind::Asm, _)] = &this.items[..] { + // Ok. + } else { + tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_span, |lint| { + let mut diag = lint.build("naked functions must contain a single asm block"); + let mut has_asm = false; + for &(kind, span) in &this.items { + match kind { + ItemKind::Asm if has_asm => { + diag.span_label( + span, + "multiple asm blocks are unsupported in naked functions", + ); + } + ItemKind::Asm => has_asm = true, + ItemKind::NonAsm => { + diag.span_label(span, "non-asm is unsupported in naked functions"); + } + } + } + diag.emit(); + }); + } +} + +struct CheckInlineAssembly<'tcx> { + tcx: TyCtxt<'tcx>, + items: Vec<(ItemKind, Span)>, +} + +#[derive(Copy, Clone)] +enum ItemKind { + Asm, + NonAsm, +} + +impl<'tcx> CheckInlineAssembly<'tcx> { + fn check_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) { + match expr.kind { + ExprKind::Box(..) + | ExprKind::ConstBlock(..) + | ExprKind::Array(..) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Tup(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Lit(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Path(..) + | ExprKind::AddrOf(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::Struct(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) => { + self.items.push((ItemKind::NonAsm, span)); + } + + ExprKind::InlineAsm(ref asm) => { + self.items.push((ItemKind::Asm, span)); + self.check_inline_asm(expr.hir_id, asm, span); + } + + ExprKind::LlvmInlineAsm(..) => { + self.items.push((ItemKind::Asm, span)); + self.tcx.struct_span_lint_hir( + UNSUPPORTED_NAKED_FUNCTIONS, + expr.hir_id, + span, + |lint| { + lint.build( + "the LLVM-style inline assembly is unsupported in naked functions", + ) + .help("use the new asm! syntax specified in RFC 2873") + .emit(); + }, + ); + } + + ExprKind::DropTemps(..) | ExprKind::Block(..) | ExprKind::Err => { + hir::intravisit::walk_expr(self, expr); + } + } + } + + fn check_inline_asm(&self, hir_id: HirId, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) { + let unsupported_operands: Vec<Span> = asm + .operands + .iter() + .filter_map(|&(ref op, op_sp)| match op { + InlineAsmOperand::Const { .. } | InlineAsmOperand::Sym { .. } => None, + InlineAsmOperand::In { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::InOut { .. } + | InlineAsmOperand::SplitInOut { .. } => Some(op_sp), + }) + .collect(); + if !unsupported_operands.is_empty() { + self.tcx.struct_span_lint_hir( + UNSUPPORTED_NAKED_FUNCTIONS, + hir_id, + unsupported_operands, + |lint| { + lint.build("only `const` and `sym` operands are supported in naked functions") + .emit(); + }, + ); + } + + let unsupported_options: Vec<&'static str> = [ + (InlineAsmOptions::NOMEM, "`nomem`"), + (InlineAsmOptions::NOSTACK, "`nostack`"), + (InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"), + (InlineAsmOptions::PURE, "`pure`"), + (InlineAsmOptions::READONLY, "`readonly`"), + ] + .iter() + .filter_map(|&(option, name)| if asm.options.contains(option) { Some(name) } else { None }) + .collect(); + + if !unsupported_options.is_empty() { + self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| { + lint.build(&format!( + "asm options unsupported in naked functions: {}", + unsupported_options.join(", ") + )) + .emit(); + }); + } + + if !asm.options.contains(InlineAsmOptions::NORETURN) { + self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| { + lint.build("asm in naked functions must use `noreturn` option").emit(); + }); + } + } +} + +impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> { + type Map = ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { + match stmt.kind { + StmtKind::Item(..) => {} + StmtKind::Local(..) => { + self.items.push((ItemKind::NonAsm, stmt.span)); + } + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + self.check_expr(expr, stmt.span); + } + } + } + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + self.check_expr(&expr, expr.span); + } +} diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 26962d2222d..b8a2a4779d9 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1929,7 +1929,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { - for op in asm.operands { + for (op, _op_sp) in asm.operands { match op { hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr } => { self.check_expr_asm_operand(expr, true); diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 1b51d5e0182..95848ac2c07 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -243,7 +243,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } hir::ExprKind::InlineAsm(ref asm) => { - for op in asm.operands { + for (op, _op_sp) in asm.operands { match op { hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr, .. } diff --git a/src/test/ui/asm/naked-functions.rs b/src/test/ui/asm/naked-functions.rs new file mode 100644 index 00000000000..a46ca4544a6 --- /dev/null +++ b/src/test/ui/asm/naked-functions.rs @@ -0,0 +1,169 @@ +// only-x86_64 +#![feature(asm)] +#![feature(llvm_asm)] +#![feature(naked_functions)] +#![feature(or_patterns)] +#![crate_type = "lib"] + +#[repr(C)] +pub struct P { x: u8, y: u16 } + +#[naked] +pub unsafe extern "C" fn patterns( + mut a: u32, + //~^ ERROR patterns not allowed in naked function parameters + &b: &i32, + //~^ ERROR patterns not allowed in naked function parameters + (None | Some(_)): Option<std::ptr::NonNull<u8>>, + //~^ ERROR patterns not allowed in naked function parameters + P { x, y }: P, + //~^ ERROR patterns not allowed in naked function parameters +) { + asm!("", options(noreturn)) +} + +#[naked] +pub unsafe extern "C" fn inc(a: u32) -> u32 { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted + a + 1 + //~^ ERROR referencing function parameters is not allowed in naked functions +} + +#[naked] +pub unsafe extern "C" fn inc_asm(a: u32) -> u32 { + asm!("/* {0} */", in(reg) a, options(noreturn)); + //~^ ERROR referencing function parameters is not allowed in naked functions + //~| WARN only `const` and `sym` operands are supported in naked functions + //~| WARN this was previously accepted +} + +#[naked] +pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted + (|| a + 1)() +} + +#[naked] +pub unsafe extern "C" fn unsupported_operands() { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted + let mut a = 0usize; + let mut b = 0usize; + let mut c = 0usize; + let mut d = 0usize; + let mut e = 0usize; + const F: usize = 0usize; + static G: usize = 0usize; + asm!("/* {0} {1} {2} {3} {4} {5} {6} */", + //~^ WARN asm in naked functions must use `noreturn` option + //~| WARN this was previously accepted + in(reg) a, + //~^ WARN only `const` and `sym` operands are supported in naked functions + //~| WARN this was previously accepted + inlateout(reg) b, + inout(reg) c, + lateout(reg) d, + out(reg) e, + const F, + sym G, + ); +} + +#[naked] +pub extern "C" fn missing_assembly() { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted +} + +#[naked] +pub extern "C" fn too_many_asm_blocks() { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted + asm!(""); + //~^ WARN asm in naked functions must use `noreturn` option + //~| WARN this was previously accepted + asm!(""); + //~^ WARN asm in naked functions must use `noreturn` option + //~| WARN this was previously accepted + asm!(""); + //~^ WARN asm in naked functions must use `noreturn` option + //~| WARN this was previously accepted + asm!("", options(noreturn)); +} + +pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { + #[naked] + pub extern "C" fn inner(y: usize) -> usize { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted + *&y + //~^ ERROR referencing function parameters is not allowed in naked functions + } + inner +} + +#[naked] +unsafe extern "C" fn llvm() -> ! { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted + llvm_asm!(""); + //~^ WARN LLVM-style inline assembly is unsupported in naked functions + //~| WARN this was previously accepted + core::hint::unreachable_unchecked(); +} + +#[naked] +unsafe extern "C" fn invalid_options() { + asm!("", options(nomem, preserves_flags, noreturn)); + //~^ WARN asm options unsupported in naked functions: `nomem`, `preserves_flags` + //~| WARN this was previously accepted +} + +#[naked] +unsafe extern "C" fn invalid_options_continued() { + asm!("", options(readonly, nostack), options(pure)); + //~^ ERROR asm with `pure` option must have at least one output + //~| WARN asm options unsupported in naked functions: `nostack`, `pure`, `readonly` + //~| WARN this was previously accepted + //~| WARN asm in naked functions must use `noreturn` option + //~| WARN this was previously accepted +} + +#[naked] +pub unsafe fn default_abi() { + //~^ WARN Rust ABI is unsupported in naked functions + //~| WARN this was previously accepted + asm!("", options(noreturn)); +} + +#[naked] +pub unsafe extern "Rust" fn rust_abi() { + //~^ WARN Rust ABI is unsupported in naked functions + //~| WARN this was previously accepted + asm!("", options(noreturn)); +} + +#[naked] +pub extern "C" fn valid_a<T>() -> T { + unsafe { asm!("", options(noreturn)); } +} + +#[naked] +pub extern "C" fn valid_b() { + unsafe { { { + asm!("", options(noreturn)); ; ; ; + } ; } ; } +} + +#[naked] +pub unsafe extern "C" fn valid_c() { + asm!("", options(noreturn)); +} + +#[cfg(target_arch = "x86_64")] +#[naked] +pub unsafe extern "C" fn valid_att_syntax() { + asm!("", options(noreturn, att_syntax)); +} diff --git a/src/test/ui/asm/naked-functions.stderr b/src/test/ui/asm/naked-functions.stderr new file mode 100644 index 00000000000..076289427b5 --- /dev/null +++ b/src/test/ui/asm/naked-functions.stderr @@ -0,0 +1,300 @@ +error: asm with `pure` option must have at least one output + --> $DIR/naked-functions.rs:126:14 + | +LL | asm!("", options(readonly, nostack), options(pure)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ + +error: patterns not allowed in naked function parameters + --> $DIR/naked-functions.rs:13:5 + | +LL | mut a: u32, + | ^^^^^ + +error: patterns not allowed in naked function parameters + --> $DIR/naked-functions.rs:15:5 + | +LL | &b: &i32, + | ^^ + +error: patterns not allowed in naked function parameters + --> $DIR/naked-functions.rs:17:6 + | +LL | (None | Some(_)): Option<std::ptr::NonNull<u8>>, + | ^^^^^^^^^^^^^^ + +error: patterns not allowed in naked function parameters + --> $DIR/naked-functions.rs:19:5 + | +LL | P { x, y }: P, + | ^^^^^^^^^^ + +error: referencing function parameters is not allowed in naked functions + --> $DIR/naked-functions.rs:29:5 + | +LL | a + 1 + | ^ + | + = help: follow the calling convention in asm block to use parameters + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:26:1 + | +LL | / pub unsafe extern "C" fn inc(a: u32) -> u32 { +LL | | +LL | | +LL | | a + 1 + | | ----- non-asm is unsupported in naked functions +LL | | +LL | | } + | |_^ + | + = note: `#[warn(unsupported_naked_functions)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +error: referencing function parameters is not allowed in naked functions + --> $DIR/naked-functions.rs:35:31 + | +LL | asm!("/* {0} */", in(reg) a, options(noreturn)); + | ^ + | + = help: follow the calling convention in asm block to use parameters + +warning: only `const` and `sym` operands are supported in naked functions + --> $DIR/naked-functions.rs:35:23 + | +LL | asm!("/* {0} */", in(reg) a, options(noreturn)); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:42:1 + | +LL | / pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { +LL | | +LL | | +LL | | (|| a + 1)() + | | ------------ non-asm is unsupported in naked functions +LL | | } + | |_^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: only `const` and `sym` operands are supported in naked functions + --> $DIR/naked-functions.rs:62:10 + | +LL | in(reg) a, + | ^^^^^^^^^ +... +LL | inlateout(reg) b, + | ^^^^^^^^^^^^^^^^ +LL | inout(reg) c, + | ^^^^^^^^^^^^ +LL | lateout(reg) d, + | ^^^^^^^^^^^^^^ +LL | out(reg) e, + | ^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:59:5 + | +LL | / asm!("/* {0} {1} {2} {3} {4} {5} {6} */", +LL | | +LL | | +LL | | in(reg) a, +... | +LL | | sym G, +LL | | ); + | |______^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:49:1 + | +LL | / pub unsafe extern "C" fn unsupported_operands() { +LL | | +LL | | +LL | | let mut a = 0usize; + | | ------------------- non-asm is unsupported in naked functions +LL | | let mut b = 0usize; + | | ------------------- non-asm is unsupported in naked functions +LL | | let mut c = 0usize; + | | ------------------- non-asm is unsupported in naked functions +LL | | let mut d = 0usize; + | | ------------------- non-asm is unsupported in naked functions +LL | | let mut e = 0usize; + | | ------------------- non-asm is unsupported in naked functions +... | +LL | | ); +LL | | } + | |_^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:75:1 + | +LL | / pub extern "C" fn missing_assembly() { +LL | | +LL | | +LL | | } + | |_^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:84:5 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:87:5 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:90:5 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:81:1 + | +LL | / pub extern "C" fn too_many_asm_blocks() { +LL | | +LL | | +LL | | asm!(""); +... | +LL | | asm!(""); + | | --------- multiple asm blocks are unsupported in naked functions +... | +LL | | asm!(""); + | | --------- multiple asm blocks are unsupported in naked functions +... | +LL | | asm!("", options(noreturn)); + | | ---------------------------- multiple asm blocks are unsupported in naked functions +LL | | } + | |_^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +error: referencing function parameters is not allowed in naked functions + --> $DIR/naked-functions.rs:101:11 + | +LL | *&y + | ^ + | + = help: follow the calling convention in asm block to use parameters + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:98:5 + | +LL | / pub extern "C" fn inner(y: usize) -> usize { +LL | | +LL | | +LL | | *&y + | | --- non-asm is unsupported in naked functions +LL | | +LL | | } + | |_____^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: the LLVM-style inline assembly is unsupported in naked functions + --> $DIR/naked-functions.rs:111:5 + | +LL | llvm_asm!(""); + | ^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + = help: use the new asm! syntax specified in RFC 2873 + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:108:1 + | +LL | / unsafe extern "C" fn llvm() -> ! { +LL | | +LL | | +LL | | llvm_asm!(""); +... | +LL | | core::hint::unreachable_unchecked(); + | | ------------------------------------ non-asm is unsupported in naked functions +LL | | } + | |_^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: asm options unsupported in naked functions: `nomem`, `preserves_flags` + --> $DIR/naked-functions.rs:119:5 + | +LL | asm!("", options(nomem, preserves_flags, noreturn)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: asm options unsupported in naked functions: `nostack`, `pure`, `readonly` + --> $DIR/naked-functions.rs:126:5 + | +LL | asm!("", options(readonly, nostack), options(pure)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:126:5 + | +LL | asm!("", options(readonly, nostack), options(pure)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: Rust ABI is unsupported in naked functions + --> $DIR/naked-functions.rs:135:15 + | +LL | pub unsafe fn default_abi() { + | ^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +warning: Rust ABI is unsupported in naked functions + --> $DIR/naked-functions.rs:142:29 + | +LL | pub unsafe extern "Rust" fn rust_abi() { + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 <https://github.com/rust-lang/rust/issues/32408> + +error: aborting due to 8 previous errors; 19 warnings emitted + diff --git a/src/test/ui/asm/naked-params.rs b/src/test/ui/asm/naked-params.rs deleted file mode 100644 index 46a4fc11e5a..00000000000 --- a/src/test/ui/asm/naked-params.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Check that use of function parameters is validate in naked functions. -// -// ignore-wasm32 asm unsupported -#![feature(asm)] -#![feature(naked_functions)] -#![feature(or_patterns)] -#![crate_type = "lib"] - -#[repr(C)] -pub struct P { x: u8, y: u16 } - -#[naked] -pub unsafe extern "C" fn f( - mut a: u32, - //~^ ERROR patterns not allowed in naked function parameters - &b: &i32, - //~^ ERROR patterns not allowed in naked function parameters - (None | Some(_)): Option<std::ptr::NonNull<u8>>, - //~^ ERROR patterns not allowed in naked function parameters - P { x, y }: P, - //~^ ERROR patterns not allowed in naked function parameters -) { - asm!("", options(noreturn)) -} - -#[naked] -pub unsafe extern "C" fn inc(a: u32) -> u32 { - a + 1 - //~^ ERROR use of parameters not allowed inside naked functions -} - -#[naked] -pub unsafe extern "C" fn inc_asm(a: u32) -> u32 { - asm!("/* {0} */", in(reg) a, options(noreturn)); - //~^ ERROR use of parameters not allowed inside naked functions -} - -#[naked] -pub unsafe extern "C" fn sum(x: u32, y: u32) -> u32 { - // FIXME: Should be detected by asm-only check. - (|| { x + y})() -} - -pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { - #[naked] - pub extern "C" fn inner(y: usize) -> usize { - *&y - //~^ ERROR use of parameters not allowed inside naked functions - } - inner -} diff --git a/src/test/ui/asm/naked-params.stderr b/src/test/ui/asm/naked-params.stderr deleted file mode 100644 index 1a99e5109fc..00000000000 --- a/src/test/ui/asm/naked-params.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: patterns not allowed in naked function parameters - --> $DIR/naked-params.rs:14:5 - | -LL | mut a: u32, - | ^^^^^ - -error: patterns not allowed in naked function parameters - --> $DIR/naked-params.rs:16:5 - | -LL | &b: &i32, - | ^^ - -error: patterns not allowed in naked function parameters - --> $DIR/naked-params.rs:18:6 - | -LL | (None | Some(_)): Option<std::ptr::NonNull<u8>>, - | ^^^^^^^^^^^^^^ - -error: patterns not allowed in naked function parameters - --> $DIR/naked-params.rs:20:5 - | -LL | P { x, y }: P, - | ^^^^^^^^^^ - -error: use of parameters not allowed inside naked functions - --> $DIR/naked-params.rs:28:5 - | -LL | a + 1 - | ^ - -error: use of parameters not allowed inside naked functions - --> $DIR/naked-params.rs:34:31 - | -LL | asm!("/* {0} */", in(reg) a, options(noreturn)); - | ^ - -error: use of parameters not allowed inside naked functions - --> $DIR/naked-params.rs:47:11 - | -LL | *&y - | ^ - -error: aborting due to 7 previous errors - diff --git a/src/test/ui/feature-gates/feature-gate-naked_functions.rs b/src/test/ui/feature-gates/feature-gate-naked_functions.rs index 16a51a1e82f..06bddc422cf 100644 --- a/src/test/ui/feature-gates/feature-gate-naked_functions.rs +++ b/src/test/ui/feature-gates/feature-gate-naked_functions.rs @@ -1,11 +1,15 @@ +#![feature(asm)] + #[naked] //~^ the `#[naked]` attribute is an experimental feature -fn naked() {} +extern "C" fn naked() { + asm!("", options(noreturn)) +} #[naked] //~^ the `#[naked]` attribute is an experimental feature -fn naked_2() -> isize { - 0 +extern "C" fn naked_2() -> isize { + asm!("", options(noreturn)) } fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-naked_functions.stderr b/src/test/ui/feature-gates/feature-gate-naked_functions.stderr index e24dde5429d..d95561d2013 100644 --- a/src/test/ui/feature-gates/feature-gate-naked_functions.stderr +++ b/src/test/ui/feature-gates/feature-gate-naked_functions.stderr @@ -1,5 +1,5 @@ error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:1:1 + --> $DIR/feature-gate-naked_functions.rs:3:1 | LL | #[naked] | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[naked] = help: add `#![feature(naked_functions)]` to the crate attributes to enable error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:5:1 + --> $DIR/feature-gate-naked_functions.rs:9:1 | LL | #[naked] | ^^^^^^^^ diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs index c60ff7dc934..70ec0e3033c 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs @@ -1,15 +1,19 @@ -#![feature(naked_functions)] +#![feature(asm, naked_functions)] #[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` #[naked] -fn f() {} +extern "C" fn f() { + asm!("", options(noreturn)); +} struct S; impl S { #[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` #[naked] - fn g() {} + extern "C" fn g() { + asm!("", options(noreturn)); + } } fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr index 211cd3f16ba..1b49148d629 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr @@ -5,7 +5,7 @@ LL | #[track_caller] | ^^^^^^^^^^^^^^^ error[E0736]: cannot use `#[track_caller]` with `#[naked]` - --> $DIR/error-with-naked.rs:10:5 + --> $DIR/error-with-naked.rs:12:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs index 400148ab81d..1bd96b2b4c8 100644 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -768,7 +768,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { ExprKind::InlineAsm(ref asm) => asm .operands .iter() - .map(|o| match o { + .map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } | InlineAsmOperand::Const { expr } diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs index d847d22275e..d942d4e12b1 100644 --- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs @@ -517,7 +517,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } asm.options.hash(&mut self.s); - for op in asm.operands { + for (op, _op_sp) in asm.operands { match op { InlineAsmOperand::In { reg, expr } => { reg.hash(&mut self.s); diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs index 8f0ef9150d4..323d8745538 100644 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -293,7 +293,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); println!("{}options: {:?}", ind, asm.options); println!("{}operands:", ind); - for op in asm.operands { + for (op, _op_sp) in asm.operands { match op { hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::InOut { expr, .. } |
