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