blob: 725e4d2828db9182f607482b69ab074b4dffb21b [file] [log] [blame]
// Copyright (C) 2020-2026 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_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<BasicBlockId, BasicBlock>;
class Statement;
class AbstractExpr;
/** 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<uint32_t>::max ()};
static constexpr BasicBlockId ENTRY_BASIC_BLOCK = {0};
/**
* 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<PlaceId> arguments;
BasicBlocks basic_blocks;
FreeRegions universal_regions;
std::vector<std::pair<FreeRegion, FreeRegion>> universal_region_bounds;
std::unordered_map<Polonius::Origin, HIR::LifetimeParam *> region_hir_map;
location_t location;
};
/** Single statement of BIR. */
class Statement
{
public:
enum class Kind
{
ASSIGNMENT, // <place> = <expr>
SWITCH, // switch <place>
RETURN, // return
GOTO, // goto
STORAGE_DEAD, // StorageDead(<place>)
STORAGE_LIVE, // StorageLive(<place>)
USER_TYPE_ASCRIPTION, // UserTypeAscription(<place>, <tyty>)
FAKE_READ,
};
private:
Kind kind;
// ASSIGNMENT: lhs
// SWITCH: switch_val
// StorageDead/StorageLive: place
// otherwise: <unused>
PlaceId place;
// ASSIGNMENT: rhs
// otherwise: <unused>
std::unique_ptr<AbstractExpr> 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; }
};
struct BasicBlock
{
// BIR "instructions".
std::vector<Statement> statements;
// A basic block can end with: goto, return or switch
std::vector<BasicBlockId> 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<AbstractExpr, InitializerExpr>
{
std::vector<PlaceId> values;
public:
explicit InitializerExpr (std::vector<PlaceId> &&values)
: VisitableImpl<AbstractExpr, InitializerExpr> (ExprKind::INITIALIZER),
values (values)
{}
public:
std::vector<PlaceId> &get_values () { return values; }
WARN_UNUSED_RESULT const std::vector<PlaceId> &get_values () const
{
return values;
}
};
template <unsigned ARITY>
class Operator : public VisitableImpl<AbstractExpr, Operator<ARITY>>
{
std::array<PlaceId, ARITY> operands;
public:
explicit Operator (std::array<PlaceId, ARITY> &&operands)
: VisitableImpl<AbstractExpr, Operator<ARITY>> (ExprKind::OPERATOR),
operands (operands)
{}
public:
template <size_t I> WARN_UNUSED_RESULT PlaceId get_operand () const
{
static_assert (I < ARITY, "Index out of bounds");
return operands[I];
}
};
class BorrowExpr : public VisitableImpl<AbstractExpr, BorrowExpr>
{
PlaceId place;
LoanId loan;
Polonius::Origin origin;
public:
explicit BorrowExpr (PlaceId place, LoanId loan_id, Polonius::Origin lifetime)
: VisitableImpl<AbstractExpr, BorrowExpr> (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<AbstractExpr, Assignment>
{
PlaceId rhs;
public:
explicit Assignment (PlaceId rhs)
: VisitableImpl<AbstractExpr, Assignment> (ExprKind::ASSIGNMENT), rhs (rhs)
{}
public:
WARN_UNUSED_RESULT PlaceId get_rhs () const { return rhs; }
};
class CallExpr final : public VisitableImpl<AbstractExpr, CallExpr>
{
std::vector<PlaceId> arguments;
PlaceId callable;
public:
explicit CallExpr (PlaceId callable, std::vector<PlaceId> &&arguments)
: VisitableImpl (ExprKind::CALL), arguments (arguments), callable (callable)
{}
public:
WARN_UNUSED_RESULT const std::vector<PlaceId> &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