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 : : #ifndef RUST_BIR_BUILDER_INTERNAL_H
20 : : #define RUST_BIR_BUILDER_INTERNAL_H
21 : :
22 : : #include "rust-bir-place.h"
23 : : #include "rust-hir-expr.h"
24 : : #include "rust-hir-item.h"
25 : : #include "rust-hir-type-check.h"
26 : : #include "rust-hir-visitor.h"
27 : : #include "rust-name-resolver.h"
28 : : #include "rust-bir.h"
29 : : #include "rust-bir-free-region.h"
30 : : #include "rust-immutable-name-resolution-context.h"
31 : : #include "options.h"
32 : :
33 : : namespace Rust {
34 : :
35 : : namespace TyTy {
36 : :
37 : : using Variance = VarianceAnalysis::Variance;
38 : :
39 : : class RenumberCtx
40 : : {
41 : : Polonius::Origin next_region = 0;
42 : :
43 : : public:
44 : : Polonius::Origin get_next_region () { return next_region++; }
45 : : };
46 : :
47 : : } // namespace TyTy
48 : :
49 : : namespace BIR {
50 : :
51 : : /** Holds the context of BIR building so that it can be shared/passed between
52 : : * different builders. */
53 : : struct BuilderContext
54 : : {
55 : : struct LoopAndLabelCtx
56 : : {
57 : : bool is_loop; // Loop or labelled block
58 : : NodeId label; // UNKNOWN_NODEID if no label (loop)
59 : : PlaceId label_var; // For break with value.
60 : : BasicBlockId break_bb;
61 : : BasicBlockId continue_bb; // Only valid for loops
62 : : ScopeId continue_scope;
63 : : // Break scope is the parent of the `continue_scope`.
64 : :
65 : 0 : LoopAndLabelCtx (bool is_loop = false, NodeId label = UNKNOWN_NODEID,
66 : : PlaceId label_var = INVALID_PLACE,
67 : : BasicBlockId break_bb = INVALID_BB,
68 : : BasicBlockId continue_bb = INVALID_BB,
69 : : ScopeId continue_scope = INVALID_SCOPE)
70 : 0 : : is_loop (is_loop), label (label), label_var (label_var),
71 : 0 : break_bb (break_bb), continue_bb (continue_bb),
72 : 0 : continue_scope (continue_scope)
73 : : {}
74 : : };
75 : :
76 : : // External context.
77 : : Resolver::TypeCheckContext &tyctx;
78 : : Resolver::Resolver &resolver;
79 : :
80 : : // BIR output
81 : : BasicBlocks basic_blocks;
82 : : BasicBlockId current_bb = ENTRY_BASIC_BLOCK;
83 : :
84 : : /**
85 : : * Allocation and lookup of places (variables, temporaries, paths, and
86 : : * constants)
87 : : */
88 : : PlaceDB place_db;
89 : : RegionBinder region_binder{place_db.expose_next_free_region ()};
90 : :
91 : : // Used for cleaner dump.
92 : : std::vector<PlaceId> arguments;
93 : : /**
94 : : * Since labels can be used to return values, we need to reserve a place for
95 : : * them. This map associates labels with their respective places.
96 : : */
97 : : std::unordered_map<NodeId, PlaceId> label_place_map;
98 : :
99 : : /** Context for current situation (loop, label, etc.) */
100 : : std::vector<LoopAndLabelCtx> loop_and_label_stack;
101 : :
102 : : FreeRegions fn_free_regions{{}};
103 : :
104 : : public:
105 : 36 : BuilderContext ()
106 : 36 : : tyctx (*Resolver::TypeCheckContext::get ()),
107 : 36 : resolver (*Resolver::Resolver::get ())
108 : : {
109 : 36 : basic_blocks.emplace_back (); // StartBB
110 : 36 : }
111 : :
112 : 166 : BasicBlock &get_current_bb () { return basic_blocks[current_bb]; }
113 : :
114 : : const LoopAndLabelCtx &lookup_label (NodeId label)
115 : : {
116 : : auto label_match = [label] (const LoopAndLabelCtx &info) {
117 : : return info.label != UNKNOWN_NODEID && info.label == label;
118 : : };
119 : :
120 : : auto found = std::find_if (loop_and_label_stack.rbegin (),
121 : : loop_and_label_stack.rend (), label_match);
122 : : rust_assert (found != loop_and_label_stack.rend ());
123 : : return *found;
124 : : }
125 : : };
126 : :
127 : : /** Common infrastructure for building BIR from HIR. */
128 : : class AbstractBuilder
129 : : {
130 : : protected:
131 : : BuilderContext &ctx;
132 : :
133 : : /**
134 : : * This emulates the return value of the visitor, to be able to use the
135 : : * current visitor infrastructure, where the return value is forced to be
136 : : * void.
137 : : */
138 : : PlaceId translated = INVALID_PLACE;
139 : :
140 : : protected:
141 : 42 : explicit AbstractBuilder (BuilderContext &ctx) : ctx (ctx) {}
142 : :
143 : 121 : PlaceId declare_variable (const Analysis::NodeMapping &node,
144 : : bool user_type_annotation = false)
145 : : {
146 : 121 : return declare_variable (node, lookup_type (node.get_hirid ()),
147 : 121 : user_type_annotation);
148 : : }
149 : :
150 : 121 : PlaceId declare_variable (const Analysis::NodeMapping &node,
151 : : TyTy::BaseType *ty,
152 : : bool user_type_annotation = false)
153 : : {
154 : 121 : const NodeId nodeid = node.get_nodeid ();
155 : :
156 : : // In debug mode, check that the variable is not already declared.
157 : 121 : rust_assert (ctx.place_db.lookup_variable (nodeid) == INVALID_PLACE);
158 : :
159 : 121 : auto place_id = ctx.place_db.add_variable (nodeid, ty);
160 : :
161 : 121 : if (ctx.place_db.get_current_scope_id () != INVALID_SCOPE)
162 : 121 : push_storage_live (place_id);
163 : :
164 : 121 : if (user_type_annotation)
165 : 0 : push_user_type_ascription (place_id, ty);
166 : :
167 : 121 : return place_id;
168 : : }
169 : :
170 : 46 : void push_new_scope () { ctx.place_db.push_new_scope (); }
171 : :
172 : 44 : void pop_scope ()
173 : : {
174 : 44 : auto &scope = ctx.place_db.get_current_scope ();
175 : 44 : if (ctx.place_db.get_current_scope_id () != INVALID_SCOPE)
176 : : {
177 : 44 : std::for_each (scope.locals.rbegin (), scope.locals.rend (),
178 : 139 : [&] (PlaceId place) { push_storage_dead (place); });
179 : : }
180 : 44 : ctx.place_db.pop_scope ();
181 : 44 : }
182 : :
183 : : bool intersection_empty (std::vector<PlaceId> &a, std::vector<PlaceId> &b)
184 : : {
185 : : for (auto &place : a)
186 : : {
187 : : if (std::find (b.begin (), b.end (), place) != b.end ())
188 : : return false;
189 : : }
190 : : return true;
191 : : }
192 : :
193 : 2 : void unwind_until (ScopeId final_scope)
194 : : {
195 : 2 : auto current_scope_id = ctx.place_db.get_current_scope_id ();
196 : 6 : while (current_scope_id != final_scope)
197 : : {
198 : 4 : auto &scope = ctx.place_db.get_scope (current_scope_id);
199 : :
200 : : // TODO: Perform stable toposort based on `borrowed_by`.
201 : :
202 : 4 : std::for_each (scope.locals.rbegin (), scope.locals.rend (),
203 : 6 : [&] (PlaceId place) { push_storage_dead (place); });
204 : 4 : current_scope_id = scope.parent;
205 : : }
206 : 2 : }
207 : :
208 : 54 : FreeRegions bind_regions (std::vector<TyTy::Region> regions,
209 : : FreeRegions parent_free_regions)
210 : : {
211 : 54 : FreeRegions free_regions;
212 : 75 : for (auto ®ion : regions)
213 : : {
214 : 21 : if (region.is_early_bound ())
215 : : {
216 : 18 : free_regions.push_back (parent_free_regions[region.get_index ()]);
217 : : }
218 : 3 : else if (region.is_static ())
219 : : {
220 : 1 : free_regions.push_back (STATIC_FREE_REGION);
221 : : }
222 : 2 : else if (region.is_anonymous ())
223 : : {
224 : 2 : free_regions.push_back (ctx.place_db.get_next_free_region ());
225 : : }
226 : 0 : else if (region.is_named ())
227 : : {
228 : 0 : rust_unreachable (); // FIXME
229 : : }
230 : : else
231 : : {
232 : 0 : rust_sorry_at (UNKNOWN_LOCATION, "Unimplemented");
233 : 0 : rust_unreachable ();
234 : : }
235 : : }
236 : 54 : return free_regions;
237 : : }
238 : :
239 : : protected: // Helpers to add BIR statements
240 : 185 : void push_assignment (PlaceId lhs, AbstractExpr *rhs, location_t location)
241 : : {
242 : 185 : ctx.get_current_bb ().statements.push_back (
243 : 185 : Statement::make_assignment (lhs, rhs, location));
244 : 185 : translated = lhs;
245 : 185 : }
246 : :
247 : 99 : void push_assignment (PlaceId lhs, PlaceId rhs, location_t location)
248 : : {
249 : 99 : push_assignment (lhs, new Assignment (rhs), location);
250 : 99 : }
251 : :
252 : 38 : void push_tmp_assignment (AbstractExpr *rhs, TyTy::BaseType *tyty,
253 : : location_t location)
254 : : {
255 : 38 : PlaceId tmp = ctx.place_db.add_temporary (tyty);
256 : 38 : push_storage_live (tmp);
257 : 38 : push_assignment (tmp, rhs, location);
258 : 38 : }
259 : :
260 : 14 : void push_tmp_assignment (PlaceId rhs, location_t location)
261 : : {
262 : 14 : push_tmp_assignment (new Assignment (rhs), ctx.place_db[rhs].tyty,
263 : : location);
264 : 14 : }
265 : :
266 : 7 : void push_switch (PlaceId switch_val, location_t location,
267 : : std::initializer_list<BasicBlockId> destinations = {})
268 : : {
269 : 7 : auto copy = move_place (switch_val, location);
270 : 7 : ctx.get_current_bb ().statements.push_back (Statement::make_switch (copy));
271 : 7 : ctx.get_current_bb ().successors.insert (
272 : 7 : ctx.get_current_bb ().successors.end (), destinations);
273 : 7 : }
274 : :
275 : 8 : void push_goto (BasicBlockId bb)
276 : : {
277 : 8 : ctx.get_current_bb ().statements.push_back (Statement::make_goto ());
278 : 8 : if (bb != INVALID_BB) // INVALID_BB means the goto will be resolved later.
279 : 0 : ctx.get_current_bb ().successors.push_back (bb);
280 : 8 : }
281 : :
282 : 159 : void push_storage_live (PlaceId place)
283 : : {
284 : 159 : ctx.get_current_bb ().statements.push_back (
285 : 159 : Statement::make_storage_live (place));
286 : 159 : }
287 : :
288 : 145 : void push_storage_dead (PlaceId place)
289 : : {
290 : 145 : ctx.get_current_bb ().statements.push_back (
291 : 145 : Statement::make_storage_dead (place));
292 : 145 : }
293 : :
294 : 0 : void push_user_type_ascription (PlaceId place, TyTy::BaseType *ty)
295 : : {
296 : 0 : ctx.get_current_bb ().statements.push_back (
297 : 0 : Statement::make_user_type_ascription (place, ty));
298 : 0 : }
299 : :
300 : 90 : void push_fake_read (PlaceId place)
301 : : {
302 : 90 : ctx.get_current_bb ().statements.push_back (
303 : 90 : Statement::make_fake_read (place));
304 : 90 : }
305 : :
306 : 38 : void push_return (location_t location)
307 : : {
308 : 38 : ctx.get_current_bb ().statements.push_back (
309 : 38 : Statement::make_return (location));
310 : 38 : }
311 : :
312 : 13 : PlaceId borrow_place (PlaceId place_id, TyTy::BaseType *ty,
313 : : location_t location)
314 : : {
315 : 13 : auto mutability = ty->as<const TyTy::ReferenceType> ()->mutability ();
316 : 13 : auto loan = ctx.place_db.add_loan ({mutability, place_id, location});
317 : 13 : push_tmp_assignment (
318 : : new BorrowExpr (place_id, loan,
319 : 13 : ctx.place_db.get_next_free_region ().value),
320 : : ty, location);
321 : 13 : return translated;
322 : : }
323 : :
324 : 29 : PlaceId move_place (PlaceId arg, location_t location)
325 : : {
326 : 29 : auto &place = ctx.place_db[arg];
327 : :
328 : 29 : if (place.is_constant ())
329 : 4 : return arg;
330 : :
331 : 25 : if (place.tyty->is<TyTy::ReferenceType> ())
332 : 13 : return reborrow_place (arg, location);
333 : :
334 : 12 : if (place.is_rvalue ())
335 : 3 : return arg;
336 : :
337 : 9 : push_tmp_assignment (arg, location);
338 : 9 : return translated;
339 : : }
340 : :
341 : 13 : PlaceId reborrow_place (PlaceId arg, location_t location)
342 : : {
343 : 13 : auto ty = ctx.place_db[arg].tyty->as<TyTy::ReferenceType> ();
344 : 13 : return borrow_place (ctx.place_db.lookup_or_add_path (Place::DEREF,
345 : : ty->get_base (), arg),
346 : 13 : ty, location);
347 : : }
348 : :
349 : : template <typename T>
350 : 17 : void move_all (T &args, std::vector<location_t> locations)
351 : : {
352 : 17 : rust_assert (args.size () == locations.size ());
353 : 17 : std::transform (args.begin (), args.end (), locations.begin (),
354 : 17 : args.begin (), [this] (PlaceId arg, location_t location) {
355 : 17 : return move_place (arg, location);
356 : : });
357 : 17 : }
358 : :
359 : : protected: // CFG helpers
360 : 28 : BasicBlockId new_bb ()
361 : : {
362 : 28 : ctx.basic_blocks.emplace_back ();
363 : 28 : return {ctx.basic_blocks.size () - 1};
364 : : }
365 : :
366 : 11 : BasicBlockId start_new_consecutive_bb ()
367 : : {
368 : 11 : BasicBlockId bb = new_bb ();
369 : 11 : if (!ctx.get_current_bb ().is_terminated ())
370 : : {
371 : 0 : push_goto (bb);
372 : : }
373 : : else
374 : : {
375 : 11 : add_jump_to (bb);
376 : : }
377 : 11 : ctx.current_bb = bb;
378 : 11 : return bb;
379 : : }
380 : :
381 : 33 : void add_jump (BasicBlockId from, BasicBlockId to)
382 : : {
383 : 15 : ctx.basic_blocks[from].successors.emplace_back (to);
384 : : }
385 : :
386 : 11 : void add_jump_to (BasicBlockId bb) { add_jump (ctx.current_bb, bb); }
387 : :
388 : : protected: // HIR resolution helpers
389 : : template <typename T>
390 : 292 : WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (T &hir_node) const
391 : : {
392 : 292 : return lookup_type (hir_node.get_mappings ().get_hirid ());
393 : : }
394 : :
395 : 413 : WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (HirId hirid) const
396 : : {
397 : 413 : TyTy::BaseType *type = nullptr;
398 : 413 : bool ok = ctx.tyctx.lookup_type (hirid, &type);
399 : 413 : rust_assert (ok);
400 : 413 : rust_assert (type != nullptr);
401 : 413 : return type;
402 : : }
403 : :
404 : 0 : template <typename T> NodeId resolve_label (T &expr)
405 : : {
406 : : NodeId resolved_label;
407 : 0 : if (flag_name_resolution_2_0)
408 : : {
409 : : auto &nr_ctx
410 : 0 : = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
411 : 0 : auto res = nr_ctx.lookup (expr.get_mappings ().get_nodeid ());
412 : 0 : rust_assert (res.has_value ());
413 : 0 : resolved_label = res.value ();
414 : : }
415 : : else
416 : : {
417 : 0 : bool ok = ctx.resolver.lookup_resolved_label (
418 : 0 : expr.get_mappings ().get_nodeid (), &resolved_label);
419 : 0 : rust_assert (ok);
420 : : }
421 : 0 : return resolved_label;
422 : : }
423 : :
424 : 2 : template <typename T> PlaceId resolve_variable (T &variable)
425 : : {
426 : : NodeId variable_id;
427 : 2 : if (flag_name_resolution_2_0)
428 : : {
429 : : auto &nr_ctx
430 : 2 : = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
431 : 2 : auto res = nr_ctx.lookup (variable.get_mappings ().get_nodeid ());
432 : 2 : rust_assert (res.has_value ());
433 : 2 : variable_id = res.value ();
434 : : }
435 : : else
436 : : {
437 : 0 : bool ok = ctx.resolver.lookup_resolved_name (
438 : 0 : variable.get_mappings ().get_nodeid (), &variable_id);
439 : 0 : rust_assert (ok);
440 : : }
441 : 2 : return ctx.place_db.lookup_variable (variable_id);
442 : : }
443 : :
444 : : template <typename T>
445 : 108 : PlaceId resolve_variable_or_fn (T &variable, TyTy::BaseType *ty)
446 : : {
447 : 108 : ty = (ty) ? ty : lookup_type (variable);
448 : : // Unlike variables,
449 : : // functions do not have to be declared in PlaceDB before use.
450 : : NodeId variable_id;
451 : 108 : if (flag_name_resolution_2_0)
452 : : {
453 : : auto &nr_ctx
454 : 108 : = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
455 : 108 : auto res = nr_ctx.lookup (variable.get_mappings ().get_nodeid ());
456 : 108 : rust_assert (res.has_value ());
457 : 108 : variable_id = res.value ();
458 : : }
459 : : else
460 : : {
461 : 0 : bool ok = ctx.resolver.lookup_resolved_name (
462 : 0 : variable.get_mappings ().get_nodeid (), &variable_id);
463 : 0 : rust_assert (ok);
464 : : }
465 : 108 : if (ty->is<TyTy::FnType> ())
466 : 11 : return ctx.place_db.get_constant (ty);
467 : : else
468 : 97 : return ctx.place_db.lookup_or_add_variable (variable_id, ty);
469 : : }
470 : :
471 : : protected: // Implicit conversions.
472 : : /**
473 : : * Performs implicit coercions on the `translated` place defined for a
474 : : * coercion site.
475 : : *
476 : : * Reference: https://doc.rust-lang.org/reference/type-coercions.html
477 : : *
478 : : * The only coercion relevant to BIR is the autoderef. All other coercions
479 : : * will be taken in account because the type is extracted from each node and
480 : : * not derived from operations in HIR/BIR. The borrowck does not care about
481 : : * type transitions. Lifetimes are not coerced, rather new are created with
482 : : * defined bounds to the original ones.
483 : : */
484 : 17 : void coercion_site (PlaceId &place, TyTy::BaseType *expected_ty)
485 : : {
486 : 51 : auto count_ref_levels = [] (TyTy::BaseType *ty) {
487 : 34 : size_t count = 0;
488 : 58 : while (auto r = ty->try_as<TyTy::ReferenceType> ())
489 : : {
490 : 24 : ty = r->get_base ();
491 : 24 : count++;
492 : 24 : }
493 : 34 : return count;
494 : : };
495 : :
496 : 17 : auto actual_ty = ctx.place_db[place].tyty;
497 : :
498 : 17 : auto deref_count
499 : 17 : = count_ref_levels (actual_ty) - count_ref_levels (expected_ty);
500 : :
501 : 17 : for (size_t i = 0; i < deref_count; ++i)
502 : : {
503 : 0 : actual_ty = actual_ty->as<TyTy::ReferenceType> ()->get_base ();
504 : 0 : place
505 : 0 : = ctx.place_db.lookup_or_add_path (Place::DEREF, actual_ty, place);
506 : : }
507 : 17 : }
508 : :
509 : : /** Dereferences the `translated` place until it is at most one reference
510 : : * and return the base type. */
511 : 5 : TyTy::BaseType *autoderef (PlaceId &place)
512 : : {
513 : 5 : auto ty = ctx.place_db[place].tyty;
514 : 6 : while (auto ref_ty = ty->try_as<TyTy::ReferenceType> ())
515 : : {
516 : 1 : ty = ref_ty->get_base ();
517 : 1 : place = ctx.place_db.lookup_or_add_path (Place::DEREF, ty, place);
518 : 1 : }
519 : 5 : return ty;
520 : : }
521 : :
522 : : void autoref ()
523 : : {
524 : : if (ctx.place_db[translated].tyty->get_kind () != TyTy::REF)
525 : : {
526 : : // FIXME: not sure how to fetch correct location for this
527 : : // this function is unused yet, so can ignore for now
528 : : auto ty = ctx.place_db[translated].tyty;
529 : : translated
530 : : = borrow_place (translated,
531 : : new TyTy::ReferenceType (ty->get_ref (),
532 : : TyTy::TyVar (ty->get_ref ()),
533 : : Mutability::Imm),
534 : : UNKNOWN_LOCATION);
535 : : }
536 : : }
537 : : };
538 : :
539 : : class AbstractExprBuilder : public AbstractBuilder,
540 : : public HIR::HIRExpressionVisitor
541 : : {
542 : : protected:
543 : : /**
544 : : * Optional place for the result of the evaluated expression.
545 : : * Valid if value is not `INVALID_PLACE`.
546 : : * Used when return place must be created by caller (return for if-else).
547 : : */
548 : : PlaceId expr_return_place = INVALID_PLACE;
549 : :
550 : : protected:
551 : 40 : explicit AbstractExprBuilder (BuilderContext &ctx,
552 : : PlaceId expr_return_place = INVALID_PLACE)
553 : 40 : : AbstractBuilder (ctx), expr_return_place (expr_return_place)
554 : : {}
555 : :
556 : : /**
557 : : * Wrapper that provides return value based API inside a visitor which has
558 : : * to use global state to pass the data around.
559 : : * @param dst_place Place to assign the produced value to, optionally
560 : : * allocated by the caller.
561 : : * */
562 : 276 : PlaceId visit_expr (HIR::Expr &expr, PlaceId dst_place = INVALID_PLACE)
563 : : {
564 : : // Save to support proper recursion.
565 : 276 : auto saved = expr_return_place;
566 : 276 : expr_return_place = dst_place;
567 : 276 : translated = INVALID_PLACE;
568 : 271 : expr.accept_vis (*this);
569 : 276 : expr_return_place = saved;
570 : 276 : auto result = translated;
571 : 276 : translated = INVALID_PLACE;
572 : 271 : return result;
573 : : }
574 : :
575 : : /**
576 : : * Create a return value of a subexpression, which produces an expression.
577 : : * Use `return_place` for subexpression that only produce a place (look it
578 : : * up) to avoid needless assignments.
579 : : *
580 : : * @param can_panic mark that expression can panic to insert jump to
581 : : * cleanup.
582 : : */
583 : 59 : void return_expr (AbstractExpr *expr, TyTy::BaseType *ty, location_t location,
584 : : bool can_panic = false)
585 : : {
586 : 59 : if (expr_return_place != INVALID_PLACE)
587 : : {
588 : 48 : push_assignment (expr_return_place, expr, location);
589 : : }
590 : : else
591 : : {
592 : 11 : push_tmp_assignment (expr, ty, location);
593 : : }
594 : :
595 : 59 : if (can_panic)
596 : : {
597 : 11 : start_new_consecutive_bb ();
598 : : }
599 : :
600 : 59 : if (ty->is<TyTy::ReferenceType> ()
601 : 59 : || ctx.place_db[translated].is_constant ())
602 : : {
603 : 43 : push_fake_read (translated);
604 : : }
605 : 59 : }
606 : :
607 : : /** Mark place to be a result of processed subexpression. */
608 : 174 : void return_place (PlaceId place, location_t location, bool can_panic = false)
609 : : {
610 : 174 : if (expr_return_place != INVALID_PLACE)
611 : : {
612 : : // Return place is already allocated, no need to defer assignment.
613 : 64 : push_assignment (expr_return_place, place, location);
614 : : }
615 : : else
616 : : {
617 : 110 : translated = place;
618 : : }
619 : :
620 : 174 : if (can_panic)
621 : : {
622 : 0 : start_new_consecutive_bb ();
623 : : }
624 : :
625 : 174 : if (ctx.place_db[place].is_constant ())
626 : : {
627 : 47 : push_fake_read (translated);
628 : : }
629 : 174 : }
630 : :
631 : : /** Explicitly return a unit value. Expression produces no value. */
632 : 4 : void return_unit (HIR::Expr &expr)
633 : : {
634 : 4 : translated = ctx.place_db.get_constant (lookup_type (expr));
635 : 4 : }
636 : :
637 : 42 : PlaceId return_borrowed (PlaceId place_id, TyTy::BaseType *ty,
638 : : location_t location)
639 : : {
640 : : // TODO: deduplicate with borrow_place
641 : 42 : auto loan = ctx.place_db.add_loan (
642 : 42 : {ty->as<const TyTy::ReferenceType> ()->mutability (), place_id,
643 : : location});
644 : 42 : return_expr (new BorrowExpr (place_id, loan,
645 : 42 : ctx.place_db.get_next_free_region ().value),
646 : : ty, location);
647 : 42 : return translated;
648 : : }
649 : :
650 : 17 : PlaceId take_or_create_return_place (TyTy::BaseType *type)
651 : : {
652 : 17 : PlaceId result = INVALID_PLACE;
653 : 17 : if (expr_return_place != INVALID_PLACE)
654 : : {
655 : 17 : result = expr_return_place;
656 : 17 : expr_return_place = INVALID_PLACE;
657 : : }
658 : : else
659 : : {
660 : 0 : result = ctx.place_db.add_temporary (type);
661 : 0 : push_storage_live (result);
662 : : }
663 : 17 : return result;
664 : : }
665 : : };
666 : :
667 : : /**
668 : : * Helper to convert a pointer to an optional. Maps nullptr to nullopt.
669 : : * Optionals are mainly used here to provide monadic operations (map) over
670 : : * possibly null pointers.
671 : : */
672 : : template <typename T>
673 : : tl::optional<T>
674 : : optional_from_ptr (T ptr)
675 : : {
676 : : if (ptr != nullptr)
677 : : return {ptr};
678 : : else
679 : : return tl::nullopt;
680 : : }
681 : :
682 : : } // namespace BIR
683 : : } // namespace Rust
684 : :
685 : : #endif // RUST_BIR_BUILDER_INTERNAL_H
|