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