Skip to content

Commit 4abb0aa

Browse files
committed
Add HIR FnDecl for #[splat]
1 parent 32cfe53 commit 4abb0aa

11 files changed

Lines changed: 148 additions & 19 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3052,9 +3052,30 @@ impl FnDecl {
30523052
pub fn has_self(&self) -> bool {
30533053
self.inputs.get(0).is_some_and(Param::is_self)
30543054
}
3055+
30553056
pub fn c_variadic(&self) -> bool {
30563057
self.inputs.last().is_some_and(|arg| matches!(arg.ty.kind, TyKind::CVarArgs))
30573058
}
3059+
3060+
/// The marker index for "no splatted arguments".
3061+
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`.
3062+
pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX;
3063+
3064+
/// Returns a splatted argument index, if any are present.
3065+
pub fn splatted(&self) -> Option<u16> {
3066+
self.inputs.iter().enumerate().find_map(|(index, arg)| {
3067+
if index == Self::NO_SPLATTED_ARG_INDEX as usize {
3068+
// AST validation has already checked the splatted argument index is valid, so just
3069+
// ignore invalid indexes here.
3070+
None
3071+
} else {
3072+
arg.attrs
3073+
.iter()
3074+
.any(|attr| attr.has_name(sym::splat))
3075+
.then_some(u16::try_from(index).unwrap())
3076+
}
3077+
})
3078+
}
30583079
}
30593080

30603081
/// Is the trait definition an auto trait?

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
199199

200200
let is_method = self.is_method(sig_id, span);
201201

202-
let (param_count, c_variadic) = self.param_count(sig_id);
202+
let (param_count, c_variadic, splatted) = self.param_count(sig_id);
203203

204204
if !self.check_block_soundness(delegation, sig_id, is_method, param_count) {
205205
return self.generate_delegation_error(span, delegation);
@@ -215,6 +215,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
215215
sig_id,
216216
param_count,
217217
c_variadic,
218+
splatted,
218219
span,
219220
&generics,
220221
delegation.id,
@@ -367,10 +368,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
367368
self.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id())
368369
}
369370

370-
// Function parameter count, including C variadic `...` if present.
371-
fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/) {
371+
// Function parameter count, including C variadic `...` and `#[splat]` if present.
372+
fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/, Option<u16> /*splatted*/) {
372373
let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
373-
(sig.inputs().len() + usize::from(sig.c_variadic()), sig.c_variadic())
374+
// FIXME(splat): use `sig.splatted()` once FnSig has it
375+
(sig.inputs().len() + usize::from(sig.c_variadic()), sig.c_variadic(), None)
374376
}
375377

376378
fn lower_delegation_decl(
@@ -379,6 +381,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
379381
sig_id: DefId,
380382
param_count: usize,
381383
c_variadic: bool,
384+
splatted: Option<u16>,
382385
span: Span,
383386
generics: &GenericsGenerationResults<'hir>,
384387
call_path_node_id: NodeId,
@@ -429,7 +432,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
429432
output: hir::FnRetTy::Return(output),
430433
fn_decl_kind: FnDeclFlags::default()
431434
.set_lifetime_elision_allowed(true)
432-
.set_c_variadic(c_variadic),
435+
.set_c_variadic(c_variadic)
436+
.set_splatted(splatted, inputs.len())
437+
.unwrap(),
433438
})
434439
}
435440

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1776,12 +1776,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
17761776
coro: Option<CoroutineKind>,
17771777
) -> &'hir hir::FnDecl<'hir> {
17781778
let c_variadic = decl.c_variadic();
1779+
let mut splatted = decl.splatted();
17791780

17801781
// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
17811782
// as they are not explicit in HIR/Ty function signatures.
17821783
// (instead, the `c_variadic` flag is set to `true`)
17831784
let mut inputs = &decl.inputs[..];
17841785
if decl.c_variadic() {
1786+
// Splat + variadic errors in AST validation, so just ignore one of them here.
1787+
splatted = None;
17851788
inputs = &inputs[..inputs.len() - 1];
17861789
}
17871790
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
@@ -1871,7 +1874,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
18711874
.set_lifetime_elision_allowed(
18721875
self.owner.id == fn_node_id && self.owner.lifetime_elision_allowed,
18731876
)
1874-
.set_c_variadic(c_variadic);
1877+
.set_c_variadic(c_variadic)
1878+
.set_splatted(splatted, inputs.len())
1879+
.unwrap();
18751880

18761881
self.arena.alloc(hir::FnDecl { inputs, output, fn_decl_kind })
18771882
}

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,18 @@ impl<'a> AstValidator<'a> {
412412
})
413413
.unzip();
414414

