Skip to content

Commit 8d3e89b

Browse files
committed
handle mixed byte literal and byte array patterns
Convert byte literal pattern to byte array patterns when they are both used together. so matching them is properly handled. I could've done the conversion eagerly, but that could have caused a bad worst-case for massive byte-array matches. Fixes #18027. Fixes #25051. Fixes #26510.
1 parent 76fb7d9 commit 8d3e89b

File tree

4 files changed

+272
-50
lines changed

4 files changed

+272
-50
lines changed

src/librustc_const_eval/_match.rs

+140-45
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ use self::WitnessPreference::*;
1515
use rustc::middle::const_val::ConstVal;
1616
use eval::{compare_const_vals};
1717

18+
use rustc_const_math::ConstInt;
19+
20+
use rustc_data_structures::fnv::FnvHashMap;
1821
use rustc_data_structures::indexed_vec::Idx;
1922

2023
use pattern::{FieldPattern, Pattern, PatternKind};
@@ -157,6 +160,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
157160
/// associated types to get field types.
158161
pub wild_pattern: &'a Pattern<'tcx>,
159162
pub pattern_arena: &'a TypedArena<Pattern<'tcx>>,
163+
pub byte_array_map: FnvHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>,
160164
}
161165

162166
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
@@ -177,8 +181,31 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
177181
tcx: tcx,
178182
wild_pattern: &wild_pattern,
179183
pattern_arena: &pattern_arena,
184+
byte_array_map: FnvHashMap(),
180185
})
181186
}
187+
188+
// convert a byte-string pattern to a list of u8 patterns.
189+
fn lower_byte_str_pattern(&mut self, pat: &'a Pattern<'tcx>) -> Vec<&'a Pattern<'tcx>> {
190+
let pattern_arena = &*self.pattern_arena;
191+
let tcx = self.tcx;
192+
self.byte_array_map.entry(pat).or_insert_with(|| {
193+
match pat.kind {
194+
box PatternKind::Constant {
195+
value: ConstVal::ByteStr(ref data)
196+
} => {
197+
data.iter().map(|c| &*pattern_arena.alloc(Pattern {
198+
ty: tcx.types.u8,
199+
span: pat.span,
200+
kind: box PatternKind::Constant {
201+
value: ConstVal::Integral(ConstInt::U8(*c))
202+
}
203+
})).collect()
204+
}
205+
_ => span_bug!(pat.span, "unexpected byte array pattern {:?}", pat)
206+
}
207+
}).clone()
208+
}
182209
}
183210

