]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/rust/typecheck/rust-tyty-bounds.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / typecheck / rust-tyty-bounds.cc
1 // Copyright (C) 2021-2024 Free Software Foundation, Inc.
2
3 // This file is part of GCC.
4
5 // GCC is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 3, or (at your option) any later
8 // version.
9
10 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 // for more details.
14
15 // You should have received a copy of the GNU General Public License
16 // along with GCC; see the file COPYING3. If not see
17 // <http://www.gnu.org/licenses/>.
18
19 #include "rust-hir-type-bounds.h"
20 #include "rust-hir-trait-resolve.h"
21 #include "rust-hir-type-check-item.h"
22
23 namespace Rust {
24 namespace Resolver {
25
26 TypeBoundsProbe::TypeBoundsProbe (const TyTy::BaseType *receiver)
27 : TypeCheckBase (), receiver (receiver)
28 {}
29
30 std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>
31 TypeBoundsProbe::Probe (const TyTy::BaseType *receiver)
32 {
33 TypeBoundsProbe probe (receiver);
34 probe.scan ();
35 return probe.trait_references;
36 }
37
38 bool
39 TypeBoundsProbe::is_bound_satisfied_for_type (TyTy::BaseType *receiver,
40 TraitReference *ref)
41 {
42 for (auto &bound : receiver->get_specified_bounds ())
43 {
44 const TraitReference *b = bound.get ();
45 if (b->is_equal (*ref))
46 return true;
47 }
48
49 std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> bounds
50 = Probe (receiver);
51 for (auto &bound : bounds)
52 {
53 const TraitReference *b = bound.first;
54 if (b->is_equal (*ref))
55 return true;
56 }
57
58 return false;
59 }
60
61 void
62 TypeBoundsProbe::scan ()
63 {
64 std::vector<std::pair<HIR::TypePath *, HIR::ImplBlock *>>
65 possible_trait_paths;
66 mappings->iterate_impl_blocks (
67 [&] (HirId id, HIR::ImplBlock *impl) mutable -> bool {
68 // we are filtering for trait-impl-blocks
69 if (!impl->has_trait_ref ())
70 return true;
71
72 HirId impl_ty_id = impl->get_type ()->get_mappings ().get_hirid ();
73 TyTy::BaseType *impl_type = nullptr;
74 if (!query_type (impl_ty_id, &impl_type))
75 return true;
76
77 if (!receiver->can_eq (impl_type, false))
78 {
79 if (!impl_type->can_eq (receiver, false))
80 return true;
81 }
82
83 possible_trait_paths.push_back ({impl->get_trait_ref ().get (), impl});
84 return true;
85 });
86
87 for (auto &path : possible_trait_paths)
88 {
89 HIR::TypePath *trait_path = path.first;
90 TraitReference *trait_ref = TraitResolver::Resolve (*trait_path);
91
92 if (!trait_ref->is_error ())
93 trait_references.push_back ({trait_ref, path.second});
94 }
95
96 // marker traits...
97 assemble_sized_builtin ();
98 }
99
100 void
101 TypeBoundsProbe::assemble_sized_builtin ()
102 {
103 const TyTy::BaseType *raw = receiver->destructure ();
104
105 // does this thing actually implement sized?
106 switch (raw->get_kind ())
107 {
108 case TyTy::ADT:
109 case TyTy::STR:
110 case TyTy::REF:
111 case TyTy::POINTER:
112 case TyTy::PARAM:
113 case TyTy::ARRAY:
114 case TyTy::SLICE:
115 case TyTy::FNDEF:
116 case TyTy::FNPTR:
117 case TyTy::TUPLE:
118 case TyTy::BOOL:
119 case TyTy::CHAR:
120 case TyTy::INT:
121 case TyTy::UINT:
122 case TyTy::FLOAT:
123 case TyTy::USIZE:
124 case TyTy::ISIZE:
125 assemble_builtin_candidate (Analysis::RustLangItem::SIZED);
126 break;
127
128 // not-sure about this.... FIXME
129 case TyTy::INFER:
130 case TyTy::NEVER:
131 case TyTy::PLACEHOLDER:
132 case TyTy::PROJECTION:
133 case TyTy::DYNAMIC:
134 case TyTy::CLOSURE:
135 case TyTy::ERROR:
136 break;
137 }
138 }
139
140 void
141 TypeBoundsProbe::assemble_builtin_candidate (
142 Analysis::RustLangItem::ItemType lang_item)
143 {
144 DefId id;
145 bool found_lang_item = mappings->lookup_lang_item (lang_item, &id);
146 if (!found_lang_item)
147 return;
148
149 HIR::Item *item = mappings->lookup_defid (id);
150 if (item == nullptr)
151 return;
152
153 rust_assert (item->get_item_kind () == HIR::Item::ItemKind::Trait);
154 HIR::Trait *trait = static_cast<HIR::Trait *> (item);
155 const TyTy::BaseType *raw = receiver->destructure ();
156
157 // assemble the reference
158 TraitReference *trait_ref = TraitResolver::Resolve (*trait);
159 trait_references.push_back ({trait_ref, mappings->lookup_builtin_marker ()});
160
161 rust_debug ("Added builtin lang_item: %s for %s",
162 Analysis::RustLangItem::ToString (lang_item).c_str (),
163 raw->get_name ().c_str ());
164 }
165
166 TraitReference *
167 TypeCheckBase::resolve_trait_path (HIR::TypePath &path)
168 {
169 return TraitResolver::Resolve (path);
170 }
171
172 TyTy::TypeBoundPredicate
173 TypeCheckBase::get_predicate_from_bound (HIR::TypePath &type_path)
174 {
175 TyTy::TypeBoundPredicate lookup = TyTy::TypeBoundPredicate::error ();
176 bool already_resolved
177 = context->lookup_predicate (type_path.get_mappings ().get_hirid (),
178 &lookup);
179 if (already_resolved)
180 return lookup;
181
182 TraitReference *trait = resolve_trait_path (type_path);
183 if (trait->is_error ())
184 return TyTy::TypeBoundPredicate::error ();
185
186 TyTy::TypeBoundPredicate predicate (*trait, type_path.get_locus ());
187 HIR::GenericArgs args
188 = HIR::GenericArgs::create_empty (type_path.get_locus ());
189
190 auto &final_seg = type_path.get_final_segment ();
191 switch (final_seg->get_type ())
192 {
193 case HIR::TypePathSegment::SegmentType::GENERIC: {
194 auto final_generic_seg
195 = static_cast<HIR::TypePathSegmentGeneric *> (final_seg.get ());
196 if (final_generic_seg->has_generic_args ())
197 {
198 args = final_generic_seg->get_generic_args ();
199 }
200 }
201 break;
202
203 case HIR::TypePathSegment::SegmentType::FUNCTION: {
204 auto final_function_seg
205 = static_cast<HIR::TypePathSegmentFunction *> (final_seg.get ());
206 auto &fn = final_function_seg->get_function_path ();
207
208 // we need to make implicit generic args which must be an implicit
209 // Tuple
210 auto crate_num = mappings->get_current_crate ();
211 HirId implicit_args_id = mappings->get_next_hir_id ();
212 Analysis::NodeMapping mapping (crate_num,
213 final_seg->get_mappings ().get_nodeid (),
214 implicit_args_id, UNKNOWN_LOCAL_DEFID);
215
216 std::vector<std::unique_ptr<HIR::Type>> params_copy;
217 for (auto &p : fn.get_params ())
218 {
219 params_copy.push_back (p->clone_type ());
220 }
221
222 HIR::TupleType *implicit_tuple
223 = new HIR::TupleType (mapping, std::move (params_copy),
224 final_seg->get_locus ());
225
226 std::vector<std::unique_ptr<HIR::Type>> inputs;
227 inputs.push_back (std::unique_ptr<HIR::Type> (implicit_tuple));
228
229 args = HIR::GenericArgs ({} /* lifetimes */,
230 std::move (inputs) /* type_args*/,
231 {} /* binding_args*/, {} /* const_args */,
232 final_seg->get_locus ());
233
234 // resolve the fn_once_output type
235 TyTy::BaseType *fn_once_output_ty
236 = fn.has_return_type ()
237 ? TypeCheckType::Resolve (fn.get_return_type ().get ())
238 : TyTy::TupleType::get_unit_type (
239 final_seg->get_mappings ().get_hirid ());
240 context->insert_implicit_type (final_seg->get_mappings ().get_hirid (),
241 fn_once_output_ty);
242
243 // setup the associated type.. ??
244 // fn_once_output_ty->debug ();
245 }
246 break;
247
248 default:
249 /* nothing to do */
250 break;
251 }
252
253 // we try to apply generic arguments when they are non empty and or when the
254 // predicate requires them so that we get the relevant Foo expects x number
255 // arguments but got zero see test case rust/compile/traits12.rs
256 if (!args.is_empty () || predicate.requires_generic_args ())
257 {
258 // this is applying generic arguments to a trait reference
259 predicate.apply_generic_arguments (&args);
260 }
261
262 context->insert_resolved_predicate (type_path.get_mappings ().get_hirid (),
263 predicate);
264 return predicate;
265 }
266
267 } // namespace Resolver
268
269 namespace TyTy {
270
271 TypeBoundPredicate::TypeBoundPredicate (
272 const Resolver::TraitReference &trait_reference, Location locus)
273 : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
274 reference (trait_reference.get_mappings ().get_defid ()), locus (locus),
275 error_flag (false)
276 {
277 substitutions.clear ();
278 for (const auto &p : trait_reference.get_trait_substs ())
279 substitutions.push_back (p.clone ());
280
281 // we setup a dummy implict self argument
282 SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
283 used_arguments.get_mappings ().push_back (placeholder_self);
284 }
285
286 TypeBoundPredicate::TypeBoundPredicate (
287 DefId reference, std::vector<SubstitutionParamMapping> subst, Location locus)
288 : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
289 reference (reference), locus (locus), error_flag (false)
290 {
291 substitutions.clear ();
292 for (const auto &p : subst)
293 substitutions.push_back (p.clone ());
294
295 // we setup a dummy implict self argument
296 SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
297 used_arguments.get_mappings ().push_back (placeholder_self);
298 }
299
300 TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other)
301 : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
302 reference (other.reference), locus (other.locus),
303 error_flag (other.error_flag)
304 {
305 substitutions.clear ();
306 for (const auto &p : other.get_substs ())
307 substitutions.push_back (p.clone ());
308
309 std::vector<SubstitutionArg> mappings;
310 for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
311 {
312 const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
313 SubstitutionArg arg (oa);
314 mappings.push_back (std::move (arg));
315 }
316
317 // we need to remap the argument mappings based on this copied constructor
318 std::vector<SubstitutionArg> copied_arg_mappings;
319 size_t i = 0;
320 for (const auto &m : other.used_arguments.get_mappings ())
321 {
322 TyTy::BaseType *argument
323 = m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
324 SubstitutionArg c (&substitutions.at (i++), argument);
325 copied_arg_mappings.push_back (std::move (c));
326 }
327
328 used_arguments
329 = SubstitutionArgumentMappings (copied_arg_mappings, {},
330 other.used_arguments.get_locus ());
331 }
332
333 TypeBoundPredicate &
334 TypeBoundPredicate::operator= (const TypeBoundPredicate &other)
335 {
336 reference = other.reference;
337 locus = other.locus;
338 error_flag = other.error_flag;
339 used_arguments = SubstitutionArgumentMappings::error ();
340
341 substitutions.clear ();
342 for (const auto &p : other.get_substs ())
343 substitutions.push_back (p.clone ());
344
345 std::vector<SubstitutionArg> mappings;
346 for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
347 {
348 const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
349 SubstitutionArg arg (oa);
350 mappings.push_back (std::move (arg));
351 }
352
353 // we need to remap the argument mappings based on this copied constructor
354 std::vector<SubstitutionArg> copied_arg_mappings;
355 size_t i = 0;
356 for (const auto &m : other.used_arguments.get_mappings ())
357 {
358 TyTy::BaseType *argument
359 = m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
360 SubstitutionArg c (&substitutions.at (i++), argument);
361 copied_arg_mappings.push_back (std::move (c));
362 }
363
364 used_arguments
365 = SubstitutionArgumentMappings (copied_arg_mappings, {},
366 other.used_arguments.get_locus ());
367
368 return *this;
369 }
370
371 TypeBoundPredicate
372 TypeBoundPredicate::error ()
373 {
374 auto p = TypeBoundPredicate (UNKNOWN_DEFID, {}, Location ());
375 p.error_flag = true;
376 return p;
377 }
378
379 std::string
380 TypeBoundPredicate::as_string () const
381 {
382 return get ()->as_string () + subst_as_string ();
383 }
384
385 std::string
386 TypeBoundPredicate::as_name () const
387 {
388 return get ()->get_name () + subst_as_string ();
389 }
390
391 const Resolver::TraitReference *
392 TypeBoundPredicate::get () const
393 {
394 auto context = Resolver::TypeCheckContext::get ();
395
396 Resolver::TraitReference *ref = nullptr;
397 bool ok = context->lookup_trait_reference (reference, &ref);
398 rust_assert (ok);
399
400 return ref;
401 }
402
403 std::string
404 TypeBoundPredicate::get_name () const
405 {
406 return get ()->get_name ();
407 }
408
409 bool
410 TypeBoundPredicate::is_object_safe (bool emit_error, Location locus) const
411 {
412 const Resolver::TraitReference *trait = get ();
413 rust_assert (trait != nullptr);
414 return trait->is_object_safe (emit_error, locus);
415 }
416
417 void
418 TypeBoundPredicate::apply_generic_arguments (HIR::GenericArgs *generic_args)
419 {
420 // we need to get the substitutions argument mappings but also remember that
421 // we have an implicit Self argument which we must be careful to respect
422 rust_assert (!used_arguments.is_empty ());
423 rust_assert (!substitutions.empty ());
424
425 // now actually perform a substitution
426 used_arguments = get_mappings_from_generic_args (*generic_args);
427
428 error_flag |= used_arguments.is_error ();
429 auto &subst_mappings = used_arguments;
430 for (auto &sub : get_substs ())
431 {
432 SubstitutionArg arg = SubstitutionArg::error ();
433 bool ok
434 = subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
435 if (ok && arg.get_tyty () != nullptr)
436 sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
437 }
438
439 // associated argument mappings
440 for (auto &it : subst_mappings.get_binding_args ())
441 {
442 std::string identifier = it.first;
443 TyTy::BaseType *type = it.second;
444
445 TypeBoundPredicateItem item = lookup_associated_item (identifier);
446 rust_assert (!item.is_error ());
447
448 const auto item_ref = item.get_raw_item ();
449 item_ref->associated_type_set (type);
450 }
451 }
452
453 bool
454 TypeBoundPredicate::contains_item (const std::string &search) const
455 {
456 auto trait_ref = get ();
457 const Resolver::TraitItemReference *trait_item_ref = nullptr;
458 return trait_ref->lookup_trait_item (search, &trait_item_ref);
459 }
460
461 TypeBoundPredicateItem
462 TypeBoundPredicate::lookup_associated_item (const std::string &search) const
463 {
464 auto trait_ref = get ();
465 const Resolver::TraitItemReference *trait_item_ref = nullptr;
466 if (!trait_ref->lookup_trait_item (search, &trait_item_ref))
467 return TypeBoundPredicateItem::error ();
468
469 return TypeBoundPredicateItem (this, trait_item_ref);
470 }
471
472 TypeBoundPredicateItem::TypeBoundPredicateItem (
473 const TypeBoundPredicate *parent,
474 const Resolver::TraitItemReference *trait_item_ref)
475 : parent (parent), trait_item_ref (trait_item_ref)
476 {}
477
478 TypeBoundPredicateItem
479 TypeBoundPredicateItem::error ()
480 {
481 return TypeBoundPredicateItem (nullptr, nullptr);
482 }
483
484 bool
485 TypeBoundPredicateItem::is_error () const
486 {
487 return parent == nullptr || trait_item_ref == nullptr;
488 }
489
490 const TypeBoundPredicate *
491 TypeBoundPredicateItem::get_parent () const
492 {
493 return parent;
494 }
495
496 TypeBoundPredicateItem
497 TypeBoundPredicate::lookup_associated_item (
498 const Resolver::TraitItemReference *ref) const
499 {
500 return lookup_associated_item (ref->get_identifier ());
501 }
502
503 BaseType *
504 TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType *receiver)
505 {
506 TyTy::BaseType *trait_item_tyty = get_raw_item ()->get_tyty ();
507 if (parent->get_substitution_arguments ().is_empty ())
508 return trait_item_tyty;
509
510 const Resolver::TraitItemReference *tref = get_raw_item ();
511 bool is_associated_type = tref->get_trait_item_type ();
512 if (is_associated_type)
513 return trait_item_tyty;
514
515 // set up the self mapping
516 SubstitutionArgumentMappings gargs = parent->get_substitution_arguments ();
517 rust_assert (!gargs.is_empty ());
518
519 // setup the adjusted mappings
520 std::vector<SubstitutionArg> adjusted_mappings;
521 for (size_t i = 0; i < gargs.get_mappings ().size (); i++)
522 {
523 auto &mapping = gargs.get_mappings ().at (i);
524
525 bool is_implicit_self = i == 0;
526 TyTy::BaseType *argument
527 = is_implicit_self ? receiver->clone () : mapping.get_tyty ();
528
529 SubstitutionArg arg (mapping.get_param_mapping (), argument);
530 adjusted_mappings.push_back (std::move (arg));
531 }
532
533 SubstitutionArgumentMappings adjusted (adjusted_mappings, {},
534 gargs.get_locus (),
535 gargs.get_subst_cb (),
536 true /* trait-mode-flag */);
537 return Resolver::SubstMapperInternal::Resolve (trait_item_tyty, adjusted);
538 }
539 bool
540 TypeBoundPredicate::is_error () const
541 {
542 auto context = Resolver::TypeCheckContext::get ();
543
544 Resolver::TraitReference *ref = nullptr;
545 bool ok = context->lookup_trait_reference (reference, &ref);
546
547 return !ok || error_flag;
548 }
549
550 BaseType *
551 TypeBoundPredicate::handle_substitions (
552 SubstitutionArgumentMappings &subst_mappings)
553 {
554 for (auto &sub : get_substs ())
555 {
556 if (sub.get_param_ty () == nullptr)
557 continue;
558
559 ParamType *p = sub.get_param_ty ();
560 BaseType *r = p->resolve ();
561 BaseType *s = Resolver::SubstMapperInternal::Resolve (r, subst_mappings);
562
563 p->set_ty_ref (s->get_ty_ref ());
564 }
565
566 // associated argument mappings
567 for (auto &it : subst_mappings.get_binding_args ())
568 {
569 std::string identifier = it.first;
570 TyTy::BaseType *type = it.second;
571
572 TypeBoundPredicateItem item = lookup_associated_item (identifier);
573 rust_assert (!item.is_error ());
574
575 const auto item_ref = item.get_raw_item ();
576 item_ref->associated_type_set (type);
577 }
578
579 // FIXME more error handling at some point
580 // used_arguments = subst_mappings;
581 // error_flag |= used_arguments.is_error ();
582
583 return nullptr;
584 }
585
586 bool
587 TypeBoundPredicate::requires_generic_args () const
588 {
589 if (is_error ())
590 return false;
591
592 return substitutions.size () > 1;
593 }
594
595 bool
596 TypeBoundPredicate::contains_associated_types () const
597 {
598 return get_num_associated_bindings () > 0;
599 }
600
601 size_t
602 TypeBoundPredicate::get_num_associated_bindings () const
603 {
604 size_t count = 0;
605 auto trait_ref = get ();
606 for (const auto &trait_item : trait_ref->get_trait_items ())
607 {
608 bool is_associated_type
609 = trait_item.get_trait_item_type ()
610 == Resolver::TraitItemReference::TraitItemType::TYPE;
611 if (is_associated_type)
612 count++;
613 }
614 return count;
615 }
616
617 TypeBoundPredicateItem
618 TypeBoundPredicate::lookup_associated_type (const std::string &search)
619 {
620 TypeBoundPredicateItem item = lookup_associated_item (search);
621
622 // only need to check that it is infact an associated type because other
623 // wise if it was not found it will just be an error node anyway
624 if (!item.is_error ())
625 {
626 const auto raw = item.get_raw_item ();
627 if (raw->get_trait_item_type ()
628 != Resolver::TraitItemReference::TraitItemType::TYPE)
629 return TypeBoundPredicateItem::error ();
630 }
631 return item;
632 }
633
634 std::vector<TypeBoundPredicateItem>
635 TypeBoundPredicate::get_associated_type_items ()
636 {
637 std::vector<TypeBoundPredicateItem> items;
638 auto trait_ref = get ();
639 for (const auto &trait_item : trait_ref->get_trait_items ())
640 {
641 bool is_associated_type
642 = trait_item.get_trait_item_type ()
643 == Resolver::TraitItemReference::TraitItemType::TYPE;
644 if (is_associated_type)
645 {
646 TypeBoundPredicateItem item (this, &trait_item);
647 items.push_back (std::move (item));
648 }
649 }
650 return items;
651 }
652
653 // trait item reference
654
655 const Resolver::TraitItemReference *
656 TypeBoundPredicateItem::get_raw_item () const
657 {
658 return trait_item_ref;
659 }
660
661 bool
662 TypeBoundPredicateItem::needs_implementation () const
663 {
664 return !get_raw_item ()->is_optional ();
665 }
666
667 Location
668 TypeBoundPredicateItem::get_locus () const
669 {
670 return get_raw_item ()->get_locus ();
671 }
672
673 // TypeBoundsMappings
674
675 TypeBoundsMappings::TypeBoundsMappings (
676 std::vector<TypeBoundPredicate> specified_bounds)
677 : specified_bounds (specified_bounds)
678 {}
679
680 std::vector<TypeBoundPredicate> &
681 TypeBoundsMappings::get_specified_bounds ()
682 {
683 return specified_bounds;
684 }
685
686 const std::vector<TypeBoundPredicate> &
687 TypeBoundsMappings::get_specified_bounds () const
688 {
689 return specified_bounds;
690 }
691
692 size_t
693 TypeBoundsMappings::num_specified_bounds () const
694 {
695 return specified_bounds.size ();
696 }
697
698 std::string
699 TypeBoundsMappings::raw_bounds_as_string () const
700 {
701 std::string buf;
702 for (size_t i = 0; i < specified_bounds.size (); i++)
703 {
704 const TypeBoundPredicate &b = specified_bounds.at (i);
705 bool has_next = (i + 1) < specified_bounds.size ();
706 buf += b.as_string () + (has_next ? " + " : "");
707 }
708 return buf;
709 }
710
711 std::string
712 TypeBoundsMappings::bounds_as_string () const
713 {
714 return "bounds:[" + raw_bounds_as_string () + "]";
715 }
716
717 std::string
718 TypeBoundsMappings::raw_bounds_as_name () const
719 {
720 std::string buf;
721 for (size_t i = 0; i < specified_bounds.size (); i++)
722 {
723 const TypeBoundPredicate &b = specified_bounds.at (i);
724 bool has_next = (i + 1) < specified_bounds.size ();
725 buf += b.as_name () + (has_next ? " + " : "");
726 }
727
728 return buf;
729 }
730
731 void
732 TypeBoundsMappings::add_bound (TypeBoundPredicate predicate)
733 {
734 for (auto &bound : specified_bounds)
735 {
736 bool same_trait_ref_p = bound.get_id () == predicate.get_id ();
737 if (same_trait_ref_p)
738 return;
739 }
740
741 specified_bounds.push_back (predicate);
742 }
743
744 } // namespace TyTy
745 } // namespace Rust