Skip to content

Commit bb45ea3

Browse files
authored
Rollup merge of #146342 - folkertdev:c-variadic-errors-take-3, r=workingjubilee
Improve C-variadic error messages: part 2 tracking issue: #44930 a reimplementation of #143546 that builds on #146165. This PR - disallows coroutines (e.g. `async fn`) from having a `...` argument - disallows associated functions (both in traits and standard impl blocks) from having a `...` argument - splits up a generic "ill-formed C-variadic function" into specific errors about using an incorrect ABI, not specifying an ABI, or missing the unsafe keyword C-variadic coroutines probably don't make sense? C-variadic functions are for FFI purposes, combining that with async functions seems weird. For associated functions, we're just cutting scope. It's probably fine, but it's probably better to explicitly allow it. So for now, at least give a more targeted error message. Made to be reviewed commit-by-commit. cc `@workingjubilee` r? compiler
2 parents 86d39a0 + 9196844 commit bb45ea3

18 files changed

Lines changed: 357 additions & 167 deletions

‎compiler/rustc_ast/src/ast.rs‎

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,6 +2284,54 @@ pub struct FnSig {
22842284
pubspan:Span,
22852285
}
22862286

2287+
implFnSig{
2288+
/// Return a span encompassing the header, or where to insert it if empty.
2289+
pubfnheader_span(&self) -> Span{
2290+
matchself.header.ext{
2291+
Extern::Implicit(span) | Extern::Explicit(_, span) => {
2292+
returnself.span.with_hi(span.hi());
2293+
}
2294+
Extern::None => {}
2295+
}
2296+
2297+
matchself.header.safety{
2298+
Safety::Unsafe(span) | Safety::Safe(span) => returnself.span.with_hi(span.hi()),
2299+
Safety::Default => {}
2300+
};
2301+
2302+
ifletSome(coroutine_kind) = self.header.coroutine_kind{
2303+
returnself.span.with_hi(coroutine_kind.span().hi());
2304+
}
2305+
2306+
ifletConst::Yes(span) = self.header.constness{
2307+
returnself.span.with_hi(span.hi());
2308+
}
2309+
2310+
self.span.shrink_to_lo()
2311+
}
2312+
2313+
/// The span of the header's safety, or where to insert it if empty.
2314+
pubfnsafety_span(&self) -> Span{
2315+
matchself.header.safety{
2316+
Safety::Unsafe(span) | Safety::Safe(span) => span,
2317+
Safety::Default => {
2318+
// Insert after the `coroutine_kind` if available.
2319+
ifletSome(extern_span) = self.header.ext.span(){
2320+
return extern_span.shrink_to_lo();
2321+
}
2322+
2323+
// Insert right at the front of the signature.
2324+
self.header_span().shrink_to_hi()
2325+
}
2326+
}
2327+
}
2328+
2329+
/// The span of the header's extern, or where to insert it if empty.
2330+
pubfnextern_span(&self) -> Span{
2331+
self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi())
2332+
}
2333+
}
2334+
22872335
/// A constraint on an associated item.
22882336
///
22892337
/// ### Examples
@@ -3526,6 +3574,13 @@ impl Extern {
35263574
None => Extern::Implicit(span),
35273575
}
35283576
}
3577+
3578+
pubfnspan(self) -> Option<Span>{
3579+
matchself{
3580+
Extern::None => None,
3581+
Extern::Implicit(span) | Extern::Explicit(_, span) => Some(span),
3582+
}
3583+
}
35293584
}
35303585

