// Copyright (C) 2020-2025 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 // . #ifndef RUST_BIR_BASE_H #define RUST_BIR_BASE_H #include "rust-bir-place.h" #include "rust-bir-visitor.h" #include "polonius/rust-polonius-ffi.h" #include "rust-tyty-variance-analysis.h" namespace Rust { namespace BIR { struct BasicBlock; struct BasicBlockId; using BasicBlocks = IndexVec; class Statement; class AbstractExpr; /** * Top-level entity of the Borrow-checker IR (BIR). * It represents a single function (method, closure, etc.), which is the * basic unit of borrow-checking. */ struct Function { PlaceDB place_db; std::vector arguments; BasicBlocks basic_blocks; FreeRegions universal_regions; std::vector> universal_region_bounds; std::unordered_map region_hir_map; location_t location; }; /** Single statement of BIR. */ class Statement { public: enum class Kind { ASSIGNMENT, // = SWITCH, // switch RETURN, // return GOTO, // goto STORAGE_DEAD, // StorageDead() STORAGE_LIVE, // StorageLive() USER_TYPE_ASCRIPTION, // UserTypeAscription(, ) FAKE_READ, }; private: Kind kind; // ASSIGNMENT: lhs // SWITCH: switch_val // StorageDead/StorageLive: place // otherwise: PlaceId place; // ASSIGNMENT: rhs // otherwise: std::unique_ptr expr; TyTy::BaseType *type; // stores location of the actual expression from source code // currently only available when kind is ASSIGNMENT | RETURN // FIXME: Add location for other statement kinds location_t location; public: static Statement make_assignment (PlaceId place, AbstractExpr *rhs, location_t location) { return Statement (Kind::ASSIGNMENT, place, rhs, nullptr, location); } static Statement make_switch (PlaceId place) { return Statement (Kind::SWITCH, place); } static Statement make_return (location_t location) { return Statement (Kind::RETURN, INVALID_PLACE, nullptr, nullptr, location); } static Statement make_goto () { return Statement (Kind::GOTO); } static Statement make_storage_dead (PlaceId place) { return Statement (Kind::STORAGE_DEAD, place); } static Statement make_storage_live (PlaceId place) { return Statement (Kind::STORAGE_LIVE, place); } static Statement make_user_type_ascription (PlaceId place, TyTy::BaseType *type) { return Statement (Kind::USER_TYPE_ASCRIPTION, place, nullptr, type); } static Statement make_fake_read (PlaceId place) { return Statement (Kind::FAKE_READ, place); } private: // compelete constructor, used by make_* functions Statement (Kind kind, PlaceId place = INVALID_PLACE, AbstractExpr *rhs = nullptr, TyTy::BaseType *type = nullptr, location_t location = UNKNOWN_LOCATION) : kind (kind), place (place), expr (rhs), type (type), location (location) {} public: WARN_UNUSED_RESULT Kind get_kind () const { return kind; } WARN_UNUSED_RESULT PlaceId get_place () const { return place; } WARN_UNUSED_RESULT AbstractExpr &get_expr () const { return *expr; } WARN_UNUSED_RESULT TyTy::BaseType *get_type () const { return type; } WARN_UNUSED_RESULT location_t get_location () const { return location; } }; /** Unique identifier for a basic block in the BIR. */ struct BasicBlockId { uint32_t value; // some overloads for comparision bool operator== (const BasicBlockId &rhs) const { return value == rhs.value; } bool operator!= (const BasicBlockId &rhs) const { return !(operator== (rhs)); } bool operator< (const BasicBlockId &rhs) const { return value < rhs.value; } bool operator> (const BasicBlockId &rhs) const { return value > rhs.value; } bool operator<= (const BasicBlockId &rhs) const { return !(operator> (rhs)); } bool operator>= (const BasicBlockId &rhs) const { return !(operator< (rhs)); } }; static constexpr BasicBlockId INVALID_BB = {std::numeric_limits::max ()}; static constexpr BasicBlockId ENTRY_BASIC_BLOCK = {0}; struct BasicBlock { // BIR "instructions". std::vector statements; // A basic block can end with: goto, return or switch std::vector successors; public: WARN_UNUSED_RESULT bool is_terminated () const; WARN_UNUSED_RESULT bool is_goto_terminated () const { return is_terminated () && statements.back ().get_kind () == Statement::Kind::GOTO; } }; enum class ExprKind { INITIALIZER, OPERATOR, BORROW, ASSIGNMENT, CALL, }; // Rhs expression of BIR assignment statements (abstract). class AbstractExpr : public Visitable { ExprKind kind; public: explicit AbstractExpr (ExprKind kind) : kind (kind) {} WARN_UNUSED_RESULT ExprKind get_kind () const { return kind; } virtual ~AbstractExpr () {} }; class InitializerExpr : public VisitableImpl { std::vector values; public: explicit InitializerExpr (std::vector &&values) : VisitableImpl (ExprKind::INITIALIZER), values (values) {} public: std::vector &get_values () { return values; } WARN_UNUSED_RESULT const std::vector &get_values () const { return values; } }; template class Operator : public VisitableImpl> { std::array operands; public: explicit Operator (std::array &&operands) : VisitableImpl> (ExprKind::OPERATOR), operands (operands) {} public: template WARN_UNUSED_RESULT PlaceId get_operand () const { static_assert (I < ARITY, "Index out of bounds"); return operands[I]; } }; class BorrowExpr : public VisitableImpl { PlaceId place; LoanId loan; Polonius::Origin origin; public: explicit BorrowExpr (PlaceId place, LoanId loan_id, Polonius::Origin lifetime) : VisitableImpl (ExprKind::BORROW), place (place), loan (loan_id), origin (lifetime) {} WARN_UNUSED_RESULT PlaceId get_place () const { return place; } WARN_UNUSED_RESULT LoanId get_loan_id () const { return loan; } WARN_UNUSED_RESULT Polonius::Origin get_origin () const { return origin; } }; /** * This expression is only to be used inside the assignment statement and acts * as identity wrapper for a place value. It is separated from `Operator<1>` to * render it more explicitly in the dump. */ class Assignment : public VisitableImpl { PlaceId rhs; public: explicit Assignment (PlaceId rhs) : VisitableImpl (ExprKind::ASSIGNMENT), rhs (rhs) {} public: WARN_UNUSED_RESULT PlaceId get_rhs () const { return rhs; } }; class CallExpr final : public VisitableImpl { std::vector arguments; PlaceId callable; public: explicit CallExpr (PlaceId callable, std::vector &&arguments) : VisitableImpl (ExprKind::CALL), arguments (arguments), callable (callable) {} public: WARN_UNUSED_RESULT const std::vector &get_arguments () const { return arguments; } WARN_UNUSED_RESULT PlaceId get_callable () const { return callable; } }; inline bool BasicBlock::is_terminated () const { if (statements.empty ()) return false; switch (statements.back ().get_kind ()) { case Statement::Kind::GOTO: case Statement::Kind::RETURN: case Statement::Kind::SWITCH: return true; case Statement::Kind::ASSIGNMENT: return statements.back ().get_expr ().get_kind () == ExprKind::CALL; default: return false; } } } // namespace BIR } // namespace Rust #endif // RUST_BIR_BASE_H