1
Fork 0

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
This commit is contained in:
bors 2019-02-28 15:00:25 +00:00
commit 1999a22881
78 changed files with 1797 additions and 921 deletions

View file

@ -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
}
```

View file

@ -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
}
```

View file

@ -617,6 +617,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
TyKind::Typeof(ref expression) => { TyKind::Typeof(ref expression) => {
visitor.visit_anon_const(expression) visitor.visit_anon_const(expression)
} }
TyKind::CVarArgs(ref lt) => {
visitor.visit_lifetime(lt)
}
TyKind::Infer | TyKind::Err => {} TyKind::Infer | TyKind::Err => {}
} }
} }

View file

@ -74,7 +74,7 @@ const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF;
pub struct LoweringContext<'a> { pub struct LoweringContext<'a> {
crate_root: Option<&'static str>, 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, sess: &'a Session,
cstore: &'a dyn CrateStore, cstore: &'a dyn CrateStore,
@ -107,25 +107,25 @@ pub struct LoweringContext<'a> {
/// written at all (e.g., `&T` or `std::cell::Ref<T>`). /// written at all (e.g., `&T` or `std::cell::Ref<T>`).
anonymous_lifetime_mode: AnonymousLifetimeMode, anonymous_lifetime_mode: AnonymousLifetimeMode,
// Used to create lifetime definitions from in-band lifetime usages. /// 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` /// 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 /// When a named lifetime is encountered in a function or impl header and
// has not been defined /// has not been defined
// (i.e., it doesn't appear in the in_scope_lifetimes list), it is added /// (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 /// to this list. The results of this list are then added to the list of
// lifetime definitions in the corresponding impl or function generics. /// lifetime definitions in the corresponding impl or function generics.
lifetimes_to_define: Vec<(Span, ParamName)>, lifetimes_to_define: Vec<(Span, ParamName)>,
// Whether or not in-band lifetimes are being collected. This is used to /// 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 /// 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, /// in in-band lifetime definitions, such a function or an impl header,
// including implicit lifetimes from `impl_header_lifetime_elision`. /// including implicit lifetimes from `impl_header_lifetime_elision`.
is_collecting_in_band_lifetimes: bool, is_collecting_in_band_lifetimes: bool,
// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked /// 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 /// against this list to see if it is already in-scope, or if a definition
// needs to be created for it. /// needs to be created for it.
in_scope_lifetimes: Vec<Ident>, in_scope_lifetimes: Vec<Ident>,
current_module: NodeId, current_module: NodeId,
@ -954,7 +954,7 @@ impl<'a> LoweringContext<'a> {
let decl = FnDecl { let decl = FnDecl {
inputs: vec![], inputs: vec![],
output, output,
variadic: false c_variadic: false
}; };
let body_id = self.record_body(body_expr, Some(&decl)); let body_id = self.record_body(body_expr, Some(&decl));
self.is_generator = prev_is_generator; 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::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); let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(t.id);
@ -2112,7 +2118,7 @@ impl<'a> LoweringContext<'a> {
P(hir::FnDecl { P(hir::FnDecl {
inputs, inputs,
output, output,
variadic: decl.variadic, c_variadic: decl.c_variadic,
implicit_self: decl.inputs.get(0).map_or( implicit_self: decl.inputs.get(0).map_or(
hir::ImplicitSelfKind::None, hir::ImplicitSelfKind::None,
|arg| { |arg| {
@ -3967,7 +3973,7 @@ impl<'a> LoweringContext<'a> {
let outer_decl = FnDecl { let outer_decl = FnDecl {
inputs: decl.inputs.clone(), inputs: decl.inputs.clone(),
output: FunctionRetTy::Default(fn_decl_span), output: FunctionRetTy::Default(fn_decl_span),
variadic: false, c_variadic: false,
}; };
// We need to lower the declaration outside the new scope, because we // 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 // have to conserve the state of being inside a loop condition for the

View file

@ -1829,6 +1829,9 @@ pub enum TyKind {
Infer, Infer,
/// Placeholder for a type that has failed to be defined. /// Placeholder for a type that has failed to be defined.
Err, 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)] #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
@ -1865,7 +1868,7 @@ pub struct Arg {
pub struct FnDecl { pub struct FnDecl {
pub inputs: HirVec<Ty>, pub inputs: HirVec<Ty>,
pub output: FunctionRetTy, pub output: FunctionRetTy,
pub variadic: bool, pub c_variadic: bool,
/// Does the function have an implicit self? /// Does the function have an implicit self?
pub implicit_self: ImplicitSelfKind, pub implicit_self: ImplicitSelfKind,
} }

View file

@ -434,6 +434,9 @@ impl<'a> State<'a> {
self.s.word("/*ERROR*/")?; self.s.word("/*ERROR*/")?;
self.pclose()?; self.pclose()?;
} }
hir::TyKind::CVarArgs(_) => {
self.s.word("...")?;
}
} }
self.end() self.end()
} }
@ -2004,7 +2007,7 @@ impl<'a> State<'a> {
s.print_type(ty)?; s.print_type(ty)?;
s.end() s.end()
})?; })?;
if decl.variadic { if decl.c_variadic {
self.s.word(", ...")?; self.s.word(", ...")?;
} }
self.pclose()?; self.pclose()?;

View file

@ -361,13 +361,14 @@ impl_stable_hash_for!(enum hir::TyKind {
TraitObject(trait_refs, lifetime), TraitObject(trait_refs, lifetime),
Typeof(body_id), Typeof(body_id),
Err, Err,
Infer Infer,
CVarArgs(lt),
}); });
impl_stable_hash_for!(struct hir::FnDecl { impl_stable_hash_for!(struct hir::FnDecl {
inputs, inputs,
output, output,
variadic, c_variadic,
implicit_self implicit_self
}); });

View file

@ -232,7 +232,7 @@ impl_stable_hash_for!(struct ty::GenSig<'tcx> {
impl_stable_hash_for!(struct ty::FnSig<'tcx> { impl_stable_hash_for!(struct ty::FnSig<'tcx> {
inputs_and_output, inputs_and_output,
variadic, c_variadic,
unsafety, unsafety,
abi abi
}); });

View file

@ -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), _ => intravisit::walk_ty(self, ty),
} }
} }
@ -2225,7 +2232,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
if let hir::TyKind::BareFn(_) = ty.node { if let hir::TyKind::BareFn(_) = ty.node {
self.outer_index.shift_in(1); self.outer_index.shift_in(1);
} }
if let hir::TyKind::TraitObject(ref bounds, ref lifetime) = ty.node { match ty.node {
hir::TyKind::TraitObject(ref bounds, ref lifetime) => {
for bound in bounds { for bound in bounds {
self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
} }
@ -2235,9 +2243,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
if !lifetime.is_elided() { if !lifetime.is_elided() {
self.visit_lifetime(lifetime); self.visit_lifetime(lifetime);
} }
} else { }
hir::TyKind::CVarArgs(_) => {}
_ => {
intravisit::walk_ty(self, ty); intravisit::walk_ty(self, ty);
} }
}
if let hir::TyKind::BareFn(_) = ty.node { if let hir::TyKind::BareFn(_) = ty.node {
self.outer_index.shift_out(1); self.outer_index.shift_out(1);
} }

View file

@ -1944,7 +1944,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
if let ty::FnSig { if let ty::FnSig {
unsafety: hir::Unsafety::Normal, unsafety: hir::Unsafety::Normal,
abi: Abi::Rust, abi: Abi::Rust,
variadic: false, c_variadic: false,
.. ..
} = self_ty.fn_sig(self.tcx()).skip_binder() } = self_ty.fn_sig(self.tcx()).skip_binder()
{ {

View file

@ -2453,7 +2453,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.mk_fn_sig( self.mk_fn_sig(
params_iter, params_iter,
s.output(), s.output(),
s.variadic, s.c_variadic,
hir::Unsafety::Normal, hir::Unsafety::Normal,
abi::Abi::Rust, abi::Abi::Rust,
) )
@ -2779,7 +2779,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub fn mk_fn_sig<I>(self, pub fn mk_fn_sig<I>(self,
inputs: I, inputs: I,
output: I::Item, output: I::Item,
variadic: bool, c_variadic: bool,
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
abi: abi::Abi) abi: abi::Abi)
-> <I::Item as InternIteratorElement<Ty<'tcx>, ty::FnSig<'tcx>>>::Output -> <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.chain(iter::once(output)).intern_with(|xs| ty::FnSig {
inputs_and_output: self.intern_type_list(xs), inputs_and_output: self.intern_type_list(xs),
variadic, unsafety, abi c_variadic, unsafety, abi
}) })
} }

View file

@ -65,7 +65,7 @@ impl<'a, 'tcx> Instance<'tcx> {
sig.map_bound(|sig| tcx.mk_fn_sig( sig.map_bound(|sig| tcx.mk_fn_sig(
iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()),
sig.output(), sig.output(),
sig.variadic, sig.c_variadic,
sig.unsafety, sig.unsafety,
sig.abi sig.abi
)) ))

View file

