Skip to content

Commit 68baa87

Browse files
committed
Auto merge of rust-lang#146165 - folkertdev:c-variadic-errors-take-2, r=lcnr
improve c-variadic error reporting tracking issue: rust-lang#44930 The parts of rust-lang#143546 that don't require any particular knowledge about c-variadic functions. This prepares the way for rejecting c-variadic functions that are also coroutines, safe functions, or associated functions.
2 parents beeb8e3 + 8d11719 commit 68baa87

File tree

8 files changed

+140
-94
lines changed

8 files changed

+140
-94
lines changed

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -665,46 +665,42 @@ 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+
// `...` is already rejected when it is not the final parameter.
669+
let variadic_param = match fk.decl().inputs.last() {
670+
Some(param) if matches!(param.ty.kind, TyKind::CVarArgs) => param,
671+
_ => return,
672+
};
675673

676-
if variadic_spans.is_empty() {
677-
return;
678-
}
674+
let FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) = fk else {
675+
// Unreachable because the parser already rejects `...` in closures.
676+
unreachable!("C variable argument list cannot be used in closures")
677+
};
679678

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);
679+
// C-variadics are not yet implemented in const evaluation.
680+
if let Const::Yes(const_span) = sig.header.constness {
685681
self.dcx().emit_err(errors::ConstAndCVariadic {
686-
spans,
682+
spans: vec![const_span, variadic_param.span],
687683
const_span,
688-
variadic_spans: variadic_spans.clone(),
684+
variadic_span: variadic_param.span,
689685
});
690686
}
691687

692-
match (fk.ctxt(), fk.header()) {
693-
(Some(FnCtxt::Foreign), _) => return,
694-
(Some(FnCtxt::Free), Some(header)) => match header.ext {
688+
match fn_ctxt {
689+
FnCtxt::Foreign => return,
690+
FnCtxt::Free => match sig.header.ext {
695691
Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
696692
| Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _)
697693
| Extern::Implicit(_)
698-
if matches!(header.safety, Safety::Unsafe(_)) =>
694+
if matches!(sig.header.safety, Safety::Unsafe(_)) =>
699695
{
700696
return;
701697
}
702698
_ => {}
703699
},
704-
_ => {}
700+
FnCtxt::Assoc(_) => {}
705701
};
706702

707-
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
703+
self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span });
708704
}
709705

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

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ pub(crate) struct ExternItemAscii {
322322
#[diag(ast_passes_bad_c_variadic)]
323323
pub(crate) struct BadCVariadic {
324324
#[primary_span]
325-
pub span: Vec<Span>,
325+
pub span: Span,
326326
}
327327

328328
#[derive(Diagnostic)]
@@ -656,7 +656,7 @@ pub(crate) struct ConstAndCVariadic {
656656
#[label(ast_passes_const)]
657657
pub const_span: Span,
658658
#[label(ast_passes_variadic)]
659-
pub variadic_spans: Vec<Span>,
659+
pub variadic_span: Span,
660660
}
661661

662662
#[derive(Diagnostic)]

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize
4747
| ^^^
4848

4949
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
50+
--> $DIR/issue-86053-1.rs:11:36
5151
|
5252
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
53-
| ^^^ ^^^
53+
| ^^^
5454

5555
error[E0412]: cannot find type `F` in this scope
5656
--> $DIR/issue-86053-1.rs:11:48

tests/ui/c-variadic/no-closure.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(c_variadic)]
2+
#![crate_type = "lib"]
3+
4+
// Check that `...` in closures is rejected.
5+
6+
const F: extern "C" fn(...) = |_: ...| {};
7+
//~^ ERROR C-variadic type `...` may not be nested inside another type
8+
9+
fn foo() {
10+
let f = |...| {};
11+
//~^ ERROR: `..` patterns are not allowed here
12+
//~| ERROR: unexpected `...`
13+
14+
let f = |_: ...| {};
15+
//~^ ERROR C-variadic type `...` may not be nested inside another type
16+
f(1i64)
17+
}

