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-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 : 4509 : Adjuster::adjust_type (const std::vector<Adjustment> &adjustments)
35 : : {
36 : 4509 : if (adjustments.size () == 0)
37 : 2767 : return base->clone ();
38 : :
39 : 1742 : return adjustments.back ().get_expected ()->clone ();
40 : : }
41 : :
42 : : Adjustment
43 : 537 : Adjuster::try_deref_type (TyTy::BaseType *ty, LangItem::Kind deref_lang_item)
44 : : {
45 : 537 : TyTy::FnType *fn = nullptr;
46 : 537 : Adjustment::AdjustmentType requires_ref_adjustment
47 : : = Adjustment::AdjustmentType::ERROR;
48 : 537 : bool operator_overloaded
49 : 537 : = resolve_operator_overload_fn (deref_lang_item, ty, &fn,
50 : : &requires_ref_adjustment);
51 : 537 : if (!operator_overloaded)
52 : : {
53 : 458 : 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 : 251 : Adjuster::try_raw_deref_type (TyTy::BaseType *ty)
87 : : {
88 : 251 : bool is_valid_type = ty->get_kind () == TyTy::TypeKind::REF;
89 : 251 : if (!is_valid_type)
90 : 104 : return Adjustment::get_error ();
91 : :
92 : 147 : const TyTy::ReferenceType *ref_base
93 : : = static_cast<const TyTy::ReferenceType *> (ty);
94 : 147 : auto infered = ref_base->get_base ()->destructure ();
95 : :
96 : 147 : return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered);
97 : : }
98 : :
99 : : Adjustment
100 : 345 : Adjuster::try_unsize_type (TyTy::BaseType *ty)
101 : : {
102 : 345 : bool is_valid_type = ty->get_kind () == TyTy::TypeKind::ARRAY;
103 : 345 : if (!is_valid_type)
104 : 279 : return Adjustment::get_error ();
105 : :
106 : 66 : auto &mappings = Analysis::Mappings::get ();
107 : 66 : auto context = TypeCheckContext::get ();
108 : :
109 : 66 : const auto ref_base = static_cast<const TyTy::ArrayType *> (ty);
110 : 66 : auto slice_elem = ref_base->get_element_type ();
111 : :
112 : 66 : auto slice
113 : 66 : = new TyTy::SliceType (mappings.get_next_hir_id (), ty->get_ident ().locus,
114 : 132 : TyTy::TyVar (slice_elem->get_ref ()));
115 : 66 : context->insert_implicit_type (slice->get_ref (), slice);
116 : :
117 : 66 : return Adjustment (Adjustment::AdjustmentType::UNSIZE, ty, slice);
118 : : }
119 : :
120 : : static bool
121 : 537 : 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 : 537 : auto context = TypeCheckContext::get ();
127 : 537 : auto &mappings = Analysis::Mappings::get ();
128 : :
129 : : // look up lang item for arithmetic type
130 : 537 : std::string associated_item_name = LangItem::ToString (lang_item_type);
131 : 537 : auto lang_item_defined = mappings.lookup_lang_item (lang_item_type);
132 : :
133 : 537 : 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 : fn->prepare_higher_ranked_bounds ();
239 : 79 : rust_debug ("resolved operator overload to: {%u} {%s}",
240 : : candidate.candidate.ty->get_ref (),
241 : : candidate.candidate.ty->debug_str ().c_str ());
242 : :
243 : 79 : if (fn->needs_substitution ())
244 : : {
245 : 79 : if (lhs->get_kind () == TyTy::TypeKind::ADT)
246 : : {
247 : 63 : const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (lhs);
248 : :
249 : 63 : auto s = fn->get_self_type ()->get_root ();
250 : 63 : rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
251 : 63 : const TyTy::ADTType *self_adt
252 : : = static_cast<const TyTy::ADTType *> (s);
253 : :
254 : : // we need to grab the Self substitutions as the inherit type
255 : : // parameters for this
256 : 63 : if (self_adt->needs_substitution ())
257 : : {
258 : 63 : rust_assert (adt->was_substituted ());
259 : :
260 : 63 : TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
261 : 63 : = GetUsedSubstArgs::From (adt);
262 : :
263 : 63 : TyTy::SubstitutionArgumentMappings inherit_type_args
264 : : = self_adt->solve_mappings_from_receiver_for_self (
265 : 63 : used_args_in_prev_segment);
266 : :
267 : : // there may or may not be inherited type arguments
268 : 63 : if (!inherit_type_args.is_error ())
269 : : {
270 : : // need to apply the inherited type arguments to the
271 : : // function
272 : 63 : lookup = fn->handle_substitions (inherit_type_args);
273 : : }
274 : 63 : }
275 : : }
276 : : else
277 : : {
278 : 16 : rust_assert (candidate.adjustments.size () < 2);
279 : :
280 : : // lets infer the params for this we could probably fix this up by
281 : : // actually just performing a substitution of a single param but this
282 : : // seems more generic i think.
283 : : //
284 : : // this is the case where we had say Foo<&Bar>> and we have derefed to
285 : : // the &Bar and we are trying to match a method self of Bar which
286 : : // requires another deref which is matched to the deref trait impl of
287 : : // &&T so this requires another reference and deref call
288 : :
289 : 16 : lookup = fn->infer_substitions (UNDEF_LOCATION);
290 : 16 : rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
291 : 16 : fn = static_cast<TyTy::FnType *> (lookup);
292 : :
293 : 16 : location_t unify_locus = mappings.lookup_location (lhs->get_ref ());
294 : 16 : unify_site (lhs->get_ref (),
295 : 16 : TyTy::TyWithLocation (fn->get_self_type ()),
296 : 16 : TyTy::TyWithLocation (adjusted_self), unify_locus);
297 : :
298 : 16 : lookup = fn;
299 : : }
300 : : }
301 : :
302 : 79 : if (candidate.adjustments.size () > 0)
303 : 77 : *requires_ref_adjustment = candidate.adjustments.at (0).get_type ();
304 : :
305 : 79 : *resolved_fn = static_cast<TyTy::FnType *> (lookup);
306 : :
307 : 79 : return true;
308 : 180 : }
309 : :
310 : 79683 : AutoderefCycle::AutoderefCycle (bool autoderef_flag)
311 : 79683 : : autoderef_flag (autoderef_flag)
312 : 79683 : {}
313 : :
314 : 79683 : AutoderefCycle::~AutoderefCycle () {}
315 : :
316 : : void
317 : 10739 : AutoderefCycle::try_hook (const TyTy::BaseType &)
318 : 10739 : {}
319 : :
320 : : bool
321 : 16440 : AutoderefCycle::cycle (TyTy::BaseType *receiver)
322 : : {
323 : 16440 : TyTy::BaseType *r = receiver;
324 : 16720 : while (true)
325 : : {
326 : 16580 : rust_debug ("autoderef try 1: {%s}", r->debug_str ().c_str ());
327 : 16580 : if (try_autoderefed (r))
328 : 16440 : return true;
329 : :
330 : : // 4. deref to to 1, if cannot deref then quit
331 : 3114 : if (autoderef_flag)
332 : : return false;
333 : :
334 : : // try unsize
335 : 345 : Adjustment unsize = Adjuster::try_unsize_type (r);
336 : 345 : if (!unsize.is_error ())
337 : : {
338 : 66 : adjustments.push_back (unsize);
339 : 66 : auto unsize_r = unsize.get_expected ();
340 : :
341 : 66 : rust_debug ("autoderef try unsize: {%s}",
342 : : unsize_r->debug_str ().c_str ());
343 : 66 : if (try_autoderefed (unsize_r))
344 : : return true;
345 : :
346 : 1 : adjustments.pop_back ();
347 : : }
348 : :
349 : 280 : bool is_ptr = receiver->get_kind () == TyTy::TypeKind::POINTER;
350 : 280 : if (is_ptr)
351 : : {
352 : : // deref of raw pointers is unsafe
353 : : return false;
354 : : }
355 : :
356 : 279 : Adjustment deref = Adjuster::try_deref_type (r, LangItem::Kind::DEREF);
357 : 279 : if (!deref.is_error ())
358 : : {
359 : 65 : auto deref_r = deref.get_expected ();
360 : 65 : adjustments.push_back (deref);
361 : :
362 : 65 : rust_debug ("autoderef try lang-item DEREF: {%s}",
363 : : deref_r->debug_str ().c_str ());
364 : 65 : if (try_autoderefed (deref_r))
365 : : return true;
366 : :
367 : 44 : adjustments.pop_back ();
368 : : }
369 : :
370 : 258 : Adjustment deref_mut
371 : 258 : = Adjuster::try_deref_type (r, LangItem::Kind::DEREF_MUT);
372 : 258 : if (!deref_mut.is_error ())
373 : : {
374 : 14 : auto deref_r = deref_mut.get_expected ();
375 : 14 : adjustments.push_back (deref_mut);
376 : :
377 : 14 : rust_debug ("autoderef try lang-item DEREF_MUT: {%s}",
378 : : deref_r->debug_str ().c_str ());
379 : 14 : if (try_autoderefed (deref_r))
380 : : return true;
381 : :
382 : 7 : adjustments.pop_back ();
383 : : }
384 : :
385 : 251 : if (!deref_mut.is_error ())
386 : : {
387 : 7 : auto deref_r = deref_mut.get_expected ();
388 : 7 : adjustments.push_back (deref_mut);
389 : 7 : Adjustment raw_deref = Adjuster::try_raw_deref_type (deref_r);
390 : 7 : adjustments.push_back (raw_deref);
391 : 7 : deref_r = raw_deref.get_expected ();
392 : :
393 : 7 : if (try_autoderefed (deref_r))
394 : 7 : return true;
395 : :
396 : 0 : adjustments.pop_back ();
397 : 0 : adjustments.pop_back ();
398 : : }
399 : :
400 : 244 : if (!deref.is_error ())
401 : : {
402 : 30 : r = deref.get_expected ();
403 : 30 : adjustments.push_back (deref);
404 : : }
405 : 244 : Adjustment raw_deref = Adjuster::try_raw_deref_type (r);
406 : 244 : if (raw_deref.is_error ())
407 : : return false;
408 : :
409 : 140 : r = raw_deref.get_expected ();
410 : 140 : adjustments.push_back (raw_deref);
411 : 140 : }
412 : : return false;
413 : : }
414 : :
415 : : bool
416 : 16732 : AutoderefCycle::try_autoderefed (TyTy::BaseType *r)
417 : : {
418 : 16732 : try_hook (*r);
419 : :
420 : : // 1. try raw
421 : 16732 : if (select (*r))
422 : : return true;
423 : :
424 : : // 2. try ref
425 : 5589 : TyTy::ReferenceType *r1
426 : 5589 : = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
427 : 11178 : Mutability::Imm);
428 : 5589 : adjustments.push_back (
429 : 5589 : Adjustment (Adjustment::AdjustmentType::IMM_REF, r, r1));
430 : 5589 : if (select (*r1))
431 : : return true;
432 : :
433 : 3246 : adjustments.pop_back ();
434 : :
435 : : // 3. try mut ref
436 : 3246 : TyTy::ReferenceType *r2
437 : 3246 : = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
438 : 6492 : Mutability::Mut);
439 : 3246 : adjustments.push_back (
440 : 3246 : Adjustment (Adjustment::AdjustmentType::MUT_REF, r, r2));
441 : 3246 : if (select (*r2))
442 : : return true;
443 : :
444 : 3166 : adjustments.pop_back ();
445 : :
446 : 3166 : return false;
447 : : }
448 : :
449 : : } // namespace Resolver
450 : : } // namespace Rust
|