35313586
/// A function header.
@@ -3534,12 +3589,12 @@ impl Extern {
35343589
/// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`).
35353590
#[derive(Clone,Copy,Encodable,Decodable,Debug,Walkable)]
35363591
pubstructFnHeader{
3537-
/// Whether this is `unsafe`, or has a default safety.
3538-
pubsafety:Safety,
3539-
/// Whether this is `async`, `gen`, or nothing.
3540-
pubcoroutine_kind:Option<CoroutineKind>,
35413592
/// The `const` keyword, if any
35423593
pubconstness:Const,
3594+
/// Whether this is `async`, `gen`, or nothing.
3595+
pubcoroutine_kind:Option<CoroutineKind>,
3596+
/// Whether this is `unsafe`, or has a default safety.
3597+
pubsafety:Safety,
35433598
/// The `extern` keyword and corresponding ABI string, if any.
35443599
pubext:Extern,
35453600
}
@@ -3553,38 +3608,6 @@ impl FnHeader {
35533608
|| matches!(constness,Const::Yes(_))
35543609
|| !matches!(ext,Extern::None)
35553610
}
3556-
3557-
/// Return a span encompassing the header, or none if all options are default.
3558-
pubfnspan(&self) -> Option<Span>{
3559-
fnappend(a:&mutOption<Span>,b:Span){
3560-
*a = match a {
3561-
None => Some(b),
3562-
Some(x) => Some(x.to(b)),
3563-
}
3564-
}
3565-
3566-
letmut full_span = None;
3567-
3568-
matchself.safety{
3569-
Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
3570-
Safety::Default => {}
3571-
};
3572-
3573-
ifletSome(coroutine_kind) = self.coroutine_kind{
3574-
append(&mut full_span, coroutine_kind.span());
3575-
}
3576-
3577-
ifletConst::Yes(span) = self.constness{
3578-
append(&mut full_span, span);
3579-
}
3580-
3581-
matchself.ext{
3582-
Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span),
3583-
Extern::None => {}
3584-
}
3585-
3586-
full_span
3587-
}
35883611
}
35893612

35903613
implDefaultforFnHeader{

‎compiler/rustc_ast_passes/messages.ftl‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,26 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim
5757
.label = {ast_passes_auto_super_lifetime}
5858
.suggestion = remove the super traits or lifetime bounds
5959
60-
ast_passes_bad_c_variadic = defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
61-
6260
ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
6361
.cannot_have = cannot have a body
6462
.invalid = the invalid body
6563
.existing = `extern` blocks define existing foreign {$kind}s and {$kind}s inside of them cannot have a body
6664
6765
ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect
6866
67+
ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list
68+
69+
ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions
70+
.label = `extern "{$abi}"` because of this
71+
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
72+
73+
ast_passes_c_variadic_must_be_unsafe =
74+
functions with a C variable argument list must be unsafe
75+
.suggestion = add the `unsafe` keyword to this definition
76+
77+
ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions
78+
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
79+
6980
ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic
7081
.const = `const` because of this
7182
.variadic = C-variadic because of this
@@ -84,6 +95,10 @@ ast_passes_const_without_body =
8495
ast_passes_constraint_on_negative_bound =
8596
associated type constraints not allowed on negative bounds
8697
98+
ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic
99+
.const = `{$coroutine_kind}` because of this
100+
.variadic = C-variadic because of this
101+
87102
ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses
88103
.label = not supported
89104
.suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax

‎compiler/rustc_ast_passes/src/ast_validation.rs‎

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ impl<'a> AstValidator<'a> {
492492
}
493493

494494
if !spans.is_empty(){
495-
let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
495+
let header_span = sig.header_span();
496496
let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
497497
let padding = if header_span.is_empty(){""}else{" "};
498498

@@ -685,22 +685,53 @@ impl<'a> AstValidator<'a> {
685685
});
686686
}
687687

