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-autoderef.h"
20 : #include "rust-hir-path-probe.h"
21 : #include "rust-hir-dot-operator.h"
22 : #include "rust-hir-trait-resolve.h"
23 : #include "rust-type-util.h"
24 : #include "rust-substitution-mapper.h"
25 :
26 : namespace Rust {
27 : namespace Resolver {
28 :
29 : static bool resolve_operator_overload_fn (
30 : LangItem::Kind lang_item_type, TyTy::BaseType *ty, TyTy::FnType **resolved_fn,
31 : Adjustment::AdjustmentType *requires_ref_adjustment);
32 :
33 : TyTy::BaseType *
34 4595 : Adjuster::adjust_type (const std::vector<Adjustment> &adjustments)
35 : {
36 4595 : if (adjustments.size () == 0)
37 2842 : return base->clone ();
38 :
39 1753 : return adjustments.back ().get_expected ()->clone ();
40 : }
41 :
42 : Adjustment
43 573 : Adjuster::try_deref_type (TyTy::BaseType *ty, LangItem::Kind deref_lang_item)
44 : {
45 573 : TyTy::FnType *fn = nullptr;
46 573 : Adjustment::AdjustmentType requires_ref_adjustment
47 : = Adjustment::AdjustmentType::ERROR;
48 573 : bool operator_overloaded
49 573 : = resolve_operator_overload_fn (deref_lang_item, ty, &fn,
50 : &requires_ref_adjustment);
51 573 : if (!operator_overloaded)
52 : {
53 494 : return Adjustment::get_error ();
54 : }
55 :
56 79 : auto resolved_base = fn->get_return_type ()->destructure ();
57 79 : bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF;
58 79 : if (!is_valid_type)
59 0 : return Adjustment::get_error ();
60 :
61 79 : TyTy::ReferenceType *ref_base
62 : = static_cast<TyTy::ReferenceType *> (resolved_base);
63 :
64 79 : Adjustment::AdjustmentType adjustment_type
65 : = Adjustment::AdjustmentType::ERROR;
66 79 : switch (deref_lang_item)
67 : {
68 65 : case LangItem::Kind::DEREF:
69 65 : adjustment_type = Adjustment::AdjustmentType::DEREF;
70 65 : break;
71 :
72 14 : case LangItem::Kind::DEREF_MUT:
73 14 : adjustment_type = Adjustment::AdjustmentType::DEREF_MUT;
74 14 : break;
75 :
76 : default:
77 : break;
78 : }
79 :
80 79 : return Adjustment::get_op_overload_deref_adjustment (adjustment_type, ty,
81 : ref_base, fn,
82 : requires_ref_adjustment);
83 : }
84 :
85 : Adjustment
86 269 : Adjuster::try_raw_deref_type (TyTy::BaseType *ty)
87 : {
88 269 : bool is_valid_type = ty->get_kind () == TyTy::TypeKind::REF;
89 269 : if (!is_valid_type)
90 110 : return Adjustment::get_error ();
91 :
92 159 : const TyTy::ReferenceType *ref_base
93 : = static_cast<const TyTy::ReferenceType *> (ty);
94 159 : auto infered = ref_base->get_base ()->destructure ();
95 :
96 159 : return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered);
97 : }
98 :
99 : Adjustment
100 375 : Adjuster::try_unsize_type (TyTy::BaseType *ty)
101 : {
102 375 : bool is_valid_type = ty->get_kind () == TyTy::TypeKind::ARRAY;
103 375 : if (!is_valid_type)
104 297 : return Adjustment::get_error ();
105 :
106 78 : auto &mappings = Analysis::Mappings::get ();
107 78 : auto context = TypeCheckContext::get ();
108 :
109 78 : const auto ref_base = static_cast<const TyTy::ArrayType *> (ty);
110 78 : auto slice_elem = ref_base->get_element_type ();
111 :
112 78 : auto slice
113 78 : = new TyTy::SliceType (mappings.get_next_hir_id (), ty->get_ident ().locus,
114 156 : TyTy::TyVar (slice_elem->get_ref ()));
115 78 : context->insert_implicit_type (slice->get_ref (), slice);
116 :
117 78 : return Adjustment (Adjustment::AdjustmentType::UNSIZE, ty, slice);
118 : }
119 :
120 : static bool
121 573 : resolve_operator_overload_fn (
122 : LangItem::Kind lang_item_type, TyTy::BaseType *lhs,
123 : TyTy::FnType **resolved_fn,
124 : Adjustment::AdjustmentType *requires_ref_adjustment)
125 : {
126 573 : auto context = TypeCheckContext::get ();
127 573 : auto &mappings = Analysis::Mappings::get ();
128 :
129 : // look up lang item for arithmetic type
130 573 : std::string associated_item_name = LangItem::ToString (lang_item_type);
131 573 : auto lang_item_defined = mappings.lookup_lang_item (lang_item_type);
132 :
133 573 : if (!lang_item_defined)
134 : return false;
135 101 : DefId &respective_lang_item_id = lang_item_defined.value ();
136 :
137 : // we might be in a static or const context and unknown is fine
138 101 : TypeCheckContextItem current_context = TypeCheckContextItem::get_error ();
139 101 : if (context->have_function_context ())
140 : {
141 101 : current_context = context->peek_context ();
142 : }
143 :
144 : // this flags stops recurisve calls to try and deref when none is available
145 : // which will cause an infinite loop
146 101 : bool autoderef_flag = true;
147 202 : auto segment = HIR::PathIdentSegment (associated_item_name);
148 101 : auto candidates = MethodResolver::Probe (lhs, segment, autoderef_flag);
149 :
150 : // remove any recursive candidates
151 101 : std::set<MethodCandidate> resolved_candidates;
152 222 : for (auto &c : candidates)
153 : {
154 121 : const TyTy::BaseType *candidate_type = c.candidate.ty;
155 121 : rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF);
156 :
157 121 : const TyTy::FnType &fn
158 : = *static_cast<const TyTy::FnType *> (candidate_type);
159 :
160 121 : DefId current_fn_defid = current_context.get_defid ();
161 242 : bool recursive_candidated = fn.get_id () == current_fn_defid;
162 121 : if (!recursive_candidated)
163 : {
164 121 : resolved_candidates.insert (c);
165 : }
166 : }
167 :
168 101 : auto selected_candidates
169 101 : = MethodResolver::Select (resolved_candidates, lhs, {});
170 101 : bool have_implementation_for_lang_item = selected_candidates.size () > 0;
171 101 : if (!have_implementation_for_lang_item)
172 : return false;
173 :
174 100 : if (selected_candidates.size () > 1)
175 : {
176 : // no need to error out as we are just trying to see if there is a fit
177 : return false;
178 : }
179 :
180 : // Get the adjusted self
181 79 : MethodCandidate candidate = *selected_candidates.begin ();
182 79 : Adjuster adj (lhs);
183 79 : TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
184 :
185 79 : PathProbeCandidate &resolved_candidate = candidate.candidate;
186 79 : TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
187 79 : rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
188 79 : TyTy::BaseType *lookup = lookup_tyty;
189 79 : TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
190 158 : rust_assert (fn->is_method ());
191 :
192 88 : rust_debug ("is_impl_item_candidate: %s",
193 : resolved_candidate.is_impl_candidate () ? "true" : "false");
194 :
195 : // in the case where we resolve to a trait bound we have to be careful we are
196 : // able to do so there is a case where we are currently resolving the deref
197 : // operator overload function which is generic and this might resolve to the
198 : // trait item of deref which is not valid as its just another recursive case
199 79 : if (current_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
200 : {
201 0 : auto &impl_item = current_context.get_impl_item ();
202 0 : HIR::ImplBlock *parent = impl_item.first;
203 0 : HIR::Function *fn = impl_item.second;
204 :
205 0 : if (parent->has_trait_ref ()
206 0 : && fn->get_function_name ().as_string ().compare (
207 : associated_item_name)
208 : == 0)
209 : {
210 0 : TraitReference *trait_reference
211 0 : = TraitResolver::Lookup (parent->get_trait_ref ());
212 0 : if (!trait_reference->is_error ())
213 : {
214 0 : TyTy::BaseType *lookup = nullptr;
215 0 : bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
216 : &lookup);
217 0 : rust_assert (ok);
218 0 : rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
219 :
220 0 : TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
221 0 : rust_assert (fntype->is_method ());
222 :
223 0 : bool is_lang_item_impl
224 0 : = trait_reference->get_mappings ().get_defid ()
225 0 : == respective_lang_item_id;
226 0 : bool self_is_lang_item_self
227 0 : = fntype->get_self_type ()->is_equal (*adjusted_self);
228 0 : bool recursive_operator_overload
229 : = is_lang_item_impl && self_is_lang_item_self;
230 :
231 0 : if (recursive_operator_overload)
232 0 : return false;
233 : }
234 : }
235 : }
236 :
237 : // we found a valid operator overload
238 79 : rust_debug ("resolved operator overload to: {%u} {%s}",
239 : candidate.candidate.ty->get_ref (),
240 : candidate.candidate.ty->debug_str ().c_str ());
241 :
242 79 : if (fn->needs_substitution ())
243 : {
244 79 : if (lhs->get_kind () == TyTy::TypeKind::ADT)
245 : {
246 63 : const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (lhs);
247 :
248 63 : auto s = fn->get_self_type ()->get_root ();
249 63 : rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
250 63 : const TyTy::ADTType *self_adt
251 : = static_cast<const TyTy::ADTType *> (s);
252 :
253 : // we need to grab the Self substitutions as the inherit type
254 : // parameters for this
255 63 : if (self_adt->needs_substitution ())
256 : {
257 63 : rust_assert (adt->was_substituted ());
258 :
259 63 : TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
260 63 : = GetUsedSubstArgs::From (adt);
261 :
262 63 : TyTy::SubstitutionArgumentMappings inherit_type_args
263 : = self_adt->solve_mappings_from_receiver_for_self (
264 63 : used_args_in_prev_segment);
265 :
266 : // there may or may not be inherited type arguments
267 63 : if (!inherit_type_args.is_error ())
268 : {
269 : // need to apply the inherited type arguments to the
270 : // function
271 63 : lookup = fn->handle_substitions (inherit_type_args);
272 : }
273 63 : }
274 : }
275 : else
276 : {
277 16 : rust_assert (candidate.adjustments.size () < 2);
278 :
279 : // lets infer the params for this we could probably fix this up by
280 : // actually just performing a substitution of a single param but this
281 : // seems more generic i think.
282 : //
283 : // this is the case where we had say Foo<&Bar>> and we have derefed to
284 : // the &Bar and we are trying to match a method self of Bar which
285 : // requires another deref which is matched to the deref trait impl of
286 : // &&T so this requires another reference and deref call
287 :
288 16 : lookup = fn->infer_substitions (UNDEF_LOCATION);
289 16 : rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
290 16 : fn = static_cast<TyTy::FnType *> (lookup);
291 :
292 16 : location_t unify_locus = mappings.lookup_location (lhs->get_ref ());
293 16 : unify_site (lhs->get_ref (),
294 16 : TyTy::TyWithLocation (fn->get_self_type ()),
295 16 : TyTy::TyWithLocation (adjusted_self), unify_locus);
296 :
297 16 : lookup = fn;
298 : }
299 : }
300 :
301 79 : if (candidate.adjustments.size () > 0)
302 77 : *requires_ref_adjustment = candidate.adjustments.at (0).get_type ();
303 :
304 79 : *resolved_fn = static_cast<TyTy::FnType *> (lookup);
305 :
306 79 : return true;
307 180 : }
308 :
309 83184 : AutoderefCycle::AutoderefCycle (bool autoderef_flag)
310 83184 : : autoderef_flag (autoderef_flag)
311 83184 : {}
312 :
313 83184 : AutoderefCycle::~AutoderefCycle () {}
314 :
315 : void
316 10944 : AutoderefCycle::try_hook (const TyTy::BaseType &)
317 10944 : {}
318 :
319 : bool
320 16713 : AutoderefCycle::cycle (TyTy::BaseType *receiver)
321 : {
322 16713 : TyTy::BaseType *r = receiver;
323 17017 : while (true)
324 : {
325 16865 : rust_debug ("autoderef try 1: {%s}", r->debug_str ().c_str ());
326 16865 : if (try_autoderefed (r))
327 16713 : return true;
328 :
329 : // 4. deref to to 1, if cannot deref then quit
330 3242 : if (autoderef_flag)
331 : return false;
332 :
333 : // try owned_box
334 376 : if (auto deref_r = try_get_box_inner_type (r))
335 : {
336 1 : Adjustment box_deref (Adjustment::AdjustmentType::DEREF, r, *deref_r);
337 1 : adjustments.push_back (box_deref);
338 :
339 1 : rust_debug ("autoderef try owned_box: {%s}",
340 : (*deref_r)->debug_str ().c_str ());
341 :
342 1 : if (try_autoderefed (*deref_r))
343 1 : return true;
344 :
345 0 : adjustments.pop_back ();
346 : }
347 :
348 : // try unsize
349 375 : Adjustment unsize = Adjuster::try_unsize_type (r);
350 375 : if (!unsize.is_error ())
351 : {
352 78 : adjustments.push_back (unsize);
353 78 : auto unsize_r = unsize.get_expected ();
354 :
355 78 : rust_debug ("autoderef try unsize: {%s}",
356 : unsize_r->debug_str ().c_str ());
357 78 : if (try_autoderefed (unsize_r))
358 : return true;
359 :
360 1 : adjustments.pop_back ();
361 : }
362 :
363 298 : bool is_ptr = receiver->get_kind () == TyTy::TypeKind::POINTER;
364 298 : if (is_ptr)
365 : {
366 : // deref of raw pointers is unsafe
367 : return false;
368 : }
369 :
370 297 : Adjustment deref = Adjuster::try_deref_type (r, LangItem::Kind::DEREF);
371 297 : if (!deref.is_error ())
372 : {
373 65 : auto deref_r = deref.get_expected ();
374 65 : adjustments.push_back (deref);
375 :
376 65 : rust_debug ("autoderef try lang-item DEREF: {%s}",
377 : deref_r->debug_str ().c_str ());
378 65 : if (try_autoderefed (deref_r))
379 : return true;
380 :
381 44 : adjustments.pop_back ();
382 : }
383 :
384 276 : Adjustment deref_mut
385 276 : = Adjuster::try_deref_type (r, LangItem::Kind::DEREF_MUT);
386 276 : if (!deref_mut.is_error ())
387 : {
388 14 : auto deref_r = deref_mut.get_expected ();
389 14 : adjustments.push_back (deref_mut);
390 :
391 14 : rust_debug ("autoderef try lang-item DEREF_MUT: {%s}",
392 : deref_r->debug_str ().c_str ());
393 14 : if (try_autoderefed (deref_r))
394 : return true;
395 :
396 7 : adjustments.pop_back ();
397 : }
398 :
399 269 : if (!deref_mut.is_error ())
400 : {
401 7 : auto deref_r = deref_mut.get_expected ();
402 7 : adjustments.push_back (deref_mut);
403 7 : Adjustment raw_deref = Adjuster::try_raw_deref_type (deref_r);
404 7 : adjustments.push_back (raw_deref);
405 7 : deref_r = raw_deref.get_expected ();
406 :
407 7 : if (try_autoderefed (deref_r))
408 7 : return true;
409 :
410 0 : adjustments.pop_back ();
411 0 : adjustments.pop_back ();
412 : }
413 :
414 262 : if (!deref.is_error ())
415 : {
416 30 : r = deref.get_expected ();
417 30 : adjustments.push_back (deref);
418 : }
419 262 : Adjustment raw_deref = Adjuster::try_raw_deref_type (r);
420 262 : if (raw_deref.is_error ())
421 : return false;
422 :
423 152 : r = raw_deref.get_expected ();
424 152 : adjustments.push_back (raw_deref);
425 152 : }
426 : return false;
427 : }
428 :
429 : bool
430 17030 : AutoderefCycle::try_autoderefed (TyTy::BaseType *r)
431 : {
432 17030 : try_hook (*r);
433 :
434 : // 1. try raw
435 17030 : if (select (*r))
436 : return true;
437 :
438 : // 2. try ref
439 5740 : TyTy::ReferenceType *r1
440 5740 : = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
441 11480 : Mutability::Imm);
442 5740 : adjustments.emplace_back (Adjustment::AdjustmentType::IMM_REF, r, r1);
443 5740 : if (select (*r1))
444 : return true;
445 :
446 3374 : adjustments.pop_back ();
447 :
448 : // 3. try mut ref
449 3374 : TyTy::ReferenceType *r2
450 3374 : = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
451 6748 : Mutability::Mut);
452 3374 : adjustments.emplace_back (Adjustment::AdjustmentType::MUT_REF, r, r2);
453 3374 : if (select (*r2))
454 : return true;
455 :
456 3294 : adjustments.pop_back ();
457 :
458 3294 : return false;
459 : }
460 :
461 : } // namespace Resolver
462 : } // namespace Rust
|