rustc_mir_build/builder/matches/test.rs
1// Testing candidates
2//
3// After candidates have been simplified, the only match pairs that
4// remain are those that require some sort of test. The functions here
5// identify what tests are needed, perform the tests, and then filter
6// the candidates based on the result.
7
8use std::cmp::Ordering;
9use std::sync::Arc;
10
11use rustc_data_structures::fx::FxIndexMap;
12use rustc_hir::{LangItem, RangeEnd};
13use rustc_middle::mir::*;
14use rustc_middle::ty::util::IntTypeExt;
15use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt};
16use rustc_middle::{bug, span_bug};
17use rustc_span::def_id::DefId;
18use rustc_span::source_map::Spanned;
19use rustc_span::{DUMMY_SP, Span, Symbol, sym};
20use tracing::{debug, instrument};
21
22use crate::builder::Builder;
23use crate::builder::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind};
24
25impl<'a, 'tcx> Builder<'a, 'tcx> {
26 /// Identifies what test is needed to decide if `match_pair` is applicable.
27 ///
28 /// It is a bug to call this with a not-fully-simplified pattern.
29 pub(super) fn pick_test_for_match_pair(
30 &mut self,
31 match_pair: &MatchPairTree<'tcx>,
32 ) -> Test<'tcx> {
33 let kind = match match_pair.test_case {
34 TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
35
36 TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If,
37 TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt,
38 TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern_ty },
39
40 TestCase::Range(ref range) => {
41 assert_eq!(range.ty, match_pair.pattern_ty);
42 TestKind::Range(Arc::clone(range))
43 }
44
45 TestCase::Slice { len, variable_length } => {
46 let op = if variable_length { BinOp::Ge } else { BinOp::Eq };
47 TestKind::Len { len: len as u64, op }
48 }
49
50 TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
51
52 TestCase::Never => TestKind::Never,
53
54 // Or-patterns are not tested directly; instead they are expanded into subcandidates,
55 // which are then distinguished by testing whatever non-or patterns they contain.
56 TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
57 };
58
59 Test { span: match_pair.pattern_span, kind }
60 }
61
62 #[instrument(skip(self, target_blocks, place), level = "debug")]
63 pub(super) fn perform_test(
64 &mut self,
65 match_start_span: Span,
66 scrutinee_span: Span,
67 block: BasicBlock,
68 otherwise_block: BasicBlock,
69 place: Place<'tcx>,
70 test: &Test<'tcx>,
71 target_blocks: FxIndexMap<TestBranch<'tcx>, BasicBlock>,
72 ) {
73 let place_ty = place.ty(&self.local_decls, self.tcx);
74 debug!(?place, ?place_ty);
75 let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
76
77 let source_info = self.source_info(test.span);
78 match test.kind {
79 TestKind::Switch { adt_def } => {
80 let otherwise_block = target_block(TestBranch::Failure);
81 let switch_targets = SwitchTargets::new(
82 adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| {
83 if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) {
84 Some((discr.val, block))
85 } else {
86 None
87 }
88 }),
89 otherwise_block,
90 );
91 debug!("num_enum_variants: {}", adt_def.variants().len());
92 let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
93 let discr = self.temp(discr_ty, test.span);
94 self.cfg.push_assign(
95 block,
96 self.source_info(scrutinee_span),
97 discr,
98 Rvalue::Discriminant(place),
99 );
100 self.cfg.terminate(
101 block,
102 self.source_info(match_start_span),
103 TerminatorKind::SwitchInt {
104 discr: Operand::Move(discr),
105 targets: switch_targets,
106 },
107 );
108 }
109
110 TestKind::SwitchInt => {
111 // The switch may be inexhaustive so we have a catch-all block
112 let otherwise_block = target_block(TestBranch::Failure);
113 let switch_targets = SwitchTargets::new(
114 target_blocks.iter().filter_map(|(&branch, &block)| {
115 if let TestBranch::Constant(_, bits) = branch {
116 Some((bits, block))
117 } else {
118 None
119 }
120 }),
121 otherwise_block,
122 );
123 let terminator = TerminatorKind::SwitchInt {
124 discr: Operand::Copy(place),
125 targets: switch_targets,
126 };
127 self.cfg.terminate(block, self.source_info(match_start_span), terminator);
128 }
129
130 TestKind::If => {
131 let success_block = target_block(TestBranch::Success);
132 let fail_block = target_block(TestBranch::Failure);
133 let terminator =
134 TerminatorKind::if_(Operand::Copy(place), success_block, fail_block);
135 self.cfg.terminate(block, self.source_info(match_start_span), terminator);
136 }
137
138 TestKind::Eq { value, mut ty } => {
139 let tcx = self.tcx;
140 let success_block = target_block(TestBranch::Success);
141 let fail_block = target_block(TestBranch::Failure);
142
143 let expect_ty = value.ty();
144 let expect = self.literal_operand(test.span, value);
145
146 let mut place = place;
147 let mut block = block;
148 match ty.kind() {
149 ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => {
150 if !tcx.features().string_deref_patterns() {
151 span_bug!(
152 test.span,
153 "matching on `String` went through without enabling string_deref_patterns"
154 );
155 }
156 let re_erased = tcx.lifetimes.re_erased;
157 let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
158 let ref_str = self.temp(ref_str_ty, test.span);
159 let eq_block = self.cfg.start_new_block();
160 // `let ref_str: &str = <String as Deref>::deref(&place);`
161 self.call_deref(
162 block,
163 eq_block,
164 place,
165 Mutability::Not,
166 ty,
167 ref_str,
168 test.span,
169 );
170 // Since we generated a `ref_str = <String as Deref>::deref(&place) -> eq_block` terminator,
171 // we need to add all further statements to `eq_block`.
172 // Similarly, the normal test code should be generated for the `&str`, instead of the `String`.
173 block = eq_block;
174 place = ref_str;
175 ty = ref_str_ty;
176 }
177 _ => {}
178 }
179
180 assert_eq!(expect_ty, ty);
181 if !ty.is_scalar() {
182 // Use `PartialEq::eq` instead of `BinOp::Eq`
183 // (the binop can only handle primitives)
184 // Make sure that we do *not* call any user-defined code here.
185 // The only type that can end up here is string literals, which have their
186 // comparison defined in `core`.
187 // (Interestingly this means that exhaustiveness analysis relies, for soundness,
188 // on the `PartialEq` impl for `str` to b correct!)
189 match *ty.kind() {
190 ty::Ref(_, deref_ty, _) if deref_ty == self.tcx.types.str_ => {}
191 _ => {
192 span_bug!(source_info.span, "invalid type for non-scalar compare: {ty}")
193 }
194 };
195 self.string_compare(
196 block,
197 success_block,
198 fail_block,
199 source_info,
200 expect,
201 Operand::Copy(place),
202 );
203 } else {
204 self.compare(
205 block,
206 success_block,
207 fail_block,
208 source_info,
209 BinOp::Eq,
210 expect,
211 Operand::Copy(place),
212 );
213 }
214 }
215
216 TestKind::Range(ref range) => {
217 let success = target_block(TestBranch::Success);
218 let fail = target_block(TestBranch::Failure);
219 // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
220 let val = Operand::Copy(place);
221
222 let intermediate_block = if !range.lo.is_finite() {
223 block
224 } else if !range.hi.is_finite() {
225 success
226 } else {
227 self.cfg.start_new_block()
228 };
229
230 if let Some(lo) = range.lo.as_finite() {
231 let lo = self.literal_operand(test.span, lo);
232 self.compare(
233 block,
234 intermediate_block,
235 fail,
236 source_info,
237 BinOp::Le,
238 lo,
239 val.clone(),
240 );
241 };
242
243 if let Some(hi) = range.hi.as_finite() {
244 let hi = self.literal_operand(test.span, hi);
245 let op = match range.end {
246 RangeEnd::Included => BinOp::Le,
247 RangeEnd::Excluded => BinOp::Lt,
248 };
249 self.compare(intermediate_block, success, fail, source_info, op, val, hi);
250 }
251 }
252
253 TestKind::Len { len, op } => {
254 let usize_ty = self.tcx.types.usize;
255 let actual = self.temp(usize_ty, test.span);
256
257 // actual = len(place)
258 self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place));
259
260 // expected = <N>
261 let expected = self.push_usize(block, source_info, len);
262
263 let success_block = target_block(TestBranch::Success);
264 let fail_block = target_block(TestBranch::Failure);
265 // result = actual == expected OR result = actual < expected
266 // branch based on result
267 self.compare(
268 block,
269 success_block,
270 fail_block,
271 source_info,
272 op,
273 Operand::Move(actual),
274 Operand::Move(expected),
275 );
276 }
277
278 TestKind::Deref { temp, mutability } => {
279 let ty = place_ty.ty;
280 let target = target_block(TestBranch::Success);
281 self.call_deref(block, target, place, mutability, ty, temp, test.span);
282 }
283
284 TestKind::Never => {
285 // Check that the place is initialized.
286 // FIXME(never_patterns): Also assert validity of the data at `place`.
287 self.cfg.push_fake_read(
288 block,
289 source_info,
290 FakeReadCause::ForMatchedPlace(None),
291 place,
292 );
293 // A never pattern is only allowed on an uninhabited type, so validity of the data
294 // implies unreachability.
295 self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
296 }
297 }
298 }
299
300 /// Perform `let temp = <ty as Deref>::deref(&place)`.
301 /// or `let temp = <ty as DerefMut>::deref_mut(&mut place)`.
302 pub(super) fn call_deref(
303 &mut self,
304 block: BasicBlock,
305 target_block: BasicBlock,
306 place: Place<'tcx>,
307 mutability: Mutability,
308 ty: Ty<'tcx>,
309 temp: Place<'tcx>,
310 span: Span,
311 ) {
312 let (trait_item, method) = match mutability {
313 Mutability::Not => (LangItem::Deref, sym::deref),
314 Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
315 };
316 let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
317 let source_info = self.source_info(span);
318 let re_erased = self.tcx.lifetimes.re_erased;
319 let trait_item = self.tcx.require_lang_item(trait_item, None);
320 let method = trait_method(self.tcx, trait_item, method, [ty]);
321 let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
322 // `let ref_src = &src_place;`
323 // or `let ref_src = &mut src_place;`
324 self.cfg.push_assign(
325 block,
326 source_info,
327 ref_src,
328 Rvalue::Ref(re_erased, borrow_kind, place),
329 );
330 // `let temp = <Ty as Deref>::deref(ref_src);`
331 // or `let temp = <Ty as DerefMut>::deref_mut(ref_src);`
332 self.cfg.terminate(
333 block,
334 source_info,
335 TerminatorKind::Call {
336 func: Operand::Constant(Box::new(ConstOperand {
337 span,
338 user_ty: None,
339 const_: method,
340 })),
341 args: [Spanned { node: Operand::Move(ref_src), span }].into(),
342 destination: temp,
343 target: Some(target_block),
344 unwind: UnwindAction::Continue,
345 call_source: CallSource::Misc,
346 fn_span: source_info.span,
347 },
348 );
349 }
350
351 /// Compare using the provided built-in comparison operator
352 fn compare(
353 &mut self,
354 block: BasicBlock,
355 success_block: BasicBlock,
356 fail_block: BasicBlock,
357 source_info: SourceInfo,
358 op: BinOp,
359 left: Operand<'tcx>,
360 right: Operand<'tcx>,
361 ) {
362 let bool_ty = self.tcx.types.bool;
363 let result = self.temp(bool_ty, source_info.span);
364
365 // result = op(left, right)
366 self.cfg.push_assign(
367 block,
368 source_info,
369 result,
370 Rvalue::BinaryOp(op, Box::new((left, right))),
371 );
372
373 // branch based on result
374 self.cfg.terminate(
375 block,
376 source_info,
377 TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
378 );
379 }
380
381 /// Compare two values of type `&str` using `<str as std::cmp::PartialEq>::eq`.
382 fn string_compare(
383 &mut self,
384 block: BasicBlock,
385 success_block: BasicBlock,
386 fail_block: BasicBlock,
387 source_info: SourceInfo,
388 expect: Operand<'tcx>,
389 val: Operand<'tcx>,
390 ) {
391 let str_ty = self.tcx.types.str_;
392 let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span));
393 let method = trait_method(self.tcx, eq_def_id, sym::eq, [str_ty, str_ty]);
394
395 let bool_ty = self.tcx.types.bool;
396 let eq_result = self.temp(bool_ty, source_info.span);
397 let eq_block = self.cfg.start_new_block();
398 self.cfg.terminate(
399 block,
400 source_info,
401 TerminatorKind::Call {
402 func: Operand::Constant(Box::new(ConstOperand {
403 span: source_info.span,
404
405 // FIXME(#54571): This constant comes from user input (a
406 // constant in a pattern). Are there forms where users can add
407 // type annotations here? For example, an associated constant?
408 // Need to experiment.
409 user_ty: None,
410
411 const_: method,
412 })),
413 args: [
414 Spanned { node: val, span: DUMMY_SP },
415 Spanned { node: expect, span: DUMMY_SP },
416 ]
417 .into(),
418 destination: eq_result,
419 target: Some(eq_block),
420 unwind: UnwindAction::Continue,
421 call_source: CallSource::MatchCmp,
422 fn_span: source_info.span,
423 },
424 );
425 self.diverge_from(block);
426
427 // check the result
428 self.cfg.terminate(
429 eq_block,
430 source_info,
431 TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
432 );
433 }
434
435 /// Given that we are performing `test` against `test_place`, this job
436 /// sorts out what the status of `candidate` will be after the test. See
437 /// `test_candidates` for the usage of this function. The candidate may
438 /// be modified to update its `match_pairs`.
439 ///
440 /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is
441 /// a variant test, then we would modify the candidate to be `(x as
442 /// Option).0 @ P0` and return the index corresponding to the variant
443 /// `Some`.
444 ///
445 /// However, in some cases, the test may just not be relevant to candidate.
446 /// For example, suppose we are testing whether `foo.x == 22`, but in one
447 /// match arm we have `Foo { x: _, ... }`... in that case, the test for
448 /// the value of `x` has no particular relevance to this candidate. In
449 /// such cases, this function just returns None without doing anything.
450 /// This is used by the overall `match_candidates` algorithm to structure
451 /// the match as a whole. See `match_candidates` for more details.
452 ///
453 /// FIXME(#29623). In some cases, we have some tricky choices to make. for
454 /// example, if we are testing that `x == 22`, but the candidate is `x @
455 /// 13..55`, what should we do? In the event that the test is true, we know
456 /// that the candidate applies, but in the event of false, we don't know
457 /// that it *doesn't* apply. For now, we return false, indicate that the
458 /// test does not apply to this candidate, but it might be we can get
459 /// tighter match code if we do something a bit different.
460 pub(super) fn sort_candidate(
461 &mut self,
462 test_place: Place<'tcx>,
463 test: &Test<'tcx>,
464 candidate: &mut Candidate<'tcx>,
465 sorted_candidates: &FxIndexMap<TestBranch<'tcx>, Vec<&mut Candidate<'tcx>>>,
466 ) -> Option<TestBranch<'tcx>> {
467 // Find the match_pair for this place (if any). At present,
468 // afaik, there can be at most one. (In the future, if we
469 // adopted a more general `@` operator, there might be more
470 // than one, but it'd be very unusual to have two sides that
471 // both require tests; you'd expect one side to be simplified
472 // away.)
473 let (match_pair_index, match_pair) = candidate
474 .match_pairs
475 .iter()
476 .enumerate()
477 .find(|&(_, mp)| mp.place == Some(test_place))?;
478
479 // If true, the match pair is completely entailed by its corresponding test
480 // branch, so it can be removed. If false, the match pair is _compatible_
481 // with its test branch, but still needs a more specific test.
482 let fully_matched;
483 let ret = match (&test.kind, &match_pair.test_case) {
484 // If we are performing a variant switch, then this
485 // informs variant patterns, but nothing else.
486 (
487 &TestKind::Switch { adt_def: tested_adt_def },
488 &TestCase::Variant { adt_def, variant_index },
489 ) => {
490 assert_eq!(adt_def, tested_adt_def);
491 fully_matched = true;
492 Some(TestBranch::Variant(variant_index))
493 }
494
495 // If we are performing a switch over integers, then this informs integer
496 // equality, but nothing else.
497 //
498 // FIXME(#29623) we could use PatKind::Range to rule
499 // things out here, in some cases.
500 (TestKind::SwitchInt, &TestCase::Constant { value })
501 if is_switch_ty(match_pair.pattern_ty) =>
502 {
503 // An important invariant of candidate sorting is that a candidate
504 // must not match in multiple branches. For `SwitchInt` tests, adding
505 // a new value might invalidate that property for range patterns that
506 // have already been sorted into the failure arm, so we must take care
507 // not to add such values here.
508 let is_covering_range = |test_case: &TestCase<'tcx>| {
509 test_case.as_range().is_some_and(|range| {
510 matches!(
511 range.contains(value, self.tcx, self.typing_env()),
512 None | Some(true)
513 )
514 })
515 };
516 let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| {
517 candidate
518 .match_pairs
519 .iter()
520 .any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case))
521 };
522 if sorted_candidates
523 .get(&TestBranch::Failure)
524 .is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate))
525 {
526 fully_matched = false;
527 None
528 } else {
529 fully_matched = true;
530 let bits = value.eval_bits(self.tcx, self.typing_env());
531 Some(TestBranch::Constant(value, bits))
532 }
533 }
534 (TestKind::SwitchInt, TestCase::Range(range)) => {
535 // When performing a `SwitchInt` test, a range pattern can be
536 // sorted into the failure arm if it doesn't contain _any_ of
537 // the values being tested. (This restricts what values can be
538 // added to the test by subsequent candidates.)
539 fully_matched = false;
540 let not_contained =
541 sorted_candidates.keys().filter_map(|br| br.as_constant()).copied().all(
542 |val| {
543 matches!(range.contains(val, self.tcx, self.typing_env()), Some(false))
544 },
545 );
546
547 not_contained.then(|| {
548 // No switch values are contained in the pattern range,
549 // so the pattern can be matched only if this test fails.
550 TestBranch::Failure
551 })
552 }
553
554 (TestKind::If, TestCase::Constant { value }) => {
555 fully_matched = true;
556 let value = value.try_eval_bool(self.tcx, self.typing_env()).unwrap_or_else(|| {
557 span_bug!(test.span, "expected boolean value but got {value:?}")
558 });
559 Some(if value { TestBranch::Success } else { TestBranch::Failure })
560 }
561
562 (
563 &TestKind::Len { len: test_len, op: BinOp::Eq },
564 &TestCase::Slice { len, variable_length },
565 ) => {
566 match (test_len.cmp(&(len as u64)), variable_length) {
567 (Ordering::Equal, false) => {
568 // on true, min_len = len = $actual_length,
569 // on false, len != $actual_length
570 fully_matched = true;
571 Some(TestBranch::Success)
572 }
573 (Ordering::Less, _) => {
574 // test_len < pat_len. If $actual_len = test_len,
575 // then $actual_len < pat_len and we don't have
576 // enough elements.
577 fully_matched = false;
578 Some(TestBranch::Failure)
579 }
580 (Ordering::Equal | Ordering::Greater, true) => {
581 // This can match both if $actual_len = test_len >= pat_len,
582 // and if $actual_len > test_len. We can't advance.
583 fully_matched = false;
584 None
585 }
586 (Ordering::Greater, false) => {
587 // test_len != pat_len, so if $actual_len = test_len, then
588 // $actual_len != pat_len.
589 fully_matched = false;
590 Some(TestBranch::Failure)
591 }
592 }
593 }
594 (
595 &TestKind::Len { len: test_len, op: BinOp::Ge },
596 &TestCase::Slice { len, variable_length },
597 ) => {
598 // the test is `$actual_len >= test_len`
599 match (test_len.cmp(&(len as u64)), variable_length) {
600 (Ordering::Equal, true) => {
601 // $actual_len >= test_len = pat_len,
602 // so we can match.
603 fully_matched = true;
604 Some(TestBranch::Success)
605 }
606 (Ordering::Less, _) | (Ordering::Equal, false) => {
607 // test_len <= pat_len. If $actual_len < test_len,
608 // then it is also < pat_len, so the test passing is
609 // necessary (but insufficient).
610 fully_matched = false;
611 Some(TestBranch::Success)
612 }
613 (Ordering::Greater, false) => {
614 // test_len > pat_len. If $actual_len >= test_len > pat_len,
615 // then we know we won't have a match.
616 fully_matched = false;
617 Some(TestBranch::Failure)
618 }
619 (Ordering::Greater, true) => {
620 // test_len < pat_len, and is therefore less
621 // strict. This can still go both ways.
622 fully_matched = false;
623 None
624 }
625 }
626 }
627
628 (TestKind::Range(test), TestCase::Range(pat)) => {
629 if test == pat {
630 fully_matched = true;
631 Some(TestBranch::Success)
632 } else {
633 fully_matched = false;
634 // If the testing range does not overlap with pattern range,
635 // the pattern can be matched only if this test fails.
636 if !test.overlaps(pat, self.tcx, self.typing_env())? {
637 Some(TestBranch::Failure)
638 } else {
639 None
640 }
641 }
642 }
643 (TestKind::Range(range), &TestCase::Constant { value }) => {
644 fully_matched = false;
645 if !range.contains(value, self.tcx, self.typing_env())? {
646 // `value` is not contained in the testing range,
647 // so `value` can be matched only if this test fails.
648 Some(TestBranch::Failure)
649 } else {
650 None
651 }
652 }
653
654 (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => {
655 if test_val == case_val {
656 fully_matched = true;
657 Some(TestBranch::Success)
658 } else {
659 fully_matched = false;
660 Some(TestBranch::Failure)
661 }
662 }
663
664 (TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. })
665 if test_temp == temp =>
666 {
667 fully_matched = true;
668 Some(TestBranch::Success)
669 }
670
671 (TestKind::Never, _) => {
672 fully_matched = true;
673 Some(TestBranch::Success)
674 }
675
676 (
677 TestKind::Switch { .. }
678 | TestKind::SwitchInt { .. }
679 | TestKind::If
680 | TestKind::Len { .. }
681 | TestKind::Range { .. }
682 | TestKind::Eq { .. }
683 | TestKind::Deref { .. },
684 _,
685 ) => {
686 fully_matched = false;
687 None
688 }
689 };
690
691 if fully_matched {
692 // Replace the match pair by its sub-pairs.
693 let match_pair = candidate.match_pairs.remove(match_pair_index);
694 candidate.match_pairs.extend(match_pair.subpairs);
695 // Move or-patterns to the end.
696 candidate.sort_match_pairs();
697 }
698
699 ret
700 }
701}
702
703fn is_switch_ty(ty: Ty<'_>) -> bool {
704 ty.is_integral() || ty.is_char()
705}
706
707fn trait_method<'tcx>(
708 tcx: TyCtxt<'tcx>,
709 trait_def_id: DefId,
710 method_name: Symbol,
711 args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
712) -> Const<'tcx> {
713 // The unhygienic comparison here is acceptable because this is only
714 // used on known traits.
715 let item = tcx
716 .associated_items(trait_def_id)
717 .filter_by_name_unhygienic(method_name)
718 .find(|item| item.kind == ty::AssocKind::Fn)
719 .expect("trait method not found");
720
721 let method_ty = Ty::new_fn_def(tcx, item.def_id, args);
722
723 Const::zero_sized(method_ty)
724}