about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJubilee Young <workingjubilee@gmail.com>2025-06-26 19:35:28 -0700
committerJubilee Young <workingjubilee@gmail.com>2025-06-27 11:04:04 -0700
commitd6b3314fb29cc688a968811b6a78dc8a5309cb7d (patch)
treee61a9aca70da93e9f83382b173a722564a3f2a2b
parentdb33e98540fca8f3268cafc6276dbefd55681d55 (diff)
downloadrust-d6b3314fb29cc688a968811b6a78dc8a5309cb7d.tar.gz
rust-d6b3314fb29cc688a968811b6a78dc8a5309cb7d.zip
compiler: allow interrupts to return () or !
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs16
-rw-r--r--tests/ui/abi/interrupt-returns-never-or-unit.rs110
2 files changed, 124 insertions, 2 deletions
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index da248251203..478cec23fd5 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -396,7 +396,13 @@ impl<'a> AstValidator<'a> {
                         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 {
+                            if let FnRetTy::Ty(ref ret_ty) = sig.decl.output
+                                && match &ret_ty.kind {
+                                    TyKind::Never => false,
+                                    TyKind::Tup(tup) if tup.is_empty() => false,
+                                    _ => true,
+                                }
+                            {
                                 self.dcx().emit_err(errors::AbiMustNotHaveReturnType {
                                     span: ret_ty.span,
                                     abi,
@@ -455,7 +461,13 @@ impl<'a> AstValidator<'a> {
 
     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 {
+        if let FnRetTy::Ty(ref ret_ty) = sig.decl.output
+            && match &ret_ty.kind {
+                TyKind::Never => false,
+                TyKind::Tup(tup) if tup.is_empty() => false,
+                _ => true,
+            }
+        {
             spans.push(ret_ty.span);
         }
 
diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs
new file mode 100644
index 00000000000..34d509c8328
--- /dev/null
+++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs
@@ -0,0 +1,110 @@
+/*! Tests interrupt ABIs can return !
+
+Most interrupt ABIs share a similar restriction in terms of not allowing most signatures,
+but it makes sense to allow them to return ! because they could indeed be divergent.
+
+This test uses `cfg` because it is not testing whether these ABIs work on the platform.
+*/
+//@ add-core-stubs
+//@ revisions: x64 i686 riscv32 riscv64 avr msp430
+//@ build-pass
+//
+//@ [x64] needs-llvm-components: x86
+//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
+//@ [i686] needs-llvm-components: x86
+//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib
+//@ [riscv32] needs-llvm-components: riscv
+//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib
+//@ [riscv64] needs-llvm-components: riscv
+//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib
+//@ [avr] needs-llvm-components: avr
+//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib
+//@ [msp430] needs-llvm-components: msp430
+//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib
+#![no_core]
+#![feature(
+    no_core,
+    abi_msp430_interrupt,
+    abi_avr_interrupt,
+    abi_x86_interrupt,
+    abi_riscv_interrupt
+)]
+
+extern crate minicore;
+use minicore::*;
+
+/* interrupts can return never */
+
+#[cfg(avr)]
+extern "avr-interrupt" fn avr_ret_never() -> ! {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(msp430)]
+extern "msp430-interrupt" fn msp430_ret_never() -> ! {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(any(riscv32,riscv64))]
+extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(any(riscv32,riscv64))]
+extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(any(x64,i686))]
+extern "x86-interrupt" fn x86_ret_never() -> ! {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+/* interrupts can return explicit () */
+
+#[cfg(avr)]
+extern "avr-interrupt" fn avr_ret_unit() -> () {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(msp430)]
+extern "msp430-interrupt" fn msp430_ret_unit() -> () {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(any(riscv32,riscv64))]
+extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(any(riscv32,riscv64))]
+extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+#[cfg(any(x64,i686))]
+extern "x86-interrupt" fn x86_ret_unit() -> () {
+    unsafe { hint::unreachable_unchecked() }
+}
+
+/* extern "interrupt" fnptrs can return ! too */
+
+#[cfg(avr)]
+fn avr_ptr(_f: extern "avr-interrupt" fn() -> !) {
+}
+
+#[cfg(msp430)]
+fn msp430_ptr(_f: extern "msp430-interrupt" fn() -> !) {
+}
+
+#[cfg(any(riscv32,riscv64))]
+fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn() -> !) {
+}
+
+#[cfg(any(riscv32,riscv64))]
+fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn() -> !) {
+}
+
+#[cfg(any(x64,i686))]
+fn x86_ptr(_f: extern "x86-interrupt" fn() -> !) {
+}