1 // Copyright (C) 2021-2025 Free Software Foundation, Inc.
3 // This file is part of GCC.
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
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
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/>.
19 #include "rust-hir-type-bounds.h"
20 #include "rust-hir-trait-resolve.h"
21 #include "rust-substitution-mapper.h"
22 #include "rust-type-util.h"
27 TypeBoundsProbe::TypeBoundsProbe (const TyTy::BaseType
*receiver
)
28 : TypeCheckBase (), receiver (receiver
)
31 std::vector
<std::pair
<TraitReference
*, HIR::ImplBlock
*>>
32 TypeBoundsProbe::Probe (const TyTy::BaseType
*receiver
)
34 TypeBoundsProbe
probe (receiver
);
36 return probe
.trait_references
;
40 TypeBoundsProbe::is_bound_satisfied_for_type (TyTy::BaseType
*receiver
,
43 for (auto &bound
: receiver
->get_specified_bounds ())
45 const TraitReference
*b
= bound
.get ();
46 if (b
->is_equal (*ref
))
50 std::vector
<std::pair
<TraitReference
*, HIR::ImplBlock
*>> bounds
52 for (auto &bound
: bounds
)
54 const TraitReference
*b
= bound
.first
;
55 if (b
->is_equal (*ref
))
63 TypeBoundsProbe::scan ()
65 std::vector
<std::pair
<HIR::TypePath
*, HIR::ImplBlock
*>>
67 mappings
->iterate_impl_blocks (
68 [&] (HirId id
, HIR::ImplBlock
*impl
) mutable -> bool {
69 // we are filtering for trait-impl-blocks
70 if (!impl
->has_trait_ref ())
73 HirId impl_ty_id
= impl
->get_type ()->get_mappings ().get_hirid ();
74 TyTy::BaseType
*impl_type
= nullptr;
75 if (!query_type (impl_ty_id
, &impl_type
))
78 if (!receiver
->can_eq (impl_type
, false))
80 if (!impl_type
->can_eq (receiver
, false))
84 possible_trait_paths
.push_back ({impl
->get_trait_ref ().get (), impl
});
88 for (auto &path
: possible_trait_paths
)
90 HIR::TypePath
*trait_path
= path
.first
;
91 TraitReference
*trait_ref
= TraitResolver::Resolve (*trait_path
);
93 if (!trait_ref
->is_error ())
94 trait_references
.push_back ({trait_ref
, path
.second
});
98 assemble_sized_builtin ();
102 TypeBoundsProbe::assemble_sized_builtin ()
104 const TyTy::BaseType
*raw
= receiver
->destructure ();
106 // https://runrust.miraheze.org/wiki/Dynamically_Sized_Type
107 // everything is sized except for:
112 // 4. ADT's which contain any of the above
113 // t. tuples which contain any of the above
114 switch (raw
->get_kind ())
132 case TyTy::PLACEHOLDER
:
133 case TyTy::PROJECTION
:
134 assemble_builtin_candidate (LangItem::Kind::SIZED
);
137 // FIXME str and slice need to be moved and test cases updated
142 // FIXME add extra checks
143 assemble_builtin_candidate (LangItem::Kind::SIZED
);
153 TypeBoundsProbe::assemble_builtin_candidate (LangItem::Kind lang_item
)
156 bool found_lang_item
= mappings
->lookup_lang_item (lang_item
, &id
);
157 if (!found_lang_item
)
160 HIR::Item
*item
= mappings
->lookup_defid (id
);
164 rust_assert (item
->get_item_kind () == HIR::Item::ItemKind::Trait
);
165 HIR::Trait
*trait
= static_cast<HIR::Trait
*> (item
);
166 const TyTy::BaseType
*raw
= receiver
->destructure ();
168 // assemble the reference
169 TraitReference
*trait_ref
= TraitResolver::Resolve (*trait
);
170 trait_references
.push_back ({trait_ref
, mappings
->lookup_builtin_marker ()});
172 rust_debug ("Added builtin lang_item: %s for %s",
173 LangItem::ToString (lang_item
).c_str (),
174 raw
->get_name ().c_str ());
178 TypeCheckBase::resolve_trait_path (HIR::TypePath
&path
)
180 return TraitResolver::Resolve (path
);
183 TyTy::TypeBoundPredicate
184 TypeCheckBase::get_predicate_from_bound (HIR::TypePath
&type_path
,
185 HIR::Type
*associated_self
,
186 BoundPolarity polarity
)
188 TyTy::TypeBoundPredicate lookup
= TyTy::TypeBoundPredicate::error ();
189 bool already_resolved
190 = context
->lookup_predicate (type_path
.get_mappings ().get_hirid (),
192 if (already_resolved
)
195 TraitReference
*trait
= resolve_trait_path (type_path
);
196 if (trait
->is_error ())
197 return TyTy::TypeBoundPredicate::error ();
199 TyTy::TypeBoundPredicate
predicate (*trait
, polarity
, type_path
.get_locus ());
200 HIR::GenericArgs args
201 = HIR::GenericArgs::create_empty (type_path
.get_locus ());
203 auto &final_seg
= type_path
.get_final_segment ();
204 switch (final_seg
->get_type ())
206 case HIR::TypePathSegment::SegmentType::GENERIC
: {
207 auto final_generic_seg
208 = static_cast<HIR::TypePathSegmentGeneric
*> (final_seg
.get ());
209 if (final_generic_seg
->has_generic_args ())
211 args
= final_generic_seg
->get_generic_args ();
216 case HIR::TypePathSegment::SegmentType::FUNCTION
: {
217 auto final_function_seg
218 = static_cast<HIR::TypePathSegmentFunction
*> (final_seg
.get ());
219 auto &fn
= final_function_seg
->get_function_path ();
221 // we need to make implicit generic args which must be an implicit
223 auto crate_num
= mappings
->get_current_crate ();
224 HirId implicit_args_id
= mappings
->get_next_hir_id ();
225 Analysis::NodeMapping
mapping (crate_num
,
226 final_seg
->get_mappings ().get_nodeid (),
227 implicit_args_id
, UNKNOWN_LOCAL_DEFID
);
229 std::vector
<std::unique_ptr
<HIR::Type
>> params_copy
;
230 for (auto &p
: fn
.get_params ())
232 params_copy
.push_back (p
->clone_type ());
235 HIR::TupleType
*implicit_tuple
236 = new HIR::TupleType (mapping
, std::move (params_copy
),
237 final_seg
->get_locus ());
239 std::vector
<std::unique_ptr
<HIR::Type
>> inputs
;
240 inputs
.push_back (std::unique_ptr
<HIR::Type
> (implicit_tuple
));
242 // resolve the fn_once_output type which assumes there must be an output
244 rust_assert (fn
.has_return_type ());
245 TypeCheckType::Resolve (fn
.get_return_type ().get ());
247 HIR::TraitItem
*trait_item
= mappings
->lookup_trait_item_lang_item (
248 LangItem::Kind::FN_ONCE_OUTPUT
, final_seg
->get_locus ());
250 std::vector
<HIR::GenericArgsBinding
> bindings
;
251 location_t output_locus
= fn
.get_return_type ()->get_locus ();
252 HIR::GenericArgsBinding
binding (Identifier (
253 trait_item
->trait_identifier ()),
254 fn
.get_return_type ()->clone_type (),
256 bindings
.push_back (std::move (binding
));
258 args
= HIR::GenericArgs ({} /* lifetimes */,
259 std::move (inputs
) /* type_args*/,
260 std::move (bindings
) /* binding_args*/,
261 {} /* const_args */, final_seg
->get_locus ());
270 if (associated_self
!= nullptr)
272 std::vector
<std::unique_ptr
<HIR::Type
>> type_args
;
273 type_args
.push_back (
274 std::unique_ptr
<HIR::Type
> (associated_self
->clone_type ()));
275 for (auto &arg
: args
.get_type_args ())
277 type_args
.push_back (std::unique_ptr
<HIR::Type
> (arg
->clone_type ()));
280 args
= HIR::GenericArgs (args
.get_lifetime_args (), std::move (type_args
),
281 args
.get_binding_args (), args
.get_const_args (),
285 // we try to apply generic arguments when they are non empty and or when the
286 // predicate requires them so that we get the relevant Foo expects x number
287 // arguments but got zero see test case rust/compile/traits12.rs
288 if (!args
.is_empty () || predicate
.requires_generic_args ())
290 // this is applying generic arguments to a trait reference
291 predicate
.apply_generic_arguments (&args
, associated_self
!= nullptr);
294 context
->insert_resolved_predicate (type_path
.get_mappings ().get_hirid (),
300 } // namespace Resolver
304 TypeBoundPredicate::TypeBoundPredicate (
305 const Resolver::TraitReference
&trait_reference
, BoundPolarity polarity
,
307 : SubstitutionRef ({}, SubstitutionArgumentMappings::empty (), {}),
308 reference (trait_reference
.get_mappings ().get_defid ()), locus (locus
),
309 error_flag (false), polarity (polarity
)
311 rust_assert (!trait_reference
.get_trait_substs ().empty ());
313 substitutions
.clear ();
314 for (const auto &p
: trait_reference
.get_trait_substs ())
315 substitutions
.push_back (p
.clone ());
317 // we setup a dummy implict self argument
318 SubstitutionArg
placeholder_self (&get_substs ().front (), nullptr);
319 used_arguments
.get_mappings ().push_back (placeholder_self
);
322 TypeBoundPredicate::TypeBoundPredicate (
323 DefId reference
, std::vector
<SubstitutionParamMapping
> subst
,
324 BoundPolarity polarity
, location_t locus
)
325 : SubstitutionRef ({}, SubstitutionArgumentMappings::empty (), {}),
326 reference (reference
), locus (locus
), error_flag (false),
329 rust_assert (!subst
.empty ());
331 substitutions
.clear ();
332 for (const auto &p
: subst
)
333 substitutions
.push_back (p
.clone ());
335 // we setup a dummy implict self argument
336 SubstitutionArg
placeholder_self (&get_substs ().front (), nullptr);
337 used_arguments
.get_mappings ().push_back (placeholder_self
);
340 TypeBoundPredicate::TypeBoundPredicate (mark_is_error
)
341 : SubstitutionRef ({}, SubstitutionArgumentMappings::empty (), {}),
342 reference (UNKNOWN_DEFID
), locus (UNDEF_LOCATION
), error_flag (true),
343 polarity (BoundPolarity::RegularBound
)
346 TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate
&other
)
347 : SubstitutionRef ({}, SubstitutionArgumentMappings::empty (), {}),
348 reference (other
.reference
), locus (other
.locus
),
349 error_flag (other
.error_flag
), polarity (other
.polarity
)
351 substitutions
.clear ();
352 for (const auto &p
: other
.get_substs ())
353 substitutions
.push_back (p
.clone ());
355 std::vector
<SubstitutionArg
> mappings
;
356 for (size_t i
= 0; i
< other
.used_arguments
.get_mappings ().size (); i
++)
358 const SubstitutionArg
&oa
= other
.used_arguments
.get_mappings ().at (i
);
359 SubstitutionArg
arg (oa
);
360 mappings
.push_back (std::move (arg
));
363 // we need to remap the argument mappings based on this copied constructor
364 std::vector
<SubstitutionArg
> copied_arg_mappings
;
366 for (const auto &m
: other
.used_arguments
.get_mappings ())
368 TyTy::BaseType
*argument
369 = m
.get_tyty () == nullptr ? nullptr : m
.get_tyty ()->clone ();
370 SubstitutionArg
c (&substitutions
.at (i
++), argument
);
371 copied_arg_mappings
.push_back (std::move (c
));
375 = SubstitutionArgumentMappings (copied_arg_mappings
, {},
376 other
.used_arguments
.get_regions (),
377 other
.used_arguments
.get_locus ());
381 TypeBoundPredicate::operator= (const TypeBoundPredicate
&other
)
383 reference
= other
.reference
;
385 error_flag
= other
.error_flag
;
386 polarity
= other
.polarity
;
387 used_arguments
= SubstitutionArgumentMappings::empty ();
389 substitutions
.clear ();
390 for (const auto &p
: other
.get_substs ())
391 substitutions
.push_back (p
.clone ());
393 if (other
.is_error ())
396 std::vector
<SubstitutionArg
> mappings
;
397 for (size_t i
= 0; i
< other
.used_arguments
.get_mappings ().size (); i
++)
399 const SubstitutionArg
&oa
= other
.used_arguments
.get_mappings ().at (i
);
400 SubstitutionArg
arg (oa
);
401 mappings
.push_back (std::move (arg
));
404 // we need to remap the argument mappings based on this copied constructor
405 std::vector
<SubstitutionArg
> copied_arg_mappings
;
407 for (const auto &m
: other
.used_arguments
.get_mappings ())
409 TyTy::BaseType
*argument
410 = m
.get_tyty () == nullptr ? nullptr : m
.get_tyty ()->clone ();
411 SubstitutionArg
c (&substitutions
.at (i
++), argument
);
412 copied_arg_mappings
.push_back (std::move (c
));
416 = SubstitutionArgumentMappings (copied_arg_mappings
, {},
417 other
.used_arguments
.get_regions (),
418 other
.used_arguments
.get_locus ());
424 TypeBoundPredicate::error ()
426 return TypeBoundPredicate (mark_is_error ());
430 TypeBoundPredicate::as_string () const
432 return get ()->as_string () + subst_as_string ();
436 TypeBoundPredicate::as_name () const
438 return get ()->get_name () + subst_as_string ();
441 const Resolver::TraitReference
*
442 TypeBoundPredicate::get () const
444 auto context
= Resolver::TypeCheckContext::get ();
446 Resolver::TraitReference
*ref
= nullptr;
447 bool ok
= context
->lookup_trait_reference (reference
, &ref
);
454 TypeBoundPredicate::get_name () const
456 return get ()->get_name ();
460 TypeBoundPredicate::is_object_safe (bool emit_error
, location_t locus
) const
462 const Resolver::TraitReference
*trait
= get ();
463 rust_assert (trait
!= nullptr);
464 return trait
->is_object_safe (emit_error
, locus
);
468 TypeBoundPredicate::apply_generic_arguments (HIR::GenericArgs
*generic_args
,
469 bool has_associated_self
)
471 rust_assert (!substitutions
.empty ());
472 if (has_associated_self
)
474 used_arguments
= SubstitutionArgumentMappings::empty ();
478 // we need to get the substitutions argument mappings but also remember
479 // that we have an implicit Self argument which we must be careful to
481 rust_assert (!used_arguments
.is_empty ());
484 // now actually perform a substitution
485 used_arguments
= get_mappings_from_generic_args (
487 Resolver::TypeCheckContext::get ()->regions_from_generic_args (
490 error_flag
|= used_arguments
.is_error ();
491 auto &subst_mappings
= used_arguments
;
492 for (auto &sub
: get_substs ())
494 SubstitutionArg arg
= SubstitutionArg::error ();
496 = subst_mappings
.get_argument_for_symbol (sub
.get_param_ty (), &arg
);
497 if (ok
&& arg
.get_tyty () != nullptr)
498 sub
.fill_param_ty (subst_mappings
, subst_mappings
.get_locus ());
501 // associated argument mappings
502 for (auto &it
: subst_mappings
.get_binding_args ())
504 std::string identifier
= it
.first
;
505 TyTy::BaseType
*type
= it
.second
;
507 TypeBoundPredicateItem item
= lookup_associated_item (identifier
);
508 rust_assert (!item
.is_error ());
510 const auto item_ref
= item
.get_raw_item ();
511 item_ref
->associated_type_set (type
);
516 TypeBoundPredicate::contains_item (const std::string
&search
) const
518 auto trait_ref
= get ();
519 const Resolver::TraitItemReference
*trait_item_ref
= nullptr;
520 return trait_ref
->lookup_trait_item (search
, &trait_item_ref
);
523 TypeBoundPredicateItem
524 TypeBoundPredicate::lookup_associated_item (const std::string
&search
) const
526 auto trait_ref
= get ();
527 const Resolver::TraitItemReference
*trait_item_ref
= nullptr;
528 if (!trait_ref
->lookup_trait_item (search
, &trait_item_ref
))
529 return TypeBoundPredicateItem::error ();
531 return TypeBoundPredicateItem (this, trait_item_ref
);
534 TypeBoundPredicateItem::TypeBoundPredicateItem (
535 const TypeBoundPredicate
*parent
,
536 const Resolver::TraitItemReference
*trait_item_ref
)
537 : parent (parent
), trait_item_ref (trait_item_ref
)
540 TypeBoundPredicateItem
541 TypeBoundPredicateItem::error ()
543 return TypeBoundPredicateItem (nullptr, nullptr);
547 TypeBoundPredicateItem::is_error () const
549 return parent
== nullptr || trait_item_ref
== nullptr;
552 const TypeBoundPredicate
*
553 TypeBoundPredicateItem::get_parent () const
558 TypeBoundPredicateItem
559 TypeBoundPredicate::lookup_associated_item (
560 const Resolver::TraitItemReference
*ref
) const
562 return lookup_associated_item (ref
->get_identifier ());
566 TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType
*receiver
)
568 TyTy::BaseType
*trait_item_tyty
= get_raw_item ()->get_tyty ();
569 if (parent
->get_substitution_arguments ().is_empty ())
570 return trait_item_tyty
;
572 const Resolver::TraitItemReference
*tref
= get_raw_item ();
573 bool is_associated_type
= tref
->get_trait_item_type ();
574 if (is_associated_type
)
575 return trait_item_tyty
;
577 // set up the self mapping
578 SubstitutionArgumentMappings gargs
= parent
->get_substitution_arguments ();
579 rust_assert (!gargs
.is_empty ());
581 // setup the adjusted mappings
582 std::vector
<SubstitutionArg
> adjusted_mappings
;
583 for (size_t i
= 0; i
< gargs
.get_mappings ().size (); i
++)
585 auto &mapping
= gargs
.get_mappings ().at (i
);
587 bool is_implicit_self
= i
== 0;
588 TyTy::BaseType
*argument
589 = is_implicit_self
? receiver
->clone () : mapping
.get_tyty ();
591 SubstitutionArg
arg (mapping
.get_param_mapping (), argument
);
592 adjusted_mappings
.push_back (std::move (arg
));
595 SubstitutionArgumentMappings
adjusted (adjusted_mappings
, {},
596 gargs
.get_regions (),
598 gargs
.get_subst_cb (),
599 true /* trait-mode-flag */);
600 return Resolver::SubstMapperInternal::Resolve (trait_item_tyty
, adjusted
);
603 TypeBoundPredicate::is_error () const
605 auto context
= Resolver::TypeCheckContext::get ();
607 Resolver::TraitReference
*ref
= nullptr;
608 bool ok
= context
->lookup_trait_reference (reference
, &ref
);
610 return !ok
|| error_flag
;
614 TypeBoundPredicate::handle_substitions (
615 SubstitutionArgumentMappings
&subst_mappings
)
617 for (auto &sub
: get_substs ())
619 if (sub
.get_param_ty () == nullptr)
622 ParamType
*p
= sub
.get_param_ty ();
623 BaseType
*r
= p
->resolve ();
624 BaseType
*s
= Resolver::SubstMapperInternal::Resolve (r
, subst_mappings
);
626 p
->set_ty_ref (s
->get_ty_ref ());
629 // associated argument mappings
630 for (auto &it
: subst_mappings
.get_binding_args ())
632 std::string identifier
= it
.first
;
633 TyTy::BaseType
*type
= it
.second
;
635 TypeBoundPredicateItem item
= lookup_associated_item (identifier
);
636 if (!item
.is_error ())
638 const auto item_ref
= item
.get_raw_item ();
639 item_ref
->associated_type_set (type
);
643 // FIXME more error handling at some point
644 // used_arguments = subst_mappings;
645 // error_flag |= used_arguments.is_error ();
651 TypeBoundPredicate::requires_generic_args () const
656 return substitutions
.size () > 1;
660 TypeBoundPredicate::contains_associated_types () const
662 return get_num_associated_bindings () > 0;
666 TypeBoundPredicate::get_num_associated_bindings () const
669 auto trait_ref
= get ();
670 for (const auto &trait_item
: trait_ref
->get_trait_items ())
672 bool is_associated_type
673 = trait_item
.get_trait_item_type ()
674 == Resolver::TraitItemReference::TraitItemType::TYPE
;
675 if (is_associated_type
)
681 TypeBoundPredicateItem
682 TypeBoundPredicate::lookup_associated_type (const std::string
&search
)
684 TypeBoundPredicateItem item
= lookup_associated_item (search
);
686 // only need to check that it is infact an associated type because other
687 // wise if it was not found it will just be an error node anyway
688 if (!item
.is_error ())
690 const auto raw
= item
.get_raw_item ();
691 if (raw
->get_trait_item_type ()
692 != Resolver::TraitItemReference::TraitItemType::TYPE
)
693 return TypeBoundPredicateItem::error ();
698 std::vector
<TypeBoundPredicateItem
>
699 TypeBoundPredicate::get_associated_type_items ()
701 std::vector
<TypeBoundPredicateItem
> items
;
702 auto trait_ref
= get ();
703 for (const auto &trait_item
: trait_ref
->get_trait_items ())
705 bool is_associated_type
706 = trait_item
.get_trait_item_type ()
707 == Resolver::TraitItemReference::TraitItemType::TYPE
;
708 if (is_associated_type
)
710 TypeBoundPredicateItem
item (this, &trait_item
);
711 items
.push_back (std::move (item
));
718 TypeBoundPredicate::is_equal (const TypeBoundPredicate
&other
) const
720 // check they match the same trait reference
721 if (reference
!= other
.reference
)
724 // check that the generics match
725 if (get_num_substitutions () != other
.get_num_substitutions ())
728 // then match the generics applied
729 for (size_t i
= 0; i
< get_num_substitutions (); i
++)
731 const SubstitutionParamMapping
&a
= substitutions
.at (i
);
732 const SubstitutionParamMapping
&b
= other
.substitutions
.at (i
);
734 const ParamType
*ap
= a
.get_param_ty ();
735 const ParamType
*bp
= b
.get_param_ty ();
737 const BaseType
*apd
= ap
->destructure ();
738 const BaseType
*bpd
= bp
->destructure ();
740 // FIXME use the unify_and infer inteface or try coerce
741 if (!apd
->can_eq (bpd
, false /*emit_errors*/))
743 if (!bpd
->can_eq (apd
, false /*emit_errors*/))
751 // trait item reference
753 const Resolver::TraitItemReference
*
754 TypeBoundPredicateItem::get_raw_item () const
756 return trait_item_ref
;
760 TypeBoundPredicateItem::needs_implementation () const
762 return !get_raw_item ()->is_optional ();
766 TypeBoundPredicateItem::get_locus () const
768 return get_raw_item ()->get_locus ();
771 // TypeBoundsMappings
773 TypeBoundsMappings::TypeBoundsMappings (
774 std::vector
<TypeBoundPredicate
> specified_bounds
)
775 : specified_bounds (specified_bounds
)
778 std::vector
<TypeBoundPredicate
> &
779 TypeBoundsMappings::get_specified_bounds ()
781 return specified_bounds
;
784 const std::vector
<TypeBoundPredicate
> &
785 TypeBoundsMappings::get_specified_bounds () const
787 return specified_bounds
;
791 TypeBoundsMappings::lookup_predicate (DefId id
)
793 for (auto &b
: specified_bounds
)
795 if (b
.get_id () == id
)
798 return TypeBoundPredicate::error ();
802 TypeBoundsMappings::num_specified_bounds () const
804 return specified_bounds
.size ();
808 TypeBoundsMappings::raw_bounds_as_string () const
811 for (size_t i
= 0; i
< specified_bounds
.size (); i
++)
813 const TypeBoundPredicate
&b
= specified_bounds
.at (i
);
814 bool has_next
= (i
+ 1) < specified_bounds
.size ();
815 buf
+= b
.as_string () + (has_next
? " + " : "");
821 TypeBoundsMappings::bounds_as_string () const
823 return "bounds:[" + raw_bounds_as_string () + "]";
827 TypeBoundsMappings::raw_bounds_as_name () const
830 for (size_t i
= 0; i
< specified_bounds
.size (); i
++)
832 const TypeBoundPredicate
&b
= specified_bounds
.at (i
);
833 bool has_next
= (i
+ 1) < specified_bounds
.size ();
834 buf
+= b
.as_name () + (has_next
? " + " : "");
841 TypeBoundsMappings::add_bound (TypeBoundPredicate predicate
)
843 for (auto &bound
: specified_bounds
)
845 bool same_trait_ref_p
= bound
.get_id () == predicate
.get_id ();
846 if (same_trait_ref_p
)
850 specified_bounds
.push_back (predicate
);