about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndrei Homescu <ah@immunant.com>2019-03-25 14:28:03 -0700
committerAndrei Homescu <ah@immunant.com>2019-06-17 16:04:49 -0700
commitb9ea653aee231114acbe6d4b3c7b1d692772d060 (patch)
treeeceeeff8ba9582d6fb956358c1dc06eceadb2a29
parent70456a6cbd67c0547d22997007afaaed0819767e (diff)
downloadrust-b9ea653aee231114acbe6d4b3c7b1d692772d060.tar.gz
rust-b9ea653aee231114acbe6d4b3c7b1d692772d060.zip
Expose `VaListImpl` as the Rust equivalent of `__va_list_tag` and implement Clone for it.
-rw-r--r--src/libcore/ffi.rs231
-rw-r--r--src/librustc/hir/lowering.rs2
-rw-r--r--src/librustc/hir/mod.rs2
-rw-r--r--src/librustc/ty/layout.rs4
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs38
-rw-r--r--src/librustc_codegen_llvm/va_arg.rs12
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs6
-rw-r--r--src/librustc_codegen_ssa/mir/mod.rs33
-rw-r--r--src/librustc_codegen_ssa/traits/intrinsic.rs4
-rw-r--r--src/librustc_lint/types.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs2
-rw-r--r--src/librustc_typeck/check/intrinsic.rs40
-rw-r--r--src/libstd/ffi/mod.rs2
-rw-r--r--src/test/auxiliary/rust_test_helpers.c17
-rw-r--r--src/test/codegen/c-variadic-copy.rs16
-rw-r--r--src/test/codegen/c-variadic-opt.rs15
-rw-r--r--src/test/codegen/c-variadic.rs2
-rw-r--r--src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs2
-rw-r--r--src/test/run-pass/variadic-ffi.rs45
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-1.stderr4
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.nll.stderr52
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.rs16
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.stderr81
-rw-r--r--src/test/ui/error-codes/E0617.rs4
-rw-r--r--src/test/ui/error-codes/E0617.stderr8
-rw-r--r--src/test/ui/symbol-names/impl1.rs2
26 files changed, 419 insertions, 223 deletions
diff --git a/src/libcore/ffi.rs b/src/libcore/ffi.rs
index 2906e5824ae..49090fb8e43 100644
--- a/src/libcore/ffi.rs
+++ b/src/libcore/ffi.rs
@@ -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")]
-extern {
-    type VaListImpl;
+#[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 = "x86_64"), not(target_arch = "asmjs")),
           all(target_arch = "aarch64", target_os = "ios"),
           windows))]
-impl fmt::Debug for VaListImpl {
+#[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 {
-        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>,
 }
 
