Skip to content

Commit 7bb820b

Browse files
committed
Store 1 bit for splatting in FnDeclFlags
1 parent 84cab65 commit 7bb820b

7 files changed

Lines changed: 130 additions & 74 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3061,23 +3061,33 @@ impl FnDecl {
30613061
}
30623062

30633063
/// The marker index for "no splatted arguments".
3064-
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`.
3064+
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX`.
3065+
// FIXME(splat): if we remove this limit from hir::FnDecl and FnSig, all instances of this
3066+
// constant can go away entirely.
30653067
pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX;
30663068

3067-
/// Returns a splatted argument index, if any are present.
3068-
pub fn splatted(&self) -> Option<u16> {
3069-
self.inputs.iter().enumerate().find_map(|(index, arg)| {
3070-
if index == Self::NO_SPLATTED_ARG_INDEX as usize {
3071-
// AST validation has already checked the splatted argument index is valid, so just
3072-
// ignore invalid indexes here.
3073-
None
3074-
} else {
3075-
arg.attrs
3076-
.iter()
3077-
.any(|attr| attr.has_name(sym::splat))
3078-
.then_some(u16::try_from(index).unwrap())
3079-
}
3080-
})
3069+
/// Returns a splatted argument index and its span, if any splatted arguments are present.
3070+
#[inline]
3071+
pub fn splatted(&self) -> Option<(u16 /* arg_index */, Span)> {
3072+
let (index, span) = self.inputs.iter().enumerate().find_map(|(index, arg)| {
3073+
arg.attrs.iter().find_map(|attr| {
3074+
attr.has_name(sym::splat).then_some((u16::try_from(index).unwrap(), attr.span))
3075+
})
3076+
})?;
3077+
3078+
if index == Self::NO_SPLATTED_ARG_INDEX {
3079+
// AST validation has already checked the splatted argument index is valid, so just
3080+
// ignore invalid indexes here.
3081+
None
3082+
} else {
3083+
Some((index, span))
3084+
}
3085+
}
3086+
3087+
/// Returns `true` if the function has a splatted argument.
3088+
#[inline(always)]
3089+
pub fn has_splatted_arg(&self) -> bool {
3090+
self.splatted().is_some()
30813091
}
30823092
}
30833093

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
141141

142142
let is_method = self.is_method(sig_id, span);
143143

144-
let (param_count, c_variadic, splatted) = self.param_count(sig_id);
144+
let (param_count, c_variadic, has_splatted_arg) = self.param_count(sig_id);
145145

146146
let mut generics = self.uplift_delegation_generics(delegation, sig_id, item_id);
147147

@@ -157,7 +157,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
157157
sig_id,
158158
param_count,
159159
c_variadic,
160-
splatted,
160+
has_splatted_arg,
161161
span,
162162
&generics,
163163
);
@@ -275,17 +275,24 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
275275
}
276276

