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 : 2739 : MethodResolver::MethodResolver (bool autoderef_flag,
30 : 2739 : const HIR::PathIdentSegment &segment_name)
31 : 2739 : : AutoderefCycle (autoderef_flag), segment_name (segment_name), result ()
32 : 2739 : {}
33 : :
34 : : std::set<MethodCandidate>
35 : 2739 : MethodResolver::Probe (TyTy::BaseType *receiver,
36 : : const HIR::PathIdentSegment &segment_name,
37 : : bool autoderef_flag)
38 : : {
39 : 2739 : MethodResolver resolver (autoderef_flag, segment_name);
40 : 2739 : resolver.cycle (receiver);
41 : 2739 : return resolver.result;
42 : 2739 : }
43 : :
44 : : std::set<MethodCandidate>
45 : 1112 : MethodResolver::Select (std::set<MethodCandidate> &candidates,
46 : : TyTy::BaseType *receiver,
47 : : std::vector<TyTy::BaseType *> arguments)
48 : : {
49 : 1112 : std::set<MethodCandidate> selected;
50 : 2165 : for (auto &candidate : candidates)
51 : : {
52 : 1053 : TyTy::BaseType *candidate_type = candidate.candidate.ty;
53 : 1053 : rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF);
54 : 1053 : TyTy::FnType &fn = *static_cast<TyTy::FnType *> (candidate_type);
55 : :
56 : : // match the number of arguments
57 : 1053 : if (fn.num_params () != (arguments.size () + 1))
58 : 0 : continue;
59 : :
60 : : // match the arguments
61 : 1720 : bool failed = false;
62 : 1720 : for (size_t i = 0; i < arguments.size (); i++)
63 : : {
64 : 707 : TyTy::BaseType *arg = arguments.at (i);
65 : 707 : TyTy::BaseType *param = fn.get_params ().at (i + 1).get_type ();
66 : 707 : TyTy::BaseType *coerced
67 : 707 : = try_coercion (0, TyTy::TyWithLocation (param),
68 : 707 : TyTy::TyWithLocation (arg), UNDEF_LOCATION);
69 : 707 : if (coerced->get_kind () == TyTy::TypeKind::ERROR)
70 : : {
71 : : failed = true;
72 : : break;
73 : : }
74 : : }
75 : :
76 : 1053 : if (!failed)
77 : 1013 : selected.insert (candidate);
78 : : }
79 : :
80 : 1112 : return selected;
81 : : }
82 : :
83 : : void
84 : 2908 : MethodResolver::try_hook (const TyTy::BaseType &r)
85 : : {
86 : 2908 : rust_debug ("MethodResolver::try_hook get_predicate_items: [%s]",
87 : : r.debug_str ().c_str ());
88 : 2908 : const auto &specified_bounds = r.get_specified_bounds ();
89 : 2908 : predicate_items = get_predicate_items (segment_name, r, specified_bounds);
90 : :
91 : 2908 : if (predicate_items.size () > 0)
92 : : return;
93 : :
94 : 2682 : if (r.get_kind () == TyTy::TypeKind::REF)
95 : : {
96 : 762 : const auto &ref = static_cast<const TyTy::ReferenceType &> (r);
97 : 762 : const auto &element = ref.get_var_element_type ();
98 : 762 : const auto &element_ty = *element.get_tyty ();
99 : 762 : const auto &specified_bounds = element_ty.get_specified_bounds ();
100 : 762 : predicate_items
101 : 762 : = get_predicate_items (segment_name, element_ty, specified_bounds);
102 : : }
103 : : }
104 : :
105 : : bool
106 : 3821 : MethodResolver::select (TyTy::BaseType &receiver)
107 : : {
108 : 7642 : rust_debug ("MethodResolver::select reciever=[%s] path=[%s]",
109 : : receiver.debug_str ().c_str (),
110 : : segment_name.as_string ().c_str ());
111 : :
112 : 3821 : struct impl_item_candidate
113 : : {
114 : : HIR::Function *item;
115 : : HIR::ImplBlock *impl_block;
116 : : TyTy::FnType *ty;
117 : : };
118 : :
119 : 3821 : const TyTy::BaseType *raw = receiver.destructure ();
120 : 3821 : bool receiver_is_raw_ptr = raw->get_kind () == TyTy::TypeKind::POINTER;
121 : 3821 : bool receiver_is_ref = raw->get_kind () == TyTy::TypeKind::REF;
122 : :
123 : : // assemble inherent impl items
124 : 3821 : std::vector<impl_item_candidate> inherent_impl_fns;
125 : 3821 : mappings.iterate_impl_items (
126 : 3821 : [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
127 : 117247 : bool is_trait_impl = impl->has_trait_ref ();
128 : 117247 : if (is_trait_impl)
129 : : return true;
130 : :
131 : 51330 : bool is_fn
132 : 51330 : = item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION;
133 : 51330 : if (!is_fn)
134 : : return true;
135 : :
136 : 51330 : HIR::Function *func = static_cast<HIR::Function *> (item);
137 : 51330 : if (!func->is_method ())
138 : : return true;
139 : :
140 : 37967 : bool name_matches = func->get_function_name ().as_string ().compare (
141 : 75934 : segment_name.as_string ())
142 : 37967 : == 0;
143 : 37967 : if (!name_matches)
144 : : return true;
145 : :
146 : 2508 : TyTy::BaseType *ty = nullptr;
147 : 2508 : if (!query_type (func->get_mappings ().get_hirid (), &ty))
148 : : return true;
149 : 2508 : rust_assert (ty != nullptr);
150 : 2508 : if (ty->get_kind () == TyTy::TypeKind::ERROR)
151 : : return true;
152 : :
153 : 2508 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
154 : 2508 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
155 : 2508 : const TyTy::BaseType *impl_self
156 : 2508 : = 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 : 2508 : bool impl_self_is_ptr = impl_self->get_kind () == TyTy::TypeKind::POINTER;
162 : 2508 : bool impl_self_is_ref = impl_self->get_kind () == TyTy::TypeKind::REF;
163 : 2508 : if (receiver_is_raw_ptr && impl_self_is_ptr)
164 : : {
165 : 511 : const TyTy::PointerType &sptr
166 : : = *static_cast<const TyTy::PointerType *> (impl_self);
167 : 511 : const TyTy::PointerType &ptr
168 : 511 : = *static_cast<const TyTy::PointerType *> (raw);
169 : :
170 : : // we could do this via lang-item assemblies if we refactor this
171 : 511 : bool mut_match = sptr.mutability () == ptr.mutability ();
172 : 511 : if (!mut_match)
173 : : return true;
174 : : }
175 : 1997 : 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 : 2374 : inherent_impl_fns.push_back ({func, impl, fnty});
189 : :
190 : 2374 : return true;
191 : : });
192 : :
193 : 3821 : struct trait_item_candidate
194 : : {
195 : : const HIR::TraitItemFunc *item;
196 : : const HIR::Trait *trait;
197 : : TyTy::FnType *ty;
198 : : const TraitReference *reference;
199 : : const TraitItemReference *item_ref;
200 : : };
201 : :
202 : 3821 : std::vector<trait_item_candidate> trait_fns;
203 : 3821 : mappings.iterate_impl_blocks ([&] (HirId id,
204 : : HIR::ImplBlock *impl) mutable -> bool {
205 : 61459 : bool is_trait_impl = impl->has_trait_ref ();
206 : 61459 : if (!is_trait_impl)
207 : : return true;
208 : :
209 : : // look for impl implementation else lookup the associated trait item
210 : 111527 : for (auto &impl_item : impl->get_impl_items ())
211 : : {
212 : 65499 : bool is_fn = impl_item->get_impl_item_type ()
213 : 65499 : == HIR::ImplItem::ImplItemType::FUNCTION;
214 : 65499 : if (!is_fn)
215 : 58860 : continue;
216 : :
217 : 43879 : HIR::Function *func = static_cast<HIR::Function *> (impl_item.get ());
218 : 43879 : if (!func->is_method ())
219 : 2795 : continue;
220 : :
221 : 41084 : bool name_matches = func->get_function_name ().as_string ().compare (
222 : 82168 : segment_name.as_string ())
223 : 41084 : == 0;
224 : 41084 : if (!name_matches)
225 : 32363 : continue;
226 : :
227 : 8721 : TyTy::BaseType *ty = nullptr;
228 : 8721 : if (!query_type (func->get_mappings ().get_hirid (), &ty))
229 : 1883 : continue;
230 : 6838 : if (ty->get_kind () == TyTy::TypeKind::ERROR)
231 : 0 : continue;
232 : :
233 : 6838 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
234 : 6838 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
235 : 6838 : const TyTy::BaseType *impl_self
236 : 6838 : = TypeCheckItem::ResolveImplBlockSelf (*impl);
237 : :
238 : : // see:
239 : : // https://gcc-rust.zulipchat.com/#narrow/stream/266897-general/topic/Method.20Resolution/near/338646280
240 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/method/probe.rs#L650-L660
241 : 6838 : bool impl_self_is_ptr
242 : 6838 : = impl_self->get_kind () == TyTy::TypeKind::POINTER;
243 : 6838 : bool impl_self_is_ref = impl_self->get_kind () == TyTy::TypeKind::REF;
244 : 6838 : if (receiver_is_raw_ptr && impl_self_is_ptr)
245 : : {
246 : 0 : const TyTy::PointerType &sptr
247 : : = *static_cast<const TyTy::PointerType *> (impl_self);
248 : 0 : const TyTy::PointerType &ptr
249 : 0 : = *static_cast<const TyTy::PointerType *> (raw);
250 : :
251 : : // we could do this via lang-item assemblies if we refactor this
252 : 0 : bool mut_match = sptr.mutability () == ptr.mutability ();
253 : 0 : if (!mut_match)
254 : 0 : continue;
255 : : }
256 : 6838 : else if (receiver_is_ref && impl_self_is_ref)
257 : : {
258 : 500 : const TyTy::ReferenceType &sptr
259 : : = *static_cast<const TyTy::ReferenceType *> (impl_self);
260 : 500 : const TyTy::ReferenceType &ptr
261 : 500 : = *static_cast<const TyTy::ReferenceType *> (raw);
262 : :
263 : : // we could do this via lang-item assemblies if we refactor this
264 : 500 : bool mut_match = sptr.mutability () == ptr.mutability ();
265 : 500 : if (!mut_match)
266 : 199 : continue;
267 : : }
268 : :
269 : 6639 : inherent_impl_fns.push_back ({func, impl, fnty});
270 : 6639 : return true;
271 : : }
272 : :
273 : 46028 : TraitReference *trait_ref = TraitResolver::Resolve (impl->get_trait_ref ());
274 : 46028 : rust_assert (!trait_ref->is_error ());
275 : :
276 : 46028 : auto item_ref
277 : 92056 : = trait_ref->lookup_trait_item (segment_name.as_string (),
278 : : TraitItemReference::TraitItemType::FN);
279 : 46028 : if (item_ref->is_error ())
280 : : return true;
281 : :
282 : 3352 : const HIR::Trait *trait = trait_ref->get_hir_trait_ref ();
283 : 3352 : HIR::TraitItem *item = item_ref->get_hir_trait_item ();
284 : 3352 : if (item->get_item_kind () != HIR::TraitItem::TraitItemKind::FUNC)
285 : : return true;
286 : :
287 : 3352 : HIR::TraitItemFunc *func = static_cast<HIR::TraitItemFunc *> (item);
288 : 3352 : if (!func->get_decl ().is_method ())
289 : : return true;
290 : :
291 : 3346 : TyTy::BaseType *ty = item_ref->get_tyty ();
292 : 3346 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
293 : 3346 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
294 : :
295 : 3346 : trait_item_candidate candidate{func, trait, fnty, trait_ref, item_ref};
296 : 3346 : trait_fns.push_back (candidate);
297 : :
298 : 3346 : return true;
299 : : });
300 : :
301 : : // lookup specified bounds for an associated item
302 : 3821 : struct precdicate_candidate
303 : : {
304 : : TyTy::TypeBoundPredicateItem lookup;
305 : : TyTy::FnType *fntype;
306 : : };
307 : :
308 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/method/probe.rs#L580-L694
309 : :
310 : 3821 : rust_debug ("inherent_impl_fns found {%lu}, trait_fns found {%lu}, "
311 : : "predicate_items found {%lu}",
312 : : (unsigned long) inherent_impl_fns.size (),
313 : : (unsigned long) trait_fns.size (),
314 : : (unsigned long) predicate_items.size ());
315 : :
316 : 3821 : bool found_possible_candidate = false;
317 : 4616 : for (const auto &predicate : predicate_items)
318 : : {
319 : 795 : const TyTy::FnType *fn = predicate.fntype;
320 : 1590 : rust_assert (fn->is_method ());
321 : :
322 : 795 : TyTy::BaseType *fn_self = fn->get_self_type ();
323 : 795 : rust_debug ("dot-operator predicate fn_self={%s} can_eq receiver={%s}",
324 : : fn_self->debug_str ().c_str (),
325 : : receiver.debug_str ().c_str ());
326 : :
327 : 795 : auto res
328 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
329 : 795 : false /*allow-autoderef*/);
330 : 795 : bool ok = !res.is_error ();
331 : 795 : if (ok)
332 : : {
333 : 730 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
334 : 730 : const TraitReference *trait_ref
335 : 730 : = predicate.lookup.get_parent ()->get ();
336 : 730 : const TraitItemReference *trait_item
337 : 730 : = predicate.lookup.get_raw_item ();
338 : :
339 : 730 : PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item,
340 : 730 : nullptr};
341 : 730 : auto try_result = MethodCandidate{
342 : : PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
343 : : fn->clone (), trait_item->get_locus (), c),
344 : 730 : adjs};
345 : 730 : result.insert (std::move (try_result));
346 : 730 : found_possible_candidate = true;
347 : 730 : }
348 : 795 : }
349 : 3821 : if (found_possible_candidate)
350 : : {
351 : : return true;
352 : : }
353 : :
354 : 10410 : for (auto &impl_item : inherent_impl_fns)
355 : : {
356 : 7319 : bool is_trait_impl_block = impl_item.impl_block->has_trait_ref ();
357 : 7319 : if (is_trait_impl_block)
358 : 4945 : continue;
359 : :
360 : 2374 : TyTy::FnType *fn = impl_item.ty;
361 : 4748 : rust_assert (fn->is_method ());
362 : :
363 : 2374 : TyTy::BaseType *fn_self = fn->get_self_type ();
364 : 2374 : rust_debug ("dot-operator impl_item fn_self={%s} can_eq receiver={%s}",
365 : : fn_self->debug_str ().c_str (),
366 : : receiver.debug_str ().c_str ());
367 : :
368 : 2374 : auto res
369 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
370 : 2374 : false /*allow-autoderef*/);
371 : 2374 : bool ok = !res.is_error ();
372 : 2374 : if (ok)
373 : : {
374 : 813 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
375 : 813 : PathProbeCandidate::ImplItemCandidate c{impl_item.item,
376 : 813 : impl_item.impl_block};
377 : 813 : auto try_result = MethodCandidate{
378 : : PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
379 : 813 : fn, impl_item.item->get_locus (), c),
380 : 813 : adjs};
381 : 813 : result.insert (std::move (try_result));
382 : 813 : found_possible_candidate = true;
383 : 813 : }
384 : 2374 : }
385 : 3091 : if (found_possible_candidate)
386 : : {
387 : : return true;
388 : : }
389 : :
390 : 7961 : for (auto &impl_item : inherent_impl_fns)
391 : : {
392 : 5681 : bool is_trait_impl_block = impl_item.impl_block->has_trait_ref ();
393 : 5681 : if (!is_trait_impl_block)
394 : 745 : continue;
395 : :
396 : 4936 : TyTy::FnType *fn = impl_item.ty;
397 : 9872 : rust_assert (fn->is_method ());
398 : :
399 : 4936 : TyTy::BaseType *fn_self = fn->get_self_type ();
400 : 4936 : rust_debug (
401 : : "dot-operator trait_impl_item fn_self={%s} can_eq receiver={%s}",
402 : : fn_self->debug_str ().c_str (), receiver.debug_str ().c_str ());
403 : :
404 : 4936 : auto res
405 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
406 : 4936 : false /*allow-autoderef*/);
407 : 4936 : bool ok = !res.is_error ();
408 : 4936 : if (ok)
409 : : {
410 : 792 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
411 : 792 : PathProbeCandidate::ImplItemCandidate c{impl_item.item,
412 : 792 : impl_item.impl_block};
413 : 792 : auto try_result = MethodCandidate{
414 : : PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
415 : 792 : fn, impl_item.item->get_locus (), c),
416 : 792 : adjs};
417 : 792 : result.insert (std::move (try_result));
418 : 792 : found_possible_candidate = true;
419 : 792 : }
420 : 4936 : }
421 : 2280 : if (found_possible_candidate)
422 : : {
423 : : return true;
424 : : }
425 : :
426 : 3345 : for (auto trait_item : trait_fns)
427 : : {
428 : 1797 : TyTy::FnType *fn = trait_item.ty;
429 : 3594 : rust_assert (fn->is_method ());
430 : :
431 : 1797 : TyTy::BaseType *fn_self = fn->get_self_type ();
432 : 1797 : rust_debug ("dot-operator trait_item fn_self={%s} can_eq receiver={%s}",
433 : : fn_self->debug_str ().c_str (),
434 : : receiver.debug_str ().c_str ());
435 : :
436 : 1797 : auto res
437 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
438 : 1797 : false /*allow-autoderef*/);
439 : 1797 : bool ok = !res.is_error ();
440 : 1797 : if (ok)
441 : : {
442 : 1628 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
443 : 1628 : PathProbeCandidate::TraitItemCandidate c{trait_item.reference,
444 : : trait_item.item_ref,
445 : 1628 : nullptr};
446 : 1628 : auto try_result = MethodCandidate{
447 : : PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
448 : 1628 : fn, trait_item.item->get_locus (), c),
449 : 1628 : adjs};
450 : 1628 : result.insert (std::move (try_result));
451 : 1628 : found_possible_candidate = true;
452 : 1628 : }
453 : 1797 : }
454 : :
455 : : return found_possible_candidate;
456 : 3821 : }
457 : :
458 : : std::vector<MethodResolver::predicate_candidate>
459 : 3670 : MethodResolver::get_predicate_items (
460 : : const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
461 : : const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
462 : : {
463 : 3670 : std::vector<predicate_candidate> predicate_items;
464 : 4838 : for (auto &bound : specified_bounds)
465 : : {
466 : 1168 : TyTy::TypeBoundPredicateItem lookup
467 : 1168 : = bound.lookup_associated_item (segment_name.as_string ());
468 : 1168 : if (lookup.is_error ())
469 : 430 : continue;
470 : :
471 : 738 : TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver);
472 : 738 : if (ty->get_kind () == TyTy::TypeKind::FNDEF)
473 : : {
474 : 738 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
475 : 2206 : if (fnty->is_method ())
476 : : {
477 : 730 : predicate_candidate candidate{lookup, fnty};
478 : 730 : predicate_items.push_back (candidate);
479 : 730 : }
480 : : }
481 : 1168 : }
482 : :
483 : 3670 : return predicate_items;
484 : : }
485 : :
486 : : std::vector<Adjustment>
487 : 3963 : MethodResolver::append_adjustments (const std::vector<Adjustment> &adjs) const
488 : : {
489 : 3963 : std::vector<Adjustment> combined;
490 : 3963 : combined.reserve (adjustments.size () + adjs.size ());
491 : :
492 : 4609 : for (const auto &a : adjustments)
493 : 646 : combined.push_back (a);
494 : 4034 : for (const auto &a : adjs)
495 : 71 : combined.push_back (a);
496 : :
497 : 3963 : return combined;
498 : : }
499 : :
500 : : } // namespace Resolver
501 : : } // namespace Rust
|