diff options
| author | kennytm <kennytm@gmail.com> | 2018-10-26 18:24:58 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-10-26 18:24:58 +0800 |
| commit | d83376c70511b2fc12506bb22c3bb02353e028d1 (patch) | |
| tree | 353af037de8376b9dbacb2bee255e5a95e0701e2 /src | |
| parent | 4212896dcaf14003579644a058145939078799a1 (diff) | |
| parent | b5336c0b9755f635db4eafba6254e192ee451e6a (diff) | |
| download | rust-d83376c70511b2fc12506bb22c3bb02353e028d1.tar.gz rust-d83376c70511b2fc12506bb22c3bb02353e028d1.zip | |
Rollup merge of #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
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc_mir/hair/pattern/_match.rs | 85 |
1 files changed, 66 insertions, 19 deletions
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 77483ad184b..d53bb1dc4d6 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -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 { @@ -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 @@ -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) @@ -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) } |
