Skip to content

Commit 241fdb9

Browse files
committed
improve c-variadic errors
1 parent c83e217 commit 241fdb9

15 files changed

+302
-61
lines changed

compiler/rustc_ast_passes/messages.ftl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim
4646
.label = {ast_passes_auto_super_lifetime}
4747
.suggestion = remove the super traits or lifetime bounds
4848
49-
ast_passes_bad_c_variadic = only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
50-
5149
ast_passes_bare_fn_invalid_safety = function pointers cannot be declared with `safe` safety qualifier
5250
.suggestion = remove safe from this item
5351
@@ -58,6 +56,16 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
5856
5957
ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect
6058
59+
ast_passes_c_variadic_associated_function = associated functions cannot have a C-variadic argument
60+
61+
ast_passes_c_variadic_bad_extern = only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic argument
62+
63+
ast_passes_c_variadic_closure = closures cannot have a C-variadic argument
64+
.help = only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic argument
65+
66+
ast_passes_c_variadic_missing_pattern = this C-variadic argument must have a pattern
67+
.suggestion = give the argument a name, or use `_` to ignore the argument
68+
6169
ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic
6270
.const = `const` because of this
6371
.variadic = C-variadic because of this

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -633,46 +633,62 @@ impl<'a> AstValidator<'a> {
633633
/// - Non-const
634634
/// - Either foreign, or free and `unsafe extern "C"` semantically
635635
fn check_c_variadic_type(&self, fk: FnKind<'a>) {
636-
let variadic_spans: Vec<_> = fk
637-
.decl()
638-
.inputs
639-
.iter()
640-
.filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs))
641-
.map(|arg| arg.span)
642-
.collect();
636+
let variadic_params: Vec<_> =
637+
fk.decl().inputs.iter().filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)).collect();
643638

644-
if variadic_spans.is_empty() {
639+
// The parser already rejects `...` if it's not the final argument, but we still want to
640+
// emit the errors below, so we only consider the final `...` here.
641+
let Some(variadic_param) = variadic_params.last() else {
645642
return;
643+
};
644+
645+
if let Some(FnHeader { constness: Const::Yes(const_span), .. }) = fk.header() {
646+
self.dcx().emit_err(errors::ConstAndCVariadic {
647+
span: const_span.to(variadic_param.span),
648+
const_span: *const_span,
649+
variadic_span: variadic_param.span,
650+
});
646651
}
647652

648-
if let Some(header) = fk.header() {
649-
if let Const::Yes(const_span) = header.constness {
650-
let mut spans = variadic_spans.clone();
651-
spans.push(const_span);
652-
self.dcx().emit_err(errors::ConstAndCVariadic {
653-
spans,
654-
const_span,
655-
variadic_spans: variadic_spans.clone(),
656-
});
653+
let (fn_ctxt, header) = match fk {
654+
FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) => (fn_ctxt, sig.header),
655+
FnKind::Closure(..) => {
656+
// Closures cannot be c-variadic (they can't even be `extern "C"`).
657+
self.dcx().emit_err(errors::CVariadicClosure { span: variadic_param.span });
658+
return;
657659
}
658-
}
660+
};
659661

660-
match (fk.ctxt(), fk.header()) {
661-
(Some(FnCtxt::Foreign), _) => return,
662-
(Some(FnCtxt::Free), Some(header)) => match header.ext {
663-
Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
664-
| Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _)
665-
| Extern::Implicit(_)
666-
if matches!(header.safety, Safety::Unsafe(_)) =>
667-
{
668-
return;
662+
match fn_ctxt {
663+
FnCtxt::Free => {
664+
let is_extern_c_abi = match header.ext {
665+
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
666+
[sym::C, sym::C_dash_unwind].contains(&symbol_unescaped)
667+
}
668+
Extern::Implicit(_) => true,
669+
Extern::None => false,
670+
};
671+
672+
if !is_extern_c_abi {
673+
self.dcx().emit_err(errors::CVariadicBadExtern { span: variadic_param.span });
669674
}
670-
_ => {}
671-
},
672-
_ => {}
673-
};
674675

675-
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
676+
// Reject `...` in function definitions, suggest `_: ...`.
677+
if let PatKind::Missing = variadic_param.pat.kind {
678+
self.dcx()
679+
.emit_err(errors::CVariadicMissingPattern { span: variadic_param.span });
680+
}
681+
}
682+
FnCtxt::Assoc(_) => {
683+
// For now, C-variadic arguments are unsupported in associated functions.
684+
self.dcx()
685+
.emit_err(errors::CVariadicAssociatedFunction { span: variadic_param.span });
686+
}
687+
FnCtxt::Foreign => {
688+
// Whether the ABI supports C-variadic arguments is checked later,
689+
// and `...` without a pattern is accepted on foreign functions.
690+
}
691+
}
676692
}
677693

