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:
bors 2019-06-18 21:50:46 +00:00
commit 605ea9d05c
26 changed files with 424 additions and 228 deletions

View File

@ -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 @@ impl fmt::Debug for c_void {
}
/// 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 @@ impl fmt::Debug for 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> {
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;
}

View File

@ -1674,7 +1674,7 @@ impl<'a> LoweringContext<'a> {
}
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)

View File

@ -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),
}

View File

@ -2711,7 +2711,7 @@ where
}
// 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 @@ where
};
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);
}

View File

@ -146,15 +146,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
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 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
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)
}

View File

@ -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))
}
}

View File

@ -503,7 +503,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
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 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
(&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 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};
'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) {

View File

@ -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| {

View File

@ -20,10 +20,10 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
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;
}

View File

@ -892,7 +892,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
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 {

View File

@ -1695,7 +1695,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
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]

View File

@ -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")
}
}

View File

@ -170,7 +170,7 @@ pub use core::ffi::c_void;
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;

View File

@ -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;
}

View 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);
}

View File

@ -10,10 +10,21 @@ extern "C" {
}
// 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
}

View File

@ -23,7 +23,7 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() {
}
// 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);

View File

@ -88,6 +88,6 @@ pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize {
}
#[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
}

View File

@ -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 @@ pub fn main() {
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);
}
}

View File

@ -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

View File

@ -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`.

View File

@ -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 @@ pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) {
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
}

View File

@ -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`.

View File

@ -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>, ...)`
}
}

View File

@ -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

View File

@ -57,7 +57,7 @@ fn main() {
}
// 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]