diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3e1fff594040e..728b14246458f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3064,6 +3064,9 @@ impl FnDecl { /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`. pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; + /// The maximum valid splatted argument index. + pub const MAX_VALID_SPLATTED_ARG_INDEX: u8 = Self::NO_SPLATTED_ARG_INDEX - 1; + /// Returns a splatted argument index, if any are present. pub fn splatted(&self) -> Option { self.inputs.iter().enumerate().find_map(|(index, arg)| { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 06925994b052c..281f417500c55 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -16,6 +16,7 @@ //! constructions produced by proc macros. This pass is only intended for simple checks that do not //! require name resolution or type checking, or other kinds of complex analysis. +use std::collections::BTreeMap; use std::mem; use std::str::FromStr; @@ -34,7 +35,7 @@ use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES, }; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use crate::diagnostics::{self, TildeConstReason}; @@ -45,6 +46,41 @@ enum SelfSemantic { No, } +/// Is `#[splat]` allowed semantically in a function or closure? +/// Only applies to the function kind and header, the parameters are checked elsewhere. +enum SplatSemantic { + Yes, + NoClosures(Span), + NoAbiCall { span: Span, abi: Symbol }, +} + +impl SplatSemantic { + /// Returns if splatting is semantically allowed for the given `FnKind`, + /// Only checks the function kind and header, not the parameters. + fn from_fn_kind(fk: &FnKind<'_>) -> Self { + match fk { + FnKind::Fn(_, _, f) => Self::from_extern(f.sig.header.ext), + // Splatting closures is banned, because closure arguments are already de-tupled. + FnKind::Closure(_, _, _, expr) => SplatSemantic::NoClosures(expr.span), + } + } + + fn from_extern(ext: Extern) -> Self { + match ext { + Extern::None => SplatSemantic::Yes, + // FIXME(splat): should splatting extern "C" or other ABIs be allowed? + Extern::Implicit(_) => SplatSemantic::Yes, + // For now, splatting rust-call is banned, because it already de-tuples args. + Extern::Explicit(abi_str, span) => match abi_str.symbol_unescaped { + sym::rust_dash_call => { + SplatSemantic::NoAbiCall { span, abi: abi_str.symbol_unescaped } + } + _ => SplatSemantic::Yes, + }, + } + } +} + enum TraitOrImpl { Trait { vis: Span, constness: Const }, TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span }, @@ -350,10 +386,15 @@ impl<'a> AstValidator<'a> { }); } - fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) { + fn check_fn_decl( + &self, + fn_decl: &FnDecl, + self_semantic: SelfSemantic, + splat_semantic: SplatSemantic, + ) { self.check_decl_num_args(fn_decl); let c_variadic_span = self.check_decl_cvariadic_pos(fn_decl); - self.check_decl_splatting(fn_decl, c_variadic_span); + self.check_decl_splatting(fn_decl, c_variadic_span, splat_semantic); self.check_decl_attrs(fn_decl); self.check_decl_self_param(fn_decl, self_semantic); } @@ -399,42 +440,76 @@ impl<'a> AstValidator<'a> { /// Emits an error if a function declaration has more than one splatted argument, with a /// C-variadic parameter, or a splat at an unsupported index (for performance). /// Example: `fn foo(#[splat] x: (), #[splat] y: ())` will emit an error. - fn check_decl_splatting(&self, fn_decl: &FnDecl, c_variadic_span: Option) { - let (splatted_arg_indexes, mut splatted_spans): (Vec, Vec) = fn_decl + fn check_decl_splatting( + &self, + fn_decl: &FnDecl, + c_variadic_span: Option, + splat_semantic: SplatSemantic, + ) { + let mut splatted_arg_spans: BTreeMap> = fn_decl .inputs .iter() .enumerate() .filter_map(|(index, arg)| { - arg.attrs + let splat_arg_spans: Vec = arg + .attrs .iter() - .any(|attr| attr.has_name(sym::splat)) - .then_some((u16::try_from(index).unwrap(), arg.span)) + .filter_map(|attr| attr.has_name(sym::splat).then_some(attr.span)) + .collect(); + if splat_arg_spans.is_empty() { + None + } else { + Some((u16::try_from(index).unwrap(), splat_arg_spans)) + } }) - .unzip(); + .collect(); // 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 >= u16::from(FnDecl::NO_SPLATTED_ARG_INDEX) - { - self.dcx().emit_err(diagnostics::InvalidSplattedArg { - splatted_arg_index, - span: splatted_span, + // supported. It is ok to drop these spans after issuing this error, because they are + // always invalid. + let out_of_range_spans = + splatted_arg_spans.split_off(&u16::from(FnDecl::NO_SPLATTED_ARG_INDEX)); + if !out_of_range_spans.is_empty() { + self.dcx().emit_err(diagnostics::InvalidSplattedArgs { + max_valid_splatted_arg_index: u16::from(FnDecl::MAX_VALID_SPLATTED_ARG_INDEX), + first_invalid_splatted_arg_index: *out_of_range_spans.keys().next().unwrap(), + spans: out_of_range_spans.values().flatten().copied().collect(), }); } - // Multiple splatted arguments are invalid: we can't know which arguments go in each splat. - if splatted_arg_indexes.len() > 1 { - self.dcx() - .emit_err(diagnostics::DuplicateSplattedArgs { spans: splatted_spans.clone() }); - } + if !splatted_arg_spans.is_empty() { + let splatted_spans = || splatted_arg_spans.values().flatten().copied().collect(); - if let Some(c_variadic_span) = c_variadic_span - && !splatted_spans.is_empty() - { - splatted_spans.push(c_variadic_span); - self.dcx().emit_err(diagnostics::CVarArgsAndSplat { spans: splatted_spans }); + // Multiple splatted arguments are invalid: we can't know which arguments go in each splat. + if splatted_arg_spans.len() > 1 { + self.dcx().emit_err(diagnostics::DuplicateSplattedArgs { spans: splatted_spans() }); + } + + // C-variadic parameters and splats are not allowed together. + if let Some(c_variadic_span) = c_variadic_span { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(c_variadic_span); + self.dcx().emit_err(diagnostics::CVarArgsAndSplat { spans: splatted_spans }); + } + + // Splatting is not allowed on closures, or some function ABIs. + match splat_semantic { + SplatSemantic::NoClosures(closure_span) => { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(closure_span); + self.dcx() + .emit_err(diagnostics::SplatNotAllowedOnClosures { spans: splatted_spans }); + } + SplatSemantic::NoAbiCall { span, abi } => { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(span); + self.dcx().emit_err(diagnostics::SplatNotAllowedOnAbiCall { + spans: splatted_spans, + abi, + }); + } + SplatSemantic::Yes => {} + } } } @@ -1055,7 +1130,11 @@ impl<'a> AstValidator<'a> { match &ty.kind { TyKind::FnPtr(bfty) => { self.check_fn_ptr_safety(bfty.decl_span, bfty.safety); - self.check_fn_decl(&bfty.decl, SelfSemantic::No); + self.check_fn_decl( + &bfty.decl, + SelfSemantic::No, + SplatSemantic::from_extern(bfty.ext), + ); Self::check_decl_no_pat(&bfty.decl, |span, _, _| { self.dcx().emit_err(diagnostics::PatternFnPointer { span }); }); @@ -1746,7 +1825,8 @@ impl Visitor<'_> for AstValidator<'_> { Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes, _ => SelfSemantic::No, }; - self.check_fn_decl(fk.decl(), self_semantic); + let splat_semantic = SplatSemantic::from_fn_kind(&fk); + self.check_fn_decl(fk.decl(), self_semantic, splat_semantic); if let Some(&FnHeader { safety, .. }) = fk.header() { self.check_item_safety(span, safety); diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 7a78e8e6213a5..0b610431cadd8 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -124,18 +124,22 @@ pub(crate) struct FnParamCVarArgsNotLast { } #[derive(Diagnostic)] -#[diag("`#[splat]` is not supported on argument index {$splatted_arg_index}")] +#[diag( + "`#[splat]` is only supported on argument index {$max_valid_splatted_arg_index} or less, this `#[splat]` is on index {$first_invalid_splatted_arg_index}" +)] #[help("remove `#[splat]`, or use it on an argument closer to the start of the argument list")] -pub(crate) struct InvalidSplattedArg { - pub splatted_arg_index: u16, +pub(crate) struct InvalidSplattedArgs { + pub max_valid_splatted_arg_index: u16, + + pub first_invalid_splatted_arg_index: u16, #[primary_span] #[label("`#[splat]` is not supported here")] - pub span: Span, + pub spans: Vec, } #[derive(Diagnostic)] -#[diag("multiple `#[splat]`s are not allowed in the same function")] +#[diag("multiple `#[splat]`s are not allowed in the same function argument list")] #[help("remove `#[splat]` from all but one argument")] pub(crate) struct DuplicateSplattedArgs { #[primary_span] @@ -143,13 +147,30 @@ pub(crate) struct DuplicateSplattedArgs { } #[derive(Diagnostic)] -#[diag("`...` and `#[splat]` are not allowed in the same function")] +#[diag("`...` and `#[splat]` are not allowed in the same function argument list")] #[help("remove `#[splat]` or remove `...`")] pub(crate) struct CVarArgsAndSplat { #[primary_span] pub spans: Vec, } +#[derive(Diagnostic)] +#[diag("`#[splat]` is not allowed on closure arguments")] +#[help("remove `#[splat]` or turn the closure into a function")] +pub(crate) struct SplatNotAllowedOnClosures { + #[primary_span] + pub spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("`#[splat]` is not allowed in the arguments of functions with the `{$abi}` ABI")] +#[help("remove `#[splat]` or change the ABI")] +pub(crate) struct SplatNotAllowedOnAbiCall { + #[primary_span] + pub spans: Vec, + pub abi: Symbol, +} + #[derive(Diagnostic)] #[diag("documentation comments cannot be applied to function parameters")] pub(crate) struct FnParamDocComment { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 7d85a5a4ded63..4321251f23ca2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -299,7 +299,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys, provided_args, expected_input_tys, - c_variadic, tuple_arguments, fn_def_id, callee_generic_args, @@ -573,10 +572,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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. + // 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, @@ -592,6 +588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { untupled_expected_input_tys: expected_input_tys, }; }; + let first_tupled_arg_index_usz = usize::from(first_tupled_arg_index); // The argument difference can range from -1 to u16::MAX - 1, so we count the number // of tupled arguments instead. @@ -604,28 +601,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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, + ?tupled_args_count, ?tuple_arguments, 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() { + if first_tupled_arg_index_usz >= 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: {}", + provided_args: {}", formal_input_tys.len(), provided_args.len(), ); } + let formal_input_tupled_ty = formal_input_tys[first_tupled_arg_index_usz]; // 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]); + let callee_tuple_type = self.resolve_vars_with_obligations(formal_input_tupled_ty); if callee_tuple_type.is_ty_var() && let Some(tupled_args_count) = tupled_args_count { @@ -674,7 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_tuple_type } } else { - self.structurally_resolve_type(call_span, formal_input_tys[first_tupled_arg_index]) + self.structurally_resolve_type(call_span, formal_input_tupled_ty) }; // We expected a tuple and got a tuple (or made one ourselves). @@ -687,7 +684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 Some(ty) = expected_input_tys.get(first_tupled_arg_index_usz) && let ty::Tuple(detup_expected_arg_tys) = ty.kind() { let substitute_tys = if Some(detup_expected_arg_tys.len()) == tupled_args_count { @@ -697,26 +694,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { detup_formal_arg_tys.iter() }; - expected_input_tys - .splice(first_tupled_arg_index..=first_tupled_arg_index, substitute_tys); + expected_input_tys.splice( + first_tupled_arg_index_usz..=first_tupled_arg_index_usz, + 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, + first_tupled_arg_index_usz..=first_tupled_arg_index_usz, detup_formal_arg_tys.iter(), ); if let Some(ref expected_input_tys) = expected_input_tys { @@ -724,7 +710,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys.len(), expected_input_tys.len(), "incorrectly constructed input type tuples, argument counts must match: \ - tuple_arguments: {tuple_arguments:?}", + tuple_arguments: {tuple_arguments:?}, \ + first_tupled_arg_index: {first_tupled_arg_index}", ) } } @@ -751,7 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index_usz) { let arg_def_span = arg_ty.span; vec![call_span, arg_def_span] @@ -768,7 +755,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_type.kind(), self.structurally_resolve_type( call_span, - formal_input_tys[first_tupled_arg_index] + formal_input_tys[first_tupled_arg_index_usz] ) .kind(), ) @@ -803,6 +790,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { untupled_expected_input_tys: None, } } else { + // 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, + tupled_args_count.unwrap().try_into().unwrap(), + ); + } + TupledArgCheckOutcome { new_err_code: err_code, untupled_formal_input_tys: formal_input_tys, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 335caf571aa6f..db753017f63d9 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -666,9 +666,9 @@ impl TupleArgumentsFlag { /// 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 */) { + fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { match self { - Self::TupleSplattedArg(index) => (Some(usize::from(index)), false), + Self::TupleSplattedArg(index) => (Some(u16::from(index)), false), Self::TupleAllCallArgs => (Some(0), false), Self::TupleSplattedSelfArg => (None, true), Self::DontTupleArguments => (None, false), diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index c286c0819aea2..650dcf61c66ac 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1225,26 +1225,50 @@ impl<'tcx> ThirBuildCx<'tcx> { 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 - ); - ( + let expr = if let Some(def_id) = def_id { + // We're calling a function via a FnDef, and its possibly generic type + 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 FnDef: 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, + // Create a new FnDef type, representing the splatted function arguments with + // user-supplied generic types applied 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, - ) + } + } else { + // We're calling a function via a FnPtr and its type + // FIXME(splat): populate the side-tables for FnPtrs, using liberated_fn_sigs if needed + let fn_ty = self.typeck_results.expr_ty_adjusted(expr); + let user_ty = + self.typeck_results.user_provided_types().get(expr.hir_id).copied().map(Box::new); + debug!( + "splatted_callee FnPtr: user_ty={:?} fn_ty={:?} arg_index={:?} arg_count={:?}", + user_ty, fn_ty, arg_index, arg_count, + ); + + if !fn_ty.is_fn() { + span_bug!(expr.span, "splatted FnPtr side-tables are not yet implemented") + } + + Expr { + temp_scope_id: expr.hir_id.local_id, + // Create a new FnPtr FnSig type, representing the splatted function arguments with + // user-supplied generic types applied + ty: Ty::new_fn_ptr(self.tcx, fn_ty.fn_sig(self.tcx)), + span, + kind: ExprKind::ZstLiteral { user_ty }, + } + }; + + (expr, arg_index, arg_count) } /// The callee has a splatted tuple argument. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b99198d9ee8c4..f223279b7cd3f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1738,6 +1738,7 @@ symbols! { rust_analyzer, rust_begin_unwind, rust_cold_cc, + rust_dash_call: "rust-call", rust_eh_personality, rust_future, rust_logo, diff --git a/tests/ui/splat/arg-count-ice-issue-158482.rs b/tests/ui/splat/arg-count-ice-issue-158482.rs new file mode 100644 index 0000000000000..27f036b8a5906 --- /dev/null +++ b/tests/ui/splat/arg-count-ice-issue-158482.rs @@ -0,0 +1,14 @@ +//! Checks that an incorrect number of arguments to splat doesn't panic. + +#![feature(splat)] +struct Foo {} +trait BarTrait { + fn trait_assoc(w: W, #[splat] _s: (u32, u8)); +} +impl BarTrait for Foo { + fn trait_assoc(_w: W, #[splat] _s: (u32, u8)) {} +} +fn main() { + Foo::trait_assoc() + //~^ ERROR: this splatted function takes 3 arguments, but 0 were provided +} diff --git a/tests/ui/splat/arg-count-ice-issue-158482.stderr b/tests/ui/splat/arg-count-ice-issue-158482.stderr new file mode 100644 index 0000000000000..b7f3a8070f2b2 --- /dev/null +++ b/tests/ui/splat/arg-count-ice-issue-158482.stderr @@ -0,0 +1,9 @@ +error[E0057]: this splatted function takes 3 arguments, but 0 were provided + --> $DIR/arg-count-ice-issue-158482.rs:12:5 + | +LL | Foo::trait_assoc() + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/reject-closure-issue-158605.rs b/tests/ui/splat/reject-closure-issue-158605.rs new file mode 100644 index 0000000000000..7464bab2a77d2 --- /dev/null +++ b/tests/ui/splat/reject-closure-issue-158605.rs @@ -0,0 +1,30 @@ +//! Checks that closures and rust-call functions can't be splatted. +//! This should be rejected until we decide on sensible semantics. + +#![feature(splat, unboxed_closures, tuple_trait)] +#![expect(incomplete_features)] + +use std::marker::Tuple; + +trait Trait: Tuple + Sized { + extern "rust-call" fn method(#[splat] self: Self); + //~^ ERROR: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI +} + +impl Trait for (i32, i64) { + extern "rust-call" fn method(#[splat] self: Self) { + //~^ ERROR: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + println!("{self:?}"); + } +} + +fn main() { + (|#[splat] x: i32| { + //~^ ERROR `#[splat]` is not allowed on closure arguments + println!("{x}"); + })(1); + + (1_i32, 2_i64).method(); + Trait::method(3_i32, 4_i64); + //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument +} diff --git a/tests/ui/splat/reject-closure-issue-158605.stderr b/tests/ui/splat/reject-closure-issue-158605.stderr new file mode 100644 index 0000000000000..c23211459a0db --- /dev/null +++ b/tests/ui/splat/reject-closure-issue-158605.stderr @@ -0,0 +1,36 @@ +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/reject-closure-issue-158605.rs:10:5 + | +LL | extern "rust-call" fn method(#[splat] self: Self); + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/reject-closure-issue-158605.rs:15:5 + | +LL | extern "rust-call" fn method(#[splat] self: Self) { + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed on closure arguments + --> $DIR/reject-closure-issue-158605.rs:22:7 + | +LL | (|#[splat] x: i32| { + | _______^^^^^^^^_________^ +LL | | +LL | | println!("{x}"); +LL | | })(1); + | |_____^ + | + = help: remove `#[splat]` or turn the closure into a function + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/reject-closure-issue-158605.rs:28:26 + | +LL | Trait::method(3_i32, 4_i64); + | ^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs index 31767f11d999b..f19ce37bc4566 100644 --- a/tests/ui/splat/splat-255-limit-fail.rs +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -47,7 +47,7 @@ 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, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 ) {} #[rustfmt::skip] @@ -68,7 +68,7 @@ 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, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 ) {} #[rustfmt::skip] @@ -88,7 +88,7 @@ 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, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, ) {} @@ -110,7 +110,7 @@ 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, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 _: A, ) {} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr index da62da8aa605b..a3a0b9cf3d350 100644 --- a/tests/ui/splat/splat-255-limit-fail.stderr +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -1,32 +1,32 @@ -error: `#[splat]` is not supported on argument index 255 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 --> $DIR/splat-255-limit-fail.rs:50:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[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 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 --> $DIR/splat-255-limit-fail.rs:71:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[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 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 --> $DIR/splat-255-limit-fail.rs:91:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[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 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 --> $DIR/splat-255-limit-fail.rs:113:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.rs b/tests/ui/splat/splat-async-fn-tuple-fail.rs index a1f67dfe606d6..308a6c4a2131e 100644 --- a/tests/ui/splat/splat-async-fn-tuple-fail.rs +++ b/tests/ui/splat/splat-async-fn-tuple-fail.rs @@ -8,7 +8,7 @@ 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 +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list fn main() { async_wrong_type(1u32); diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.stderr b/tests/ui/splat/splat-async-fn-tuple-fail.stderr index e643b29c88c8a..7c73382d73a5a 100644 --- a/tests/ui/splat/splat-async-fn-tuple-fail.stderr +++ b/tests/ui/splat/splat-async-fn-tuple-fail.stderr @@ -1,8 +1,8 @@ -error: multiple `#[splat]`s are not allowed in the same function +error: multiple `#[splat]`s are not allowed in the same function argument list --> $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 diff --git a/tests/ui/splat/splat-fn-ptr-cast.rs b/tests/ui/splat/splat-fn-ptr-cast.rs new file mode 100644 index 0000000000000..918e26c4dd741 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-cast.rs @@ -0,0 +1,17 @@ +//@ run-fail +//! Test never type casts to splatted and non-splatted functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![allow(unused_features)] + +fn main() { + // Bug #158603 regression test variants + #[rustfmt::skip] + let _x: fn(#[splat] (f32,)) = None.unwrap(); + // FIXME(splat): causes an ICE until #158603 is fixed + //x(1.0); + + let x: fn((i32,)) = None.unwrap(); + x((1,)); +} diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs new file mode 100644 index 0000000000000..321511f270915 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs @@ -0,0 +1,42 @@ +//! Test using `#[splat]` on tuple arguments of pointers to pointers to simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + +//@ 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" -> "" + +#![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 + #[rustfmt::skip] + let fn_pp: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); + unsafe { + (*fn_pp)(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + // The ICE means that code after this line is not fully checked + (*fn_pp)(1u32, 2i8); + } + + #[rustfmt::skip] + let fn_pp: *const fn(#[splat] (u32, i8), f64) = + splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); + unsafe { + (*fn_pp)(1, 2, 3.5); + (*fn_pp)(1u32, 2i8, 3.5f64); + } +} diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr new file mode 100644 index 0000000000000..726561b366f80 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr @@ -0,0 +1,24 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-ptr-tuple.rs:30:9 + | +LL | (*fn_pp)(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_ptr_tuple` +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/splat/splat-fn-ptr-rust-call.rs b/tests/ui/splat/splat-fn-ptr-rust-call.rs new file mode 100644 index 0000000000000..a9fe48a459d8a --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-rust-call.rs @@ -0,0 +1,18 @@ +//! Test using `#[splat]` on tuple arguments of pointers to "rust-call" functions. +//! Currently ICEs at a later stage, but AST validation should catch it earlier. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(unboxed_closures)] + +extern "rust-call" fn f(#[splat] _: ()) {} //~ ERROR `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + +fn main() { + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + let f2: extern "rust-call" fn(#[splat] ()) = f; //~ ERROR `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + // These errors could be confusing, but they're useful if the user meant to use "rust-call" + // instead of #[splat] + f(); //~ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + f2(); //~ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument +} diff --git a/tests/ui/splat/splat-fn-ptr-rust-call.stderr b/tests/ui/splat/splat-fn-ptr-rust-call.stderr new file mode 100644 index 0000000000000..6eacdfab5faac --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-rust-call.stderr @@ -0,0 +1,30 @@ +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/splat-fn-ptr-rust-call.rs:8:1 + | +LL | extern "rust-call" fn f(#[splat] _: ()) {} + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/splat-fn-ptr-rust-call.rs:13:13 + | +LL | let f2: extern "rust-call" fn(#[splat] ()) = f; + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/splat-fn-ptr-rust-call.rs:16:5 + | +LL | f(); + | ^^^ + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/splat-fn-ptr-rust-call.rs:17:5 + | +LL | f2(); + | ^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.rs b/tests/ui/splat/splat-fn-ptr-tuple-const.rs new file mode 100644 index 0000000000000..faf8501cc650a --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.rs @@ -0,0 +1,34 @@ +//! Test using `#[splat]` on tuple arguments of generic function constants. +//! Currently ICEs (#158603), but if we fix it, we'll want to know and update this test to pass. + +//@ 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" -> "" + +#![allow(incomplete_features)] +#![feature(splat, tuple_trait)] + +use std::marker::Tuple; + +fn f(#[splat] args: Args) {} + +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 + #[rustfmt::skip] + const F2: fn(#[splat] (u8, u32)) = f::<(u8, u32)>; + const R2: () = F2(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + + #[rustfmt::skip] + const F1: fn(#[splat] ((u8, u32),)) = f::<((u8, u32),)>; + const R1: () = F1((1, 2)); //~ ERROR splatted FnPtr side-tables are not yet implemented +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.stderr b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr new file mode 100644 index 0000000000000..1767782a9535e --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr @@ -0,0 +1,52 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-tuple-const.rs:29:20 + | +LL | const R2: () = F2(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::R2` +#1 [check_match] match-checking `main::R2` +#2 [mir_built] building MIR for `main::R2` +#3 [trivial_const] checking if `main::R2` is a trivial const +#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R2` +#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` +end of query stack +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-tuple-const.rs:33:20 + | +LL | const R1: () = F1((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::R1` +#1 [check_match] match-checking `main::R1` +#2 [mir_built] building MIR for `main::R1` +#3 [trivial_const] checking if `main::R1` is a trivial const +#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R1` +#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` +end of query stack +error: aborting due to 2 previous errors + diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs index 297dbc0457794..395d30baedaaf 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.rs +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -1,3 +1,6 @@ +//! Test using `#[splat]` on tuple arguments of pointers to simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + //@ failure-status: 101 //@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" @@ -10,9 +13,6 @@ //@ 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)] @@ -24,10 +24,10 @@ 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(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + // The ICE means that code after this line is not fully checked fn_ptr(1u32, 2i8); // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? @@ -39,15 +39,8 @@ fn main() { 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); - + // Bug #158603 regression test #[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); + let x: fn(#[splat] (i32,)) = None.unwrap(); + x(1); } diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr index a69175bd5e886..4cc861cafe968 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.stderr +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -1,4 +1,4 @@ -error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented | LL | fn_ptr(1, 2); | ^^^^^^^^^^^^ diff --git a/tests/ui/splat/splat-invalid.rs b/tests/ui/splat/splat-invalid.rs index 2c4bf57ef972a..1fe4d131fc584 100644 --- a/tests/ui/splat/splat-invalid.rs +++ b/tests/ui/splat/splat-invalid.rs @@ -4,14 +4,32 @@ #![feature(splat)] #![feature(c_variadic)] -fn multisplat_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} -//~^ ERROR multiple `#[splat]`s are not allowed in the same function +fn multisplat_fn_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list + +fn multisplat_arg_bad( + #[splat] + #[splat] + //~^ ERROR multiple `splat` attributes + (_a, _b): (u32, i8), +) { +} + +fn multisplat_arg_fn_bad( + #[splat] + //~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list + #[splat] + //~^ ERROR multiple `splat` attributes + (_a, _b): (u32, i8), + #[splat] (_c, _d): (u32, i8), +) { +} unsafe extern "C" fn splat_variadic(#[splat] (_a, _b): (u32, i8), varargs: ...) {} -//~^ ERROR `...` and `#[splat]` are not allowed in the same function +//~^ ERROR `...` and `#[splat]` are not allowed in the same function argument list unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} -//~^ ERROR `...` and `#[splat]` are not allowed in the same function +//~^ ERROR `...` and `#[splat]` are not allowed in the same function argument list //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { diff --git a/tests/ui/splat/splat-invalid.stderr b/tests/ui/splat/splat-invalid.stderr index 74c54c00cd285..86b1dbbb7e44b 100644 --- a/tests/ui/splat/splat-invalid.stderr +++ b/tests/ui/splat/splat-invalid.stderr @@ -1,35 +1,49 @@ -error: multiple `#[splat]`s are not allowed in the same function - --> $DIR/splat-invalid.rs:7:19 +error: multiple `#[splat]`s are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:7:22 | -LL | fn multisplat_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn multisplat_fn_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` from all but one argument -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:10:37 +error: multiple `#[splat]`s are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:19:5 + | +LL | #[splat] + | ^^^^^^^^ +LL | +LL | #[splat] + | ^^^^^^^^ +... +LL | #[splat] (_c, _d): (u32, i8), + | ^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:28:37 | LL | unsafe extern "C" fn splat_variadic(#[splat] (_a, _b): (u32, i8), varargs: ...) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ + | ^^^^^^^^ ^^^^^^^^^^^^ | = help: remove `#[splat]` or remove `...` error: `...` must be the last argument of a C-variadic function - --> $DIR/splat-invalid.rs:13:38 + --> $DIR/splat-invalid.rs:31:38 | LL | unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} | ^^^^^^^^^^^^ -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:13:38 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:31:38 | LL | unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} - | ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` or remove `...` error: incorrect function inside `extern` block - --> $DIR/splat-invalid.rs:18:8 + --> $DIR/splat-invalid.rs:36:8 | LL | extern "C" { | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body @@ -39,16 +53,16 @@ LL | fn splat_variadic3(#[splat] (_a, _b): (u32, i8), ...) {} = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:18:24 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:36:24 | LL | fn splat_variadic3(#[splat] (_a, _b): (u32, i8), ...) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ + | ^^^^^^^^ ^^^ | = help: remove `#[splat]` or remove `...` error: incorrect function inside `extern` block - --> $DIR/splat-invalid.rs:22:8 + --> $DIR/splat-invalid.rs:40:8 | LL | extern "C" { | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body @@ -60,27 +74,51 @@ LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html error: `...` must be the last argument of a C-variadic function - --> $DIR/splat-invalid.rs:22:24 + --> $DIR/splat-invalid.rs:40:24 | LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} | ^^^ -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:22:24 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:40:24 | LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} - | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ ^^^^^^^^ | = help: remove `#[splat]` or remove `...` +error: multiple `splat` attributes + --> $DIR/splat-invalid.rs:12:5 + | +LL | #[splat] + | ^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/splat-invalid.rs:11:5 + | +LL | #[splat] + | ^^^^^^^^ + +error: multiple `splat` attributes + --> $DIR/splat-invalid.rs:21:5 + | +LL | #[splat] + | ^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/splat-invalid.rs:19:5 + | +LL | #[splat] + | ^^^^^^^^ + error[E0053]: method `has_splat` has an incompatible type for trait - --> $DIR/splat-invalid.rs:42:5 + --> $DIR/splat-invalid.rs:60: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 + --> $DIR/splat-invalid.rs:52:5 | LL | fn has_splat(#[splat] _: ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,19 +126,19 @@ LL | fn has_splat(#[splat] _: ()); found signature `fn(())` error[E0053]: method `no_splat` has an incompatible type for trait - --> $DIR/splat-invalid.rs:44:5 + --> $DIR/splat-invalid.rs:62: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 + --> $DIR/splat-invalid.rs:54:5 | LL | fn no_splat(_: (u32, f64)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature `fn((_, _))` found signature `fn(#[splat] (_, _))` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs index 3f29b9d292d22..473aff7891636 100644 --- a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs @@ -7,7 +7,7 @@ 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 +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list fn main() { unsafe { diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr index 67935e170671a..7ad15f0f960e2 100644 --- a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr @@ -1,8 +1,8 @@ -error: multiple `#[splat]`s are not allowed in the same function +error: multiple `#[splat]`s are not allowed in the same function argument list --> $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