Skip to content

Make naked functions incompatible with certain attributes #93809

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

Closed
wants to merge 4 commits into from
Closed
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
12 changes: 12 additions & 0 deletions compiler/rustc_builtin_macros/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_ast as ast;
use rustc_ast::attr;
use rustc_ast::ptr::P;
use rustc_ast_pretty::pprust;
use rustc_errors::struct_span_err;
use rustc_expand::base::*;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident, Symbol};
Expand Down Expand Up @@ -110,6 +111,17 @@ pub fn expand_test_or_bench(
return vec![Annotatable::Item(item)];
}

if let Some(attr) = cx.sess.find_by_name(&item.attrs, sym::naked) {
struct_span_err!(
cx.sess,
attr.span,
E0788,
"cannot use testing attributes with `#[naked]`",
)
.emit();
return vec![Annotatable::Item(item)];
}

// has_*_signature will report any errors in the type so compilation
// will fail. We shouldn't try to expand in this case because the errors
// would be spurious.
Expand Down
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 @@ -488,6 +488,7 @@ 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"),
E0788: include_str!("./error_codes/E0788.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
20 changes: 15 additions & 5 deletions compiler/rustc_error_codes/src/error_codes/E0736.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
`#[track_caller]` and `#[naked]` cannot both be applied to the same function.
Functions marked with the `#[naked]` attribute are restricted in what other
code generation attributes they may be marked with.

The following code generation attributes are incompatible with `#[naked]`:

* `#[inline]`
* `#[track_caller]`
* `#[target_feature]`

Erroneous code example:

```compile_fail,E0736
#[inline]
#[naked]
#[track_caller]
fn foo() {}
```

This is primarily due to ABI incompatibilities between the two attributes.
See [RFC 2091] for details on this and other limitations.
These incompatibilities are due to the fact that naked functions impose
deliberately strict restrictions regarding the code that the compiler is
allowed to produce for this function.

See [the reference page for codegen attributes] for more information.

[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md
[the reference page for codegen attributes]: https://doc.rust-lang.org/reference/attributes/codegen.html
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0739.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
`#[track_caller]` can not be applied on struct.
`#[track_caller]` must be applied to a function.

Erroneous code example:

Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0788.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Testing attributes cannot be applied to functions marked with `#[naked]`.

Erroneous code example:

```ignore (requires test runner)
#[test]
#[should_panic]
#[naked]
fn foo() {}
```

See [the reference page for codegen attributes] for more information.

[the reference page for codegen attributes]: https://doc.rust-lang.org/reference/attributes/codegen.html
1 change: 1 addition & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3036,6 +3036,7 @@ declare_lint_pass! {
UNINHABITED_STATIC,
FUNCTION_ITEM_REFERENCES,
USELESS_DEPRECATED,
UNDEFINED_NAKED_FUNCTION_ABI,
MISSING_ABI,
INVALID_DOC_ATTRIBUTES,
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
Expand Down
88 changes: 48 additions & 40 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,51 +80,49 @@ impl CheckAttrVisitor<'_> {
self.check_rustc_must_implement_one_of(attr, span, target)
}
sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
sym::track_caller => {
self.check_track_caller(hir_id, attr.span, attrs, span, target)
}
sym::track_caller => self.check_track_caller(hir_id, attr, span, target),
sym::doc => self.check_doc_attrs(
attr,
hir_id,
target,
&mut specified_inline,
&mut doc_aliases,
),
sym::no_link => self.check_no_link(hir_id, &attr, span, target),
sym::export_name => self.check_export_name(hir_id, &attr, span, target),
sym::no_link => self.check_no_link(hir_id, attr, span, target),
sym::export_name => self.check_export_name(hir_id, attr, span, target),
sym::rustc_layout_scalar_valid_range_start
| sym::rustc_layout_scalar_valid_range_end => {
self.check_rustc_layout_scalar_valid_range(&attr, span, target)
self.check_rustc_layout_scalar_valid_range(attr, span, target)
}
sym::allow_internal_unstable => {
self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
self.check_allow_internal_unstable(hir_id, attr, span, target, attrs)
}
sym::rustc_allow_const_fn_unstable => {
self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target)
}
sym::naked => self.check_naked(hir_id, attr, span, target),
sym::naked => self.check_naked(hir_id, attr, span, target, attrs),
sym::rustc_legacy_const_generics => {
self.check_rustc_legacy_const_generics(&attr, span, target, item)
self.check_rustc_legacy_const_generics(attr, span, target, item)
}
sym::rustc_lint_query_instability => {
self.check_rustc_lint_query_instability(&attr, span, target)
self.check_rustc_lint_query_instability(attr, span, target)
}
sym::rustc_clean
| sym::rustc_dirty
| sym::rustc_if_this_changed
| sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
| sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(attr),
sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
sym::default_method_body_is_const => {
self.check_default_method_body_is_const(attr, span, target)
}
sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
sym::must_use => self.check_must_use(hir_id, &attr, span, target),
sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
sym::must_not_suspend => self.check_must_not_suspend(attr, span, target),
sym::must_use => self.check_must_use(hir_id, attr, span, target),
sym::rustc_pass_by_value => self.check_pass_by_value(attr, span, target),
sym::rustc_const_unstable
| sym::rustc_const_stable
| sym::unstable
| sym::stable
| sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
| sym::rustc_promotable => self.check_stability_promotable(attr, span, target),
_ => true,
};
is_valid &= attr_is_valid;
Expand Down Expand Up @@ -338,12 +336,35 @@ impl CheckAttrVisitor<'_> {
}

