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