Branch data Line data Source code
1 : : // Copyright (C) 2020-2024 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 : 1751 : MethodResolver::MethodResolver (bool autoderef_flag,
30 : 1751 : const HIR::PathIdentSegment &segment_name)
31 : 1751 : : AutoderefCycle (autoderef_flag), segment_name (segment_name), result ()
32 : 1751 : {}
33 : :
34 : : std::set<MethodCandidate>
35 : 1751 : MethodResolver::Probe (TyTy::BaseType *receiver,
36 : : const HIR::PathIdentSegment &segment_name,
37 : : bool autoderef_flag)
38 : : {
39 : 1751 : MethodResolver resolver (autoderef_flag, segment_name);
40 : 1751 : resolver.cycle (receiver);
41 : 1751 : return resolver.result;
42 : 1751 : }
43 : :
44 : : std::set<MethodCandidate>
45 : 662 : MethodResolver::Select (std::set<MethodCandidate> &candidates,
46 : : TyTy::BaseType *receiver,
47 : : std::vector<TyTy::BaseType *> arguments)
48 : : {
49 : 662 : std::set<MethodCandidate> selected;
50 : 1275 : for (auto &candidate : candidates)
51 : : {
52 : 613 : TyTy::BaseType *candidate_type = candidate.candidate.ty;
53 : 613 : rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF);
54 : 613 : TyTy::FnType &fn = *static_cast<TyTy::FnType *> (candidate_type);
55 : :
56 : : // match the number of arguments
57 : 613 : if (fn.num_params () != (arguments.size () + 1))
58 : 0 : continue;
59 : :
60 : : // match the arguments
61 : 885 : bool failed = false;
62 : 885 : for (size_t i = 0; i < arguments.size (); i++)
63 : : {
64 : 289 : TyTy::BaseType *arg = arguments.at (i);
65 : 289 : TyTy::BaseType *param = fn.get_params ().at (i + 1).second;
66 : 289 : TyTy::BaseType *coerced
67 : 289 : = try_coercion (0, TyTy::TyWithLocation (param),
68 : 289 : TyTy::TyWithLocation (arg), UNDEF_LOCATION);
69 : 289 : if (coerced->get_kind () == TyTy::TypeKind::ERROR)
70 : : {
71 : : failed = true;
72 : : break;
73 : : }
74 : : }
75 : :
76 : 613 : if (!failed)
77 : 596 : selected.insert (candidate);
78 : : }
79 : :
80 : 662 : return selected;
81 : : }
82 : :
83 : : void
84 : 1889 : MethodResolver::try_hook (const TyTy::BaseType &r)
85 : : {
86 : 1889 : rust_debug ("MethodResolver::try_hook get_predicate_items: [%s]",
87 : : r.debug_str ().c_str ());
88 : 1889 : const auto &specified_bounds = r.get_specified_bounds ();
89 : 1889 : predicate_items = get_predicate_items (segment_name, r, specified_bounds);
90 : :
91 : 1889 : if (predicate_items.size () > 0)
92 : : return;
93 : :
94 : 1704 : if (r.get_kind () == TyTy::TypeKind::REF)
95 : : {
96 : 539 : const auto &ref = static_cast<const TyTy::ReferenceType &> (r);
97 : 539 : const auto &element = ref.get_var_element_type ();
98 : 539 : const auto &element_ty = *element.get_tyty ();
99 : 539 : const auto &specified_bounds = element_ty.get_specified_bounds ();
100 : 539 : predicate_items
101 : 539 : = get_predicate_items (segment_name, element_ty, specified_bounds);
102 : : }
103 : : }
104 : :
105 : : bool
106 : 2534 : MethodResolver::select (TyTy::BaseType &receiver)
107 : : {
108 : 2534 : rust_debug ("MethodResolver::select reciever=[%s] path=[%s]",
109 : : receiver.debug_str ().c_str (),
110 : : segment_name.as_string ().c_str ());
111 : :
112 : 2534 : struct impl_item_candidate
113 : : {
114 : : HIR::Function *item;
115 : : HIR::ImplBlock *impl_block;
116 : : TyTy::FnType *ty;
117 : : };
118 : :
119 : 2534 : const TyTy::BaseType *raw = receiver.destructure ();
120 : 2534 : bool receiver_is_raw_ptr = raw->get_kind () == TyTy::TypeKind::POINTER;
121 : 2534 : bool receiver_is_ref = raw->get_kind () == TyTy::TypeKind::REF;
122 : :
123 : : // assemble inherent impl items
124 : 2534 : std::vector<impl_item_candidate> inherent_impl_fns;
125 : 2534 : mappings->iterate_impl_items (
126 : 2534 : [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
127 : 47203 : bool is_trait_impl = impl->has_trait_ref ();
128 : 47203 : if (is_trait_impl)
129 : : return true;
130 : :
131 : 19400 : bool is_fn
132 : 19400 : = item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION;
133 : 19400 : if (!is_fn)
134 : : return true;
135 : :
136 : 19400 : HIR::Function *func = static_cast<HIR::Function *> (item);
137 : 19400 : if (!func->is_method ())
138 : : return true;
139 : :
140 : 14795 : bool name_matches = func->get_function_name ().as_string ().compare (
141 : 29590 : segment_name.as_string ())
142 : 14795 : == 0;
143 : 14795 : if (!name_matches)
144 : : return true;
145 : :
146 : 1387 : TyTy::BaseType *ty = nullptr;
147 : 1387 : if (!query_type (func->get_mappings ().get_hirid (), &ty))
148 : : return true;
149 : 1387 : rust_assert (ty != nullptr);
150 : 1387 : if (ty->get_kind () == TyTy::TypeKind::ERROR)
151 : : return true;
152 : :
153 : 1387 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
154 : 1387 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
155 : 1387 : const TyTy::BaseType *impl_self
156 : 1387 : = 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 : 1387 : bool impl_self_is_ptr = impl_self->get_kind () == TyTy::TypeKind::POINTER;
162 : 1387 : bool impl_self_is_ref = impl_self->get_kind () == TyTy::TypeKind::REF;
163 : 1387 : if (receiver_is_raw_ptr && impl_self_is_ptr)
164 : : {
165 : 330 : const TyTy::PointerType &sptr
166 : : = *static_cast<const TyTy::PointerType *> (impl_self);
167 : 330 : const TyTy::PointerType &ptr
168 : 330 : = *static_cast<const TyTy::PointerType *> (raw);
169 : :
170 : : // we could do this via lang-item assemblies if we refactor this
171 : 330 : bool mut_match = sptr.mutability () == ptr.mutability ();
172 : 330 : if (!mut_match)
173 : : return true;
174 : : }
175 : 1057 : 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 : 1342 : inherent_impl_fns.push_back ({func, impl, fnty});
189 : :
190 : 1342 : return true;
191 : : });
192 : :
193 : 2534 : 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 : 2534 : std::vector<trait_item_candidate> trait_fns;
203 : 2534 : mappings->iterate_impl_blocks (
204 : 2534 : [&] (HirId id, HIR::ImplBlock *impl) mutable -> bool {
205 : 24436 : bool is_trait_impl = impl->has_trait_ref ();
206 : 24436 : if (!is_trait_impl)
207 : : return true;
208 : :
209 : : // look for impl implementation else lookup the associated trait item
210 : 43960 : for (auto &impl_item : impl->get_impl_items ())
211 : : {
212 : 27540 : bool is_fn = impl_item->get_impl_item_type ()
213 : 27540 : == HIR::ImplItem::ImplItemType::FUNCTION;
214 : 27540 : if (!is_fn)
215 : 23723 : continue;
216 : :
217 : 18066 : HIR::Function *func = static_cast<HIR::Function *> (impl_item.get ());
218 : 18066 : if (!func->is_method ())
219 : 898 : continue;
220 : :
221 : 17168 : bool name_matches = func->get_function_name ().as_string ().compare (
222 : 34336 : segment_name.as_string ())
223 : 17168 : == 0;
224 : 17168 : if (!name_matches)
225 : 12564 : continue;
226 : :
227 : 4604 : TyTy::BaseType *ty = nullptr;
228 : 4604 : if (!query_type (func->get_mappings ().get_hirid (), &ty))
229 : 605 : continue;
230 : 3999 : if (ty->get_kind () == TyTy::TypeKind::ERROR)
231 : 0 : continue;
232 : :
233 : 3999 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
234 : 3999 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
235 : 3999 : const TyTy::BaseType *impl_self
236 : 3999 : = 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 : 3999 : bool impl_self_is_ptr
242 : 3999 : = impl_self->get_kind () == TyTy::TypeKind::POINTER;
243 : 3999 : bool impl_self_is_ref = impl_self->get_kind () == TyTy::TypeKind::REF;
244 : 3999 : 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 : 3999 : else if (receiver_is_ref && impl_self_is_ref)
257 : : {
258 : 462 : const TyTy::ReferenceType &sptr
259 : : = *static_cast<const TyTy::ReferenceType *> (impl_self);
260 : 462 : const TyTy::ReferenceType &ptr
261 : 462 : = *static_cast<const TyTy::ReferenceType *> (raw);
262 : :
263 : : // we could do this via lang-item assemblies if we refactor this
264 : 462 : bool mut_match = sptr.mutability () == ptr.mutability ();
265 : 462 : if (!mut_match)
266 : 182 : continue;
267 : : }
268 : :
269 : 3817 : inherent_impl_fns.push_back ({func, impl, fnty});
270 : 3817 : return true;
271 : : }
272 : :
273 : 16420 : TraitReference *trait_ref
274 : 16420 : = TraitResolver::Resolve (*impl->get_trait_ref ().get ());
275 : 16420 : rust_assert (!trait_ref->is_error ());
276 : :
277 : 16420 : auto item_ref
278 : 16420 : = trait_ref->lookup_trait_item (segment_name.as_string (),
279 : : TraitItemReference::TraitItemType::FN);
280 : 16420 : if (item_ref->is_error ())
281 : : return true;
282 : :
283 : 1496 : const HIR::Trait *trait = trait_ref->get_hir_trait_ref ();
284 : 1496 : HIR::TraitItem *item = item_ref->get_hir_trait_item ();
285 : 1496 : if (item->get_item_kind () != HIR::TraitItem::TraitItemKind::FUNC)
286 : : return true;
287 : :
288 : 1496 : HIR::TraitItemFunc *func = static_cast<HIR::TraitItemFunc *> (item);
289 : 1496 : if (!func->get_decl ().is_method ())
290 : : return true;
291 : :
292 : 1493 : TyTy::BaseType *ty = item_ref->get_tyty ();
293 : 1493 : rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
294 : 1493 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
295 : :
296 : 1493 : trait_item_candidate candidate{func, trait, fnty, trait_ref, item_ref};
297 : 1493 : trait_fns.push_back (candidate);
298 : :
299 : 1493 : return true;
300 : : });
301 : :
302 : : // lookup specified bounds for an associated item
303 : 2534 : struct precdicate_candidate
304 : : {
305 : : TyTy::TypeBoundPredicateItem lookup;
306 : : TyTy::FnType *fntype;
307 : : };
308 : :
309 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/method/probe.rs#L580-L694
310 : :
311 : 2534 : rust_debug ("inherent_impl_fns found {%lu}, trait_fns found {%lu}, "
312 : : "predicate_items found {%lu}",
313 : : (unsigned long) inherent_impl_fns.size (),
314 : : (unsigned long) trait_fns.size (),
315 : : (unsigned long) predicate_items.size ());
316 : :
317 : 2534 : bool found_possible_candidate = false;
318 : 3083 : for (const auto &predicate : predicate_items)
319 : : {
320 : 549 : const TyTy::FnType *fn = predicate.fntype;
321 : 549 : rust_assert (fn->is_method ());
322 : :
323 : 549 : TyTy::BaseType *fn_self = fn->get_self_type ();
324 : 549 : rust_debug ("dot-operator predicate fn_self={%s} can_eq receiver={%s}",
325 : : fn_self->debug_str ().c_str (),
326 : : receiver.debug_str ().c_str ());
327 : :
328 : 549 : auto res
329 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
330 : 549 : false /*allow-autoderef*/);
331 : 549 : bool ok = !res.is_error ();
332 : 549 : if (ok)
333 : : {
334 : 506 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
335 : 506 : const TraitReference *trait_ref
336 : 506 : = predicate.lookup.get_parent ()->get ();
337 : 506 : const TraitItemReference *trait_item
338 : 506 : = predicate.lookup.get_raw_item ();
339 : :
340 : 506 : PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item,
341 : 506 : nullptr};
342 : 506 : auto try_result = MethodCandidate{
343 : : PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
344 : : fn->clone (), trait_item->get_locus (), c),
345 : 506 : adjs};
346 : 506 : result.insert (std::move (try_result));
347 : 506 : found_possible_candidate = true;
348 : 506 : }
349 : 549 : }
350 : 2534 : if (found_possible_candidate)
351 : : {
352 : : return true;
353 : : }
354 : :
355 : 6018 : for (auto &impl_item : inherent_impl_fns)
356 : : {
357 : 3990 : bool is_trait_impl_block = impl_item.impl_block->has_trait_ref ();
358 : 3990 : if (is_trait_impl_block)
359 : 2648 : continue;
360 : :
361 : 1342 : TyTy::FnType *fn = impl_item.ty;
362 : 1342 : rust_assert (fn->is_method ());
363 : :
364 : 1342 : TyTy::BaseType *fn_self = fn->get_self_type ();
365 : 1342 : rust_debug ("dot-operator impl_item fn_self={%s} can_eq receiver={%s}",
366 : : fn_self->debug_str ().c_str (),
367 : : receiver.debug_str ().c_str ());
368 : :
369 : 1342 : auto res
370 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
371 : 1342 : false /*allow-autoderef*/);
372 : 1342 : bool ok = !res.is_error ();
373 : 1342 : if (ok)
374 : : {
375 : 558 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
376 : 558 : PathProbeCandidate::ImplItemCandidate c{impl_item.item,
377 : 558 : impl_item.impl_block};
378 : 558 : auto try_result = MethodCandidate{
379 : : PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
380 : 558 : fn, impl_item.item->get_locus (), c),
381 : 558 : adjs};
382 : 558 : result.insert (std::move (try_result));
383 : 558 : found_possible_candidate = true;
384 : 558 : }
385 : 1342 : }
386 : 2028 : if (found_possible_candidate)
387 : : {
388 : : return true;
389 : : }
390 : :
391 : 4548 : for (auto &impl_item : inherent_impl_fns)
392 : : {
393 : 3077 : bool is_trait_impl_block = impl_item.impl_block->has_trait_ref ();
394 : 3077 : if (!is_trait_impl_block)
395 : 437 : continue;
396 : :
397 : 2640 : TyTy::FnType *fn = impl_item.ty;
398 : 2640 : rust_assert (fn->is_method ());
399 : :
400 : 2640 : TyTy::BaseType *fn_self = fn->get_self_type ();
401 : 2640 : rust_debug (
402 : : "dot-operator trait_impl_item fn_self={%s} can_eq receiver={%s}",
403 : : fn_self->debug_str ().c_str (), receiver.debug_str ().c_str ());
404 : :
405 : 2640 : auto res
406 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
407 : 2640 : false /*allow-autoderef*/);
408 : 2640 : bool ok = !res.is_error ();
409 : 2640 : if (ok)
410 : : {
411 : 570 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
412 : 570 : PathProbeCandidate::ImplItemCandidate c{impl_item.item,
413 : 570 : impl_item.impl_block};
414 : 570 : auto try_result = MethodCandidate{
415 : : PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
416 : 570 : fn, impl_item.item->get_locus (), c),
417 : 570 : adjs};
418 : 570 : result.insert (std::move (try_result));
419 : 570 : found_possible_candidate = true;
420 : 570 : }
421 : 2640 : }
422 : 1471 : if (found_possible_candidate)
423 : : {
424 : : return true;
425 : : }
426 : :
427 : 1423 : for (auto trait_item : trait_fns)
428 : : {
429 : 494 : TyTy::FnType *fn = trait_item.ty;
430 : 494 : rust_assert (fn->is_method ());
431 : :
432 : 494 : TyTy::BaseType *fn_self = fn->get_self_type ();
433 : 494 : rust_debug ("dot-operator trait_item fn_self={%s} can_eq receiver={%s}",
434 : : fn_self->debug_str ().c_str (),
435 : : receiver.debug_str ().c_str ());
436 : :
437 : 494 : auto res
438 : : = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
439 : 494 : false /*allow-autoderef*/);
440 : 494 : bool ok = !res.is_error ();
441 : 494 : if (ok)
442 : : {
443 : 478 : std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
444 : 478 : PathProbeCandidate::TraitItemCandidate c{trait_item.reference,
445 : : trait_item.item_ref,
446 : 478 : nullptr};
447 : 478 : auto try_result = MethodCandidate{
448 : : PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
449 : 478 : fn, trait_item.item->get_locus (), c),
450 : 478 : adjs};
451 : 478 : result.insert (std::move (try_result));
452 : 478 : found_possible_candidate = true;
453 : 478 : }
454 : 494 : }
455 : :
456 : : return found_possible_candidate;
457 : 2534 : }
458 : :
459 : : std::vector<MethodResolver::predicate_candidate>
460 : 2428 : MethodResolver::get_predicate_items (
461 : : const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
462 : : const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
463 : : {
464 : 2428 : std::vector<predicate_candidate> predicate_items;
465 : 3248 : for (auto &bound : specified_bounds)
466 : : {
467 : 820 : TyTy::TypeBoundPredicateItem lookup
468 : 820 : = bound.lookup_associated_item (segment_name.as_string ());
469 : 820 : if (lookup.is_error ())
470 : 314 : continue;
471 : :
472 : 506 : TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver);
473 : 506 : if (ty->get_kind () == TyTy::TypeKind::FNDEF)
474 : : {
475 : 506 : TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
476 : 506 : predicate_candidate candidate{lookup, fnty};
477 : 506 : predicate_items.push_back (candidate);
478 : : }
479 : : }
480 : :
481 : 2428 : return predicate_items;
482 : : }
483 : :
484 : : std::vector<Adjustment>
485 : 2112 : MethodResolver::append_adjustments (const std::vector<Adjustment> &adjs) const
486 : : {
487 : 2112 : std::vector<Adjustment> combined;
488 : 2112 : combined.reserve (adjustments.size () + adjs.size ());
489 : :
490 : 2515 : for (const auto &a : adjustments)
491 : 403 : combined.push_back (a);
492 : 2182 : for (const auto &a : adjs)
493 : 70 : combined.push_back (a);
494 : :
495 : 2112 : return combined;
496 : : }
497 : :
498 : : } // namespace Resolver
499 : : } // namespace Rust
|