]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #57760 - dlrobertson:varargs1, r=alexreg
authorbors <bors@rust-lang.org>
Thu, 28 Feb 2019 15:00:25 +0000 (15:00 +0000)
committerbors <bors@rust-lang.org>
Thu, 28 Feb 2019 15:00:25 +0000 (15:00 +0000)
Support defining C compatible variadic functions

## Summary

Add support for defining C compatible variadic functions in unsafe rust with
`extern "C"` according to [RFC 2137].

## Details

### Parsing
When parsing a user defined function that is `unsafe` and `extern "C"` allow
variadic signatures and inject a "spoofed" `VaList` in the new functions
signature. This allows the user to interact with the variadic arguments via a
`VaList` instead of manually using `va_start` and `va_end` (See [RFC 2137] for
details).

### Codegen

When running codegen for a variadic function, remove the "spoofed" `VaList`
from the function signature and inject `va_start` when the arg local
references are created for the function and `va_end` on return.

## TODO

 - [x] Get feedback on injecting `va_start/va_end` in MIR vs codegen
 - [x] Properly inject `va_end` - It seems like it should be possible to inject
       `va_end` on the `TerminatorKind::Return`. I just need to figure out how
       to get the `LocalRef` here.
 - [x] Properly call Rust defined C variadic functions in Rust - The spoofed
       `VaList` causes problems here.

Related to: #44930

r? @ghost

[RFC 2137]: https://github.com/rust-lang/rfcs/blob/master/text/2137-variadic.md

