diff --git a/src/doc/unstable-book/src/language-features/c-variadic.md b/src/doc/unstable-book/src/language-features/c-variadic.md new file mode 100644 index 0000000000000..9e7968d906fbf --- /dev/null +++ b/src/doc/unstable-book/src/language-features/c-variadic.md @@ -0,0 +1,24 @@ +# `c_variadic` + +The tracking issue for this feature is: [#44930] + +[#44930]: https://github.com/rust-lang/rust/issues/44930 + +------------------------ + +The `c_variadic` language feature enables C-variadic functions to be +defined in Rust. The may be called both from within Rust and via FFI. + +## Examples + +```rust +#![feature(c_variadic)] + +pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { + let mut sum = 0; + for _ in 0..n { + sum += args.arg::(); + } + sum +} +``` diff --git a/src/doc/unstable-book/src/library-features/c-variadic.md b/src/doc/unstable-book/src/library-features/c-variadic.md new file mode 100644 index 0000000000000..77762116e6b1c --- /dev/null +++ b/src/doc/unstable-book/src/library-features/c-variadic.md @@ -0,0 +1,26 @@ +# `c_variadic` + +The tracking issue for this feature is: [#44930] + +[#44930]: https://github.com/rust-lang/rust/issues/44930 + +------------------------ + +The `c_variadic` library feature exposes the `VaList` structure, +Rust's analogue of C's `va_list` type. + +## Examples + +```rust +#![feature(c_variadic)] + +use std::ffi::VaList; + +pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize { + let mut sum = 0; + for _ in 0..n { + sum += args.arg::(); + } + sum +} +``` diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 0bc40da7e4f2c..8e4b9a5e8e641 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 df45a085944d6..19bb7f41e1b02 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -74,7 +74,7 @@ const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF; pub struct LoweringContext<'a> { crate_root: Option<&'static str>, - // Used to assign ids to HIR nodes that do not directly correspond to an AST node. + /// Used to assign ids to HIR nodes that do not directly correspond to an AST node. sess: &'a Session, cstore: &'a dyn CrateStore, @@ -107,25 +107,25 @@ pub struct LoweringContext<'a> { /// written at all (e.g., `&T` or `std::cell::Ref`). anonymous_lifetime_mode: AnonymousLifetimeMode, - // Used to create lifetime definitions from in-band lifetime usages. - // e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8` - // When a named lifetime is encountered in a function or impl header and - // has not been defined - // (i.e., it doesn't appear in the in_scope_lifetimes list), it is added - // to this list. The results of this list are then added to the list of - // lifetime definitions in the corresponding impl or function generics. + /// Used to create lifetime definitions from in-band lifetime usages. + /// e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8` + /// When a named lifetime is encountered in a function or impl header and + /// has not been defined + /// (i.e., it doesn't appear in the in_scope_lifetimes list), it is added + /// to this list. The results of this list are then added to the list of + /// lifetime definitions in the corresponding impl or function generics. lifetimes_to_define: Vec<(Span, ParamName)>, - // Whether or not in-band lifetimes are being collected. This is used to - // indicate whether or not we're in a place where new lifetimes will result - // in in-band lifetime definitions, such a function or an impl header, - // including implicit lifetimes from `impl_header_lifetime_elision`. + /// Whether or not in-band lifetimes are being collected. This is used to + /// indicate whether or not we're in a place where new lifetimes will result + /// in in-band lifetime definitions, such a function or an impl header, + /// including implicit lifetimes from `impl_header_lifetime_elision`. is_collecting_in_band_lifetimes: bool, - // Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. - // When `is_collectin_in_band_lifetimes` is true, each lifetime is checked - // against this list to see if it is already in-scope, or if a definition - // needs to be created for it. + /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. + /// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked + /// against this list to see if it is already in-scope, or if a definition + /// needs to be created for it. in_scope_lifetimes: Vec, current_module: NodeId, @@ -954,7 +954,7 @@ impl<'a> LoweringContext<'a> { let decl = FnDecl { inputs: vec![], output, - variadic: false + c_variadic: false }; let body_id = self.record_body(body_expr, Some(&decl)); self.is_generator = prev_is_generator; @@ -1345,6 +1345,12 @@ 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); @@ -2112,7 +2118,7 @@ impl<'a> LoweringContext<'a> { P(hir::FnDecl { inputs, output, - variadic: decl.variadic, + c_variadic: decl.c_variadic, implicit_self: decl.inputs.get(0).map_or( hir::ImplicitSelfKind::None, |arg| { @@ -3967,7 +3973,7 @@ impl<'a> LoweringContext<'a> { let outer_decl = FnDecl { inputs: decl.inputs.clone(), output: FunctionRetTy::Default(fn_decl_span), - variadic: false, + c_variadic: false, }; // We need to lower the declaration outside the new scope, because we // have to conserve the state of being inside a loop condition for the diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 38e6e61592b1a..d8169d05dd4d0 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)] @@ -1865,7 +1868,7 @@ pub struct Arg { pub struct FnDecl { pub inputs: HirVec, pub output: FunctionRetTy, - pub variadic: bool, + pub c_variadic: bool, /// Does the function have an implicit self? pub implicit_self: ImplicitSelfKind, } diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 17d374884134a..dab4b9c824d9a 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() } @@ -2004,7 +2007,7 @@ impl<'a> State<'a> { s.print_type(ty)?; s.end() })?; - if decl.variadic { + if decl.c_variadic { self.s.word(", ...")?; } self.pclose()?; diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index d1161dda1e2ff..775822786900a 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -361,13 +361,14 @@ 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 { inputs, output, - variadic, + c_variadic, implicit_self }); diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index cce1273b7f025..f77a88128f252 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -232,7 +232,7 @@ impl_stable_hash_for!(struct ty::GenSig<'tcx> { impl_stable_hash_for!(struct ty::FnSig<'tcx> { inputs_and_output, - variadic, + c_variadic, unsafety, abi }); diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 31e9eb9b7463c..832391d44162b 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/traits/select.rs b/src/librustc/traits/select.rs index e85b84bce4321..e7cc9618080c2 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1944,7 +1944,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { if let ty::FnSig { unsafety: hir::Unsafety::Normal, abi: Abi::Rust, - variadic: false, + c_variadic: false, .. } = self_ty.fn_sig(self.tcx()).skip_binder() { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index b37b632f4beec..9767396147cf9 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2453,7 +2453,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_fn_sig( params_iter, s.output(), - s.variadic, + s.c_variadic, hir::Unsafety::Normal, abi::Abi::Rust, ) @@ -2779,7 +2779,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn mk_fn_sig(self, inputs: I, output: I::Item, - variadic: bool, + c_variadic: bool, unsafety: hir::Unsafety, abi: abi::Abi) -> , ty::FnSig<'tcx>>>::Output @@ -2788,7 +2788,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { { inputs.chain(iter::once(output)).intern_with(|xs| ty::FnSig { inputs_and_output: self.intern_type_list(xs), - variadic, unsafety, abi + c_variadic, unsafety, abi }) } diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 709dce4589f6b..49ebd202813f6 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> Instance<'tcx> { sig.map_bound(|sig| tcx.mk_fn_sig( iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), sig.output(), - sig.variadic, + sig.c_variadic, sig.unsafety, sig.abi )) diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index b15aa86290166..2940757fa905b 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -147,9 +147,9 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { { let tcx = relation.tcx(); - if a.variadic != b.variadic { + if a.c_variadic != b.c_variadic { return Err(TypeError::VariadicMismatch( - expected_found(relation, &a.variadic, &b.variadic))); + expected_found(relation, &a.c_variadic, &b.c_variadic))); } let unsafety = relation.relate(&a.unsafety, &b.unsafety)?; let abi = relation.relate(&a.abi, &b.abi)?; @@ -171,7 +171,7 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { }); Ok(ty::FnSig { inputs_and_output: tcx.mk_type_list(inputs_and_output)?, - variadic: a.variadic, + c_variadic: a.c_variadic, unsafety, abi, }) diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index a81d5c9d86edd..f1a465e1f1724 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -396,7 +396,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> { tcx.lift(&self.inputs_and_output).map(|x| { ty::FnSig { inputs_and_output: x, - variadic: self.variadic, + c_variadic: self.c_variadic, unsafety: self.unsafety, abi: self.abi, } @@ -832,7 +832,7 @@ BraceStructTypeFoldableImpl! { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> { - inputs_and_output, variadic, unsafety, abi + inputs_and_output, c_variadic, unsafety, abi } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index df8b14b1f10c7..3fd2e38a3d3e5 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -977,13 +977,13 @@ 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. +/// - `c_variadic`: indicates whether this is a C-variadic function. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] pub struct FnSig<'tcx> { pub inputs_and_output: &'tcx List>, - pub variadic: bool, + pub c_variadic: bool, pub unsafety: hir::Unsafety, pub abi: abi::Abi, } @@ -1016,8 +1016,8 @@ impl<'tcx> PolyFnSig<'tcx> { pub fn output(&self) -> ty::Binder> { self.map_bound_ref(|fn_sig| fn_sig.output()) } - pub fn variadic(&self) -> bool { - self.skip_binder().variadic + pub fn c_variadic(&self) -> bool { + self.skip_binder().c_variadic } pub fn unsafety(&self) -> hir::Unsafety { self.skip_binder().unsafety diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index fbe9e3359bfeb..aecef3c5ec71e 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -360,7 +360,7 @@ impl PrintContext { fn fn_sig(&mut self, f: &mut F, inputs: &[Ty<'_>], - variadic: bool, + c_variadic: bool, output: Ty<'_>) -> fmt::Result { write!(f, "(")?; @@ -370,7 +370,7 @@ impl PrintContext { for &ty in inputs { print!(f, self, write(", "), print_display(ty))?; } - if variadic { + if c_variadic { write!(f, ", ...")?; } } @@ -1074,10 +1074,10 @@ define_print! { } write!(f, "fn")?; - cx.fn_sig(f, self.inputs(), self.variadic, self.output()) + cx.fn_sig(f, self.inputs(), self.c_variadic, self.output()) } debug { - write!(f, "({:?}; variadic: {})->{:?}", self.inputs(), self.variadic, self.output()) + write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output()) } } } diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 992149f7a47b5..49c9555a2c682 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); } @@ -422,7 +422,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { let mut inputs = sig.inputs(); let extra_args = if sig.abi == RustCall { - assert!(!sig.variadic && extra_args.is_empty()); + assert!(!sig.c_variadic && extra_args.is_empty()); match sig.inputs().last().unwrap().sty { ty::Tuple(ref tupled_arguments) => { @@ -435,7 +435,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { } } } else { - assert!(sig.variadic || extra_args.is_empty()); + assert!(sig.c_variadic || extra_args.is_empty()); extra_args }; @@ -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| { 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.c_variadic { + match (last_arg_idx, arg_idx) { + (Some(last_idx), Some(cur_idx)) if last_idx == cur_idx => { + let va_list_did = match cx.tcx.lang_items().va_list() { + Some(did) => did, + None => bug!("`va_list` lang item required for C-variadic functions"), + }; + match ty.sty { + ty::Adt(def, _) if def.did == va_list_did => { + // This is the "spoofed" `VaList`. Set the arguments mode + // so that it will be ignored. + arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs); + }, + _ => (), + } + } + _ => {} } } @@ -558,7 +589,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { args: inputs.iter().chain(extra_args).enumerate().map(|(i, ty)| { arg_of(ty, Some(i)) }).collect(), - variadic: sig.variadic, + c_variadic: sig.c_variadic, conv, }; fn_ty.adjust_for_abi(cx, sig.abi); @@ -646,7 +677,9 @@ 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)); @@ -684,7 +717,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { llargument_tys.push(llarg_ty); } - if self.variadic { + if self.c_variadic { cx.type_variadic_func(&llargument_tys, llreturn_ty) } else { cx.type_func(&llargument_tys, llreturn_ty) @@ -733,7 +766,7 @@ 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/debuginfo/type_names.rs b/src/librustc_codegen_llvm/debuginfo/type_names.rs index 176c9b8c542b1..8b218ab39d99b 100644 --- a/src/librustc_codegen_llvm/debuginfo/type_names.rs +++ b/src/librustc_codegen_llvm/debuginfo/type_names.rs @@ -143,7 +143,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, output.pop(); } - if sig.variadic { + if sig.c_variadic { if !sig.inputs().is_empty() { output.push_str(", ..."); } else { diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index d1cbe1d4dd686..3268af396a2f4 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 4fe6a1f4f4b1c..7f0cdb9f58008 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 8719390b51aca..7aceaea4510ce 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 caca1789fc98c..627380ee38ff1 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; @@ -13,730 +13,872 @@ use crate::meth; use crate::traits::*; +use std::borrow::Cow; + use syntax::symbol::Symbol; use syntax_pos::Pos; use super::{FunctionCx, LocalRef}; use super::place::PlaceRef; -use super::operand::OperandRef; +use super::operand::{OperandValue, OperandRef}; use super::operand::OperandValue::{Pair, Ref, Immediate}; -impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_block( - &mut self, - bb: mir::BasicBlock, - ) { - let mut bx = self.build_block(bb); - let data = &self.mir[bb]; +/// Used by `FunctionCx::codegen_terminator` for emitting common patterns +/// e.g., creating a basic block, calling a function, etc. +struct TerminatorCodegenHelper<'a, 'tcx> { + bb: &'a mir::BasicBlock, + terminator: &'a mir::Terminator<'tcx>, + funclet_bb: Option, +} - debug!("codegen_block({:?}={:?})", bb, data); +impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> { + /// Returns the associated funclet from `FunctionCx::funclets` for the + /// `funclet_bb` member if it is not `None`. + fn funclet<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>( + &self, + fx: &'c mut FunctionCx<'b, 'tcx, Bx>, + ) -> Option<&'c Bx::Funclet> { + match self.funclet_bb { + Some(funcl) => fx.funclets[funcl].as_ref(), + None => None, + } + } - for statement in &data.statements { - bx = self.codegen_statement(bx, statement); + fn lltarget<'b, 'c, Bx: BuilderMethods<'b, 'tcx>>( + &self, + fx: &'c mut FunctionCx<'b, 'tcx, Bx>, + target: mir::BasicBlock, + ) -> (Bx::BasicBlock, bool) { + let span = self.terminator.source_info.span; + let lltarget = fx.blocks[target]; + let target_funclet = fx.cleanup_kinds[target].funclet_bb(target); + match (self.funclet_bb, target_funclet) { + (None, None) => (lltarget, false), + (Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) => + (lltarget, false), + // jump *into* cleanup - need a landing pad if GNU + (None, Some(_)) => (fx.landing_pad_to(target), false), + (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator), + (Some(_), Some(_)) => (fx.landing_pad_to(target), true), } + } - self.codegen_terminator(bx, bb, data.terminator()); + /// Create a basic block. + fn llblock<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>( + &self, + fx: &'c mut FunctionCx<'b, 'tcx, Bx>, + target: mir::BasicBlock, + ) -> Bx::BasicBlock { + let (lltarget, is_cleanupret) = self.lltarget(fx, target); + if is_cleanupret { + // MSVC cross-funclet jump - need a trampoline + + debug!("llblock: creating cleanup trampoline for {:?}", target); + let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target); + let mut trampoline = fx.new_block(name); + trampoline.cleanup_ret(self.funclet(fx).unwrap(), + Some(lltarget)); + trampoline.llbb() + } else { + lltarget + } } - fn codegen_terminator( - &mut self, - mut bx: Bx, - bb: mir::BasicBlock, - terminator: &mir::Terminator<'tcx> + fn funclet_br<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>( + &self, + fx: &'c mut FunctionCx<'b, 'tcx, Bx>, + bx: &mut Bx, + target: mir::BasicBlock, ) { - debug!("codegen_terminator: {:?}", terminator); - - // Create the cleanup bundle, if needed. - let tcx = self.cx.tcx(); - let span = terminator.source_info.span; - let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb); + let (lltarget, is_cleanupret) = self.lltarget(fx, target); + if is_cleanupret { + // micro-optimization: generate a `ret` rather than a jump + // to a trampoline. + bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget)); + } else { + bx.br(lltarget); + } + } - // HACK(eddyb) force the right lifetimes, NLL can't figure them out. - fn funclet_closure_factory<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( - funclet_bb: Option - ) -> impl for<'b> Fn( - &'b FunctionCx<'a, 'tcx, Bx>, - ) -> Option<&'b Bx::Funclet> { - move |this| { - match funclet_bb { - Some(funclet_bb) => this.funclets[funclet_bb].as_ref(), - None => None, - } + /// Call `fn_ptr` of `fn_ty` with the arguments `llargs`, the optional + /// return destination `destination` and the cleanup function `cleanup`. + fn do_call<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>( + &self, + fx: &'c mut FunctionCx<'b, 'tcx, Bx>, + bx: &mut Bx, + fn_ty: FnType<'tcx, Ty<'tcx>>, + fn_ptr: Bx::Value, + llargs: &[Bx::Value], + destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, + cleanup: Option, + ) { + if let Some(cleanup) = cleanup { + let ret_bx = if let Some((_, target)) = destination { + fx.blocks[target] + } else { + fx.unreachable_block() + }; + let invokeret = bx.invoke(fn_ptr, + &llargs, + ret_bx, + self.llblock(fx, cleanup), + self.funclet(fx)); + bx.apply_attrs_callsite(&fn_ty, invokeret); + + if let Some((ret_dest, target)) = destination { + let mut ret_bx = fx.build_block(target); + fx.set_debug_loc(&mut ret_bx, self.terminator.source_info); + fx.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret); } - } - let funclet = funclet_closure_factory(funclet_bb); - - let lltarget = |this: &mut Self, target: mir::BasicBlock| { - let lltarget = this.blocks[target]; - let target_funclet = this.cleanup_kinds[target].funclet_bb(target); - match (funclet_bb, target_funclet) { - (None, None) => (lltarget, false), - (Some(f), Some(t_f)) - if f == t_f || !base::wants_msvc_seh(tcx.sess) - => (lltarget, false), - (None, Some(_)) => { - // jump *into* cleanup - need a landing pad if GNU - (this.landing_pad_to(target), false) - } - (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator), - (Some(_), Some(_)) => { - (this.landing_pad_to(target), true) - } + } else { + let llret = bx.call(fn_ptr, &llargs, self.funclet(fx)); + bx.apply_attrs_callsite(&fn_ty, llret); + if fx.mir[*self.bb].is_cleanup { + // Cleanup is always the cold path. Don't inline + // drop glue. Also, when there is a deeply-nested + // struct, there are "symmetry" issues that cause + // exponential inlining - see issue #41696. + bx.do_not_inline(llret); } - }; - - let llblock = |this: &mut Self, target: mir::BasicBlock| { - let (lltarget, is_cleanupret) = lltarget(this, target); - if is_cleanupret { - // MSVC cross-funclet jump - need a trampoline - debug!("llblock: creating cleanup trampoline for {:?}", target); - let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); - let mut trampoline = this.new_block(name); - trampoline.cleanup_ret(funclet(this).unwrap(), Some(lltarget)); - trampoline.llbb() + if let Some((ret_dest, target)) = destination { + fx.store_return(bx, ret_dest, &fn_ty.ret, llret); + self.funclet_br(fx, bx, target); } else { - lltarget + bx.unreachable(); } - }; - - let funclet_br = - |this: &mut Self, bx: &mut Bx, target: mir::BasicBlock| { - let (lltarget, is_cleanupret) = lltarget(this, target); - if is_cleanupret { - // micro-optimization: generate a `ret` rather than a jump - // to a trampoline. - bx.cleanup_ret(funclet(this).unwrap(), Some(lltarget)); - } else { - bx.br(lltarget); - } - }; + } + } +} - let do_call = | - this: &mut Self, - bx: &mut Bx, - fn_ty: FnType<'tcx, Ty<'tcx>>, - fn_ptr: Bx::Value, - llargs: &[Bx::Value], - destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, - cleanup: Option - | { - if let Some(cleanup) = cleanup { - let ret_bx = if let Some((_, target)) = destination { - this.blocks[target] - } else { - this.unreachable_block() - }; - let invokeret = bx.invoke(fn_ptr, - &llargs, - ret_bx, - llblock(this, cleanup), - funclet(this)); - bx.apply_attrs_callsite(&fn_ty, invokeret); - - if let Some((ret_dest, target)) = destination { - let mut ret_bx = this.build_block(target); - this.set_debug_loc(&mut ret_bx, terminator.source_info); - this.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret); - } +/// Codegen implementations for some terminator variants. +impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + /// Generates code for a `Resume` terminator. + fn codegen_resume_terminator<'b>( + &mut self, + helper: TerminatorCodegenHelper<'b, 'tcx>, + mut bx: Bx, + ) { + if let Some(funclet) = helper.funclet(self) { + bx.cleanup_ret(funclet, None); + } else { + let slot = self.get_personality_slot(&mut bx); + let lp0 = slot.project_field(&mut bx, 0); + let lp0 = bx.load_operand(lp0).immediate(); + let lp1 = slot.project_field(&mut bx, 1); + let lp1 = bx.load_operand(lp1).immediate(); + slot.storage_dead(&mut bx); + + if !bx.sess().target.target.options.custom_unwind_resume { + let mut lp = bx.const_undef(self.landing_pad_type()); + lp = bx.insert_value(lp, lp0, 0); + lp = bx.insert_value(lp, lp1, 1); + bx.resume(lp); } else { - let llret = bx.call(fn_ptr, &llargs, funclet(this)); - bx.apply_attrs_callsite(&fn_ty, llret); - if this.mir[bb].is_cleanup { - // Cleanup is always the cold path. Don't inline - // drop glue. Also, when there is a deeply-nested - // struct, there are "symmetry" issues that cause - // exponential inlining - see issue #41696. - bx.do_not_inline(llret); - } - - if let Some((ret_dest, target)) = destination { - this.store_return(bx, ret_dest, &fn_ty.ret, llret); - funclet_br(this, bx, target); - } else { - bx.unreachable(); - } + bx.call(bx.eh_unwind_resume(), &[lp0], + helper.funclet(self)); + bx.unreachable(); } - }; + } + } - self.set_debug_loc(&mut bx, terminator.source_info); - match terminator.kind { - mir::TerminatorKind::Resume => { - if let Some(funclet) = funclet(self) { - bx.cleanup_ret(funclet, None); + fn codegen_switchint_terminator<'b>( + &mut self, + helper: TerminatorCodegenHelper<'b, 'tcx>, + mut bx: Bx, + discr: &mir::Operand<'tcx>, + switch_ty: Ty<'tcx>, + values: &Cow<'tcx, [u128]>, + targets: &Vec, + ) { + let discr = self.codegen_operand(&mut bx, &discr); + if targets.len() == 2 { + // If there are two targets, emit br instead of switch + let lltrue = helper.llblock(self, targets[0]); + let llfalse = helper.llblock(self, targets[1]); + if switch_ty == bx.tcx().types.bool { + // Don't generate trivial icmps when switching on bool + if let [0] = values[..] { + bx.cond_br(discr.immediate(), llfalse, lltrue); } else { - let slot = self.get_personality_slot(&mut bx); - let lp0 = slot.project_field(&mut bx, 0); - let lp0 = bx.load_operand(lp0).immediate(); - let lp1 = slot.project_field(&mut bx, 1); - let lp1 = bx.load_operand(lp1).immediate(); - slot.storage_dead(&mut bx); - - if !bx.sess().target.target.options.custom_unwind_resume { - let mut lp = bx.const_undef(self.landing_pad_type()); - lp = bx.insert_value(lp, lp0, 0); - lp = bx.insert_value(lp, lp1, 1); - bx.resume(lp); - } else { - bx.call(bx.eh_unwind_resume(), &[lp0], funclet(self)); - bx.unreachable(); - } + assert_eq!(&values[..], &[1]); + bx.cond_br(discr.immediate(), lltrue, llfalse); } + } else { + let switch_llty = bx.immediate_backend_type( + bx.layout_of(switch_ty) + ); + let llval = bx.const_uint_big(switch_llty, values[0]); + let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval); + bx.cond_br(cmp, lltrue, llfalse); + } + } else { + let (otherwise, targets) = targets.split_last().unwrap(); + let switch = bx.switch(discr.immediate(), + helper.llblock(self, *otherwise), + values.len()); + let switch_llty = bx.immediate_backend_type( + bx.layout_of(switch_ty) + ); + for (&value, target) in values.iter().zip(targets) { + let llval = bx.const_uint_big(switch_llty, value); + let llbb = helper.llblock(self, *target); + bx.add_case(switch, llval, llbb) } + } + } - mir::TerminatorKind::Abort => { - bx.abort(); - bx.unreachable(); + fn codegen_return_terminator<'b>( + &mut self, + mut bx: Bx, + ) { + if self.fn_ty.c_variadic { + if let Some(va_list) = self.va_list_ref { + bx.va_end(va_list.llval); + } + } + let llval = match self.fn_ty.ret.mode { + PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => { + bx.ret_void(); + return; } - mir::TerminatorKind::Goto { target } => { - funclet_br(self, &mut bx, target); + PassMode::Ignore(IgnoreMode::CVarArgs) => { + bug!("C-variadic arguments should never be the return type"); } - mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - let discr = self.codegen_operand(&mut bx, discr); - if targets.len() == 2 { - // If there are two targets, emit br instead of switch - let lltrue = llblock(self, targets[0]); - let llfalse = llblock(self, targets[1]); - if switch_ty == bx.tcx().types.bool { - // Don't generate trivial icmps when switching on bool - if let [0] = values[..] { - bx.cond_br(discr.immediate(), llfalse, lltrue); - } else { - assert_eq!(&values[..], &[1]); - bx.cond_br(discr.immediate(), lltrue, llfalse); - } - } else { - let switch_llty = bx.immediate_backend_type( - bx.layout_of(switch_ty) - ); - let llval = bx.const_uint_big(switch_llty, values[0]); - let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval); - bx.cond_br(cmp, lltrue, llfalse); - } + PassMode::Direct(_) | PassMode::Pair(..) => { + let op = + self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE)); + if let Ref(llval, _, align) = op.val { + bx.load(llval, align) } else { - let (otherwise, targets) = targets.split_last().unwrap(); - let switch = bx.switch(discr.immediate(), - llblock(self, *otherwise), - values.len()); - let switch_llty = bx.immediate_backend_type( - bx.layout_of(switch_ty) - ); - for (&value, target) in values.iter().zip(targets) { - let llval = bx.const_uint_big(switch_llty, value); - let llbb = llblock(self, *target); - bx.add_case(switch, llval, llbb) - } + op.immediate_or_packed_pair(&mut bx) } } - mir::TerminatorKind::Return => { - let llval = match self.fn_ty.ret.mode { - PassMode::Ignore | PassMode::Indirect(..) => { - bx.ret_void(); - return; - } - - PassMode::Direct(_) | PassMode::Pair(..) => { - let op = - self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE)); - if let Ref(llval, _, align) = op.val { - bx.load(llval, align) - } else { - op.immediate_or_packed_pair(&mut bx) + PassMode::Cast(cast_ty) => { + let op = match self.locals[mir::RETURN_PLACE] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Place(cg_place) => { + OperandRef { + val: Ref(cg_place.llval, None, cg_place.align), + layout: cg_place.layout } } - - PassMode::Cast(cast_ty) => { - let op = match self.locals[mir::RETURN_PLACE] { - LocalRef::Operand(Some(op)) => op, - LocalRef::Operand(None) => bug!("use of return before def"), - LocalRef::Place(cg_place) => { - OperandRef { - val: Ref(cg_place.llval, None, cg_place.align), - layout: cg_place.layout - } - } - LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), - }; - let llslot = match op.val { - Immediate(_) | Pair(..) => { - let scratch = - PlaceRef::alloca(&mut bx, self.fn_ty.ret.layout, "ret"); - op.val.store(&mut bx, scratch); - scratch.llval - } - Ref(llval, _, align) => { - assert_eq!(align, op.layout.align.abi, - "return place is unaligned!"); - llval - } - }; - let addr = bx.pointercast(llslot, bx.type_ptr_to( - bx.cast_backend_type(&cast_ty) - )); - bx.load(addr, self.fn_ty.ret.layout.align.abi) + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let scratch = + PlaceRef::alloca(&mut bx, self.fn_ty.ret.layout, "ret"); + op.val.store(&mut bx, scratch); + scratch.llval + } + Ref(llval, _, align) => { + assert_eq!(align, op.layout.align.abi, + "return place is unaligned!"); + llval } }; - bx.ret(llval); + let addr = bx.pointercast(llslot, bx.type_ptr_to( + bx.cast_backend_type(&cast_ty) + )); + bx.load(addr, self.fn_ty.ret.layout.align.abi) } + }; + bx.ret(llval); + } - mir::TerminatorKind::Unreachable => { - bx.unreachable(); + + fn codegen_drop_terminator<'b>( + &mut self, + helper: TerminatorCodegenHelper<'b, 'tcx>, + mut bx: Bx, + location: &mir::Place<'tcx>, + target: mir::BasicBlock, + unwind: Option, + ) { + let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx()); + let ty = self.monomorphize(&ty); + let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty); + + if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + // we don't actually need to drop anything. + helper.funclet_br(self, &mut bx, target); + return + } + + let place = self.codegen_place(&mut bx, location); + let (args1, args2); + let mut args = if let Some(llextra) = place.llextra { + args2 = [place.llval, llextra]; + &args2[..] + } else { + args1 = [place.llval]; + &args1[..] + }; + let (drop_fn, fn_ty) = match ty.sty { + ty::Dynamic(..) => { + let sig = drop_fn.fn_sig(self.cx.tcx()); + let sig = self.cx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + let fn_ty = bx.new_vtable(sig, &[]); + let vtable = args[1]; + args = &args[..1]; + (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_ty), fn_ty) + } + _ => { + (bx.get_fn(drop_fn), + bx.fn_type_of_instance(&drop_fn)) } + }; + helper.do_call(self, &mut bx, fn_ty, drop_fn, args, + Some((ReturnDest::Nothing, target)), + unwind); + } - mir::TerminatorKind::Drop { ref location, target, unwind } => { - let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx()); - let ty = self.monomorphize(&ty); - let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty); - - if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { - // we don't actually need to drop anything. - funclet_br(self, &mut bx, target); - return - } + fn codegen_assert_terminator<'b>( + &mut self, + helper: TerminatorCodegenHelper<'b, 'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + cond: &mir::Operand<'tcx>, + expected: bool, + msg: &mir::AssertMessage<'tcx>, + target: mir::BasicBlock, + cleanup: Option, + ) { + let span = terminator.source_info.span; + let cond = self.codegen_operand(&mut bx, cond).immediate(); + let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1); + + // This case can currently arise only from functions marked + // with #[rustc_inherit_overflow_checks] and inlined from + // another crate (mostly core::num generic/#[inline] fns), + // while the current crate doesn't use overflow checks. + // NOTE: Unlike binops, negation doesn't have its own + // checked operation, just a comparison with the minimum + // value, so we have to check for the assert message. + if !bx.check_overflow() { + if let mir::interpret::EvalErrorKind::OverflowNeg = *msg { + const_cond = Some(expected); + } + } - let place = self.codegen_place(&mut bx, location); - let (args1, args2); - let mut args = if let Some(llextra) = place.llextra { - args2 = [place.llval, llextra]; - &args2[..] - } else { - args1 = [place.llval]; - &args1[..] - }; - let (drop_fn, fn_ty) = match ty.sty { - ty::Dynamic(..) => { - let sig = drop_fn.fn_sig(tcx); - let sig = tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - let fn_ty = bx.new_vtable(sig, &[]); - let vtable = args[1]; - args = &args[..1]; - (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_ty), fn_ty) - } - _ => { - (bx.get_fn(drop_fn), - bx.fn_type_of_instance(&drop_fn)) - } - }; - do_call(self, &mut bx, fn_ty, drop_fn, args, - Some((ReturnDest::Nothing, target)), - unwind); + // Don't codegen the panic block if success if known. + if const_cond == Some(expected) { + helper.funclet_br(self, &mut bx, target); + return; + } + + // Pass the condition through llvm.expect for branch hinting. + let cond = bx.expect(cond, expected); + + // Create the failure block and the conditional branch to it. + let lltarget = helper.llblock(self, target); + let panic_block = self.new_block("panic"); + if expected { + bx.cond_br(cond, lltarget, panic_block.llbb()); + } else { + bx.cond_br(cond, panic_block.llbb(), lltarget); + } + + // After this point, bx is the block for the call to panic. + bx = panic_block; + self.set_debug_loc(&mut bx, terminator.source_info); + + // Get the location information. + let loc = bx.sess().source_map().lookup_char_pos(span.lo()); + let filename = Symbol::intern(&loc.file.name.to_string()).as_str(); + let filename = bx.const_str_slice(filename); + let line = bx.const_u32(loc.line as u32); + let col = bx.const_u32(loc.col.to_usize() as u32 + 1); + let align = self.cx.tcx().data_layout.aggregate_align.abi + .max(self.cx.tcx().data_layout.i32_align.abi) + .max(self.cx.tcx().data_layout.pointer_align.abi); + + // Put together the arguments to the panic entry point. + let (lang_item, args) = match *msg { + EvalErrorKind::BoundsCheck { ref len, ref index } => { + let len = self.codegen_operand(&mut bx, len).immediate(); + let index = self.codegen_operand(&mut bx, index).immediate(); + + let file_line_col = bx.const_struct(&[filename, line, col], false); + let file_line_col = bx.static_addr_of( + file_line_col, + align, + Some("panic_bounds_check_loc") + ); + (lang_items::PanicBoundsCheckFnLangItem, + vec![file_line_col, index, len]) + } + _ => { + let str = msg.description(); + let msg_str = Symbol::intern(str).as_str(); + let msg_str = bx.const_str_slice(msg_str); + let msg_file_line_col = bx.const_struct( + &[msg_str, filename, line, col], + false + ); + let msg_file_line_col = bx.static_addr_of( + msg_file_line_col, + align, + Some("panic_loc") + ); + (lang_items::PanicFnLangItem, + vec![msg_file_line_col]) } + }; - mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { - let cond = self.codegen_operand(&mut bx, cond).immediate(); - let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1); - - // This case can currently arise only from functions marked - // with #[rustc_inherit_overflow_checks] and inlined from - // another crate (mostly core::num generic/#[inline] fns), - // while the current crate doesn't use overflow checks. - // NOTE: Unlike binops, negation doesn't have its own - // checked operation, just a comparison with the minimum - // value, so we have to check for the assert message. - if !bx.check_overflow() { - if let mir::interpret::EvalErrorKind::OverflowNeg = *msg { - const_cond = Some(expected); - } - } + // Obtain the panic entry point. + let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_ty = bx.fn_type_of_instance(&instance); + let llfn = bx.get_fn(instance); - // Don't codegen the panic block if success if known. - if const_cond == Some(expected) { - funclet_br(self, &mut bx, target); - return; - } + // Codegen the actual panic invoke/call. + helper.do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup); + } - // Pass the condition through llvm.expect for branch hinting. - let cond = bx.expect(cond, expected); + fn codegen_call_terminator<'b>( + &mut self, + helper: TerminatorCodegenHelper<'b, 'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + func: &mir::Operand<'tcx>, + args: &Vec>, + destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, + cleanup: Option, + ) { + let span = terminator.source_info.span; + // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. + let callee = self.codegen_operand(&mut bx, func); + + let (instance, mut llfn) = match callee.layout.ty.sty { + ty::FnDef(def_id, substs) => { + (Some(ty::Instance::resolve(bx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs).unwrap()), + None) + } + ty::FnPtr(_) => { + (None, Some(callee.immediate())) + } + _ => bug!("{} is not callable", callee.layout.ty), + }; + let def = instance.map(|i| i.def); + let sig = callee.layout.ty.fn_sig(bx.tcx()); + let sig = bx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + let abi = sig.abi; + + // Handle intrinsics old codegen wants Expr's for, ourselves. + let intrinsic = match def { + Some(ty::InstanceDef::Intrinsic(def_id)) => + Some(bx.tcx().item_name(def_id).as_str()), + _ => None + }; + let intrinsic = intrinsic.as_ref().map(|s| &s[..]); - // Create the failure block and the conditional branch to it. - let lltarget = llblock(self, target); - let panic_block = self.new_block("panic"); - if expected { - bx.cond_br(cond, lltarget, panic_block.llbb()); - } else { - bx.cond_br(cond, panic_block.llbb(), lltarget); - } + if intrinsic == Some("transmute") { + if let Some(destination_ref) = destination.as_ref() { + let &(ref dest, target) = destination_ref; + self.codegen_transmute(&mut bx, &args[0], dest); + helper.funclet_br(self, &mut bx, target); + } else { + // If we are trying to transmute to an uninhabited type, + // it is likely there is no allotted destination. In fact, + // transmuting to an uninhabited type is UB, which means + // we can do what we like. Here, we declare that transmuting + // into an uninhabited type is impossible, so anything following + // it must be unreachable. + assert_eq!(bx.layout_of(sig.output()).abi, layout::Abi::Uninhabited); + bx.unreachable(); + } + return; + } - // After this point, bx is the block for the call to panic. - bx = panic_block; - self.set_debug_loc(&mut bx, terminator.source_info); + // The "spoofed" `VaList` added to a C-variadic functions signature + // should not be included in the `extra_args` calculation. + let extra_args_start_idx = sig.inputs().len() - if sig.c_variadic { 1 } else { 0 }; + let extra_args = &args[extra_args_start_idx..]; + let extra_args = extra_args.iter().map(|op_arg| { + let op_ty = op_arg.ty(self.mir, bx.tcx()); + self.monomorphize(&op_ty) + }).collect::>(); + + let fn_ty = match def { + Some(ty::InstanceDef::Virtual(..)) => { + bx.new_vtable(sig, &extra_args) + } + Some(ty::InstanceDef::DropGlue(_, None)) => { + // Empty drop glue; a no-op. + let &(_, target) = destination.as_ref().unwrap(); + helper.funclet_br(self, &mut bx, target); + return; + } + _ => bx.new_fn_type(sig, &extra_args) + }; - // Get the location information. + // Emit a panic or a no-op for `panic_if_uninhabited`. + if intrinsic == Some("panic_if_uninhabited") { + let ty = instance.unwrap().substs.type_at(0); + let layout = bx.layout_of(ty); + if layout.abi.is_uninhabited() { let loc = bx.sess().source_map().lookup_char_pos(span.lo()); let filename = Symbol::intern(&loc.file.name.to_string()).as_str(); let filename = bx.const_str_slice(filename); let line = bx.const_u32(loc.line as u32); let col = bx.const_u32(loc.col.to_usize() as u32 + 1); - let align = tcx.data_layout.aggregate_align.abi - .max(tcx.data_layout.i32_align.abi) - .max(tcx.data_layout.pointer_align.abi); - - // Put together the arguments to the panic entry point. - let (lang_item, args) = match *msg { - EvalErrorKind::BoundsCheck { ref len, ref index } => { - let len = self.codegen_operand(&mut bx, len).immediate(); - let index = self.codegen_operand(&mut bx, index).immediate(); - - let file_line_col = bx.const_struct(&[filename, line, col], false); - let file_line_col = bx.static_addr_of( - file_line_col, - align, - Some("panic_bounds_check_loc") - ); - (lang_items::PanicBoundsCheckFnLangItem, - vec![file_line_col, index, len]) - } - _ => { - let str = msg.description(); - let msg_str = Symbol::intern(str).as_str(); - let msg_str = bx.const_str_slice(msg_str); - let msg_file_line_col = bx.const_struct( - &[msg_str, filename, line, col], - false - ); - let msg_file_line_col = bx.static_addr_of( - msg_file_line_col, - align, - Some("panic_loc") - ); - (lang_items::PanicFnLangItem, - vec![msg_file_line_col]) - } - }; + let align = self.cx.tcx().data_layout.aggregate_align.abi + .max(self.cx.tcx().data_layout.i32_align.abi) + .max(self.cx.tcx().data_layout.pointer_align.abi); + + let str = format!( + "Attempted to instantiate uninhabited type {}", + ty + ); + let msg_str = Symbol::intern(&str).as_str(); + let msg_str = bx.const_str_slice(msg_str); + let msg_file_line_col = bx.const_struct( + &[msg_str, filename, line, col], + false, + ); + let msg_file_line_col = bx.static_addr_of( + msg_file_line_col, + align, + Some("panic_loc"), + ); // Obtain the panic entry point. - let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); + let def_id = + common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem); let instance = ty::Instance::mono(bx.tcx(), def_id); let fn_ty = bx.fn_type_of_instance(&instance); let llfn = bx.get_fn(instance); // Codegen the actual panic invoke/call. - do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup); + helper.do_call( + self, + &mut bx, + fn_ty, + llfn, + &[msg_file_line_col], + destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)), + cleanup, + ); + } else { + // a NOP + helper.funclet_br(self, &mut bx, destination.as_ref().unwrap().1) } + return; + } - mir::TerminatorKind::DropAndReplace { .. } => { - bug!("undesugared DropAndReplace in codegen: {:?}", terminator); - } + // The arguments we'll be passing. Plus one to account for outptr, if used. + let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; + let mut llargs = Vec::with_capacity(arg_count); - mir::TerminatorKind::Call { - ref func, - ref args, - ref destination, - cleanup, - from_hir_call: _ - } => { - // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. - let callee = self.codegen_operand(&mut bx, func); - - let (instance, mut llfn) = match callee.layout.ty.sty { - ty::FnDef(def_id, substs) => { - (Some(ty::Instance::resolve(bx.tcx(), - ty::ParamEnv::reveal_all(), - def_id, - substs).unwrap()), - None) - } - ty::FnPtr(_) => { - (None, Some(callee.immediate())) - } - _ => bug!("{} is not callable", callee.layout.ty) - }; - let def = instance.map(|i| i.def); - let sig = callee.layout.ty.fn_sig(bx.tcx()); - let sig = bx.tcx().normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - let abi = sig.abi; + // Prepare the return value destination + let ret_dest = if let Some((ref dest, _)) = *destination { + let is_intrinsic = intrinsic.is_some(); + self.make_return_dest(&mut bx, dest, &fn_ty.ret, &mut llargs, + is_intrinsic) + } else { + ReturnDest::Nothing + }; - // Handle intrinsics old codegen wants Expr's for, ourselves. - let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) - => Some(bx.tcx().item_name(def_id).as_str()), - _ => None - }; - let intrinsic = intrinsic.as_ref().map(|s| &s[..]); + if intrinsic.is_some() && intrinsic != Some("drop_in_place") { + let dest = match ret_dest { + _ if fn_ty.ret.is_indirect() => llargs[0], + ReturnDest::Nothing => + bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret))), + ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => + dst.llval, + ReturnDest::DirectOperand(_) => + bug!("Cannot use direct operand with an intrinsic call"), + }; - if intrinsic == Some("transmute") { - if let Some(destination_ref) = destination.as_ref() { - let &(ref dest, target) = destination_ref; - self.codegen_transmute(&mut bx, &args[0], dest); - funclet_br(self, &mut bx, target); - } else { - // If we are trying to transmute to an uninhabited type, - // it is likely there is no allotted destination. In fact, - // transmuting to an uninhabited type is UB, which means - // we can do what we like. Here, we declare that transmuting - // into an uninhabited type is impossible, so anything following - // it must be unreachable. - assert_eq!(bx.layout_of(sig.output()).abi, layout::Abi::Uninhabited); - bx.unreachable(); + let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { + // The indices passed to simd_shuffle* in the + // third argument must be constant. This is + // checked by const-qualification, which also + // promotes any complex rvalues to constants. + if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { + match *arg { + // The shuffle array argument is usually not an explicit constant, + // but specified directly in the code. This means it gets promoted + // and we can then extract the value by evaluating the promoted. + mir::Operand::Copy(mir::Place::Promoted(box(index, ty))) | + mir::Operand::Move(mir::Place::Promoted(box(index, ty))) => { + let param_env = ty::ParamEnv::reveal_all(); + let cid = mir::interpret::GlobalId { + instance: self.instance, + promoted: Some(index), + }; + let c = bx.tcx().const_eval(param_env.and(cid)); + let (llval, ty) = self.simd_shuffle_indices( + &bx, + terminator.source_info.span, + ty, + c, + ); + return OperandRef { + val: Immediate(llval), + layout: bx.layout_of(ty), + }; + + } + mir::Operand::Copy(_) | + mir::Operand::Move(_) => { + span_bug!(span, "shuffle indices must be constant"); + } + mir::Operand::Constant(ref constant) => { + let c = self.eval_mir_constant(&bx, constant); + let (llval, ty) = self.simd_shuffle_indices( + &bx, + constant.span, + constant.ty, + c, + ); + return OperandRef { + val: Immediate(llval), + layout: bx.layout_of(ty) + }; + } } - return; } - let extra_args = &args[sig.inputs().len()..]; - let extra_args = extra_args.iter().map(|op_arg| { - let op_ty = op_arg.ty(self.mir, bx.tcx()); - self.monomorphize(&op_ty) - }).collect::>(); + self.codegen_operand(&mut bx, arg) + }).collect(); - let fn_ty = match def { - Some(ty::InstanceDef::Virtual(..)) => { - bx.new_vtable(sig, &extra_args) - } - Some(ty::InstanceDef::DropGlue(_, None)) => { - // empty drop glue - a nop. - let &(_, target) = destination.as_ref().unwrap(); - funclet_br(self, &mut bx, target); - return; - } - _ => bx.new_fn_type(sig, &extra_args) + + let callee_ty = instance.as_ref().unwrap().ty(bx.tcx()); + bx.codegen_intrinsic_call(callee_ty, &fn_ty, &args, dest, + terminator.source_info.span); + + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + self.store_return(&mut bx, ret_dest, &fn_ty.ret, dst.llval); + } + + if let Some((_, target)) = *destination { + helper.funclet_br(self, &mut bx, target); + } else { + bx.unreachable(); + } + + return; + } + + // Split the rust-call tupled arguments off. + let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { + let (tup, args) = args.split_last().unwrap(); + (args, Some(tup)) + } else { + (&args[..], None) + }; + + // Useful determining if the current argument is the "spoofed" `VaList` + let last_arg_idx = if sig.inputs().is_empty() { + None + } else { + Some(sig.inputs().len() - 1) + }; + 'make_args: for (i, arg) in first_args.iter().enumerate() { + // If this is a C-variadic function the function signature contains + // an "spoofed" `VaList`. This argument is ignored, but we need to + // populate it with a dummy operand so that the users real arguments + // are not overwritten. + let i = if sig.c_variadic && last_arg_idx.map(|x| x == i).unwrap_or(false) { + let layout = match self.cx.tcx().lang_items().va_list() { + Some(did) => bx.cx().layout_of(bx.tcx().type_of(did)), + None => bug!("`va_list` language item required for C-variadics"), }; + let op = OperandRef { + val: OperandValue::Immediate( + bx.cx().const_undef(bx.cx().immediate_backend_type(layout) + )), + layout: layout, + }; + self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]); + if i + 1 < fn_ty.args.len() { + i + 1 + } else { + break 'make_args + } + } else { + i + }; + let mut op = self.codegen_operand(&mut bx, arg); + + if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { + if let Pair(..) = op.val { + // In the case of Rc, we need to explicitly pass a + // *mut RcBox with a Scalar (not ScalarPair) ABI. This is a hack + // that is understood elsewhere in the compiler as a method on + // `dyn Trait`. + // To get a `*mut RcBox`, we just keep unwrapping newtypes until + // we get a value of a built-in pointer type + 'descend_newtypes: while !op.layout.ty.is_unsafe_ptr() + && !op.layout.ty.is_region_ptr() + { + 'iter_fields: for i in 0..op.layout.fields.count() { + let field = op.extract_field(&mut bx, i); + if !field.layout.is_zst() { + // we found the one non-zero-sized field that is allowed + // now find *its* non-zero-sized field, or stop if it's a + // pointer + op = field; + continue 'descend_newtypes + } + } - // emit a panic or a NOP for `panic_if_uninhabited` - if intrinsic == Some("panic_if_uninhabited") { - let ty = instance.unwrap().substs.type_at(0); - let layout = bx.layout_of(ty); - if layout.abi.is_uninhabited() { - let loc = bx.sess().source_map().lookup_char_pos(span.lo()); - let filename = Symbol::intern(&loc.file.name.to_string()).as_str(); - let filename = bx.const_str_slice(filename); - let line = bx.const_u32(loc.line as u32); - let col = bx.const_u32(loc.col.to_usize() as u32 + 1); - let align = tcx.data_layout.aggregate_align.abi - .max(tcx.data_layout.i32_align.abi) - .max(tcx.data_layout.pointer_align.abi); - - let str = format!( - "Attempted to instantiate uninhabited type {}", - ty - ); - let msg_str = Symbol::intern(&str).as_str(); - let msg_str = bx.const_str_slice(msg_str); - let msg_file_line_col = bx.const_struct( - &[msg_str, filename, line, col], - false, - ); - let msg_file_line_col = bx.static_addr_of( - msg_file_line_col, - align, - Some("panic_loc"), - ); - - // Obtain the panic entry point. - let def_id = - common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem); - let instance = ty::Instance::mono(bx.tcx(), def_id); - let fn_ty = bx.fn_type_of_instance(&instance); - let llfn = bx.get_fn(instance); - - // Codegen the actual panic invoke/call. - do_call( - self, - &mut bx, - fn_ty, - llfn, - &[msg_file_line_col], - destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)), - cleanup, - ); - } else { - // a NOP - funclet_br(self, &mut bx, destination.as_ref().unwrap().1); + span_bug!(span, "receiver has no non-zero-sized fields {:?}", op); } - return; + + // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its + // data pointer and vtable. Look up the method in the vtable, and pass + // the data pointer as the first argument + match op.val { + Pair(data_ptr, meta) => { + llfn = Some(meth::VirtualIndex::from_index(idx) + .get_fn(&mut bx, meta, &fn_ty)); + llargs.push(data_ptr); + continue 'make_args + } + other => bug!("expected a Pair, got {:?}", other), + } + } else if let Ref(data_ptr, Some(meta), _) = op.val { + // by-value dynamic dispatch + llfn = Some(meth::VirtualIndex::from_index(idx) + .get_fn(&mut bx, meta, &fn_ty)); + llargs.push(data_ptr); + continue; + } else { + span_bug!(span, "can't codegen a virtual call on {:?}", op); } + } - // The arguments we'll be passing. Plus one to account for outptr, if used. - let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; - let mut llargs = Vec::with_capacity(arg_count); + // The callee needs to own the argument memory if we pass it + // by-ref, so make a local copy of non-immediate constants. + match (arg, op.val) { + (&mir::Operand::Copy(_), Ref(_, None, _)) | + (&mir::Operand::Constant(_), Ref(_, None, _)) => { + let tmp = PlaceRef::alloca(&mut bx, op.layout, "const"); + op.val.store(&mut bx, tmp); + op.val = Ref(tmp.llval, None, tmp.align); + } + _ => {} + } - // Prepare the return value destination - let ret_dest = if let Some((ref dest, _)) = *destination { - let is_intrinsic = intrinsic.is_some(); - self.make_return_dest(&mut bx, dest, &fn_ty.ret, &mut llargs, - is_intrinsic) - } else { - ReturnDest::Nothing - }; + self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]); + } + if let Some(tup) = untuple { + self.codegen_arguments_untupled(&mut bx, tup, &mut llargs, + &fn_ty.args[first_args.len()..]) + } - if intrinsic.is_some() && intrinsic != Some("drop_in_place") { - let dest = match ret_dest { - _ if fn_ty.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => { - bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret))) - } - ReturnDest::IndirectOperand(dst, _) | - ReturnDest::Store(dst) => dst.llval, - ReturnDest::DirectOperand(_) => - bug!("Cannot use direct operand with an intrinsic call") - }; + let fn_ptr = match (llfn, instance) { + (Some(llfn), _) => llfn, + (None, Some(instance)) => bx.get_fn(instance), + _ => span_bug!(span, "no llfn for call"), + }; - let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { - // The indices passed to simd_shuffle* in the - // third argument must be constant. This is - // checked by const-qualification, which also - // promotes any complex rvalues to constants. - if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { - match *arg { - // The shuffle array argument is usually not an explicit constant, - // but specified directly in the code. This means it gets promoted - // and we can then extract the value by evaluating the promoted. - mir::Operand::Copy(mir::Place::Promoted(box(index, ty))) | - mir::Operand::Move(mir::Place::Promoted(box(index, ty))) => { - let param_env = ty::ParamEnv::reveal_all(); - let cid = mir::interpret::GlobalId { - instance: self.instance, - promoted: Some(index), - }; - let c = bx.tcx().const_eval(param_env.and(cid)); - let (llval, ty) = self.simd_shuffle_indices( - &bx, - terminator.source_info.span, - ty, - c, - ); - return OperandRef { - val: Immediate(llval), - layout: bx.layout_of(ty), - }; - - }, - mir::Operand::Copy(_) | - mir::Operand::Move(_) => { - span_bug!(span, "shuffle indices must be constant"); - } - mir::Operand::Constant(ref constant) => { - let c = self.eval_mir_constant(&bx, constant); - let (llval, ty) = self.simd_shuffle_indices( - &bx, - constant.span, - constant.ty, - c, - ); - return OperandRef { - val: Immediate(llval), - layout: bx.layout_of(ty) - }; - } - } - } + helper.do_call(self, &mut bx, fn_ty, fn_ptr, &llargs, + destination.as_ref().map(|&(_, target)| (ret_dest, target)), + cleanup); + } +} - self.codegen_operand(&mut bx, arg) - }).collect(); +impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn codegen_block( + &mut self, + bb: mir::BasicBlock, + ) { + let mut bx = self.build_block(bb); + let data = &self.mir[bb]; + debug!("codegen_block({:?}={:?})", bb, data); - let callee_ty = instance.as_ref().unwrap().ty(bx.tcx()); - bx.codegen_intrinsic_call(callee_ty, &fn_ty, &args, dest, - terminator.source_info.span); + for statement in &data.statements { + bx = self.codegen_statement(bx, statement); + } - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(&mut bx, ret_dest, &fn_ty.ret, dst.llval); - } + self.codegen_terminator(bx, bb, data.terminator()); + } - if let Some((_, target)) = *destination { - funclet_br(self, &mut bx, target); - } else { - bx.unreachable(); - } + fn codegen_terminator( + &mut self, + mut bx: Bx, + bb: mir::BasicBlock, + terminator: &mir::Terminator<'tcx> + ) { + debug!("codegen_terminator: {:?}", terminator); - return; - } + // Create the cleanup bundle, if needed. + let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb); + let helper = TerminatorCodegenHelper { + bb: &bb, terminator, funclet_bb + }; - // Split the rust-call tupled arguments off. - let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { - let (tup, args) = args.split_last().unwrap(); - (args, Some(tup)) - } else { - (&args[..], None) - }; + self.set_debug_loc(&mut bx, terminator.source_info); + match terminator.kind { + mir::TerminatorKind::Resume => { + self.codegen_resume_terminator(helper, bx) + } - 'make_args: for (i, arg) in first_args.iter().enumerate() { - let mut op = self.codegen_operand(&mut bx, arg); - - if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { - if let Pair(..) = op.val { - // In the case of Rc, we need to explicitly pass a - // *mut RcBox with a Scalar (not ScalarPair) ABI. This is a hack - // that is understood elsewhere in the compiler as a method on - // `dyn Trait`. - // To get a `*mut RcBox`, we just keep unwrapping newtypes until - // we get a value of a built-in pointer type - 'descend_newtypes: while !op.layout.ty.is_unsafe_ptr() - && !op.layout.ty.is_region_ptr() - { - 'iter_fields: for i in 0..op.layout.fields.count() { - let field = op.extract_field(&mut bx, i); - if !field.layout.is_zst() { - // we found the one non-zero-sized field that is allowed - // now find *its* non-zero-sized field, or stop if it's a - // pointer - op = field; - continue 'descend_newtypes - } - } - - span_bug!(span, "receiver has no non-zero-sized fields {:?}", op); - } + mir::TerminatorKind::Abort => { + bx.abort(); + bx.unreachable(); + } - // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its - // data pointer and vtable. Look up the method in the vtable, and pass - // the data pointer as the first argument - match op.val { - Pair(data_ptr, meta) => { - llfn = Some(meth::VirtualIndex::from_index(idx) - .get_fn(&mut bx, meta, &fn_ty)); - llargs.push(data_ptr); - continue 'make_args - } - other => bug!("expected a Pair, got {:?}", other) - } - } else if let Ref(data_ptr, Some(meta), _) = op.val { - // by-value dynamic dispatch - llfn = Some(meth::VirtualIndex::from_index(idx) - .get_fn(&mut bx, meta, &fn_ty)); - llargs.push(data_ptr); - continue; - } else { - span_bug!(span, "can't codegen a virtual call on {:?}", op); - } - } + mir::TerminatorKind::Goto { target } => { + helper.funclet_br(self, &mut bx, target); + } - // The callee needs to own the argument memory if we pass it - // by-ref, so make a local copy of non-immediate constants. - match (arg, op.val) { - (&mir::Operand::Copy(_), Ref(_, None, _)) | - (&mir::Operand::Constant(_), Ref(_, None, _)) => { - let tmp = PlaceRef::alloca(&mut bx, op.layout, "const"); - op.val.store(&mut bx, tmp); - op.val = Ref(tmp.llval, None, tmp.align); - } - _ => {} - } + mir::TerminatorKind::SwitchInt { + ref discr, switch_ty, ref values, ref targets + } => { + self.codegen_switchint_terminator(helper, bx, discr, switch_ty, + values, targets); + } - self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]); - } - if let Some(tup) = untuple { - self.codegen_arguments_untupled(&mut bx, tup, &mut llargs, - &fn_ty.args[first_args.len()..]) - } + mir::TerminatorKind::Return => { + self.codegen_return_terminator(bx); + } - let fn_ptr = match (llfn, instance) { - (Some(llfn), _) => llfn, - (None, Some(instance)) => bx.get_fn(instance), - _ => span_bug!(span, "no llfn for call"), - }; + mir::TerminatorKind::Unreachable => { + bx.unreachable(); + } - do_call(self, &mut bx, fn_ty, fn_ptr, &llargs, - destination.as_ref().map(|&(_, target)| (ret_dest, target)), - cleanup); + mir::TerminatorKind::Drop { ref location, target, unwind } => { + self.codegen_drop_terminator(helper, bx, location, target, unwind); + } + + mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { + self.codegen_assert_terminator(helper, bx, terminator, cond, + expected, msg, target, cleanup); + } + + mir::TerminatorKind::DropAndReplace { .. } => { + bug!("undesugared DropAndReplace in codegen: {:?}", terminator); + } + + mir::TerminatorKind::Call { + ref func, + ref args, + ref destination, + cleanup, + from_hir_call: _ + } => { + self.codegen_call_terminator(helper, bx, terminator, func, + args, destination, cleanup); } mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"), diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index e1528921a5962..dc77d4673cd2a 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>, } 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 >, memory_locals: &BitSet, + va_list_ref: &mut Option>, ) -> Vec> { let mir = fx.mir; let tcx = fx.cx.tcx(); @@ -447,6 +457,15 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( None }; + // Store the index of the last argument. This is used to + // call va_start on the va_list instead of attempting + // to store_fn_arg. + let last_arg_idx = if fx.fn_ty.args.is_empty() { + None + } else { + Some(fx.fn_ty.args.len() - 1) + }; + mir.args_iter().enumerate().map(|(arg_index, local)| { let arg_decl = &mir.local_decls[local]; @@ -510,9 +529,16 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( // of putting everything in allocas just so we can use llvm.dbg.declare. let local = |op| LocalRef::Operand(Some(op)); match arg.mode { - PassMode::Ignore => { + PassMode::Ignore(IgnoreMode::Zst) => { return local(OperandRef::new_zst(bx.cx(), arg.layout)); } + PassMode::Ignore(IgnoreMode::CVarArgs) => { + let backend_type = bx.cx().immediate_backend_type(arg.layout); + return local(OperandRef { + val: OperandValue::Immediate(bx.cx().const_undef(backend_type)), + layout: arg.layout, + }); + } PassMode::Direct(_) => { let llarg = bx.get_param(bx.llfn(), llarg_idx as c_uint); bx.set_value_name(llarg, &name); @@ -559,9 +585,35 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( indirect_operand.store(bx, tmp); tmp } else { - let tmp = PlaceRef::alloca(bx, arg.layout, &name); - bx.store_fn_arg(arg, &mut llarg_idx, tmp); - tmp + if fx.fn_ty.c_variadic && last_arg_idx.map(|idx| arg_index == idx).unwrap_or(false) { + let va_list_impl = match arg_decl.ty.ty_adt_def() { + Some(adt) => adt.non_enum_variant(), + None => bug!("`va_list` language item improperly constructed") + }; + match tcx.type_of(va_list_impl.fields[0].did).sty { + ty::Ref(_, ty, _) => { + // If the underlying structure the `VaList` contains is a structure, + // we need to allocate it (e.g., X86_64 on Linux). + let tmp = PlaceRef::alloca(bx, arg.layout, &name); + if let ty::Adt(..) = ty.sty { + let layout = bx.layout_of(ty); + // Create an unnamed allocation for the backing structure + // and store it in the the spoofed `VaList`. + let backing = PlaceRef::alloca(bx, layout, ""); + bx.store(backing.llval, tmp.llval, layout.align.abi); + } + // Call `va_start` on the spoofed `VaList`. + bx.va_start(tmp.llval); + *va_list_ref = Some(tmp); + tmp + } + _ => bug!("improperly constructed `va_list` lang item"), + } + } else { + let tmp = PlaceRef::alloca(bx, arg.layout, &name); + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + tmp + } }; arg_scope.map(|scope| { // Is this a regular argument? diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs index 3cd0c39d4139a..cd5278989778f 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 a56c3215f9d69..35489ab42e730 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.c_variadic { + // Don't include the spoofed `VaList` in the functions list + // of inputs. + &sig.inputs()[..sig.inputs().len() - 1] + } else { + &sig.inputs()[..] + }; - for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) { + for (input_ty, input_hir) in inputs.iter().zip(&decl.inputs) { self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty); } 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 4202d10aa63d0..df035aab54c9c 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.c_variadic { + &sig.inputs()[..sig.inputs().len() - 1] + } else { + &sig.inputs()[..] + }; + if args.len() < inputs.len() || (args.len() > inputs.len() && !sig.c_variadic) { span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); } - for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { + for (n, (fn_arg, op_arg)) in inputs.iter().zip(args).enumerate() { let op_arg_ty = op_arg.ty(mir, self.tcx()); let category = if from_hir_call { ConstraintCategory::CallArgument diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index a26a1a7861eb0..059af2dbba944 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -353,7 +353,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { output.pop(); } - if sig.variadic { + if sig.c_variadic { if !sig.inputs().is_empty() { output.push_str(", ..."); } else { diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index 52f3a20777021..64a2c92d04dbd 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/arm.rs b/src/librustc_target/abi/call/arm.rs index 52d7f3ac3dcbf..e3fee8e5700c1 100644 --- a/src/librustc_target/abi/call/arm.rs +++ b/src/librustc_target/abi/call/arm.rs @@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>) // `extern "aapcs"`, then we must use the VFP registers for homogeneous aggregates. let vfp = cx.target_spec().llvm_target.ends_with("hf") && fty.conv != Conv::ArmAapcs - && !fty.variadic; + && !fty.c_variadic; if !fty.ret.is_ignore() { classify_ret_ty(cx, &mut fty.ret, vfp); diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index 411eb192d902b..fbbd120f934be 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -23,10 +23,18 @@ mod x86_64; 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 + } } } @@ -520,7 +531,7 @@ pub struct FnType<'a, Ty> { /// LLVM return type. pub ret: ArgType<'a, Ty>, - pub variadic: bool, + pub c_variadic: bool, pub conv: Conv, } diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs index 2e809571ab18b..6ca3ce88bd6eb 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_traits/chalk_context/program_clauses.rs b/src/librustc_traits/chalk_context/program_clauses.rs index 4d8a67ca63863..3f88d0e08b46a 100644 --- a/src/librustc_traits/chalk_context/program_clauses.rs +++ b/src/librustc_traits/chalk_context/program_clauses.rs @@ -105,7 +105,7 @@ fn assemble_builtin_sized_impls<'tcx>( let fn_ptr = generic_types::fn_ptr( tcx, fn_ptr.inputs_and_output.len(), - fn_ptr.variadic, + fn_ptr.c_variadic, fn_ptr.unsafety, fn_ptr.abi ); @@ -190,11 +190,11 @@ fn wf_clause_for_raw_ptr<'tcx>( fn wf_clause_for_fn_ptr<'tcx>( tcx: ty::TyCtxt<'_, '_, 'tcx>, arity_and_output: usize, - variadic: bool, + c_variadic: bool, unsafety: hir::Unsafety, abi: abi::Abi ) -> Clauses<'tcx> { - let fn_ptr = generic_types::fn_ptr(tcx, arity_and_output, variadic, unsafety, abi); + let fn_ptr = generic_types::fn_ptr(tcx, arity_and_output, c_variadic, unsafety, abi); let wf_clause = ProgramClause { goal: DomainGoal::WellFormed(WellFormed::Ty(fn_ptr)), @@ -503,7 +503,7 @@ impl ChalkInferenceContext<'cx, 'gcx, 'tcx> { wf_clause_for_fn_ptr( self.infcx.tcx, fn_ptr.inputs_and_output.len(), - fn_ptr.variadic, + fn_ptr.c_variadic, fn_ptr.unsafety, fn_ptr.abi ) diff --git a/src/librustc_traits/generic_types.rs b/src/librustc_traits/generic_types.rs index 634c024b06466..f2ce9631f35ab 100644 --- a/src/librustc_traits/generic_types.rs +++ b/src/librustc_traits/generic_types.rs @@ -24,7 +24,7 @@ crate fn raw_ptr(tcx: TyCtxt<'_, '_, 'tcx>, mutbl: hir::Mutability) -> Ty<'tcx> crate fn fn_ptr( tcx: ty::TyCtxt<'_, '_, 'tcx>, arity_and_output: usize, - variadic: bool, + c_variadic: bool, unsafety: hir::Unsafety, abi: abi::Abi ) -> Ty<'tcx> { @@ -37,7 +37,7 @@ crate fn fn_ptr( let fn_sig = ty::Binder::bind(ty::FnSig { inputs_and_output, - variadic, + c_variadic, unsafety, abi, }); diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index a81134cb07ef4..4a88922b50ee2 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -18,7 +18,7 @@ use rustc::ty::subst::{Kind, Subst, InternalSubsts, SubstsRef}; use rustc::ty::wf::object_region_bounds; use rustc_data_structures::sync::Lrc; use rustc_target::spec::abi; -use crate::require_c_abi_if_variadic; +use crate::require_c_abi_if_c_variadic; use smallvec::SmallVec; use syntax::ast; use syntax::feature_gate::{GateIssue, emit_feature_err}; @@ -1769,7 +1769,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o { tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(&t))) } hir::TyKind::BareFn(ref bf) => { - require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); + require_c_abi_if_c_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); tcx.mk_fn_ptr(self.ty_of_fn(bf.unsafety, bf.abi, &bf.decl)) } hir::TyKind::TraitObject(ref bounds, ref lifetime) => { @@ -1823,6 +1823,15 @@ 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); @@ -1905,7 +1914,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o { let bare_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( input_tys, output_ty, - decl.variadic, + decl.c_variadic, unsafety, abi )); diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 7bf7d82540655..0a4c0eb3aff72 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -368,20 +368,30 @@ 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.c_variadic { + if fn_sig.inputs().len() > 1 { + &fn_sig.inputs()[..fn_sig.inputs().len() - 1] + } else { + span_bug!(call_expr.span, + "C-variadic functions are only valid with one or more fixed arguments"); + } + } else { + &fn_sig.inputs()[..] + }; // Call the generic checker. let expected_arg_tys = self.expected_inputs_for_expected_output( call_expr.span, expected, fn_sig.output(), - fn_sig.inputs(), + inputs, ); self.check_argument_types( call_expr.span, call_expr.span, - fn_sig.inputs(), + inputs, &expected_arg_tys[..], arg_exprs, - fn_sig.variadic, + fn_sig.c_variadic, TupleArgumentsFlag::DontTupleArguments, def_span, ); @@ -414,7 +424,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn_sig.inputs(), &expected_arg_tys, arg_exprs, - fn_sig.variadic, + fn_sig.c_variadic, TupleArgumentsFlag::TupleArguments, None, ); diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 2a4b17a639956..db89b32be7b68 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -141,7 +141,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.tcx.mk_fn_sig( iter::once(self.tcx.intern_tup(sig.inputs())), sig.output(), - sig.variadic, + sig.c_variadic, sig.unsafety, sig.abi, ) @@ -386,7 +386,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Watch out for some surprises and just ignore the // expectation if things don't see to match up with what we // expect. - if expected_sig.sig.variadic != decl.variadic { + if expected_sig.sig.c_variadic != decl.c_variadic { return self.sig_of_closure_no_expectation(expr_def_id, decl, body); } else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 { return self.sig_of_closure_with_mismatched_number_of_arguments( @@ -404,7 +404,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let bound_sig = ty::Binder::bind(self.tcx.mk_fn_sig( expected_sig.sig.inputs().iter().cloned(), expected_sig.sig.output(), - decl.variadic, + decl.c_variadic, hir::Unsafety::Normal, Abi::RustCall, )); @@ -586,7 +586,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let result = ty::Binder::bind(self.tcx.mk_fn_sig( supplied_arguments, supplied_return, - decl.variadic, + decl.c_variadic, hir::Unsafety::Normal, Abi::RustCall, )); @@ -621,7 +621,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let result = ty::Binder::bind(self.tcx.mk_fn_sig( supplied_arguments, self.tcx.types.err, - decl.variadic, + decl.c_variadic, hir::Unsafety::Normal, Abi::RustCall, )); diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 3b174b55f2ba0..924ced2e2a3c7 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/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 202d8bec4e957..3a430f77b6c64 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -130,7 +130,7 @@ use std::mem::replace; use std::ops::{self, Deref}; use std::slice; -use crate::require_c_abi_if_variadic; +use crate::require_c_abi_if_c_variadic; use crate::session::{CompileIncomplete, Session}; use crate::session::config::EntryFnType; use crate::TypeAndSubsts; @@ -1072,7 +1072,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, fn_sig = fcx.tcx.mk_fn_sig( fn_sig.inputs().iter().cloned(), revealed_ret_ty, - fn_sig.variadic, + fn_sig.c_variadic, fn_sig.unsafety, fn_sig.abi ); @@ -1426,7 +1426,7 @@ pub fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Ite } if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.node { - require_c_abi_if_variadic(tcx, fn_decl, m.abi, item.span); + require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span); } } } @@ -2783,7 +2783,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { &method.sig.inputs()[1..] ); self.check_argument_types(sp, expr_sp, &method.sig.inputs()[1..], &expected_arg_tys[..], - args_no_rcvr, method.sig.variadic, tuple_arguments, + args_no_rcvr, method.sig.c_variadic, tuple_arguments, self.tcx.hir().span_if_local(method.def_id)); method.sig.output() } @@ -2862,7 +2862,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn_inputs: &[Ty<'tcx>], mut expected_arg_tys: &[Ty<'tcx>], args: &'gcx [hir::Expr], - variadic: bool, + c_variadic: bool, tuple_arguments: TupleArgumentsFlag, def_span: Option) { let tcx = self.tcx; @@ -2886,11 +2886,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let param_count_error = |expected_count: usize, arg_count: usize, error_code: &str, - variadic: bool, + c_variadic: bool, sugg_unit: bool| { let mut err = tcx.sess.struct_span_err_with_code(sp, &format!("this function takes {}{} but {} {} supplied", - if variadic {"at least "} else {""}, + if c_variadic { "at least " } else { "" }, potentially_plural_count(expected_count, "parameter"), potentially_plural_count(arg_count, "parameter"), if arg_count == 1 {"was"} else {"were"}), @@ -2910,7 +2910,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Applicability::MachineApplicable); } else { err.span_label(sp, format!("expected {}{}", - if variadic {"at least "} else {""}, + if c_variadic { "at least " } else { "" }, potentially_plural_count(expected_count, "parameter"))); } err.emit(); @@ -2944,7 +2944,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } else if expected_arg_count == supplied_arg_count { fn_inputs.to_vec() - } else if variadic { + } else if c_variadic { if supplied_arg_count >= expected_arg_count { fn_inputs.to_vec() } else { @@ -2991,10 +2991,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.select_obligations_where_possible(false); } - // For variadic functions, we don't have a declared type for all of + // For C-variadic functions, we don't have a declared type for all of // the arguments hence we only do our usual type checking with // the arguments who's types we do know. - let t = if variadic { + let t = if c_variadic { expected_arg_count } else if tuple_arguments == TupleArguments { args.len() @@ -3043,7 +3043,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // We also need to make sure we at least write the ty of the other // arguments which we skipped above. - if variadic { + if c_variadic { fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { use crate::structured_errors::{VariadicError, StructuredDiagnostic}; VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 419796a2014e0..2095c81d0fb0d 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -136,14 +136,14 @@ fn check_type_alias_enum_variants_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, } } -fn require_c_abi_if_variadic(tcx: TyCtxt<'_, '_, '_>, - decl: &hir::FnDecl, - abi: Abi, - span: Span) { - if decl.variadic && !(abi == Abi::C || abi == Abi::Cdecl) { +fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_, '_, '_>, + decl: &hir::FnDecl, + abi: Abi, + span: Span) { + if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) { let mut err = struct_span_err!(tcx.sess, span, E0045, - "variadic function must have C or cdecl calling convention"); - err.span_label(span, "variadics require C or cdecl calling convention").emit(); + "C-variadic function must have C or cdecl calling convention"); + err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 72abbae231aa6..53dcc258c690b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1752,7 +1752,6 @@ impl Clean 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 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 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), Array(Box, String), Never, + CVarArgs, Unique(Box), RawPointer(Mutability, Box), 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 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 4463dad1c8a1f..d204a179ca62c 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(",
..."); - 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 b6f9ae36da70c..a01a5bb0a3638 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 { @@ -1802,7 +1804,7 @@ impl Arg { pub struct FnDecl { pub inputs: Vec, pub output: FunctionRetTy, - pub variadic: bool, + pub c_variadic: bool, } impl FnDecl { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 2f88749ace85e..e95f05894491b 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -985,7 +985,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { P(ast::FnDecl { inputs, output, - variadic: false + c_variadic: false }) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 669c68eda39d2..1c0b931b289c2 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -471,6 +471,9 @@ declare_features! ( // #[repr(align(X))] on enums (active, repr_align_enum, "1.34.0", Some(57996), None), + + // Allows the use of C-variadics + (active, c_variadic, "1.34.0", Some(44930), None), ); declare_features! ( @@ -1901,6 +1904,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { if header.asyncness.node.is_async() { gate_feature_post!(&self, async_await, span, "async fn is unstable"); } + + if fn_decl.c_variadic { + gate_feature_post!(&self, c_variadic, span, + "C-varaidic functions are unstable"); + } // Stability of const fn methods are covered in // `visit_trait_item` and `visit_impl_item` below; this is // because default methods don't pass through this point. @@ -1929,6 +1937,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { if block.is_none() { self.check_abi(sig.header.abi, ti.span); } + if sig.decl.c_variadic { + gate_feature_post!(&self, c_variadic, ti.span, + "C-varaidic functions are unstable"); + } if sig.header.constness.node == ast::Constness::Const { gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable"); } diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index d30372d45d754..802b780869597 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -409,7 +409,8 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { let Ty { id, node, span } = ty.deref_mut(); vis.visit_id(id); match node { - TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never => {} + TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | + TyKind::Never | TyKind::CVarArgs => {} TyKind::Slice(ty) => vis.visit_ty(ty), TyKind::Ptr(mt) => vis.visit_mt(mt), TyKind::Rptr(lt, mt) => { @@ -680,7 +681,7 @@ pub fn noop_visit_asyncness(asyncness: &mut IsAsync, vis: &mut T) } pub fn noop_visit_fn_decl(decl: &mut P, vis: &mut T) { - let FnDecl { inputs, output, variadic: _ } = decl.deref_mut(); + let FnDecl { inputs, output, c_variadic: _ } = decl.deref_mut(); visit_vec(inputs, |input| vis.visit_arg(input)); match output { FunctionRetTy::Default(span) => vis.visit_span(span), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b762365c31f97..fd5038a8614f2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1457,12 +1457,12 @@ impl<'a> Parser<'a> { }; self.expect_keyword(keywords::Fn)?; - let (inputs, variadic) = self.parse_fn_args(false, true)?; + let (inputs, c_variadic) = self.parse_fn_args(false, true)?; let ret_ty = self.parse_ret_ty(false)?; let decl = P(FnDecl { inputs, output: ret_ty, - variadic, + c_variadic, }); Ok(TyKind::BareFn(P(BareFnTy { abi, @@ -1543,7 +1543,7 @@ 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> { - 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> { - 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> { + fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool, + allow_c_variadic: bool) -> PResult<'a, P> { 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_c_variadic { + self.eat(&token::DotDotDot); + TyKind::CVarArgs + } else { + return Err(self.fatal( + "only foreign functions are allowed to be C-variadic" + )); + } } else { let msg = format!("expected type, found {}", self.this_token_descr()); return Err(self.fatal(&msg)); @@ -1959,7 +1968,8 @@ 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_c_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_c_variadic)?) } else { debug!("parse_arg_general ident_to_pat"); let parser_snapshot_before_ty = self.clone(); self.eat_incorrect_doc_comment("a method argument's type"); - let mut ty = self.parse_ty(); + let mut ty = self.parse_ty_common(true, true, allow_c_variadic); if ty.is_ok() && self.token != token::Comma && self.token != token::CloseDelim(token::Paren) { // This wasn't actually a type, but a pattern looking like a type, @@ -2032,6 +2042,11 @@ impl<'a> Parser<'a> { (pat, ty) } Err(mut err) => { + // If this is a C-variadic argument and we hit an error, return the + // error. + if self.token == token::DotDotDot { + return Err(err); + } // Recover from attempting to parse the argument as a type without pattern. err.cancel(); mem::replace(self, parser_snapshot_before_ty); @@ -2068,7 +2083,7 @@ 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 }; @@ -6113,55 +6128,49 @@ impl<'a> Parser<'a> { Ok(where_clause) } - fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool) + fn parse_fn_args(&mut self, named_args: bool, allow_c_variadic: bool) -> PResult<'a, (Vec , bool)> { self.expect(&token::OpenDelim(token::Paren))?; let sp = self.span; - let mut variadic = false; + let mut c_variadic = false; let (args, recovered): (Vec>, bool) = self.parse_seq_to_before_end( &token::CloseDelim(token::Paren), SeqSep::trailing_allowed(token::Comma), |p| { - if p.token == token::DotDotDot { - p.bump(); - variadic = true; - if allow_variadic { - if p.token != token::CloseDelim(token::Paren) { - let span = p.span; - p.span_err(span, - "`...` must be last in argument list for variadic function"); - } - Ok(None) - } else { - let span = p.prev_span; - if p.token == token::CloseDelim(token::Paren) { - // continue parsing to present any further errors - p.struct_span_err( - span, - "only foreign functions are allowed to be variadic" - ).emit(); - Ok(Some(dummy_arg(span))) - } else { - // this function definition looks beyond recovery, stop parsing - p.span_err(span, - "only foreign functions are allowed to be variadic"); - Ok(None) - } - } + // If the argument is a C-variadic argument we should not + // enforce named arguments. + let enforce_named_args = if p.token == token::DotDotDot { + false } else { - match p.parse_arg_general(named_args, false) { - Ok(arg) => Ok(Some(arg)), - Err(mut e) => { - e.emit(); - let lo = p.prev_span; - // Skip every token until next possible arg or end. - p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); - // Create a placeholder argument for proper arg count (#34264). - let span = lo.to(p.prev_span); - Ok(Some(dummy_arg(span))) + named_args + }; + match p.parse_arg_general(enforce_named_args, false, + allow_c_variadic) { + Ok(arg) => { + if let TyKind::CVarArgs = arg.ty.node { + c_variadic = true; + if p.token != token::CloseDelim(token::Paren) { + let span = p.span; + p.span_err(span, + "`...` must be the last argument of a C-variadic function"); + Ok(None) + } else { + Ok(Some(arg)) + } + } else { + Ok(Some(arg)) } + }, + Err(mut e) => { + e.emit(); + let lo = p.prev_span; + // Skip every token until next possible arg or end. + p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); + // Create a placeholder argument for proper arg count (issue #34264). + let span = lo.to(p.prev_span); + Ok(Some(dummy_arg(span))) } } } @@ -6173,24 +6182,24 @@ impl<'a> Parser<'a> { let args: Vec<_> = args.into_iter().filter_map(|x| x).collect(); - if variadic && args.is_empty() { + if c_variadic && args.is_empty() { self.span_err(sp, - "variadic function must be declared with at least one named argument"); + "C-variadic function must be declared with at least one named argument"); } - Ok((args, variadic)) + Ok((args, c_variadic)) } /// Parses the argument list and result type of a function declaration. - fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P> { + fn parse_fn_decl(&mut self, allow_c_variadic: bool) -> PResult<'a, P> { - let (args, variadic) = self.parse_fn_args(true, allow_variadic)?; + let (args, c_variadic) = self.parse_fn_args(true, allow_c_variadic)?; let ret_ty = self.parse_ret_ty(true)?; Ok(P(FnDecl { inputs: args, output: ret_ty, - variadic, + c_variadic, })) } @@ -6337,7 +6346,7 @@ impl<'a> Parser<'a> { Ok(P(FnDecl { inputs: fn_inputs, output: self.parse_ret_ty(true)?, - variadic: false + c_variadic: false })) } @@ -6363,7 +6372,7 @@ impl<'a> Parser<'a> { Ok(P(FnDecl { inputs: inputs_captures, output, - variadic: false + c_variadic: false })) } @@ -6395,7 +6404,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_c_variadic = abi == Abi::C && unsafety == Unsafety::Unsafe; + let decl = self.parse_fn_decl(allow_c_variadic)?; generics.where_clause = self.parse_where_clause()?; let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; let header = FnHeader { unsafety, asyncness, constness, abi }; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index d6265ebde1bbc..942bd96939173 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() } @@ -2811,7 +2814,7 @@ impl<'a> State<'a> { -> io::Result<()> { self.popen()?; self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false))?; - if decl.variadic { + if decl.c_variadic { self.s.word(", ...")?; } self.pclose()?; @@ -3238,7 +3241,7 @@ mod tests { let decl = ast::FnDecl { inputs: Vec::new(), output: ast::FunctionRetTy::Default(syntax_pos::DUMMY_SP), - variadic: false + c_variadic: false }; let generics = ast::Generics::default(); assert_eq!( diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 46d8d772e9321..6b3a30ccb54b7 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -319,7 +319,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { walk_list!(visitor, visit_lifetime, opt_lifetime); visitor.visit_ty(&mutable_type.ty) } - TyKind::Never => {}, + TyKind::Never | TyKind::CVarArgs => {} TyKind::Tup(ref tuple_element_types) => { walk_list!(visitor, visit_ty, tuple_element_types); } diff --git a/src/test/codegen/c-variadic.rs b/src/test/codegen/c-variadic.rs new file mode 100644 index 0000000000000..09c18ed90b216 --- /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::(); + } + 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 d55aac1e40f4f..96a238afaec05 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::() == 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::().floor() == 3.14f64.floor()); + continue_if!(ap.arg::() == 12); + continue_if!(ap.arg::() == 'A' as c_char); + continue_if!(ap.arg::() == 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 95cf0ef46ca1e..91b060dce26f4 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/run-pass-fulldeps/pprust-expr-roundtrip.rs b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs index 956bc5ad862ca..80e0b0102af75 100644 --- a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs @@ -112,7 +112,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P)) { let decl = P(FnDecl { inputs: vec![], output: FunctionRetTy::Default(DUMMY_SP), - variadic: false, + c_variadic: false, }); iter_exprs(depth - 1, &mut |e| g( ExprKind::Closure(CaptureBy::Value, diff --git a/src/test/rustdoc/variadic.rs b/src/test/rustdoc/variadic.rs index bd8f1775b3d04..5af2aea21fcac 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 similarity index 100% rename from src/test/ui/variadic/variadic-ffi.rs rename to 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 similarity index 51% rename from src/test/ui/variadic/variadic-ffi.stderr rename to src/test/ui/c-variadic/variadic-ffi-1.stderr index 617b1f46ea3fc..61d55ce0d3ef9 100644 --- a/src/test/ui/variadic/variadic-ffi.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-1.stderr @@ -1,8 +1,8 @@ -error[E0045]: variadic function must have C or cdecl calling convention - --> $DIR/variadic-ffi.rs:5:5 +error[E0045]: C-variadic function must have C or cdecl calling convention + --> $DIR/variadic-ffi-1.rs:5:5 | LL | fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variadics require C or cdecl calling convention + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention error: aborting due to previous error diff --git a/src/test/ui/variadic/variadic-ffi-2.rs b/src/test/ui/c-variadic/variadic-ffi-2.rs similarity index 100% rename from src/test/ui/variadic/variadic-ffi-2.rs rename to 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 similarity index 53% rename from src/test/ui/variadic/variadic-ffi-2.stderr rename to src/test/ui/c-variadic/variadic-ffi-2.stderr index cb2a9f874b7b3..4c8b8d2b2e1a9 100644 --- a/src/test/ui/variadic/variadic-ffi-2.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-2.stderr @@ -1,8 +1,8 @@ -error[E0045]: variadic function must have C or cdecl calling convention +error[E0045]: C-variadic function must have C or cdecl calling convention --> $DIR/variadic-ffi-2.rs:3:11 | LL | fn baz(f: extern "stdcall" fn(usize, ...)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variadics require C or cdecl calling convention + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention error: aborting due to previous error diff --git a/src/test/ui/variadic/variadic-ffi-3.rs b/src/test/ui/c-variadic/variadic-ffi-3.rs similarity index 85% rename from src/test/ui/variadic/variadic-ffi-3.rs rename to src/test/ui/c-variadic/variadic-ffi-3.rs index 12b3426d9da18..c02d1f54e5649 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 similarity index 90% rename from src/test/ui/variadic/variadic-ffi-3.stderr rename to src/test/ui/c-variadic/variadic-ffi-3.stderr index 0fecbbf36b803..82e3c6cd06fc5 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 0000000000000..9101be5645646 --- /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 0000000000000..1d752be065c4a --- /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 0000000000000..d96482ff4d17a --- /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 0000000000000..2d452872baf4e --- /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 0000000000000..4dd8a2d452181 --- /dev/null +++ b/src/test/ui/c-variadic/variadic-ffi-6.rs @@ -0,0 +1,13 @@ +#![crate_type="lib"] +#![feature(c_variadic)] + +pub unsafe extern "C" fn use_vararg_lifetime( + x: usize, + y: ... +) -> &usize { //~ ERROR missing lifetime specifier + &0 +} + +pub unsafe extern "C" fn use_normal_arg_lifetime(x: &usize, y: ...) -> &usize { // OK + x +} diff --git a/src/test/ui/c-variadic/variadic-ffi-6.stderr b/src/test/ui/c-variadic/variadic-ffi-6.stderr new file mode 100644 index 0000000000000..76bd18959a51f --- /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/E0045.stderr b/src/test/ui/error-codes/E0045.stderr index b38bbc169bd60..0ce91f0a40103 100644 --- a/src/test/ui/error-codes/E0045.stderr +++ b/src/test/ui/error-codes/E0045.stderr @@ -1,8 +1,8 @@ -error[E0045]: variadic function must have C or cdecl calling convention +error[E0045]: C-variadic function must have C or cdecl calling convention --> $DIR/E0045.rs:1:17 | LL | extern "Rust" { fn foo(x: u8, ...); } //~ ERROR E0045 - | ^^^^^^^^^^^^^^^^^^^ variadics require C or cdecl calling convention + | ^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0617.rs b/src/test/ui/error-codes/E0617.rs index 9eed1225ab82d..51f13c7dbd540 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 486ca1fa92f15..8387d5c7e93a5 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/feature-gate/feature-gate-c_variadic.rs b/src/test/ui/feature-gate/feature-gate-c_variadic.rs new file mode 100644 index 0000000000000..5801a2a89e2c1 --- /dev/null +++ b/src/test/ui/feature-gate/feature-gate-c_variadic.rs @@ -0,0 +1,4 @@ +#![crate_type="lib"] + +pub unsafe extern "C" fn test(_: i32, ap: ...) { } +//~^ C-varaidic functions are unstable diff --git a/src/test/ui/feature-gate/feature-gate-c_variadic.stderr b/src/test/ui/feature-gate/feature-gate-c_variadic.stderr new file mode 100644 index 0000000000000..a876e16fdea41 --- /dev/null +++ b/src/test/ui/feature-gate/feature-gate-c_variadic.stderr @@ -0,0 +1,11 @@ +error[E0658]: C-varaidic functions are unstable (see issue #44930) + --> $DIR/feature-gate-c_variadic.rs:3:1 + | +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(c_variadic)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/invalid/invalid-variadic-function.rs b/src/test/ui/invalid/invalid-variadic-function.rs index aea630e7c26a1..8d23f0e477077 100644 --- a/src/test/ui/invalid/invalid-variadic-function.rs +++ b/src/test/ui/invalid/invalid-variadic-function.rs @@ -1,3 +1,3 @@ extern "C" fn foo(x: u8, ...); -//~^ ERROR only foreign functions are allowed to be variadic +//~^ ERROR only foreign functions are allowed to be C-variadic //~| ERROR expected one of `->`, `where`, or `{`, found `;` diff --git a/src/test/ui/invalid/invalid-variadic-function.stderr b/src/test/ui/invalid/invalid-variadic-function.stderr index 7a0b8066fd52a..b2dbf8b919084 100644 --- a/src/test/ui/invalid/invalid-variadic-function.stderr +++ b/src/test/ui/invalid/invalid-variadic-function.stderr @@ -1,4 +1,4 @@ -error: only foreign functions are allowed to be variadic +error: only foreign functions are allowed to be C-variadic --> $DIR/invalid-variadic-function.rs:1:26 | LL | extern "C" fn foo(x: u8, ...); diff --git a/src/test/ui/parser/recover-enum2.stderr b/src/test/ui/parser/recover-enum2.stderr index 2473420a77930..b308e644ad9f8 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 diff --git a/src/test/ui/parser/variadic-ffi-3.rs b/src/test/ui/parser/variadic-ffi-3.rs index 13bce27bb8344..ce83cc87abe00 100644 --- a/src/test/ui/parser/variadic-ffi-3.rs +++ b/src/test/ui/parser/variadic-ffi-3.rs @@ -1,5 +1,5 @@ fn foo(x: isize, ...) { - //~^ ERROR: only foreign functions are allowed to be variadic + //~^ ERROR: only foreign functions are allowed to be C-variadic } fn main() {} diff --git a/src/test/ui/parser/variadic-ffi-3.stderr b/src/test/ui/parser/variadic-ffi-3.stderr index 150de9e63d39d..8ea4d194396fa 100644 --- a/src/test/ui/parser/variadic-ffi-3.stderr +++ b/src/test/ui/parser/variadic-ffi-3.stderr @@ -1,4 +1,4 @@ -error: only foreign functions are allowed to be variadic +error: only foreign functions are allowed to be C-variadic --> $DIR/variadic-ffi-3.rs:1:18 | LL | fn foo(x: isize, ...) { diff --git a/src/test/ui/parser/variadic-ffi-4.rs b/src/test/ui/parser/variadic-ffi-4.rs index 812ed256a5d32..5f8b3f8f539b8 100644 --- a/src/test/ui/parser/variadic-ffi-4.rs +++ b/src/test/ui/parser/variadic-ffi-4.rs @@ -1,5 +1,5 @@ extern "C" fn foo(x: isize, ...) { - //~^ ERROR: only foreign functions are allowed to be variadic + //~^ ERROR: only foreign functions are allowed to be C-variadic } fn main() {} diff --git a/src/test/ui/parser/variadic-ffi-4.stderr b/src/test/ui/parser/variadic-ffi-4.stderr index 2d036b0cf3792..69fbf84869c13 100644 --- a/src/test/ui/parser/variadic-ffi-4.stderr +++ b/src/test/ui/parser/variadic-ffi-4.stderr @@ -1,4 +1,4 @@ -error: only foreign functions are allowed to be variadic +error: only foreign functions are allowed to be C-variadic --> $DIR/variadic-ffi-4.rs:1:29 | LL | extern "C" fn foo(x: isize, ...) { diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs index 63196eda7cc5f..cd60f36b1d273 100644 --- a/src/tools/tidy/src/unstable_book.rs +++ b/src/tools/tidy/src/unstable_book.rs @@ -82,25 +82,27 @@ pub fn check(path: &path::Path, bad: &mut bool) { !lang_features.contains_key(name) }).collect(); + // Library features let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features); let unstable_book_lib_features_section_file_names = collect_unstable_book_lib_features_section_file_names(path); - // Check for Unstable Book sections that don't have a corresponding unstable feature - for feature_name in &unstable_book_lib_features_section_file_names - - &unstable_lib_feature_names { - tidy_error!(bad, - "The Unstable Book has a 'library feature' section '{}' which doesn't \ - correspond to an unstable library feature", - feature_name) - } - // Language features - let unstable_lang_feature_names = collect_unstable_feature_names(&lang_features); let unstable_book_lang_features_section_file_names = collect_unstable_book_lang_features_section_file_names(path); + // Check for Unstable Book sections that don't have a corresponding unstable feature + for feature_name in &unstable_book_lib_features_section_file_names - + &unstable_lib_feature_names { + if !unstable_lang_feature_names.contains(&feature_name) { + tidy_error!(bad, + "The Unstable Book has a 'library feature' section '{}' which doesn't \ + correspond to an unstable library feature", + feature_name); + } + } + // Check for Unstable Book sections that don't have a corresponding unstable feature. for feature_name in &unstable_book_lang_features_section_file_names - &unstable_lang_feature_names {