use build::matches::{Candidate, MatchPair, Test, TestKind};
use hair::*;
use rustc_data_structures::fnv::FnvHashMap;
-use rustc::middle::const_eval::ConstVal;
-use rustc::middle::ty::{self, Ty};
+use rustc::middle::const_val::ConstVal;
+use rustc::ty::{self, Ty};
use rustc::mir::repr::*;
use syntax::codemap::Span;
}
}
- PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
+ PatternKind::Slice { ref prefix, ref slice, ref suffix }
+ if !match_pair.slice_len_checked => {
let len = prefix.len() + suffix.len();
let op = if slice.is_some() {
BinOp::Ge
};
Test {
span: match_pair.pattern.span,
- kind: TestKind::Len { len: len, op: op },
+ kind: TestKind::Len { len: len as u64, op: op },
}
}
PatternKind::Array { .. } |
+ PatternKind::Slice { .. } |
PatternKind::Wild |
PatternKind::Binding { .. } |
PatternKind::Leaf { .. } |
lvalue: &Lvalue<'tcx>,
test: &Test<'tcx>)
-> Vec<BasicBlock> {
+ let scope_id = self.innermost_scope_id();
match test.kind {
TestKind::Switch { adt_def } => {
let num_enum_variants = self.hir.num_variants(adt_def);
let target_blocks: Vec<_> =
(0..num_enum_variants).map(|_| self.cfg.start_new_block())
.collect();
- self.cfg.terminate(block, Terminator::Switch {
+ self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch {
discr: lvalue.clone(),
adt_def: adt_def,
targets: target_blocks.clone()
.map(|_| self.cfg.start_new_block())
.chain(Some(otherwise))
.collect();
- self.cfg.terminate(block, Terminator::SwitchInt {
- discr: lvalue.clone(),
- switch_ty: switch_ty,
- values: options.clone(),
- targets: targets.clone(),
- });
+ self.cfg.terminate(block,
+ scope_id,
+ test.span,
+ TerminatorKind::SwitchInt {
+ discr: lvalue.clone(),
+ switch_ty: switch_ty,
+ values: options.clone(),
+ targets: targets.clone(),
+ });
targets
}
- TestKind::Eq { ref value, ty } => {
- let expect = self.literal_operand(test.span, ty.clone(), Literal::Value {
- value: value.clone()
- });
- let val = Operand::Consume(lvalue.clone());
+ TestKind::Eq { ref value, mut ty } => {
+ let mut val = Operand::Consume(lvalue.clone());
+
+ // If we're using b"..." as a pattern, we need to insert an
+ // unsizing coercion, as the byte string has the type &[u8; N].
+ let expect = if let ConstVal::ByteStr(ref bytes) = *value {
+ let tcx = self.hir.tcx();
+
+ // Unsize the lvalue to &[u8], too, if necessary.
+ if let ty::TyRef(region, mt) = ty.sty {
+ if let ty::TyArray(_, _) = mt.ty.sty {
+ ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
+ let val_slice = self.temp(ty);
+ self.cfg.push_assign(block, scope_id, test.span, &val_slice,
+ Rvalue::Cast(CastKind::Unsize, val, ty));
+ val = Operand::Consume(val_slice);
+ }
+ }
+
+ assert!(ty.is_slice());
+
+ let array_ty = tcx.mk_array(tcx.types.u8, bytes.len());
+ let array_ref = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), array_ty);
+ let array = self.literal_operand(test.span, array_ref, Literal::Value {
+ value: value.clone()
+ });
+
+ let slice = self.temp(ty);
+ self.cfg.push_assign(block, scope_id, test.span, &slice,
+ Rvalue::Cast(CastKind::Unsize, array, ty));
+ Operand::Consume(slice)
+ } else {
+ self.literal_operand(test.span, ty, Literal::Value {
+ value: value.clone()
+ })
+ };
+
+ // Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq.
let fail = self.cfg.start_new_block();
- let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val.clone());
- vec![block, fail]
+ if let ty::TyRef(_, mt) = ty.sty {
+ assert!(ty.is_slice());
+ let eq_def_id = self.hir.tcx().lang_items.eq_trait().unwrap();
+ let ty = mt.ty;
+ let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, vec![ty]);
+
+ let bool_ty = self.hir.bool_ty();
+ let eq_result = self.temp(bool_ty);
+ let eq_block = self.cfg.start_new_block();
+ let cleanup = self.diverge_cleanup();
+ self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Call {
+ func: Operand::Constant(Constant {
+ span: test.span,
+ ty: mty,
+ literal: method
+ }),
+ args: vec![val, expect],
+ destination: Some((eq_result.clone(), eq_block)),
+ cleanup: cleanup,
+ });
+
+ // check the result
+ let block = self.cfg.start_new_block();
+ self.cfg.terminate(eq_block, scope_id, test.span, TerminatorKind::If {
+ cond: Operand::Consume(eq_result),
+ targets: (block, fail),
+ });
+
+ vec![block, fail]
+ } else {
+ let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
+ vec![block, fail]
+ }
}
TestKind::Range { ref lo, ref hi, ty } => {
let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
// actual = len(lvalue)
- self.cfg.push_assign(block, test.span, &actual, Rvalue::Len(lvalue.clone()));
+ self.cfg.push_assign(block, scope_id, test.span,
+ &actual, Rvalue::Len(lvalue.clone()));
// expected = <N>
- let expected = self.push_usize(block, test.span, len);
+ let expected = self.push_usize(block, scope_id, test.span, len);
// result = actual == expected OR result = actual < expected
self.cfg.push_assign(block,
+ scope_id,
test.span,
&result,
Rvalue::BinaryOp(op,
// branch based on result
let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
self.cfg.start_new_block()];
- self.cfg.terminate(block, Terminator::If {
+ self.cfg.terminate(block, scope_id, test.span, TerminatorKind::If {
cond: Operand::Consume(result),
targets: (target_blocks[0], target_blocks[1])
});
let result = self.temp(bool_ty);
// result = op(left, right)
- self.cfg.push_assign(block, span, &result, Rvalue::BinaryOp(op, left, right));
+ let scope_id = self.innermost_scope_id();
+ self.cfg.push_assign(block, scope_id, span, &result,
+ Rvalue::BinaryOp(op, left, right));
// branch based on result
let target_block = self.cfg.start_new_block();
- self.cfg.terminate(block, Terminator::If {
+ self.cfg.terminate(block, scope_id, span, TerminatorKind::If {
cond: Operand::Consume(result),
targets: (target_block, fail_block)
});
}
}
- TestKind::Eq { .. } |
- TestKind::Range { .. } |
+ // If we are performing a length check, then this
+ // informs slice patterns, but nothing else.
TestKind::Len { .. } => {
+ let pattern_test = self.test(&match_pair);
+ match *match_pair.pattern.kind {
+ PatternKind::Slice { .. } if pattern_test.kind == test.kind => {
+ let mut new_candidate = candidate.clone();
+
+ // Set up the MatchKind to simplify this like an array.
+ new_candidate.match_pairs[match_pair_index]
+ .slice_len_checked = true;
+ resulting_candidates[0].push(new_candidate);
+ true
+ }
+ _ => false
+ }
+ }
+
+ TestKind::Eq { .. } |
+ TestKind::Range { .. } => {
// These are all binary tests.
//
// FIXME(#29623) we can be more clever here
.map(|(_, mp)| mp.clone())
.collect();
Candidate {
+ span: candidate.span,
match_pairs: other_match_pairs,
bindings: candidate.bindings.clone(),
guard: candidate.guard.clone(),
.map(|subpattern| {
// e.g., `(x as Variant).0`
let lvalue = downcast_lvalue.clone().field(subpattern.field,
- subpattern.field_ty());
+ subpattern.pattern.ty);
// e.g., `(x as Variant).0 @ P1`
MatchPair::new(lvalue, &subpattern.pattern)
});
let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
Candidate {
+ span: candidate.span,
match_pairs: all_match_pairs,
bindings: candidate.bindings.clone(),
guard: candidate.guard.clone(),
}
fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
- self.hir.span_bug(match_pair.pattern.span,
- &format!("simplifyable pattern found: {:?}", match_pair.pattern))
+ span_bug!(match_pair.pattern.span,
+ "simplifyable pattern found: {:?}",
+ match_pair.pattern)
}
}