-/// A wrapper for a `va_list`
+/// 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"]
-#[derive(Debug)]
+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`
 #[repr(transparent)]
-pub struct VaList<'a>(&'a mut VaListImpl);
+#[derive(Debug)]
+#[unstable(feature = "c_variadic",
+           reason = "the `c_variadic` feature has not been properly tested on \
+                     all supported platforms",
+           issue = "44930")]
+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;
 }
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 5a548ce8d9f..ae8fdaea285 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -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)
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 6ace4c4174b..49b6fd67cb7 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -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),
 }
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 6cee2709b63..9ce1d2eec5d 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -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);
                             }
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs
index dc3631e2ee1..7831c200114 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -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)
     }
diff --git a/src/librustc_codegen_llvm/va_arg.rs b/src/librustc_codegen_llvm/va_arg.rs
index 410e00fe0b8..86b0ad761af 100644
--- a/src/librustc_codegen_llvm/va_arg.rs
+++ b/src/librustc_codegen_llvm/va_arg.rs
@@ -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))
     }
 }
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index e4b82d84966..cee9c9009a2 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -503,7 +503,7 @@ impl<'a, 'tcx: 'a, 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: 'a, 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: 'a, 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) {
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index dd69d358313..df3823dd9ca 100644
--- a/src/librustc_codegen_ssa/mir/mod.rs
+++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -83,7 +83,7 @@ pub struct FunctionCx<'a, 'tcx: 'a, 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>>,
 }
 
@@ -561,35 +561,24 @@ fn arg_local_refs<'a, 'tcx: 'a, 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| {
diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs
index cd527898977..ede30a0bed7 100644
--- a/src/librustc_codegen_ssa/traits/intrinsic.rs
+++ b/src/librustc_codegen_ssa/traits/intrinsic.rs
@@ -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;
 }
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index a843ee6d45d..3b159e5da27 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -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 {
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index ad79f210920..6767a820e6f 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -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]
diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs
index 7f690b6e828..8c2b8d1565f 100644
--- a/src/librustc_typeck/check/intrinsic.rs
+++ b/src/librustc_typeck/check/intrinsic.rs
@@ -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")
                 }
             }
diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs
index 7a38f0ebd5a..69fcfa8b39c 100644
--- a/src/libstd/ffi/mod.rs
+++ b/src/libstd/ffi/mod.rs
@@ -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;
diff --git a/src/test/auxiliary/rust_test_helpers.c b/src/test/auxiliary/rust_test_helpers.c
index 27a26d244c2..7f2afd9c571 100644
--- a/src/test/auxiliary/rust_test_helpers.c
+++ b/src/test/auxiliary/rust_test_helpers.c
@@ -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;
 }
diff --git a/src/test/codegen/c-variadic-copy.rs b/src/test/codegen/c-variadic-copy.rs
new file mode 100644
index 00000000000..4c61c4fcf68
--- /dev/null
+++ b/src/test/codegen/c-variadic-copy.rs
@@ -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);
+}
diff --git a/src/test/codegen/c-variadic-opt.rs b/src/test/codegen/c-variadic-opt.rs
index 8594d309b0a..969dce80f58 100644
--- a/src/test/codegen/c-variadic-opt.rs
+++ b/src/test/codegen/c-variadic-opt.rs
@@ -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
 }
diff --git a/src/test/codegen/c-variadic.rs b/src/test/codegen/c-variadic.rs
index 09c18ed90b2..13be5ced27f 100644
--- a/src/test/codegen/c-variadic.rs
+++ b/src/test/codegen/c-variadic.rs
@@ -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);
diff --git a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
index 163d50c4e4b..a0a5b141ec0 100644
--- a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
+++ b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
@@ -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
 }
diff --git a/src/test/run-pass/variadic-ffi.rs b/src/test/run-pass/variadic-ffi.rs
index 3539c8d6b72..d6fbb1773b2 100644
--- a/src/test/run-pass/variadic-ffi.rs
+++ b/src/test/run-pass/variadic-ffi.rs
@@ -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);
+    }
 }
diff --git a/src/test/ui/c-variadic/variadic-ffi-1.stderr b/src/test/ui/c-variadic/variadic-ffi-1.stderr
index e16d15a98bf..695eba2a7ee 100644
--- a/src/test/ui/c-variadic/variadic-ffi-1.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-1.stderr
@@ -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
diff --git a/src/test/ui/c-variadic/variadic-ffi-4.nll.stderr b/src/test/ui/c-variadic/variadic-ffi-4.nll.stderr
index a1afbb06390..3c5131835b5 100644
--- a/src/test/ui/c-variadic/variadic-ffi-4.nll.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-4.nll.stderr
@@ -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`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-4.rs b/src/test/ui/c-variadic/variadic-ffi-4.rs
index 1c77479d02f..07c32ecbfc2 100644
--- a/src/test/ui/c-variadic/variadic-ffi-4.rs
+++ b/src/test/ui/c-variadic/variadic-ffi-4.rs
@@ -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
+}
diff --git a/src/test/ui/c-variadic/variadic-ffi-4.stderr b/src/test/ui/c-variadic/variadic-ffi-4.stderr
index 80b765671c5..72d4d8b6344 100644
--- a/src/test/ui/c-variadic/variadic-ffi-4.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-4.stderr
@@ -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`.
diff --git a/src/test/ui/error-codes/E0617.rs b/src/test/ui/error-codes/E0617.rs
index 51f13c7dbd5..439c3db5768 100644
--- a/src/test/ui/error-codes/E0617.rs
+++ b/src/test/ui/error-codes/E0617.rs
@@ -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>, ...)`
     }
 }
diff --git a/src/test/ui/error-codes/E0617.stderr b/src/test/ui/error-codes/E0617.stderr
index 8387d5c7e93..d866320bbcd 100644
--- a/src/test/ui/error-codes/E0617.stderr
+++ b/src/test/ui/error-codes/E0617.stderr
@@ -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
 
diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs
index 9ed93bb9818..52bb118fa23 100644
--- a/src/test/ui/symbol-names/impl1.rs
+++ b/src/test/ui/symbol-names/impl1.rs
@@ -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]