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::Float } => {
42 TestKind::Eq { value, cast_ty: match_pair.pattern_ty }
43 }
44 TestableCase::Constant { value, kind: PatConstKind::Other } => {
45 TestKind::Eq { value, cast_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::Eq { value, mut cast_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 mut expect_ty = value.ty;
150 let mut expect = self.literal_operand(test.span, Const::from_ty_value(tcx, value));
151
152 let mut place = place;
153 let mut block = block;
154 match cast_ty.kind() {
155 ty::Str => {
156 if !tcx.features().deref_patterns() {
160 span_bug!(
161 test.span,
162 "matching on `str` went through without enabling deref_patterns"
163 );
164 }
165 let re_erased = tcx.lifetimes.re_erased;
166 let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
167 let ref_place = self.temp(ref_str_ty, test.span);
168 self.cfg.push_assign(
170 block,
171 self.source_info(test.span),
172 ref_place,
173 Rvalue::Ref(re_erased, BorrowKind::Shared, place),
174 );
175 place = ref_place;
176 cast_ty = ref_str_ty;
177 }
178 ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => {
179 if !tcx.features().string_deref_patterns() {
180 span_bug!(
181 test.span,
182 "matching on `String` went through without enabling string_deref_patterns"
183 );
184 }
185 let re_erased = tcx.lifetimes.re_erased;
186 let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
187 let ref_str = self.temp(ref_str_ty, test.span);
188 let eq_block = self.cfg.start_new_block();
189 self.call_deref(
191 block,
192 eq_block,
193 place,
194 Mutability::Not,
195 cast_ty,
196 ref_str,
197 test.span,
198 );
199 block = eq_block;
203 place = ref_str;
204 cast_ty = ref_str_ty;
205 }
206 &ty::Pat(base, _) => {
207 assert_eq!(cast_ty, value.ty);
208 assert!(base.is_trivially_pure_clone_copy());
209
210 let transmuted_place = self.temp(base, test.span);
211 self.cfg.push_assign(
212 block,
213 self.source_info(scrutinee_span),
214 transmuted_place,
215 Rvalue::Cast(CastKind::Transmute, Operand::Copy(place), base),
216 );
217
218 let transmuted_expect = self.temp(base, test.span);
219 self.cfg.push_assign(
220 block,
221 self.source_info(test.span),
222 transmuted_expect,
223 Rvalue::Cast(CastKind::Transmute, expect, base),
224 );
225
226 place = transmuted_place;
227 expect = Operand::Copy(transmuted_expect);
228 cast_ty = base;
229 expect_ty = base;
230 }
231 _ => {}
232 }
233
234 assert_eq!(expect_ty, cast_ty);
235 if !cast_ty.is_scalar() {
236 match *cast_ty.kind() {
244 ty::Ref(_, deref_ty, _) if deref_ty == self.tcx.types.str_ => {}
245 _ => {
246 span_bug!(
247 source_info.span,
248 "invalid type for non-scalar compare: {cast_ty}"
249 )
250 }
251 };
252 self.string_compare(
253 block,
254 success_block,
255 fail_block,
256 source_info,
257 expect,
258 Operand::Copy(place),
259 );
260 } else {
261 self.compare(
262 block,
263 success_block,
264 fail_block,
265 source_info,
266 BinOp::Eq,
267 expect,
268 Operand::Copy(place),
269 );
270 }
271 }
272
273 TestKind::Range(ref range) => {
274 let success = target_block(TestBranch::Success);
275 let fail = target_block(TestBranch::Failure);
276 let val = Operand::Copy(place);
278
279 let intermediate_block = if !range.lo.is_finite() {
280 block
281 } else if !range.hi.is_finite() {
282 success
283 } else {
284 self.cfg.start_new_block()
285 };
286
287 if let Some(lo) = range.lo.as_finite() {
288 let lo = ty::Value { ty: range.ty, valtree: lo };
289 let lo = self.literal_operand(test.span, Const::from_ty_value(self.tcx, lo));
290 self.compare(
291 block,
292 intermediate_block,
293 fail,
294 source_info,
295 BinOp::Le,
296 lo,
297 val.clone(),
298 );
299 };
300
301 if let Some(hi) = range.hi.as_finite() {
302 let hi = ty::Value { ty: range.ty, valtree: hi };
303 let hi = self.literal_operand(test.span, Const::from_ty_value(self.tcx, hi));
304 let op = match range.end {
305 RangeEnd::Included => BinOp::Le,
306 RangeEnd::Excluded => BinOp::Lt,
307 };
308 self.compare(intermediate_block, success, fail, source_info, op, val, hi);
309 }
310 }
311
312 TestKind::SliceLen { len, op } => {
313 let usize_ty = self.tcx.types.usize;
314 let actual = self.temp(usize_ty, test.span);
315
316 let length_op = self.len_of_slice_or_array(block, place, test.span, source_info);
318 self.cfg.push_assign(block, source_info, actual, Rvalue::Use(length_op));
319
320 let expected = self.push_usize(block, source_info, len);
322
323 let success_block = target_block(TestBranch::Success);
324 let fail_block = target_block(TestBranch::Failure);
325 self.compare(
328 block,
329 success_block,
330 fail_block,
331 source_info,
332 match op {
333 SliceLenOp::Equal => BinOp::Eq,
334 SliceLenOp::GreaterOrEqual => BinOp::Ge,
335 },
336 Operand::Move(actual),
337 Operand::Move(expected),
338 );
339 }
340
341 TestKind::Deref { temp, mutability } => {
342 let ty = place_ty.ty;
343 let target = target_block(TestBranch::Success);
344 self.call_deref(block, target, place, mutability, ty, temp, test.span);
345 }
346
347 TestKind::Never => {
348 self.cfg.push_fake_read(
351 block,
352 source_info,
353 FakeReadCause::ForMatchedPlace(None),
354 place,
355 );
356 self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
359 }
360 }
361 }
362
363 pub(super) fn call_deref(
366 &mut self,
367 block: BasicBlock,
368 target_block: BasicBlock,
369 place: Place<'tcx>,
370 mutability: Mutability,
371 ty: Ty<'tcx>,
372 temp: Place<'tcx>,
373 span: Span,
374 ) {
375 let (trait_item, method) = match mutability {
376 Mutability::Not => (LangItem::Deref, sym::deref),
377 Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
378 };
379 let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
380 let source_info = self.source_info(span);
381 let re_erased = self.tcx.lifetimes.re_erased;
382 let trait_item = self.tcx.require_lang_item(trait_item, span);
383 let method = trait_method(self.tcx, trait_item, method, [ty]);
384 let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
385 self.cfg.push_assign(
388 block,
389 source_info,
390 ref_src,
391 Rvalue::Ref(re_erased, borrow_kind, place),
392 );
393 self.cfg.terminate(
396 block,
397 source_info,
398 TerminatorKind::Call {
399 func: Operand::Constant(Box::new(ConstOperand {
400 span,
401 user_ty: None,
402 const_: method,
403 })),
404 args: [Spanned { node: Operand::Move(ref_src), span }].into(),
405 destination: temp,
406 target: Some(target_block),
407 unwind: UnwindAction::Continue,
408 call_source: CallSource::Misc,
409 fn_span: source_info.span,
410 },
411 );
412 }
413
414 fn compare(
416 &mut self,
417 block: BasicBlock,
418 success_block: BasicBlock,
419 fail_block: BasicBlock,
420 source_info: SourceInfo,
421 op: BinOp,
422 left: Operand<'tcx>,
423 right: Operand<'tcx>,
424 ) {
425 let bool_ty = self.tcx.types.bool;
426 let result = self.temp(bool_ty, source_info.span);
427
428 self.cfg.push_assign(
430 block,
431 source_info,
432 result,
433 Rvalue::BinaryOp(op, Box::new((left, right))),
434 );
435
436 self.cfg.terminate(
438 block,
439 source_info,
440 TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
441 );
442 }
443
444 fn string_compare(
446 &mut self,
447 block: BasicBlock,
448 success_block: BasicBlock,
449 fail_block: BasicBlock,
450 source_info: SourceInfo,
451 expect: Operand<'tcx>,
452 val: Operand<'tcx>,
453 ) {
454 let str_ty = self.tcx.types.str_;
455 let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, source_info.span);
456 let method = trait_method(self.tcx, eq_def_id, sym::eq, [str_ty, str_ty]);
457
458 let bool_ty = self.tcx.types.bool;
459 let eq_result = self.temp(bool_ty, source_info.span);
460 let eq_block = self.cfg.start_new_block();
461 self.cfg.terminate(
462 block,
463 source_info,
464 TerminatorKind::Call {
465 func: Operand::Constant(Box::new(ConstOperand {
466 span: source_info.span,
467
468 user_ty: None,
473
474 const_: method,
475 })),
476 args: [
477 Spanned { node: val, span: DUMMY_SP },
478 Spanned { node: expect, span: DUMMY_SP },
479 ]
480 .into(),
481 destination: eq_result,
482 target: Some(eq_block),
483 unwind: UnwindAction::Continue,
484 call_source: CallSource::MatchCmp,
485 fn_span: source_info.span,
486 },
487 );
488 self.diverge_from(block);
489
490 self.cfg.terminate(
492 eq_block,
493 source_info,
494 TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
495 );
496 }
497}
498
499fn trait_method<'tcx>(
500 tcx: TyCtxt<'tcx>,
501 trait_def_id: DefId,
502 method_name: Symbol,
503 args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
504) -> Const<'tcx> {
505 let item = tcx
508 .associated_items(trait_def_id)
509 .filter_by_name_unhygienic(method_name)
510 .find(|item| item.is_fn())
511 .expect("trait method not found");
512
513 let method_ty = Ty::new_fn_def(tcx, item.def_id, args);
514
515 Const::zero_sized(method_ty)
516}