]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/rust/typecheck/rust-autoderef.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / typecheck / rust-autoderef.cc
CommitLineData
83ffe9cd 1// Copyright (C) 2020-2023 Free Software Foundation, Inc.
06688fe4
PH
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
24namespace Rust {
25namespace Resolver {
26
27static bool
28resolve_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
33TyTy::BaseType *
34Adjuster::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
42Adjustment
43Adjuster::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
87Adjustment
88Adjuster::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
101Adjustment
102Adjuster::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
122static bool
123resolve_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 candidate
143 = MethodResolver::Probe (ty, HIR::PathIdentSegment (associated_item_name),
144 true);
145
146 bool have_implementation_for_lang_item = !candidate.is_error ();
147 if (!have_implementation_for_lang_item)
148 return false;
149
150 // Get the adjusted self
151 Adjuster adj (ty);
152 TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
153
154 // is this the case we are recursive
155 // handle the case where we are within the impl block for this
156 // lang_item otherwise we end up with a recursive operator overload
157 // such as the i32 operator overload trait
158 TypeCheckContextItem &fn_context = context->peek_context ();
159 if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
160 {
161 auto &impl_item = fn_context.get_impl_item ();
162 HIR::ImplBlock *parent = impl_item.first;
163 HIR::Function *fn = impl_item.second;
164
165 if (parent->has_trait_ref ()
166 && fn->get_function_name ().compare (associated_item_name) == 0)
167 {
168 TraitReference *trait_reference
169 = TraitResolver::Lookup (*parent->get_trait_ref ().get ());
170 if (!trait_reference->is_error ())
171 {
172 TyTy::BaseType *lookup = nullptr;
173 bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
174 &lookup);
175 rust_assert (ok);
176 rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
177
178 TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
179 rust_assert (fntype->is_method ());
180
181 bool is_lang_item_impl
182 = trait_reference->get_mappings ().get_defid ()
183 == respective_lang_item_id;
184 bool self_is_lang_item_self
185 = fntype->get_self_type ()->is_equal (*adjusted_self);
186 bool recursive_operator_overload
187 = is_lang_item_impl && self_is_lang_item_self;
188
189 if (recursive_operator_overload)
190 return false;
191 }
192 }
193 }
194
195 TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
196
197 // rust only support impl item deref operator overloading ie you must have an
198 // impl block for it
199 rust_assert (candidate.candidate.type
200 == PathProbeCandidate::CandidateType::IMPL_FUNC);
201 *impl_item = candidate.candidate.item.impl.impl_item;
202
203 rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
204 TyTy::BaseType *lookup = lookup_tyty;
205 TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
206 rust_assert (fn->is_method ());
207
208 if (fn->needs_substitution ())
209 {
210 if (ty->get_kind () == TyTy::TypeKind::ADT)
211 {
212 const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (ty);
213
214 auto s = fn->get_self_type ()->get_root ();
215 rust_assert (s->can_eq (adt, false));
216 rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
217 const TyTy::ADTType *self_adt
218 = static_cast<const TyTy::ADTType *> (s);
219
220 // we need to grab the Self substitutions as the inherit type
221 // parameters for this
222 if (self_adt->needs_substitution ())
223 {
224 rust_assert (adt->was_substituted ());
225
226 TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
227 = GetUsedSubstArgs::From (adt);
228
229 TyTy::SubstitutionArgumentMappings inherit_type_args
230 = self_adt->solve_mappings_from_receiver_for_self (
231 used_args_in_prev_segment);
232
233 // there may or may not be inherited type arguments
234 if (!inherit_type_args.is_error ())
235 {
236 // need to apply the inherited type arguments to the
237 // function
238 lookup = fn->handle_substitions (inherit_type_args);
239 }
240 }
241 }
242 else
243 {
244 rust_assert (candidate.adjustments.size () < 2);
245
246 // lets infer the params for this we could probably fix this up by
247 // actually just performing a substitution of a single param but this
248 // seems more generic i think.
249 //
250 // this is the case where we had say Foo<&Bar>> and we have derefed to
251 // the &Bar and we are trying to match a method self of Bar which
252 // requires another deref which is matched to the deref trait impl of
253 // &&T so this requires another reference and deref call
254
255 lookup = fn->infer_substitions (Location ());
256 rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
257 fn = static_cast<TyTy::FnType *> (lookup);
258 fn->get_self_type ()->unify (adjusted_self);
259 lookup = fn;
260 }
261 }
262
263 if (candidate.adjustments.size () > 0)
264 *requires_ref_adjustment = candidate.adjustments.at (0).get_type ();
265
266 *resolved_fn = static_cast<TyTy::FnType *> (lookup);
267
268 return true;
269}
270
271AutoderefCycle::AutoderefCycle (bool autoderef_flag)
272 : autoderef_flag (autoderef_flag)
273{}
274
275AutoderefCycle::~AutoderefCycle () {}
276
277void
278AutoderefCycle::try_hook (const TyTy::BaseType &)
279{}
280
281bool
282AutoderefCycle::cycle (const TyTy::BaseType *receiver)
283{
284 const TyTy::BaseType *r = receiver;
285 while (true)
286 {
287 if (try_autoderefed (r))
288 return true;
289
290 // 4. deref to to 1, if cannot deref then quit
291 if (autoderef_flag)
292 return false;
293
294 // try unsize
295 Adjustment unsize = Adjuster::try_unsize_type (r);
296 if (!unsize.is_error ())
297 {
298 adjustments.push_back (unsize);
299 auto unsize_r = unsize.get_expected ();
300
301 if (try_autoderefed (unsize_r))
302 return true;
303
304 adjustments.pop_back ();
305 }
306
307 Adjustment deref
308 = Adjuster::try_deref_type (r, Analysis::RustLangItem::ItemType::DEREF);
309 if (!deref.is_error ())
310 {
311 auto deref_r = deref.get_expected ();
312 adjustments.push_back (deref);
313
314 if (try_autoderefed (deref_r))
315 return true;
316
317 adjustments.pop_back ();
318 }
319
320 Adjustment deref_mut = Adjuster::try_deref_type (
321 r, Analysis::RustLangItem::ItemType::DEREF_MUT);
322 if (!deref_mut.is_error ())
323 {
324 auto deref_r = deref_mut.get_expected ();
325 adjustments.push_back (deref_mut);
326
327 if (try_autoderefed (deref_r))
328 return true;
329
330 adjustments.pop_back ();
331 }
332
333 if (!deref_mut.is_error ())
334 {
335 auto deref_r = deref_mut.get_expected ();
336 adjustments.push_back (deref_mut);
337 Adjustment raw_deref = Adjuster::try_raw_deref_type (deref_r);
338 adjustments.push_back (raw_deref);
339 deref_r = raw_deref.get_expected ();
340
341 if (try_autoderefed (deref_r))
342 return true;
343
344 adjustments.pop_back ();
345 adjustments.pop_back ();
346 }
347
348 if (!deref.is_error ())
349 {
350 r = deref.get_expected ();
351 adjustments.push_back (deref);
352 }
353 Adjustment raw_deref = Adjuster::try_raw_deref_type (r);
354 if (raw_deref.is_error ())
355 return false;
356
357 r = raw_deref.get_expected ();
358 adjustments.push_back (raw_deref);
359 }
360 return false;
361}
362
363bool
364AutoderefCycle::try_autoderefed (const TyTy::BaseType *r)
365{
366 try_hook (*r);
367
368 // 1. try raw
369 if (select (*r))
370 return true;
371
372 // 2. try ref
373 TyTy::ReferenceType *r1
374 = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
375 Mutability::Imm);
376 adjustments.push_back (
377 Adjustment (Adjustment::AdjustmentType::IMM_REF, r, r1));
378 if (select (*r1))
379 return true;
380
381 adjustments.pop_back ();
382
383 // 3. try mut ref
384 TyTy::ReferenceType *r2
385 = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
386 Mutability::Mut);
387 adjustments.push_back (
388 Adjustment (Adjustment::AdjustmentType::MUT_REF, r, r2));
389 if (select (*r2))
390 return true;
391
392 adjustments.pop_back ();
393
394 return false;
395}
396
397} // namespace Resolver
398} // namespace Rust