Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3057,6 +3057,9 @@ impl FnDecl {
pub fn c_variadic(&self) -> bool {
self.inputs.last().is_some_and(|arg| matches!(arg.ty.kind, TyKind::CVarArgs))
}
pub fn splatted(&self) -> bool {
self.inputs.last().is_some_and(|arg| arg.attrs.iter().any(|attr| attr.has_name(sym::splat)))
}
}

/// Is the trait definition an auto trait?
Expand Down
14 changes: 9 additions & 5 deletions compiler/rustc_ast_lowering/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {

// Here we use `root_function_id` as we can not get params information out of potential delegation reuse,
// we need a function to extract this information
let (param_count, c_variadic) = self.param_count(root_function_id);
let (param_count, c_variadic, splatted) = self.param_count(root_function_id);

let mut generics = self.lower_delegation_generics(
delegation,
Expand All @@ -211,6 +211,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
delegee_id,
param_count,
c_variadic,
splatted,
span,
&generics,
);
Expand Down Expand Up @@ -399,15 +400,15 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
}

// Function parameter count, including C variadic `...` if present.
fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/) {
fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/, bool /*splatted*/) {
if let Some(local_sig_id) = def_id.as_local() {
match self.resolver.delegation_fn_sig(local_sig_id) {
Some(sig) => (sig.param_count, sig.c_variadic),
None => (0, false),
Some(sig) => (sig.param_count, sig.c_variadic, sig.splatted),
None => (0, false, false),
}
} else {
let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
(sig.inputs().len() + usize::from(sig.c_variadic), sig.c_variadic)
(sig.inputs().len() + usize::from(sig.c_variadic), sig.c_variadic, sig.splatted)
}
}

