Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reject unsupported naked functions #93153

Merged
merged 2 commits into from
Jan 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ E0783: include_str!("./error_codes/E0783.md"),
E0784: include_str!("./error_codes/E0784.md"),
E0785: include_str!("./error_codes/E0785.md"),
E0786: include_str!("./error_codes/E0786.md"),
E0787: include_str!("./error_codes/E0787.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0787.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
An unsupported naked function definition.

Erroneous code example:

```compile_fail,E0787
#![feature(naked_functions)]

#[naked]
pub extern "C" fn f() -> u32 {
42
}
```

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` and `raw` options, but others options are not allowed.

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

### Additional information

For more information, please see [RFC 2972].

[RFC 2972]: https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md
5 changes: 5 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,11 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
<https://github.com/rust-lang/rust/issues/59014> for more information",
);
store.register_removed("plugin_as_library", "plugins have been deprecated and retired");
store.register_removed(
"unsupported_naked_functions",
"converted into hard error, see RFC 2972 \
<https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md> for more information",
);
}

fn register_internals(store: &mut LintStore) {
Expand Down
47 changes: 0 additions & 47 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2759,52 +2759,6 @@ declare_lint! {
"undefined naked function ABI"
}

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 extern "C" 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.
///
/// Naked functions cannot be inlined. All forms of the `inline` attribute
/// are prohibited.
///
/// 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>",
};
}

declare_lint! {
/// The `ineffective_unstable_trait_impl` lint detects `#[unstable]` attributes which are not used.
///
Expand Down Expand Up @@ -3070,7 +3024,6 @@ declare_lint_pass! {
UNINHABITED_STATIC,
FUNCTION_ITEM_REFERENCES,
USELESS_DEPRECATED,
UNSUPPORTED_NAKED_FUNCTIONS,
MISSING_ABI,
INVALID_DOC_ATTRIBUTES,
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
Expand Down
92 changes: 47 additions & 45 deletions compiler/rustc_passes/src/naked_functions.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! Checks validity of naked functions.

use rustc_ast::{Attribute, InlineAsmOptions};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{FnKind, Visitor};
use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
use rustc_session::lint::builtin::UNSUPPORTED_NAKED_FUNCTIONS;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
Expand Down Expand Up @@ -64,18 +64,16 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
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);
check_inline(self.tcx, hir_id, attrs);
check_asm(self.tcx, body, span);
check_inline(self.tcx, attrs);
}
}
}

/// Check that the function isn't inlined.
fn check_inline(tcx: TyCtxt<'_>, hir_id: HirId, attrs: &[Attribute]) {
fn check_inline(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
for attr in attrs.iter().filter(|attr| attr.has_name(sym::inline)) {
tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, attr.span, |lint| {
lint.build("naked functions cannot be inlined").emit();
});
tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit();
}
}

Expand Down Expand Up @@ -146,31 +144,31 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
}

/// 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) {
fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, 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");
}
let mut diag = struct_span_err!(
tcx.sess,
fn_span,
E0787,
"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();
});
}
diag.emit();
}
}

Expand Down Expand Up @@ -221,7 +219,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {

ExprKind::InlineAsm(ref asm) => {
self.items.push((ItemKind::Asm, span));
self.check_inline_asm(expr.hir_id, asm, span);
self.check_inline_asm(asm, span);
}

ExprKind::DropTemps(..) | ExprKind::Block(..) | ExprKind::Err => {
Expand All @@ -230,7 +228,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
}
}

fn check_inline_asm(&self, hir_id: HirId, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) {
fn check_inline_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) {
let unsupported_operands: Vec<Span> = asm
.operands
.iter()
Expand All @@ -243,18 +241,17 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
})
.collect();
if !unsupported_operands.is_empty() {
self.tcx.struct_span_lint_hir(
UNSUPPORTED_NAKED_FUNCTIONS,
hir_id,
struct_span_err!(
self.tcx.sess,
unsupported_operands,
|lint| {
lint.build("only `const` and `sym` operands are supported in naked functions")
.emit();
},
);
E0787,
"only `const` and `sym` operands are supported in naked functions",
)
.emit();
}

let unsupported_options: Vec<&'static str> = [
(InlineAsmOptions::MAY_UNWIND, "`may_unwind`"),
(InlineAsmOptions::NOMEM, "`nomem`"),
(InlineAsmOptions::NOSTACK, "`nostack`"),
(InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"),
Expand All @@ -266,19 +263,24 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
.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();
});
struct_span_err!(
self.tcx.sess,
span,
E0787,
"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();
});
struct_span_err!(
self.tcx.sess,
span,
E0787,
"asm in naked functions must use `noreturn` option"
)
.emit();
}
}
}
Expand Down
40 changes: 15 additions & 25 deletions src/test/codegen/naked-functions.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
// compile-flags: -C no-prepopulate-passes
// needs-asm-support
// only-x86_64

#![crate_type = "lib"]
#![feature(naked_functions)]
use std::arch::asm;

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}void @naked_empty()
#[no_mangle]
#[naked]
pub fn naked_empty() {
pub unsafe extern "C" fn naked_empty() {
// CHECK-NEXT: {{.+}}:
// CHECK-NEXT: ret void
// CHECK-NEXT: call void asm
// CHECK-NEXT: unreachable
asm!("ret",
options(noreturn));
}

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i64 %a, i64 %b)
#[no_mangle]
#[naked]
// CHECK-NEXT: define{{.*}}void @naked_with_args(i{{[0-9]+( %a)?}})
pub fn naked_with_args(a: isize) {
pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
// CHECK-NEXT: {{.+}}:
// CHECK: ret void
}

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_return()
#[no_mangle]
#[naked]
pub fn naked_with_return() -> isize {
// CHECK-NEXT: {{.+}}:
// CHECK-NEXT: ret i{{[0-9]+}} 0
0
}

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+( %a)?}})
#[no_mangle]
#[naked]
pub fn naked_with_args_and_return(a: isize) -> isize {
// CHECK-NEXT: {{.+}}:
// CHECK: ret i{{[0-9]+}} 0
0
// CHECK-NEXT: call void asm
// CHECK-NEXT: unreachable
asm!("lea rax, [rdi + rsi]",
"ret",
options(noreturn));
}
1 change: 0 additions & 1 deletion src/test/codegen/naked-noinline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

use std::arch::asm;

#[inline(always)]
#[naked]
#[no_mangle]
pub unsafe extern "C" fn f() {
Expand Down
Loading