diff options
author | Dan Robertson <dan@dlrobertson.com> | 2018-11-30 15:53:44 +0000 |
---|---|---|
committer | Dan Robertson <dan@dlrobertson.com> | 2019-02-27 10:21:35 -0500 |
commit | 58147d486bc26eb59d18d8e0d83aa6fe0b467fb9 (patch) | |
tree | 3e25be958b33c7e5727b059511157ac9f1a22722 | |
parent | cd56472cc47981e62c684ceada7922ac3731b785 (diff) | |
download | rust-58147d486bc26eb59d18d8e0d83aa6fe0b467fb9.tar.gz |
Support defining C compatible variadic functions
Add support for defining C compatible variadic functions in unsafe rust
with extern "C".
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(<, 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 |