Skip to content

Commit 7dc5bad

Browse files
Rollup merge of rust-lang#146342 - folkertdev:c-variadic-errors-take-3, r=workingjubilee
Improve C-variadic error messages: part 2 tracking issue: rust-lang#44930 a reimplementation of rust-lang#143546 that builds on rust-lang#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 1c44607 + 9196844 commit 7dc5bad

18 files changed

+357
-167
lines changed

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
pub span: Span,
22852285
}
22862286

2287+
impl FnSig {
2288+
/// Return a span encompassing the header, or where to insert it if empty.
2289+
pub fn header_span(&self) -> Span {
2290+
match self.header.ext {
2291+
Extern::Implicit(span) | Extern::Explicit(_, span) => {
2292+
return self.span.with_hi(span.hi());
2293+
}
2294+
Extern::None => {}
2295+
}
2296+
2297+
match self.header.safety {
2298+
Safety::Unsafe(span) | Safety::Safe(span) => return self.span.with_hi(span.hi()),
2299+
Safety::Default => {}
2300+
};
2301+
2302+
if let Some(coroutine_kind) = self.header.coroutine_kind {
2303+
return self.span.with_hi(coroutine_kind.span().hi());
2304+
}
2305+
2306+
if let Const::Yes(span) = self.header.constness {
2307+
return self.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+
pub fn safety_span(&self) -> Span {
2315+
match self.header.safety {
2316+
Safety::Unsafe(span) | Safety::Safe(span) => span,
2317+
Safety::Default => {
2318+
// Insert after the `coroutine_kind` if available.
2319+
if let Some(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+
pub fn extern_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+
pub fn span(self) -> Option<Span> {
3579+
match self {
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
pub struct FnHeader {
3537-
/// Whether this is `unsafe`, or has a default safety.
3538-
pub safety: Safety,
3539-
/// Whether this is `async`, `gen`, or nothing.
3540-
pub coroutine_kind: Option<CoroutineKind>,
35413592
/// The `const` keyword, if any
35423593
pub constness: Const,
3594+
/// Whether this is `async`, `gen`, or nothing.
3595+
pub coroutine_kind: Option<CoroutineKind>,
3596+
/// Whether this is `unsafe`, or has a default safety.
3597+
pub safety: Safety,
35433598
/// The `extern` keyword and corresponding ABI string, if any.
35443599
pub ext: 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-
pub fn span(&self) -> Option<Span> {
3559-
fn append(a: &mut Option<Span>, b: Span) {
3560-
*a = match a {
3561-
None => Some(b),
3562-
Some(x) => Some(x.to(b)),
3563-
}
3564-
}
3565-
3566-
let mut full_span = None;
3567-
3568-
match self.safety {
3569-
Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
3570-
Safety::Default => {}
3571-
};
3572-
3573-
if let Some(coroutine_kind) = self.coroutine_kind {
3574-
append(&mut full_span, coroutine_kind.span());
3575-
}
3576-
3577-
if let Const::Yes(span) = self.constness {
3578-
append(&mut full_span, span);
3579-
}
3580-
3581-
match self.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
impl Default for FnHeader {

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+
if let Some(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-
if matches!(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
fn check_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) struct BadCVariadic {
322+
#[diag(ast_passes_c_variadic_associated_function)]
323+
pub(crate) struct CVariadicAssociatedFunction {
324324
#[primary_span]
325325
pub span: Span,
326326
}
327327

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

696+
#[derive(Diagnostic)]
697+
#[diag(ast_passes_coroutine_and_c_variadic)]
698+
pub(crate) struct CoroutineAndCVariadic {
699+
#[primary_span]
700+
pub spans: Vec<Span>,
701+
pub coroutine_kind: &'static str,
702+
#[label(ast_passes_const)]
703+
pub coroutine_span: Span,
704+
#[label(ast_passes_variadic)]
705+
pub variadic_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+
async unsafe extern "C" fn cannot_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

tests/ui/c-variadic/not-async.stderr

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" fn static_trait_object(
57-
x: &'static dyn Trait,
58-
) -> &'static dyn Trait {
56+
extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait {
5957
//~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798]
6058
x
6159
}
6260

6361
#[repr(transparent)]
6462
struct WrapperTransparent<'a>(&'a dyn Trait);
6563

66-
extern "cmse-nonsecure-entry" fn wrapped_trait_object(
67-
x: WrapperTransparent,
68-
) -> WrapperTransparent {
64+
extern "cmse-nonsecure-entry" fn wrapped_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" fn c_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+
unsafe extern "cmse-nonsecure-entry" fn c_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)