Skip to content

Commit b5336c0

Browse files
committed
Add a cheap mode for compute_missing_ctors.
`compute_missing_ctors` is called a lot. It produces a vector, which can be reasonably large (e.g. 100+ elements), but the vector is almost always only checked for emptiness. This commit changes `compute_missing_ctors` so it can be called in a cheap way that just indicates if the vector would be empty. If necessary, the function can subsequently be called in an expensive way to compute the full vector. This change reduces instruction counts for several benchmarks up to 2%.
1 parent 8a7048b commit b5336c0

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.clone(), 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)