Skip to content

Commit d83376c

Browse files
authored
Rollup merge of rust-lang#55167 - nnethercote:is_missing_ctors_empty, r=varkor
Add a "cheap" mode for `compute_missing_ctors`. `compute_missing_ctors` produces a vector. It is called a lot, but the vector is almost always only checked for emptiness. This commit introduces a specialized variant of `compute_missing_ctors` (called `is_missing_ctors_empty`) that determines if the resulting set would be empty, and changes the callsite so that `compute_missing_ctors` is only called in the rare cases where it is needed. The code duplication is unfortunate but I can't see a better way to do it. This change reduces instruction counts for several benchmarks up to 2%. r? @varkor
2 parents 4212896 + b5336c0 commit d83376c

File tree

1 file changed

+66
-19
lines changed

1 file changed

+66
-19
lines changed

src/librustc_mir/hair/pattern/_match.rs

+66-19
Original file line numberDiff line numberDiff line change
@@ -931,12 +931,37 @@ impl<'tcx> IntRange<'tcx> {
931931
}
932932
}
933933

934-
// Return a set of constructors equivalent to `all_ctors \ used_ctors`.
934+
// A request for missing constructor data in terms of either:
935+
// - whether or not there any missing constructors; or
936+
// - the actual set of missing constructors.
937+
#[derive(PartialEq)]
938+
enum MissingCtorsInfo {
939+
Emptiness,
940+
Ctors,
941+
}
942+
943+
// Used by `compute_missing_ctors`.
944+
#[derive(Debug, PartialEq)]
945+
enum MissingCtors<'tcx> {
946+
Empty,
947+
NonEmpty,
948+
949+
// Note that the Vec can be empty.
950+
Ctors(Vec<Constructor<'tcx>>),
951+
}
952+
953+
// When `info` is `MissingCtorsInfo::Ctors`, compute a set of constructors
954+
// equivalent to `all_ctors \ used_ctors`. When `info` is
955+
// `MissingCtorsInfo::Emptiness`, just determines if that set is empty or not.
956+
// (The split logic gives a performance win, because we always need to know if
957+
// the set is empty, but we rarely need the full set, and it can be expensive
958+
// to compute the full set.)
935959
fn compute_missing_ctors<'a, 'tcx: 'a>(
960+
info: MissingCtorsInfo,
936961
tcx: TyCtxt<'a, 'tcx, 'tcx>,
937962
all_ctors: &Vec<Constructor<'tcx>>,
938963
used_ctors: &Vec<Constructor<'tcx>>,
939-
) -> Vec<Constructor<'tcx>> {
964+
) -> MissingCtors<'tcx> {
940965
let mut missing_ctors = vec![];
941966

942967
for req_ctor in all_ctors {
@@ -965,10 +990,22 @@ fn compute_missing_ctors<'a, 'tcx: 'a>(
965990
// We add `refined_ctors` instead of `req_ctor`, because then we can
966991
// provide more detailed error information about precisely which
967992
// ranges have been omitted.
968-
missing_ctors.extend(refined_ctors);
993+
if info == MissingCtorsInfo::Emptiness {
994+
if !refined_ctors.is_empty() {
995+
// The set is non-empty; return early.
996+
return MissingCtors::NonEmpty;
997+
}
998+
} else {
999+
missing_ctors.extend(refined_ctors);
1000+
}
9691001
}
9701002

971-
missing_ctors
1003+
if info == MissingCtorsInfo::Emptiness {
1004+
// If we reached here, the set is empty.
1005+
MissingCtors::Empty
1006+
} else {
1007+
MissingCtors::Ctors(missing_ctors)
1008+
}
9721009
}
9731010

9741011
/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
@@ -1081,20 +1118,23 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
10811118
// feature flag is not present, so this is only
10821119
// needed for that case.
10831120

1084-
// Find those constructors that are not matched by any non-wildcard patterns in the
1085-
// current column.
1086-
let missing_ctors = compute_missing_ctors(cx.tcx, &all_ctors, &used_ctors);
1121+
// Missing constructors are those that are not matched by any
1122+
// non-wildcard patterns in the current column. We always determine if
1123+
// the set is empty, but we only fully construct them on-demand,
1124+
// because they're rarely used and can be big.
1125+
let cheap_missing_ctors =
1126+
compute_missing_ctors(MissingCtorsInfo::Emptiness, cx.tcx, &all_ctors, &used_ctors);
10871127

10881128
let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
10891129
let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
1090-
debug!("missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
1091-
missing_ctors, is_privately_empty, is_declared_nonexhaustive);
1130+
debug!("cheap_missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
1131+
cheap_missing_ctors, is_privately_empty, is_declared_nonexhaustive);
10921132

10931133
// For privately empty and non-exhaustive enums, we work as if there were an "extra"
10941134
// `_` constructor for the type, so we can never match over all constructors.
10951135
let is_non_exhaustive = is_privately_empty || is_declared_nonexhaustive;
10961136

1097-
if missing_ctors.is_empty() && !is_non_exhaustive {
1137+
if cheap_missing_ctors == MissingCtors::Empty && !is_non_exhaustive {
10981138
split_grouped_constructors(cx.tcx, all_ctors, matrix, pcx.ty).into_iter().map(|c| {
10991139
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)
11001140
}).find(|result| result.is_useful()).unwrap_or(NotUseful)
@@ -1165,15 +1205,22 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
11651205
witness
11661206
}).collect()
11671207
} else {
1168-
pats.into_iter().flat_map(|witness| {
1169-
missing_ctors.iter().map(move |ctor| {
1170-
// Extends the witness with a "wild" version of this
1171-
// constructor, that matches everything that can be built with
1172-
// it. For example, if `ctor` is a `Constructor::Variant` for
1173-
// `Option::Some`, this pushes the witness for `Some(_)`.
1174-
witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
1175-
})
1176-
}).collect()
1208+
let expensive_missing_ctors =
1209+
compute_missing_ctors(MissingCtorsInfo::Ctors, cx.tcx, &all_ctors,
1210+
&used_ctors);
1211+
if let MissingCtors::Ctors(missing_ctors) = expensive_missing_ctors {
1212+
pats.into_iter().flat_map(|witness| {
1213+
missing_ctors.iter().map(move |ctor| {
1214+
// Extends the witness with a "wild" version of this
1215+
// constructor, that matches everything that can be built with
1216+
// it. For example, if `ctor` is a `Constructor::Variant` for
1217+
// `Option::Some`, this pushes the witness for `Some(_)`.
1218+
witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
1219+
})
1220+
}).collect()
1221+
} else {
1222+
bug!("cheap missing ctors")
1223+
}
11771224
};
11781225
UsefulWithWitness(new_witnesses)
11791226
}

0 commit comments

Comments
 (0)