@ -147,9 +147,9 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> {
{ {
let tcx = relation.tcx(); let tcx = relation.tcx();
if a.variadic != b.variadic { if a.c_variadic != b.c_variadic {
return Err(TypeError::VariadicMismatch( 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 unsafety = relation.relate(&a.unsafety, &b.unsafety)?;
let abi = relation.relate(&a.abi, &b.abi)?; let abi = relation.relate(&a.abi, &b.abi)?;
@ -171,7 +171,7 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> {
}); });
Ok(ty::FnSig { Ok(ty::FnSig {
inputs_and_output: tcx.mk_type_list(inputs_and_output)?, inputs_and_output: tcx.mk_type_list(inputs_and_output)?,
variadic: a.variadic, c_variadic: a.c_variadic,
unsafety, unsafety,
abi, abi,
}) })

View file

@ -396,7 +396,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> {
tcx.lift(&self.inputs_and_output).map(|x| { tcx.lift(&self.inputs_and_output).map(|x| {
ty::FnSig { ty::FnSig {
inputs_and_output: x, inputs_and_output: x,
variadic: self.variadic, c_variadic: self.c_variadic,
unsafety: self.unsafety, unsafety: self.unsafety,
abi: self.abi, abi: self.abi,
} }
@ -832,7 +832,7 @@ BraceStructTypeFoldableImpl! {
BraceStructTypeFoldableImpl! { BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> { impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
inputs_and_output, variadic, unsafety, abi inputs_and_output, c_variadic, unsafety, abi
} }
} }

View file

@ -977,13 +977,13 @@ impl<'tcx> PolyGenSig<'tcx> {
/// Signature of a function type, which I have arbitrarily /// Signature of a function type, which I have arbitrarily
/// decided to use to refer to the input/output types. /// decided to use to refer to the input/output types.
/// ///
/// - `inputs` is the list of arguments and their modes. /// - `inputs`: is the list of arguments and their modes.
/// - `output` is the return type. /// - `output`: is the return type.
/// - `variadic` indicates whether this is a variadic function. (only true for foreign fns) /// - `c_variadic`: indicates whether this is a C-variadic function.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
pub struct FnSig<'tcx> { pub struct FnSig<'tcx> {
pub inputs_and_output: &'tcx List<Ty<'tcx>>, pub inputs_and_output: &'tcx List<Ty<'tcx>>,
pub variadic: bool, pub c_variadic: bool,
pub unsafety: hir::Unsafety, pub unsafety: hir::Unsafety,
pub abi: abi::Abi, pub abi: abi::Abi,
} }
@ -1016,8 +1016,8 @@ impl<'tcx> PolyFnSig<'tcx> {
pub fn output(&self) -> ty::Binder<Ty<'tcx>> { pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.output()) self.map_bound_ref(|fn_sig| fn_sig.output())
} }
pub fn variadic(&self) -> bool { pub fn c_variadic(&self) -> bool {
self.skip_binder().variadic self.skip_binder().c_variadic
} }
pub fn unsafety(&self) -> hir::Unsafety { pub fn unsafety(&self) -> hir::Unsafety {
self.skip_binder().unsafety self.skip_binder().unsafety

View file

@ -360,7 +360,7 @@ impl PrintContext {
fn fn_sig<F: fmt::Write>(&mut self, fn fn_sig<F: fmt::Write>(&mut self,
f: &mut F, f: &mut F,
inputs: &[Ty<'_>], inputs: &[Ty<'_>],
variadic: bool, c_variadic: bool,
output: Ty<'_>) output: Ty<'_>)
-> fmt::Result { -> fmt::Result {
write!(f, "(")?; write!(f, "(")?;
@ -370,7 +370,7 @@ impl PrintContext {
for &ty in inputs { for &ty in inputs {
print!(f, self, write(", "), print_display(ty))?; print!(f, self, write(", "), print_display(ty))?;
} }
if variadic { if c_variadic {
write!(f, ", ...")?; write!(f, ", ...")?;
} }
} }
@ -1074,10 +1074,10 @@ define_print! {
} }
write!(f, "fn")?; 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 { debug {
write!(f, "({:?}; variadic: {})->{:?}", self.inputs(), self.variadic, self.output()) write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output())
} }
} }
} }

View file

@ -258,7 +258,7 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
val val
}; };
match self.mode { match self.mode {
PassMode::Ignore => {}, PassMode::Ignore(_) => {}
PassMode::Pair(..) => { PassMode::Pair(..) => {
OperandValue::Pair(next(), next()).store(bx, dst); 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 mut inputs = sig.inputs();
let extra_args = if sig.abi == RustCall { 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 { match sig.inputs().last().unwrap().sty {
ty::Tuple(ref tupled_arguments) => { ty::Tuple(ref tupled_arguments) => {
@ -435,7 +435,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
} }
} }
} else { } else {
assert!(sig.variadic || extra_args.is_empty()); assert!(sig.c_variadic || extra_args.is_empty());
extra_args 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 arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| {
let is_return = arg_idx.is_none(); let is_return = arg_idx.is_none();
let mut arg = mk_arg_type(ty, arg_idx); 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 // The same is true for s390x-unknown-linux-gnu
// and sparc64-unknown-linux-gnu. // and sparc64-unknown-linux-gnu.
if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) { 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)| { args: inputs.iter().chain(extra_args).enumerate().map(|(i, ty)| {
arg_of(ty, Some(i)) arg_of(ty, Some(i))
}).collect(), }).collect(),
variadic: sig.variadic, c_variadic: sig.c_variadic,
conv, conv,
}; };
fn_ty.adjust_for_abi(cx, sig.abi); 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 { 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(..) => { PassMode::Direct(_) | PassMode::Pair(..) => {
self.ret.layout.immediate_llvm_type(cx) 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 { let llarg_ty = match arg.mode {
PassMode::Ignore => continue, PassMode::Ignore(_) => continue,
PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx), PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
PassMode::Pair(..) => { PassMode::Pair(..) => {
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true)); 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); llargument_tys.push(llarg_ty);
} }
if self.variadic { if self.c_variadic {
cx.type_variadic_func(&llargument_tys, llreturn_ty) cx.type_variadic_func(&llargument_tys, llreturn_ty)
} else { } else {
cx.type_func(&llargument_tys, llreturn_ty) cx.type_func(&llargument_tys, llreturn_ty)
@ -733,7 +766,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
apply(&ArgAttributes::new()); apply(&ArgAttributes::new());
} }
match arg.mode { match arg.mode {
PassMode::Ignore => {} PassMode::Ignore(_) => {}
PassMode::Direct(ref attrs) | PassMode::Direct(ref attrs) |
PassMode::Indirect(ref attrs, None) => apply(attrs), PassMode::Indirect(ref attrs, None) => apply(attrs),
PassMode::Indirect(ref attrs, Some(ref extra_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()); apply(&ArgAttributes::new());
} }
match arg.mode { match arg.mode {
PassMode::Ignore => {} PassMode::Ignore(_) => {}
PassMode::Direct(ref attrs) | PassMode::Direct(ref attrs) |
PassMode::Indirect(ref attrs, None) => apply(attrs), PassMode::Indirect(ref attrs, None) => apply(attrs),
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => { PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {

View file

@ -143,7 +143,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
output.pop(); output.pop();
} }
if sig.variadic { if sig.c_variadic {
if !sig.inputs().is_empty() { if !sig.inputs().is_empty() {
output.push_str(", ..."); output.push_str(", ...");
} else { } else {

View file

@ -136,22 +136,18 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
let tp_ty = substs.type_at(0); let tp_ty = substs.type_at(0);
self.const_usize(self.size_of(tp_ty).bytes()) self.const_usize(self.size_of(tp_ty).bytes())
} }
func @ "va_start" | func @ "va_end" => { "va_start" => {
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) { self.va_start(args[0].immediate())
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(), }
(Some(_), _) => self.load(args[0].immediate(), "va_end" => {
tcx.data_layout.pointer_align.abi), self.va_end(args[0].immediate())
(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_copy" => { "va_copy" => {
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) { 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(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
(Some(_), _) => self.load(args[0].immediate(), (Some(_), _) => self.load(args[0].immediate(),
tcx.data_layout.pointer_align.abi), 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")); let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
self.call(intrinsic, &[llresult, va_list], None); 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"); let expect = self.get_intrinsic(&"llvm.expect.i1");
self.call(expect, &[cond, self.const_bool(expected)], None) 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( fn copy_intrinsic(

View file

@ -109,12 +109,12 @@ pub(super) fn emit_va_arg(
Align::from_bytes(4).unwrap(), true) Align::from_bytes(4).unwrap(), true)
} }
// Windows Aarch64 // Windows Aarch64
("aarch4", true) => { ("aarch64", true) => {
emit_ptr_va_arg(bx, addr, target_ty, false, emit_ptr_va_arg(bx, addr, target_ty, false,
Align::from_bytes(8).unwrap(), false) Align::from_bytes(8).unwrap(), false)
} }
// iOS Aarch64 // iOS Aarch64
("aarch4", _) if target.target_os == "ios" => { ("aarch64", _) if target.target_os == "ios" => {
emit_ptr_va_arg(bx, addr, target_ty, false, emit_ptr_va_arg(bx, addr, target_ty, false,
Align::from_bytes(8).unwrap(), true) Align::from_bytes(8).unwrap(), true)
} }

View file

@ -3,7 +3,7 @@ use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::layout::{self, LayoutOf, HasTyCtxt}; use rustc::ty::layout::{self, LayoutOf, HasTyCtxt};
use rustc::mir; use rustc::mir;
use rustc::mir::interpret::EvalErrorKind; 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_target::spec::abi::Abi;
use rustc_mir::monomorphize; use rustc_mir::monomorphize;
use crate::base; use crate::base;
@ -13,136 +13,127 @@ use crate::meth;
use crate::traits::*; use crate::traits::*;
use std::borrow::Cow;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
use syntax_pos::Pos; use syntax_pos::Pos;
use super::{FunctionCx, LocalRef}; use super::{FunctionCx, LocalRef};
use super::place::PlaceRef; use super::place::PlaceRef;
use super::operand::OperandRef; use super::operand::{OperandValue, OperandRef};
use super::operand::OperandValue::{Pair, Ref, Immediate}; use super::operand::OperandValue::{Pair, Ref, Immediate};
impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Used by `FunctionCx::codegen_terminator` for emitting common patterns
pub fn codegen_block( /// e.g., creating a basic block, calling a function, etc.
&mut self, struct TerminatorCodegenHelper<'a, 'tcx> {
bb: mir::BasicBlock, bb: &'a mir::BasicBlock,
) { terminator: &'a mir::Terminator<'tcx>,
let mut bx = self.build_block(bb); funclet_bb: Option<mir::BasicBlock>,
let data = &self.mir[bb];
debug!("codegen_block({:?}={:?})", bb, data);
for statement in &data.statements {
bx = self.codegen_statement(bx, statement);
} }
self.codegen_terminator(bx, bb, data.terminator()); impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
} /// Returns the associated funclet from `FunctionCx::funclets` for the
/// `funclet_bb` member if it is not `None`.
fn codegen_terminator( fn funclet<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
&mut self, &self,
mut bx: Bx, fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
bb: mir::BasicBlock, ) -> Option<&'c Bx::Funclet> {
terminator: &mir::Terminator<'tcx> match self.funclet_bb {
) { Some(funcl) => fx.funclets[funcl].as_ref(),
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);
// 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, None => None,
} }
} }
}
let funclet = funclet_closure_factory(funclet_bb);
let lltarget = |this: &mut Self, target: mir::BasicBlock| { fn lltarget<'b, 'c, Bx: BuilderMethods<'b, 'tcx>>(
let lltarget = this.blocks[target]; &self,
let target_funclet = this.cleanup_kinds[target].funclet_bb(target); fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
match (funclet_bb, target_funclet) { 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), (None, None) => (lltarget, false),
(Some(f), Some(t_f)) (Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) =>
if f == t_f || !base::wants_msvc_seh(tcx.sess) (lltarget, false),
=> (lltarget, false),
(None, Some(_)) => {
// jump *into* cleanup - need a landing pad if GNU // jump *into* cleanup - need a landing pad if GNU
(this.landing_pad_to(target), false) (None, Some(_)) => (fx.landing_pad_to(target), false),
} (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator),
(Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator), (Some(_), Some(_)) => (fx.landing_pad_to(target), true),
(Some(_), Some(_)) => {
(this.landing_pad_to(target), true)
} }
} }
};
let llblock = |this: &mut Self, target: mir::BasicBlock| { /// Create a basic block.
let (lltarget, is_cleanupret) = lltarget(this, target); 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 { if is_cleanupret {
// MSVC cross-funclet jump - need a trampoline // MSVC cross-funclet jump - need a trampoline
debug!("llblock: creating cleanup trampoline for {:?}", target); debug!("llblock: creating cleanup trampoline for {:?}", target);
let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target);
let mut trampoline = this.new_block(name); let mut trampoline = fx.new_block(name);
trampoline.cleanup_ret(funclet(this).unwrap(), Some(lltarget)); trampoline.cleanup_ret(self.funclet(fx).unwrap(),
Some(lltarget));
trampoline.llbb() trampoline.llbb()
} else { } else {
lltarget lltarget
} }
}; }
let funclet_br = fn funclet_br<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
|this: &mut Self, bx: &mut Bx, target: mir::BasicBlock| { &self,
let (lltarget, is_cleanupret) = lltarget(this, target); fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
bx: &mut Bx,
target: mir::BasicBlock,
) {
let (lltarget, is_cleanupret) = self.lltarget(fx, target);
if is_cleanupret { if is_cleanupret {
// micro-optimization: generate a `ret` rather than a jump // micro-optimization: generate a `ret` rather than a jump
// to a trampoline. // to a trampoline.
bx.cleanup_ret(funclet(this).unwrap(), Some(lltarget)); bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
} else { } else {
bx.br(lltarget); bx.br(lltarget);
} }
}; }
let do_call = | /// Call `fn_ptr` of `fn_ty` with the arguments `llargs`, the optional
this: &mut Self, /// 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, bx: &mut Bx,
fn_ty: FnType<'tcx, Ty<'tcx>>, fn_ty: FnType<'tcx, Ty<'tcx>>,
fn_ptr: Bx::Value, fn_ptr: Bx::Value,
llargs: &[Bx::Value], llargs: &[Bx::Value],
destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
cleanup: Option<mir::BasicBlock> cleanup: Option<mir::BasicBlock>,
| { ) {
if let Some(cleanup) = cleanup { if let Some(cleanup) = cleanup {
let ret_bx = if let Some((_, target)) = destination { let ret_bx = if let Some((_, target)) = destination {
this.blocks[target] fx.blocks[target]
} else { } else {
this.unreachable_block() fx.unreachable_block()
}; };
let invokeret = bx.invoke(fn_ptr, let invokeret = bx.invoke(fn_ptr,
&llargs, &llargs,
ret_bx, ret_bx,
llblock(this, cleanup), self.llblock(fx, cleanup),
funclet(this)); self.funclet(fx));
bx.apply_attrs_callsite(&fn_ty, invokeret); bx.apply_attrs_callsite(&fn_ty, invokeret);
if let Some((ret_dest, target)) = destination { if let Some((ret_dest, target)) = destination {
let mut ret_bx = this.build_block(target); let mut ret_bx = fx.build_block(target);
this.set_debug_loc(&mut ret_bx, terminator.source_info); fx.set_debug_loc(&mut ret_bx, self.terminator.source_info);
this.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret); fx.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret);
} }
} else { } else {
let llret = bx.call(fn_ptr, &llargs, funclet(this)); let llret = bx.call(fn_ptr, &llargs, self.funclet(fx));
bx.apply_attrs_callsite(&fn_ty, llret); bx.apply_attrs_callsite(&fn_ty, llret);
if this.mir[bb].is_cleanup { if fx.mir[*self.bb].is_cleanup {
// Cleanup is always the cold path. Don't inline // Cleanup is always the cold path. Don't inline
// drop glue. Also, when there is a deeply-nested // drop glue. Also, when there is a deeply-nested
// struct, there are "symmetry" issues that cause // struct, there are "symmetry" issues that cause
@ -151,18 +142,24 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} }
if let Some((ret_dest, target)) = destination { if let Some((ret_dest, target)) = destination {
this.store_return(bx, ret_dest, &fn_ty.ret, llret); fx.store_return(bx, ret_dest, &fn_ty.ret, llret);
funclet_br(this, bx, target); self.funclet_br(fx, bx, target);
} else { } else {
bx.unreachable(); bx.unreachable();
} }
} }
}; }
}
self.set_debug_loc(&mut bx, terminator.source_info); /// Codegen implementations for some terminator variants.
match terminator.kind { impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::TerminatorKind::Resume => { /// Generates code for a `Resume` terminator.
if let Some(funclet) = funclet(self) { 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); bx.cleanup_ret(funclet, None);
} else { } else {
let slot = self.get_personality_slot(&mut bx); let slot = self.get_personality_slot(&mut bx);
@ -178,27 +175,27 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
lp = bx.insert_value(lp, lp1, 1); lp = bx.insert_value(lp, lp1, 1);
bx.resume(lp); bx.resume(lp);
} else { } else {
bx.call(bx.eh_unwind_resume(), &[lp0], funclet(self)); bx.call(bx.eh_unwind_resume(), &[lp0],
helper.funclet(self));
bx.unreachable(); bx.unreachable();
} }
} }
} }
mir::TerminatorKind::Abort => { fn codegen_switchint_terminator<'b>(
bx.abort(); &mut self,
bx.unreachable(); helper: TerminatorCodegenHelper<'b, 'tcx>,
} mut bx: Bx,
discr: &mir::Operand<'tcx>,
mir::TerminatorKind::Goto { target } => { switch_ty: Ty<'tcx>,
funclet_br(self, &mut bx, target); values: &Cow<'tcx, [u128]>,
} targets: &Vec<mir::BasicBlock>,
) {
mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { let discr = self.codegen_operand(&mut bx, &discr);
let discr = self.codegen_operand(&mut bx, discr);
if targets.len() == 2 { if targets.len() == 2 {
// If there are two targets, emit br instead of switch // If there are two targets, emit br instead of switch
let lltrue = llblock(self, targets[0]); let lltrue = helper.llblock(self, targets[0]);
let llfalse = llblock(self, targets[1]); let llfalse = helper.llblock(self, targets[1]);
if switch_ty == bx.tcx().types.bool { if switch_ty == bx.tcx().types.bool {
// Don't generate trivial icmps when switching on bool // Don't generate trivial icmps when switching on bool
if let [0] = values[..] { if let [0] = values[..] {
@ -218,26 +215,38 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} else { } else {
let (otherwise, targets) = targets.split_last().unwrap(); let (otherwise, targets) = targets.split_last().unwrap();
let switch = bx.switch(discr.immediate(), let switch = bx.switch(discr.immediate(),
llblock(self, *otherwise), helper.llblock(self, *otherwise),
values.len()); values.len());
let switch_llty = bx.immediate_backend_type( let switch_llty = bx.immediate_backend_type(
bx.layout_of(switch_ty) bx.layout_of(switch_ty)
); );
for (&value, target) in values.iter().zip(targets) { for (&value, target) in values.iter().zip(targets) {
let llval = bx.const_uint_big(switch_llty, value); let llval = bx.const_uint_big(switch_llty, value);
let llbb = llblock(self, *target); let llbb = helper.llblock(self, *target);
bx.add_case(switch, llval, llbb) bx.add_case(switch, llval, llbb)
} }
} }
} }
mir::TerminatorKind::Return => { 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 { let llval = match self.fn_ty.ret.mode {
PassMode::Ignore | PassMode::Indirect(..) => { PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => {
bx.ret_void(); bx.ret_void();
return; return;
} }
PassMode::Ignore(IgnoreMode::CVarArgs) => {
bug!("C-variadic arguments should never be the return type");
}
PassMode::Direct(_) | PassMode::Pair(..) => { PassMode::Direct(_) | PassMode::Pair(..) => {
let op = let op =
self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE)); self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE));
@ -282,18 +291,22 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.ret(llval); bx.ret(llval);
} }
mir::TerminatorKind::Unreachable => {
bx.unreachable();
}
mir::TerminatorKind::Drop { ref location, target, unwind } => { 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 = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx());
let ty = self.monomorphize(&ty); let ty = self.monomorphize(&ty);
let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty); let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty);
if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
// we don't actually need to drop anything. // we don't actually need to drop anything.
funclet_br(self, &mut bx, target); helper.funclet_br(self, &mut bx, target);
return return
} }
@ -308,8 +321,8 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}; };
let (drop_fn, fn_ty) = match ty.sty { let (drop_fn, fn_ty) = match ty.sty {
ty::Dynamic(..) => { ty::Dynamic(..) => {
let sig = drop_fn.fn_sig(tcx); let sig = drop_fn.fn_sig(self.cx.tcx());
let sig = tcx.normalize_erasing_late_bound_regions( let sig = self.cx.tcx().normalize_erasing_late_bound_regions(
ty::ParamEnv::reveal_all(), ty::ParamEnv::reveal_all(),
&sig, &sig,
); );
@ -323,12 +336,23 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.fn_type_of_instance(&drop_fn)) bx.fn_type_of_instance(&drop_fn))
} }
}; };
do_call(self, &mut bx, fn_ty, drop_fn, args, helper.do_call(self, &mut bx, fn_ty, drop_fn, args,
Some((ReturnDest::Nothing, target)), Some((ReturnDest::Nothing, target)),
unwind); unwind);
} }
mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { 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 cond = self.codegen_operand(&mut bx, cond).immediate();
let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1); let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
@ -347,7 +371,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// Don't codegen the panic block if success if known. // Don't codegen the panic block if success if known.
if const_cond == Some(expected) { if const_cond == Some(expected) {
funclet_br(self, &mut bx, target); helper.funclet_br(self, &mut bx, target);
return; return;
} }
@ -355,7 +379,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let cond = bx.expect(cond, expected); let cond = bx.expect(cond, expected);
// Create the failure block and the conditional branch to it. // Create the failure block and the conditional branch to it.
let lltarget = llblock(self, target); let lltarget = helper.llblock(self, target);
let panic_block = self.new_block("panic"); let panic_block = self.new_block("panic");
if expected { if expected {
bx.cond_br(cond, lltarget, panic_block.llbb()); bx.cond_br(cond, lltarget, panic_block.llbb());
@ -373,9 +397,9 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let filename = bx.const_str_slice(filename); let filename = bx.const_str_slice(filename);
let line = bx.const_u32(loc.line as u32); let line = bx.const_u32(loc.line as u32);
let col = bx.const_u32(loc.col.to_usize() as u32 + 1); let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
let align = tcx.data_layout.aggregate_align.abi let align = self.cx.tcx().data_layout.aggregate_align.abi
.max(tcx.data_layout.i32_align.abi) .max(self.cx.tcx().data_layout.i32_align.abi)
.max(tcx.data_layout.pointer_align.abi); .max(self.cx.tcx().data_layout.pointer_align.abi);
// Put together the arguments to the panic entry point. // Put together the arguments to the panic entry point.
let (lang_item, args) = match *msg { let (lang_item, args) = match *msg {
@ -417,20 +441,20 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let llfn = bx.get_fn(instance); let llfn = bx.get_fn(instance);
// Codegen the actual panic invoke/call. // 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, &args, None, cleanup);
} }
mir::TerminatorKind::DropAndReplace { .. } => { fn codegen_call_terminator<'b>(
bug!("undesugared DropAndReplace in codegen: {:?}", terminator); &mut self,
} helper: TerminatorCodegenHelper<'b, 'tcx>,
mut bx: Bx,
mir::TerminatorKind::Call { terminator: &mir::Terminator<'tcx>,
ref func, func: &mir::Operand<'tcx>,
ref args, args: &Vec<mir::Operand<'tcx>>,
ref destination, destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
cleanup, cleanup: Option<mir::BasicBlock>,
from_hir_call: _ ) {
} => { let span = terminator.source_info.span;
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. // 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 callee = self.codegen_operand(&mut bx, func);
@ -445,7 +469,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
ty::FnPtr(_) => { ty::FnPtr(_) => {
(None, Some(callee.immediate())) (None, Some(callee.immediate()))
} }
_ => bug!("{} is not callable", callee.layout.ty) _ => bug!("{} is not callable", callee.layout.ty),
}; };
let def = instance.map(|i| i.def); let def = instance.map(|i| i.def);
let sig = callee.layout.ty.fn_sig(bx.tcx()); let sig = callee.layout.ty.fn_sig(bx.tcx());
@ -457,8 +481,8 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// Handle intrinsics old codegen wants Expr's for, ourselves. // Handle intrinsics old codegen wants Expr's for, ourselves.
let intrinsic = match def { let intrinsic = match def {
Some(ty::InstanceDef::Intrinsic(def_id)) Some(ty::InstanceDef::Intrinsic(def_id)) =>
=> Some(bx.tcx().item_name(def_id).as_str()), Some(bx.tcx().item_name(def_id).as_str()),
_ => None _ => None
}; };
let intrinsic = intrinsic.as_ref().map(|s| &s[..]); let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
@ -467,7 +491,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if let Some(destination_ref) = destination.as_ref() { if let Some(destination_ref) = destination.as_ref() {
let &(ref dest, target) = destination_ref; let &(ref dest, target) = destination_ref;
self.codegen_transmute(&mut bx, &args[0], dest); self.codegen_transmute(&mut bx, &args[0], dest);
funclet_br(self, &mut bx, target); helper.funclet_br(self, &mut bx, target);
} else { } else {
// If we are trying to transmute to an uninhabited type, // If we are trying to transmute to an uninhabited type,
// it is likely there is no allotted destination. In fact, // it is likely there is no allotted destination. In fact,
@ -481,7 +505,10 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return; return;
} }
let extra_args = &args[sig.inputs().len()..]; // The "spoofed" `VaList` added to a C-variadic functions signature
// should not be included in the `extra_args` calculation.
let extra_args_start_idx = sig.inputs().len() - if sig.c_variadic { 1 } else { 0 };
let extra_args = &args[extra_args_start_idx..];
let extra_args = extra_args.iter().map(|op_arg| { let extra_args = extra_args.iter().map(|op_arg| {
let op_ty = op_arg.ty(self.mir, bx.tcx()); let op_ty = op_arg.ty(self.mir, bx.tcx());
self.monomorphize(&op_ty) self.monomorphize(&op_ty)
@ -492,15 +519,15 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.new_vtable(sig, &extra_args) bx.new_vtable(sig, &extra_args)
} }
Some(ty::InstanceDef::DropGlue(_, None)) => { Some(ty::InstanceDef::DropGlue(_, None)) => {
// empty drop glue - a nop. // Empty drop glue; a no-op.
let &(_, target) = destination.as_ref().unwrap(); let &(_, target) = destination.as_ref().unwrap();
funclet_br(self, &mut bx, target); helper.funclet_br(self, &mut bx, target);
return; return;
} }
_ => bx.new_fn_type(sig, &extra_args) _ => bx.new_fn_type(sig, &extra_args)
}; };
// emit a panic or a NOP for `panic_if_uninhabited` // Emit a panic or a no-op for `panic_if_uninhabited`.
if intrinsic == Some("panic_if_uninhabited") { if intrinsic == Some("panic_if_uninhabited") {
let ty = instance.unwrap().substs.type_at(0); let ty = instance.unwrap().substs.type_at(0);
let layout = bx.layout_of(ty); let layout = bx.layout_of(ty);
@ -510,9 +537,9 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let filename = bx.const_str_slice(filename); let filename = bx.const_str_slice(filename);
let line = bx.const_u32(loc.line as u32); let line = bx.const_u32(loc.line as u32);
let col = bx.const_u32(loc.col.to_usize() as u32 + 1); let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
let align = tcx.data_layout.aggregate_align.abi let align = self.cx.tcx().data_layout.aggregate_align.abi
.max(tcx.data_layout.i32_align.abi) .max(self.cx.tcx().data_layout.i32_align.abi)
.max(tcx.data_layout.pointer_align.abi); .max(self.cx.tcx().data_layout.pointer_align.abi);
let str = format!( let str = format!(
"Attempted to instantiate uninhabited type {}", "Attempted to instantiate uninhabited type {}",
@ -538,7 +565,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let llfn = bx.get_fn(instance); let llfn = bx.get_fn(instance);
// Codegen the actual panic invoke/call. // Codegen the actual panic invoke/call.
do_call( helper.do_call(
self, self,
&mut bx, &mut bx,
fn_ty, fn_ty,
@ -549,7 +576,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
); );
} else { } else {
// a NOP // a NOP
funclet_br(self, &mut bx, destination.as_ref().unwrap().1); helper.funclet_br(self, &mut bx, destination.as_ref().unwrap().1)
} }
return; return;
} }
@ -570,13 +597,12 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if intrinsic.is_some() && intrinsic != Some("drop_in_place") { if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
let dest = match ret_dest { let dest = match ret_dest {
_ if fn_ty.ret.is_indirect() => llargs[0], _ if fn_ty.ret.is_indirect() => llargs[0],
ReturnDest::Nothing => { ReturnDest::Nothing =>
bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret))) bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret))),
} ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) =>
ReturnDest::IndirectOperand(dst, _) | dst.llval,
ReturnDest::Store(dst) => dst.llval,
ReturnDest::DirectOperand(_) => ReturnDest::DirectOperand(_) =>
bug!("Cannot use direct operand with an intrinsic call") bug!("Cannot use direct operand with an intrinsic call"),
}; };
let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
@ -608,7 +634,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
layout: bx.layout_of(ty), layout: bx.layout_of(ty),
}; };
}, }
mir::Operand::Copy(_) | mir::Operand::Copy(_) |
mir::Operand::Move(_) => { mir::Operand::Move(_) => {
span_bug!(span, "shuffle indices must be constant"); span_bug!(span, "shuffle indices must be constant");
@ -642,7 +668,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} }
if let Some((_, target)) = *destination { if let Some((_, target)) = *destination {
funclet_br(self, &mut bx, target); helper.funclet_br(self, &mut bx, target);
} else { } else {
bx.unreachable(); bx.unreachable();
} }
@ -658,7 +684,37 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
(&args[..], None) (&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() { '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); let mut op = self.codegen_operand(&mut bx, arg);
if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
@ -696,7 +752,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
llargs.push(data_ptr); llargs.push(data_ptr);
continue 'make_args continue 'make_args
} }
other => bug!("expected a Pair, got {:?}", other) other => bug!("expected a Pair, got {:?}", other),
} }
} else if let Ref(data_ptr, Some(meta), _) = op.val { } else if let Ref(data_ptr, Some(meta), _) = op.val {
// by-value dynamic dispatch // by-value dynamic dispatch
@ -734,10 +790,96 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => span_bug!(span, "no llfn for call"), _ => span_bug!(span, "no llfn for call"),
}; };
do_call(self, &mut bx, fn_ty, fn_ptr, &llargs, helper.do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
destination.as_ref().map(|&(_, target)| (ret_dest, target)), destination.as_ref().map(|&(_, target)| (ret_dest, target)),
cleanup); cleanup);
} }
}
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);
for statement in &data.statements {
bx = self.codegen_statement(bx, statement);
}
self.codegen_terminator(bx, bb, data.terminator());
}
fn codegen_terminator(
&mut self,
mut bx: Bx,
bb: mir::BasicBlock,
terminator: &mir::Terminator<'tcx>
) {
debug!("codegen_terminator: {:?}", terminator);
// Create the cleanup bundle, if needed.
let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
let helper = TerminatorCodegenHelper {
bb: &bb, terminator, funclet_bb
};
self.set_debug_loc(&mut bx, terminator.source_info);
match terminator.kind {
mir::TerminatorKind::Resume => {
self.codegen_resume_terminator(helper, bx)
}
mir::TerminatorKind::Abort => {
bx.abort();
bx.unreachable();
}
mir::TerminatorKind::Goto { target } => {
helper.funclet_br(self, &mut bx, target);
}
mir::TerminatorKind::SwitchInt {
ref discr, switch_ty, ref values, ref targets
} => {
self.codegen_switchint_terminator(helper, bx, discr, switch_ty,
values, targets);
}
mir::TerminatorKind::Return => {
self.codegen_return_terminator(bx);
}
mir::TerminatorKind::Unreachable => {
bx.unreachable();
}
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::GeneratorDrop |
mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"), mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"),
mir::TerminatorKind::FalseEdges { .. } | mir::TerminatorKind::FalseEdges { .. } |

View file

@ -5,7 +5,7 @@ use rustc::mir::{self, Mir};
use rustc::ty::subst::SubstsRef; use rustc::ty::subst::SubstsRef;
use rustc::session::config::DebugInfo; use rustc::session::config::DebugInfo;
use rustc_mir::monomorphize::Instance; 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::base;
use crate::debuginfo::{self, VariableAccess, VariableKind, FunctionDebugContext}; use crate::debuginfo::{self, VariableAccess, VariableKind, FunctionDebugContext};
use crate::traits::*; 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. /// If this function is being monomorphized, this contains the type substitutions used.
param_substs: SubstsRef<'tcx>, 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> { 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()); assert!(!instance.substs.needs_infer());
instance.substs instance.substs
}, },
va_list_ref: None,
}; };
let memory_locals = analyze::non_ssa_locals(&fx); let memory_locals = analyze::non_ssa_locals(&fx);
// Allocate variable and temp allocas // Allocate variable and temp allocas
fx.locals = { 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 mut allocate_local = |local| {
let decl = &mir.local_decls[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> debuginfo::MirDebugScope<Bx::DIScope>
>, >,
memory_locals: &BitSet<mir::Local>, memory_locals: &BitSet<mir::Local>,
va_list_ref: &mut Option<PlaceRef<'tcx, Bx::Value>>,
) -> Vec<LocalRef<'tcx, Bx::Value>> { ) -> Vec<LocalRef<'tcx, Bx::Value>> {
let mir = fx.mir; let mir = fx.mir;
let tcx = fx.cx.tcx(); let tcx = fx.cx.tcx();
@ -447,6 +457,15 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
None 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)| { mir.args_iter().enumerate().map(|(arg_index, local)| {
let arg_decl = &mir.local_decls[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. // of putting everything in allocas just so we can use llvm.dbg.declare.
let local = |op| LocalRef::Operand(Some(op)); let local = |op| LocalRef::Operand(Some(op));
match arg.mode { match arg.mode {
PassMode::Ignore => { PassMode::Ignore(IgnoreMode::Zst) => {
return local(OperandRef::new_zst(bx.cx(), arg.layout)); 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(_) => { PassMode::Direct(_) => {
let llarg = bx.get_param(bx.llfn(), llarg_idx as c_uint); let llarg = bx.get_param(bx.llfn(), llarg_idx as c_uint);
bx.set_value_name(llarg, &name); bx.set_value_name(llarg, &name);
@ -558,10 +584,36 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout, &name); let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout, &name);
indirect_operand.store(bx, tmp); indirect_operand.store(bx, tmp);
tmp tmp
} else {
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 { } else {
let tmp = PlaceRef::alloca(bx, arg.layout, &name); let tmp = PlaceRef::alloca(bx, arg.layout, &name);
bx.store_fn_arg(arg, &mut llarg_idx, tmp); bx.store_fn_arg(arg, &mut llarg_idx, tmp);
tmp tmp
}
}; };
arg_scope.map(|scope| { arg_scope.map(|scope| {
// Is this a regular argument? // Is this a regular argument?

View file

@ -20,4 +20,10 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
fn abort(&mut self); fn abort(&mut self);
fn assume(&mut self, val: Self::Value); fn assume(&mut self, val: Self::Value);
fn expect(&mut self, cond: Self::Value, expected: bool) -> 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;
} }

View file

@ -766,8 +766,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
let def_id = self.cx.tcx.hir().local_def_id(id); 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.fn_sig(def_id);
let sig = self.cx.tcx.erase_late_bound_regions(&sig); 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); self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty);
} }

View file

@ -1602,10 +1602,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
from_hir_call: bool, from_hir_call: bool,
) { ) {
debug!("check_call_inputs({:?}, {:?})", sig, args); 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); 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 op_arg_ty = op_arg.ty(mir, self.tcx());
let category = if from_hir_call { let category = if from_hir_call {
ConstraintCategory::CallArgument ConstraintCategory::CallArgument

View file

@ -353,7 +353,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> {
output.pop(); output.pop();
} }
if sig.variadic { if sig.c_variadic {
if !sig.inputs().is_empty() { if !sig.inputs().is_empty() {
output.push_str(", ..."); output.push_str(", ...");
} else { } else {

View file

@ -190,6 +190,7 @@ impl Sig for ast::Ty {
Ok(replace_text(nested, text)) Ok(replace_text(nested, text))
} }
ast::TyKind::Never => Ok(text_sig("!".to_owned())), ast::TyKind::Never => Ok(text_sig("!".to_owned())),
ast::TyKind::CVarArgs => Ok(text_sig("...".to_owned())),
ast::TyKind::Tup(ref ts) => { ast::TyKind::Tup(ref ts) => {
let mut text = "(".to_owned(); let mut text = "(".to_owned();
let mut defs = vec![]; let mut defs = vec![];

View file

@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
// `extern "aapcs"`, then we must use the VFP registers for homogeneous aggregates. // `extern "aapcs"`, then we must use the VFP registers for homogeneous aggregates.
let vfp = cx.target_spec().llvm_target.ends_with("hf") let vfp = cx.target_spec().llvm_target.ends_with("hf")
&& fty.conv != Conv::ArmAapcs && fty.conv != Conv::ArmAapcs
&& !fty.variadic; && !fty.c_variadic;
if !fty.ret.is_ignore() { if !fty.ret.is_ignore() {
classify_ret_ty(cx, &mut fty.ret, vfp); classify_ret_ty(cx, &mut fty.ret, vfp);

View file

@ -23,10 +23,18 @@ mod x86_64;
mod x86_win64; mod x86_win64;
mod wasm32; 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)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PassMode { pub enum PassMode {
/// Ignore the argument (useful for empty struct). /// Ignore the argument (useful for empty structs and C-variadic args).
Ignore, Ignore(IgnoreMode),
/// Pass the argument directly. /// Pass the argument directly.
Direct(ArgAttributes), Direct(ArgAttributes),
/// Pass a pair's elements directly in two arguments. /// 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 { 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. /// LLVM return type.
pub ret: ArgType<'a, Ty>, pub ret: ArgType<'a, Ty>,
pub variadic: bool, pub c_variadic: bool,
pub conv: Conv, pub conv: Conv,
} }

View file

@ -88,7 +88,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla
for arg in &mut fty.args { for arg in &mut fty.args {
let attrs = match arg.mode { let attrs = match arg.mode {
PassMode::Ignore | PassMode::Ignore(_) |
PassMode::Indirect(_, None) => continue, PassMode::Indirect(_, None) => continue,
PassMode::Direct(ref mut attrs) => attrs, PassMode::Direct(ref mut attrs) => attrs,
PassMode::Pair(..) | PassMode::Pair(..) |

View file

@ -105,7 +105,7 @@ fn assemble_builtin_sized_impls<'tcx>(
let fn_ptr = generic_types::fn_ptr( let fn_ptr = generic_types::fn_ptr(
tcx, tcx,
fn_ptr.inputs_and_output.len(), fn_ptr.inputs_and_output.len(),
fn_ptr.variadic, fn_ptr.c_variadic,
fn_ptr.unsafety, fn_ptr.unsafety,
fn_ptr.abi fn_ptr.abi
); );
@ -190,11 +190,11 @@ fn wf_clause_for_raw_ptr<'tcx>(
fn wf_clause_for_fn_ptr<'tcx>( fn wf_clause_for_fn_ptr<'tcx>(
tcx: ty::TyCtxt<'_, '_, 'tcx>, tcx: ty::TyCtxt<'_, '_, 'tcx>,
arity_and_output: usize, arity_and_output: usize,
variadic: bool, c_variadic: bool,
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
abi: abi::Abi abi: abi::Abi
) -> Clauses<'tcx> { ) -> 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 { let wf_clause = ProgramClause {
goal: DomainGoal::WellFormed(WellFormed::Ty(fn_ptr)), goal: DomainGoal::WellFormed(WellFormed::Ty(fn_ptr)),
@ -503,7 +503,7 @@ impl ChalkInferenceContext<'cx, 'gcx, 'tcx> {
wf_clause_for_fn_ptr( wf_clause_for_fn_ptr(
self.infcx.tcx, self.infcx.tcx,
fn_ptr.inputs_and_output.len(), fn_ptr.inputs_and_output.len(),
fn_ptr.variadic, fn_ptr.c_variadic,
fn_ptr.unsafety, fn_ptr.unsafety,
fn_ptr.abi fn_ptr.abi
) )

View file

@ -24,7 +24,7 @@ crate fn raw_ptr(tcx: TyCtxt<'_, '_, 'tcx>, mutbl: hir::Mutability) -> Ty<'tcx>
crate fn fn_ptr( crate fn fn_ptr(
tcx: ty::TyCtxt<'_, '_, 'tcx>, tcx: ty::TyCtxt<'_, '_, 'tcx>,
arity_and_output: usize, arity_and_output: usize,
variadic: bool, c_variadic: bool,
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
abi: abi::Abi abi: abi::Abi
) -> Ty<'tcx> { ) -> Ty<'tcx> {
@ -37,7 +37,7 @@ crate fn fn_ptr(
let fn_sig = ty::Binder::bind(ty::FnSig { let fn_sig = ty::Binder::bind(ty::FnSig {
inputs_and_output, inputs_and_output,
variadic, c_variadic,
unsafety, unsafety,
abi, abi,
}); });

View file

@ -18,7 +18,7 @@ use rustc::ty::subst::{Kind, Subst, InternalSubsts, SubstsRef};
use rustc::ty::wf::object_region_bounds; use rustc::ty::wf::object_region_bounds;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_target::spec::abi; use rustc_target::spec::abi;
use crate::require_c_abi_if_variadic; use crate::require_c_abi_if_c_variadic;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::ast; use syntax::ast;
use syntax::feature_gate::{GateIssue, emit_feature_err}; 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))) tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(&t)))
} }
hir::TyKind::BareFn(ref bf) => { 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)) tcx.mk_fn_ptr(self.ty_of_fn(bf.unsafety, bf.abi, &bf.decl))
} }
hir::TyKind::TraitObject(ref bounds, ref lifetime) => { hir::TyKind::TraitObject(ref bounds, ref lifetime) => {
@ -1823,6 +1823,15 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
hir::TyKind::Err => { hir::TyKind::Err => {
tcx.types.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); 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( let bare_fn_ty = ty::Binder::bind(tcx.mk_fn_sig(
input_tys, input_tys,
output_ty, output_ty,
decl.variadic, decl.c_variadic,
unsafety, unsafety,
abi abi
)); ));

View file

@ -368,20 +368,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
.0; .0;
let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig); 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. // Call the generic checker.
let expected_arg_tys = self.expected_inputs_for_expected_output( let expected_arg_tys = self.expected_inputs_for_expected_output(
call_expr.span, call_expr.span,
expected, expected,
fn_sig.output(), fn_sig.output(),
fn_sig.inputs(), inputs,
); );
self.check_argument_types( self.check_argument_types(
call_expr.span, call_expr.span,
call_expr.span, call_expr.span,
fn_sig.inputs(), inputs,
&expected_arg_tys[..], &expected_arg_tys[..],
arg_exprs, arg_exprs,
fn_sig.variadic, fn_sig.c_variadic,
TupleArgumentsFlag::DontTupleArguments, TupleArgumentsFlag::DontTupleArguments,
def_span, def_span,
); );
@ -414,7 +424,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn_sig.inputs(), fn_sig.inputs(),
&expected_arg_tys, &expected_arg_tys,
arg_exprs, arg_exprs,
fn_sig.variadic, fn_sig.c_variadic,
TupleArgumentsFlag::TupleArguments, TupleArgumentsFlag::TupleArguments,
None, None,
); );

View file

@ -141,7 +141,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.tcx.mk_fn_sig( self.tcx.mk_fn_sig(
iter::once(self.tcx.intern_tup(sig.inputs())), iter::once(self.tcx.intern_tup(sig.inputs())),
sig.output(), sig.output(),
sig.variadic, sig.c_variadic,
sig.unsafety, sig.unsafety,
sig.abi, sig.abi,
) )
@ -386,7 +386,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Watch out for some surprises and just ignore the // Watch out for some surprises and just ignore the
// expectation if things don't see to match up with what we // expectation if things don't see to match up with what we
// expect. // 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); 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 { } else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 {
return self.sig_of_closure_with_mismatched_number_of_arguments( 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( let bound_sig = ty::Binder::bind(self.tcx.mk_fn_sig(
expected_sig.sig.inputs().iter().cloned(), expected_sig.sig.inputs().iter().cloned(),
expected_sig.sig.output(), expected_sig.sig.output(),
decl.variadic, decl.c_variadic,
hir::Unsafety::Normal, hir::Unsafety::Normal,
Abi::RustCall, Abi::RustCall,
)); ));
@ -586,7 +586,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let result = ty::Binder::bind(self.tcx.mk_fn_sig( let result = ty::Binder::bind(self.tcx.mk_fn_sig(
supplied_arguments, supplied_arguments,
supplied_return, supplied_return,
decl.variadic, decl.c_variadic,
hir::Unsafety::Normal, hir::Unsafety::Normal,
Abi::RustCall, Abi::RustCall,
)); ));
@ -621,7 +621,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let result = ty::Binder::bind(self.tcx.mk_fn_sig( let result = ty::Binder::bind(self.tcx.mk_fn_sig(
supplied_arguments, supplied_arguments,
self.tcx.types.err, self.tcx.types.err,
decl.variadic, decl.c_variadic,
hir::Unsafety::Normal, hir::Unsafety::Normal,
Abi::RustCall, Abi::RustCall,
)); ));

View file

@ -337,7 +337,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
"va_start" | "va_end" => { "va_start" | "va_end" => {
match mk_va_list_ty() { match mk_va_list_ty() {
Some(va_list_ty) => (0, vec![va_list_ty], tcx.mk_unit()), 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) (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" => { "va_arg" => {
match mk_va_list_ty() { match mk_va_list_ty() {
Some(va_list_ty) => (1, vec![va_list_ty], param(0)), 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")
} }
} }

View file

@ -130,7 +130,7 @@ use std::mem::replace;
use std::ops::{self, Deref}; use std::ops::{self, Deref};
use std::slice; 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::{CompileIncomplete, Session};
use crate::session::config::EntryFnType; use crate::session::config::EntryFnType;
use crate::TypeAndSubsts; 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 = fcx.tcx.mk_fn_sig(
fn_sig.inputs().iter().cloned(), fn_sig.inputs().iter().cloned(),
revealed_ret_ty, revealed_ret_ty,
fn_sig.variadic, fn_sig.c_variadic,
fn_sig.unsafety, fn_sig.unsafety,
fn_sig.abi 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 { 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..] &method.sig.inputs()[1..]
); );
self.check_argument_types(sp, expr_sp, &method.sig.inputs()[1..], &expected_arg_tys[..], 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)); self.tcx.hir().span_if_local(method.def_id));
method.sig.output() method.sig.output()
} }
@ -2862,7 +2862,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn_inputs: &[Ty<'tcx>], fn_inputs: &[Ty<'tcx>],
mut expected_arg_tys: &[Ty<'tcx>], mut expected_arg_tys: &[Ty<'tcx>],
args: &'gcx [hir::Expr], args: &'gcx [hir::Expr],
variadic: bool, c_variadic: bool,
tuple_arguments: TupleArgumentsFlag, tuple_arguments: TupleArgumentsFlag,
def_span: Option<Span>) { def_span: Option<Span>) {
let tcx = self.tcx; let tcx = self.tcx;
@ -2886,11 +2886,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let param_count_error = |expected_count: usize, let param_count_error = |expected_count: usize,
arg_count: usize, arg_count: usize,
error_code: &str, error_code: &str,
variadic: bool, c_variadic: bool,
sugg_unit: bool| { sugg_unit: bool| {
let mut err = tcx.sess.struct_span_err_with_code(sp, let mut err = tcx.sess.struct_span_err_with_code(sp,
&format!("this function takes {}{} but {} {} supplied", &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(expected_count, "parameter"),
potentially_plural_count(arg_count, "parameter"), potentially_plural_count(arg_count, "parameter"),
if arg_count == 1 {"was"} else {"were"}), if arg_count == 1 {"was"} else {"were"}),
@ -2910,7 +2910,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Applicability::MachineApplicable); Applicability::MachineApplicable);
} else { } else {
err.span_label(sp, format!("expected {}{}", err.span_label(sp, format!("expected {}{}",
if variadic {"at least "} else {""}, if c_variadic { "at least " } else { "" },
potentially_plural_count(expected_count, "parameter"))); potentially_plural_count(expected_count, "parameter")));
} }
err.emit(); err.emit();
@ -2944,7 +2944,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
} else if expected_arg_count == supplied_arg_count { } else if expected_arg_count == supplied_arg_count {
fn_inputs.to_vec() fn_inputs.to_vec()
} else if variadic { } else if c_variadic {
if supplied_arg_count >= expected_arg_count { if supplied_arg_count >= expected_arg_count {
fn_inputs.to_vec() fn_inputs.to_vec()
} else { } else {
@ -2991,10 +2991,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.select_obligations_where_possible(false); 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 hence we only do our usual type checking with
// the arguments who's types we do know. // the arguments who's types we do know.
let t = if variadic { let t = if c_variadic {
expected_arg_count expected_arg_count
} else if tuple_arguments == TupleArguments { } else if tuple_arguments == TupleArguments {
args.len() 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 // We also need to make sure we at least write the ty of the other
// arguments which we skipped above. // arguments which we skipped above.
if variadic { if c_variadic {
fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
use crate::structured_errors::{VariadicError, StructuredDiagnostic}; use crate::structured_errors::{VariadicError, StructuredDiagnostic};
VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); VariadicError::new(s, span, t, cast_ty).diagnostic().emit();

View file

@ -136,14 +136,14 @@ fn check_type_alias_enum_variants_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx,
} }
} }
fn require_c_abi_if_variadic(tcx: TyCtxt<'_, '_, '_>, fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_, '_, '_>,
decl: &hir::FnDecl, decl: &hir::FnDecl,
abi: Abi, abi: Abi,
span: Span) { span: Span) {
if decl.variadic && !(abi == Abi::C || abi == Abi::Cdecl) { if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) {
let mut err = struct_span_err!(tcx.sess, span, E0045, let mut err = struct_span_err!(tcx.sess, span, E0045,
"variadic function must have C or cdecl calling convention"); "C-variadic function must have C or cdecl calling convention");
err.span_label(span, "variadics require C or cdecl calling convention").emit(); err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
} }
} }

View file

@ -1752,7 +1752,6 @@ impl Clean<Item> for doctree::Function {
pub struct FnDecl { pub struct FnDecl {
pub inputs: Arguments, pub inputs: Arguments,
pub output: FunctionRetTy, pub output: FunctionRetTy,
pub variadic: bool,
pub attrs: Attributes, pub attrs: Attributes,
} }
@ -1831,7 +1830,6 @@ impl<'a, A: Copy> Clean<FnDecl> for (&'a hir::FnDecl, A)
FnDecl { FnDecl {
inputs: (&self.0.inputs[..], self.1).clean(cx), inputs: (&self.0.inputs[..], self.1).clean(cx),
output: self.0.output.clean(cx), output: self.0.output.clean(cx),
variadic: self.0.variadic,
attrs: Attributes::default() attrs: Attributes::default()
} }
} }
@ -1849,7 +1847,6 @@ impl<'a, 'tcx> Clean<FnDecl> for (DefId, ty::PolyFnSig<'tcx>) {
FnDecl { FnDecl {
output: Return(sig.skip_binder().output().clean(cx)), output: Return(sig.skip_binder().output().clean(cx)),
attrs: Attributes::default(), attrs: Attributes::default(),
variadic: sig.skip_binder().variadic,
inputs: Arguments { inputs: Arguments {
values: sig.skip_binder().inputs().iter().map(|t| { values: sig.skip_binder().inputs().iter().map(|t| {
Argument { Argument {
@ -2252,6 +2249,7 @@ pub enum Type {
Slice(Box<Type>), Slice(Box<Type>),
Array(Box<Type>, String), Array(Box<Type>, String),
Never, Never,
CVarArgs,
Unique(Box<Type>), Unique(Box<Type>),
RawPointer(Mutability, Box<Type>), RawPointer(Mutability, Box<Type>),
BorrowedRef { BorrowedRef {
@ -2290,6 +2288,7 @@ pub enum PrimitiveType {
Reference, Reference,
Fn, Fn,
Never, Never,
CVarArgs,
} }
#[derive(Clone, RustcEncodable, RustcDecodable, Copy, Debug)] #[derive(Clone, RustcEncodable, RustcDecodable, Copy, Debug)]
@ -2469,6 +2468,7 @@ impl PrimitiveType {
Reference => "reference", Reference => "reference",
Fn => "fn", Fn => "fn",
Never => "never", Never => "never",
CVarArgs => "...",
} }
} }
@ -2518,6 +2518,7 @@ impl Clean<Type> for hir::Ty {
match self.node { match self.node {
TyKind::Never => Never, TyKind::Never => Never,
TyKind::CVarArgs(_) => CVarArgs,
TyKind::Ptr(ref m) => RawPointer(m.mutbl.clean(cx), box m.ty.clean(cx)), TyKind::Ptr(ref m) => RawPointer(m.mutbl.clean(cx), box m.ty.clean(cx)),
TyKind::Rptr(ref l, ref m) => { TyKind::Rptr(ref l, ref m) => {
let lifetime = if l.is_elided() { let lifetime = if l.is_elided() {
@ -3654,6 +3655,7 @@ fn build_deref_target_impls(cx: &DocContext<'_, '_, '_>,
Reference => None, Reference => None,
Fn => None, Fn => None,
Never => None, Never => None,
CVarArgs => tcx.lang_items().va_list(),
}; };
if let Some(did) = did { if let Some(did) = did {
if !did.is_local() { if !did.is_local() {

View file

@ -609,6 +609,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
primitive_link(f, PrimitiveType::Array, &format!("; {}]", n)) primitive_link(f, PrimitiveType::Array, &format!("; {}]", n))
} }
clean::Never => primitive_link(f, PrimitiveType::Never, "!"), clean::Never => primitive_link(f, PrimitiveType::Never, "!"),
clean::CVarArgs => primitive_link(f, PrimitiveType::CVarArgs, "..."),
clean::RawPointer(m, ref t) => { clean::RawPointer(m, ref t) => {
match **t { match **t {
clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => { clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
@ -834,13 +835,6 @@ impl fmt::Display for clean::FunctionRetTy {
impl fmt::Display for clean::FnDecl { impl fmt::Display for clean::FnDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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)
}
} else {
if f.alternate() { if f.alternate() {
write!(f, "({args:#}){arrow:#}", args = self.inputs, arrow = self.output) write!(f, "({args:#}){arrow:#}", args = self.inputs, arrow = self.output)
} else { } else {
@ -848,7 +842,6 @@ impl fmt::Display for clean::FnDecl {
} }
} }
} }
}
impl<'a> fmt::Display for Function<'a> { impl<'a> fmt::Display for Function<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -907,12 +900,7 @@ impl<'a> fmt::Display for Function<'a> {
} }
} }
let mut args_plain = format!("({})", args_plain); let args_plain = format!("({})", args_plain);
if decl.variadic {
args.push_str(",<br> ...");
args_plain.push_str(", ...");
}
let output = if let hir::IsAsync::Async = asyncness { let output = if let hir::IsAsync::Async = asyncness {
Cow::Owned(decl.sugared_async_return_type()) Cow::Owned(decl.sugared_async_return_type())

View file

@ -1643,6 +1643,8 @@ pub enum TyKind {
Mac(Mac), Mac(Mac),
/// Placeholder for a kind that has failed to be defined. /// Placeholder for a kind that has failed to be defined.
Err, Err,
/// Placeholder for a `va_list`.
CVarArgs,
} }
impl TyKind { impl TyKind {
@ -1802,7 +1804,7 @@ impl Arg {
pub struct FnDecl { pub struct FnDecl {
pub inputs: Vec<Arg>, pub inputs: Vec<Arg>,
pub output: FunctionRetTy, pub output: FunctionRetTy,
pub variadic: bool, pub c_variadic: bool,
} }
impl FnDecl { impl FnDecl {

View file

@ -985,7 +985,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
P(ast::FnDecl { P(ast::FnDecl {
inputs, inputs,
output, output,
variadic: false c_variadic: false
}) })
} }

View file

@ -471,6 +471,9 @@ declare_features! (
// #[repr(align(X))] on enums // #[repr(align(X))] on enums
(active, repr_align_enum, "1.34.0", Some(57996), None), (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! ( declare_features! (
@ -1901,6 +1904,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
if header.asyncness.node.is_async() { if header.asyncness.node.is_async() {
gate_feature_post!(&self, async_await, span, "async fn is unstable"); 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 // Stability of const fn methods are covered in
// `visit_trait_item` and `visit_impl_item` below; this is // `visit_trait_item` and `visit_impl_item` below; this is
// because default methods don't pass through this point. // because default methods don't pass through this point.
@ -1929,6 +1937,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
if block.is_none() { if block.is_none() {
self.check_abi(sig.header.abi, ti.span); 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 { if sig.header.constness.node == ast::Constness::Const {
gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable"); gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
} }

View file

@ -409,7 +409,8 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
let Ty { id, node, span } = ty.deref_mut(); let Ty { id, node, span } = ty.deref_mut();
vis.visit_id(id); vis.visit_id(id);
match node { 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::Slice(ty) => vis.visit_ty(ty),
TyKind::Ptr(mt) => vis.visit_mt(mt), TyKind::Ptr(mt) => vis.visit_mt(mt),
TyKind::Rptr(lt, 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) { 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)); visit_vec(inputs, |input| vis.visit_arg(input));
match output { match output {
FunctionRetTy::Default(span) => vis.visit_span(span), FunctionRetTy::Default(span) => vis.visit_span(span),

View file

@ -1457,12 +1457,12 @@ impl<'a> Parser<'a> {
}; };
self.expect_keyword(keywords::Fn)?; 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 ret_ty = self.parse_ret_ty(false)?;
let decl = P(FnDecl { let decl = P(FnDecl {
inputs, inputs,
output: ret_ty, output: ret_ty,
variadic, c_variadic,
}); });
Ok(TyKind::BareFn(P(BareFnTy { Ok(TyKind::BareFn(P(BareFnTy {
abi, abi,
@ -1543,7 +1543,7 @@ impl<'a> Parser<'a> {
// definition... // definition...
// We don't allow argument names to be left off in edition 2018. // 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()?; 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. /// Parses an optional return type `[ -> TY ]` in a function declaration.
fn parse_ret_ty(&mut self, allow_plus: bool) -> PResult<'a, FunctionRetTy> { fn parse_ret_ty(&mut self, allow_plus: bool) -> PResult<'a, FunctionRetTy> {
if self.eat(&token::RArrow) { 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 { } else {
Ok(FunctionRetTy::Default(self.span.shrink_to_lo())) Ok(FunctionRetTy::Default(self.span.shrink_to_lo()))
} }
@ -1621,7 +1621,7 @@ impl<'a> Parser<'a> {
/// Parses a type. /// Parses a type.
pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> { 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. /// Parses a type in restricted contexts where `+` is not permitted.
@ -1631,11 +1631,11 @@ impl<'a> Parser<'a> {
/// Example 2: `value1 as TYPE + value2` /// Example 2: `value1 as TYPE + value2`
/// `+` is prohibited to avoid interactions with expression grammar. /// `+` is prohibited to avoid interactions with expression grammar.
fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> { 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) fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool,
-> PResult<'a, P<Ty>> { allow_c_variadic: bool) -> PResult<'a, P<Ty>> {
maybe_whole!(self, NtTy, |x| x); maybe_whole!(self, NtTy, |x| x);
let lo = self.span; let lo = self.span;
@ -1772,6 +1772,15 @@ impl<'a> Parser<'a> {
TyKind::Path(None, path) 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 { } else {
let msg = format!("expected type, found {}", self.this_token_descr()); let msg = format!("expected type, found {}", self.this_token_descr());
return Err(self.fatal(&msg)); return Err(self.fatal(&msg));
@ -1959,7 +1968,8 @@ impl<'a> Parser<'a> {
} }
/// This version of parse arg doesn't necessarily require identifier names. /// 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); maybe_whole!(self, NtArg, |x| x);
if let Ok(Some(_)) = self.parse_self_arg() { 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"); 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 { } else {
debug!("parse_arg_general ident_to_pat"); debug!("parse_arg_general ident_to_pat");
let parser_snapshot_before_ty = self.clone(); let parser_snapshot_before_ty = self.clone();
self.eat_incorrect_doc_comment("a method argument's type"); 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 && if ty.is_ok() && self.token != token::Comma &&
self.token != token::CloseDelim(token::Paren) { self.token != token::CloseDelim(token::Paren) {
// This wasn't actually a type, but a pattern looking like a type, // This wasn't actually a type, but a pattern looking like a type,
@ -2032,6 +2042,11 @@ impl<'a> Parser<'a> {
(pat, ty) (pat, ty)
} }
Err(mut err) => { 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. // Recover from attempting to parse the argument as a type without pattern.
err.cancel(); err.cancel();
mem::replace(self, parser_snapshot_before_ty); mem::replace(self, parser_snapshot_before_ty);
@ -2068,7 +2083,7 @@ impl<'a> Parser<'a> {
/// Parses a single function argument. /// Parses a single function argument.
crate fn parse_arg(&mut self) -> PResult<'a, Arg> { 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|`). /// 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 span = lo.to(self.prev_span);
let output = if self.eat(&token::RArrow) { let output = if self.eat(&token::RArrow) {
Some(self.parse_ty_common(false, false)?) Some(self.parse_ty_common(false, false, false)?)
} else { } else {
None None
}; };
@ -6113,58 +6128,52 @@ impl<'a> Parser<'a> {
Ok(where_clause) 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)> { -> PResult<'a, (Vec<Arg> , bool)> {
self.expect(&token::OpenDelim(token::Paren))?; self.expect(&token::OpenDelim(token::Paren))?;
let sp = self.span; let sp = self.span;
let mut variadic = false; let mut c_variadic = false;
let (args, recovered): (Vec<Option<Arg>>, bool) = let (args, recovered): (Vec<Option<Arg>>, bool) =
self.parse_seq_to_before_end( self.parse_seq_to_before_end(
&token::CloseDelim(token::Paren), &token::CloseDelim(token::Paren),
SeqSep::trailing_allowed(token::Comma), SeqSep::trailing_allowed(token::Comma),
|p| { |p| {
if p.token == token::DotDotDot { // If the argument is a C-variadic argument we should not
p.bump(); // enforce named arguments.
variadic = true; let enforce_named_args = if p.token == token::DotDotDot {
if allow_variadic { false
} else {
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) { if p.token != token::CloseDelim(token::Paren) {
let span = p.span; let span = p.span;
p.span_err(span, p.span_err(span,
"`...` must be last in argument list for variadic function"); "`...` must be the last argument of a C-variadic function");
}
Ok(None) Ok(None)
} else { } else {
let span = p.prev_span; Ok(Some(arg))
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)
}
} }
} else { } else {
match p.parse_arg_general(named_args, false) { Ok(Some(arg))
Ok(arg) => Ok(Some(arg)), }
},
Err(mut e) => { Err(mut e) => {
e.emit(); e.emit();
let lo = p.prev_span; let lo = p.prev_span;
// Skip every token until next possible arg or end. // Skip every token until next possible arg or end.
p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
// Create a placeholder argument for proper arg count (#34264). // Create a placeholder argument for proper arg count (issue #34264).
let span = lo.to(p.prev_span); let span = lo.to(p.prev_span);
Ok(Some(dummy_arg(span))) Ok(Some(dummy_arg(span)))
} }
} }
} }
}
)?; )?;
if !recovered { if !recovered {
@ -6173,24 +6182,24 @@ impl<'a> Parser<'a> {
let args: Vec<_> = args.into_iter().filter_map(|x| x).collect(); 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, 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. /// 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)?; let ret_ty = self.parse_ret_ty(true)?;
Ok(P(FnDecl { Ok(P(FnDecl {
inputs: args, inputs: args,
output: ret_ty, output: ret_ty,
variadic, c_variadic,
})) }))
} }
@ -6337,7 +6346,7 @@ impl<'a> Parser<'a> {
Ok(P(FnDecl { Ok(P(FnDecl {
inputs: fn_inputs, inputs: fn_inputs,
output: self.parse_ret_ty(true)?, output: self.parse_ret_ty(true)?,
variadic: false c_variadic: false
})) }))
} }
@ -6363,7 +6372,7 @@ impl<'a> Parser<'a> {
Ok(P(FnDecl { Ok(P(FnDecl {
inputs: inputs_captures, inputs: inputs_captures,
output, output,
variadic: false c_variadic: false
})) }))
} }
@ -6395,7 +6404,8 @@ impl<'a> Parser<'a> {
abi: Abi) abi: Abi)
-> PResult<'a, ItemInfo> { -> PResult<'a, ItemInfo> {
let (ident, mut generics) = self.parse_fn_header()?; 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()?; generics.where_clause = self.parse_where_clause()?;
let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
let header = FnHeader { unsafety, asyncness, constness, abi }; let header = FnHeader { unsafety, asyncness, constness, abi };

View file

@ -1118,6 +1118,9 @@ impl<'a> State<'a> {
ast::TyKind::Mac(ref m) => { ast::TyKind::Mac(ref m) => {
self.print_mac(m)?; self.print_mac(m)?;
} }
ast::TyKind::CVarArgs => {
self.s.word("...")?;
}
} }
self.end() self.end()
} }
@ -2811,7 +2814,7 @@ impl<'a> State<'a> {
-> io::Result<()> { -> io::Result<()> {
self.popen()?; self.popen()?;
self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false))?; self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false))?;
if decl.variadic { if decl.c_variadic {
self.s.word(", ...")?; self.s.word(", ...")?;
} }
self.pclose()?; self.pclose()?;
@ -3238,7 +3241,7 @@ mod tests {
let decl = ast::FnDecl { let decl = ast::FnDecl {
inputs: Vec::new(), inputs: Vec::new(),
output: ast::FunctionRetTy::Default(syntax_pos::DUMMY_SP), output: ast::FunctionRetTy::Default(syntax_pos::DUMMY_SP),
variadic: false c_variadic: false
}; };
let generics = ast::Generics::default(); let generics = ast::Generics::default();
assert_eq!( assert_eq!(

View file

@ -319,7 +319,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
walk_list!(visitor, visit_lifetime, opt_lifetime); walk_list!(visitor, visit_lifetime, opt_lifetime);
visitor.visit_ty(&mutable_type.ty) visitor.visit_ty(&mutable_type.ty)
} }
TyKind::Never => {}, TyKind::Never | TyKind::CVarArgs => {}
TyKind::Tup(ref tuple_element_types) => { TyKind::Tup(ref tuple_element_types) => {
walk_list!(visitor, visit_ty, tuple_element_types); walk_list!(visitor, visit_ty, tuple_element_types);
} }

View file

@ -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);
}

View file

@ -18,8 +18,10 @@ macro_rules! continue_if {
unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool { unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool {
let cstr0 = CStr::from_ptr(ptr); let cstr0 = CStr::from_ptr(ptr);
let cstr1 = CString::new(val).unwrap(); match CString::new(val) {
&*cstr1 == cstr0 Ok(cstr1) => &*cstr1 == cstr0,
Err(_) => false,
}
} }
#[no_mangle] #[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
}

View file

@ -8,6 +8,9 @@ extern size_t check_list_0(va_list ap);
extern size_t check_list_1(va_list ap); extern size_t check_list_1(va_list ap);
extern size_t check_list_2(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_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), ...) { int test_rust(size_t (*fn)(va_list), ...) {
size_t ret = 0; 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_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(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; return 0;
} }

View file

@ -112,7 +112,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P<Expr>)) {
let decl = P(FnDecl { let decl = P(FnDecl {
inputs: vec![], inputs: vec![],
output: FunctionRetTy::Default(DUMMY_SP), output: FunctionRetTy::Default(DUMMY_SP),
variadic: false, c_variadic: false,
}); });
iter_exprs(depth - 1, &mut |e| g( iter_exprs(depth - 1, &mut |e| g(
ExprKind::Closure(CaptureBy::Value, ExprKind::Closure(CaptureBy::Value,

View file

@ -1,4 +1,4 @@
extern "C" { 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, ...); pub fn foo(x: i32, ...);
} }

View file

@ -1,8 +1,8 @@
error[E0045]: variadic function must have C or cdecl calling convention error[E0045]: C-variadic function must have C or cdecl calling convention
--> $DIR/variadic-ffi.rs:5:5 --> $DIR/variadic-ffi-1.rs:5:5
| |
LL | fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling 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 error: aborting due to previous error

View file

@ -1,8 +1,8 @@
error[E0045]: variadic function must have C or cdecl calling convention error[E0045]: C-variadic function must have C or cdecl calling convention
--> $DIR/variadic-ffi-2.rs:3:11 --> $DIR/variadic-ffi-2.rs:3:11
| |
LL | fn baz(f: extern "stdcall" fn(usize, ...)) { 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 error: aborting due to previous error

View file

@ -14,12 +14,10 @@ fn main() {
let x: unsafe extern "C" fn(f: isize, x: u8) = foo; let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
//~^ ERROR: mismatched types //~^ ERROR: mismatched types
//~| expected type `unsafe extern "C" fn(isize, u8)` //~| 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; let y: extern "C" fn(f: isize, x: u8, ...) = bar;
//~^ ERROR: mismatched types //~^ ERROR: mismatched types
//~| expected type `extern "C" fn(isize, u8, ...)` //~| expected type `for<'r> extern "C" fn(isize, u8, std::ffi::VaList<'r>, ...)`
//~| found type `extern "C" fn(isize, u8) {bar}`
foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function

View file

@ -23,49 +23,49 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
| ^^^ expected non-variadic fn, found variadic function | ^^^ expected non-variadic fn, found variadic function
| |
= note: expected type `unsafe extern "C" fn(isize, u8)` = 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 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; LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
| ^^^ expected variadic fn, found non-variadic function | ^^^ 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}` found type `extern "C" fn(isize, u8) {bar}`
error[E0617]: can't pass `f32` to variadic function 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 LL | foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function
| ^^^^ help: cast the value to `c_double`: `3f32 as c_double` | ^^^^ help: cast the value to `c_double`: `3f32 as c_double`
error[E0617]: can't pass `bool` to variadic function 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 LL | foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function
| ^^^^ help: cast the value to `c_int`: `true as c_int` | ^^^^ help: cast the value to `c_int`: `true as c_int`
error[E0617]: can't pass `i8` to variadic function 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 LL | foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function
| ^^^ help: cast the value to `c_int`: `1i8 as c_int` | ^^^ help: cast the value to `c_int`: `1i8 as c_int`
error[E0617]: can't pass `u8` to variadic function 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 LL | foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function
| ^^^ help: cast the value to `c_uint`: `1u8 as c_uint` | ^^^ help: cast the value to `c_uint`: `1u8 as c_uint`
error[E0617]: can't pass `i16` to variadic function 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 LL | foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function
| ^^^^ help: cast the value to `c_int`: `1i16 as c_int` | ^^^^ help: cast the value to `c_int`: `1i16 as c_int`
error[E0617]: can't pass `u16` to variadic function 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 LL | foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function
| ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint` | ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint`

View file

@ -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
}

View file

@ -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`.

View file

@ -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
}

View file

@ -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`.

View file

@ -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
}

View file

@ -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`.

View file

@ -1,8 +1,8 @@
error[E0045]: variadic function must have C or cdecl calling convention error[E0045]: C-variadic function must have C or cdecl calling convention
--> $DIR/E0045.rs:1:17 --> $DIR/E0045.rs:1:17
| |
LL | extern "Rust" { fn foo(x: u8, ...); } //~ ERROR E0045 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 error: aborting due to previous error

View file

@ -22,7 +22,7 @@ fn main() {
//~^ ERROR can't pass `u16` to variadic function //~^ ERROR can't pass `u16` to variadic function
//~| HELP cast the value to `c_uint` //~| HELP cast the value to `c_uint`
printf(::std::ptr::null(), printf); printf(::std::ptr::null(), printf);
//~^ ERROR can't pass `unsafe extern "C" fn(*const i8, ...) {printf}` to variadic function //~^ 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 `unsafe extern "C" fn(*const i8, ...)` //~| HELP cast the value to `for<'r> unsafe extern "C" fn(*const i8, std::ffi::VaList<'r>, ...)`
} }
} }

View file

@ -28,15 +28,15 @@ error[E0617]: can't pass `u16` to variadic function
LL | printf(::std::ptr::null(), 0u16); LL | printf(::std::ptr::null(), 0u16);
| ^^^^ help: cast the value to `c_uint`: `0u16 as c_uint` | ^^^^ 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 --> $DIR/E0617.rs:24:36
| |
LL | printf(::std::ptr::null(), printf); 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 error: aborting due to 6 previous errors

View file

@ -0,0 +1,4 @@
#![crate_type="lib"]
pub unsafe extern "C" fn test(_: i32, ap: ...) { }
//~^ C-varaidic functions are unstable

View file

@ -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`.

View file

@ -1,3 +1,3 @@
extern "C" fn foo(x: u8, ...); 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 `;` //~| ERROR expected one of `->`, `where`, or `{`, found `;`

View file

@ -1,4 +1,4 @@
error: only foreign functions are allowed to be variadic error: only foreign functions are allowed to be C-variadic
--> $DIR/invalid-variadic-function.rs:1:26 --> $DIR/invalid-variadic-function.rs:1:26
| |
LL | extern "C" fn foo(x: u8, ...); LL | extern "C" fn foo(x: u8, ...);

View file

@ -10,11 +10,11 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{`
LL | Nope(i32 {}) //~ ERROR: found `{` LL | Nope(i32 {}) //~ ERROR: found `{`
| ^ expected one of 7 possible tokens here | ^ 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 --> $DIR/recover-enum2.rs:27:22
| |
LL | Nope(i32 {}) //~ ERROR: found `{` 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 `_` error: expected expression, found reserved identifier `_`
--> $DIR/recover-enum2.rs:32:22 --> $DIR/recover-enum2.rs:32:22

View file

@ -1,5 +1,5 @@
fn foo(x: isize, ...) { 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() {} fn main() {}

View file

@ -1,4 +1,4 @@
error: only foreign functions are allowed to be variadic error: only foreign functions are allowed to be C-variadic
--> $DIR/variadic-ffi-3.rs:1:18 --> $DIR/variadic-ffi-3.rs:1:18
| |
LL | fn foo(x: isize, ...) { LL | fn foo(x: isize, ...) {

View file

@ -1,5 +1,5 @@
extern "C" fn foo(x: isize, ...) { 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() {} fn main() {}

View file

@ -1,4 +1,4 @@
error: only foreign functions are allowed to be variadic error: only foreign functions are allowed to be C-variadic
--> $DIR/variadic-ffi-4.rs:1:29 --> $DIR/variadic-ffi-4.rs:1:29
| |
LL | extern "C" fn foo(x: isize, ...) { LL | extern "C" fn foo(x: isize, ...) {

View file

@ -82,25 +82,27 @@ pub fn check(path: &path::Path, bad: &mut bool) {
!lang_features.contains_key(name) !lang_features.contains_key(name)
}).collect(); }).collect();
// Library features
let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features); let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features);
let unstable_book_lib_features_section_file_names = let unstable_book_lib_features_section_file_names =
collect_unstable_book_lib_features_section_file_names(path); 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 // Language features
let unstable_lang_feature_names = collect_unstable_feature_names(&lang_features); let unstable_lang_feature_names = collect_unstable_feature_names(&lang_features);
let unstable_book_lang_features_section_file_names = let unstable_book_lang_features_section_file_names =
collect_unstable_book_lang_features_section_file_names(path); 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. // Check for Unstable Book sections that don't have a corresponding unstable feature.
for feature_name in &unstable_book_lang_features_section_file_names - for feature_name in &unstable_book_lang_features_section_file_names -
&unstable_lang_feature_names { &unstable_lang_feature_names {