Skip to content

Commit

Permalink
Rollup merge of rust-lang#55167 - nnethercote:is_missing_ctors_empty,…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
kennytm authored Oct 26, 2018
2 parents 9e6165c + b5336c0 commit 386f6c3
Showing 1 changed file with 66 additions and 19 deletions.
85 changes: 66 additions & 19 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -931,12 +931,37 @@ impl<'tcx> IntRange<'tcx> {
}
}

// Return a set of constructors equivalent to `all_ctors \ used_ctors`.
// A request for missing constructor data in terms of either:
// - whether or not there any missing constructors; or
// - the actual set of missing constructors.
#[derive(PartialEq)]
enum MissingCtorsInfo {
Emptiness,
Ctors,
}

// Used by `compute_missing_ctors`.
#[derive(Debug, PartialEq)]
enum MissingCtors<'tcx> {
Empty,
NonEmpty,

// Note that the Vec can be empty.
Ctors(Vec<Constructor<'tcx>>),
}

// When `info` is `MissingCtorsInfo::Ctors`, compute a set of constructors
// equivalent to `all_ctors \ used_ctors`. When `info` is
// `MissingCtorsInfo::Emptiness`, just determines if that set is empty or not.
// (The split logic gives a performance win, because we always need to know if
// the set is empty, but we rarely need the full set, and it can be expensive
// to compute the full set.)
fn compute_missing_ctors<'a, 'tcx: 'a>(
info: MissingCtorsInfo,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
all_ctors: &Vec<Constructor<'tcx>>,
used_ctors: &Vec<Constructor<'tcx>>,
) -> Vec<Constructor<'tcx>> {
) -> MissingCtors<'tcx> {
let mut missing_ctors = vec![];

for req_ctor in all_ctors {
Expand Down Expand Up @@ -965,10 +990,22 @@ fn compute_missing_ctors<'a, 'tcx: 'a>(
// We add `refined_ctors` instead of `req_ctor`, because then we can
// provide more detailed error information about precisely which
// ranges have been omitted.
missing_ctors.extend(refined_ctors);
if info == MissingCtorsInfo::Emptiness {
if !refined_ctors.is_empty() {
// The set is non-empty; return early.
return MissingCtors::NonEmpty;
}
} else {
missing_ctors.extend(refined_ctors);
}
}

missing_ctors
if info == MissingCtorsInfo::Emptiness {
// If we reached here, the set is empty.
MissingCtors::Empty
} else {
MissingCtors::Ctors(missing_ctors)
}
}

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

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

let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
debug!("missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
missing_ctors, is_privately_empty, is_declared_nonexhaustive);
debug!("cheap_missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
cheap_missing_ctors, is_privately_empty, is_declared_nonexhaustive);

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

if missing_ctors.is_empty() && !is_non_exhaustive {
if cheap_missing_ctors == MissingCtors::Empty && !is_non_exhaustive {
split_grouped_constructors(cx.tcx, all_ctors, matrix, pcx.ty).into_iter().map(|c| {
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)
}).find(|result| result.is_useful()).unwrap_or(NotUseful)
Expand Down Expand Up @@ -1165,15 +1205,22 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
witness
}).collect()
} else {
pats.into_iter().flat_map(|witness| {
missing_ctors.iter().map(move |ctor| {
// Extends the witness with a "wild" version of this
// constructor, that matches everything that can be built with
// it. For example, if `ctor` is a `Constructor::Variant` for
// `Option::Some`, this pushes the witness for `Some(_)`.
witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
})
}).collect()
let expensive_missing_ctors =
compute_missing_ctors(MissingCtorsInfo::Ctors, cx.tcx, &all_ctors,
&used_ctors);
if let MissingCtors::Ctors(missing_ctors) = expensive_missing_ctors {
pats.into_iter().flat_map(|witness| {
missing_ctors.iter().map(move |ctor| {
// Extends the witness with a "wild" version of this
// constructor, that matches everything that can be built with
// it. For example, if `ctor` is a `Constructor::Variant` for
// `Option::Some`, this pushes the witness for `Some(_)`.
witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
})
}).collect()
} else {
bug!("cheap missing ctors")
}
};
UsefulWithWitness(new_witnesses)
}
Expand Down

0 comments on commit 386f6c3

Please sign in to comment.