277277
// Function parameter count, including C variadic `...` and `#[splat]` if present.
278-
fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/, Option<u16> /*splatted*/) {
278+
fn param_count(
279+
&self,
280+
def_id: DefId,
281+
) -> (usize, bool /*c_variadic*/, bool /*has_splatted_arg*/) {
279282
let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
280-
(sig.inputs().len() + usize::from(sig.c_variadic()), sig.c_variadic(), sig.splatted())
283+
(
284+
sig.inputs().len() + usize::from(sig.c_variadic()),
285+
sig.c_variadic(),
286+
sig.splatted().is_some(),
287+
)
281288
}
282289

283290
fn lower_delegation_decl(
284291
&mut self,
285292
sig_id: DefId,
286293
param_count: usize,
287294
c_variadic: bool,
288-
splatted: Option<u16>,
295+
has_splatted_arg: bool,
289296
span: Span,
290297
generics: &GenericsGenerationResults<'hir>,
291298
) -> &'hir hir::FnDecl<'hir> {
@@ -319,8 +326,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
319326
fn_decl_kind: FnDeclFlags::default()
320327
.set_lifetime_elision_allowed(true)
321328
.set_c_variadic(c_variadic)
322-
.set_splatted(splatted, inputs.len())
323-
.unwrap(),
329+
.set_has_splatted_arg(has_splatted_arg),
324330
})
325331
}
326332

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,15 +1844,15 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
18441844
coro: Option<CoroutineKind>,
18451845
) -> &'hir hir::FnDecl<'hir> {
18461846
let c_variadic = decl.c_variadic();
1847-
let mut splatted = decl.splatted();
1847+
let mut splatted_arg_index = decl.splatted();
18481848

18491849
// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
18501850
// as they are not explicit in HIR/Ty function signatures.
18511851
// (instead, the `c_variadic` flag is set to `true`)
18521852
let mut inputs = &decl.inputs[..];
18531853
if decl.c_variadic() {
18541854
// Splat + variadic errors in AST validation, so just ignore one of them here.
1855-
splatted = None;
1855+
splatted_arg_index = None;
18561856
inputs = &inputs[..inputs.len() - 1];
18571857
}
18581858
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
@@ -1941,8 +1941,17 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
19411941
}))
19421942
.set_lifetime_elision_allowed(self.resolver.lifetime_elision_allowed(fn_node_id))
19431943
.set_c_variadic(c_variadic)
1944-
.set_splatted(splatted, inputs.len())
1945-
.unwrap();
1944+
.set_has_splatted_arg(splatted_arg_index.is_some());
1945+
1946+
if let Some((index, span)) = splatted_arg_index {
1947+
// For performance, just lower the one attribute fn args care about to HIR.
1948+
let local_id = inputs[usize::from(index)].hir_id.local_id;
1949+
assert!(!self.attrs.contains_key(&local_id));
1950+
self.attrs.insert(
1951+
local_id,
1952+
arena_vec![self; hir::Attribute::Parsed(hir::attrs::AttributeKind::Splat(span))],
1953+
);
1954+
}
19461955

19471956
self.arena.alloc(hir::FnDecl { inputs, output, fn_decl_kind })
19481957
}

