Skip to content

Commit 1899c48

Browse files
committed
Auto merge of #78553 - Nadrieril:fix-78549, r=varkor
Fix #78549 Before #78430, this worked because `specialize_constructor` didn't actually care too much which constructor was passed to it unless needed. That PR however handles `&str` as a special case, and I did not anticipate patterns for the `&str` type other than string literals. I am not very confident there are not other similar oversights left, but hopefully only `&str` was different enough to break my assumptions. Fixes #78549
2 parents e8cbaf2 + 1bdcd02 commit 1899c48

File tree

4 files changed

+86
-38
lines changed

4 files changed

+86
-38
lines changed

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

+55-32
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,23 @@ struct LiteralExpander;
327327
impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
328328
fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
329329
debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind);
330-
match (pat.ty.kind(), &*pat.kind) {
331-
(_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self),
332-
(_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self),
330+
match (pat.ty.kind(), pat.kind.as_ref()) {
331+
(_, PatKind::Binding { subpattern: Some(s), .. }) => s.fold_with(self),
332+
(_, PatKind::AscribeUserType { subpattern: s, .. }) => s.fold_with(self),
333+
(ty::Ref(_, t, _), PatKind::Constant { .. }) if t.is_str() => {
334+
// Treat string literal patterns as deref patterns to a `str` constant, i.e.
335+
// `&CONST`. This expands them like other const patterns. This could have been done
336+
// in `const_to_pat`, but that causes issues with the rest of the matching code.
337+
let mut new_pat = pat.super_fold_with(self);
338+
// Make a fake const pattern of type `str` (instead of `&str`). That the carried
339+
// constant value still knows it is of type `&str`.
340+
new_pat.ty = t;
341+
Pat {
342+
kind: Box::new(PatKind::Deref { subpattern: new_pat }),
343+
span: pat.span,
344+
ty: pat.ty,
345+
}
346+
}
333347
_ => pat.super_fold_with(self),
334348
}
335349
}
@@ -782,11 +796,9 @@ enum Constructor<'tcx> {
782796
/// boxes for the purposes of exhaustiveness: we must not inspect them, and they
783797
/// don't count towards making a match exhaustive.
784798
Opaque,
785-
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
799+
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
800+
/// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
786801
NonExhaustive,
787-
/// Fake constructor for those types for which we can't list constructors explicitly, like
788-
/// `f64` and `&str`.
789-
Unlistable,
790802
/// Wildcard pattern.
791803
Wildcard,
792804
}
@@ -880,6 +892,7 @@ impl<'tcx> Constructor<'tcx> {
880892
/// For the simple cases, this is simply checking for equality. For the "grouped" constructors,
881893
/// this checks for inclusion.
882894
fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool {
895+
// This must be kept in sync with `is_covered_by_any`.
883896
match (self, other) {
884897
// Wildcards cover anything
885898
(_, Wildcard) => true,
@@ -922,18 +935,19 @@ impl<'tcx> Constructor<'tcx> {
922935
(Opaque, _) | (_, Opaque) => false,
923936
// Only a wildcard pattern can match the special extra constructor.
924937
(NonExhaustive, _) => false,
925-
// If we encounter a `Single` here, this means there was only one constructor for this
926-
// type after all.
927-
(Unlistable, Single) => true,
928-
// Otherwise, only a wildcard pattern can match the special extra constructor.
929-
(Unlistable, _) => false,
930938

931-
_ => bug!("trying to compare incompatible constructors {:?} and {:?}", self, other),
939+
_ => span_bug!(
940+
pcx.span,
941+
"trying to compare incompatible constructors {:?} and {:?}",
942+
self,
943+
other
944+
),
932945
}
933946
}
934947

935948
/// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is
936-
/// assumed to be built from `matrix.head_ctors()`, and `self` is assumed to have been split.
949+
/// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is
950+
/// assumed to have been split from a wildcard.
937951
fn is_covered_by_any<'p>(
938952
&self,
939953
pcx: PatCtxt<'_, 'p, 'tcx>,
@@ -943,8 +957,9 @@ impl<'tcx> Constructor<'tcx> {
943957
return false;
944958
}
945959

960+
// This must be kept in sync with `is_covered_by`.
946961
match self {
947-
// `used_ctors` cannot contain anything else than `Single`s.
962+
// If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s.
948963
Single => !used_ctors.is_empty(),
949964
Variant(_) => used_ctors.iter().any(|c| c == self),
950965
IntRange(range) => used_ctors
@@ -957,8 +972,6 @@ impl<'tcx> Constructor<'tcx> {
957972
.any(|other| slice.is_covered_by(other)),
958973
// This constructor is never covered by anything else
959974
NonExhaustive => false,
960-
// This constructor is only covered by `Single`s
961-
Unlistable => used_ctors.iter().any(|c| *c == Single),
962975
Str(..) | FloatRange(..) | Opaque | Wildcard => {
963976
bug!("found unexpected ctor in all_ctors: {:?}", self)
964977
}
@@ -1006,6 +1019,10 @@ impl<'tcx> Constructor<'tcx> {
10061019
PatKind::Leaf { subpatterns }
10071020
}
10081021
}
1022+
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
1023+
// be careful to reconstruct the correct constant pattern here. However a string
1024+
// literal pattern will never be reported as a non-exhaustiveness witness, so we
1025+
// can ignore this issue.
10091026
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
10101027
ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, pcx.ty),
10111028
_ => PatKind::Wild,
@@ -1038,7 +1055,7 @@ impl<'tcx> Constructor<'tcx> {
10381055
&Str(value) => PatKind::Constant { value },
10391056
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
10401057
IntRange(range) => return range.to_pat(pcx.cx.tcx),
1041-
NonExhaustive | Unlistable => PatKind::Wild,
1058+
NonExhaustive => PatKind::Wild,
10421059
Opaque => bug!("we should not try to apply an opaque constructor"),
10431060
Wildcard => bug!(
10441061
"trying to apply a wildcard constructor; this should have been done in `apply_constructors`"
@@ -1187,8 +1204,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
11871204
}
11881205
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
11891206
},
1190-
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Unlistable
1191-
| Wildcard => Fields::empty(),
1207+
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => {
1208+
Fields::empty()
1209+
}
11921210
};
11931211
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
11941212
ret
@@ -1300,9 +1318,13 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
13001318
/// [Some(0), ..] => {}
13011319
/// }
13021320
/// ```
1321+
/// This is guaranteed to preserve the number of patterns in `self`.
13031322
fn replace_with_pattern_arguments(&self, pat: &'p Pat<'tcx>) -> Self {
13041323
match pat.kind.as_ref() {
1305-
PatKind::Deref { subpattern } => Self::from_single_pattern(subpattern),
1324+
PatKind::Deref { subpattern } => {
1325+
assert_eq!(self.len(), 1);
1326+
Fields::from_single_pattern(subpattern)
1327+
}
13061328
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
13071329
self.replace_with_fieldpats(subpatterns)
13081330
}
@@ -1590,10 +1612,9 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
15901612
vec![make_range(0, max)]
15911613
}
15921614
_ if cx.is_uninhabited(pcx.ty) => vec![],
1593-
ty::Adt(..) | ty::Tuple(..) => vec![Single],
1594-
ty::Ref(_, t, _) if !t.is_str() => vec![Single],
1595-
// This type is one for which we don't know how to list constructors, like `&str` or `f64`.
1596-
_ => vec![Unlistable],
1615+
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single],
1616+
// This type is one for which we cannot list constructors, like `str` or `f64`.
1617+
_ => vec![NonExhaustive],
15971618
}
15981619
}
15991620

