LCOV - code coverage report
Current view: top level - gcc/rust/checks/errors/borrowck - rust-bir-fact-collector.h (source / functions) Coverage Total Hit
Test: gcc.info Lines: 76.5 % 494 378
Test Date: 2026-10-03 16:17:38 Functions: 89.8 % 49 44
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.