1 // Copyright (C) 2020-2025 Free Software Foundation, Inc.
3 // This file is part of GCC.
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
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
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/>.
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"
29 MethodResolver::MethodResolver (bool autoderef_flag
,
30 const HIR::PathIdentSegment
&segment_name
)
31 : AutoderefCycle (autoderef_flag
), segment_name (segment_name
), result ()
34 std::set
<MethodCandidate
>
35 MethodResolver::Probe (TyTy::BaseType
*receiver
,
36 const HIR::PathIdentSegment
&segment_name
,
39 MethodResolver
resolver (autoderef_flag
, segment_name
);
40 resolver
.cycle (receiver
);
41 return resolver
.result
;
44 std::set
<MethodCandidate
>
45 MethodResolver::Select (std::set
<MethodCandidate
> &candidates
,
46 TyTy::BaseType
*receiver
,
47 std::vector
<TyTy::BaseType
*> arguments
)
49 std::set
<MethodCandidate
> selected
;
50 for (auto &candidate
: candidates
)
52 TyTy::BaseType
*candidate_type
= candidate
.candidate
.ty
;
53 rust_assert (candidate_type
->get_kind () == TyTy::TypeKind::FNDEF
);
54 TyTy::FnType
&fn
= *static_cast<TyTy::FnType
*> (candidate_type
);
56 // match the number of arguments
57 if (fn
.num_params () != (arguments
.size () + 1))
60 // match the arguments
62 for (size_t i
= 0; i
< arguments
.size (); i
++)
64 TyTy::BaseType
*arg
= arguments
.at (i
);
65 TyTy::BaseType
*param
= fn
.get_params ().at (i
+ 1).second
;
66 TyTy::BaseType
*coerced
67 = try_coercion (0, TyTy::TyWithLocation (param
),
68 TyTy::TyWithLocation (arg
), UNDEF_LOCATION
);
69 if (coerced
->get_kind () == TyTy::TypeKind::ERROR
)
77 selected
.insert (candidate
);
84 MethodResolver::try_hook (const TyTy::BaseType
&r
)
86 rust_debug ("MethodResolver::try_hook get_predicate_items: [%s]",
87 r
.debug_str ().c_str ());
88 const auto &specified_bounds
= r
.get_specified_bounds ();
89 predicate_items
= get_predicate_items (segment_name
, r
, specified_bounds
);
91 if (predicate_items
.size () > 0)
94 if (r
.get_kind () == TyTy::TypeKind::REF
)
96 const auto &ref
= static_cast<const TyTy::ReferenceType
&> (r
);
97 const auto &element
= ref
.get_var_element_type ();
98 const auto &element_ty
= *element
.get_tyty ();
99 const auto &specified_bounds
= element_ty
.get_specified_bounds ();
101 = get_predicate_items (segment_name
, element_ty
, specified_bounds
);
106 MethodResolver::select (TyTy::BaseType
&receiver
)
108 rust_debug ("MethodResolver::select reciever=[%s] path=[%s]",
109 receiver
.debug_str ().c_str (),
110 segment_name
.as_string ().c_str ());
112 struct impl_item_candidate
115 HIR::ImplBlock
*impl_block
;
119 const TyTy::BaseType
*raw
= receiver
.destructure ();
120 bool receiver_is_raw_ptr
= raw
->get_kind () == TyTy::TypeKind::POINTER
;
121 bool receiver_is_ref
= raw
->get_kind () == TyTy::TypeKind::REF
;
123 // assemble inherent impl items
124 std::vector
<impl_item_candidate
> inherent_impl_fns
;
125 mappings
->iterate_impl_items (
126 [&] (HirId id
, HIR::ImplItem
*item
, HIR::ImplBlock
*impl
) mutable -> bool {
127 bool is_trait_impl
= impl
->has_trait_ref ();
132 = item
->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION
;
136 HIR::Function
*func
= static_cast<HIR::Function
*> (item
);
137 if (!func
->is_method ())
140 bool name_matches
= func
->get_function_name ().as_string ().compare (
141 segment_name
.as_string ())
146 TyTy::BaseType
*ty
= nullptr;
147 if (!query_type (func
->get_mappings ().get_hirid (), &ty
))
149 rust_assert (ty
!= nullptr);
150 if (ty
->get_kind () == TyTy::TypeKind::ERROR
)
153 rust_assert (ty
->get_kind () == TyTy::TypeKind::FNDEF
);
154 TyTy::FnType
*fnty
= static_cast<TyTy::FnType
*> (ty
);
155 const TyTy::BaseType
*impl_self
156 = TypeCheckItem::ResolveImplBlockSelf (*impl
);
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 bool impl_self_is_ptr
= impl_self
->get_kind () == TyTy::TypeKind::POINTER
;
162 bool impl_self_is_ref
= impl_self
->get_kind () == TyTy::TypeKind::REF
;
163 if (receiver_is_raw_ptr
&& impl_self_is_ptr
)
165 const TyTy::PointerType
&sptr
166 = *static_cast<const TyTy::PointerType
*> (impl_self
);
167 const TyTy::PointerType
&ptr
168 = *static_cast<const TyTy::PointerType
*> (raw
);
170 // we could do this via lang-item assemblies if we refactor this
171 bool mut_match
= sptr
.mutability () == ptr
.mutability ();
175 else if (receiver_is_ref
&& impl_self_is_ref
)
177 const TyTy::ReferenceType
&sptr
178 = *static_cast<const TyTy::ReferenceType
*> (impl_self
);
179 const TyTy::ReferenceType
&ptr
180 = *static_cast<const TyTy::ReferenceType
*> (raw
);
182 // we could do this via lang-item assemblies if we refactor this
183 bool mut_match
= sptr
.mutability () == ptr
.mutability ();
188 inherent_impl_fns
.push_back ({func
, impl
, fnty
});
193 struct trait_item_candidate
195 const HIR::TraitItemFunc
*item
;
196 const HIR::Trait
*trait
;
198 const TraitReference
*reference
;
199 const TraitItemReference
*item_ref
;
202 std::vector
<trait_item_candidate
> trait_fns
;
203 mappings
->iterate_impl_blocks (
204 [&] (HirId id
, HIR::ImplBlock
*impl
) mutable -> bool {
205 bool is_trait_impl
= impl
->has_trait_ref ();
209 // look for impl implementation else lookup the associated trait item
210 for (auto &impl_item
: impl
->get_impl_items ())
212 bool is_fn
= impl_item
->get_impl_item_type ()
213 == HIR::ImplItem::ImplItemType::FUNCTION
;
217 HIR::Function
*func
= static_cast<HIR::Function
*> (impl_item
.get ());
218 if (!func
->is_method ())
221 bool name_matches
= func
->get_function_name ().as_string ().compare (
222 segment_name
.as_string ())
227 TyTy::BaseType
*ty
= nullptr;
228 if (!query_type (func
->get_mappings ().get_hirid (), &ty
))
230 if (ty
->get_kind () == TyTy::TypeKind::ERROR
)
233 rust_assert (ty
->get_kind () == TyTy::TypeKind::FNDEF
);
234 TyTy::FnType
*fnty
= static_cast<TyTy::FnType
*> (ty
);
235 const TyTy::BaseType
*impl_self
236 = TypeCheckItem::ResolveImplBlockSelf (*impl
);
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 bool impl_self_is_ptr
242 = impl_self
->get_kind () == TyTy::TypeKind::POINTER
;
243 bool impl_self_is_ref
= impl_self
->get_kind () == TyTy::TypeKind::REF
;
244 if (receiver_is_raw_ptr
&& impl_self_is_ptr
)
246 const TyTy::PointerType
&sptr
247 = *static_cast<const TyTy::PointerType
*> (impl_self
);
248 const TyTy::PointerType
&ptr
249 = *static_cast<const TyTy::PointerType
*> (raw
);
251 // we could do this via lang-item assemblies if we refactor this
252 bool mut_match
= sptr
.mutability () == ptr
.mutability ();
256 else if (receiver_is_ref
&& impl_self_is_ref
)
258 const TyTy::ReferenceType
&sptr
259 = *static_cast<const TyTy::ReferenceType
*> (impl_self
);
260 const TyTy::ReferenceType
&ptr
261 = *static_cast<const TyTy::ReferenceType
*> (raw
);
263 // we could do this via lang-item assemblies if we refactor this
264 bool mut_match
= sptr
.mutability () == ptr
.mutability ();
269 inherent_impl_fns
.push_back ({func
, impl
, fnty
});
273 TraitReference
*trait_ref
274 = TraitResolver::Resolve (*impl
->get_trait_ref ().get ());
275 rust_assert (!trait_ref
->is_error ());
278 = trait_ref
->lookup_trait_item (segment_name
.as_string (),
279 TraitItemReference::TraitItemType::FN
);
280 if (item_ref
->is_error ())
283 const HIR::Trait
*trait
= trait_ref
->get_hir_trait_ref ();
284 HIR::TraitItem
*item
= item_ref
->get_hir_trait_item ();
285 if (item
->get_item_kind () != HIR::TraitItem::TraitItemKind::FUNC
)
288 HIR::TraitItemFunc
*func
= static_cast<HIR::TraitItemFunc
*> (item
);
289 if (!func
->get_decl ().is_method ())
292 TyTy::BaseType
*ty
= item_ref
->get_tyty ();
293 rust_assert (ty
->get_kind () == TyTy::TypeKind::FNDEF
);
294 TyTy::FnType
*fnty
= static_cast<TyTy::FnType
*> (ty
);
296 trait_item_candidate candidate
{func
, trait
, fnty
, trait_ref
, item_ref
};
297 trait_fns
.push_back (candidate
);
302 // lookup specified bounds for an associated item
303 struct precdicate_candidate
305 TyTy::TypeBoundPredicateItem lookup
;
306 TyTy::FnType
*fntype
;
309 // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/method/probe.rs#L580-L694
311 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 ());
317 bool found_possible_candidate
= false;
318 for (const auto &predicate
: predicate_items
)
320 const TyTy::FnType
*fn
= predicate
.fntype
;
321 rust_assert (fn
->is_method ());
323 TyTy::BaseType
*fn_self
= fn
->get_self_type ();
324 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 ());
329 = TypeCoercionRules::TryCoerce (&receiver
, fn_self
, UNDEF_LOCATION
,
330 false /*allow-autoderef*/);
331 bool ok
= !res
.is_error ();
334 std::vector
<Adjustment
> adjs
= append_adjustments (res
.adjustments
);
335 const TraitReference
*trait_ref
336 = predicate
.lookup
.get_parent ()->get ();
337 const TraitItemReference
*trait_item
338 = predicate
.lookup
.get_raw_item ();
340 PathProbeCandidate::TraitItemCandidate c
{trait_ref
, trait_item
,
342 auto try_result
= MethodCandidate
{
343 PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC
,
344 fn
->clone (), trait_item
->get_locus (), c
),
346 result
.insert (std::move (try_result
));
347 found_possible_candidate
= true;
350 if (found_possible_candidate
)
355 for (auto &impl_item
: inherent_impl_fns
)
357 bool is_trait_impl_block
= impl_item
.impl_block
->has_trait_ref ();
358 if (is_trait_impl_block
)
361 TyTy::FnType
*fn
= impl_item
.ty
;
362 rust_assert (fn
->is_method ());
364 TyTy::BaseType
*fn_self
= fn
->get_self_type ();
365 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 ());
370 = TypeCoercionRules::TryCoerce (&receiver
, fn_self
, UNDEF_LOCATION
,
371 false /*allow-autoderef*/);
372 bool ok
= !res
.is_error ();
375 std::vector
<Adjustment
> adjs
= append_adjustments (res
.adjustments
);
376 PathProbeCandidate::ImplItemCandidate c
{impl_item
.item
,
377 impl_item
.impl_block
};
378 auto try_result
= MethodCandidate
{
379 PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC
,
380 fn
, impl_item
.item
->get_locus (), c
),
382 result
.insert (std::move (try_result
));
383 found_possible_candidate
= true;
386 if (found_possible_candidate
)
391 for (auto &impl_item
: inherent_impl_fns
)
393 bool is_trait_impl_block
= impl_item
.impl_block
->has_trait_ref ();
394 if (!is_trait_impl_block
)
397 TyTy::FnType
*fn
= impl_item
.ty
;
398 rust_assert (fn
->is_method ());
400 TyTy::BaseType
*fn_self
= fn
->get_self_type ();
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 ());
406 = TypeCoercionRules::TryCoerce (&receiver
, fn_self
, UNDEF_LOCATION
,
407 false /*allow-autoderef*/);
408 bool ok
= !res
.is_error ();
411 std::vector
<Adjustment
> adjs
= append_adjustments (res
.adjustments
);
412 PathProbeCandidate::ImplItemCandidate c
{impl_item
.item
,
413 impl_item
.impl_block
};
414 auto try_result
= MethodCandidate
{
415 PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC
,
416 fn
, impl_item
.item
->get_locus (), c
),
418 result
.insert (std::move (try_result
));
419 found_possible_candidate
= true;
422 if (found_possible_candidate
)
427 for (auto trait_item
: trait_fns
)
429 TyTy::FnType
*fn
= trait_item
.ty
;
430 rust_assert (fn
->is_method ());
432 TyTy::BaseType
*fn_self
= fn
->get_self_type ();
433 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 ());
438 = TypeCoercionRules::TryCoerce (&receiver
, fn_self
, UNDEF_LOCATION
,
439 false /*allow-autoderef*/);
440 bool ok
= !res
.is_error ();
443 std::vector
<Adjustment
> adjs
= append_adjustments (res
.adjustments
);
444 PathProbeCandidate::TraitItemCandidate c
{trait_item
.reference
,
447 auto try_result
= MethodCandidate
{
448 PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC
,
449 fn
, trait_item
.item
->get_locus (), c
),
451 result
.insert (std::move (try_result
));
452 found_possible_candidate
= true;
456 return found_possible_candidate
;
459 std::vector
<MethodResolver::predicate_candidate
>
460 MethodResolver::get_predicate_items (
461 const HIR::PathIdentSegment
&segment_name
, const TyTy::BaseType
&receiver
,
462 const std::vector
<TyTy::TypeBoundPredicate
> &specified_bounds
)
464 std::vector
<predicate_candidate
> predicate_items
;
465 for (auto &bound
: specified_bounds
)
467 TyTy::TypeBoundPredicateItem lookup
468 = bound
.lookup_associated_item (segment_name
.as_string ());
469 if (lookup
.is_error ())
472 TyTy::BaseType
*ty
= lookup
.get_tyty_for_receiver (&receiver
);
473 if (ty
->get_kind () == TyTy::TypeKind::FNDEF
)
475 TyTy::FnType
*fnty
= static_cast<TyTy::FnType
*> (ty
);
476 predicate_candidate candidate
{lookup
, fnty
};
477 predicate_items
.push_back (candidate
);
481 return predicate_items
;
484 std::vector
<Adjustment
>
485 MethodResolver::append_adjustments (const std::vector
<Adjustment
> &adjs
) const
487 std::vector
<Adjustment
> combined
;
488 combined
.reserve (adjustments
.size () + adjs
.size ());
490 for (const auto &a
: adjustments
)
491 combined
.push_back (a
);
492 for (const auto &a
: adjs
)
493 combined
.push_back (a
);
498 } // namespace Resolver