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