84 files changed:
src/doc/unstable-book/src/language-features/c-variadic.md [new file with mode: 0644]
src/doc/unstable-book/src/library-features/c-variadic.md [new file with mode: 0644]
src/librustc/hir/intravisit.rs
src/librustc/hir/lowering.rs
src/librustc/hir/mod.rs
src/librustc/hir/print.rs
src/librustc/ich/impls_hir.rs
src/librustc/ich/impls_ty.rs
src/librustc/middle/resolve_lifetime.rs
src/librustc/traits/select.rs
src/librustc/ty/context.rs
src/librustc/ty/instance.rs
src/librustc/ty/relate.rs
src/librustc/ty/structural_impls.rs
src/librustc/ty/sty.rs
src/librustc/util/ppaux.rs
src/librustc_codegen_llvm/abi.rs
src/librustc_codegen_llvm/debuginfo/type_names.rs
src/librustc_codegen_llvm/intrinsic.rs
src/librustc_codegen_llvm/mono_item.rs
src/librustc_codegen_llvm/va_arg.rs
src/librustc_codegen_ssa/mir/block.rs
src/librustc_codegen_ssa/mir/mod.rs
src/librustc_codegen_ssa/traits/intrinsic.rs
src/librustc_lint/types.rs
src/librustc_mir/borrow_check/nll/type_check/mod.rs
src/librustc_mir/monomorphize/item.rs
src/librustc_save_analysis/sig.rs
src/librustc_target/abi/call/arm.rs
src/librustc_target/abi/call/mod.rs
src/librustc_target/abi/call/x86.rs
src/librustc_traits/chalk_context/program_clauses.rs
src/librustc_traits/generic_types.rs
src/librustc_typeck/astconv.rs
src/librustc_typeck/check/callee.rs
src/librustc_typeck/check/closure.rs
src/librustc_typeck/check/intrinsic.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/lib.rs
src/librustdoc/clean/mod.rs
src/librustdoc/html/format.rs
src/libsyntax/ast.rs
src/libsyntax/ext/build.rs
src/libsyntax/feature_gate.rs
src/libsyntax/mut_visit.rs
src/libsyntax/parse/parser.rs
src/libsyntax/print/pprust.rs
src/libsyntax/visit.rs
src/test/codegen/c-variadic.rs [new file with mode: 0644]
src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
src/test/rustdoc/variadic.rs
src/test/ui/c-variadic/variadic-ffi-1.rs [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-1.stderr [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-2.rs [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-2.stderr [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-3.rs [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-3.stderr [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-4.rs [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-4.stderr [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-5.rs [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-5.stderr [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-6.rs [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-6.stderr [new file with mode: 0644]
src/test/ui/error-codes/E0045.stderr
src/test/ui/error-codes/E0617.rs
src/test/ui/error-codes/E0617.stderr
src/test/ui/feature-gate/feature-gate-c_variadic.rs [new file with mode: 0644]
src/test/ui/feature-gate/feature-gate-c_variadic.stderr [new file with mode: 0644]
src/test/ui/invalid/invalid-variadic-function.rs
src/test/ui/invalid/invalid-variadic-function.stderr
src/test/ui/parser/recover-enum2.stderr
src/test/ui/parser/variadic-ffi-3.rs
src/test/ui/parser/variadic-ffi-3.stderr
src/test/ui/parser/variadic-ffi-4.rs
src/test/ui/parser/variadic-ffi-4.stderr
src/test/ui/variadic/variadic-ffi-2.rs [deleted file]
src/test/ui/variadic/variadic-ffi-2.stderr [deleted file]
src/test/ui/variadic/variadic-ffi-3.rs [deleted file]
src/test/ui/variadic/variadic-ffi-3.stderr [deleted file]
src/test/ui/variadic/variadic-ffi.rs [deleted file]
src/test/ui/variadic/variadic-ffi.stderr [deleted file]
src/tools/tidy/src/unstable_book.rs

diff --git a/src/doc/unstable-book/src/language-features/c-variadic.md b/src/doc/unstable-book/src/language-features/c-variadic.md
new file mode 100644 (file)
index 0000000..9e7968d
--- /dev/null
@@ -0,0 +1,24 @@
+# `c_variadic`
+
+The tracking issue for this feature is: [#44930]
+
+[#44930]: https://github.com/rust-lang/rust/issues/44930
+
+------------------------
+
+The `c_variadic` language feature enables C-variadic functions to be
+defined in Rust. The may be called both from within Rust and via FFI.
+
+## Examples
+
+```rust
+#![feature(c_variadic)]
+
+pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize {
+    let mut sum = 0;
+    for _ in 0..n {
+        sum += args.arg::<usize>();
+    }
+    sum
+}
+```
diff --git a/src/doc/unstable-book/src/library-features/c-variadic.md b/src/doc/unstable-book/src/library-features/c-variadic.md
new file mode 100644 (file)
index 0000000..7776211
--- /dev/null
@@ -0,0 +1,26 @@
+# `c_variadic`
+
+The tracking issue for this feature is: [#44930]
+
+[#44930]: https://github.com/rust-lang/rust/issues/44930
+
+------------------------
+
+The `c_variadic` library feature exposes the `VaList` structure,
+Rust's analogue of C's `va_list` type.
+
+## Examples
+
+```rust
+#![feature(c_variadic)]
+
+use std::ffi::VaList;
+
+pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize {
+    let mut sum = 0;
+    for _ in 0..n {
+        sum += args.arg::<usize>();
+    }
+    sum
+}
+```
index 0bc40da7e4f2c0c0cf9841624494a9c73a397abe..8e4b9a5e8e641db7e529351fe4682b87cfc76d9b 100644 (file)
@@ -617,6 +617,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
         TyKind::Typeof(ref expression) => {
             visitor.visit_anon_const(expression)
         }
+        TyKind::CVarArgs(ref lt) => {
+            visitor.visit_lifetime(lt)
+        }
         TyKind::Infer | TyKind::Err => {}
     }
 }
index df45a085944d66436ff5934fad75bcf69e8c8879..19bb7f41e1b0211eac3140ca54875adcfdfd7d01 100644 (file)
@@ -74,7 +74,7 @@
 pub struct LoweringContext<'a> {
     crate_root: Option<&'static str>,
 
-    // Used to assign ids to HIR nodes that do not directly correspond to an AST node.
+    /// Used to assign ids to HIR nodes that do not directly correspond to an AST node.
     sess: &'a Session,
 
     cstore: &'a dyn CrateStore,
@@ -107,25 +107,25 @@ pub struct LoweringContext<'a> {
     /// written at all (e.g., `&T` or `std::cell::Ref<T>`).
     anonymous_lifetime_mode: AnonymousLifetimeMode,
 
-    // Used to create lifetime definitions from in-band lifetime usages.
-    // e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
-    // When a named lifetime is encountered in a function or impl header and
-    // has not been defined
-    // (i.e., it doesn't appear in the in_scope_lifetimes list), it is added
-    // to this list. The results of this list are then added to the list of
-    // lifetime definitions in the corresponding impl or function generics.
+    /// Used to create lifetime definitions from in-band lifetime usages.
+    /// e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
+    /// When a named lifetime is encountered in a function or impl header and
+    /// has not been defined
+    /// (i.e., it doesn't appear in the in_scope_lifetimes list), it is added
+    /// to this list. The results of this list are then added to the list of
+    /// lifetime definitions in the corresponding impl or function generics.
     lifetimes_to_define: Vec<(Span, ParamName)>,
 
-    // Whether or not in-band lifetimes are being collected. This is used to
-    // indicate whether or not we're in a place where new lifetimes will result
-    // in in-band lifetime definitions, such a function or an impl header,
-    // including implicit lifetimes from `impl_header_lifetime_elision`.
+    /// Whether or not in-band lifetimes are being collected. This is used to
+    /// indicate whether or not we're in a place where new lifetimes will result
+    /// in in-band lifetime definitions, such a function or an impl header,
+    /// including implicit lifetimes from `impl_header_lifetime_elision`.
     is_collecting_in_band_lifetimes: bool,
 
-    // Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
-    // When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
-    // against this list to see if it is already in-scope, or if a definition
-    // needs to be created for it.
+    /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
+    /// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
+    /// against this list to see if it is already in-scope, or if a definition
+    /// needs to be created for it.
     in_scope_lifetimes: Vec<Ident>,
 
     current_module: NodeId,
@@ -954,7 +954,7 @@ fn make_async_expr(
         let decl = FnDecl {
             inputs: vec![],
             output,
-            variadic: false
+            c_variadic: false
         };
         let body_id = self.record_body(body_expr, Some(&decl));
         self.is_generator = prev_is_generator;
@@ -1345,6 +1345,12 @@ fn lower_ty_direct(&mut self, t: &Ty, mut itctx: ImplTraitContext<'_>) -> hir::T
                 }
             }
             TyKind::Mac(_) => panic!("TyMac should have been expanded by now."),
+            TyKind::CVarArgs => {
+                // Create the implicit lifetime of the "spoofed" `VaList`.
+                let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
+                let lt = self.new_implicit_lifetime(span);
+                hir::TyKind::CVarArgs(lt)
+            },
         };
 
         let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(t.id);
@@ -2112,7 +2118,7 @@ fn lower_fn_decl(
         P(hir::FnDecl {
             inputs,
             output,
-            variadic: decl.variadic,
+            c_variadic: decl.c_variadic,
             implicit_self: decl.inputs.get(0).map_or(
                 hir::ImplicitSelfKind::None,
                 |arg| {
@@ -3967,7 +3973,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
                     let outer_decl = FnDecl {
                         inputs: decl.inputs.clone(),
                         output: FunctionRetTy::Default(fn_decl_span),
-                        variadic: false,
+                        c_variadic: false,
                     };
                     // We need to lower the declaration outside the new scope, because we
                     // have to conserve the state of being inside a loop condition for the
index 38e6e61592b1a4fc321220eb9393f21a1012c7a2..d8169d05dd4d0df0ac64ca3291567b7d11eb1193 100644 (file)
@@ -1829,6 +1829,9 @@ 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
+    /// from the variadic arguments. This type is only valid up to typeck.
+    CVarArgs(Lifetime),
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
@@ -1865,7 +1868,7 @@ pub struct Arg {
 pub struct FnDecl {
     pub inputs: HirVec<Ty>,
     pub output: FunctionRetTy,
-    pub variadic: bool,
+    pub c_variadic: bool,
     /// Does the function have an implicit self?
     pub implicit_self: ImplicitSelfKind,
 }
index 17d374884134a86f429dba2f548f10d2dbce9732..dab4b9c824d9a9389ea8fec78ccaaca87f6a50f1 100644 (file)
@@ -434,6 +434,9 @@ pub fn print_type(&mut self, ty: &hir::Ty) -> io::Result<()> {
                 self.s.word("/*ERROR*/")?;
                 self.pclose()?;
             }
+            hir::TyKind::CVarArgs(_) => {
+                self.s.word("...")?;
+            }
         }
         self.end()
     }
@@ -2004,7 +2007,7 @@ pub fn print_fn(&mut self,
             s.print_type(ty)?;
             s.end()
         })?;
-        if decl.variadic {
+        if decl.c_variadic {
             self.s.word(", ...")?;
         }
         self.pclose()?;
index d1161dda1e2ff592c04f5a7469634e0cc38620a0..775822786900a9d39d0bdaff718544fe37d2c86e 100644 (file)
@@ -361,13 +361,14 @@ fn hash_stable<W: StableHasherResult>(&self,
     TraitObject(trait_refs, lifetime),
     Typeof(body_id),
     Err,
-    Infer
+    Infer,
+    CVarArgs(lt),
 });
 
 impl_stable_hash_for!(struct hir::FnDecl {
     inputs,
     output,
-    variadic,
+    c_variadic,
     implicit_self
 });
 
index cce1273b7f025d0743b29c355061f141e7752960..f77a88128f252ef36deb2a9665d78a087547da56 100644 (file)
@@ -232,7 +232,7 @@ fn hash_stable<W: StableHasherResult>(&self,
 
 impl_stable_hash_for!(struct ty::FnSig<'tcx> {
     inputs_and_output,
-    variadic,
+    c_variadic,
     unsafety,
     abi
 });
index 31e9eb9b7463ca861c25b2aa0f2ae77ed57798c1..832391d44162bba2ca5610f3d7d88c11e0938207 100644 (file)
@@ -764,6 +764,13 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
                     });
                 }
             }
+            hir::TyKind::CVarArgs(ref lt) => {
+                // Resolve the generated lifetime for the C-variadic arguments.
+                // The lifetime is generated in AST -> HIR lowering.
+                if lt.name.is_elided() {
+                    self.resolve_elided_lifetimes(vec![lt])
+                }
+            }
             _ => intravisit::walk_ty(self, ty),
         }
     }
@@ -2225,18 +2232,22 @@ fn visit_ty(&mut self, ty: &hir::Ty) {
                 if let hir::TyKind::BareFn(_) = ty.node {
                     self.outer_index.shift_in(1);
                 }
-                if let hir::TyKind::TraitObject(ref bounds, ref lifetime) = ty.node {
-                    for bound in bounds {
-                        self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
-                    }
+                match ty.node {
+                    hir::TyKind::TraitObject(ref bounds, ref lifetime) => {
+                        for bound in bounds {
+                            self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
+                        }
 
-                    // Stay on the safe side and don't include the object
-                    // lifetime default (which may not end up being used).
-                    if !lifetime.is_elided() {
-                        self.visit_lifetime(lifetime);
+                        // Stay on the safe side and don't include the object
+                        // lifetime default (which may not end up being used).
+                        if !lifetime.is_elided() {
+                            self.visit_lifetime(lifetime);
+                        }
+                    }
+                    hir::TyKind::CVarArgs(_) => {}
+                    _ => {
+                        intravisit::walk_ty(self, ty);
                     }
-                } else {
-                    intravisit::walk_ty(self, ty);
                 }
                 if let hir::TyKind::BareFn(_) = ty.node {
                     self.outer_index.shift_out(1);
index e85b84bce4321d5e036c3ca67a9c70c46cd5e1d0..e7cc9618080c2689f0196b84364dfcc4126612ec 100644 (file)
@@ -1944,7 +1944,7 @@ fn assemble_fn_pointer_candidates(
                 if let ty::FnSig {
                     unsafety: hir::Unsafety::Normal,
                     abi: Abi::Rust,
-                    variadic: false,
+                    c_variadic: false,
                     ..
                 } = self_ty.fn_sig(self.tcx()).skip_binder()
                 {
index b37b632f4beec655cec6b0dc9191f6ccb1e2597a..9767396147cf911c098185dfbbf12f3409fc5285 100644 (file)
@@ -2453,7 +2453,7 @@ pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
             self.mk_fn_sig(
                 params_iter,
                 s.output(),
-                s.variadic,
+                s.c_variadic,
                 hir::Unsafety::Normal,
                 abi::Abi::Rust,
             )
@@ -2779,7 +2779,7 @@ pub fn intern_goals(self, ts: &[Goal<'tcx>]) -> Goals<'tcx> {
     pub fn mk_fn_sig<I>(self,
                         inputs: I,
                         output: I::Item,
-                        variadic: bool,
+                        c_variadic: bool,
                         unsafety: hir::Unsafety,
                         abi: abi::Abi)
         -> <I::Item as InternIteratorElement<Ty<'tcx>, ty::FnSig<'tcx>>>::Output
@@ -2788,7 +2788,7 @@ pub fn mk_fn_sig<I>(self,
     {
         inputs.chain(iter::once(output)).intern_with(|xs| ty::FnSig {
             inputs_and_output: self.intern_type_list(xs),
-            variadic, unsafety, abi
+            c_variadic, unsafety, abi
         })
     }
 
index 709dce4589f6be0a77a033553ab3140e9be91772..49ebd202813f636b7518493d91a746b15eae453c 100644 (file)
@@ -65,7 +65,7 @@ fn fn_sig_noadjust(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> PolyFnSig<'tcx> {
                 sig.map_bound(|sig| tcx.mk_fn_sig(
                     iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()),
                     sig.output(),
-                    sig.variadic,
+                    sig.c_variadic,
                     sig.unsafety,
                     sig.abi
                 ))
index b15aa8629016641cc7b68b5022953e847ea37c2f..2940757fa905be45e68db9baa12418123ca595c8 100644 (file)
@@ -147,9 +147,9 @@ fn relate<'a, 'gcx, R>(relation: &mut R,
     {
         let tcx = relation.tcx();
 
-        if a.variadic != b.variadic {
+        if a.c_variadic != b.c_variadic {
             return Err(TypeError::VariadicMismatch(
-                expected_found(relation, &a.variadic, &b.variadic)));
+                expected_found(relation, &a.c_variadic, &b.c_variadic)));
         }
         let unsafety = relation.relate(&a.unsafety, &b.unsafety)?;
         let abi = relation.relate(&a.abi, &b.abi)?;
@@ -171,7 +171,7 @@ fn relate<'a, 'gcx, R>(relation: &mut R,
             });
         Ok(ty::FnSig {
             inputs_and_output: tcx.mk_type_list(inputs_and_output)?,
-            variadic: a.variadic,
+            c_variadic: a.c_variadic,
             unsafety,
             abi,
         })
index a81d5c9d86edd1ae17bbe81fcdf52a26a1dfc25e..f1a465e1f1724b384b1c7df0dbe0671a73965f9b 100644 (file)
@@ -396,7 +396,7 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
         tcx.lift(&self.inputs_and_output).map(|x| {
             ty::FnSig {
                 inputs_and_output: x,
-                variadic: self.variadic,
+                c_variadic: self.c_variadic,
                 unsafety: self.unsafety,
                 abi: self.abi,
             }
@@ -832,7 +832,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::GenSig<'tcx> {
 
 BraceStructTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
-        inputs_and_output, variadic, unsafety, abi
+        inputs_and_output, c_variadic, unsafety, abi
     }
 }
 
index df8b14b1f10c71c80cb801ce939f9c0ec2a4a0e9..3fd2e38a3d3e51cd10baca00a68b8bd20bbf766c 100644 (file)
@@ -977,13 +977,13 @@ pub fn return_ty(&self) -> ty::Binder<Ty<'tcx>> {
 /// Signature of a function type, which I have arbitrarily
 /// decided to use to refer to the input/output types.
 ///
-/// - `inputs` is the list of arguments and their modes.
-/// - `output` is the return type.
-/// - `variadic` indicates whether this is a variadic function. (only true for foreign fns)
+/// - `inputs`: is the list of arguments and their modes.
+/// - `output`: is the return type.
+/// - `c_variadic`: indicates whether this is a C-variadic function.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct FnSig<'tcx> {
     pub inputs_and_output: &'tcx List<Ty<'tcx>>,
-    pub variadic: bool,
+    pub c_variadic: bool,
     pub unsafety: hir::Unsafety,
     pub abi: abi::Abi,
 }
@@ -1016,8 +1016,8 @@ pub fn inputs_and_output(&self) -> ty::Binder<&'tcx List<Ty<'tcx>>> {
     pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
         self.map_bound_ref(|fn_sig| fn_sig.output())
     }
-    pub fn variadic(&self) -> bool {
-        self.skip_binder().variadic
+    pub fn c_variadic(&self) -> bool {
+        self.skip_binder().c_variadic
     }
     pub fn unsafety(&self) -> hir::Unsafety {
         self.skip_binder().unsafety
index fbe9e3359bfeb7437d7af7d80ec27ad60c9c2515..aecef3c5ec71e4d5439efd1718065508b0e1c1d2 100644 (file)
@@ -360,7 +360,7 @@ impl PrintContext {
     fn fn_sig<F: fmt::Write>(&mut self,
                              f: &mut F,
                              inputs: &[Ty<'_>],
-                             variadic: bool,
+                             c_variadic: bool,
                              output: Ty<'_>)
                              -> fmt::Result {
         write!(f, "(")?;
@@ -370,7 +370,7 @@ fn fn_sig<F: fmt::Write>(&mut self,
             for &ty in inputs {
                 print!(f, self, write(", "), print_display(ty))?;
             }
-            if variadic {
+            if c_variadic {
                 write!(f, ", ...")?;
             }
         }
@@ -1074,10 +1074,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             }
 
             write!(f, "fn")?;
-            cx.fn_sig(f, self.inputs(), self.variadic, self.output())
+            cx.fn_sig(f, self.inputs(), self.c_variadic, self.output())
         }
         debug {
-            write!(f, "({:?}; variadic: {})->{:?}", self.inputs(), self.variadic, self.output())
+            write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output())
         }
     }
 }
index 992149f7a47b50e8abc1048d71225dd4c71d9b31..49c9555a2c682d74f642016699c9e4116940b953 100644 (file)
@@ -258,7 +258,7 @@ fn store_fn_arg(
             val
         };
         match self.mode {
-            PassMode::Ignore => {},
+            PassMode::Ignore(_) => {}
             PassMode::Pair(..) => {
                 OperandValue::Pair(next(), next()).store(bx, dst);
             }
@@ -422,7 +422,7 @@ fn new_internal(
 
         let mut inputs = sig.inputs();
         let extra_args = if sig.abi == RustCall {
-            assert!(!sig.variadic && extra_args.is_empty());
+            assert!(!sig.c_variadic && extra_args.is_empty());
 
             match sig.inputs().last().unwrap().sty {
                 ty::Tuple(ref tupled_arguments) => {
@@ -435,7 +435,7 @@ fn new_internal(
                 }
             }
         } else {
-            assert!(sig.variadic || extra_args.is_empty());
+            assert!(sig.c_variadic || extra_args.is_empty());
             extra_args
         };
 
@@ -507,6 +507,14 @@ fn new_internal(
             }
         };
 
+        // Store the index of the last argument. This is useful for working with
+        // C-compatible variadic arguments.
+        let last_arg_idx = if sig.inputs().is_empty() {
+            None
+        } else {
+            Some(sig.inputs().len() - 1)
+        };
+
         let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| {
             let is_return = arg_idx.is_none();
             let mut arg = mk_arg_type(ty, arg_idx);
@@ -516,7 +524,30 @@ fn new_internal(
                 // The same is true for s390x-unknown-linux-gnu
                 // and sparc64-unknown-linux-gnu.
                 if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) {
-                    arg.mode = PassMode::Ignore;
+                    arg.mode = PassMode::Ignore(IgnoreMode::Zst);
+                }
+            }
+
+            // 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`
+            // is ignored as an argument.
+            if sig.c_variadic {
+                match (last_arg_idx, arg_idx) {
+                    (Some(last_idx), Some(cur_idx)) if last_idx == cur_idx => {
+                        let va_list_did = match cx.tcx.lang_items().va_list() {
+                            Some(did) => did,
+                            None => bug!("`va_list` lang item required for C-variadic functions"),
+                        };
+                        match ty.sty {
+                            ty::Adt(def, _) if def.did == va_list_did => {
+                                // This is the "spoofed" `VaList`. Set the arguments mode
+                                // so that it will be ignored.
+                                arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs);
+                            },
+                            _ => (),
+                        }
+                    }
+                    _ => {}
                 }
             }
 
@@ -558,7 +589,7 @@ fn new_internal(
             args: inputs.iter().chain(extra_args).enumerate().map(|(i, ty)| {
                 arg_of(ty, Some(i))
             }).collect(),
-            variadic: sig.variadic,
+            c_variadic: sig.c_variadic,
             conv,
         };
         fn_ty.adjust_for_abi(cx, sig.abi);
@@ -646,7 +677,9 @@ fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
         );
 
         let llreturn_ty = match self.ret.mode {
-            PassMode::Ignore => cx.type_void(),
+            PassMode::Ignore(IgnoreMode::Zst) => cx.type_void(),
+            PassMode::Ignore(IgnoreMode::CVarArgs) =>
+                bug!("`va_list` should never be a return type"),
             PassMode::Direct(_) | PassMode::Pair(..) => {
                 self.ret.layout.immediate_llvm_type(cx)
             }
@@ -664,7 +697,7 @@ fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
             }
 
             let llarg_ty = match arg.mode {
-                PassMode::Ignore => continue,
+                PassMode::Ignore(_) => continue,
                 PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
                 PassMode::Pair(..) => {
                     llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true));
@@ -684,7 +717,7 @@ fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
             llargument_tys.push(llarg_ty);
         }
 
-        if self.variadic {
+        if self.c_variadic {
             cx.type_variadic_func(&llargument_tys, llreturn_ty)
         } else {
             cx.type_func(&llargument_tys, llreturn_ty)
@@ -733,7 +766,7 @@ fn apply_attrs_llfn(&self, llfn: &'ll Value) {
                 apply(&ArgAttributes::new());
             }
             match arg.mode {
-                PassMode::Ignore => {}
+                PassMode::Ignore(_) => {}
                 PassMode::Direct(ref attrs) |
                 PassMode::Indirect(ref attrs, None) => apply(attrs),
                 PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
@@ -780,7 +813,7 @@ fn apply_attrs_callsite(&self, bx: &mut Builder<'a, 'll, 'tcx>, callsite: &'ll V
                 apply(&ArgAttributes::new());
             }
             match arg.mode {
-                PassMode::Ignore => {}
+                PassMode::Ignore(_) => {}
                 PassMode::Direct(ref attrs) |
                 PassMode::Indirect(ref attrs, None) => apply(attrs),
                 PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
index 176c9b8c542b184172cd2bb63bf78f1ff86c0c5b..8b218ab39d99b520d60a3faa078b6d05d736d6ff 100644 (file)
@@ -143,7 +143,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                 output.pop();
             }
 
-            if sig.variadic {
+            if sig.c_variadic {
                 if !sig.inputs().is_empty() {
                     output.push_str(", ...");
                 } else {
index d1cbe1d4dd6866c44880432ada15a6e69a6c7ea5..3268af396a2f4f475c2353b3ed76ea66083c8ec9 100644 (file)
@@ -136,22 +136,18 @@ fn codegen_intrinsic_call(
                 let tp_ty = substs.type_at(0);
                 self.const_usize(self.size_of(tp_ty).bytes())
             }
-            func @ "va_start" | func @ "va_end" => {
-                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(&format!("llvm.{}", func));
-                self.call(intrinsic, &[va_list], None)
+            "va_start" => {
+                self.va_start(args[0].immediate())
+            }
+            "va_end" => {
+                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")
+                    (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);
@@ -722,6 +718,41 @@ fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
         let expect = self.get_intrinsic(&"llvm.expect.i1");
         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,
+        };
+        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,
+        };
+        let intrinsic = self.cx().get_intrinsic("llvm.va_end");
+        self.call(intrinsic, &[va_list], None)
+    }
 }
 
 fn copy_intrinsic(
index 4fe6a1f4f4b1ca132e031470fafe00b7d1ecbb7b..7f0cdb9f580088b1b49f0e0ee1d08596f42afcb7 100644 (file)
@@ -36,10 +36,10 @@ fn predefine_static(&self,
     }
 
     fn predefine_fn(&self,
-                              instance: Instance<'tcx>,
-                              linkage: Linkage,
-                              visibility: Visibility,
-                              symbol_name: &str) {
+                    instance: Instance<'tcx>,
+                    linkage: Linkage,
+                    visibility: Visibility,
+                    symbol_name: &str) {
         assert!(!instance.substs.needs_infer() &&
                 !instance.substs.has_param_types());
 
index 8719390b51acaa64dcd724ccafaa01681374f377..7aceaea4510ceb156246f1cea7cafcb110b548fc 100644 (file)
@@ -109,12 +109,12 @@ pub(super) fn emit_va_arg(
                             Align::from_bytes(4).unwrap(), true)
         }
         // Windows Aarch64
-        ("aarch4", true) => {
+        ("aarch64", true) => {
             emit_ptr_va_arg(bx, addr, target_ty, false,
                             Align::from_bytes(8).unwrap(), false)
         }
         // iOS Aarch64
-        ("aarch4", _) if target.target_os == "ios" => {
+        ("aarch64", _) if target.target_os == "ios" => {
             emit_ptr_va_arg(bx, addr, target_ty, false,
                             Align::from_bytes(8).unwrap(), true)
         }
index caca1789fc98c5989ced9c96018a3f6adfa71572..627380ee38ff14d6bd16b99ab41bd79635c13c71 100644 (file)
@@ -3,7 +3,7 @@
 use rustc::ty::layout::{self, LayoutOf, HasTyCtxt};
 use rustc::mir;
 use rustc::mir::interpret::EvalErrorKind;
-use rustc_target::abi::call::{ArgType, FnType, PassMode};
+use rustc_target::abi::call::{ArgType, FnType, PassMode, IgnoreMode};
 use rustc_target::spec::abi::Abi;
 use rustc_mir::monomorphize;
 use crate::base;
 
 use crate::traits::*;
 
+use std::borrow::Cow;
+
 use syntax::symbol::Symbol;
 use syntax_pos::Pos;
 
 use super::{FunctionCx, LocalRef};
 use super::place::PlaceRef;
-use super::operand::OperandRef;
+use super::operand::{OperandValue, OperandRef};
 use super::operand::OperandValue::{Pair, Ref, Immediate};
 
-impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
-    pub fn codegen_block(
-        &mut self,
-        bb: mir::BasicBlock,
-    ) {
-        let mut bx = self.build_block(bb);
-        let data = &self.mir[bb];
+/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
+/// e.g., creating a basic block, calling a function, etc.
+struct TerminatorCodegenHelper<'a, 'tcx> {
+    bb: &'a mir::BasicBlock,
+    terminator: &'a mir::Terminator<'tcx>,
+    funclet_bb: Option<mir::BasicBlock>,
+}
 
-        debug!("codegen_block({:?}={:?})", bb, data);
+impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
+    /// Returns the associated funclet from `FunctionCx::funclets` for the
+    /// `funclet_bb` member if it is not `None`.
+    fn funclet<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+        &self,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+    ) -> Option<&'c Bx::Funclet> {
+        match self.funclet_bb {
+            Some(funcl) => fx.funclets[funcl].as_ref(),
+            None => None,
+        }
+    }
 
-        for statement in &data.statements {
-            bx = self.codegen_statement(bx, statement);
+    fn lltarget<'b, 'c, Bx: BuilderMethods<'b, 'tcx>>(
+        &self,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        target: mir::BasicBlock,
+    ) -> (Bx::BasicBlock, bool) {
+        let span = self.terminator.source_info.span;
+        let lltarget = fx.blocks[target];
+        let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
+        match (self.funclet_bb, target_funclet) {
+            (None, None) => (lltarget, false),
+            (Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) =>
+                (lltarget, false),
+            // jump *into* cleanup - need a landing pad if GNU
+            (None, Some(_)) => (fx.landing_pad_to(target), false),
+            (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator),
+            (Some(_), Some(_)) => (fx.landing_pad_to(target), true),
         }
+    }
 
-        self.codegen_terminator(bx, bb, data.terminator());
+    /// Create a basic block.
+    fn llblock<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+        &self,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        target: mir::BasicBlock,
+    ) -> Bx::BasicBlock {
+        let (lltarget, is_cleanupret) = self.lltarget(fx, target);
+        if is_cleanupret {
+            // MSVC cross-funclet jump - need a trampoline
+
+            debug!("llblock: creating cleanup trampoline for {:?}", target);
+            let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target);
+            let mut trampoline = fx.new_block(name);
+            trampoline.cleanup_ret(self.funclet(fx).unwrap(),
+                                   Some(lltarget));
+            trampoline.llbb()
+        } else {
+            lltarget
+        }
     }
 
-    fn codegen_terminator(
-        &mut self,
-        mut bx: Bx,
-        bb: mir::BasicBlock,
-        terminator: &mir::Terminator<'tcx>
+    fn funclet_br<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+        &self,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        bx: &mut Bx,
+        target: mir::BasicBlock,
     ) {
-        debug!("codegen_terminator: {:?}", terminator);
-
-        // Create the cleanup bundle, if needed.
-        let tcx = self.cx.tcx();
-        let span = terminator.source_info.span;
-        let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
+        let (lltarget, is_cleanupret) = self.lltarget(fx, target);
+        if is_cleanupret {
+            // micro-optimization: generate a `ret` rather than a jump
+            // to a trampoline.
+            bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
+        } else {
+            bx.br(lltarget);
+        }
+    }
 
-        // HACK(eddyb) force the right lifetimes, NLL can't figure them out.
-        fn funclet_closure_factory<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
-            funclet_bb: Option<mir::BasicBlock>
-        ) -> impl for<'b> Fn(
-            &'b FunctionCx<'a, 'tcx, Bx>,
-        ) -> Option<&'b Bx::Funclet> {
-            move |this| {
-                match funclet_bb {
-                    Some(funclet_bb) => this.funclets[funclet_bb].as_ref(),
-                    None => None,
-                }
+    /// Call `fn_ptr` of `fn_ty` with the arguments `llargs`, the optional
+    /// return destination `destination` and the cleanup function `cleanup`.
+    fn do_call<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+        &self,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        bx: &mut Bx,
+        fn_ty: FnType<'tcx, Ty<'tcx>>,
+        fn_ptr: Bx::Value,
+        llargs: &[Bx::Value],
+        destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
+        cleanup: Option<mir::BasicBlock>,
+    ) {
+        if let Some(cleanup) = cleanup {
+            let ret_bx = if let Some((_, target)) = destination {
+                fx.blocks[target]
+            } else {
+                fx.unreachable_block()
+            };
+            let invokeret = bx.invoke(fn_ptr,
+                                      &llargs,
+                                      ret_bx,
+                                      self.llblock(fx, cleanup),
+                                      self.funclet(fx));
+            bx.apply_attrs_callsite(&fn_ty, invokeret);
+
+            if let Some((ret_dest, target)) = destination {
+                let mut ret_bx = fx.build_block(target);
+                fx.set_debug_loc(&mut ret_bx, self.terminator.source_info);
+                fx.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret);
             }
-        }
-        let funclet = funclet_closure_factory(funclet_bb);
-
-        let lltarget = |this: &mut Self, target: mir::BasicBlock| {
-            let lltarget = this.blocks[target];
-            let target_funclet = this.cleanup_kinds[target].funclet_bb(target);
-            match (funclet_bb, target_funclet) {
-                (None, None) => (lltarget, false),
-                (Some(f), Some(t_f))
-                    if f == t_f || !base::wants_msvc_seh(tcx.sess)
-                    => (lltarget, false),
-                (None, Some(_)) => {
-                    // jump *into* cleanup - need a landing pad if GNU
-                    (this.landing_pad_to(target), false)
-                }
-                (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator),
-                (Some(_), Some(_)) => {
-                    (this.landing_pad_to(target), true)
-                }
+        } else {
+            let llret = bx.call(fn_ptr, &llargs, self.funclet(fx));
+            bx.apply_attrs_callsite(&fn_ty, llret);
+            if fx.mir[*self.bb].is_cleanup {
+                // Cleanup is always the cold path. Don't inline
+                // drop glue. Also, when there is a deeply-nested
+                // struct, there are "symmetry" issues that cause
+                // exponential inlining - see issue #41696.
+                bx.do_not_inline(llret);
             }
-        };
-
-        let llblock = |this: &mut Self, target: mir::BasicBlock| {
-            let (lltarget, is_cleanupret) = lltarget(this, target);
-            if is_cleanupret {
-                // MSVC cross-funclet jump - need a trampoline
 
-                debug!("llblock: creating cleanup trampoline for {:?}", target);
-                let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
-                let mut trampoline = this.new_block(name);
-                trampoline.cleanup_ret(funclet(this).unwrap(), Some(lltarget));
-                trampoline.llbb()
+            if let Some((ret_dest, target)) = destination {
+                fx.store_return(bx, ret_dest, &fn_ty.ret, llret);
+                self.funclet_br(fx, bx, target);
             } else {
-                lltarget
+                bx.unreachable();
             }
-        };
-
-        let funclet_br =
-            |this: &mut Self, bx: &mut Bx, target: mir::BasicBlock| {
-                let (lltarget, is_cleanupret) = lltarget(this, target);
-                if is_cleanupret {
-                    // micro-optimization: generate a `ret` rather than a jump
-                    // to a trampoline.
-                    bx.cleanup_ret(funclet(this).unwrap(), Some(lltarget));
-                } else {
-                    bx.br(lltarget);
-                }
-            };
+        }
+    }
+}
 
-        let do_call = |
-            this: &mut Self,
-            bx: &mut Bx,
-            fn_ty: FnType<'tcx, Ty<'tcx>>,
-            fn_ptr: Bx::Value,
-            llargs: &[Bx::Value],
-            destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
-            cleanup: Option<mir::BasicBlock>
-        | {
-            if let Some(cleanup) = cleanup {
-                let ret_bx = if let Some((_, target)) = destination {
-                    this.blocks[target]
-                } else {
-                    this.unreachable_block()
-                };
-                let invokeret = bx.invoke(fn_ptr,
-                                          &llargs,
-                                          ret_bx,
-                                          llblock(this, cleanup),
-                                          funclet(this));
-                bx.apply_attrs_callsite(&fn_ty, invokeret);
-
-                if let Some((ret_dest, target)) = destination {
-                    let mut ret_bx = this.build_block(target);
-                    this.set_debug_loc(&mut ret_bx, terminator.source_info);
-                    this.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret);
-                }
+/// Codegen implementations for some terminator variants.
+impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
+    /// Generates code for a `Resume` terminator.
+    fn codegen_resume_terminator<'b>(
+        &mut self,
+        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        mut bx: Bx,
+    ) {
+        if let Some(funclet) = helper.funclet(self) {
+            bx.cleanup_ret(funclet, None);
+        } else {
+            let slot = self.get_personality_slot(&mut bx);
+            let lp0 = slot.project_field(&mut bx, 0);
+            let lp0 = bx.load_operand(lp0).immediate();
+            let lp1 = slot.project_field(&mut bx, 1);
+            let lp1 = bx.load_operand(lp1).immediate();
+            slot.storage_dead(&mut bx);
+
+            if !bx.sess().target.target.options.custom_unwind_resume {
+                let mut lp = bx.const_undef(self.landing_pad_type());
+                lp = bx.insert_value(lp, lp0, 0);
+                lp = bx.insert_value(lp, lp1, 1);
+                bx.resume(lp);
             } else {
-                let llret = bx.call(fn_ptr, &llargs, funclet(this));
-                bx.apply_attrs_callsite(&fn_ty, llret);
-                if this.mir[bb].is_cleanup {
-                    // Cleanup is always the cold path. Don't inline
-                    // drop glue. Also, when there is a deeply-nested
-                    // struct, there are "symmetry" issues that cause
-                    // exponential inlining - see issue #41696.
-                    bx.do_not_inline(llret);
-                }
-
-                if let Some((ret_dest, target)) = destination {
-                    this.store_return(bx, ret_dest, &fn_ty.ret, llret);
-                    funclet_br(this, bx, target);
-                } else {
-                    bx.unreachable();
-                }
+                bx.call(bx.eh_unwind_resume(), &[lp0],
+                        helper.funclet(self));
+                bx.unreachable();
             }
-        };
+        }
+    }
 
-        self.set_debug_loc(&mut bx, terminator.source_info);
-        match terminator.kind {
-            mir::TerminatorKind::Resume => {
-                if let Some(funclet) = funclet(self) {
-                    bx.cleanup_ret(funclet, None);
+    fn codegen_switchint_terminator<'b>(
+        &mut self,
+        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        mut bx: Bx,
+        discr: &mir::Operand<'tcx>,
+        switch_ty: Ty<'tcx>,
+        values: &Cow<'tcx, [u128]>,
+        targets: &Vec<mir::BasicBlock>,
+    ) {
+        let discr = self.codegen_operand(&mut bx, &discr);
+        if targets.len() == 2 {
+            // If there are two targets, emit br instead of switch
+            let lltrue = helper.llblock(self, targets[0]);
+            let llfalse = helper.llblock(self, targets[1]);
+            if switch_ty == bx.tcx().types.bool {
+                // Don't generate trivial icmps when switching on bool
+                if let [0] = values[..] {
+                    bx.cond_br(discr.immediate(), llfalse, lltrue);
                 } else {
-                    let slot = self.get_personality_slot(&mut bx);
-                    let lp0 = slot.project_field(&mut bx, 0);
-                    let lp0 = bx.load_operand(lp0).immediate();
-                    let lp1 = slot.project_field(&mut bx, 1);
-                    let lp1 = bx.load_operand(lp1).immediate();
-                    slot.storage_dead(&mut bx);
-
-                    if !bx.sess().target.target.options.custom_unwind_resume {
-                        let mut lp = bx.const_undef(self.landing_pad_type());
-                        lp = bx.insert_value(lp, lp0, 0);
-                        lp = bx.insert_value(lp, lp1, 1);
-                        bx.resume(lp);
-                    } else {
-                        bx.call(bx.eh_unwind_resume(), &[lp0], funclet(self));
-                        bx.unreachable();
-                    }
+                    assert_eq!(&values[..], &[1]);
+                    bx.cond_br(discr.immediate(), lltrue, llfalse);
                 }
+            } else {
+                let switch_llty = bx.immediate_backend_type(
+                    bx.layout_of(switch_ty)
+                );
+                let llval = bx.const_uint_big(switch_llty, values[0]);
+                let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
+                bx.cond_br(cmp, lltrue, llfalse);
+            }
+        } else {
+            let (otherwise, targets) = targets.split_last().unwrap();
+            let switch = bx.switch(discr.immediate(),
+                                   helper.llblock(self, *otherwise),
+                                   values.len());
+            let switch_llty = bx.immediate_backend_type(
+                bx.layout_of(switch_ty)
+            );
+            for (&value, target) in values.iter().zip(targets) {
+                let llval = bx.const_uint_big(switch_llty, value);
+                let llbb = helper.llblock(self, *target);
+                bx.add_case(switch, llval, llbb)
             }
+        }
+    }
 
-            mir::TerminatorKind::Abort => {
-                bx.abort();
-                bx.unreachable();
+    fn codegen_return_terminator<'b>(
+        &mut self,
+        mut bx: Bx,
+    ) {
+        if self.fn_ty.c_variadic {
+            if let Some(va_list) = self.va_list_ref {
+                bx.va_end(va_list.llval);
+            }
+        }
+        let llval = match self.fn_ty.ret.mode {
+            PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => {
+                bx.ret_void();
+                return;
             }
 
-            mir::TerminatorKind::Goto { target } => {
-                funclet_br(self, &mut bx, target);
+            PassMode::Ignore(IgnoreMode::CVarArgs) => {
+                bug!("C-variadic arguments should never be the return type");
             }
 
-            mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
-                let discr = self.codegen_operand(&mut bx, discr);
-                if targets.len() == 2 {
-                    // If there are two targets, emit br instead of switch
-                    let lltrue = llblock(self, targets[0]);
-                    let llfalse = llblock(self, targets[1]);
-                    if switch_ty == bx.tcx().types.bool {
-                        // Don't generate trivial icmps when switching on bool
-                        if let [0] = values[..] {
-                            bx.cond_br(discr.immediate(), llfalse, lltrue);
-                        } else {
-                            assert_eq!(&values[..], &[1]);
-                            bx.cond_br(discr.immediate(), lltrue, llfalse);
-                        }
-                    } else {
-                        let switch_llty = bx.immediate_backend_type(
-                            bx.layout_of(switch_ty)
-                        );
-                        let llval = bx.const_uint_big(switch_llty, values[0]);
-                        let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
-                        bx.cond_br(cmp, lltrue, llfalse);
-                    }
+            PassMode::Direct(_) | PassMode::Pair(..) => {
+                let op =
+                    self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE));
+                if let Ref(llval, _, align) = op.val {
+                    bx.load(llval, align)
                 } else {
-                    let (otherwise, targets) = targets.split_last().unwrap();
-                    let switch = bx.switch(discr.immediate(),
-                                           llblock(self, *otherwise),
-                                           values.len());
-                    let switch_llty = bx.immediate_backend_type(
-                        bx.layout_of(switch_ty)
-                    );
-                    for (&value, target) in values.iter().zip(targets) {
-                        let llval = bx.const_uint_big(switch_llty, value);
-                        let llbb = llblock(self, *target);
-                        bx.add_case(switch, llval, llbb)
-                    }
+                    op.immediate_or_packed_pair(&mut bx)
                 }
             }
 
-            mir::TerminatorKind::Return => {
-                let llval = match self.fn_ty.ret.mode {
-                    PassMode::Ignore | PassMode::Indirect(..) => {
-                        bx.ret_void();
-                        return;
-                    }
-
-                    PassMode::Direct(_) | PassMode::Pair(..) => {
-                        let op =
-                            self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE));
-                        if let Ref(llval, _, align) = op.val {
-                            bx.load(llval, align)
-                        } else {
-                            op.immediate_or_packed_pair(&mut bx)
+            PassMode::Cast(cast_ty) => {
+                let op = match self.locals[mir::RETURN_PLACE] {
+                    LocalRef::Operand(Some(op)) => op,
+                    LocalRef::Operand(None) => bug!("use of return before def"),
+                    LocalRef::Place(cg_place) => {
+                        OperandRef {
+                            val: Ref(cg_place.llval, None, cg_place.align),
+                            layout: cg_place.layout
                         }
                     }
-
-                    PassMode::Cast(cast_ty) => {
-                        let op = match self.locals[mir::RETURN_PLACE] {
-                            LocalRef::Operand(Some(op)) => op,
-                            LocalRef::Operand(None) => bug!("use of return before def"),
-                            LocalRef::Place(cg_place) => {
-                                OperandRef {
-                                    val: Ref(cg_place.llval, None, cg_place.align),
-                                    layout: cg_place.layout
-                                }
-                            }
-                            LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
-                        };
-                        let llslot = match op.val {
-                            Immediate(_) | Pair(..) => {
-                                let scratch =
-                                    PlaceRef::alloca(&mut bx, self.fn_ty.ret.layout, "ret");
-                                op.val.store(&mut bx, scratch);
-                                scratch.llval
-                            }
-                            Ref(llval, _, align) => {
-                                assert_eq!(align, op.layout.align.abi,
-                                           "return place is unaligned!");
-                                llval
-                            }
-                        };
-                        let addr = bx.pointercast(llslot, bx.type_ptr_to(
-                            bx.cast_backend_type(&cast_ty)
-                        ));
-                        bx.load(addr, self.fn_ty.ret.layout.align.abi)
+                    LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
+                };
+                let llslot = match op.val {
+                    Immediate(_) | Pair(..) => {
+                        let scratch =
+                            PlaceRef::alloca(&mut bx, self.fn_ty.ret.layout, "ret");
+                        op.val.store(&mut bx, scratch);
+                        scratch.llval
+                    }
+                    Ref(llval, _, align) => {
+                        assert_eq!(align, op.layout.align.abi,
+                                   "return place is unaligned!");
+                        llval
                     }
                 };
-                bx.ret(llval);
+                let addr = bx.pointercast(llslot, bx.type_ptr_to(
+                    bx.cast_backend_type(&cast_ty)
+                ));
+                bx.load(addr, self.fn_ty.ret.layout.align.abi)
             }
+        };
+        bx.ret(llval);
+    }
 
-            mir::TerminatorKind::Unreachable => {
-                bx.unreachable();
+
+    fn codegen_drop_terminator<'b>(
+        &mut self,
+        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        mut bx: Bx,
+        location: &mir::Place<'tcx>,
+        target: mir::BasicBlock,
+        unwind: Option<mir::BasicBlock>,
+    ) {
+        let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx());
+        let ty = self.monomorphize(&ty);
+        let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty);
+
+        if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
+            // we don't actually need to drop anything.
+            helper.funclet_br(self, &mut bx, target);
+            return
+        }
+
+        let place = self.codegen_place(&mut bx, location);
+        let (args1, args2);
+        let mut args = if let Some(llextra) = place.llextra {
+            args2 = [place.llval, llextra];
+            &args2[..]
+        } else {
+            args1 = [place.llval];
+            &args1[..]
+        };
+        let (drop_fn, fn_ty) = match ty.sty {
+            ty::Dynamic(..) => {
+                let sig = drop_fn.fn_sig(self.cx.tcx());
+                let sig = self.cx.tcx().normalize_erasing_late_bound_regions(
+                    ty::ParamEnv::reveal_all(),
+                    &sig,
+                );
+                let fn_ty = bx.new_vtable(sig, &[]);
+                let vtable = args[1];
+                args = &args[..1];
+                (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_ty), fn_ty)
+            }
+            _ => {
+                (bx.get_fn(drop_fn),
+                 bx.fn_type_of_instance(&drop_fn))
             }
+        };
+        helper.do_call(self, &mut bx, fn_ty, drop_fn, args,
+                       Some((ReturnDest::Nothing, target)),
+                       unwind);
+    }
 
-            mir::TerminatorKind::Drop { ref location, target, unwind } => {
-                let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx());
-                let ty = self.monomorphize(&ty);
-                let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty);
-
-                if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
-                    // we don't actually need to drop anything.
-                    funclet_br(self, &mut bx, target);
-                    return
-                }
+    fn codegen_assert_terminator<'b>(
+        &mut self,
+        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        mut bx: Bx,
+        terminator: &mir::Terminator<'tcx>,
+        cond: &mir::Operand<'tcx>,
+        expected: bool,
+        msg: &mir::AssertMessage<'tcx>,
+        target: mir::BasicBlock,
+        cleanup: Option<mir::BasicBlock>,
+    ) {
+        let span = terminator.source_info.span;
+        let cond = self.codegen_operand(&mut bx, cond).immediate();
+        let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
+
+        // This case can currently arise only from functions marked
+        // with #[rustc_inherit_overflow_checks] and inlined from
+        // another crate (mostly core::num generic/#[inline] fns),
+        // while the current crate doesn't use overflow checks.
+        // NOTE: Unlike binops, negation doesn't have its own
+        // checked operation, just a comparison with the minimum
+        // value, so we have to check for the assert message.
+        if !bx.check_overflow() {
+            if let mir::interpret::EvalErrorKind::OverflowNeg = *msg {
+                const_cond = Some(expected);
+            }
+        }
 
-                let place = self.codegen_place(&mut bx, location);
-                let (args1, args2);
-                let mut args = if let Some(llextra) = place.llextra {
-                    args2 = [place.llval, llextra];
-                    &args2[..]
-                } else {
-                    args1 = [place.llval];
-                    &args1[..]
-                };
-                let (drop_fn, fn_ty) = match ty.sty {
-                    ty::Dynamic(..) => {
-                        let sig = drop_fn.fn_sig(tcx);
-                        let sig = tcx.normalize_erasing_late_bound_regions(
-                            ty::ParamEnv::reveal_all(),
-                            &sig,
-                        );
-                        let fn_ty = bx.new_vtable(sig, &[]);
-                        let vtable = args[1];
-                        args = &args[..1];
-                        (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_ty), fn_ty)
-                    }
-                    _ => {
-                        (bx.get_fn(drop_fn),
-                         bx.fn_type_of_instance(&drop_fn))
-                    }
-                };
-                do_call(self, &mut bx, fn_ty, drop_fn, args,
-                        Some((ReturnDest::Nothing, target)),
-                        unwind);
+        // Don't codegen the panic block if success if known.
+        if const_cond == Some(expected) {
+            helper.funclet_br(self, &mut bx, target);
+            return;
+        }
+
+        // Pass the condition through llvm.expect for branch hinting.
+        let cond = bx.expect(cond, expected);
+
+        // Create the failure block and the conditional branch to it.
+        let lltarget = helper.llblock(self, target);
+        let panic_block = self.new_block("panic");
+        if expected {
+            bx.cond_br(cond, lltarget, panic_block.llbb());
+        } else {
+            bx.cond_br(cond, panic_block.llbb(), lltarget);
+        }
+
+        // After this point, bx is the block for the call to panic.
+        bx = panic_block;
+        self.set_debug_loc(&mut bx, terminator.source_info);
+
+        // Get the location information.
+        let loc = bx.sess().source_map().lookup_char_pos(span.lo());
+        let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
+        let filename = bx.const_str_slice(filename);
+        let line = bx.const_u32(loc.line as u32);
+        let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
+        let align = self.cx.tcx().data_layout.aggregate_align.abi
+            .max(self.cx.tcx().data_layout.i32_align.abi)
+            .max(self.cx.tcx().data_layout.pointer_align.abi);
+
+        // Put together the arguments to the panic entry point.
+        let (lang_item, args) = match *msg {
+            EvalErrorKind::BoundsCheck { ref len, ref index } => {
+                let len = self.codegen_operand(&mut bx, len).immediate();
+                let index = self.codegen_operand(&mut bx, index).immediate();
+
+                let file_line_col = bx.const_struct(&[filename, line, col], false);
+                let file_line_col = bx.static_addr_of(
+                    file_line_col,
+                    align,
+                    Some("panic_bounds_check_loc")
+                );
+                (lang_items::PanicBoundsCheckFnLangItem,
+                 vec![file_line_col, index, len])
+            }
+            _ => {
+                let str = msg.description();
+                let msg_str = Symbol::intern(str).as_str();
+                let msg_str = bx.const_str_slice(msg_str);
+                let msg_file_line_col = bx.const_struct(
+                    &[msg_str, filename, line, col],
+                    false
+                );
+                let msg_file_line_col = bx.static_addr_of(
+                    msg_file_line_col,
+                    align,
+                    Some("panic_loc")
+                );
+                (lang_items::PanicFnLangItem,
+                 vec![msg_file_line_col])
             }
+        };
 
-            mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
-                let cond = self.codegen_operand(&mut bx, cond).immediate();
-                let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
-
-                // This case can currently arise only from functions marked
-                // with #[rustc_inherit_overflow_checks] and inlined from
-                // another crate (mostly core::num generic/#[inline] fns),
-                // while the current crate doesn't use overflow checks.
-                // NOTE: Unlike binops, negation doesn't have its own
-                // checked operation, just a comparison with the minimum
-                // value, so we have to check for the assert message.
-                if !bx.check_overflow() {
-                    if let mir::interpret::EvalErrorKind::OverflowNeg = *msg {
-                        const_cond = Some(expected);
-                    }
-                }
+        // Obtain the panic entry point.
+        let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
+        let instance = ty::Instance::mono(bx.tcx(), def_id);
+        let fn_ty = bx.fn_type_of_instance(&instance);
+        let llfn = bx.get_fn(instance);
 
-                // Don't codegen the panic block if success if known.
-                if const_cond == Some(expected) {
-                    funclet_br(self, &mut bx, target);
-                    return;
-                }
+        // Codegen the actual panic invoke/call.
+        helper.do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup);
+    }
 
-                // Pass the condition through llvm.expect for branch hinting.
-                let cond = bx.expect(cond, expected);
+    fn codegen_call_terminator<'b>(
+        &mut self,
+        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        mut bx: Bx,
+        terminator: &mir::Terminator<'tcx>,
+        func: &mir::Operand<'tcx>,
+        args: &Vec<mir::Operand<'tcx>>,
+        destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
+        cleanup: Option<mir::BasicBlock>,
+    ) {
+        let span = terminator.source_info.span;
+        // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
+        let callee = self.codegen_operand(&mut bx, func);
+
+        let (instance, mut llfn) = match callee.layout.ty.sty {
+            ty::FnDef(def_id, substs) => {
+                (Some(ty::Instance::resolve(bx.tcx(),
+                                            ty::ParamEnv::reveal_all(),
+                                            def_id,
+                                            substs).unwrap()),
+                 None)
+            }
+            ty::FnPtr(_) => {
+                (None, Some(callee.immediate()))
+            }
+            _ => bug!("{} is not callable", callee.layout.ty),
+        };
+        let def = instance.map(|i| i.def);
+        let sig = callee.layout.ty.fn_sig(bx.tcx());
+        let sig = bx.tcx().normalize_erasing_late_bound_regions(
+            ty::ParamEnv::reveal_all(),
+            &sig,
+        );
+        let abi = sig.abi;
+
+        // Handle intrinsics old codegen wants Expr's for, ourselves.
+        let intrinsic = match def {
+            Some(ty::InstanceDef::Intrinsic(def_id)) =>
+                Some(bx.tcx().item_name(def_id).as_str()),
+            _ => None
+        };
+        let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
 
-                // Create the failure block and the conditional branch to it.
-                let lltarget = llblock(self, target);
-                let panic_block = self.new_block("panic");
-                if expected {
-                    bx.cond_br(cond, lltarget, panic_block.llbb());
-                } else {
-                    bx.cond_br(cond, panic_block.llbb(), lltarget);
-                }
+        if intrinsic == Some("transmute") {
+            if let Some(destination_ref) = destination.as_ref() {
+                let &(ref dest, target) = destination_ref;
+                self.codegen_transmute(&mut bx, &args[0], dest);
+                helper.funclet_br(self, &mut bx, target);
+            } else {
+                // If we are trying to transmute to an uninhabited type,
+                // it is likely there is no allotted destination. In fact,
+                // transmuting to an uninhabited type is UB, which means
+                // we can do what we like. Here, we declare that transmuting
+                // into an uninhabited type is impossible, so anything following
+                // it must be unreachable.
+                assert_eq!(bx.layout_of(sig.output()).abi, layout::Abi::Uninhabited);
+                bx.unreachable();
+            }
+            return;
+        }
 
-                // After this point, bx is the block for the call to panic.
-                bx = panic_block;
-                self.set_debug_loc(&mut bx, terminator.source_info);
+        // The "spoofed" `VaList` 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..];
+        let extra_args = extra_args.iter().map(|op_arg| {
+            let op_ty = op_arg.ty(self.mir, bx.tcx());
+            self.monomorphize(&op_ty)
+        }).collect::<Vec<_>>();
+
+        let fn_ty = match def {
+            Some(ty::InstanceDef::Virtual(..)) => {
+                bx.new_vtable(sig, &extra_args)
+            }
+            Some(ty::InstanceDef::DropGlue(_, None)) => {
+                // Empty drop glue; a no-op.
+                let &(_, target) = destination.as_ref().unwrap();
+                helper.funclet_br(self, &mut bx, target);
+                return;
+            }
+            _ => bx.new_fn_type(sig, &extra_args)
+        };
 
-                // Get the location information.
+        // Emit a panic or a no-op for `panic_if_uninhabited`.
+        if intrinsic == Some("panic_if_uninhabited") {
+            let ty = instance.unwrap().substs.type_at(0);
+            let layout = bx.layout_of(ty);
+            if layout.abi.is_uninhabited() {
                 let loc = bx.sess().source_map().lookup_char_pos(span.lo());
                 let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
                 let filename = bx.const_str_slice(filename);
                 let line = bx.const_u32(loc.line as u32);
                 let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
-                let align = tcx.data_layout.aggregate_align.abi
-                    .max(tcx.data_layout.i32_align.abi)
-                    .max(tcx.data_layout.pointer_align.abi);
-
-                // Put together the arguments to the panic entry point.
-                let (lang_item, args) = match *msg {
-                    EvalErrorKind::BoundsCheck { ref len, ref index } => {
-                        let len = self.codegen_operand(&mut bx, len).immediate();
-                        let index = self.codegen_operand(&mut bx, index).immediate();
-
-                        let file_line_col = bx.const_struct(&[filename, line, col], false);
-                        let file_line_col = bx.static_addr_of(
-                            file_line_col,
-                            align,
-                            Some("panic_bounds_check_loc")
-                        );
-                        (lang_items::PanicBoundsCheckFnLangItem,
-                         vec![file_line_col, index, len])
-                    }
-                    _ => {
-                        let str = msg.description();
-                        let msg_str = Symbol::intern(str).as_str();
-                        let msg_str = bx.const_str_slice(msg_str);
-                        let msg_file_line_col = bx.const_struct(
-                            &[msg_str, filename, line, col],
-                            false
-                        );
-                        let msg_file_line_col = bx.static_addr_of(
-                            msg_file_line_col,
-                            align,
-                            Some("panic_loc")
-                        );
-                        (lang_items::PanicFnLangItem,
-                         vec![msg_file_line_col])
-                    }
-                };
+                let align = self.cx.tcx().data_layout.aggregate_align.abi
+                    .max(self.cx.tcx().data_layout.i32_align.abi)
+                    .max(self.cx.tcx().data_layout.pointer_align.abi);
+
+                let str = format!(
+                    "Attempted to instantiate uninhabited type {}",
+                    ty
+                );
+                let msg_str = Symbol::intern(&str).as_str();
+                let msg_str = bx.const_str_slice(msg_str);
+                let msg_file_line_col = bx.const_struct(
+                    &[msg_str, filename, line, col],
+                    false,
+                );
+                let msg_file_line_col = bx.static_addr_of(
+                    msg_file_line_col,
+                    align,
+                    Some("panic_loc"),
+                );
 
                 // Obtain the panic entry point.
-                let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
+                let def_id =
+                    common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
                 let instance = ty::Instance::mono(bx.tcx(), def_id);
                 let fn_ty = bx.fn_type_of_instance(&instance);
                 let llfn = bx.get_fn(instance);
 
                 // Codegen the actual panic invoke/call.
-                do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup);
+                helper.do_call(
+                    self,
+                    &mut bx,
+                    fn_ty,
+                    llfn,
+                    &[msg_file_line_col],
+                    destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
+                    cleanup,
+                );
+            } else {
+                // a NOP
+                helper.funclet_br(self, &mut bx, destination.as_ref().unwrap().1)
             }
+            return;
+        }
 
-            mir::TerminatorKind::DropAndReplace { .. } => {
-                bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
-            }
+        // The arguments we'll be passing. Plus one to account for outptr, if used.
+        let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
+        let mut llargs = Vec::with_capacity(arg_count);
 
-            mir::TerminatorKind::Call {
-                ref func,
-                ref args,
-                ref destination,
-                cleanup,
-                from_hir_call: _
-            } => {
-                // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
-                let callee = self.codegen_operand(&mut bx, func);
-
-                let (instance, mut llfn) = match callee.layout.ty.sty {
-                    ty::FnDef(def_id, substs) => {
-                        (Some(ty::Instance::resolve(bx.tcx(),
-                                                    ty::ParamEnv::reveal_all(),
-                                                    def_id,
-                                                    substs).unwrap()),
-                         None)
-                    }
-                    ty::FnPtr(_) => {
-                        (None, Some(callee.immediate()))
-                    }
-                    _ => bug!("{} is not callable", callee.layout.ty)
-                };
-                let def = instance.map(|i| i.def);
-                let sig = callee.layout.ty.fn_sig(bx.tcx());
-                let sig = bx.tcx().normalize_erasing_late_bound_regions(
-                    ty::ParamEnv::reveal_all(),
-                    &sig,
-                );
-                let abi = sig.abi;
+        // Prepare the return value destination
+        let ret_dest = if let Some((ref dest, _)) = *destination {
+            let is_intrinsic = intrinsic.is_some();
+            self.make_return_dest(&mut bx, dest, &fn_ty.ret, &mut llargs,
+                                  is_intrinsic)
+        } else {
+            ReturnDest::Nothing
+        };
 
-                // Handle intrinsics old codegen wants Expr's for, ourselves.
-                let intrinsic = match def {
-                    Some(ty::InstanceDef::Intrinsic(def_id))
-                        => Some(bx.tcx().item_name(def_id).as_str()),
-                    _ => None
-                };
-                let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
+        if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
+            let dest = match ret_dest {
+                _ if fn_ty.ret.is_indirect() => llargs[0],
+                ReturnDest::Nothing =>
+                    bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret))),
+                ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) =>
+                    dst.llval,
+                ReturnDest::DirectOperand(_) =>
+                    bug!("Cannot use direct operand with an intrinsic call"),
+            };
 
-                if intrinsic == Some("transmute") {
-                    if let Some(destination_ref) = destination.as_ref() {
-                        let &(ref dest, target) = destination_ref;
-                        self.codegen_transmute(&mut bx, &args[0], dest);
-                        funclet_br(self, &mut bx, target);
-                    } else {
-                        // If we are trying to transmute to an uninhabited type,
-                        // it is likely there is no allotted destination. In fact,
-                        // transmuting to an uninhabited type is UB, which means
-                        // we can do what we like. Here, we declare that transmuting
-                        // into an uninhabited type is impossible, so anything following
-                        // it must be unreachable.
-                        assert_eq!(bx.layout_of(sig.output()).abi, layout::Abi::Uninhabited);
-                        bx.unreachable();
+            let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
+                // The indices passed to simd_shuffle* in the
+                // third argument must be constant. This is
+                // checked by const-qualification, which also
+                // promotes any complex rvalues to constants.
+                if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") {
+                    match *arg {
+                        // The shuffle array argument is usually not an explicit constant,
+                        // but specified directly in the code. This means it gets promoted
+                        // and we can then extract the value by evaluating the promoted.
+                        mir::Operand::Copy(mir::Place::Promoted(box(index, ty))) |
+                        mir::Operand::Move(mir::Place::Promoted(box(index, ty))) => {
+                            let param_env = ty::ParamEnv::reveal_all();
+                            let cid = mir::interpret::GlobalId {
+                                instance: self.instance,
+                                promoted: Some(index),
+                            };
+                            let c = bx.tcx().const_eval(param_env.and(cid));
+                            let (llval, ty) = self.simd_shuffle_indices(
+                                &bx,
+                                terminator.source_info.span,
+                                ty,
+                                c,
+                            );
+                            return OperandRef {
+                                val: Immediate(llval),
+                                layout: bx.layout_of(ty),
+                            };
+
+                        }
+                        mir::Operand::Copy(_) |
+                        mir::Operand::Move(_) => {
+                            span_bug!(span, "shuffle indices must be constant");
+                        }
+                        mir::Operand::Constant(ref constant) => {
+                            let c = self.eval_mir_constant(&bx, constant);
+                            let (llval, ty) = self.simd_shuffle_indices(
+                                &bx,
+                                constant.span,
+                                constant.ty,
+                                c,
+                            );
+                            return OperandRef {
+                                val: Immediate(llval),
+                                layout: bx.layout_of(ty)
+                            };
+                        }
                     }
-                    return;
                 }
 
-                let extra_args = &args[sig.inputs().len()..];
-                let extra_args = extra_args.iter().map(|op_arg| {
-                    let op_ty = op_arg.ty(self.mir, bx.tcx());
-                    self.monomorphize(&op_ty)
-                }).collect::<Vec<_>>();
+                self.codegen_operand(&mut bx, arg)
+            }).collect();
 
-                let fn_ty = match def {
-                    Some(ty::InstanceDef::Virtual(..)) => {
-                        bx.new_vtable(sig, &extra_args)
-                    }
-                    Some(ty::InstanceDef::DropGlue(_, None)) => {
-                        // empty drop glue - a nop.
-                        let &(_, target) = destination.as_ref().unwrap();
-                        funclet_br(self, &mut bx, target);
-                        return;
-                    }
-                    _ => bx.new_fn_type(sig, &extra_args)
+
+            let callee_ty = instance.as_ref().unwrap().ty(bx.tcx());
+            bx.codegen_intrinsic_call(callee_ty, &fn_ty, &args, dest,
+                                      terminator.source_info.span);
+
+            if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
+                self.store_return(&mut bx, ret_dest, &fn_ty.ret, dst.llval);
+            }
+
+            if let Some((_, target)) = *destination {
+                helper.funclet_br(self, &mut bx, target);
+            } else {
+                bx.unreachable();
+            }
+
+            return;
+        }
+
+        // Split the rust-call tupled arguments off.
+        let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() {
+            let (tup, args) = args.split_last().unwrap();
+            (args, Some(tup))
+        } else {
+            (&args[..], None)
+        };
+
+        // Useful determining if the current argument is the "spoofed" `VaList`
+        let last_arg_idx = if sig.inputs().is_empty() {
+            None
+        } else {
+            Some(sig.inputs().len() - 1)
+        };
+        '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
+            // 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| x == i).unwrap_or(false) {
+                let layout = match self.cx.tcx().lang_items().va_list() {
+                    Some(did) => bx.cx().layout_of(bx.tcx().type_of(did)),
+                    None => bug!("`va_list` language item required for C-variadics"),
                 };
+                let op = OperandRef {
+                    val: OperandValue::Immediate(
+                        bx.cx().const_undef(bx.cx().immediate_backend_type(layout)
+                    )),
+                    layout: layout,
+                };
+                self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
+                if i + 1 < fn_ty.args.len() {
+                    i + 1
+                } else {
+                    break 'make_args
+                }
+            } else {
+                i
+            };
+            let mut op = self.codegen_operand(&mut bx, arg);
+
+            if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
+                if let Pair(..) = op.val {
+                    // In the case of Rc<Self>, we need to explicitly pass a
+                    // *mut RcBox<Self> with a Scalar (not ScalarPair) ABI. This is a hack
+                    // that is understood elsewhere in the compiler as a method on
+                    // `dyn Trait`.
+                    // To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until
+                    // we get a value of a built-in pointer type
+                    'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
+                                    && !op.layout.ty.is_region_ptr()
+                    {
+                        'iter_fields: for i in 0..op.layout.fields.count() {
+                            let field = op.extract_field(&mut bx, i);
+                            if !field.layout.is_zst() {
+                                // we found the one non-zero-sized field that is allowed
+                                // now find *its* non-zero-sized field, or stop if it's a
+                                // pointer
+                                op = field;
+                                continue 'descend_newtypes
+                            }
+                        }
 
-                // emit a panic or a NOP for `panic_if_uninhabited`
-                if intrinsic == Some("panic_if_uninhabited") {
-                    let ty = instance.unwrap().substs.type_at(0);
-                    let layout = bx.layout_of(ty);
-                    if layout.abi.is_uninhabited() {
-                        let loc = bx.sess().source_map().lookup_char_pos(span.lo());
-                        let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
-                        let filename = bx.const_str_slice(filename);
-                        let line = bx.const_u32(loc.line as u32);
-                        let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
-                        let align = tcx.data_layout.aggregate_align.abi
-                            .max(tcx.data_layout.i32_align.abi)
-                            .max(tcx.data_layout.pointer_align.abi);
-
-                        let str = format!(
-                            "Attempted to instantiate uninhabited type {}",
-                            ty
-                        );
-                        let msg_str = Symbol::intern(&str).as_str();
-                        let msg_str = bx.const_str_slice(msg_str);
-                        let msg_file_line_col = bx.const_struct(
-                            &[msg_str, filename, line, col],
-                            false,
-                        );
-                        let msg_file_line_col = bx.static_addr_of(
-                            msg_file_line_col,
-                            align,
-                            Some("panic_loc"),
-                        );
-
-                        // Obtain the panic entry point.
-                        let def_id =
-                            common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
-                        let instance = ty::Instance::mono(bx.tcx(), def_id);
-                        let fn_ty = bx.fn_type_of_instance(&instance);
-                        let llfn = bx.get_fn(instance);
-
-                        // Codegen the actual panic invoke/call.
-                        do_call(
-                            self,
-                            &mut bx,
-                            fn_ty,
-                            llfn,
-                            &[msg_file_line_col],
-                            destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
-                            cleanup,
-                        );
-                    } else {
-                        // a NOP
-                        funclet_br(self, &mut bx, destination.as_ref().unwrap().1);
+                        span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
                     }
-                    return;
+
+                    // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
+                    // data pointer and vtable. Look up the method in the vtable, and pass
+                    // the data pointer as the first argument
+                    match op.val {
+                        Pair(data_ptr, meta) => {
+                            llfn = Some(meth::VirtualIndex::from_index(idx)
+                                .get_fn(&mut bx, meta, &fn_ty));
+                            llargs.push(data_ptr);
+                            continue 'make_args
+                        }
+                        other => bug!("expected a Pair, got {:?}", other),
+                    }
+                } else if let Ref(data_ptr, Some(meta), _) = op.val {
+                    // by-value dynamic dispatch
+                    llfn = Some(meth::VirtualIndex::from_index(idx)
+                        .get_fn(&mut bx, meta, &fn_ty));
+                    llargs.push(data_ptr);
+                    continue;
+                } else {
+                    span_bug!(span, "can't codegen a virtual call on {:?}", op);
                 }
+            }
 
-                // The arguments we'll be passing. Plus one to account for outptr, if used.
-                let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
-                let mut llargs = Vec::with_capacity(arg_count);
+            // The callee needs to own the argument memory if we pass it
+            // by-ref, so make a local copy of non-immediate constants.
+            match (arg, op.val) {
+                (&mir::Operand::Copy(_), Ref(_, None, _)) |
+                (&mir::Operand::Constant(_), Ref(_, None, _)) => {
+                    let tmp = PlaceRef::alloca(&mut bx, op.layout, "const");
+                    op.val.store(&mut bx, tmp);
+                    op.val = Ref(tmp.llval, None, tmp.align);
+                }
+                _ => {}
+            }
 
-                // Prepare the return value destination
-                let ret_dest = if let Some((ref dest, _)) = *destination {
-                    let is_intrinsic = intrinsic.is_some();
-                    self.make_return_dest(&mut bx, dest, &fn_ty.ret, &mut llargs,
-                                          is_intrinsic)
-                } else {
-                    ReturnDest::Nothing
-                };
+            self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
+        }
+        if let Some(tup) = untuple {
+            self.codegen_arguments_untupled(&mut bx, tup, &mut llargs,
+                &fn_ty.args[first_args.len()..])
+        }
 
-                if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
-                    let dest = match ret_dest {
-                        _ if fn_ty.ret.is_indirect() => llargs[0],
-                        ReturnDest::Nothing => {
-                            bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret)))
-                        }
-                        ReturnDest::IndirectOperand(dst, _) |
-                        ReturnDest::Store(dst) => dst.llval,
-                        ReturnDest::DirectOperand(_) =>
-                            bug!("Cannot use direct operand with an intrinsic call")
-                    };
+        let fn_ptr = match (llfn, instance) {
+            (Some(llfn), _) => llfn,
+            (None, Some(instance)) => bx.get_fn(instance),
+            _ => span_bug!(span, "no llfn for call"),
+        };
 
-                    let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
-                        // The indices passed to simd_shuffle* in the
-                        // third argument must be constant. This is
-                        // checked by const-qualification, which also
-                        // promotes any complex rvalues to constants.
-                        if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") {
-                            match *arg {
-                                // The shuffle array argument is usually not an explicit constant,
-                                // but specified directly in the code. This means it gets promoted
-                                // and we can then extract the value by evaluating the promoted.
-                                mir::Operand::Copy(mir::Place::Promoted(box(index, ty))) |
-                                mir::Operand::Move(mir::Place::Promoted(box(index, ty))) => {
-                                    let param_env = ty::ParamEnv::reveal_all();
-                                    let cid = mir::interpret::GlobalId {
-                                        instance: self.instance,
-                                        promoted: Some(index),
-                                    };
-                                    let c = bx.tcx().const_eval(param_env.and(cid));
-                                    let (llval, ty) = self.simd_shuffle_indices(
-                                        &bx,
-                                        terminator.source_info.span,
-                                        ty,
-                                        c,
-                                    );
-                                    return OperandRef {
-                                        val: Immediate(llval),
-                                        layout: bx.layout_of(ty),
-                                    };
-
-                                },
-                                mir::Operand::Copy(_) |
-                                mir::Operand::Move(_) => {
-                                    span_bug!(span, "shuffle indices must be constant");
-                                }
-                                mir::Operand::Constant(ref constant) => {
-                                    let c = self.eval_mir_constant(&bx, constant);
-                                    let (llval, ty) = self.simd_shuffle_indices(
-                                        &bx,
-                                        constant.span,
-                                        constant.ty,
-                                        c,
-                                    );
-                                    return OperandRef {
-                                        val: Immediate(llval),
-                                        layout: bx.layout_of(ty)
-                                    };
-                                }
-                            }
-                        }
+        helper.do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
+                       destination.as_ref().map(|&(_, target)| (ret_dest, target)),
+                       cleanup);
+    }
+}
 
-                        self.codegen_operand(&mut bx, arg)
-                    }).collect();
+impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
+    pub fn codegen_block(
+        &mut self,
+        bb: mir::BasicBlock,
+    ) {
+        let mut bx = self.build_block(bb);
+        let data = &self.mir[bb];
 
+        debug!("codegen_block({:?}={:?})", bb, data);
 
-                    let callee_ty = instance.as_ref().unwrap().ty(bx.tcx());
-                    bx.codegen_intrinsic_call(callee_ty, &fn_ty, &args, dest,
-                                               terminator.source_info.span);
+        for statement in &data.statements {
+            bx = self.codegen_statement(bx, statement);
+        }
 
-                    if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
-                        self.store_return(&mut bx, ret_dest, &fn_ty.ret, dst.llval);
-                    }
+        self.codegen_terminator(bx, bb, data.terminator());
+    }
 
