Skip to content

Commit f92ba67

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

19 files changed

+459
-161
lines changed

compiler/rustc_ast/src/ast.rs

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

22532253
self.span.shrink_to_lo()
22542254
}
2255+
2256+
/// The span of the current 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+
}
22552276
}
22562277

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

compiler/rustc_ast_passes/messages.ftl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,21 @@ 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 = only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C variable argument list
59+
60+
ast_passes_c_variadic_must_be_unsafe =
61+
functions with a C variable argument list must be unsafe
62+
.suggestion = add the `unsafe` keyword to this definition
63+
5864
ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic
5965
.const = `const` because of this
6066
.variadic = C-variadic because of this

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -633,46 +633,59 @@ 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;
646-
}
643+
};
647644

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-
}
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+
});
658657
}
659658

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;
659+
match fn_ctxt {
660+
FnCtxt::Free => {
661+
let is_extern_c_abi = match sig.header.ext {
662+
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
663+
matches!(symbol_unescaped, sym::C | sym::C_dash_unwind)
664+
}
665+
Extern::Implicit(_) => true,
666+
Extern::None => false,
667+
};
668+
669+
if !is_extern_c_abi {
670+
self.dcx().emit_err(errors::CVariadicBadExtern { span: variadic_param.span });
669671
}
670-
_ => {}
671-
},
672-
_ => {}
673-
};
674672

675-
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
673+
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
674+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
675+
span: variadic_param.span,
676+
unsafe_span: sig.safety_span(),
677+
});
678+
}
679+
}
680+
FnCtxt::Assoc(_) => {
681+
// For now, C variable argument lists are unsupported in associated functions.
682+
self.dcx()
683+
.emit_err(errors::CVariadicAssociatedFunction { span: variadic_param.span });
684+
}
685+
FnCtxt::Foreign => {
686+
// Whether the ABI supports C variable argument lists is checked later.
687+
}
688+
}
676689
}
677690

678691
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_bad_extern)]
320+
pub(crate) struct CVariadicBadExtern {
321+
#[primary_span]
322+
pub span: Span,
323+
}
324+
325+
#[derive(Diagnostic)]
326+
#[diag(ast_passes_c_variadic_must_be_unsafe)]
327+
pub(crate) struct CVariadicMustBeUnsafe {
328+
#[primary_span]
329+
pub span: Span,
330+
331+
#[suggestion(
332+
ast_passes_suggestion,
333+
applicability = "maybe-incorrect",
334+
code = "unsafe ",
335+
style = "verbose"
336+
)]
337+
pub unsafe_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: 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 only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C variable argument list
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: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C variable argument list
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)