diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index e0e78a4d60953..6ad6e664316f4 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 44dfdcfccab9b..c7dc66b70fef1 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 44dc6673564a5..280e863d4744d 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 3e8fc689acf7a..3c330c5d6c528 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 25b09d76295f2..597c55b4bd7f3 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 fa82dce0ae2ed..a9358c9610a5e 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 ", + 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 e404afeb698a6..e934d8ed9da5f 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 956be925be815..711e8e87c6c6e 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 debb873beb93b..a64c6fa319ca9 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 6ef45cdd391de..5b50ef8627b25 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 { @@ -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 = 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 { + 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 26962d2222d32..b8a2a4779d985 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 1b51d5e0182fd..95848ac2c0767 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 0000000000000..a46ca4544a68f --- /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>, + //~^ 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 { + 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 0000000000000..076289427b54f --- /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>, + | ^^^^^^^^^^^^^^ + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + = 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 + +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 + +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 + +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 + +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 + +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 + +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 46a4fc11e5a7c..0000000000000 --- 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>, - //~^ 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 1a99e5109fcdb..0000000000000 --- 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>, - | ^^^^^^^^^^^^^^ - -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 16a51a1e82fc6..06bddc422cf80 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 e24dde5429d51..d95561d20133e 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 c60ff7dc93449..70ec0e3033c6f 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 211cd3f16ba1b..1b49148d629b2 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 400148ab81dd9..1bd96b2b4c89b 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 d847d22275e83..d942d4e12b106 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 8f0ef9150d450..323d874553885 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, .. }