Skip to content

Commit 3e71754

Browse files
committed
Distinguish user patterns from reconstructed witnesses
1 parent e20cb77 commit 3e71754

File tree

4 files changed

+199
-166
lines changed

4 files changed

+199
-166
lines changed

compiler/rustc_mir_build/src/errors.rs

+2-2
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

+9-9
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
};
@@ -661,8 +661,8 @@ fn report_arm_reachability<'p, 'tcx>(
661661
}
662662
}
663663

664-
fn collect_non_exhaustive_tys<'p, 'tcx>(
665-
pat: &DeconstructedPat<'p, 'tcx>,
664+
fn collect_non_exhaustive_tys<'tcx>(
665+
pat: &WitnessPat<'tcx>,
666666
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
667667
) {
668668
if matches!(pat.ctor(), Constructor::NonExhaustive) {
@@ -678,7 +678,7 @@ fn non_exhaustive_match<'p, 'tcx>(
678678
thir: &Thir<'tcx>,
679679
scrut_ty: Ty<'tcx>,
680680
sp: Span,
681-
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
681+
witnesses: Vec<WitnessPat<'tcx>>,
682682
arms: &[ArmId],
683683
expr_span: Span,
684684
) -> ErrorGuaranteed {
@@ -860,10 +860,10 @@ fn non_exhaustive_match<'p, 'tcx>(
860860

861861
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
862862
cx: &MatchCheckCtxt<'p, 'tcx>,
863-
witnesses: &[DeconstructedPat<'p, 'tcx>],
863+
witnesses: &[WitnessPat<'tcx>],
864864
) -> String {
865865
const LIMIT: usize = 3;
866-
let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
866+
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_pat(cx).to_string();
867867
match witnesses {
868868
[] => bug!(),
869869
[witness] => format!("`{}`", witness.to_pat(cx)),
@@ -880,7 +880,7 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
880880
}
881881

882882
pub(crate) fn pattern_not_covered_label(
883-
witnesses: &[DeconstructedPat<'_, '_>],
883+
witnesses: &[WitnessPat<'_>],
884884
joined_patterns: &str,
885885
) -> String {
886886
format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
@@ -891,7 +891,7 @@ fn adt_defined_here<'p, 'tcx>(
891891
cx: &MatchCheckCtxt<'p, 'tcx>,
892892
err: &mut Diagnostic,
893893
ty: Ty<'tcx>,
894-
witnesses: &[DeconstructedPat<'p, 'tcx>],
894+
witnesses: &[WitnessPat<'tcx>],
895895
) {
896896
let ty = ty.peel_refs();
897897
if let ty::Adt(def, _) = ty.kind() {
@@ -922,7 +922,7 @@ fn adt_defined_here<'p, 'tcx>(
922922
fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
923923
cx: &MatchCheckCtxt<'p, 'tcx>,
924924
def: AdtDef<'tcx>,
925-
patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
925+
patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>,
926926
) -> Vec<Span> {
927927
use Constructor::*;
928928
let mut covered = vec![];

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

+132-106
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;
@@ -1529,95 +1516,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
15291516
DeconstructedPat::new(ctor, fields, pat.ty, pat.span)
15301517
}
15311518

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

0 commit comments

Comments
 (0)