Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit b2532a8

Browse files
committed
Implement destructuring for all aggregates and for references
1 parent 34c62e0 commit b2532a8

18 files changed

+312
-115
lines changed

compiler/rustc_mir/src/const_eval/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ pub(crate) fn deref_const<'tcx>(
9393
MemPlaceMeta::None => mplace.layout.ty,
9494
MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
9595
// In case of unsized types, figure out the real type behind.
96-
MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind {
97-
ty::Dynamic(..) => ecx.read_drop_type_from_vtable(scalar).unwrap().1,
96+
MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
9897
ty::Str => bug!("there's no sized equivalent of a `str`"),
9998
ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
10099
_ => bug!(

compiler/rustc_mir_build/src/build/matches/test.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
409409

410410
let deref_ty = match *ty.kind() {
411411
ty::Ref(_, deref_ty, _) => deref_ty,
412-
_ => bug!("non_scalar_compare called on non-reference type: {}", ty),
412+
_ => {
413+
trace!("non_scalar_compare called on non-reference type: {}", ty);
414+
// Backcompat hack: due to non-structural matches not being a hard error, we can
415+
// reach this for types that have manual `Eq` or `PartialEq` impls.
416+
assert!(!ty.is_structural_eq_shallow(self.hir.tcx()));
417+
let ref_ty = self.hir.tcx().mk_imm_ref(self.hir.tcx().lifetimes.re_erased, ty);
418+
// let y = &place;
419+
let y = self.temp(ref_ty, source_info.span);
420+
self.cfg.push_assign(
421+
block,
422+
source_info,
423+
y,
424+
Rvalue::Ref(self.hir.tcx().lifetimes.re_erased, BorrowKind::Shared, place),
425+
);
426+
val = Operand::Move(y);
427+
// let temp = expect;
428+
let temp = self.temp(ty, source_info.span);
429+
self.cfg.push_assign(
430+
block,
431+
source_info,
432+
temp,
433+
Rvalue::Use(expect),
434+
);
435+
// reftemp = &temp;
436+
let reftemp = self.temp(ref_ty, source_info.span);
437+
self.cfg.push_assign(
438+
block,
439+
source_info,
440+
reftemp,
441+
Rvalue::Ref(self.hir.tcx().lifetimes.re_erased, BorrowKind::Shared, temp),
442+
);
443+
expect = Operand::Move(reftemp);
444+
ty
445+
},
413446
};
414447

415448
let eq_def_id = self.hir.tcx().require_lang_item(LangItem::PartialEq, None);

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

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
2828
debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
2929
debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
3030

31-
self.tcx.infer_ctxt().enter(|infcx| {
31+
let pat = self.tcx.infer_ctxt().enter(|infcx| {
3232
let mut convert = ConstToPat::new(self, id, span, infcx);
3333
convert.to_pat(cv, mir_structural_match_violation)
34-
})
34+
});
35+
36+
debug!("const_to_pat: pat={:?}", pat);
37+
pat
3538
}
3639
}
3740

@@ -45,6 +48,10 @@ struct ConstToPat<'a, 'tcx> {
4548
// value.
4649
saw_const_match_error: Cell<bool>,
4750

51+
// For backcompat we need to keep allowing non-structurally-eq types behind references.
52+
// See also all the `cant-hide-behind` tests.
53+
behind_reference: Cell<bool>,
54+
4855
// inference context used for checking `T: Structural` bounds.
4956
infcx: InferCtxt<'a, 'tcx>,
5057

@@ -65,6 +72,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
6572
param_env: pat_ctxt.param_env,
6673
include_lint_checks: pat_ctxt.include_lint_checks,
6774
saw_const_match_error: Cell::new(false),
75+
behind_reference: Cell::new(false),
6876
}
6977
}
7078

