Introduce MatchCtxt
This commit is contained in:
parent
60ea14bfaa
commit
4bcf66f875
6 changed files with 93 additions and 77 deletions
|
@ -718,7 +718,7 @@ impl<Cx: MatchCx> Constructor<Cx> {
|
||||||
/// The number of fields for this constructor. This must be kept in sync with
|
/// The number of fields for this constructor. This must be kept in sync with
|
||||||
/// `Fields::wildcards`.
|
/// `Fields::wildcards`.
|
||||||
pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize {
|
pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize {
|
||||||
pcx.cx.ctor_arity(self, pcx.ty)
|
pcx.ctor_arity(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`.
|
/// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`.
|
||||||
|
@ -729,7 +729,8 @@ impl<Cx: MatchCx> Constructor<Cx> {
|
||||||
pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool {
|
pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Wildcard, _) => pcx
|
(Wildcard, _) => pcx
|
||||||
.cx
|
.mcx
|
||||||
|
.tycx
|
||||||
.bug(format_args!("Constructor splitting should not have returned `Wildcard`")),
|
.bug(format_args!("Constructor splitting should not have returned `Wildcard`")),
|
||||||
// Wildcards cover anything
|
// Wildcards cover anything
|
||||||
(_, Wildcard) => true,
|
(_, Wildcard) => true,
|
||||||
|
@ -771,7 +772,7 @@ impl<Cx: MatchCx> Constructor<Cx> {
|
||||||
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
|
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
|
||||||
(Opaque(..), _) | (_, Opaque(..)) => false,
|
(Opaque(..), _) | (_, Opaque(..)) => false,
|
||||||
|
|
||||||
_ => pcx.cx.bug(format_args!(
|
_ => pcx.mcx.tycx.bug(format_args!(
|
||||||
"trying to compare incompatible constructors {self:?} and {other:?}"
|
"trying to compare incompatible constructors {self:?} and {other:?}"
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
@ -1007,7 +1008,7 @@ impl<Cx: MatchCx> ConstructorSet<Cx> {
|
||||||
// We have now grouped all the constructors into 3 buckets: present, missing, missing_empty.
|
// We have now grouped all the constructors into 3 buckets: present, missing, missing_empty.
|
||||||
// In the absence of the `exhaustive_patterns` feature however, we don't count nested empty
|
// In the absence of the `exhaustive_patterns` feature however, we don't count nested empty
|
||||||
// types as empty. Only non-nested `!` or `enum Foo {}` are considered empty.
|
// types as empty. Only non-nested `!` or `enum Foo {}` are considered empty.
|
||||||
if !pcx.cx.is_exhaustive_patterns_feature_on()
|
if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on()
|
||||||
&& !(pcx.is_scrutinee && matches!(self, Self::NoConstructors))
|
&& !(pcx.is_scrutinee && matches!(self, Self::NoConstructors))
|
||||||
{
|
{
|
||||||
// Treat all missing constructors as nonempty.
|
// Treat all missing constructors as nonempty.
|
||||||
|
|
|
@ -36,6 +36,19 @@ use crate::rustc::RustcMatchCheckCtxt;
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
|
use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
|
||||||
|
|
||||||
|
// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so
|
||||||
|
// we use another feature instead. The crate won't compile if one of these isn't enabled.
|
||||||
|
#[cfg(feature = "rustc")]
|
||||||
|
pub(crate) use rustc_arena::TypedArena;
|
||||||
|
#[cfg(feature = "stable")]
|
||||||
|
pub(crate) use typed_arena::Arena as TypedArena;
|
||||||
|
|
||||||
|
pub trait Captures<'a> {}
|
||||||
|
impl<'a, T: ?Sized> Captures<'a> for T {}
|
||||||
|
|
||||||
|
/// Context that provides type information about constructors.
|
||||||
|
///
|
||||||
|
/// Most of the crate is parameterized on a type that implements this trait.
|
||||||
pub trait MatchCx: Sized + Clone + fmt::Debug {
|
pub trait MatchCx: Sized + Clone + fmt::Debug {
|
||||||
/// The type of a pattern.
|
/// The type of a pattern.
|
||||||
type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy
|
type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy
|
||||||
|
@ -71,10 +84,20 @@ pub trait MatchCx: Sized + Clone + fmt::Debug {
|
||||||
fn bug(&self, fmt: fmt::Arguments<'_>) -> !;
|
fn bug(&self, fmt: fmt::Arguments<'_>) -> !;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Context that provides information global to a match.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MatchCtxt<'a, 'p, Cx: MatchCx> {
|
||||||
|
/// The context for type information.
|
||||||
|
pub tycx: &'a Cx,
|
||||||
|
/// An arena to store the wildcards we produce during analysis.
|
||||||
|
pub wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'p, Cx: MatchCx> Copy for MatchCtxt<'a, 'p, Cx> {}
|
||||||
|
|
||||||
/// The arm of a match expression.
|
/// The arm of a match expression.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MatchArm<'p, Cx: MatchCx> {
|
pub struct MatchArm<'p, Cx: MatchCx> {
|
||||||
/// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`.
|
|
||||||
pub pat: &'p DeconstructedPat<'p, Cx>,
|
pub pat: &'p DeconstructedPat<'p, Cx>,
|
||||||
pub has_guard: bool,
|
pub has_guard: bool,
|
||||||
pub arm_data: Cx::ArmData,
|
pub arm_data: Cx::ArmData,
|
||||||
|
@ -82,31 +105,30 @@ pub struct MatchArm<'p, Cx: MatchCx> {
|
||||||
|
|
||||||
impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {}
|
impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {}
|
||||||
|
|
||||||
pub trait Captures<'a> {}
|
|
||||||
impl<'a, T: ?Sized> Captures<'a> for T {}
|
|
||||||
|
|
||||||
/// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are
|
/// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are
|
||||||
/// useful, and runs some lints.
|
/// useful, and runs some lints.
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
pub fn analyze_match<'p, 'tcx>(
|
pub fn analyze_match<'p, 'tcx>(
|
||||||
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
||||||
arms: &[rustc::MatchArm<'p, 'tcx>],
|
arms: &[rustc::MatchArm<'p, 'tcx>],
|
||||||
scrut_ty: Ty<'tcx>,
|
scrut_ty: Ty<'tcx>,
|
||||||
) -> rustc::UsefulnessReport<'p, 'tcx> {
|
) -> rustc::UsefulnessReport<'p, 'tcx> {
|
||||||
// Arena to store the extra wildcards we construct during analysis.
|
// Arena to store the extra wildcards we construct during analysis.
|
||||||
let wildcard_arena = cx.pattern_arena;
|
let wildcard_arena = tycx.pattern_arena;
|
||||||
|
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
|
||||||
|
let cx = MatchCtxt { tycx, wildcard_arena };
|
||||||
|
|
||||||
|
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity);
|
||||||
|
|
||||||
let pat_column = PatternColumn::new(arms);
|
let pat_column = PatternColumn::new(arms);
|
||||||
|
|
||||||
let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee);
|
|
||||||
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity, wildcard_arena);
|
|
||||||
|
|
||||||
// Lint on ranges that overlap on their endpoints, which is likely a mistake.
|
// Lint on ranges that overlap on their endpoints, which is likely a mistake.
|
||||||
lint_overlapping_range_endpoints(cx, &pat_column, wildcard_arena);
|
lint_overlapping_range_endpoints(cx, &pat_column);
|
||||||
|
|
||||||
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
|
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
|
||||||
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
|
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
|
||||||
if cx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
|
if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
|
||||||
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty, wildcard_arena)
|
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
report
|
report
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use rustc_arena::TypedArena;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use rustc_data_structures::captures::Captures;
|
use rustc_data_structures::captures::Captures;
|
||||||
|
@ -13,8 +12,8 @@ use crate::errors::{
|
||||||
OverlappingRangeEndpoints, Uncovered,
|
OverlappingRangeEndpoints, Uncovered,
|
||||||
};
|
};
|
||||||
use crate::rustc::{
|
use crate::rustc::{
|
||||||
Constructor, DeconstructedPat, MatchArm, PlaceCtxt, RustcMatchCheckCtxt, SplitConstructorSet,
|
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt,
|
||||||
WitnessPat,
|
SplitConstructorSet, WitnessPat,
|
||||||
};
|
};
|
||||||
use crate::MatchCx;
|
use crate::MatchCx;
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> {
|
||||||
/// Do constructor splitting on the constructors of the column.
|
/// Do constructor splitting on the constructors of the column.
|
||||||
fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> {
|
fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> {
|
||||||
let column_ctors = self.patterns.iter().map(|p| p.ctor());
|
let column_ctors = self.patterns.iter().map(|p| p.ctor());
|
||||||
pcx.cx.ctors_for_ty(pcx.ty).split(pcx, column_ctors)
|
pcx.ctors_for_ty().split(pcx, column_ctors)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter<'b>(&'b self) -> impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>> + Captures<'b> {
|
fn iter<'b>(&'b self) -> impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>> + Captures<'b> {
|
||||||
|
@ -121,16 +120,15 @@ impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> {
|
||||||
|
|
||||||
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
|
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
|
||||||
/// in a given column.
|
/// in a given column.
|
||||||
#[instrument(level = "debug", skip(cx, wildcard_arena), ret)]
|
#[instrument(level = "debug", skip(cx), ret)]
|
||||||
fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||||
column: &PatternColumn<'a, 'p, 'tcx>,
|
column: &PatternColumn<'a, 'p, 'tcx>,
|
||||||
wildcard_arena: &TypedArena<DeconstructedPat<'p, 'tcx>>,
|
|
||||||
) -> Vec<WitnessPat<'p, 'tcx>> {
|
) -> Vec<WitnessPat<'p, 'tcx>> {
|
||||||
let Some(ty) = column.head_ty() else {
|
let Some(ty) = column.head_ty() else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena);
|
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||||
|
|
||||||
let set = column.analyze_ctors(pcx);
|
let set = column.analyze_ctors(pcx);
|
||||||
if set.present.is_empty() {
|
if set.present.is_empty() {
|
||||||
|
@ -141,7 +139,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut witnesses = Vec::new();
|
let mut witnesses = Vec::new();
|
||||||
if cx.is_foreign_non_exhaustive_enum(ty) {
|
if cx.tycx.is_foreign_non_exhaustive_enum(ty) {
|
||||||
witnesses.extend(
|
witnesses.extend(
|
||||||
set.missing
|
set.missing
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -157,7 +155,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
|
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
|
||||||
for (i, col_i) in specialized_columns.iter().enumerate() {
|
for (i, col_i) in specialized_columns.iter().enumerate() {
|
||||||
// Compute witnesses for each column.
|
// Compute witnesses for each column.
|
||||||
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i, wildcard_arena);
|
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i);
|
||||||
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
|
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
|
||||||
// adding enough wildcards to match `arity`.
|
// adding enough wildcards to match `arity`.
|
||||||
for wit in wits_for_col_i {
|
for wit in wits_for_col_i {
|
||||||
|
@ -171,29 +169,29 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||||
arms: &[MatchArm<'p, 'tcx>],
|
arms: &[MatchArm<'p, 'tcx>],
|
||||||
pat_column: &PatternColumn<'a, 'p, 'tcx>,
|
pat_column: &PatternColumn<'a, 'p, 'tcx>,
|
||||||
scrut_ty: Ty<'tcx>,
|
scrut_ty: Ty<'tcx>,
|
||||||
wildcard_arena: &TypedArena<DeconstructedPat<'p, 'tcx>>,
|
|
||||||
) {
|
) {
|
||||||
|
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
|
||||||
if !matches!(
|
if !matches!(
|
||||||
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, cx.match_lint_level).0,
|
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
|
||||||
rustc_session::lint::Level::Allow
|
rustc_session::lint::Level::Allow
|
||||||
) {
|
) {
|
||||||
let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column, wildcard_arena);
|
let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column);
|
||||||
if !witnesses.is_empty() {
|
if !witnesses.is_empty() {
|
||||||
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
|
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
|
||||||
// is not exhaustive enough.
|
// is not exhaustive enough.
|
||||||
//
|
//
|
||||||
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
|
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
|
||||||
cx.tcx.emit_spanned_lint(
|
rcx.tcx.emit_spanned_lint(
|
||||||
NON_EXHAUSTIVE_OMITTED_PATTERNS,
|
NON_EXHAUSTIVE_OMITTED_PATTERNS,
|
||||||
cx.match_lint_level,
|
rcx.match_lint_level,
|
||||||
cx.scrut_span,
|
rcx.scrut_span,
|
||||||
NonExhaustiveOmittedPattern {
|
NonExhaustiveOmittedPattern {
|
||||||
scrut_ty,
|
scrut_ty,
|
||||||
uncovered: Uncovered::new(cx.scrut_span, cx, witnesses),
|
uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -203,17 +201,17 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
// usage of the lint.
|
// usage of the lint.
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
let (lint_level, lint_level_source) =
|
let (lint_level, lint_level_source) =
|
||||||
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
|
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
|
||||||
if !matches!(lint_level, rustc_session::lint::Level::Allow) {
|
if !matches!(lint_level, rustc_session::lint::Level::Allow) {
|
||||||
let decorator = NonExhaustiveOmittedPatternLintOnArm {
|
let decorator = NonExhaustiveOmittedPatternLintOnArm {
|
||||||
lint_span: lint_level_source.span(),
|
lint_span: lint_level_source.span(),
|
||||||
suggest_lint_on_match: cx.whole_match_span.map(|span| span.shrink_to_lo()),
|
suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
|
||||||
lint_level: lint_level.as_str(),
|
lint_level: lint_level.as_str(),
|
||||||
lint_name: "non_exhaustive_omitted_patterns",
|
lint_name: "non_exhaustive_omitted_patterns",
|
||||||
};
|
};
|
||||||
|
|
||||||
use rustc_errors::DecorateLint;
|
use rustc_errors::DecorateLint;
|
||||||
let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.data(), "");
|
let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data(), "");
|
||||||
err.set_primary_message(decorator.msg());
|
err.set_primary_message(decorator.msg());
|
||||||
decorator.decorate_lint(&mut err);
|
decorator.decorate_lint(&mut err);
|
||||||
err.emit();
|
err.emit();
|
||||||
|
@ -223,30 +221,30 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
|
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
|
||||||
#[instrument(level = "debug", skip(cx, wildcard_arena))]
|
#[instrument(level = "debug", skip(cx))]
|
||||||
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||||
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||||
column: &PatternColumn<'a, 'p, 'tcx>,
|
column: &PatternColumn<'a, 'p, 'tcx>,
|
||||||
wildcard_arena: &TypedArena<DeconstructedPat<'p, 'tcx>>,
|
|
||||||
) {
|
) {
|
||||||
let Some(ty) = column.head_ty() else {
|
let Some(ty) = column.head_ty() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena);
|
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||||
|
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
|
||||||
|
|
||||||
let set = column.analyze_ctors(pcx);
|
let set = column.analyze_ctors(pcx);
|
||||||
|
|
||||||
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
|
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
|
||||||
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
|
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
|
||||||
let overlap_as_pat = cx.hoist_pat_range(overlap, ty);
|
let overlap_as_pat = rcx.hoist_pat_range(overlap, ty);
|
||||||
let overlaps: Vec<_> = overlapped_spans
|
let overlaps: Vec<_> = overlapped_spans
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
|
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
|
||||||
.collect();
|
.collect();
|
||||||
cx.tcx.emit_spanned_lint(
|
rcx.tcx.emit_spanned_lint(
|
||||||
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
|
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
|
||||||
cx.match_lint_level,
|
rcx.match_lint_level,
|
||||||
this_span,
|
this_span,
|
||||||
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
|
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
|
||||||
);
|
);
|
||||||
|
@ -291,7 +289,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||||
// Recurse into the fields.
|
// Recurse into the fields.
|
||||||
for ctor in set.present {
|
for ctor in set.present {
|
||||||
for col in column.specialize(pcx, &ctor) {
|
for col in column.specialize(pcx, &ctor) {
|
||||||
lint_overlapping_range_endpoints(cx, &col, wildcard_arena);
|
lint_overlapping_range_endpoints(cx, &col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,10 +81,10 @@ impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> {
|
||||||
other_ctor: &Constructor<Cx>,
|
other_ctor: &Constructor<Cx>,
|
||||||
) -> SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]> {
|
) -> SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]> {
|
||||||
let wildcard_sub_tys = || {
|
let wildcard_sub_tys = || {
|
||||||
let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty);
|
let tys = pcx.ctor_sub_tys(other_ctor);
|
||||||
tys.iter()
|
tys.iter()
|
||||||
.map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default()))
|
.map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default()))
|
||||||
.map(|pat| pcx.wildcard_arena.alloc(pat) as &_)
|
.map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_)
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
match (&self.ctor, other_ctor) {
|
match (&self.ctor, other_ctor) {
|
||||||
|
@ -179,7 +179,7 @@ impl<Cx: MatchCx> WitnessPat<Cx> {
|
||||||
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
|
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
|
||||||
/// `Some(_)`.
|
/// `Some(_)`.
|
||||||
pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, '_, Cx>, ctor: Constructor<Cx>) -> Self {
|
pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, '_, Cx>, ctor: Constructor<Cx>) -> Self {
|
||||||
let field_tys = pcx.cx.ctor_sub_tys(&ctor, pcx.ty);
|
let field_tys = pcx.ctor_sub_tys(&ctor);
|
||||||
let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect();
|
let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect();
|
||||||
Self::new(ctor, fields, pcx.ty)
|
Self::new(ctor, fields, pcx.ty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub type ConstructorSet<'p, 'tcx> =
|
||||||
pub type DeconstructedPat<'p, 'tcx> =
|
pub type DeconstructedPat<'p, 'tcx> =
|
||||||
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
|
pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
|
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
|
||||||
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
pub(crate) type SplitConstructorSet<'p, 'tcx> =
|
pub(crate) type SplitConstructorSet<'p, 'tcx> =
|
||||||
|
|
|
@ -555,16 +555,9 @@
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so
|
|
||||||
// we use another feature instead. The crate won't compile if one of these isn't enabled.
|
|
||||||
#[cfg(feature = "rustc")]
|
|
||||||
use rustc_arena::TypedArena;
|
|
||||||
#[cfg(feature = "stable")]
|
|
||||||
use typed_arena::Arena as TypedArena;
|
|
||||||
|
|
||||||
use crate::constructor::{Constructor, ConstructorSet};
|
use crate::constructor::{Constructor, ConstructorSet};
|
||||||
use crate::pat::{DeconstructedPat, WitnessPat};
|
use crate::pat::{DeconstructedPat, WitnessPat};
|
||||||
use crate::{Captures, MatchArm, MatchCx};
|
use crate::{Captures, MatchArm, MatchCtxt, MatchCx, TypedArena};
|
||||||
|
|
||||||
use self::ValidityConstraint::*;
|
use self::ValidityConstraint::*;
|
||||||
|
|
||||||
|
@ -578,9 +571,7 @@ pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R {
|
||||||
/// Context that provides information local to a place under investigation.
|
/// Context that provides information local to a place under investigation.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> {
|
pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> {
|
||||||
pub(crate) cx: &'a Cx,
|
pub(crate) mcx: MatchCtxt<'a, 'p, Cx>,
|
||||||
/// An arena to store the wildcards we produce during analysis.
|
|
||||||
pub(crate) wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
|
|
||||||
/// Type of the place under investigation.
|
/// Type of the place under investigation.
|
||||||
pub(crate) ty: Cx::Ty,
|
pub(crate) ty: Cx::Ty,
|
||||||
/// Whether the place is the original scrutinee place, as opposed to a subplace of it.
|
/// Whether the place is the original scrutinee place, as opposed to a subplace of it.
|
||||||
|
@ -590,12 +581,18 @@ pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> {
|
||||||
impl<'a, 'p, Cx: MatchCx> PlaceCtxt<'a, 'p, Cx> {
|
impl<'a, 'p, Cx: MatchCx> PlaceCtxt<'a, 'p, Cx> {
|
||||||
/// A `PlaceCtxt` when code other than `is_useful` needs one.
|
/// A `PlaceCtxt` when code other than `is_useful` needs one.
|
||||||
#[cfg_attr(not(feature = "rustc"), allow(dead_code))]
|
#[cfg_attr(not(feature = "rustc"), allow(dead_code))]
|
||||||
pub(crate) fn new_dummy(
|
pub(crate) fn new_dummy(mcx: MatchCtxt<'a, 'p, Cx>, ty: Cx::Ty) -> Self {
|
||||||
cx: &'a Cx,
|
PlaceCtxt { mcx, ty, is_scrutinee: false }
|
||||||
ty: Cx::Ty,
|
}
|
||||||
wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
|
|
||||||
) -> Self {
|
pub(crate) fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize {
|
||||||
PlaceCtxt { cx, ty, is_scrutinee: false, wildcard_arena }
|
self.mcx.tycx.ctor_arity(ctor, self.ty)
|
||||||
|
}
|
||||||
|
pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<Cx>) -> &[Cx::Ty] {
|
||||||
|
self.mcx.tycx.ctor_sub_tys(ctor, self.ty)
|
||||||
|
}
|
||||||
|
pub(crate) fn ctors_for_ty(&self) -> ConstructorSet<Cx> {
|
||||||
|
self.mcx.tycx.ctors_for_ty(self.ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1176,11 +1173,10 @@ impl<Cx: MatchCx> WitnessMatrix<Cx> {
|
||||||
/// - unspecialization, where we lift the results from the previous step into results for this step
|
/// - unspecialization, where we lift the results from the previous step into results for this step
|
||||||
/// (using `apply_constructor` and by updating `row.useful` for each parent row).
|
/// (using `apply_constructor` and by updating `row.useful` for each parent row).
|
||||||
/// This is all explained at the top of the file.
|
/// This is all explained at the top of the file.
|
||||||
#[instrument(level = "debug", skip(cx, is_top_level, wildcard_arena), ret)]
|
#[instrument(level = "debug", skip(mcx, is_top_level), ret)]
|
||||||
fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>(
|
fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>(
|
||||||
cx: &'a Cx,
|
mcx: MatchCtxt<'a, 'p, Cx>,
|
||||||
matrix: &mut Matrix<'a, 'p, Cx>,
|
matrix: &mut Matrix<'a, 'p, Cx>,
|
||||||
wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
|
|
||||||
is_top_level: bool,
|
is_top_level: bool,
|
||||||
) -> WitnessMatrix<Cx> {
|
) -> WitnessMatrix<Cx> {
|
||||||
debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count()));
|
debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count()));
|
||||||
|
@ -1202,7 +1198,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("ty: {ty:?}");
|
debug!("ty: {ty:?}");
|
||||||
let pcx = &PlaceCtxt { cx, ty, is_scrutinee: is_top_level, wildcard_arena };
|
let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level };
|
||||||
|
|
||||||
// Whether the place/column we are inspecting is known to contain valid data.
|
// Whether the place/column we are inspecting is known to contain valid data.
|
||||||
let place_validity = matrix.place_validity[0];
|
let place_validity = matrix.place_validity[0];
|
||||||
|
@ -1211,7 +1207,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>(
|
||||||
|
|
||||||
// Analyze the constructors present in this column.
|
// Analyze the constructors present in this column.
|
||||||
let ctors = matrix.heads().map(|p| p.ctor());
|
let ctors = matrix.heads().map(|p| p.ctor());
|
||||||
let ctors_for_ty = &cx.ctors_for_ty(ty);
|
let ctors_for_ty = pcx.ctors_for_ty();
|
||||||
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
|
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
|
||||||
let split_set = ctors_for_ty.split(pcx, ctors);
|
let split_set = ctors_for_ty.split(pcx, ctors);
|
||||||
let all_missing = split_set.present.is_empty();
|
let all_missing = split_set.present.is_empty();
|
||||||
|
@ -1245,7 +1241,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>(
|
||||||
// Dig into rows that match `ctor`.
|
// Dig into rows that match `ctor`.
|
||||||
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor);
|
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor);
|
||||||
let mut witnesses = ensure_sufficient_stack(|| {
|
let mut witnesses = ensure_sufficient_stack(|| {
|
||||||
compute_exhaustiveness_and_usefulness(cx, &mut spec_matrix, wildcard_arena, false)
|
compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false)
|
||||||
});
|
});
|
||||||
|
|
||||||
let counts_for_exhaustiveness = match ctor {
|
let counts_for_exhaustiveness = match ctor {
|
||||||
|
@ -1307,17 +1303,15 @@ pub struct UsefulnessReport<'p, Cx: MatchCx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes whether a match is exhaustive and which of its arms are useful.
|
/// Computes whether a match is exhaustive and which of its arms are useful.
|
||||||
#[instrument(skip(cx, arms, wildcard_arena), level = "debug")]
|
#[instrument(skip(cx, arms), level = "debug")]
|
||||||
pub fn compute_match_usefulness<'p, Cx: MatchCx>(
|
pub fn compute_match_usefulness<'p, Cx: MatchCx>(
|
||||||
cx: &Cx,
|
cx: MatchCtxt<'_, 'p, Cx>,
|
||||||
arms: &[MatchArm<'p, Cx>],
|
arms: &[MatchArm<'p, Cx>],
|
||||||
scrut_ty: Cx::Ty,
|
scrut_ty: Cx::Ty,
|
||||||
scrut_validity: ValidityConstraint,
|
scrut_validity: ValidityConstraint,
|
||||||
wildcard_arena: &TypedArena<DeconstructedPat<'p, Cx>>,
|
|
||||||
) -> UsefulnessReport<'p, Cx> {
|
) -> UsefulnessReport<'p, Cx> {
|
||||||
let mut matrix = Matrix::new(wildcard_arena, arms, scrut_ty, scrut_validity);
|
let mut matrix = Matrix::new(cx.wildcard_arena, arms, scrut_ty, scrut_validity);
|
||||||
let non_exhaustiveness_witnesses =
|
let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true);
|
||||||
compute_exhaustiveness_and_usefulness(cx, &mut matrix, wildcard_arena, true);
|
|
||||||
|
|
||||||
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
|
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
|
||||||
let arm_usefulness: Vec<_> = arms
|
let arm_usefulness: Vec<_> = arms
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue