1use std::sync::Arc;
9
10use rustc_data_structures::fx::FxIndexMap;
11use rustc_hir::RangeEnd;
12use rustc_hir::attrs::lang_items::LangItem;
13use rustc_middle::mir::*;
14use rustc_middle::ty::util::IntTypeExt;
15use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt};
16use rustc_span::def_id::DefId;
17use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, bug, sym};
18use tracing::{debug, instrument};
19
20use crate::builder::Builder;
21use crate::builder::matches::{
22 MatchPairKind, MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind,
23 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 MatchPairKind::Testable { ref testable_case, .. } = match_pair.kind else {
37 bug_impl(None, format_args!("or-patterns should have already been handled"),
Location::caller())bug!("or-patterns should have already been handled")
38 };
39 let kind = match *testable_case {
40 TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
41
42 TestableCase::Constant { value: _, kind: PatConstKind::Bool } => TestKind::If,
43 TestableCase::Constant { value: _, kind: PatConstKind::IntOrChar } => {
44 TestKind::SwitchInt
45 }
46 TestableCase::Constant { value, kind: PatConstKind::String } => {
47 TestKind::StringEq { value }
48 }
49 TestableCase::Constant { value, kind: PatConstKind::Float | PatConstKind::Other } => {
50 TestKind::ScalarEq { value }
51 }
52
53 TestableCase::Range(ref range) => TestKind::Range(Arc::clone(range)),
54
55 TestableCase::Slice { len, op } => TestKind::SliceLen { len, op },
56
57 TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
58
59 TestableCase::Never => TestKind::Never,
60 };
61
62 Test { span: match_pair.pattern_span, kind }
63 }
64
65 {}
#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("perform_test",
"rustc_mir_build::builder::matches::test",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs"),
::tracing_core::__macro_support::Option::Some(65u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::test"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("match_start_span")
}> =
::tracing::__macro_support::FieldName::new("match_start_span");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("scrutinee_span")
}> =
::tracing::__macro_support::FieldName::new("scrutinee_span");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("block")
}> =
::tracing::__macro_support::FieldName::new("block");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("otherwise_block")
}> =
::tracing::__macro_support::FieldName::new("otherwise_block");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("test")
}> =
::tracing::__macro_support::FieldName::new("test");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&match_start_span)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrutinee_span)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&block)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&otherwise_block)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&test)
as &dyn ::tracing::field::Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
let place_ty = place.ty(&self.local_decls, self.tcx);
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs:77",
"rustc_mir_build::builder::matches::test",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs"),
::tracing_core::__macro_support::Option::Some(77u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::test"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("place")
}> =
::tracing::__macro_support::FieldName::new("place");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("place_ty")
}> =
::tracing::__macro_support::FieldName::new("place_ty");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&place)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&place_ty)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};
let target_block =
|branch|
target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
let source_info = self.source_info(test.span);
match test.kind {
TestKind::Switch { adt_def } => {
let otherwise_block = target_block(TestBranch::Failure);
let switch_targets =
SwitchTargets::new(adt_def.discriminants(self.tcx).filter_map(|(idx,
discr)|
{
if let Some(&block) =
target_blocks.get(&TestBranch::Variant(idx)) {
Some((discr.val, block))
} else { None }
}), otherwise_block);
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs:94",
"rustc_mir_build::builder::matches::test",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs"),
::tracing_core::__macro_support::Option::Some(94u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::test"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("num_enum_variants: {0}",
adt_def.variants().len()) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};
let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
let discr = self.temp(discr_ty, test.span);
self.cfg.push_assign(block,
self.source_info(scrutinee_span), discr,
Rvalue::Discriminant(place));
self.cfg.terminate(block,
self.source_info(match_start_span),
TerminatorKind::SwitchInt {
discr: Operand::Move(discr),
targets: switch_targets,
});
}
TestKind::SwitchInt => {
let otherwise_block = target_block(TestBranch::Failure);
let switch_targets =
SwitchTargets::new(target_blocks.iter().filter_map(|(&branch,
&block)|
{
if let TestBranch::Constant(value) = branch {
let bits = value.to_leaf().to_bits_unchecked();
Some((bits, block))
} else { None }
}), otherwise_block);
let terminator =
TerminatorKind::SwitchInt {
discr: Operand::Copy(place),
targets: switch_targets,
};
self.cfg.terminate(block,
self.source_info(match_start_span), terminator);
}
TestKind::If => {
let success_block = target_block(TestBranch::Success);
let fail_block = target_block(TestBranch::Failure);
let terminator =
TerminatorKind::if_(Operand::Copy(place), success_block,
fail_block);
self.cfg.terminate(block,
self.source_info(match_start_span), terminator);
}
TestKind::StringEq { value } => {
let tcx = self.tcx;
let success_block = target_block(TestBranch::Success);
let fail_block = target_block(TestBranch::Failure);
let ref_str_ty =
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased,
tcx.types.str_);
if !ref_str_ty.is_imm_ref_str() {
{
::core::panicking::panic_fmt(format_args!("{0:?}",
ref_str_ty));
}
};
if !value.ty.is_str() {
{
::core::panicking::panic_fmt(format_args!("unexpected value type for StringEq test: {0:?}",
value));
}
};
let expected_value =
ty::Value { ty: ref_str_ty, valtree: value.valtree };
let expected_value_operand =
self.literal_operand(test.span,
Const::from_ty_value(tcx, expected_value));
let actual_value_ref_place =
self.temp(ref_str_ty, test.span);
self.cfg.push_assign(block, self.source_info(test.span),
actual_value_ref_place,
Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared,
place));
self.string_compare(block, success_block, fail_block,
source_info, expected_value_operand,
Operand::Copy(actual_value_ref_place));
}
TestKind::ScalarEq { value } => {
let tcx = self.tcx;
let success_block = target_block(TestBranch::Success);
let fail_block = target_block(TestBranch::Failure);
let mut expected_value_ty = value.ty;
let mut expected_value_operand =
self.literal_operand(test.span,
Const::from_ty_value(tcx, value));
let mut actual_value_place = place;
match value.ty.kind() {
&ty::Pat(base, _) => {
if !base.is_trivially_pure_clone_copy() {
::core::panicking::panic("assertion failed: base.is_trivially_pure_clone_copy()")
};
let transmuted_place = self.temp(base, test.span);
self.cfg.push_assign(block,
self.source_info(scrutinee_span), transmuted_place,
Rvalue::Cast(CastKind::Transmute,
Operand::Copy(actual_value_place), base));
let transmuted_expect = self.temp(base, test.span);
self.cfg.push_assign(block, self.source_info(test.span),
transmuted_expect,
Rvalue::Cast(CastKind::Transmute, expected_value_operand,
base));
actual_value_place = transmuted_place;
expected_value_operand = Operand::Copy(transmuted_expect);
expected_value_ty = base;
}
_ => {}
}
if !expected_value_ty.is_scalar() {
::core::panicking::panic("assertion failed: expected_value_ty.is_scalar()")
};
self.compare(block, success_block, fail_block, source_info,
BinOp::Eq, expected_value_operand,
Operand::Copy(actual_value_place));
}
TestKind::Range(ref range) => {
let success = target_block(TestBranch::Success);
let fail = target_block(TestBranch::Failure);
let val = Operand::Copy(place);
let intermediate_block =
if !range.lo.is_finite() {
block
} else if !range.hi.is_finite() {
success
} else { self.cfg.start_new_block() };
if let Some(lo) = range.lo.as_finite() {
let lo = ty::Value { ty: range.ty, valtree: lo };
let lo =
self.literal_operand(test.span,
Const::from_ty_value(self.tcx, lo));
self.compare(block, intermediate_block, fail, source_info,
BinOp::Le, lo, val.clone());
};
if let Some(hi) = range.hi.as_finite() {
let hi = ty::Value { ty: range.ty, valtree: hi };
let hi =
self.literal_operand(test.span,
Const::from_ty_value(self.tcx, hi));
let op =
match range.end {
RangeEnd::Included => BinOp::Le,
RangeEnd::Excluded => BinOp::Lt,
};
self.compare(intermediate_block, success, fail, source_info,
op, val, hi);
}
}
TestKind::SliceLen { len, op } => {
let usize_ty = self.tcx.types.usize;
let actual = self.temp(usize_ty, test.span);
let length_op =
self.len_of_slice_or_array(block, place, test.span,
source_info);
self.cfg.push_assign(block, source_info, actual,
Rvalue::Use(length_op, WithRetag::Yes));
let expected = self.push_usize(block, source_info, len);
let success_block = target_block(TestBranch::Success);
let fail_block = target_block(TestBranch::Failure);
self.compare(block, success_block, fail_block, source_info,
match op {
SliceLenOp::Equal => BinOp::Eq,
SliceLenOp::GreaterOrEqual => BinOp::Ge,
}, Operand::Move(actual), Operand::Move(expected));
}
TestKind::Deref { temp, mutability } => {
let ty = place_ty.ty;
let target = target_block(TestBranch::Success);
self.call_deref(block, target, place, mutability, ty, temp,
test.span);
}
TestKind::Never => {
self.cfg.push_fake_read(block, source_info,
FakeReadCause::ForMatchedPlace(None), place);
self.cfg.terminate(block, source_info,
TerminatorKind::Unreachable);
}
}
}
}
}#[instrument(skip(self, target_blocks, place), level = "debug")]
66 pub(super) fn perform_test(
67 &mut self,
68 match_start_span: Span,
69 scrutinee_span: Span,
70 block: BasicBlock,
71 otherwise_block: BasicBlock,
72 place: Place<'tcx>,
73 test: &Test<'tcx>,
74 target_blocks: FxIndexMap<TestBranch<'tcx>, BasicBlock>,
75 ) {
76 let place_ty = place.ty(&self.local_decls, self.tcx);
77 debug!(?place, ?place_ty);
78 let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
79
80 let source_info = self.source_info(test.span);
81 match test.kind {
82 TestKind::Switch { adt_def } => {
83 let otherwise_block = target_block(TestBranch::Failure);
84 let switch_targets = SwitchTargets::new(
85 adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| {
86 if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) {
87 Some((discr.val, block))
88 } else {
89 None
90 }
91 }),
92 otherwise_block,
93 );
94 debug!("num_enum_variants: {}", adt_def.variants().len());
95 let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
96 let discr = self.temp(discr_ty, test.span);
97 self.cfg.push_assign(
98 block,
99 self.source_info(scrutinee_span),
100 discr,
101 Rvalue::Discriminant(place),
102 );
103 self.cfg.terminate(
104 block,
105 self.source_info(match_start_span),
106 TerminatorKind::SwitchInt {
107 discr: Operand::Move(discr),
108 targets: switch_targets,
109 },
110 );
111 }
112
113 TestKind::SwitchInt => {
114 let otherwise_block = target_block(TestBranch::Failure);
116 let switch_targets = SwitchTargets::new(
117 target_blocks.iter().filter_map(|(&branch, &block)| {
118 if let TestBranch::Constant(value) = branch {
119 let bits = value.to_leaf().to_bits_unchecked();
120 Some((bits, block))
121 } else {
122 None
123 }
124 }),
125 otherwise_block,
126 );
127 let terminator = TerminatorKind::SwitchInt {
128 discr: Operand::Copy(place),
129 targets: switch_targets,
130 };
131 self.cfg.terminate(block, self.source_info(match_start_span), terminator);
132 }
133
134 TestKind::If => {
135 let success_block = target_block(TestBranch::Success);
136 let fail_block = target_block(TestBranch::Failure);
137 let terminator =
138 TerminatorKind::if_(Operand::Copy(place), success_block, fail_block);
139 self.cfg.terminate(block, self.source_info(match_start_span), terminator);
140 }
141
142 TestKind::StringEq { value } => {
143 let tcx = self.tcx;
144 let success_block = target_block(TestBranch::Success);
145 let fail_block = target_block(TestBranch::Failure);
146
147 let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, tcx.types.str_);
148 assert!(ref_str_ty.is_imm_ref_str(), "{ref_str_ty:?}");
149
150 assert!(value.ty.is_str(), "unexpected value type for StringEq test: {value:?}");
156 let expected_value = ty::Value { ty: ref_str_ty, valtree: value.valtree };
157 let expected_value_operand =
158 self.literal_operand(test.span, Const::from_ty_value(tcx, expected_value));
159
160 let actual_value_ref_place = self.temp(ref_str_ty, test.span);
163 self.cfg.push_assign(
164 block,
165 self.source_info(test.span),
166 actual_value_ref_place,
167 Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared, place),
168 );
169
170 self.string_compare(
174 block,
175 success_block,
176 fail_block,
177 source_info,
178 expected_value_operand,
179 Operand::Copy(actual_value_ref_place),
180 );
181 }
182
183 TestKind::ScalarEq { value } => {
184 let tcx = self.tcx;
185 let success_block = target_block(TestBranch::Success);
186 let fail_block = target_block(TestBranch::Failure);
187
188 let mut expected_value_ty = value.ty;
189 let mut expected_value_operand =
190 self.literal_operand(test.span, Const::from_ty_value(tcx, value));
191
192 let mut actual_value_place = place;
193
194 match value.ty.kind() {
195 &ty::Pat(base, _) => {
196 assert!(base.is_trivially_pure_clone_copy());
197
198 let transmuted_place = self.temp(base, test.span);
199 self.cfg.push_assign(
200 block,
201 self.source_info(scrutinee_span),
202 transmuted_place,
203 Rvalue::Cast(
204 CastKind::Transmute,
205 Operand::Copy(actual_value_place),
206 base,
207 ),
208 );
209
210 let transmuted_expect = self.temp(base, test.span);
211 self.cfg.push_assign(
212 block,
213 self.source_info(test.span),
214 transmuted_expect,
215 Rvalue::Cast(CastKind::Transmute, expected_value_operand, base),
216 );
217
218 actual_value_place = transmuted_place;
219 expected_value_operand = Operand::Copy(transmuted_expect);
220 expected_value_ty = base;
221 }
222 _ => {}
223 }
224
225 assert!(expected_value_ty.is_scalar());
226
227 self.compare(
228 block,
229 success_block,
230 fail_block,
231 source_info,
232 BinOp::Eq,
233 expected_value_operand,
234 Operand::Copy(actual_value_place),
235 );
236 }
237
238 TestKind::Range(ref range) => {
239 let success = target_block(TestBranch::Success);
240 let fail = target_block(TestBranch::Failure);
241 let val = Operand::Copy(place);
243
244 let intermediate_block = if !range.lo.is_finite() {
245 block
246 } else if !range.hi.is_finite() {
247 success
248 } else {
249 self.cfg.start_new_block()
250 };
251
252 if let Some(lo) = range.lo.as_finite() {
253 let lo = ty::Value { ty: range.ty, valtree: lo };
254 let lo = self.literal_operand(test.span, Const::from_ty_value(self.tcx, lo));
255 self.compare(
256 block,
257 intermediate_block,
258 fail,
259 source_info,
260 BinOp::Le,
261 lo,
262 val.clone(),
263 );
264 };
265
266 if let Some(hi) = range.hi.as_finite() {
267 let hi = ty::Value { ty: range.ty, valtree: hi };
268 let hi = self.literal_operand(test.span, Const::from_ty_value(self.tcx, hi));
269 let op = match range.end {
270 RangeEnd::Included => BinOp::Le,
271 RangeEnd::Excluded => BinOp::Lt,
272 };
273 self.compare(intermediate_block, success, fail, source_info, op, val, hi);
274 }
275 }
276
277 TestKind::SliceLen { len, op } => {
278 let usize_ty = self.tcx.types.usize;
279 let actual = self.temp(usize_ty, test.span);
280
281 let length_op = self.len_of_slice_or_array(block, place, test.span, source_info);
283 self.cfg.push_assign(
284 block,
285 source_info,
286 actual,
287 Rvalue::Use(length_op, WithRetag::Yes),
288 );
289
290 let expected = self.push_usize(block, source_info, len);
292
293 let success_block = target_block(TestBranch::Success);
294 let fail_block = target_block(TestBranch::Failure);
295 self.compare(
298 block,
299 success_block,
300 fail_block,
301 source_info,
302 match op {
303 SliceLenOp::Equal => BinOp::Eq,
304 SliceLenOp::GreaterOrEqual => BinOp::Ge,
305 },
306 Operand::Move(actual),
307 Operand::Move(expected),
308 );
309 }
310
311 TestKind::Deref { temp, mutability } => {
312 let ty = place_ty.ty;
313 let target = target_block(TestBranch::Success);
314 self.call_deref(block, target, place, mutability, ty, temp, test.span);
315 }
316
317 TestKind::Never => {
318 self.cfg.push_fake_read(
321 block,
322 source_info,
323 FakeReadCause::ForMatchedPlace(None),
324 place,
325 );
326 self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
329 }
330 }
331 }
332
333 pub(super) fn call_deref(
336 &mut self,
337 block: BasicBlock,
338 target_block: BasicBlock,
339 place: Place<'tcx>,
340 mutability: Mutability,
341 ty: Ty<'tcx>,
342 temp: Place<'tcx>,
343 span: Span,
344 ) {
345 let (trait_item, method) = match mutability {
346 Mutability::Not => (LangItem::Deref, sym::deref),
347 Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
348 };
349 let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
350 let source_info = self.source_info(span);
351 let re_erased = self.tcx.lifetimes.re_erased;
352 let trait_item = self.tcx.require_lang_item(trait_item, span);
353 let method = trait_method(self.tcx, trait_item, method, &[ty.into()]);
354 let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
355 self.cfg.push_assign(
358 block,
359 source_info,
360 ref_src,
361 Rvalue::Ref(re_erased, borrow_kind, place),
362 );
363 self.cfg.terminate(
366 block,
367 source_info,
368 TerminatorKind::Call {
369 func: Operand::Constant(Box::new(ConstOperand {
370 span,
371 user_ty: None,
372 const_: method,
373 })),
374 args: [Spanned { node: Operand::Move(ref_src), span }].into(),
375 destination: temp,
376 target: Some(target_block),
377 unwind: UnwindAction::Continue,
378 call_source: CallSource::Misc,
379 fn_span: source_info.span,
380 },
381 );
382 }
383
384 fn compare(
386 &mut self,
387 block: BasicBlock,
388 success_block: BasicBlock,
389 fail_block: BasicBlock,
390 source_info: SourceInfo,
391 op: BinOp,
392 left: Operand<'tcx>,
393 right: Operand<'tcx>,
394 ) {
395 let bool_ty = self.tcx.types.bool;
396 let result = self.temp(bool_ty, source_info.span);
397
398 self.cfg.push_assign(
400 block,
401 source_info,
402 result,
403 Rvalue::BinaryOp(op, Box::new((left, right))),
404 );
405
406 self.cfg.terminate(
408 block,
409 source_info,
410 TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
411 );
412 }
413
414 fn string_compare(
416 &mut self,
417 block: BasicBlock,
418 success_block: BasicBlock,
419 fail_block: BasicBlock,
420 source_info: SourceInfo,
421 expect: Operand<'tcx>,
422 val: Operand<'tcx>,
423 ) {
424 let str_ty = self.tcx.types.str_;
425 let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, source_info.span);
426 let method = trait_method(self.tcx, eq_def_id, sym::eq, &[str_ty.into(), str_ty.into()]);
427
428 let bool_ty = self.tcx.types.bool;
429 let eq_result = self.temp(bool_ty, source_info.span);
430 let eq_block = self.cfg.start_new_block();
431 self.cfg.terminate(
432 block,
433 source_info,
434 TerminatorKind::Call {
435 func: Operand::Constant(Box::new(ConstOperand {
436 span: source_info.span,
437
438 user_ty: None,
443
444 const_: method,
445 })),
446 args: [
447 Spanned { node: val, span: DUMMY_SP },
448 Spanned { node: expect, span: DUMMY_SP },
449 ]
450 .into(),
451 destination: eq_result,
452 target: Some(eq_block),
453 unwind: UnwindAction::Continue,
454 call_source: CallSource::MatchCmp,
455 fn_span: source_info.span,
456 },
457 );
458 self.diverge_from(block);
459
460 self.cfg.terminate(
462 eq_block,
463 source_info,
464 TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
465 );
466 }
467}
468
469fn trait_method<'tcx>(
470 tcx: TyCtxt<'tcx>,
471 trait_def_id: DefId,
472 method_name: Symbol,
473 args: &[GenericArg<'tcx>],
474) -> Const<'tcx> {
475 let item = tcx
478 .associated_items(trait_def_id)
479 .filter_by_name_unhygienic(method_name)
480 .find(|item| item.is_fn())
481 .expect("trait method not found");
482
483 Const::zero_sized(tcx.type_of(item.def_id).instantiate(tcx, args).skip_norm_wip())
484}