Skip to content

Commit 31015cd

Browse files
committed
Ban splat on closures and rust-call, add extra tests
1 parent 9b891ae commit 31015cd

6 files changed

Lines changed: 143 additions & 18 deletions

File tree

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ enum SelfSemantic {
4545
No,
4646
}
4747

48+
/// Is `#[splat]` allowed semantically on a parameter of a `FnDecl`?
49+
enum SplatSemantic {
50+
Yes,
51+
NoClosures(Span),
52+
NoRustCall(Span),
53+
}
54+
4855
enum TraitOrImpl {
4956
Trait { vis: Span, constness: Const },
5057
TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span },
@@ -350,10 +357,15 @@ impl<'a> AstValidator<'a> {
350357
});
351358
}
352359

353-
fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
360+
fn check_fn_decl(
361+
&self,
362+
fn_decl: &FnDecl,
363+
self_semantic: SelfSemantic,
364+
splat_semantic: SplatSemantic,
365+
) {
354366
self.check_decl_num_args(fn_decl);
355367
let c_variadic_span = self.check_decl_cvariadic_pos(fn_decl);
356-
self.check_decl_splatting(fn_decl, c_variadic_span);
368+
self.check_decl_splatting(fn_decl, c_variadic_span, splat_semantic);
357369
self.check_decl_attrs(fn_decl);
358370
self.check_decl_self_param(fn_decl, self_semantic);
359371
}
@@ -399,8 +411,13 @@ impl<'a> AstValidator<'a> {
399411
/// Emits an error if a function declaration has more than one splatted argument, with a
400412
/// C-variadic parameter, or a splat at an unsupported index (for performance).
401413
/// Example: `fn foo(#[splat] x: (), #[splat] y: ())` will emit an error.
402-
fn check_decl_splatting(&self, fn_decl: &FnDecl, c_variadic_span: Option<Span>) {
403-
let (splatted_arg_indexes, mut splatted_spans): (Vec<u16>, Vec<Span>) = fn_decl
414+
fn check_decl_splatting(
415+
&self,
416+
fn_decl: &FnDecl,
417+
c_variadic_span: Option<Span>,
418+
splat_semantic: SplatSemantic,
419+
) {
420+
let (splatted_arg_indexes, splatted_spans): (Vec<u16>, Vec<Span>) = fn_decl
404421
.inputs
405422
.iter()
406423
.enumerate()
@@ -433,9 +450,28 @@ impl<'a> AstValidator<'a> {
433450
if let Some(c_variadic_span) = c_variadic_span
434451
&& !splatted_spans.is_empty()
435452
{
453+
let mut splatted_spans = splatted_spans.clone();
436454
splatted_spans.push(c_variadic_span);
437455
self.dcx().emit_err(diagnostics::CVarArgsAndSplat { spans: splatted_spans });
438456
}
457+
458+
if !splatted_arg_indexes.is_empty() {
459+
match splat_semantic {
460+
SplatSemantic::NoClosures(closure_span) => {
461+
let mut splatted_spans = splatted_spans.clone();
462+
splatted_spans.push(closure_span);
463+
self.dcx()
464+
.emit_err(diagnostics::SplatNotAllowedOnClosures { spans: splatted_spans });
465+
}
466+
SplatSemantic::NoRustCall(abi_span) => {
467+
let mut splatted_spans = splatted_spans;
468+
splatted_spans.push(abi_span);
469+
self.dcx()
470+
.emit_err(diagnostics::SplatNotAllowedOnRustCall { spans: splatted_spans });
471+
}
472+
SplatSemantic::Yes => {}
473+
}
474+
}
439475
}
440476

441477
fn check_decl_attrs(&self, fn_decl: &FnDecl) {
@@ -1055,7 +1091,7 @@ impl<'a> AstValidator<'a> {
10551091
match &ty.kind {
10561092
TyKind::FnPtr(bfty) => {
10571093
self.check_fn_ptr_safety(bfty.decl_span, bfty.safety);
1058-
self.check_fn_decl(&bfty.decl, SelfSemantic::No);
1094+
self.check_fn_decl(&bfty.decl, SelfSemantic::No, SplatSemantic::Yes);
10591095
Self::check_decl_no_pat(&bfty.decl, |span, _, _| {
10601096
self.dcx().emit_err(diagnostics::PatternFnPointer { span });
10611097
});
@@ -1746,7 +1782,24 @@ impl Visitor<'_> for AstValidator<'_> {
17461782
Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes,
17471783
_ => SelfSemantic::No,
17481784
};
1749-
self.check_fn_decl(fk.decl(), self_semantic);
1785+
let splat_semantic = match fk {
1786+
FnKind::Fn(_, _, _) => match fk.header().unwrap().ext {
1787+
Extern::None => SplatSemantic::Yes,
1788+
// FIXME(splat): should splatting extern "C" or other ABIs be allowed?
1789+
Extern::Implicit(_) => SplatSemantic::Yes,
1790+
// For now, splatting rust-call is banned, because it already de-tuples args.
1791+
Extern::Explicit(abi_str, span)
1792+
if abi_str.symbol_unescaped.as_str() == "rust-call" =>
1793+
{
1794+
SplatSemantic::NoRustCall(span)
1795+
}
1796+
Extern::Explicit(_abi_str, _span) => SplatSemantic::Yes,
1797+
},
1798+
// Splatting closures is banned, because closure arguments are already de-tupled.
1799+
FnKind::Closure(_, _, _, expr) => SplatSemantic::NoClosures(expr.span),
1800+
};
1801+
1802+
self.check_fn_decl(fk.decl(), self_semantic, splat_semantic);
17501803

17511804
if let Some(&FnHeader { safety, .. }) = fk.header() {
17521805
self.check_item_safety(span, safety);

compiler/rustc_ast_passes/src/diagnostics.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,22 @@ pub(crate) struct CVarArgsAndSplat {
150150
pub spans: Vec<Span>,
151151
}
152152

153+
#[derive(Diagnostic)]
154+
#[diag("`#[splat]` is not allowed on closures")]
155+
#[help("remove `#[splat]` or turn the closure into a function")]
156+
pub(crate) struct SplatNotAllowedOnClosures {
157+
#[primary_span]
158+
pub spans: Vec<Span>,
159+
}
160+
161+
#[derive(Diagnostic)]
162+
#[diag("`#[splat]` is not allowed on functions with the `rust-call` ABI")]
163+
#[help("remove `#[splat]` or change the ABI")]
164+
pub(crate) struct SplatNotAllowedOnRustCall {
165+
#[primary_span]
166+
pub spans: Vec<Span>,
167+
}
168+
153169
#[derive(Diagnostic)]
154170
#[diag("documentation comments cannot be applied to function parameters")]
155171
pub(crate) struct FnParamDocComment {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//! Checks that closures and rust-call functions can't be splatted.
2+
//! This should be rejected until we decide on sensible semantics.
3+
4+
#![feature(splat, unboxed_closures, tuple_trait)]
5+
#![expect(incomplete_features)]
6+
7+
use std::marker::Tuple;
8+
9+
trait Trait: Tuple + Sized {
10+
extern "rust-call" fn method(#[splat] self: Self);
11+
//~^ ERROR: `#[splat]` is not allowed on functions with the `rust-call` ABI
12+
}
13+
14+
impl Trait for (i32, i64) {
15+
extern "rust-call" fn method(#[splat] self: Self) {
16+
//~^ ERROR: `#[splat]` is not allowed on functions with the `rust-call` ABI
17+
println!("{self:?}");
18+
}
19+
}
20+
21+
fn main() {
22+
(|#[splat] x: i32| {
23+
//~^ ERROR `#[splat]` is not allowed on closures
24+
println!("{x}");
25+
})(1);
26+
27+
(1_i32, 2_i64).method();
28+
Trait::method(3_i32, 4_i64);
29+
//~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
30+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error: `#[splat]` is not allowed on functions with the `rust-call` ABI
2+
--> $DIR/reject-closure-issue-158605.rs:10:5
3+
|
4+
LL | extern "rust-call" fn method(#[splat] self: Self);
5+
| ^^^^^^^^^^^^^^^^^^ ^^^^
6+
|
7+
= help: remove `#[splat]` or change the ABI
8+
9+
error: `#[splat]` is not allowed on functions with the `rust-call` ABI
10+
--> $DIR/reject-closure-issue-158605.rs:15:5
11+
|
12+
LL | extern "rust-call" fn method(#[splat] self: Self) {
13+
| ^^^^^^^^^^^^^^^^^^ ^^^^
14+
|
15+
= help: remove `#[splat]` or change the ABI
16+
17+
error: `#[splat]` is not allowed on closures
18+
--> $DIR/reject-closure-issue-158605.rs:22:7
19+
|
20+
LL | (|#[splat] x: i32| {
21+
| _______^^^^^^^^^^^^^^^__^
22+
LL | |
23+
LL | | println!("{x}");
24+
LL | | })(1);
25+
| |_____^
26+
|
27+
= help: remove `#[splat]` or turn the closure into a function
28+
29+
error: functions with the "rust-call" ABI must take a single non-self tuple argument
30+
--> $DIR/reject-closure-issue-158605.rs:28:26
31+
|
32+
LL | Trait::method(3_i32, 4_i64);
33+
| ^^^^^
34+
35+
error: aborting due to 4 previous errors
36+

tests/ui/splat/splat-fn-ptr-cast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
fn main() {
99
// Bug #158603 regression test variants
1010
#[rustfmt::skip]
11-
let _x: fn(#[splat] (i32,)) = None.unwrap();
11+
let _x: fn(#[splat] (f32,)) = None.unwrap();
1212
// FIXME(splat): causes an ICE until #158603 is fixed
13-
//x(1);
13+
//x(1.0);
1414

1515
let x: fn((i32,)) = None.unwrap();
1616
x((1,));

tests/ui/splat/splat-fn-ptr-cast.stderr

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)