Auto merge of #59625 - immunant:copy_variadics_typealias, r=eddyb
Refactor C FFI variadics to more closely match their C counterparts, and add Clone implementation We had to make some changes to expose `va_copy` and `va_end` directly to users (mainly for C2Rust, but not exclusively): - redefine the Rust variadic structures to more closely correspond to C: `VaList` now matches `va_list`, and `VaListImpl` matches `__va_list_tag` - add `Clone` for `VaListImpl` - add explicit `as_va_list()` conversion function from `VaListImpl` to `VaList` - add deref coercion from `VaList` to `VaListImpl` - add support for the `asmjs` target All these changes were needed for use cases like: ```Rust let mut ap2 = va_copy(ap); vprintf(fmt, ap2); va_end(&mut ap2); ```
This commit is contained in:
commit
605ea9d05c
@ -5,6 +5,8 @@
|
||||
//! Utilities related to FFI bindings.
|
||||
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
|
||||
/// Equivalent to C's `void` type when used as a [pointer].
|
||||
///
|
||||
@ -45,25 +47,33 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
|
||||
/// Basic implementation of a `va_list`.
|
||||
// The name is WIP, using `VaListImpl` for now.
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
not(target_arch = "x86_64"), not(target_arch = "asmjs")),
|
||||
all(target_arch = "aarch64", target_os = "ios"),
|
||||
windows))]
|
||||
#[repr(transparent)]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
#[lang = "va_list"]
|
||||
pub struct VaListImpl<'f> {
|
||||
ptr: *mut c_void,
|
||||
_marker: PhantomData<&'f c_void>,
|
||||
}
|
||||
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64"), not(target_arch = "asmjs")),
|
||||
all(target_arch = "aarch64", target_os = "ios"),
|
||||
windows))]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
extern {
|
||||
type VaListImpl;
|
||||
}
|
||||
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
all(target_arch = "aarch64", target_os = "ios"),
|
||||
windows))]
|
||||
impl fmt::Debug for VaListImpl {
|
||||
impl<'f> fmt::Debug for VaListImpl<'f> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "va_list* {:p}", self)
|
||||
write!(f, "va_list* {:p}", self.ptr)
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,12 +89,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
struct VaListImpl {
|
||||
#[lang = "va_list"]
|
||||
pub struct VaListImpl<'f> {
|
||||
stack: *mut c_void,
|
||||
gr_top: *mut c_void,
|
||||
vr_top: *mut c_void,
|
||||
gr_offs: i32,
|
||||
vr_offs: i32,
|
||||
_marker: PhantomData<&'f c_void>,
|
||||
}
|
||||
|
||||
/// PowerPC ABI implementation of a `va_list`.
|
||||
@ -95,12 +107,14 @@ struct VaListImpl {
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
struct VaListImpl {
|
||||
#[lang = "va_list"]
|
||||
pub struct VaListImpl<'f> {
|
||||
gpr: u8,
|
||||
fpr: u8,
|
||||
reserved: u16,
|
||||
overflow_arg_area: *mut c_void,
|
||||
reg_save_area: *mut c_void,
|
||||
_marker: PhantomData<&'f c_void>,
|
||||
}
|
||||
|
||||
/// x86_64 ABI implementation of a `va_list`.
|
||||
@ -111,22 +125,131 @@ struct VaListImpl {
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
struct VaListImpl {
|
||||
#[lang = "va_list"]
|
||||
pub struct VaListImpl<'f> {
|
||||
gp_offset: i32,
|
||||
fp_offset: i32,
|
||||
overflow_arg_area: *mut c_void,
|
||||
reg_save_area: *mut c_void,
|
||||
_marker: PhantomData<&'f c_void>,
|
||||
}
|
||||
|
||||
/// asm.js ABI implementation of a `va_list`.
|
||||
// asm.js uses the PNaCl ABI, which specifies that a `va_list` is
|
||||
// an array of 4 32-bit integers, according to the old PNaCl docs at
|
||||
// https://web.archive.org/web/20130518054430/https://www.chromium.org/nativeclient/pnacl/bitcode-abi#TOC-Derived-Types
|
||||
// and clang does the same in `CreatePNaClABIBuiltinVaListDecl` from `lib/AST/ASTContext.cpp`
|
||||
#[cfg(all(target_arch = "asmjs", not(windows)))]
|
||||
#[repr(C)]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
#[lang = "va_list"]
|
||||
pub struct VaListImpl<'f> {
|
||||
inner: [crate::mem::MaybeUninit<i32>; 4],
|
||||
_marker: PhantomData<&'f c_void>,
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "asmjs", not(windows)))]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
impl<'f> fmt::Debug for VaListImpl<'f> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unsafe {
|
||||
write!(f, "va_list* [{:#x}, {:#x}, {:#x}, {:#x}]",
|
||||
self.inner[0].read(), self.inner[1].read(),
|
||||
self.inner[2].read(), self.inner[3].read())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for a `va_list`
|
||||
#[lang = "va_list"]
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
#[repr(transparent)]
|
||||
pub struct VaList<'a>(&'a mut VaListImpl);
|
||||
pub struct VaList<'a, 'f: 'a> {
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64"), not(target_arch = "asmjs")),
|
||||
all(target_arch = "aarch64", target_os = "ios"),
|
||||
windows))]
|
||||
inner: VaListImpl<'f>,
|
||||
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc",
|
||||
target_arch = "x86_64", target_arch = "asmjs"),
|
||||
any(not(target_arch = "aarch64"), not(target_os = "ios")),
|
||||
not(windows)))]
|
||||
inner: &'a mut VaListImpl<'f>,
|
||||
|
||||
_marker: PhantomData<&'a mut VaListImpl<'f>>,
|
||||
}
|
||||
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64"), not(target_arch = "asmjs")),
|
||||
all(target_arch = "aarch64", target_os = "ios"),
|
||||
windows))]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
impl<'f> VaListImpl<'f> {
|
||||
/// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
|
||||
#[inline]
|
||||
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
|
||||
VaList {
|
||||
inner: VaListImpl { ..*self },
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc",
|
||||
target_arch = "x86_64", target_arch = "asmjs"),
|
||||
any(not(target_arch = "aarch64"), not(target_os = "ios")),
|
||||
not(windows)))]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
impl<'f> VaListImpl<'f> {
|
||||
/// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
|
||||
#[inline]
|
||||
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
|
||||
VaList {
|
||||
inner: self,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
|
||||
type Target = VaListImpl<'f>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &VaListImpl<'f> {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
// The VaArgSafe trait needs to be used in public interfaces, however, the trait
|
||||
// itself must not be allowed to be used outside this module. Allowing users to
|
||||
@ -175,56 +298,76 @@ impl<T> sealed_trait::VaArgSafe for *mut T {}
|
||||
issue = "44930")]
|
||||
impl<T> sealed_trait::VaArgSafe for *const T {}
|
||||
|
||||
impl<'a> VaList<'a> {
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
#[cfg(not(bootstrap))]
|
||||
impl<'f> VaListImpl<'f> {
|
||||
/// Advance to the next arg.
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
#[inline]
|
||||
pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
|
||||
va_arg(self)
|
||||
}
|
||||
|
||||
/// Copies the `va_list` at the current location.
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
|
||||
where F: for<'copy> FnOnce(VaList<'copy>) -> R {
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
all(target_arch = "aarch64", target_os = "ios"),
|
||||
windows))]
|
||||
let mut ap = va_copy(self);
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
|
||||
not(windows), not(all(target_arch = "aarch64", target_os = "ios"))))]
|
||||
let mut ap_inner = va_copy(self);
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
|
||||
not(windows), not(all(target_arch = "aarch64", target_os = "ios"))))]
|
||||
let mut ap = VaList(&mut ap_inner);
|
||||
let ret = f(VaList(ap.0));
|
||||
where F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R {
|
||||
let mut ap = self.clone();
|
||||
let ret = f(ap.as_va_list());
|
||||
va_end(&mut ap);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
#[cfg(not(bootstrap))]
|
||||
impl<'f> Clone for VaListImpl<'f> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
let mut dest = crate::mem::MaybeUninit::uninit();
|
||||
unsafe {
|
||||
va_copy(dest.as_mut_ptr(), self);
|
||||
dest.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
#[cfg(not(bootstrap))]
|
||||
impl<'f> Drop for VaListImpl<'f> {
|
||||
fn drop(&mut self) {
|
||||
// FIXME: this should call `va_end`, but there's no clean way to
|
||||
// guarantee that `drop` always gets inlined into its caller,
|
||||
// so the `va_end` would get directly called from the same function as
|
||||
// the corresponding `va_copy`. `man va_end` states that C requires this,
|
||||
// and LLVM basically follows the C semantics, so we need to make sure
|
||||
// that `va_end` is always called from the same function as `va_copy`.
|
||||
// For more details, see https://github.com/rust-lang/rust/pull/59625
|
||||
// and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
|
||||
//
|
||||
// This works for now, since `va_end` is a no-op on all current LLVM targets.
|
||||
}
|
||||
}
|
||||
|
||||
extern "rust-intrinsic" {
|
||||
/// Destroy the arglist `ap` after initialization with `va_start` or
|
||||
/// `va_copy`.
|
||||
fn va_end(ap: &mut VaList<'_>);
|
||||
#[cfg(not(bootstrap))]
|
||||
fn va_end(ap: &mut VaListImpl<'_>);
|
||||
|
||||
/// Copies the current location of arglist `src` to the arglist `dst`.
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
all(target_arch = "aarch64", target_os = "ios"),
|
||||
windows))]
|
||||
fn va_copy<'a>(src: &VaList<'a>) -> VaList<'a>;
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
|
||||
not(windows), not(all(target_arch = "aarch64", target_os = "ios"))))]
|
||||
fn va_copy(src: &VaList<'_>) -> VaListImpl;
|
||||
#[cfg(not(bootstrap))]
|
||||
fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
|
||||
|
||||
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
|
||||
/// argument `ap` points to.
|
||||
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaList<'_>) -> T;
|
||||
#[cfg(not(bootstrap))]
|
||||
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
|
||||
}
|
||||
|
@ -1674,7 +1674,7 @@ fn lower_ty_direct(&mut self, t: &Ty, mut itctx: ImplTraitContext<'_>) -> hir::T
|
||||
}
|
||||
TyKind::Mac(_) => bug!("`TyMac` should have been expanded by now."),
|
||||
TyKind::CVarArgs => {
|
||||
// Create the implicit lifetime of the "spoofed" `VaList`.
|
||||
// Create the implicit lifetime of the "spoofed" `VaListImpl`.
|
||||
let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
|
||||
let lt = self.new_implicit_lifetime(span);
|
||||
hir::TyKind::CVarArgs(lt)
|
||||
|
@ -1930,7 +1930,7 @@ pub enum TyKind {
|
||||
Infer,
|
||||
/// Placeholder for a type that has failed to be defined.
|
||||
Err,
|
||||
/// Placeholder for C-variadic arguments. We "spoof" the `VaList` created
|
||||
/// Placeholder for C-variadic arguments. We "spoof" the `VaListImpl` created
|
||||
/// from the variadic arguments. This type is only valid up to typeck.
|
||||
CVarArgs(Lifetime),
|
||||
}
|
||||
|
@ -2711,7 +2711,7 @@ fn new_internal(
|
||||
}
|
||||
|
||||
// If this is a C-variadic function, this is not the return value,
|
||||
// and there is one or more fixed arguments; ensure that the `VaList`
|
||||
// and there is one or more fixed arguments; ensure that the `VaListImpl`
|
||||
// is ignored as an argument.
|
||||
if sig.c_variadic {
|
||||
match (last_arg_idx, arg_idx) {
|
||||
@ -2722,7 +2722,7 @@ fn new_internal(
|
||||
};
|
||||
match ty.sty {
|
||||
ty::Adt(def, _) if def.did == va_list_did => {
|
||||
// This is the "spoofed" `VaList`. Set the arguments mode
|
||||
// This is the "spoofed" `VaListImpl`. Set the arguments mode
|
||||
// so that it will be ignored.
|
||||
arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs);
|
||||
}
|
||||
|
@ -146,15 +146,8 @@ fn codegen_intrinsic_call(
|
||||
self.va_end(args[0].immediate())
|
||||
}
|
||||
"va_copy" => {
|
||||
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
|
||||
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
|
||||
(Some(_), _) => self.load(args[0].immediate(),
|
||||
tcx.data_layout.pointer_align.abi),
|
||||
(None, _) => bug!("`va_list` language item must be defined")
|
||||
};
|
||||
let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
|
||||
self.call(intrinsic, &[llresult, va_list], None);
|
||||
return;
|
||||
self.call(intrinsic, &[args[0].immediate(), args[1].immediate()], None)
|
||||
}
|
||||
"va_arg" => {
|
||||
match fn_ty.ret.layout.abi {
|
||||
@ -743,37 +736,12 @@ fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
|
||||
self.call(expect, &[cond, self.const_bool(expected)], None)
|
||||
}
|
||||
|
||||
fn va_start(&mut self, list: &'ll Value) -> &'ll Value {
|
||||
let target = &self.cx.tcx.sess.target.target;
|
||||
let arch = &target.arch;
|
||||
// A pointer to the architecture specific structure is passed to this
|
||||
// function. For pointer variants (i686, RISC-V, Windows, etc), we
|
||||
// should do do nothing, as the address to the pointer is needed. For
|
||||
// architectures with a architecture specific structure (`Aarch64`,
|
||||
// `X86_64`, etc), this function should load the structure from the
|
||||
// address provided.
|
||||
let va_list = match &**arch {
|
||||
_ if target.options.is_like_windows => list,
|
||||
"aarch64" if target.target_os == "ios" => list,
|
||||
"aarch64" | "x86_64" | "powerpc" =>
|
||||
self.load(list, self.tcx().data_layout.pointer_align.abi),
|
||||
_ => list,
|
||||
};
|
||||
fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value {
|
||||
let intrinsic = self.cx().get_intrinsic("llvm.va_start");
|
||||
self.call(intrinsic, &[va_list], None)
|
||||
}
|
||||
|
||||
fn va_end(&mut self, list: &'ll Value) -> &'ll Value {
|
||||
let target = &self.cx.tcx.sess.target.target;
|
||||
let arch = &target.arch;
|
||||
// See the comment in `va_start` for the purpose of the following.
|
||||
let va_list = match &**arch {
|
||||
_ if target.options.is_like_windows => list,
|
||||
"aarch64" if target.target_os == "ios" => list,
|
||||
"aarch64" | "x86_64" | "powerpc" =>
|
||||
self.load(list, self.tcx().data_layout.pointer_align.abi),
|
||||
_ => list,
|
||||
};
|
||||
fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value {
|
||||
let intrinsic = self.cx().get_intrinsic("llvm.va_end");
|
||||
self.call(intrinsic, &[va_list], None)
|
||||
}
|
||||
|
@ -132,16 +132,6 @@ pub(super) fn emit_va_arg(
|
||||
// For all other architecture/OS combinations fall back to using
|
||||
// the LLVM va_arg instruction.
|
||||
// https://llvm.org/docs/LangRef.html#va-arg-instruction
|
||||
_ => {
|
||||
let va_list = if (target.arch == "aarch64" ||
|
||||
target.arch == "x86_64" ||
|
||||
target.arch == "powerpc") &&
|
||||
!target.options.is_like_windows {
|
||||
bx.load(addr.immediate(), bx.tcx().data_layout.pointer_align.abi)
|
||||
} else {
|
||||
addr.immediate()
|
||||
};
|
||||
bx.va_arg(va_list, bx.cx.layout_of(target_ty).llvm_type(bx.cx))
|
||||
}
|
||||
_ => bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx))
|
||||
}
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ fn codegen_call_terminator<'b>(
|
||||
return;
|
||||
}
|
||||
|
||||
// The "spoofed" `VaList` added to a C-variadic functions signature
|
||||
// The "spoofed" `VaListImpl` added to a C-variadic functions signature
|
||||
// should not be included in the `extra_args` calculation.
|
||||
let extra_args_start_idx = sig.inputs().len() - if sig.c_variadic { 1 } else { 0 };
|
||||
let extra_args = &args[extra_args_start_idx..];
|
||||
@ -687,7 +687,7 @@ fn codegen_call_terminator<'b>(
|
||||
(&args[..], None)
|
||||
};
|
||||
|
||||
// Useful determining if the current argument is the "spoofed" `VaList`
|
||||
// Useful determining if the current argument is the "spoofed" `VaListImpl`
|
||||
let last_arg_idx = if sig.inputs().is_empty() {
|
||||
None
|
||||
} else {
|
||||
@ -695,7 +695,7 @@ fn codegen_call_terminator<'b>(
|
||||
};
|
||||
'make_args: for (i, arg) in first_args.iter().enumerate() {
|
||||
// If this is a C-variadic function the function signature contains
|
||||
// an "spoofed" `VaList`. This argument is ignored, but we need to
|
||||
// an "spoofed" `VaListImpl`. This argument is ignored, but we need to
|
||||
// populate it with a dummy operand so that the users real arguments
|
||||
// are not overwritten.
|
||||
let i = if sig.c_variadic && last_arg_idx.map(|x| i >= x).unwrap_or(false) {
|
||||
|
@ -83,7 +83,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
|
||||
scopes: IndexVec<mir::SourceScope, debuginfo::MirDebugScope<Bx::DIScope>>,
|
||||
|
||||
/// If this function is a C-variadic function, this contains the `PlaceRef` of the
|
||||
/// "spoofed" `VaList`.
|
||||
/// "spoofed" `VaListImpl`.
|
||||
va_list_ref: Option<PlaceRef<'tcx, Bx::Value>>,
|
||||
}
|
||||
|
||||
@ -562,35 +562,24 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
indirect_operand.store(bx, tmp);
|
||||
tmp
|
||||
} else {
|
||||
let tmp = PlaceRef::alloca(bx, arg.layout, &name);
|
||||
if fx.fn_ty.c_variadic && last_arg_idx.map(|idx| arg_index == idx).unwrap_or(false) {
|
||||
let va_list_impl = match arg_decl.ty.ty_adt_def() {
|
||||
Some(adt) => adt.non_enum_variant(),
|
||||
None => bug!("`va_list` language item improperly constructed")
|
||||
let va_list_did = match tcx.lang_items().va_list() {
|
||||
Some(did) => did,
|
||||
None => bug!("`va_list` lang item required for C-variadic functions"),
|
||||
};
|
||||
match tcx.type_of(va_list_impl.fields[0].did).sty {
|
||||
ty::Ref(_, ty, _) => {
|
||||
// If the underlying structure the `VaList` contains is a structure,
|
||||
// we need to allocate it (e.g., X86_64 on Linux).
|
||||
let tmp = PlaceRef::alloca(bx, arg.layout, &name);
|
||||
if let ty::Adt(..) = ty.sty {
|
||||
let layout = bx.layout_of(ty);
|
||||
// Create an unnamed allocation for the backing structure
|
||||
// and store it in the the spoofed `VaList`.
|
||||
let backing = PlaceRef::alloca(bx, layout, "");
|
||||
bx.store(backing.llval, tmp.llval, layout.align.abi);
|
||||
}
|
||||
// Call `va_start` on the spoofed `VaList`.
|
||||
match arg_decl.ty.sty {
|
||||
ty::Adt(def, _) if def.did == va_list_did => {
|
||||
// Call `va_start` on the spoofed `VaListImpl`.
|
||||
bx.va_start(tmp.llval);
|
||||
*va_list_ref = Some(tmp);
|
||||
tmp
|
||||
}
|
||||
_ => bug!("improperly constructed `va_list` lang item"),
|
||||
},
|
||||
_ => bug!("last argument of variadic function is not a `va_list`")
|
||||
}
|
||||
} else {
|
||||
let tmp = PlaceRef::alloca(bx, arg.layout, &name);
|
||||
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
|
||||
tmp
|
||||
}
|
||||
tmp
|
||||
};
|
||||
let upvar_debuginfo = &mir.__upvar_debuginfo_codegen_only_do_not_use;
|
||||
arg_scope.map(|scope| {
|
||||
|
@ -20,10 +20,10 @@ fn codegen_intrinsic_call(
|
||||
fn abort(&mut self);
|
||||
fn assume(&mut self, val: Self::Value);
|
||||
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
|
||||
/// Trait method used to inject `va_start` on the "spoofed" `VaList` in
|
||||
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
|
||||
/// Rust defined C-variadic functions.
|
||||
fn va_start(&mut self, val: Self::Value) -> Self::Value;
|
||||
/// Trait method used to inject `va_end` on the "spoofed" `VaList` before
|
||||
/// Trait method used to inject `va_end` on the "spoofed" `VaListImpl` before
|
||||
/// Rust defined C-variadic functions return.
|
||||
fn va_end(&mut self, val: Self::Value) -> Self::Value;
|
||||
}
|
||||
|
@ -892,7 +892,7 @@ fn check_foreign_fn(&mut self, id: hir::HirId, decl: &hir::FnDecl) {
|
||||
let sig = self.cx.tcx.fn_sig(def_id);
|
||||
let sig = self.cx.tcx.erase_late_bound_regions(&sig);
|
||||
let inputs = if sig.c_variadic {
|
||||
// Don't include the spoofed `VaList` in the functions list
|
||||
// Don't include the spoofed `VaListImpl` in the functions list
|
||||
// of inputs.
|
||||
&sig.inputs()[..sig.inputs().len() - 1]
|
||||
} else {
|
||||
|
@ -1695,7 +1695,7 @@ fn check_call_inputs(
|
||||
from_hir_call: bool,
|
||||
) {
|
||||
debug!("check_call_inputs({:?}, {:?})", sig, args);
|
||||
// Do not count the `VaList` argument as a "true" argument to
|
||||
// Do not count the `VaListImpl` argument as a "true" argument to
|
||||
// a C-variadic function.
|
||||
let inputs = if sig.c_variadic {
|
||||
&sig.inputs()[..sig.inputs().len() - 1]
|
||||
|
@ -83,12 +83,15 @@ pub fn check_intrinsic_type<'tcx>(tcx: TyCtxt<'tcx>, it: &hir::ForeignItem) {
|
||||
let param = |n| tcx.mk_ty_param(n, InternedString::intern(&format!("P{}", n)));
|
||||
let name = it.ident.as_str();
|
||||
|
||||
let mk_va_list_ty = || {
|
||||
let mk_va_list_ty = |mutbl| {
|
||||
tcx.lang_items().va_list().map(|did| {
|
||||
let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0)));
|
||||
let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
|
||||
let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
|
||||
tcx.mk_mut_ref(tcx.mk_region(env_region), va_list_ty)
|
||||
(tcx.mk_ref(tcx.mk_region(env_region), ty::TypeAndMut {
|
||||
ty: va_list_ty,
|
||||
mutbl
|
||||
}), va_list_ty)
|
||||
})
|
||||
};
|
||||
|
||||
@ -340,42 +343,25 @@ pub fn check_intrinsic_type<'tcx>(tcx: TyCtxt<'tcx>, it: &hir::ForeignItem) {
|
||||
}
|
||||
|
||||
"va_start" | "va_end" => {
|
||||
match mk_va_list_ty() {
|
||||
Some(va_list_ty) => (0, vec![va_list_ty], tcx.mk_unit()),
|
||||
match mk_va_list_ty(hir::MutMutable) {
|
||||
Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], tcx.mk_unit()),
|
||||
None => bug!("`va_list` language item needed for C-variadic intrinsics")
|
||||
}
|
||||
}
|
||||
|
||||
"va_copy" => {
|
||||
match tcx.lang_items().va_list() {
|
||||
Some(did) => {
|
||||
let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0)));
|
||||
let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
|
||||
let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
|
||||
let ret_ty = match va_list_ty.sty {
|
||||
ty::Adt(def, _) if def.is_struct() => {
|
||||
let fields = &def.non_enum_variant().fields;
|
||||
match tcx.type_of(fields[0].did).subst(tcx, &[region.into()]).sty {
|
||||
ty::Ref(_, element_ty, _) => match element_ty.sty {
|
||||
ty::Adt(..) => element_ty,
|
||||
_ => va_list_ty
|
||||
}
|
||||
_ => bug!("va_list structure is invalid")
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
bug!("va_list structure is invalid")
|
||||
}
|
||||
};
|
||||
(0, vec![tcx.mk_imm_ref(tcx.mk_region(env_region), va_list_ty)], ret_ty)
|
||||
match mk_va_list_ty(hir::MutImmutable) {
|
||||
Some((va_list_ref_ty, va_list_ty)) => {
|
||||
let va_list_ptr_ty = tcx.mk_mut_ptr(va_list_ty);
|
||||
(0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.mk_unit())
|
||||
}
|
||||
None => bug!("`va_list` language item needed for C-variadic intrinsics")
|
||||
}
|
||||
}
|
||||
|
||||
"va_arg" => {
|
||||
match mk_va_list_ty() {
|
||||
Some(va_list_ty) => (1, vec![va_list_ty], param(0)),
|
||||
match mk_va_list_ty(hir::MutMutable) {
|
||||
Some((va_list_ref_ty, _)) => (1, vec![va_list_ref_ty], param(0)),
|
||||
None => bug!("`va_list` language item needed for C-variadic intrinsics")
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +170,7 @@
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
pub use core::ffi::VaList;
|
||||
pub use core::ffi::{VaList, VaListImpl};
|
||||
|
||||
mod c_str;
|
||||
mod os_str;
|
||||
|
@ -216,20 +216,27 @@ uint64_t get_c_many_params(void *a, void *b, void *c, void *d, struct quad f) {
|
||||
}
|
||||
|
||||
// Calculates the average of `(x + y) / n` where x: i64, y: f64. There must be exactly n pairs
|
||||
// passed as variadic arguments.
|
||||
double rust_interesting_average(uint64_t n, ...) {
|
||||
va_list pairs;
|
||||
// passed as variadic arguments. There are two versions of this function: the
|
||||
// variadic one, and the one that takes a `va_list`.
|
||||
double rust_valist_interesting_average(uint64_t n, va_list pairs) {
|
||||
double sum = 0.0;
|
||||
int i;
|
||||
va_start(pairs, n);
|
||||
for(i = 0; i < n; i += 1) {
|
||||
sum += (double)va_arg(pairs, int64_t);
|
||||
sum += va_arg(pairs, double);
|
||||
}
|
||||
va_end(pairs);
|
||||
return sum / n;
|
||||
}
|
||||
|
||||
double rust_interesting_average(uint64_t n, ...) {
|
||||
double sum;
|
||||
va_list pairs;
|
||||
va_start(pairs, n);
|
||||
sum = rust_valist_interesting_average(n, pairs);
|
||||
va_end(pairs);
|
||||
return sum;
|
||||
}
|
||||
|
||||
int32_t rust_int8_to_int32(int8_t x) {
|
||||
return (int32_t)x;
|
||||
}
|
||||
|
16
src/test/codegen/c-variadic-copy.rs
Normal file
16
src/test/codegen/c-variadic-copy.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Tests that `VaListImpl::clone` gets inlined into a call to `llvm.va_copy`
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(c_variadic)]
|
||||
#![no_std]
|
||||
use core::ffi::VaList;
|
||||
|
||||
extern "C" {
|
||||
fn foreign_c_variadic_1(_: VaList, ...);
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn clone_variadic(ap: VaList) {
|
||||
let mut ap2 = ap.clone();
|
||||
// CHECK: call void @llvm.va_copy
|
||||
foreign_c_variadic_1(ap2.as_va_list(), 42i32);
|
||||
}
|
@ -10,10 +10,21 @@
|
||||
}
|
||||
|
||||
// Ensure that `va_start` and `va_end` are properly injected even
|
||||
// when the "spoofed" `VaList` is not used.
|
||||
// when the "spoofed" `VaListImpl` is not used.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn c_variadic_no_use(fmt: *const i8, mut ap: ...) -> i32 {
|
||||
// CHECK: call void @llvm.va_start
|
||||
vprintf(fmt, ap)
|
||||
vprintf(fmt, ap.as_va_list())
|
||||
// CHECK: call void @llvm.va_end
|
||||
}
|
||||
|
||||
// Check that `VaListImpl::clone` gets inlined into a direct call to `llvm.va_copy`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn c_variadic_clone(fmt: *const i8, mut ap: ...) -> i32 {
|
||||
// CHECK: call void @llvm.va_start
|
||||
let mut ap2 = ap.clone();
|
||||
// CHECK: call void @llvm.va_copy
|
||||
let res = vprintf(fmt, ap2.as_va_list());
|
||||
res
|
||||
// CHECK: call void @llvm.va_end
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
}
|
||||
|
||||
// Ensure that we do not remove the `va_list` passed to the foreign function when
|
||||
// removing the "spoofed" `VaList` that is used by Rust defined C-variadics.
|
||||
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
|
||||
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
|
||||
// CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
|
||||
foreign_c_variadic_1(ap);
|
||||
|
@ -88,6 +88,6 @@ unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn check_varargs_2(_: c_int, mut ap: ...) -> usize {
|
||||
pub unsafe extern "C" fn check_varargs_2(_: c_int, _ap: ...) -> usize {
|
||||
0
|
||||
}
|
||||
|
@ -1,8 +1,45 @@
|
||||
// ignore-wasm32-bare no libc to test ffi with
|
||||
#![feature(c_variadic)]
|
||||
|
||||
use std::ffi::VaList;
|
||||
|
||||
#[link(name = "rust_test_helpers", kind = "static")]
|
||||
extern {
|
||||
fn rust_interesting_average(_: u64, ...) -> f64;
|
||||
|
||||
// FIXME: we need to disable this lint for `VaList`,
|
||||
// since it contains a `MaybeUninit<i32>` on the asmjs target,
|
||||
// and this type isn't FFI-safe. This is OK for now,
|
||||
// since the type is layout-compatible with `i32`.
|
||||
#[cfg_attr(target_arch = "asmjs", allow(improper_ctypes))]
|
||||
fn rust_valist_interesting_average(_: u64, _: VaList) -> f64;
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn test_valist_forward(n: u64, mut ap: ...) -> f64 {
|
||||
rust_valist_interesting_average(n, ap.as_va_list())
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) {
|
||||
let mut ap2 = ap.clone();
|
||||
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 30);
|
||||
|
||||
// Advance one pair in the copy before checking
|
||||
let mut ap2 = ap.clone();
|
||||
let _ = ap2.arg::<u64>();
|
||||
let _ = ap2.arg::<f64>();
|
||||
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
|
||||
|
||||
// Advance one pair in the original
|
||||
let _ = ap.arg::<u64>();
|
||||
let _ = ap.arg::<f64>();
|
||||
|
||||
let mut ap2 = ap.clone();
|
||||
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
|
||||
|
||||
let mut ap2 = ap.clone();
|
||||
let _ = ap2.arg::<u64>();
|
||||
let _ = ap2.arg::<f64>();
|
||||
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 70);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
@ -35,4 +72,12 @@ unsafe fn call(fp: unsafe extern fn(u64, ...) -> f64) {
|
||||
let x: unsafe extern fn(u64, ...) -> f64 = rust_interesting_average;
|
||||
call(x);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
assert_eq!(test_valist_forward(2, 10i64, 10f64, 20i64, 20f64) as i64, 30);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
test_va_copy(4, 10i64, 10f64, 20i64, 20f64, 30i64, 30f64, 40i64, 40f64);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
|
||||
| ^^^ expected non-variadic fn, found variadic function
|
||||
|
|
||||
= note: expected type `unsafe extern "C" fn(isize, u8)`
|
||||
found type `for<'r> unsafe extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...) {foo}`
|
||||
found type `for<'r> unsafe extern "C" fn(isize, u8, std::ffi::VaListImpl<'r>, ...) {foo}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/variadic-ffi-1.rs:20:54
|
||||
@ -37,7 +37,7 @@ error[E0308]: mismatched types
|
||||
LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
|
||||
| ^^^ expected variadic fn, found non-variadic function
|
||||
|
|
||||
= note: expected type `for<'r> extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...)`
|
||||
= note: expected type `for<'r> extern "C" fn(isize, u8, std::ffi::VaListImpl<'r>, ...)`
|
||||
found type `extern "C" fn(isize, u8) {bar}`
|
||||
|
||||
error[E0617]: can't pass `f32` to variadic function
|
||||
|
@ -1,16 +1,16 @@
|
||||
error[E0621]: explicit lifetime required in the type of `ap`
|
||||
--> $DIR/variadic-ffi-4.rs:8:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
|
||||
| --- help: add explicit lifetime `'a` to the type of `ap`: `core::ffi::VaList<'a>`
|
||||
LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> {
|
||||
| --- help: add explicit lifetime `'f` to the type of `ap`: `core::ffi::VaListImpl<'f>`
|
||||
LL | ap
|
||||
| ^^ lifetime `'a` required
|
||||
| ^^ lifetime `'f` required
|
||||
|
||||
error[E0621]: explicit lifetime required in the type of `ap`
|
||||
--> $DIR/variadic-ffi-4.rs:12:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
|
||||
| --- help: add explicit lifetime `'static` to the type of `ap`: `core::ffi::VaList<'static>`
|
||||
LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> {
|
||||
| --- help: add explicit lifetime `'static` to the type of `ap`: `core::ffi::VaListImpl<'static>`
|
||||
LL | ap
|
||||
| ^^ lifetime `'static` required
|
||||
|
||||
@ -20,43 +20,43 @@ error: lifetime may not live long enough
|
||||
LL | let _ = ap.with_copy(|ap| { ap });
|
||||
| --- ^^ returning this value requires that `'1` must outlive `'2`
|
||||
| | |
|
||||
| | return type of closure is core::ffi::VaList<'2>
|
||||
| has type `core::ffi::VaList<'1>`
|
||||
| | return type of closure is core::ffi::VaList<'2, '_>
|
||||
| has type `core::ffi::VaList<'1, '_>`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/variadic-ffi-4.rs:20:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
|
||||
| ------- ------- has type `core::ffi::VaList<'1>`
|
||||
LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
| ------- ------- has type `core::ffi::VaListImpl<'1>`
|
||||
| |
|
||||
| has type `&mut core::ffi::VaList<'2>`
|
||||
| has type `&mut core::ffi::VaListImpl<'2>`
|
||||
LL | *ap0 = ap1;
|
||||
| ^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||
| ^^^^ assignment requires that `'1` must outlive `'2`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/variadic-ffi-4.rs:24:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
| --- ------- has type `core::ffi::VaList<'2>`
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
| --- ------- has type `core::ffi::VaListImpl<'2>`
|
||||
| |
|
||||
| has type `&mut core::ffi::VaList<'1>`
|
||||
| has type `&mut core::ffi::VaListImpl<'1>`
|
||||
LL | ap0 = &mut ap1;
|
||||
| ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/variadic-ffi-4.rs:24:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
| --- ------- has type `core::ffi::VaList<'1>`
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
| --- ------- has type `core::ffi::VaListImpl<'1>`
|
||||
| |
|
||||
| has type `&mut core::ffi::VaList<'2>`
|
||||
| has type `&mut core::ffi::VaListImpl<'2>`
|
||||
LL | ap0 = &mut ap1;
|
||||
| ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||
|
||||
error[E0384]: cannot assign to immutable argument `ap0`
|
||||
--> $DIR/variadic-ffi-4.rs:24:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
| --- help: make this binding mutable: `mut ap0`
|
||||
LL | ap0 = &mut ap1;
|
||||
| ^^^^^^^^^^^^^^ cannot assign to immutable argument
|
||||
@ -64,7 +64,7 @@ LL | ap0 = &mut ap1;
|
||||
error[E0597]: `ap1` does not live long enough
|
||||
--> $DIR/variadic-ffi-4.rs:24:11
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
| - let's call the lifetime of this reference `'1`
|
||||
LL | ap0 = &mut ap1;
|
||||
| ------^^^^^^^^
|
||||
@ -73,9 +73,19 @@ LL | ap0 = &mut ap1;
|
||||
| assignment requires that `ap1` is borrowed for `'1`
|
||||
...
|
||||
LL | }
|
||||
| - `ap1` dropped here while still borrowed
|
||||
| - `ap1` dropped here while still borrowed
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/variadic-ffi-4.rs:32:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
| ------- ------- has type `core::ffi::VaListImpl<'1>`
|
||||
| |
|
||||
| has type `&mut core::ffi::VaListImpl<'2>`
|
||||
LL | *ap0 = ap1.clone();
|
||||
| ^^^^ assignment requires that `'1` must outlive `'2`
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0384, E0597, E0621.
|
||||
For more information about an error, try `rustc --explain E0384`.
|
||||
|
@ -2,13 +2,13 @@
|
||||
#![no_std]
|
||||
#![feature(c_variadic)]
|
||||
|
||||
use core::ffi::VaList;
|
||||
use core::ffi::{VaList, VaListImpl};
|
||||
|
||||
pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
|
||||
pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> {
|
||||
ap //~ ERROR: explicit lifetime required
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
|
||||
pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> {
|
||||
ap //~ ERROR: explicit lifetime required
|
||||
}
|
||||
|
||||
@ -16,14 +16,18 @@
|
||||
let _ = ap.with_copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
|
||||
pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
*ap0 = ap1; //~ ERROR: mismatched types
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
ap0 = &mut ap1;
|
||||
//~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
|
||||
//~^ ERROR: a value of type `core::ffi::VaListImpl<'_>` is borrowed for too long
|
||||
//~^^ ERROR: mismatched types
|
||||
//~^^^ ERROR: mismatched types
|
||||
//~^^^^ ERROR: cannot infer an appropriate lifetime
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
*ap0 = ap1.clone(); //~ ERROR: cannot infer an appropriate lifetime
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
error[E0621]: explicit lifetime required in the type of `ap`
|
||||
--> $DIR/variadic-ffi-4.rs:8:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
|
||||
| --- help: add explicit lifetime `'a` to the type of `ap`: `core::ffi::VaList<'a>`
|
||||
LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> {
|
||||
| --- help: add explicit lifetime `'f` to the type of `ap`: `core::ffi::VaListImpl<'f>`
|
||||
LL | ap
|
||||
| ^^ lifetime `'a` required
|
||||
| ^^ lifetime `'f` required
|
||||
|
||||
error[E0621]: explicit lifetime required in the type of `ap`
|
||||
--> $DIR/variadic-ffi-4.rs:12:5
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
|
||||
| --- help: add explicit lifetime `'static` to the type of `ap`: `core::ffi::VaList<'static>`
|
||||
LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> {
|
||||
| --- help: add explicit lifetime `'static` to the type of `ap`: `core::ffi::VaListImpl<'static>`
|
||||
LL | ap
|
||||
| ^^ lifetime `'static` required
|
||||
|
||||
@ -26,14 +26,14 @@ note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on th
|
||||
LL | let _ = ap.with_copy(|ap| { ap });
|
||||
| ^^^^^^^^^^^
|
||||
= note: ...so that the expression is assignable:
|
||||
expected core::ffi::VaList<'_>
|
||||
found core::ffi::VaList<'_>
|
||||
expected core::ffi::VaList<'_, '_>
|
||||
found core::ffi::VaList<'_, '_>
|
||||
note: but, the lifetime must be valid for the method call at 16:13...
|
||||
--> $DIR/variadic-ffi-4.rs:16:13
|
||||
|
|
||||
LL | let _ = ap.with_copy(|ap| { ap });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...so type `core::ffi::VaList<'_>` of expression is valid during the expression
|
||||
note: ...so type `core::ffi::VaList<'_, '_>` of expression is valid during the expression
|
||||
--> $DIR/variadic-ffi-4.rs:16:13
|
||||
|
|
||||
LL | let _ = ap.with_copy(|ap| { ap });
|
||||
@ -45,24 +45,24 @@ error[E0308]: mismatched types
|
||||
LL | *ap0 = ap1;
|
||||
| ^^^ lifetime mismatch
|
||||
|
|
||||
= note: expected type `core::ffi::VaList<'_>`
|
||||
found type `core::ffi::VaList<'_>`
|
||||
= note: expected type `core::ffi::VaListImpl<'_>`
|
||||
found type `core::ffi::VaListImpl<'_>`
|
||||
note: the anonymous lifetime #3 defined on the function body at 19:1...
|
||||
--> $DIR/variadic-ffi-4.rs:19:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | *ap0 = ap1;
|
||||
LL | | }
|
||||
| |_^
|
||||
note: ...does not necessarily outlive the anonymous lifetime #2 defined on the function body at 19:1
|
||||
--> $DIR/variadic-ffi-4.rs:19:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | *ap0 = ap1;
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error[E0490]: a value of type `core::ffi::VaList<'_>` is borrowed for too long
|
||||
error[E0490]: a value of type `core::ffi::VaListImpl<'_>` is borrowed for too long
|
||||
--> $DIR/variadic-ffi-4.rs:24:11
|
||||
|
|
||||
LL | ap0 = &mut ap1;
|
||||
@ -71,7 +71,7 @@ LL | ap0 = &mut ap1;
|
||||
note: the type is valid for the anonymous lifetime #1 defined on the function body at 23:1
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -82,7 +82,7 @@ LL | | }
|
||||
note: but the borrow lasts for the anonymous lifetime #3 defined on the function body at 23:1
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -97,12 +97,12 @@ error[E0308]: mismatched types
|
||||
LL | ap0 = &mut ap1;
|
||||
| ^^^^^^^^ lifetime mismatch
|
||||
|
|
||||
= note: expected type `&mut core::ffi::VaList<'_>`
|
||||
found type `&mut core::ffi::VaList<'_>`
|
||||
= note: expected type `&mut core::ffi::VaListImpl<'_>`
|
||||
found type `&mut core::ffi::VaListImpl<'_>`
|
||||
note: the anonymous lifetime #3 defined on the function body at 23:1...
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -113,7 +113,7 @@ LL | | }
|
||||
note: ...does not necessarily outlive the anonymous lifetime #2 defined on the function body at 23:1
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -128,12 +128,12 @@ error[E0308]: mismatched types
|
||||
LL | ap0 = &mut ap1;
|
||||
| ^^^^^^^^ lifetime mismatch
|
||||
|
|
||||
= note: expected type `&mut core::ffi::VaList<'_>`
|
||||
found type `&mut core::ffi::VaList<'_>`
|
||||
= note: expected type `&mut core::ffi::VaListImpl<'_>`
|
||||
found type `&mut core::ffi::VaListImpl<'_>`
|
||||
note: the anonymous lifetime #2 defined on the function body at 23:1...
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -144,7 +144,7 @@ LL | | }
|
||||
note: ...does not necessarily outlive the anonymous lifetime #3 defined on the function body at 23:1
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -162,7 +162,7 @@ LL | ap0 = &mut ap1;
|
||||
note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the function body at 23:1...
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -170,7 +170,7 @@ LL | |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
note: ...so that the type `core::ffi::VaList<'_>` is not borrowed for too long
|
||||
note: ...so that the type `core::ffi::VaListImpl<'_>` is not borrowed for too long
|
||||
--> $DIR/variadic-ffi-4.rs:24:11
|
||||
|
|
||||
LL | ap0 = &mut ap1;
|
||||
@ -178,7 +178,7 @@ LL | ap0 = &mut ap1;
|
||||
note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the function body at 23:1...
|
||||
--> $DIR/variadic-ffi-4.rs:23:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
|
||||
LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | ap0 = &mut ap1;
|
||||
LL | |
|
||||
LL | |
|
||||
@ -192,7 +192,34 @@ note: ...so that reference does not outlive borrowed content
|
||||
LL | ap0 = &mut ap1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
|
||||
--> $DIR/variadic-ffi-4.rs:32:16
|
||||
|
|
||||
LL | *ap0 = ap1.clone();
|
||||
| ^^^^^
|
||||
|
|
||||
note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the function body at 31:1...
|
||||
--> $DIR/variadic-ffi-4.rs:31:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | *ap0 = ap1.clone();
|
||||
LL | | }
|
||||
| |_^
|
||||
= note: ...so that the types are compatible:
|
||||
expected &core::ffi::VaListImpl<'_>
|
||||
found &core::ffi::VaListImpl<'_>
|
||||
note: but, the lifetime must be valid for the anonymous lifetime #2 defined on the function body at 31:1...
|
||||
--> $DIR/variadic-ffi-4.rs:31:1
|
||||
|
|
||||
LL | / pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
LL | | *ap0 = ap1.clone();
|
||||
LL | | }
|
||||
| |_^
|
||||
= note: ...so that the expression is assignable:
|
||||
expected core::ffi::VaListImpl<'_>
|
||||
found core::ffi::VaListImpl<'_>
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0308, E0621.
|
||||
For more information about an error, try `rustc --explain E0308`.
|
||||
|
@ -22,7 +22,7 @@ fn main() {
|
||||
//~^ ERROR can't pass `u16` to variadic function
|
||||
//~| HELP cast the value to `c_uint`
|
||||
printf(::std::ptr::null(), printf);
|
||||
//~^ ERROR can't pass `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...) {printf}` to variadic function
|
||||
//~| HELP cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...)`
|
||||
//~^ ERROR can't pass `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaListImpl<'r>, ...) {printf}` to variadic function
|
||||
//~| HELP cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaListImpl<'r>, ...)`
|
||||
}
|
||||
}
|
||||
|
@ -28,15 +28,15 @@ error[E0617]: can't pass `u16` to variadic function
|
||||
LL | printf(::std::ptr::null(), 0u16);
|
||||
| ^^^^ help: cast the value to `c_uint`: `0u16 as c_uint`
|
||||
|
||||
error[E0617]: can't pass `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...) {printf}` to variadic function
|
||||
error[E0617]: can't pass `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaListImpl<'r>, ...) {printf}` to variadic function
|
||||
--> $DIR/E0617.rs:24:36
|
||||
|
|
||||
LL | printf(::std::ptr::null(), printf);
|
||||
| ^^^^^^
|
||||
help: cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...)`
|
||||
help: cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaListImpl<'r>, ...)`
|
||||
|
|
||||
LL | printf(::std::ptr::null(), printf as for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | printf(::std::ptr::null(), printf as for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaListImpl<'r>, ...));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
@ -57,7 +57,7 @@ fn method(&self) {}
|
||||
}
|
||||
|
||||
// Test type mangling, by putting them in an `impl` header.
|
||||
// FIXME(eddyb) test C varargs when `core::ffi::VaList` stops leaking into the signature
|
||||
// FIXME(eddyb) test C varargs when `core::ffi::VaListImpl` stops leaking into the signature
|
||||
// (which is a problem because `core` has an unpredictable hash) - see also #44930.
|
||||
impl Bar for [&'_ (dyn Foo<Assoc = extern fn(&u8, /*...*/)> + AutoTrait); 3] {
|
||||
#[rustc_symbol_name]
|
||||
|
Loading…
Reference in New Issue
Block a user