415+
// A splatted argument at the "no splatted" marker index is not supported (this is an
416+
// unlikely edge case).
417+
if let (Some(&splatted_arg_index), Some(&splatted_span)) =
418+
(splatted_arg_indexes.last(), splatted_spans.last())
419+
&& splatted_arg_index == FnDecl::NO_SPLATTED_ARG_INDEX
420+
{
421+
self.dcx().emit_err(diagnostics::InvalidSplattedArg {
422+
splatted_arg_index,
423+
span: splatted_span,
424+
});
425+
}
426+
415427
// Multiple splatted arguments are invalid: we can't know which arguments go in each splat.
416428
if splatted_arg_indexes.len() > 1 {
417429
self.dcx()

compiler/rustc_ast_passes/src/diagnostics.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ pub(crate) struct FnParamCVarArgsNotLast {
123123
pub span: Span,
124124
}
125125

126+
#[derive(Diagnostic)]
127+
#[diag("`#[splat]` is not supported on argument index {$splatted_arg_index}")]
128+
#[help("remove `#[splat]`, or use it on an argument closer to the start of the argument list")]
129+
pub(crate) struct InvalidSplattedArg {
130+
pub splatted_arg_index: u16,
131+
132+
#[primary_span]
133+
#[label("`#[splat]` is not supported here")]
134+
pub span: Span,
135+
}
136+
126137
#[derive(Diagnostic)]
127138
#[diag("multiple `#[splat]`s are not allowed in the same function")]
128139
#[help("remove `#[splat]` from all but one argument")]

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ use rustc_middle::bug;
1515
use rustc_middle::hir::place::PlaceBase;
1616
use rustc_middle::mir::{AnnotationSource, ConstraintCategory, ReturnConstraint};
1717
use rustc_middle::ty::{
18-
self, FnSigKind, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitor,
19-
fold_regions,
18+
self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitor, fold_regions,
2019
};
2120
use rustc_span::{Ident, Span, kw};
2221
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
@@ -1094,9 +1093,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
10941093
}
10951094

