Skip to content

Commit e0dd7ec

Browse files
authored
Rollup merge of #143414 - dianne:box-usefulness-cleanup, r=Nadrieril
remove special-casing of boxes from match exhaustiveness/usefulness analysis As a first step in replacing `box_patterns` with `deref_patterns`, this treats box patterns as deref patterns in the THIR and exhaustiveness analysis. This allows a bunch of special-casing to be removed. The emitted MIR is unchanged. Incidentally, this fixes a bug caused by box patterns being treated like structs rather than pointers, where enabling `exhaustive_patterns` (#51085) could give rise to spurious `unreachable_patterns` lints on arms required for exhaustiveness. Following the lint's advice to remove the match arm would result in an error. I'm not sure what the current state of `exhaustive_patterns` is with regard to reference/box opsem, or whether there's any intention to have `unreachable_patterns` be more granular than the whole arm, but regardless this should hopefully make them easier to handle consistently. Tracking issue for deref patterns: #87121 r? `@Nadrieril`
2 parents 2cde070 + 98659a3 commit e0dd7ec

File tree

4 files changed

+55
-81
lines changed

4 files changed

+55
-81
lines changed

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
319319
}
320320
PatKind::Deref { subpattern }
321321
}
322-
hir::PatKind::Box(subpattern) => {
323-
PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
324-
}
322+
hir::PatKind::Box(subpattern) => PatKind::DerefPattern {
323+
subpattern: self.lower_pattern(subpattern),
324+
borrow: hir::ByRef::No,
325+
},
325326

326327
hir::PatKind::Slice(prefix, slice, suffix) => {
327328
self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)

compiler/rustc_pattern_analysis/src/rustc.rs

