Skip to content

Commit 2483477

Browse files
committed
improve c-variadic errors
1 parent e4d33bc commit 2483477

21 files changed

+531
-161
lines changed

compiler/rustc_ast/src/ast.rs

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

22532253
self.span.shrink_to_lo()
22542254
}
2255+
2256+
/// The span of the header's safety, or where to insert it if empty.
2257+
pub fn safety_span(&self) -> Span {
2258+
match self.header.safety {
2259+
Safety::Unsafe(span) | Safety::Safe(span) => span,
2260+
Safety::Default => {
2261+
// Insert after the `coroutine_kind` if available.
2262+
if let Some(coroutine_kind) = self.header.coroutine_kind {
2263+
return coroutine_kind.span().shrink_to_hi();
2264+
}
2265+
2266+
// Insert after the `const` keyword if available.
2267+
if let Const::Yes(const_span) = self.header.constness {
2268+
return const_span.shrink_to_hi();
2269+
}
2270+
2271+
// Insert right at the front of the signature.
2272+
self.span.shrink_to_lo()
2273+
}
2274+
}
2275+
}
2276+
2277+
/// The span of the header's extern, or where to insert it if empty.
2278+
pub fn extern_span(&self) -> Span {
2279+
match self.header.ext {
2280+
Extern::Implicit(span) | Extern::Explicit(_, span) => span,
2281+
Extern::None => self.safety_span().shrink_to_hi(),
2282+
}
2283+
}
22552284
}
22562285

22572286
#[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: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -633,46 +633,73 @@ 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+
let FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) = fk else {
646+
// Unreachable because the parser already rejects `...` in closures.
647+
unreachable!("C variable argument list cannot be used in closures")
648+
};
649+
650+
// C-variadics are not yet implemented in const evaluation.
651+
if let Const::Yes(const_span) = sig.header.constness {
652+
self.dcx().emit_err(errors::ConstAndCVariadic {
653+
span: const_span.to(variadic_param.span),
654+
const_span,
655+
variadic_span: variadic_param.span,
656+
});
646657
}
647658

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-
});
657-
}
659+
if let Some(coroutine_kind) = sig.header.coroutine_kind {
660+
self.dcx().emit_err(errors::CoroutineAndCVariadic {
661+
span: coroutine_kind.span().to(variadic_param.span),
662+
coroutine_kind: coroutine_kind.as_str(),
663+
coroutine_span: coroutine_kind.span(),
664+
variadic_span: variadic_param.span,
665+
});
658666
}
659667

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;
669-
}
670-
_ => {}
671-
},
672-
_ => {}
673-
};
668+
match fn_ctxt {
669+
FnCtxt::Free => {
670+
match sig.header.ext {
671+
Extern::Implicit(_) => { /* defaults to "C" */ }
672+
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
673+
if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
674+
self.dcx().emit_err(errors::CVariadicBadExtern {
675+
span: variadic_param.span,
676+
abi: symbol_unescaped,
677+
extern_span: sig.extern_span(),
678+
});
679+
}
680+
}
681+
Extern::None => {
682+
self.dcx()
683+
.emit_err(errors::CVariadicNoExtern { span: variadic_param.span });
684+
}
685+
};
674686

675-
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
687+
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
688+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
689+
span: variadic_param.span,
690+
unsafe_span: sig.safety_span(),
691+
});
692+
}
693+
}
694+
FnCtxt::Assoc(_) => {
695+
// For now, C variable argument lists are unsupported in associated functions.
696+
self.dcx()
697+
.emit_err(errors::CVariadicAssociatedFunction { span: variadic_param.span });
698+
}
699+
FnCtxt::Foreign => {
700+
// Whether the ABI supports C variable argument lists is checked later.
701+
}
702+
}
676703
}
677704

678705
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)]
@@ -663,11 +695,23 @@ pub(crate) struct ConstAndCoroutine {
663695
#[diag(ast_passes_const_and_c_variadic)]
664696
pub(crate) struct ConstAndCVariadic {
665697
#[primary_span]
666-
pub spans: Vec<Span>,
698+
pub span: Span,
667699
#[label(ast_passes_const)]
668700
pub const_span: Span,
669701
#[label(ast_passes_variadic)]
670-
pub variadic_spans: Vec<Span>,
702+
pub variadic_span: Span,
703+
}
704+
705+
#[derive(Diagnostic)]
706+
#[diag(ast_passes_coroutine_and_c_variadic)]
707+
pub(crate) struct CoroutineAndCVariadic {
708+
#[primary_span]
709+
pub span: Span,
710+
pub coroutine_kind: &'static str,
711+
#[label(ast_passes_const)]
712+
pub coroutine_span: Span,
713+
#[label(ast_passes_variadic)]
714+
pub variadic_span: Span,
671715
}
672716

673717
#[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)