Skip to content

Commit 5965b79

Browse files
committed
Auto merge of #47503 - arielb1:check-size, r=eddyb
avoid double-unsizing arrays in bytestring match lowering The match lowering code, when lowering matches against bytestrings, works by coercing both the scrutinee and the pattern to `&[u8]` and then comparing them using `<[u8] as Eq>::eq`. If the scrutinee is already of type `&[u8]`, then unsizing it is both unneccessary and a trait error caught by the new and updated MIR typeck, so this PR changes lowering to avoid doing that (match lowering tried to avoid that before, but that attempt was quite broken). Fixes #46920. r? @eddyb
2 parents b71cbd8 + 06e32d0 commit 5965b79

File tree

2 files changed

+88
-23
lines changed

2 files changed

+88
-23
lines changed

src/librustc_mir/build/matches/test.rs

+51-23
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,50 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
174174
}
175175
}
176176

177+
/// Convert a byte array or byte slice to a byte slice.
178+
fn to_slice_operand(&mut self,
179+
block: BasicBlock,
180+
source_info: SourceInfo,
181+
operand: Operand<'tcx>)
182+
-> Operand<'tcx>
183+
{
184+
let tcx = self.hir.tcx();
185+
let ty = operand.ty(&self.local_decls, tcx);
186+
debug!("to_slice_operand({:?}, {:?}: {:?})", block, operand, ty);
187+
match ty.sty {
188+
ty::TyRef(region, mt) => match mt.ty.sty {
189+
ty::TyArray(ety, _) => {
190+
let ty = tcx.mk_imm_ref(region, tcx.mk_slice(ety));
191+
let temp = self.temp(ty, source_info.span);
192+
self.cfg.push_assign(block, source_info, &temp,
193+
Rvalue::Cast(CastKind::Unsize, operand, ty));
194+
Operand::Move(temp)
195+
}
196+
ty::TySlice(_) => operand,
197+
_ => {
198+
span_bug!(source_info.span,
199+
"bad operand {:?}: {:?} to `to_slice_operand`", operand, ty)
200+
}
201+
}
202+
_ => {
203+
span_bug!(source_info.span,
204+
"bad operand {:?}: {:?} to `to_slice_operand`", operand, ty)
205+
}
206+
}
207+
208+
}
209+
177210
/// Generates the code to perform a test.
178211
pub fn perform_test(&mut self,
179212
block: BasicBlock,
180213
place: &Place<'tcx>,
181214
test: &Test<'tcx>)
182215
-> Vec<BasicBlock> {
216+
debug!("perform_test({:?}, {:?}: {:?}, {:?})",
217+
block,
218+
place,
219+
place.ty(&self.local_decls, self.hir.tcx()),
220+
test);
183221
let source_info = self.source_info(test.span);
184222
match test.kind {
185223
TestKind::Switch { adt_def, ref variants } => {
@@ -258,45 +296,35 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
258296
ret
259297
}
260298

261-
TestKind::Eq { value, mut ty } => {
299+
TestKind::Eq { value, ty } => {
300+
let tcx = self.hir.tcx();
262301
let mut val = Operand::Copy(place.clone());
263302

264303
// If we're using b"..." as a pattern, we need to insert an
265304
// unsizing coercion, as the byte string has the type &[u8; N].
266-
let expect = if let ConstVal::ByteStr(bytes) = value.val {
267-
let tcx = self.hir.tcx();
268-
269-
// Unsize the place to &[u8], too, if necessary.
270-
if let ty::TyRef(region, mt) = ty.sty {
271-
if let ty::TyArray(_, _) = mt.ty.sty {
272-
ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
273-
let val_slice = self.temp(ty, test.span);
274-
self.cfg.push_assign(block, source_info, &val_slice,
275-
Rvalue::Cast(CastKind::Unsize, val, ty));
276-
val = Operand::Move(val_slice);
277-
}
278-
}
279-
280-
assert!(ty.is_slice());
281-
305+
//
306+
// We want to do this even when the scrutinee is a reference to an
307+
// array, so we can call `<[u8]>::eq` rather than having to find an
308+
// `<[u8; N]>::eq`.
309+
let (expect, val) = if let ConstVal::ByteStr(bytes) = value.val {
282310
let array_ty = tcx.mk_array(tcx.types.u8, bytes.data.len() as u64);
283311
let array_ref = tcx.mk_imm_ref(tcx.types.re_static, array_ty);
284312
let array = self.literal_operand(test.span, array_ref, Literal::Value {
285313
value
286314
});
287315

288-
let slice = self.temp(ty, test.span);
289-
self.cfg.push_assign(block, source_info, &slice,
290-
Rvalue::Cast(CastKind::Unsize, array, ty));
291-
Operand::Move(slice)
316+
let val = self.to_slice_operand(block, source_info, val);
317+
let slice = self.to_slice_operand(block, source_info, array);
318+
(slice, val)
292319
} else {
293-
self.literal_operand(test.span, ty, Literal::Value {
320+
(self.literal_operand(test.span, ty, Literal::Value {
294321
value
295-
})
322+
}), val)
296323
};
297324

298325
// Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq.
299326
let fail = self.cfg.start_new_block();
327+
let ty = expect.ty(&self.local_decls, tcx);
300328
if let ty::TyRef(_, mt) = ty.sty {
301329
assert!(ty.is_slice());
302330
let eq_def_id = self.hir.tcx().lang_items().eq_trait().unwrap();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
const CURSOR_PARTITION_LABEL: &'static [u8] = b"partition";
12+
const CURSOR_EVENT_TYPE_LABEL: &'static [u8] = b"event_type";
13+
const BYTE_PATTERN: &'static [u8; 5] = b"hello";
14+
15+
fn match_slice(x: &[u8]) -> u32 {
16+
match x {
17+
CURSOR_PARTITION_LABEL => 0,
18+
CURSOR_EVENT_TYPE_LABEL => 1,
19+
_ => 2,
20+
}
21+
}
22+
23+
fn match_array(x: &[u8; 5]) -> bool {
24+
match x {
25+
BYTE_PATTERN => true,
26+
_ => false
27+
}
28+
}
29+
30+
fn main() {
31+
assert_eq!(match_slice(b"abcde"), 2);
32+
assert_eq!(match_slice(b"event_type"), 1);
33+
assert_eq!(match_slice(b"partition"), 0);
34+
35+
assert_eq!(match_array(b"hello"), true);
36+
assert_eq!(match_array(b"hella"), false);
37+
}

0 commit comments

Comments
 (0)