tests/ui/c-variadic/no-closure.stderr

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0743]: C-variadic type `...` may not be nested inside another type
2+
--> $DIR/no-closure.rs:6:35
3+
|
4+
LL | const F: extern "C" fn(...) = |_: ...| {};
5+
| ^^^
6+
7+
error: unexpected `...`
8+
--> $DIR/no-closure.rs:10:14
9+
|
10+
LL | let f = |...| {};
11+
| ^^^ not a valid pattern
12+
|
13+
help: for a rest pattern, use `..` instead of `...`
14+
|
15+
LL - let f = |...| {};
16+
LL + let f = |..| {};
17+
|
18+
19+
error[E0743]: C-variadic type `...` may not be nested inside another type
20+
--> $DIR/no-closure.rs:14:17
21+
|
22+
LL | let f = |_: ...| {};
23+
| ^^^
24+
25+
error: `..` patterns are not allowed here
26+
--> $DIR/no-closure.rs:10:14
27+
|
28+
LL | let f = |...| {};
29+
| ^^^
30+
|
31+
= note: only allowed in tuple, tuple struct, and slice patterns
32+
33+
error: aborting due to 4 previous errors
34+
35+
For more information about this error, try `rustc --explain E0743`.

tests/ui/c-variadic/valid.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ run-pass
2+
#![feature(c_variadic)]
3+
4+
// In rust (and C23 and above) `...` can be the only argument.
5+
unsafe extern "C" fn only_dot_dot_dot(mut ap: ...) -> i32 {
6+
unsafe { ap.arg() }
7+
}
8+
9+
unsafe extern "C-unwind" fn abi_c_unwind(mut ap: ...) -> i32 {
10+
unsafe { ap.arg() }
11+
}
12+
13+
#[allow(improper_ctypes_definitions)]
14+
unsafe extern "C" fn mix_int_float(mut ap: ...) -> (i64, f64, *const i32, f64) {
15+
(ap.arg(), ap.arg(), ap.arg(), ap.arg())
16+
}
17+
18+
fn main() {
19+
unsafe {
20+
assert_eq!(only_dot_dot_dot(32), 32);
21+
assert_eq!(abi_c_unwind(32), 32);
22+
23+
// Passing more arguments than expected is allowed.
24+
assert_eq!(only_dot_dot_dot(32, 1i64, core::ptr::null::<i32>(), 3.14f64), 32);
25+
26+
let ptr = &14i32 as *const i32;
27+
assert_eq!(mix_int_float(12i64, 13.0f64, ptr, 15.0f64), (12, 13.0, ptr, 15.0));
28+
}
29+
}

tests/ui/parser/variadic-ffi-semantic-restrictions.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ extern "C" fn f2_2(...) {}
1616
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
1717

1818
extern "C" fn f2_3(..., x: isize) {}
19-
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
20-
//~| ERROR `...` must be the last argument of a C-variadic function
19+
//~^ ERROR `...` must be the last argument of a C-variadic function
2120

2221
extern "C" fn f3_1(x: isize, ...) {}
2322
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
@@ -26,8 +25,7 @@ extern "C" fn f3_2(...) {}
2625
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
2726

2827
extern "C" fn f3_3(..., x: isize) {}
29-
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
30-
//~| ERROR `...` must be the last argument of a C-variadic function
28+
//~^ ERROR `...` must be the last argument of a C-variadic function
3129

3230
const unsafe extern "C" fn f4_1(x: isize, ...) {}
3331
//~^ ERROR functions cannot be both `const` and C-variadic
@@ -77,9 +75,7 @@ trait T {
7775
fn t_f4(...);
7876
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
7977
fn t_f5(..., x: isize) {}
80-
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
81-
//~| ERROR `...` must be the last argument of a C-variadic function
78+
//~^ ERROR `...` must be the last argument of a C-variadic function
8279
fn t_f6(..., x: isize);
83-
//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
84-
//~| ERROR `...` must be the last argument of a C-variadic function
80+
//~^ ERROR `...` must be the last argument of a C-variadic function
8581
}

0 commit comments

Comments
 (0)