Lines changed: 24 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -221,27 +221,19 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
221221
let slice = match ctor {
222222
Struct | Variant(_) | UnionField => match ty.kind() {
223223
ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
224-
ty::Adt(adt, args) => {
225-
if adt.is_box() {
226-
// The only legal patterns of type `Box` (outside `std`) are `_` and box
227-
// patterns. If we're here we can assume this is a box pattern.
228-
reveal_and_alloc(cx, once(args.type_at(0)))
229-
} else {
230-
let variant =
231-
&adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
232-
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
233-
let is_visible =
234-
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
235-
let is_uninhabited = cx.is_uninhabited(*ty);
236-
let is_unstable =
237-
cx.tcx.lookup_stability(field.did).is_some_and(|stab| {
238-
stab.is_unstable() && stab.feature != sym::rustc_private
239-
});
240-
let skip = is_uninhabited && (!is_visible || is_unstable);
241-
(ty, PrivateUninhabitedField(skip))
224+
ty::Adt(adt, _) => {
225+
let variant = &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
226+
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
227+
let is_visible =
228+
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
229+
let is_uninhabited = cx.is_uninhabited(*ty);
230+
let is_unstable = cx.tcx.lookup_stability(field.did).is_some_and(|stab| {
231+
stab.is_unstable() && stab.feature != sym::rustc_private
242232
});
243-
cx.dropless_arena.alloc_from_iter(tys)
244-
}
233+
let skip = is_uninhabited && (!is_visible || is_unstable);
234+
(ty, PrivateUninhabitedField(skip))
235+
});
236+
cx.dropless_arena.alloc_from_iter(tys)
245237
}
246238
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
247239
},
@@ -273,14 +265,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
273265
Struct | Variant(_) | UnionField => match ty.kind() {
274266
ty::Tuple(fs) => fs.len(),
275267
ty::Adt(adt, ..) => {
276-
if adt.is_box() {
277-
// The only legal patterns of type `Box` (outside `std`) are `_` and box
278-
// patterns. If we're here we can assume this is a box pattern.
279-
1
280-
} else {
281-
let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt);
282-
adt.variant(variant_idx).fields.len()
283-
}
268+
let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt);
269+
adt.variant(variant_idx).fields.len()
284270
}
285271
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
286272
},
@@ -470,8 +456,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
470456
fields = vec![self.lower_pat(subpattern).at_index(0)];
471457
arity = 1;
472458
ctor = match ty.kind() {
473-
// This is a box pattern.
474-
ty::Adt(adt, ..) if adt.is_box() => Struct,
475459
ty::Ref(..) => Ref,
476460
_ => span_bug!(
477461
pat.span,
@@ -501,28 +485,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
501485
.map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
502486
.collect();
503487
}
504-
ty::Adt(adt, _) if adt.is_box() => {
505-
// The only legal patterns of type `Box` (outside `std`) are `_` and box
506-
// patterns. If we're here we can assume this is a box pattern.
507-
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
508-
// _)` or a box pattern. As a hack to avoid an ICE with the former, we
509-
// ignore other fields than the first one. This will trigger an error later
510-
// anyway.
511-
// See https://github.com/rust-lang/rust/issues/82772,
512-
// explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
513-
// The problem is that we can't know from the type whether we'll match
514-
// normally or through box-patterns. We'll have to figure out a proper
515-
// solution when we introduce generalized deref patterns. Also need to
516-
// prevent mixing of those two options.
517-
let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
518-
if let Some(pat) = pattern {
519-
fields = vec![self.lower_pat(&pat.pattern).at_index(0)];
520-
} else {
521-
fields = vec![];
522-
}
523-
ctor = Struct;
524-
arity = 1;
525-
}
526488
ty::Adt(adt, _) => {
527489
ctor = match pat.kind {
528490
PatKind::Leaf { .. } if adt.is_union() => UnionField,
@@ -825,11 +787,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
825787
Bool(b) => b.to_string(),
826788
Str(s) => s.to_string(),
827789
IntRange(range) => return self.print_pat_range(range, *pat.ty()),
828-
Struct if pat.ty().is_box() => {
829-
// Outside of the `alloc` crate, the only way to create a struct pattern
830-
// of type `Box` is to use a `box` pattern via #[feature(box_patterns)].
831-
format!("box {}", print(&pat.fields[0]))
832-
}
833790
Struct | Variant(_) | UnionField => {
834791
let enum_info = match *pat.ty().kind() {
835792
ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
@@ -866,6 +823,14 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
866823
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
867824
s
868825
}
826+
DerefPattern(_) if pat.ty().is_box() && !self.tcx.features().deref_patterns() => {
827+
// FIXME(deref_patterns): Remove this special handling once `box_patterns` is gone.
828+
// HACK(@dianne): `box _` syntax is exposed on stable in diagnostics, e.g. to
829+
// witness non-exhaustiveness of `match Box::new(0) { Box { .. } if false => {} }`.
830+
// To avoid changing diagnostics before deref pattern syntax is finalized, let's use
831+
// `box _` syntax unless `deref_patterns` is enabled.
832+
format!("box {}", print(&pat.fields[0]))
833+
}
869834
DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
870835
Slice(slice) => {
871836
let (prefix_len, has_dot_dot) = match slice.kind {
@@ -964,12 +929,8 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
964929
ty: &Self::Ty,
965930
) -> fmt::Result {
966931
if let ty::Adt(adt, _) = ty.kind() {
967-
if adt.is_box() {
968-
write!(f, "Box")?
969-
} else {
970-
let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt));
971-
write!(f, "{}", variant.name)?;
972-
}
932+
let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt));
933+
write!(f, "{}", variant.name)?;
973934
}
974935
Ok(())
975936
}

tests/ui/uninhabited/uninhabited-patterns.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ fn main() {
2727

2828
let x: Result<Box<NotSoSecretlyEmpty>, &[Result<!, !>]> = Err(&[]);
2929
match x {
30-
Ok(box _) => (), //~ ERROR unreachable pattern
30+
Ok(box _) => (), // We'd get a non-exhaustiveness error if this arm was removed; don't lint.
31+
Err(&[]) => (),
32+
Err(&[..]) => (),
33+
}
34+
match x { //~ ERROR non-exhaustive patterns
3135
Err(&[]) => (),
3236
Err(&[..]) => (),
3337
}
Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
error: unreachable pattern
2-
--> $DIR/uninhabited-patterns.rs:30:9
1+
error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
2+
--> $DIR/uninhabited-patterns.rs:34:11
33
|
4-
LL | Ok(box _) => (),
5-
| ^^^^^^^^^-------
6-
| |
7-
| matches no values because `NotSoSecretlyEmpty` is uninhabited
8-
| help: remove the match arm
4+
LL | match x {
5+
| ^ pattern `Ok(_)` not covered
96
|
10-
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types
11-
note: the lint level is defined here
12-
--> $DIR/uninhabited-patterns.rs:4:9
7+
note: `Result<Box<NotSoSecretlyEmpty>, &[Result<!, !>]>` defined here
8+
--> $SRC_DIR/core/src/result.rs:LL:COL
9+
::: $SRC_DIR/core/src/result.rs:LL:COL
10+
|
11+
= note: not covered
12+
= note: the matched value is of type `Result<Box<NotSoSecretlyEmpty>, &[Result<!, !>]>`
13+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
14+
|
15+
LL ~ Err(&[..]) => (),
16+
LL ~ Ok(_) => todo!(),
1317
|
14-
LL | #![deny(unreachable_patterns)]
15-
| ^^^^^^^^^^^^^^^^^^^^
1618

1719
error: unreachable pattern
18-
--> $DIR/uninhabited-patterns.rs:39:9
20+
--> $DIR/uninhabited-patterns.rs:43:9
1921
|
2022
LL | Err(Ok(_y)) => (),
2123
| ^^^^^^^^^^^-------
@@ -24,9 +26,14 @@ LL | Err(Ok(_y)) => (),
2426
| help: remove the match arm
2527
|
2628
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types
29+
note: the lint level is defined here
30+
--> $DIR/uninhabited-patterns.rs:4:9
31+
|
32+
LL | #![deny(unreachable_patterns)]
33+
| ^^^^^^^^^^^^^^^^^^^^
2734

2835
error: unreachable pattern
29-
--> $DIR/uninhabited-patterns.rs:42:15
36+
--> $DIR/uninhabited-patterns.rs:46:15
3037
|
3138
LL | while let Some(_y) = foo() {
3239
| ^^^^^^^^ matches no values because `NotSoSecretlyEmpty` is uninhabited
@@ -35,3 +42,4 @@ LL | while let Some(_y) = foo() {
3542

3643
error: aborting due to 3 previous errors
3744

45+
For more information about this error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)