add extern "custom" functions

This commit is contained in:
Folkert de Vries 2025-05-07 20:02:29 +02:00
parent 0a39445252
commit 5f73ce2b7e
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
34 changed files with 988 additions and 10 deletions

View file

@ -28,6 +28,9 @@ pub enum CanonAbi {
Rust,
RustCold,
/// An ABI that rustc does not know how to call or define.
Custom,
/// ABIs relevant to 32-bit Arm targets
Arm(ArmCall),
/// ABI relevant to GPUs: the entry point for a GPU kernel
@ -57,6 +60,7 @@ impl fmt::Display for CanonAbi {
CanonAbi::C => ExternAbi::C { unwind: false },
CanonAbi::Rust => ExternAbi::Rust,
CanonAbi::RustCold => ExternAbi::RustCold,
CanonAbi::Custom => ExternAbi::Custom,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,

View file

@ -40,6 +40,11 @@ pub enum ExternAbi {
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
Unadjusted,
/// An ABI that rustc does not know how to call or define. Functions with this ABI can
/// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only
/// be called from inline assembly.
Custom,
/// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias
/// and only valid on platforms that have a UEFI standard
EfiApi,
@ -141,6 +146,7 @@ abi_impls! {
AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
Cdecl { unwind: false } =><= "cdecl",
Cdecl { unwind: true } =><= "cdecl-unwind",
Custom =><= "custom",
EfiApi =><= "efiapi",
Fastcall { unwind: false } =><= "fastcall",
Fastcall { unwind: true } =><= "fastcall-unwind",

View file

@ -3520,6 +3520,38 @@ impl FnHeader {
|| matches!(constness, Const::Yes(_))
|| !matches!(ext, Extern::None)
}
/// Return a span encompassing the header, or none if all options are default.
pub fn span(&self) -> Option<Span> {
fn append(a: &mut Option<Span>, b: Span) {
*a = match a {
None => Some(b),
Some(x) => Some(x.to(b)),
}
}
let mut full_span = None;
match self.safety {
Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
Safety::Default => {}
};
if let Some(coroutine_kind) = self.coroutine_kind {
append(&mut full_span, coroutine_kind.span());
}
if let Const::Yes(span) = self.constness {
append(&mut full_span, span);
}
match self.ext {
Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span),
Extern::None => {}
}
full_span
}
}
impl Default for FnHeader {

View file

@ -134,5 +134,8 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
feature: sym::cmse_nonsecure_entry,
explain: GateReason::Experimental,
}),
ExternAbi::Custom => {
Err(UnstableAbi { abi, feature: sym::abi_custom, explain: GateReason::Experimental })
}
}
}

View file

@ -1,3 +1,20 @@
ast_passes_abi_custom_coroutine =
functions with the `"custom"` 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
.suggestion = remove the `safe` keyword from this definition
ast_passes_abi_custom_safe_function =
functions with the `"custom"` ABI must be unsafe
.suggestion = add the `unsafe` keyword to this definition
ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant

View file

