Skip to content

Commit b41d8bd

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 669c1ab commit b41d8bd

8 files changed

Lines changed: 47 additions & 24 deletions

File tree

‎compiler/rustc_pattern_analysis/src/constructor.rs‎

Lines changed: 22 additions & 0 deletions
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
letmut missing = Vec::new();
10401050
// Constructors in `ctors`, except wildcards and opaques.
10411051
letmut 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+
letmut 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
matchself{
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‎

Lines changed: 12 additions & 6 deletions
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,15 @@ 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, exhaustiveness must take
500+
// into account that multiple calls to deref may return different results. Hence
501+
// multiple deref! patterns cannot be exhaustive together unless each is exhaustive
502+
// by itself.
503+
fields = vec![self.lower_pat(subpattern).at_index(0)];
504+
arity = 1;
505+
ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty));
501506
}
502507
PatKind::Leaf{ subpatterns } | PatKind::Variant{ subpatterns, .. } => {
503508
match ty.kind(){
@@ -874,6 +879,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
874879
print::write_ref_like(&mut s, pat.ty().inner(),&print(&pat.fields[0])).unwrap();
875880
s
876881
}
882+
DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
877883
Slice(slice) => {
878884
let(prefix_len, has_dot_dot) = match slice.kind{
879885
SliceKind::FixedLen(len) => (len,false),

‎compiler/rustc_pattern_analysis/src/usefulness.rs‎

Lines changed: 3 additions & 1 deletion
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
fnspecialize<Cx:PatCx>(self,ctor:&Constructor<Cx>) -> Self{
868869
// We preserve validity except when we go inside a reference or a union field.
869-
ifmatches!(ctor,Constructor::Ref | Constructor::UnionField){
870+
ifmatches!(ctor,Constructor::Ref | Constructor::DerefPattern(_) | Constructor::UnionField)
871+
{
870872
// Validity of `x: &T` does not imply validity of `*x: T`.
871873
MaybeInvalid
872874
}else{

‎src/doc/unstable-book/src/language-features/deref-patterns.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ Like [`box_patterns`], deref patterns may move out of boxes:
6060
# #![feature(deref_patterns)]
6161
# #![allow(incomplete_features)]
6262
structNoCopy;
63-
// Match exhaustiveness analysis is not yet implemented.
64-
letderef!(x) =Box::new(NoCopy) else { unreachable!() };
63+
letderef!(x) =Box::new(NoCopy);
6564
drop::<NoCopy>(x);
6665
```
6766

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ fn simple_vec(vec: Vec<u32>) -> u32 {
1313
deref!([x]) => x,
1414
deref!([1, x]) => x + 200,
1515
deref!(ref slice) => slice.iter().sum(),
16-
_ => 2000,
1716
}
1817
}
1918

@@ -25,7 +24,6 @@ fn simple_vec(vec: Vec<u32>) -> u32 {
2524
[x] => x,
2625
[1, x] => x + 200,
2726
deref!(ref slice) => slice.iter().sum(),
28-
_ => 2000,
2927
}
3028
}
3129

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct NoCopy;
99
fnmain(){
1010
let b = Rc::new("aaa".to_string());
1111
let f = || {
12-
letderef!(ref s) = belse{unreachable!()};
12+
letderef!(ref s) = b;
1313
assert_eq!(s.len(),3);
1414
};
1515
assert_eq!(b.len(),3);
@@ -26,7 +26,7 @@ fn main() {
2626

2727
letmut b = "aaa".to_string();
2828
letmut f = || {
29-
letderef!(ref mut s) = belse{unreachable!()};
29+
letderef!(ref mut s) = b;
3030
s.make_ascii_uppercase();
3131
};
3232
f();
@@ -53,15 +53,15 @@ fn main() {
5353
let b = Box::new(NoCopy);
5454
let f = || {
5555
// this should move out of the box rather than borrow.
56-
letderef!(x) = belse{unreachable!()};
56+
letderef!(x) = b;
5757
drop::<NoCopy>(x);
5858
};
5959
f();
6060

6161
let b = Box::new((NoCopy,));
6262
let f = || {
6363
// this should move out of the box rather than borrow.
64-
let(x,) = belse{unreachable!()};
64+
let(x,) = b;
6565
drop::<NoCopy>(x);
6666
};
6767
f();

‎tests/ui/pattern/deref-patterns/deref-box.rs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@
66
#![expect(incomplete_features)]
77

88
fnunbox_1<T>(b:Box<T>) -> T{
9-
letderef!(x) = belse{unreachable!()};
9+
letderef!(x) = b;
1010
x
1111
}
1212

1313
fnunbox_2<T>(b:Box<(T,)>) -> T{
14-
let(x,) = belse{unreachable!()};
14+
let(x,) = b;
1515
x
1616
}
1717

1818
fnunbox_separately<T>(b:Box<(T,T)>) -> (T,T){
19-
let(x, _) = belse{unreachable!()};
20-
let(_, y) = belse{unreachable!()};
19+
let(x, _) = b;
20+
let(_, y) = b;
2121
(x, y)
2222
}
2323

@@ -31,7 +31,7 @@ fn main() {
3131

3232
// test that borrowing from a box also works
3333
letmut b = "hi".to_owned().into_boxed_str();
34-
letderef!(ref mut s) = belse{unreachable!()};
34+
letderef!(ref mut s) = b;
3535
s.make_ascii_uppercase();
3636
assert_eq!(&*b,"HI");
3737
}

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ fn main() {
1111

1212
match cow {
1313
[..] => {}
14-
_ => unreachable!(),
1514
}
1615

1716
match cow {
@@ -22,14 +21,12 @@ fn main() {
2221
matchRc::new(&cow){
2322
Cow::Borrowed{0: _ } => {}
2423
Cow::Owned{0: _ } => unreachable!(),
25-
_ => unreachable!(),
2624
}
2725

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

3028
match cow_of_cow {
3129
[..] => {}
32-
_ => unreachable!(),
3330
}
3431

3532
// This matches on the outer `Cow` (the owned one).
@@ -41,6 +38,5 @@ fn main() {
4138
matchRc::new(&cow_of_cow){
4239
Cow::Borrowed{0: _ } => unreachable!(),
4340
Cow::Owned{0: _ } => {}
44-
_ => unreachable!(),
4541
}
4642
}

0 commit comments

Comments
 (0)