Expand All @@ -416,6 +417,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
sig_id: DefId,
param_count: usize,
c_variadic: bool,
splatted: bool,
span: Span,
generics: &GenericsGenerationResults<'hir>,
) -> &'hir hir::FnDecl<'hir> {
Expand Down Expand Up @@ -444,6 +446,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
inputs,
output: hir::FnRetTy::Return(output),
c_variadic,
splatted,
lifetime_elision_allowed: true,
implicit_self: hir::ImplicitSelfKind::None,
})
Expand Down Expand Up @@ -757,6 +760,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
inputs: &[],
output: hir::FnRetTy::DefaultReturn(span),
c_variadic: false,
splatted: false,
lifetime_elision_allowed: true,
implicit_self: hir::ImplicitSelfKind::None,
});
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
inputs,
output,
c_variadic: false,
splatted: false,
implicit_self: hir::ImplicitSelfKind::None,
lifetime_elision_allowed: false,
});
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
coro: Option<CoroutineKind>,
) -> &'hir hir::FnDecl<'hir> {
let c_variadic = decl.c_variadic();
let splatted = decl.splatted();

// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
// as they are not explicit in HIR/Ty function signatures.
Expand Down Expand Up @@ -1811,6 +1812,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
inputs,
output,
c_variadic,
splatted,
lifetime_elision_allowed: self.resolver.lifetime_elision_allowed(fn_node_id),
implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| {
let is_mutable_pat = matches!(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ impl<'a> AstValidator<'a> {
sym::deny,
sym::expect,
sym::forbid,
sym::splat,
sym::warn,
];
!attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(*attr)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ pub(crate) struct FnParamDocComment {
pub span: Span,
}

// FIXME(splat): add splat to the allowed built-in attributes when it is complete/stabilized
#[derive(Diagnostic)]
#[diag(
"allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters"
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(const_block_items, "const block items are experimental");
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
gate_all!(impl_restriction, "`impl` restrictions are experimental");
gate_all!(splat, "`#[splat] fn` is incomplete", "call as func((a, ...)) instead");

if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub(crate) mod rustc_allocator;
pub(crate) mod rustc_dump;
pub(crate) mod rustc_internal;
pub(crate) mod semantics;
pub(crate) mod splat;
pub(crate) mod stability;
pub(crate) mod test_attrs;
pub(crate) mod traits;
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/splat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! Attribute parsing for the `#[splat]` function argument overloading attribute.
//! This attribute modifies typecheck to support overload resolution, then modifies codegen for performance.

use super::prelude::*;

pub(crate) struct SplatParser;

impl<S: Stage> NoArgsAttributeParser<S> for SplatParser {
const PATH: &[Symbol] = &[sym::splat];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Param),
// FIXME(splat): only allow MacroCall if the macro creates an argument
Allow(Target::MacroCall),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Splat;
}
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use crate::attributes::rustc_allocator::*;
use crate::attributes::rustc_dump::*;
use crate::attributes::rustc_internal::*;
use crate::attributes::semantics::*;
use crate::attributes::splat::*;
use crate::attributes::stability::*;
use crate::attributes::test_attrs::*;
use crate::attributes::traits::*;
Expand Down Expand Up @@ -325,6 +326,7 @@ attribute_parsers!(
Single<WithoutArgs<RustcStrictCoherenceParser>>,
Single<WithoutArgs<RustcTrivialFieldReadsParser>>,
Single<WithoutArgs<RustcUnsafeSpecializationMarkerParser>>,
Single<WithoutArgs<SplatParser>>,
Single<WithoutArgs<ThreadLocalParser>>,
Single<WithoutArgs<TrackCallerParser>>,
// tidy-alphabetical-end
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
liberated_sig.inputs().iter().copied(),
peeled_ty,
liberated_sig.c_variadic,
liberated_sig.splatted,
hir::Safety::Safe,
rustc_abi::ExternAbi::Rust,
)),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/type_check/input_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
user_provided_sig.inputs().iter().copied(),
output_ty,
user_provided_sig.c_variadic,
user_provided_sig.splatted,
user_provided_sig.safety,
user_provided_sig.abi,
);
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_codegen_cranelift/src/value_and_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,7 @@ pub(crate) fn assert_assignable<'tcx>(
let FnSig {
inputs_and_output: types_from,
c_variadic: c_variadic_from,
splatted: splatted_from,
safety: unsafety_from,
abi: abi_from,
} = from_sig;
Expand All @@ -881,6 +882,7 @@ pub(crate) fn assert_assignable<'tcx>(
let FnSig {
inputs_and_output: types_to,
c_variadic: c_variadic_to,
splatted: splatted_to,
safety: unsafety_to,
abi: abi_to,
} = to_sig;
Expand All @@ -898,6 +900,11 @@ pub(crate) fn assert_assignable<'tcx>(
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
from_sig, to_sig, fx,
);
assert_eq!(
splatted_from, splatted_to,
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
from_sig, to_sig, fx,
);
assert_eq!(
unsafety_from, unsafety_to,
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(
iter::once(i8p),
tcx.types.unit,
false,
false,
rustc_hir::Safety::Unsafe,
ExternAbi::Rust,
)),
Expand All @@ -1483,6 +1484,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(
[i8p, i8p].iter().cloned(),
tcx.types.unit,
false,
false,
rustc_hir::Safety::Unsafe,
ExternAbi::Rust,
)),
Expand All @@ -1492,6 +1494,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(
[try_fn_ty, i8p, catch_fn_ty],
tcx.types.i32,
false,
false,
rustc_hir::Safety::Unsafe,
ExternAbi::Rust,
));
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>(
[i8p],
tcx.types.unit,
false,
false,
hir::Safety::Unsafe,
ExternAbi::Rust,
)),
Expand All @@ -1290,6 +1291,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>(
[i8p, i8p],
tcx.types.unit,
false,
false,
hir::Safety::Unsafe,
ExternAbi::Rust,
)),
Expand All @@ -1299,6 +1301,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>(
[try_fn_ty, i8p, catch_fn_ty],
tcx.types.i32,
false,
false,
hir::Safety::Unsafe,
ExternAbi::Rust,
));
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};