-                    if let Some((_, target)) = *destination {
-                        funclet_br(self, &mut bx, target);
-                    } else {
-                        bx.unreachable();
-                    }
+    fn codegen_terminator(
+        &mut self,
+        mut bx: Bx,
+        bb: mir::BasicBlock,
+        terminator: &mir::Terminator<'tcx>
+    ) {
+        debug!("codegen_terminator: {:?}", terminator);
 
-                    return;
-                }
+        // Create the cleanup bundle, if needed.
+        let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
+        let helper = TerminatorCodegenHelper {
+            bb: &bb, terminator, funclet_bb
+        };
 
-                // Split the rust-call tupled arguments off.
-                let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() {
-                    let (tup, args) = args.split_last().unwrap();
-                    (args, Some(tup))
-                } else {
-                    (&args[..], None)
-                };
+        self.set_debug_loc(&mut bx, terminator.source_info);
+        match terminator.kind {
+            mir::TerminatorKind::Resume => {
+                self.codegen_resume_terminator(helper, bx)
+            }
 
-                'make_args: for (i, arg) in first_args.iter().enumerate() {
-                    let mut op = self.codegen_operand(&mut bx, arg);
-
-                    if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
-                        if let Pair(..) = op.val {
-                            // In the case of Rc<Self>, we need to explicitly pass a
-                            // *mut RcBox<Self> with a Scalar (not ScalarPair) ABI. This is a hack
-                            // that is understood elsewhere in the compiler as a method on
-                            // `dyn Trait`.
-                            // To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until
-                            // we get a value of a built-in pointer type
-                            'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
-                                            && !op.layout.ty.is_region_ptr()
-                            {
-                                'iter_fields: for i in 0..op.layout.fields.count() {
-                                    let field = op.extract_field(&mut bx, i);
-                                    if !field.layout.is_zst() {
-                                        // we found the one non-zero-sized field that is allowed
-                                        // now find *its* non-zero-sized field, or stop if it's a
-                                        // pointer
-                                        op = field;
-                                        continue 'descend_newtypes
-                                    }
-                                }
-
-                                span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
-                            }
+            mir::TerminatorKind::Abort => {
+                bx.abort();
+                bx.unreachable();
+            }
 
-                            // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
-                            // data pointer and vtable. Look up the method in the vtable, and pass
-                            // the data pointer as the first argument
-                            match op.val {
-                                Pair(data_ptr, meta) => {
-                                    llfn = Some(meth::VirtualIndex::from_index(idx)
-                                        .get_fn(&mut bx, meta, &fn_ty));
-                                    llargs.push(data_ptr);
-                                    continue 'make_args
-                                }
-                                other => bug!("expected a Pair, got {:?}", other)
-                            }
-                        } else if let Ref(data_ptr, Some(meta), _) = op.val {
-                            // by-value dynamic dispatch
-                            llfn = Some(meth::VirtualIndex::from_index(idx)
-                                .get_fn(&mut bx, meta, &fn_ty));
-                            llargs.push(data_ptr);
-                            continue;
-                        } else {
-                            span_bug!(span, "can't codegen a virtual call on {:?}", op);
-                        }
-                    }
+            mir::TerminatorKind::Goto { target } => {
+                helper.funclet_br(self, &mut bx, target);
+            }
 
