Branch data Line data Source code
1 : : // Copyright (C) 2020-2025 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-dot-operator.h"
20 : : #include "rust-hir-path-probe.h"
21 : : #include "rust-hir-trait-resolve.h"
22 : : #include "rust-hir-type-check-item.h"
23 : : #include "rust-type-util.h"
24 : : #include "rust-coercion.h"
25 : :
26 : : namespace Rust {
27 : : namespace Resolver {
28 : :
29 : 5836 : MethodResolver::MethodResolver (bool autoderef_flag,
30 : 5836 : const HIR::PathIdentSegment &segment_name)
31 : 5836 : : AutoderefCycle (autoderef_flag), segment_name (segment_name), result ()
32 : 5836 : {}
33 : :
34 : : std::set<MethodCandidate>
35 : 5836 : MethodResolver::Probe (TyTy::BaseType *receiver,
36 : : const HIR::PathIdentSegment &segment_name,
37 : : bool autoderef_flag)
38 : : {
39 : 5836 : MethodResolver resolver (autoderef_flag, segment_name);
40 : 5836 : resolver.cycle (receiver);
41 : 5836 : return resolver.result;
42 : 5836 : }
43 : :
44 : : std::set<MethodCandidate>
45 : 2809 : MethodResolver::Select (std::set<MethodCandidate> &candidates,
46 : : TyTy::BaseType *receiver,
47 : : std::vector<TyTy::BaseType *> arguments)
48 : : {
49 : 2809 : std::set<MethodCandidate> selected;
50 : 4465 : for (auto &candidate : candidates)
51 : : {
52 : 1656 : TyTy::BaseType *candidate_type = candidate.candidate.ty;
53 : 1656 : rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF);
54 : 1656 : TyTy::FnType &fn = *static_cast<TyTy::FnType *> (candidate_type);
55 : :
56 : : // match the number of arguments
57 : 1656 : if (fn.num_params () != (arguments.size () + 1))
58 : 0 : continue;
59 : :
60 : : // match the arguments
61 : 2953 : bool failed = false;
62 : 2953 : for (size_t i = 0; i < arguments.size (); i++)
63 : : {
64 : 1324 : TyTy::BaseType *arg = arguments.at (i);
65 : 1324 : TyTy::BaseType *param = fn.get_params ().at (i + 1).get_type ();
66 : 1324 : TyTy::BaseType *coerced
67 : 1324 : = try_coercion (0, TyTy::TyWithLocation (param),
68 : 1324 : TyTy::TyWithLocation (arg), UNDEF_LOCATION);
69 : 1324 : if (coerced->get_kind () == TyTy::TypeKind::ERROR)
70 : : {
71 : : failed = true;
72 : : break;
73 : : }
74 : : }
75 : :
76 : 1656 : if (!failed)
77 : 1629 : selected.insert (candidate);
78 : : }
79 : :
80 : 2809 : return selected;
81 : : }
82 : :
83 : : void
84 : 5993 : MethodResolver::try_hook (const TyTy::BaseType &r)
85 : : {
86 : 5993 : rust_debug ("MethodResolver::try_hook get_predicate_items: [%s]",
87 : : r.debug_str ().c_str ());
88 : 5993 : const auto &specified_bounds = r.get_specified_bounds ();
89 : 5993 : predicate_items = get_predicate_items (segment_name, r, specified_bounds);
90 : :
91 : 5993 : if (predicate_items.size () > 0)
92 : : return;
93 : :
94 : 5601 : if (r.get_kind () == TyTy::TypeKind::REF)
95 : : {
96 : 1156 : const auto &ref = static_cast<const TyTy::ReferenceType &> (r);
97 : 1156 : const auto &element = ref.get_var_element_type ();
98 : 1156 : const auto &element_ty = *element.get_tyty ();
99 : 1156 : const auto &specified_bounds = element_ty.get_specified_bounds ();
100 : 1156 : predicate_items
101 : 1156 : = get_predicate_items (segment_name, element_ty, specified_bounds);
102 : : }
103 : : }
104 : :
105 : : std::vector<MethodResolver::impl_item_candidate>
106 : 8900 : MethodResolver::assemble_inherent_impl_candidates (
107 : : const TyTy::BaseType &receiver)
108 : : {
109 : 8900 : std::vector<impl_item_candidate> inherent_impl_fns;
110 : 8900 : const TyTy::BaseType *raw = receiver.destructure ();
111 : 8900 : bool receiver_is_raw_ptr = raw->get_kind () == TyTy::TypeKind::POINTER;
112 : 8900 : bool receiver_is_ref = raw->get_kind () == TyTy::TypeKind::REF;
113 : :
114 : : // Assemble inherent impl items (non-trait impl blocks)
115 : 8900 : mappings.iterate_impl_items (
116 : 8900 : [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
117 : 237197 : bool is_trait_impl = impl->has_trait_ref ();
118 : 237197 : if (is_trait_impl)
119 : : return true;
120 : :
121 : 77266 : bool is_fn
122 : 77266 : = item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION;
123 : 77266 : if (!is_fn)
124 : : return true;
125 : :
126 : 77266 : HIR::Function *func = static_cast<HIR::Function *> (item);
127 : 77266 : if (!func->is_method ())
128 : : return true;
129 : :
130 : 59788 : bool name_matches = func->get_function_name ().as_string ().compare (
131 : 119576 : segment_name.as_string ())
132 : 59788 : == 0;
133 : 59788 : if (!name_matches)
134 : : return true;
135 : :
136 : 6869 : TyTy::BaseType *ty = nullptr;
137 : 6869 : if (!query_type (func->get_mappings ().get_hirid (), &ty))
138 : : return true;
139 : 6869 : rust_assert (ty != nullptr);
140 : 6869 : if (ty->get_kind () == TyTy::TypeKind::ERROR)
141 : : return true;
142 : :
143 : 6869 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
144 : 6869 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
145 : 6869 : const TyTy::BaseType *impl_self
146 : 6869 : = TypeCheckItem::ResolveImplBlockSelf (*impl);
147 : :
148 : : // see:
149 : : // https://gcc-rust.zulipchat.com/#narrow/stream/266897-general/topic/Method.20Resolution/near/338646280
150 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/method/probe.rs#L650-L660
151 : 6869 : bool impl_self_is_ptr = impl_self->get_kind () == TyTy::TypeKind::POINTER;
152 : 6869 : bool impl_self_is_ref = impl_self->get_kind () == TyTy::TypeKind::REF;
153 : 6869 : if (receiver_is_raw_ptr && impl_self_is_ptr)
154 : : {
155 : 493 : const TyTy::PointerType &sptr
156 : : = *static_cast<const TyTy::PointerType *> (impl_self);
157 : 493 : const TyTy::PointerType &ptr
158 : 493 : = *static_cast<const TyTy::PointerType *> (raw);
159 : :
160 : : // we could do this via lang-item assemblies if we refactor this
161 : 493 : bool mut_match = sptr.mutability () == ptr.mutability ();
162 : 493 : if (!mut_match)
163 : : return true;
164 : : }
165 : 6376 : else if (receiver_is_ref && impl_self_is_ref)
166 : : {
167 : 0 : const TyTy::ReferenceType &sptr
168 : : = *static_cast<const TyTy::ReferenceType *> (impl_self);
169 : 0 : const TyTy::ReferenceType &ptr
170 : 0 : = *static_cast<const TyTy::ReferenceType *> (raw);
171 : :
172 : : // we could do this via lang-item assemblies if we refactor this
173 : 0 : bool mut_match = sptr.mutability () == ptr.mutability ();
174 : 0 : if (!mut_match)
175 : : return true;
176 : : }
177 : :
178 : 6760 : inherent_impl_fns.push_back ({func, impl, fnty});
179 : :
180 : 6760 : return true;
181 : : });
182 : :
183 : 8900 : return inherent_impl_fns;
184 : : }
185 : :
186 : : void
187 : 8900 : MethodResolver::assemble_trait_impl_candidates (
188 : : const TyTy::BaseType &receiver,
189 : : std::vector<impl_item_candidate> &impl_candidates,
190 : : std::vector<trait_item_candidate> &trait_candidates)
191 : : {
192 : 8900 : const TyTy::BaseType *raw = receiver.destructure ();
193 : 8900 : bool receiver_is_raw_ptr = raw->get_kind () == TyTy::TypeKind::POINTER;
194 : 8900 : bool receiver_is_ref = raw->get_kind () == TyTy::TypeKind::REF;
195 : :
196 : 8900 : mappings.iterate_impl_blocks ([&] (HirId id,
197 : : HIR::ImplBlock *impl) mutable -> bool {
198 : 131753 : bool is_trait_impl = impl->has_trait_ref ();
199 : 131753 : if (!is_trait_impl)
200 : : return true;
201 : :
202 : : // look for impl implementation else lookup the associated trait item
203 : 241966 : for (auto &impl_item : impl->get_impl_items ())
204 : : {
205 : 149491 : bool is_fn = impl_item->get_impl_item_type ()
206 : 149491 : == HIR::ImplItem::ImplItemType::FUNCTION;
207 : 149491 : if (!is_fn)
208 : 129909 : continue;
209 : :
210 : 115322 : HIR::Function *func = static_cast<HIR::Function *> (impl_item.get ());
211 : 115322 : if (!func->is_method ())
212 : 6742 : continue;
213 : :
214 : 108580 : bool name_matches = func->get_function_name ().as_string ().compare (
215 : 217160 : segment_name.as_string ())
216 : 108580 : == 0;
217 : 108580 : if (!name_matches)
218 : 88743 : continue;
219 : :
220 : 19837 : TyTy::BaseType *ty = nullptr;
221 : 19837 : if (!query_type (func->get_mappings ().get_hirid (), &ty))
222 : 0 : continue;
223 : 19837 : if (ty->get_kind () == TyTy::TypeKind::ERROR)
224 : 0 : continue;
225 : :
226 : 19837 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
227 : 19837 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
228 : 19837 : const TyTy::BaseType *impl_self
229 : 19837 : = TypeCheckItem::ResolveImplBlockSelf (*impl);
230 : :
231 : : // see:
232 : : // https://gcc-rust.zulipchat.com/#narrow/stream/266897-general/topic/Method.20Resolution/near/338646280
233 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/method/probe.rs#L650-L660
234 : 19837 : bool impl_self_is_ptr
235 : 19837 : = impl_self->get_kind () == TyTy::TypeKind::POINTER;
236 : 19837 : bool impl_self_is_ref = impl_self->get_kind () == TyTy::TypeKind::REF;
237 : 19837 : if (receiver_is_raw_ptr && impl_self_is_ptr)
238 : : {
239 : 0 : const TyTy::PointerType &sptr
240 : : = *static_cast<const TyTy::PointerType *> (impl_self);
241 : 0 : const TyTy::PointerType &ptr
242 : 0 : = *static_cast<const TyTy::PointerType *> (raw);
243 : :
244 : : // we could do this via lang-item assemblies if we refactor this
245 : 0 : bool mut_match = sptr.mutability () == ptr.mutability ();
246 : 0 : if (!mut_match)
247 : 0 : continue;
248 : : }
249 : 19837 : else if (receiver_is_ref && impl_self_is_ref)
250 : : {
251 : 545 : const TyTy::ReferenceType &sptr
252 : : = *static_cast<const TyTy::ReferenceType *> (impl_self);
253 : 545 : const TyTy::ReferenceType &ptr
254 : 545 : = *static_cast<const TyTy::ReferenceType *> (raw);
255 : :
256 : : // we could do this via lang-item assemblies if we refactor this
257 : 545 : bool mut_match = sptr.mutability () == ptr.mutability ();
258 : 545 : if (!mut_match)
259 : 255 : continue;
260 : : }
261 : :
262 : 19582 : impl_candidates.push_back ({func, impl, fnty});
263 : 19582 : return true;
264 : : }
265 : :
266 : 92475 : TraitReference *trait_ref = TraitResolver::Resolve (impl->get_trait_ref ());
267 : 92475 : rust_assert (!trait_ref->is_error ());
268 : :
269 : 92475 : auto item_ref
270 : 184950 : = trait_ref->lookup_trait_item (segment_name.as_string (),
271 : : TraitItemReference::TraitItemType::FN);
272 : 92475 : if (item_ref->is_error ())
273 : : return true;
274 : :
275 : 15477 : const HIR::Trait *trait = trait_ref->get_hir_trait_ref ();
276 : 15477 : HIR::TraitItem *item = item_ref->get_hir_trait_item ();
277 : 15477 : if (item->get_item_kind () != HIR::TraitItem::TraitItemKind::FUNC)
278 : : return true;
279 : :
280 : 15477 : HIR::TraitItemFunc *func = static_cast<HIR::TraitItemFunc *> (item);
281 : 15477 : if (!func->get_decl ().is_method ())
282 : : return true;
283 : :
284 : 15474 : TyTy::BaseType *ty = item_ref->get_tyty ();
285 : 15474 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
286 : 15474 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
287 : :
288 : 15474 : trait_item_candidate candidate{func, trait, fnty, trait_ref, item_ref};
289 : 15474 : trait_candidates.push_back (candidate);
290 : :
291 : 15474 : return true;
292 : : });
293 : 8900 : }
294 : :
295 : : bool
296 : 8900 : MethodResolver::try_select_predicate_candidates (TyTy::BaseType &receiver)
297 : : {
298 : 8900 : bool found_possible_candidate = false;
299 : 10397 : for (const auto &predicate : predicate_items)
300 : : {
301 : 1497 : const TyTy::FnType *fn = predicate.fntype;
302 : 2994 : rust_assert (fn->is_method ());
303 : :
304 : 1497 : TyTy::BaseType *fn_self = fn->get_self_type ();
305 : 1497 : rust_debug ("dot-operator predicate fn_self={%s} can_eq receiver={%s}",
306 : : fn_self->debug_str ().c_str (),
307 : : receiver.debug_str ().c_str ());
308 : :
309 : 1497 : auto res
310 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
311 : 1497 : false /*allow-autoderef*/);
312 : 1497 : bool ok = !res.is_error ();
313 : 1497 : if (ok)
314 : : {
315 : 1259 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
316 : 1259 : const TraitReference *trait_ref
317 : 1259 : = predicate.lookup.get_parent ()->get ();
318 : 1259 : const TraitItemReference *trait_item
319 : 1259 : = predicate.lookup.get_raw_item ();
320 : :
321 : 1259 : PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item,
322 : 1259 : nullptr};
323 : 1259 : auto try_result = MethodCandidate{
324 : : PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
325 : : fn->clone (), trait_item->get_locus (), c),
326 : 1259 : adjs};
327 : 1259 : result.insert (std::move (try_result));
328 : 1259 : found_possible_candidate = true;
329 : 1259 : }
330 : 1497 : }
331 : 8900 : return found_possible_candidate;
332 : : }
333 : :
334 : : bool
335 : 13738 : MethodResolver::try_select_inherent_impl_candidates (
336 : : TyTy::BaseType &receiver, const std::vector<impl_item_candidate> &candidates,
337 : : bool trait_impl_blocks_only)
338 : : {
339 : 13738 : bool found_possible_candidate = false;
340 : 54099 : for (auto &impl_item : candidates)
341 : : {
342 : 40361 : bool is_trait_impl_block = impl_item.impl_block->has_trait_ref ();
343 : 40361 : if (trait_impl_blocks_only && !is_trait_impl_block)
344 : 17432 : continue;
345 : 39498 : if (!trait_impl_blocks_only && is_trait_impl_block)
346 : 16569 : continue;
347 : :
348 : 22929 : TyTy::FnType *fn = impl_item.ty;
349 : 45858 : rust_assert (fn->is_method ());
350 : :
351 : 22929 : TyTy::BaseType *fn_self = fn->get_self_type ();
352 : :
353 : 6760 : const char *debug_prefix
354 : 22929 : = trait_impl_blocks_only ? "trait_impl_item" : "impl_item";
355 : 22929 : rust_debug ("dot-operator %s fn_self={%s} can_eq receiver={%s}",
356 : : debug_prefix, fn_self->debug_str ().c_str (),
357 : : receiver.debug_str ().c_str ());
358 : :
359 : 22929 : auto res
360 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
361 : 22929 : false /*allow-autoderef*/);
362 : 22929 : bool ok = !res.is_error ();
363 : 22929 : if (ok)
364 : : {
365 : 4503 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
366 : 4503 : PathProbeCandidate::ImplItemCandidate c{impl_item.item,
367 : 4503 : impl_item.impl_block};
368 : 4503 : auto try_result = MethodCandidate{
369 : : PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
370 : 4503 : fn, impl_item.item->get_locus (), c),
371 : 4503 : adjs};
372 : 4503 : result.insert (std::move (try_result));
373 : 4503 : found_possible_candidate = true;
374 : 4503 : }
375 : 22929 : }
376 : 13738 : return found_possible_candidate;
377 : : }
378 : :
379 : : bool
380 : 3291 : MethodResolver::try_select_trait_impl_candidates (
381 : : TyTy::BaseType &receiver, const std::vector<trait_item_candidate> &candidates)
382 : : {
383 : 3291 : bool found_possible_candidate = false;
384 : 10418 : for (auto trait_item : candidates)
385 : : {
386 : 7127 : TyTy::FnType *fn = trait_item.ty;
387 : 14254 : rust_assert (fn->is_method ());
388 : :
389 : 7127 : TyTy::BaseType *fn_self = fn->get_self_type ();
390 : 7127 : rust_debug ("dot-operator trait_item fn_self={%s} can_eq receiver={%s}",
391 : : fn_self->debug_str ().c_str (),
392 : : receiver.debug_str ().c_str ());
393 : :
394 : 7127 : auto res
395 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
396 : 7127 : false /*allow-autoderef*/);
397 : 7127 : bool ok = !res.is_error ();
398 : 7127 : if (ok)
399 : : {
400 : 531 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
401 : 531 : PathProbeCandidate::TraitItemCandidate c{trait_item.reference,
402 : : trait_item.item_ref,
403 : 531 : nullptr};
404 : 531 : auto try_result = MethodCandidate{
405 : : PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
406 : 531 : fn, trait_item.item->get_locus (), c),
407 : 531 : adjs};
408 : 531 : result.insert (std::move (try_result));
409 : 531 : found_possible_candidate = true;
410 : 531 : }
411 : 7127 : }
412 : 3291 : return found_possible_candidate;
413 : : }
414 : :
415 : : bool
416 : 8900 : MethodResolver::select (TyTy::BaseType &receiver)
417 : : {
418 : 17800 : rust_debug ("MethodResolver::select reciever=[%s] path=[%s]",
419 : : receiver.debug_str ().c_str (),
420 : : segment_name.as_string ().c_str ());
421 : :
422 : : // Assemble candidates
423 : 8900 : std::vector<impl_item_candidate> inherent_impl_fns
424 : 8900 : = assemble_inherent_impl_candidates (receiver);
425 : 8900 : std::vector<impl_item_candidate> trait_impl_fns;
426 : 8900 : std::vector<trait_item_candidate> trait_fns;
427 : 8900 : assemble_trait_impl_candidates (receiver, trait_impl_fns, trait_fns);
428 : :
429 : : // Combine inherent and trait impl functions
430 : 8900 : inherent_impl_fns.insert (inherent_impl_fns.end (), trait_impl_fns.begin (),
431 : : trait_impl_fns.end ());
432 : :
433 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/method/probe.rs#L580-L694
434 : :
435 : 8900 : rust_debug ("inherent_impl_fns found {%lu}, trait_fns found {%lu}, "
436 : : "predicate_items found {%lu}",
437 : : (unsigned long) inherent_impl_fns.size (),
438 : : (unsigned long) trait_fns.size (),
439 : : (unsigned long) predicate_items.size ());
440 : :
441 : : // Try selection in the priority order defined by Rust's method resolution:
442 : :
443 : : // 1. Try predicate candidates first (highest priority)
444 : 8900 : if (try_select_predicate_candidates (receiver))
445 : : return true;
446 : :
447 : : // 2. Try inherent impl functions (non-trait impl blocks)
448 : 7641 : if (try_select_inherent_impl_candidates (receiver, inherent_impl_fns, false))
449 : : return true;
450 : :
451 : : // 3. Try inherent impl functions from trait impl blocks
452 : 6097 : if (try_select_inherent_impl_candidates (receiver, inherent_impl_fns, true))
453 : : return true;
454 : :
455 : : // 4. Try trait functions (lowest priority)
456 : 3291 : return try_select_trait_impl_candidates (receiver, trait_fns);
457 : 8900 : }
458 : :
459 : : std::vector<MethodResolver::predicate_candidate>
460 : 7149 : MethodResolver::get_predicate_items (
461 : : const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
462 : : const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
463 : : {
464 : 7149 : std::vector<predicate_candidate> predicate_items;
465 : 9394 : for (auto &bound : specified_bounds)
466 : : {
467 : 2245 : TyTy::TypeBoundPredicateItem lookup
468 : 2245 : = bound.lookup_associated_item (segment_name.as_string ());
469 : 2245 : if (lookup.is_error ())
470 : 982 : continue;
471 : :
472 : 1263 : TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver);
473 : 1263 : if (ty->get_kind () == TyTy::TypeKind::FNDEF)
474 : : {
475 : 1263 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
476 : 3785 : if (fnty->is_method ())
477 : : {
478 : 1259 : predicate_candidate candidate{lookup, fnty};
479 : 1259 : predicate_items.push_back (candidate);
480 : 1259 : }
481 : : }
482 : 2245 : }
483 : :
484 : 7149 : return predicate_items;
485 : : }
486 : :
487 : : std::vector<Adjustment>
488 : 6293 : MethodResolver::append_adjustments (const std::vector<Adjustment> &adjs) const
489 : : {
490 : 6293 : std::vector<Adjustment> combined;
491 : 6293 : combined.reserve (adjustments.size () + adjs.size ());
492 : :
493 : 9179 : for (const auto &a : adjustments)
494 : 2886 : combined.push_back (a);
495 : 6363 : for (const auto &a : adjs)
496 : 70 : combined.push_back (a);
497 : :
498 : 6293 : return combined;
499 : : }
500 : :
501 : : } // namespace Resolver
502 : : } // namespace Rust
|