compiler/rustc_hir/src/hir.rs

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use tracing::debug;
3434
use crate::attrs::AttributeKind;
3535
use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res};
3636
use crate::def_id::{DefId, LocalDefIdMap};
37+
use crate::find_attr;
3738
pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
3839
use crate::intravisit::{FnKind, VisitorExt};
3940
use crate::lints::DelayedLints;
@@ -3937,7 +3938,8 @@ pub enum SplattedArgIndexError {
39373938
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39383939
#[derive(Encodable, Decodable, HashStable_Generic)]
39393940
pub struct FnDeclFlags {
3940-
/// Holds the c_variadic and lifetime_elision_allowed bitflags, and 3 bits for the `ImplicitSelfKind`.
3941+
/// Holds the c_variadic, lifetime_elision_allowed, and has_splatted_arg bitflags, and 3 bits
3942+
/// for the `ImplicitSelfKind`.
39413943
flags: u8,
39423944

39433945
/// Which function argument is splatted into multiple arguments in callers, if any?
@@ -3961,8 +3963,8 @@ impl fmt::Debug for FnDeclFlags {
39613963
f.field(&"CVariadic");
39623964
}
39633965

3964-
if let Some(index) = self.splatted() {
3965-
f.field(&format!("Splatted({})", index));
3966+
if self.has_splatted_arg() {
3967+
f.field(&"HasSplattedArg");
39663968
}
39673969

39683970
f.finish()
@@ -3979,20 +3981,19 @@ impl FnDeclFlags {
39793981
/// Bitflag for lifetime elision.
39803982
const LIFETIME_ELISION_ALLOWED_FLAG: u8 = 1 << 4;
39813983

3982-
/// Marker index for "no splatted argument".
3983-
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`.
3984-
const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX;
3984+
/// Bitflag set if any argument is splatted (for performance).
3985+
const HAS_SPLATTED_ARG_FLAG: u8 = 1 << 5;
39853986

39863987
/// Create a new FnDeclKind with no implicit self, no lifetime elision, no C-style variadic
3987-
/// argument, and no splatting.
3988+
/// argument, and no splatted argument.
39883989
/// To modify these flags, use the `set_*` methods, for readability.
39893990
// FIXME: use Default instead when that trait is const stable.
39903991
pub const fn default() -> Self {
39913992
Self { flags: 0, splatted: 0 }
39923993
.set_implicit_self(ImplicitSelfKind::None)
39933994
.set_lifetime_elision_allowed(false)
39943995
.set_c_variadic(false)
3995-
.set_no_splatted_args()
3996+
.set_has_splatted_arg(false)
39963997
}
39973998

39983999
/// Set the implicit self kind.
@@ -4035,38 +4036,15 @@ impl FnDeclFlags {
40354036
self
40364037
}
40374038

4038-
/// Set the splatted argument index.
4039-
/// The number of function arguments is used for error checking.
4039+
/// Set the splatted argument flag.
40404040
#[must_use = "this method does not modify the receiver"]
4041-
pub const fn set_splatted(
4042-
mut self,
4043-
splatted: Option<u16>,
4044-
args_len: usize,
4045-
) -> Result<Self, SplattedArgIndexError> {
4046-
if let Some(splatted_arg_index) = splatted {
4047-
if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX {
4048-
// This index value is used as a marker for "no splatting", so it is unsupported.
4049-
return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index });
4050-
} else if splatted_arg_index as usize >= args_len {
4051-
return Err(SplattedArgIndexError::OutOfBounds {
4052-
splatted_arg_index,
4053-
args_len: args_len as u16,
4054-
});
4055-
}
4056-
4057-
self.splatted = splatted_arg_index;
4041+
pub const fn set_has_splatted_arg(mut self, has_splatted_arg: bool) -> Self {
4042+
if has_splatted_arg {
4043+
self.flags |= Self::HAS_SPLATTED_ARG_FLAG;
40584044
} else {
4059-
self.splatted = Self::NO_SPLATTED_ARG_INDEX;
4045+
self.flags &= !Self::HAS_SPLATTED_ARG_FLAG;
40604046
}
40614047

4062-
Ok(self)
4063-
}
4064-
4065-
/// Set "no splatted arguments" for the function declaration.
4066-
#[must_use = "this method does not modify the receiver"]
4067-
pub const fn set_no_splatted_args(mut self) -> Self {
4068-
self.splatted = Self::NO_SPLATTED_ARG_INDEX;
4069-
40704048
self
40714049
}
40724050

@@ -4092,9 +4070,38 @@ impl FnDeclFlags {
40924070
self.flags & Self::LIFETIME_ELISION_ALLOWED_FLAG != 0
40934071
}
40944072

4095-
/// Get the splatted argument index, if any.
4096-
pub const fn splatted(self) -> Option<u16> {
4097-
if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) }
4073+
/// Does this function have a splatted argument?
4074+
pub const fn has_splatted_arg(self) -> bool {
4075+
self.flags & Self::HAS_SPLATTED_ARG_FLAG != 0
4076+
}
4077+
4078+
/// Returns `true` if the given input contains a `#[splat]` attribute in `attrs`.
4079+
pub fn is_splatted_arg<'hir>(
4080+
&self,
4081+
input: &'hir Ty<'hir>,
4082+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4083+
) -> bool {
4084+
self.has_splatted_arg() && find_attr!(attrs(input.hir_id), Splat(_))
4085+
}
4086+
4087+
/// Searches `inputs` and `attrs` for the index of the splatted argument. Returns `None` if
4088+
/// there is no splatted argument.
4089+
pub fn splatted_arg_index<'hir>(
4090+
&self,
4091+
inputs: &'hir [Ty<'hir>],
4092+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4093+
) -> Option<u16> {
4094+
if !self.has_splatted_arg() {
4095+
return None;
4096+
}
4097+
4098+
for (index, input) in inputs.iter().enumerate() {
4099+
if self.is_splatted_arg(input, attrs) {
4100+
return Some(u16::try_from(index).unwrap());
4101+
}
4102+
}
4103+
4104+
unreachable!("no splatted argument found");
40984105
}
40994106
}
41004107

@@ -4143,8 +4150,27 @@ impl<'hir> FnDecl<'hir> {
41434150
self.fn_decl_kind.lifetime_elision_allowed()
41444151
}
41454152

