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_FACT_COLLECTOR_H
20 : #define RUST_BIR_FACT_COLLECTOR_H
21 :
22 : #include "rust-bir-visitor.h"
23 : #include "rust-bir.h"
24 : #include "rust-bir-place.h"
25 : #include "polonius/rust-polonius.h"
26 :
27 : namespace Rust {
28 : namespace BIR {
29 :
30 : enum class PointPosition : uint8_t
31 : {
32 : START,
33 : MID
34 : };
35 :
36 : class FactCollector : public Visitor
37 : {
38 : // Output.
39 : Polonius::Facts facts;
40 :
41 : // Read-only context.
42 : const PlaceDB &place_db;
43 : const BasicBlocks &basic_blocks;
44 : const PlaceId first_local;
45 : const location_t location;
46 :
47 : Resolver::TypeCheckContext &tyctx;
48 :
49 : // Collector state.
50 : BasicBlockId current_bb = ENTRY_BASIC_BLOCK;
51 : uint32_t current_stmt = 0;
52 : PlaceId lhs = INVALID_PLACE;
53 :
54 : // PlaceDB is const in this phase, so this is used to generate fresh regions.
55 : FreeRegion next_fresh_region;
56 : RegionBinder region_binder{next_fresh_region};
57 :
58 : std::vector<Polonius::Point> cfg_points_all;
59 :
60 22 : FreeRegions bind_regions (std::vector<TyTy::Region> regions,
61 : FreeRegions parent_free_regions)
62 : {
63 22 : return region_binder.bind_regions (regions, parent_free_regions);
64 : }
65 :
66 11 : FreeRegions make_fresh_regions (size_t size)
67 : {
68 11 : FreeRegions free_regions;
69 23 : for (size_t i = 0; i < size; i++)
70 12 : free_regions.push_back (region_binder.get_next_free_region ());
71 :
72 11 : return free_regions;
73 : }
74 :
75 : public:
76 36 : static Polonius::Facts collect (Function &func)
77 : {
78 36 : FactCollector collector (func);
79 36 : collector.init_universal_regions (func.universal_regions,
80 36 : func.universal_region_bounds);
81 :
82 36 : collector.visit_statemensts ();
83 36 : collector.visit_places (func.arguments);
84 :
85 36 : return std::move (collector.facts);
86 36 : }
87 :
88 : protected: // Constructor and destructor.
89 36 : explicit FactCollector (Function &func)
90 72 : : place_db (func.place_db), basic_blocks (func.basic_blocks),
91 36 : first_local (func.arguments.empty ()
92 36 : ? FIRST_VARIABLE_PLACE
93 9 : : PlaceId{func.arguments.rbegin ()->value + 1}),
94 36 : location (func.location), tyctx (*Resolver::TypeCheckContext::get ()),
95 36 : next_fresh_region (place_db.peek_next_free_region ())
96 36 : {}
97 36 : ~FactCollector () = default;
98 :
99 : protected: // Main collection entry points (for different categories).
100 36 : void init_universal_regions (
101 : const FreeRegions &universal_regions,
102 : const decltype (Function::universal_region_bounds) &universal_region_bounds)
103 : {
104 36 : size_t next_loan = place_db.get_loans ().size ();
105 36 : facts.universal_region.emplace_back (0);
106 36 : facts.placeholder.emplace_back (0, next_loan++);
107 :
108 47 : for (auto ®ion : universal_regions)
109 : {
110 11 : facts.universal_region.emplace_back (region.value);
111 11 : facts.placeholder.emplace_back (region.value, next_loan++);
112 11 : facts.known_placeholder_subset.emplace_back (0, region.value);
113 : }
114 :
115 : // Copy already collected subset facts, that are universally valid.
116 38 : for (auto &bound : universal_region_bounds)
117 2 : facts.known_placeholder_subset.emplace_back (bound.first.value,
118 2 : bound.second.value);
119 36 : }
120 :
121 36 : void visit_places (const std::vector<PlaceId> &args)
122 : {
123 371 : for (PlaceId place_id = INVALID_PLACE; place_id.value < place_db.size ();
124 : ++place_id.value)
125 : {
126 335 : auto &place = place_db[place_id];
127 :
128 335 : switch (place.kind)
129 : {
130 195 : case Place::VARIABLE:
131 195 : case Place::TEMPORARY:
132 195 : facts.path_is_var.emplace_back (place_id.value, place_id.value);
133 303 : for (auto ®ion : place.regions)
134 108 : facts.use_of_var_derefs_origin.emplace_back (place_id.value,
135 108 : region.value);
136 :
137 : // TODO: drop_of_var_derefs_origin
138 : break;
139 5 : case Place::FIELD:
140 5 : sanizite_field (place_id);
141 5 : facts.child_path.emplace_back (place_id.value,
142 5 : place.path.parent.value);
143 5 : break;
144 0 : case Place::INDEX:
145 0 : push_subset_all (place.tyty, place.regions,
146 0 : place_db[place.path.parent].regions);
147 0 : facts.child_path.emplace_back (place_id.value,
148 0 : place.path.parent.value);
149 0 : break;
150 22 : case Place::DEREF:
151 22 : sanitize_deref (place_id);
152 22 : facts.child_path.emplace_back (place_id.value,
153 22 : place.path.parent.value);
154 22 : break;
155 : case Place::CONSTANT:
156 : case Place::INVALID:
157 : break;
158 : }
159 : }
160 :
161 36 : for (PlaceId arg = PlaceId{FIRST_VARIABLE_PLACE.value + 1};
162 54 : arg < first_local; ++arg.value)
163 18 : facts.path_assigned_at_base.emplace_back (
164 18 : arg.value, get_point (ENTRY_BASIC_BLOCK, 0, PointPosition::START));
165 :
166 308 : for (PlaceId place = first_local; place.value < place_db.size ();
167 : ++place.value)
168 : {
169 272 : if (place_db[place].is_var ())
170 168 : facts.path_moved_at_base.emplace_back (
171 : place.value,
172 168 : get_point (ENTRY_BASIC_BLOCK, 0, PointPosition::START));
173 : }
174 36 : }
175 :
176 22 : void sanitize_deref (PlaceId place_id)
177 : {
178 22 : auto &place = place_db[place_id];
179 22 : auto &base = place_db[place.path.parent];
180 :
181 22 : rust_debug ("\tSanitize deref of %s", base.tyty->as_string ().c_str ());
182 :
183 22 : FreeRegions regions;
184 23 : for (auto it = base.regions.begin () + 1; it != base.regions.end (); ++it)
185 : {
186 1 : regions.push_back (*it);
187 : }
188 22 : push_subset_all (place.tyty, regions, place.regions);
189 22 : }
190 5 : void sanizite_field (PlaceId place_id)
191 : {
192 5 : auto &place = place_db[place_id];
193 5 : auto &base = place_db[place.path.parent];
194 :
195 5 : rust_debug ("\tSanitize field .%d of %s", place.variable_or_field_index,
196 : base.tyty->as_string ().c_str ());
197 :
198 5 : if (base.tyty->is<TyTy::TupleType> ())
199 0 : return;
200 5 : auto r = Resolver::TypeCheckContext::get ()
201 5 : ->get_variance_analysis_ctx ()
202 5 : .query_field_regions (base.tyty->as<TyTy::ADTType> (), 0,
203 5 : place.variable_or_field_index,
204 5 : base.regions); // FIXME
205 5 : push_subset_all (place.tyty, r, place.regions);
206 5 : }
207 :
208 36 : void visit_statemensts ()
209 : {
210 36 : rust_debug ("visit_statemensts");
211 :
212 36 : for (current_bb = ENTRY_BASIC_BLOCK;
213 100 : current_bb.value < basic_blocks.size (); ++current_bb.value)
214 : {
215 64 : auto &bb = basic_blocks[current_bb];
216 696 : for (current_stmt = 0; current_stmt < bb.statements.size ();
217 632 : ++current_stmt)
218 : {
219 1264 : cfg_points_all.push_back (get_current_point_start ());
220 1264 : cfg_points_all.push_back (get_current_point_mid ());
221 :
222 632 : add_stmt_to_cfg (current_bb, current_stmt);
223 :
224 632 : visit (bb.statements[current_stmt]);
225 : }
226 : }
227 36 : current_bb = ENTRY_BASIC_BLOCK;
228 36 : current_stmt = 0;
229 36 : }
230 :
231 632 : void visit (const Statement &stmt) override
232 : {
233 632 : switch (stmt.get_kind ())
234 : {
235 185 : case Statement::Kind::ASSIGNMENT:
236 185 : {
237 : // TODO: for unwind, must had hadning for non-panic-only assignements
238 185 : issue_write_deep (stmt.get_place ());
239 185 : visit_assignment_expr (stmt.get_place (), stmt.get_expr ());
240 185 : break;
241 : }
242 7 : case Statement::Kind::SWITCH:
243 7 : {
244 7 : issue_read_move (stmt.get_place ());
245 7 : issue_jumps ();
246 : }
247 7 : break;
248 8 : case Statement::Kind::GOTO:
249 8 : {
250 8 : issue_jumps ();
251 : }
252 8 : break;
253 38 : case Statement::Kind::RETURN:
254 38 : {
255 38 : issue_place_access (RETURN_VALUE_PLACE);
256 38 : issue_locals_dealloc ();
257 38 : break;
258 : }
259 145 : case Statement::Kind::STORAGE_DEAD:
260 145 : {
261 290 : facts.path_moved_at_base.emplace_back (stmt.get_place ().value,
262 290 : get_current_point_mid ());
263 290 : facts.var_defined_at.emplace_back (stmt.get_place ().value,
264 290 : get_current_point_mid ());
265 145 : break;
266 : }
267 159 : case Statement::Kind::STORAGE_LIVE:
268 159 : {
269 159 : issue_write_deep (stmt.get_place (), true);
270 159 : break;
271 : }
272 0 : case Statement::Kind::USER_TYPE_ASCRIPTION:
273 0 : {
274 0 : issue_user_type_constraints (stmt.get_place (), stmt.get_type ());
275 0 : break;
276 : }
277 90 : case Statement::Kind::FAKE_READ:
278 90 : {
279 90 : issue_place_access (stmt.get_place ());
280 90 : break;
281 : }
282 : }
283 632 : }
284 :
285 185 : void visit_assignment_expr (PlaceId lhs, AbstractExpr &expr)
286 : {
287 185 : this->lhs = lhs;
288 185 : expr.accept_vis (*this);
289 185 : this->lhs = INVALID_PLACE;
290 : }
291 :
292 6 : void visit (const InitializerExpr &expr) override
293 : {
294 6 : sanitize_constrains_at_init (lhs);
295 :
296 12 : for (auto init_value : expr.get_values ())
297 6 : issue_read_move (init_value);
298 6 : }
299 :
300 0 : void visit (const Operator<1> &expr) override
301 : {
302 0 : sanitize_constrains_at_init (lhs);
303 0 : issue_read_move (expr.get_operand<0> ());
304 0 : }
305 :
306 0 : void visit (const Operator<2> &expr) override
307 : {
308 0 : sanitize_constrains_at_init (lhs);
309 0 : issue_read_move (expr.get_operand<0> ());
310 0 : issue_read_move (expr.get_operand<1> ());
311 0 : }
312 :
313 55 : void visit (const BorrowExpr &expr) override
314 : {
315 55 : rust_debug ("\t_%u = BorrowExpr(_%u)", lhs.value - 1,
316 : expr.get_place ().value - 1);
317 :
318 55 : auto loan = place_db.get_loan (expr.get_loan_id ());
319 :
320 55 : auto &base_place = place_db[expr.get_place ()];
321 55 : auto &ref_place = place_db[lhs];
322 :
323 55 : issue_place_access (expr.get_place ());
324 :
325 : // See compiler/rustc_borrowck/src/type_check/mod.rs:add_reborrow_constraint
326 55 : if (base_place.kind == Place::DEREF)
327 : {
328 : // Reborrow
329 :
330 22 : auto &main_loan_place = place_db[base_place.path.parent];
331 22 : if (loan.mutability == Mutability::Mut)
332 : {
333 11 : if (!main_loan_place.tyty->as<TyTy::ReferenceType> ()
334 11 : ->is_mutable ())
335 2 : rust_error_at (location,
336 : "Cannot reborrow immutable borrow as mutable");
337 11 : issue_loan (expr.get_origin (), expr.get_loan_id ());
338 : }
339 :
340 22 : push_subset (main_loan_place.regions[0], {expr.get_origin ()});
341 : }
342 : else
343 : {
344 33 : issue_loan (expr.get_origin (), expr.get_loan_id ());
345 : }
346 :
347 55 : auto loan_regions = base_place.regions.prepend ({expr.get_origin ()});
348 55 : push_subset (ref_place.tyty, loan_regions, ref_place.regions);
349 55 : }
350 :
351 113 : void visit (const Assignment &expr) override
352 : {
353 113 : rust_debug ("\t_%u = Assignment(_%u) at %u:%u", lhs.value - 1,
354 : expr.get_rhs ().value - 1, current_bb.value, current_stmt);
355 :
356 113 : issue_read_move (expr.get_rhs ());
357 113 : push_place_subset (lhs, expr.get_rhs ());
358 113 : }
359 :
360 11 : void visit (const CallExpr &expr) override
361 : {
362 11 : rust_debug ("\t_%u = CallExpr(_%u)", lhs.value - 1,
363 : expr.get_callable ().value - 1);
364 :
365 11 : auto &return_place = place_db[lhs];
366 11 : auto &callable_place = place_db[expr.get_callable ()];
367 11 : auto callable_ty = callable_place.tyty->as<TyTy::CallableTypeInterface> ();
368 :
369 11 : issue_read_move (expr.get_callable ());
370 :
371 : // Each call needs unique regions.
372 11 : auto call_regions = make_fresh_regions (callable_place.regions.size ());
373 :
374 22 : for (size_t i = 0; i < expr.get_arguments ().size (); ++i)
375 : {
376 11 : auto arg = expr.get_arguments ().at (i);
377 11 : auto arg_regions
378 22 : = bind_regions (Resolver::TypeCheckContext::get ()
379 11 : ->get_variance_analysis_ctx ()
380 22 : .query_type_regions (
381 11 : callable_ty->get_param_type_at (i)),
382 11 : call_regions);
383 11 : issue_read_move (arg);
384 11 : push_subset (place_db[arg].tyty, place_db[arg].regions, arg_regions);
385 11 : }
386 :
387 : // sanitize return regions
388 11 : sanitize_constrains_at_init (lhs);
389 :
390 11 : auto return_regions
391 22 : = bind_regions (Resolver::TypeCheckContext::get ()
392 11 : ->get_variance_analysis_ctx ()
393 22 : .query_type_regions (
394 11 : callable_ty->as<TyTy::FnType> ()->get_return_type ()),
395 11 : call_regions);
396 11 : push_subset (return_place.tyty, return_regions, return_place.regions);
397 :
398 11 : issue_jumps ();
399 11 : }
400 :
401 : protected: // Statement visitor helpers
402 26 : WARN_UNUSED_RESULT const BasicBlock &get_current_bb () const
403 : {
404 26 : return basic_blocks[current_bb];
405 : }
406 :
407 : WARN_UNUSED_RESULT static Polonius::Point
408 4196 : get_point (BasicBlockId bb, uint32_t stmt, PointPosition pos)
409 : {
410 4196 : Polonius::Point point = 0;
411 4196 : point |= (bb.value << 16);
412 4196 : point |= (stmt << 1);
413 4196 : point |= (static_cast<uint8_t> (pos) & 1);
414 4196 : return point;
415 : }
416 :
417 805 : WARN_UNUSED_RESULT Polonius::Point get_current_point_start () const
418 : {
419 632 : return get_point (current_bb, current_stmt, PointPosition::START);
420 : }
421 :
422 2191 : WARN_UNUSED_RESULT Polonius::Point get_current_point_mid () const
423 : {
424 777 : return get_point (current_bb, current_stmt, PointPosition::MID);
425 : }
426 :
427 632 : void add_stmt_to_cfg (BasicBlockId bb, uint32_t stmt)
428 : {
429 632 : if (stmt != 0)
430 : {
431 1136 : facts.cfg_edge.emplace_back (get_point (bb, stmt - 1,
432 : PointPosition::MID),
433 568 : get_point (bb, stmt,
434 : PointPosition::START));
435 : }
436 :
437 1264 : facts.cfg_edge.emplace_back (get_point (bb, stmt, PointPosition::START),
438 632 : get_point (bb, stmt, PointPosition::MID));
439 632 : }
440 :
441 : protected: // Generic BIR operations.
442 26 : void issue_jumps ()
443 : {
444 59 : for (auto succ : get_current_bb ().successors)
445 66 : facts.cfg_edge.emplace_back (get_current_point_mid (),
446 33 : get_point (succ, 0, PointPosition::START));
447 26 : }
448 :
449 : /* Shallow r/w access */
450 331 : void issue_place_access (PlaceId place_id)
451 : {
452 331 : auto &place = place_db[place_id];
453 :
454 331 : if (place.is_constant ())
455 : return;
456 :
457 233 : if (place_id != RETURN_VALUE_PLACE)
458 194 : facts.path_accessed_at_base.emplace_back (place_id.value,
459 194 : get_current_point_mid ());
460 :
461 233 : if (place.is_var ())
462 209 : facts.var_used_at.emplace_back (place_id.value, get_current_point_mid ());
463 24 : else if (place.is_path ())
464 : {
465 24 : facts.var_used_at.emplace_back (place_db.get_var (place_id).value,
466 48 : get_current_point_mid ());
467 : }
468 : }
469 :
470 : /** Deep read access, which consumes the place. */
471 148 : void issue_read_move (PlaceId place_id)
472 : {
473 148 : auto &place = place_db[place_id];
474 :
475 148 : issue_place_access (place_id);
476 148 : if (place.should_be_moved ())
477 : {
478 46 : issue_move (place_id);
479 : }
480 : else
481 : {
482 102 : check_read_for_conflicts (place_id);
483 : }
484 148 : }
485 :
486 344 : void issue_write_deep (PlaceId place_id, bool is_init = false)
487 : {
488 344 : auto &place = place_db[place_id];
489 459 : rust_assert (place.is_lvalue () || place.is_rvalue ());
490 :
491 344 : if (place.is_var ())
492 343 : facts.var_defined_at.emplace_back (place_id.value,
493 343 : get_current_point_mid ());
494 :
495 344 : if (!is_init)
496 : {
497 185 : facts.path_assigned_at_base.emplace_back (place_id.value,
498 185 : get_current_point_mid ());
499 185 : check_write_for_conflict (place_id);
500 185 : kill_borrows_for_place (place_id);
501 : }
502 344 : }
503 :
504 46 : void issue_move (PlaceId place_id, bool initial = false)
505 : {
506 46 : if (!place_db[place_id].should_be_moved ())
507 : return;
508 :
509 92 : facts.path_moved_at_base.emplace_back (place_id.value,
510 : initial
511 92 : ? get_point (ENTRY_BASIC_BLOCK, 0,
512 : PointPosition::START)
513 46 : : get_current_point_mid ());
514 :
515 46 : check_move_behind_reference (place_id);
516 :
517 46 : if (!initial)
518 : {
519 46 : check_write_for_conflict (place_id);
520 46 : kill_borrows_for_place (place_id);
521 : }
522 : }
523 :
524 44 : void issue_loan (Polonius::Origin origin, LoanId loan_id)
525 : {
526 44 : facts.loan_issued_at.emplace_back (origin, loan_id.value,
527 44 : get_current_point_mid ());
528 :
529 44 : check_for_borrow_conficts (place_db.get_loan (loan_id).place, loan_id,
530 44 : place_db.get_loan (loan_id).mutability);
531 44 : }
532 :
533 38 : void issue_locals_dealloc ()
534 : {
535 99 : for (LoanId loan_id = {0}; loan_id.value < place_db.get_loans ().size ();
536 : ++loan_id.value)
537 : {
538 61 : auto &loan = place_db.get_loan (loan_id);
539 61 : auto loaned_var_id = place_db.get_var (loan.place);
540 61 : if (place_db[loaned_var_id].tyty->is<TyTy::ReferenceType> ())
541 26 : continue;
542 35 : if (loaned_var_id >= first_local)
543 35 : facts.loan_invalidated_at.emplace_back (get_current_point_start (),
544 : loan_id.value);
545 : }
546 38 : }
547 :
548 0 : void issue_user_type_constraints (PlaceId place_id, TyTy::BaseType *type)
549 : {
550 0 : auto user_regions = Resolver::TypeCheckContext::get ()
551 0 : ->get_variance_analysis_ctx ()
552 0 : .query_type_regions (type);
553 0 : push_subset_user (place_db[place_id].tyty, place_db[place_id].regions,
554 : user_regions);
555 0 : }
556 :
557 102 : void check_read_for_conflicts (PlaceId place_id)
558 : {
559 102 : place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
560 105 : for (auto loan : place_db[id].borrowed_by)
561 : {
562 3 : if (place_db.get_loan (loan).mutability == Mutability::Mut)
563 : {
564 3 : facts.loan_invalidated_at.emplace_back (
565 3 : get_current_point_start (), loan.value);
566 : }
567 : }
568 102 : });
569 102 : place_db.for_each_path_from_root (place_id, [&] (PlaceId id) {
570 0 : for (auto loan : place_db[id].borrowed_by)
571 : {
572 0 : if (place_db.get_loan (loan).mutability == Mutability::Mut)
573 : {
574 0 : facts.loan_invalidated_at.emplace_back (
575 0 : get_current_point_start (), loan.value);
576 : }
577 : }
578 0 : });
579 102 : }
580 :
581 231 : void check_write_for_conflict (PlaceId place_id)
582 : {
583 231 : place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
584 294 : for (auto loan : place_db[id].borrowed_by)
585 59 : facts.loan_invalidated_at.emplace_back (get_current_point_start (),
586 : loan.value);
587 235 : });
588 231 : place_db.for_each_path_from_root (place_id, [&] (PlaceId id) {
589 51 : for (auto loan : place_db[id].borrowed_by)
590 24 : facts.loan_invalidated_at.emplace_back (get_current_point_start (),
591 : loan.value);
592 27 : });
593 231 : }
594 :
595 44 : void check_for_borrow_conficts (PlaceId place_id, LoanId loan,
596 : Mutability mutability)
597 : {
598 44 : place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
599 131 : for (auto other_loan : place_db[id].borrowed_by)
600 : {
601 98 : if (mutability == Mutability::Imm
602 75 : && place_db.get_loan (other_loan).mutability == Mutability::Imm)
603 23 : continue;
604 : else
605 52 : facts.loan_invalidated_at.emplace_back (get_current_point_start (),
606 : other_loan.value);
607 : }
608 56 : });
609 :
610 44 : place_db.for_each_path_from_root (place_id, [&] (PlaceId id) {
611 0 : for (auto other_loan : place_db[id].borrowed_by)
612 : {
613 0 : if (mutability == Mutability::Imm
614 0 : && place_db.get_loan (other_loan).mutability == Mutability::Imm)
615 0 : continue;
616 : else
617 0 : facts.loan_invalidated_at.emplace_back (get_current_point_start (),
618 : other_loan.value);
619 : }
620 0 : });
621 44 : }
622 :
623 46 : void check_move_behind_reference (PlaceId place_id)
624 : {
625 46 : place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
626 49 : if (id == place_id)
627 : return;
628 3 : if (place_db[id].kind == Place::DEREF)
629 1 : rust_error_at (location, "Cannot move from behind a reference.");
630 : });
631 46 : }
632 :
633 231 : void kill_borrows_for_place (PlaceId place_id)
634 : {
635 231 : auto &place = place_db[place_id];
636 288 : for (auto loan : place.borrowed_by)
637 : {
638 : // TODO: this is more complicated, see
639 : // compiler/rustc_borrowck/src/constraint_generation.rs:176
640 57 : facts.loan_killed_at.emplace_back (loan.value,
641 57 : get_current_point_mid ());
642 : }
643 231 : }
644 :
645 : protected: // Subset helpers.
646 134 : void push_subset (FreeRegion lhs, FreeRegion rhs)
647 : {
648 134 : rust_debug ("\t\tpush_subset: '?%lu: '?%lu", (unsigned long) lhs.value,
649 : (unsigned long) rhs.value);
650 :
651 134 : facts.subset_base.emplace_back (lhs.value, rhs.value,
652 134 : get_current_point_mid ());
653 134 : }
654 :
655 4 : void push_subset_all (FreeRegion lhs, FreeRegion rhs)
656 : {
657 4 : rust_debug ("\t\tpush_subset_all: '?%lu: '?%lu", (unsigned long) lhs.value,
658 : (unsigned long) rhs.value);
659 :
660 194 : for (auto point : cfg_points_all)
661 190 : facts.subset_base.emplace_back (lhs.value, rhs.value, point);
662 4 : }
663 :
664 111 : void push_subset (Variance variance, FreeRegion lhs, FreeRegion rhs)
665 : {
666 111 : if (variance.is_covariant ())
667 111 : push_subset (lhs, rhs);
668 0 : else if (variance.is_contravariant ())
669 0 : push_subset (rhs, lhs);
670 0 : else if (variance.is_invariant ())
671 : {
672 0 : push_subset (lhs, rhs);
673 0 : push_subset (rhs, lhs);
674 : }
675 111 : }
676 :
677 4 : void push_subset_all (Variance variance, FreeRegion lhs, FreeRegion rhs)
678 : {
679 4 : if (variance.is_covariant ())
680 4 : push_subset_all (lhs, rhs);
681 0 : else if (variance.is_contravariant ())
682 0 : push_subset_all (rhs, lhs);
683 0 : else if (variance.is_invariant ())
684 : {
685 0 : push_subset_all (lhs, rhs);
686 0 : push_subset_all (rhs, lhs);
687 : }
688 4 : }
689 :
690 113 : void push_place_subset (PlaceId lhs, PlaceId rhs)
691 : {
692 113 : auto &lhs_place = place_db[lhs];
693 113 : auto &rhs_place = place_db[rhs];
694 :
695 113 : push_subset (lhs_place.tyty, rhs_place.regions, lhs_place.regions);
696 113 : }
697 :
698 190 : void push_subset (TyTy::BaseType *type, FreeRegions lhs, FreeRegions rhs)
699 : {
700 190 : auto variances = Resolver::TypeCheckContext::get ()
701 190 : ->get_variance_analysis_ctx ()
702 190 : .query_type_variances (type);
703 190 : rust_assert (lhs.size () == rhs.size ());
704 190 : rust_assert (lhs.size () == variances.size ());
705 301 : for (size_t i = 0; i < lhs.size (); ++i)
706 111 : push_subset (variances[i], lhs[i], rhs[i]);
707 190 : }
708 :
709 27 : void push_subset_all (TyTy::BaseType *type, FreeRegions lhs, FreeRegions rhs)
710 : {
711 27 : auto variances = Resolver::TypeCheckContext::get ()
712 27 : ->get_variance_analysis_ctx ()
713 27 : .query_type_variances (type);
714 27 : rust_assert (lhs.size () == rhs.size ());
715 27 : rust_assert (lhs.size () == variances.size ());
716 31 : for (size_t i = 0; i < lhs.size (); ++i)
717 4 : push_subset_all (variances[i], lhs[i], rhs[i]);
718 27 : }
719 :
720 0 : void push_subset_user (TyTy::BaseType *type, FreeRegions free_regions,
721 : std::vector<TyTy::Region> user_regions)
722 : {
723 0 : auto variances = Resolver::TypeCheckContext::get ()
724 0 : ->get_variance_analysis_ctx ()
725 0 : .query_type_variances (type);
726 0 : rust_assert (free_regions.size () == user_regions.size ());
727 0 : rust_assert (free_regions.size () == variances.size ());
728 :
729 0 : for (size_t i = 0; i < free_regions.size (); ++i)
730 : {
731 0 : if (user_regions[i].is_named ())
732 0 : push_subset (variances[i], free_regions[i],
733 0 : {Polonius::Origin (user_regions[i].get_index ())});
734 0 : else if (user_regions[i].is_anonymous ())
735 : {
736 : // IGNORE
737 : }
738 : else
739 0 : rust_internal_error_at (UNKNOWN_LOCATION, "Unexpected region type");
740 : }
741 0 : }
742 :
743 : /**
744 : * Apply type and lifetime bounds
745 : *
746 : * For a place we have a list of fresh regions. We need to apply constraints
747 : * from type definition to it. First `n` regions belong to the lifetime
748 : * parameters of the type. The rest are flatten lifetime parameters of the
749 : * type arguments. We walk the type arguments with a offset
750 : */
751 17 : void sanitize_constrains_at_init (PlaceId place_id)
752 : {
753 17 : auto &place = place_db[place_id];
754 :
755 17 : rust_debug ("\tSanitize constraints of %s",
756 : place.tyty->as_string ().c_str ());
757 :
758 17 : if (auto generic = place.tyty->try_as<TyTy::SubstitutionRef> ())
759 : {
760 16 : auto ®ions = place.regions;
761 16 : auto region_end = sanitize_constraints (*generic, 0, regions);
762 16 : rust_assert (region_end == regions.size ());
763 : }
764 1 : else if (place.tyty->is<TyTy::ReferenceType> ())
765 : {
766 3 : for (auto ®ion : place.regions)
767 : {
768 2 : if (region != place.regions[0])
769 1 : push_subset (region, place.regions[0]);
770 : }
771 : }
772 17 : }
773 :
774 0 : size_t sanitize_constraints (const TyTy::BaseType *type, size_t region_start,
775 : const FreeRegions ®ions)
776 : {
777 0 : switch (type->get_kind ())
778 : {
779 0 : case TyTy::ADT:
780 0 : return sanitize_constraints (type->as<const TyTy::ADTType> (),
781 0 : region_start, regions);
782 : case TyTy::STR:
783 : return region_start;
784 0 : case TyTy::REF:
785 0 : return 1
786 : + sanitize_constraints (
787 0 : type->as<const TyTy::ReferenceType> ()->get_base (),
788 0 : region_start, regions);
789 0 : case TyTy::POINTER:
790 0 : return sanitize_constraints (
791 0 : type->as<const TyTy::PointerType> ()->get_base (), region_start,
792 0 : regions);
793 0 : case TyTy::ARRAY:
794 0 : return sanitize_constraints (
795 0 : type->as<const TyTy::ArrayType> ()->get_element_type (), region_start,
796 0 : regions);
797 0 : case TyTy::SLICE:
798 0 : return sanitize_constraints (
799 0 : type->as<const TyTy::SliceType> ()->get_element_type (), region_start,
800 0 : regions);
801 0 : case TyTy::FNDEF:
802 0 : case TyTy::TUPLE:
803 0 : {
804 0 : for (auto &field : type->as<const TyTy::TupleType> ()->get_fields ())
805 0 : sanitize_constraints (field.get_tyty (), region_start, regions);
806 : }
807 : break;
808 0 : case TyTy::FNPTR:
809 0 : case TyTy::PROJECTION:
810 0 : return sanitize_constraints (*type->as<const TyTy::SubstitutionRef> (),
811 0 : region_start, regions);
812 : case TyTy::BOOL:
813 : case TyTy::CHAR:
814 : case TyTy::INT:
815 : case TyTy::UINT:
816 : case TyTy::FLOAT:
817 : case TyTy::USIZE:
818 : case TyTy::ISIZE:
819 : case TyTy::NEVER:
820 : case TyTy::DYNAMIC:
821 : case TyTy::CLOSURE:
822 : case TyTy::ERROR:
823 : return region_start;
824 0 : case TyTy::PLACEHOLDER:
825 0 : case TyTy::INFER:
826 0 : case TyTy::PARAM:
827 0 : case TyTy::CONST:
828 0 : case TyTy::OPAQUE:
829 0 : rust_unreachable ();
830 : }
831 0 : rust_unreachable ();
832 : }
833 :
834 16 : size_t sanitize_constraints (const TyTy::SubstitutionRef &type,
835 : size_t region_start, const FreeRegions ®ions)
836 : {
837 16 : for (auto constr : type.get_region_constraints ().region_region)
838 : {
839 0 : rust_assert (constr.first.is_early_bound ());
840 0 : rust_assert (constr.second.is_early_bound ());
841 0 : auto lhs = constr.first.get_index () + region_start;
842 0 : auto rhs = constr.second.get_index () + region_start;
843 0 : push_subset (regions[lhs], regions[rhs]);
844 : }
845 :
846 16 : size_t region_end = region_start + type.get_num_lifetime_params ();
847 :
848 : /*
849 : * For type `Foo<'a, T1, T2>`, where `T1 = &'b Vec<&'c i32>` and `T2 = &'d
850 : * i32 the regions are `['a, 'b, 'c, 'd]`. The ranges
851 : */
852 16 : std::vector<size_t> type_param_region_ranges;
853 16 : type_param_region_ranges.push_back (region_end);
854 :
855 16 : for (auto type_param : type.get_substs ())
856 : {
857 0 : TyTy::SubstitutionArg arg = TyTy::SubstitutionArg::error ();
858 0 : bool ok = type.get_used_arguments ().get_argument_for_symbol (
859 0 : type_param.get_param_ty (), &arg);
860 0 : rust_assert (ok);
861 0 : region_end
862 0 : = sanitize_constraints (arg.get_tyty (), region_end, regions);
863 0 : type_param_region_ranges.push_back (region_end);
864 : }
865 :
866 : /*
867 : * For constrain of form: `T: 'a` push outlives with all in range
868 : * `indexof(T)..(indexof(T) + 1)`
869 : */
870 16 : for (auto constr : type.get_region_constraints ().type_region)
871 : {
872 0 : auto type_param_index_opt
873 0 : = type.get_used_arguments ().find_symbol (*constr.first);
874 0 : rust_assert (type_param_index_opt.has_value ());
875 0 : size_t type_param_index = type_param_index_opt.value ();
876 :
877 0 : for (size_t i = type_param_region_ranges[type_param_index];
878 0 : i < type_param_region_ranges[type_param_index + 1]; ++i)
879 : {
880 0 : push_subset (regions[i],
881 0 : regions[constr.second.get_index () + region_start]);
882 : }
883 : }
884 :
885 16 : return region_end;
886 16 : }
887 : };
888 :
889 : } // namespace BIR
890 : } // namespace Rust
891 :
892 : #endif // RUST_BIR_FACT_COLLECTOR_H
|