Skip to content

Commit 2d5437a

Browse files
committed
Distinguish user patterns from reconstructed witnesses
1 parent 0d410be commit 2d5437a

File tree

4 files changed

+200
-167
lines changed

4 files changed

+200
-167
lines changed

compiler/rustc_mir_build/src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
fluent_generated as fluent,
3-
thir::pattern::{deconstruct_pat::DeconstructedPat, MatchCheckCtxt},
3+
thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
44
};
55
use rustc_errors::{
66
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -810,7 +810,7 @@ impl<'tcx> Uncovered<'tcx> {
810810
pub fn new<'p>(
811811
span: Span,
812812
cx: &MatchCheckCtxt<'p, 'tcx>,
813-
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
813+
witnesses: Vec<WitnessPat<'tcx>>,
814814
) -> Self {
815815
let witness_1 = witnesses.get(0).unwrap().to_pat(cx);
816816
Self {

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::deconstruct_pat::{Constructor, DeconstructedPat};
1+
use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat};
22
use super::usefulness::{
33
compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
44
};
@@ -685,8 +685,8 @@ fn report_arm_reachability<'p, 'tcx>(
685685
}
686686
}
687687

688-
fn collect_non_exhaustive_tys<'p, 'tcx>(
689-
pat: &DeconstructedPat<'p, 'tcx>,
688+
fn collect_non_exhaustive_tys<'tcx>(
689+
pat: &WitnessPat<'tcx>,
690690
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
691691
) {
692692
if matches!(pat.ctor(), Constructor::NonExhaustive) {
@@ -702,7 +702,7 @@ fn non_exhaustive_match<'p, 'tcx>(
702702
thir: &Thir<'tcx>,
703703
scrut_ty: Ty<'tcx>,
704704
sp: Span,
705-
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
705+
witnesses: Vec<WitnessPat<'tcx>>,
706706
arms: &[ArmId],
707707
expr_span: Span,
708708
) -> ErrorGuaranteed {
@@ -878,10 +878,10 @@ fn non_exhaustive_match<'p, 'tcx>(
878878

879879
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
880880
cx: &MatchCheckCtxt<'p, 'tcx>,
881-
witnesses: &[DeconstructedPat<'p, 'tcx>],
881+
witnesses: &[WitnessPat<'tcx>],
882882
) -> String {
883883
const LIMIT: usize = 3;
884-
let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
884+
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_pat(cx).to_string();
885885
match witnesses {
886886
[] => bug!(),
887887
[witness] => format!("`{}`", witness.to_pat(cx)),
@@ -898,7 +898,7 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
898898
}
899899

900900
pub(crate) fn pattern_not_covered_label(
901-
witnesses: &[DeconstructedPat<'_, '_>],
901+
witnesses: &[WitnessPat<'_>],
902902
joined_patterns: &str,
903903
) -> String {
904904
format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
@@ -909,7 +909,7 @@ fn adt_defined_here<'p, 'tcx>(
909909
cx: &MatchCheckCtxt<'p, 'tcx>,
910910
err: &mut Diagnostic,
911911
ty: Ty<'tcx>,
912-
witnesses: &[DeconstructedPat<'p, 'tcx>],
912+
witnesses: &[WitnessPat<'tcx>],
913913
) {
914914
let ty = ty.peel_refs();
915915
if let ty::Adt(def, _) = ty.kind() {
@@ -940,7 +940,7 @@ fn adt_defined_here<'p, 'tcx>(
940940
fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
941941
cx: &MatchCheckCtxt<'p, 'tcx>,
942942
def: AdtDef<'tcx>,
943-
patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
943+
patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>,
944944
) -> Vec<Span> {
945945
use Constructor::*;
946946
let mut covered = vec![];

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

Lines changed: 132 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,9 +1312,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
13121312

13131313
/// Values and patterns can be represented as a constructor applied to some fields. This represents
13141314
/// a pattern in this form.
1315-
/// This also keeps track of whether the pattern has been found reachable during analysis. For this
1316-
/// reason we should be careful not to clone patterns for which we care about that. Use
1317-
/// `clone_and_forget_reachability` if you're sure.
1315+
/// This also uses interior mutability to keep track of whether the pattern has been found reachable
1316+
/// during analysis. For this reason they cannot be cloned.
1317+
/// A `DeconstructedPat` will almost always come from user input; the only exception are some
1318+
/// `Wildcard`s introduced during specialization.
13181319
pub(crate) struct DeconstructedPat<'p, 'tcx> {
13191320
ctor: Constructor<'tcx>,
13201321
fields: Fields<'p, 'tcx>,
@@ -1337,20 +1338,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
13371338
DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) }
13381339
}
13391340

1340-
/// Construct a pattern that matches everything that starts with this constructor.
1341-
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
1342-
/// `Some(_)`.
1343-
pub(super) fn wild_from_ctor(pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
1344-
let fields = Fields::wildcards(pcx, &ctor);
1345-
DeconstructedPat::new(ctor, fields, pcx.ty, pcx.span)
1346-
}
1347-
1348-
/// Clone this value. This method emphasizes that cloning loses reachability information and
1349-
/// should be done carefully.
1350-
pub(super) fn clone_and_forget_reachability(&self) -> Self {
1351-
DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span)
1352-
}
1353-
13541341
pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self {
13551342
let mkpat = |pat| DeconstructedPat::from_pat(cx, pat);
13561343
let ctor;
@@ -1533,95 +1520,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
15331520
DeconstructedPat::new(ctor, fields, pat.ty, pat.span)
15341521
}
15351522

1536-
pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> {
1537-
let is_wildcard = |pat: &Pat<'_>| {
1538-
matches!(pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
1539-
};
1540-
let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx)));
1541-
let kind = match &self.ctor {
1542-
Single | Variant(_) => match self.ty.kind() {
1543-
ty::Tuple(..) => PatKind::Leaf {
1544-
subpatterns: subpatterns
1545-
.enumerate()
1546-
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
1547-
.collect(),
1548-
},
1549-
ty::Adt(adt_def, _) if adt_def.is_box() => {
1550-
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
1551-
// of `std`). So this branch is only reachable when the feature is enabled and
1552-
// the pattern is a box pattern.
1553-
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
1554-
}
1555-
ty::Adt(adt_def, args) => {
1556-
let variant_index = self.ctor.variant_index_for_adt(*adt_def);
1557-
let variant = &adt_def.variant(variant_index);
1558-
let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant)
1559-
.zip(subpatterns)
1560-
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
1561-
.collect();
1562-
1563-
if adt_def.is_enum() {
1564-
PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns }
1565-
} else {
1566-
PatKind::Leaf { subpatterns }
1567-
}
1568-
}
1569-
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
1570-
// be careful to reconstruct the correct constant pattern here. However a string
1571-
// literal pattern will never be reported as a non-exhaustiveness witness, so we
1572-
// ignore this issue.
1573-
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
1574-
_ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty),
1575-
},
1576-
Slice(slice) => {
1577-
match slice.kind {
1578-
FixedLen(_) => PatKind::Slice {
1579-
prefix: subpatterns.collect(),
1580-
slice: None,
1581-
suffix: Box::new([]),
1582-
},
1583-
VarLen(prefix, _) => {
1584-
let mut subpatterns = subpatterns.peekable();
1585-
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
1586-
if slice.array_len.is_some() {
1587-
// Improves diagnostics a bit: if the type is a known-size array, instead
1588-
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
1589-
// This is incorrect if the size is not known, since `[_, ..]` captures
1590-
// arrays of lengths `>= 1` whereas `[..]` captures any length.
1591-
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
1592-
prefix.pop();
1593-
}
1594-
while subpatterns.peek().is_some()
1595-
&& is_wildcard(subpatterns.peek().unwrap())
1596-
{
1597-
subpatterns.next();
1598-
}
1599-
}
1600-
let suffix: Box<[_]> = subpatterns.collect();
1601-
let wild = Pat::wildcard_from_ty(self.ty);
1602-
PatKind::Slice {
1603-
prefix: prefix.into_boxed_slice(),
1604-
slice: Some(Box::new(wild)),
1605-
suffix,
1606-
}
1607-
}
1608-
}
1609-
}
1610-
&Str(value) => PatKind::Constant { value },
1611-
IntRange(range) => return range.to_pat(cx.tcx, self.ty),
1612-
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
1613-
Missing { .. } => bug!(
1614-
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
1615-
`Missing` should have been processed in `apply_constructors`"
1616-
),
1617-
F32Range(..) | F64Range(..) | Opaque | Or => {
1618-
bug!("can't convert to pattern: {:?}", self)
1619-
}
1620-
};
1621-
1622-
Pat { ty: self.ty, span: DUMMY_SP, kind }
1623-
}
1624-
16251523
pub(super) fn is_or_pat(&self) -> bool {
16261524
matches!(self.ctor, Or)
16271525
}
@@ -1804,3 +1702,131 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
18041702
}
18051703
}
18061704
}
1705+
1706+
/// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics
1707+
/// purposes. As such they don't use interning and can be cloned.
1708+
#[derive(Debug, Clone)]
1709+
pub(crate) struct WitnessPat<'tcx> {
1710+
ctor: Constructor<'tcx>,
1711+
fields: Vec<WitnessPat<'tcx>>,
1712+
ty: Ty<'tcx>,
1713+
}
1714+
1715+
impl<'tcx> WitnessPat<'tcx> {
1716+
pub(super) fn new(ctor: Constructor<'tcx>, fields: Vec<Self>, ty: Ty<'tcx>) -> Self {
1717+
Self { ctor, fields, ty }
1718+
}
1719+
pub(super) fn wildcard(ty: Ty<'tcx>) -> Self {
1720+
Self::new(Wildcard, Vec::new(), ty)
1721+
}
1722+
1723+
/// Construct a pattern that matches everything that starts with this constructor.
1724+
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
1725+
/// `Some(_)`.
1726+
pub(super) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, 'tcx>, ctor: Constructor<'tcx>) -> Self {
1727+
// Reuse `Fields::wildcards` to get the types.
1728+
let fields = Fields::wildcards(pcx, &ctor)
1729+
.iter_patterns()
1730+
.map(|deco_pat| Self::wildcard(deco_pat.ty()))
1731+
.collect();
1732+
Self::new(ctor, fields, pcx.ty)
1733+
}
1734+
1735+
pub(super) fn ctor(&self) -> &Constructor<'tcx> {
1736+
&self.ctor
1737+
}
1738+
pub(super) fn ty(&self) -> Ty<'tcx> {
1739+
self.ty
1740+
}
1741+
1742+
pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> Pat<'tcx> {
1743+
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
1744+
let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx)));
1745+
let kind = match &self.ctor {
1746+
Single | Variant(_) => match self.ty.kind() {
1747+
ty::Tuple(..) => PatKind::Leaf {
1748+
subpatterns: subpatterns
1749+
.enumerate()
1750+
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
1751+
.collect(),
1752+
},
1753+
ty::Adt(adt_def, _) if adt_def.is_box() => {
1754+
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
1755+
// of `std`). So this branch is only reachable when the feature is enabled and
1756+
// the pattern is a box pattern.
1757+
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
1758+
}
1759+
ty::Adt(adt_def, args) => {
1760+
let variant_index = self.ctor.variant_index_for_adt(*adt_def);
1761+
let variant = &adt_def.variant(variant_index);
1762+
let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant)
1763+
.zip(subpatterns)
1764+
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
1765+
.collect();
1766+
1767+
if adt_def.is_enum() {
1768+
PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns }
1769+
} else {
1770+
PatKind::Leaf { subpatterns }
1771+
}
1772+
}
1773+
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
1774+
// be careful to reconstruct the correct constant pattern here. However a string
1775+
// literal pattern will never be reported as a non-exhaustiveness witness, so we
1776+
// ignore this issue.
1777+
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
1778+
_ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty),
1779+
},
1780+
Slice(slice) => {
1781+
match slice.kind {
1782+
FixedLen(_) => PatKind::Slice {
1783+
prefix: subpatterns.collect(),
1784+
slice: None,
1785+
suffix: Box::new([]),
1786+
},
1787+
VarLen(prefix, _) => {
1788+
let mut subpatterns = subpatterns.peekable();
1789+
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
1790+
if slice.array_len.is_some() {
1791+
// Improves diagnostics a bit: if the type is a known-size array, instead
1792+
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
1793+
// This is incorrect if the size is not known, since `[_, ..]` captures
1794+
// arrays of lengths `>= 1` whereas `[..]` captures any length.
1795+
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
1796+
prefix.pop();
1797+
}
1798+
while subpatterns.peek().is_some()
1799+
&& is_wildcard(subpatterns.peek().unwrap())
1800+
{
1801+
subpatterns.next();
1802+
}
1803+
}
1804+
let suffix: Box<[_]> = subpatterns.collect();
1805+
let wild = Pat::wildcard_from_ty(self.ty);
1806+
PatKind::Slice {
1807+
prefix: prefix.into_boxed_slice(),
1808+
slice: Some(Box::new(wild)),
1809+
suffix,
1810+
}
1811+
}
1812+
}
1813+
}
1814+
&Str(value) => PatKind::Constant { value },
1815+
IntRange(range) => return range.to_pat(cx.tcx, self.ty),
1816+
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
1817+
Missing { .. } => bug!(
1818+
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
1819+
`Missing` should have been processed in `apply_constructors`"
1820+
),
1821+
F32Range(..) | F64Range(..) | Opaque | Or => {
1822+
bug!("can't convert to pattern: {:?}", self)
1823+
}
1824+
};
1825+
1826+
Pat { ty: self.ty, span: DUMMY_SP, kind }
1827+
}
1828+
1829+
pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a WitnessPat<'tcx>> {
1830+
self.fields.iter()
1831+
}
1832+
}

0 commit comments

Comments
 (0)