Skip to content

Commit c30b51d

Browse files
committed
improve c-variadic errors
1 parent 801026e commit c30b51d

23 files changed

+549
-160
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,6 +2310,35 @@ impl FnSig {
23102310

23112311
self.span.shrink_to_lo()
23122312
}
2313+
2314+
/// The span of the header's safety, or where to insert it if empty.
2315+
pub fn safety_span(&self) -> Span {
2316+
match self.header.safety {
2317+
Safety::Unsafe(span) | Safety::Safe(span) => span,
2318+
Safety::Default => {
2319+
// Insert after the `coroutine_kind` if available.
2320+
if let Some(coroutine_kind) = self.header.coroutine_kind {
2321+
return coroutine_kind.span().shrink_to_hi();
2322+
}
2323+
2324+
// Insert after the `const` keyword if available.
2325+
if let Const::Yes(const_span) = self.header.constness {
2326+
return const_span.shrink_to_hi();
2327+
}
2328+
2329+
// Insert right at the front of the signature.
2330+
self.span.shrink_to_lo()
2331+
}
2332+
}
2333+
}
2334+
2335+
/// The span of the header's extern, or where to insert it if empty.
2336+
pub fn extern_span(&self) -> Span {
2337+
match self.header.ext {
2338+
Extern::Implicit(span) | Extern::Explicit(_, span) => span,
2339+
Extern::None => self.safety_span().shrink_to_hi(),
2340+
}
2341+
}
23132342
}
23142343

23152344
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]

compiler/rustc_ast_passes/messages.ftl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,25 @@ 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_body_in_extern = incorrect `{$kind}` inside `extern` block
5250
.cannot_have = cannot have a body
5351
.invalid = the invalid body
5452
.existing = `extern` blocks define existing foreign {$kind}s and {$kind}s inside of them cannot have a body
5553
5654
ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect
5755
56+
ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list
57+
58+
ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions
59+
.label = `extern "{$abi}"` because of this
60+
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
61+
62+
ast_passes_c_variadic_must_be_unsafe =
63+
functions with a C variable argument list must be unsafe
64+
.suggestion = add the `unsafe` keyword to this definition
65+
66+
ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions
67+
5868
ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic
5969
.const = `const` because of this
6070
.variadic = C-variadic because of this
@@ -73,6 +83,10 @@ ast_passes_const_without_body =
7383
ast_passes_constraint_on_negative_bound =
7484
associated type constraints not allowed on negative bounds
7585
86+
ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic
87+
.const = `{$coroutine_kind}` because of this
88+
.variadic = C-variadic because of this
89+
7690
ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses
7791
.label = not supported
7892
.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: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -628,46 +628,73 @@ impl<'a> AstValidator<'a> {
628628
/// - Non-const
629629
/// - Either foreign, or free and `unsafe extern "C"` semantically
630630
fn check_c_variadic_type(&self, fk: FnKind<'a>) {
631-
let variadic_spans: Vec<_> = fk
632-
.decl()
633-
.inputs
634-
.iter()
635-
.filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs))
636-
.map(|arg| arg.span)
637-
.collect();
631+
let variadic_params: Vec<_> =
632+
fk.decl().inputs.iter().filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)).collect();
638633

639-
if variadic_spans.is_empty() {
634+
// The parser already rejects `...` if it's not the final argument, but we still want to
635+
// emit the errors below, so we only consider the final `...` here.
636+
let Some(variadic_param) = variadic_params.last() else {
640637
return;
641-
}
638+
};
642639

643-
if let Some(header) = fk.header()
644-
&& let Const::Yes(const_span) = header.constness
645-
{
646-
let mut spans = variadic_spans.clone();
647-
spans.push(const_span);
640+
let FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) = fk else {
641+
// Unreachable because the parser already rejects `...` in closures.
642+
unreachable!("C variable argument list cannot be used in closures")
643+
};
644+
645+
// C-variadics are not yet implemented in const evaluation.
646+
if let Const::Yes(const_span) = sig.header.constness {
648647
self.dcx().emit_err(errors::ConstAndCVariadic {
649-
spans,
648+
span: const_span.to(variadic_param.span),
650649
const_span,
651-
variadic_spans: variadic_spans.clone(),
650+
variadic_span: variadic_param.span,
652651
});
653652
}
654653

