Skip to content

Commit dc7aff4

Browse files
committed
improve c-variadic errors
1 parent e95e205 commit dc7aff4

25 files changed

+559
-170
lines changed

compiler/rustc_ast/src/ast.rs

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

23102310
self.span.shrink_to_lo()
23112311
}
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(coroutine_kind) = self.header.coroutine_kind {
2320+
return coroutine_kind.span().shrink_to_hi();
2321+
}
2322+
2323+
// Insert after the `const` keyword if available.
2324+
if let Const::Yes(const_span) = self.header.constness {
2325+
return const_span.shrink_to_hi();
2326+
}
2327+
2328+
// Insert right at the front of the signature.
2329+
self.span.shrink_to_lo()
2330+
}
2331+
}
2332+
}
2333+
2334+
/// The span of the header's extern, or where to insert it if empty.
2335+
pub fn extern_span(&self) -> Span {
2336+
match self.header.ext {
2337+
Extern::Implicit(span) | Extern::Explicit(_, span) => span,
2338+
Extern::None => self.safety_span().shrink_to_hi(),
2339+
}
2340+
}
23122341
}
23132342

23142343
/// A constraint on an associated item.

compiler/rustc_ast_passes/messages.ftl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,25 @@ 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+
6979
ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic
7080
.const = `const` because of this
7181
.variadic = C-variadic because of this
@@ -84,6 +94,10 @@ ast_passes_const_without_body =
8494
ast_passes_constraint_on_negative_bound =
8595
associated type constraints not allowed on negative bounds
8696
97+
ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic
98+
.const = `{$coroutine_kind}` because of this
99+
.variadic = C-variadic because of this
100+
87101
ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses
88102
.label = not supported
89103
.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
@@ -665,46 +665,73 @@ impl<'a> AstValidator<'a> {
665665
/// - Non-const
666666
/// - Either foreign, or free and `unsafe extern "C"` semantically
667667
fn check_c_variadic_type(&self, fk: FnKind<'a>) {
668-
let variadic_spans: Vec<_> = fk
669-
.decl()
670-
.inputs
671-
.iter()
672-
.filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs))
673-
.map(|arg| arg.span)
674-
.collect();
668+
let variadic_params: Vec<_> =
669+
fk.decl().inputs.iter().filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)).collect();
675670

676-
if variadic_spans.is_empty() {
671+
// The parser already rejects `...` if it's not the final argument, but we still want to
672+
// emit the errors below, so we only consider the final `...` here.
673+
let Some(variadic_param) = variadic_params.last() else {
677674
return;
678-
}
675+
};
679676

680-
if let Some(header) = fk.header()
681-
&& let Const::Yes(const_span) = header.constness
682-
{
683-
let mut spans = variadic_spans.clone();
684-
spans.push(const_span);
677+
let FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) = fk else {
678+
// Unreachable because the parser already rejects `...` in closures.
679+
unreachable!("C variable argument list cannot be used in closures")
680+
};
681+
682+
// C-variadics are not yet implemented in const evaluation.
683+
if let Const::Yes(const_span) = sig.header.constness {
685684
self.dcx().emit_err(errors::ConstAndCVariadic {
686-
spans,
685+
span: const_span.to(variadic_param.span),
687686
const_span,
688-
variadic_spans: variadic_spans.clone(),
687+
variadic_span: variadic_param.span,
689688
});
690689
}
691690

692-
match (fk.ctxt(), fk.header()) {
693-
(Some(FnCtxt::Foreign), _) => return,
694-
(Some(FnCtxt::Free), Some(header)) => match header.ext {
695-
Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
696-
| Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _)
697-
| Extern::Implicit(_)
698-
if matches!(header.safety, Safety::Unsafe(_)) =>
699-
{
700-
return;
701-
}
702-
_ => {}
703-
},
704-
_ => {}
705-
};
691+
if let Some(coroutine_kind) = sig.header.coroutine_kind {
692+
self.dcx().emit_err(errors::CoroutineAndCVariadic {
693+
span: coroutine_kind.span().to(variadic_param.span),
694+
coroutine_kind: coroutine_kind.as_str(),
695+
coroutine_span: coroutine_kind.span(),
696+
variadic_span: variadic_param.span,
697+
});
698+
}
706699

