Skip to content

Fully destructure slice and array constants into patterns #77390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 4 additions & 28 deletions compiler/rustc_mir_build/src/thir/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,11 @@ use std::iter::{FromIterator, IntoIterator};
use std::ops::RangeInclusive;

crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> {
LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat)
LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat)
}

struct LiteralExpander<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}

impl<'tcx> LiteralExpander<'tcx> {
Expand All @@ -328,40 +327,17 @@ impl<'tcx> LiteralExpander<'tcx> {
) -> ConstValue<'tcx> {
debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
match (val, &crty.kind(), &rty.kind()) {
// the easy case, deref a reference
(ConstValue::Scalar(p), x, y) if x == y => {
match p {
Scalar::Ptr(p) => {
let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory();
ConstValue::ByRef { alloc, offset: p.offset }
}
Scalar::Raw { .. } => {
let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap();
if layout.is_zst() {
// Deref of a reference to a ZST is a nop.
ConstValue::Scalar(Scalar::zst())
} else {
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;`
bug!("cannot deref {:#?}, {} -> {}", val, crty, rty);
}
}
}
}
// unsize array to slice if pattern is array but match value or other patterns are slice
(ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
assert_eq!(t, u);
assert_eq!(p.offset, Size::ZERO);
ConstValue::Slice {
data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(),
start: p.offset.bytes().try_into().unwrap(),
start: 0,
end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(),
}
}
// fat pointers stay the same
(ConstValue::Slice { .. }, _, _)
| (_, ty::Slice(_), ty::Slice(_))
| (_, ty::Str, ty::Str) => val,
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used
_ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
_ => val,
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
// matching against references, you can only use byte string literals.
// FIXME: clean this up, likely by permitting array patterns when matching on slices
ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
// The typechecker has a special case for byte string literals, by treating them
// as slices. This means we turn `&[T; N]` constants into slice patterns, which
// has no negative effects on pattern matching, even if we're actually matching on
// arrays.
ty::Array(..) |
// Cannot merge this with the catch all branch below, because the `const_deref`
// changes the type from slice to array, and slice patterns behave differently from
// array patterns.
// changes the type from slice to array, we need to keep the original type in the
// pattern.
ty::Slice(..) => {
let old = self.behind_reference.replace(true);
let array = tcx.deref_const(self.param_env.and(cv));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ LL | match buf {
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `&[u8; 4]`

error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
--> $DIR/match-byte-array-patterns-2.rs:10:11
|
LL | match buf {
| ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
| ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `&[u8]`
Expand Down