@@ -2152,28 +2173,31 @@ fn pat_constructor<'p, 'tcx>(
21522173
cx: &MatchCheckCtxt<'p, 'tcx>,
21532174
pat: &'p Pat<'tcx>,
21542175
) -> Constructor<'tcx> {
2155-
match *pat.kind {
2176+
match pat.kind.as_ref() {
21562177
PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
21572178
PatKind::Binding { .. } | PatKind::Wild => Wildcard,
21582179
PatKind::Leaf { .. } | PatKind::Deref { .. } => Single,
2159-
PatKind::Variant { adt_def, variant_index, .. } => {
2180+
&PatKind::Variant { adt_def, variant_index, .. } => {
21602181
Variant(adt_def.variants[variant_index].def_id)
21612182
}
21622183
PatKind::Constant { value } => {
21632184
if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value, pat.span) {
21642185
IntRange(int_range)
21652186
} else {
2166-
match value.ty.kind() {
2187+
match pat.ty.kind() {
21672188
ty::Float(_) => FloatRange(value, value, RangeEnd::Included),
2168-
ty::Ref(_, t, _) if t.is_str() => Str(value),
2189+
// In `expand_pattern`, we convert string literals to `&CONST` patterns with
2190+
// `CONST` a pattern of type `str`. In truth this contains a constant of type
2191+
// `&str`.
2192+
ty::Str => Str(value),
21692193
// All constants that can be structurally matched have already been expanded
21702194
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
21712195
// opaque.
21722196
_ => Opaque,
21732197
}
21742198
}
21752199
}
2176-
PatKind::Range(PatRange { lo, hi, end }) => {
2200+
&PatKind::Range(PatRange { lo, hi, end }) => {
21772201
let ty = lo.ty;
21782202
if let Some(int_range) = IntRange::from_range(
21792203
cx.tcx,
@@ -2188,8 +2212,7 @@ fn pat_constructor<'p, 'tcx>(
21882212
FloatRange(lo, hi, end)
21892213
}
21902214
}
2191-
PatKind::Array { ref prefix, ref slice, ref suffix }
2192-
| PatKind::Slice { ref prefix, ref slice, ref suffix } => {
2215+
PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
21932216
let array_len = match pat.ty.kind() {
21942217
ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env)),
21952218
ty::Slice(_) => None,

src/test/ui/issues/issue-30240.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
fn main() {
2-
match "world" { //~ ERROR non-exhaustive patterns: `_`
2+
match "world" { //~ ERROR non-exhaustive patterns: `&_`
33
"hello" => {}
44
}
55

6-
match "world" { //~ ERROR non-exhaustive patterns: `_`
6+
match "world" { //~ ERROR non-exhaustive patterns: `&_`
77
ref _x if false => {}
88
"hello" => {}
99
}

src/test/ui/issues/issue-30240.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
error[E0004]: non-exhaustive patterns: `_` not covered
1+
error[E0004]: non-exhaustive patterns: `&_` not covered
22
--> $DIR/issue-30240.rs:2:11
33
|
44
LL | match "world" {
5-
| ^^^^^^^ pattern `_` not covered
5+
| ^^^^^^^ pattern `&_` not covered
66
|
77
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
88
= note: the matched value is of type `&str`
99

10-
error[E0004]: non-exhaustive patterns: `_` not covered
10+
error[E0004]: non-exhaustive patterns: `&_` not covered
1111
--> $DIR/issue-30240.rs:6:11
1212
|
1313
LL | match "world" {
14-
| ^^^^^^^ pattern `_` not covered
14+
| ^^^^^^^ pattern `&_` not covered
1515
|
1616
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1717
= note: the matched value is of type `&str`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// check-pass
2+
// From https://github.com/rust-lang/rust/issues/78549
3+
4+
fn main() {
5+
match "foo" {
6+
"foo" => {},
7+
&_ => {},
8+
}
9+
10+
match "foo" {
11+
&_ => {},
12+
"foo" => {},
13+
}
14+
15+
match ("foo", 0, "bar") {
16+
(&_, 0, &_) => {},
17+
("foo", _, "bar") => {},
18+
(&_, _, &_) => {},
19+
}
20+
21+
match (&"foo", "bar") {
22+
(&"foo", &_) => {},
23+
(&&_, &_) => {},
24+
}
25+
}

0 commit comments

Comments
 (0)