[PR testsuite/116860] Testsuite adjustment for recently added tests
[official-gcc.git] / gcc / rust / typecheck / rust-hir-dot-operator.cc
blob93b35f872615db7e74d32f0dca0b2dbd394b5c31
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
8 // version.
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.
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"
26 namespace Rust {
27 namespace Resolver {
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,
37 bool autoderef_flag)
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))
58 continue;
60 // match the arguments
61 bool failed = false;
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)
71 failed = true;
72 break;
76 if (!failed)
77 selected.insert (candidate);
80 return selected;
83 void
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)
92 return;
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 ();
100 predicate_items
101 = get_predicate_items (segment_name, element_ty, specified_bounds);
105 bool
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
114 HIR::Function *item;
115 HIR::ImplBlock *impl_block;
116 TyTy::FnType *ty;
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 ();
128 if (is_trait_impl)
129 return true;
131 bool is_fn
132 = item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION;
133 if (!is_fn)
134 return true;
136 HIR::Function *func = static_cast<HIR::Function *> (item);
137 if (!func->is_method ())
138 return true;
140 bool name_matches = func->get_function_name ().as_string ().compare (
141 segment_name.as_string ())
142 == 0;
143 if (!name_matches)
144 return true;
146 TyTy::BaseType *ty = nullptr;
147 if (!query_type (func->get_mappings ().get_hirid (), &ty))
148 return true;
149 rust_assert (ty != nullptr);
150 if (ty->get_kind () == TyTy::TypeKind::ERROR)
151 return true;
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);
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 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 ();
172 if (!mut_match)
173 return true;
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 ();
184 if (!mut_match)
185 return true;
188 inherent_impl_fns.push_back ({func, impl, fnty});
190 return true;
193 struct trait_item_candidate
195 const HIR::TraitItemFunc *item;
196 const HIR::Trait *trait;
197 TyTy::FnType *ty;
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 ();
206 if (!is_trait_impl)
207 return true;
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;
214 if (!is_fn)
215 continue;
217 HIR::Function *func = static_cast<HIR::Function *> (impl_item.get ());
218 if (!func->is_method ())
219 continue;
221 bool name_matches = func->get_function_name ().as_string ().compare (
222 segment_name.as_string ())
223 == 0;
224 if (!name_matches)
225 continue;
227 TyTy::BaseType *ty = nullptr;
228 if (!query_type (func->get_mappings ().get_hirid (), &ty))
229 continue;
230 if (ty->get_kind () == TyTy::TypeKind::ERROR)
231 continue;
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);
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 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 ();
253 if (!mut_match)
254 continue;
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 ();
265 if (!mut_match)
266 continue;
269 inherent_impl_fns.push_back ({func, impl, fnty});
270 return true;
273 TraitReference *trait_ref
274 = TraitResolver::Resolve (*impl->get_trait_ref ().get ());
275 rust_assert (!trait_ref->is_error ());
277 auto item_ref
278 = trait_ref->lookup_trait_item (segment_name.as_string (),
279 TraitItemReference::TraitItemType::FN);
280 if (item_ref->is_error ())
281 return true;
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)
286 return true;
288 HIR::TraitItemFunc *func = static_cast<HIR::TraitItemFunc *> (item);
289 if (!func->get_decl ().is_method ())
290 return true;
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);
299 return true;
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 ());
328 auto res
329 = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
330 false /*allow-autoderef*/);
331 bool ok = !res.is_error ();
332 if (ok)
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,
341 nullptr};
342 auto try_result = MethodCandidate{
343 PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
344 fn->clone (), trait_item->get_locus (), c),
345 adjs};
346 result.insert (std::move (try_result));
347 found_possible_candidate = true;
350 if (found_possible_candidate)
352 return true;
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)
359 continue;
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 ());
369 auto res
370 = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
371 false /*allow-autoderef*/);
372 bool ok = !res.is_error ();
373 if (ok)
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),
381 adjs};
382 result.insert (std::move (try_result));
383 found_possible_candidate = true;
386 if (found_possible_candidate)
388 return true;
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)
395 continue;
397 TyTy::FnType *fn = impl_item.ty;
398 rust_assert (fn->is_method ());
400 TyTy::BaseType *fn_self = fn->get_self_type ();
401 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 ());
405 auto res
406 = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
407 false /*allow-autoderef*/);
408 bool ok = !res.is_error ();
409 if (ok)
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),
417 adjs};
418 result.insert (std::move (try_result));
419 found_possible_candidate = true;
422 if (found_possible_candidate)
424 return true;
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 ());
437 auto res
438 = TypeCoercionRules::TryCoerce (&receiver, fn_self, UNDEF_LOCATION,
439 false /*allow-autoderef*/);
440 bool ok = !res.is_error ();
441 if (ok)
443 std::vector<Adjustment> adjs = append_adjustments (res.adjustments);
444 PathProbeCandidate::TraitItemCandidate c{trait_item.reference,
445 trait_item.item_ref,
446 nullptr};
447 auto try_result = MethodCandidate{
448 PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
449 fn, trait_item.item->get_locus (), c),
450 adjs};
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 ())
470 continue;
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);
495 return combined;
498 } // namespace Resolver
499 } // namespace Rust