1use std::sync::Arc;
9
10use rustc_data_structures::fx::FxIndexMap;
11use rustc_hir::{LangItem, RangeEnd};
12use rustc_middle::mir::*;
13use rustc_middle::ty::util::IntTypeExt;
14use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt};
15use rustc_middle::{bug, span_bug};
16use rustc_span::def_id::DefId;
17use rustc_span::source_map::Spanned;
18use rustc_span::{DUMMY_SP, Span, Symbol, sym};
19use tracing::{debug, instrument};
20
21use crate::builder::Builder;
22use crate::builder::matches::{
23 MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase,
24};
25
26impl<'a, 'tcx> Builder<'a, 'tcx> {
27 pub(super) fn pick_test_for_match_pair(
31 &mut self,
32 match_pair: &MatchPairTree<'tcx>,
33 ) -> Test<'tcx> {
34 let kind = match match_pair.testable_case {
35 TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
36
37 TestableCase::Constant { value: _, kind: PatConstKind::Bool } => TestKind::If,
38 TestableCase::Constant { value: _, kind: PatConstKind::IntOrChar } => {
39 TestKind::SwitchInt
40 }
41 TestableCase::Constant { value, kind: PatConstKind::String } => {
42 TestKind::StringEq { value, pat_ty: match_pair.pattern_ty }
43 }
44 TestableCase::Constant { value, kind: PatConstKind::Float | PatConstKind::Other } => {
45 TestKind::ScalarEq { value, pat_ty: match_pair.pattern_ty }
46 }
47
48 TestableCase::Range(ref range) => {
49 assert_eq!(range.ty, match_pair.pattern_ty);
50 TestKind::Range(Arc::clone(range))
51 }
52
53 TestableCase::Slice { len, op } => TestKind::SliceLen { len, op },
54
55 TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
56
57 TestableCase::Never => TestKind::Never,
58
59 TestableCase::Or { .. } => bug!("or-patterns should have already been handled"),
62 };
63
64 Test { span: match_pair.pattern_span, kind }
65 }
66
67 #[instrument(skip(self, target_blocks, place), level = "debug")]
68 pub(super) fn perform_test(
69 &mut self,
70 match_start_span: Span,
71 scrutinee_span: Span,
72 block: BasicBlock,
73 otherwise_block: BasicBlock,
74 place: Place<'tcx>,
75 test: &Test<'tcx>,
76 target_blocks: FxIndexMap<TestBranch<'tcx>, BasicBlock>,
77 ) {
78 let place_ty = place.ty(&self.local_decls, self.tcx);
79 debug!(?place, ?place_ty);
80 let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
81
82 let source_info = self.source_info(test.span);
83 match test.kind {
84 TestKind::Switch { adt_def } => {
85 let otherwise_block = target_block(TestBranch::Failure);
86 let switch_targets = SwitchTargets::new(
87 adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| {
88 if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) {
89 Some((discr.val, block))
90 } else {
91 None
92 }
93 }),
94 otherwise_block,
95 );
96 debug!("num_enum_variants: {}", adt_def.variants().len());
97 let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
98 let discr = self.temp(discr_ty, test.span);
99 self.cfg.push_assign(
100 block,
101 self.source_info(scrutinee_span),
102 discr,
103 Rvalue::Discriminant(place),
104 );
105 self.cfg.terminate(
106 block,
107 self.source_info(match_start_span),
108 TerminatorKind::SwitchInt {
109 discr: Operand::Move(discr),
110 targets: switch_targets,
111 },
112 );
113 }
114
115 TestKind::SwitchInt => {
116 let otherwise_block = target_block(TestBranch::Failure);
118 let switch_targets = SwitchTargets::new(
119 target_blocks.iter().filter_map(|(&branch, &block)| {
120 if let TestBranch::Constant(value) = branch {
121 let bits = value.to_leaf().to_bits_unchecked();
122 Some((bits, block))
123 } else {
124 None
125 }
126 }),
127 otherwise_block,
128 );
129 let terminator = TerminatorKind::SwitchInt {
130 discr: Operand::Copy(place),
131 targets: switch_targets,
132 };
133 self.cfg.terminate(block, self.source_info(match_start_span), terminator);
134 }
135
136 TestKind::If => {
137 let success_block = target_block(TestBranch::Success);
138 let fail_block = target_block(TestBranch::Failure);
139 let terminator =
140 TerminatorKind::if_(Operand::Copy(place), success_block, fail_block);
141 self.cfg.terminate(block, self.source_info(match_start_span), terminator);
142 }
143
144 TestKind::StringEq { value, pat_ty } => {
145 let tcx = self.tcx;
146 let success_block = target_block(TestBranch::Success);
147 let fail_block = target_block(TestBranch::Failure);
148
149 let expected_value_ty = value.ty;
150 let expected_value_operand =
151 self.literal_operand(test.span, Const::from_ty_value(tcx, value));
152
153 let mut actual_value_ty = pat_ty;
154 let mut actual_value_place = place;
155
156 match pat_ty.kind() {
157 ty::Str => {
158 if !tcx.features().deref_patterns() {
162 span_bug!(
163 test.span,
164 "matching on `str` went through without enabling deref_patterns"
165 );
166 }
167 let re_erased = tcx.lifetimes.re_erased;
168 let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
169 let ref_place = self.temp(ref_str_ty, test.span);
170 self.cfg.push_assign(
172 block,
173 self.source_info(test.span),
174 ref_place,
175 Rvalue::Ref(re_erased, BorrowKind::Shared, place),
176 );
177 actual_value_place = ref_place;
178 actual_value_ty = ref_str_ty;
179 }
180 _ => {}
181 }
182
183 assert_eq!(expected_value_ty, actual_value_ty);
184 assert!(actual_value_ty.is_imm_ref_str());
185
186 self.string_compare(
190 block,
191 success_block,
192 fail_block,
193 source_info,
194 expected_value_operand,
195 Operand::Copy(actual_value_place),
196 );
197 }
198
199 TestKind::ScalarEq { value, pat_ty } => {
200 let tcx = self.tcx;
201 let success_block = target_block(TestBranch::Success);
202 let fail_block = target_block(TestBranch::Failure);
203
204 let mut expected_value_ty = value.ty;
205 let mut expected_value_operand =
206 self.literal_operand(test.span, Const::from_ty_value(tcx, value));
207
208 let mut actual_value_ty = pat_ty;
209 let mut actual_value_place = place;
210
211 match pat_ty.kind() {
212 &ty::Pat(base, _) => {
213 assert_eq!(pat_ty, value.ty);
214 assert!(base.is_trivially_pure_clone_copy());
215
216 let transmuted_place = self.temp(base, test.span);
217 self.cfg.push_assign(
218 block,
219 self.source_info(scrutinee_span),
220 transmuted_place,
221 Rvalue::Cast(
222 CastKind::Transmute,
223 Operand::Copy(actual_value_place),
224 base,
225 ),
226 );
227
228 let transmuted_expect = self.temp(base, test.span);
229 self.cfg.push_assign(
230 block,
231 self.source_info(test.span),
232 transmuted_expect,
233 Rvalue::Cast(CastKind::Transmute, expected_value_operand, base),
234 );
235
236 actual_value_place = transmuted_place;
237 actual_value_ty = base;
238 expected_value_operand = Operand::Copy(transmuted_expect);
239 expected_value_ty = base;
240 }
241 _ => {}
242 }
243
244 assert_eq!(expected_value_ty, actual_value_ty);
245 assert!(actual_value_ty.is_scalar());
246
247 self.compare(
248 block,
249 success_block,
250 fail_block,
251 source_info,
252 BinOp::Eq,
253 expected_value_operand,
254 Operand::Copy(actual_value_place),
255 );
256 }
257
258 TestKind::Range(ref range) => {
259 let success = target_block(TestBranch::Success);
260 let fail = target_block(TestBranch::Failure);
261 let val = Operand::Copy(place);
263
264 let intermediate_block = if !range.lo.is_finite() {
265 block
266 } else if !range.hi.is_finite() {
267 success
268 } else {
269 self.cfg.start_new_block()
270 };
271
272 if let Some(lo) = range.lo.as_finite() {
273 let lo = ty::Value { ty: range.ty, valtree: lo };
274 let lo = self.literal_operand(test.span, Const::from_ty_value(self.tcx, lo));
275 self.compare(
276 block,
277 intermediate_block,
278 fail,
279 source_info,
280 BinOp::Le,
281 lo,
282 val.clone(),
283 );
284 };
285
286 if let Some(hi) = range.hi.as_finite() {
287 let hi = ty::Value { ty: range.ty, valtree: hi };
288 let hi = self.literal_operand(test.span, Const::from_ty_value(self.tcx, hi));
289 let op = match range.end {
290 RangeEnd::Included => BinOp::Le,
291 RangeEnd::Excluded => BinOp::Lt,
292 };
293 self.compare(intermediate_block, success, fail, source_info, op, val, hi);
294 }
295 }
296
297 TestKind::SliceLen { len, op } => {
298 let usize_ty = self.tcx.types.usize;
299 let actual = self.temp(usize_ty, test.span);
300
301 let length_op = self.len_of_slice_or_array(block, place, test.span, source_info);
303 self.cfg.push_assign(block, source_info, actual, Rvalue::Use(length_op));
304
305 let expected = self.push_usize(block, source_info, len);
307
308 let success_block = target_block(TestBranch::Success);
309 let fail_block = target_block(TestBranch::Failure);
310 self.compare(
313 block,
314 success_block,
315 fail_block,
316 source_info,
317 match op {
318 SliceLenOp::Equal => BinOp::Eq,
319 SliceLenOp::GreaterOrEqual => BinOp::Ge,
320 },
321 Operand::Move(actual),
322 Operand::Move(expected),
323 );
324 }
325
326 TestKind::Deref { temp, mutability } => {
327 let ty = place_ty.ty;
328 let target = target_block(TestBranch::Success);
329 self.call_deref(block, target, place, mutability, ty, temp, test.span);
330 }
331
332 TestKind::Never => {
333 self.cfg.push_fake_read(
336 block,
337 source_info,
338 FakeReadCause::ForMatchedPlace(None),
339 place,
340 );
341 self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
344 }
345 }
346 }
347
348 pub(super) fn call_deref(
351 &mut self,
352 block: BasicBlock,
353 target_block: BasicBlock,
354 place: Place<'tcx>,
355 mutability: Mutability,
356 ty: Ty<'tcx>,
357 temp: Place<'tcx>,
358 span: Span,
359 ) {
360 let (trait_item, method) = match mutability {
361 Mutability::Not => (LangItem::Deref, sym::deref),
362 Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
363 };
364 let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
365 let source_info = self.source_info(span);
366 let re_erased = self.tcx.lifetimes.re_erased;
367 let trait_item = self.tcx.require_lang_item(trait_item, span);
368 let method = trait_method(self.tcx, trait_item, method, [ty]);
369 let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
370 self.cfg.push_assign(
373 block,
374 source_info,
375 ref_src,
376 Rvalue::Ref(re_erased, borrow_kind, place),
377 );
378 self.cfg.terminate(
381 block,
382 source_info,
383 TerminatorKind::Call {
384 func: Operand::Constant(Box::new(ConstOperand {
385 span,
386 user_ty: None,
387 const_: method,
388 })),
389 args: [Spanned { node: Operand::Move(ref_src), span }].into(),
390 destination: temp,
391 target: Some(target_block),
392 unwind: UnwindAction::Continue,
393 call_source: CallSource::Misc,
394 fn_span: source_info.span,
395 },
396 );
397 }
398
399 fn compare(
401 &mut self,
402 block: BasicBlock,
403 success_block: BasicBlock,
404 fail_block: BasicBlock,
405 source_info: SourceInfo,
406 op: BinOp,
407 left: Operand<'tcx>,
408 right: Operand<'tcx>,
409 ) {
410 let bool_ty = self.tcx.types.bool;
411 let result = self.temp(bool_ty, source_info.span);
412
413 self.cfg.push_assign(
415 block,
416 source_info,
417 result,
418 Rvalue::BinaryOp(op, Box::new((left, right))),
419 );
420
421 self.cfg.terminate(
423 block,
424 source_info,
425 TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
426 );
427 }
428
429 fn string_compare(
431 &mut self,
432 block: BasicBlock,
433 success_block: BasicBlock,
434 fail_block: BasicBlock,
435 source_info: SourceInfo,
436 expect: Operand<'tcx>,
437 val: Operand<'tcx>,
438 ) {
439 let str_ty = self.tcx.types.str_;
440 let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, source_info.span);
441 let method = trait_method(self.tcx, eq_def_id, sym::eq, [str_ty, str_ty]);
442
443 let bool_ty = self.tcx.types.bool;
444 let eq_result = self.temp(bool_ty, source_info.span);
445 let eq_block = self.cfg.start_new_block();
446 self.cfg.terminate(
447 block,
448 source_info,
449 TerminatorKind::Call {
450 func: Operand::Constant(Box::new(ConstOperand {
451 span: source_info.span,
452
453 user_ty: None,
458
459 const_: method,
460 })),
461 args: [
462 Spanned { node: val, span: DUMMY_SP },
463 Spanned { node: expect, span: DUMMY_SP },
464 ]
465 .into(),
466 destination: eq_result,
467 target: Some(eq_block),
468 unwind: UnwindAction::Continue,
469 call_source: CallSource::MatchCmp,
470 fn_span: source_info.span,
471 },
472 );
473 self.diverge_from(block);
474
475 self.cfg.terminate(
477 eq_block,
478 source_info,
479 TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
480 );
481 }
482}
483
484fn trait_method<'tcx>(
485 tcx: TyCtxt<'tcx>,
486 trait_def_id: DefId,
487 method_name: Symbol,
488 args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
489) -> Const<'tcx> {
490 let item = tcx
493 .associated_items(trait_def_id)
494 .filter_by_name_unhygienic(method_name)
495 .find(|item| item.is_fn())
496 .expect("trait method not found");
497
498 let method_ty = Ty::new_fn_def(tcx, item.def_id, args);
499
500 Const::zero_sized(method_ty)
501}