678694
fn check_item_named(&self, ident: Ident, kind: &str) {

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,32 @@ pub(crate) struct ExternItemAscii {
309309
}
310310

311311
#[derive(Diagnostic)]
312-
#[diag(ast_passes_bad_c_variadic)]
313-
pub(crate) struct BadCVariadic {
312+
#[diag(ast_passes_c_variadic_associated_function)]
313+
pub(crate) struct CVariadicAssociatedFunction {
314314
#[primary_span]
315-
pub span: Vec<Span>,
315+
pub span: Span,
316+
}
317+
318+
#[derive(Diagnostic)]
319+
#[diag(ast_passes_c_variadic_closure)]
320+
pub(crate) struct CVariadicClosure {
321+
#[primary_span]
322+
pub span: Span,
323+
}
324+
325+
#[derive(Diagnostic)]
326+
#[diag(ast_passes_c_variadic_bad_extern)]
327+
pub(crate) struct CVariadicBadExtern {
328+
#[primary_span]
329+
pub span: Span,
330+
}
331+
332+
#[derive(Diagnostic)]
333+
#[diag(ast_passes_c_variadic_missing_pattern)]
334+
pub(crate) struct CVariadicMissingPattern {
335+
#[primary_span]
336+
#[suggestion(code = "_: ...", applicability = "machine-applicable", style = "verbose")]
337+
pub span: Span,
316338
}
317339

318340
#[derive(Diagnostic)]
@@ -663,11 +685,11 @@ pub(crate) struct ConstAndCoroutine {
663685
#[diag(ast_passes_const_and_c_variadic)]
664686
pub(crate) struct ConstAndCVariadic {
665687
#[primary_span]
666-
pub spans: Vec<Span>,
688+
pub span: Span,
667689
#[label(ast_passes_const)]
668690
pub const_span: Span,
669691
#[label(ast_passes_variadic)]
670-
pub variadic_spans: Vec<Span>,
692+
pub variadic_span: Span,
671693
}
672694

673695
#[derive(Diagnostic)]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn ordering4 < 'a , 'b > ( a : , self , self , self ,
88
//~| ERROR unexpected `self` parameter in function
99
//~| ERROR unexpected `self` parameter in function
1010
//~| ERROR unexpected `self` parameter in function
11-
self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
11+
self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
1212
//~^ ERROR unexpected `self` parameter in function
1313
//~| ERROR unexpected `self` parameter in function
1414
//~| ERROR unexpected `self` parameter in function

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,46 +25,46 @@ LL | fn ordering4 < 'a , 'b > ( a : , self , self , self ,
2525
error: unexpected `self` parameter in function
2626
--> $DIR/issue-86053-1.rs:11:5
2727
|
28-
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
28+
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
2929
| ^^^^ must be the first parameter of an associated function
3030

3131
error: unexpected `self` parameter in function
32-
--> $DIR/issue-86053-1.rs:11:20
32+
--> $DIR/issue-86053-1.rs:11:23
3333
|
34-
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
35-
| ^^^^ must be the first parameter of an associated function
34+
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
35+
| ^^^^ must be the first parameter of an associated function
3636

3737
error: unexpected `self` parameter in function
38-
--> $DIR/issue-86053-1.rs:11:29
38+
--> $DIR/issue-86053-1.rs:11:32
3939
|
40-
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
41-
| ^^^^ must be the first parameter of an associated function
40+
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
41+
| ^^^^ must be the first parameter of an associated function
4242

4343
error: `...` must be the last argument of a C-variadic function
4444
--> $DIR/issue-86053-1.rs:11:12
4545
|
46-
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
47-
| ^^^
46+
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
47+
| ^^^^^^
4848

49-
error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
50-
--> $DIR/issue-86053-1.rs:11:12
49+
error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic argument
50+
--> $DIR/issue-86053-1.rs:11:39
5151
|
52-
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
53-
| ^^^ ^^^
52+
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
53+
| ^^^^^^
5454

5555
error[E0412]: cannot find type `F` in this scope
56-
--> $DIR/issue-86053-1.rs:11:48
56+
--> $DIR/issue-86053-1.rs:11:54
5757
|
58-
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
59-
| ^
58+
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
59+
| ^
6060
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
6161
|
6262
= note: similarly named trait `Fn` defined here
6363
|
6464
help: a trait with a similar name exists
6565
|
66-
LL | self , ... , self , self , ... ) where Fn : FnOnce ( & 'a & 'b usize ) {
67-
| +
66+
LL | self , _: ... , self , self , _: ... ) where Fn : FnOnce ( & 'a & 'b usize ) {
67+
| +
6868
help: you might be missing a type parameter
6969
|
7070
LL | fn ordering4 < 'a , 'b, F > ( a : , self , self , self ,

tests/ui/c-variadic/issue-86053-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
trait H<T> {}
77

8-
unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), ...) {}
8+
unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), _: ...) {}
99
//~^ ERROR: in type `&'static &'a ()`, reference has a longer lifetime than the data it references [E0491]
1010

1111
fn main() {}

tests/ui/c-variadic/issue-86053-2.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
error[E0491]: in type `&'static &'a ()`, reference has a longer lifetime than the data it references
22
--> $DIR/issue-86053-2.rs:8:39
33
|
4-
LL | unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), ...) {}
4+
LL | unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), _: ...) {}
55
| ^^^^^^^^^^^^^^^^^^
66
|
77
= note: the pointer is valid for the static lifetime
88
note: but the referenced data is only valid for the lifetime `'a` as defined here
99
--> $DIR/issue-86053-2.rs:8:32
1010
|
11-
LL | unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), ...) {}
11+
LL | unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), _: ...) {}
1212
| ^^
1313

