Skip to content

Commit 853de82

Browse files
committed
let deref patterns participate in usefulness/exhaustiveness
This does not yet handle the case of mixed deref patterns with normal constructors; it'll ICE in `Constructor::is_covered_by`. That'll be fixed in a later commit.
1 parent 191df20 commit 853de82

File tree

6 files changed

+40
-19
lines changed

6 files changed

+40
-19
lines changed

compiler/rustc_pattern_analysis/src/constructor.rs

+22
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,10 @@ pub enum Constructor<Cx: PatCx> {
696696
F128Range(IeeeFloat<QuadS>, IeeeFloat<QuadS>, RangeEnd),
697697
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
698698
Str(Cx::StrLit),
699+
/// Deref patterns (enabled by the `deref_patterns` feature) provide a way of matching on a
700+
/// smart pointer ADT through its pointee. They don't directly correspond to ADT constructors,
701+
/// and currently are not supported alongside them. Carries the type of the pointee.
702+
DerefPattern(Cx::Ty),
699703
/// Constants that must not be matched structurally. They are treated as black boxes for the
700704
/// purposes of exhaustiveness: we must not inspect them, and they don't count towards making a
701705
/// match exhaustive.
@@ -740,6 +744,7 @@ impl<Cx: PatCx> Clone for Constructor<Cx> {
740744
Constructor::F64Range(lo, hi, end) => Constructor::F64Range(*lo, *hi, *end),
741745
Constructor::F128Range(lo, hi, end) => Constructor::F128Range(*lo, *hi, *end),
742746
Constructor::Str(value) => Constructor::Str(value.clone()),
747+
Constructor::DerefPattern(ty) => Constructor::DerefPattern(ty.clone()),
743748
Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
744749
Constructor::Or => Constructor::Or,
745750
Constructor::Never => Constructor::Never,
@@ -856,6 +861,10 @@ impl<Cx: PatCx> Constructor<Cx> {
856861
}
857862
(Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),
858863

864+
// Deref patterns only interact with other deref patterns. Prior to usefulness analysis,
865+
// we ensure they don't appear alongside any other non-wild non-opaque constructors.
866+
(DerefPattern(_), DerefPattern(_)) => true,
867+
859868
// Opaque constructors don't interact with anything unless they come from the
860869
// syntactically identical pattern.
861870
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
@@ -932,6 +941,7 @@ impl<Cx: PatCx> Constructor<Cx> {
932941
F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
933942
F128Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
934943
Str(value) => write!(f, "{value:?}")?,
944+
DerefPattern(_) => write!(f, "deref!({:?})", fields.next().unwrap())?,
935945
Opaque(..) => write!(f, "<constant pattern>")?,
936946
Or => {
937947
for pat in fields {
@@ -1039,15 +1049,27 @@ impl<Cx: PatCx> ConstructorSet<Cx> {
10391049
let mut missing = Vec::new();
10401050
// Constructors in `ctors`, except wildcards and opaques.
10411051
let mut seen = Vec::new();
1052+
// If we see a deref pattern, it must be the only non-wildcard non-opaque constructor; we
1053+
// ensure this prior to analysis.
1054+
let mut deref_pat_present = false;
10421055
for ctor in ctors.cloned() {
10431056
match ctor {
1057+
DerefPattern(..) => {
1058+
if !deref_pat_present {
1059+
deref_pat_present = true;
1060+
present.push(ctor);
1061+
}
1062+
}
10441063
Opaque(..) => present.push(ctor),
10451064
Wildcard => {} // discard wildcards
10461065
_ => seen.push(ctor),
10471066
}
10481067
}
10491068

10501069
match self {
1070+
_ if deref_pat_present => {
1071+
// Deref patterns are the only constructor; nothing is missing.
1072+
}
10511073
ConstructorSet::Struct { empty } => {
10521074
if !seen.is_empty() {
10531075
present.push(Struct);

compiler/rustc_pattern_analysis/src/rustc.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
269269
}
270270
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
271271
},
272+
DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())),
272273
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
273274
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
274275
| PrivateUninhabited | Wildcard => &[],
@@ -296,7 +297,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
296297
}
297298
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
298299
},
299-
Ref => 1,
300+
Ref | DerefPattern(_) => 1,
300301
Slice(slice) => slice.arity(),
301302
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
302303
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
@@ -493,11 +494,14 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
493494
),
494495
};
495496
}
496-
PatKind::DerefPattern { .. } => {
497-
// FIXME(deref_patterns): At least detect that `box _` is irrefutable.
498-
fields = vec![];
499-
arity = 0;
500-
ctor = Opaque(OpaqueId::new());
497+
PatKind::DerefPattern { subpattern, .. } => {
498+
// NB(deref_patterns): This assumes the deref pattern is matching on a trusted
499+
// `DerefPure` type. If the `Deref` impl isn't trusted, any deref pattern that can
500+
// fail (possibly due to expanding or-patterns inside it) must not influence
501+
// exhaustiveness analysis.
502+
fields = vec![self.lower_pat(subpattern).at_index(0)];
503+
arity = 1;
504+
ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty));
501505
}
502506
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
503507
match ty.kind() {
@@ -874,6 +878,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
874878
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
875879
s
876880
}
881+
DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
877882
Slice(slice) => {
878883
let (prefix_len, has_dot_dot) = match slice.kind {
879884
SliceKind::FixedLen(len) => (len, false),

compiler/rustc_pattern_analysis/src/usefulness.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,7 @@
702702
//! - `ui/consts/const_in_pattern`
703703
//! - `ui/rfc-2008-non-exhaustive`
704704
//! - `ui/half-open-range-patterns`
705+
//! - `ui/pattern/deref-patterns`
705706
//! - probably many others
706707
//!
707708
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
@@ -866,7 +867,8 @@ impl PlaceValidity {
866867
/// inside `&` and union fields where validity is reset to `MaybeInvalid`.
867868
fn specialize<Cx: PatCx>(self, ctor: &Constructor<Cx>) -> Self {
868869
// We preserve validity except when we go inside a reference or a union field.
869-
if matches!(ctor, Constructor::Ref | Constructor::UnionField) {
870+
if matches!(ctor, Constructor::Ref | Constructor::DerefPattern(_) | Constructor::UnionField)
871+
{
870872
// Validity of `x: &T` does not imply validity of `*x: T`.
871873
MaybeInvalid
872874
} else {

tests/ui/pattern/deref-patterns/bindings.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ fn simple_vec(vec: Vec<u32>) -> u32 {
1111
deref!([x]) => x,
1212
deref!([1, x]) => x + 200,
1313
deref!(ref slice) => slice.iter().sum(),
14-
_ => 2000,
1514
}
1615
}
1716

@@ -23,7 +22,6 @@ fn simple_vec(vec: Vec<u32>) -> u32 {
2322
[x] => x,
2423
[1, x] => x + 200,
2524
deref!(ref slice) => slice.iter().sum(),
26-
_ => 2000,
2725
}
2826
}
2927

@@ -59,9 +57,8 @@ fn ref_mut(val: u32) -> u32 {
5957
deref!(x) => {
6058
*x = val;
6159
}
62-
_ => unreachable!(),
6360
}
64-
let deref!(x) = &b else { unreachable!() };
61+
let deref!(x) = &b;
6562
*x
6663
}
6764

@@ -73,9 +70,8 @@ fn ref_mut(val: u32) -> u32 {
7370
(x,) => {
7471
*x = val;
7572
}
76-
_ => unreachable!(),
7773
}
78-
let (x,) = &b else { unreachable!() };
74+
let (x,) = &b;
7975
*x
8076
}
8177

tests/ui/pattern/deref-patterns/closure_capture.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
fn main() {
66
let b = Box::new("aaa".to_string());
77
let f = || {
8-
let deref!(ref s) = b else { unreachable!() };
8+
let deref!(ref s) = b;
99
assert_eq!(s.len(), 3);
1010
};
1111
assert_eq!(b.len(), 3);
@@ -22,7 +22,7 @@ fn main() {
2222

2323
let mut b = Box::new("aaa".to_string());
2424
let mut f = || {
25-
let deref!(ref mut s) = b else { unreachable!() };
25+
let deref!(ref mut s) = b;
2626
s.push_str("aa");
2727
};
2828
f();

tests/ui/pattern/deref-patterns/implicit-cow-deref.rs

-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ fn main() {
1010

1111
match cow {
1212
[..] => {}
13-
_ => unreachable!(),
1413
}
1514

1615
match cow {
@@ -21,14 +20,12 @@ fn main() {
2120
match Box::new(&cow) {
2221
Cow::Borrowed { 0: _ } => {}
2322
Cow::Owned { 0: _ } => unreachable!(),
24-
_ => unreachable!(),
2523
}
2624

2725
let cow_of_cow: Cow<'_, Cow<'static, [u8]>> = Cow::Owned(cow);
2826

2927
match cow_of_cow {
3028
[..] => {}
31-
_ => unreachable!(),
3229
}
3330

3431
// This matches on the outer `Cow` (the owned one).
@@ -40,6 +37,5 @@ fn main() {
4037
match Box::new(&cow_of_cow) {
4138
Cow::Borrowed { 0: _ } => unreachable!(),
4239
Cow::Owned { 0: _ } => {}
43-
_ => unreachable!(),
4440
}
4541
}

0 commit comments

Comments
 (0)