diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index d0b92587b59f0..df02b91996f34 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -2518,6 +2518,9 @@ bitflags! { /// `#[used]`: indicates that LLVM can't eliminate this function (but the /// linker can!). const USED = 1 << 9; + /// #[ffi_returns_twice], indicates that an extern function can return + /// multiple times + const FFI_RETURNS_TWICE = 1 << 10; } } diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 827ebff10f51c..dab5cab65cd02 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -223,6 +223,9 @@ pub fn from_fn_attrs( if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { Attribute::Cold.apply_llfn(Function, llfn); } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) { + Attribute::ReturnsTwice.apply_llfn(Function, llfn); + } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { naked(llfn, true); } diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index e761d2247a757..fe2664118f798 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -114,6 +114,7 @@ pub enum Attribute { SanitizeMemory = 22, NonLazyBind = 23, OptimizeNone = 24, + ReturnsTwice = 25, } /// LLVMIntPredicate diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index ec1d9d24730ed..1a03af4398dc2 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2388,6 +2388,18 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; } else if attr.check_name("unwind") { codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND; + } else if attr.check_name("ffi_returns_twice") { + if tcx.is_foreign_item(id) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE; + } else { + // `#[ffi_returns_twice]` is only allowed `extern fn`s + struct_span_err!( + tcx.sess, + attr.span, + E0723, + "`#[ffi_returns_twice]` may only be used on foreign functions" + ).emit(); + } } else if attr.check_name("rustc_allocator_nounwind") { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; } else if attr.check_name("naked") { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 3c4a0760f3ee2..6104c52141217 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4738,4 +4738,5 @@ register_diagnostics! { E0698, // type inside generator must be known in this context E0719, // duplicate values for associated type binding E0722, // Malformed #[optimize] attribute + E0723, // `#[ffi_returns_twice]` is only allowed in foreign functions } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d574b410ccc06..c9b441193b76d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -290,6 +290,9 @@ declare_features! ( // The `repr(i128)` annotation for enums. (active, repr128, "1.16.0", Some(35118), None), + // Allows the use of `#[ffi_returns_twice]` on foreign functions. + (active, ffi_returns_twice, "1.34.0", Some(58314), None), + // The `unadjusted` ABI; perma-unstable. // // rustc internal @@ -1128,6 +1131,11 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu "the `#[naked]` attribute \ is an experimental feature", cfg_fn!(naked_functions))), + ("ffi_returns_twice", Whitelisted, template!(Word), Gated(Stability::Unstable, + "ffi_returns_twice", + "the `#[ffi_returns_twice]` attribute \ + is an experimental feature", + cfg_fn!(ffi_returns_twice))), ("target_feature", Whitelisted, template!(List: r#"enable = "name""#), Ungated), ("export_name", Whitelisted, template!(NameValueStr: "name"), Ungated), ("inline", Whitelisted, template!(Word, List: "always|never"), Ungated), diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index b33165b846339..a00417a362927 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -190,6 +190,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::NonLazyBind; case OptimizeNone: return Attribute::OptimizeNone; + case ReturnsTwice: + return Attribute::ReturnsTwice; } report_fatal_error("bad AttributeKind"); } diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index 933266b402526..a9d267cdb3175 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -85,6 +85,7 @@ enum LLVMRustAttribute { SanitizeMemory = 22, NonLazyBind = 23, OptimizeNone = 24, + ReturnsTwice = 25, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/src/test/codegen/ffi-returns-twice.rs b/src/test/codegen/ffi-returns-twice.rs new file mode 100644 index 0000000000000..4db328f1cdfaf --- /dev/null +++ b/src/test/codegen/ffi-returns-twice.rs @@ -0,0 +1,12 @@ +// compile-flags: -C no-prepopulate-passes +#![crate_type = "lib"] +#![feature(ffi_returns_twice)] + +pub fn bar() { unsafe { foo() } } + +extern { + // CHECK-LABEL: declare void @foo() + // CHECK-SAME: [[ATTRS:#[0-9]+]] + // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}returns_twice{{.*}} } + #[ffi_returns_twice] pub fn foo(); +} diff --git a/src/test/ui/feature-gates/feature-gate-ffi_returns_twice.rs b/src/test/ui/feature-gates/feature-gate-ffi_returns_twice.rs new file mode 100644 index 0000000000000..d3df6e5a852b5 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-ffi_returns_twice.rs @@ -0,0 +1,7 @@ +// ignore-tidy-linelength +#![crate_type = "lib"] + +extern { + #[ffi_returns_twice] //~ ERROR the `#[ffi_returns_twice]` attribute is an experimental feature (see issue #58314) + pub fn foo(); +} diff --git a/src/test/ui/feature-gates/feature-gate-ffi_returns_twice.stderr b/src/test/ui/feature-gates/feature-gate-ffi_returns_twice.stderr new file mode 100644 index 0000000000000..5c111fe78f48a --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-ffi_returns_twice.stderr @@ -0,0 +1,11 @@ +error[E0658]: the `#[ffi_returns_twice]` attribute is an experimental feature (see issue #58314) + --> $DIR/feature-gate-ffi_returns_twice.rs:5:5 + | +LL | #[ffi_returns_twice] //~ ERROR the `#[ffi_returns_twice]` attribute is an experimental feature (see issue #58314) + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(ffi_returns_twice)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/ffi_returns_twice.rs b/src/test/ui/ffi_returns_twice.rs new file mode 100644 index 0000000000000..93c372e1d83dc --- /dev/null +++ b/src/test/ui/ffi_returns_twice.rs @@ -0,0 +1,6 @@ +// ignore-tidy-linelength +#![feature(ffi_returns_twice)] +#![crate_type = "lib"] + +#[ffi_returns_twice] //~ ERROR `#[ffi_returns_twice]` may only be used on foreign functions +pub fn foo() {} diff --git a/src/test/ui/ffi_returns_twice.stderr b/src/test/ui/ffi_returns_twice.stderr new file mode 100644 index 0000000000000..5a6026c403dae --- /dev/null +++ b/src/test/ui/ffi_returns_twice.stderr @@ -0,0 +1,9 @@ +error[E0723]: `#[ffi_returns_twice]` may only be used on foreign functions + --> $DIR/ffi_returns_twice.rs:5:1 + | +LL | #[ffi_returns_twice] //~ ERROR `#[ffi_returns_twice]` may only be used on foreign functions + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0723`.