707-
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
700+
match fn_ctxt {
701+
FnCtxt::Free => {
702+
match sig.header.ext {
703+
Extern::Implicit(_) => { /* defaults to "C" */ }
704+
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
705+
if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
706+
self.dcx().emit_err(errors::CVariadicBadExtern {
707+
span: variadic_param.span,
708+
abi: symbol_unescaped,
709+
extern_span: sig.extern_span(),
710+
});
711+
}
712+
}
713+
Extern::None => {
714+
self.dcx()
715+
.emit_err(errors::CVariadicNoExtern { span: variadic_param.span });
716+
}
717+
};
718+
719+
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
720+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
721+
span: variadic_param.span,
722+
unsafe_span: sig.safety_span(),
723+
});
724+
}
725+
}
726+
FnCtxt::Assoc(_) => {
727+
// For now, C variable argument lists are unsupported in associated functions.
728+
self.dcx()
729+
.emit_err(errors::CVariadicAssociatedFunction { span: variadic_param.span });
730+
}
731+
FnCtxt::Foreign => {
732+
// Whether the ABI supports C variable argument lists is checked later.
733+
}
734+
}
708735
}
709736

710737
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
@@ -319,10 +319,42 @@ 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]
325-
pub span: Vec<Span>,
325+
pub span: Span,
326+
}
327+
328+
#[derive(Diagnostic)]
329+
#[diag(ast_passes_c_variadic_bad_extern)]
330+
pub(crate) struct CVariadicBadExtern {
331+
#[primary_span]
332+
pub span: Span,
333+
pub abi: Symbol,
334+
#[label]
335+
pub extern_span: Span,
336+
}
337+
338+
#[derive(Diagnostic)]
339+
#[diag(ast_passes_c_variadic_no_extern)]
340+
pub(crate) struct CVariadicNoExtern {
341+
#[primary_span]
342+
pub span: Span,
343+
}
344+
345+
#[derive(Diagnostic)]
346+
#[diag(ast_passes_c_variadic_must_be_unsafe)]
347+
pub(crate) struct CVariadicMustBeUnsafe {
348+
#[primary_span]
349+
pub span: Span,
350+
351+
#[suggestion(
352+
ast_passes_suggestion,
353+
applicability = "maybe-incorrect",
354+
code = "unsafe ",
355+
style = "verbose"
356+
)]
357+
pub unsafe_span: Span,
326358
}
327359

328360
#[derive(Diagnostic)]
@@ -652,11 +684,23 @@ pub(crate) struct ConstAndCoroutine {
652684
#[diag(ast_passes_const_and_c_variadic)]
653685
pub(crate) struct ConstAndCVariadic {
654686
#[primary_span]
655-
pub spans: Vec<Span>,
687+
pub span: Span,
656688
#[label(ast_passes_const)]
657689
pub const_span: Span,
658690
#[label(ast_passes_variadic)]
659-
pub variadic_spans: Vec<Span>,
691+
pub variadic_span: Span,
692+
}
693+
694+
#[derive(Diagnostic)]
695+
#[diag(ast_passes_coroutine_and_c_variadic)]
696+
pub(crate) struct CoroutineAndCVariadic {
697+
#[primary_span]
698+
pub span: Span,
699+
pub coroutine_kind: &'static str,
700+
#[label(ast_passes_const)]
701+
pub coroutine_span: Span,
702+
#[label(ast_passes_variadic)]
703+
pub variadic_span: Span,
660704
}
661705

662706
#[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 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
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: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
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)