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