@ -18,6 +18,7 @@
use std::mem;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use itertools::{Either, Itertools};
use rustc_abi::ExternAbi;
@ -81,6 +82,7 @@ struct AstValidator<'a> {
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
extern_mod_safety: Option<Safety>,
extern_mod_abi: Option<ExternAbi>,
lint_node_id: NodeId,
@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> {
self.outer_trait_or_trait_impl = old;
}
fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
fn with_in_extern_mod(
&mut self,
extern_mod_safety: Safety,
abi: Option<ExternAbi>,
f: impl FnOnce(&mut Self),
) {
let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
let old_abi = mem::replace(&mut self.extern_mod_abi, abi);
f(self);
self.extern_mod_safety = old;
self.extern_mod_safety = old_safety;
self.extern_mod_abi = old_abi;
}
fn with_tilde_const(
@ -370,6 +379,65 @@ 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) {
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));
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 {
span: sig.span,
unsafe_span: sig.span.shrink_to_lo(),
});
}
},
}
// An `extern "custom"` function cannot be `async` and/or `gen`.
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 {
span: sig.span,
coroutine_kind_span,
coroutine_kind_str: coroutine_kind.as_str(),
});
}
// An `extern "custom"` function must not have any parameters or return type.
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);
}
if !spans.is_empty() {
let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
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 {
spans,
symbol: ident.name,
suggestion_span,
padding,
});
}
}
/// This ensures that items can only be `unsafe` (or unmarked) outside of extern
/// blocks.
///
@ -1005,7 +1073,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if abi.is_none() {
self.handle_missing_abi(*extern_span, item.id);
}
self.with_in_extern_mod(*safety, |this| {
let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok());
self.with_in_extern_mod(*safety, extern_abi, |this| {
visit::walk_item(this, item);
});
self.extern_mod_span = old_item;
@ -1145,6 +1215,9 @@ 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);
}
}
ForeignItemKind::TyAlias(box TyAlias {
defaultness,
@ -1352,6 +1425,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_item_safety(span, safety);
}
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())
{
self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
}
self.check_c_variadic_type(fk);
// Functions cannot both be `const async` or `const gen`
@ -1703,6 +1783,7 @@ pub fn check_crate(
outer_impl_trait_span: None,
disallow_tilde_const: Some(TildeConstReason::Item),
extern_mod_safety: None,
extern_mod_abi: None,
lint_node_id: CRATE_NODE_ID,
is_sdylib_interface,
lint_buffer: lints,

View file

@ -824,3 +824,67 @@ pub(crate) struct MissingAbi {
#[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_safe_foreign_function)]
pub(crate) struct AbiCustomSafeForeignFunction {
#[primary_span]
pub span: Span,
#[suggestion(
ast_passes_suggestion,
applicability = "maybe-incorrect",
code = "",
style = "verbose"
)]
pub safe_span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_safe_function)]
pub(crate) struct AbiCustomSafeFunction {
#[primary_span]
pub span: Span,
#[suggestion(
ast_passes_suggestion,
applicability = "maybe-incorrect",
code = "unsafe ",
style = "verbose"
)]
pub unsafe_span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_coroutine)]
pub(crate) struct AbiCustomCoroutine {
#[primary_span]
pub span: Span,
#[suggestion(
ast_passes_suggestion,
applicability = "maybe-incorrect",
code = "",
style = "verbose"
)]
pub coroutine_kind_span: Span,
pub coroutine_kind_str: &'static str,
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_invalid_signature)]
#[note]
pub(crate) struct AbiCustomInvalidSignature {
#[primary_span]
pub spans: Vec<Span>,
#[suggestion(
ast_passes_suggestion,
applicability = "maybe-incorrect",
code = "{padding}fn {symbol}()",
style = "verbose"
)]
pub suggestion_span: Span,
pub symbol: Symbol,
pub padding: &'static str,
}

View file

@ -51,6 +51,11 @@ pub(crate) fn conv_to_call_conv(
CanonAbi::Rust | CanonAbi::C => default_call_conv,
CanonAbi::RustCold => CallConv::Cold,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => default_call_conv,
CanonAbi::X86(x86_call) => match x86_call {
X86Call::SysV64 => CallConv::SystemV,
X86Call::Win64 => CallConv::WindowsFastcall,

View file

@ -239,12 +239,16 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &str) -> Option<FnAttribute<'gcc>> {
let attribute = match conv {
CanonAbi::C | CanonAbi::Rust => return None,
CanonAbi::RustCold => FnAttribute::Cold,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => return None,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall,
ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry,
ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"),
},
CanonAbi::RustCold => FnAttribute::Cold,
CanonAbi::GpuKernel => {
if arch == "amdgpu" {
FnAttribute::GcnAmdGpuHsaKernel

View file

@ -649,6 +649,10 @@ impl llvm::CallConv {
match conv {
CanonAbi::C | CanonAbi::Rust => llvm::CCallConv,
CanonAbi::RustCold => llvm::PreserveMost,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => llvm::CCallConv,
CanonAbi::GpuKernel => {
if arch == "amdgpu" {
llvm::AmdgpuKernel

View file

@ -353,6 +353,8 @@ declare_features! (
(unstable, abi_avr_interrupt, "1.45.0", Some(69664)),
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
(unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)),
/// Allows `extern "custom" fn()`.
(unstable, abi_custom, "CURRENT_RUSTC_VERSION", Some(140829)),
/// Allows `extern "gpu-kernel" fn()`.
(unstable, abi_gpu_kernel, "1.86.0", Some(135467)),
/// Allows `extern "msp430-interrupt" fn()`.

View file

@ -1,3 +1,7 @@
hir_analysis_abi_custom_clothed_function =
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}`
.label = ambiguous associated {$assoc_kind} `{$assoc_ident}`

View file

@ -21,7 +21,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt;
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::util::Discr;
use rustc_middle::ty::{
AdtDef, BottomUpFolder, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
AdtDef, BottomUpFolder, FnSig, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, fold_regions,
};
use rustc_session::lint::builtin::UNINHABITED_STATIC;
@ -100,6 +100,18 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ex
}
}
pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) {
if fn_sig.abi == ExternAbi::Custom {
// Function definitions that use `extern "custom"` must be naked functions.
if !tcx.has_attr(def_id, sym::naked) {
tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction {
span: fn_sig_span,
naked_span: tcx.def_span(def_id).shrink_to_lo(),
});
}
}
}
fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let def = tcx.adt_def(def_id);
let span = tcx.def_span(def_id);

View file

@ -72,7 +72,7 @@ pub mod wfcheck;
use std::num::NonZero;
pub use check::{check_abi, check_abi_fn_ptr};
pub use check::{check_abi, check_abi_fn_ptr, check_custom_abi};
use rustc_abi::{ExternAbi, VariantIdx};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};

View file

@ -1698,3 +1698,17 @@ pub(crate) struct SelfInTypeAlias {
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_abi_custom_clothed_function)]
pub(crate) struct AbiCustomClothedFunction {
#[primary_span]
pub span: Span,
#[suggestion(
hir_analysis_suggestion,
applicability = "maybe-incorrect",
code = "#[unsafe(naked)]\n",
style = "short"
)]
pub naked_span: Span,
}

View file

@ -1,3 +1,7 @@
hir_typeck_abi_custom_call =
functions with the `"custom"` ABI cannot be called
.note = an `extern "custom"` function can only be called from within inline assembly
hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
hir_typeck_add_return_type_add = try adding a return type

View file

@ -1,5 +1,6 @@
use std::iter;
use rustc_abi::ExternAbi;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
use rustc_hir::def::{self, CtorKind, Namespace, Res};
@ -83,6 +84,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
while result.is_none() && autoderef.next().is_some() {
result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef);
}
self.check_call_custom_abi(autoderef.final_ty(false), call_expr.span);
self.register_predicates(autoderef.into_obligations());
let output = match result {
@ -135,6 +137,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
output
}
/// Functions of type `extern "custom" fn(/* ... */)` cannot be called using `ExprKind::Call`.
///
/// These functions have a calling convention that is unknown to rust, hence it cannot generate
/// code for the call. The only way to execute such a function is via inline assembly.
fn check_call_custom_abi(&self, callee_ty: Ty<'tcx>, span: Span) {
let abi = match callee_ty.kind() {
ty::FnDef(def_id, _) => self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi,
ty::FnPtr(_, header) => header.abi,
_ => return,
};
if let ExternAbi::Custom = abi {
self.tcx.dcx().emit_err(errors::AbiCustomCall { span });
}
}
#[instrument(level = "debug", skip(self, call_expr, callee_expr, arg_exprs, autoderef), ret)]
fn try_overloaded_call_step(
&self,

View file

@ -1163,3 +1163,10 @@ pub(crate) struct NakedFunctionsMustNakedAsm {
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_typeck_abi_custom_call)]
pub(crate) struct AbiCustomCall {
#[primary_span]
pub span: Span,
}

View file

@ -5,7 +5,7 @@
//!
//! See [`rustc_hir_analysis::check`] for more context on type checking in general.
use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::unord::UnordMap;
@ -1627,6 +1627,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(method.def_id),
);
// Functions of type `extern "custom" fn(/* ... */)` cannot be called using
// `ExprKind::MethodCall`. These functions have a calling convention that is
// unknown to rust, hence it cannot generate code for the call. The only way
// to execute such a function is via inline assembly.
if let ExternAbi::Custom = method.sig.abi {
self.tcx.dcx().emit_err(crate::errors::AbiCustomCall { span: expr.span });
}
method.sig.output()
}
Err(error) => {

View file

@ -48,7 +48,7 @@ use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_e
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{HirId, HirIdMap, Node};
use rustc_hir_analysis::check::check_abi;
use rustc_hir_analysis::check::{check_abi, check_custom_abi};
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc};
use rustc_middle::query::Providers;
@ -138,7 +138,7 @@ fn typeck_with_inspect<'tcx>(
// for visit the asm expr of the body.
let ty = fcx.check_expr(body.value);
fcx.write_ty(id, ty);
} else if let Some(hir::FnSig { header, decl, .. }) = node.fn_sig() {
} else if let Some(hir::FnSig { header, decl, span: fn_sig_span }) = node.fn_sig() {
let fn_sig = if decl.output.is_suggestable_infer_ty().is_some() {
// In the case that we're recovering `fn() -> W<_>` or some other return
// type that has an infer in it, lower the type directly so that it'll
@ -150,6 +150,8 @@ fn typeck_with_inspect<'tcx>(
};
check_abi(tcx, id, span, fn_sig.abi());
check_custom_abi(tcx, def_id, fn_sig.skip_binder(), *fn_sig_span);
loops::check(tcx, def_id, body);
// Compute the function signature from point of view of inside the fn.

View file

@ -1266,6 +1266,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
| RiscvInterruptS
| CCmseNonSecureCall
| CCmseNonSecureEntry
| Custom
| Unadjusted => false,
Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
}

View file

@ -496,6 +496,7 @@ impl RustcInternal for Abi {
Abi::RustCold => rustc_abi::ExternAbi::RustCold,
Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
Abi::Custom => rustc_abi::ExternAbi::Custom,
}
}
}

View file

@ -101,6 +101,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi {
CanonAbi::C => CallConvention::C,
CanonAbi::Rust => CallConvention::Rust,
CanonAbi::RustCold => CallConvention::Cold,
CanonAbi::Custom => CallConvention::Custom,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => CallConvention::ArmAapcs,
ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall,

View file

@ -879,6 +879,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
ExternAbi::RustCold => Abi::RustCold,
ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
ExternAbi::Custom => Abi::Custom,
}
}
}

View file

@ -430,6 +430,8 @@ pub enum CallConvention {
PreserveMost,
PreserveAll,
Custom,
// Target-specific calling conventions.
ArmAapcs,
CCmseNonSecureCall,

View file

@ -1103,6 +1103,7 @@ pub enum Abi {
RustCold,
RiscvInterruptM,
RiscvInterruptS,
Custom,
}
/// A binder represents a possibly generic type and its bound vars.

View file

@ -407,6 +407,7 @@ symbols! {
abi_amdgpu_kernel,
abi_avr_interrupt,
abi_c_cmse_nonsecure_call,
abi_custom,
abi_efiapi,
abi_gpu_kernel,
abi_msp430_interrupt,

View file

@ -71,6 +71,8 @@ impl AbiMap {
(ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust,
(ExternAbi::RustCold, _) => CanonAbi::RustCold,
(ExternAbi::Custom, _) => CanonAbi::Custom,
(ExternAbi::System { .. }, Arch::X86) if os == OsKind::Windows && !has_c_varargs => {
CanonAbi::X86(X86Call::Stdcall)
}

121
tests/ui/abi/bad-custom.rs Normal file
View file

@ -0,0 +1,121 @@
//@ edition: 2021
//@ check-fail
//@ needs-asm-support
#![feature(abi_custom)]
#[unsafe(naked)]
extern "custom" fn must_be_unsafe(a: i64) -> i64 {
//~^ ERROR functions with the `"custom"` ABI must be unsafe
//~| ERROR invalid signature for `extern "custom"` function
std::arch::naked_asm!("")
}
#[unsafe(naked)]
unsafe extern "custom" fn no_parameters(a: i64) {
//~^ ERROR invalid signature for `extern "custom"` function
std::arch::naked_asm!("")
}
#[unsafe(naked)]
unsafe extern "custom" fn no_return_type() -> i64 {
//~^ ERROR invalid signature for `extern "custom"` function
std::arch::naked_asm!("")
}
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 invalid signature for `extern "custom"` function
unimplemented!()
}
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 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 invalid signature for `extern "custom"` function
unimplemented!()
}
}
impl BitwiseNot for Thing {}
trait Negate {
extern "custom" fn negate(a: i64) -> i64;
//~^ 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 invalid signature for `extern "custom"` function
-a
}
}
unsafe extern "custom" {
fn increment(a: i64) -> i64;
//~^ ERROR invalid signature for `extern "custom"` function
safe fn extern_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 {
unsafe { f(x) }
//~^ ERROR functions with the `"custom"` ABI cannot be called
}
fn caller_by_ref(f: &unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
unsafe { f(x) }
//~^ ERROR functions with the `"custom"` ABI cannot be called
}
type Custom = unsafe extern "custom" fn(i64) -> i64;
fn caller_alias(f: Custom, mut x: i64) -> i64 {
unsafe { f(x) }
//~^ ERROR functions with the `"custom"` ABI cannot be called
}
#[unsafe(naked)]
const unsafe extern "custom" fn no_const_fn() {
std::arch::naked_asm!("")
//~^ ERROR inline assembly is not allowed in constant functions
}
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`
}
fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() {
//~^ ERROR expected a `Fn()` closure, found `unsafe extern "custom" fn()`
f
}
pub fn main() {
unsafe {
assert_eq!(double(21), 42);
//~^ ERROR functions with the `"custom"` ABI cannot be called
assert_eq!(unsafe { increment(41) }, 42);
//~^ ERROR functions with the `"custom"` ABI cannot be called
assert!(Thing(41).is_even());
//~^ ERROR functions with the `"custom"` ABI cannot be called
assert_eq!(Thing::bitwise_not(42), !42);
//~^ ERROR functions with the `"custom"` ABI cannot be called
}
}

View file

@ -0,0 +1,299 @@
error: functions with the `"custom"` ABI must be unsafe
--> $DIR/bad-custom.rs:7:1
|
LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add the `unsafe` keyword to this definition
|
LL | unsafe extern "custom" fn must_be_unsafe(a: i64) -> i64 {
| ++++++
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:7:35
|
LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - extern "custom" fn must_be_unsafe(a: i64) -> i64 {
LL + extern "custom" fn must_be_unsafe() {
|
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:14:41
|
LL | unsafe extern "custom" fn no_parameters(a: i64) {
| ^^^^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn no_parameters(a: i64) {
LL + unsafe extern "custom" fn no_parameters() {
|
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:20:47
|
LL | unsafe extern "custom" fn no_return_type() -> i64 {
| ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn no_return_type() -> i64 {
LL + unsafe extern "custom" fn no_return_type() {
|
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:25:34
|
LL | unsafe extern "custom" fn double(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn double(a: i64) -> i64 {
LL + unsafe extern "custom" fn double() {
|
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:34:39
|
LL | unsafe extern "custom" fn is_even(self) -> bool {
| ^^^^ ^^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn is_even(self) -> bool {
LL + unsafe extern "custom" fn is_even() {
|
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:42:43
|
LL | unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
LL + unsafe extern "custom" fn bitwise_not() {
|
error: functions with the `"custom"` ABI must be unsafe
--> $DIR/bad-custom.rs:52:5
|
LL | extern "custom" fn negate(a: i64) -> i64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add the `unsafe` keyword to this definition
|
LL | unsafe extern "custom" fn negate(a: i64) -> i64;
| ++++++
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:52:31
|
LL | extern "custom" fn negate(a: i64) -> i64;
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - extern "custom" fn negate(a: i64) -> i64;
LL + extern "custom" fn negate();
|
error: functions with the `"custom"` ABI must be unsafe
--> $DIR/bad-custom.rs:58:5
|
LL | extern "custom" fn negate(a: i64) -> i64 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add the `unsafe` keyword to this definition
|
LL | unsafe extern "custom" fn negate(a: i64) -> i64 {
| ++++++
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:58:31
|
LL | extern "custom" fn negate(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - extern "custom" fn negate(a: i64) -> i64 {
LL + extern "custom" fn negate() {
|
error: invalid signature for `extern "custom"` function
--> $DIR/bad-custom.rs:67:18
|
LL | fn increment(a: i64) -> i64;
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - fn increment(a: i64) -> i64;
LL + fn increment();
|
error: foreign functions with the `"custom"` ABI cannot be safe
--> $DIR/bad-custom.rs:70:5
|
LL | safe fn extern_cannot_be_safe();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the `safe` keyword from this definition
|
LL - safe fn extern_cannot_be_safe();
LL + fn extern_cannot_be_safe();
|
error: functions with the `"custom"` ABI cannot be `async`
--> $DIR/bad-custom.rs:97:1
|
LL | async unsafe extern "custom" fn no_async_fn() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the `async` keyword from this definiton
|
LL - async unsafe extern "custom" fn no_async_fn() {
LL + unsafe extern "custom" fn no_async_fn() {
|
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:97:1
|
LL | async unsafe extern "custom" fn no_async_fn() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: convert this to an `#[unsafe(naked)]` function
|
LL + #[unsafe(naked)]
LL | async unsafe extern "custom" fn no_async_fn() {
|
error[E0277]: expected a `Fn()` closure, found `unsafe extern "custom" fn()`
--> $DIR/bad-custom.rs:102:64
|
LL | fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() {
| ^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
LL |
LL | f
| - return type was inferred to be `unsafe extern "custom" fn()` here
|
= help: the trait `Fn()` is not implemented for `unsafe extern "custom" fn()`
= note: unsafe function cannot be called generically without an unsafe block
= note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }`
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:25:1
|
LL | unsafe extern "custom" fn double(a: i64) -> i64 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: convert this to an `#[unsafe(naked)]` function
|
LL + #[unsafe(naked)]
LL | unsafe extern "custom" fn double(a: i64) -> i64 {
|
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:34:5
|
LL | unsafe extern "custom" fn is_even(self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: convert this to an `#[unsafe(naked)]` function
|
LL + #[unsafe(naked)]
LL | unsafe extern "custom" fn is_even(self) -> bool {
|
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:42:5
|
LL | unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: convert this to an `#[unsafe(naked)]` function
|
LL + #[unsafe(naked)]
LL | 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
--> $DIR/bad-custom.rs:58:5
|
LL | extern "custom" fn negate(a: i64) -> i64 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: convert this to an `#[unsafe(naked)]` function
|
LL + #[unsafe(naked)]
LL | extern "custom" fn negate(a: i64) -> i64 {
|
error: functions with the `"custom"` ABI cannot be called
--> $DIR/bad-custom.rs:75:14
|
LL | unsafe { f(x) }
| ^^^^
error: functions with the `"custom"` ABI cannot be called
--> $DIR/bad-custom.rs:80:14
|
LL | unsafe { f(x) }
| ^^^^
error: functions with the `"custom"` ABI cannot be called
--> $DIR/bad-custom.rs:87:14
|
LL | unsafe { f(x) }
| ^^^^
error: functions with the `"custom"` ABI cannot be called
--> $DIR/bad-custom.rs:109:20
|
LL | assert_eq!(double(21), 42);
| ^^^^^^^^^^
error: functions with the `"custom"` ABI cannot be called
--> $DIR/bad-custom.rs:112:29
|
LL | assert_eq!(unsafe { increment(41) }, 42);
| ^^^^^^^^^^^^^
error: functions with the `"custom"` ABI cannot be called
--> $DIR/bad-custom.rs:115:17
|
LL | assert!(Thing(41).is_even());
| ^^^^^^^^^^^^^^^^^^^
error: functions with the `"custom"` ABI cannot be called
--> $DIR/bad-custom.rs:118:20
|
LL | assert_eq!(Thing::bitwise_not(42), !42);
| ^^^^^^^^^^^^^^^^^^^^^^
error[E0015]: inline assembly is not allowed in constant functions
--> $DIR/bad-custom.rs:93:5
|
LL | std::arch::naked_asm!("")
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 28 previous errors
Some errors have detailed explanations: E0015, E0277.
For more information about an error, try `rustc --explain E0015`.

88
tests/ui/abi/custom.rs Normal file
View file

@ -0,0 +1,88 @@
// Test that `extern "custom"` functions can be called from assembly, and defined using a naked
// function, and `global_asm!` with an `extern "custom"` block.
//
//@ run-pass
//@ only-x86_64
#![feature(abi_custom)]
use std::arch::{asm, global_asm, naked_asm};
#[unsafe(naked)]
unsafe extern "custom" fn double() {
naked_asm!("add rax, rax", "ret");
}
global_asm!(
// work around macOS prefixing symbols with _
" .globl {0}",
"{0}:",
" add rax, 1",
" ret",
sym increment,
);
unsafe extern "custom" {
fn increment();
}
#[repr(transparent)]
struct Thing(u64);
impl Thing {
#[unsafe(naked)]
unsafe extern "custom" fn is_even() {
naked_asm!("test al, 1", "sete al", "ret");
}
}
trait BitwiseNot {
#[unsafe(naked)]
unsafe extern "custom" fn bitwise_not() {
naked_asm!("not rax", "ret");
}
}
impl BitwiseNot for Thing {}
#[unsafe(naked)]
unsafe extern "C" fn const_generic<const N: u64>() {
naked_asm!(
"mov rax, {}",
"ret",
const N,
);
}
pub fn main() {
let mut x: u64 = 21;
unsafe { asm!("call {}", sym double, inout("rax") x) };
assert_eq!(x, 42);
let mut x: u64 = 41;
unsafe { asm!("call {}", sym increment, inout("rax") x) };
assert_eq!(x, 42);
let mut x: u8;
unsafe { asm!("call {}", sym Thing::is_even, inout("al") 42u8 => x) };
assert!(x != 0);
let mut x: u64 = 42;
unsafe { asm!("call {}", sym Thing::bitwise_not, inout("rax") x) };
assert_eq!(x, !42);
// Create and call in `asm!` an `extern "custom"` function pointer.
fn caller(f: unsafe extern "custom" fn(), mut x: u64) -> u64 {
unsafe { asm!("call {}", in(reg) f, inout("rax") x) };
x
}
assert_eq!(caller(double, 2), 4);
let x: u64;
unsafe { asm!("call {}", sym const_generic::<42>, out("rax") x) };
assert_eq!(x, 42);
let x: u64;
unsafe { asm!("call {}", sym const_generic::<84>, out("rax") x) };
assert_eq!(x, 84);
}

View file

@ -0,0 +1,51 @@
//@ add-core-stubs
//@ needs-asm-support
#![no_core]
#![feature(no_core, lang_items)]
#![crate_type = "rlib"]
extern crate minicore;
use minicore::*;
#[unsafe(naked)]
unsafe extern "custom" fn f7() {
//~^ ERROR "custom" ABI is experimental
naked_asm!("")
}
trait Tr {
extern "custom" fn m7();
//~^ ERROR "custom" ABI is experimental
//~| ERROR functions with the `"custom"` ABI must be unsafe
#[unsafe(naked)]
extern "custom" fn dm7() {
//~^ ERROR "custom" ABI is experimental
//~| ERROR functions with the `"custom"` ABI must be unsafe
naked_asm!("")
}
}
struct S;
// Methods in trait impl
impl Tr for S {
#[unsafe(naked)]
extern "custom" fn m7() {
//~^ ERROR "custom" ABI is experimental
//~| ERROR functions with the `"custom"` ABI must be unsafe
naked_asm!("")
}
}
// Methods in inherent impl
impl S {
#[unsafe(naked)]
extern "custom" fn im7() {
//~^ ERROR "custom" ABI is experimental
//~| ERROR functions with the `"custom"` ABI must be unsafe
naked_asm!("")
}
}
type A7 = extern "custom" fn(); //~ ERROR "custom" ABI is experimental
extern "custom" {} //~ ERROR "custom" ABI is experimental

View file

@ -0,0 +1,117 @@
error: functions with the `"custom"` ABI must be unsafe
--> $DIR/feature-gate-abi-custom.rs:16:5
|
LL | extern "custom" fn m7();
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add the `unsafe` keyword to this definition
|
LL | unsafe extern "custom" fn m7();
| ++++++
error: functions with the `"custom"` ABI must be unsafe
--> $DIR/feature-gate-abi-custom.rs:20:5
|
LL | extern "custom" fn dm7() {
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add the `unsafe` keyword to this definition
|
LL | unsafe extern "custom" fn dm7() {
| ++++++
error: functions with the `"custom"` ABI must be unsafe
--> $DIR/feature-gate-abi-custom.rs:32:5
|
LL | extern "custom" fn m7() {
| ^^^^^^^^^^^^^^^^^^^^^^^
|
help: add the `unsafe` keyword to this definition
|
LL | unsafe extern "custom" fn m7() {
| ++++++
error: functions with the `"custom"` ABI must be unsafe
--> $DIR/feature-gate-abi-custom.rs:42:5
|
LL | extern "custom" fn im7() {
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add the `unsafe` keyword to this definition
|
LL | unsafe extern "custom" fn im7() {
| ++++++
error[E0658]: the extern "custom" ABI is experimental and subject to change
--> $DIR/feature-gate-abi-custom.rs:11:15
|
LL | unsafe extern "custom" fn f7() {
| ^^^^^^^^
|
= note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
= help: add `#![feature(abi_custom)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "custom" ABI is experimental and subject to change
--> $DIR/feature-gate-abi-custom.rs:16:12
|
LL | extern "custom" fn m7();
| ^^^^^^^^
|
= note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
= help: add `#![feature(abi_custom)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "custom" ABI is experimental and subject to change
--> $DIR/feature-gate-abi-custom.rs:20:12
|
LL | extern "custom" fn dm7() {
| ^^^^^^^^
|
= note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
= help: add `#![feature(abi_custom)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "custom" ABI is experimental and subject to change
--> $DIR/feature-gate-abi-custom.rs:32:12
|
LL | extern "custom" fn m7() {
| ^^^^^^^^
|
= note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
= help: add `#![feature(abi_custom)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "custom" ABI is experimental and subject to change
--> $DIR/feature-gate-abi-custom.rs:42:12
|
LL | extern "custom" fn im7() {
| ^^^^^^^^
|
= note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
= help: add `#![feature(abi_custom)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "custom" ABI is experimental and subject to change
--> $DIR/feature-gate-abi-custom.rs:49:18
|
LL | type A7 = extern "custom" fn();
| ^^^^^^^^
|
= note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
= help: add `#![feature(abi_custom)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "custom" ABI is experimental and subject to change
--> $DIR/feature-gate-abi-custom.rs:51:8
|
LL | extern "custom" {}
| ^^^^^^^^
|
= note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
= help: add `#![feature(abi_custom)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 11 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -9,6 +9,7 @@ avr-interrupt
avr-non-blocking-interrupt
cdecl
cdecl-unwind
custom
efiapi
fastcall
fastcall-unwind