688+
ifletSome(coroutine_kind) = sig.header.coroutine_kind{
689+
self.dcx().emit_err(errors::CoroutineAndCVariadic{
690+
spans:vec![coroutine_kind.span(), variadic_param.span],
691+
coroutine_kind: coroutine_kind.as_str(),
692+
coroutine_span: coroutine_kind.span(),
693+
variadic_span: variadic_param.span,
694+
});
695+
}
696+
688697
match fn_ctxt {
689698
FnCtxt::Foreign => return,
690699
FnCtxt::Free => match sig.header.ext{
691-
Extern::Explicit(StrLit{symbol_unescaped: sym::C, .. }, _)
692-
| Extern::Explicit(StrLit{symbol_unescaped: sym::C_dash_unwind, .. }, _)
693-
| Extern::Implicit(_)
694-
ifmatches!(sig.header.safety,Safety::Unsafe(_)) =>
695-
{
696-
return;
700+
Extern::Implicit(_) => {
701+
if !matches!(sig.header.safety,Safety::Unsafe(_)){
702+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe{
703+
span: variadic_param.span,
704+
unsafe_span: sig.safety_span(),
705+
});
706+
}
697707
}
698-
_ => {}
699-
},
700-
FnCtxt::Assoc(_) => {}
701-
};
708+
Extern::Explicit(StrLit{ symbol_unescaped, .. }, _) => {
709+
if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind){
710+
self.dcx().emit_err(errors::CVariadicBadExtern{
711+
span: variadic_param.span,
712+
abi: symbol_unescaped,
713+
extern_span: sig.extern_span(),
714+
});
715+
}
702716

703-
self.dcx().emit_err(errors::BadCVariadic{span: variadic_param.span});
717+
if !matches!(sig.header.safety,Safety::Unsafe(_)){
718+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe{
719+
span: variadic_param.span,
720+
unsafe_span: sig.safety_span(),
721+
});
722+
}
723+
}
724+
Extern::None => {
725+
let err = errors::CVariadicNoExtern{span: variadic_param.span};
726+
self.dcx().emit_err(err);
727+
}
728+
},
729+
FnCtxt::Assoc(_) => {
730+
// For now, C variable argument lists are unsupported in associated functions.
731+
let err = errors::CVariadicAssociatedFunction{span: variadic_param.span};
732+
self.dcx().emit_err(err);
733+
}
734+
}
704735
}
705736

706737
fncheck_item_named(&self,ident:Ident,kind:&str){

‎compiler/rustc_ast_passes/src/errors.rs‎

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,46 @@ pub(crate) struct ExternItemAscii {
319319
}
320320

321321
#[derive(Diagnostic)]
322-
#[diag(ast_passes_bad_c_variadic)]
323-
pub(crate)structBadCVariadic{
322+
#[diag(ast_passes_c_variadic_associated_function)]
323+
pub(crate)structCVariadicAssociatedFunction{
324324
#[primary_span]
325325
pubspan:Span,
326326
}
327327

328+
#[derive(Diagnostic)]
329+
#[diag(ast_passes_c_variadic_no_extern)]
330+
#[help]
331+
pub(crate)structCVariadicNoExtern{
332+
#[primary_span]
333+
pubspan:Span,
334+
}
335+
336+
#[derive(Diagnostic)]
337+
#[diag(ast_passes_c_variadic_must_be_unsafe)]
338+
pub(crate)structCVariadicMustBeUnsafe{
339+
#[primary_span]
340+
pubspan:Span,
341+
342+
#[suggestion(
343+
ast_passes_suggestion,
344+
applicability = "maybe-incorrect",
345+
code = "unsafe ",
346+
style = "verbose"
347+
)]
348+
pubunsafe_span:Span,
349+
}
350+
351+
#[derive(Diagnostic)]
352+
#[diag(ast_passes_c_variadic_bad_extern)]
353+
#[help]
354+
pub(crate)structCVariadicBadExtern{
355+
#[primary_span]
356+
pubspan:Span,
357+
pubabi:Symbol,
358+
#[label]
359+
pubextern_span:Span,
360+
}
361+
328362
#[derive(Diagnostic)]
329363
#[diag(ast_passes_item_underscore)]
330364
pub(crate)structItemUnderscore<'a>{
@@ -659,6 +693,18 @@ pub(crate) struct ConstAndCVariadic {
659693
pubvariadic_span:Span,
660694
}
661695