-                    // The callee needs to own the argument memory if we pass it
-                    // by-ref, so make a local copy of non-immediate constants.
-                    match (arg, op.val) {
-                        (&mir::Operand::Copy(_), Ref(_, None, _)) |
-                        (&mir::Operand::Constant(_), Ref(_, None, _)) => {
-                            let tmp = PlaceRef::alloca(&mut bx, op.layout, "const");
-                            op.val.store(&mut bx, tmp);
-                            op.val = Ref(tmp.llval, None, tmp.align);
-                        }
-                        _ => {}
-                    }
+            mir::TerminatorKind::SwitchInt {
+                ref discr, switch_ty, ref values, ref targets
+            } => {
+                self.codegen_switchint_terminator(helper, bx, discr, switch_ty,
+                                                  values, targets);
+            }
 
-                    self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
-                }
-                if let Some(tup) = untuple {
-                    self.codegen_arguments_untupled(&mut bx, tup, &mut llargs,
-                        &fn_ty.args[first_args.len()..])
-                }
+            mir::TerminatorKind::Return => {
+                self.codegen_return_terminator(bx);
+            }
 
-                let fn_ptr = match (llfn, instance) {
-                    (Some(llfn), _) => llfn,
-                    (None, Some(instance)) => bx.get_fn(instance),
-                    _ => span_bug!(span, "no llfn for call"),
-                };
+            mir::TerminatorKind::Unreachable => {
+                bx.unreachable();
+            }
 