// Split the rust-call tupled arguments off.
// FIXME(splat): un-tuple splatted arguments in codegen, for performance
let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall
&& let Some((tup, args)) = args.split_last()
{
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let arg_decl = &mir.local_decls[local];
let arg_ty = fx.monomorphize(arg_decl.ty);

// FIXME(splat): re-tuple splatted arguments that were un-tupled in the ABI
if Some(local) == mir.spread_arg {
// This argument (e.g., the last argument in the "rust-call" ABI)
// is a tuple that was spread at the ABI level and now we have
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
sig: &FnSigTys<TyCtxt<'tcx>>,
fn_header: &FnHeader<TyCtxt<'tcx>>,
) -> InterpResult<'tcx> {
let FnHeader { safety, c_variadic, abi } = fn_header;
let FnHeader { safety, c_variadic, splatted, abi } = fn_header;

for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
Expand Down Expand Up @@ -465,6 +465,9 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
sym::variadic => {
self.write_scalar(Scalar::from_bool(*c_variadic), &field_place)?;
}
sym::splat => {
self.write_scalar(Scalar::from_bool(*splatted), &field_place)?;
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};

// Special handling for the closure ABI: untuple the last argument.
// FIXME(splat): un-tuple splatted arguments that were tupled in typecheck
let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> =
if caller_abi == ExternAbi::RustCall && !args.is_empty() {
// Untuple
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_v2),
),

// The `#[splat]` attribute is part of the `splat` experiment
// that improves the ergonomics of function overloading, tracked in:
//
// - https://github.com/rust-lang/rust/issues/153629
gated!(
splat, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::Yes, experimental!(splat)
),

// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,9 @@ declare_features! (
(unstable, sparc_target_feature, "1.84.0", Some(132783)),
/// Allows specialization of implementations (RFC 1210).
(incomplete, specialization, "1.7.0", Some(31844)),
/// Experimental "splatting" of function call arguments at the call site.
/// e.g. `foo(a, b, c)` calls `#[splat] fn foo((a: A, b: B, c: C))`.
(incomplete, splat, "CURRENT_RUSTC_VERSION", Some(153629)),
/// Allows using `#[rustc_align_static(...)]` on static items.
(unstable, static_align, "1.91.0", Some(146177)),
/// Allows attributes on expressions and non-item statements.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,9 @@ pub enum AttributeKind {
span: Span,
},

/// Represents `#[splat]`
Splat(Span),

/// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
Stability {
stability: Stability,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ impl AttributeKind {
RustcUnsafeSpecializationMarker(..) => No,
Sanitize { .. } => No,
ShouldPanic { .. } => No,
Splat(..) => Yes,
Stability { .. } => Yes,
TargetFeature { .. } => No,
TestRunner(..) => Yes,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3935,6 +3935,8 @@ pub struct FnDecl<'hir> {
pub inputs: &'hir [Ty<'hir>],
pub output: FnRetTy<'hir>,
pub c_variadic: bool,
/// Is the last argument of the function splatted into multiple arguments in callers?
pub splatted: bool,
/// Does the function have an implicit self?
pub implicit_self: ImplicitSelfKind,
/// Is lifetime elision allowed.
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,8 +1212,14 @@ pub fn walk_fn_decl<'v, V: Visitor<'v>>(
visitor: &mut V,
function_declaration: &'v FnDecl<'v>,
) -> V::Result {
let FnDecl { inputs, output, c_variadic: _, implicit_self: _, lifetime_elision_allowed: _ } =
function_declaration;
let FnDecl {
inputs,
output,
c_variadic: _,
splatted: _,
implicit_self: _,
lifetime_elision_allowed: _,
} = function_declaration;
walk_list!(visitor, visit_ty_unambig, *inputs);
visitor.visit_fn_ret_ty(output)
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/check/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
error = true;
}

// FIXME(splat): also reject `#[splat]` on main function arguments
if let Some(attr_span) = find_attr!(tcx, main_def_id, TrackCaller(span) => *span) {
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span });
error = true;
Expand Down Expand Up @@ -155,6 +156,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
[],
expected_return_type,
false,
false,
hir::Safety::Safe,
ExternAbi::Rust,
));
Expand Down
Loading
Loading