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