4146-
pub fn splatted(&self) -> Option<u16> {
4147-
self.fn_decl_kind.splatted()
4153+
/// Returns `true` if the function has a splatted argument.
4154+
pub fn has_splatted_arg(&self) -> bool {
4155+
self.fn_decl_kind.has_splatted_arg()
4156+
}
4157+
4158+
/// Returns `true` if the given argument `index` contains a `#[splat]` attribute in `attrs`.
4159+
pub fn is_splatted_arg(
4160+
&self,
4161+
index: usize,
4162+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4163+
) -> bool {
4164+
self.fn_decl_kind.is_splatted_arg(&self.inputs[index], attrs)
4165+
}
4166+
4167+
/// Searches `self.inputs` and `attrs` for the index of the splatted argument. Returns `None`
4168+
/// if there is no splatted argument.
4169+
pub fn splatted_arg_index(
4170+
&self,
4171+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4172+
) -> Option<u16> {
4173+
self.fn_decl_kind.splatted_arg_index(self.inputs, attrs)
41484174
}
41494175

41504176
pub fn dummy(span: Span) -> Self {

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use rustc_errors::{
2929
Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level, StashKey,
3030
struct_span_code_err,
3131
};
32+
use rustc_hir::attrs::HasAttrs;
3233
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
3334
use rustc_hir::def_id::{DefId, LocalDefId};
3435
use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
@@ -3593,7 +3594,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
35933594
.set_abi(abi)
35943595
.set_safe(safety.is_safe())
35953596
.set_c_variadic(decl.fn_decl_kind.c_variadic())
3596-
.set_splatted(decl.fn_decl_kind.splatted(), input_tys.len())
3597+
.set_splatted(
3598+
decl.splatted_arg_index(&move |id| HasAttrs::get_attrs(id, &tcx)),
3599+
input_tys.len(),
3600+
)
35973601
.unwrap();
35983602
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, fn_sig_kind);
35993603
let fn_ptr_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);

compiler/rustc_hir_pretty/src/lib.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
1616
use rustc_ast_pretty::pp::{self, BoxMarker, Breaks};
1717
use rustc_ast_pretty::pprust::state::MacHeader;
1818
use rustc_ast_pretty::pprust::{Comments, PrintState};
19-
use rustc_hir as hir;
2019
use rustc_hir::attrs::{AttributeKind, PrintAttribute};
2120
use rustc_hir::{
22-
BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg, GenericBound,
23-
GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind,
24-
PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind,
21+
self as hir, BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg,
22+
GenericBound, GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node,
23+
PatKind, PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind, find_attr,
2524
};
2625
use rustc_span::source_map::SourceMap;
2726
use rustc_span::{DUMMY_SP, FileName, Ident, Span, Spanned, Symbol, kw, sym};
@@ -2264,7 +2263,7 @@ impl<'a> State<'a> {
22642263
assert!(arg_idents.is_empty() || body_id.is_none());
22652264
let mut i = 0;
22662265
let mut print_arg = |s: &mut Self, ty: Option<&hir::Ty<'_>>| {
2267-
if Some(i) == decl.splatted().map(usize::from) {
2266+
if decl.has_splatted_arg() && find_attr!(s.attrs(decl.inputs[i].hir_id), Splat(_)) {
22682267
s.word("#[splat]");
22692268
}
22702269
if i == 0 && decl.implicit_self().has_implicit_self() {

compiler/rustc_type_ir/src/ty_kind.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,9 +858,11 @@ impl FnSigKind {
858858
const C_VARIADIC_FLAG: u8 = 1 << 7;
859859

860860
/// The marker index for "no splatted arguments".
861-
/// Must have the same value as `FnDeclFlags::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`.
861+
/// Must have the same value as `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`.
862862
///
863863
/// This is an implementation detail, which should only be used in low-level encoding.
864+
// FIXME(splat): if we remove this limit from hir::FnDecl and FnSig, all instances of this
865+
// constant can go away entirely.
864866
pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX;
865867

866868
/// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic or splatted arguments.

0 commit comments

Comments
 (0)