Skip to content

Error on invalid signatures for interrupt ABIs #142633

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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 Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,7 @@ dependencies = [
"rustc_parse",
"rustc_session",
"rustc_span",
"rustc_target",
"thin-vec",
]

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ rustc_macros = { path = "../rustc_macros" }
rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
thin-vec = "0.2.12"
# tidy-alphabetical-end
23 changes: 14 additions & 9 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
ast_passes_abi_custom_coroutine =
functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}`
ast_passes_abi_cannot_be_coroutine =
functions with the {$abi} ABI cannot be `{$coroutine_kind_str}`
.suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton

ast_passes_abi_custom_invalid_signature =
invalid signature for `extern "custom"` function
.note = functions with the `"custom"` ABI cannot have any parameters or return type
.suggestion = remove the parameters and return type

ast_passes_abi_custom_safe_foreign_function =
foreign functions with the `"custom"` ABI cannot be safe
foreign functions with the "custom" ABI cannot be safe
.suggestion = remove the `safe` keyword from this definition

ast_passes_abi_custom_safe_function =
functions with the `"custom"` ABI must be unsafe
functions with the "custom" ABI must be unsafe
.suggestion = add the `unsafe` keyword to this definition

ast_passes_abi_must_not_have_parameters_or_return_type=
invalid signature for `extern {$abi}` function
.note = functions with the {$abi} ABI cannot have any parameters or return type
.suggestion = remove the parameters and return type

ast_passes_abi_must_not_have_return_type=
invalid signature for `extern {$abi}` function
.note = functions with the "custom" ABI cannot have a return type
.help = remove the return type

ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant
Expand Down
87 changes: 70 additions & 17 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::ops::{Deref, DerefMut};
use std::str::FromStr;

use itertools::{Either, Itertools};
use rustc_abi::ExternAbi;
use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
use rustc_ast::ptr::P;
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
use rustc_ast::*;
Expand All @@ -37,6 +37,7 @@ use rustc_session::lint::builtin::{
};
use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
use rustc_span::{Ident, Span, kw, sym};
use rustc_target::spec::{AbiMap, AbiMapping};
use thin_vec::thin_vec;

use crate::errors::{self, TildeConstReason};
Expand Down Expand Up @@ -365,46 +366,94 @@ impl<'a> AstValidator<'a> {
}
}

/// An `extern "custom"` function must be unsafe, and must not have any parameters or return
/// type.
fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
/// Check that the signature of this function does not violate the constraints of its ABI.
fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
match AbiMap::from_target(&self.sess.target).canonize_abi(abi, false) {
AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => {
match canon_abi {
CanonAbi::C
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::Arm(_)
| CanonAbi::GpuKernel
| CanonAbi::X86(_) => { /* nothing to check */ }

CanonAbi::Custom => {
// An `extern "custom"` function must be unsafe.
self.reject_safe_fn(abi, ctxt, sig);

// An `extern "custom"` function cannot be `async` and/or `gen`.
self.reject_coroutine(abi, sig);

// An `extern "custom"` function must have type `fn()`.
Copy link
Member

Choose a reason for hiding this comment

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

pedantic note: its type is unsafe extern "custom" fn(). and yes, you can have different implementations on unsafe fn() and fn().

don't actually change anything, I just couldn't resist noting.

self.reject_params_or_return(abi, ident, sig);
}

CanonAbi::Interrupt(interrupt_kind) => {
// An interrupt handler cannot be `async` and/or `gen`.
self.reject_coroutine(abi, sig);

if let InterruptKind::X86 = interrupt_kind {
// "x86-interrupt" is special because it does have arguments.
// FIXME(workingjubilee): properly lint on acceptable input types.
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
self.dcx().emit_err(errors::AbiMustNotHaveReturnType {
span: ret_ty.span,
abi,
});
}
} else {
// An `extern "interrupt"` function must have type `fn()`.
self.reject_params_or_return(abi, ident, sig);
}
}
}
}
AbiMapping::Invalid => { /* ignore */ }
}
}

fn reject_safe_fn(&self, abi: ExternAbi, ctxt: FnCtxt, sig: &FnSig) {
let dcx = self.dcx();

// An `extern "custom"` function must be unsafe.
match sig.header.safety {
Safety::Unsafe(_) => { /* all good */ }
Safety::Safe(safe_span) => {
let safe_span =
self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span));
let source_map = self.sess.psess.source_map();
let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span));
dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
}
Safety::Default => match ctxt {
FnCtxt::Foreign => { /* all good */ }
FnCtxt::Free | FnCtxt::Assoc(_) => {
self.dcx().emit_err(errors::AbiCustomSafeFunction {
dcx.emit_err(errors::AbiCustomSafeFunction {
span: sig.span,
abi,
unsafe_span: sig.span.shrink_to_lo(),
});
}
},
}
}

// An `extern "custom"` function cannot be `async` and/or `gen`.
fn reject_coroutine(&self, abi: ExternAbi, sig: &FnSig) {
if let Some(coroutine_kind) = sig.header.coroutine_kind {
let coroutine_kind_span = self
.sess
.psess
.source_map()
.span_until_non_whitespace(coroutine_kind.span().to(sig.span));

self.dcx().emit_err(errors::AbiCustomCoroutine {
self.dcx().emit_err(errors::AbiCannotBeCoroutine {
span: sig.span,
abi,
coroutine_kind_span,
coroutine_kind_str: coroutine_kind.as_str(),
});
}
}

// An `extern "custom"` function must not have any parameters or return type.
fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) {
let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
spans.push(ret_ty.span);
Expand All @@ -415,11 +464,12 @@ impl<'a> AstValidator<'a> {
let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
let padding = if header_span.is_empty() { "" } else { " " };

self.dcx().emit_err(errors::AbiCustomInvalidSignature {
self.dcx().emit_err(errors::AbiMustNotHaveParametersOrReturnType {
spans,
symbol: ident.name,
suggestion_span,
padding,
abi,
});
}
}
Expand Down Expand Up @@ -1199,9 +1249,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_foreign_fn_bodyless(*ident, body.as_deref());
self.check_foreign_fn_headerless(sig.header);
self.check_foreign_item_ascii_only(*ident);
if self.extern_mod_abi == Some(ExternAbi::Custom) {
self.check_custom_abi(FnCtxt::Foreign, ident, sig);
}
self.check_extern_fn_signature(
self.extern_mod_abi.unwrap_or(ExternAbi::FALLBACK),
FnCtxt::Foreign,
ident,
sig,
);
}
ForeignItemKind::TyAlias(box TyAlias {
defaultness,
Expand Down Expand Up @@ -1411,9 +1464,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

if let FnKind::Fn(ctxt, _, fun) = fk
&& let Extern::Explicit(str_lit, _) = fun.sig.header.ext
&& let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
&& let Ok(abi) = ExternAbi::from_str(str_lit.symbol.as_str())
{
self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig);
}

self.check_c_variadic_type(fk);
Expand Down
22 changes: 18 additions & 4 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Errors emitted by ast_passes.

use rustc_abi::ExternAbi;
use rustc_ast::ParamKindOrd;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
Expand Down Expand Up @@ -845,6 +846,7 @@ pub(crate) struct AbiCustomSafeForeignFunction {
pub(crate) struct AbiCustomSafeFunction {
#[primary_span]
pub span: Span,
pub abi: ExternAbi,

#[suggestion(
ast_passes_suggestion,
Expand All @@ -856,10 +858,11 @@ pub(crate) struct AbiCustomSafeFunction {
}

#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_coroutine)]
pub(crate) struct AbiCustomCoroutine {
#[diag(ast_passes_abi_cannot_be_coroutine)]
pub(crate) struct AbiCannotBeCoroutine {
#[primary_span]
pub span: Span,
pub abi: ExternAbi,

#[suggestion(
ast_passes_suggestion,
Expand All @@ -872,11 +875,12 @@ pub(crate) struct AbiCustomCoroutine {
}

#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_invalid_signature)]
#[diag(ast_passes_abi_must_not_have_parameters_or_return_type)]
#[note]
pub(crate) struct AbiCustomInvalidSignature {
pub(crate) struct AbiMustNotHaveParametersOrReturnType {
#[primary_span]
pub spans: Vec<Span>,
pub abi: ExternAbi,

#[suggestion(
ast_passes_suggestion,
Expand All @@ -888,3 +892,13 @@ pub(crate) struct AbiCustomInvalidSignature {
pub symbol: Symbol,
pub padding: &'static str,
}

#[derive(Diagnostic)]
#[diag(ast_passes_abi_must_not_have_return_type)]
#[note]
pub(crate) struct AbiMustNotHaveReturnType {
#[primary_span]
#[help]
pub span: Span,
pub abi: ExternAbi,
}
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
hir_analysis_abi_custom_clothed_function =
items with the `"custom"` ABI can only be declared externally or defined via naked functions
items with the "custom" ABI can only be declared externally or defined via naked functions
.suggestion = convert this to an `#[unsafe(naked)]` function

hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`
Expand Down
6 changes: 2 additions & 4 deletions tests/codegen/abi-x86-interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
extern crate minicore;
use minicore::*;

// CHECK: define x86_intrcc i64 @has_x86_interrupt_abi
// CHECK: define x86_intrcc void @has_x86_interrupt_abi
#[no_mangle]
pub extern "x86-interrupt" fn has_x86_interrupt_abi(a: i64) -> i64 {
a
}
pub extern "x86-interrupt" fn has_x86_interrupt_abi() {}
20 changes: 10 additions & 10 deletions tests/ui/abi/bad-custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#[unsafe(naked)]
extern "custom" fn must_be_unsafe(a: i64) -> i64 {
//~^ ERROR functions with the `"custom"` ABI must be unsafe
//~^ ERROR functions with the "custom" ABI must be unsafe
//~| ERROR invalid signature for `extern "custom"` function
std::arch::naked_asm!("")
}
Expand All @@ -23,7 +23,7 @@ unsafe extern "custom" fn no_return_type() -> i64 {
}

unsafe extern "custom" fn double(a: i64) -> i64 {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR invalid signature for `extern "custom"` function
unimplemented!()
}
Expand All @@ -32,15 +32,15 @@ struct Thing(i64);

impl Thing {
unsafe extern "custom" fn is_even(self) -> bool {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR invalid signature for `extern "custom"` function
unimplemented!()
}
}

trait BitwiseNot {
unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR invalid signature for `extern "custom"` function
unimplemented!()
}
Expand All @@ -50,14 +50,14 @@ impl BitwiseNot for Thing {}

trait Negate {
extern "custom" fn negate(a: i64) -> i64;
//~^ ERROR functions with the `"custom"` ABI must be unsafe
//~^ ERROR functions with the "custom" ABI must be unsafe
//~| ERROR invalid signature for `extern "custom"` function
}

impl Negate for Thing {
extern "custom" fn negate(a: i64) -> i64 {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the `"custom"` ABI must be unsafe
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the "custom" ABI must be unsafe
//~| ERROR invalid signature for `extern "custom"` function
-a
}
Expand All @@ -68,7 +68,7 @@ unsafe extern "custom" {
//~^ ERROR invalid signature for `extern "custom"` function

safe fn extern_cannot_be_safe();
//~^ ERROR foreign functions with the `"custom"` ABI cannot be safe
//~^ ERROR foreign functions with the "custom" ABI cannot be safe
}

fn caller(f: unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
Expand All @@ -95,8 +95,8 @@ const unsafe extern "custom" fn no_const_fn() {
}

async unsafe extern "custom" fn no_async_fn() {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the `"custom"` ABI cannot be `async`
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the "custom" ABI cannot be `async`
}

fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() {
Expand Down
Loading
Loading