-                do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
-                        destination.as_ref().map(|&(_, target)| (ret_dest, target)),
-                        cleanup);
+            mir::TerminatorKind::Drop { ref location, target, unwind } => {
+                self.codegen_drop_terminator(helper, bx, location, target, unwind);
+            }
+
+            mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
+                self.codegen_assert_terminator(helper, bx, terminator, cond,
+                                               expected, msg, target, cleanup);
+            }
+
+            mir::TerminatorKind::DropAndReplace { .. } => {
+                bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
+            }
+
+            mir::TerminatorKind::Call {
+                ref func,
+                ref args,
+                ref destination,
+                cleanup,
+                from_hir_call: _
+            } => {
+                self.codegen_call_terminator(helper, bx, terminator, func,
+                                             args, destination, cleanup);
             }
             mir::TerminatorKind::GeneratorDrop |
             mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"),
index e1528921a596218f4adc04f68cc14648bf95f1a4..dc77d4673cd2a3594b3e9d4865925eb04a3dc7c4 100644 (file)
@@ -5,7 +5,7 @@
 use rustc::ty::subst::SubstsRef;
 use rustc::session::config::DebugInfo;
 use rustc_mir::monomorphize::Instance;
-use rustc_target::abi::call::{FnType, PassMode};
+use rustc_target::abi::call::{FnType, PassMode, IgnoreMode};
 use crate::base;
 use crate::debuginfo::{self, VariableAccess, VariableKind, FunctionDebugContext};
 use crate::traits::*;
@@ -86,6 +86,10 @@ pub struct FunctionCx<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> {
 
     /// If this function is being monomorphized, this contains the type substitutions used.
     param_substs: SubstsRef<'tcx>,
+
+    /// If this function is a C-variadic function, this contains the `PlaceRef` of the
+    /// "spoofed" `VaList`.
+    va_list_ref: Option<PlaceRef<'tcx, Bx::Value>>,
 }
 
 impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
@@ -246,13 +250,18 @@ pub fn codegen_mir<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
             assert!(!instance.substs.needs_infer());
             instance.substs
         },
+        va_list_ref: None,
     };
 
     let memory_locals = analyze::non_ssa_locals(&fx);
 
     // Allocate variable and temp allocas
     fx.locals = {
-        let args = arg_local_refs(&mut bx, &fx, &fx.scopes, &memory_locals);
+        // FIXME(dlrobertson): This is ugly. Find a better way of getting the `PlaceRef` or
+        // `LocalRef` from `arg_local_refs`
+        let mut va_list_ref = None;
+        let args = arg_local_refs(&mut bx, &fx, &fx.scopes, &memory_locals, &mut va_list_ref);
+        fx.va_list_ref = va_list_ref;
 
         let mut allocate_local = |local| {
             let decl = &mir.local_decls[local];
@@ -433,6 +442,7 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
         debuginfo::MirDebugScope<Bx::DIScope>
     >,
     memory_locals: &BitSet<mir::Local>,
+    va_list_ref: &mut Option<PlaceRef<'tcx, Bx::Value>>,
 ) -> Vec<LocalRef<'tcx, Bx::Value>> {
     let mir = fx.mir;
     let tcx = fx.cx.tcx();
@@ -447,6 +457,15 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
         None
     };
 