655-
match (fk.ctxt(), fk.header()) {
656-
(Some(FnCtxt::Foreign), _) => return,
657-
(Some(FnCtxt::Free), Some(header)) => match header.ext {
658-
Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
659-
| Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _)
660-
| Extern::Implicit(_)
661-
if matches!(header.safety, Safety::Unsafe(_)) =>
662-
{
663-
return;
664-
}
665-
_ => {}
666-
},
667-
_ => {}
668-
};
654+
if let Some(coroutine_kind) = sig.header.coroutine_kind {
655+
self.dcx().emit_err(errors::CoroutineAndCVariadic {
656+
span: coroutine_kind.span().to(variadic_param.span),
657+
coroutine_kind: coroutine_kind.as_str(),
658+
coroutine_span: coroutine_kind.span(),
659+
variadic_span: variadic_param.span,
660+
});
661+
}
669662

670-
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
663+
match fn_ctxt {
664+
FnCtxt::Free => {
665+
match sig.header.ext {
666+
Extern::Implicit(_) => { /* defaults to "C" */ }
667+
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
668+
if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
669+
self.dcx().emit_err(errors::CVariadicBadExtern {
670+
span: variadic_param.span,
671+
abi: symbol_unescaped,
672+
extern_span: sig.extern_span(),
673+
});
674+
}
675+
}
676+
Extern::None => {
677+
self.dcx()
678+
.emit_err(errors::CVariadicNoExtern { span: variadic_param.span });
679+
}
680+
};
681+
682+
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
683+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
684+
span: variadic_param.span,
685+
unsafe_span: sig.safety_span(),
686+
});
687+
}
688+
}
689+
FnCtxt::Assoc(_) => {
690+
// For now, C variable argument lists are unsupported in associated functions.
691+
self.dcx()
692+
.emit_err(errors::CVariadicAssociatedFunction { span: variadic_param.span });
693+
}
694+
FnCtxt::Foreign => {
695+
// Whether the ABI supports C variable argument lists is checked later.
696+
}
697+
}
671698
}
672699

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

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,42 @@ 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_bad_extern)]
320+
pub(crate) struct CVariadicBadExtern {
321+
#[primary_span]
322+
pub span: Span,
323+
pub abi: Symbol,
324+
#[label]
325+
pub extern_span: Span,
326+
}
327+
328+
#[derive(Diagnostic)]
329+
#[diag(ast_passes_c_variadic_no_extern)]
330+
pub(crate) struct CVariadicNoExtern {
331+
#[primary_span]
332+
pub span: Span,
333+
}
334+
335+
#[derive(Diagnostic)]
336+
#[diag(ast_passes_c_variadic_must_be_unsafe)]
337+
pub(crate) struct CVariadicMustBeUnsafe {
338+
#[primary_span]
339+
pub span: Span,
340+
341+
#[suggestion(
342+
ast_passes_suggestion,
343+
applicability = "maybe-incorrect",
344+
code = "unsafe ",
345+
style = "verbose"
346+
)]
347+
pub unsafe_span: Span,
316348
}
317349

