Re-use the deref-pattern recursion instead of duplicating the logic

This commit is contained in:
Oli Scherer 2023-05-31 17:10:34 +00:00
parent 2304917aad
commit 0f7174a02a

View File

@ -359,6 +359,15 @@ fn recur(
def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx(), substs)), def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx(), substs)),
))?, ))?,
}, },
ty::Slice(elem_ty) => PatKind::Slice {
prefix: cv
.unwrap_branch()
.iter()
.map(|val| self.recur(*val, *elem_ty, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Box::new([]),
},
ty::Array(elem_ty, _) => PatKind::Array { ty::Array(elem_ty, _) => PatKind::Array {
prefix: cv prefix: cv
.unwrap_branch() .unwrap_branch()
@ -372,58 +381,6 @@ fn recur(
// `&str` is represented as a valtree, let's keep using this // `&str` is represented as a valtree, let's keep using this
// optimization for now. // optimization for now.
ty::Str => PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) }, ty::Str => PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) },
// `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.
// 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(elem_ty, _) if !self.treat_byte_string_as_slice => {
let old = self.behind_reference.replace(true);
// References have the same valtree representation as their pointee.
let array = cv;
let val = PatKind::Deref {
subpattern: Box::new(Pat {
kind: PatKind::Array {
prefix: array.unwrap_branch()
.iter()
.map(|val| self.recur(*val, elem_ty, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Box::new([]),
},
span,
ty: tcx.mk_slice(elem_ty),
}),
};
self.behind_reference.set(old);
val
}
ty::Array(elem_ty, _) |
// Cannot merge this with the catch all branch below, because the `const_deref`
// changes the type from slice to array, we need to keep the original type in the
// pattern.
ty::Slice(elem_ty) => {
let old = self.behind_reference.replace(true);
// References have the same valtree representation as their pointee.
let array = cv;
let val = PatKind::Deref {
subpattern: Box::new(Pat {
kind: PatKind::Slice {
prefix: array.unwrap_branch()
.iter()
.map(|val| self.recur(*val, elem_ty, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Box::new([]),
},
span,
ty: tcx.mk_slice(elem_ty),
}),
};
self.behind_reference.set(old);
val
}
// Backwards compatibility hack: support references to non-structural types, // Backwards compatibility hack: support references to non-structural types,
// but hard error if we aren't behind a double reference. We could just use // but hard error if we aren't behind a double reference. We could just use
// the fallback code path below, but that would allow *more* of this fishy // the fallback code path below, but that would allow *more* of this fishy
@ -431,11 +388,9 @@ fn recur(
// instead of a hard error. // instead of a hard error.
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
if self.behind_reference.get() { if self.behind_reference.get() {
if !self.saw_const_match_error.get() if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
&& !self.saw_const_match_lint.get() self.saw_const_match_lint.set(true);
{ tcx.emit_spanned_lint(
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH, lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id, self.id,
span, span,
@ -456,7 +411,7 @@ fn recur(
// convert the dereferenced constant to a pattern that is the sub-pattern of the // convert the dereferenced constant to a pattern that is the sub-pattern of the
// deref pattern. // deref pattern.
_ => { _ => {
if !pointee_ty.is_sized(tcx, param_env) { if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
tcx.sess.emit_err(err); tcx.sess.emit_err(err);
@ -464,8 +419,20 @@ fn recur(
PatKind::Wild PatKind::Wild
} else { } else {
let old = self.behind_reference.replace(true); let old = self.behind_reference.replace(true);
// `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.
// 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.
let pointee_ty = match *pointee_ty.kind() {
ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
tcx.mk_slice(elem_ty)
}
_ => *pointee_ty,
};
// References have the same valtree representation as their pointee. // References have the same valtree representation as their pointee.
let subpattern = self.recur(cv, *pointee_ty, false)?; let subpattern = self.recur(cv, pointee_ty, false)?;
self.behind_reference.set(old); self.behind_reference.set(old);
PatKind::Deref { subpattern } PatKind::Deref { subpattern }
} }