+    // Store the index of the last argument. This is used to
+    // call va_start on the va_list instead of attempting
+    // to store_fn_arg.
+    let last_arg_idx = if fx.fn_ty.args.is_empty() {
+        None
+    } else {
+        Some(fx.fn_ty.args.len() - 1)
+    };
+
     mir.args_iter().enumerate().map(|(arg_index, local)| {
         let arg_decl = &mir.local_decls[local];
 
@@ -510,9 +529,16 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
             // of putting everything in allocas just so we can use llvm.dbg.declare.
             let local = |op| LocalRef::Operand(Some(op));
             match arg.mode {
-                PassMode::Ignore => {
+                PassMode::Ignore(IgnoreMode::Zst) => {
                     return local(OperandRef::new_zst(bx.cx(), arg.layout));
                 }
+                PassMode::Ignore(IgnoreMode::CVarArgs) => {
+                    let backend_type = bx.cx().immediate_backend_type(arg.layout);
+                    return local(OperandRef {
+                        val: OperandValue::Immediate(bx.cx().const_undef(backend_type)),
+                        layout: arg.layout,
+                    });
+                }
                 PassMode::Direct(_) => {
                     let llarg = bx.get_param(bx.llfn(), llarg_idx as c_uint);
                     bx.set_value_name(llarg, &name);
@@ -559,9 +585,35 @@ 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);
-            bx.store_fn_arg(arg, &mut llarg_idx, tmp);
-            tmp
+            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")
+                };
+                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`.
+                        bx.va_start(tmp.llval);
+                        *va_list_ref = Some(tmp);
+                        tmp
+                    }
+                    _ => bug!("improperly constructed `va_list` lang item"),
+                }
+            } else {
+                let tmp = PlaceRef::alloca(bx, arg.layout, &name);
+                bx.store_fn_arg(arg, &mut llarg_idx, tmp);
+                tmp
+            }
         };
         arg_scope.map(|scope| {
             // Is this a regular argument?
index 3cd0c39d4139a9c823375307c6f32c1f5c64a0d0..cd5278989778ffac031c05d1b92e47883a5a808f 100644 (file)
@@ -20,4 +20,10 @@ fn codegen_intrinsic_call(
     fn abort(&mut self);
     fn assume(&mut self, val: Self::Value);
     fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
+    /// Trait method used to inject `va_start` on the "spoofed" `VaList` in
+    /// 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
+    /// Rust defined C-variadic functions return.
+    fn va_end(&mut self, val: Self::Value) -> Self::Value;
 }
index a56c3215f9d69f4b10ff8ecad840556201b808d8..35489ab42e73038c056cea9f25597d1903155bca 100644 (file)
@@ -766,8 +766,15 @@ fn check_foreign_fn(&mut self, id: ast::NodeId, decl: &hir::FnDecl) {
         let def_id = self.cx.tcx.hir().local_def_id(id);
         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
+            // of inputs.
+            &sig.inputs()[..sig.inputs().len() - 1]
+        } else {
+            &sig.inputs()[..]
+        };
 
-        for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) {
+        for (input_ty, input_hir) in inputs.iter().zip(&decl.inputs) {
             self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty);
         }
 
index 4202d10aa63d0cc790c1d22ad3da7a2fc0a0208b..df035aab54c9cdb042b7e04268e67b11615c9678 100644 (file)
@@ -1602,10 +1602,17 @@ fn check_call_inputs(
         from_hir_call: bool,
     ) {
         debug!("check_call_inputs({:?}, {:?})", sig, args);
-        if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.variadic) {
+        // Do not count the `VaList` argument as a "true" argument to
+        // a C-variadic function.
+        let inputs = if sig.c_variadic {
+            &sig.inputs()[..sig.inputs().len() - 1]
+        } else {
+            &sig.inputs()[..]
+        };
+        if args.len() < inputs.len() || (args.len() > inputs.len() && !sig.c_variadic) {
             span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
         }
-        for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() {
+        for (n, (fn_arg, op_arg)) in inputs.iter().zip(args).enumerate() {
             let op_arg_ty = op_arg.ty(mir, self.tcx());
             let category = if from_hir_call {
                 ConstraintCategory::CallArgument
index a26a1a7861eb0146ef84c80ba6232a80ed990180..059af2dbba944710f23d5e815e6c3d451acf35c8 100644 (file)
@@ -353,7 +353,7 @@ pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String, debug: bool) {
                     output.pop();
                 }
 
-                if sig.variadic {
+                if sig.c_variadic {
                     if !sig.inputs().is_empty() {
                         output.push_str(", ...");
                     } else {
index 52f3a2077702192a22ac8c8419b0145952cb665e..64a2c92d04dbd9289fe7d872843f8c60965599f0 100644 (file)
@@ -190,6 +190,7 @@ fn make(&self, offset: usize, _parent_id: Option<NodeId>, scx: &SaveContext<'_,
                 Ok(replace_text(nested, text))
             }
             ast::TyKind::Never => Ok(text_sig("!".to_owned())),
+            ast::TyKind::CVarArgs => Ok(text_sig("...".to_owned())),
             ast::TyKind::Tup(ref ts) => {
                 let mut text = "(".to_owned();
                 let mut defs = vec![];
index 52d7f3ac3dcbf5363b2e9c09de79ad2284150dd0..e3fee8e5700c196bb62e68d420a0b481e3fa7fd8 100644 (file)
@@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
     // `extern "aapcs"`, then we must use the VFP registers for homogeneous aggregates.
     let vfp = cx.target_spec().llvm_target.ends_with("hf")
         && fty.conv != Conv::ArmAapcs
-        && !fty.variadic;
+        && !fty.c_variadic;
 
     if !fty.ret.is_ignore() {
         classify_ret_ty(cx, &mut fty.ret, vfp);
index 411eb192d902bda8c39009af308833628018e8f4..fbbd120f934be845fc21ac5e5a5c2245699cdc59 100644 (file)
 mod x86_win64;
 mod wasm32;
 
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum IgnoreMode {
+    /// C-variadic arguments.
+    CVarArgs,
+    /// A zero-sized type.
+    Zst,
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum PassMode {
-    /// Ignore the argument (useful for empty struct).
-    Ignore,
+    /// Ignore the argument (useful for empty structs and C-variadic args).
+    Ignore(IgnoreMode),
     /// Pass the argument directly.
     Direct(ArgAttributes),
     /// Pass a pair's elements directly in two arguments.
@@ -481,7 +489,10 @@ pub fn is_unsized_indirect(&self) -> bool {
     }
 
     pub fn is_ignore(&self) -> bool {
-        self.mode == PassMode::Ignore
+        match self.mode {
+            PassMode::Ignore(_) => true,
+            _ => false
+        }
     }
 }
 
@@ -520,7 +531,7 @@ pub struct FnType<'a, Ty> {
     /// LLVM return type.
     pub ret: ArgType<'a, Ty>,
 
-    pub variadic: bool,
+    pub c_variadic: bool,
 
     pub conv: Conv,
 }
index 2e809571ab18b60fc87e40dc08961133f9133366..6ca3ce88bd6eb37a9aa9aaadf0d35c23812bccfe 100644 (file)
@@ -88,7 +88,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla
 
         for arg in &mut fty.args {
             let attrs = match arg.mode {
-                PassMode::Ignore |
+                PassMode::Ignore(_) |
                 PassMode::Indirect(_, None) => continue,
                 PassMode::Direct(ref mut attrs) => attrs,
                 PassMode::Pair(..) |
index 4d8a67ca638634b69999b0b306749ab1958ccc3a..3f88d0e08b46ae29eb286880dcdb5935fbe80df2 100644 (file)
@@ -105,7 +105,7 @@ fn assemble_builtin_sized_impls<'tcx>(
             let fn_ptr = generic_types::fn_ptr(
                 tcx,
                 fn_ptr.inputs_and_output.len(),
-                fn_ptr.variadic,
+                fn_ptr.c_variadic,
                 fn_ptr.unsafety,
                 fn_ptr.abi
             );
@@ -190,11 +190,11 @@ fn wf_clause_for_raw_ptr<'tcx>(
 fn wf_clause_for_fn_ptr<'tcx>(
     tcx: ty::TyCtxt<'_, '_, 'tcx>,
     arity_and_output: usize,
-    variadic: bool,
+    c_variadic: bool,
     unsafety: hir::Unsafety,
     abi: abi::Abi
 ) -> Clauses<'tcx> {
-    let fn_ptr = generic_types::fn_ptr(tcx, arity_and_output, variadic, unsafety, abi);
+    let fn_ptr = generic_types::fn_ptr(tcx, arity_and_output, c_variadic, unsafety, abi);
 
     let wf_clause = ProgramClause {
         goal: DomainGoal::WellFormed(WellFormed::Ty(fn_ptr)),
@@ -503,7 +503,7 @@ pub(super) fn program_clauses_impl(
                         wf_clause_for_fn_ptr(
                             self.infcx.tcx,
                             fn_ptr.inputs_and_output.len(),
-                            fn_ptr.variadic,
+                            fn_ptr.c_variadic,
                             fn_ptr.unsafety,
                             fn_ptr.abi
                         )
index 634c024b064660f840b593d153efa68ce050fe5d..f2ce9631f35aba590620b862ac87efc73c397523 100644 (file)
@@ -24,7 +24,7 @@
 crate fn fn_ptr(
     tcx: ty::TyCtxt<'_, '_, 'tcx>,
     arity_and_output: usize,
-    variadic: bool,
+    c_variadic: bool,
     unsafety: hir::Unsafety,
     abi: abi::Abi
 ) -> Ty<'tcx> {
@@ -37,7 +37,7 @@
 
     let fn_sig = ty::Binder::bind(ty::FnSig {
         inputs_and_output,
-        variadic,
+        c_variadic,
         unsafety,
         abi,
     });
index a81134cb07ef40efc8133f42153b8e7b863f504d..4a88922b50ee2b3fd4a728620d7c60409763aec8 100644 (file)
@@ -18,7 +18,7 @@
 use rustc::ty::wf::object_region_bounds;
 use rustc_data_structures::sync::Lrc;
 use rustc_target::spec::abi;
-use crate::require_c_abi_if_variadic;
+use crate::require_c_abi_if_c_variadic;
 use smallvec::SmallVec;
 use syntax::ast;
 use syntax::feature_gate::{GateIssue, emit_feature_err};
@@ -1769,7 +1769,7 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> {
                 tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(&t)))
             }
             hir::TyKind::BareFn(ref bf) => {
-                require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span);
+                require_c_abi_if_c_variadic(tcx, &bf.decl, bf.abi, ast_ty.span);
                 tcx.mk_fn_ptr(self.ty_of_fn(bf.unsafety, bf.abi, &bf.decl))
             }
             hir::TyKind::TraitObject(ref bounds, ref lifetime) => {
@@ -1823,6 +1823,15 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> {
             hir::TyKind::Err => {
                 tcx.types.err
             }
+            hir::TyKind::CVarArgs(lt) => {
+                let va_list_did = match tcx.lang_items().va_list() {
+                    Some(did) => did,
+                    None => span_bug!(ast_ty.span,
+                                      "`va_list` lang item required for variadics"),
+                };
+                let region = self.ast_region_to_region(&lt, None);
+                tcx.type_of(va_list_did).subst(tcx, &[region.into()])
+            }
         };
 
         self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
@@ -1905,7 +1914,7 @@ pub fn ty_of_fn(&self,
         let bare_fn_ty = ty::Binder::bind(tcx.mk_fn_sig(
             input_tys,
             output_ty,
-            decl.variadic,
+            decl.c_variadic,
             unsafety,
             abi
         ));
index 7bf7d82540655325bca06a4f2feafec6e10b228a..0a4c0eb3aff72c1b25b5a34fb47dee71ec71c67f 100644 (file)
@@ -368,20 +368,30 @@ fn confirm_builtin_call(
             .0;
         let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig);
 
+        let inputs = if fn_sig.c_variadic {
+            if fn_sig.inputs().len() > 1 {
+                &fn_sig.inputs()[..fn_sig.inputs().len() - 1]
+            } else {
+                span_bug!(call_expr.span,
+                          "C-variadic functions are only valid with one or more fixed arguments");
+            }
+        } else {
+            &fn_sig.inputs()[..]
+        };
         // Call the generic checker.
         let expected_arg_tys = self.expected_inputs_for_expected_output(
             call_expr.span,
             expected,
             fn_sig.output(),
-            fn_sig.inputs(),
+            inputs,
         );
         self.check_argument_types(
             call_expr.span,
             call_expr.span,
-            fn_sig.inputs(),
+            inputs,
             &expected_arg_tys[..],
             arg_exprs,
-            fn_sig.variadic,
+            fn_sig.c_variadic,
             TupleArgumentsFlag::DontTupleArguments,
             def_span,
         );
@@ -414,7 +424,7 @@ fn confirm_deferred_closure_call(
             fn_sig.inputs(),
             &expected_arg_tys,
             arg_exprs,
-            fn_sig.variadic,
+            fn_sig.c_variadic,
             TupleArgumentsFlag::TupleArguments,
             None,
         );
index 2a4b17a639956c5b7dbccd783b3eaee8c0abe810..db89b32be7b68d109acbc19f557bdcb747d495de 100644 (file)
@@ -141,7 +141,7 @@ fn check_closure(
             self.tcx.mk_fn_sig(
                 iter::once(self.tcx.intern_tup(sig.inputs())),
                 sig.output(),
-                sig.variadic,
+                sig.c_variadic,
                 sig.unsafety,
                 sig.abi,
             )
@@ -386,7 +386,7 @@ fn sig_of_closure_with_expectation(
         // Watch out for some surprises and just ignore the
         // expectation if things don't see to match up with what we
         // expect.
-        if expected_sig.sig.variadic != decl.variadic {
+        if expected_sig.sig.c_variadic != decl.c_variadic {
             return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
         } else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 {
             return self.sig_of_closure_with_mismatched_number_of_arguments(
@@ -404,7 +404,7 @@ fn sig_of_closure_with_expectation(
         let bound_sig = ty::Binder::bind(self.tcx.mk_fn_sig(
             expected_sig.sig.inputs().iter().cloned(),
             expected_sig.sig.output(),
-            decl.variadic,
+            decl.c_variadic,
             hir::Unsafety::Normal,
             Abi::RustCall,
         ));
@@ -586,7 +586,7 @@ fn supplied_sig_of_closure(
         let result = ty::Binder::bind(self.tcx.mk_fn_sig(
             supplied_arguments,
             supplied_return,
-            decl.variadic,
+            decl.c_variadic,
             hir::Unsafety::Normal,
             Abi::RustCall,
         ));
@@ -621,7 +621,7 @@ fn error_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> {
         let result = ty::Binder::bind(self.tcx.mk_fn_sig(
             supplied_arguments,
             self.tcx.types.err,
-            decl.variadic,
+            decl.c_variadic,
             hir::Unsafety::Normal,
             Abi::RustCall,
         ));
index 3b174b55f2ba02c0e0934a81b1658897896a05f8..924ced2e2a3c77c1cd76729e63734ebd2384fd82 100644 (file)
@@ -337,7 +337,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             "va_start" | "va_end" => {
                 match mk_va_list_ty() {
                     Some(va_list_ty) => (0, vec![va_list_ty], tcx.mk_unit()),
-                    None => bug!("va_list lang_item must be defined to use va_list intrinsics")
+                    None => bug!("`va_list` language item needed for C-variadic intrinsics")
                 }
             }
 
@@ -364,14 +364,14 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         };
                         (0, vec![tcx.mk_imm_ref(tcx.mk_region(env_region), va_list_ty)], ret_ty)
                     }
-                    None => bug!("va_list lang_item must be defined to use va_list intrinsics")
+                    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)),
-                    None => bug!("va_list lang_item must be defined to use va_list intrinsics")
+                    None => bug!("`va_list` language item needed for C-variadic intrinsics")
                 }
             }
 
index 202d8bec4e9578551405610c0e35e44694f3ad19..3a430f77b6c64b8373847c0f5224af35d5487250 100644 (file)
 use std::ops::{self, Deref};
 use std::slice;
 
-use crate::require_c_abi_if_variadic;
+use crate::require_c_abi_if_c_variadic;
 use crate::session::{CompileIncomplete, Session};
 use crate::session::config::EntryFnType;
 use crate::TypeAndSubsts;
@@ -1072,7 +1072,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
     fn_sig = fcx.tcx.mk_fn_sig(
         fn_sig.inputs().iter().cloned(),
         revealed_ret_ty,
-        fn_sig.variadic,
+        fn_sig.c_variadic,
         fn_sig.unsafety,
         fn_sig.abi
     );
@@ -1426,7 +1426,7 @@ pub fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Ite
                     }
 
                     if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.node {
-                        require_c_abi_if_variadic(tcx, fn_decl, m.abi, item.span);
+                        require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
                     }
                 }
             }
@@ -2783,7 +2783,7 @@ fn check_method_argument_types(&self,
             &method.sig.inputs()[1..]
         );
         self.check_argument_types(sp, expr_sp, &method.sig.inputs()[1..], &expected_arg_tys[..],
-                                  args_no_rcvr, method.sig.variadic, tuple_arguments,
+                                  args_no_rcvr, method.sig.c_variadic, tuple_arguments,
                                   self.tcx.hir().span_if_local(method.def_id));
         method.sig.output()
     }
@@ -2862,7 +2862,7 @@ fn check_argument_types(&self,
                             fn_inputs: &[Ty<'tcx>],
                             mut expected_arg_tys: &[Ty<'tcx>],
                             args: &'gcx [hir::Expr],
-                            variadic: bool,
+                            c_variadic: bool,
                             tuple_arguments: TupleArgumentsFlag,
                             def_span: Option<Span>) {
         let tcx = self.tcx;
@@ -2886,11 +2886,11 @@ fn check_argument_types(&self,
         let param_count_error = |expected_count: usize,
                                  arg_count: usize,
                                  error_code: &str,
-                                 variadic: bool,
+                                 c_variadic: bool,
                                  sugg_unit: bool| {
             let mut err = tcx.sess.struct_span_err_with_code(sp,
                 &format!("this function takes {}{} but {} {} supplied",
-                    if variadic {"at least "} else {""},
+                    if c_variadic { "at least " } else { "" },
                     potentially_plural_count(expected_count, "parameter"),
                     potentially_plural_count(arg_count, "parameter"),
                     if arg_count == 1 {"was"} else {"were"}),
@@ -2910,7 +2910,7 @@ fn check_argument_types(&self,
                     Applicability::MachineApplicable);
             } else {
                 err.span_label(sp, format!("expected {}{}",
-                                           if variadic {"at least "} else {""},
+                                           if c_variadic { "at least " } else { "" },
                                            potentially_plural_count(expected_count, "parameter")));
             }
             err.emit();
@@ -2944,7 +2944,7 @@ fn check_argument_types(&self,
             }
         } else if expected_arg_count == supplied_arg_count {
             fn_inputs.to_vec()
-        } else if variadic {
+        } else if c_variadic {
             if supplied_arg_count >= expected_arg_count {
                 fn_inputs.to_vec()
             } else {
@@ -2991,10 +2991,10 @@ fn check_argument_types(&self,
                 self.select_obligations_where_possible(false);
             }
 
-            // For variadic functions, we don't have a declared type for all of
+            // For C-variadic functions, we don't have a declared type for all of
             // the arguments hence we only do our usual type checking with
             // the arguments who's types we do know.
-            let t = if variadic {
+            let t = if c_variadic {
                 expected_arg_count
             } else if tuple_arguments == TupleArguments {
                 args.len()
@@ -3043,7 +3043,7 @@ fn check_argument_types(&self,
 
         // We also need to make sure we at least write the ty of the other
         // arguments which we skipped above.
-        if variadic {
+        if c_variadic {
             fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
                 use crate::structured_errors::{VariadicError, StructuredDiagnostic};
                 VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
index 419796a2014e04d8d965d40aeb986a57fd4fbfd7..2095c81d0fb0db1893945846097a587ff6551b99 100644 (file)
@@ -136,14 +136,14 @@ fn check_type_alias_enum_variants_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx,
     }
 }
 
-fn require_c_abi_if_variadic(tcx: TyCtxt<'_, '_, '_>,
-                             decl: &hir::FnDecl,
-                             abi: Abi,
-                             span: Span) {
-    if decl.variadic && !(abi == Abi::C || abi == Abi::Cdecl) {
+fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_, '_, '_>,
+                               decl: &hir::FnDecl,
+                               abi: Abi,
+                               span: Span) {
+    if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) {
         let mut err = struct_span_err!(tcx.sess, span, E0045,
-            "variadic function must have C or cdecl calling convention");
-        err.span_label(span, "variadics require C or cdecl calling convention").emit();
+            "C-variadic function must have C or cdecl calling convention");
+        err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
     }
 }
 
index 72abbae231aa6e188b6fb8cb43fd89aad65ada65..53dcc258c690b592495010d0e2046700d03f48c3 100644 (file)
@@ -1752,7 +1752,6 @@ fn clean(&self, cx: &DocContext<'_, '_, '_>) -> Item {
 pub struct FnDecl {
     pub inputs: Arguments,
     pub output: FunctionRetTy,
-    pub variadic: bool,
     pub attrs: Attributes,
 }
 
@@ -1831,7 +1830,6 @@ fn clean(&self, cx: &DocContext<'_, '_, '_>) -> FnDecl {
         FnDecl {
             inputs: (&self.0.inputs[..], self.1).clean(cx),
             output: self.0.output.clean(cx),
-            variadic: self.0.variadic,
             attrs: Attributes::default()
         }
     }
@@ -1849,7 +1847,6 @@ fn clean(&self, cx: &DocContext<'_, '_, '_>) -> FnDecl {
         FnDecl {
             output: Return(sig.skip_binder().output().clean(cx)),
             attrs: Attributes::default(),
-            variadic: sig.skip_binder().variadic,
             inputs: Arguments {
                 values: sig.skip_binder().inputs().iter().map(|t| {
                     Argument {
@@ -2252,6 +2249,7 @@ pub enum Type {
     Slice(Box<Type>),
     Array(Box<Type>, String),
     Never,
+    CVarArgs,
     Unique(Box<Type>),
     RawPointer(Mutability, Box<Type>),
     BorrowedRef {
@@ -2290,6 +2288,7 @@ pub enum PrimitiveType {
     Reference,
     Fn,
     Never,
+    CVarArgs,
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Copy, Debug)]
@@ -2469,6 +2468,7 @@ pub fn as_str(&self) -> &'static str {
             Reference => "reference",
             Fn => "fn",
             Never => "never",
+            CVarArgs => "...",
         }
     }
 
@@ -2518,6 +2518,7 @@ fn clean(&self, cx: &DocContext<'_, '_, '_>) -> Type {
 
         match self.node {
             TyKind::Never => Never,
+            TyKind::CVarArgs(_) => CVarArgs,
             TyKind::Ptr(ref m) => RawPointer(m.mutbl.clean(cx), box m.ty.clean(cx)),
             TyKind::Rptr(ref l, ref m) => {
                 let lifetime = if l.is_elided() {
@@ -3654,6 +3655,7 @@ fn build_deref_target_impls(cx: &DocContext<'_, '_, '_>,
             Reference => None,
             Fn => None,
             Never => None,
+            CVarArgs => tcx.lang_items().va_list(),
         };
         if let Some(did) = did {
             if !did.is_local() {
index 4463dad1c8a1f8de1e264ebad0909b3474e86084..d204a179ca62cb68098cbe9b2dd5ac5b6bf13da7 100644 (file)
@@ -609,6 +609,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
             primitive_link(f, PrimitiveType::Array, &format!("; {}]", n))
         }
         clean::Never => primitive_link(f, PrimitiveType::Never, "!"),
+        clean::CVarArgs => primitive_link(f, PrimitiveType::CVarArgs, "..."),
         clean::RawPointer(m, ref t) => {
             match **t {
                 clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
@@ -834,18 +835,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 impl fmt::Display for clean::FnDecl {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.variadic {
-            if f.alternate() {
-                write!(f, "({args:#}, ...){arrow:#}", args = self.inputs, arrow = self.output)
-            } else {
-                write!(f, "({args}, ...){arrow}", args = self.inputs, arrow = self.output)
-            }
+        if f.alternate() {
+            write!(f, "({args:#}){arrow:#}", args = self.inputs, arrow = self.output)
         } else {
-            if f.alternate() {
-                write!(f, "({args:#}){arrow:#}", args = self.inputs, arrow = self.output)
-            } else {
-                write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
-            }
+            write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
         }
     }
 }
@@ -907,12 +900,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             }
         }
 
-        let mut args_plain = format!("({})", args_plain);
-
-        if decl.variadic {
-            args.push_str(",<br> ...");
-            args_plain.push_str(", ...");
-        }
+        let args_plain = format!("({})", args_plain);
 
         let output = if let hir::IsAsync::Async = asyncness {
             Cow::Owned(decl.sugared_async_return_type())
index b6f9ae36da70c4af2c3f58feae08740958237a40..a01a5bb0a36383da7785012885af8b86b9c93bcb 100644 (file)
@@ -1643,6 +1643,8 @@ pub enum TyKind {
     Mac(Mac),
     /// Placeholder for a kind that has failed to be defined.
     Err,
+    /// Placeholder for a `va_list`.
+    CVarArgs,
 }
 
 impl TyKind {
@@ -1802,7 +1804,7 @@ pub fn from_self(eself: ExplicitSelf, eself_ident: Ident) -> Arg {
 pub struct FnDecl {
     pub inputs: Vec<Arg>,
     pub output: FunctionRetTy,
-    pub variadic: bool,
+    pub c_variadic: bool,
 }
 
 impl FnDecl {
index 2f88749ace85ea3048ac6b4698c0fef69fd0270c..e95f05894491b670f6c5e8d9df58bd22951226f1 100644 (file)
@@ -985,7 +985,7 @@ fn fn_decl(&self, inputs: Vec<ast::Arg>, output: ast::FunctionRetTy) -> P<ast::F
         P(ast::FnDecl {
             inputs,
             output,
-            variadic: false
+            c_variadic: false
         })
     }
 
index 669c68eda39d2a5261892799a6016fc33ccfa5f5..1c0b931b289c23d8826d02030b67fb07e3b5bd10 100644 (file)
@@ -471,6 +471,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
 
     // #[repr(align(X))] on enums
     (active, repr_align_enum, "1.34.0", Some(57996), None),
+
+    // Allows the use of C-variadics
+    (active, c_variadic, "1.34.0", Some(44930), None),
 );
 
 declare_features! (
@@ -1901,6 +1904,11 @@ fn visit_fn(&mut self,
                 if header.asyncness.node.is_async() {
                     gate_feature_post!(&self, async_await, span, "async fn is unstable");
                 }
+
+                if fn_decl.c_variadic {
+                    gate_feature_post!(&self, c_variadic, span,
+                                       "C-varaidic functions are unstable");
+                }
                 // Stability of const fn methods are covered in
                 // `visit_trait_item` and `visit_impl_item` below; this is
                 // because default methods don't pass through this point.
@@ -1929,6 +1937,10 @@ fn visit_trait_item(&mut self, ti: &'a ast::TraitItem) {
                 if block.is_none() {
                     self.check_abi(sig.header.abi, ti.span);
                 }
+                if sig.decl.c_variadic {
+                    gate_feature_post!(&self, c_variadic, ti.span,
+                                       "C-varaidic functions are unstable");
+                }
                 if sig.header.constness.node == ast::Constness::Const {
                     gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
                 }
index d30372d45d754b20943a5a6eee1dde055a32947d..802b78086959721be2aed40543a8712feb949a07 100644 (file)
@@ -409,7 +409,8 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
     let Ty { id, node, span } = ty.deref_mut();
     vis.visit_id(id);
     match node {
-        TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never => {}
+        TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err |
+            TyKind::Never | TyKind::CVarArgs => {}
         TyKind::Slice(ty) => vis.visit_ty(ty),
         TyKind::Ptr(mt) => vis.visit_mt(mt),
         TyKind::Rptr(lt, mt) => {
@@ -680,7 +681,7 @@ pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut IsAsync, vis: &mut T)
 }
 
 pub fn noop_visit_fn_decl<T: MutVisitor>(decl: &mut P<FnDecl>, vis: &mut T) {
-    let FnDecl { inputs, output, variadic: _ } = decl.deref_mut();
+    let FnDecl { inputs, output, c_variadic: _ } = decl.deref_mut();
     visit_vec(inputs, |input| vis.visit_arg(input));
     match output {
         FunctionRetTy::Default(span) => vis.visit_span(span),
index b762365c31f9760ea9d1b9ef3b6b0cbdd9d92a4c..fd5038a8614f25823cf6c0aee5bd3664701d119d 100644 (file)
@@ -1457,12 +1457,12 @@ fn parse_ty_bare_fn(&mut self, generic_params: Vec<GenericParam>) -> PResult<'a,
         };
 
         self.expect_keyword(keywords::Fn)?;
-        let (inputs, variadic) = self.parse_fn_args(false, true)?;
+        let (inputs, c_variadic) = self.parse_fn_args(false, true)?;
         let ret_ty = self.parse_ret_ty(false)?;
         let decl = P(FnDecl {
             inputs,
             output: ret_ty,
-            variadic,
+            c_variadic,
         });
         Ok(TyKind::BareFn(P(BareFnTy {
             abi,
@@ -1543,7 +1543,7 @@ fn parse_trait_item_(&mut self,
                 // definition...
 
                 // We don't allow argument names to be left off in edition 2018.
-                p.parse_arg_general(p.span.rust_2018(), true)
+                p.parse_arg_general(p.span.rust_2018(), true, false)
             })?;
             generics.where_clause = self.parse_where_clause()?;
 
@@ -1613,7 +1613,7 @@ fn parse_trait_item_(&mut self,
     /// Parses an optional return type `[ -> TY ]` in a function declaration.
     fn parse_ret_ty(&mut self, allow_plus: bool) -> PResult<'a, FunctionRetTy> {
         if self.eat(&token::RArrow) {
-            Ok(FunctionRetTy::Ty(self.parse_ty_common(allow_plus, true)?))
+            Ok(FunctionRetTy::Ty(self.parse_ty_common(allow_plus, true, false)?))
         } else {
             Ok(FunctionRetTy::Default(self.span.shrink_to_lo()))
         }
@@ -1621,7 +1621,7 @@ fn parse_ret_ty(&mut self, allow_plus: bool) -> PResult<'a, FunctionRetTy> {
 
     /// Parses a type.
     pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
-        self.parse_ty_common(true, true)
+        self.parse_ty_common(true, true, false)
     }
 
     /// Parses a type in restricted contexts where `+` is not permitted.
@@ -1631,11 +1631,11 @@ pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
     /// Example 2: `value1 as TYPE + value2`
     ///     `+` is prohibited to avoid interactions with expression grammar.
     fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
-        self.parse_ty_common(false, true)
+        self.parse_ty_common(false, true, false)
     }
 
-    fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool)
-                       -> PResult<'a, P<Ty>> {
+    fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool,
+                       allow_c_variadic: bool) -> PResult<'a, P<Ty>> {
         maybe_whole!(self, NtTy, |x| x);
 
         let lo = self.span;
@@ -1772,6 +1772,15 @@ fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool)
                     TyKind::Path(None, path)
                 }
             }
+        } else if self.check(&token::DotDotDot) {
+            if allow_c_variadic {
+                self.eat(&token::DotDotDot);
+                TyKind::CVarArgs
+            } else {
+                return Err(self.fatal(
+                    "only foreign functions are allowed to be C-variadic"
+                ));
+            }
         } else {
             let msg = format!("expected type, found {}", self.this_token_descr());
             return Err(self.fatal(&msg));
@@ -1959,7 +1968,8 @@ fn eat_incorrect_doc_comment(&mut self, applied_to: &str) {
     }
 
     /// This version of parse arg doesn't necessarily require identifier names.
-    fn parse_arg_general(&mut self, require_name: bool, is_trait_item: bool) -> PResult<'a, Arg> {
+    fn parse_arg_general(&mut self, require_name: bool, is_trait_item: bool,
+                         allow_c_variadic: bool) -> PResult<'a, Arg> {
         maybe_whole!(self, NtArg, |x| x);
 
         if let Ok(Some(_)) = self.parse_self_arg() {
@@ -2008,12 +2018,12 @@ fn parse_arg_general(&mut self, require_name: bool, is_trait_item: bool) -> PRes
             }
 
             self.eat_incorrect_doc_comment("a method argument's type");
-            (pat, self.parse_ty()?)
+            (pat, self.parse_ty_common(true, true, allow_c_variadic)?)
         } else {
             debug!("parse_arg_general ident_to_pat");
             let parser_snapshot_before_ty = self.clone();
             self.eat_incorrect_doc_comment("a method argument's type");
-            let mut ty = self.parse_ty();
+            let mut ty = self.parse_ty_common(true, true, allow_c_variadic);
             if ty.is_ok() && self.token != token::Comma &&
                self.token != token::CloseDelim(token::Paren) {
                 // This wasn't actually a type, but a pattern looking like a type,
@@ -2032,6 +2042,11 @@ fn parse_arg_general(&mut self, require_name: bool, is_trait_item: bool) -> PRes
                     (pat, ty)
                 }
                 Err(mut err) => {
+                    // If this is a C-variadic argument and we hit an error, return the
+                    // error.
+                    if self.token == token::DotDotDot {
+                        return Err(err);
+                    }
                     // Recover from attempting to parse the argument as a type without pattern.
                     err.cancel();
                     mem::replace(self, parser_snapshot_before_ty);
@@ -2068,7 +2083,7 @@ fn parse_arg_general(&mut self, require_name: bool, is_trait_item: bool) -> PRes
 
     /// Parses a single function argument.
     crate fn parse_arg(&mut self) -> PResult<'a, Arg> {
-        self.parse_arg_general(true, false)
+        self.parse_arg_general(true, false, false)
     }
 
     /// Parses an argument in a lambda header (e.g., `|arg, arg|`).
@@ -2406,7 +2421,7 @@ fn parse_path_segment(&mut self, style: PathStyle, enable_warning: bool)
                 }
                 let span = lo.to(self.prev_span);
                 let output = if self.eat(&token::RArrow) {
-                    Some(self.parse_ty_common(false, false)?)
+                    Some(self.parse_ty_common(false, false, false)?)
                 } else {
                     None
                 };
@@ -6113,55 +6128,49 @@ fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
         Ok(where_clause)
     }
 
-    fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool)
+    fn parse_fn_args(&mut self, named_args: bool, allow_c_variadic: bool)
                      -> PResult<'a, (Vec<Arg> , bool)> {
         self.expect(&token::OpenDelim(token::Paren))?;
 
         let sp = self.span;
-        let mut variadic = false;
+        let mut c_variadic = false;
         let (args, recovered): (Vec<Option<Arg>>, bool) =
             self.parse_seq_to_before_end(
                 &token::CloseDelim(token::Paren),
                 SeqSep::trailing_allowed(token::Comma),
                 |p| {
-                    if p.token == token::DotDotDot {
-                        p.bump();
-                        variadic = true;
-                        if allow_variadic {
-                            if p.token != token::CloseDelim(token::Paren) {
-                                let span = p.span;
-                                p.span_err(span,
-                                    "`...` must be last in argument list for variadic function");
-                            }
-                            Ok(None)
-                        } else {
-                            let span = p.prev_span;
-                            if p.token == token::CloseDelim(token::Paren) {
-                                // continue parsing to present any further errors
-                                p.struct_span_err(
-                                    span,
-                                    "only foreign functions are allowed to be variadic"
-                                ).emit();
-                                Ok(Some(dummy_arg(span)))
-                           } else {
-                               // this function definition looks beyond recovery, stop parsing
-                                p.span_err(span,
-                                           "only foreign functions are allowed to be variadic");
-                                Ok(None)
-                            }
-                        }
+                    // If the argument is a C-variadic argument we should not
+                    // enforce named arguments.
+                    let enforce_named_args = if p.token == token::DotDotDot {
+                        false
                     } else {
-                        match p.parse_arg_general(named_args, false) {
-                            Ok(arg) => Ok(Some(arg)),
-                            Err(mut e) => {
-                                e.emit();
-                                let lo = p.prev_span;
-                                // Skip every token until next possible arg or end.
-                                p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
-                                // Create a placeholder argument for proper arg count (#34264).
-                                let span = lo.to(p.prev_span);
-                                Ok(Some(dummy_arg(span)))
+                        named_args
+                    };
+                    match p.parse_arg_general(enforce_named_args, false,
+                                              allow_c_variadic) {
+                        Ok(arg) => {
+                            if let TyKind::CVarArgs = arg.ty.node {
+                                c_variadic = true;
+                                if p.token != token::CloseDelim(token::Paren) {
+                                    let span = p.span;
+                                    p.span_err(span,
+                                        "`...` must be the last argument of a C-variadic function");
+                                    Ok(None)
+                                } else {
+                                    Ok(Some(arg))
+                                }
+                            } else {
+                                Ok(Some(arg))
                             }
+                        },
+                        Err(mut e) => {
+                            e.emit();
+                            let lo = p.prev_span;
+                            // Skip every token until next possible arg or end.
+                            p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
+                            // Create a placeholder argument for proper arg count (issue #34264).
+                            let span = lo.to(p.prev_span);
+                            Ok(Some(dummy_arg(span)))
                         }
                     }
                 }
@@ -6173,24 +6182,24 @@ fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool)
 
         let args: Vec<_> = args.into_iter().filter_map(|x| x).collect();
 
-        if variadic && args.is_empty() {
+        if c_variadic && args.is_empty() {
             self.span_err(sp,
-                          "variadic function must be declared with at least one named argument");
+                          "C-variadic function must be declared with at least one named argument");
         }
 
-        Ok((args, variadic))
+        Ok((args, c_variadic))
     }
 
     /// Parses the argument list and result type of a function declaration.
-    fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P<FnDecl>> {
+    fn parse_fn_decl(&mut self, allow_c_variadic: bool) -> PResult<'a, P<FnDecl>> {
 
-        let (args, variadic) = self.parse_fn_args(true, allow_variadic)?;
+        let (args, c_variadic) = self.parse_fn_args(true, allow_c_variadic)?;
         let ret_ty = self.parse_ret_ty(true)?;
 
         Ok(P(FnDecl {
             inputs: args,
             output: ret_ty,
-            variadic,
+            c_variadic,
         }))
     }
 
@@ -6337,7 +6346,7 @@ fn parse_fn_decl_with_self<F>(&mut self, parse_arg_fn: F) -> PResult<'a, P<FnDec
         Ok(P(FnDecl {
             inputs: fn_inputs,
             output: self.parse_ret_ty(true)?,
-            variadic: false
+            c_variadic: false
         }))
     }
 
@@ -6363,7 +6372,7 @@ fn parse_fn_block_decl(&mut self) -> PResult<'a, P<FnDecl>> {
         Ok(P(FnDecl {
             inputs: inputs_captures,
             output,
-            variadic: false
+            c_variadic: false
         }))
     }
 
@@ -6395,7 +6404,8 @@ fn parse_item_fn(&mut self,
                      abi: Abi)
                      -> PResult<'a, ItemInfo> {
         let (ident, mut generics) = self.parse_fn_header()?;
-        let decl = self.parse_fn_decl(false)?;
+        let allow_c_variadic = abi == Abi::C && unsafety == Unsafety::Unsafe;
+        let decl = self.parse_fn_decl(allow_c_variadic)?;
         generics.where_clause = self.parse_where_clause()?;
         let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
         let header = FnHeader { unsafety, asyncness, constness, abi };
index d6265ebde1bbc9dcb1d97dd8748d7a372ee7c872..942bd96939173bf20f4e100f6cb6833617d75b20 100644 (file)
@@ -1118,6 +1118,9 @@ pub fn print_type(&mut self, ty: &ast::Ty) -> io::Result<()> {
             ast::TyKind::Mac(ref m) => {
                 self.print_mac(m)?;
             }
+            ast::TyKind::CVarArgs => {
+                self.s.word("...")?;
+            }
         }
         self.end()
     }
@@ -2811,7 +2814,7 @@ pub fn print_fn_args_and_ret(&mut self, decl: &ast::FnDecl)
         -> io::Result<()> {
         self.popen()?;
         self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false))?;
-        if decl.variadic {
+        if decl.c_variadic {
             self.s.word(", ...")?;
         }
         self.pclose()?;
@@ -3238,7 +3241,7 @@ fn test_fun_to_string() {
             let decl = ast::FnDecl {
                 inputs: Vec::new(),
                 output: ast::FunctionRetTy::Default(syntax_pos::DUMMY_SP),
-                variadic: false
+                c_variadic: false
             };
             let generics = ast::Generics::default();
             assert_eq!(
index 46d8d772e9321ac707c7b6eba245a66de378cf84..6b3a30ccb54b76738354be8bc8b4ddda3fe3ae55 100644 (file)
@@ -319,7 +319,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
             walk_list!(visitor, visit_lifetime, opt_lifetime);
             visitor.visit_ty(&mutable_type.ty)
         }
-        TyKind::Never => {},
+        TyKind::Never | TyKind::CVarArgs => {}
         TyKind::Tup(ref tuple_element_types) => {
             walk_list!(visitor, visit_ty, tuple_element_types);
         }
diff --git a/src/test/codegen/c-variadic.rs b/src/test/codegen/c-variadic.rs
new file mode 100644 (file)
index 0000000..09c18ed
--- /dev/null
@@ -0,0 +1,69 @@
+// compile-flags: -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+#![feature(c_variadic)]
+#![no_std]
+use core::ffi::VaList;
+
+extern "C" {
+    fn foreign_c_variadic_0(_: i32, ...);
+    fn foreign_c_variadic_1(_: VaList, ...);
+}
+
+pub unsafe extern "C" fn use_foreign_c_variadic_0() {
+    // Ensure that we correctly call foreign C-variadic functions.
+    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0)
+    foreign_c_variadic_0(0);
+    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42)
+    foreign_c_variadic_0(0, 42i32);
+    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024)
+    foreign_c_variadic_0(0, 42i32, 1024i32);
+    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024, i32 0)
+    foreign_c_variadic_0(0, 42i32, 1024i32, 0i32);
+}
+
+// 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.
+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);
+}
+
+pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
+    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 42)
+    foreign_c_variadic_1(ap, 42i32);
+}
+pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
+    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42)
+    foreign_c_variadic_1(ap, 2i32, 42i32);
+}
+
+pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
+    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42, i32 0)
+    foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
+}
+
+// Ensure that `va_start` and `va_end` are properly injected.
+#[no_mangle]
+pub unsafe extern "C" fn c_variadic(n: i32, mut ap: ...) -> i32 {
+    // CHECK: call void @llvm.va_start
+    let mut sum = 0;
+    for _ in 0..n {
+        sum += ap.arg::<i32>();
+    }
+    sum
+    // CHECK: call void @llvm.va_end
+}
+
+// Ensure that we generate the correct `call` signature when calling a Rust
+// defined C-variadic.
+pub unsafe fn test_c_variadic_call() {
+    // CHECK: call i32 (i32, ...) @c_variadic(i32 0)
+    c_variadic(0);
+    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42)
+    c_variadic(0, 42i32);
+    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024)
+    c_variadic(0, 42i32, 1024i32);
+    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024, i32 0)
+    c_variadic(0, 42i32, 1024i32, 0i32);
+}
index d55aac1e40f4f6b320344290f03fbb33402ee315..96a238afaec05039a5c515bd7b79cfa3317f0523 100644 (file)
@@ -18,8 +18,10 @@ macro_rules! continue_if {
 
 unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool {
     let cstr0 = CStr::from_ptr(ptr);
-    let cstr1 = CString::new(val).unwrap();
-    &*cstr1 == cstr0
+    match CString::new(val) {
+        Ok(cstr1) => &*cstr1 == cstr0,
+        Err(_) => false,
+    }
 }
 
 #[no_mangle]
@@ -68,3 +70,24 @@ unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool {
         }
     })
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn check_varargs_0(_: c_int, mut ap: ...) -> usize {
+    continue_if!(ap.arg::<c_int>() == 42);
+    continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello, World!"));
+    0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize {
+    continue_if!(ap.arg::<c_double>().floor() == 3.14f64.floor());
+    continue_if!(ap.arg::<c_long>() == 12);
+    continue_if!(ap.arg::<c_char>() == 'A' as c_char);
+    continue_if!(ap.arg::<c_longlong>() == 1);
+    0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn check_varargs_2(_: c_int, mut ap: ...) -> usize {
+    0
+}
index 95cf0ef46ca1e443a8a9e412ac647ac5574d0cb3..91b060dce26f436f6f9f5d21b7d038784dba115d 100644 (file)
@@ -8,6 +8,9 @@ extern size_t check_list_0(va_list ap);
 extern size_t check_list_1(va_list ap);
 extern size_t check_list_2(va_list ap);
 extern size_t check_list_copy_0(va_list ap);
+extern size_t check_varargs_0(int fixed, ...);
+extern size_t check_varargs_1(int fixed, ...);
+extern size_t check_varargs_2(int fixed, ...);
 
 int test_rust(size_t (*fn)(va_list), ...) {
     size_t ret = 0;
@@ -26,5 +29,12 @@ int main(int argc, char* argv[]) {
     assert(test_rust(check_list_2, 3.14, 12l, 'a', 6.28, "Hello", 42, "World") == 0);
 
     assert(test_rust(check_list_copy_0, 6.28, 16, 'A', "Skip Me!", "Correct") == 0);
+
+    assert(check_varargs_0(0, 42, "Hello, World!") == 0);
+
+    assert(check_varargs_1(0, 3.14, 12l, 'A', 0x1LL) == 0);
+
+    assert(check_varargs_2(0, "All", "of", "these", "are", "ignored", ".") == 0);
+
     return 0;
 }
index 956bc5ad862caf9f8cfcbbecd5efa21863a99700..80e0b0102af75f8ff8fd4af4eae9132ff3f75dcb 100644 (file)
@@ -112,7 +112,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P<Expr>)) {
                 let decl = P(FnDecl {
                     inputs: vec![],
                     output: FunctionRetTy::Default(DUMMY_SP),
-                    variadic: false,
+                    c_variadic: false,
                 });
                 iter_exprs(depth - 1, &mut |e| g(
                         ExprKind::Closure(CaptureBy::Value,
index bd8f1775b3d048dcb8ee387f44f2be317c4781d6..5af2aea21fcacd94040f88f726176fedf46a66b4 100644 (file)
@@ -1,4 +1,4 @@
 extern "C" {
-    // @has variadic/fn.foo.html //pre 'pub unsafe extern "C" fn foo(x: i32, ...)'
+    // @has variadic/fn.foo.html //pre 'pub unsafe extern "C" fn foo(x: i32, _: ...)'
     pub fn foo(x: i32, ...);
 }
diff --git a/src/test/ui/c-variadic/variadic-ffi-1.rs b/src/test/ui/c-variadic/variadic-ffi-1.rs
new file mode 100644 (file)
index 0000000..61b2ad4
--- /dev/null
@@ -0,0 +1,31 @@
+// ignore-arm stdcall isn't supported
+// ignore-aarch64 stdcall isn't supported
+
+extern "stdcall" {
+    fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
+}
+
+extern {
+    fn foo(f: isize, x: u8, ...);
+}
+
+extern "C" fn bar(f: isize, x: u8) {}
+
+fn main() {
+    // errors below are no longer checked because error above aborts
+    // compilation; see variadic-ffi-3.rs for corresponding test.
+    unsafe {
+        foo();
+        foo(1);
+
+        let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
+        let y: extern "C" fn(f: isize, x: u8, ...) = bar;
+
+        foo(1, 2, 3f32);
+        foo(1, 2, true);
+        foo(1, 2, 1i8);
+        foo(1, 2, 1u8);
+        foo(1, 2, 1i16);
+        foo(1, 2, 1u16);
+    }
+}
diff --git a/src/test/ui/c-variadic/variadic-ffi-1.stderr b/src/test/ui/c-variadic/variadic-ffi-1.stderr
new file mode 100644 (file)
index 0000000..61d55ce
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0045]: C-variadic function must have C or cdecl calling convention
+  --> $DIR/variadic-ffi-1.rs:5:5
+   |
+LL |     fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0045`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-2.rs b/src/test/ui/c-variadic/variadic-ffi-2.rs
new file mode 100644 (file)
index 0000000..224ac16
--- /dev/null
@@ -0,0 +1,8 @@
+// ignore-arm stdcall isn't supported
+
+fn baz(f: extern "stdcall" fn(usize, ...)) {
+    //~^ ERROR: variadic function must have C or cdecl calling convention
+    f(22, 44);
+}
+
+fn main() {}
diff --git a/src/test/ui/c-variadic/variadic-ffi-2.stderr b/src/test/ui/c-variadic/variadic-ffi-2.stderr
new file mode 100644 (file)
index 0000000..4c8b8d2
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0045]: C-variadic function must have C or cdecl calling convention
+  --> $DIR/variadic-ffi-2.rs:3:11
+   |
+LL | fn baz(f: extern "stdcall" fn(usize, ...)) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0045`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-3.rs b/src/test/ui/c-variadic/variadic-ffi-3.rs
new file mode 100644 (file)
index 0000000..c02d1f5
--- /dev/null
@@ -0,0 +1,29 @@
+extern {
+    fn foo(f: isize, x: u8, ...);
+    //~^ defined here
+    //~| defined here
+}
+
+extern "C" fn bar(f: isize, x: u8) {}
+
+fn main() {
+    unsafe {
+        foo(); //~ ERROR: this function takes at least 2 parameters but 0 parameters were supplied
+        foo(1); //~ ERROR: this function takes at least 2 parameters but 1 parameter was supplied
+
+        let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
+        //~^ ERROR: mismatched types
+        //~| expected type `unsafe extern "C" fn(isize, u8)`
+
+        let y: extern "C" fn(f: isize, x: u8, ...) = bar;
+        //~^ ERROR: mismatched types
+        //~| expected type `for<'r> extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...)`
+
+        foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
+        foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function
+        foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function
+        foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function
+        foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function
+        foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function
+    }
+}
diff --git a/src/test/ui/c-variadic/variadic-ffi-3.stderr b/src/test/ui/c-variadic/variadic-ffi-3.stderr
new file mode 100644 (file)
index 0000000..82e3c6c
--- /dev/null
@@ -0,0 +1,76 @@
+error[E0060]: this function takes at least 2 parameters but 0 parameters were supplied
+  --> $DIR/variadic-ffi-3.rs:11:9
+   |
+LL |     fn foo(f: isize, x: u8, ...);
+   |     ----------------------------- defined here
+...
+LL |         foo(); //~ ERROR: this function takes at least 2 parameters but 0 parameters were supplied
+   |         ^^^^^ expected at least 2 parameters
+
+error[E0060]: this function takes at least 2 parameters but 1 parameter was supplied
+  --> $DIR/variadic-ffi-3.rs:12:9
+   |
+LL |     fn foo(f: isize, x: u8, ...);
+   |     ----------------------------- defined here
+...
+LL |         foo(1); //~ ERROR: this function takes at least 2 parameters but 1 parameter was supplied
+   |         ^^^^^^ expected at least 2 parameters
+
+error[E0308]: mismatched types
+  --> $DIR/variadic-ffi-3.rs:14:56
+   |
+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}`
+
+error[E0308]: mismatched types
+  --> $DIR/variadic-ffi-3.rs:18:54
+   |
+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>, ...)`
+              found type `extern "C" fn(isize, u8) {bar}`
+
+error[E0617]: can't pass `f32` to variadic function
+  --> $DIR/variadic-ffi-3.rs:22:19
+   |
+LL |         foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
+   |                   ^^^^ help: cast the value to `c_double`: `3f32 as c_double`
+
+error[E0617]: can't pass `bool` to variadic function
+  --> $DIR/variadic-ffi-3.rs:23:19
+   |
+LL |         foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function
+   |                   ^^^^ help: cast the value to `c_int`: `true as c_int`
+
+error[E0617]: can't pass `i8` to variadic function
+  --> $DIR/variadic-ffi-3.rs:24:19
+   |
+LL |         foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function
+   |                   ^^^ help: cast the value to `c_int`: `1i8 as c_int`
+
+error[E0617]: can't pass `u8` to variadic function
+  --> $DIR/variadic-ffi-3.rs:25:19
+   |
+LL |         foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function
+   |                   ^^^ help: cast the value to `c_uint`: `1u8 as c_uint`
+
+error[E0617]: can't pass `i16` to variadic function
+  --> $DIR/variadic-ffi-3.rs:26:19
+   |
+LL |         foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function
+   |                   ^^^^ help: cast the value to `c_int`: `1i16 as c_int`
+
+error[E0617]: can't pass `u16` to variadic function
+  --> $DIR/variadic-ffi-3.rs:27:19
+   |
+LL |         foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function
+   |                   ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint`
+
+error: aborting due to 10 previous errors
+
+Some errors occurred: E0060, E0308, E0617.
+For more information about an error, try `rustc --explain E0060`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-4.rs b/src/test/ui/c-variadic/variadic-ffi-4.rs
new file mode 100644 (file)
index 0000000..9101be5
--- /dev/null
@@ -0,0 +1,29 @@
+#![crate_type="lib"]
+#![no_std]
+#![feature(c_variadic)]
+
+use core::ffi::VaList;
+
+pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
+    ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
+    ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) {
+    let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+}
+
+pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+    *ap0 = ap1; //~ ERROR: mismatched types
+}
+
+pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+    ap0 = &mut ap1;
+    //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+    //~^^ ERROR: mismatched types
+    //~^^^ ERROR: mismatched types
+    //~^^^^ 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
new file mode 100644 (file)
index 0000000..1d752be
--- /dev/null
@@ -0,0 +1,198 @@
+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 |     ap //~ ERROR: explicit lifetime required
+   |     ^^ lifetime `'a` 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 |     ap //~ ERROR: explicit lifetime required
+   |     ^^ lifetime `'static` required
+
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+  --> $DIR/variadic-ffi-4.rs:16:28
+   |
+LL |     let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+   |                            ^^
+   |
+note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 16:21...
+  --> $DIR/variadic-ffi-4.rs:16:21
+   |
+LL |     let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+   |                     ^^^^^^^^^^^
+   = note: ...so that the expression is assignable:
+           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.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+   |             ^^^^^^^^^^^^^^^^^^^^
+note: ...so type `core::ffi::VaList<'_>` of expression is valid during the expression
+  --> $DIR/variadic-ffi-4.rs:16:13
+   |
+LL |     let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+   |             ^^^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/variadic-ffi-4.rs:20:12
+   |
+LL |     *ap0 = ap1; //~ ERROR: mismatched types
+   |            ^^^ lifetime mismatch
+   |
+   = note: expected type `core::ffi::VaList<'_>`
+              found type `core::ffi::VaList<'_>`
+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 | |     *ap0 = ap1; //~ ERROR: mismatched types
+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 | |     *ap0 = ap1; //~ ERROR: mismatched types
+LL | | }
+   | |_^
+
+error[E0490]: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+  --> $DIR/variadic-ffi-4.rs:24:11
+   |
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+   | |_^
+
+error[E0308]: mismatched types
+  --> $DIR/variadic-ffi-4.rs:24:11
+   |
+LL |     ap0 = &mut ap1;
+   |           ^^^^^^^^ lifetime mismatch
+   |
+   = note: expected type `&mut core::ffi::VaList<'_>`
+              found type `&mut core::ffi::VaList<'_>`
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+   | |_^
+
+error[E0308]: mismatched types
+  --> $DIR/variadic-ffi-4.rs:24:11
+   |
+LL |     ap0 = &mut ap1;
+   |           ^^^^^^^^ lifetime mismatch
+   |
+   = note: expected type `&mut core::ffi::VaList<'_>`
+              found type `&mut core::ffi::VaList<'_>`
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+   | |_^
+
+error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
+  --> $DIR/variadic-ffi-4.rs:24:11
+   |
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+   | |_^
+note: ...so that the type `core::ffi::VaList<'_>` is not borrowed for too long
+  --> $DIR/variadic-ffi-4.rs:24:11
+   |
+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 | |     ap0 = &mut ap1;
+LL | |     //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | |     //~^^ ERROR: mismatched types
+LL | |     //~^^^ ERROR: mismatched types
+LL | |     //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+   | |_^
+note: ...so that reference does not outlive borrowed content
+  --> $DIR/variadic-ffi-4.rs:24:11
+   |
+LL |     ap0 = &mut ap1;
+   |           ^^^^^^^^
+
+error: aborting due to 8 previous errors
+
+Some errors occurred: E0308, E0490, E0495, E0621.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-5.rs b/src/test/ui/c-variadic/variadic-ffi-5.rs
new file mode 100644 (file)
index 0000000..d96482f
--- /dev/null
@@ -0,0 +1,31 @@
+#![crate_type="lib"]
+#![no_std]
+#![feature(c_variadic)]
+// The tests in this file are similar to that of variadic-ffi-4, but this
+// one enables nll.
+#![feature(nll)]
+
+use core::ffi::VaList;
+
+pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
+    ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
+    ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) {
+    let _ = ap.copy(|ap| { ap }); //~ ERROR: lifetime may not live long enough
+}
+
+pub unsafe extern "C" fn no_escape3(_: usize, ap0: &mut VaList, mut ap1: ...) {
+    *ap0 = ap1; //~ ERROR: lifetime may not live long enough
+}
+
+pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+    ap0 = &mut ap1;
+    //~^ ERROR: lifetime may not live long enough
+    //~^^ ERROR: lifetime may not live long enough
+    //~^^^ ERROR: `ap1` does not live long enough
+}
diff --git a/src/test/ui/c-variadic/variadic-ffi-5.stderr b/src/test/ui/c-variadic/variadic-ffi-5.stderr
new file mode 100644 (file)
index 0000000..2d45287
--- /dev/null
@@ -0,0 +1,73 @@
+error[E0621]: explicit lifetime required in the type of `ap`
+  --> $DIR/variadic-ffi-5.rs:11: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 |     ap //~ ERROR: explicit lifetime required
+   |     ^^ lifetime `'a` required
+
+error[E0621]: explicit lifetime required in the type of `ap`
+  --> $DIR/variadic-ffi-5.rs:15: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 |     ap //~ ERROR: explicit lifetime required
+   |     ^^ lifetime `'static` required
+
+error: lifetime may not live long enough
+  --> $DIR/variadic-ffi-5.rs:19:28
+   |
+LL |     let _ = ap.copy(|ap| { ap }); //~ ERROR: lifetime may not live long enough
+   |                      ---   ^^ returning this value requires that `'1` must outlive `'2`
+   |                      | |
+   |                      | 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-5.rs:23:5
+   |
+LL | pub unsafe extern "C" fn no_escape3(_: usize, ap0: &mut VaList, mut ap1: ...) {
+   |                                               ---               ------- has type `core::ffi::VaList<'1>`
+   |                                               |
+   |                                               has type `&mut core::ffi::VaList<'2>`
+LL |     *ap0 = ap1; //~ ERROR: lifetime may not live long enough
+   |     ^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+
+error: lifetime may not live long enough
+  --> $DIR/variadic-ffi-5.rs:27:5
+   |
+LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+   |                                               -------               ------- has type `core::ffi::VaList<'2>`
+   |                                               |
+   |                                               has type `&mut core::ffi::VaList<'1>`
+LL |     ap0 = &mut ap1;
+   |     ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+
+error: lifetime may not live long enough
+  --> $DIR/variadic-ffi-5.rs:27:5
+   |
+LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+   |                                               -------               ------- has type `core::ffi::VaList<'1>`
+   |                                               |
+   |                                               has type `&mut core::ffi::VaList<'2>`
+LL |     ap0 = &mut ap1;
+   |     ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+
+error[E0597]: `ap1` does not live long enough
+  --> $DIR/variadic-ffi-5.rs:27:11
+   |
+LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+   |                                                        - let's call the lifetime of this reference `'1`
+LL |     ap0 = &mut ap1;
+   |     ------^^^^^^^^
+   |     |     |
+   |     |     borrowed value does not live long enough
+   |     assignment requires that `ap1` is borrowed for `'1`
+...
+LL | }
+   |  - `ap1` dropped here while still borrowed
+
+error: aborting due to 7 previous errors
+
+Some errors occurred: E0597, E0621.
+For more information about an error, try `rustc --explain E0597`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-6.rs b/src/test/ui/c-variadic/variadic-ffi-6.rs
new file mode 100644 (file)
index 0000000..4dd8a2d
--- /dev/null
@@ -0,0 +1,13 @@
+#![crate_type="lib"]
+#![feature(c_variadic)]
+
+pub unsafe extern "C" fn use_vararg_lifetime(
+    x: usize,
+    y: ...
+) -> &usize { //~ ERROR missing lifetime specifier
+    &0
+}
+
+pub unsafe extern "C" fn use_normal_arg_lifetime(x: &usize, y: ...) -> &usize { // OK
+    x
+}
diff --git a/src/test/ui/c-variadic/variadic-ffi-6.stderr b/src/test/ui/c-variadic/variadic-ffi-6.stderr
new file mode 100644 (file)
index 0000000..76bd189
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/variadic-ffi-6.rs:7:6
+   |
+LL | ) -> &usize { //~ ERROR missing lifetime specifier
+   |      ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
+   |
+   = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0106`.
index b38bbc169bd60e0a33cf7d2e512263927efe7581..0ce91f0a401031b3a1591c7681f06883032cba83 100644 (file)
@@ -1,8 +1,8 @@
-error[E0045]: variadic function must have C or cdecl calling convention
+error[E0045]: C-variadic function must have C or cdecl calling convention
   --> $DIR/E0045.rs:1:17
    |
 LL | extern "Rust" { fn foo(x: u8, ...); }   //~ ERROR E0045
-   |                 ^^^^^^^^^^^^^^^^^^^ variadics require C or cdecl calling convention
+   |                 ^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
 
 error: aborting due to previous error
 
index 9eed1225ab82de1e47f55d0a1ebc402ec86806a1..51f13c7dbd5404e3670fb65327967bf2679e87e0 100644 (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 `unsafe extern "C" fn(*const i8, ...) {printf}` to variadic function
-        //~| HELP cast the value to `unsafe extern "C" fn(*const i8, ...)`
+        //~^ 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>, ...)`
     }
 }