1414
error: aborting due to 1 previous error
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// A rust c-variadic function definition must use `_: ...`, not `...`.
2+
//@ run-rustfix
3+
#![feature(c_variadic)]
4+
#![crate_type = "lib"]
5+
6+
extern "C" {
7+
// A bare `...` is allowed in extern blocks.
8+
#[allow(unused)]
9+
fn foo(...);
10+
}
11+
12+
#[allow(unused)]
13+
unsafe extern "C" fn bar(_: ...) {}
14+
//~^ ERROR this C-variadic argument must have a pattern
15+
16+
#[allow(unused)]
17+
unsafe extern "C" fn baz(_: ...) {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// A rust c-variadic function definition must use `_: ...`, not `...`.
2+
//@ run-rustfix
3+
#![feature(c_variadic)]
4+
#![crate_type = "lib"]
5+
6+
extern "C" {
7+
// A bare `...` is allowed in extern blocks.
8+
#[allow(unused)]
9+
fn foo(...);
10+
}
11+
12+
#[allow(unused)]
13+
unsafe extern "C" fn bar(...) {}
14+
//~^ ERROR this C-variadic argument must have a pattern
15+
16+
#[allow(unused)]
17+
unsafe extern "C" fn baz(_: ...) {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: this C-variadic argument must have a pattern
2+
--> $DIR/must-have-pattern.rs:13:26
3+
|
4+
LL | unsafe extern "C" fn bar(...) {}
5+
| ^^^
6+
|
7+
help: give the argument a name, or use `_` to ignore the argument
8+
|
9+
LL | unsafe extern "C" fn bar(_: ...) {}
10+
| ++
11+
12+
error: aborting due to 1 previous error
13+

0 commit comments

Comments
 (0)