696+
#[derive(Diagnostic)]
697+
#[diag(ast_passes_coroutine_and_c_variadic)]
698+
pub(crate)structCoroutineAndCVariadic{
699+
#[primary_span]
700+
pubspans:Vec<Span>,
701+
pubcoroutine_kind:&'staticstr,
702+
#[label(ast_passes_const)]
703+
pubcoroutine_span:Span,
704+
#[label(ast_passes_variadic)]
705+
pubvariadic_span:Span,
706+
}
707+
662708
#[derive(Diagnostic)]
663709
#[diag(ast_passes_pattern_in_foreign, code = E0130)]
664710
// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`)

‎tests/ui/c-variadic/issue-86053-1.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ fn ordering4 < 'a , 'b > ( a : , self , self , self ,
1313
//~| ERROR unexpected `self` parameter in function
1414
//~| ERROR unexpected `self` parameter in function
1515
//~| ERROR `...` must be the last argument of a C-variadic function
16-
//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
16+
//~| ERROR `...` is not supported for non-extern functions
1717
//~| ERROR cannot find type `F` in this scope
1818
}

‎tests/ui/c-variadic/issue-86053-1.stderr‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ error: `...` must be the last argument of a C-variadic function
4646
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
4747
| ^^^
4848

49-
error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
49+
error: `...` is not supported for non-extern functions
5050
--> $DIR/issue-86053-1.rs:11:36
5151
|
5252
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
5353
| ^^^
54+
|
55+
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
5456

5557
error[E0412]: cannot find type `F` in this scope
5658
--> $DIR/issue-86053-1.rs:11:48

‎tests/ui/c-variadic/not-async.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//@ edition: 2021
2+
#![feature(c_variadic)]
3+
#![crate_type = "lib"]
4+
5+
asyncunsafeextern"C"fncannot_be_async(x:isize, ...){}
6+
//~^ ERROR functions cannot be both `async` and C-variadic
7+
//~| ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: functions cannot be both `async` and C-variadic
2+
--> $DIR/not-async.rs:5:1
3+
|
4+
LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
5+
| ^^^^^ `async` because of this ^^^ C-variadic because of this
6+
7+
error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
8+
--> $DIR/not-async.rs:5:59
9+
|
10+
LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
11+
| --------------------------------------------------------- ^^
12+
| |
13+
| opaque type defined here
14+
|
15+
= note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_`
16+
17+
error: aborting due to 2 previous errors
18+
19+
For more information about this error, try `rustc --explain E0700`.

‎tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs‎

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,20 @@ extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait {
5353
x
5454
}
5555

56-
extern"cmse-nonsecure-entry"fnstatic_trait_object(
57-
x:&'staticdynTrait,
58-
) -> &'staticdynTrait{
56+
extern"cmse-nonsecure-entry"fnstatic_trait_object(x:&'staticdynTrait) -> &'staticdynTrait{
5957
//~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798]
6058
x
6159
}
6260

6361
#[repr(transparent)]
6462
structWrapperTransparent<'a>(&'adynTrait);
6563

66-
extern"cmse-nonsecure-entry"fnwrapped_trait_object(
67-
x:WrapperTransparent,
68-
) -> WrapperTransparent{
64+
extern"cmse-nonsecure-entry"fnwrapped_trait_object(x:WrapperTransparent) -> WrapperTransparent{
6965
//~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798]
7066
x
7167
}
7268

73-
extern"cmse-nonsecure-entry"fnc_variadic(_:u32, _: ...){
74-
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
69+
unsafeextern"cmse-nonsecure-entry"fnc_variadic(_:u32, _: ...){
70+
//~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions
7571
//~| ERROR requires `va_list` lang_item
7672
}

0 commit comments

Comments
 (0)