10961095
// Build a new closure where the return type is an owned value, instead of a ref.
1097-
let fn_sig_kind = FnSigKind::default()
1098-
.set_safety(hir::Safety::Safe)
1099-
.set_c_variadic(liberated_sig.c_variadic());
1096+
// The new closure is safe, but otherwise has the same ABI, splat, and c-variadic.
1097+
let fn_sig_kind = liberated_sig.fn_sig_kind.set_safety(hir::Safety::Safe);
11001098
let closure_sig_as_fn_ptr_ty = Ty::new_fn_ptr(
11011099
tcx,
11021100
ty::Binder::dummy(tcx.mk_fn_sig(

compiler/rustc_hir/src/hir.rs

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4018,13 +4018,29 @@ pub struct Param<'hir> {
40184018
pub span: Span,
40194019
}
40204020

4021+
/// Error type for splatted argument index errors.
4022+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4023+
pub enum SplattedArgIndexError {
4024+
/// The splatted argument index is invalid.
4025+
/// Currently the only unsupported index is `u16::MAX`, which is used to indicate that no argument
4026+
/// is splatted.
4027+
InvalidIndex { splatted_arg_index: u16 },
4028+
4029+
/// The splatted argument index is outside the bounds of the function arguments.
4030+
OutOfBounds { splatted_arg_index: u16, args_len: u16 },
4031+
}
4032+
40214033
/// Contains the packed non-type fields of a function declaration.
4022-
// FIXME(splat): add the splatted argument index as a u16
40234034
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40244035
#[derive(Encodable, Decodable, StableHash)]
40254036
pub struct FnDeclFlags {
40264037
/// Holds the c_variadic and lifetime_elision_allowed bitflags, and 3 bits for the `ImplicitSelfKind`.
40274038
flags: u8,
4039+
4040+
/// Which function argument is splatted into multiple arguments in callers, if any?
4041+
/// Splatting functions with `u16::MAX` arguments is not supported, see `FnSigKind` for
4042+
/// details.
4043+
splatted: u16,
40284044
}
40294045

40304046
impl fmt::Debug for FnDeclFlags {
@@ -4036,11 +4052,15 @@ impl fmt::Debug for FnDeclFlags {
40364052
f.field(&"LifetimeElisionAllowed");
40374053
} else {
40384054
f.field(&"NoLifetimeElision");
4039-
};
4055+
}
40404056

40414057
if self.c_variadic() {
40424058
f.field(&"CVariadic");
4043-
};
4059+
}
4060+
4061+
if let Some(index) = self.splatted() {
4062+
f.field(&format!("Splatted({})", index));
4063+
}
40444064

40454065
f.finish()
40464066
}
@@ -4056,14 +4076,20 @@ impl FnDeclFlags {
40564076
/// Bitflag for lifetime elision.
40574077
const LIFETIME_ELISION_ALLOWED_FLAG: u8 = 1 << 4;
40584078

4059-
/// Create a new FnDeclKind with no implicit self, no lifetime elision, and no C-style variadic argument.
4079+
/// Marker index for "no splatted argument".
4080+
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`.
4081+
const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX;
4082+
4083+
/// Create a new FnDeclKind with no implicit self, no lifetime elision, no C-style variadic
4084+
/// argument, and no splatting.
40604085
/// To modify these flags, use the `set_*` methods, for readability.
40614086
// FIXME: use Default instead when that trait is const stable.
40624087
pub const fn default() -> Self {
4063-
Self { flags: 0 }
4088+
Self { flags: 0, splatted: 0 }
40644089
.set_implicit_self(ImplicitSelfKind::None)
40654090
.set_lifetime_elision_allowed(false)
40664091
.set_c_variadic(false)
4092+
.set_no_splatted_args()
40674093
}
40684094

40694095
/// Set the implicit self kind.
@@ -4106,6 +4132,41 @@ impl FnDeclFlags {
41064132
self
41074133
}
41084134

4135+
/// Set the splatted argument index.
4136+
/// The number of function arguments is used for error checking.
4137+
#[must_use = "this method does not modify the receiver"]
4138+
pub const fn set_splatted(
4139+
mut self,
4140+
splatted: Option<u16>,
4141+
args_len: usize,
4142+
) -> Result<Self, SplattedArgIndexError> {
4143+
if let Some(splatted_arg_index) = splatted {
4144+
if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX {
4145+
// This index value is used as a marker for "no splatting", so it is unsupported.
4146+
return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index });
4147+
} else if splatted_arg_index as usize >= args_len {
4148+
return Err(SplattedArgIndexError::OutOfBounds {
4149+
splatted_arg_index,
4150+
args_len: args_len as u16,
4151+
});
4152+
}
4153+
4154+
self.splatted = splatted_arg_index;
4155+
} else {
4156+
self.splatted = Self::NO_SPLATTED_ARG_INDEX;
4157+
}
4158+
4159+
Ok(self)
4160+
}
4161+
4162+
/// Set "no splatted arguments" for the function declaration.
4163+
#[must_use = "this method does not modify the receiver"]
4164+
pub const fn set_no_splatted_args(mut self) -> Self {
4165+
self.splatted = Self::NO_SPLATTED_ARG_INDEX;
4166+
4167+
self
4168+
}
4169+
41094170
/// Get the implicit self kind.
41104171
pub const fn implicit_self(self) -> ImplicitSelfKind {
41114172
match self.flags & Self::IMPLICIT_SELF_MASK {
@@ -4127,6 +4188,11 @@ impl FnDeclFlags {
41274188
pub const fn lifetime_elision_allowed(self) -> bool {
41284189
self.flags & Self::LIFETIME_ELISION_ALLOWED_FLAG != 0
41294190
}
4191+
4192+
/// Get the splatted argument index, if any.
4193+
pub const fn splatted(self) -> Option<u16> {
4194+
if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) }
4195+
}
41304196
}
41314197

41324198
/// Represents the header (not the body) of a function declaration.
@@ -4174,6 +4240,10 @@ impl<'hir> FnDecl<'hir> {
41744240
self.fn_decl_kind.lifetime_elision_allowed()
41754241
}
41764242

4243+
pub fn splatted(&self) -> Option<u16> {
4244+
self.fn_decl_kind.splatted()
4245+
}
4246+
41774247
pub fn dummy(span: Span) -> Self {
41784248
Self {
41794249
inputs: &[],

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ fn fn_sig_suggestion<'tcx>(
450450
.iter()
451451
.enumerate()
452452
.map(|(i, ty)| {
453-
Some(match ty.kind() {
453+
let arg_ty = match ty.kind() {
454454
ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(),
455455
ty::Ref(reg, ref_ty, mutability) if i == 0 => {
456456
let reg = format!("{reg} ");
@@ -477,7 +477,8 @@ fn fn_sig_suggestion<'tcx>(
477477
format!("_: {ty}")
478478
}
479479
}
480-
})
480+
};
481+
Some(format!("{arg_ty}"))
481482
})
482483
.chain(std::iter::once(if sig.c_variadic() { Some("...".to_string()) } else { None }))
483484
.flatten()

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1711,8 +1711,9 @@ fn check_fn_or_method<'tcx>(
17111711

17121712
if sig.abi() == ExternAbi::RustCall {
17131713
let span = tcx.def_span(def_id);
1714-
let has_implicit_self = hir_decl.implicit_self() != hir::ImplicitSelfKind::None;
1714+
let has_implicit_self = hir_decl.implicit_self().has_implicit_self();
17151715
let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 });
1716+
// FIXME(splat): use `sig.splatted()` once FnSig has it
17161717
// Check that the argument is a tuple and is sized
17171718
if let Some(ty) = inputs.next() {
17181719
wfcx.register_bound(

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3497,6 +3497,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
34973497

34983498
debug!(?output_ty);
34993499

3500+
debug!(?abi, ?safety, ?decl.fn_decl_kind, input_tys_len = ?input_tys.len());
3501+
// FIXME(splat): use `set_splatted()` once FnSig has it
35003502
let fn_sig_kind = FnSigKind::default()
35013503
.set_abi(abi)
35023504
.set_safety(safety)

0 commit comments

Comments
 (0)