/// Checks if `#[naked]` is applied to a function definition.
fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
fn check_naked(
&self,
hir_id: HirId,
attr: &Attribute,
span: Span,
target: Target,
attrs: &[Attribute],
) -> bool {
for any_attr in attrs {
if any_attr.has_name(sym::track_caller)
|| any_attr.has_name(sym::inline)
|| any_attr.has_name(sym::target_feature)
{
struct_span_err!(
self.tcx.sess,
any_attr.span,
E0736,
"cannot use additional code generation attributes with `#[naked]`",
)
.emit();
return false;
}
}

match target {
Target::Fn
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
// `#[allow_internal_unstable]` attribute with just a lint, because we previously
// `#[naked]` attribute with just a lint, because we previously
// erroneously allowed it and some crates used it accidentally, to to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
Expand Down Expand Up @@ -383,41 +404,28 @@ impl CheckAttrVisitor<'_> {
}
}

/// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
/// Checks if a `#[track_caller]` is applied to a function. Returns `true` if valid.
fn check_track_caller(
&self,
hir_id: HirId,
attr_span: Span,
attrs: &[Attribute],
attr: &Attribute,
span: Span,
target: Target,
) -> bool {
match target {
_ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
struct_span_err!(
self.tcx.sess,
attr_span,
E0736,
"cannot use `#[track_caller]` with `#[naked]`",
)
.emit();
false
}
Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
// `#[track_caller]` attribute with just a lint, because we previously
// erroneously allowed it and some crates used it accidentally, to to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
for attr in attrs {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
}
self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
true
}
_ => {
struct_span_err!(
self.tcx.sess,
attr_span,
attr.span,
E0739,
"attribute should be applied to function"
)
Expand Down Expand Up @@ -1210,8 +1218,8 @@ impl CheckAttrVisitor<'_> {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
}
_ => {
// FIXME: #[cold] was previously allowed on non-functions/statics and some crates
// used this, so only emit a warning.
// FIXME: #[link_name] was previously allowed on non-functions/statics and
// some crates used this, so only emit a warning.
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
let mut diag =
lint.build("attribute should be applied to a foreign function or static");
Expand Down Expand Up @@ -1796,7 +1804,7 @@ impl CheckAttrVisitor<'_> {
}
}

/// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
/// Outputs an error for `#[allow_const_fn_unstable]` which can only be applied to macros.
/// (Allows proc_macro functions)
fn check_rustc_allow_const_fn_unstable(
&self,
Expand All @@ -1812,11 +1820,11 @@ impl CheckAttrVisitor<'_> {
true
}
// FIXME(#80564): We permit struct fields and match arms to have an
// `#[allow_internal_unstable]` attribute with just a lint, because we previously
// `#[allow_const_fn_unstable]` attribute with just a lint, because we previously
// erroneously allowed it and some crates used it accidentally, to to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_const_fn_unstable");
true
}
_ => {
Expand Down
10 changes: 1 addition & 9 deletions compiler/rustc_passes/src/naked_functions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Checks validity of naked functions.

use rustc_ast::{Attribute, InlineAsmOptions};
use rustc_ast::InlineAsmOptions;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
Expand Down Expand Up @@ -65,18 +65,10 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
check_no_patterns(self.tcx, body.params);
check_no_parameters_use(self.tcx, body);
check_asm(self.tcx, body, span);
check_inline(self.tcx, attrs);
}
}
}

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

/// 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 {
Expand Down
32 changes: 32 additions & 0 deletions src/test/ui/asm/naked-functions-inline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// needs-asm-support

#![feature(naked_functions)]
#![crate_type = "lib"]

use std::arch::asm;

#[naked]
pub unsafe extern "C" fn inline_none() {
asm!("", options(noreturn));
}

#[naked]
#[inline]
//~^ ERROR cannot use additional code generation attributes with `#[naked]`
pub unsafe extern "C" fn inline_hint() {
asm!("", options(noreturn));
}

#[naked]
#[inline(always)]
//~^ ERROR cannot use additional code generation attributes with `#[naked]`
pub unsafe extern "C" fn inline_always() {
asm!("", options(noreturn));
}

#[naked]
#[inline(never)]
//~^ ERROR cannot use additional code generation attributes with `#[naked]`
pub unsafe extern "C" fn inline_never() {
asm!("", options(noreturn));
}
21 changes: 21 additions & 0 deletions src/test/ui/asm/naked-functions-inline.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0736]: cannot use additional code generation attributes with `#[naked]`
--> $DIR/naked-functions-inline.rs:14:1
|
LL | #[inline]
| ^^^^^^^^^

error[E0736]: cannot use additional code generation attributes with `#[naked]`
--> $DIR/naked-functions-inline.rs:21:1
|
LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^

error[E0736]: cannot use additional code generation attributes with `#[naked]`
--> $DIR/naked-functions-inline.rs:28:1
|
LL | #[inline(never)]
| ^^^^^^^^^^^^^^^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0736`.
26 changes: 26 additions & 0 deletions src/test/ui/asm/naked-functions-target-feature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// ignore-arm
// ignore-aarch64
// ignore-wasm
// ignore-emscripten
// ignore-mips
// ignore-mips64
// ignore-powerpc
// ignore-powerpc64
// ignore-powerpc64le
// ignore-riscv64
// ignore-s390x
// ignore-sparc
// ignore-sparc64
// needs-asm-support

#![feature(naked_functions)]
#![crate_type = "lib"]

use std::arch::asm;

#[target_feature(enable = "sse2")]
//~^ ERROR cannot use additional code generation attributes with `#[naked]`
#[naked]
pub unsafe extern "C" fn naked_target_feature() {
asm!("", options(noreturn));
}
9 changes: 9 additions & 0 deletions src/test/ui/asm/naked-functions-target-feature.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0736]: cannot use additional code generation attributes with `#[naked]`
--> $DIR/naked-functions-target-feature.rs:21:1
|
LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Comment on lines +1 to +5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we point at #[naked] as well here, and maybe at the item name too?


error: aborting due to previous error

For more information about this error, try `rustc --explain E0736`.
Loading