318350
#[derive(Diagnostic)]
@@ -647,11 +679,23 @@ pub(crate) struct ConstAndCoroutine {
647679
#[diag(ast_passes_const_and_c_variadic)]
648680
pub(crate) struct ConstAndCVariadic {
649681
#[primary_span]
650-
pub spans: Vec<Span>,
682+
pub span: Span,
651683
#[label(ast_passes_const)]
652684
pub const_span: Span,
653685
#[label(ast_passes_variadic)]
654-
pub variadic_spans: Vec<Span>,
686+
pub variadic_span: Span,
687+
}
688+
689+
#[derive(Diagnostic)]
690+
#[diag(ast_passes_coroutine_and_c_variadic)]
691+
pub(crate) struct CoroutineAndCVariadic {
692+
#[primary_span]
693+
pub span: Span,
694+
pub coroutine_kind: &'static str,
695+
#[label(ast_passes_const)]
696+
pub coroutine_span: Span,
697+
#[label(ast_passes_variadic)]
698+
pub variadic_span: Span,
655699
}
656700

657701
#[derive(Diagnostic)]

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ 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 only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
16+
//~| ERROR `...` is not supported for non-extern functions
17+
//~| ERROR: functions with a C variable argument list must be unsafe
1718
//~| ERROR cannot find type `F` in this scope
1819
}

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,22 @@ 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: 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: `...` is not supported for non-extern functions
50+
--> $DIR/issue-86053-1.rs:11:36
51+
|
52+
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
53+
| ^^^
54+
55+
error: functions with a C variable argument list must be unsafe
56+
--> $DIR/issue-86053-1.rs:11:36
5157
|
5258
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
53-
| ^^^ ^^^
59+
| ^^^
60+
|
61+
help: add the `unsafe` keyword to this definition
62+
|
63+
LL | unsafe fn ordering4 < 'a , 'b > ( a : , self , self , self ,
64+
| ++++++
5465

5566
error[E0412]: cannot find type `F` in this scope
5667
--> $DIR/issue-86053-1.rs:11:48
@@ -70,6 +81,6 @@ help: you might be missing a type parameter
7081
LL | fn ordering4 < 'a , 'b, F > ( a : , self , self , self ,
7182
| +++
7283

73-
error: aborting due to 10 previous errors
84+
error: aborting due to 11 previous errors
7485

7586
For more information about this error, try `rustc --explain E0412`.

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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// For now C-variadic arguments in associated functions are rejected. Especially when `self`
2+
// parameters are used there may be weird interactions with other compiler features. We may
3+
// relax this restriction in the future.
4+
#![feature(c_variadic)]
5+
#![crate_type = "lib"]
6+
struct S;
7+
8+
impl S {
9+
unsafe extern "C" fn associated_function(mut ap: ...) -> i32 {
10+
//~^ ERROR: associated functions cannot have a C variable argument list
11+
unsafe { ap.arg() }
12+
}
13+
14+
unsafe extern "C" fn method(&self, mut ap: ...) -> i32 {
15+
//~^ ERROR: associated functions cannot have a C variable argument list
16+
unsafe { ap.arg() }
17+
}
18+
}
19+
20+
trait T {
21+
unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 {
22+
//~^ ERROR: associated functions cannot have a C variable argument list
23+
unsafe { ap.arg() }
24+
}
25+
26+
unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 {
27+
//~^ ERROR: associated functions cannot have a C variable argument list
28+
unsafe { ap.arg() }
29+
}
30+
}
31+
32+
impl T for S {}
33+
34+
fn main() {
35+
unsafe {
36+
assert_eq!(S::associated_function(32), 32);
37+
assert_eq!(S.method(32), 32);
38+
39+
assert_eq!(S::trait_associated_function(32), 32);
40+
assert_eq!(S.trait_method(32), 32);
41+
}
42+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: associated functions cannot have a C variable argument list
2+
--> $DIR/no-associated-function.rs:9:46
3+
|
4+
LL | unsafe extern "C" fn associated_function(mut ap: ...) -> i32 {
5+
| ^^^^^^^^^^^
6+
7+
error: associated functions cannot have a C variable argument list
8+
--> $DIR/no-associated-function.rs:14:40
9+
|
10+
LL | unsafe extern "C" fn method(&self, mut ap: ...) -> i32 {
11+
| ^^^^^^^^^^^
12+
13+
error: associated functions cannot have a C variable argument list
14+
--> $DIR/no-associated-function.rs:21:52
15+
|
16+
LL | unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 {
17+
| ^^^^^^^^^^^
18+
19+
error: associated functions cannot have a C variable argument list
20+
--> $DIR/no-associated-function.rs:26:46
21+
|
22+
LL | unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 {
23+
| ^^^^^^^^^^^
24+
25+
error: aborting due to 4 previous errors
26+

0 commit comments

Comments
 (0)