From: Jakub Dupak <d...@jakubdupak.com>

This is the main Polonius based logic which creates the information
Polonius needs from BIR. It is largly guessed and rever engineered, so
some aspects are probably wrong.

gcc/rust/ChangeLog:

        * checks/errors/borrowck/rust-bir-fact-collector.h: New file.
        * checks/errors/borrowck/rust-borrow-checker.cc (BorrowChecker::go):
        Enable fact collection.

Signed-off-by: Jakub Dupak <d...@jakubdupak.com>
---
 .../errors/borrowck/rust-bir-fact-collector.h | 895 ++++++++++++++++++
 .../errors/borrowck/rust-borrow-checker.cc    |   3 +
 2 files changed, 898 insertions(+)
 create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h

diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h
new file mode 100644
index 00000000000..17c198e0fa7
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h
@@ -0,0 +1,895 @@
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_BIR_FACT_COLLECTOR_H
+#define RUST_BIR_FACT_COLLECTOR_H
+
+#include "rust-bir-visitor.h"
+#include "rust-bir.h"
+#include "rust-bir-place.h"
+#include "polonius/rust-polonius.h"
+
+namespace Rust {
+namespace BIR {
+
+enum class PointPosition : uint8_t
+{
+  START,
+  MID
+};
+
+class FactCollector : public Visitor
+{
+  // Output.
+  Polonius::Facts facts;
+
+  // Read-only context.
+  const PlaceDB &place_db;
+  const std::vector<BasicBlock> &basic_blocks;
+  const PlaceId first_local;
+  const location_t location;
+
+  Resolver::TypeCheckContext &tyctx;
+
+  // Collector state.
+  BasicBlockId current_bb = 0;
+  uint32_t current_stmt = 0;
+  PlaceId lhs = INVALID_PLACE;
+
+  // PlaceDB is const in this phase, so this is used to generate fresh regions.
+  FreeRegion next_fresh_region;
+  RegionBinder region_binder{next_fresh_region};
+
+  std::vector<Polonius::Point> cfg_points_all;
+
+  FreeRegions bind_regions (std::vector<TyTy::Region> regions,
+                           FreeRegions parent_free_regions)
+  {
+    return region_binder.bind_regions (regions, parent_free_regions);
+  }
+
+  FreeRegions make_fresh_regions (size_t size)
+  {
+    std::vector<FreeRegion> free_regions;
+    for (size_t i = 0; i < size; i++)
+      {
+       free_regions.push_back (region_binder.get_next_free_region ());
+      }
+    return FreeRegions (std::move (free_regions));
+  }
+
+public:
+  static Polonius::Facts collect (Function &func)
+  {
+    FactCollector collector (func);
+    collector.init_universal_regions (func.universal_regions,
+                                     func.universal_region_bounds);
+
+    collector.visit_statemensts ();
+    collector.visit_places (func.arguments);
+
+    return std::move (collector.facts);
+  }
+
+protected: // Constructor and destructor.
+  explicit FactCollector (Function &func)
+    : place_db (func.place_db), basic_blocks (func.basic_blocks),
+      first_local (func.arguments.empty () ? FIRST_VARIABLE_PLACE
+                                          : *func.arguments.rbegin () + 1),
+      location (func.location), tyctx (*Resolver::TypeCheckContext::get ()),
+      next_fresh_region (place_db.peek_next_free_region ())
+  {}
+  ~FactCollector () = default;
+
+protected: // Main collection entry points (for different categories).
+  void init_universal_regions (
+    const FreeRegions &universal_regions,
+    const decltype (Function::universal_region_bounds) 
&universal_region_bounds)
+  {
+    size_t next_loan = place_db.get_loans ().size ();
+    facts.universal_region.emplace_back (0);
+    facts.placeholder.emplace_back (0, next_loan++);
+
+    for (auto &region : universal_regions)
+      {
+       facts.universal_region.emplace_back (region);
+       facts.placeholder.emplace_back (region, next_loan++);
+       facts.known_placeholder_subset.emplace_back (0, region);
+      }
+
+    // Copy already collected subset facts, that are universally valid.
+    for (auto &bound : universal_region_bounds)
+      facts.known_placeholder_subset.emplace_back (bound.first, bound.second);
+  }
+
+  void visit_places (const std::vector<PlaceId> &args)
+  {
+    for (PlaceId place_id = 0; place_id < place_db.size (); ++place_id)
+      {
+       auto &place = place_db[place_id];
+
+       switch (place.kind)
+         {
+         case Place::VARIABLE:
+         case Place::TEMPORARY:
+           facts.path_is_var.emplace_back (place_id, place_id);
+           for (auto &region : place.regions)
+             {
+               facts.use_of_var_derefs_origin.emplace_back (place_id, region);
+             }
+           // TODO: drop_of_var_derefs_origin
+           break;
+         case Place::FIELD:
+           sanizite_field (place_id);
+           facts.child_path.emplace_back (place_id, place.path.parent);
+           break;
+         case Place::INDEX:
+           push_subset_all (place.tyty, place.regions,
+                            place_db[place.path.parent].regions);
+           facts.child_path.emplace_back (place_id, place.path.parent);
+           break;
+         case Place::DEREF:
+           sanitize_deref (place_id);
+           facts.child_path.emplace_back (place_id, place.path.parent);
+           break;
+         case Place::CONSTANT:
+         case Place::INVALID:
+           break;
+         }
+      }
+
+    for (PlaceId arg = FIRST_VARIABLE_PLACE + 1; arg < first_local; ++arg)
+      {
+       facts.path_assigned_at_base.emplace_back (
+         arg, get_point (0, 0, PointPosition::START));
+      }
+    for (PlaceId place = first_local; place < place_db.size (); ++place)
+      {
+       if (place_db[place].is_var ())
+         facts.path_moved_at_base.emplace_back (
+           place, get_point (0, 0, PointPosition::START));
+      }
+  }
+
+  void sanitize_deref (PlaceId place_id)
+  {
+    auto &place = place_db[place_id];
+    auto &base = place_db[place.path.parent];
+
+    rust_debug ("\tSanitize deref of %s", base.tyty->as_string ().c_str ());
+
+    std::vector<Polonius::Origin> regions;
+    regions.insert (regions.end (), base.regions.begin () + 1,
+                   base.regions.end ());
+    FreeRegions r (std::move (regions));
+    push_subset_all (place.tyty, r, place.regions);
+  }
+  void sanizite_field (PlaceId place_id)
+  {
+    auto &place = place_db[place_id];
+    auto &base = place_db[place.path.parent];
+
+    rust_debug ("\tSanitize field .%d of %s", place.variable_or_field_index,
+               base.tyty->as_string ().c_str ());
+
+    if (base.tyty->is<TyTy::TupleType> ())
+      return;
+    auto r = Resolver::TypeCheckContext::get ()
+              ->get_variance_analysis_ctx ()
+              .query_field_regions (base.tyty->as<TyTy::ADTType> (), 0,
+                                    place.variable_or_field_index,
+                                    base.regions); // FIXME
+    FreeRegions f (std::move (r));
+    push_subset_all (place.tyty, f, place.regions);
+  }
+
+  void visit_statemensts ()
+  {
+    rust_debug ("visit_statemensts");
+
+    for (current_bb = 0; current_bb < basic_blocks.size (); ++current_bb)
+      {
+       auto &bb = basic_blocks[current_bb];
+       for (current_stmt = 0; current_stmt < bb.statements.size ();
+            ++current_stmt)
+         {
+           cfg_points_all.push_back (get_current_point_start ());
+           cfg_points_all.push_back (get_current_point_mid ());
+
+           add_stmt_to_cfg (current_bb, current_stmt);
+
+           visit (bb.statements[current_stmt]);
+         }
+      }
+    current_bb = 0;
+    current_stmt = 0;
+  }
+
+  void visit (const Statement &stmt) override
+  {
+    switch (stmt.get_kind ())
+      {
+       case Statement::Kind::ASSIGNMENT: {
+         // TODO: for unwind, must had hadning for non-panic-only assignements
+         issue_write_deep (stmt.get_place ());
+         visit_assignment_expr (stmt.get_place (), stmt.get_expr ());
+         break;
+       }
+       case Statement::Kind::SWITCH: {
+         issue_read_move (stmt.get_place ());
+         issue_jumps ();
+       }
+       break;
+       case Statement::Kind::GOTO: {
+         issue_jumps ();
+       }
+       break;
+       case Statement::Kind::RETURN: {
+         issue_place_access (RETURN_VALUE_PLACE);
+         issue_locals_dealloc ();
+         break;
+       }
+       case Statement::Kind::STORAGE_DEAD: {
+         facts.path_moved_at_base.emplace_back (stmt.get_place (),
+                                                get_current_point_mid ());
+         facts.var_defined_at.emplace_back (stmt.get_place (),
+                                            get_current_point_mid ());
+         break;
+       }
+       case Statement::Kind::STORAGE_LIVE: {
+         issue_write_deep (stmt.get_place (), true);
+         break;
+       }
+       case Statement::Kind::USER_TYPE_ASCRIPTION: {
+         issue_user_type_constraints (stmt.get_place (), stmt.get_type ());
+         break;
+       }
+       case Statement::Kind::FAKE_READ: {
+         issue_place_access (stmt.get_place ());
+         break;
+       }
+      }
+  }
+
+  void visit_assignment_expr (PlaceId lhs, AbstractExpr &expr)
+  {
+    this->lhs = lhs;
+    expr.accept_vis (*this);
+    this->lhs = INVALID_PLACE;
+  }
+
+  void visit (const InitializerExpr &expr) override
+  {
+    sanitize_constrains_at_init (lhs);
+
+    for (auto init_value : expr.get_values ())
+      issue_read_move (init_value);
+  }
+
+  void visit (const Operator<1> &expr) override
+  {
+    sanitize_constrains_at_init (lhs);
+    issue_read_move (expr.get_operand<0> ());
+  }
+
+  void visit (const Operator<2> &expr) override
+  {
+    sanitize_constrains_at_init (lhs);
+    issue_read_move (expr.get_operand<0> ());
+    issue_read_move (expr.get_operand<1> ());
+  }
+
+  void visit (const BorrowExpr &expr) override
+  {
+    rust_debug ("\t_%u = BorrowExpr(_%u)", lhs - 1, expr.get_place () - 1);
+
+    auto loan = place_db.get_loans ()[expr.get_loan ()];
+
+    auto &base_place = place_db[expr.get_place ()];
+    auto &ref_place = place_db[lhs];
+
+    issue_place_access (expr.get_place ());
+
+    // See 
compiler/rustc_borrowck/src/type_check/mod.rs:add_reborrow_constraint
+    if (base_place.kind == Place::DEREF)
+      {
+       // Reborrow
+
+       auto &main_loan_place = place_db[base_place.path.parent];
+       if (loan.mutability == Mutability::Mut)
+         {
+           if (!main_loan_place.tyty->as<TyTy::ReferenceType> ()
+                  ->is_mutable ())
+             rust_error_at (location,
+                            "Cannot reborrow immutable borrow as mutable");
+           issue_loan (expr.get_origin (), expr.get_loan ());
+         }
+
+       push_subset (main_loan_place.regions[0], expr.get_origin ());
+      }
+    else
+      {
+       issue_loan (expr.get_origin (), expr.get_loan ());
+      }
+
+    auto loan_regions = base_place.regions.prepend (expr.get_origin ());
+    push_subset (ref_place.tyty, loan_regions, ref_place.regions);
+  }
+
+  void visit (const Assignment &expr) override
+  {
+    rust_debug ("\t_%u = Assignment(_%u) at %u:%u", lhs - 1,
+               expr.get_rhs () - 1, current_bb, current_stmt);
+
+    issue_read_move (expr.get_rhs ());
+    push_subset (lhs, expr.get_rhs ());
+  }
+
+  void visit (const CallExpr &expr) override
+  {
+    rust_debug ("\t_%u = CallExpr(_%u)", lhs - 1, expr.get_callable () - 1);
+
+    auto &return_place = place_db[lhs];
+    auto &callable_place = place_db[expr.get_callable ()];
+    auto callable_ty = callable_place.tyty->as<TyTy::CallableTypeInterface> ();
+
+    issue_read_move (expr.get_callable ());
+
+    // Each call needs unique regions.
+    auto call_regions = make_fresh_regions (callable_place.regions.size ());
+
+    for (size_t i = 0; i < expr.get_arguments ().size (); ++i)
+      {
+       auto arg = expr.get_arguments ().at (i);
+       auto arg_regions
+         = bind_regions (Resolver::TypeCheckContext::get ()
+                           ->get_variance_analysis_ctx ()
+                           .query_type_regions (
+                             callable_ty->get_param_type_at (i)),
+                         call_regions);
+       issue_read_move (arg);
+       push_subset (place_db[arg].tyty, place_db[arg].regions, arg_regions);
+      }
+
+    // sanitize return regions
+    sanitize_constrains_at_init (lhs);
+
+    auto return_regions
+      = bind_regions (Resolver::TypeCheckContext::get ()
+                       ->get_variance_analysis_ctx ()
+                       .query_type_regions (
+                         callable_ty->as<TyTy::FnType> ()->get_return_type ()),
+                     call_regions);
+    push_subset (return_place.tyty, return_regions, return_place.regions);
+
+    issue_jumps ();
+  }
+
+protected: // Statement visitor helpers
+  WARN_UNUSED_RESULT const BasicBlock &get_current_bb () const
+  {
+    return basic_blocks[current_bb];
+  }
+
+  WARN_UNUSED_RESULT static Polonius::Point
+  get_point (BasicBlockId bb, uint32_t stmt, PointPosition pos)
+  {
+    Polonius::Point point = 0;
+    point |= (bb << 16);
+    point |= (stmt << 1);
+    point |= (static_cast<uint8_t> (pos) & 1);
+    return point;
+  }
+
+  WARN_UNUSED_RESULT Polonius::Point get_current_point_start () const
+  {
+    return get_point (current_bb, current_stmt, PointPosition::START);
+  }
+
+  WARN_UNUSED_RESULT Polonius::Point get_current_point_mid () const
+  {
+    return get_point (current_bb, current_stmt, PointPosition::MID);
+  }
+
+  void add_stmt_to_cfg (BasicBlockId bb, uint32_t stmt)
+  {
+    if (stmt != 0)
+      {
+       facts.cfg_edge.emplace_back (get_point (bb, stmt - 1,
+                                               PointPosition::MID),
+                                    get_point (bb, stmt,
+                                               PointPosition::START));
+      }
+
+    facts.cfg_edge.emplace_back (get_point (bb, stmt, PointPosition::START),
+                                get_point (bb, stmt, PointPosition::MID));
+  }
+
+protected: // Generic BIR operations.
+  void issue_jumps ()
+  {
+    for (auto succ : get_current_bb ().successors)
+      facts.cfg_edge.emplace_back (get_current_point_mid (),
+                                  get_point (succ, 0, PointPosition::START));
+  }
+
+  /* Shallow r/w access */
+  void issue_place_access (PlaceId place_id)
+  {
+    auto &place = place_db[place_id];
+
+    if (place.is_constant ())
+      return;
+
+    if (place_id != RETURN_VALUE_PLACE)
+      facts.path_accessed_at_base.emplace_back (place_id,
+                                               get_current_point_mid ());
+
+    if (place.is_var ())
+      facts.var_used_at.emplace_back (place_id, get_current_point_mid ());
+    else if (place.is_path ())
+      {
+       facts.var_used_at.emplace_back (place_db.get_var (place_id),
+                                       get_current_point_mid ());
+      }
+  }
+
+  /** Deep read access, which consumes the place. */
+  void issue_read_move (PlaceId place_id)
+  {
+    auto &place = place_db[place_id];
+
+    issue_place_access (place_id);
+    if (place.should_be_moved ())
+      {
+       issue_move (place_id);
+      }
+    else
+      {
+       check_read_for_conflicts (place_id);
+      }
+  }
+
+  void issue_write_deep (PlaceId place_id, bool is_init = false)
+  {
+    auto &place = place_db[place_id];
+    rust_assert (place.is_lvalue () || place.is_rvalue ());
+
+    if (place.is_var ())
+      facts.var_defined_at.emplace_back (place_id, get_current_point_mid ());
+
+    if (!is_init)
+      {
+       facts.path_assigned_at_base.emplace_back (place_id,
+                                                 get_current_point_mid ());
+       check_write_for_conflict (place_id);
+       kill_borrows_for_place (place_id);
+      }
+  }
+
+  void issue_move (PlaceId place_id, bool initial = false)
+  {
+    if (!place_db[place_id].should_be_moved ())
+      return;
+
+    facts.path_moved_at_base.emplace_back (
+      place_id, initial ? get_point (0, 0, PointPosition::START)
+                       : get_current_point_mid ());
+
+    check_move_behind_reference (place_id);
+
+    if (!initial)
+      {
+       check_write_for_conflict (place_id);
+       kill_borrows_for_place (place_id);
+      }
+  }
+
+  void issue_loan (Polonius::Origin origin, LoanId loan_id)
+  {
+    facts.loan_issued_at.emplace_back (origin, loan_id,
+                                      get_current_point_mid ());
+
+    check_for_borrow_conficts (place_db.get_loans ()[loan_id].place, loan_id,
+                              place_db.get_loans ()[loan_id].mutability);
+  }
+
+  void issue_locals_dealloc ()
+  {
+    for (LoanId loan_id = 0; loan_id < place_db.get_loans ().size (); 
++loan_id)
+      {
+       auto &loan = place_db.get_loans ()[loan_id];
+       auto loaned_var_id = place_db.get_var (loan.place);
+       if (place_db[loaned_var_id].tyty->is<TyTy::ReferenceType> ())
+         continue;
+       if (loaned_var_id >= first_local)
+         facts.loan_invalidated_at.emplace_back (get_current_point_start (),
+                                                 loan_id);
+      }
+  }
+
+  void issue_user_type_constraints (PlaceId place_id, TyTy::BaseType *type)
+  {
+    auto user_regions = Resolver::TypeCheckContext::get ()
+                         ->get_variance_analysis_ctx ()
+                         .query_type_regions (type);
+    push_subset_user (place_db[place_id].tyty, place_db[place_id].regions,
+                     user_regions);
+  }
+
+  void check_read_for_conflicts (PlaceId place_id)
+  {
+    place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
+      for (auto loan : place_db[id].borrowed_by)
+       {
+         if (place_db.get_loans ()[loan].mutability == Mutability::Mut)
+           {
+             facts.loan_invalidated_at.emplace_back (
+               get_current_point_start (), loan);
+           }
+       }
+    });
+    place_db.for_each_path_from_root (place_id, [&] (PlaceId id) {
+      for (auto loan : place_db[id].borrowed_by)
+       {
+         if (place_db.get_loans ()[loan].mutability == Mutability::Mut)
+           {
+             facts.loan_invalidated_at.emplace_back (
+               get_current_point_start (), loan);
+           }
+       }
+    });
+  }
+
+  void check_write_for_conflict (PlaceId place_id)
+  {
+    place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
+      for (auto loan : place_db[id].borrowed_by)
+       {
+         facts.loan_invalidated_at.emplace_back (get_current_point_start (),
+                                                 loan);
+       }
+    });
+    place_db.for_each_path_from_root (place_id, [&] (PlaceId id) {
+      for (auto loan : place_db[id].borrowed_by)
+       {
+         facts.loan_invalidated_at.emplace_back (get_current_point_start (),
+                                                 loan);
+       }
+    });
+  }
+
+  void check_for_borrow_conficts (PlaceId place_id, LoanId loan,
+                                 Mutability mutability)
+  {
+    place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
+      for (auto other_loan : place_db[id].borrowed_by)
+       {
+         if (mutability == Mutability::Imm
+             && place_db.get_loans ()[other_loan].mutability
+                  == Mutability::Imm)
+           {
+             continue;
+           }
+         else
+           {
+             facts.loan_invalidated_at.emplace_back (
+               get_current_point_start (), other_loan);
+           }
+       }
+    });
+
+    place_db.for_each_path_from_root (place_id, [&] (PlaceId id) {
+      for (auto other_loan : place_db[id].borrowed_by)
+       {
+         if (mutability == Mutability::Imm
+             && place_db.get_loans ()[other_loan].mutability
+                  == Mutability::Imm)
+           {
+             continue;
+           }
+         else
+           {
+             facts.loan_invalidated_at.emplace_back (
+               get_current_point_start (), other_loan);
+           }
+       }
+    });
+  }
+
+  void check_move_behind_reference (PlaceId place_id)
+  {
+    place_db.for_each_path_segment (place_id, [&] (PlaceId id) {
+      if (id == place_id)
+       return;
+      if (place_db[id].kind == Place::DEREF)
+       {
+         rust_error_at (location, "Cannot move from behind a reference.");
+       }
+    });
+  }
+
+  void kill_borrows_for_place (PlaceId place_id)
+  {
+    auto &place = place_db[place_id];
+    for (auto loan : place.borrowed_by)
+      {
+       // TODO: this is more complicated, see
+       // compiler/rustc_borrowck/src/constraint_generation.rs:176
+       facts.loan_killed_at.emplace_back (loan, get_current_point_mid ());
+      }
+  }
+
+protected: // Subset helpers.
+  void push_subset (FreeRegion lhs, FreeRegion rhs)
+  {
+    rust_debug ("\t\tpush_subset: '?%lu: '?%lu", lhs, rhs);
+
+    facts.subset_base.emplace_back (lhs, rhs, get_current_point_mid ());
+  }
+
+  void push_subset_all (FreeRegion lhs, FreeRegion rhs)
+  {
+    rust_debug ("\t\tpush_subset_all: '?%lu: '?%lu", lhs, rhs);
+
+    for (auto point : cfg_points_all)
+      facts.subset_base.emplace_back (lhs, rhs, point);
+  }
+
+  void push_subset (Variance variance, FreeRegion lhs, FreeRegion rhs)
+  {
+    if (variance.is_covariant ())
+      {
+       push_subset (lhs, rhs);
+      }
+    else if (variance.is_contravariant ())
+      {
+       push_subset (rhs, lhs);
+      }
+    else if (variance.is_invariant ())
+      {
+       push_subset (lhs, rhs);
+       push_subset (rhs, lhs);
+      }
+  }
+
+  void push_subset_all (Variance variance, FreeRegion lhs, FreeRegion rhs)
+  {
+    if (variance.is_covariant ())
+      {
+       push_subset_all (lhs, rhs);
+      }
+    else if (variance.is_contravariant ())
+      {
+       push_subset_all (rhs, lhs);
+      }
+    else if (variance.is_invariant ())
+      {
+       push_subset_all (lhs, rhs);
+       push_subset_all (rhs, lhs);
+      }
+  }
+
+  void push_subset (PlaceId lhs, PlaceId rhs)
+  {
+    auto &lhs_place = place_db[lhs];
+    auto &rhs_place = place_db[rhs];
+
+    push_subset (lhs_place.tyty, rhs_place.regions, lhs_place.regions);
+  }
+
+  void push_subset (TyTy::BaseType *type, FreeRegions lhs, FreeRegions rhs)
+  {
+    auto variances = Resolver::TypeCheckContext::get ()
+                      ->get_variance_analysis_ctx ()
+                      .query_type_variances (type);
+    rust_assert (lhs.size () == rhs.size ());
+    rust_assert (lhs.size () == variances.size ());
+    for (size_t i = 0; i < lhs.size (); ++i)
+      {
+       push_subset (variances[i], lhs[i], rhs[i]);
+      }
+  }
+
+  void push_subset_all (TyTy::BaseType *type, FreeRegions lhs, FreeRegions rhs)
+  {
+    auto variances = Resolver::TypeCheckContext::get ()
+                      ->get_variance_analysis_ctx ()
+                      .query_type_variances (type);
+    rust_assert (lhs.size () == rhs.size ());
+    rust_assert (lhs.size () == variances.size ());
+    for (size_t i = 0; i < lhs.size (); ++i)
+      {
+       push_subset_all (variances[i], lhs[i], rhs[i]);
+      }
+  }
+
+  void push_subset_user (TyTy::BaseType *type, FreeRegions free_regions,
+                        std::vector<TyTy::Region> user_regions)
+  {
+    auto variances = Resolver::TypeCheckContext::get ()
+                      ->get_variance_analysis_ctx ()
+                      .query_type_variances (type);
+    rust_assert (free_regions.size () == user_regions.size ());
+    rust_assert (free_regions.size () == variances.size ());
+
+    for (size_t i = 0; i < free_regions.size (); ++i)
+      {
+       if (user_regions[i].is_named ())
+         {
+           push_subset (variances[i], free_regions[i],
+                        {Polonius::Origin (user_regions[i].get_index ())});
+         }
+       else if (user_regions[i].is_anonymous ())
+         {
+           // IGNORE
+         }
+       else
+         {
+           rust_internal_error_at (UNKNOWN_LOCATION, "Unexpected region type");
+         }
+      }
+  }
+
+  /**
+   * Apply type and lifetime bounds
+   *
+   * For a place we have a list of fresh regions. We need to apply constraints
+   * from type definition to it. First `n` regions belong to the lifetime
+   * parameters of the type. The rest are flatten lifetime parameters of the
+   * type arguments. We walk the type arguments with a offset
+   */
+  void sanitize_constrains_at_init (PlaceId place_id)
+  {
+    auto &place = place_db[place_id];
+
+    rust_debug ("\tSanitize constraints of %s",
+               place.tyty->as_string ().c_str ());
+
+    if (auto generic = place.tyty->try_as<TyTy::SubstitutionRef> ())
+      {
+       auto &regions = place.regions;
+       auto region_end = sanitize_constraints (*generic, 0, regions);
+       rust_assert (region_end == regions.size ());
+      }
+    else if (place.tyty->is<TyTy::ReferenceType> ())
+      {
+       for (auto &region : place.regions)
+         {
+           if (region != place.regions[0])
+             push_subset (region, place.regions[0]);
+         }
+      }
+  }
+
+  size_t sanitize_constraints (const TyTy::BaseType *type, size_t region_start,
+                              const FreeRegions &regions)
+  {
+    switch (type->get_kind ())
+      {
+      case TyTy::ADT:
+       return sanitize_constraints (type->as<const TyTy::ADTType> (),
+                                    region_start, regions);
+      case TyTy::STR:
+       return region_start;
+      case TyTy::REF:
+       return 1
+              + sanitize_constraints (
+                type->as<const TyTy::ReferenceType> ()->get_base (),
+                region_start, regions);
+      case TyTy::POINTER:
+       return sanitize_constraints (
+         type->as<const TyTy::PointerType> ()->get_base (), region_start,
+         regions);
+      case TyTy::ARRAY:
+       return sanitize_constraints (
+         type->as<const TyTy::ArrayType> ()->get_element_type (), region_start,
+         regions);
+      case TyTy::SLICE:
+       return sanitize_constraints (
+         type->as<const TyTy::SliceType> ()->get_element_type (), region_start,
+         regions);
+      case TyTy::FNDEF:
+       case TyTy::TUPLE: {
+         for (auto &field : type->as<const TyTy::TupleType> ()->get_fields ())
+           sanitize_constraints (field.get_tyty (), region_start, regions);
+       }
+       break;
+      case TyTy::FNPTR:
+      case TyTy::PROJECTION:
+       return sanitize_constraints (*type->as<const TyTy::SubstitutionRef> (),
+                                    region_start, regions);
+      case TyTy::BOOL:
+      case TyTy::CHAR:
+      case TyTy::INT:
+      case TyTy::UINT:
+      case TyTy::FLOAT:
+      case TyTy::USIZE:
+      case TyTy::ISIZE:
+      case TyTy::NEVER:
+      case TyTy::DYNAMIC:
+      case TyTy::CLOSURE:
+      case TyTy::ERROR:
+       return region_start;
+      case TyTy::PLACEHOLDER:
+      case TyTy::INFER:
+      case TyTy::PARAM:
+       rust_unreachable ();
+      }
+    rust_unreachable ();
+  }
+
+  size_t sanitize_constraints (const TyTy::SubstitutionRef &type,
+                              size_t region_start, const FreeRegions &regions)
+  {
+    for (auto constr : type.get_region_constraints ().region_region)
+      {
+       rust_assert (constr.first.is_early_bound ());
+       rust_assert (constr.second.is_early_bound ());
+       auto lhs = constr.first.get_index () + region_start;
+       auto rhs = constr.second.get_index () + region_start;
+       push_subset (regions[lhs], regions[rhs]);
+      }
+
+    size_t region_end = region_start + type.get_num_lifetime_params ();
+
+    /*
+     * For type `Foo<'a, T1, T2>`, where `T1 = &'b Vec<&'c i32>` and `T2 = &'d
+     * i32 the regions are `['a, 'b, 'c, 'd]`. The ranges
+     */
+    std::vector<size_t> type_param_region_ranges;
+    type_param_region_ranges.push_back (region_end);
+
+    for (auto type_param : type.get_substs ())
+      {
+       TyTy::SubstitutionArg arg = TyTy::SubstitutionArg::error ();
+       bool ok = type.get_used_arguments ().get_argument_for_symbol (
+         type_param.get_param_ty (), &arg);
+       rust_assert (ok);
+       region_end
+         = sanitize_constraints (arg.get_tyty (), region_end, regions);
+       type_param_region_ranges.push_back (region_end);
+      }
+
+    /*
+     * For constrain of form: `T: 'a` push outlives with all in range
+     * `indexof(T)..(indexof(T) + 1)`
+     */
+    for (auto constr : type.get_region_constraints ().type_region)
+      {
+       auto type_param_index_opt
+         = type.get_used_arguments ().find_symbol (*constr.first);
+       rust_assert (type_param_index_opt.has_value ());
+       size_t type_param_index = type_param_index_opt.value ();
+
+       for (size_t i = type_param_region_ranges[type_param_index];
+            i < type_param_region_ranges[type_param_index + 1]; ++i)
+         {
+           push_subset (regions[i],
+                        regions[constr.second.get_index () + region_start]);
+         }
+      }
+
+    return region_end;
+  }
+}; // namespace BIR
+
+} // namespace BIR
+} // namespace Rust
+
+#endif // RUST_BIR_FACT_COLLECTOR_H
diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc 
b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc
index 5daa7eb8ded..77726eb1c26 100644
--- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc
+++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc
@@ -20,6 +20,7 @@
 #include "rust-function-collector.h"
 #include "rust-bir-builder.h"
 #include "rust-bir-dump.h"
+#include "rust-bir-fact-collector.h"
 
 namespace Rust {
 namespace HIR {
@@ -86,6 +87,8 @@ BorrowChecker::go (HIR::Crate &crate)
          dump_function_bir (filename, bir,
                             func->get_function_name ().as_string ());
        }
+
+      auto facts = BIR::FactCollector::collect (bir);
     }
 
   for (auto closure ATTRIBUTE_UNUSED : collector.get_closures ())
-- 
2.45.2

Reply via email to