diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 11a531353eba0..bd5ce5d18b839 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3059,13 +3059,15 @@ impl FnDecl { } /// The marker index for "no splatted arguments". + /// Higher values are also not supported, for performance reasons. + /// /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`. - pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Returns a splatted argument index, if any are present. - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.inputs.iter().enumerate().find_map(|(index, arg)| { - if index == Self::NO_SPLATTED_ARG_INDEX as usize { + if index >= usize::from(Self::NO_SPLATTED_ARG_INDEX) { // AST validation has already checked the splatted argument index is valid, so just // ignore invalid indexes here. None @@ -3073,7 +3075,7 @@ impl FnDecl { arg.attrs .iter() .any(|attr| attr.has_name(sym::splat)) - .then_some(u16::try_from(index).unwrap()) + .then_some(u8::try_from(index).unwrap()) } }) } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index fc466246bf137..078ebd6987ae3 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -93,7 +93,7 @@ struct ParamInfo { pub c_variadic: bool, /// The index of the splatted parameter, if any. - pub splatted: Option, + pub splatted: Option, } const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; @@ -364,11 +364,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn param_info(&self, def_id: DefId) -> ParamInfo { let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); - // FIXME(splat): use `sig.splatted()` once FnSig has it ParamInfo { param_count: sig.inputs().len() + usize::from(sig.c_variadic()), c_variadic: sig.c_variadic(), - splatted: None, + splatted: sig.splatted(), } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 186a43ca1693b..06925994b052c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -412,11 +412,11 @@ impl<'a> AstValidator<'a> { }) .unzip(); - // A splatted argument at the "no splatted" marker index is not supported (this is an - // unlikely edge case). + // A splatted argument greater than or equal to the "no splatted" marker index is not + // supported. if let (Some(&splatted_arg_index), Some(&splatted_span)) = (splatted_arg_indexes.last(), splatted_spans.last()) - && splatted_arg_index == FnDecl::NO_SPLATTED_ARG_INDEX + && splatted_arg_index >= u16::from(FnDecl::NO_SPLATTED_ARG_INDEX) { self.dcx().emit_err(diagnostics::InvalidSplattedArg { splatted_arg_index, diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 8f965a5ef7b12..012951098fa15 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -271,6 +271,7 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ .map(|local| { let arg_ty = fx.monomorphize(fx.mir.local_decls[local].ty); + // FIXME(splat): un-tuple splatted arguments in codegen, for performance // Adapted from https://github.com/rust-lang/rust/blob/145155dc96757002c7b2e9de8489416e2fdbbd57/src/librustc_codegen_llvm/mir/mod.rs#L442-L482 if Some(local) == fx.mir.spread_arg { // This argument (e.g. the last argument in the "rust-call" ABI) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 9b8d05c9f72c0..5ece363fcd8d9 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -375,6 +375,7 @@ fn push_debuginfo_type_name<'tcx>( output.push_str("fn("); } + // FIXME(splat): should debuginfo be de-tupled in the callee (and caller)? if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { push_debuginfo_type_name(tcx, parameter_type, true, output, visited); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 115c50edf4e9f..00cfe1c845d7a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1202,6 +1202,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 67c9a85ca408f..68fed6c867fa5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -433,6 +433,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/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 3d8d9592459c4..8f58e39d419ce 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -7,7 +7,7 @@ use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, Const, FnHeader, FnSigTys, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, Const, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt}; use rustc_span::{Symbol, sym}; use crate::const_eval::CompileTimeMachine; @@ -436,6 +436,22 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sym::variadic => { self.write_scalar(Scalar::from_bool(fn_sig_kind.c_variadic()), &field_place)?; } + sym::is_splatted => { + self.write_scalar( + Scalar::from_bool(fn_sig_kind.splatted().is_some()), + &field_place, + )?; + } + sym::splatted_index => { + self.write_scalar( + Scalar::from_u8( + // Currently the same encoding as FnSigKind.splatted + // FIXME(splat): make these two fields into a single Option, or choose a stable encoding + fn_sig_kind.splatted().unwrap_or(FnSigKind::NO_SPLATTED_ARG_INDEX), + ), + &field_place, + )?; + } other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 44420b7148478..e2fe8c3a79690 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -681,6 +681,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_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f429d14e3a51c..6d2e68d20bf30 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4045,12 +4045,12 @@ pub struct Param<'hir> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SplattedArgIndexError { /// The splatted argument index is invalid. - /// Currently the only unsupported index is `u16::MAX`, which is used to indicate that no argument - /// is splatted. - InvalidIndex { splatted_arg_index: u16 }, + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, /// The splatted argument index is outside the bounds of the function arguments. - OutOfBounds { splatted_arg_index: u16, args_len: u16 }, + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, } /// Contains the packed non-type fields of a function declaration. @@ -4061,9 +4061,9 @@ pub struct FnDeclFlags { flags: u8, /// Which function argument is splatted into multiple arguments in callers, if any? - /// Splatting functions with `u16::MAX` arguments is not supported, see `FnSigKind` for + /// Splatting functions with `>= u8::MAX` arguments is not supported, see `FnSigKind` for /// details. - splatted: u16, + splatted: u8, } impl fmt::Debug for FnDeclFlags { @@ -4101,13 +4101,13 @@ impl FnDeclFlags { /// Marker index for "no splatted argument". /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. - const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Create a new FnDeclKind with no implicit self, no lifetime elision, no C-style variadic /// argument, and no splatting. /// To modify these flags, use the `set_*` methods, for readability. // FIXME: use Default instead when that trait is const stable. - pub const fn default() -> Self { + pub fn default() -> Self { Self { flags: 0, splatted: 0 } .set_implicit_self(ImplicitSelfKind::None) .set_lifetime_elision_allowed(false) @@ -4117,7 +4117,7 @@ impl FnDeclFlags { /// Set the implicit self kind. #[must_use = "this method does not modify the receiver"] - pub const fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { + pub fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { self.flags &= !Self::IMPLICIT_SELF_MASK; match implicit_self { @@ -4133,7 +4133,7 @@ impl FnDeclFlags { /// Set the C-style variadic argument flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_c_variadic(mut self, c_variadic: bool) -> Self { + pub fn set_c_variadic(mut self, c_variadic: bool) -> Self { if c_variadic { self.flags |= Self::C_VARIADIC_FLAG; } else { @@ -4145,7 +4145,7 @@ impl FnDeclFlags { /// Set the lifetime elision allowed flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { + pub fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { if allowed { self.flags |= Self::LIFETIME_ELISION_ALLOWED_FLAG; } else { @@ -4158,16 +4158,17 @@ impl FnDeclFlags { /// Set the splatted argument index. /// The number of function arguments is used for error checking. #[must_use = "this method does not modify the receiver"] - pub const fn set_splatted( + pub fn set_splatted( mut self, - splatted: Option, + splatted: Option, args_len: usize, ) -> Result { if let Some(splatted_arg_index) = splatted { if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); - } else if splatted_arg_index as usize >= args_len { + } else if usize::from(splatted_arg_index) >= args_len { return Err(SplattedArgIndexError::OutOfBounds { splatted_arg_index, args_len: args_len as u16, @@ -4184,14 +4185,14 @@ impl FnDeclFlags { /// Set "no splatted arguments" for the function declaration. #[must_use = "this method does not modify the receiver"] - pub const fn set_no_splatted_args(mut self) -> Self { + pub fn set_no_splatted_args(mut self) -> Self { self.splatted = Self::NO_SPLATTED_ARG_INDEX; self } /// Get the implicit self kind. - pub const fn implicit_self(self) -> ImplicitSelfKind { + pub fn implicit_self(self) -> ImplicitSelfKind { match self.flags & Self::IMPLICIT_SELF_MASK { 0 => ImplicitSelfKind::None, 1 => ImplicitSelfKind::Imm, @@ -4203,17 +4204,17 @@ impl FnDeclFlags { } /// Do the function arguments end with a C-style variadic argument? - pub const fn c_variadic(self) -> bool { + pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } /// Is lifetime elision allowed? - pub const fn lifetime_elision_allowed(self) -> bool { + pub fn lifetime_elision_allowed(self) -> bool { self.flags & Self::LIFETIME_ELISION_ALLOWED_FLAG != 0 } /// Get the splatted argument index, if any. - pub const fn splatted(self) -> Option { + pub fn splatted(self) -> Option { if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } } } @@ -4263,7 +4264,7 @@ impl<'hir> FnDecl<'hir> { self.fn_decl_kind.lifetime_elision_allowed() } - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.fn_decl_kind.splatted() } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index e1bbc8f0641de..9d8e0a512ab66 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -444,11 +444,13 @@ fn fn_sig_suggestion<'tcx>( predicates: impl IntoIterator, Span)>, assoc: ty::AssocItem, ) -> String { + let splatted_arg_index = sig.splatted().map(usize::from); let args = sig .inputs() .iter() .enumerate() .map(|(i, ty)| { + let splat = if splatted_arg_index == Some(i) { "#[splat] " } else { "" }; let arg_ty = match ty.kind() { ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(), ty::Ref(reg, ref_ty, mutability) if i == 0 => { @@ -477,7 +479,7 @@ fn fn_sig_suggestion<'tcx>( } } }; - Some(format!("{arg_ty}")) + Some(format!("{splat}{arg_ty}")) }) .chain(std::iter::once(if sig.c_variadic() { Some("...".to_string()) } else { None })) .flatten() diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index bf8593eb61882..b89fc81190d43 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1714,7 +1714,16 @@ fn check_fn_or_method<'tcx>( let span = tcx.def_span(def_id); let has_implicit_self = hir_decl.implicit_self().has_implicit_self(); let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 }); - // FIXME(splat): use `sig.splatted()` once FnSig has it + // FIXME(splat): support the rest of closure splatting, or replace this code with an error + if let Some(mut splatted_arg_index) = sig.splatted() { + let mut inputs_count = sig.inputs().len(); + if has_implicit_self { + splatted_arg_index = splatted_arg_index.strict_sub(1); + inputs_count = inputs_count.strict_sub(1); + } + debug!(?splatted_arg_index, ?inputs_count, ?has_implicit_self, ?sig); + sig = sig.set_splatted(Some(splatted_arg_index), inputs_count).unwrap(); + } // Check that the argument is a tuple and is sized if let Some(ty) = inputs.next() { wfcx.register_bound( 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 baa9fddc2a651..95a91f1444404 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3582,11 +3582,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?output_ty); debug!(?abi, ?safety, ?decl.fn_decl_kind, input_tys_len = ?input_tys.len()); - // FIXME(splat): use `set_splatted()` once FnSig has it let fn_sig_kind = FnSigKind::default() .set_abi(abi) .set_safety(safety) - .set_c_variadic(decl.fn_decl_kind.c_variadic()); + .set_c_variadic(decl.fn_decl_kind.c_variadic()) + .set_splatted(decl.splatted(), input_tys.len()) + .unwrap(); let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, fn_sig_kind); let fn_ptr_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 57ab29ac752ad..859e3463cb79f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -58,9 +58,13 @@ 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. + /// Includes functions with splatted arguments. 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>), @@ -544,7 +548,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let (fn_sig, def_id) = match *callee_ty.kind() { + let (fn_sig, def_id, callee_generic_args) = 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).skip_norm_wip(); @@ -573,11 +577,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } } - (fn_sig, Some(def_id)) + (fn_sig, Some(def_id), Some(args)) } // FIXME(const_trait_impl): these arms should error because we can't enforce them - ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None), + ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None, None), _ => unreachable!(), }; @@ -595,12 +599,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = self.normalize(call_expr.span, Unnormalized::new_wip(fn_sig)); self.check_argument_types_maybe_method_like( - &fn_sig, call_expr, arg_exprs, expected, def_id, + &fn_sig, + call_expr, + arg_exprs, + expected, + TupleArgumentsFlag::with_fn_sig_kind(fn_sig.fn_sig_kind, false), + def_id, + callee_generic_args, ); + // Splatting is currently incompatible with RustCall. if fn_sig.abi() == rustc_abi::ExternAbi::RustCall { let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); - if let Some(ty) = fn_sig.inputs().last().copied() { + if let Some(ty) = fn_sig.inputs().last().copied() + && fn_sig.splatted().is_none() + { self.register_bound( ty, self.tcx.require_lang_item(hir::LangItem::Tuple, sp), @@ -627,7 +640,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, + tuple_arguments_flag: TupleArgumentsFlag, def_id: Option, + callee_generic_args: Option>, ) { let do_check = || { self.check_argument_types( @@ -638,8 +653,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, arg_exprs, fn_sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + tuple_arguments_flag, def_id, + callee_generic_args, ); }; @@ -1009,9 +1025,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig.output(), expected, arg_exprs, - fn_sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + fn_sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(closure_def_id.to_def_id()), + None, ); fn_sig.output() @@ -1092,6 +1109,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, method: MethodCallee<'tcx>, ) -> Ty<'tcx> { + // FIXME(splat): if we ever support splatting here, decrement the splatted index, because + // the receiver argument is removed below. + assert_eq!( + method.sig.fn_sig_kind.splatted(), + None, + "splatting is not supported on RustCall tuples", + ); self.check_argument_types( call_expr.span, call_expr, @@ -1099,9 +1123,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method.sig.output(), expected, arg_exprs, - method.sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + method.sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(method.def_id), + None, ); self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 41aebea1c7ebe..95e67b135e411 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -720,6 +720,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in this binder we are creating. assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); let bound_sig = expected_sig.sig.map_bound(|sig| { + // Ignore splatting, it is unsupported on closures. let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) .set_safety(hir::Safety::Safe) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6e4d6aa80a63a..bd8295ba3a328 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -3,11 +3,11 @@ use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, find_attr}; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_middle::bug; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, BottomUpFolder, Ty, TypeFoldable, TypeVisitableExt}; +use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; @@ -402,9 +402,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unify the method signature with our incompatible arg, to // do inference in the *opposite* direction and to find out // what our ideal rcvr ty would look like. + let Some(input_arg) = method.sig.inputs().get(idx + 1) else { + if method.sig.splatted().is_some() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + return None; + } else { + span_bug!( + self.tcx.def_span(method.def_id), + "arg index {} out of bounds for method with {} inputs", + idx + 1, + method.sig.inputs().len(), + ); + } + }; let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::Yes, method.sig.inputs()[idx + 1], arg_ty) + .eq(DefineOpaqueTypes::Yes, *input_arg, arg_ty) .ok()?; self.select_obligations_where_possible(|errs| { // Yeet the errors, we're already reporting errors. diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index a9a819935287c..a5c39bd1584a1 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -100,6 +100,7 @@ impl IntoDiagArg for ReturnLikeStatementKind { } } +// FIXME(splat): add "non-splatted" to all 4 instances of this error message #[derive(Diagnostic)] #[diag("functions with the \"rust-call\" ABI must take a single non-self tuple argument")] pub(crate) struct RustCallIncorrectArgs { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 0e13f7a4bfe1c..22c211b199474 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1463,16 +1463,23 @@ 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 + // self is already handled as `rcvr`, so it's never splatted here + let method_inputs = &method.sig.inputs()[1..]; + let method_tuple_args_flag = + TupleArgumentsFlag::with_fn_sig_kind(method.sig.fn_sig_kind, true); + self.check_argument_types( segment.ident.span, expr, - &method.sig.inputs()[1..], + method_inputs, method.sig.output(), expected, args, - method.sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + method.sig.fn_sig_kind.c_variadic(), + method_tuple_args_flag, Some(method.def_id), + Some(method.args), ); self.check_call_abi(method.sig.abi(), expr.span); @@ -1495,6 +1502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false, TupleArgumentsFlag::DontTupleArguments, None, + Some(GenericArgsRef::default()), ); err_output diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c8c6de4f99c03..db2fb43522e14 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, - SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Unnormalized, - UserArgs, UserSelfTy, + SizedTraitKind, SplattedDef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, + Unnormalized, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -143,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// also select obligations if it seems useful, in an effort /// to get more type information. // FIXME(-Znext-solver): A lot of the calls to this method should - // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead. + // probably be `resolve_vars_with_obligations` or `structurally_resolve_type` instead. #[instrument(skip(self), level = "debug", ret)] pub(crate) fn resolve_vars_with_obligations>>( &self, @@ -236,6 +236,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_resolution( + &self, + hir_id: HirId, + r: Result, + ) { + self.typeck_results.borrow_mut().splatted_defs_mut().insert(hir_id, r); + } + #[instrument(level = "debug", skip(self))] pub(crate) fn write_method_call_and_enforce_effects( &self, @@ -248,6 +257,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_args(hir_id, method.args); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_call( + &self, + hir_id: HirId, + span: Span, + callee_def_id: Option, + callee_generic_args: Option>, + first_tupled_arg_index: u16, + tupled_args_count: u16, + ) { + // FIXME(const_trait_impl): enforce constness using enforce_context_effects() and add + // _and_enforce_effects to this method's name + + self.write_splatted_resolution( + hir_id, + Ok(SplattedDef { + def_id: callee_def_id, + arg_index: first_tupled_arg_index, + arg_count: tupled_args_count, + }), + ); + if let Some(callee_generic_args) = callee_generic_args { + self.write_args(hir_id, callee_generic_args); + } + } + fn write_args(&self, node_id: HirId, args: GenericArgsRef<'tcx>) { if !args.is_empty() { debug!("write_args({:?}, {:?}) in fcx {}", node_id, args, self.tag()); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 890110e431fa2..9a773cc7808d4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -50,6 +50,19 @@ rustc_index::newtype_index! { pub(crate) struct GenericIdx {} } +/// Outcome of checking arguments that are tupled by "rust-call" or `#[splat]`. +#[derive(Debug, Clone, Eq, PartialEq)] +struct TupledArgCheckOutcome<'tcx> { + /// The error code to emit if the arguments are not compatible. + new_err_code: Option, + + /// The formal input types after checking. + untupled_formal_input_tys: Vec>, + + /// The expected input types after checking. + untupled_expected_input_tys: Option>>, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { let mut deferred_cast_checks = self.root_ctxt.deferred_cast_checks.borrow_mut(); @@ -185,13 +198,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expectation: Expectation<'tcx>, // The expressions for each provided argument provided_args: &'tcx [hir::Expr<'tcx>], - // Whether the function is variadic, for example when imported from C - // FIXME(splat): maybe change this to FnSigKind? + // Whether the function is variadic (e.g. from C) c_variadic: bool, - // Whether the arguments have been bundled in a tuple (ex: closures) + // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, // The DefId for the function being called, for better error messages fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, ) { let tcx = self.tcx; @@ -220,11 +234,12 @@ 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 + let mut expected_input_tys: Option> = expectation .only_has_type(self) .and_then(|expected_output| { // FIXME(#149379): This operation results in expected input @@ -272,45 +287,34 @@ 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]); - 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() { - 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, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // 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(); - (self.err_args(provided_args.len(), guar), None) - } + let mut formal_input_tys = formal_input_tys.to_vec(); + + // If the arguments should be wrapped in a tuple (ex: closures, splats), unwrap them here + if tuple_arguments.is_tupled() { + // Caller arguments are tupled before typechecking, starting at the given index. + // Tupling makes the callee and caller argument counts match. + let outcome = self.check_tupled_arguments( + call_span, + call_expr, + formal_input_tys, + provided_args, + expected_input_tys, + c_variadic, + tuple_arguments, + fn_def_id, + callee_generic_args, + ); + let TupledArgCheckOutcome { + new_err_code, + untupled_formal_input_tys, + untupled_expected_input_tys, + } = outcome; + if let Some(new_err_code) = new_err_code { + err_code = new_err_code; } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; + formal_input_tys = untupled_formal_input_tys; + expected_input_tys = untupled_expected_input_tys; + } // If there are no external expectations at the call site, just use the types from the function defn let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { @@ -556,6 +560,257 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Check arguments that are tupled by "rust-call" or `#[splat]`. + fn check_tupled_arguments( + &self, + // Span enclosing the call site + call_span: Span, + // Expression of the call site + call_expr: &'tcx hir::Expr<'tcx>, + // Types (as defined in the *signature* of the target function) + mut formal_input_tys: Vec>, + // The expressions for each provided argument + provided_args: &'tcx [hir::Expr<'tcx>], + // The expected input types from the context of the call site + mut expected_input_tys: Option>>, + // Whether the function is variadic (e.g. from C) + c_variadic: bool, + // Whether all the arguments have been bundled in a tuple (ex: closures). + // Splatting is handled separately. + tuple_arguments: TupleArgumentsFlag, + // The DefId for the function being called, for better error messages + fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, + ) -> TupledArgCheckOutcome<'tcx> { + let (first_tupled_arg_index, is_self_splatted) = tuple_arguments.tupled_arg_index(); + let Some(first_tupled_arg_index) = first_tupled_arg_index else { + // If we're not tupling any of the current arguments, we're done. + return TupledArgCheckOutcome { + new_err_code: None, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + }; + }; + + // The argument difference can range from -1 to u16::MAX - 1, so we count the number + // of tupled arguments instead. + // (An empty argument list becomes a unit tuple in the callee.) + // 0: f() -> f(#[splat] _: ()) + // 1: f(a) -> f(#[splat] _: (A,)) + // 2: f(a, b) -> f(#[splat] _: (A, B)) + // The Fn* traits ensure this by construction, and `#[splat]` can only be applied to + // an actual argument. + let tupled_args_count = (1 + provided_args.len()).checked_sub(formal_input_tys.len()); + debug!( + ?first_tupled_arg_index, ?is_self_splatted, + ?tupled_args_count, ?tuple_arguments, ?c_variadic, + provided_args_len = ?provided_args.len(), formal_input_tys_len = ?formal_input_tys.len() + ); + + // If earlier code has modified the FnSig argument list without adjusting the splatted + // argument, indexing into the formal input types will panic. + if first_tupled_arg_index >= formal_input_tys.len() { + span_bug!( + call_span, + "splatted argument index is out of bounds: {first_tupled_arg_index:?} >= {}, \ + is_self_splatted = {is_self_splatted:?}, \ + tupled_args_count = {tupled_args_count:?}, {tuple_arguments:?}, \ + c_variadic = {c_variadic:?}, provided_args: {}", + formal_input_tys.len(), + provided_args.len(), + ); + } + + // 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 callee_tuple_type = + self.resolve_vars_with_obligations(formal_input_tys[first_tupled_arg_index]); + if callee_tuple_type.is_ty_var() + && let Some(tupled_args_count) = tupled_args_count + { + // Make the original type variable resolve to a tuple containing new type variables + let ocx = ObligationCtxt::new(self); + let origin = self.misc(call_span); + + let new_tupled_type = Ty::new_tup_from_iter( + self.tcx, + iter::repeat_with(|| self.next_ty_var(call_span)).take(tupled_args_count), + ); + + // FIXME(splat): should this be a sub/super type relationship? + let ocx_error = ocx.eq(&origin, self.param_env, callee_tuple_type, new_tupled_type); + if let Err(ocx_error) = ocx_error { + // FIXME(splat): add a test for this error and the one below, if they are reachable + struct_span_code_err!( + self.dcx(), + call_span, + // FIXME(splat): add a new error code before stabilization (and below as well) + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + ocx_error, + ) + .emit(); + } + + let type_errors = ocx.try_evaluate_obligations(); + if type_errors.is_empty() { + new_tupled_type + } else { + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + type_errors, + ) + .emit(); + Ty::new_error(self.tcx, guar) + } + } else { + // Otherwise, just let the argument type checker make a suggestion + callee_tuple_type + } + } else { + self.structurally_resolve_type(call_span, formal_input_tys[first_tupled_arg_index]) + }; + + // We expected a tuple and got a tuple (or made one ourselves). + // If it's not a tuple, we error out in the next block. + let mut err_code = None; + if let ty::Tuple(detup_formal_arg_tys) = tuple_type.kind() { + // Argument length differs + // FIXME(splat): update the error code E0057 docs when splat is stabilized + if Some(detup_formal_arg_tys.len()) != tupled_args_count { + err_code = Some(E0057); + } + if let Some(ref mut expected_input_tys) = expected_input_tys + && let Some(ty) = expected_input_tys.get(first_tupled_arg_index) + && let ty::Tuple(detup_expected_arg_tys) = ty.kind() + { + let substitute_tys = if Some(detup_expected_arg_tys.len()) == tupled_args_count { + detup_expected_arg_tys.iter() + } else { + // Just fall back to the formal argument types + detup_formal_arg_tys.iter() + }; + + expected_input_tys + .splice(first_tupled_arg_index..=first_tupled_arg_index, substitute_tys); + } else { + expected_input_tys = None; + } + // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments + if tuple_arguments.is_splatted() { + // FIXME(const_trait_impl): does not enforce constness yet + self.write_splatted_call( + call_expr.hir_id, + call_span, + fn_def_id, + callee_generic_args, + first_tupled_arg_index.try_into().unwrap(), + tupled_args_count.unwrap().try_into().unwrap(), + ); + } + + formal_input_tys.splice( + first_tupled_arg_index..=first_tupled_arg_index, + detup_formal_arg_tys.iter(), + ); + if let Some(ref expected_input_tys) = expected_input_tys { + assert_eq!( + formal_input_tys.len(), + expected_input_tys.len(), + "incorrectly constructed input type tuples, argument counts must match: \ + tuple_arguments: {tuple_arguments:?}", + ) + } + } + + // Otherwise, there's a mismatch during splatting or a rust-call. + // 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 = + if tuple_arguments == TupleAllCallArgs && !matches!(tuple_type.kind(), ty::Tuple(_)) { + 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(); + + Some(guar) + } else if tuple_arguments.is_splatted() { + // If we don't check argument counts here, and there's a subtle bug in the code above, + // later compilation stages can fail in unrelated places with confusing errors. + if !matches!(tuple_type.kind(), ty::Tuple(_)) { + let spans = if let Some(def_id) = fn_def_id + && let Some(hir_node) = self.tcx.hir_get_if_local(def_id) + && let Some(fn_decl) = hir_node.fn_decl() + && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index) + { + let arg_def_span = arg_ty.span; + vec![call_span, arg_def_span] + } else { + vec![call_span] + }; + let guar = struct_span_code_err!( + self.dcx(), + spans, + // FIXME(splat): add a new error code before stabilization + E0277, + "cannot use splat attribute; the splatted argument type \ + must be a tuple or unit, not a {:?} ({:?})", + tuple_type.kind(), + self.structurally_resolve_type( + call_span, + formal_input_tys[first_tupled_arg_index] + ) + .kind(), + ) + .emit(); + + Some(guar) + } else if formal_input_tys.len() != provided_args.len() { + // FIXME(splat): suggest alternative argument counts, if there are any + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0057, + "this splatted function takes {} arguments, but {} {} provided", + formal_input_tys.len(), + provided_args.len(), + if provided_args.len() == 1 { "was" } else { "were" }, + ) + .emit(); + + Some(guar) + } else { + None + } + } else { + None + }; + + if let Some(guar) = guar { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: self.err_args(provided_args.len(), guar), + untupled_expected_input_tys: None, + } + } else { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + } + } + } + /// If `unsized_fn_params` is active, check that unsized values are place expressions. Since /// the removal of `unsized_locals` in we can't /// store them in MIR locals as temporaries. @@ -581,6 +836,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_def_id: Option, call_span: Span, call_expr: &'tcx hir::Expr<'tcx>, + // FIXME(splat): when the feature design is settled, improve the errors here tuple_arguments: TupleArgumentsFlag, ) -> ErrorGuaranteed { // Next, let's construct the error @@ -1354,8 +1610,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 + // TupleAllCallArgs is set only when this is an implicit call `my_closure(...)` rather + // than explicit `my_closure.call(...)`. + if tuple_arguments == TupleAllCallArgs && 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. @@ -1442,25 +1699,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deps: SmallVec<[ExpectedIdx; 4]>, } - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !tuple_arguments.is_splatted() { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } // Gather all mismatched parameters with generics. let mut mismatched_params = Vec::>::new(); + let mut use_splat_fallback = false; if let Some(expected_idx) = expected_idx { let expected_idx = ExpectedIdx::from_usize(expected_idx); - let &(expected_generic, ref expected_param) = - ¶ms_with_generics[expected_idx]; - if let Some(expected_generic) = expected_generic { - mismatched_params.push(MismatchedParam { - idx: expected_idx, - generic: expected_generic, - param: expected_param, - deps: SmallVec::new(), - }); - } else { - // Still mark the mismatched parameter - spans.push_span_label(expected_param.span(), ""); - } - } else { + match params_with_generics.get(expected_idx) { + Some(&(Some(expected_generic), ref expected_param)) => mismatched_params + .push(MismatchedParam { + idx: expected_idx, + generic: expected_generic, + param: expected_param, + deps: SmallVec::new(), + }), + Some((None, expected_param)) => { + // Still mark the mismatched parameter + spans.push_span_label(expected_param.span(), ""); + } + None => { + if tuple_arguments.is_splatted() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + use_splat_fallback = true; + } else { + span_bug!( + self.tcx.def_span(def_id), + "arg index {} out of bounds for method with {} inputs", + expected_idx.as_usize(), + params_with_generics.len(), + ); + } + } + }; + } + + if expected_idx.is_none() || use_splat_fallback { mismatched_params.extend( params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( |((idx, &(generic, ref param)), matched_idx)| { @@ -1671,15 +1947,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_arg_tys: &IndexVec, Span)>, formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, + is_splat: bool, ) { let Some(def_id) = callable_def_id else { return; }; if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !is_splat { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { - if matched_inputs[idx].is_none() { + if matched_inputs.get(idx).flatten_ref().is_none() { continue; } @@ -1701,7 +1981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(other_generic_param) = other_generic_param else { return false; }; - if matched_inputs[other_idx].is_some() { + if matched_inputs.get(other_idx).flatten_ref().is_some() { return false; } other_generic_param == generic_param @@ -2004,7 +2284,11 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span, format!( "{call_name} takes {}{} but {} {} supplied", - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { + "at least " + } else { + "" + }, potentially_plural_count( self.formal_and_expected_inputs.len(), "argument" @@ -2147,14 +2431,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!("arguments to this {call_name} are incorrect"), ); - self.fn_ctxt.label_generic_mismatches( - &mut err, - self.fn_def_id, - &self.matched_inputs, - &self.provided_arg_tys, - &self.formal_and_expected_inputs, - self.call_metadata.is_method, - ); + self.label_generic_mismatches(&mut err); if let hir::ExprKind::MethodCall(_, rcvr, _, _) = self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind @@ -2237,7 +2514,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!( "this {} takes {}{} but {} {} supplied", self.call_metadata.call_name, - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { "at least " } else { "" }, potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"), potentially_plural_count(self.provided_args.len(), "argument"), pluralize!("was", self.provided_args.len()) @@ -2614,6 +2891,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { &self.provided_arg_tys, &self.formal_and_expected_inputs, self.call_metadata.is_method, + self.arg_matching_ctxt.tuple_arguments.is_splatted(), ); } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 3941f06c3e7d3..3b3f5caa85c09 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -3,6 +3,7 @@ #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(never_type)] +#![feature(option_reference_flattening)] #![feature(trim_prefix_suffix)] // tidy-alphabetical-end @@ -50,7 +51,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, FnSigKind, Ty, TyCtxt, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -589,12 +590,10 @@ fn report_unexpected_variant_res( .emit() } -/// Controls whether the arguments are tupled. This is used for the call -/// operator. +/// Controls whether all arguments are tupled. This is used for the call operator only. /// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: +/// Tupling means that all call-side arguments are packed into a tuple and passed as a single +/// parameter. For example, if tupling is enabled, this function: /// ``` /// fn f(x: (isize, isize)) {} /// ``` @@ -608,10 +607,72 @@ fn report_unexpected_variant_res( /// # fn f(x: (isize, isize)) {} /// f((1, 2)); /// ``` -#[derive(Copy, Clone, Eq, PartialEq)] +/// +/// Note: splatted arguments are handled separately. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] enum TupleArgumentsFlag { + /// Arguments are typechecked unchanged. DontTupleArguments, - TupleArguments, + /// This is a call operator: all caller arguments are tupled before typechecking. + /// Set based on the "rust-call" ABI and Fn* traits. + TupleAllCallArgs, + /// The `self` method argument is splatted, so `Self` should be tupled before typechecking. + TupleSplattedSelfArg, + /// A non-self argument is splatted, so that argument should be tupled before typechecking. + TupleSplattedArg(u8), +} + +impl TupleArgumentsFlag { + /// Returns the TupleArgumentsFlag for a known RustCall function. + fn rust_fn_trait_call() -> Self { + Self::TupleAllCallArgs + } + + /// Returns the appropriate TupleArgumentsFlag for the given FnSigKind and method flag. + fn with_fn_sig_kind<'tcx>(fn_sig_kind: FnSigKind<'tcx>, is_method: bool) -> Self { + if let Some(splatted_arg_index) = fn_sig_kind.splatted() { + if is_method { + if let Some(splatted_arg_index) = splatted_arg_index.checked_sub(1) { + return Self::TupleSplattedArg(splatted_arg_index); + } else { + // In `check_argument_types`, this is effectively `TupleSplattedArg(-1)` + return Self::TupleSplattedSelfArg; + } + } + + return Self::TupleSplattedArg(splatted_arg_index); + } + + Self::DontTupleArguments + } + + /// Returns true if the arguments are tupled through "rust-call" or splatting. + fn is_tupled(self) -> bool { + match self { + Self::DontTupleArguments => false, + Self::TupleAllCallArgs | Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + } + } + + /// Returns true if the arguments are tupled through splatting. + /// (But false if they are "rust-call" or not tupled.) + fn is_splatted(self) -> bool { + match self { + Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + Self::DontTupleArguments | Self::TupleAllCallArgs => false, + } + } + + /// Returns the tupled argument index, and whether the `self` argument is splatted. + /// Returns `None` if the arguments are not tupled, or if the `self` argument is splatted. + fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { + match self { + Self::TupleSplattedArg(index) => (Some(usize::from(index)), false), + Self::TupleAllCallArgs => (Some(0), false), + Self::TupleSplattedSelfArg => (None, true), + Self::DontTupleArguments => (None, false), + } + } } fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 298fee525fc55..3d50befe042f1 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -663,6 +663,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().insert(hir_id, def); } + // Export splatted function call resolutions. + if let Some(def) = self.fcx.typeck_results.borrow_mut().splatted_defs_mut().remove(hir_id) { + self.typeck_results.splatted_defs_mut().insert(hir_id, def); + } + // Resolve any borrowings for the node with id `node_id` self.visit_adjustments(span, hir_id); diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 27a21381ffce9..3010eadb61057 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -329,6 +329,14 @@ fn structurally_same_type_impl<'tcx>( let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig); let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig); + // FIXME(splat): Is splatting ever repr(C)? + // Can two splatted functions to have the same structure? + // Can a splatted and non-splatted function have the same structure? + // For now, we require splatting to match exactly. + if a_sig.splatted() != b_sig.splatted() { + return false; + } + (a_sig.abi(), a_sig.safety(), a_sig.c_variadic()) == (b_sig.abi(), b_sig.safety(), b_sig.c_variadic()) && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5e87dd7667c14..569a1d5786095 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2085,6 +2085,8 @@ impl<'tcx> TyCtxt<'tcx> { ty::Tuple(params) => *params, _ => bug!(), }; + // Ignore splatting, it is unsupported on closures. + assert!(s.splatted().is_none()); self.mk_fn_sig( params, s.output(), diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 23b5899b0cc39..208e94270a45e 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -96,6 +96,20 @@ impl<'tcx> TypeError<'tcx> { if values.found { "variadic" } else { "non-variadic" } ) .into(), + TypeError::SplatMismatch(ref values) => format!( + "expected fn with {}, found fn with {}", + if let Some(index) = values.expected { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + }, + if let Some(index) = values.found { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + } + ) + .into(), TypeError::ProjectionMismatched(ref values) => format!( "expected `{}`, found `{}`", tcx.alias_term_kind_def_path_str(values.expected), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3ea798ee45fb2..f081e98127cf4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -113,7 +113,8 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, - Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, + Rust2024IncompatiblePatInfo, SplattedDef, TypeckResults, UserType, UserTypeAnnotationIndex, + UserTypeKind, }; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::{AmbigModChild, ModChild}; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c1f96c3a94fdd..89b43480b3fc3 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1435,6 +1435,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p.pretty_print_fn_sig( tys, false, + // FIXME(splat): support splatted arguments here? + None, proj.skip_binder().term.as_type().expect("Return type was a const"), )?; resugared = true; @@ -1538,10 +1540,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, + splatted: Option, output: Ty<'tcx>, ) -> Result<(), PrintError> { write!(self, "(")?; - self.comma_sep(inputs.iter().copied())?; + let splatted_arg_index = splatted.map(usize::from); + let mut input_iter = inputs.iter().copied(); + if let Some(index) = splatted_arg_index { + self.comma_sep((&mut input_iter).take(usize::from(index)))?; + write!(self, ", #[splat]")?; + self.comma_sep(input_iter)?; + } else { + self.comma_sep(input_iter)?; + } if c_variadic { if !inputs.is_empty() { write!(self, ", ")?; @@ -3150,7 +3161,7 @@ define_print! { } write!(p, "fn")?; - p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.output())?; + p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.splatted(), self.output())?; } ty::TraitRef<'tcx> { diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 1287047581196..77820beef0228 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -36,6 +36,9 @@ pub struct TypeckResults<'tcx> { /// method calls, including those of overloaded operators. type_dependent_defs: ItemLocalMap>, + /// Resolved definitions for splatted function calls. + splatted_defs: ItemLocalMap>, + /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) /// or patterns (`S { field }`). The index is often useful by itself, but to learn more /// about the field you also need definition of the variant to which the field @@ -229,6 +232,7 @@ impl<'tcx> TypeckResults<'tcx> { TypeckResults { hir_owner, type_dependent_defs: Default::default(), + splatted_defs: Default::default(), field_indices: Default::default(), user_provided_types: Default::default(), user_provided_sigs: Default::default(), @@ -287,6 +291,21 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } } + pub fn splatted_defs(&self) -> LocalTableInContext<'_, Result> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.splatted_defs } + } + + pub fn splatted_def(&self, id: HirId) -> Option { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.splatted_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) + } + + pub fn splatted_defs_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Result> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.splatted_defs } + } + pub fn field_indices(&self) -> LocalTableInContext<'_, FieldIdx> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.field_indices } } @@ -407,6 +426,10 @@ impl<'tcx> TypeckResults<'tcx> { matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } + pub fn is_splatted_call(&self, expr: &hir::Expr<'_>) -> bool { + matches!(self.splatted_defs().get(expr.hir_id), Some(Ok(SplattedDef { .. }))) + } + /// Returns the computed binding mode for a `PatKind::Binding` pattern /// (after match ergonomics adjustments). pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> BindingMode { @@ -569,6 +592,18 @@ impl<'tcx> TypeckResults<'tcx> { } } +/// A resolved splatted function call. +#[derive(Debug, Copy, Clone, PartialEq, Eq, StableHash, TyEncodable, TyDecodable)] +pub struct SplattedDef { + /// The function DefId, if available (FnPtrs don't have DefIds) + pub def_id: Option, + /// The index of the first argument in the callee's splatted tuple, and the index of the + /// splatted tuple argument in the caller. + pub arg_index: u16, + /// The number of arguments in the splatted tuple. + pub arg_count: u16, +} + /// Validate that the given HirId (respectively its `local_id` part) can be /// safely used as a key in the maps of a TypeckResults. For that to be /// the case, the HirId must have the same `owner` as all the other IDs in diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 854a378bb993d..378e2e618fb93 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -547,6 +547,7 @@ fn construct_fn<'tcx>( body.spread_arg = if abi == ExternAbi::RustCall { // RustCall pseudo-ABI untuples the last argument. + // FIXME(splat): splat can untuple any argument, set spread_arg here Some(Local::new(arguments.len())) } else { None diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b71793c157621..c286c0819aea2 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -18,8 +18,8 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ - self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, TyCtxt, - UpvarArgs, + self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, SplattedDef, Ty, + TyCtxt, UpvarArgs, }; use rustc_middle::{bug, span_bug}; use rustc_span::{DesugaringKind, Span}; @@ -369,19 +369,26 @@ impl<'tcx> ThirBuildCx<'tcx> { let kind = match expr.kind { // Here comes the interesting stuff: hir::ExprKind::MethodCall(segment, receiver, args, fn_span) => { - // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = self.method_callee(expr, segment.ident.span, None); - info!("Using method span: {:?}", expr.span); - let args = std::iter::once(receiver) - .chain(args.iter()) - .map(|expr| self.mirror_expr(expr)) - .collect(); - ExprKind::Call { - ty: expr.ty, - fun: self.thir.exprs.push(expr), - args, - from_hir_call: true, - fn_span, + if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fn_span, args, Some(receiver)) + } else { + // Rewrite a.b(c) into UFCS form like Trait::b(a, c) + let expr = self.method_callee(expr, segment.ident.span, None); + info!("Using method span: {:?}", expr.span); + + let args = std::iter::once(receiver) + .chain(args.iter()) + .map(|expr| self.mirror_expr(expr)) + .collect(); + ExprKind::Call { + ty: expr.ty, + fun: self.thir.exprs.push(expr), + args, + from_hir_call: true, + fn_span, + } } } @@ -412,6 +419,10 @@ impl<'tcx> ThirBuildCx<'tcx> { from_hir_call: true, fn_span: expr.span, } + } else if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `f(a, u, v)` into `f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fun.span, args, None) } else { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind @@ -1205,6 +1216,116 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + fn splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + span: Span, + ) -> (Expr<'tcx>, u16 /* arg_index */, u16 /* arg_count */) { + let SplattedDef { def_id, arg_index, arg_count } = + self.typeck_results.splatted_def(expr.hir_id).unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_id = def_id.unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_kind = self.tcx.def_kind(def_id); + let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); + debug!( + "splatted_callee: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", + user_ty, def_kind, def_id, arg_index, arg_count + ); + + ( + Expr { + temp_scope_id: expr.hir_id.local_id, + ty: Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), + span, + kind: ExprKind::ZstLiteral { user_ty }, + }, + arg_index, + arg_count, + ) + } + + /// The callee has a splatted tuple argument. + /// Rewrite a splatted call `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))`. + /// The receiver is optional. + fn convert_splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + fn_span: Span, + args: &'tcx [hir::Expr<'tcx>], + receiver: Option<&'tcx hir::Expr<'tcx>>, + ) -> ExprKind<'tcx> { + let tcx = self.tcx; + + // The callee has a splatted tuple argument. + let (func, tupled_arg_index, tupled_args_count) = self.splatted_callee(expr, fn_span); + let tupled_arg_index = usize::from(tupled_arg_index); + let tupled_args_count = usize::from(tupled_args_count); + + // Splatting an empty tuple is permitted: `a.f() -> Trait::f(a, #[splat] ())`. + // In that case, the tupled arg index is one past the end of the args. + if tupled_arg_index + tupled_args_count > args.len() { + span_bug!( + expr.span, + "splatted arg index out of bounds of function args: {:?} + {:?} > {:?} for function call: receiver {:?}, args {:?}", + tupled_arg_index, + tupled_args_count, + args.len(), + receiver, + args, + ); + } + + info!("Using splatted function span: {:?}", func.span); + + // Split into non-tupled and tupled arguments + let initial_non_tupled_args = + args.iter().take(tupled_arg_index).map(|e| self.mirror_expr(e)).collect_vec(); + let tupled_args = if tupled_arg_index == args.len() || tupled_args_count == 0 { + // Splatting an empty tuple, in the ABI this gets ignored + Default::default() + } else { + &args[tupled_arg_index..(tupled_arg_index + tupled_args_count)] + }; + let final_non_tupled_args = args + .iter() + .skip(tupled_arg_index + tupled_args_count) + .map(|e| self.mirror_expr(e)) + .collect_vec(); + + let tupled_arg_tys = tupled_args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); + + let temp_scope_id = + if receiver.is_some() { func.temp_scope_id } else { expr.hir_id.local_id }; + let tupled_args = Expr { + ty: Ty::new_tup_from_iter(tcx, tupled_arg_tys), + temp_scope_id, + span: expr.span, + kind: ExprKind::Tuple { fields: self.mirror_exprs(tupled_args) }, + }; + + let tupled_args = self.thir.exprs.push(tupled_args); + + let mut args = + if let Some(receiver) = receiver { vec![self.mirror_expr(receiver)] } else { vec![] }; + args.extend(initial_non_tupled_args); + args.push(tupled_args); + args.extend(final_non_tupled_args); + + // We need the tupled arguments in HIR/MIR for type checking, but codegen can + // de-tuple them for performance + let fn_span = if receiver.is_some() { func.span } else { expr.span }; + ExprKind::Call { + ty: func.ty, + fun: self.thir.exprs.push(func), + args: args.into_boxed_slice(), + from_hir_call: true, + fn_span, + } + } + fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId { let arm = Arm { pattern: self.pattern_from_hir(&arm.pat), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 81b32f4da10f6..181498443073d 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -312,6 +312,7 @@ impl RustcInternal for FnSig { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { + // FIXME(splat): When `#[splat]` is complete (or stable), add splatted to the public FnSig let fn_sig_kind = rustc_ty::FnSigKind::default() .set_abi(self.abi.internal(tables, tcx)) .set_safety(self.safety.internal(tables, tcx)) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 827e33dcd6632..b3344d150d999 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1141,6 +1141,7 @@ symbols! { irrefutable_let_patterns, is, is_auto, + is_splatted, is_val_statically_known, isa_attribute, isize, @@ -1996,6 +1997,7 @@ symbols! { speed, spirv, splat, + splatted_index, spotlight, sqrtf16, sqrtf32, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 3aaf17095a6ce..9822e8cdef8b2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -823,9 +823,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // ^^^^^ let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); + let splatted_arg_index1 = sig1.splatted().map(usize::from); + let splatted_arg_index2 = sig2.splatted().map(usize::from); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { self.push_comma(&mut values.0, &mut values.1, i); + if Some(i) == splatted_arg_index1 { + values.0.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.0.push_normal(" "); + } + if Some(i) == splatted_arg_index2 { + values.1.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.1.push_normal(" "); + } let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); 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 d7dfce166742b..4e3a31f0e84b5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4962,6 +4962,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let fn_sig @ ty::FnSig { .. } = fn_ty.fn_sig(tcx).skip_binder() + // FIXME(splat): this might need to change if the Fn* traits start using/supporting splat && fn_sig.abi() == ExternAbi::Rust && !fn_sig.c_variadic() && fn_sig.safety() == hir::Safety::Safe diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index c08cec21e5b1f..74abfad6e5abc 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -41,6 +41,7 @@ pub enum TypeError { ArgumentSorts(ExpectedFound, usize), Traits(ExpectedFound), VariadicMismatch(ExpectedFound), + SplatMismatch(ExpectedFound>), /// Instantiating a type variable with the given type would have /// created a cycle (because it appears somewhere within that @@ -76,7 +77,7 @@ impl TypeError { match self { CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | ArraySize(_) | ArgumentSorts(..) | Sorts(_) - | VariadicMismatch(_) | TargetFeatureCast(_) => false, + | VariadicMismatch(_) | SplatMismatch(_) | TargetFeatureCast(_) => false, Mutability | ArgumentMutability(_) diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index 857738d207b4f..ea92699958094 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -45,6 +45,7 @@ TrivialTypeTraversalImpls! { (), bool, usize, + u8, u16, u32, u64, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index d64cc2450eb19..a683252f03716 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -169,6 +169,10 @@ impl Relate for ty::FnSig { return Err(TypeError::AbiMismatch(ExpectedFound::new(a.abi(), b.abi()))); }; + if a.splatted() != b.splatted() { + return Err(TypeError::SplatMismatch(ExpectedFound::new(a.splatted(), b.splatted()))); + } + let a_inputs = a.inputs(); let b_inputs = b.inputs(); if a_inputs.len() != b_inputs.len() { diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 89fac9d200214..31824b61cfb27 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -888,8 +888,19 @@ pub struct TypeAndMut { impl Eq for TypeAndMut {} +/// Error type for splatted argument index errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SplattedArgIndexError { + /// The splatted argument index is invalid. + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, + + /// The splatted argument index is outside the bounds of the function arguments. + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, +} + /// Contains the packed non-type fields of a function signature. -// FIXME(splat): add the splatted argument index as a u16 #[derive_where(Copy, Clone, PartialEq, Eq, Hash; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( @@ -903,6 +914,15 @@ pub struct FnSigKind { #[type_visitable(ignore)] #[type_foldable(identity)] flags: u8, + + /// Which function argument is splatted into multiple arguments in callers, if any? + /// Splatting functions with `>= u8::MAX` arguments is not supported, for performance reasons. + /// (And spending an extra byte on an edge case is not worth the perf.) + #[lift(identity)] + #[type_visitable(ignore)] + #[type_foldable(identity)] + splatted: u8, + #[type_visitable(ignore)] #[type_foldable(identity)] _marker: PhantomData I>, @@ -922,12 +942,28 @@ impl fmt::Debug for FnSigKind { if self.c_variadic() { f.field(&"CVariadic"); - }; + } + + if let Some(index) = self.splatted() { + f.field(&format!("Splatted({})", index)); + } f.finish() } } +impl Default for FnSigKind { + /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic or splatted arguments. + /// To modify these flags, use the `set_*` methods, for readability. + fn default() -> Self { + Self { flags: 0, splatted: 0, _marker: PhantomData } + .set_abi(ExternAbi::Rust) + .set_safety(I::Safety::unsafe_mode()) + .set_c_variadic(false) + .set_no_splatted_args() + } +} + impl FnSigKind { /// Mask for the `ExternAbi` variant, including the unwind flag. const EXTERN_ABI_MASK: u8 = 0b111111; @@ -938,19 +974,34 @@ impl FnSigKind { /// Bitflag for a trailing C-style variadic argument. const C_VARIADIC_FLAG: u8 = 1 << 7; - /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic argument. - /// To modify these flags, use the `set_*` methods, for readability. - // FIXME: use Default instead when that trait is const stable. - pub fn default() -> Self { - Self { flags: 0, _marker: PhantomData } - .set_abi(ExternAbi::Rust) - .set_safety(I::Safety::unsafe_mode()) - .set_c_variadic(false) - } + /// The marker index for "no splatted arguments". Higher values are also not supported, for + /// performance reasons. + /// + /// Must have the same value as `FnDeclFlags::NO_SPLATTED_ARG_INDEX` and + /// `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. + /// + /// This is an implementation detail, which should only be used in low-level encoding. + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; - /// Create a new FnSigKind with the given ABI, safety, and C-style variadic flag. - pub fn new(abi: ExternAbi, safety: I::Safety, c_variadic: bool) -> Self { - Self::default().set_abi(abi).set_safety(safety).set_c_variadic(c_variadic) + /// Create a new FnSigKind with the given ABI, safety, C-style variadic, and splatted argument + /// index. + pub fn new( + abi: ExternAbi, + safety: I::Safety, + c_variadic: bool, + splatted: Option, + args_len: usize, + ) -> Result { + Self::default() + .set_abi(abi) + .set_safety(safety) + .set_c_variadic(c_variadic) + .set_splatted(splatted, args_len) + } + + /// Create a new safe FnSigKind with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. + pub fn dummy() -> Self { + Self::default().set_safety(I::Safety::safe()) } /// Set the ABI, including the unwind flag. @@ -989,6 +1040,41 @@ impl FnSigKind { self } + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + mut self, + splatted: Option, + args_len: usize, + ) -> Result { + if let Some(splatted_arg_index) = splatted { + if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { + // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. + return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); + } else if usize::from(splatted_arg_index) >= args_len { + return Err(SplattedArgIndexError::OutOfBounds { + splatted_arg_index, + args_len: args_len as u16, + }); + } + + self.splatted = splatted_arg_index; + } else { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + } + + Ok(self) + } + + /// Set the splatted argument index to "no splatted arguments". + #[must_use = "this method does not modify the receiver"] + pub fn set_no_splatted_args(mut self) -> Self { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + self + } + /// Get the ABI, including the unwind flag. pub fn abi(self) -> ExternAbi { let abi_index = self.flags & Self::EXTERN_ABI_MASK; @@ -1009,6 +1095,11 @@ impl FnSigKind { pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } + + /// Get the index of the splatted argument, if any. + pub fn splatted(self) -> Option { + if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } + } } #[derive_where(Clone, Copy, PartialEq, Hash; I: Interner)] @@ -1039,8 +1130,21 @@ impl FnSig { !self.c_variadic() && self.safety().is_safe() && self.abi() == ExternAbi::Rust } + /// Set the safety flag. + #[must_use = "this method does not modify the receiver"] pub fn set_safety(self, safety: I::Safety) -> Self { - Self { fn_sig_kind: FnSigKind::new(self.abi(), safety, self.c_variadic()), ..self } + Self { fn_sig_kind: self.fn_sig_kind.set_safety(safety), ..self } + } + + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + self, + splatted: Option, + args_len: usize, + ) -> Result { + Ok(Self { fn_sig_kind: self.fn_sig_kind.set_splatted(splatted, args_len)?, ..self }) } pub fn safety(self) -> I::Safety { @@ -1055,11 +1159,14 @@ impl FnSig { self.fn_sig_kind.c_variadic() } + pub fn splatted(self) -> Option { + self.fn_sig_kind.splatted() + } + + /// Create a new safe FnSig with no arguments or return type, using the `extern "Rust"` ABI, + /// that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { - inputs_and_output: Default::default(), - fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false), - } + Self { inputs_and_output: Default::default(), fn_sig_kind: FnSigKind::dummy() } } } @@ -1092,6 +1199,10 @@ impl ty::Binder> { self.skip_binder().c_variadic() } + pub fn splatted(self) -> Option { + self.skip_binder().splatted() + } + pub fn safety(self) -> I::Safety { self.skip_binder().safety() } @@ -1127,6 +1238,9 @@ impl fmt::Debug for FnSig { if i > 0 { write!(f, ", ")?; } + if Some(i) == fn_sig_kind.splatted().map(usize::from) { + write!(f, "#[splat] ")?; + } write!(f, "{ty:?}")?; } if fn_sig_kind.c_variadic() { @@ -1288,8 +1402,9 @@ impl FnHeader { self.fn_sig_kind.abi() } + /// Create a new safe FnHeader with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false) } + Self { fn_sig_kind: FnSigKind::dummy() } } } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index b688a001f73fa..9453d30ce91c9 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -364,7 +364,7 @@ pub struct CoroutineClosureSignature { // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types // never actually differ. But we save them rather than recreating them // from scratch just for good measure. - /// Always safe, RustCall, non-c-variadic + /// Always safe, RustCall, non-c-variadic, non-splatted #[type_visitable(ignore)] #[type_foldable(identity)] pub fn_sig_kind: FnSigKind, diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 9ec36561839b5..7614adbcc532b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -215,7 +215,7 @@ pub struct Variant { pub name: &'static str, /// All fields of the variant. pub fields: &'static [Field], - /// Whether the enum variant fields is non-exhaustive. + /// Whether the enum variant fields are non-exhaustive. pub non_exhaustive: bool, } @@ -342,6 +342,22 @@ pub struct FnPtr { /// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...); pub variadic: bool, + + // FIXME(splat): should these fields be private, or merged into an Option? + /// Is any function argument splatted? + pub is_splatted: bool, + + /// The index of the splatted function argument in `inputs`, only valid if `is_splatted` is true. + /// e.g. in `fn overload(a: u8, #[splat] b: (f32, usize))` the index is 1, and it can be called + /// as `overload(a, 1.0, 2)`. + pub splatted_index: u8, +} + +impl FnPtr { + /// Returns the splatted function argument index, or `None` if no argument is splatted. + pub const fn splatted(&self) -> Option { + if self.is_splatted { Some(self.splatted_index) } else { None } + } } #[derive(Debug, Default)] diff --git a/library/coretests/tests/mem/fn_ptr.rs b/library/coretests/tests/mem/fn_ptr.rs index 1d50a2552a193..6e7e170917f55 100644 --- a/library/coretests/tests/mem/fn_ptr.rs +++ b/library/coretests/tests/mem/fn_ptr.rs @@ -5,6 +5,7 @@ const STRING_TY: TypeId = const { TypeId::of::() }; const U8_TY: TypeId = const { TypeId::of::() }; const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() }; const UNIT_TY: TypeId = const { TypeId::of::<()>() }; +const TUPLE_STRING_U8_TY: TypeId = const { TypeId::of::<(String, u8)>() }; #[test] fn test_fn_ptrs() { @@ -14,6 +15,8 @@ fn test_fn_ptrs() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -31,6 +34,8 @@ fn test_ref() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -61,6 +66,8 @@ fn test_unsafe() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -75,6 +82,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -87,6 +96,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -99,6 +110,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -114,6 +127,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -128,6 +143,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -145,6 +162,8 @@ fn test_output() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of:: u8>().kind }) else { panic!(); @@ -160,6 +179,8 @@ fn test_variadic() { inputs: [ty1], output, variadic: true, + is_splatted: false, + splatted_index: _, }) = &(const { Type::of::().kind }) else { panic!(); @@ -167,3 +188,48 @@ fn test_variadic() { assert_eq!(output, &UNIT_TY); assert_eq!(*ty1, U8_TY); } + +#[test] +fn test_splat() { + #[rustfmt::skip] + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: true, + splatted_index: 0, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), Some(0)); +} + +#[test] +fn test_not_splat() { + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: false, + splatted_index: _, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), None); +} diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 730c7d9fac611..58fe7dc541fc3 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -406,6 +406,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let sig = this.tcx.mk_fn_sig( args.iter().map(|a| a.layout.ty), dest.layout.ty, + // FIXME(splat): Do we need to set splatted here? + // (Currently this also ignores c_variadic) FnSigKind::default().set_abi(caller_abi).set_safety(rustc_hir::Safety::Safe), ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index 68b13a6ed58a0..99673319240fe 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -274,7 +274,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { inputs_and_output.push(shim_sig.ret); let fn_sig_binder = Binder::dummy(FnSig { inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), - // Safety does not matter for the ABI. + // Safety and splatted do not matter for the ABI. fn_sig_kind: FnSigKind::default() .set_abi(shim_sig.abi) .set_safety(rustc_hir::Safety::Safe), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 057ba7fa868fe..a661731e60f46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -173,6 +173,8 @@ impl<'db> InferenceContext<'_, 'db> { // impl forces the closure kind to `FnOnce` i.e. `u8`. let kind_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); let interner = autoderef.ctx().interner(); + + // Ignore splatting, it is unsupported on closures. let call_sig = interner.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], coroutine_closure_sig.to_coroutine( 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 cc48ba06dbfc5..1fddfc09c666f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -464,6 +464,7 @@ pub fn callable_sig_from_fn_trait<'db>( args.tuple_fields(), ret, false, + // FIXME(splat): handle splatted arguments Safety::Safe, ExternAbi::Rust, )); 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 fae63ddc2deae..d63ac7f7a0ed4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -637,6 +637,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn_.abi, if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, fn_.is_varargs, + // FIXME(splat): handle splatted arguments ), inputs_and_output: Tys::new_from_slice(&args), }), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index ef626fc0c8869..3c2976eccd980 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -2238,6 +2238,7 @@ impl<'db> DbInterner<'db> { self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } + // FIXME: add splat support when the experiment is complete pub fn mk_fn_sig( self, inputs: I, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index fe31d44207dff..d57d824e325fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1514,6 +1514,7 @@ impl<'db> DbInterner<'db> { TyKind::Tuple(params) => params, _ => panic!(), }; + // Ignore splatting, it is unsupported on closures. self.mk_fn_sig(params, s.output(), s.c_variadic(), safety, ExternAbi::Rust) }) } diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs new file mode 100644 index 0000000000000..31767f11d999b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -0,0 +1,199 @@ +// ignore-tidy-linelength +//! Test `#[splat]` fails over the 255th argument index (or higher). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + +#[rustfmt::skip] +fn s_255_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 +) {} + +#[rustfmt::skip] +fn s_256_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 +) {} + +#[rustfmt::skip] +fn s_255_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_256_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + _: A, +) {} + +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 255 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + /* missing: a, */ + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 257 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, /* unexpected: */ a, + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 512 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + /* unexpected: */ + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr new file mode 100644 index 0000000000000..da62da8aa605b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -0,0 +1,71 @@ +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:50:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:71:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:91:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:113:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error[E0057]: this splatted function takes 256 arguments, but 255 were provided + --> $DIR/splat-255-limit-fail.rs:124:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | /* missing: a, */ +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 257 were provided + --> $DIR/splat-255-limit-fail.rs:144:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, /* unexpected: */ a, +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 512 were provided + --> $DIR/splat-255-limit-fail.rs:164:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, +LL | | ); + | |_____^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-255-limit-pass.rs b/tests/ui/splat/splat-255-limit-pass.rs new file mode 100644 index 0000000000000..9713616830f9b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-pass.rs @@ -0,0 +1,201 @@ +//@ run-pass +// ignore-tidy-linelength +//! Test `#[splat]` on the 255th argument index (or lower). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + +#[rustfmt::skip] +fn s_253_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_non_terminal_272_args( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_253_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_254_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_255_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. +#[rustfmt::skip] +fn s_0_initial_256_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, +) {} + +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} diff --git a/tests/ui/splat/splat-assoc-fn-tuple-simple.rs b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs new file mode 100644 index 0000000000000..d2681c0d2574c --- /dev/null +++ b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs @@ -0,0 +1,22 @@ +//@ run-pass +//! Test using `#[splat]` on associated function tuple arguments (no receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + fn tuple_1(#[splat] (_a,): (u32,)) {} + + fn tuple_3(#[splat] (_a, _b, _c): (u32, i32, i8)) {} +} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1((1u32,)); + + Foo::tuple_1(1u32); + Foo::tuple_3(1u32, 2i32, 3i8); +} diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.rs b/tests/ui/splat/splat-async-fn-tuple-fail.rs new file mode 100644 index 0000000000000..a1f67dfe606d6 --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.rs @@ -0,0 +1,17 @@ +//@ edition:2024 +//! Test that using `#[splat]` incorrectly on async functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + async_wrong_type(1u32); + async_multi_splat(1u32, 2i8, 3u32, 4i8); + //~^ ERROR this splatted function takes 3 arguments, but 4 were provided +} diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.stderr b/tests/ui/splat/splat-async-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..e643b29c88c8a --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.stderr @@ -0,0 +1,27 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-async-fn-tuple-fail.rs:10:28 + | +LL | async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-async-fn-tuple-fail.rs:7:40 + | +LL | async fn async_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | async_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 3 arguments, but 4 were provided + --> $DIR/splat-async-fn-tuple-fail.rs:15:5 + | +LL | async_multi_splat(1u32, 2i8, 3u32, 4i8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277. +For more information about an error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-async-fn-tuple.rs b/tests/ui/splat/splat-async-fn-tuple.rs new file mode 100644 index 0000000000000..81e56193bd4ab --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple.rs @@ -0,0 +1,18 @@ +//@ run-pass +//@ edition:2024 +//! Test using `#[splat]` on tuple arguments of async functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +async fn async_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + let _ = async_tuple_args(1u32, 2i8); + let _ = async_tuple_args(1, 2); + + let _ = async_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + let _ = async_splat_non_terminal_arg(1, 2, 3.5); +} diff --git a/tests/ui/splat/splat-cannot-resolve.rs b/tests/ui/splat/splat-cannot-resolve.rs new file mode 100644 index 0000000000000..1c22a53f82916 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.rs @@ -0,0 +1,49 @@ +//! Test that using `#[splat]` on un-resolvable types is an error. + +#![allow(incomplete_features)] +#![allow(unconditional_recursion)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn tuple(#[splat] t: impl Sized) -> impl Sized { + //~^ ERROR cannot resolve opaque type + tuple(tuple((t, ()))) +} + +fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + //~^ ERROR cannot resolve opaque type + tuple_trait(tuple_trait((t, ()))) +} + +trait Trait { + type MaybeTup; + type Tup: std::marker::Tuple; +} + +fn ambig(#[splat] t: Trait::MaybeTup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +fn ambig_tup(#[splat] t: Trait::Tup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a + +fn main() { + tuple(); + tuple_trait(); + ambig(); + ambig_tup(); + + tuple(1); + tuple_trait(1); + ambig(1); + ambig_tup(1); + + tuple(1, 2.0); + tuple_trait(1, 2.0); + ambig(1, 2.0); + ambig_tup(1, 2.0); +} diff --git a/tests/ui/splat/splat-cannot-resolve.stderr b/tests/ui/splat/splat-cannot-resolve.stderr new file mode 100644 index 0000000000000..f91267d37dbc8 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.stderr @@ -0,0 +1,94 @@ +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig(#[splat] t: Trait::MaybeTup) {} +LL + fn ambig(#[splat] t: ::MaybeTup) {} + | + +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig_tup(#[splat] t: Trait::Tup) {} +LL + fn ambig_tup(#[splat] t: ::Tup) {} + | + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:8:37 + | +LL | fn tuple(#[splat] t: impl Sized) -> impl Sized { + | ^^^^^^^^^^ + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:13:56 + | +LL | fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(); + | ^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(); + | ^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1); + | ^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1); + | ^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1, 2.0); + | ^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1, 2.0); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0223, E0277, E0720. +For more information about an error, try `rustc --explain E0223`. diff --git a/tests/ui/splat/splat-const-fn-tuple-generic.rs b/tests/ui/splat/splat-const-fn-tuple-generic.rs new file mode 100644 index 0000000000000..646bf7e9b9f19 --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple-generic.rs @@ -0,0 +1,34 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions with generics. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Generic type in first position +const fn const_generic_first(#[splat] _: (T, u32)) {} + +// Generic type in second position +const fn const_generic_second(#[splat] _: (u32, T)) {} + +// Multiple generic types +const fn const_generic_both(#[splat] _: (T, U)) {} + +// Generic with extra non-splatted arg +const fn const_generic_extra(#[splat] _: (T, u32), _extra: i32) {} + +fn main() { + const_generic_first(1i8, 2u32); + const_generic_first(true, 2u32); + const_generic_first(1u64, 2u32); + + const_generic_second(1u32, 2i8); + const_generic_second(1u32, true); + const_generic_second(1u32, 2u64); + + const_generic_both(1u32, 2i8); + const_generic_both(true, 2u64); + const_generic_both(1i8, false); + + const_generic_extra(1i8, 2u32, 42i32); + const_generic_extra(true, 2u32, 42i32); +} diff --git a/tests/ui/splat/splat-const-fn-tuple.rs b/tests/ui/splat/splat-const-fn-tuple.rs new file mode 100644 index 0000000000000..f4ae2e124a72d --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple.rs @@ -0,0 +1,17 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +const fn sum(#[splat] (a, b): (u32, u32)) -> u32 { + a + b +} + +const RESULT: u32 = sum(1, 2); + +fn main() { + assert_eq!(RESULT, 3); + assert_eq!(sum(12, 18) , 30); + assert_eq!(sum(1, 2), 3); +} diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.rs b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs new file mode 100644 index 0000000000000..40d702346b7a3 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs @@ -0,0 +1,14 @@ +//! Test that `#[splat]` on `&dyn AsRef` where `T: Tuple` is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} +//~^ ERROR cannot use splat attribute + +fn main() { + let s: String = "hello".to_owned(); + dyn_asref_splat::(&s); + //~^ ERROR `String` is not a tuple +} diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr new file mode 100644 index 0000000000000..cba1ab28181a4 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr @@ -0,0 +1,24 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a &'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3 (&'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3) + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:36 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^ +... +LL | dyn_asref_splat::(&s); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `String` is not a tuple + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:23 + | +LL | dyn_asref_splat::(&s); + | ^^^^^^ the nightly-only, unstable trait `std::marker::Tuple` is not implemented for `String` + | +note: required by a bound in `dyn_asref_splat` + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:60 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^^^^^^ required by this bound in `dyn_asref_splat` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs new file mode 100644 index 0000000000000..297dbc0457794 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -0,0 +1,53 @@ +//@ failure-status: 101 + +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" + +//! Test using `#[splat]` on tuple arguments of simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + // Functions + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8)) = tuple_args; + fn_ptr(1, 2); //~ ERROR no splatted def for function or method callee + fn_ptr(1u32, 2i8); + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //fn_ptr((1, 2)); // ERROR this splatted function takes 2 arguments, but 1 was provided + + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8), f64) = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); + + // Function pointers + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); + (*fn_ptr)(1, 2); + (*fn_ptr)(1u32, 2i8); + + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8), f64) = + splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); + (*fn_ptr)(1, 2, 3.5); + (*fn_ptr)(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr new file mode 100644 index 0000000000000..a69175bd5e886 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -0,0 +1,23 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee + | +LL | fn_ptr(1, 2); + | ^^^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main` +#1 [check_unsafety] unsafety-checking `main` +#2 [analysis] running analysis passes on crate `splat_fn_ptr_tuple` +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.rs b/tests/ui/splat/splat-fn-tuple-generic-fail.rs new file mode 100644 index 0000000000000..8a8651e75c9ff --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.rs @@ -0,0 +1,27 @@ +//! Test failing use of `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // FIXME(splat): Make the splat generic handling code handle tuples inside tuples + // (if we want to support tupled calls) + splat_generic_tuple::<(((u32, i8)))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<((u32, i8))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<(u32, i8)>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided +} diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.stderr b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr new file mode 100644 index 0000000000000..7fd4a0719b493 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr @@ -0,0 +1,39 @@ +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:19:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:20:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:22:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:23:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:25:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:26:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-fn-tuple-generic.rs b/tests/ui/splat/splat-fn-tuple-generic.rs new file mode 100644 index 0000000000000..b7e3615f62c45 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic.rs @@ -0,0 +1,23 @@ +//@ run-pass +//! Test using `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // Generic tuple trait implementers are resolved during caller typeck. + splat_generic_tuple::<(u32, i8)>(1u32, 2i8); + splat_generic_tuple(1u32, 2i8); + splat_generic_tuple(1, 2); + + splat_generic_tuple::<()>(); + splat_generic_tuple(); +} diff --git a/tests/ui/splat/splat-fn-tuple-simple.rs b/tests/ui/splat/splat-fn-tuple-simple.rs new file mode 100644 index 0000000000000..c7234a15b9d55 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-simple.rs @@ -0,0 +1,32 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of simple functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + tuple_args(1, 2); + // 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(1, 2); + tuple_args(1u32, 2i8); + + splat_non_terminal_arg(1, 2, 3.5); + splat_non_terminal_arg(1u32, 2i8, 3.5f64); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = tuple_args; + fn_ptr(1, 2); + fn_ptr(1u32, 2i8); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-generics-complex-types.rs b/tests/ui/splat/splat-generics-complex-types.rs new file mode 100644 index 0000000000000..7d4490c18da75 --- /dev/null +++ b/tests/ui/splat/splat-generics-complex-types.rs @@ -0,0 +1,26 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with complex generic types inside the splatted tuple. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Vec and Option inside splatted tuple +fn nested_generic(#[splat] _: (Vec, Option)) {} + +// Box inside splatted tuple +fn box_generic(#[splat] _: (Box, u32)) {} + +// Multiple complex generics +fn multi_generic(#[splat] _: (Vec, Option, Box)) {} + +fn main() { + nested_generic(vec![1u32, 2u32], Some(2i8)); + nested_generic(vec![1, 2, 3], None::); + nested_generic::(vec![], Some("hello")); + + box_generic(Box::new(1u32), 42u32); + box_generic(Box::new("hello"), 1u32); + + multi_generic(vec![1u32], Some(2i8), Box::new(3.0f64)); + multi_generic::(vec![], None::, Box::new("hello")); +} diff --git a/tests/ui/splat/splat-generics-everywhere.rs b/tests/ui/splat/splat-generics-everywhere.rs new file mode 100644 index 0000000000000..be4f917ce21df --- /dev/null +++ b/tests/ui/splat/splat-generics-everywhere.rs @@ -0,0 +1,64 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics in various positions. + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo(T); + +// FIXME(splat): also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +impl Foo { + fn new(t: T) -> Self { + Self(t) + } + + fn assoc(_u: U, #[splat] _s: ()) {} + + fn method(&self, _v: V, #[splat] _s: (u32, f64)) {} + + fn lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +// FIXME(splat): also add generics to the trait +// also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +trait BarTrait { + fn trait_assoc(w: W, #[splat] _s: ()); + + fn trait_method(&self, x: X, #[splat] _s: (u32, f64)); + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +impl BarTrait for Foo { + fn trait_assoc(_w: W, #[splat] _s: ()) {} + + fn trait_method(&self, _x: X, #[splat] _s: (u32, f64)) {} + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::::assoc(("u",)); + + Foo::::assoc("u"); + Foo::::trait_assoc("w"); + + let foo = Foo::new("t"); + foo.method("v", 1u32, 2.3); + foo.lifetime(1u32, 2.3, "asdf"); + foo.const_generic(1u32, 2.3, [1, 2, 3]); + + foo.trait_method("x", 42u32, 9.8); + foo.trait_lifetime(1u32, 2.3, "asdf"); + foo.trait_const_generic(1u32, 2.3, [1, 2, 3]); +} diff --git a/tests/ui/splat/splat-generics-inside-tuple.rs b/tests/ui/splat/splat-generics-inside-tuple.rs new file mode 100644 index 0000000000000..dce26e55fa2d9 --- /dev/null +++ b/tests/ui/splat/splat-generics-inside-tuple.rs @@ -0,0 +1,29 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics inside the splatted tuple. +#![allow(incomplete_features)] +#![feature(splat)] + +fn generic_second(#[splat] _s: (u32, T)) {} + +fn generic_first(#[splat] _s: (T, u32)) {} + +fn generic_both(#[splat] _s: (T, U)) {} + +fn generic_triple(#[splat] _s: (T, U, V)) {} + +fn main() { + generic_second(1u32, 2i8); + generic_second(1u32, 2.0f64); + generic_second(1u32, "hello"); + + generic_first(1i8, 2u32); + generic_first(2.0f64, 2u32); + generic_first("hello", 2u32); + + generic_both(1u32, 2i8); + generic_both("hello", 2.0f64); + generic_both(true, "world"); + + generic_triple(1u32, 2.0f64, "hello"); + generic_triple(true, 42i32, 3.14f32); +} diff --git a/tests/ui/splat/splat-invalid-trait-impl.rs b/tests/ui/splat/splat-invalid-trait-impl.rs new file mode 100644 index 0000000000000..fe0d5ddc5a1c8 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.rs @@ -0,0 +1,27 @@ +//! Test that `#[splat]` trait impls with mismatched tuple element types are rejected. +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn method(#[splat] _: (u32, i8)); +} + +struct Foo; +struct Foo1; +struct Foo2; + +impl FooTrait for Foo { + fn method(#[splat] _: (u32, f32)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo1 { + fn method(#[splat] _: (f32, i8)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo2 { + fn method(#[splat] _: (f32, f64)) {} + //~^ ERROR method `method` has an incompatible type for trait +} +fn main() {} diff --git a/tests/ui/splat/splat-invalid-trait-impl.stderr b/tests/ui/splat/splat-invalid-trait-impl.stderr new file mode 100644 index 0000000000000..684cc65d7a326 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.stderr @@ -0,0 +1,60 @@ +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:14:27 + | +LL | fn method(#[splat] _: (u32, f32)) {} + | ^^^^^^^^^^ expected `i8`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (_, i8))` + found signature `fn(#[splat] (_, f32))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (u32, f32)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:19:27 + | +LL | fn method(#[splat] _: (f32, i8)) {} + | ^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, _))` + found signature `fn(#[splat] (f32, _))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, i8)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:24:27 + | +LL | fn method(#[splat] _: (f32, f64)) {} + | ^^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, i8))` + found signature `fn(#[splat] (f32, f64))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, f64)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-invalid.rs b/tests/ui/splat/splat-invalid.rs index 314003459ef72..2c4bf57ef972a 100644 --- a/tests/ui/splat/splat-invalid.rs +++ b/tests/ui/splat/splat-invalid.rs @@ -30,4 +30,18 @@ extern "C" { fn bar_2(#[splat] _: (u32, i8)); } +trait FooTrait { + fn has_splat(#[splat] _: ()); + + fn no_splat(_: (u32, f64)); +} + +struct Foo; + +impl FooTrait for Foo { + fn has_splat(_: ()) {} //~ ERROR method `has_splat` has an incompatible type for trait + + fn no_splat(#[splat] _: (u32, f64)) {} //~ ERROR method `no_splat` has an incompatible type for trait +} + fn main() {} diff --git a/tests/ui/splat/splat-invalid.stderr b/tests/ui/splat/splat-invalid.stderr index d88cbf4ff0b6d..74c54c00cd285 100644 --- a/tests/ui/splat/splat-invalid.stderr +++ b/tests/ui/splat/splat-invalid.stderr @@ -73,5 +73,34 @@ LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} | = help: remove `#[splat]` or remove `...` -error: aborting due to 9 previous errors +error[E0053]: method `has_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:42:5 + | +LL | fn has_splat(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-invalid.rs:34:5 + | +LL | fn has_splat(#[splat] _: ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] ())` + found signature `fn(())` + +error[E0053]: method `no_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:44:5 + | +LL | fn no_splat(#[splat] _: (u32, f64)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | +note: type in trait + --> $DIR/splat-invalid.rs:36:5 + | +LL | fn no_splat(_: (u32, f64)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn((_, _))` + found signature `fn(#[splat] (_, _))` + +error: aborting due to 11 previous errors +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-maybe-tuple.rs b/tests/ui/splat/splat-maybe-tuple.rs new file mode 100644 index 0000000000000..a74af66e9a874 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.rs @@ -0,0 +1,22 @@ +//! Test that using `#[splat]` on maybe-tuple generic function arguments is an error, +//! but only when the generics aren't tuples. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn unbound_generic_arg(#[splat] t: T) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +fn main() { + unbound_generic_arg(); + unbound_generic_arg::<()>(); + + unbound_generic_arg(1); + unbound_generic_arg::<(u32,)>(1); + + unbound_generic_arg(1, 2.0); + unbound_generic_arg::<(u32, f32)>(1, 2.0); + + // The error comes from this call + unbound_generic_arg::(1); +} diff --git a/tests/ui/splat/splat-maybe-tuple.stderr b/tests/ui/splat/splat-maybe-tuple.stderr new file mode 100644 index 0000000000000..9abacdd657b93 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.stderr @@ -0,0 +1,12 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-maybe-tuple.rs:8:39 + | +LL | fn unbound_generic_arg(#[splat] t: T) {} + | ^ +... +LL | unbound_generic_arg::(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-method-tuple-simple.rs b/tests/ui/splat/splat-method-tuple-simple.rs new file mode 100644 index 0000000000000..887c9515bcdae --- /dev/null +++ b/tests/ui/splat/splat-method-tuple-simple.rs @@ -0,0 +1,40 @@ +//@ run-pass +//! Test using `#[splat]` on method tuple arguments (with receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + 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 + } +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl TupleStruct { + fn tuple_2(&self, #[splat] (a, _b): (u32, i8)) -> u32 { + a + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.tuple_2((1, 2)); + + foo.tuple_2(1u32, 2i8); + foo.tuple_4(1u32, 2i8, (), 3f32); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct.tuple_2(1u32, 2i8); +} diff --git a/tests/ui/splat/splat-non-tuple.rs b/tests/ui/splat/splat-non-tuple.rs new file mode 100644 index 0000000000000..35841a770b226 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.rs @@ -0,0 +1,90 @@ +//! Test that using `#[splat]` on non-tuple function arguments is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn primitive_arg(#[splat] x: u32) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +enum NotATuple { + A(u32), + B(i8), +} + +fn enum_arg(#[splat] y: NotATuple) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple + +trait FooTrait { + fn tuple_1(#[splat] _: (u32,)); //~ NOTE type in trait + + // 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) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo + +impl Foo { + fn tuple_2_self( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + (a, b): (u32, i8), + ) -> u32 { + a + } +} + +impl FooTrait for Foo { + fn tuple_1(_: (u32,)) {} + //~^ ERROR method `tuple_1` has an incompatible type for trait + //~| NOTE expected fn with arg 0 splatted, found fn with no splatted arg + //~| NOTE expected signature `fn(#[splat] (_,))` + //~| NOTE found signature `fn((_,))` + + fn tuple_4( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + _: (u32, i8, (), f32), + ) { + } +} + +struct TupleStruct(u32, i8); + +fn tuple_struct_arg(#[splat] z: TupleStruct) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct + +impl TupleStruct { + fn tuple_2( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + (a, b): (f32, f64), + ) -> f32 { + 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() { + // FIXME(splat): is it enough for just the definitions/callees to error, + // or should the callers also error? + primitive_arg(1u32); + enum_arg(NotATuple::A(1u32)); + + let foo = Foo; + struct_arg(foo); + foo.tuple_2_self((1u32, 2i8)); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct_arg(tuple_struct); + tuple_struct.tuple_2((1f32, 2f64)); + TupleStruct::tuple_1(1u32); + tuple_struct.tuple_4((1u32, 2i8, (), 3f32)); +} diff --git a/tests/ui/splat/splat-non-tuple.stderr b/tests/ui/splat/splat-non-tuple.stderr new file mode 100644 index 0000000000000..64f7148d8dbd2 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.stderr @@ -0,0 +1,54 @@ +error[E0053]: method `tuple_1` has an incompatible type for trait + --> $DIR/splat-non-tuple.rs:38:5 + | +LL | fn tuple_1(_: (u32,)) {} + | ^^^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-non-tuple.rs:17:5 + | +LL | fn tuple_1(#[splat] _: (u32,)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] (_,))` + found signature `fn((_,))` + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-non-tuple.rs:7:30 + | +LL | fn primitive_arg(#[splat] x: u32) {} + | ^^^ +... +LL | primitive_arg(1u32); + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple (NotATuple) + --> $DIR/splat-non-tuple.rs:14:25 + | +LL | fn enum_arg(#[splat] y: NotATuple) {} + | ^^^^^^^^^ +... +LL | enum_arg(NotATuple::A(1u32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo (Foo) + --> $DIR/splat-non-tuple.rs:25:27 + | +LL | fn struct_arg(#[splat] z: Foo) {} + | ^^^ +... +LL | struct_arg(foo); + | ^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct (TupleStruct) + --> $DIR/splat-non-tuple.rs:54:33 + | +LL | fn tuple_struct_arg(#[splat] z: TupleStruct) {} + | ^^^^^^^^^^^ +... +LL | tuple_struct_arg(tuple_struct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0053, E0277. +For more information about an error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-overload-at-home-fail.rs b/tests/ui/splat/splat-overload-at-home-fail.rs new file mode 100644 index 0000000000000..8e6a375e0eb96 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.rs @@ -0,0 +1,41 @@ +//! Test error cases for `#[splat]` "overloading at home" example code. +//! Splatted calls that don't match any registered MethodArgs impl should fail. +#![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 { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // No impl for (f32,) — wrong type + foo.method(42f32); + //~^ ERROR mismatched types + + // No impl for (i32,i32) - wrong type + foo.method(42i32, 42i32); + //~^ ERROR mismatched types +} diff --git a/tests/ui/splat/splat-overload-at-home-fail.stderr b/tests/ui/splat/splat-overload-at-home-fail.stderr new file mode 100644 index 0000000000000..fcedccf28c359 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.stderr @@ -0,0 +1,40 @@ +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:35:16 + | +LL | foo.method(42f32); + | ------ ^^^^^ expected `i32`, found `f32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:26:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ ---------------- +help: change the type of the numeric literal from `f32` to `i32` + | +LL - foo.method(42f32); +LL + foo.method(42i32); + | + +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:39:23 + | +LL | foo.method(42i32, 42i32); + | ------ ^^^^^ expected `String`, found `i32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:26:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ +help: try using a conversion method + | +LL | foo.method(42i32, 42i32.to_string()); + | ++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. 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..621f0e04f67e1 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home.rs @@ -0,0 +1,52 @@ +//@ run-pass +// ignore-tidy-linelength +//! 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 { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // 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(()); + //foo.method((42i32,)); + + // Generic tuple trait implementers work without explicit tuple type parameters. + foo.method::<()>(); + foo.method(); + + foo.method::<(i32,)>(42i32); + foo.method::<(i32,)>(42); + foo.method(42i32); + foo.method(42); + + foo.method::<(i32, String)>(42i32, "asdf".to_owned()); + foo.method::<(i32, String)>(42, "asdf".to_owned()); + foo.method(42i32, "asdf".to_owned()); + foo.method(42, "asdf".to_owned()); +} diff --git a/tests/ui/splat/splat-trait-tuple.rs b/tests/ui/splat/splat-trait-tuple.rs new file mode 100644 index 0000000000000..a5b74a40cffd9 --- /dev/null +++ b/tests/ui/splat/splat-trait-tuple.rs @@ -0,0 +1,45 @@ +//@ run-pass +//! Test using `#[splat]` on trait assoc function/method tuple arguments. + +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn tuple_1_trait(#[splat] _: (u32,)); + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)); +} + +struct Foo; + +impl FooTrait for Foo { + // Currently, splat attributes on impls must match traits. This provides better UX. + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl FooTrait for TupleStruct { + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1_trait((1u32,)); + //foo.tuple_2_trait((1, 3.5)); + + Foo::tuple_1_trait(1u32); + foo.tuple_2_trait(1, 3.5); + + let tuple_struct = TupleStruct(1u32, 2i8); + TupleStruct::tuple_1_trait(1u32); + tuple_struct.tuple_2_trait(1, 3.5) +} diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs new file mode 100644 index 0000000000000..3f29b9d292d22 --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs @@ -0,0 +1,16 @@ +//! Test that using `#[splat]` incorrectly on unsafe functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + unsafe { + unsafe_wrong_type(1u32); + } +} diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..67935e170671a --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr @@ -0,0 +1,20 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-unsafe-fn-tuple-fail.rs:9:30 + | +LL | unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-unsafe-fn-tuple-fail.rs:6:42 + | +LL | unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | unsafe_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-unsafe-fn-tuple.rs b/tests/ui/splat/splat-unsafe-fn-tuple.rs new file mode 100644 index 0000000000000..0b9b3510ad50b --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple.rs @@ -0,0 +1,19 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of unsafe functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +unsafe fn unsafe_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + unsafe { + unsafe_tuple_args(1u32, 2i8); + unsafe_tuple_args(1, 2); + + unsafe_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + unsafe_splat_non_terminal_arg(1, 2, 3.5); + } +} diff --git a/tests/ui/splat/splat-where-clause.rs b/tests/ui/splat/splat-where-clause.rs new file mode 100644 index 0000000000000..42a23fe67b216 --- /dev/null +++ b/tests/ui/splat/splat-where-clause.rs @@ -0,0 +1,44 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments with where clause bounds. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn where_splat(#[splat] _t: T) where T: std::marker::Tuple {} + +fn where_splat_with_extra(#[splat] _t: T, _extra: u32) where T: std::marker::Tuple {} + +fn impl_tuple_splat(#[splat] _t: impl std::marker::Tuple) {} + +fn impl_tuple_splat_with_extra(#[splat] _t: impl std::marker::Tuple, _extra: u32) {} + +fn main() { + // empty tuple + where_splat(); + + // single element + where_splat(1u32); + where_splat(1); + + // two elements + where_splat(1u32, 2i8); + where_splat(1, 2); + + // three elements + where_splat(1u32, 2i8, 3.0f64); + where_splat(1, 2, 3.0); + + // with extra non-splatted arg + where_splat_with_extra(1u32, 2i8, 42u32); + where_splat_with_extra(1, 2, 42); + + // impl Trait syntax variants + impl_tuple_splat(); + impl_tuple_splat(1u32); + impl_tuple_splat(1u32, 2i8); + impl_tuple_splat(1, 2, 3.0); + + impl_tuple_splat_with_extra(1u32, 2i8, 42u32); + impl_tuple_splat_with_extra(1, 2, 42); +} diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index f88fdf7509c76..59085aeb3279b 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h27f62b2d0b2beac6E) +error: symbol-name(_ZN5basic4main17h33f35fba43592008E) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h27f62b2d0b2beac6) +error: demangling(basic::main::h33f35fba43592008) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index c1fc9f4b1cea1..367c2d14011ac 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h1eb769490ff06e77E) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hc741419c44ba4d79E) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::h1eb769490ff06e77) +error: demangling(issue_60925::foo::Foo::foo::hc741419c44ba4d79) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name]