From 1fb14f8f673037cf94f8690d023da69f7bb9ad70 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 1 Jul 2026 13:01:02 +1000 Subject: [PATCH 1/4] Add extra dyn non-tuple trait splat tests --- tests/ui/splat/splat-dyn-asref-tuple-fail.rs | 29 +++++++++++- .../splat/splat-dyn-asref-tuple-fail.stderr | 46 +++++++++++++++---- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.rs b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs index 40d702346b7a3..d9b3b0351281c 100644 --- a/tests/ui/splat/splat-dyn-asref-tuple-fail.rs +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs @@ -4,11 +4,36 @@ #![feature(splat)] #![feature(tuple_trait)] -fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} -//~^ ERROR cannot use splat attribute +// Strip binders and their lifetime numbers from error messages +//@ normalize-stderr: "&.*value: (.*), bound_vars: .*" -> "$1" + +// FIXME(splat): Some errors are reported on the callee, but they would be more ergonomic on the +// caller as well +fn dyn_asref_splat(#[splat] _t: &dyn AsRef) +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +where + T: std::marker::Tuple, +{ +} fn main() { + // These error patterns are reported on the function definition, but we can't check for two + // strings in the same error message let s: String = "hello".to_owned(); dyn_asref_splat::(&s); //~^ ERROR `String` is not a tuple + //@regex-error-pattern: type must be a tuple or unit, not a .* Trait\(.*AsRef<.*String>\) + + dyn_asref_splat(&s); + //@regex-error-pattern: type must be a tuple or unit, not a .* Trait\(.*AsRef<_>\) + + let t = (1u8, 2f32); + dyn_asref_splat::<(u8, f32)>(&t); + //@regex-error-pattern: type must be a tuple or unit, not a .* Trait\(.*AsRef<\(u8, f32\)>\) + + dyn_asref_splat(&t); + //@regex-error-pattern: type must be a tuple or unit, not a .* Trait\(.*AsRef<_>\) } diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr index cba1ab28181a4..e9388eee63de0 100644 --- a/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr @@ -1,24 +1,54 @@ -error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a &'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3 (&'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3) - --> $DIR/splat-dyn-asref-tuple-fail.rs:7:36 +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Trait(std::convert::AsRef) + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:36 | -LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) | ^^^^^^^^^^^^^ ... LL | dyn_asref_splat::(&s); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: `String` is not a tuple - --> $DIR/splat-dyn-asref-tuple-fail.rs:12:23 + --> $DIR/splat-dyn-asref-tuple-fail.rs:26:23 | LL | dyn_asref_splat::(&s); | ^^^^^^ the nightly-only, unstable trait `std::marker::Tuple` is not implemented for `String` | note: required by a bound in `dyn_asref_splat` - --> $DIR/splat-dyn-asref-tuple-fail.rs:7:60 + --> $DIR/splat-dyn-asref-tuple-fail.rs:18:8 | -LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} - | ^^^^^^^^^^^^^^^^^^ required by this bound in `dyn_asref_splat` +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) + | --------------- required by a bound in this function +... +LL | T: std::marker::Tuple, + | ^^^^^^^^^^^^^^^^^^ required by this bound in `dyn_asref_splat` + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Trait(std::convert::AsRef<_>) + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:36 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) + | ^^^^^^^^^^^^^ +... +LL | dyn_asref_splat(&s); + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Trait(std::convert::AsRef<(u8, f32)>) + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:36 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) + | ^^^^^^^^^^^^^ +... +LL | dyn_asref_splat::<(u8, f32)>(&t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Trait(std::convert::AsRef<_>) + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:36 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) + | ^^^^^^^^^^^^^ +... +LL | dyn_asref_splat(&t); + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0277`. From 941bc65fc3c9e903b440d9e468da7a8d01a24e70 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 1 Jul 2026 18:23:18 +1000 Subject: [PATCH 2/4] Add a splat codegen run test --- tests/ui/splat/run-splat.rs | 37 +++++++++++++++++++++++++++++ tests/ui/splat/run-splat.run.stderr | 3 +++ 2 files changed, 40 insertions(+) create mode 100644 tests/ui/splat/run-splat.rs create mode 100644 tests/ui/splat/run-splat.run.stderr diff --git a/tests/ui/splat/run-splat.rs b/tests/ui/splat/run-splat.rs new file mode 100644 index 0000000000000..06360883d5a43 --- /dev/null +++ b/tests/ui/splat/run-splat.rs @@ -0,0 +1,37 @@ +//! Check that splat codegen works for simple cases. +//@ run-pass +//@ check-run-results +#![feature(splat, tuple_trait)] +#![expect(incomplete_features)] + +use std::marker::Tuple; + +struct Foo; + +trait MethodArgs: Tuple { + fn call_method(self, this: &Foo); +} + +impl Foo { + fn method(&self, #[splat] args: impl MethodArgs) { + args.call_method(self) + } +} + +impl MethodArgs for (i32, String) { + fn call_method(self, _this: &Foo) { + dbg!(self.1, self.0); + } +} + +impl MethodArgs for (f64,) { + fn call_method(self, _this: &Foo) { + dbg!(self.0); + } +} + +fn main() { + let foo = Foo; + foo.method(42, "hello splat".to_string()); + foo.method(3.141); +} diff --git a/tests/ui/splat/run-splat.run.stderr b/tests/ui/splat/run-splat.run.stderr new file mode 100644 index 0000000000000..337f5f01e6029 --- /dev/null +++ b/tests/ui/splat/run-splat.run.stderr @@ -0,0 +1,3 @@ +[$DIR/run-splat.rs:23:9] self.1 = "hello splat" +[$DIR/run-splat.rs:23:9] self.0 = 42 +[$DIR/run-splat.rs:29:9] self.0 = 3.141 From 26fdc4fb8769aa2b713c7bba2fd0c5c008308e04 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 2 Jul 2026 11:50:08 +1000 Subject: [PATCH 3/4] Add tests for invalid splatted casts --- tests/ui/splat/splat-fn-ptr-cast-fail.rs | 25 ++++++++ tests/ui/splat/splat-fn-ptr-cast-fail.stderr | 60 +++++++++++++++++++ tests/ui/splat/splat-fn-tuple-generic-fail.rs | 8 ++- .../splat/splat-fn-tuple-generic-fail.stderr | 28 ++++++--- 4 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 tests/ui/splat/splat-fn-ptr-cast-fail.rs create mode 100644 tests/ui/splat/splat-fn-ptr-cast-fail.stderr diff --git a/tests/ui/splat/splat-fn-ptr-cast-fail.rs b/tests/ui/splat/splat-fn-ptr-cast-fail.rs new file mode 100644 index 0000000000000..1cd1bdacf582c --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-cast-fail.rs @@ -0,0 +1,25 @@ +//! Test casting splatted functions to non-splatted function pointers fails. + +#![allow(incomplete_features)] +#![feature(splat, tuple_trait)] + +use std::marker::Tuple; + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn f(#[splat] args: Args) {} + +fn main() { + // Function pointers + let _fn_ptr: fn((u32, i8)) = tuple_args; //~ ERROR mismatched types + let _fn_ptr: fn((u32, i8), f64) = splat_non_terminal_arg; //~ ERROR mismatched types + + let _fn_ptr: fn((u32, i8)) = tuple_args as fn((u32, i8)); //~ ERROR non-primitive cast + let _fn_ptr: fn((u32, i8), f64) = splat_non_terminal_arg as fn((u32, i8), f64); //~ ERROR non-primitive cast + + // Bug #158603 regression test variants + const _F2: fn((u8, u32)) = f::<(u8, u32)>; //~ ERROR mismatched types + const _F1: fn(((u8, u32),)) = f::<((u8, u32),)>; //~ ERROR mismatched types +} diff --git a/tests/ui/splat/splat-fn-ptr-cast-fail.stderr b/tests/ui/splat/splat-fn-ptr-cast-fail.stderr new file mode 100644 index 0000000000000..93d7c8493048d --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-cast-fail.stderr @@ -0,0 +1,60 @@ +error[E0308]: mismatched types + --> $DIR/splat-fn-ptr-cast-fail.rs:16:34 + | +LL | let _fn_ptr: fn((u32, i8)) = tuple_args; + | ------------- ^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | | + | expected due to this + | + = note: expected fn pointer `fn((_, _))` + found fn item `fn(#[splat] (_, _)) {tuple_args}` + +error[E0308]: mismatched types + --> $DIR/splat-fn-ptr-cast-fail.rs:17:39 + | +LL | let _fn_ptr: fn((u32, i8), f64) = splat_non_terminal_arg; + | ------------------ ^^^^^^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | | + | expected due to this + | + = note: expected fn pointer `fn((_, _), _)` + found fn item `fn(#[splat] (_, _), _) {splat_non_terminal_arg}` + +error[E0605]: non-primitive cast: `fn(, #[splat](u32, i8)) {tuple_args}` as `fn((u32, i8))` + --> $DIR/splat-fn-ptr-cast-fail.rs:19:34 + | +LL | let _fn_ptr: fn((u32, i8)) = tuple_args as fn((u32, i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + +error[E0605]: non-primitive cast: `fn(, #[splat](u32, i8), f64) {splat_non_terminal_arg}` as `fn((u32, i8), f64)` + --> $DIR/splat-fn-ptr-cast-fail.rs:20:39 + | +LL | let _fn_ptr: fn((u32, i8), f64) = splat_non_terminal_arg as fn((u32, i8), f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + +error[E0308]: mismatched types + --> $DIR/splat-fn-ptr-cast-fail.rs:23:32 + | +LL | const _F2: fn((u8, u32)) = f::<(u8, u32)>; + | ------------- ^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | | + | expected because of the type of the constant + | + = note: expected fn pointer `fn((_, _))` + found fn item `fn(#[splat] (_, _)) {f::<(u8, u32)>}` + +error[E0308]: mismatched types + --> $DIR/splat-fn-ptr-cast-fail.rs:24:35 + | +LL | const _F1: fn(((u8, u32),)) = f::<((u8, u32),)>; + | ---------------- ^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | | + | expected because of the type of the constant + | + = note: expected fn pointer `fn(((_, _),))` + found fn item `fn(#[splat] ((_, _),)) {f::<((u8, u32),)>}` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.rs b/tests/ui/splat/splat-fn-tuple-generic-fail.rs index 8a8651e75c9ff..17e8e46db0672 100644 --- a/tests/ui/splat/splat-fn-tuple-generic-fail.rs +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.rs @@ -4,7 +4,11 @@ #![feature(splat)] #![feature(tuple_trait)] -fn splat_generic_tuple(#[splat] _t: T) {} +use std::marker::Tuple; + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn f(#[splat] args: Args) {} fn main() { // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? @@ -24,4 +28,6 @@ fn main() { splat_generic_tuple::<(u32, i8)>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + const F1: fn((u8, u32)) = f::<(u8, u32)>; //~ ERROR mismatched types } diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.stderr b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr index 7fd4a0719b493..c4fc6aa1d56c1 100644 --- a/tests/ui/splat/splat-fn-tuple-generic-fail.stderr +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr @@ -1,39 +1,51 @@ error[E0057]: this splatted function takes 2 arguments, but 1 was provided - --> $DIR/splat-fn-tuple-generic-fail.rs:19:5 + --> $DIR/splat-fn-tuple-generic-fail.rs:23:5 | LL | splat_generic_tuple::<(((u32, i8)))>((1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0057]: this splatted function takes 2 arguments, but 1 was provided - --> $DIR/splat-fn-tuple-generic-fail.rs:20:5 + --> $DIR/splat-fn-tuple-generic-fail.rs:24:5 | LL | splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0057]: this splatted function takes 2 arguments, but 1 was provided - --> $DIR/splat-fn-tuple-generic-fail.rs:22:5 + --> $DIR/splat-fn-tuple-generic-fail.rs:26:5 | LL | splat_generic_tuple::<((u32, i8))>((1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0057]: this splatted function takes 2 arguments, but 1 was provided - --> $DIR/splat-fn-tuple-generic-fail.rs:23:5 + --> $DIR/splat-fn-tuple-generic-fail.rs:27:5 | LL | splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0057]: this splatted function takes 2 arguments, but 1 was provided - --> $DIR/splat-fn-tuple-generic-fail.rs:25:5 + --> $DIR/splat-fn-tuple-generic-fail.rs:29:5 | LL | splat_generic_tuple::<(u32, i8)>((1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0057]: this splatted function takes 2 arguments, but 1 was provided - --> $DIR/splat-fn-tuple-generic-fail.rs:26:5 + --> $DIR/splat-fn-tuple-generic-fail.rs:30:5 | LL | splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/splat-fn-tuple-generic-fail.rs:32:31 + | +LL | const F1: fn((u8, u32)) = f::<(u8, u32)>; + | ------------- ^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | | + | expected because of the type of the constant + | + = note: expected fn pointer `fn((_, _))` + found fn item `fn(#[splat] (_, _)) {f::<(u8, u32)>}` + +error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0057`. +Some errors have detailed explanations: E0057, E0308. +For more information about an error, try `rustc --explain E0057`. From 759248517e05b761277c8f8a798c137519d832fe Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 2 Jul 2026 12:00:28 +1000 Subject: [PATCH 4/4] Add tests for splatted self --- tests/ui/splat/splat-self.rs | 21 +++++++++++++++++++++ tests/ui/splat/splat-self.run.stdout | 2 ++ 2 files changed, 23 insertions(+) create mode 100644 tests/ui/splat/splat-self.rs create mode 100644 tests/ui/splat/splat-self.run.stdout diff --git a/tests/ui/splat/splat-self.rs b/tests/ui/splat/splat-self.rs new file mode 100644 index 0000000000000..b826a5b30b9bc --- /dev/null +++ b/tests/ui/splat/splat-self.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ check-run-results +//! Test using `#[splat]` on self arguments of trait methods. + +#![feature(splat)] +#![expect(incomplete_features)] + +trait Trait { + fn method(#[splat] self: Self); +} + +impl Trait for (i32, i64) { + fn method(#[splat] self: Self) { + println!("{self:?}"); + } +} + +fn main() { + (1_i32, 2_i64).method(); + Trait::method(3_i32, 4_i64); +} diff --git a/tests/ui/splat/splat-self.run.stdout b/tests/ui/splat/splat-self.run.stdout new file mode 100644 index 0000000000000..7bb2d763632f2 --- /dev/null +++ b/tests/ui/splat/splat-self.run.stdout @@ -0,0 +1,2 @@ +(1, 2) +(3, 4)