Skip to content

Commit 355abcb

Browse files
committed
mir: Match against slices by calling PartialEq::eq.
1 parent 8d3e13c commit 355abcb

File tree

2 files changed

+99
-24
lines changed

2 files changed

+99
-24
lines changed

src/librustc_mir/build/matches/test.rs

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -174,33 +174,78 @@ impl<'a,'tcx> Builder<'a,'tcx> {
174174
targets
175175
}
176176

177-
TestKind::Eq { ref value, ty } => {
178-
// If we're matching against &[u8] with b"...", we need to insert
179-
// an unsizing coercion, as the byte string has type &[u8; N].
180-
let expect = match *value {
181-
ConstVal::ByteStr(ref bytes) if ty.is_slice() => {
182-
let tcx = self.hir.tcx();
183-
let array_ty = tcx.mk_array(tcx.types.u8, bytes.len());
184-
let ref_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), array_ty);
185-
let array = self.literal_operand(test.span, ref_ty, Literal::Value {
186-
value: value.clone()
187-
});
188-
189-
let sliced = self.temp(ty);
190-
self.cfg.push_assign(block, test.span, &sliced,
191-
Rvalue::Cast(CastKind::Unsize, array, ty));
192-
Operand::Consume(sliced)
193-
}
194-
_ => {
195-
self.literal_operand(test.span, ty, Literal::Value {
196-
value: value.clone()
197-
})
177+
TestKind::Eq { ref value, mut ty } => {
178+
let mut val = Operand::Consume(lvalue.clone());
179+
180+
// If we're using b"..." as a pattern, we need to insert an
181+
// unsizing coercion, as the byte string has the type &[u8; N].
182+
let expect = if let ConstVal::ByteStr(ref bytes) = *value {
183+
let tcx = self.hir.tcx();
184+
185+
// Unsize the lvalue to &[u8], too, if necessary.
186+
if let ty::TyRef(region, mt) = ty.sty {
187+
if let ty::TyArray(_, _) = mt.ty.sty {
188+
ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
189+
let val_slice = self.temp(ty);
190+
self.cfg.push_assign(block, test.span, &val_slice,
191+
Rvalue::Cast(CastKind::Unsize, val, ty));
192+
val = Operand::Consume(val_slice);
193+
}
198194
}
195+
196+
assert!(ty.is_slice());
197+
198+
let array_ty = tcx.mk_array(tcx.types.u8, bytes.len());
199+
let array_ref = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), array_ty);
200+
let array = self.literal_operand(test.span, array_ref, Literal::Value {
201+
value: value.clone()
202+
});
203+
204+
let slice = self.temp(ty);
205+
self.cfg.push_assign(block, test.span, &slice,
206+
Rvalue::Cast(CastKind::Unsize, array, ty));
207+
Operand::Consume(slice)
208+
} else {
209+
self.literal_operand(test.span, ty, Literal::Value {
210+
value: value.clone()
211+
})
199212
};
200-
let val = Operand::Consume(lvalue.clone());
213+
214+
// Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq.
201215
let fail = self.cfg.start_new_block();
202-
let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val.clone());
203-
vec![block, fail]
216+
if let ty::TyRef(_, mt) = ty.sty {
217+
assert!(ty.is_slice());
218+
let eq_def_id = self.hir.tcx().lang_items.eq_trait().unwrap();
219+
let ty = mt.ty;
220+
let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, vec![ty]);
221+
222+
let bool_ty = self.hir.bool_ty();
223+
let eq_result = self.temp(bool_ty);
224+
let eq_block = self.cfg.start_new_block();
225+
let cleanup = self.diverge_cleanup();
226+
self.cfg.terminate(block, Terminator::Call {
227+
func: Operand::Constant(Constant {
228+
span: test.span,
229+
ty: mty,
230+
literal: method
231+
}),
232+
args: vec![val, expect],
233+
destination: Some((eq_result.clone(), eq_block)),
234+
cleanup: cleanup,
235+
});
236+
237+
// check the result
238+
let block = self.cfg.start_new_block();
239+
self.cfg.terminate(eq_block, Terminator::If {
240+
cond: Operand::Consume(eq_result),
241+
targets: (block, fail),
242+
});
243+
244+
vec![block, fail]
245+
} else {
246+
let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
247+
vec![block, fail]
248+
}
204249
}
205250

206251
TestKind::Range { ref lo, ref hi, ty } => {

src/librustc_mir/hair/cx/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use hair::*;
1919
use rustc::mir::repr::*;
2020

2121
use rustc::middle::const_eval::{self, ConstVal};
22+
use rustc::middle::def_id::DefId;
2223
use rustc::middle::infer::InferCtxt;
24+
use rustc::middle::subst::{Subst, Substs};
2325
use rustc::middle::ty::{self, Ty, TyCtxt};
2426
use syntax::codemap::Span;
2527
use syntax::parse::token;
@@ -92,6 +94,34 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
9294
})
9395
}
9496

97+
pub fn trait_method(&mut self,
98+
trait_def_id: DefId,
99+
method_name: &str,
100+
self_ty: Ty<'tcx>,
101+
params: Vec<Ty<'tcx>>)
102+
-> (Ty<'tcx>, Literal<'tcx>) {
103+
let method_name = token::intern(method_name);
104+
let substs = Substs::new_trait(params, vec![], self_ty);
105+
for trait_item in self.tcx.trait_items(trait_def_id).iter() {
106+
match *trait_item {
107+
ty::ImplOrTraitItem::MethodTraitItem(ref method) => {
108+
if method.name == method_name {
109+
let method_ty = self.tcx.lookup_item_type(method.def_id);
110+
let method_ty = method_ty.ty.subst(self.tcx, &substs);
111+
return (method_ty, Literal::Item {
112+
def_id: method.def_id,
113+
substs: self.tcx.mk_substs(substs),
114+
});
115+
}
116+
}
117+
ty::ImplOrTraitItem::ConstTraitItem(..) |
118+
ty::ImplOrTraitItem::TypeTraitItem(..) => {}
119+
}
120+
}
121+
122+
self.tcx.sess.bug(&format!("found no method `{}` in `{:?}`", method_name, trait_def_id));
123+
}
124+
95125
pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize {
96126
adt_def.variants.len()
97127
}

0 commit comments

Comments
 (0)