index 486ca1fa92f15c670448309b14ffef43d07aa8be..8387d5c7e93a5fb05d2bdb0f16f69c873a972935 100644 (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 `unsafe extern "C" fn(*const i8, ...) {printf}` to variadic function
+error[E0617]: can't pass `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...) {printf}` to variadic function
   --> $DIR/E0617.rs:24:36
    |
 LL |         printf(::std::ptr::null(), printf);
    |                                    ^^^^^^
-help: cast the value to `unsafe extern "C" fn(*const i8, ...)`
+help: cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...)`
    |
-LL |         printf(::std::ptr::null(), printf as unsafe extern "C" fn(*const i8, ...));
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         printf(::std::ptr::null(), printf as for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...));
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/feature-gate/feature-gate-c_variadic.rs b/src/test/ui/feature-gate/feature-gate-c_variadic.rs
new file mode 100644 (file)
index 0000000..5801a2a
--- /dev/null
@@ -0,0 +1,4 @@
+#![crate_type="lib"]
+
+pub unsafe extern "C" fn test(_: i32, ap: ...) { }
+//~^ C-varaidic functions are unstable
diff --git a/src/test/ui/feature-gate/feature-gate-c_variadic.stderr b/src/test/ui/feature-gate/feature-gate-c_variadic.stderr
new file mode 100644 (file)
index 0000000..a876e16
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0658]: C-varaidic functions are unstable (see issue #44930)
+  --> $DIR/feature-gate-c_variadic.rs:3:1
+   |
+LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(c_variadic)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
index aea630e7c26a12cca779c75e7e49cad520ccecb5..8d23f0e477077676cb2e2b4773f8d8a98bc5d22a 100644 (file)
@@ -1,3 +1,3 @@
 extern "C" fn foo(x: u8, ...);
-//~^ ERROR only foreign functions are allowed to be variadic
+//~^ ERROR only foreign functions are allowed to be C-variadic
 //~| ERROR expected one of `->`, `where`, or `{`, found `;`
index 7a0b8066fd52a81e67a4edce949f8a68d9aaf170..b2dbf8b9190848ef69c0886ebb9becb71be116b7 100644 (file)
@@ -1,4 +1,4 @@
-error: only foreign functions are allowed to be variadic
+error: only foreign functions are allowed to be C-variadic
   --> $DIR/invalid-variadic-function.rs:1:26
    |
 LL | extern "C" fn foo(x: u8, ...);
index 2473420a77930fb88522e9983b9986812be1d39f..b308e644ad9f89dc743cd8a58e5d34f2ce6198b1 100644 (file)
@@ -10,11 +10,11 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{`
 LL |             Nope(i32 {}) //~ ERROR: found `{`
    |                      ^ expected one of 7 possible tokens here
 
-error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `::`, `<`, `?`, `[`, `_`, `crate`, `dyn`, `extern`, `fn`, `for`, `impl`, `pub`, `unsafe`, `}`, or lifetime, found `{`
+error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `...`, `::`, `<`, `?`, `[`, `_`, `crate`, `dyn`, `extern`, `fn`, `for`, `impl`, `pub`, `unsafe`, `}`, or lifetime, found `{`
   --> $DIR/recover-enum2.rs:27:22
    |
 LL |             Nope(i32 {}) //~ ERROR: found `{`
-   |                      ^ expected one of 23 possible tokens here
+   |                      ^ expected one of 24 possible tokens here
 
 error: expected expression, found reserved identifier `_`
   --> $DIR/recover-enum2.rs:32:22
index 13bce27bb83440c8210b6acebeda237b815631a1..ce83cc87abe00f937b45e97d4d4c60a6a29a62d3 100644 (file)
@@ -1,5 +1,5 @@
 fn foo(x: isize, ...) {
-    //~^ ERROR: only foreign functions are allowed to be variadic
+    //~^ ERROR: only foreign functions are allowed to be C-variadic
 }
 
 fn main() {}
index 150de9e63d39dde070e37db8a840a4f37e444076..8ea4d194396faa0082cd2598cab38d97d9e46829 100644 (file)
@@ -1,4 +1,4 @@
-error: only foreign functions are allowed to be variadic
+error: only foreign functions are allowed to be C-variadic
   --> $DIR/variadic-ffi-3.rs:1:18
    |
 LL | fn foo(x: isize, ...) {
index 812ed256a5d32955b8e3375b1f95df8b1289c4e0..5f8b3f8f539b8df463f457d8ae7ac8724bdc84ed 100644 (file)
@@ -1,5 +1,5 @@
 extern "C" fn foo(x: isize, ...) {
-    //~^ ERROR: only foreign functions are allowed to be variadic
+    //~^ ERROR: only foreign functions are allowed to be C-variadic
 }
 
 fn main() {}
index 2d036b0cf3792bd78a356a7d3d96763991bd2b52..69fbf84869c13bf7ad809dc6ab54f6c0bc21fae4 100644 (file)
@@ -1,4 +1,4 @@
-error: only foreign functions are allowed to be variadic
+error: only foreign functions are allowed to be C-variadic
   --> $DIR/variadic-ffi-4.rs:1:29
    |
 LL | extern "C" fn foo(x: isize, ...) {
diff --git a/src/test/ui/variadic/variadic-ffi-2.rs b/src/test/ui/variadic/variadic-ffi-2.rs
deleted file mode 100644 (file)
index 224ac16..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-// ignore-arm stdcall isn't supported
-
-fn baz(f: extern "stdcall" fn(usize, ...)) {
-    //~^ ERROR: variadic function must have C or cdecl calling convention
-    f(22, 44);
-}
-
-fn main() {}
diff --git a/src/test/ui/variadic/variadic-ffi-2.stderr b/src/test/ui/variadic/variadic-ffi-2.stderr
deleted file mode 100644 (file)
index cb2a9f8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0045]: variadic function must have C or cdecl calling convention
-  --> $DIR/variadic-ffi-2.rs:3:11
-   |
-LL | fn baz(f: extern "stdcall" fn(usize, ...)) {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variadics require C or cdecl calling convention
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0045`.
diff --git a/src/test/ui/variadic/variadic-ffi-3.rs b/src/test/ui/variadic/variadic-ffi-3.rs
deleted file mode 100644 (file)
index 12b3426..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-extern {
-    fn foo(f: isize, x: u8, ...);
-    //~^ defined here
-    //~| defined here
-}
-
-extern "C" fn bar(f: isize, x: u8) {}
-
-fn main() {
-    unsafe {
-        foo(); //~ ERROR: this function takes at least 2 parameters but 0 parameters were supplied
-        foo(1); //~ ERROR: this function takes at least 2 parameters but 1 parameter was supplied
-
-        let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
-        //~^ ERROR: mismatched types
-        //~| expected type `unsafe extern "C" fn(isize, u8)`
-        //~| found type `unsafe extern "C" fn(isize, u8, ...) {foo}`
-
-        let y: extern "C" fn(f: isize, x: u8, ...) = bar;
-        //~^ ERROR: mismatched types
-        //~| expected type `extern "C" fn(isize, u8, ...)`
-        //~| found type `extern "C" fn(isize, u8) {bar}`
-
-        foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
-        foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function
-        foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function
-        foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function
-        foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function
-        foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function
-    }
-}
diff --git a/src/test/ui/variadic/variadic-ffi-3.stderr b/src/test/ui/variadic/variadic-ffi-3.stderr
deleted file mode 100644 (file)
index 0fecbbf..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-error[E0060]: this function takes at least 2 parameters but 0 parameters were supplied
-  --> $DIR/variadic-ffi-3.rs:11:9
-   |
-LL |     fn foo(f: isize, x: u8, ...);
-   |     ----------------------------- defined here
-...
-LL |         foo(); //~ ERROR: this function takes at least 2 parameters but 0 parameters were supplied
-   |         ^^^^^ expected at least 2 parameters
-
-error[E0060]: this function takes at least 2 parameters but 1 parameter was supplied
-  --> $DIR/variadic-ffi-3.rs:12:9
-   |
-LL |     fn foo(f: isize, x: u8, ...);
-   |     ----------------------------- defined here
-...
-LL |         foo(1); //~ ERROR: this function takes at least 2 parameters but 1 parameter was supplied
-   |         ^^^^^^ expected at least 2 parameters
-
-error[E0308]: mismatched types
-  --> $DIR/variadic-ffi-3.rs:14:56
-   |
-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 `unsafe extern "C" fn(isize, u8, ...) {foo}`
-
-error[E0308]: mismatched types
-  --> $DIR/variadic-ffi-3.rs:19:54
-   |
-LL |         let y: extern "C" fn(f: isize, x: u8, ...) = bar;
-   |                                                      ^^^ expected variadic fn, found non-variadic function
-   |
-   = note: expected type `extern "C" fn(isize, u8, ...)`
-              found type `extern "C" fn(isize, u8) {bar}`
-
-error[E0617]: can't pass `f32` to variadic function
-  --> $DIR/variadic-ffi-3.rs:24:19
-   |
-LL |         foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
-   |                   ^^^^ help: cast the value to `c_double`: `3f32 as c_double`
-
-error[E0617]: can't pass `bool` to variadic function
-  --> $DIR/variadic-ffi-3.rs:25:19
-   |
-LL |         foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function
-   |                   ^^^^ help: cast the value to `c_int`: `true as c_int`
-
-error[E0617]: can't pass `i8` to variadic function
-  --> $DIR/variadic-ffi-3.rs:26:19
-   |
-LL |         foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function
-   |                   ^^^ help: cast the value to `c_int`: `1i8 as c_int`
-
-error[E0617]: can't pass `u8` to variadic function
-  --> $DIR/variadic-ffi-3.rs:27:19
-   |
-LL |         foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function
-   |                   ^^^ help: cast the value to `c_uint`: `1u8 as c_uint`
-
-error[E0617]: can't pass `i16` to variadic function
-  --> $DIR/variadic-ffi-3.rs:28:19
-   |
-LL |         foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function
-   |                   ^^^^ help: cast the value to `c_int`: `1i16 as c_int`
-
-error[E0617]: can't pass `u16` to variadic function
-  --> $DIR/variadic-ffi-3.rs:29:19
-   |
-LL |         foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function
-   |                   ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint`
-
-error: aborting due to 10 previous errors
-
-Some errors occurred: E0060, E0308, E0617.
-For more information about an error, try `rustc --explain E0060`.
diff --git a/src/test/ui/variadic/variadic-ffi.rs b/src/test/ui/variadic/variadic-ffi.rs
deleted file mode 100644 (file)
index 61b2ad4..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-// ignore-arm stdcall isn't supported
-// ignore-aarch64 stdcall isn't supported
-
-extern "stdcall" {
-    fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
-}
-
-extern {
-    fn foo(f: isize, x: u8, ...);
-}
-
-extern "C" fn bar(f: isize, x: u8) {}
-
-fn main() {
-    // errors below are no longer checked because error above aborts
-    // compilation; see variadic-ffi-3.rs for corresponding test.
-    unsafe {
-        foo();
-        foo(1);
-
-        let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
-        let y: extern "C" fn(f: isize, x: u8, ...) = bar;
-
-        foo(1, 2, 3f32);
-        foo(1, 2, true);
-        foo(1, 2, 1i8);
-        foo(1, 2, 1u8);
-        foo(1, 2, 1i16);
-        foo(1, 2, 1u16);
-    }
-}
diff --git a/src/test/ui/variadic/variadic-ffi.stderr b/src/test/ui/variadic/variadic-ffi.stderr
deleted file mode 100644 (file)
index 617b1f4..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0045]: variadic function must have C or cdecl calling convention
-  --> $DIR/variadic-ffi.rs:5:5
-   |
-LL |     fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variadics require C or cdecl calling convention
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0045`.
index 63196eda7cc5fd1be91e049b907a572db17221b8..cd60f36b1d273a012eb1b2611ec5d7ccd5c74271 100644 (file)
@@ -82,25 +82,27 @@ pub fn check(path: &path::Path, bad: &mut bool) {
         !lang_features.contains_key(name)
     }).collect();
 
+    // Library features
     let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features);
     let unstable_book_lib_features_section_file_names =
         collect_unstable_book_lib_features_section_file_names(path);
 
-    // Check for Unstable Book sections that don't have a corresponding unstable feature
-    for feature_name in &unstable_book_lib_features_section_file_names -
-                        &unstable_lib_feature_names {
-        tidy_error!(bad,
-                    "The Unstable Book has a 'library feature' section '{}' which doesn't \
-                     correspond to an unstable library feature",
-                    feature_name)
-    }
-
     // Language features
-
     let unstable_lang_feature_names = collect_unstable_feature_names(&lang_features);
     let unstable_book_lang_features_section_file_names =
         collect_unstable_book_lang_features_section_file_names(path);
 
+    // Check for Unstable Book sections that don't have a corresponding unstable feature
+    for feature_name in &unstable_book_lib_features_section_file_names -
+                        &unstable_lib_feature_names {
+        if !unstable_lang_feature_names.contains(&feature_name) {
+            tidy_error!(bad,
+                        "The Unstable Book has a 'library feature' section '{}' which doesn't \
+                         correspond to an unstable library feature",
+                        feature_name);
+        }
+    }
+
     // Check for Unstable Book sections that don't have a corresponding unstable feature.
     for feature_name in &unstable_book_lang_features_section_file_names -
                         &unstable_lang_feature_names {