@@ -233,7 +241,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
233241
tcx.sess.span_err(span, "cannot use unions in constant patterns");
234242
PatKind::Wild
235243
}
236-
// keep old code until future-compat upgraded to errors.
244+
// If the type is not structurally comparable, just emit the constant directly,
245+
// causing the pattern match code to treat it opaquely.
246+
// FIXME: This code doesn't emit errors itself, the caller emits the errors.
247+
// So instead of specific errors, you just get blanket errors about the whole
248+
// const type. See
249+
// https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
250+
// details.
251+
// Backwards compatibility hack because we can't cause hard errors on these
252+
// types, so we compare them via `PartialEq::eq` at runtime.
253+
ty::Adt(..) if !self.type_marked_structural(cv.ty) && self.behind_reference.get() => {
254+
PatKind::Constant { value: cv }
255+
}
237256
ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
238257
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
239258
let path = tcx.def_path_str(adt_def.did);
@@ -246,28 +265,6 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
246265
tcx.sess.span_err(span, &msg);
247266
PatKind::Wild
248267
}
249-
// keep old code until future-compat upgraded to errors.
250-
ty::Ref(_, adt_ty, _) if adt_ty.is_adt() && !self.type_marked_structural(adt_ty) => {
251-
let adt_def =
252-
if let ty::Adt(adt_def, _) = adt_ty.kind() { adt_def } else { unreachable!() };
253-
254-
debug!(
255-
"adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
256-
adt_def, adt_ty
257-
);
258-
259-
// HACK(estebank): Side-step ICE #53708, but anything other than erroring here
260-
// would be wrong. Returnging `PatKind::Wild` is not technically correct.
261-
let path = tcx.def_path_str(adt_def.did);
262-
let msg = format!(
263-
"to use a constant of type `{}` in a pattern, \
264-
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
265-
path, path,
266-
);
267-
self.saw_const_match_error.set(true);
268-
tcx.sess.span_err(span, &msg);
269-
PatKind::Wild
270-
}
271268
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
272269
let destructured = tcx.destructure_const(param_env.and(cv));
273270
PatKind::Variant {
@@ -293,7 +290,68 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
293290
slice: None,
294291
suffix: Vec::new(),
295292
},
296-
_ => PatKind::Constant { value: cv },
293+
ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
294+
// These are not allowed and will error elsewhere anyway.
295+
ty::Dynamic(..) => PatKind::Constant { value: cv },
296+
// `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
297+
// optimization for now.
298+
ty::Str => PatKind::Constant { value: cv },
299+
ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
300+
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
301+
// matching against references, you can only use byte string literals.
302+
// FIXME: clean this up, likely by permitting array patterns when matching on slices
303+
ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
304+
// Cannot merge this with the catch all branch below, because the `const_deref`
305+
// changes the type from slice to array, and slice patterns behave differently from
306+
// array patterns.
307+
ty::Slice(..) => {
308+
let old = self.behind_reference.replace(true);
309+
let array = tcx.deref_const(self.param_env.and(cv));
310+
let val = PatKind::Deref {
311+
subpattern: Pat {
312+
kind: Box::new(PatKind::Slice {
313+
prefix: tcx
314+
.destructure_const(param_env.and(array))
315+
.fields
316+
.iter()
317+
.map(|val| self.recur(val))
318+
.collect(),
319+
slice: None,
320+
suffix: vec![],
321+
}),
322+
span,
323+
ty: pointee_ty,
324+
},
325+
};
326+
self.behind_reference.set(old);
327+
val
328+
}
329+
// Backwards compatibility hack. Don't take away the reference, since
330+
// `PartialEq::eq` takes a reference, this makes the rest of the matching logic
331+
// simpler.
332+
ty::Adt(..) if !self.type_marked_structural(pointee_ty) => {
333+
PatKind::Constant { value: cv }
334+
}
335+
_ => {
336+
let old = self.behind_reference.replace(true);
337+
let val = PatKind::Deref {
338+
subpattern: self.recur(tcx.deref_const(self.param_env.and(cv))),
339+
};
340+
self.behind_reference.set(old);
341+
val
342+
}
343+
},
344+
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
345+
PatKind::Constant { value: cv }
346+
}
347+
// FIXME: these can have very suprising behaviour where optimization levels or other
348+
// compilation choices change the runtime behaviour of the match.
349+
// See https://github.com/rust-lang/rust/issues/70861 for examples.
350+
ty::FnPtr(..) | ty::RawPtr(..) => PatKind::Constant { value: cv },
351+
_ => {
352+
tcx.sess.delay_span_bug(span, &format!("cannot make a pattern out of {}", cv.ty));
353+
PatKind::Wild
354+
}
297355
};
298356

299357
Pat { span, ty: cv.ty, kind: Box::new(kind) }

