summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Robertson <dan@dlrobertson.com>2018-11-30 15:53:44 +0000
committerDan Robertson <dan@dlrobertson.com>2019-02-27 10:21:35 -0500
commit58147d486bc26eb59d18d8e0d83aa6fe0b467fb9 (patch)
tree3e25be958b33c7e5727b059511157ac9f1a22722
parentcd56472cc47981e62c684ceada7922ac3731b785 (diff)
downloadrust-58147d486bc26eb59d18d8e0d83aa6fe0b467fb9.tar.gz
Support defining C compatible variadic functions
Add support for defining C compatible variadic functions in unsafe rust with extern "C".
-rw-r--r--src/librustc/hir/intravisit.rs3
-rw-r--r--src/librustc/hir/lowering.rs6
-rw-r--r--src/librustc/hir/mod.rs3
-rw-r--r--src/librustc/hir/print.rs3
-rw-r--r--src/librustc/ich/impls_hir.rs3
-rw-r--r--src/librustc/middle/resolve_lifetime.rs31
-rw-r--r--src/librustc/ty/sty.rs6
-rw-r--r--src/librustc_codegen_llvm/abi.rs45
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs51
-rw-r--r--src/librustc_codegen_llvm/mono_item.rs8
-rw-r--r--src/librustc_codegen_llvm/va_arg.rs4
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs50
-rw-r--r--src/librustc_codegen_ssa/mir/mod.rs64
-rw-r--r--src/librustc_codegen_ssa/traits/intrinsic.rs6
-rw-r--r--src/librustc_lint/types.rs9
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs11
-rw-r--r--src/librustc_save_analysis/sig.rs1
-rw-r--r--src/librustc_target/abi/call/mod.rs17
-rw-r--r--src/librustc_target/abi/call/x86.rs2
-rw-r--r--src/librustc_typeck/astconv.rs9
-rw-r--r--src/librustc_typeck/check/callee.rs14
-rw-r--r--src/librustc_typeck/check/intrinsic.rs6
-rw-r--r--src/librustdoc/clean/mod.rs8
-rw-r--r--src/librustdoc/html/format.rs22
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/mut_visit.rs3
-rw-r--r--src/libsyntax/parse/parser.rs106
-rw-r--r--src/libsyntax/print/pprust.rs3
-rw-r--r--src/libsyntax/visit.rs2
-rw-r--r--src/test/codegen/c-variadic.rs69
-rw-r--r--src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs27
-rw-r--r--src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c10
-rw-r--r--src/test/rustdoc/variadic.rs2
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-1.rs (renamed from src/test/ui/variadic/variadic-ffi.rs)0
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-1.stderr (renamed from src/test/ui/variadic/variadic-ffi.stderr)2
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-2.rs (renamed from src/test/ui/variadic/variadic-ffi-2.rs)0
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-2.stderr (renamed from src/test/ui/variadic/variadic-ffi-2.stderr)0
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-3.rs (renamed from src/test/ui/variadic/variadic-ffi-3.rs)4
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-3.stderr (renamed from src/test/ui/variadic/variadic-ffi-3.stderr)18
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.rs29
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.stderr198
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-5.rs31
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-5.stderr73
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-6.rs12
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-6.stderr11
-rw-r--r--src/test/ui/error-codes/E0617.rs4
-rw-r--r--src/test/ui/error-codes/E0617.stderr8
-rw-r--r--src/test/ui/parser/recover-enum2.stderr4
48 files changed, 848 insertions, 152 deletions
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 0bc40da7e4f..8e4b9a5e8e6 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -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 => {}
}
}
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 0211dd72875..0e2b34d4fac 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -1345,6 +1345,12 @@ impl<'a> LoweringContext<'a> {
}
}
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);
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 38e6e61592b..205109d18fe 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -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)]
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 17d37488413..8c252b0d027 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -434,6 +434,9 @@ impl<'a> State<'a> {
self.s.word("/*ERROR*/")?;
self.pclose()?;
}
+ hir::TyKind::CVarArgs(_) => {
+ self.s.word("...")?;
+ }
}
self.end()
}
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index d1161dda1e2..b7ec5889d6a 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -361,7 +361,8 @@ impl_stable_hash_for!(enum hir::TyKind {
TraitObject(trait_refs, lifetime),
Typeof(body_id),
Err,
- Infer
+ Infer,
+ CVarArgs(lt),
});
impl_stable_hash_for!(struct hir::FnDecl {
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 31e9eb9b746..832391d4416 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -764,6 +764,13 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
});
}
}
+ 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 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
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);
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index df8b14b1f10..7ade035ce89 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -977,9 +977,9 @@ impl<'tcx> PolyGenSig<'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.
+/// - `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>>,
diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs
index 992149f7a47..aea62360651 100644
--- a/src/librustc_codegen_llvm/abi.rs
+++ b/src/librustc_codegen_llvm/abi.rs
@@ -258,7 +258,7 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
val
};
match self.mode {
- PassMode::Ignore => {},
+ PassMode::Ignore(_) => {}
PassMode::Pair(..) => {
OperandValue::Pair(next(), next()).store(bx, dst);
}
@@ -507,6 +507,14 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
}
};
+ // 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 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
// 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.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);
+ },
+ _ => (),
+ }
+ }
+ _ => {}
}
}
@@ -646,7 +677,9 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
);
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 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
}
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));
@@ -733,7 +766,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
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 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
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)) => {
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs
index d1cbe1d4dd6..3268af396a2 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -136,22 +136,18 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
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 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
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(
diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs
index 4fe6a1f4f4b..7f0cdb9f580 100644
--- a/src/librustc_codegen_llvm/mono_item.rs
+++ b/src/librustc_codegen_llvm/mono_item.rs
@@ -36,10 +36,10 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
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());
diff --git a/src/librustc_codegen_llvm/va_arg.rs b/src/librustc_codegen_llvm/va_arg.rs
index 8719390b51a..7aceaea4510 100644
--- a/src/librustc_codegen_llvm/va_arg.rs
+++ b/src/librustc_codegen_llvm/va_arg.rs
@@ -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)
}
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index caca1789fc9..f40aa0cb6d1 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -3,7 +3,7 @@ use rustc::ty::{self, Ty, TypeFoldable};
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;
@@ -18,7 +18,7 @@ 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> {
@@ -232,12 +232,21 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
mir::TerminatorKind::Return => {
+ if self.fn_ty.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 | PassMode::Indirect(..) => {
+ PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => {
bx.ret_void();
return;
}
+ PassMode::Ignore(IgnoreMode::CVarArgs) => {
+ bug!("C variadic arguments should never be the return type");
+ }
+
PassMode::Direct(_) | PassMode::Pair(..) => {
let op =
self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE));
@@ -481,7 +490,10 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return;
}
- let extra_args = &args[sig.inputs().len()..];
+ // 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.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)
@@ -658,7 +670,37 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
(&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.variadic && last_arg_idx.map(|x| x == i).unwrap_or(false) {
+ let layout = match 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) {
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index e1528921a59..95cf8cfe2d0 100644
--- a/src/librustc_codegen_ssa/mir/mod.rs
+++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -5,7 +5,7 @@ use rustc::mir::{self, Mir};
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.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?
diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs
index 3cd0c39d413..cd527898977 100644
--- a/src/librustc_codegen_ssa/traits/intrinsic.rs
+++ b/src/librustc_codegen_ssa/traits/intrinsic.rs
@@ -20,4 +20,10 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
fn abort(&mut self);
fn assume(&mut self, val: Self::Value);
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
+ /// Trait method used to inject `va_start` on the "spoofed" `VaList` in
+ /// 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;
}
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index a56c3215f9d..fb279a5d9b8 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -766,8 +766,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
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.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);
}
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 4202d10aa63..f897795d86f 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -1602,10 +1602,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
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.variadic {
+ &sig.inputs()[..sig.inputs().len() - 1]
+ } else {
+ &sig.inputs()[..]
+ };
+ if args.len() < inputs.len() || (args.len() > inputs.len() && !sig.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
diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs
index 50a335bf908..222c43de3b7 100644
--- a/src/librustc_save_analysis/sig.rs
+++ b/src/librustc_save_analysis/sig.rs
@@ -190,6 +190,7 @@ impl Sig for ast::Ty {
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![];
diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs
index 411eb192d90..8ada328a158 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -24,9 +24,17 @@ 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 @@ impl<'a, Ty> ArgType<'a, Ty> {
}
pub fn is_ignore(&self) -> bool {
- self.mode == PassMode::Ignore
+ match self.mode {
+ PassMode::Ignore(_) => true,
+ _ => false
+ }
}
}
diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs
index 2e809571ab1..6ca3ce88bd6 100644
--- a/src/librustc_target/abi/call/x86.rs
+++ b/src/librustc_target/abi/call/x86.rs
@@ -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(..) |
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 6f3dc8b969c..1d99584eec4 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1822,6 +1822,15 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
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);
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 7bf7d825406..aeb43635eb7 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -368,17 +368,27 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
.0;
let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig);
+ let inputs = if fn_sig.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,
diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs
index 3b174b55f2b..924ced2e2a3 100644
--- a/src/librustc_typeck/check/intrinsic.rs
+++ b/src/librustc_typeck/check/intrinsic.rs
@@ -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")
}
}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 72abbae231a..53dcc258c69 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1752,7 +1752,6 @@ impl Clean<Item> for doctree::Function {
pub struct FnDecl {
pub inputs: Arguments,
pub output: FunctionRetTy,
- pub variadic: bool,
pub attrs: Attributes,
}
@@ -1831,7 +1830,6 @@ impl<'a, A: Copy> Clean<FnDecl> for (&'a hir::FnDecl, A)
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 @@ impl<'a, 'tcx> Clean<FnDecl> for (DefId, ty::PolyFnSig<'tcx>) {
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 @@ impl PrimitiveType {
Reference => "reference",
Fn => "fn",
Never => "never",
+ CVarArgs => "...",
}
}
@@ -2518,6 +2518,7 @@ impl Clean<Type> for hir::Ty {
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() {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 4463dad1c8a..d204a179ca6 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -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 @@ impl fmt::Display for clean::FunctionRetTy {
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 @@ impl<'a> fmt::Display for Function<'a> {
}
}
- 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())
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 9c4945d74db..5bae00b9cb8 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -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 {
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 86849f580d0..8efc4689cac 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -401,7 +401,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) => {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 7e900dfeb1e..b58091b57da 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1543,7 +1543,7 @@ impl<'a> Parser<'a> {
// 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 @@ impl<'a> Parser<'a> {
/// 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 @@ impl<'a> Parser<'a> {
/// 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 @@ impl<'a> Parser<'a> {
/// 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_variadic: bool) -> PResult<'a, P<Ty>> {
maybe_whole!(self, NtTy, |x| x);
let lo = self.span;
@@ -1772,6 +1772,15 @@ impl<'a> Parser<'a> {
TyKind::Path(None, path)
}
}
+ } else if self.check(&token::DotDotDot) {
+ if allow_variadic {
+ self.eat(&token::DotDotDot);
+ TyKind::CVarArgs
+ } else {
+ return Err(self.fatal(
+ "only foreign functions are allowed to be variadic"
+ ));
+ }
} else {
let msg = format!("expected type, found {}", self.this_token_descr());
return Err(self.fatal(&msg));
@@ -1959,7 +1968,8 @@ impl<'a> Parser<'a> {
}
/// 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_variadic: bool) -> PResult<'a, Arg> {
maybe_whole!(self, NtArg, |x| x);
if let Ok(Some(_)) = self.parse_self_arg() {
@@ -2008,12 +2018,12 @@ impl<'a> Parser<'a> {
}
self.eat_incorrect_doc_comment("a method argument's type");
- (pat, self.parse_ty()?)
+ (pat, self.parse_ty_common(true, true, allow_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_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 @@ impl<'a> Parser<'a> {
(pat, ty)
}
Err(mut err) => {
+ // If this is a 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 @@ impl<'a> Parser<'a> {
/// 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 @@ impl<'a> Parser<'a> {
}
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
};
@@ -6118,44 +6133,38 @@ impl<'a> Parser<'a> {
&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_variadic) {
+ Ok(arg) => {
+ if let TyKind::CVarArgs = arg.ty.node {
+ variadic = true;
+ if p.token != token::CloseDelim(token::Paren) {
+ let span = p.span;
+ p.span_err(span,
+ "`...` must be last in argument list in 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)))
}
}
}
@@ -6389,7 +6398,8 @@ impl<'a> Parser<'a> {
abi: Abi)
-> PResult<'a, ItemInfo> {
let (ident, mut generics) = self.parse_fn_header()?;
- let decl = self.parse_fn_decl(false)?;
+ let allow_variadic = abi == Abi::C && unsafety == Unsafety::Unsafe;
+ let decl = self.parse_fn_decl(allow_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 };
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index dcf9815f6d1..b3964d0ce9c 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1118,6 +1118,9 @@ impl<'a> State<'a> {
ast::TyKind::Mac(ref m) => {
self.print_mac(m)?;
}
+ ast::TyKind::CVarArgs => {
+ self.s.word("...")?;
+ }
}
self.end()
}
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index a002394c710..dd9f4f74d9e 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -315,7 +315,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
index 00000000000..09c18ed90b2
--- /dev/null
+++ b/src/test/codegen/c-variadic.rs
@@ -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);
+}
diff --git a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
index d55aac1e40f..96a238afaec 100644
--- a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
+++ b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
@@ -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 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize {
}
})
}
+
+#[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
+}
diff --git a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
index 95cf0ef46ca..91b060dce26 100644
--- a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
+++ b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
@@ -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;
}
diff --git a/src/test/rustdoc/variadic.rs b/src/test/rustdoc/variadic.rs
index bd8f1775b3d..5af2aea21fc 100644
--- a/src/test/rustdoc/variadic.rs
+++ b/src/test/rustdoc/variadic.rs
@@ -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/variadic/variadic-ffi.rs b/src/test/ui/c-variadic/variadic-ffi-1.rs
index 61b2ad4bed5..61b2ad4bed5 100644
--- a/src/test/ui/variadic/variadic-ffi.rs
+++ b/src/test/ui/c-variadic/variadic-ffi-1.rs
diff --git a/src/test/ui/variadic/variadic-ffi.stderr b/src/test/ui/c-variadic/variadic-ffi-1.stderr
index 617b1f46ea3..52d7394d6af 100644
--- a/src/test/ui/variadic/variadic-ffi.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-1.stderr
@@ -1,5 +1,5 @@
error[E0045]: variadic function must have C or cdecl calling convention
- --> $DIR/variadic-ffi.rs:5:5
+ --> $DIR/variadic-ffi-1.rs:5:5
|
LL | fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variadics require C or cdecl calling convention
diff --git a/src/test/ui/variadic/variadic-ffi-2.rs b/src/test/ui/c-variadic/variadic-ffi-2.rs
index 224ac16f458..224ac16f458 100644
--- a/src/test/ui/variadic/variadic-ffi-2.rs
+++ b/src/test/ui/c-variadic/variadic-ffi-2.rs
diff --git a/src/test/ui/variadic/variadic-ffi-2.stderr b/src/test/ui/c-variadic/variadic-ffi-2.stderr
index cb2a9f874b7..cb2a9f874b7 100644
--- a/src/test/ui/variadic/variadic-ffi-2.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-2.stderr
diff --git a/src/test/ui/variadic/variadic-ffi-3.rs b/src/test/ui/c-variadic/variadic-ffi-3.rs
index 12b3426d9da..c02d1f54e56 100644
--- a/src/test/ui/variadic/variadic-ffi-3.rs
+++ b/src/test/ui/c-variadic/variadic-ffi-3.rs
@@ -14,12 +14,10 @@ fn main() {
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}`
+ //~| 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
diff --git a/src/test/ui/variadic/variadic-ffi-3.stderr b/src/test/ui/c-variadic/variadic-ffi-3.stderr
index 0fecbbf36b8..82e3c6cd06f 100644
--- a/src/test/ui/variadic/variadic-ffi-3.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-3.stderr
@@ -23,49 +23,49 @@ 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}`
+ found type `for<'r> unsafe extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...) {foo}`
error[E0308]: mismatched types
- --> $DIR/variadic-ffi-3.rs:19:54
+ --> $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 `extern "C" fn(isize, u8, ...)`
+ = 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:24:19
+ --> $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:25:19
+ --> $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:26:19
+ --> $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:27:19
+ --> $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:28:19
+ --> $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:29:19
+ --> $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`
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
index 00000000000..9101be56456
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-4.rs
@@ -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
index 00000000000..1d752be065c
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-4.stderr
@@ -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
index 00000000000..d96482ff4d1
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-5.rs
@@ -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
index 00000000000..2d452872baf
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-5.stderr
@@ -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
index 00000000000..9b5293fad3b
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-6.rs
@@ -0,0 +1,12 @@
+#![crate_type="lib"]
+
+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
index 00000000000..76bd18959a5
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-6.stderr
@@ -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`.
diff --git a/src/test/ui/error-codes/E0617.rs b/src/test/ui/error-codes/E0617.rs
index 9eed1225ab8..51f13c7dbd5 100644
--- a/src/test/ui/error-codes/E0617.rs
+++ b/src/test/ui/error-codes/E0617.rs
@@ -22,7 +22,7 @@ fn main() {
//~^ ERROR can't pass `u16` to variadic function
//~| HELP cast the value to `c_uint`
printf(::std::ptr::null(), printf);
- //~^ ERROR can't pass `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>, ...)`
}
}
diff --git a/src/test/ui/error-codes/E0617.stderr b/src/test/ui/error-codes/E0617.stderr
index 486ca1fa92f..8387d5c7e93 100644
--- a/src/test/ui/error-codes/E0617.stderr
+++ b/src/test/ui/error-codes/E0617.stderr
@@ -28,15 +28,15 @@ error[E0617]: can't pass `u16` to variadic function
LL | printf(::std::ptr::null(), 0u16);
| ^^^^ help: cast the value to `c_uint`: `0u16 as c_uint`
-error[E0617]: can't pass `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/parser/recover-enum2.stderr b/src/test/ui/parser/recover-enum2.stderr
index 2473420a779..b308e644ad9 100644
--- a/src/test/ui/parser/recover-enum2.stderr
+++ b/src/test/ui/parser/recover-enum2.stderr
@@ -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