184211
#[derive(Clone, Debug, PartialEq)]
@@ -357,7 +384,8 @@ impl Witness {
357384
/// Therefore, if there is some pattern that is unmatched by `matrix`, it will
358385
/// still be unmatched if the first constructor is replaced by any of the constructors
359386
/// in the return value.
360-
fn missing_constructors(cx: &MatchCheckCtxt, matrix: &Matrix,
387+
fn missing_constructors(cx: &mut MatchCheckCtxt,
388+
matrix: &Matrix,
361389
pcx: PatternContext) -> Vec<Constructor> {
362390
let used_constructors: Vec<Constructor> =
363391
matrix.0.iter()
@@ -371,14 +399,20 @@ fn missing_constructors(cx: &MatchCheckCtxt, matrix: &Matrix,
371399

372400
/// This determines the set of all possible constructors of a pattern matching
373401
/// values of type `left_ty`. For vectors, this would normally be an infinite set
402+
///
403+
/// This intentionally does not list ConstantValue specializations for
404+
/// non-booleans, because we currently assume that there is always a
405+
/// "non-standard constant" that matches. See issue #12483.
406+
///
374407
/// but is instead bounded by the maximum fixed length of slice patterns in
375408
/// the column of patterns being analyzed.
376-
fn all_constructors(_cx: &MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
409+
fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
377410
match pcx.ty.sty {
378411
ty::TyBool =>
379412
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
380413
ty::TySlice(_) =>
381414
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(),
415+
ty::TyArray(_, length) => vec![Slice(length)],
382416
ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 =>
383417
def.variants.iter().map(|v| Variant(v.did)).collect(),
384418
_ => vec![Single]
@@ -398,7 +432,7 @@ fn all_constructors(_cx: &MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructo
398432
///
399433
/// Note: is_useful doesn't work on empty types, as the paper notes.
400434
/// So it assumes that v is non-empty.
401-
pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
435+
pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
402436
matrix: &Matrix<'a, 'tcx>,
403437
v: &[&'a Pattern<'tcx>],
404438
witness: WitnessPreference)
@@ -416,19 +450,22 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
416450
if rows[0].is_empty() {
417451
return NotUseful;
418452
}
419-
assert!(rows.iter().all(|r| r.len() == v.len()));
420453

454+
let &Matrix(ref rows) = matrix;
455+
assert!(rows.iter().all(|r| r.len() == v.len()));
421456
let pcx = PatternContext {
422457
ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error())
423458
.unwrap_or(v[0].ty),
424459
max_slice_length: rows.iter().filter_map(|row| match *row[0].kind {
425460
PatternKind::Slice { ref prefix, slice: _, ref suffix } =>
426461
Some(prefix.len() + suffix.len()),
462+
PatternKind::Constant { value: ConstVal::ByteStr(ref data) } =>
463+
Some(data.len()),
427464
_ => None
428465
}).max().map_or(0, |v| v + 1)
429466
};
430467

431-
debug!("is_useful: pcx={:?}, expanding {:?}", pcx, v[0]);
468+
debug!("is_useful_expand_first_col: pcx={:?}, expanding {:?}", pcx, v[0]);
432469

433470
if let Some(constructors) = pat_constructors(cx, v[0], pcx) {
434471
debug!("is_useful - expanding constructors: {:?}", constructors);
@@ -453,6 +490,7 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
453490
}).collect();
454491
match is_useful(cx, &matrix, &v[1..], witness) {
455492
UsefulWithWitness(pats) => {
493+
let cx = &*cx;
456494
UsefulWithWitness(pats.into_iter().flat_map(|witness| {
457495
constructors.iter().map(move |ctor| {
458496
witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
@@ -466,15 +504,15 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
466504
}
467505

468506
fn is_useful_specialized<'a, 'tcx>(
469-
cx: &MatchCheckCtxt<'a, 'tcx>,
507+
cx: &mut MatchCheckCtxt<'a, 'tcx>,
470508
&Matrix(ref m): &Matrix<'a, 'tcx>,
471509
v: &[&'a Pattern<'tcx>],
472510
ctor: Constructor,
473511
lty: Ty<'tcx>,
474512
witness: WitnessPreference) -> Usefulness
475513
{
476514
let arity = constructor_arity(cx, &ctor, lty);
477-
let matrix = Matrix(m.iter().filter_map(|r| {
515+
let matrix = Matrix(m.iter().flat_map(|r| {
478516
specialize(cx, &r[..], &ctor, 0, arity)
479517
}).collect());
480518
match specialize(cx, v, &ctor, 0, arity) {
@@ -498,22 +536,26 @@ fn is_useful_specialized<'a, 'tcx>(
498536
/// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on.
499537
///
500538
/// Returns None in case of a catch-all, which can't be specialized.
501-
fn pat_constructors(_cx: &MatchCheckCtxt,
539+
fn pat_constructors(_cx: &mut MatchCheckCtxt,
502540
pat: &Pattern,
503541
pcx: PatternContext)
504542
-> Option<Vec<Constructor>>
505543
{
506544
match *pat.kind {
507545
PatternKind::Binding { .. } | PatternKind::Wild =>
508546
None,
509-
PatternKind::Leaf { .. } | PatternKind::Deref { .. } | PatternKind::Array { .. } =>
547+
PatternKind::Leaf { .. } | PatternKind::Deref { .. } =>
510548
Some(vec![Single]),
511549
PatternKind::Variant { adt_def, variant_index, .. } =>
512550
Some(vec![Variant(adt_def.variants[variant_index].did)]),
513551
PatternKind::Constant { ref value } =>
514552
Some(vec![ConstantValue(value.clone())]),
515553
PatternKind::Range { ref lo, ref hi } =>
516554
Some(vec![ConstantRange(lo.clone(), hi.clone())]),
555+
PatternKind::Array { .. } => match pcx.ty.sty {
556+
ty::TyArray(_, length) => Some(vec![Slice(length)]),
557+
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty)
558+
},
517559
PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
518560
let pat_len = prefix.len() + suffix.len();
519561
if slice.is_some() {
@@ -535,23 +577,55 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize
535577
match ty.sty {
536578
ty::TyTuple(ref fs) => fs.len(),
537579
ty::TyBox(_) => 1,
538-
ty::TySlice(_) => match *ctor {
580+
ty::TySlice(..) | ty::TyArray(..) => match *ctor {
539581
Slice(length) => length,
540-
ConstantValue(_) => {
541-
// TODO: this is utterly wrong, but required for byte arrays
542-
0
543-
}
582+
ConstantValue(_) => 0,
544583
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty)
545584
},
546585
ty::TyRef(..) => 1,
547586
ty::TyAdt(adt, _) => {
548587
ctor.variant_for_adt(adt).fields.len()
549588
}
550-
ty::TyArray(_, n) => n,
551589
_ => 0
552590
}
553591
}
554592

593+
fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span,
594+
ctor: &Constructor,
595+
prefix: &[Pattern],
596+
slice: &Option<Pattern>,
597+
suffix: &[Pattern])
598+
-> Result<bool, ErrorReported> {
599+
let data = match *ctor {
600+
ConstantValue(ConstVal::ByteStr(ref data)) => data,
601+
_ => bug!()
602+
};
603+
604+
let pat_len = prefix.len() + suffix.len();
605+
if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) {
606+
return Ok(false);
607+
}
608+
609+
for (ch, pat) in
610+
data[..prefix.len()].iter().zip(prefix).chain(
611+
data[data.len()-suffix.len()..].iter().zip(suffix))
612+
{
613+
match pat.kind {
614+
box PatternKind::Constant { ref value } => match *value {
615+
ConstVal::Integral(ConstInt::U8(u)) => {
616+
if u != *ch {
617+
return Ok(false);
618+
}
619+
},
620+
_ => span_bug!(pat.span, "bad const u8 {:?}", value)
621+
},
622+
_ => {}
623+
}
624+
}
625+
626+
Ok(true)
627+
}
628+
555629
fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
556630
ctor: &Constructor,
557631
from: &ConstVal, to: &ConstVal)
@@ -568,7 +642,7 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
568642
}
569643

570644
fn patterns_for_variant<'a, 'tcx>(
571-
cx: &MatchCheckCtxt<'a, 'tcx>,
645+
cx: &mut MatchCheckCtxt<'a, 'tcx>,
572646
subpatterns: &'a [FieldPattern<'tcx>],
573647
arity: usize)
574648
-> Vec<&'a Pattern<'tcx>>
@@ -592,7 +666,7 @@ fn patterns_for_variant<'a, 'tcx>(
592666
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
593667
/// fields filled with wild patterns.
594668
fn specialize<'a, 'tcx>(
595-
cx: &MatchCheckCtxt<'a, 'tcx>,
669+
cx: &mut MatchCheckCtxt<'a, 'tcx>,
596670
r: &[&'a Pattern<'tcx>],
597671
constructor: &Constructor, col: usize, arity: usize)
598672
-> Option<Vec<&'a Pattern<'tcx>>>
@@ -616,13 +690,27 @@ fn specialize<'a, 'tcx>(
616690
PatternKind::Deref { ref subpattern } => Some(vec![subpattern]),
617691

618692
PatternKind::Constant { ref value } => {
619-
assert_eq!(constructor_arity(cx, constructor, pat.ty), 0);
620-
match range_covered_by_constructor(
621-
cx.tcx, pat.span, constructor, value, value
622-
) {
623-
Ok(true) => Some(vec![]),
624-
Ok(false) => None,
625-
Err(ErrorReported) => None,
693+
match *constructor {
694+
Slice(..) => match *value {
695+
ConstVal::ByteStr(ref data) => {
696+
if arity == data.len() {
697+
Some(cx.lower_byte_str_pattern(pat))
698+
} else {
699+
None
700+
}
701+
}
702+
_ => span_bug!(pat.span,
703+
"unexpected const-val {:?} with ctor {:?}", value, constructor)
704+
},
705+
_ => {
706+
match range_covered_by_constructor(
707+
cx.tcx, pat.span, constructor, value, value
708+
) {
709+
Ok(true) => Some(vec![]),
710+
Ok(false) => None,
711+
Err(ErrorReported) => None,
712+
}
713+
}
626714
}
627715
}
628716

@@ -636,29 +724,36 @@ fn specialize<'a, 'tcx>(
636724
}
637725
}
638726

639-
PatternKind::Array { ref prefix, slice: _, ref suffix } => {
640-
let pat_len = prefix.len() + suffix.len();
641-
Some(
642-
prefix.iter().chain(
643-
repeat(cx.wild_pattern).take(arity - pat_len).chain(
644-
suffix.iter()
645-
)).collect())
646-
}
647-
727+
PatternKind::Array { ref prefix, ref slice, ref suffix } |
648728
PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
649-
let pat_len = prefix.len() + suffix.len();
650-
if let Some(slice_count) = arity.checked_sub(pat_len) {
651-
if slice_count == 0 || slice.is_some() {
652-
Some(
653-
prefix.iter().chain(
654-
repeat(cx.wild_pattern).take(slice_count).chain(
655-
suffix.iter()
656-
)).collect())
657-
} else {
658-
None
729+
match *constructor {
730+
Slice(..) => {
731+
let pat_len = prefix.len() + suffix.len();
732+
if let Some(slice_count) = arity.checked_sub(pat_len) {
733+
if slice_count == 0 || slice.is_some() {
734+
Some(
735+
prefix.iter().chain(
736+
repeat(cx.wild_pattern).take(slice_count).chain(
737+
suffix.iter()
738+
)).collect())
739+
} else {
740+
None
741+
}
742+
} else {
743+
None
744+
}
659745
}
660-
} else {
661-
None
746+
ConstantValue(..) => {
747+
match slice_pat_covered_by_constructor(
748+
cx.tcx, pat.span, constructor, prefix, slice, suffix
749+
) {
750+
Ok(true) => Some(vec![]),
751+
Ok(false) => None,
752+
Err(ErrorReported) => None
753+
}
754+
}
755+
_ => span_bug!(pat.span,
756+
"unexpected ctor {:?} for slice pat", constructor)
662757
}
663758
}
664759
};

src/librustc_const_eval/check_match.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
175175
}
176176
}
177177

178-
MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| {
178+
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
179179
let mut have_errors = false;
180180

181181
let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
@@ -235,7 +235,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
235235
"local binding"
236236
};
237237

238-
MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| {
238+
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
239239
let mut patcx = PatternContext::new(self.tcx);
240240
let pats : Matrix = vec![vec![
241241
expand_pattern(cx, patcx.lower_pattern(pat))
@@ -302,8 +302,8 @@ fn pat_is_catchall(dm: &DefMap, pat: &Pat) -> bool {
302302
}
303303

304304
// Check for unreachable patterns
305-
fn check_arms<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
306-
arms: &[(Vec<(&Pattern<'tcx>, &'a hir::Pat)>, Option<&hir::Expr>)],
305+
fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
306+
arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
307307
source: hir::MatchSource)
308308
{
309309
let mut seen = Matrix::empty();
@@ -381,7 +381,7 @@ fn check_arms<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
381381
}
382382
}
383383

384-
fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
384+
fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
385385
sp: Span,
386386
matrix: &Matrix<'a, 'tcx>,
387387
source: hir::MatchSource) {

0 commit comments

Comments
 (0)