library/std/src/ffi/os_str.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub struct OsString {
9494
// `OsStr::from_inner` current implementation relies
9595
// on `OsStr` being layout-compatible with `Slice`.
9696
// When attribute privacy is implemented, `OsStr` should be annotated as `#[repr(transparent)]`.
97-
// Anyway, `OsStr` representation and layout are considered implementation detail, are
97+
// Anyway, `OsStr` representation and layout are considered implementation details, are
9898
// not documented and must not be relied upon.
9999
pub struct OsStr {
100100
inner: Slice,

src/test/ui/consts/consts-in-patterns.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ pub fn main() {
1919
assert_eq!(y, 2);
2020
let z = match &() {
2121
ZST => 9,
22-
// FIXME: this should not be required
23-
_ => 42,
2422
};
2523
assert_eq!(z, 9);
2624
let z = match b"" {

src/test/ui/consts/match_ice.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ fn main() {
1010
match C {
1111
C => {}
1212
//~^ ERROR to use a constant of type `S` in a pattern, `S` must be annotated with
13-
//~| ERROR to use a constant of type `S` in a pattern, `S` must be annotated with
1413
}
1514
const K: &T = &T;
16-
match K { //~ ERROR non-exhaustive patterns: `&T` not covered
15+
match K {
1716
K => {}
1817
}
1918
}

src/test/ui/consts/match_ice.stderr

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,5 @@ error: to use a constant of type `S` in a pattern, `S` must be annotated with `#
44
LL | C => {}
55
| ^
66

7-
error[E0004]: non-exhaustive patterns: `&T` not covered
8-
--> $DIR/match_ice.rs:16:11
9-
|
10-
LL | struct T;
11-
| --------- `T` defined here
12-
...
13-
LL | match K {
14-
| ^ pattern `&T` not covered
15-
|
16-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
17-
= note: the matched value is of type `&T`
18-
19-
error: to use a constant of type `S` in a pattern, `S` must be annotated with `#[derive(PartialEq, Eq)]`
20-
--> $DIR/match_ice.rs:11:9
21-
|
22-
LL | C => {}
23-
| ^
24-
25-
error: aborting due to 3 previous errors
7+
error: aborting due to previous error
268

27-
For more information about this error, try `rustc --explain E0004`.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// run-pass
2+
3+
fn main() {
4+
match b"." as &[u8] {
5+
b"." if true => {},
6+
b"." => panic!(),
7+
b".." => panic!(),
8+
b"" => panic!(),
9+
_ => panic!(),
10+
}
11+
match b"." as &[u8] {
12+
b"." if false => panic!(),
13+
b"." => {},
14+
b".." => panic!(),
15+
b"" => panic!(),
16+
_ => panic!(),
17+
}
18+
match b".." as &[u8] {
19+
b"." if true => panic!(), // the miscompile caused this arm to be reached
20+
b"." => panic!(),
21+
b".." => {},
22+
b"" => panic!(),
23+
_ => panic!(),
24+
}
25+
match b".." as &[u8] {
26+
b"." if false => panic!(),
27+
b"." => panic!(),
28+
b".." => {},
29+
b"" => panic!(),
30+
_ => panic!(),
31+
}
32+
match b"" as &[u8] {
33+
b"." if true => panic!(),
34+
b"." => panic!(),
35+
b".." => panic!(),
36+
b"" => {},
37+
_ => panic!(),
38+
}
39+
match b"" as &[u8] {
40+
b"." if false => panic!(),
41+
b"." => panic!(),
42+
b".." => panic!(),
43+
b"" => {},
44+
_ => panic!(),
45+
}
46+
}

src/test/ui/pattern/const-pat-ice.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
// failure-status: 101
2-
// rustc-env:RUST_BACKTRACE=0
3-
// normalize-stderr-test "note: rustc 1.* running on .*" -> "note: rustc VERSION running on TARGET"
4-
// normalize-stderr-test "note: compiler flags: .*" -> "note: compiler flags: FLAGS"
5-
// normalize-stderr-test "/_match.rs:[0-9]+:[0-9]+" -> "/_match.rs:LL:CC"
6-
7-
// This is a repro test for an ICE in our pattern handling of constants.
1+
// check-pass
82

93
const FOO: &&&u32 = &&&42;
104

src/test/ui/pattern/const-pat-ice.stderr

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)