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 ®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 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