new file mode 100644
@@ -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 ®ion : 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 ®ion : 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 ®ions = 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 ®ion : 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 ®ions)
+ {
+ 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 ®ions)
+ {
+ 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
@@ -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 ())