Skip to content

Commit 5af5f26

Browse files
committed
handle string literals correctly in match checking
The root of the problem is that a string literal pattern is essentially of the form `&LITERAL`, in a single block, while match checking wants to split that. To fix that, I added a type field to the patterns in match checking, which allows us to distinguish between a full and split pattern. That file is ugly and needs to be cleaned. However, `trans::_match` calls it, so I think we should delay the cleanup until we kill that. Fixes #30240
1 parent 1a614f8 commit 5af5f26

File tree

4 files changed

+162
-79
lines changed

4 files changed

+162
-79
lines changed

src/librustc_const_eval/check_match.rs

+112-75
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
4949
span: DUMMY_SP
5050
};
5151

52-
struct Matrix<'a>(Vec<Vec<&'a Pat>>);
52+
struct Matrix<'a, 'tcx>(Vec<Vec<(&'a Pat, Option<Ty<'tcx>>)>>);
5353

5454
/// Pretty-printer for matrices of patterns, example:
5555
/// ++++++++++++++++++++++++++
@@ -63,14 +63,14 @@ struct Matrix<'a>(Vec<Vec<&'a Pat>>);
6363
/// ++++++++++++++++++++++++++
6464
/// + _ + [_, _, ..tail] +
6565
/// ++++++++++++++++++++++++++
66-
impl<'a> fmt::Debug for Matrix<'a> {
66+
impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> {
6767
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6868
write!(f, "\n")?;
6969

7070
let &Matrix(ref m) = self;
7171
let pretty_printed_matrix: Vec<Vec<String>> = m.iter().map(|row| {
7272
row.iter()
73-
.map(|&pat| pat_to_string(&pat))
73+
.map(|&(pat,ty)| format!("{}: {:?}", pat_to_string(&pat), ty))
7474
.collect::<Vec<String>>()
7575
}).collect();
7676

@@ -97,8 +97,10 @@ impl<'a> fmt::Debug for Matrix<'a> {
9797
}
9898
}
9999

100-
impl<'a> FromIterator<Vec<&'a Pat>> for Matrix<'a> {
101-
fn from_iter<T: IntoIterator<Item=Vec<&'a Pat>>>(iter: T) -> Matrix<'a> {
100+
impl<'a, 'tcx> FromIterator<Vec<(&'a Pat, Option<Ty<'tcx>>)>> for Matrix<'a, 'tcx> {
101+
fn from_iter<T: IntoIterator<Item=Vec<(&'a Pat, Option<Ty<'tcx>>)>>>(iter: T)
102+
-> Self
103+
{
102104
Matrix(iter.into_iter().collect())
103105
}
104106
}
@@ -229,7 +231,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
229231
.iter()
230232
.filter(|&&(_, guard)| guard.is_none())
231233
.flat_map(|arm| &arm.0)
232-
.map(|pat| vec![&**pat])
234+
.map(|pat| vec![wrap_pat(cx, &pat)])
233235
.collect();
234236
check_exhaustive(cx, ex.span, &matrix, source);
235237
},
@@ -301,7 +303,7 @@ fn check_arms(cx: &MatchCheckCtxt,
301303
let mut printed_if_let_err = false;
302304
for &(ref pats, guard) in arms {
303305
for pat in pats {
304-
let v = vec![&**pat];
306+
let v = vec![wrap_pat(cx, &pat)];
305307

306308
match is_useful(cx, &seen, &v[..], LeaveOutWitness) {
307309
NotUseful => {
@@ -341,8 +343,9 @@ fn check_arms(cx: &MatchCheckCtxt,
341343
"unreachable pattern");
342344
// if we had a catchall pattern, hint at that
343345
for row in &seen.0 {
344-
if pat_is_catchall(&cx.tcx.def_map.borrow(), row[0]) {
345-
span_note!(err, row[0].span, "this pattern matches any value");
346+
if pat_is_catchall(&cx.tcx.def_map.borrow(), row[0].0) {
347+
span_note!(err, row[0].0.span,
348+
"this pattern matches any value");
346349
}
347350
}
348351
err.emit();
@@ -383,13 +386,16 @@ fn raw_pat(p: &Pat) -> &Pat {
383386
}
384387
}
385388

386-
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir::MatchSource) {
387-
match is_useful(cx, matrix, &[DUMMY_WILD_PAT], ConstructWitness) {
389+
fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
390+
sp: Span,
391+
matrix: &Matrix<'a, 'tcx>,
392+
source: hir::MatchSource) {
393+
match is_useful(cx, matrix, &[(DUMMY_WILD_PAT, None)], ConstructWitness) {
388394
UsefulWithWitness(pats) => {
389395
let witnesses = if pats.is_empty() {
390396
vec![DUMMY_WILD_PAT]
391397
} else {
392-
pats.iter().map(|w| &**w ).collect()
398+
pats.iter().map(|w| &**w).collect()
393399
};
394400
match source {
395401
hir::MatchSource::ForLoopDesugar => {
@@ -631,7 +637,7 @@ impl Constructor {
631637
fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
632638
left_ty: Ty, max_slice_length: usize) -> Vec<Constructor> {
633639
let used_constructors: Vec<Constructor> = rows.iter()
634-
.flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length))
640+
.flat_map(|row| pat_constructors(cx, row[0].0, left_ty, max_slice_length))
635641
.collect();
636642
all_constructors(cx, left_ty, max_slice_length)
637643
.into_iter()
@@ -668,13 +674,13 @@ fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty,
668674

669675
// Note: is_useful doesn't work on empty types, as the paper notes.
670676
// So it assumes that v is non-empty.
671-
fn is_useful(cx: &MatchCheckCtxt,
672-
matrix: &Matrix,
673-
v: &[&Pat],
674-
witness: WitnessPreference)
675-
-> Usefulness {
677+
fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
678+
matrix: &Matrix<'a, 'tcx>,
679+
v: &[(&Pat, Option<Ty<'tcx>>)],
680+
witness: WitnessPreference)
681+
-> Usefulness {
676682
let &Matrix(ref rows) = matrix;
677-
debug!("{:?}", matrix);
683+
debug!("is_useful({:?}, {:?})", matrix, v);
678684
if rows.is_empty() {
679685
return match witness {
680686
ConstructWitness => UsefulWithWitness(vec!()),
@@ -685,32 +691,25 @@ fn is_useful(cx: &MatchCheckCtxt,
685691
return NotUseful;
686692
}
687693
assert!(rows.iter().all(|r| r.len() == v.len()));
688-
let real_pat = match rows.iter().find(|r| (*r)[0].id != DUMMY_NODE_ID) {
689-
Some(r) => raw_pat(r[0]),
690-
None if v.is_empty() => return NotUseful,
691-
None => v[0]
692-
};
693-
let left_ty = if real_pat.id == DUMMY_NODE_ID {
694-
cx.tcx.mk_nil()
695-
} else {
696-
let left_ty = cx.tcx.pat_ty(&real_pat);
697-
698-
match real_pat.node {
699-
PatKind::Binding(hir::BindByRef(..), _, _) => {
700-
left_ty.builtin_deref(false, NoPreference).unwrap().ty
701-
}
702-
_ => left_ty,
694+
let left_ty = match rows.iter().filter_map(|r| r[0].1).next().or_else(|| v[0].1) {
695+
Some(ty) => ty,
696+
None => {
697+
// all patterns are wildcards - we can pick any type we want
698+
cx.tcx.types.bool
703699
}
704700
};
705701

706-
let max_slice_length = rows.iter().filter_map(|row| match row[0].node {
702+
let max_slice_length = rows.iter().filter_map(|row| match row[0].0.node {
707703
PatKind::Vec(ref before, _, ref after) => Some(before.len() + after.len()),
708704
_ => None
709705
}).max().map_or(0, |v| v + 1);
710706

711-
let constructors = pat_constructors(cx, v[0], left_ty, max_slice_length);
707+
let constructors = pat_constructors(cx, v[0].0, left_ty, max_slice_length);
708+
debug!("is_useful - pat_constructors = {:?} left_ty = {:?}", constructors,
709+
left_ty);
712710
if constructors.is_empty() {
713711
let constructors = missing_constructors(cx, matrix, left_ty, max_slice_length);
712+
debug!("is_useful - missing_constructors = {:?}", constructors);
714713
if constructors.is_empty() {
715714
all_constructors(cx, left_ty, max_slice_length).into_iter().map(|c| {
716715
match is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) {
@@ -731,7 +730,7 @@ fn is_useful(cx: &MatchCheckCtxt,
731730
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
732731
} else {
733732
let matrix = rows.iter().filter_map(|r| {
734-
match raw_pat(r[0]).node {
733+
match raw_pat(r[0].0).node {
735734
PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()),
736735
_ => None,
737736
}
@@ -756,9 +755,14 @@ fn is_useful(cx: &MatchCheckCtxt,
756755
}
757756
}
758757

759-
fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix,
760-
v: &[&Pat], ctor: Constructor, lty: Ty,
761-
witness: WitnessPreference) -> Usefulness {
758+
fn is_useful_specialized<'a, 'tcx>(
759+
cx: &MatchCheckCtxt<'a, 'tcx>,
760+
&Matrix(ref m): &Matrix<'a, 'tcx>,
761+
v: &[(&Pat, Option<Ty<'tcx>>)],
762+
ctor: Constructor,
763+
lty: Ty<'tcx>,
764+
witness: WitnessPreference) -> Usefulness
765+
{
762766
let arity = constructor_arity(cx, &ctor, lty);
763767
let matrix = Matrix(m.iter().filter_map(|r| {
764768
specialize(cx, &r[..], &ctor, 0, arity)
@@ -859,6 +863,19 @@ fn range_covered_by_constructor(ctor: &Constructor,
859863
}
860864
}
861865

866+
fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>,
867+
pat: &'a Pat)
868+
-> (&'a Pat, Option<Ty<'tcx>>)
869+
{
870+
let pat_ty = cx.tcx.pat_ty(pat);
871+
(pat, Some(match pat.node {
872+
PatKind::Binding(hir::BindByRef(..), _, _) => {
873+
pat_ty.builtin_deref(false, NoPreference).unwrap().ty
874+
}
875+
_ => pat_ty
876+
}))
877+
}
878+
862879
/// This is the main specialization step. It expands the first pattern in the given row
863880
/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
864881
/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
@@ -867,14 +884,22 @@ fn range_covered_by_constructor(ctor: &Constructor,
867884
/// different patterns.
868885
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
869886
/// fields filled with wild patterns.
870-
pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
871-
constructor: &Constructor, col: usize, arity: usize) -> Option<Vec<&'a Pat>> {
887+
pub fn specialize<'a, 'b, 'tcx>(
888+
cx: &MatchCheckCtxt<'b, 'tcx>,
889+
r: &[(&'a Pat, Option<Ty<'tcx>>)],
890+
constructor: &Constructor, col: usize, arity: usize)
891+
-> Option<Vec<(&'a Pat, Option<Ty<'tcx>>)>>
892+
{
893+
let pat = raw_pat(r[col].0);
872894
let &Pat {
873895
id: pat_id, ref node, span: pat_span
874-
} = raw_pat(r[col]);
875-
let head: Option<Vec<&Pat>> = match *node {
896+
} = pat;
897+
let wpat = |pat: &'a Pat| wrap_pat(cx, pat);
898+
let dummy_pat = (DUMMY_WILD_PAT, None);
899+
900+
let head: Option<Vec<(&Pat, Option<Ty>)>> = match *node {
876901
PatKind::Binding(..) | PatKind::Wild =>
877-
Some(vec![DUMMY_WILD_PAT; arity]),
902+
Some(vec![dummy_pat; arity]),
878903

879904
PatKind::Path(..) => {
880905
let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
@@ -899,12 +924,14 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
899924
Def::Variant(..) | Def::Struct(..) => {
900925
match ddpos {
901926
Some(ddpos) => {
902-
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| &**p).collect();
903-
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
904-
pats.extend(args[ddpos..].iter().map(|p| &**p));
927+
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| {
928+
wpat(p)
929+
}).collect();
930+
pats.extend(repeat((DUMMY_WILD_PAT, None)).take(arity - args.len()));
931+
pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
905932
Some(pats)
906933
}
907-
None => Some(args.iter().map(|p| &**p).collect())
934+
None => Some(args.iter().map(|p| wpat(p)).collect())
908935
}
909936
}
910937
_ => None
@@ -923,8 +950,8 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
923950
if variant.did == def_variant.did {
924951
Some(variant.fields.iter().map(|sf| {
925952
match pattern_fields.iter().find(|f| f.node.name == sf.name) {
926-
Some(ref f) => &*f.node.pat,
927-
_ => DUMMY_WILD_PAT
953+
Some(ref f) => wpat(&f.node.pat),
954+
_ => dummy_pat
928955
}
929956
}).collect())
930957
} else {
@@ -933,25 +960,32 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
933960
}
934961

935962
PatKind::Tuple(ref args, Some(ddpos)) => {
936-
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| &**p).collect();
937-
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
938-
pats.extend(args[ddpos..].iter().map(|p| &**p));
963+
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect();
964+
pats.extend(repeat(dummy_pat).take(arity - args.len()));
965+
pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
939966
Some(pats)
940967
}
941968
PatKind::Tuple(ref args, None) =>
942-
Some(args.iter().map(|p| &**p).collect()),
969+
Some(args.iter().map(|p| wpat(&**p)).collect()),
943970

944971
PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) =>
945-
Some(vec![&**inner]),
972+
Some(vec![wpat(&**inner)]),
946973

947974
PatKind::Lit(ref expr) => {
948-
let expr_value = eval_const_expr(cx.tcx, &expr);
949-
match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
950-
Some(true) => Some(vec![]),
951-
Some(false) => None,
952-
None => {
953-
span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
954-
None
975+
if let Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) = r[col].1 {
976+
// HACK: handle string literals. A string literal pattern
977+
// serves both as an unary reference pattern and as a
978+
// nullary value pattern, depending on the type.
979+
Some(vec![(pat, Some(mt.ty))])
980+
} else {
981+
let expr_value = eval_const_expr(cx.tcx, &expr);
982+
match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
983+
Some(true) => Some(vec![]),
984+
Some(false) => None,
985+
None => {
986+
span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
987+
None
988+
}
955989
}
956990
}
957991
}
@@ -975,37 +1009,40 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
9751009
Single => {
9761010
// Fixed-length vectors.
9771011
Some(
978-
before.iter().map(|p| &**p).chain(
979-
repeat(DUMMY_WILD_PAT).take(arity - pat_len).chain(
980-
after.iter().map(|p| &**p)
1012+
before.iter().map(|p| wpat(p)).chain(
1013+
repeat(dummy_pat).take(arity - pat_len).chain(
1014+
after.iter().map(|p| wpat(p))
9811015
)).collect())
9821016
},
9831017
Slice(length) if pat_len <= length && slice.is_some() => {
9841018
Some(
985-
before.iter().map(|p| &**p).chain(
986-
repeat(DUMMY_WILD_PAT).take(arity - pat_len).chain(
987-
after.iter().map(|p| &**p)
1019+
before.iter().map(|p| wpat(p)).chain(
1020+
repeat(dummy_pat).take(arity - pat_len).chain(
1021+
after.iter().map(|p| wpat(p))
9881022
)).collect())
9891023
}
9901024
Slice(length) if pat_len == length => {
9911025
Some(
992-
before.iter().map(|p| &**p).chain(
993-
after.iter().map(|p| &**p)
1026+
before.iter().map(|p| wpat(p)).chain(
1027+
after.iter().map(|p| wpat(p))
9941028
).collect())
9951029
}
9961030
SliceWithSubslice(prefix, suffix)
9971031
if before.len() == prefix
9981032
&& after.len() == suffix
9991033
&& slice.is_some() => {
10001034
// this is used by trans::_match only
1001-
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
1002-
pats.extend(after.iter().map(|p| &**p));
1035+
let mut pats: Vec<_> = before.iter()
1036+
.map(|p| (&**p, None)).collect();
1037+
pats.extend(after.iter().map(|p| (&**p, None)));
10031038
Some(pats)
10041039
}
10051040
_ => None
10061041
}
10071042
}
10081043
};
1044+
debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head);
1045+
10091046
head.map(|mut head| {
10101047
head.extend_from_slice(&r[..col]);
10111048
head.extend_from_slice(&r[col + 1..]);
@@ -1063,8 +1100,8 @@ fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) {
10631100
fn is_refutable<A, F>(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option<A> where
10641101
F: FnOnce(&Pat) -> A,
10651102
{
1066-
let pats = Matrix(vec!(vec!(pat)));
1067-
match is_useful(cx, &pats, &[DUMMY_WILD_PAT], ConstructWitness) {
1103+
let pats = Matrix(vec!(vec!(wrap_pat(cx, pat))));
1104+
match is_useful(cx, &pats, &[(DUMMY_WILD_PAT, None)], ConstructWitness) {
10681105
UsefulWithWitness(pats) => Some(refutable(&pats[0])),
10691106
NotUseful => None,
10701107
Useful => bug!()

0 commit comments

Comments
 (0)