From 3745c9b381dc152debefea8ebea26cb15a93f5b7 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 11 Mar 2026 17:34:32 +1000 Subject: [PATCH 01/10] Add splat builtin attribute & feature gate --- .../rustc_ast_passes/src/ast_validation.rs | 1 + compiler/rustc_ast_passes/src/errors.rs | 1 + compiler/rustc_ast_passes/src/feature_gate.rs | 1 + .../rustc_attr_parsing/src/attributes/mod.rs | 1 + .../src/attributes/splat.rs | 17 ++++ compiler/rustc_attr_parsing/src/context.rs | 2 + compiler/rustc_codegen_ssa/src/mir/block.rs | 1 + compiler/rustc_codegen_ssa/src/mir/mod.rs | 1 + .../rustc_const_eval/src/interpret/call.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 9 ++ compiler/rustc_feature/src/unstable.rs | 3 + .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir_analysis/src/check/entry.rs | 1 + compiler/rustc_mir_build/src/builder/mod.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + tests/ui/README.md | 6 ++ tests/ui/feature-gates/feature-gate-splat.rs | 8 ++ .../feature-gates/feature-gate-splat.stderr | 13 +++ tests/ui/splat/splat-non-function.rs | 89 +++++++++++++++++++ tests/ui/splat/splat-non-function.stderr | 82 +++++++++++++++++ 22 files changed, 244 insertions(+) create mode 100644 compiler/rustc_attr_parsing/src/attributes/splat.rs create mode 100644 tests/ui/feature-gates/feature-gate-splat.rs create mode 100644 tests/ui/feature-gates/feature-gate-splat.stderr create mode 100644 tests/ui/splat/splat-non-function.rs create mode 100644 tests/ui/splat/splat-non-function.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bdb0981311248..41ec74eef8e5c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -393,6 +393,7 @@ impl<'a> AstValidator<'a> { sym::deny, sym::expect, sym::forbid, + sym::splat, sym::warn, ]; !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(*attr) diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 390c1556f191c..2c7fc65a3e1b7 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -132,6 +132,7 @@ pub(crate) struct FnParamDocComment { pub span: Span, } +// FIXME(splat): add splat to the allowed built-in attributes when it is complete/stabilized #[derive(Diagnostic)] #[diag( "allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters" diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 830eb3d6d8170..b5c67f13ca2d1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -597,6 +597,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(const_block_items, "const block items are experimental"); gate_all!(final_associated_functions, "`final` on trait functions is experimental"); gate_all!(impl_restriction, "`impl` restrictions are experimental"); + gate_all!(splat, "`#[splat] fn` is incomplete", "call as func((a, ...)) instead"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 223c88972d75e..ca35fdf257766 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -63,6 +63,7 @@ pub(crate) mod rustc_allocator; pub(crate) mod rustc_dump; pub(crate) mod rustc_internal; pub(crate) mod semantics; +pub(crate) mod splat; pub(crate) mod stability; pub(crate) mod test_attrs; pub(crate) mod traits; diff --git a/compiler/rustc_attr_parsing/src/attributes/splat.rs b/compiler/rustc_attr_parsing/src/attributes/splat.rs new file mode 100644 index 0000000000000..89fe6cc29aee0 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/splat.rs @@ -0,0 +1,17 @@ +//! Attribute parsing for the `#[splat]` function argument overloading attribute. +//! This attribute modifies typecheck to support overload resolution, then modifies codegen for performance. + +use super::prelude::*; + +pub(crate) struct SplatParser; + +impl NoArgsAttributeParser for SplatParser { + const PATH: &[Symbol] = &[sym::splat]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Param), + // FIXME(splat): only allow MacroCall if the macro creates an argument + Allow(Target::MacroCall), + ]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::Splat; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 190568bed508d..b99387dd6dcee 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -52,6 +52,7 @@ use crate::attributes::rustc_allocator::*; use crate::attributes::rustc_dump::*; use crate::attributes::rustc_internal::*; use crate::attributes::semantics::*; +use crate::attributes::splat::*; use crate::attributes::stability::*; use crate::attributes::test_attrs::*; use crate::attributes::traits::*; @@ -325,6 +326,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, // tidy-alphabetical-end diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index dcbd7f7e7708c..6945c7f816624 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1136,6 +1136,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; // Split the rust-call tupled arguments off. + // FIXME(splat): un-tuple splatted arguments in codegen, for performance let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall && let Some((tup, args)) = args.split_last() { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1a0f66d31cca4..8e8b06a52f67a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -405,6 +405,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg_decl = &mir.local_decls[local]; let arg_ty = fx.monomorphize(arg_decl.ty); + // FIXME(splat): re-tuple splatted arguments that were un-tupled in the ABI if Some(local) == mir.spread_arg { // This argument (e.g., the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 0381571ef6dae..0f0192db5e8de 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -659,6 +659,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Special handling for the closure ABI: untuple the last argument. + // FIXME(splat): un-tuple splatted arguments that were tupled in typecheck let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = if caller_abi == ExternAbi::RustCall && !args.is_empty() { // Untuple diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3a2f548902d11..5f4a28110f1da 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -905,6 +905,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_v2), ), + // The `#[splat]` attribute is part of the `splat` experiment + // that improves the ergonomics of function overloading, tracked in: + // + // - https://github.com/rust-lang/rust/issues/153629 + gated!( + splat, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, experimental!(splat) + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index c508344b9cc1f..b8a28cfe910b7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -666,6 +666,9 @@ declare_features! ( (unstable, sparc_target_feature, "1.84.0", Some(132783)), /// Allows specialization of implementations (RFC 1210). (incomplete, specialization, "1.7.0", Some(31844)), + /// Experimental "splatting" of function call arguments at the call site. + /// e.g. `foo(a, b, c)` calls `#[splat] fn foo((a: A, b: B, c: C))`. + (incomplete, splat, "CURRENT_RUSTC_VERSION", Some(153629)), /// Allows using `#[rustc_align_static(...)]` on static items. (unstable, static_align, "1.91.0", Some(146177)), /// Allows attributes on expressions and non-item statements. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index b5b9da1e8e00c..647a1f138ddec 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1598,6 +1598,9 @@ pub enum AttributeKind { span: Span, }, + /// Represents `#[splat]` + Splat(Span), + /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`. Stability { stability: Stability, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 27128f6996370..de665b95b237d 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -189,6 +189,7 @@ impl AttributeKind { RustcUnsafeSpecializationMarker(..) => No, Sanitize { .. } => No, ShouldPanic { .. } => No, + Splat(..) => Yes, Stability { .. } => Yes, TargetFeature { .. } => No, TestRunner(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index a6dae521db884..c325b850e8fa6 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -98,6 +98,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { error = true; } + // FIXME(splat): also reject `#[splat]` on main function arguments if let Some(attr_span) = find_attr!(tcx, main_def_id, TrackCaller(span) => *span) { tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span }); error = true; diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index bcac45199437c..187c6524f89b4 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -551,6 +551,7 @@ fn construct_fn<'tcx>( body.spread_arg = if abi == ExternAbi::RustCall { // RustCall pseudo-ABI untuples the last argument. + // FIXME(splat): so does splat Some(Local::new(arguments.len())) } else { None diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bec6ab7e83551..51fa81e938d23 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -379,6 +379,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcTrivialFieldReads | AttributeKind::RustcUnsafeSpecializationMarker(..) | AttributeKind::ShouldPanic { .. } + | AttributeKind::Splat(..) | AttributeKind::TestRunner(..) | AttributeKind::ThreadLocal | AttributeKind::TypeLengthLimit { .. } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 286fea7d90505..6e7986cbd4afc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1922,6 +1922,7 @@ symbols! { specialization, speed, spirv, + splat, spotlight, sqrtf16, sqrtf32, diff --git a/tests/ui/README.md b/tests/ui/README.md index b6e6557088922..79545a5ccbcc5 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1275,6 +1275,12 @@ An assorted collection of tests that involves specific diagnostic spans. See [Tracking issue for specialization (RFC 1210) #31844](https://github.com/rust-lang/rust/issues/31844). +## `tests/ui/splat` + +Tests for the `#![feature(splat)]` attribute. + +See [Tracking Issue for argument splatting #153629](https://github.com/rust-lang/rust/issues/153629). + ## `tests/ui/stability-attribute/` Stability attributes used internally by the standard library: `#[stable()]` and `#[unstable()]`. diff --git a/tests/ui/feature-gates/feature-gate-splat.rs b/tests/ui/feature-gates/feature-gate-splat.rs new file mode 100644 index 0000000000000..ebcfc0e5a1d9f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-splat.rs @@ -0,0 +1,8 @@ +#[rustfmt::skip] +fn tuple_args( + #[splat] //~ ERROR the `#[splat]` attribute is an experimental feature + (a, b, c): (u32, i8, char), +) { +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-splat.stderr b/tests/ui/feature-gates/feature-gate-splat.stderr new file mode 100644 index 0000000000000..9881cacef4074 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-splat.stderr @@ -0,0 +1,13 @@ +error[E0658]: the `#[splat]` attribute is an experimental feature + --> $DIR/feature-gate-splat.rs:3:5 + | +LL | #[splat] + | ^^^^^^^^ + | + = note: see issue #153629 for more information + = help: add `#![feature(splat)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/splat/splat-non-function.rs b/tests/ui/splat/splat-non-function.rs new file mode 100644 index 0000000000000..6948734996254 --- /dev/null +++ b/tests/ui/splat/splat-non-function.rs @@ -0,0 +1,89 @@ +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (a, b): (u32, i8)) {} + +#[splat] //~ ERROR `#[splat]` attribute cannot be used on functions +fn tuple_args_bad((a, b): (u32, i8)) {} + +#[splat] //~ ERROR `#[splat]` attribute cannot be used on traits +trait FooTraitBad { + fn tuple_1(_: (u32,)); + + fn tuple_4(self, _: (u32, i8, (), f32)); +} + +trait FooTrait { + fn tuple_1(#[splat] _: (u32,)); + + fn tuple_4(#[splat] self, _: (u32, i8, (), f32)); +} + +struct Foo; + +#[splat] //~ ERROR `#[splat]` attribute cannot be used on inherent impl blocks +impl Foo { + fn tuple_1_bad((a,): (u32,)) {} + + fn tuple_4_bad(self, (a, b, c, d): (u32, i8, (), f32)) -> u32 { + a + } +} + +impl Foo { + #[splat] //~ ERROR `#[splat]` attribute cannot be used on inherent methods + fn tuple_3_bad((a, b, c): (u32, i32, i8)) {} + + #[splat] //~ ERROR `#[splat]` attribute cannot be used on inherent methods + fn tuple_2_bad(self, (a, b): (u32, i8)) -> u32 { + a + } + + fn tuple_1(#[splat] (a,): (u32,)) {} + + // FIXME(splat): this should error except when `self` (or any splatted arg) is a tuple. + // Tuple structs should also error until we have a specific use case for them, and so should multiple splats in a fn. + fn tuple_2_self(#[splat] self, (a, b): (u32, i8)) -> u32 { + a + } + + fn tuple_3(#[splat] (a, b, c): (u32, i32, i8)) {} + + fn tuple_2(self, #[splat] (a, b): (u32, i8)) -> u32 { + a + } + + fn tuple_4(self, #[splat] (a, b, c, d): (u32, i8, (), f32)) -> u32 { + a + } +} + +impl FooTrait for Foo { + // FIXME(splat): should conflicting splat attributes be allowed on traits and impls? + fn tuple_1(_: (u32,)) {} + + fn tuple_4(#[splat] self, _: (u32, i8, (), f32)) {} +} + +#[splat] //~ ERROR `#[splat]` attribute cannot be used on foreign modules +extern "C" { + fn foo_2(_: (u32, i8)); +} + +extern "C" { + fn bar_2(#[splat] _: (u32, i8)); + + #[splat] //~ ERROR `#[splat]` attribute cannot be used on foreign functions + fn bar_2_bad(_: (u32, i8)); +} + +#[splat] //~ ERROR `#[splat]` attribute cannot be used on modules +mod foo_mod {} + +#[splat] //~ ERROR `#[splat]` attribute cannot be used on use statements +use std::mem; + +#[splat] //~ ERROR `#[splat]` attribute cannot be used on structs +struct FooStruct; + +fn main() {} diff --git a/tests/ui/splat/splat-non-function.stderr b/tests/ui/splat/splat-non-function.stderr new file mode 100644 index 0000000000000..4de8568c8baaf --- /dev/null +++ b/tests/ui/splat/splat-non-function.stderr @@ -0,0 +1,82 @@ +error: `#[splat]` attribute cannot be used on functions + --> $DIR/splat-non-function.rs:6:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on traits + --> $DIR/splat-non-function.rs:9:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on inherent impl blocks + --> $DIR/splat-non-function.rs:24:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on inherent methods + --> $DIR/splat-non-function.rs:34:5 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on inherent methods + --> $DIR/splat-non-function.rs:37:5 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on foreign modules + --> $DIR/splat-non-function.rs:66:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on foreign functions + --> $DIR/splat-non-function.rs:74:5 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on modules + --> $DIR/splat-non-function.rs:78:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on use statements + --> $DIR/splat-non-function.rs:81:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: `#[splat]` attribute cannot be used on structs + --> $DIR/splat-non-function.rs:84:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + +error: aborting due to 10 previous errors + From 56e840593c5d3e9c520c6076510c7b8feb4d7d22 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 18 Mar 2026 18:09:57 +1000 Subject: [PATCH 02/10] Plumb arg splatting through to typecheck --- compiler/rustc_ast/src/ast.rs | 3 ++ compiler/rustc_ast_lowering/src/delegation.rs | 14 +++++--- compiler/rustc_ast_lowering/src/expr.rs | 1 + compiler/rustc_ast_lowering/src/lib.rs | 2 ++ .../src/diagnostics/region_errors.rs | 1 + .../src/type_check/input_output.rs | 1 + compiler/rustc_codegen_llvm/src/intrinsic.rs | 3 ++ .../src/const_eval/type_info.rs | 5 ++- compiler/rustc_hir/src/hir.rs | 2 ++ compiler/rustc_hir/src/intravisit.rs | 10 ++++-- .../rustc_hir_analysis/src/check/entry.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 4 ++- compiler/rustc_hir_analysis/src/collect.rs | 3 +- .../src/hir_ty_lowering/mod.rs | 3 +- compiler/rustc_hir_typeck/src/callee.rs | 1 + compiler/rustc_hir_typeck/src/check.rs | 19 +++++++++-- compiler/rustc_hir_typeck/src/closure.rs | 9 ++++++ compiler/rustc_hir_typeck/src/upvar.rs | 1 + compiler/rustc_middle/src/ty/context.rs | 4 ++- compiler/rustc_middle/src/ty/mod.rs | 1 + compiler/rustc_mir_transform/src/shim.rs | 1 + .../src/shim/async_destructor_ctor.rs | 2 ++ compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_public/src/ty.rs | 1 + .../src/unstable/convert/internal.rs | 1 + .../src/unstable/convert/stable/ty.rs | 1 + .../rustc_query_impl/src/from_cycle_error.rs | 1 + compiler/rustc_resolve/src/late.rs | 1 + .../traits/fulfillment_errors.rs | 2 ++ .../src/error_reporting/traits/suggestions.rs | 2 ++ .../src/traits/project.rs | 1 + compiler/rustc_ty_utils/src/abi.rs | 5 +++ compiler/rustc_type_ir/src/relate.rs | 1 + compiler/rustc_type_ir/src/ty_kind.rs | 32 +++++++++++++++++-- compiler/rustc_type_ir/src/ty_kind/closure.rs | 3 ++ 35 files changed, 126 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 74cc1ec17e6f0..02f66f87c8db2 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3057,6 +3057,9 @@ impl FnDecl { pub fn c_variadic(&self) -> bool { self.inputs.last().is_some_and(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)) } + pub fn splatted(&self) -> bool { + self.inputs.last().is_some_and(|arg| arg.attrs.iter().any(|attr| attr.has_name(sym::splat))) + } } /// Is the trait definition an auto trait? diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index f2589ed198add..4a004aeff31ef 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -186,7 +186,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { // Here we use `root_function_id` as we can not get params information out of potential delegation reuse, // we need a function to extract this information - let (param_count, c_variadic) = self.param_count(root_function_id); + let (param_count, c_variadic, splatted) = self.param_count(root_function_id); let mut generics = self.lower_delegation_generics( delegation, @@ -211,6 +211,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { delegee_id, param_count, c_variadic, + splatted, span, &generics, ); @@ -399,15 +400,15 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { } // Function parameter count, including C variadic `...` if present. - fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/) { + fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/, bool /*splatted*/) { if let Some(local_sig_id) = def_id.as_local() { match self.resolver.delegation_fn_sig(local_sig_id) { - Some(sig) => (sig.param_count, sig.c_variadic), - None => (0, false), + Some(sig) => (sig.param_count, sig.c_variadic, sig.splatted), + None => (0, false, false), } } else { let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); - (sig.inputs().len() + usize::from(sig.c_variadic), sig.c_variadic) + (sig.inputs().len() + usize::from(sig.c_variadic), sig.c_variadic, sig.splatted) } } @@ -416,6 +417,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { sig_id: DefId, param_count: usize, c_variadic: bool, + splatted: bool, span: Span, generics: &GenericsGenerationResults<'hir>, ) -> &'hir hir::FnDecl<'hir> { @@ -444,6 +446,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { inputs, output: hir::FnRetTy::Return(output), c_variadic, + splatted, lifetime_elision_allowed: true, implicit_self: hir::ImplicitSelfKind::None, }) @@ -757,6 +760,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { inputs: &[], output: hir::FnRetTy::DefaultReturn(span), c_variadic: false, + splatted: false, lifetime_elision_allowed: true, implicit_self: hir::ImplicitSelfKind::None, }); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 920ca8a6d06c3..c1c941e324237 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -763,6 +763,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { inputs, output, c_variadic: false, + splatted: false, implicit_self: hir::ImplicitSelfKind::None, lifetime_elision_allowed: false, }); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fa44a37125fd2..8c61e08e1efc5 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1739,6 +1739,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { coro: Option, ) -> &'hir hir::FnDecl<'hir> { let c_variadic = decl.c_variadic(); + let splatted = decl.splatted(); // Skip the `...` (`CVarArgs`) trailing arguments from the AST, // as they are not explicit in HIR/Ty function signatures. @@ -1811,6 +1812,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { inputs, output, c_variadic, + splatted, lifetime_elision_allowed: self.resolver.lifetime_elision_allowed(fn_node_id), implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| { let is_mutable_pat = matches!( diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index a3738689ed05f..6f67453e627e4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1087,6 +1087,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { liberated_sig.inputs().iter().copied(), peeled_ty, liberated_sig.c_variadic, + liberated_sig.splatted, hir::Safety::Safe, rustc_abi::ExternAbi::Rust, )), diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 4e762b368496d..a3fb1c8368dbc 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -95,6 +95,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { user_provided_sig.inputs().iter().copied(), output_ty, user_provided_sig.c_variadic, + user_provided_sig.splatted, user_provided_sig.safety, user_provided_sig.abi, ); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cc6ecee60b0e4..5f6602f85cdca 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1279,6 +1279,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( [i8p], tcx.types.unit, false, + false, hir::Safety::Unsafe, ExternAbi::Rust, )), @@ -1290,6 +1291,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( [i8p, i8p], tcx.types.unit, false, + false, hir::Safety::Unsafe, ExternAbi::Rust, )), @@ -1299,6 +1301,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( [try_fn_ty, i8p, catch_fn_ty], tcx.types.i32, false, + false, hir::Safety::Unsafe, ExternAbi::Rust, )); diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 0ed04a5ab20b4..05bcaf69d7296 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -419,7 +419,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sig: &FnSigTys>, fn_header: &FnHeader>, ) -> InterpResult<'tcx> { - let FnHeader { safety, c_variadic, abi } = fn_header; + let FnHeader { safety, c_variadic, splatted, abi } = fn_header; for (field_idx, field) in place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() @@ -465,6 +465,9 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sym::variadic => { self.write_scalar(Scalar::from_bool(*c_variadic), &field_place)?; } + sym::splat => { + self.write_scalar(Scalar::from_bool(*splatted), &field_place)?; + } other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 7cce0eda4dda7..b8d263c70697c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3935,6 +3935,8 @@ pub struct FnDecl<'hir> { pub inputs: &'hir [Ty<'hir>], pub output: FnRetTy<'hir>, pub c_variadic: bool, + /// Is the last argument of the function splatted into multiple arguments in callers? + pub splatted: bool, /// Does the function have an implicit self? pub implicit_self: ImplicitSelfKind, /// Is lifetime elision allowed. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 25ef56f8b0f2c..f180724892873 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1212,8 +1212,14 @@ pub fn walk_fn_decl<'v, V: Visitor<'v>>( visitor: &mut V, function_declaration: &'v FnDecl<'v>, ) -> V::Result { - let FnDecl { inputs, output, c_variadic: _, implicit_self: _, lifetime_elision_allowed: _ } = - function_declaration; + let FnDecl { + inputs, + output, + c_variadic: _, + splatted: _, + implicit_self: _, + lifetime_elision_allowed: _, + } = function_declaration; walk_list!(visitor, visit_ty_unambig, *inputs); visitor.visit_fn_ret_ty(output) } diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index c325b850e8fa6..ef31bc1320d88 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -156,6 +156,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { [], expected_return_type, false, + false, hir::Safety::Safe, ExternAbi::Rust, )); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 47420997a509a..a8d5ade97d290 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -646,6 +646,7 @@ pub(crate) fn check_intrinsic_type( [mut_u8], tcx.types.unit, false, + false, hir::Safety::Safe, ExternAbi::Rust, )); @@ -653,6 +654,7 @@ pub(crate) fn check_intrinsic_type( [mut_u8, mut_u8], tcx.types.unit, false, + false, hir::Safety::Safe, ExternAbi::Rust, )); @@ -816,7 +818,7 @@ pub(crate) fn check_intrinsic_type( return; } }; - let sig = tcx.mk_fn_sig(inputs, output, false, safety, ExternAbi::Rust); + let sig = tcx.mk_fn_sig(inputs, output, false, false, safety, ExternAbi::Rust); let sig = ty::Binder::bind_with_vars(sig, bound_vars); equate_intrinsic_type(tcx, span, intrinsic_id, n_tps, n_lts, n_cts, sig) } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 80ef2001cc72e..37232e4a56668 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1025,7 +1025,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn (Bound::Unbounded, Bound::Unbounded) => hir::Safety::Safe, _ => hir::Safety::Unsafe, }; - ty::Binder::dummy(tcx.mk_fn_sig(inputs, ty, false, safety, ExternAbi::Rust)) + ty::Binder::dummy(tcx.mk_fn_sig(inputs, ty, false, false, safety, ExternAbi::Rust)) } Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { @@ -1219,6 +1219,7 @@ fn recover_infer_ret_ty<'tcx>( fn_sig.inputs().iter().copied(), recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)), fn_sig.c_variadic, + fn_sig.splatted, fn_sig.safety, fn_sig.abi, ); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 9396bf6352c59..ab8b0c3209322 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3392,7 +3392,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?output_ty); - let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi); + let fn_ty = + tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, decl.splatted, safety, abi); let fn_ptr_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); if let hir::Node::Ty(hir::Ty { kind: hir::TyKind::FnPtr(fn_ptr_ty), span, .. }) = diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 93662b36a05df..8a514a746aa07 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -276,6 +276,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tupled_upvars_ty, ), coroutine_closure_sig.c_variadic, + coroutine_closure_sig.splatted, coroutine_closure_sig.safety, coroutine_closure_sig.abi, ); diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 612396858841f..a0e9eb1ce8449 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -204,7 +204,14 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon), ]); let expected_sig = ty::Binder::bind_with_vars( - tcx.mk_fn_sig([panic_info_ref_ty], tcx.types.never, false, fn_sig.safety, ExternAbi::Rust), + tcx.mk_fn_sig( + [panic_info_ref_ty], + tcx.types.never, + false, + false, + fn_sig.safety, + ExternAbi::Rust, + ), bounds, ); @@ -227,7 +234,14 @@ fn check_lang_start_fn<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: ty::FnSig<'tcx>, def_id: let generic_ty = Ty::new_param(tcx, fn_generic.index, fn_generic.name); let main_fn_ty = Ty::new_fn_ptr( tcx, - Binder::dummy(tcx.mk_fn_sig([], generic_ty, false, hir::Safety::Safe, ExternAbi::Rust)), + Binder::dummy(tcx.mk_fn_sig( + [], + generic_ty, + false, + false, + hir::Safety::Safe, + ExternAbi::Rust, + )), ); let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig( @@ -239,6 +253,7 @@ fn check_lang_start_fn<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: ty::FnSig<'tcx>, def_id: ], tcx.types.isize, false, + false, fn_sig.safety, ExternAbi::Rust, )); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 82b7c578a1f25..fda93301f7adf 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -89,6 +89,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [Ty::new_tup(tcx, sig.inputs())], sig.output(), sig.c_variadic, + sig.splatted, sig.safety, sig.abi, ) @@ -232,6 +233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ], Ty::new_tup(tcx, &[bound_yield_ty, bound_return_ty]), sig.c_variadic, + sig.splatted, sig.safety, sig.abi, ) @@ -274,6 +276,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { liberated_sig.inputs().iter().copied(), coroutine_output_ty, liberated_sig.c_variadic, + liberated_sig.splatted, liberated_sig.safety, liberated_sig.abi, ); @@ -548,6 +551,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { input_tys, ret_param_ty, false, + false, hir::Safety::Safe, ExternAbi::Rust, )); @@ -634,6 +638,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { input_tys, return_ty, false, + false, hir::Safety::Safe, ExternAbi::Rust, )); @@ -746,6 +751,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sig.inputs().iter().cloned(), sig.output(), sig.c_variadic, + sig.splatted, hir::Safety::Safe, ExternAbi::RustCall, ) @@ -885,6 +891,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inputs, supplied_output_ty, expected_sigs.liberated_sig.c_variadic, + expected_sigs.liberated_sig.splatted, hir::Safety::Safe, ExternAbi::RustCall, ); @@ -962,6 +969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { supplied_arguments, supplied_return, decl.c_variadic, + decl.splatted, hir::Safety::Safe, ExternAbi::RustCall, ), @@ -1125,6 +1133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { supplied_arguments, err_ty, decl.c_variadic, + decl.splatted, hir::Safety::Safe, ExternAbi::RustCall, )); diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index df02974d2fb2d..3759562908ef7 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -442,6 +442,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [], tupled_upvars_ty_for_borrow, false, + false, hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 56428780d22da..8464d4d4c8d17 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2139,7 +2139,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::Tuple(params) => *params, _ => bug!(), }; - self.mk_fn_sig(params, s.output(), s.c_variadic, safety, ExternAbi::Rust) + self.mk_fn_sig(params, s.output(), s.c_variadic, s.splatted, safety, ExternAbi::Rust) }) } @@ -2417,6 +2417,7 @@ impl<'tcx> TyCtxt<'tcx> { inputs: I, output: I::Item, c_variadic: bool, + splatted: bool, safety: hir::Safety, abi: ExternAbi, ) -> T::Output @@ -2427,6 +2428,7 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(inputs.into_iter().chain(iter::once(output)), |xs| ty::FnSig { inputs_and_output: self.mk_type_list(xs), c_variadic, + splatted, safety, abi, }) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d35b755b3193f..ca597aa7305ae 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -257,6 +257,7 @@ pub struct DelegationFnSig { pub param_count: usize, pub has_self: bool, pub c_variadic: bool, + pub splatted: bool, pub attrs: DelegationAttrs, } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 4fd0629befecf..326d61f2a7758 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -1172,6 +1172,7 @@ fn build_construct_coroutine_by_move_shim<'tcx>( args.as_coroutine_closure().coroutine_captures_by_ref_ty(), ), sig.c_variadic, + sig.splatted, sig.safety, sig.abi, ) diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index a0f1260cd986d..4c9eeeb865652 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -71,6 +71,7 @@ pub(super) fn build_async_drop_shim<'tcx>( [ty, resume_ty], tcx.types.unit, false, + false, Safety::Safe, ExternAbi::Rust, )); @@ -314,6 +315,7 @@ fn build_adrop_for_adrop_shim<'tcx>( [env_ty, Ty::new_task_context(tcx)], ret_ty, false, + false, hir::Safety::Safe, ExternAbi::Rust, ); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 51fa81e938d23..bec4c840761ac 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1708,6 +1708,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ), token_stream, false, + false, Safety::Safe, ExternAbi::Rust, ); diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 8205d29c4534d..39508b6826140 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1089,6 +1089,7 @@ impl PolyFnSig { pub struct FnSig { pub inputs_and_output: Vec, pub c_variadic: bool, + pub splatted: bool, pub safety: Safety, pub abi: Abi, } diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 8594f65100415..713299e5fb93b 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -311,6 +311,7 @@ impl RustcInternal for FnSig { tcx.lift(rustc_ty::FnSig { inputs_and_output: tcx.mk_type_list(&self.inputs_and_output.internal(tables, tcx)), c_variadic: self.c_variadic, + splatted: self.splatted, safety: self.safety.internal(tables, tcx), abi: self.abi.internal(tables, tcx), }) diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 4bf98c4f0731f..56738754589ab 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -265,6 +265,7 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { .map(|ty| ty.stable(tables, cx)) .collect(), c_variadic: self.c_variadic, + splatted: self.splatted, safety: self.safety.stable(tables, cx), abi: self.abi.stable(tables, cx), } diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 0c6082e65f624..696346eea8275 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -67,6 +67,7 @@ fn fn_sig<'tcx>( std::iter::repeat_n(err, arity), err, false, + false, rustc_hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ))) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 453fe9d7a8e0e..8c182dc373cb2 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5447,6 +5447,7 @@ impl ItemInfoCollector<'_, '_, '_> { param_count: decl.inputs.len(), has_self: decl.has_self(), c_variadic: decl.c_variadic(), + splatted: decl.splatted(), attrs: create_delegation_attrs(attrs), }, ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 6bf08170bcf7e..9e59f8836bc85 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -3240,6 +3240,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { given, self.tcx.types.unit, false, + false, hir::Safety::Safe, ExternAbi::Rust, )), @@ -3250,6 +3251,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { expected, self.tcx.types.unit, false, + false, hir::Safety::Safe, ExternAbi::Rust, )), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index ee2f8d9783cfe..6ff0acd9698a9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2064,6 +2064,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { *inputs, infcx.next_ty_var(DUMMY_SP), false, + false, hir::Safety::Safe, ExternAbi::Rust, ) @@ -2072,6 +2073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { [inputs], infcx.next_ty_var(DUMMY_SP), false, + false, hir::Safety::Safe, ExternAbi::Rust, ), diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 3df5c9e33438a..cad5ba6a13db7 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1657,6 +1657,7 @@ fn confirm_closure_candidate<'cx, 'tcx>( [sig.tupled_inputs_ty], output_ty, sig.c_variadic, + sig.splatted, sig.safety, sig.abi, ) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 5008794bcb191..3991afb2c2e99 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -43,6 +43,7 @@ fn fn_sig_for_fn_abi<'tcx>( [], tcx.thread_local_ptr_ty(instance.def_id()), false, + false, hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ); @@ -75,6 +76,7 @@ fn fn_sig_for_fn_abi<'tcx>( iter::once(env_ty).chain(sig.inputs().iter().cloned()), sig.output(), sig.c_variadic, + sig.splatted, sig.safety, sig.abi, ) @@ -120,6 +122,7 @@ fn fn_sig_for_fn_abi<'tcx>( args.as_coroutine_closure().coroutine_captures_by_ref_ty(), ), sig.c_variadic, + sig.splatted, sig.safety, sig.abi, ) @@ -228,6 +231,7 @@ fn fn_sig_for_fn_abi<'tcx>( [env_ty, resume_ty], ret_ty, false, + false, hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ) @@ -237,6 +241,7 @@ fn fn_sig_for_fn_abi<'tcx>( [env_ty], ret_ty, false, + false, hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ) diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index d33c6036dadd8..fe0f6247038be 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -203,6 +203,7 @@ impl Relate for ty::FnSig { Ok(ty::FnSig { inputs_and_output: cx.mk_type_list_from_iter(inputs_and_output)?, c_variadic: a.c_variadic, + splatted: a.splatted, safety: a.safety, abi: a.abi, }) diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 983d8f0820b6b..a25dc989096aa 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -281,6 +281,7 @@ impl TyKind { ty::Binder::dummy(ty::FnSig { inputs_and_output: Default::default(), c_variadic: false, + splatted: false, safety: I::Safety::safe(), abi: I::Abi::rust(), }) @@ -750,6 +751,9 @@ impl Eq for TypeAndMut {} pub struct FnSig { pub inputs_and_output: I::Tys, pub c_variadic: bool, + /// Is the final argument of this function splatted? + /// FIXME(splat): combine this with c_variadic in an enum, they are mutually exclusive. + pub splatted: bool, #[type_visitable(ignore)] #[type_foldable(identity)] pub safety: I::Safety, @@ -773,6 +777,14 @@ impl FnSig { let FnSig { safety, abi, c_variadic, .. } = self; !c_variadic && safety.is_safe() && abi.is_rust() } + + pub fn splatted_arg_index(self) -> Option { + // A function with no inputs behaves the same regardless of splatting. + let last_index = self.inputs().len().checked_sub(1)?; + + debug_assert!(last_index <= u16::MAX as usize); + self.splatted.then_some(last_index as u16) + } } impl ty::Binder> { @@ -800,6 +812,10 @@ impl ty::Binder> { self.skip_binder().c_variadic } + pub fn splatted(self) -> bool { + self.skip_binder().splatted + } + pub fn safety(self) -> I::Safety { self.skip_binder().safety } @@ -814,8 +830,12 @@ impl ty::Binder> { // Used to split a single value into the two fields in `TyKind::FnPtr`. pub fn split(self) -> (ty::Binder>, FnHeader) { - let hdr = - FnHeader { c_variadic: self.c_variadic(), safety: self.safety(), abi: self.abi() }; + let hdr = FnHeader { + c_variadic: self.c_variadic(), + splatted: self.splatted(), + safety: self.safety(), + abi: self.abi(), + }; (self.map_bound(|sig| FnSigTys { inputs_and_output: sig.inputs_and_output }), hdr) } } @@ -823,7 +843,7 @@ impl ty::Binder> { impl fmt::Debug for FnSig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let sig = self; - let FnSig { inputs_and_output: _, c_variadic, safety, abi } = sig; + let FnSig { inputs_and_output: _, c_variadic, splatted: _, safety, abi } = sig; write!(f, "{}", safety.prefix_str())?; if !abi.is_rust() { @@ -832,10 +852,14 @@ impl fmt::Debug for FnSig { write!(f, "fn(")?; let inputs = sig.inputs(); + let splatted_arg_index = sig.splatted_arg_index(); for (i, ty) in inputs.iter().enumerate() { if i > 0 { write!(f, ", ")?; } + if splatted_arg_index == Some(i as u16) { + write!(f, "#[splat] ")?; + } write!(f, "{ty:?}")?; } if *c_variadic { @@ -948,6 +972,7 @@ impl ty::Binder> { self.map_bound(|sig_tys| FnSig { inputs_and_output: sig_tys.inputs_and_output, c_variadic: hdr.c_variadic, + splatted: hdr.splatted, safety: hdr.safety, abi: hdr.abi, }) @@ -982,6 +1007,7 @@ impl ty::Binder> { #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct FnHeader { pub c_variadic: bool, + pub splatted: bool, pub safety: I::Safety, pub abi: I::Abi, } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index e8f94c8e7cc92..f2691feafe4f1 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -308,6 +308,7 @@ impl CoroutineClosureArgs { yield_ty, return_ty, c_variadic: hdr.c_variadic, + splatted: hdr.splatted, safety: hdr.safety, abi: hdr.abi, } @@ -368,6 +369,8 @@ pub struct CoroutineClosureSignature { // from scratch just for good measure. /// Always false pub c_variadic: bool, + // FIXME(splat): is this always false? + pub splatted: bool, /// Always `Normal` (safe) #[type_visitable(ignore)] #[type_foldable(identity)] From ca7769015ed51471143526dcd1e61e7a92a8e960 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 18 Mar 2026 18:12:06 +1000 Subject: [PATCH 03/10] Impl typeck for argument splatting --- compiler/rustc_hir_typeck/src/callee.rs | 81 ++++++++++++++++++- compiler/rustc_hir_typeck/src/errors.rs | 4 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 57 +++++++++---- compiler/rustc_hir_typeck/src/lib.rs | 23 +++++- 4 files changed, 144 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 8a514a746aa07..0071c01a80666 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -53,12 +53,17 @@ pub(crate) fn check_legal_trait_for_method_call( tcx.ensure_result().coherent_trait(trait_id) } +/// State machine for typechecking a call, based on the callee type. #[derive(Debug)] enum CallStep<'tcx> { + /// Typecheck a call to a function definition or pointer. Builtin(Ty<'tcx>), + /// Deferred closure Fn* trait typechecking, when the callee is a closure. DeferredClosure(LocalDefId, ty::FnSig<'tcx>), /// Call overloading when callee implements one of the Fn* traits. Overloaded(MethodCallee<'tcx>), + /// Caller argument tupling, when callee uses `#[splat]`. + Splatted(Ty<'tcx>), } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -147,6 +152,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(CallStep::Overloaded(method_callee)) => { self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee) } + + Some(CallStep::Splatted(callee_ty)) => { + self.confirm_splatted_call(call_expr, callee_ty, arg_exprs, expected) + } }; // we must check that return type of called functions is WF: @@ -219,12 +228,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::FnDef(..) | ty::FnPtr(..) => { let adjustments = self.adjust_steps(autoderef); self.apply_adjustments(callee_expr, adjustments); + + // If the callee has `#[splat]` on an argument + if let hir::ExprKind::Path(ref qpath) = callee_expr.kind + && let Res::Def(_def_kind, def_id) = + self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) + && self.tcx.fn_sig(def_id).skip_binder().skip_binder().splatted + { + return Some(CallStep::Splatted(adjusted_ty)); + } + return Some(CallStep::Builtin(adjusted_ty)); } // Check whether this is a call to a closure where we // haven't yet decided on whether the closure is fn vs // fnmut vs fnonce. If so, we have to defer further processing. + // FIXME(splat): handle splatting of closure arguments ty::Closure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => { let def_id = def_id.expect_local(); let closure_sig = args.as_closure().sig(); @@ -252,6 +272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // signature with an infer var for the `tupled_upvars_ty` of the coroutine, // and record a deferred call resolution which will constrain that var // as part of `AsyncFn*` trait confirmation. + // FIXME(splat): handle splatting of coroutine closure arguments ty::CoroutineClosure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => { let def_id = def_id.expect_local(); let closure_args = args.as_coroutine_closure(); @@ -907,7 +928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, arg_exprs, fn_sig.c_variadic, - TupleArgumentsFlag::TupleArguments, + TupleArgumentsFlag::TupleAllArguments, Some(closure_def_id.to_def_id()), ); @@ -978,7 +999,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, arg_exprs, method.sig.c_variadic, - TupleArgumentsFlag::TupleArguments, + TupleArgumentsFlag::TupleAllArguments, Some(method.def_id), ); @@ -986,6 +1007,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method.sig.output() } + + fn confirm_splatted_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_ty: Ty<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let (fn_sig, def_id) = match *callee_ty.kind() { + ty::FnDef(def_id, args) => { + self.enforce_context_effects(Some(call_expr.hir_id), call_expr.span, def_id, args); + let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args); + (fn_sig, Some(def_id)) + } + + // FIXME(const_trait_impl): these arms should error because we can't enforce them + ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None), + + _ => unreachable!(), + }; + + // Replace any late-bound regions that appear in the function + // signature with region variables. We also have to + // renormalize the associated types at this point, since they + // previously appeared within a `Binder<>` and hence would not + // have been normalized before. + let fn_sig = self.instantiate_binder_with_fresh_vars( + call_expr.span, + BoundRegionConversionTime::FnCall, + fn_sig, + ); + let fn_sig = self.normalize(call_expr.span, fn_sig); + + self.check_argument_types( + call_expr.span, + call_expr, + fn_sig.inputs(), + fn_sig.output(), + expected, + arg_exprs, + fn_sig.c_variadic, + TupleArgumentsFlag::TupleSplattedArguments { + splatted_arg_index: fn_sig + .splatted_arg_index() + .expect("must have a splatted argument"), + }, + def_id, + ); + + if fn_sig.abi == rustc_abi::ExternAbi::RustCall { + let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); + self.dcx().emit_err(errors::RustCallIncorrectArgs { span: sp }); + } + + fn_sig.output() + } } #[derive(Debug)] diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 6eef156846972..091dd4d25dd57 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -101,7 +101,9 @@ impl IntoDiagArg for ReturnLikeStatementKind { } #[derive(Diagnostic)] -#[diag("functions with the \"rust-call\" ABI must take a single non-self tuple argument")] +#[diag( + "functions with the \"rust-call\" ABI must take a single non-self, non-splatted tuple argument" +)] pub(crate) struct RustCallIncorrectArgs { #[primary_span] pub span: Span, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 0471fd965cd82..227dd02fce85e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -216,9 +216,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // First, let's unify the formal method signature with the expectation eagerly. - // We use this to guide coercion inference; it's output is "fudged" which means + // We use this to guide coercion inference; its output is "fudged" which means // any remaining type variables are assigned to new, unrelated variables. This // is because the inference guidance here is only speculative. + // FIXME(splat): do we need to splat arguments before this type inference? let formal_output = self.resolve_vars_with_obligations(formal_output); let expected_input_tys: Option> = expectation .only_has_type(self) @@ -284,27 +285,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err_code = E0061; - // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]); + // If the arguments should be wrapped in a tuple (ex: closures, splats), unwrap them here + let (formal_input_tys, expected_input_tys) = if let Some(tupled_arg_index) = + tuple_arguments.tupled_argument_position() + { + let tupled_arg_index = usize::from(tupled_arg_index); + debug_assert_eq!( + Some(tupled_arg_index), + formal_input_tys.len().checked_sub(1), + "only trailing tupled arguments are implemented", + ); + let tuple_type = + self.structurally_resolve_type(call_span, formal_input_tys[tupled_arg_index]); match tuple_type.kind() { // We expected a tuple and got a tuple ty::Tuple(arg_types) => { // Argument length differs - if arg_types.len() != provided_args.len() { + // FIXME(splat): update the error code docs when splat is stabilized + if Some(arg_types.len()) != provided_args.len().checked_sub(tupled_arg_index) { err_code = E0057; } let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.get(0) { - Some(ty) => match ty.kind() { - ty::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, + Some(expected_input_tys) => { + match expected_input_tys.get(tupled_arg_index) { + Some(ty) => match ty.kind() { + ty::Tuple(tys) => Some( + expected_input_tys + .into_iter() + .take(tupled_arg_index) + .chain(tys.iter()) + .collect::>(), + ), + _ => None, + }, + None => None, + } + } None => None, }; - (arg_types.iter().collect(), expected_input_tys) + ( + formal_input_tys[..tupled_arg_index] + .into_iter() + .cloned() + .chain(arg_types.into_iter()) + .collect(), + expected_input_tys, + ) } _ => { // Otherwise, there's a mismatch, so clear out what we're expecting, and set @@ -1371,8 +1397,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly // (eg invoking a closure) we want to point at the underlying callable, // not the method implicitly invoked (eg call_once). - // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) - if tuple_arguments == TupleArguments + // TupleAllArguments is set only when this is an implicit call `my_closure(...)` rather + // than explicit `my_closure.call(...)`. + if tuple_arguments == TupleAllArguments && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) // Since this is an associated item, it might point at either an impl or a trait item. // We want it to always point to the trait item. diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 734e5136f8806..950d890a96cbb 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -611,9 +611,9 @@ fn report_unexpected_variant_res( } /// Controls whether the arguments are tupled. This is used for the call -/// operator. +/// operator and function argument splatting. /// -/// Tupling means that all call-side arguments are packed into a tuple and +/// Tupling means that trailing call-side arguments are packed into a tuple and /// passed as a single parameter. For example, if tupling is enabled, this /// function: /// ``` @@ -629,10 +629,27 @@ fn report_unexpected_variant_res( /// # fn f(x: (isize, isize)) {} /// f((1, 2)); /// ``` +/// +/// For the call operator, all arguments are tupled. For splatting, trailing arguments after +/// `#[splat]` in the function signature are tupled. #[derive(Copy, Clone, Eq, PartialEq)] enum TupleArgumentsFlag { + /// Arguments are typechecked unchanged. DontTupleArguments, - TupleArguments, + /// All arguments are tupled before typechecking. + TupleAllArguments, + /// Only the splatted arguments are tupled before typechecking. + TupleSplattedArguments { splatted_arg_index: u16 }, +} + +impl TupleArgumentsFlag { + fn tupled_argument_position(&self) -> Option { + match self { + Self::DontTupleArguments => None, + Self::TupleAllArguments => Some(0), + Self::TupleSplattedArguments { splatted_arg_index } => Some(*splatted_arg_index), + } + } } fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { From 4b86bc99d0c3b7be98ce8d41296b446ee3255b34 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 19 Mar 2026 13:11:47 +1000 Subject: [PATCH 04/10] Add tests for splat typeck --- ...at-non-function.rs => splat-non-fn-arg.rs} | 45 +++--------- ...unction.stderr => splat-non-fn-arg.stderr} | 43 ++++++++--- tests/ui/splat/splat-non-tuple.rs | 73 +++++++++++++++++++ 3 files changed, 116 insertions(+), 45 deletions(-) rename tests/ui/splat/{splat-non-function.rs => splat-non-fn-arg.rs} (56%) rename tests/ui/splat/{splat-non-function.stderr => splat-non-fn-arg.stderr} (63%) create mode 100644 tests/ui/splat/splat-non-tuple.rs diff --git a/tests/ui/splat/splat-non-function.rs b/tests/ui/splat/splat-non-fn-arg.rs similarity index 56% rename from tests/ui/splat/splat-non-function.rs rename to tests/ui/splat/splat-non-fn-arg.rs index 6948734996254..974d743e31ab1 100644 --- a/tests/ui/splat/splat-non-function.rs +++ b/tests/ui/splat/splat-non-fn-arg.rs @@ -1,7 +1,7 @@ #![allow(incomplete_features)] #![feature(splat)] -fn tuple_args(#[splat] (a, b): (u32, i8)) {} +//! Test that using `#[splat]` on non-function-arguments is an error. #[splat] //~ ERROR `#[splat]` attribute cannot be used on functions fn tuple_args_bad((a, b): (u32, i8)) {} @@ -13,21 +13,11 @@ trait FooTraitBad { fn tuple_4(self, _: (u32, i8, (), f32)); } -trait FooTrait { - fn tuple_1(#[splat] _: (u32,)); - - fn tuple_4(#[splat] self, _: (u32, i8, (), f32)); -} - struct Foo; #[splat] //~ ERROR `#[splat]` attribute cannot be used on inherent impl blocks impl Foo { fn tuple_1_bad((a,): (u32,)) {} - - fn tuple_4_bad(self, (a, b, c, d): (u32, i8, (), f32)) -> u32 { - a - } } impl Foo { @@ -38,31 +28,13 @@ impl Foo { fn tuple_2_bad(self, (a, b): (u32, i8)) -> u32 { a } - - fn tuple_1(#[splat] (a,): (u32,)) {} - - // FIXME(splat): this should error except when `self` (or any splatted arg) is a tuple. - // Tuple structs should also error until we have a specific use case for them, and so should multiple splats in a fn. - fn tuple_2_self(#[splat] self, (a, b): (u32, i8)) -> u32 { - a - } - - fn tuple_3(#[splat] (a, b, c): (u32, i32, i8)) {} - - fn tuple_2(self, #[splat] (a, b): (u32, i8)) -> u32 { - a - } - - fn tuple_4(self, #[splat] (a, b, c, d): (u32, i8, (), f32)) -> u32 { - a - } } -impl FooTrait for Foo { - // FIXME(splat): should conflicting splat attributes be allowed on traits and impls? +#[splat] //~ ERROR `#[splat]` attribute cannot be used on trait impl blocks +impl FooTraitBad for Foo { fn tuple_1(_: (u32,)) {} - fn tuple_4(#[splat] self, _: (u32, i8, (), f32)) {} + fn tuple_4(self, _: (u32, i8, (), f32)) {} } #[splat] //~ ERROR `#[splat]` attribute cannot be used on foreign modules @@ -71,8 +43,6 @@ extern "C" { } extern "C" { - fn bar_2(#[splat] _: (u32, i8)); - #[splat] //~ ERROR `#[splat]` attribute cannot be used on foreign functions fn bar_2_bad(_: (u32, i8)); } @@ -86,4 +56,11 @@ use std::mem; #[splat] //~ ERROR `#[splat]` attribute cannot be used on structs struct FooStruct; +fn multisplat_bad( + #[splat] + #[splat] //~ WARN unused attribute + (a, b): (u32, i8), +) { +} + fn main() {} diff --git a/tests/ui/splat/splat-non-function.stderr b/tests/ui/splat/splat-non-fn-arg.stderr similarity index 63% rename from tests/ui/splat/splat-non-function.stderr rename to tests/ui/splat/splat-non-fn-arg.stderr index 4de8568c8baaf..145732f00df04 100644 --- a/tests/ui/splat/splat-non-function.stderr +++ b/tests/ui/splat/splat-non-fn-arg.stderr @@ -1,5 +1,5 @@ error: `#[splat]` attribute cannot be used on functions - --> $DIR/splat-non-function.rs:6:1 + --> $DIR/splat-non-fn-arg.rs:6:1 | LL | #[splat] | ^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on traits - --> $DIR/splat-non-function.rs:9:1 + --> $DIR/splat-non-fn-arg.rs:9:1 | LL | #[splat] | ^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on inherent impl blocks - --> $DIR/splat-non-function.rs:24:1 + --> $DIR/splat-non-fn-arg.rs:18:1 | LL | #[splat] | ^^^^^^^^ @@ -23,7 +23,7 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on inherent methods - --> $DIR/splat-non-function.rs:34:5 + --> $DIR/splat-non-fn-arg.rs:24:5 | LL | #[splat] | ^^^^^^^^ @@ -31,15 +31,23 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on inherent methods - --> $DIR/splat-non-function.rs:37:5 + --> $DIR/splat-non-fn-arg.rs:27:5 | LL | #[splat] | ^^^^^^^^ | = help: `#[splat]` can only be applied to function params +error: `#[splat]` attribute cannot be used on trait impl blocks + --> $DIR/splat-non-fn-arg.rs:33:1 + | +LL | #[splat] + | ^^^^^^^^ + | + = help: `#[splat]` can only be applied to function params + error: `#[splat]` attribute cannot be used on foreign modules - --> $DIR/splat-non-function.rs:66:1 + --> $DIR/splat-non-fn-arg.rs:40:1 | LL | #[splat] | ^^^^^^^^ @@ -47,7 +55,7 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on foreign functions - --> $DIR/splat-non-function.rs:74:5 + --> $DIR/splat-non-fn-arg.rs:46:5 | LL | #[splat] | ^^^^^^^^ @@ -55,7 +63,7 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on modules - --> $DIR/splat-non-function.rs:78:1 + --> $DIR/splat-non-fn-arg.rs:50:1 | LL | #[splat] | ^^^^^^^^ @@ -63,7 +71,7 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on use statements - --> $DIR/splat-non-function.rs:81:1 + --> $DIR/splat-non-fn-arg.rs:53:1 | LL | #[splat] | ^^^^^^^^ @@ -71,12 +79,25 @@ LL | #[splat] = help: `#[splat]` can only be applied to function params error: `#[splat]` attribute cannot be used on structs - --> $DIR/splat-non-function.rs:84:1 + --> $DIR/splat-non-fn-arg.rs:56:1 | LL | #[splat] | ^^^^^^^^ | = help: `#[splat]` can only be applied to function params -error: aborting due to 10 previous errors +warning: unused attribute + --> $DIR/splat-non-fn-arg.rs:61:5 + | +LL | #[splat] + | ^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/splat-non-fn-arg.rs:60:5 + | +LL | #[splat] + | ^^^^^^^^ + = note: requested on the command line with `-W unused-attributes` + +error: aborting due to 11 previous errors; 1 warning emitted diff --git a/tests/ui/splat/splat-non-tuple.rs b/tests/ui/splat/splat-non-tuple.rs new file mode 100644 index 0000000000000..c5bdfccbf6e76 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.rs @@ -0,0 +1,73 @@ +//! Test that using `#[splat]` on non-tuple function arguments is an error. +//@ check-pass +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn primitive_arg(#[splat] x: u32) {} // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + +enum NotATuple { + A(u32), + B(i8), +} + +fn enum_arg(#[splat] y: NotATuple) {} // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + +trait FooTrait { + fn tuple_1(#[splat] _: (u32,)); + + // Ambiguous case, self could be a tuple or a non-tuple + fn tuple_4(#[splat] self, _: (u32, i8, (), f32)); +} + +struct Foo; + +fn struct_arg(#[splat] z: Foo) {} + +impl Foo { + // FIXME(splat): this should error except when `self` (or any splatted arg) is a tuple. + fn tuple_2_self( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + (a, b): (u32, i8), + ) -> u32 { + a + } +} + +impl FooTrait for Foo { + // FIXME(splat): should splat attributes be inherited from traits? + // Can splat attributes be added on impls? + fn tuple_1(_: (u32,)) {} + + fn tuple_4( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + _: (u32, i8, (), f32), + ) { + } +} + +struct TupleStruct(u32, i8); + +// FIXME(splat): tuple structs should error until we have a specific use case for them. +fn tuple_struct_arg(#[splat] z: TupleStruct) {} // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + +impl TupleStruct { + fn tuple_2( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + (a, b): (u32, i8), + ) -> u32 { + a + } +} + +impl FooTrait for TupleStruct { + fn tuple_1(#[splat] _: (u32,)) {} + + fn tuple_4( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + _: (u32, i8, (), f32), + ) { + } +} + +fn main() {} From a93ac8c750bc42323da31a9c4ab6a7b17d7eb361 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 19 Mar 2026 18:17:27 +1000 Subject: [PATCH 05/10] Handle splatted method arguments --- compiler/rustc_hir_typeck/src/expr.rs | 15 ++++++++- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 32 +++++++++++++------ compiler/rustc_hir_typeck/src/lib.rs | 2 +- tests/ui/splat/splat-non-fn-arg.rs | 4 +-- tests/ui/splat/splat-non-fn-arg.stderr | 22 ++++++------- tests/ui/splat/splat-non-tuple.rs | 1 + 6 files changed, 51 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6b77169994a03..603fd805feb84 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1482,6 +1482,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) => { self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method); + // Handle splatted method arguments + let tuple_arguments = + if let Some(meth_splatted_arg) = method.sig.splatted_arg_index() { + // self is already handled as `rcvr` + TupleArgumentsFlag::TupleSplattedArguments { + splatted_arg_index: meth_splatted_arg + .checked_sub(1) + .expect("missing splatted arg or self in method call"), + } + } else { + TupleArgumentsFlag::DontTupleArguments + }; + self.check_argument_types( segment.ident.span, expr, @@ -1490,7 +1503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, args, method.sig.c_variadic, - TupleArgumentsFlag::DontTupleArguments, + tuple_arguments, Some(method.def_id), ); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 227dd02fce85e..1913caafc7e6d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -290,7 +290,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments.tupled_argument_position() { let tupled_arg_index = usize::from(tupled_arg_index); - debug_assert_eq!( + assert_eq!( Some(tupled_arg_index), formal_input_tys.len().checked_sub(1), "only trailing tupled arguments are implemented", @@ -301,7 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We expected a tuple and got a tuple ty::Tuple(arg_types) => { // Argument length differs - // FIXME(splat): update the error code docs when splat is stabilized + // FIXME(splat): update the error code E0057 docs when splat is stabilized if Some(arg_types.len()) != provided_args.len().checked_sub(tupled_arg_index) { err_code = E0057; } @@ -335,14 +335,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { // Otherwise, there's a mismatch, so clear out what we're expecting, and set // our input types to err_args so we don't blow up the error messages - let guar = struct_span_code_err!( - self.dcx(), - call_span, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); + let guar = match tuple_arguments { + TupleAllArguments => struct_span_code_err!( + self.dcx(), + call_span, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(), + TupleSplattedArguments { .. } => struct_span_code_err!( + self.dcx(), + call_span, + // FIXME(splat): add a new error code before stabilization + E0277, + "cannot use splat attribute; the last type parameter \ + for the function is neither a tuple nor unit" + ) + .emit(), + DontTupleArguments => unreachable!(), + }; (self.err_args(provided_args.len(), guar), None) } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 950d890a96cbb..dcf9aea4284b3 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -632,7 +632,7 @@ fn report_unexpected_variant_res( /// /// For the call operator, all arguments are tupled. For splatting, trailing arguments after /// `#[splat]` in the function signature are tupled. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] enum TupleArgumentsFlag { /// Arguments are typechecked unchanged. DontTupleArguments, diff --git a/tests/ui/splat/splat-non-fn-arg.rs b/tests/ui/splat/splat-non-fn-arg.rs index 974d743e31ab1..92c758cb14582 100644 --- a/tests/ui/splat/splat-non-fn-arg.rs +++ b/tests/ui/splat/splat-non-fn-arg.rs @@ -1,8 +1,8 @@ +//! Test that using `#[splat]` on non-function-arguments is an error. + #![allow(incomplete_features)] #![feature(splat)] -//! Test that using `#[splat]` on non-function-arguments is an error. - #[splat] //~ ERROR `#[splat]` attribute cannot be used on functions fn tuple_args_bad((a, b): (u32, i8)) {} diff --git a/tests/ui/splat/splat-non-fn-arg.stderr b/tests/ui/splat/splat-non-fn-arg.stderr index 145732f00df04..95b28fde5825f 100644 --- a/tests/ui/splat/splat-non-fn-arg.stderr +++ b/tests/ui/splat/splat-non-fn-arg.stderr @@ -4,7 +4,7 @@ error: `#[splat]` attribute cannot be used on functions LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on traits --> $DIR/splat-non-fn-arg.rs:9:1 @@ -12,7 +12,7 @@ error: `#[splat]` attribute cannot be used on traits LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on inherent impl blocks --> $DIR/splat-non-fn-arg.rs:18:1 @@ -20,7 +20,7 @@ error: `#[splat]` attribute cannot be used on inherent impl blocks LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on inherent methods --> $DIR/splat-non-fn-arg.rs:24:5 @@ -28,7 +28,7 @@ error: `#[splat]` attribute cannot be used on inherent methods LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on inherent methods --> $DIR/splat-non-fn-arg.rs:27:5 @@ -36,7 +36,7 @@ error: `#[splat]` attribute cannot be used on inherent methods LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on trait impl blocks --> $DIR/splat-non-fn-arg.rs:33:1 @@ -44,7 +44,7 @@ error: `#[splat]` attribute cannot be used on trait impl blocks LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on foreign modules --> $DIR/splat-non-fn-arg.rs:40:1 @@ -52,7 +52,7 @@ error: `#[splat]` attribute cannot be used on foreign modules LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on foreign functions --> $DIR/splat-non-fn-arg.rs:46:5 @@ -60,7 +60,7 @@ error: `#[splat]` attribute cannot be used on foreign functions LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on modules --> $DIR/splat-non-fn-arg.rs:50:1 @@ -68,7 +68,7 @@ error: `#[splat]` attribute cannot be used on modules LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on use statements --> $DIR/splat-non-fn-arg.rs:53:1 @@ -76,7 +76,7 @@ error: `#[splat]` attribute cannot be used on use statements LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls error: `#[splat]` attribute cannot be used on structs --> $DIR/splat-non-fn-arg.rs:56:1 @@ -84,7 +84,7 @@ error: `#[splat]` attribute cannot be used on structs LL | #[splat] | ^^^^^^^^ | - = help: `#[splat]` can only be applied to function params + = help: `#[splat]` can be applied to function params and macro calls warning: unused attribute --> $DIR/splat-non-fn-arg.rs:61:5 diff --git a/tests/ui/splat/splat-non-tuple.rs b/tests/ui/splat/splat-non-tuple.rs index c5bdfccbf6e76..3287ed1ca203e 100644 --- a/tests/ui/splat/splat-non-tuple.rs +++ b/tests/ui/splat/splat-non-tuple.rs @@ -1,5 +1,6 @@ //! Test that using `#[splat]` on non-tuple function arguments is an error. //@ check-pass + #![allow(incomplete_features)] #![feature(splat)] #![expect(unused)] From 5d1856a86b726f5d49e53c0f1f227b7dc41fee39 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 19 Mar 2026 18:18:14 +1000 Subject: [PATCH 06/10] Add UI tests that fail at the MIR stage --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 32 +++++- compiler/rustc_hir_typeck/src/lib.rs | 4 + tests/ui/splat/splat-overload-at-home.rs | 53 ++++++++++ tests/ui/splat/splat-tuple.rs | 100 ++++++++++++++++++ 4 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 tests/ui/splat/splat-overload-at-home.rs create mode 100644 tests/ui/splat/splat-tuple.rs diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 1913caafc7e6d..513d5d9814319 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -295,8 +295,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys.len().checked_sub(1), "only trailing tupled arguments are implemented", ); - let tuple_type = - self.structurally_resolve_type(call_span, formal_input_tys[tupled_arg_index]); + // Keep the type variable if the argument is splatted, so we can force it to be a tuple later. + let tuple_type = if tuple_arguments.is_splatted() { + let tuple_type = self + .try_structurally_resolve_type(call_span, formal_input_tys[tupled_arg_index]); + if tuple_type.is_ty_var() { + let tuple_len = provided_args.len().checked_sub(tupled_arg_index); + if let Some(tuple_len) = tuple_len { + // TODO: how do we force the type variable to resolve to (a supertype) of the caller's tupled argument types? + Ty::new_tup_from_iter( + self.tcx, + iter::repeat_with(|| self.next_ty_var(call_span)).take(tuple_len), + ) + } else { + // Just let it likely fail later + tuple_type + } + } else { + tuple_type + } + } else { + self.structurally_resolve_type(call_span, formal_input_tys[tupled_arg_index]) + }; match tuple_type.kind() { // We expected a tuple and got a tuple ty::Tuple(arg_types) => { @@ -350,7 +370,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(splat): add a new error code before stabilization E0277, "cannot use splat attribute; the last type parameter \ - for the function is neither a tuple nor unit" + for the function must be a tuple or unit, not a {:?} ({:?})", + tuple_type.kind(), + self.structurally_resolve_type( + call_span, + formal_input_tys[tupled_arg_index] + ) + .kind(), ) .emit(), DontTupleArguments => unreachable!(), diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index dcf9aea4284b3..e38d2e1751e56 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -650,6 +650,10 @@ impl TupleArgumentsFlag { Self::TupleSplattedArguments { splatted_arg_index } => Some(*splatted_arg_index), } } + + fn is_splatted(&self) -> bool { + matches!(self, Self::TupleSplattedArguments { .. }) + } } fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { diff --git a/tests/ui/splat/splat-overload-at-home.rs b/tests/ui/splat/splat-overload-at-home.rs new file mode 100644 index 0000000000000..3f0f039af7abe --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home.rs @@ -0,0 +1,53 @@ +//@ dont-check-compiler-stderr +//@ dont-check-failure-status +//@ dont-require-annotations: ERROR +// FIXME(splat): ^change the actual types during typeck so MIR doesn't ICE. + +//! Test using `#[splat]` on some "overloading at home" example code. +//! + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +struct Foo; + +trait MethodArgs: std::marker::Tuple { + fn call_method(self, this: &Foo); +} +impl MethodArgs for () { + fn call_method(self, this: &Foo) {} +} +impl MethodArgs for (i32,) { + fn call_method(self, this: &Foo) {} +} +impl MethodArgs for (i32, String) { + fn call_method(self, this: &Foo) {} +} + +impl Foo { + // FIXME(splat): make this work with impl MethodArgs + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): incorrectly assumes that splat must have one or more arguments to tuple + //foo.method::<()>(); + + // FIXME(splat): generic tuple trait implementers should work without explicit tuple type parameters. + // actually modify the argument list during typeck, to avoid "broken MIR" errors + foo.method::<(i32,)>(42i32); //~ ERROR broken MIR + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.method((42i32,)); + + foo.method::<(i32,)>(42); //~ ERROR broken MIR + + foo.method::<(i32, String)>(42i32, "asdf".to_owned()); //~ ERROR broken MIR + + foo.method::<(i32, String)>(42, "asdf".to_owned()); //~ ERROR broken MIR +} diff --git a/tests/ui/splat/splat-tuple.rs b/tests/ui/splat/splat-tuple.rs new file mode 100644 index 0000000000000..f9eb7827d6656 --- /dev/null +++ b/tests/ui/splat/splat-tuple.rs @@ -0,0 +1,100 @@ +//@ dont-check-compiler-stderr +//@ dont-check-failure-status +//@ dont-require-annotations: ERROR +// FIXME(splat): ^change the actual types during typeck so MIR doesn't ICE. + +//! Test using `#[splat]` on tuple function arguments. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn tuple_args(#[splat] (a, b): (u32, i8)) {} + +trait FooTrait { + fn tuple_1_trait(#[splat] _: (u32,)); + + // Ambiguous case, self could be a tuple or a non-tuple. + fn tuple_4_trait(#[splat] &self, _: (u32, i8, (), f32)) {} +} + +struct Foo; + +impl Foo { + fn tuple_1(#[splat] (a,): (u32,)) {} + + fn tuple_2(&self, #[splat] (a, b): (u32, i8)) -> u32 { + a + } + + fn tuple_3(#[splat] (a, b, c): (u32, i32, i8)) {} + + fn tuple_4(&self, #[splat] (a, b, c, d): (u32, i8, (), f32)) -> u32 { + a + } +} + +impl FooTrait for Foo { + // FIXME(splat): should splat attributes be inherited from traits? + // Can splat attributes be added on impls? + fn tuple_1_trait(#[splat] _: (u32,)) {} +} + +struct TupleStruct(u32, i8); + +impl TupleStruct { + // FIXME(splat): tuple structs should error until we have a specific use case for them. + fn tuple_2(#[splat] &self, (a, b): (u32, i8)) -> u32 { + a + } +} + +impl FooTrait for TupleStruct { + fn tuple_1_trait(#[splat] _: (u32,)) {} +} + +extern "C" { + // FIXME(splat): tuple layouts are unspecified, so this should error. + #[expect(improper_ctypes)] + fn bar_2(#[splat] _: (u32, i8)); +} + +// FIXME(splat): multiple splats in a fn should error. +fn multisplat_bad_2(#[splat] (a, b): (u32, i8), #[splat] (c, d): (u32, i8)) {} + +// FIXME(splat): non-terminal splat attributes should error, until we have a specific use case for them. +fn splat_non_terminal_bad(#[splat] (a, b): (u32, i8), (c, d): (u32, i8)) {} + +fn splat_generic_tuple(#[splat] t: T) {} + +fn main() { + // FIXME(splat): actually modify the argument list during typeck, to avoid "broken MIR" errors + tuple_args(1, 2); //~ ERROR: broken MIR in + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //tuple_args((1, 2)); + tuple_args(1u32, 2i8); //~ ERROR: broken MIR in + + let foo = Foo; + Foo::tuple_1(1u32); //~ ERROR: broken MIR in + foo.tuple_2(1u32, 2i8); //~ ERROR: broken MIR in + Foo::tuple_3(1u32, 2i32, 3i8); //~ ERROR broken MIR + foo.tuple_4(1u32, 2i8, (), 3f32); + + Foo::tuple_1_trait(1u32); + // FIXME(splat): this should error because `self` is splatted, but `Foo` is not a tuple. + foo.tuple_4_trait((1u32, 2i8, (), 3f32)); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct.tuple_2((1u32, 2i8)); + + TupleStruct::tuple_1_trait(1u32); + // FIXME(splat): this should error because `self` is splatted, but `TupleStruct` is not a tuple. + tuple_struct.tuple_4_trait((1u32, 2i8, (), 3f32)); + + // FIXME(splat): generic tuple trait implementers should work without explicit tuple type parameters. + splat_generic_tuple::<(u32, i8)>(1u32, 2i8); + + // FIXME(splat): incorrectly assumes that splat must have one or more arguments to tuple + //splat_generic_tuple::<()>(); +} From 2d9e5638af80906ae87d8917351b0a6c4ca74352 Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 20 Mar 2026 11:21:26 +1000 Subject: [PATCH 07/10] Handle splatted empty tuples and simplify the code --- compiler/rustc_hir_typeck/src/callee.rs | 7 ++----- compiler/rustc_hir_typeck/src/expr.rs | 17 ++++++----------- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 14 +++++--------- compiler/rustc_hir_typeck/src/lib.rs | 15 +++++++++------ compiler/rustc_type_ir/src/ty_kind.rs | 16 ++++++---------- tests/ui/splat/splat-overload-at-home.rs | 3 +-- tests/ui/splat/splat-tuple.rs | 3 +-- 7 files changed, 30 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 0071c01a80666..fdeeb933e47c5 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -1048,14 +1048,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, arg_exprs, fn_sig.c_variadic, - TupleArgumentsFlag::TupleSplattedArguments { - splatted_arg_index: fn_sig - .splatted_arg_index() - .expect("must have a splatted argument"), - }, + TupleArgumentsFlag::TupleSplattedArguments, def_id, ); + // FIXME(splat): is splatting incompatible with RustCall? if fn_sig.abi == rustc_abi::ExternAbi::RustCall { let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); self.dcx().emit_err(errors::RustCallIncorrectArgs { span: sp }); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 603fd805feb84..f46c84a652f61 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1483,17 +1483,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method); // Handle splatted method arguments - let tuple_arguments = - if let Some(meth_splatted_arg) = method.sig.splatted_arg_index() { - // self is already handled as `rcvr` - TupleArgumentsFlag::TupleSplattedArguments { - splatted_arg_index: meth_splatted_arg - .checked_sub(1) - .expect("missing splatted arg or self in method call"), - } - } else { - TupleArgumentsFlag::DontTupleArguments - }; + let tuple_arguments = if method.sig.splatted { + // self is already handled as `rcvr`, so it's never splatted here + TupleArgumentsFlag::TupleSplattedArguments + } else { + TupleArgumentsFlag::DontTupleArguments + }; self.check_argument_types( segment.ident.span, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 513d5d9814319..f4be489b720aa 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -286,15 +286,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err_code = E0061; // If the arguments should be wrapped in a tuple (ex: closures, splats), unwrap them here - let (formal_input_tys, expected_input_tys) = if let Some(tupled_arg_index) = - tuple_arguments.tupled_argument_position() - { - let tupled_arg_index = usize::from(tupled_arg_index); - assert_eq!( - Some(tupled_arg_index), - formal_input_tys.len().checked_sub(1), - "only trailing tupled arguments are implemented", - ); + let (formal_input_tys, expected_input_tys) = if tuple_arguments.last_argument_is_tupled() { + // An empty argument list should be a single unit type argument in the callee. + // The Fn* traits ensure this by construction, and `#[splat]` can only be applied to an actual argument. + let tupled_arg_index = + formal_input_tys.len().checked_sub(1).expect("must have a tuple argument"); // Keep the type variable if the argument is splatted, so we can force it to be a tuple later. let tuple_type = if tuple_arguments.is_splatted() { let tuple_type = self diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index e38d2e1751e56..346a4f4ea7553 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -638,16 +638,19 @@ enum TupleArgumentsFlag { DontTupleArguments, /// All arguments are tupled before typechecking. TupleAllArguments, - /// Only the splatted arguments are tupled before typechecking. - TupleSplattedArguments { splatted_arg_index: u16 }, + /// Only the splatted final argument is tupled before typechecking. + TupleSplattedArguments, } impl TupleArgumentsFlag { - fn tupled_argument_position(&self) -> Option { + // Is the last argument in the callee tupled? + fn last_argument_is_tupled(&self) -> bool { match self { - Self::DontTupleArguments => None, - Self::TupleAllArguments => Some(0), - Self::TupleSplattedArguments { splatted_arg_index } => Some(*splatted_arg_index), + Self::DontTupleArguments => false, + // The callee always has a single tuple argument. + Self::TupleAllArguments => true, + // The last argument of the callee is a splatted tuple in the caller. + Self::TupleSplattedArguments { .. } => true, } } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index a25dc989096aa..8a0a9a42f86ea 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -777,14 +777,6 @@ impl FnSig { let FnSig { safety, abi, c_variadic, .. } = self; !c_variadic && safety.is_safe() && abi.is_rust() } - - pub fn splatted_arg_index(self) -> Option { - // A function with no inputs behaves the same regardless of splatting. - let last_index = self.inputs().len().checked_sub(1)?; - - debug_assert!(last_index <= u16::MAX as usize); - self.splatted.then_some(last_index as u16) - } } impl ty::Binder> { @@ -852,16 +844,20 @@ impl fmt::Debug for FnSig { write!(f, "fn(")?; let inputs = sig.inputs(); - let splatted_arg_index = sig.splatted_arg_index(); + // Always the last argument. + let splatted_arg_index = self.splatted.then(|| inputs.len().checked_sub(1)).flatten(); for (i, ty) in inputs.iter().enumerate() { if i > 0 { write!(f, ", ")?; } - if splatted_arg_index == Some(i as u16) { + if splatted_arg_index == Some(i) { write!(f, "#[splat] ")?; } write!(f, "{ty:?}")?; } + if self.splatted && inputs.is_empty() { + write!(f, "#[splat] ()")?; + } if *c_variadic { if inputs.is_empty() { write!(f, "...")?; diff --git a/tests/ui/splat/splat-overload-at-home.rs b/tests/ui/splat/splat-overload-at-home.rs index 3f0f039af7abe..c16b781e72bb1 100644 --- a/tests/ui/splat/splat-overload-at-home.rs +++ b/tests/ui/splat/splat-overload-at-home.rs @@ -35,8 +35,7 @@ impl Foo { fn main() { let foo = Foo; - // FIXME(splat): incorrectly assumes that splat must have one or more arguments to tuple - //foo.method::<()>(); + foo.method::<()>(); //~ ERROR broken MIR // FIXME(splat): generic tuple trait implementers should work without explicit tuple type parameters. // actually modify the argument list during typeck, to avoid "broken MIR" errors diff --git a/tests/ui/splat/splat-tuple.rs b/tests/ui/splat/splat-tuple.rs index f9eb7827d6656..2b8e8058e3300 100644 --- a/tests/ui/splat/splat-tuple.rs +++ b/tests/ui/splat/splat-tuple.rs @@ -95,6 +95,5 @@ fn main() { // FIXME(splat): generic tuple trait implementers should work without explicit tuple type parameters. splat_generic_tuple::<(u32, i8)>(1u32, 2i8); - // FIXME(splat): incorrectly assumes that splat must have one or more arguments to tuple - //splat_generic_tuple::<()>(); + splat_generic_tuple::<()>(); } From 57d83fc9fff95745063cd1cfdb899dbb64b3347a Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 20 Mar 2026 11:37:47 +1000 Subject: [PATCH 08/10] Appease tidy --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 3 ++- tests/ui/splat/splat-non-tuple.rs | 9 ++++++--- tests/ui/splat/splat-overload-at-home.rs | 6 ++++-- tests/ui/splat/splat-tuple.rs | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index f4be489b720aa..38eb4558fe9ad 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -298,7 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if tuple_type.is_ty_var() { let tuple_len = provided_args.len().checked_sub(tupled_arg_index); if let Some(tuple_len) = tuple_len { - // TODO: how do we force the type variable to resolve to (a supertype) of the caller's tupled argument types? + // FIXME(splat before merging): how do we force the type variable to resolve to (a supertype) of the caller's tupled argument types? Ty::new_tup_from_iter( self.tcx, iter::repeat_with(|| self.next_ty_var(call_span)).take(tuple_len), @@ -339,6 +339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } None => None, }; + // FIXME(splat before merging): if splatting, update the caller's arguments to be a tuple, so MIR typecheck passes ( formal_input_tys[..tupled_arg_index] .into_iter() diff --git a/tests/ui/splat/splat-non-tuple.rs b/tests/ui/splat/splat-non-tuple.rs index 3287ed1ca203e..e87ddda794746 100644 --- a/tests/ui/splat/splat-non-tuple.rs +++ b/tests/ui/splat/splat-non-tuple.rs @@ -5,14 +5,16 @@ #![feature(splat)] #![expect(unused)] -fn primitive_arg(#[splat] x: u32) {} // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple +// FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple +fn primitive_arg(#[splat] x: u32) {} enum NotATuple { A(u32), B(i8), } -fn enum_arg(#[splat] y: NotATuple) {} // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple +// FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple +fn enum_arg(#[splat] y: NotATuple) {} trait FooTrait { fn tuple_1(#[splat] _: (u32,)); @@ -50,7 +52,8 @@ impl FooTrait for Foo { struct TupleStruct(u32, i8); // FIXME(splat): tuple structs should error until we have a specific use case for them. -fn tuple_struct_arg(#[splat] z: TupleStruct) {} // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple +// FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple +fn tuple_struct_arg(#[splat] z: TupleStruct) {} impl TupleStruct { fn tuple_2( diff --git a/tests/ui/splat/splat-overload-at-home.rs b/tests/ui/splat/splat-overload-at-home.rs index c16b781e72bb1..02980a263a6b0 100644 --- a/tests/ui/splat/splat-overload-at-home.rs +++ b/tests/ui/splat/splat-overload-at-home.rs @@ -3,6 +3,7 @@ //@ dont-require-annotations: ERROR // FIXME(splat): ^change the actual types during typeck so MIR doesn't ICE. +// ignore-tidy-linelength //! Test using `#[splat]` on some "overloading at home" example code. //! @@ -37,8 +38,9 @@ fn main() { foo.method::<()>(); //~ ERROR broken MIR - // FIXME(splat): generic tuple trait implementers should work without explicit tuple type parameters. - // actually modify the argument list during typeck, to avoid "broken MIR" errors + // FIXME(splat): + // - generic tuple trait implementers should work without explicit tuple type parameters. + // - actually modify the argument list during typeck, to avoid "broken MIR" errors. foo.method::<(i32,)>(42i32); //~ ERROR broken MIR // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? // Add a tupled test for each call if they are. diff --git a/tests/ui/splat/splat-tuple.rs b/tests/ui/splat/splat-tuple.rs index 2b8e8058e3300..9826f1c3cdc63 100644 --- a/tests/ui/splat/splat-tuple.rs +++ b/tests/ui/splat/splat-tuple.rs @@ -62,7 +62,8 @@ extern "C" { // FIXME(splat): multiple splats in a fn should error. fn multisplat_bad_2(#[splat] (a, b): (u32, i8), #[splat] (c, d): (u32, i8)) {} -// FIXME(splat): non-terminal splat attributes should error, until we have a specific use case for them. +// FIXME(splat): non-terminal splat attributes should error, until we have a specific use case for +// them. fn splat_non_terminal_bad(#[splat] (a, b): (u32, i8), (c, d): (u32, i8)) {} fn splat_generic_tuple(#[splat] t: T) {} @@ -92,7 +93,8 @@ fn main() { // FIXME(splat): this should error because `self` is splatted, but `TupleStruct` is not a tuple. tuple_struct.tuple_4_trait((1u32, 2i8, (), 3f32)); - // FIXME(splat): generic tuple trait implementers should work without explicit tuple type parameters. + // FIXME(splat): generic tuple trait implementers should work without explicit tuple type + // parameters. splat_generic_tuple::<(u32, i8)>(1u32, 2i8); splat_generic_tuple::<()>(); From d07b805a445a30e682f98f85270f168fa17533df Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 20 Mar 2026 12:21:38 +1000 Subject: [PATCH 09/10] Make gcc and cranelift codegen happy --- compiler/rustc_codegen_cranelift/src/value_and_place.rs | 7 +++++++ compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 5b76a4cb97793..1743bf9850c19 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -873,6 +873,7 @@ pub(crate) fn assert_assignable<'tcx>( let FnSig { inputs_and_output: types_from, c_variadic: c_variadic_from, + splatted: splatted_from, safety: unsafety_from, abi: abi_from, } = from_sig; @@ -881,6 +882,7 @@ pub(crate) fn assert_assignable<'tcx>( let FnSig { inputs_and_output: types_to, c_variadic: c_variadic_to, + splatted: splatted_to, safety: unsafety_to, abi: abi_to, } = to_sig; @@ -898,6 +900,11 @@ pub(crate) fn assert_assignable<'tcx>( "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", from_sig, to_sig, fx, ); + assert_eq!( + splatted_from, splatted_to, + "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", + from_sig, to_sig, fx, + ); assert_eq!( unsafety_from, unsafety_to, "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 3f1b33c73e638..36675988b03eb 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -1472,6 +1472,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( iter::once(i8p), tcx.types.unit, false, + false, rustc_hir::Safety::Unsafe, ExternAbi::Rust, )), @@ -1483,6 +1484,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( [i8p, i8p].iter().cloned(), tcx.types.unit, false, + false, rustc_hir::Safety::Unsafe, ExternAbi::Rust, )), @@ -1492,6 +1494,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( [try_fn_ty, i8p, catch_fn_ty], tcx.types.i32, false, + false, rustc_hir::Safety::Unsafe, ExternAbi::Rust, )); From 4072dbb0a8c5fa6badd16fc8914ec9e52b59ec8f Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 20 Mar 2026 13:03:14 +1000 Subject: [PATCH 10/10] Add splat to clippy, miri, fixme to r-a --- src/tools/clippy/clippy_lints/src/eta_reduction.rs | 3 ++- src/tools/miri/src/bin/miri.rs | 1 + src/tools/miri/src/helpers.rs | 1 + src/tools/miri/src/shims/sig.rs | 1 + src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 1 + src/tools/rust-analyzer/crates/hir-ty/src/lower.rs | 1 + 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 3562200cbd929..a813ca2496d56 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -173,7 +173,8 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx && let output = typeck.expr_ty(body.value) && let ty::Tuple(tys) = *subs.type_at(1).kind() { - cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, ExternAbi::Rust) + cx.tcx + .mk_fn_sig(tys, output, false, false, Safety::Safe, ExternAbi::Rust) } else { return; } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index e73c870508bd0..0c6f49669e1da 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -102,6 +102,7 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) { [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))], tcx.types.isize, false, + false, hir::Safety::Safe, ExternAbi::Rust, )); diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index a40ad4b55317f..d0c32896afd68 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -409,6 +409,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { args.iter().map(|a| a.layout.ty), dest.layout.ty, /*c_variadic*/ false, + /*splatted*/ false, Safety::Safe, caller_abi, ); diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index 43b913edbebf5..14443edfa0ed7 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -276,6 +276,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fn_sig_binder = Binder::dummy(FnSig { inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), c_variadic: false, + splatted: false, // This does not matter for the ABI. safety: Safety::Safe, abi: shim_sig.abi, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 8d87276a0b152..208fbd1aaee94 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -584,6 +584,7 @@ pub fn callable_sig_from_fn_trait<'db>( Binder::dummy(FnSig { inputs_and_output, c_variadic: false, + // FIXME(splat): handle splatted arguments safety: abi::Safety::Safe, abi: FnAbi::RustCall, }), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 49594f34fd732..64ef71dea35fa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -513,6 +513,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, c_variadic: fn_.is_varargs, + // FIXME(splat): handle splatted arguments inputs_and_output: Tys::new_from_slice(&args), }), )