summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-02-28 15:00:25 +0000
committerbors <bors@rust-lang.org>2019-02-28 15:00:25 +0000
commit1999a2288123173b2e487865c9a04386173025f7 (patch)
tree3fccdd2d553dbd4f0c44a8c4ef280e14a0f9ceee
parent190feb65290d39d7ab6d44e994bd99188d339f16 (diff)
parentf7dd4389f8145fe0f29c5b29784b244fa38d2bfb (diff)
downloadrust-1999a2288123173b2e487865c9a04386173025f7.tar.gz
Auto merge of #57760 - dlrobertson:varargs1, r=alexreg
Support defining C compatible variadic functions ## Summary Add support for defining C compatible variadic functions in unsafe rust with `extern "C"` according to [RFC 2137]. ## Details ### Parsing When parsing a user defined function that is `unsafe` and `extern "C"` allow variadic signatures and inject a "spoofed" `VaList` in the new functions signature. This allows the user to interact with the variadic arguments via a `VaList` instead of manually using `va_start` and `va_end` (See [RFC 2137] for details). ### Codegen When running codegen for a variadic function, remove the "spoofed" `VaList` from the function signature and inject `va_start` when the arg local references are created for the function and `va_end` on return. ## TODO - [x] Get feedback on injecting `va_start/va_end` in MIR vs codegen - [x] Properly inject `va_end` - It seems like it should be possible to inject `va_end` on the `TerminatorKind::Return`. I just need to figure out how to get the `LocalRef` here. - [x] Properly call Rust defined C variadic functions in Rust - The spoofed `VaList` causes problems here. Related to: #44930 r? @ghost [RFC 2137]: https://github.com/rust-lang/rfcs/blob/master/text/2137-variadic.md
-rw-r--r--src/doc/unstable-book/src/language-features/c-variadic.md24
-rw-r--r--src/doc/unstable-book/src/library-features/c-variadic.md26
-rw-r--r--src/librustc/hir/intravisit.rs3
-rw-r--r--src/librustc/hir/lowering.rs44
-rw-r--r--src/librustc/hir/mod.rs5
-rw-r--r--src/librustc/hir/print.rs5
-rw-r--r--src/librustc/ich/impls_hir.rs5
-rw-r--r--src/librustc/ich/impls_ty.rs2
-rw-r--r--src/librustc/middle/resolve_lifetime.rs31
-rw-r--r--src/librustc/traits/select.rs2
-rw-r--r--src/librustc/ty/context.rs6
-rw-r--r--src/librustc/ty/instance.rs2
-rw-r--r--src/librustc/ty/relate.rs6
-rw-r--r--src/librustc/ty/structural_impls.rs4
-rw-r--r--src/librustc/ty/sty.rs12
-rw-r--r--src/librustc/util/ppaux.rs8
-rw-r--r--src/librustc_codegen_llvm/abi.rs53
-rw-r--r--src/librustc_codegen_llvm/debuginfo/type_names.rs2
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs51
-rw-r--r--src/librustc_codegen_llvm/mono_item.rs8
-rw-r--r--src/librustc_codegen_llvm/va_arg.rs4
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs1404
-rw-r--r--src/librustc_codegen_ssa/mir/mod.rs64
-rw-r--r--src/librustc_codegen_ssa/traits/intrinsic.rs6
-rw-r--r--src/librustc_lint/types.rs9
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs11
-rw-r--r--src/librustc_mir/monomorphize/item.rs2
-rw-r--r--src/librustc_save_analysis/sig.rs1
-rw-r--r--src/librustc_target/abi/call/arm.rs2
-rw-r--r--src/librustc_target/abi/call/mod.rs19
-rw-r--r--src/librustc_target/abi/call/x86.rs2
-rw-r--r--src/librustc_traits/chalk_context/program_clauses.rs8
-rw-r--r--src/librustc_traits/generic_types.rs4
-rw-r--r--src/librustc_typeck/astconv.rs15
-rw-r--r--src/librustc_typeck/check/callee.rs18
-rw-r--r--src/librustc_typeck/check/closure.rs10
-rw-r--r--src/librustc_typeck/check/intrinsic.rs6
-rw-r--r--src/librustc_typeck/check/mod.rs24
-rw-r--r--src/librustc_typeck/lib.rs14
-rw-r--r--src/librustdoc/clean/mod.rs8
-rw-r--r--src/librustdoc/html/format.rs22
-rw-r--r--src/libsyntax/ast.rs4
-rw-r--r--src/libsyntax/ext/build.rs2
-rw-r--r--src/libsyntax/feature_gate.rs12
-rw-r--r--src/libsyntax/mut_visit.rs5
-rw-r--r--src/libsyntax/parse/parser.rs130
-rw-r--r--src/libsyntax/print/pprust.rs7
-rw-r--r--src/libsyntax/visit.rs2
-rw-r--r--src/test/codegen/c-variadic.rs69
-rw-r--r--src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs27
-rw-r--r--src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c10
-rw-r--r--src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs2
-rw-r--r--src/test/rustdoc/variadic.rs2
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-1.rs (renamed from src/test/ui/variadic/variadic-ffi.rs)0
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-1.stderr (renamed from src/test/ui/variadic/variadic-ffi.stderr)6
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-2.rs (renamed from src/test/ui/variadic/variadic-ffi-2.rs)0
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-2.stderr (renamed from src/test/ui/variadic/variadic-ffi-2.stderr)4
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-3.rs (renamed from src/test/ui/variadic/variadic-ffi-3.rs)4
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-3.stderr (renamed from src/test/ui/variadic/variadic-ffi-3.stderr)18
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.rs29
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.stderr198
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-5.rs31
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-5.stderr73
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-6.rs13
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-6.stderr11
-rw-r--r--src/test/ui/error-codes/E0045.stderr4
-rw-r--r--src/test/ui/error-codes/E0617.rs4
-rw-r--r--src/test/ui/error-codes/E0617.stderr8
-rw-r--r--src/test/ui/feature-gate/feature-gate-c_variadic.rs4
-rw-r--r--src/test/ui/feature-gate/feature-gate-c_variadic.stderr11
-rw-r--r--src/test/ui/invalid/invalid-variadic-function.rs2
-rw-r--r--src/test/ui/invalid/invalid-variadic-function.stderr2
-rw-r--r--src/test/ui/parser/recover-enum2.stderr4
-rw-r--r--src/test/ui/parser/variadic-ffi-3.rs2
-rw-r--r--src/test/ui/parser/variadic-ffi-3.stderr2
-rw-r--r--src/test/ui/parser/variadic-ffi-4.rs2
-rw-r--r--src/test/ui/parser/variadic-ffi-4.stderr2
-rw-r--r--src/tools/tidy/src/unstable_book.rs22
78 files changed, 1778 insertions, 902 deletions
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 00000000000..9e7968d906f
--- /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::<usize>();
+ }
+ sum
+}
+```
diff --git a/src/doc/unstable-book/src/library-features/c-variadic.md b/src/doc/unstable-book/src/library-features/c-variadic.md
new file mode 100644
index 00000000000..77762116e6b
--- /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::<usize>();
+ }
+ sum
+}
+```
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 0bc40da7e4f..8e4b9a5e8e6 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -617,6 +617,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
TyKind::Typeof(ref expression) => {
visitor.visit_anon_const(expression)
}
+ TyKind::CVarArgs(ref lt) => {
+ visitor.visit_lifetime(lt)
+ }
TyKind::Infer | TyKind::Err => {}
}
}
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index df45a085944..19bb7f41e1b 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<T>`).
anonymous_lifetime_mode: AnonymousLifetimeMode,
- // Used to create lifetime definitions from in-band lifetime usages.
- // e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
- // When a named lifetime is encountered in a function or impl header and
- // has not been defined
- // (i.e., it doesn't appear in the in_scope_lifetimes list), it is added
- // to this list. The results of this list are then added to the list of
- // lifetime definitions in the corresponding impl or function generics.
+ /// Used to create lifetime definitions from in-band lifetime usages.
+ /// e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
+ /// When a named lifetime is encountered in a function or impl header and
+ /// has not been defined
+ /// (i.e., it doesn't appear in the in_scope_lifetimes list), it is added
+ /// to this list. The results of this list are then added to the list of
+ /// lifetime definitions in the corresponding impl or function generics.
lifetimes_to_define: Vec<(Span, ParamName)>,
- // Whether or not in-band lifetimes are being collected. This is used to
- // indicate whether or not we're in a place where new lifetimes will result
- // in in-band lifetime definitions, such a function or an impl header,
- // including implicit lifetimes from `impl_header_lifetime_elision`.
+ /// Whether or not in-band lifetimes are being collected. This is used to
+ /// indicate whether or not we're in a place where new lifetimes will result
+ /// in in-band lifetime definitions, such a function or an impl header,
+ /// including implicit lifetimes from `impl_header_lifetime_elision`.
is_collecting_in_band_lifetimes: bool,
- // Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
- // When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
- // against this list to see if it is already in-scope, or if a definition
- // needs to be created for it.
+ /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
+ /// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
+ /// against this list to see if it is already in-scope, or if a definition
+ /// needs to be created for it.
in_scope_lifetimes: Vec<Ident>,
current_module: NodeId,
@@ -954,7 +954,7 @@ 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 38e6e61592b..d8169d05dd4 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<Ty>,
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 17d37488413..dab4b9c824d 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 d1161dda1e2..77582278690 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 cce1273b7f0..f77a88128f2 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 31e9eb9b746..832391d4416 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -764,6 +764,13 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
});
}
}
+ hir::TyKind::CVarArgs(ref lt) => {
+ // Resolve the generated lifetime for the C-variadic arguments.
+ // The lifetime is generated in AST -> HIR lowering.
+ if lt.name.is_elided() {
+ self.resolve_elided_lifetimes(vec![lt])
+ }
+ }
_ => intravisit::walk_ty(self, ty),
}
}
@@ -2225,18 +2232,22 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
if let hir::TyKind::BareFn(_) = ty.node {
self.outer_index.shift_in(1);
}
- if let hir::TyKind::TraitObject(ref bounds, ref lifetime) = ty.node {
- for bound in bounds {
- self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
- }
+ match ty.node {
+ hir::TyKind::TraitObject(ref bounds, ref lifetime) => {
+ for bound in bounds {
+ self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
+ }
- // Stay on the safe side and don't include the object
- // lifetime default (which may not end up being used).
- if !lifetime.is_elided() {
- self.visit_lifetime(lifetime);
+ // Stay on the safe side and don't include the object
+ // lifetime default (which may not end up being used).
+ if !lifetime.is_elided() {
+ self.visit_lifetime(lifetime);
+ }
+ }
+ hir::TyKind::CVarArgs(_) => {}
+ _ => {
+ intravisit::walk_ty(self, ty);
}
- } else {
- intravisit::walk_ty(self, ty);
}
if let hir::TyKind::BareFn(_) = ty.node {
self.outer_index.shift_out(1);
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index e85b84bce43..e7cc9618080 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 b37b632f4be..9767396147c 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<I>(self,
inputs: I,
output: I::Item,
- variadic: bool,
+ c_variadic: bool,
unsafety: hir::Unsafety,
abi: abi::Abi)
-> <I::Item as InternIteratorElement<Ty<'tcx>, ty::FnSig<'tcx>>>::Output
@@ -2788,7 +2788,7 @@ 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 709dce4589f..49ebd202813 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 b15aa862901..2940757fa90 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 a81d5c9d86e..f1a465e1f17 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 df8b14b1f10..3fd2e38a3d3 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<Ty<'tcx>>,
- 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<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.output())
}
- pub fn variadic(&self) -> bool {
- self.skip_binder().variadic
+ pub fn c_variadic(&self) -> bool {
+ self.skip_binder().c_variadic
}
pub fn unsafety(&self) -> hir::Unsafety {
self.skip_binder().unsafety
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index fbe9e3359bf..aecef3c5ec7 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -360,7 +360,7 @@ impl PrintContext {
fn fn_sig<F: fmt::Write>(&mut self,
f: &mut F,
inputs: &[Ty<'_>],
- variadic: bool,
+ c_variadic: bool,
output: Ty<'_>)
-> fmt::Result {
write!(f, "(")?;
@@ -370,7 +370,7 @@ 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 992149f7a47..49c9555a2c6 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<usize>| {
let is_return = arg_idx.is_none();
let mut arg = mk_arg_type(ty, arg_idx);
@@ -516,7 +524,30 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
// The same is true for s390x-unknown-linux-gnu
// and sparc64-unknown-linux-gnu.
if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) {
- arg.mode = PassMode::Ignore;
+ arg.mode = PassMode::Ignore(IgnoreMode::Zst);
+ }
+ }
+
+ // If this is a C-variadic function, this is not the return value,
+ // and there is one or more fixed arguments; ensure that the `VaList`
+ // is ignored as an argument.
+ if sig.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 176c9b8c542..8b218ab39d9 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 d1cbe1d4dd6..3268af396a2 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -136,22 +136,18 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
let tp_ty = substs.type_at(0);
self.const_usize(self.size_of(tp_ty).bytes())
}
- func @ "va_start" | func @ "va_end" => {
- let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
- (Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
- (Some(_), _) => self.load(args[0].immediate(),
- tcx.data_layout.pointer_align.abi),
- (None, _) => bug!("va_list language item must be defined")
- };
- let intrinsic = self.cx().get_intrinsic(&format!("llvm.{}", func));
- self.call(intrinsic, &[va_list], None)
+ "va_start" => {
+ self.va_start(args[0].immediate())
+ }
+ "va_end" => {
+ self.va_end(args[0].immediate())
}
"va_copy" => {
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
(Some(_), _) => self.load(args[0].immediate(),
tcx.data_layout.pointer_align.abi),
- (None, _) => bug!("va_list language item must be defined")
+ (None, _) => bug!("`va_list` language item must be defined")
};
let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
self.call(intrinsic, &[llresult, va_list], None);
@@ -722,6 +718,41 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
let expect = self.get_intrinsic(&"llvm.expect.i1");
self.call(expect, &[cond, self.const_bool(expected)], None)
}
+
+ fn va_start(&mut self, list: &'ll Value) -> &'ll Value {
+ let target = &self.cx.tcx.sess.target.target;
+ let arch = &target.arch;
+ // A pointer to the architecture specific structure is passed to this
+ // function. For pointer variants (i686, RISC-V, Windows, etc), we
+ // should do do nothing, as the address to the pointer is needed. For
+ // architectures with a architecture specific structure (`Aarch64`,
+ // `X86_64`, etc), this function should load the structure from the
+ // address provided.
+ let va_list = match &**arch {
+ _ if target.options.is_like_windows => list,
+ "aarch64" if target.target_os == "ios" => list,
+ "aarch64" | "x86_64" | "powerpc" =>
+ self.load(list, self.tcx().data_layout.pointer_align.abi),
+ _ => list,
+ };
+ let intrinsic = self.cx().get_intrinsic("llvm.va_start");
+ self.call(intrinsic, &[va_list], None)
+ }
+
+ fn va_end(&mut self, list: &'ll Value) -> &'ll Value {
+ let target = &self.cx.tcx.sess.target.target;
+ let arch = &target.arch;
+ // See the comment in `va_start` for the purpose of the following.
+ let va_list = match &**arch {
+ _ if target.options.is_like_windows => list,
+ "aarch64" if target.target_os == "ios" => list,
+ "aarch64" | "x86_64" | "powerpc" =>
+ self.load(list, self.tcx().data_layout.pointer_align.abi),
+ _ => list,
+ };
+ let intrinsic = self.cx().get_intrinsic("llvm.va_end");
+ self.call(intrinsic, &[va_list], None)
+ }
}
fn copy_intrinsic(
diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs
index 4fe6a1f4f4b..7f0cdb9f580 100644
--- a/src/librustc_codegen_llvm/mono_item.rs
+++ b/src/librustc_codegen_llvm/mono_item.rs
@@ -36,10 +36,10 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
fn predefine_fn(&self,
- instance: Instance<'tcx>,
- linkage: Linkage,
- visibility: Visibility,
- symbol_name: &str) {
+ instance: Instance<'tcx>,
+ linkage: Linkage,
+ visibility: Visibility,
+ symbol_name: &str) {
assert!(!instance.substs.needs_infer() &&
!instance.substs.has_param_types());
diff --git a/src/librustc_codegen_llvm/va_arg.rs b/src/librustc_codegen_llvm/va_arg.rs
index 8719390b51a..7aceaea4510 100644
--- a/src/librustc_codegen_llvm/va_arg.rs
+++ b/src/librustc_codegen_llvm/va_arg.rs
@@ -109,12 +109,12 @@ pub(super) fn emit_va_arg(
Align::from_bytes(4).unwrap(), true)
}
// Windows Aarch64
- ("aarch4", true) => {
+ ("aarch64", true) => {
emit_ptr_va_arg(bx, addr, target_ty, false,
Align::from_bytes(8).unwrap(), false)
}
// iOS Aarch64
- ("aarch4", _) if target.target_os == "ios" => {
+ ("aarch64", _) if target.target_os == "ios" => {
emit_ptr_va_arg(bx, addr, target_ty, false,
Align::from_bytes(8).unwrap(), true)
}
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index caca1789fc9..627380ee38f 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<mir::BasicBlock>,
+}
- debug!("codegen_block({:?}={:?})", bb, data);
+impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
+ /// Returns the associated funclet from `FunctionCx::funclets` for the
+ /// `funclet_bb` member if it is not `None`.
+ fn funclet<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+ &self,
+ fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+ ) -> Option<&'c Bx::Funclet> {
+ match self.funclet_bb {
+ Some(funcl) => fx.funclets[funcl].as_ref(),
+ None => None,
+ }
+ }
- for statement in &data.statements {
- bx = self.codegen_statement(bx, statement);
+ fn lltarget<'b, 'c, Bx: BuilderMethods<'b, 'tcx>>(
+ &self,
+ fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+ target: mir::BasicBlock,
+ ) -> (Bx::BasicBlock, bool) {
+ let span = self.terminator.source_info.span;
+ let lltarget = fx.blocks[target];
+ let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
+ match (self.funclet_bb, target_funclet) {
+ (None, None) => (lltarget, false),
+ (Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) =>
+ (lltarget, false),
+ // jump *into* cleanup - need a landing pad if GNU
+ (None, Some(_)) => (fx.landing_pad_to(target), false),
+ (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator),
+ (Some(_), Some(_)) => (fx.landing_pad_to(target), true),
}
+ }
- self.codegen_terminator(bx, bb, data.terminator());
+ /// Create a basic block.
+ fn llblock<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+ &self,
+ fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+ target: mir::BasicBlock,
+ ) -> Bx::BasicBlock {
+ let (lltarget, is_cleanupret) = self.lltarget(fx, target);
+ if is_cleanupret {
+ // MSVC cross-funclet jump - need a trampoline
+
+ debug!("llblock: creating cleanup trampoline for {:?}", target);
+ let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target);
+ let mut trampoline = fx.new_block(name);
+ trampoline.cleanup_ret(self.funclet(fx).unwrap(),
+ Some(lltarget));
+ trampoline.llbb()
+ } else {
+ lltarget
+ }
}
- fn codegen_terminator(
- &mut self,
- mut bx: Bx,
- bb: mir::BasicBlock,
- terminator: &mir::Terminator<'tcx>
+ fn funclet_br<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+ &self,
+ fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+ bx: &mut Bx,
+ target: mir::BasicBlock,
) {
- debug!("codegen_terminator: {:?}", terminator);
-
- // Create the cleanup bundle, if needed.
- let tcx = self.cx.tcx();
- let span = terminator.source_info.span;
- let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
+ let (lltarget, is_cleanupret) = self.lltarget(fx, target);
+ if is_cleanupret {
+ // micro-optimization: generate a `ret` rather than a jump
+ // to a trampoline.
+ bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
+ } else {
+ bx.br(lltarget);
+ }
+ }
- // HACK(eddyb) force the right lifetimes, NLL can't figure them out.
- fn funclet_closure_factory<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
- funclet_bb: Option<mir::BasicBlock>
- ) -> impl for<'b> Fn(
- &'b FunctionCx<'a, 'tcx, Bx>,
- ) -> Option<&'b Bx::Funclet> {
- move |this| {
- match funclet_bb {
- Some(funclet_bb) => this.funclets[funclet_bb].as_ref(),
- None => None,
- }
+ /// Call `fn_ptr` of `fn_ty` with the arguments `llargs`, the optional
+ /// return destination `destination` and the cleanup function `cleanup`.
+ fn do_call<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+ &self,
+ fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+ bx: &mut Bx,
+ fn_ty: FnType<'tcx, Ty<'tcx>>,
+ fn_ptr: Bx::Value,
+ llargs: &[Bx::Value],
+ destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
+ cleanup: Option<mir::BasicBlock>,
+ ) {
+ if let Some(cleanup) = cleanup {
+ let ret_bx = if let Some((_, target)) = destination {
+ fx.blocks[target]
+ } else {
+ fx.unreachable_block()
+ };
+ let invokeret = bx.invoke(fn_ptr,
+ &llargs,
+ ret_bx,
+ self.llblock(fx, cleanup),
+ self.funclet(fx));
+ bx.apply_attrs_callsite(&fn_ty, invokeret);
+
+ if let Some((ret_dest, target)) = destination {
+ let mut ret_bx = fx.build_block(target);
+ fx.set_debug_loc(&mut ret_bx, self.terminator.source_info);
+ fx.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret);
}
- }
- let funclet = funclet_closure_factory(funclet_bb);
-
- let lltarget = |this: &mut Self, target: mir::BasicBlock| {
- let lltarget = this.blocks[target];
- let target_funclet = this.cleanup_kinds[target].funclet_bb(target);
- match (funclet_bb, target_funclet) {
- (None, None) => (lltarget, false),
- (Some(f), Some(t_f))
- if f == t_f || !base::wants_msvc_seh(tcx.sess)
- => (lltarget, false),
- (None, Some(_)) => {
- // jump *into* cleanup - need a landing pad if GNU
- (this.landing_pad_to(target), false)
- }
- (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator),
- (Some(_), Some(_)) => {
- (this.landing_pad_to(target), true)
- }
+ } else {
+ let llret = bx.call(fn_ptr, &llargs, self.funclet(fx));
+ bx.apply_attrs_callsite(&fn_ty, llret);
+ if fx.mir[*self.bb].is_cleanup {
+ // Cleanup is always the cold path. Don't inline
+ // drop glue. Also, when there is a deeply-nested
+ // struct, there are "symmetry" issues that cause
+ // exponential inlining - see issue #41696.
+ bx.do_not_inline(llret);
}
- };
-
- let llblock = |this: &mut Self, target: mir::BasicBlock| {
- let (lltarget, is_cleanupret) = lltarget(this, target);
- if is_cleanupret {
- // MSVC cross-funclet jump - need a trampoline
- debug!("llblock: creating cleanup trampoline for {:?}", target);
- let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
- let mut trampoline = this.new_block(name);
- trampoline.cleanup_ret(funclet(this).unwrap(), Some(lltarget));
- trampoline.llbb()
+ if let Some((ret_dest, target)) = destination {
+ fx.store_return(bx, ret_dest, &fn_ty.ret, llret);
+ self.funclet_br(fx, bx, target);
} else {
- lltarget
+ bx.unreachable();
}
- };
-
- let funclet_br =
- |this: &mut Self, bx: &mut Bx, target: mir::BasicBlock| {
- let (lltarget, is_cleanupret) = lltarget(this, target);
- if is_cleanupret {
- // micro-optimization: generate a `ret` rather than a jump
- // to a trampoline.
- bx.cleanup_ret(funclet(this).unwrap(), Some(lltarget));
- } else {
- bx.br(lltarget);
- }
- };
+ }
+ }
+}
- let do_call = |
- this: &mut Self,
- bx: &mut Bx,
- fn_ty: FnType<'tcx, Ty<'tcx>>,
- fn_ptr: Bx::Value,
- llargs: &[Bx::Value],
- destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
- cleanup: Option<mir::BasicBlock>
- | {
- if let Some(cleanup) = cleanup {
- let ret_bx = if let Some((_, target)) = destination {
- this.blocks[target]
- } else {
- this.unreachable_block()
- };
- let invokeret = bx.invoke(fn_ptr,
- &llargs,
- ret_bx,
- llblock(this, cleanup),
- funclet(this));
- bx.apply_attrs_callsite(&fn_ty, invokeret);
-
- if let Some((ret_dest, target)) = destination {
- let mut ret_bx = this.build_block(target);
- this.set_debug_loc(&mut ret_bx, terminator.source_info);
- this.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret);
- }
+/// Codegen implementations for some terminator variants.
+impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
+ /// Generates code for a `Resume` terminator.
+ fn codegen_resume_terminator<'b>(
+ &mut self,
+ helper: TerminatorCodegenHelper<'b, 'tcx>,
+ mut bx: Bx,
+ ) {
+ if let Some(funclet) = helper.funclet(self) {
+ bx.cleanup_ret(funclet, None);
+ } else {
+ let slot = self.get_personality_slot(&mut bx);
+ let lp0 = slot.project_field(&mut bx, 0);
+ let lp0 = bx.load_operand(lp0).immediate();
+ let lp1 = slot.project_field(&mut bx, 1);
+ let lp1 = bx.load_operand(lp1).immediate();
+ slot.storage_dead(&mut bx);
+
+ if !bx.sess().target.target.options.custom_unwind_resume {
+ let mut lp = bx.const_undef(self.landing_pad_type());
+ lp = bx.insert_value(lp, lp0, 0);
+ lp = bx.insert_value(lp, lp1, 1);
+ bx.resume(lp);
} else {
- let llret = bx.call(fn_ptr, &llargs, funclet(this));
- bx.apply_attrs_callsite(&fn_ty, llret);
- if this.mir[bb].is_cleanup {
- // Cleanup is always the cold path. Don't inline
- // drop glue. Also, when there is a deeply-nested
- // struct, there are "symmetry" issues that cause
- // exponential inlining - see issue #41696.
- bx.do_not_inline(llret);
- }
-
- if let Some((ret_dest, target)) = destination {
- this.store_return(bx, ret_dest, &fn_ty.ret, llret);
- funclet_br(this, bx, target);
- } else {
- bx.unreachable();
- }
+ bx.call(bx.eh_unwind_resume(), &[lp0],
+ helper.funclet(self));
+ bx.unreachable();
}
- };
+ }
+ }
- self.set_debug_loc(&mut bx, terminator.source_info);
- match terminator.kind {
- mir::TerminatorKind::Resume => {
- if let Some(funclet) = funclet(self) {
- bx.cleanup_ret(funclet, None);
+ fn codegen_switchint_terminator<'b>(
+ &mut self,
+ helper: TerminatorCodegenHelper<'b, 'tcx>,
+ mut bx: Bx,
+ discr: &mir::Operand<'tcx>,
+ switch_ty: Ty<'tcx>,
+ values: &Cow<'tcx, [u128]>,
+ targets: &Vec<mir::BasicBlock>,
+ ) {
+ let discr = self.codegen_operand(&mut bx, &discr);
+ if targets.len() == 2 {
+ // If there are two targets, emit br instead of switch
+ let lltrue = helper.llblock(self, targets[0]);
+ let llfalse = helper.llblock(self, targets[1]);
+ if switch_ty == bx.tcx().types.bool {
+ // Don't generate trivial icmps when switching on bool
+ if let [0] = values[..] {
+ bx.cond_br(discr.immediate(), llfalse, lltrue);
} else {
- let slot = self.get_personality_slot(&mut bx);
- let lp0 = slot.project_field(&mut bx, 0);
- let lp0 = bx.load_operand(lp0).immediate();
- let lp1 = slot.project_field(&mut bx, 1);
- let lp1 = bx.load_operand(lp1).immediate();
- slot.storage_dead(&mut bx);
-
- if !bx.sess().target.target.options.custom_unwind_resume {
- let mut lp = bx.const_undef(self.landing_pad_type());
- lp = bx.insert_value(lp, lp0, 0);
- lp = bx.insert_value(lp, lp1, 1);
- bx.resume(lp);
- } else {
- bx.call(bx.eh_unwind_resume(), &[lp0], funclet(self));
- bx.unreachable();
- }
+ assert_eq!(&values[..], &[1]);
+ bx.cond_br(discr.immediate(), lltrue, llfalse);
}
+ } else {
+ let switch_llty = bx.immediate_backend_type(
+ bx.layout_of(switch_ty)
+ );
+ let llval = bx.const_uint_big(switch_llty, values[0]);
+ let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
+ bx.cond_br(cmp, lltrue, llfalse);
+ }
+ } else {
+ let (otherwise, targets) = targets.split_last().unwrap();
+ let switch = bx.switch(discr.immediate(),
+ helper.llblock(self, *otherwise),
+ values.len());
+ let switch_llty = bx.immediate_backend_type(
+ bx.layout_of(switch_ty)
+ );
+ for (&value, target) in values.iter().zip(targets) {
+ let llval = bx.const_uint_big(switch_llty, value);
+ let llbb = helper.llblock(self, *target);
+ bx.add_case(switch, llval, llbb)
}
+ }
+ }
- mir::TerminatorKind::Abort => {
- bx.abort();
- bx.unreachable();
+ fn codegen_return_terminator<'b>(
+ &mut self,
+ mut bx: Bx,
+ ) {
+ if self.fn_ty.c_variadic {
+ if let Some(va_list) = self.va_list_ref {
+ bx.va_end(va_list.llval);
+ }
+ }
+ let llval = match self.fn_ty.ret.mode {
+ PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => {
+ bx.ret_void();
+ return;
}
- mir::TerminatorKind::Goto { target } => {
- funclet_br(self, &mut bx, target);
+ PassMode::Ignore(IgnoreMode::CVarArgs) => {
+ bug!("C-variadic arguments should never be the return type");
}
- mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
- let discr = self.codegen_operand(&mut bx, discr);
- if targets.len() == 2 {
- // If there are two targets, emit br instead of switch
- let lltrue = llblock(self, targets[0]);
- let llfalse = llblock(self, targets[1]);
- if switch_ty == bx.tcx().types.bool {
- // Don't generate trivial icmps when switching on bool
- if let [0] = values[..] {
- bx.cond_br(discr.immediate(), llfalse, lltrue);
- } else {
- assert_eq!(&values[..], &[1]);
- bx.cond_br(discr.immediate(), lltrue, llfalse);
- }
- } else {
- let switch_llty = bx.immediate_backend_type(
- bx.layout_of(switch_ty)
- );
- let llval = bx.const_uint_big(switch_llty, values[0]);
- let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
- bx.cond_br(cmp, lltrue, llfalse);
- }
+ PassMode::Direct(_) | PassMode::Pair(..) => {
+ let op =
+ self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE));
+ if let Ref(llval, _, align) = op.val {
+ bx.load(llval, align)
} else {
- let (otherwise, targets) = targets.split_last().unwrap();
- let switch = bx.switch(discr.immediate(),
- llblock(self, *otherwise),
- values.len());
- let switch_llty = bx.immediate_backend_type(
- bx.layout_of(switch_ty)
- );
- for (&value, target) in values.iter().zip(targets) {
- let llval = bx.const_uint_big(switch_llty, value);
- let llbb = llblock(self, *target);
- bx.add_case(switch, llval, llbb)
- }
+ op.immediate_or_packed_pair(&mut bx)
}
}
- mir::TerminatorKind::Return => {
- let llval = match self.fn_ty.ret.mode {
- PassMode::Ignore | PassMode::Indirect(..) => {
- bx.ret_void();
- return;
- }
-
- PassMode::Direct(_) | PassMode::Pair(..) => {
- let op =
- self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE));
- if let Ref(llval, _, align) = op.val {
- bx.load(llval, align)
- } else {
- op.immediate_or_packed_pair(&mut bx)
+ PassMode::Cast(cast_ty) => {
+ let op = match self.locals[mir::RETURN_PLACE] {
+ LocalRef::Operand(Some(op)) => op,
+ LocalRef::Operand(None) => bug!("use of return before def"),
+ LocalRef::Place(cg_place) => {
+ OperandRef {
+ val: Ref(cg_place.llval, None, cg_place.align),
+ layout: cg_place.layout
}
}
-
- PassMode::Cast(cast_ty) => {
- let op = match self.locals[mir::RETURN_PLACE] {
- LocalRef::Operand(Some(op)) => op,
- LocalRef::Operand(None) => bug!("use of return before def"),
- LocalRef::Place(cg_place) => {
- OperandRef {
- val: Ref(cg_place.llval, None, cg_place.align),
- layout: cg_place.layout
- }
- }
- LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
- };
- let llslot = match op.val {
- Immediate(_) | Pair(..) => {
- let scratch =
- PlaceRef::alloca(&mut bx, self.fn_ty.ret.layout, "ret");
- op.val.store(&mut bx, scratch);
- scratch.llval
- }
- Ref(llval, _, align) => {
- assert_eq!(align, op.layout.align.abi,
- "return place is unaligned!");
- llval
- }
- };
- let addr = bx.pointercast(llslot, bx.type_ptr_to(
- bx.cast_backend_type(&cast_ty)
- ));
- bx.load(addr, self.fn_ty.ret.layout.align.abi)
+ LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
+ };
+ let llslot = match op.val {
+ Immediate(_) | Pair(..) => {
+ let scratch =
+ PlaceRef::alloca(&mut bx, self.fn_ty.ret.layout, "ret");
+ op.val.store(&mut bx, scratch);
+ scratch.llval
+ }
+ Ref(llval, _, align) => {
+ assert_eq!(align, op.layout.align.abi,
+ "return place is unaligned!");
+ llval
}
};
- bx.ret(llval);
+ let addr = bx.pointercast(llslot, bx.type_ptr_to(
+ bx.cast_backend_type(&cast_ty)
+ ));
+ bx.load(addr, self.fn_ty.ret.layout.align.abi)
}
+ };
+ bx.ret(llval);
+ }
- mir::TerminatorKind::Unreachable => {
- bx.unreachable();
+
+ fn codegen_drop_terminator<'b>(
+ &mut self,
+ helper: TerminatorCodegenHelper<'b, 'tcx>,
+ mut bx: Bx,
+ location: &mir::Place<'tcx>,
+ target: mir::BasicBlock,
+ unwind: Option<mir::BasicBlock>,
+ ) {
+ let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx());
+ let ty = self.monomorphize(&ty);
+ let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty);
+
+ if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
+ // we don't actually need to drop anything.
+ helper.funclet_br(self, &mut bx, target);
+ return
+ }
+
+ let place = self.codegen_place(&mut bx, location);
+ let (args1, args2);
+ let mut args = if let Some(llextra) = place.llextra {
+ args2 = [place.llval, llextra];
+ &args2[..]
+ } else {
+ args1 = [place.llval];
+ &args1[..]
+ };
+ let (drop_fn, fn_ty) = match ty.sty {
+ ty::Dynamic(..) => {
+ let sig = drop_fn.fn_sig(self.cx.tcx());
+ let sig = self.cx.tcx().normalize_erasing_late_bound_regions(
+ ty::ParamEnv::reveal_all(),
+ &sig,
+ );
+ let fn_ty = bx.new_vtable(sig, &[]);
+ let vtable = args[1];
+ args = &args[..1];
+ (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_ty), fn_ty)
+ }
+ _ => {
+ (bx.get_fn(drop_fn),
+ bx.fn_type_of_instance(&drop_fn))
}
+ };
+ helper.do_call(self, &mut bx, fn_ty, drop_fn, args,
+ Some((ReturnDest::Nothing, target)),
+ unwind);
+ }
- mir::TerminatorKind::Drop { ref location, target, unwind } => {
- let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx());
- let ty = self.monomorphize(&ty);
- let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty);
-
- if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
- // we don't actually need to drop anything.
- funclet_br(self, &mut bx, target);
- return
- }
+ fn codegen_assert_terminator<'b>(
+ &mut self,
+ helper: TerminatorCodegenHelper<'b, 'tcx>,
+ mut bx: Bx,
+ terminator: &mir::Terminator<'tcx>,
+ cond: &mir::Operand<'tcx>,
+ expected: bool,
+ msg: &mir::AssertMessage<'tcx>,
+ target: mir::BasicBlock,
+ cleanup: Option<mir::BasicBlock>,
+ ) {
+ let span = terminator.source_info.span;
+ let cond = self.codegen_operand(&mut bx, cond).immediate();
+ let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
+
+ // This case can currently arise only from functions marked
+ // with #[rustc_inherit_overflow_checks] and inlined from
+ // another crate (mostly core::num generic/#[inline] fns),
+ // while the current crate doesn't use overflow checks.
+ // NOTE: Unlike binops, negation doesn't have its own
+ // checked operation, just a comparison with the minimum
+ // value, so we have to check for the assert message.
+ if !bx.check_overflow() {
+ if let mir::interpret::EvalErrorKind::OverflowNeg = *msg {
+ const_cond = Some(expected);
+ }
+ }
- let place = self.codegen_place(&mut bx, location);
- let (args1, args2);
- let mut args = if let Some(llextra) = place.llextra {
- args2 = [place.llval, llextra];
- &args2[..]
- } else {
- args1 = [place.llval];
- &args1[..]
- };
- let (drop_fn, fn_ty) = match ty.sty {
- ty::Dynamic(..) => {
- let sig = drop_fn.fn_sig(tcx);
- let sig = tcx.normalize_erasing_late_bound_regions(
- ty::ParamEnv::reveal_all(),
- &sig,
- );
- let fn_ty = bx.new_vtable(sig, &[]);
- let vtable = args[1];
- args = &args[..1];
- (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_ty), fn_ty)
- }
- _ => {
- (bx.get_fn(drop_fn),
- bx.fn_type_of_instance(&drop_fn))
- }
- };
- do_call(self, &mut bx, fn_ty, drop_fn, args,
- Some((ReturnDest::Nothing, target)),
- unwind);
+ // Don't codegen the panic block if success if known.
+ if const_cond == Some(expected) {
+ helper.funclet_br(self, &mut bx, target);
+ return;
+ }
+
+ // Pass the condition through llvm.expect for branch hinting.
+ let cond = bx.expect(cond, expected);
+
+ // Create the failure block and the conditional branch to it.
+ let lltarget = helper.llblock(self, target);
+ let panic_block = self.new_block("panic");
+ if expected {
+ bx.cond_br(cond, lltarget, panic_block.llbb());
+ } else {
+ bx.cond_br(cond, panic_block.llbb(), lltarget);
+ }
+
+ // After this point, bx is the block for the call to panic.
+ bx = panic_block;
+ self.set_debug_loc(&mut bx, terminator.source_info);
+
+ // Get the location information.
+ let loc = bx.sess().source_map().lookup_char_pos(span.lo());
+ let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
+ let filename = bx.const_str_slice(filename);
+ let line = bx.const_u32(loc.line as u32);
+ let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
+ let align = self.cx.tcx().data_layout.aggregate_align.abi
+ .max(self.cx.tcx().data_layout.i32_align.abi)
+ .max(self.cx.tcx().data_layout.pointer_align.abi);
+
+ // Put together the arguments to the panic entry point.
+ let (lang_item, args) = match *msg {
+ EvalErrorKind::BoundsCheck { ref len, ref index } => {
+ let len = self.codegen_operand(&mut bx, len).immediate();
+ let index = self.codegen_operand(&mut bx, index).immediate();
+
+ let file_line_col = bx.const_struct(&[filename, line, col], false);
+ let file_line_col = bx.static_addr_of(
+ file_line_col,
+ align,
+ Some("panic_bounds_check_loc")
+ );
+ (lang_items::PanicBoundsCheckFnLangItem,
+ vec![file_line_col, index, len])
+ }
+ _ => {
+ let str = msg.description();
+ let msg_str = Symbol::intern(str).as_str();
+ let msg_str = bx.const_str_slice(msg_str);
+ let msg_file_line_col = bx.const_struct(
+ &[msg_str, filename, line, col],
+ false
+ );
+ let msg_file_line_col = bx.static_addr_of(
+ msg_file_line_col,
+ align,
+ Some("panic_loc")
+ );
+ (lang_items::PanicFnLangItem,
+ vec![msg_file_line_col])
}
+ };
- mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
- let cond = self.codegen_operand(&mut bx, cond).immediate();
- let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
-
- // This case can currently arise only from functions marked
- // with #[rustc_inherit_overflow_checks] and inlined from
- // another crate (mostly core::num generic/#[inline] fns),
- // while the current crate doesn't use overflow checks.
- // NOTE: Unlike binops, negation doesn't have its own
- // checked operation, just a comparison with the minimum
- // value, so we have to check for the assert message.
- if !bx.check_overflow() {
- if let mir::interpret::EvalErrorKind::OverflowNeg = *msg {
- const_cond = Some(expected);
- }
- }
+ // Obtain the panic entry point.
+ let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
+ let instance = ty::Instance::mono(bx.tcx(), def_id);
+ let fn_ty = bx.fn_type_of_instance(&instance);
+ let llfn = bx.get_fn(instance);
- // Don't codegen the panic block if success if known.
- if const_cond == Some(expected) {
- funclet_br(self, &mut bx, target);
- return;
- }
+ // Codegen the actual panic invoke/call.
+ helper.do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup);
+ }
- // Pass the condition through llvm.expect for branch hinting.
- let cond = bx.expect(cond, expected);
+ fn codegen_call_terminator<'b>(
+ &mut self,
+ helper: TerminatorCodegenHelper<'b, 'tcx>,
+ mut bx: Bx,
+ terminator: &mir::Terminator<'tcx>,
+ func: &mir::Operand<'tcx>,
+ args: &Vec<mir::Operand<'tcx>>,
+ destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
+ cleanup: Option<mir::BasicBlock>,
+ ) {
+ let span = terminator.source_info.span;
+ // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
+ let callee = self.codegen_operand(&mut bx, func);
+
+ let (instance, mut llfn) = match callee.layout.ty.sty {
+ ty::FnDef(def_id, substs) => {
+ (Some(ty::Instance::resolve(bx.tcx(),
+ ty::ParamEnv::reveal_all(),
+ def_id,
+ substs).unwrap()),
+ None)
+ }
+ ty::FnPtr(_) => {
+ (None, Some(callee.immediate()))
+ }
+ _ => bug!("{} is not callable", callee.layout.ty),
+ };
+ let def = instance.map(|i| i.def);
+ let sig = callee.layout.ty.fn_sig(bx.tcx());
+ let sig = bx.tcx().normalize_erasing_late_bound_regions(
+ ty::ParamEnv::reveal_all(),
+ &sig,
+ );
+ let abi = sig.abi;
+
+ // Handle intrinsics old codegen wants Expr's for, ourselves.
+ let intrinsic = match def {
+ Some(ty::InstanceDef::Intrinsic(def_id)) =>
+ Some(bx.tcx().item_name(def_id).as_str()),
+ _ => None
+ };
+ let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
- // Create the failure block and the conditional branch to it.
- let lltarget = llblock(self, target);
- let panic_block = self.new_block("panic");
- if expected {
- bx.cond_br(cond, lltarget, panic_block.llbb());
- } else {
- bx.cond_br(cond, panic_block.llbb(), lltarget);
- }
+ if intrinsic == Some("transmute") {
+ if let Some(destination_ref) = destination.as_ref() {
+ let &(ref dest, target) = destination_ref;
+ self.codegen_transmute(&mut bx, &args[0], dest);
+ helper.funclet_br(self, &mut bx, target);
+ } else {
+ // If we are trying to transmute to an uninhabited type,
+ // it is likely there is no allotted destination. In fact,
+ // transmuting to an uninhabited type is UB, which means
+ // we can do what we like. Here, we declare that transmuting
+ // into an uninhabited type is impossible, so anything following
+ // it must be unreachable.
+ assert_eq!(bx.layout_of(sig.output()).abi, layout::Abi::Uninhabited);
+ bx.unreachable();
+ }
+ return;
+ }
- // After this point, bx is the block for the call to panic.
- bx = panic_block;
- self.set_debug_loc(&mut bx, terminator.source_info);
+ // The "spoofed" `VaList` added to a C-variadic functions signature
+ // should not be included in the `extra_args` calculation.
+ let extra_args_start_idx = sig.inputs().len() - if sig.c_variadic { 1 } else { 0 };
+ let extra_args = &args[extra_args_start_idx..];
+ let extra_args = extra_args.iter().map(|op_arg| {
+ let op_ty = op_arg.ty(self.mir, bx.tcx());
+ self.monomorphize(&op_ty)
+ }).collect::<Vec<_>>();
+
+ let fn_ty = match def {
+ Some(ty::InstanceDef::Virtual(..)) => {
+ bx.new_vtable(sig, &extra_args)
+ }
+ Some(ty::InstanceDef::DropGlue(_, None)) => {
+ // Empty drop glue; a no-op.
+ let &(_, target) = destination.as_ref().unwrap();
+ helper.funclet_br(self, &mut bx, target);
+ return;
+ }
+ _ => bx.new_fn_type(sig, &extra_args)
+ };
- // Get the location information.
+ // Emit a panic or a no-op for `panic_if_uninhabited`.
+ if intrinsic == Some("panic_if_uninhabited") {
+ let ty = instance.unwrap().substs.type_at(0);
+ let layout = bx.layout_of(ty);
+ if layout.abi.is_uninhabited() {
let loc = bx.sess().source_map().lookup_char_pos(span.lo());
let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
let filename = bx.const_str_slice(filename);
let line = bx.const_u32(loc.line as u32);
let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
- let align = tcx.data_layout.aggregate_align.abi
- .max(tcx.data_layout.i32_align.abi)
- .max(tcx.data_layout.pointer_align.abi);
-
- // Put together the arguments to the panic entry point.
- let (lang_item, args) = match *msg {
- EvalErrorKind::BoundsCheck { ref len, ref index } => {
- let len = self.codegen_operand(&mut bx, len).immediate();
- let index = self.codegen_operand(&mut bx, index).immediate();
-
- let file_line_col = bx.const_struct(&[filename, line, col], false);
- let file_line_col = bx.static_addr_of(
- file_line_col,
- align,
- Some("panic_bounds_check_loc")
- );
- (lang_items::PanicBoundsCheckFnLangItem,
- vec![file_line_col, index, len])
- }
- _ => {
- let str = msg.description();
- let msg_str = Symbol::intern(str).as_str();
- let msg_str = bx.const_str_slice(msg_str);
- let msg_file_line_col = bx.const_struct(
- &[msg_str, filename, line, col],
- false
- );
- let msg_file_line_col = bx.static_addr_of(
- msg_file_line_col,
- align,
- Some("panic_loc")
- );
- (lang_items::PanicFnLangItem,
- vec![msg_file_line_col])
- }
- };
+ let align = self.cx.tcx().data_layout.aggregate_align.abi
+ .max(self.cx.tcx().data_layout.i32_align.abi)
+ .max(self.cx.tcx().data_layout.pointer_align.abi);
+
+ let str = format!(
+ "Attempted to instantiate uninhabited type {}",
+ ty
+ );
+ let msg_str = Symbol::intern(&str).as_str();
+ let msg_str = bx.const_str_slice(msg_str);
+ let msg_file_line_col = bx.const_struct(
+ &[msg_str, filename, line, col],
+ false,
+ );
+ let msg_file_line_col = bx.static_addr_of(
+ msg_file_line_col,
+ align,
+ Some("panic_loc"),
+ );
// Obtain the panic entry point.
- let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
+ let def_id =
+ common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
let instance = ty::Instance::mono(bx.tcx(), def_id);
let fn_ty = bx.fn_type_of_instance(&instance);
let llfn = bx.get_fn(instance);
// Codegen the actual panic invoke/call.
- do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup);
+ helper.do_call(
+ self,
+ &mut bx,
+ fn_ty,
+ llfn,
+ &[msg_file_line_col],
+ destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
+ cleanup,
+ );
+ } else {
+ // a NOP
+ helper.funclet_br(self, &mut bx, destination.as_ref().unwrap().1)
}
+ return;
+ }
- mir::TerminatorKind::DropAndReplace { .. } => {
- bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
- }
+ // The arguments we'll be passing. Plus one to account for outptr, if used.
+ let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
+ let mut llargs = Vec::with_capacity(arg_count);
- mir::TerminatorKind::Call {
- ref func,
- ref args,
- ref destination,
- cleanup,
- from_hir_call: _
- } => {
- // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
- let callee = self.codegen_operand(&mut bx, func);
-
- let (instance, mut llfn) = match callee.layout.ty.sty {
- ty::FnDef(def_id, substs) => {
- (Some(ty::Instance::resolve(bx.tcx(),
- ty::ParamEnv::reveal_all(),
- def_id,
- substs).unwrap()),
- None)
- }
- ty::FnPtr(_) => {
- (None, Some(callee.immediate()))
- }
- _ => bug!("{} is not callable", callee.layout.ty)
- };
- let def = instance.map(|i| i.def);
- let sig = callee.layout.ty.fn_sig(bx.tcx());
- let sig = bx.tcx().normalize_erasing_late_bound_regions(
- ty::ParamEnv::reveal_all(),
- &sig,
- );
- let abi = sig.abi;
+ // Prepare the return value destination
+ let ret_dest = if let Some((ref dest, _)) = *destination {
+ let is_intrinsic = intrinsic.is_some();
+ self.make_return_dest(&mut bx, dest, &fn_ty.ret, &mut llargs,
+ is_intrinsic)
+ } else {
+ ReturnDest::Nothing
+ };
- // Handle intrinsics old codegen wants Expr's for, ourselves.
- let intrinsic = match def {
- Some(ty::InstanceDef::Intrinsic(def_id))
- => Some(bx.tcx().item_name(def_id).as_str()),
- _ => None
- };
- let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
+ if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
+ let dest = match ret_dest {
+ _ if fn_ty.ret.is_indirect() => llargs[0],
+ ReturnDest::Nothing =>
+ bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret))),
+ ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) =>
+ dst.llval,
+ ReturnDest::DirectOperand(_) =>
+ bug!("Cannot use direct operand with an intrinsic call"),
+ };
- if intrinsic == Some("transmute") {
- if let Some(destination_ref) = destination.as_ref() {
- let &(ref dest, target) = destination_ref;
- self.codegen_transmute(&mut bx, &args[0], dest);
- funclet_br(self, &mut bx, target);
- } else {
- // If we are trying to transmute to an uninhabited type,
- // it is likely there is no allotted destination. In fact,
- // transmuting to an uninhabited type is UB, which means
- // we can do what we like. Here, we declare that transmuting
- // into an uninhabited type is impossible, so anything following
- // it must be unreachable.
- assert_eq!(bx.layout_of(sig.output()).abi, layout::Abi::Uninhabited);
- bx.unreachable();
+ let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
+ // The indices passed to simd_shuffle* in the
+ // third argument must be constant. This is
+ // checked by const-qualification, which also
+ // promotes any complex rvalues to constants.
+ if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") {
+ match *arg {
+ // The shuffle array argument is usually not an explicit constant,
+ // but specified directly in the code. This means it gets promoted
+ // and we can then extract the value by evaluating the promoted.
+ mir::Operand::Copy(mir::Place::Promoted(box(index, ty))) |
+ mir::Operand::Move(mir::Place::Promoted(box(index, ty))) => {
+ let param_env = ty::ParamEnv::reveal_all();
+ let cid = mir::interpret::GlobalId {
+ instance: self.instance,
+ promoted: Some(index),
+ };
+ let c = bx.tcx().const_eval(param_env.and(cid));
+ let (llval, ty) = self.simd_shuffle_indices(
+ &bx,
+ terminator.source_info.span,
+ ty,
+ c,
+ );
+ return OperandRef {
+ val: Immediate(llval),
+ layout: bx.layout_of(ty),
+ };
+
+ }
+ mir::Operand::Copy(_) |
+ mir::Operand::Move(_) => {
+ span_bug!(span, "shuffle indices must be constant");
+ }
+ mir::Operand::Constant(ref constant) => {
+ let c = self.eval_mir_constant(&bx, constant);
+ let (llval, ty) = self.simd_shuffle_indices(
+ &bx,
+ constant.span,
+ constant.ty,
+ c,
+ );
+ return OperandRef {
+ val: Immediate(llval),
+ layout: bx.layout_of(ty)
+ };
+ }
}
- return;
}
- let extra_args = &args[sig.inputs().len()..];
- let extra_args = extra_args.iter().map(|op_arg| {
- let op_ty = op_arg.ty(self.mir, bx.tcx());
- self.monomorphize(&op_ty)
- }).collect::<Vec<_>>();
+ self.codegen_operand(&mut bx, arg)
+ }).collect();
- let fn_ty = match def {
- Some(ty::InstanceDef::Virtual(..)) => {
- bx.new_vtable(sig, &extra_args)
- }
- Some(ty::InstanceDef::DropGlue(_, None)) => {
- // empty drop glue - a nop.
- let &(_, target) = destination.as_ref().unwrap();
- funclet_br(self, &mut bx, target);
- return;
- }
- _ => bx.new_fn_type(sig, &extra_args)
+
+ let callee_ty = instance.as_ref().unwrap().ty(bx.tcx());
+ bx.codegen_intrinsic_call(callee_ty, &fn_ty, &args, dest,
+ terminator.source_info.span);
+
+ if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
+ self.store_return(&mut bx, ret_dest, &fn_ty.ret, dst.llval);
+ }
+
+ if let Some((_, target)) = *destination {
+ helper.funclet_br(self, &mut bx, target);
+ } else {
+ bx.unreachable();
+ }
+
+ return;
+ }
+
+ // Split the rust-call tupled arguments off.
+ let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() {
+ let (tup, args) = args.split_last().unwrap();
+ (args, Some(tup))
+ } else {
+ (&args[..], None)
+ };
+
+ // Useful determining if the current argument is the "spoofed" `VaList`
+ let last_arg_idx = if sig.inputs().is_empty() {
+ None
+ } else {
+ Some(sig.inputs().len() - 1)
+ };
+ 'make_args: for (i, arg) in first_args.iter().enumerate() {
+ // If this is a C-variadic function the function signature contains
+ // an "spoofed" `VaList`. This argument is ignored, but we need to
+ // populate it with a dummy operand so that the users real arguments
+ // are not overwritten.
+ let i = if sig.c_variadic && last_arg_idx.map(|x| x == i).unwrap_or(false) {
+ let layout = match self.cx.tcx().lang_items().va_list() {
+ Some(did) => bx.cx().layout_of(bx.tcx().type_of(did)),
+ None => bug!("`va_list` language item required for C-variadics"),
};
+ let op = OperandRef {
+ val: OperandValue::Immediate(
+ bx.cx().const_undef(bx.cx().immediate_backend_type(layout)
+ )),
+ layout: layout,
+ };
+ self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
+ if i + 1 < fn_ty.args.len() {
+ i + 1
+ } else {
+ break 'make_args
+ }
+ } else {
+ i
+ };
+ let mut op = self.codegen_operand(&mut bx, arg);
+
+ if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
+ if let Pair(..) = op.val {
+ // In the case of Rc<Self>, we need to explicitly pass a
+ // *mut RcBox<Self> with a Scalar (not ScalarPair) ABI. This is a hack
+ // that is understood elsewhere in the compiler as a method on
+ // `dyn Trait`.
+ // To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until
+ // we get a value of a built-in pointer type
+ 'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
+ && !op.layout.ty.is_region_ptr()
+ {
+ 'iter_fields: for i in 0..op.layout.fields.count() {
+ let field = op.extract_field(&mut bx, i);
+ if !field.layout.is_zst() {
+ // we found the one non-zero-sized field that is allowed
+ // now find *its* non-zero-sized field, or stop if it's a
+ // pointer
+ op = field;
+ continue 'descend_newtypes
+ }
+ }
- // emit a panic or a NOP for `panic_if_uninhabited`
- if intrinsic == Some("panic_if_uninhabited") {
- let ty = instance.unwrap().substs.type_at(0);
- let layout = bx.layout_of(ty);
- if layout.abi.is_uninhabited() {
- let loc = bx.sess().source_map().lookup_char_pos(span.lo());
- let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
- let filename = bx.const_str_slice(filename);
- let line = bx.const_u32(loc.line as u32);
- let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
- let align = tcx.data_layout.aggregate_align.abi
- .max(tcx.data_layout.i32_align.abi)
- .max(tcx.data_layout.pointer_align.abi);
-
- let str = format!(
- "Attempted to instantiate uninhabited type {}",
- ty
- );
- let msg_str = Symbol::intern(&str).as_str();
- let msg_str = bx.const_str_slice(msg_str);
- let msg_file_line_col = bx.const_struct(
- &[msg_str, filename, line, col],
- false,
- );
- let msg_file_line_col = bx.static_addr_of(
- msg_file_line_col,
- align,
- Some("panic_loc"),
- );
-
- // Obtain the panic entry point.
- let def_id =
- common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
- let instance = ty::Instance::mono(bx.tcx(), def_id);
- let fn_ty = bx.fn_type_of_instance(&instance);
- let llfn = bx.get_fn(instance);
-
- // Codegen the actual panic invoke/call.
- do_call(
- self,
- &mut bx,
- fn_ty,
- llfn,
- &[msg_file_line_col],
- destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
- cleanup,
- );
- } else {
- // a NOP
- funclet_br(self, &mut bx, destination.as_ref().unwrap().1);
+ span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
}
- return;
+
+ // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
+ // data pointer and vtable. Look up the method in the vtable, and pass
+ // the data pointer as the first argument
+ match op.val {
+ Pair(data_ptr, meta) => {
+ llfn = Some(meth::VirtualIndex::from_index(idx)
+ .get_fn(&mut bx, meta, &fn_ty));
+ llargs.push(data_ptr);
+ continue 'make_args
+ }
+ other => bug!("expected a Pair, got {:?}", other),
+ }
+ } else if let Ref(data_ptr, Some(meta), _) = op.val {
+ // by-value dynamic dispatch
+ llfn = Some(meth::VirtualIndex::from_index(idx)
+ .get_fn(&mut bx, meta, &fn_ty));
+ llargs.push(data_ptr);
+ continue;
+ } else {
+ span_bug!(span, "can't codegen a virtual call on {:?}", op);
}
+ }
- // The arguments we'll be passing. Plus one to account for outptr, if used.
- let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
- let mut llargs = Vec::with_capacity(arg_count);
+ // The callee needs to own the argument memory if we pass it
+ // by-ref, so make a local copy of non-immediate constants.
+ match (arg, op.val) {
+ (&mir::Operand::Copy(_), Ref(_, None, _)) |
+ (&mir::Operand::Constant(_), Ref(_, None, _)) => {
+ let tmp = PlaceRef::alloca(&mut bx, op.layout, "const");
+ op.val.store(&mut bx, tmp);
+ op.val = Ref(tmp.llval, None, tmp.align);
+ }
+ _ => {}
+ }
- // Prepare the return value destination
- let ret_dest = if let Some((ref dest, _)) = *destination {
- let is_intrinsic = intrinsic.is_some();
- self.make_return_dest(&mut bx, dest, &fn_ty.ret, &mut llargs,
- is_intrinsic)
- } else {
- ReturnDest::Nothing
- };
+ self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
+ }
+ if let Some(tup) = untuple {
+ self.codegen_arguments_untupled(&mut bx, tup, &mut llargs,
+ &fn_ty.args[first_args.len()..])
+ }
- if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
- let dest = match ret_dest {
- _ if fn_ty.ret.is_indirect() => llargs[0],
- ReturnDest::Nothing => {
- bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret)))
- }
- ReturnDest::IndirectOperand(dst, _) |
- ReturnDest::Store(dst) => dst.llval,
- ReturnDest::DirectOperand(_) =>
- bug!("Cannot use direct operand with an intrinsic call")
- };
+ let fn_ptr = match (llfn, instance) {
+ (Some(llfn), _) => llfn,
+ (None, Some(instance)) => bx.get_fn(instance),
+ _ => span_bug!(span, "no llfn for call"),
+ };
- let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
- // The indices passed to simd_shuffle* in the
- // third argument must be constant. This is
- // checked by const-qualification, which also
- // promotes any complex rvalues to constants.
- if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") {
- match *arg {
- // The shuffle array argument is usually not an explicit constant,
- // but specified directly in the code. This means it gets promoted
- // and we can then extract the value by evaluating the promoted.
- mir::Operand::Copy(mir::Place::Promoted(box(index, ty))) |
- mir::Operand::Move(mir::Place::Promoted(box(index, ty))) => {
- let param_env = ty::ParamEnv::reveal_all();
- let cid = mir::interpret::GlobalId {
- instance: self.instance,
- promoted: Some(index),
- };
- let c = bx.tcx().const_eval(param_env.and(cid));
- let (llval, ty) = self.simd_shuffle_indices(
- &bx,
- terminator.source_info.span,
- ty,
- c,
- );
- return OperandRef {
- val: Immediate(llval),
- layout: bx.layout_of(ty),
- };
-
- },
- mir::Operand::Copy(_) |
- mir::Operand::Move(_) => {
- span_bug!(span, "shuffle indices must be constant");
- }
- mir::Operand::Constant(ref constant) => {
- let c = self.eval_mir_constant(&bx, constant);
- let (llval, ty) = self.simd_shuffle_indices(
- &bx,
- constant.span,
- constant.ty,
- c,
- );
- return OperandRef {
- val: Immediate(llval),
- layout: bx.layout_of(ty)
- };
- }
- }
- }
+ helper.do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
+ destination.as_ref().map(|&(_, target)| (ret_dest, target)),
+ cleanup);
+ }
+}
- self.codegen_operand(&mut bx, arg)
- }).collect();
+impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
+ pub fn codegen_block(
+ &mut self,
+ bb: mir::BasicBlock,
+ ) {
+ let mut bx = self.build_block(bb);
+ let data = &self.mir[bb];
+ debug!("codegen_block({:?}={:?})", bb, data);
- let callee_ty = instance.as_ref().unwrap().ty(bx.tcx());
- bx.codegen_intrinsic_call(callee_ty, &fn_ty, &args, dest,
- terminator.source_info.span);
+ for statement in &data.statements {
+ bx = self.codegen_statement(bx, statement);
+ }
- if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
- self.store_return(&mut bx, ret_dest, &fn_ty.ret, dst.llval);
- }
+ self.codegen_terminator(bx, bb, data.terminator());
+ }
- if let Some((_, target)) = *destination {
- funclet_br(self, &mut bx, target);
- } else {
- bx.unreachable();
- }
+ fn codegen_terminator(
+ &mut self,
+ mut bx: Bx,
+ bb: mir::BasicBlock,
+ terminator: &mir::Terminator<'tcx>
+ ) {
+ debug!("codegen_terminator: {:?}", terminator);
- return;
- }
+ // Create the cleanup bundle, if needed.
+ let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
+ let helper = TerminatorCodegenHelper {
+ bb: &bb, terminator, funclet_bb
+ };
- // Split the rust-call tupled arguments off.
- let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() {
- let (tup, args) = args.split_last().unwrap();
- (args, Some(tup))
- } else {
- (&args[..], None)
- };
+ self.set_debug_loc(&mut bx, terminator.source_info);
+ match terminator.kind {
+ mir::TerminatorKind::Resume => {
+ self.codegen_resume_terminator(helper, bx)
+ }
- 'make_args: for (i, arg) in first_args.iter().enumerate() {
- let mut op = self.codegen_operand(&mut bx, arg);
-
- if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
- if let Pair(..) = op.val {
- // In the case of Rc<Self>, we need to explicitly pass a
- // *mut RcBox<Self> with a Scalar (not ScalarPair) ABI. This is a hack
- // that is understood elsewhere in the compiler as a method on
- // `dyn Trait`.
- // To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until
- // we get a value of a built-in pointer type
- 'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
- && !op.layout.ty.is_region_ptr()
- {
- 'iter_fields: for i in 0..op.layout.fields.count() {
- let field = op.extract_field(&mut bx, i);
- if !field.layout.is_zst() {
- // we found the one non-zero-sized field that is allowed
- // now find *its* non-zero-sized field, or stop if it's a
- // pointer
- op = field;
- continue 'descend_newtypes
- }
- }
-
- span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
- }
+ mir::TerminatorKind::Abort => {
+ bx.abort();
+ bx.unreachable();
+ }
- // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
- // data pointer and vtable. Look up the method in the vtable, and pass
- // the data pointer as the first argument
- match op.val {
- Pair(data_ptr, meta) => {
- llfn = Some(meth::VirtualIndex::from_index(idx)
- .get_fn(&mut bx, meta, &fn_ty));
- llargs.push(data_ptr);
- continue 'make_args
- }
- other => bug!("expected a Pair, got {:?}", other)
- }
- } else if let Ref(data_ptr, Some(meta), _) = op.val {
- // by-value dynamic dispatch
- llfn = Some(meth::VirtualIndex::from_index(idx)
- .get_fn(&mut bx, meta, &fn_ty));
- llargs.push(data_ptr);
- continue;
- } else {
- span_bug!(span, "can't codegen a virtual call on {:?}", op);
- }
- }
+ mir::TerminatorKind::Goto { target } => {
+ helper.funclet_br(self, &mut bx, target);
+ }
- // The callee needs to own the argument memory if we pass it
- // by-ref, so make a local copy of non-immediate constants.
- match (arg, op.val) {
- (&mir::Operand::Copy(_), Ref(_, None, _)) |
- (&mir::Operand::Constant(_), Ref(_, None, _)) => {
- let tmp = PlaceRef::alloca(&mut bx, op.layout, "const");
- op.val.store(&mut bx, tmp);
- op.val = Ref(tmp.llval, None, tmp.align);
- }
- _ => {}
- }
+ mir::TerminatorKind::SwitchInt {
+ ref discr, switch_ty, ref values, ref targets
+ } => {
+ self.codegen_switchint_terminator(helper, bx, discr, switch_ty,
+ values, targets);
+ }
- self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
- }
- if let Some(tup) = untuple {
- self.codegen_arguments_untupled(&mut bx, tup, &mut llargs,
- &fn_ty.args[first_args.len()..])
- }
+ mir::TerminatorKind::Return => {
+ self.codegen_return_terminator(bx);
+ }
- let fn_ptr = match (llfn, instance) {
- (Some(llfn), _) => llfn,
- (None, Some(instance)) => bx.get_fn(instance),
- _ => span_bug!(span, "no llfn for call"),
- };
+ mir::TerminatorKind::Unreachable => {
+ bx.unreachable();
+ }
- do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
- destination.as_ref().map(|&(_, target)| (ret_dest, target)),
- cleanup);
+ mir::TerminatorKind::Drop { ref location, target, unwind } => {
+ self.codegen_drop_terminator(helper, bx, location, target, unwind);
+ }
+
+ mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
+ self.codegen_assert_terminator(helper, bx, terminator, cond,
+ expected, msg, target, cleanup);
+ }
+
+ mir::TerminatorKind::DropAndReplace { .. } => {
+ bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
+ }
+
+ mir::TerminatorKind::Call {
+ ref func,
+ ref args,
+ ref destination,
+ cleanup,
+ from_hir_call: _
+ } => {
+ self.codegen_call_terminator(helper, bx, terminator, func,
+ args, destination, cleanup);
}
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"),
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index e1528921a59..dc77d4673cd 100644
--- a/src/librustc_codegen_ssa/mir/mod.rs
+++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -5,7 +5,7 @@ use rustc::mir::{self, Mir};
use rustc::ty::subst::SubstsRef;
use rustc::session::config::DebugInfo;
use rustc_mir::monomorphize::Instance;
-use rustc_target::abi::call::{FnType, PassMode};
+use rustc_target::abi::call::{FnType, PassMode, IgnoreMode};
use crate::base;
use crate::debuginfo::{self, VariableAccess, VariableKind, FunctionDebugContext};
use crate::traits::*;
@@ -86,6 +86,10 @@ pub struct FunctionCx<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> {
/// If this function is being monomorphized, this contains the type substitutions used.
param_substs: SubstsRef<'tcx>,
+
+ /// If this function is a C-variadic function, this contains the `PlaceRef` of the
+ /// "spoofed" `VaList`.
+ va_list_ref: Option<PlaceRef<'tcx, Bx::Value>>,
}
impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
@@ -246,13 +250,18 @@ pub fn codegen_mir<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
assert!(!instance.substs.needs_infer());
instance.substs
},
+ va_list_ref: None,
};
let memory_locals = analyze::non_ssa_locals(&fx);
// Allocate variable and temp allocas
fx.locals = {
- let args = arg_local_refs(&mut bx, &fx, &fx.scopes, &memory_locals);
+ // FIXME(dlrobertson): This is ugly. Find a better way of getting the `PlaceRef` or
+ // `LocalRef` from `arg_local_refs`
+ let mut va_list_ref = None;
+ let args = arg_local_refs(&mut bx, &fx, &fx.scopes, &memory_locals, &mut va_list_ref);
+ fx.va_list_ref = va_list_ref;
let mut allocate_local = |local| {
let decl = &mir.local_decls[local];
@@ -433,6 +442,7 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
debuginfo::MirDebugScope<Bx::DIScope>
>,
memory_locals: &BitSet<mir::Local>,
+ va_list_ref: &mut Option<PlaceRef<'tcx, Bx::Value>>,
) -> Vec<LocalRef<'tcx, Bx::Value>> {
let mir = fx.mir;
let tcx = fx.cx.tcx();
@@ -447,6 +457,15 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
None
};
+ // Store the index of the last argument. This is used to
+ // call va_start on the va_list instead of attempting
+ // to store_fn_arg.
+ let last_arg_idx = if fx.fn_ty.args.is_empty() {
+ None
+ } else {
+ Some(fx.fn_ty.args.len() - 1)
+ };
+
mir.args_iter().enumerate().map(|(arg_index, local)| {
let arg_decl = &mir.local_decls[local];
@@ -510,9 +529,16 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
// of putting everything in allocas just so we can use llvm.dbg.declare.
let local = |op| LocalRef::Operand(Some(op));
match arg.mode {
- PassMode::Ignore => {
+ PassMode::Ignore(IgnoreMode::Zst) => {
return local(OperandRef::new_zst(bx.cx(), arg.layout));
}
+ PassMode::Ignore(IgnoreMode::CVarArgs) => {
+ let backend_type = bx.cx().immediate_backend_type(arg.layout);
+ return local(OperandRef {
+ val: OperandValue::Immediate(bx.cx().const_undef(backend_type)),
+ layout: arg.layout,
+ });
+ }
PassMode::Direct(_) => {
let llarg = bx.get_param(bx.llfn(), llarg_idx as c_uint);
bx.set_value_name(llarg, &name);
@@ -559,9 +585,35 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
indirect_operand.store(bx, tmp);
tmp
} else {
- let tmp = PlaceRef::alloca(bx, arg.layout, &name);
- bx.store_fn_arg(arg, &mut llarg_idx, tmp);
- tmp
+ if fx.fn_ty.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 3cd0c39d413..cd527898977 100644
--- a/src/librustc_codegen_ssa/traits/intrinsic.rs
+++ b/src/librustc_codegen_ssa/traits/intrinsic.rs
@@ -20,4 +20,10 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
fn abort(&mut self);
fn assume(&mut self, val: Self::Value);
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
+ /// Trait method used to inject `va_start` on the "spoofed" `VaList` in
+ /// Rust defined C-variadic functions.
+ fn va_start(&mut self, val: Self::Value) -> Self::Value;
+ /// Trait method used to inject `va_end` on the "spoofed" `VaList` before
+ /// Rust defined C-variadic functions return.
+ fn va_end(&mut self, val: Self::Value) -> Self::Value;
}
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index a56c3215f9d..35489ab42e7 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 4202d10aa63..df035aab54c 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 a26a1a7861e..059af2dbba9 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 52f3a207770..64a2c92d04d 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 52d7f3ac3dc..e3fee8e5700 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 411eb192d90..fbbd120f934 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -24,9 +24,17 @@ mod x86_win64;
mod wasm32;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum IgnoreMode {
+ /// C-variadic arguments.
+ CVarArgs,
+ /// A zero-sized type.
+ Zst,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PassMode {
- /// Ignore the argument (useful for empty struct).
- Ignore,
+ /// Ignore the argument (useful for empty structs and C-variadic args).
+ Ignore(IgnoreMode),
/// Pass the argument directly.
Direct(ArgAttributes),
/// Pass a pair's elements directly in two arguments.
@@ -481,7 +489,10 @@ impl<'a, Ty> ArgType<'a, Ty> {
}
pub fn is_ignore(&self) -> bool {
- self.mode == PassMode::Ignore
+ match self.mode {
+ PassMode::Ignore(_) => true,
+ _ => false
+ }
}
}
@@ -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 2e809571ab1..6ca3ce88bd6 100644
--- a/src/librustc_target/abi/call/x86.rs
+++ b/src/librustc_target/abi/call/x86.rs
@@ -88,7 +88,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla
for arg in &mut fty.args {
let attrs = match arg.mode {
- PassMode::Ignore |
+ PassMode::Ignore(_) |
PassMode::Indirect(_, None) => continue,
PassMode::Direct(ref mut attrs) => attrs,
PassMode::Pair(..) |
diff --git a/src/librustc_traits/chalk_context/program_clauses.rs b/src/librustc_traits/chalk_context/program_clauses.rs
index 4d8a67ca638..3f88d0e08b4 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 634c024b064..f2ce9631f35 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 a81134cb07e..4a88922b50e 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(&lt, None);
+ tcx.type_of(va_list_did).subst(tcx, &[region.into()])
+ }
};
self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
@@ -1905,7 +1914,7 @@ 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 7bf7d825406..0a4c0eb3aff 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 2a4b17a6399..db89b32be7b 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 3b174b55f2b..924ced2e2a3 100644
--- a/src/librustc_typeck/check/intrinsic.rs
+++ b/src/librustc_typeck/check/intrinsic.rs
@@ -337,7 +337,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
"va_start" | "va_end" => {
match mk_va_list_ty() {
Some(va_list_ty) => (0, vec![va_list_ty], tcx.mk_unit()),
- None => bug!("va_list lang_item must be defined to use va_list intrinsics")
+ None => bug!("`va_list` language item needed for C-variadic intrinsics")
}
}
@@ -364,14 +364,14 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
};
(0, vec![tcx.mk_imm_ref(tcx.mk_region(env_region), va_list_ty)], ret_ty)
}
- None => bug!("va_list lang_item must be defined to use va_list intrinsics")
+ None => bug!("`va_list` language item needed for C-variadic intrinsics")
}
}
"va_arg" => {
match mk_va_list_ty() {
Some(va_list_ty) => (1, vec![va_list_ty], param(0)),
- None => bug!("va_list lang_item must be defined to use va_list intrinsics")
+ None => bug!("`va_list` language item needed for C-variadic intrinsics")
}
}
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 202d8bec4e9..3a430f77b6c 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<Span>) {
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 419796a2014..2095c81d0fb 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 72abbae231a..53dcc258c69 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1752,7 +1752,6 @@ impl Clean<Item> for doctree::Function {
pub struct FnDecl {
pub inputs: Arguments,
pub output: FunctionRetTy,
- pub variadic: bool,
pub attrs: Attributes,
}
@@ -1831,7 +1830,6 @@ impl<'a, A: Copy> Clean<FnDecl> for (&'a hir::FnDecl, A)
FnDecl {
inputs: (&self.0.inputs[..], self.1).clean(cx),
output: self.0.output.clean(cx),
- variadic: self.0.variadic,
attrs: Attributes::default()
}
}
@@ -1849,7 +1847,6 @@ impl<'a, 'tcx> Clean<FnDecl> for (DefId, ty::PolyFnSig<'tcx>) {
FnDecl {
output: Return(sig.skip_binder().output().clean(cx)),
attrs: Attributes::default(),
- variadic: sig.skip_binder().variadic,
inputs: Arguments {
values: sig.skip_binder().inputs().iter().map(|t| {
Argument {
@@ -2252,6 +2249,7 @@ pub enum Type {
Slice(Box<Type>),
Array(Box<Type>, String),
Never,
+ CVarArgs,
Unique(Box<Type>),
RawPointer(Mutability, Box<Type>),
BorrowedRef {
@@ -2290,6 +2288,7 @@ pub enum PrimitiveType {
Reference,
Fn,
Never,
+ CVarArgs,
}
#[derive(Clone, RustcEncodable, RustcDecodable, Copy, Debug)]
@@ -2469,6 +2468,7 @@ impl PrimitiveType {
Reference => "reference",
Fn => "fn",
Never => "never",
+ CVarArgs => "...",
}
}
@@ -2518,6 +2518,7 @@ impl Clean<Type> for hir::Ty {
match self.node {
TyKind::Never => Never,
+ TyKind::CVarArgs(_) => CVarArgs,
TyKind::Ptr(ref m) => RawPointer(m.mutbl.clean(cx), box m.ty.clean(cx)),
TyKind::Rptr(ref l, ref m) => {
let lifetime = if l.is_elided() {
@@ -3654,6 +3655,7 @@ fn build_deref_target_impls(cx: &DocContext<'_, '_, '_>,
Reference => None,
Fn => None,
Never => None,
+ CVarArgs => tcx.lang_items().va_list(),
};
if let Some(did) = did {
if !did.is_local() {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 4463dad1c8a..d204a179ca6 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -609,6 +609,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
primitive_link(f, PrimitiveType::Array, &format!("; {}]", n))
}
clean::Never => primitive_link(f, PrimitiveType::Never, "!"),
+ clean::CVarArgs => primitive_link(f, PrimitiveType::CVarArgs, "..."),
clean::RawPointer(m, ref t) => {
match **t {
clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
@@ -834,18 +835,10 @@ impl fmt::Display for clean::FunctionRetTy {
impl fmt::Display for clean::FnDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.variadic {
- if f.alternate() {
- write!(f, "({args:#}, ...){arrow:#}", args = self.inputs, arrow = self.output)
- } else {
- write!(f, "({args}, ...){arrow}", args = self.inputs, arrow = self.output)
- }
+ if f.alternate() {
+ write!(f, "({args:#}){arrow:#}", args = self.inputs, arrow = self.output)
} else {
- if f.alternate() {
- write!(f, "({args:#}){arrow:#}", args = self.inputs, arrow = self.output)
- } else {
- write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
- }
+ write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
}
}
}
@@ -907,12 +900,7 @@ impl<'a> fmt::Display for Function<'a> {
}
}
- let mut args_plain = format!("({})", args_plain);
-
- if decl.variadic {
- args.push_str(",<br> ...");
- args_plain.push_str(", ...");
- }
+ let args_plain = format!("({})", args_plain);
let output = if let hir::IsAsync::Async = asyncness {
Cow::Owned(decl.sugared_async_return_type())
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index b6f9ae36da7..a01a5bb0a36 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<Arg>,
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 2f88749ace8..e95f0589449 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 669c68eda39..1c0b931b289 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 d30372d45d7..802b7808695 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -409,7 +409,8 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
let Ty { id, node, span } = ty.deref_mut();
vis.visit_id(id);
match node {
- TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never => {}
+ TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err |
+ TyKind::Never | TyKind::CVarArgs => {}
TyKind::Slice(ty) => vis.visit_ty(ty),
TyKind::Ptr(mt) => vis.visit_mt(mt),
TyKind::Rptr(lt, mt) => {
@@ -680,7 +681,7 @@ pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut IsAsync, vis: &mut T)
}
pub fn noop_visit_fn_decl<T: MutVisitor>(decl: &mut P<FnDecl>, vis: &mut T) {
- let FnDecl { inputs, output, variadic: _ } = decl.deref_mut();
+ let FnDecl { inputs, output, c_variadic: _ } = decl.deref_mut();
visit_vec(inputs, |input| vis.visit_arg(input));
match output {
FunctionRetTy::Default(span) => vis.visit_span(span),
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index b762365c31f..fd5038a8614 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<Ty>> {
- self.parse_ty_common(true, true)
+ self.parse_ty_common(true, true, false)
}
/// Parses a type in restricted contexts where `+` is not permitted.
@@ -1631,11 +1631,11 @@ impl<'a> Parser<'a> {
/// Example 2: `value1 as TYPE + value2`
/// `+` is prohibited to avoid interactions with expression grammar.
fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
- self.parse_ty_common(false, true)
+ self.parse_ty_common(false, true, false)
}
- fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool)
- -> PResult<'a, P<Ty>> {
+ fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool,
+ allow_c_variadic: bool) -> PResult<'a, P<Ty>> {
maybe_whole!(self, NtTy, |x| x);
let lo = self.span;
@@ -1772,6 +1772,15 @@ impl<'a> Parser<'a> {
TyKind::Path(None, path)
}
}
+ } else if self.check(&token::DotDotDot) {
+ if allow_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<Arg> , bool)> {
self.expect(&token::OpenDelim(token::Paren))?;
let sp = self.span;
- let mut variadic = false;
+ let mut c_variadic = false;
let (args, recovered): (Vec<Option<Arg>>, bool) =
self.parse_seq_to_before_end(
&token::CloseDelim(token::Paren),
SeqSep::trailing_allowed(token::Comma),
|p| {
- if p.token == token::DotDotDot {
- p.bump();
- variadic = true;
- if allow_variadic {
- if p.token != token::CloseDelim(token::Paren) {
- let span = p.span;
- p.span_err(span,
- "`...` must be last in argument list for variadic function");
- }
- Ok(None)
- } else {
- let span = p.prev_span;
- if p.token == token::CloseDelim(token::Paren) {
- // continue parsing to present any further errors
- p.struct_span_err(
- span,
- "only foreign functions are allowed to be variadic"
- ).emit();
- Ok(Some(dummy_arg(span)))
- } else {
- // this function definition looks beyond recovery, stop parsing
- p.span_err(span,
- "only foreign functions are allowed to be variadic");
- Ok(None)
- }
- }
+ // If the argument is a C-variadic argument we should not
+ // enforce named arguments.
+ let enforce_named_args = if p.token == token::DotDotDot {
+ false
} else {
- match p.parse_arg_general(named_args, false) {
- Ok(arg) => Ok(Some(arg)),
- Err(mut e) => {
- e.emit();
- let lo = p.prev_span;
- // Skip every token until next possible arg or end.
- p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
- // Create a placeholder argument for proper arg count (#34264).
- let span = lo.to(p.prev_span);
- Ok(Some(dummy_arg(span)))
+ named_args
+ };
+ match p.parse_arg_general(enforce_named_args, false,
+ allow_c_variadic) {
+ Ok(arg) => {
+ if let TyKind::CVarArgs = arg.ty.node {
+ c_variadic = true;
+ if p.token != token::CloseDelim(token::Paren) {
+ let span = p.span;
+ p.span_err(span,
+ "`...` must be the last argument of a C-variadic function");
+ Ok(None)
+ } else {
+ Ok(Some(arg))
+ }
+ } else {
+ Ok(Some(arg))
}
+ },
+ Err(mut e) => {
+ e.emit();
+ let lo = p.prev_span;
+ // Skip every token until next possible arg or end.
+ p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
+ // Create a placeholder argument for proper arg count (issue #34264).
+ let span = lo.to(p.prev_span);
+ Ok(Some(dummy_arg(span)))
}
}
}
@@ -6173,24 +6182,24 @@ 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<FnDecl>> {
+ fn parse_fn_decl(&mut self, allow_c_variadic: bool) -> PResult<'a, P<FnDecl>> {
- let (args, variadic) = self.parse_fn_args(true, allow_variadic)?;
+ let (args, c_variadic) = self.parse_fn_args(true, allow_c_variadic)?;
let ret_ty = self.parse_ret_ty(true)?;
Ok(P(FnDecl {
inputs: args,
output: ret_ty,
- variadic,
+ c_variadic,
}))
}
@@ -6337,7 +6346,7 @@ 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 d6265ebde1b..942bd969391 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 46d8d772e93..6b3a30ccb54 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 00000000000..09c18ed90b2
--- /dev/null
+++ b/src/test/codegen/c-variadic.rs
@@ -0,0 +1,69 @@
+// compile-flags: -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+#![feature(c_variadic)]
+#![no_std]
+use core::ffi::VaList;
+
+extern "C" {
+ fn foreign_c_variadic_0(_: i32, ...);
+ fn foreign_c_variadic_1(_: VaList, ...);
+}
+
+pub unsafe extern "C" fn use_foreign_c_variadic_0() {
+ // Ensure that we correctly call foreign C-variadic functions.
+ // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0)
+ foreign_c_variadic_0(0);
+ // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42)
+ foreign_c_variadic_0(0, 42i32);
+ // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024)
+ foreign_c_variadic_0(0, 42i32, 1024i32);
+ // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024, i32 0)
+ foreign_c_variadic_0(0, 42i32, 1024i32, 0i32);
+}
+
+// Ensure that we do not remove the `va_list` passed to the foreign function when
+// removing the "spoofed" `VaList` that is used by Rust defined C-variadics.
+pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
+ // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
+ foreign_c_variadic_1(ap);
+}
+
+pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
+ // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 42)
+ foreign_c_variadic_1(ap, 42i32);
+}
+pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
+ // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42)
+ foreign_c_variadic_1(ap, 2i32, 42i32);
+}
+
+pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
+ // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42, i32 0)
+ foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
+}
+
+// Ensure that `va_start` and `va_end` are properly injected.
+#[no_mangle]
+pub unsafe extern "C" fn c_variadic(n: i32, mut ap: ...) -> i32 {
+ // CHECK: call void @llvm.va_start
+ let mut sum = 0;
+ for _ in 0..n {
+ sum += ap.arg::<i32>();
+ }
+ sum
+ // CHECK: call void @llvm.va_end
+}
+
+// Ensure that we generate the correct `call` signature when calling a Rust
+// defined C-variadic.
+pub unsafe fn test_c_variadic_call() {
+ // CHECK: call i32 (i32, ...) @c_variadic(i32 0)
+ c_variadic(0);
+ // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42)
+ c_variadic(0, 42i32);
+ // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024)
+ c_variadic(0, 42i32, 1024i32);
+ // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024, i32 0)
+ c_variadic(0, 42i32, 1024i32, 0i32);
+}
diff --git a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
index d55aac1e40f..96a238afaec 100644
--- a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
+++ b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs
@@ -18,8 +18,10 @@ macro_rules! continue_if {
unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool {
let cstr0 = CStr::from_ptr(ptr);
- let cstr1 = CString::new(val).unwrap();
- &*cstr1 == cstr0
+ match CString::new(val) {
+ Ok(cstr1) => &*cstr1 == cstr0,
+ Err(_) => false,
+ }
}
#[no_mangle]
@@ -68,3 +70,24 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize {
}
})
}
+
+#[no_mangle]
+pub unsafe extern "C" fn check_varargs_0(_: c_int, mut ap: ...) -> usize {
+ continue_if!(ap.arg::<c_int>() == 42);
+ continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello, World!"));
+ 0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize {
+ continue_if!(ap.arg::<c_double>().floor() == 3.14f64.floor());
+ continue_if!(ap.arg::<c_long>() == 12);
+ continue_if!(ap.arg::<c_char>() == 'A' as c_char);
+ continue_if!(ap.arg::<c_longlong>() == 1);
+ 0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn check_varargs_2(_: c_int, mut ap: ...) -> usize {
+ 0
+}
diff --git a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
index 95cf0ef46ca..91b060dce26 100644
--- a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
+++ b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
@@ -8,6 +8,9 @@ extern size_t check_list_0(va_list ap);
extern size_t check_list_1(va_list ap);
extern size_t check_list_2(va_list ap);
extern size_t check_list_copy_0(va_list ap);
+extern size_t check_varargs_0(int fixed, ...);
+extern size_t check_varargs_1(int fixed, ...);
+extern size_t check_varargs_2(int fixed, ...);
int test_rust(size_t (*fn)(va_list), ...) {
size_t ret = 0;
@@ -26,5 +29,12 @@ int main(int argc, char* argv[]) {
assert(test_rust(check_list_2, 3.14, 12l, 'a', 6.28, "Hello", 42, "World") == 0);
assert(test_rust(check_list_copy_0, 6.28, 16, 'A', "Skip Me!", "Correct") == 0);
+
+ assert(check_varargs_0(0, 42, "Hello, World!") == 0);
+
+ assert(check_varargs_1(0, 3.14, 12l, 'A', 0x1LL) == 0);
+
+ assert(check_varargs_2(0, "All", "of", "these", "are", "ignored", ".") == 0);
+
return 0;
}
diff --git a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
index 956bc5ad862..80e0b0102af 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<Expr>)) {
let decl = P(FnDecl {
inputs: vec![],
output: FunctionRetTy::Default(DUMMY_SP),
- variadic: false,
+ c_variadic: false,
});
iter_exprs(depth - 1, &mut |e| g(
ExprKind::Closure(CaptureBy::Value,
diff --git a/src/test/rustdoc/variadic.rs b/src/test/rustdoc/variadic.rs
index bd8f1775b3d..5af2aea21fc 100644
--- a/src/test/rustdoc/variadic.rs
+++ b/src/test/rustdoc/variadic.rs
@@ -1,4 +1,4 @@
extern "C" {
- // @has variadic/fn.foo.html //pre 'pub unsafe extern "C" fn foo(x: i32, ...)'
+ // @has variadic/fn.foo.html //pre 'pub unsafe extern "C" fn foo(x: i32, _: ...)'
pub fn foo(x: i32, ...);
}
diff --git a/src/test/ui/variadic/variadic-ffi.rs b/src/test/ui/c-variadic/variadic-ffi-1.rs
index 61b2ad4bed5..61b2ad4bed5 100644
--- a/src/test/ui/variadic/variadic-ffi.rs
+++ b/src/test/ui/c-variadic/variadic-ffi-1.rs
diff --git a/src/test/ui/variadic/variadic-ffi.stderr b/src/test/ui/c-variadic/variadic-ffi-1.stderr
index 617b1f46ea3..61d55ce0d3e 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
index 224ac16f458..224ac16f458 100644
--- a/src/test/ui/variadic/variadic-ffi-2.rs
+++ b/src/test/ui/c-variadic/variadic-ffi-2.rs
diff --git a/src/test/ui/variadic/variadic-ffi-2.stderr b/src/test/ui/c-variadic/variadic-ffi-2.stderr
index cb2a9f874b7..4c8b8d2b2e1 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
index 12b3426d9da..c02d1f54e56 100644
--- a/src/test/ui/variadic/variadic-ffi-3.rs
+++ b/src/test/ui/c-variadic/variadic-ffi-3.rs
@@ -14,12 +14,10 @@ fn main() {
let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
//~^ ERROR: mismatched types
//~| expected type `unsafe extern "C" fn(isize, u8)`
- //~| found type `unsafe extern "C" fn(isize, u8, ...) {foo}`
let y: extern "C" fn(f: isize, x: u8, ...) = bar;
//~^ ERROR: mismatched types
- //~| expected type `extern "C" fn(isize, u8, ...)`
- //~| found type `extern "C" fn(isize, u8) {bar}`
+ //~| expected type `for<'r> extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...)`
foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function
diff --git a/src/test/ui/variadic/variadic-ffi-3.stderr b/src/test/ui/c-variadic/variadic-ffi-3.stderr
index 0fecbbf36b8..82e3c6cd06f 100644
--- a/src/test/ui/variadic/variadic-ffi-3.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-3.stderr
@@ -23,49 +23,49 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
| ^^^ expected non-variadic fn, found variadic function
|
= note: expected type `unsafe extern "C" fn(isize, u8)`
- found type `unsafe extern "C" fn(isize, u8, ...) {foo}`
+ found type `for<'r> unsafe extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...) {foo}`
error[E0308]: mismatched types
- --> $DIR/variadic-ffi-3.rs:19:54
+ --> $DIR/variadic-ffi-3.rs:18:54
|
LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
| ^^^ expected variadic fn, found non-variadic function
|
- = note: expected type `extern "C" fn(isize, u8, ...)`
+ = note: expected type `for<'r> extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...)`
found type `extern "C" fn(isize, u8) {bar}`
error[E0617]: can't pass `f32` to variadic function
- --> $DIR/variadic-ffi-3.rs:24:19
+ --> $DIR/variadic-ffi-3.rs:22:19
|
LL | foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
| ^^^^ help: cast the value to `c_double`: `3f32 as c_double`
error[E0617]: can't pass `bool` to variadic function
- --> $DIR/variadic-ffi-3.rs:25:19
+ --> $DIR/variadic-ffi-3.rs:23:19
|
LL | foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function
| ^^^^ help: cast the value to `c_int`: `true as c_int`
error[E0617]: can't pass `i8` to variadic function
- --> $DIR/variadic-ffi-3.rs:26:19
+ --> $DIR/variadic-ffi-3.rs:24:19
|
LL | foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function
| ^^^ help: cast the value to `c_int`: `1i8 as c_int`
error[E0617]: can't pass `u8` to variadic function
- --> $DIR/variadic-ffi-3.rs:27:19
+ --> $DIR/variadic-ffi-3.rs:25:19
|
LL | foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function
| ^^^ help: cast the value to `c_uint`: `1u8 as c_uint`
error[E0617]: can't pass `i16` to variadic function
- --> $DIR/variadic-ffi-3.rs:28:19
+ --> $DIR/variadic-ffi-3.rs:26:19
|
LL | foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function
| ^^^^ help: cast the value to `c_int`: `1i16 as c_int`
error[E0617]: can't pass `u16` to variadic function
- --> $DIR/variadic-ffi-3.rs:29:19
+ --> $DIR/variadic-ffi-3.rs:27:19
|
LL | foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function
| ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint`
diff --git a/src/test/ui/c-variadic/variadic-ffi-4.rs b/src/test/ui/c-variadic/variadic-ffi-4.rs
new file mode 100644
index 00000000000..9101be56456
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-4.rs
@@ -0,0 +1,29 @@
+#![crate_type="lib"]
+#![no_std]
+#![feature(c_variadic)]
+
+use core::ffi::VaList;
+
+pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
+ ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
+ ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) {
+ let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+}
+
+pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+ *ap0 = ap1; //~ ERROR: mismatched types
+}
+
+pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+ ap0 = &mut ap1;
+ //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+ //~^^ ERROR: mismatched types
+ //~^^^ ERROR: mismatched types
+ //~^^^^ ERROR: cannot infer an appropriate lifetime
+}
diff --git a/src/test/ui/c-variadic/variadic-ffi-4.stderr b/src/test/ui/c-variadic/variadic-ffi-4.stderr
new file mode 100644
index 00000000000..1d752be065c
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-4.stderr
@@ -0,0 +1,198 @@
+error[E0621]: explicit lifetime required in the type of `ap`
+ --> $DIR/variadic-ffi-4.rs:8:5
+ |
+LL | pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
+ | --- help: add explicit lifetime `'a` to the type of `ap`: `core::ffi::VaList<'a>`
+LL | ap //~ ERROR: explicit lifetime required
+ | ^^ lifetime `'a` required
+
+error[E0621]: explicit lifetime required in the type of `ap`
+ --> $DIR/variadic-ffi-4.rs:12:5
+ |
+LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
+ | --- help: add explicit lifetime `'static` to the type of `ap`: `core::ffi::VaList<'static>`
+LL | ap //~ ERROR: explicit lifetime required
+ | ^^ lifetime `'static` required
+
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/variadic-ffi-4.rs:16:28
+ |
+LL | let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+ | ^^
+ |
+note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 16:21...
+ --> $DIR/variadic-ffi-4.rs:16:21
+ |
+LL | let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+ | ^^^^^^^^^^^
+ = note: ...so that the expression is assignable:
+ expected core::ffi::VaList<'_>
+ found core::ffi::VaList<'_>
+note: but, the lifetime must be valid for the method call at 16:13...
+ --> $DIR/variadic-ffi-4.rs:16:13
+ |
+LL | let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+ | ^^^^^^^^^^^^^^^^^^^^
+note: ...so type `core::ffi::VaList<'_>` of expression is valid during the expression
+ --> $DIR/variadic-ffi-4.rs:16:13
+ |
+LL | let _ = ap.copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+ --> $DIR/variadic-ffi-4.rs:20:12
+ |
+LL | *ap0 = ap1; //~ ERROR: mismatched types
+ | ^^^ lifetime mismatch
+ |
+ = note: expected type `core::ffi::VaList<'_>`
+ found type `core::ffi::VaList<'_>`
+note: the anonymous lifetime #3 defined on the function body at 19:1...
+ --> $DIR/variadic-ffi-4.rs:19:1
+ |
+LL | / pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+LL | | *ap0 = ap1; //~ ERROR: mismatched types
+LL | | }
+ | |_^
+note: ...does not necessarily outlive the anonymous lifetime #2 defined on the function body at 19:1
+ --> $DIR/variadic-ffi-4.rs:19:1
+ |
+LL | / pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+LL | | *ap0 = ap1; //~ ERROR: mismatched types
+LL | | }
+ | |_^
+
+error[E0490]: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+ --> $DIR/variadic-ffi-4.rs:24:11
+ |
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^
+ |
+note: the type is valid for the anonymous lifetime #1 defined on the function body at 23:1
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+note: but the borrow lasts for the anonymous lifetime #3 defined on the function body at 23:1
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+
+error[E0308]: mismatched types
+ --> $DIR/variadic-ffi-4.rs:24:11
+ |
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^ lifetime mismatch
+ |
+ = note: expected type `&mut core::ffi::VaList<'_>`
+ found type `&mut core::ffi::VaList<'_>`
+note: the anonymous lifetime #3 defined on the function body at 23:1...
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+note: ...does not necessarily outlive the anonymous lifetime #2 defined on the function body at 23:1
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+
+error[E0308]: mismatched types
+ --> $DIR/variadic-ffi-4.rs:24:11
+ |
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^ lifetime mismatch
+ |
+ = note: expected type `&mut core::ffi::VaList<'_>`
+ found type `&mut core::ffi::VaList<'_>`
+note: the anonymous lifetime #2 defined on the function body at 23:1...
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+note: ...does not necessarily outlive the anonymous lifetime #3 defined on the function body at 23:1
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+
+error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
+ --> $DIR/variadic-ffi-4.rs:24:11
+ |
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the function body at 23:1...
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+note: ...so that the type `core::ffi::VaList<'_>` is not borrowed for too long
+ --> $DIR/variadic-ffi-4.rs:24:11
+ |
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^
+note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the function body at 23:1...
+ --> $DIR/variadic-ffi-4.rs:23:1
+ |
+LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaList, mut ap1: ...) {
+LL | | ap0 = &mut ap1;
+LL | | //~^ ERROR: a value of type `core::ffi::VaList<'_>` is borrowed for too long
+LL | | //~^^ ERROR: mismatched types
+LL | | //~^^^ ERROR: mismatched types
+LL | | //~^^^^ ERROR: cannot infer an appropriate lifetime
+LL | | }
+ | |_^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/variadic-ffi-4.rs:24:11
+ |
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^
+
+error: aborting due to 8 previous errors
+
+Some errors occurred: E0308, E0490, E0495, E0621.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-5.rs b/src/test/ui/c-variadic/variadic-ffi-5.rs
new file mode 100644
index 00000000000..d96482ff4d1
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-5.rs
@@ -0,0 +1,31 @@
+#![crate_type="lib"]
+#![no_std]
+#![feature(c_variadic)]
+// The tests in this file are similar to that of variadic-ffi-4, but this
+// one enables nll.
+#![feature(nll)]
+
+use core::ffi::VaList;
+
+pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
+ ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
+ ap //~ ERROR: explicit lifetime required
+}
+
+pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) {
+ let _ = ap.copy(|ap| { ap }); //~ ERROR: lifetime may not live long enough
+}
+
+pub unsafe extern "C" fn no_escape3(_: usize, ap0: &mut VaList, mut ap1: ...) {
+ *ap0 = ap1; //~ ERROR: lifetime may not live long enough
+}
+
+pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+ ap0 = &mut ap1;
+ //~^ ERROR: lifetime may not live long enough
+ //~^^ ERROR: lifetime may not live long enough
+ //~^^^ ERROR: `ap1` does not live long enough
+}
diff --git a/src/test/ui/c-variadic/variadic-ffi-5.stderr b/src/test/ui/c-variadic/variadic-ffi-5.stderr
new file mode 100644
index 00000000000..2d452872baf
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-5.stderr
@@ -0,0 +1,73 @@
+error[E0621]: explicit lifetime required in the type of `ap`
+ --> $DIR/variadic-ffi-5.rs:11:5
+ |
+LL | pub unsafe extern "C" fn no_escape0<'a>(_: usize, ap: ...) -> VaList<'a> {
+ | --- help: add explicit lifetime `'a` to the type of `ap`: `core::ffi::VaList<'a>`
+LL | ap //~ ERROR: explicit lifetime required
+ | ^^ lifetime `'a` required
+
+error[E0621]: explicit lifetime required in the type of `ap`
+ --> $DIR/variadic-ffi-5.rs:15:5
+ |
+LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
+ | --- help: add explicit lifetime `'static` to the type of `ap`: `core::ffi::VaList<'static>`
+LL | ap //~ ERROR: explicit lifetime required
+ | ^^ lifetime `'static` required
+
+error: lifetime may not live long enough
+ --> $DIR/variadic-ffi-5.rs:19:28
+ |
+LL | let _ = ap.copy(|ap| { ap }); //~ ERROR: lifetime may not live long enough
+ | --- ^^ returning this value requires that `'1` must outlive `'2`
+ | | |
+ | | return type of closure is core::ffi::VaList<'2>
+ | has type `core::ffi::VaList<'1>`
+
+error: lifetime may not live long enough
+ --> $DIR/variadic-ffi-5.rs:23:5
+ |
+LL | pub unsafe extern "C" fn no_escape3(_: usize, ap0: &mut VaList, mut ap1: ...) {
+ | --- ------- has type `core::ffi::VaList<'1>`
+ | |
+ | has type `&mut core::ffi::VaList<'2>`
+LL | *ap0 = ap1; //~ ERROR: lifetime may not live long enough
+ | ^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+
+error: lifetime may not live long enough
+ --> $DIR/variadic-ffi-5.rs:27:5
+ |
+LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+ | ------- ------- has type `core::ffi::VaList<'2>`
+ | |
+ | has type `&mut core::ffi::VaList<'1>`
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+
+error: lifetime may not live long enough
+ --> $DIR/variadic-ffi-5.rs:27:5
+ |
+LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+ | ------- ------- has type `core::ffi::VaList<'1>`
+ | |
+ | has type `&mut core::ffi::VaList<'2>`
+LL | ap0 = &mut ap1;
+ | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+
+error[E0597]: `ap1` does not live long enough
+ --> $DIR/variadic-ffi-5.rs:27:11
+ |
+LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
+ | - let's call the lifetime of this reference `'1`
+LL | ap0 = &mut ap1;
+ | ------^^^^^^^^
+ | | |
+ | | borrowed value does not live long enough
+ | assignment requires that `ap1` is borrowed for `'1`
+...
+LL | }
+ | - `ap1` dropped here while still borrowed
+
+error: aborting due to 7 previous errors
+
+Some errors occurred: E0597, E0621.
+For more information about an error, try `rustc --explain E0597`.
diff --git a/src/test/ui/c-variadic/variadic-ffi-6.rs b/src/test/ui/c-variadic/variadic-ffi-6.rs
new file mode 100644
index 00000000000..4dd8a2d4521
--- /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 00000000000..76bd18959a5
--- /dev/null
+++ b/src/test/ui/c-variadic/variadic-ffi-6.stderr
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+ --> $DIR/variadic-ffi-6.rs:7:6
+ |
+LL | ) -> &usize { //~ ERROR missing lifetime specifier
+ | ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
+ |
+ = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/error-codes/E0045.stderr b/src/test/ui/error-codes/E0045.stderr
index b38bbc169bd..0ce91f0a401 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 9eed1225ab8..51f13c7dbd5 100644
--- a/src/test/ui/error-codes/E0617.rs
+++ b/src/test/ui/error-codes/E0617.rs
@@ -22,7 +22,7 @@ fn main() {
//~^ ERROR can't pass `u16` to variadic function
//~| HELP cast the value to `c_uint`
printf(::std::ptr::null(), printf);
- //~^ ERROR can't pass `unsafe extern "C" fn(*const i8, ...) {printf}` to variadic function
- //~| HELP cast the value to `unsafe extern "C" fn(*const i8, ...)`
+ //~^ ERROR can't pass `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...) {printf}` to variadic function
+ //~| HELP cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...)`
}
}
diff --git a/src/test/ui/error-codes/E0617.stderr b/src/test/ui/error-codes/E0617.stderr
index 486ca1fa92f..8387d5c7e93 100644
--- a/src/test/ui/error-codes/E0617.stderr
+++ b/src/test/ui/error-codes/E0617.stderr
@@ -28,15 +28,15 @@ error[E0617]: can't pass `u16` to variadic function
LL | printf(::std::ptr::null(), 0u16);
| ^^^^ help: cast the value to `c_uint`: `0u16 as c_uint`
-error[E0617]: can't pass `unsafe extern "C" fn(*const i8, ...) {printf}` to variadic function
+error[E0617]: can't pass `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...) {printf}` to variadic function
--> $DIR/E0617.rs:24:36
|
LL | printf(::std::ptr::null(), printf);
| ^^^^^^
-help: cast the value to `unsafe extern "C" fn(*const i8, ...)`
+help: cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...)`
|
-LL | printf(::std::ptr::null(), printf as unsafe extern "C" fn(*const i8, ...));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | printf(::std::ptr::null(), printf as for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 6 previous errors
diff --git a/src/test/ui/feature-gate/feature-gate-c_variadic.rs b/src/test/ui/feature-gate/feature-gate-c_variadic.rs
new file mode 100644
index 00000000000..5801a2a89e2
--- /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 00000000000..a876e16fdea
--- /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 aea630e7c26..8d23f0e4770 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 7a0b8066fd5..b2dbf8b9190 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 2473420a779..b308e644ad9 100644
--- a/src/test/ui/parser/recover-enum2.stderr
+++ b/src/test/ui/parser/recover-enum2.stderr
@@ -10,11 +10,11 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{`
LL | Nope(i32 {}) //~ ERROR: found `{`
| ^ expected one of 7 possible tokens here
-error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `::`, `<`, `?`, `[`, `_`, `crate`, `dyn`, `extern`, `fn`, `for`, `impl`, `pub`, `unsafe`, `}`, or lifetime, found `{`
+error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `...`, `::`, `<`, `?`, `[`, `_`, `crate`, `dyn`, `extern`, `fn`, `for`, `impl`, `pub`, `unsafe`, `}`, or lifetime, found `{`
--> $DIR/recover-enum2.rs:27:22
|
LL | Nope(i32 {}) //~ ERROR: found `{`
- | ^ expected one of 23 possible tokens here
+ | ^ expected one of 24 possible tokens here
error: expected expression, found reserved identifier `_`
--> $DIR/recover-enum2.rs:32:22
diff --git a/src/test/ui/parser/variadic-ffi-3.rs b/src/test/ui/parser/variadic-ffi-3.rs
index 13bce27bb83..ce83cc87abe 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 150de9e63d3..8ea4d194396 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 812ed256a5d..5f8b3f8f539 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 2d036b0cf37..69fbf84869c 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 63196eda7cc..cd60f36b1d2 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 {