mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 15:10:12 +01:00
Refactor InstKind to move metadata from macros to the type. (#4119)
This adds `DefinitionInfo` for `Define`-based configuration so that
parameters are optional. It also makes it easier to provide the
equivalent functions on both `Definition` and `Define`.
A common pattern used here is to change from a `switch` with in-line
`case`s to instead have `case`s that call an overloaded function. What's
happening here is that the instruction type is used to select an
overload, and if an overload is not defined, a compiler error would
result. Meanwhile, clusters of overloads are being defined using
`requires`-based templating, so that equivalent implementations are not
copied. This addresses a limitation of a vanilla `switch` approach where
it's hard to have redundant cases using conditional logic, while also
getting compiler errors when adding new `InstKind` entries, which had
been a significant part of why we used macros previously.
This starts hitting some odd clang-format edge cases causing
`CARBON_KIND_SWITCH(inst){` (missing space), which I haven't seen
before. Adding `CARBON_KIND_SWITCH` to .clang-format works around it.
This commit is contained in:
+5
-1
@@ -15,5 +15,9 @@ PointerAlignment: Left
|
||||
# We abuse control macros for formatting other kinds of macros.
|
||||
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
||||
IfMacros:
|
||||
['CARBON_DEFINE_RAW_ENUM_CLASS', 'CARBON_DEFINE_RAW_ENUM_CLASS_NO_NAMES']
|
||||
[
|
||||
'CARBON_DEFINE_RAW_ENUM_CLASS',
|
||||
'CARBON_DEFINE_RAW_ENUM_CLASS_NO_NAMES',
|
||||
'CARBON_KIND_SWITCH',
|
||||
]
|
||||
StatementMacros: ['ABSTRACT']
|
||||
|
||||
+84
-71
@@ -856,7 +856,7 @@ class TypeCompleter {
|
||||
return value_rep;
|
||||
}
|
||||
|
||||
auto BuildBuiltinValueRepr(SemIR::TypeId type_id,
|
||||
auto BuildValueReprForInst(SemIR::TypeId type_id,
|
||||
SemIR::BuiltinInst builtin) const
|
||||
-> SemIR::ValueRepr {
|
||||
switch (builtin.builtin_inst_kind) {
|
||||
@@ -901,8 +901,8 @@ class TypeCompleter {
|
||||
return MakePointerValueRepr(elementwise_rep, aggregate_kind);
|
||||
}
|
||||
|
||||
auto BuildStructTypeValueRepr(SemIR::TypeId type_id,
|
||||
SemIR::StructType struct_type) const
|
||||
auto BuildValueReprForInst(SemIR::TypeId type_id,
|
||||
SemIR::StructType struct_type) const
|
||||
-> SemIR::ValueRepr {
|
||||
// TODO: Share more code with tuples.
|
||||
auto fields = context_.inst_blocks().Get(struct_type.fields_id);
|
||||
@@ -935,8 +935,8 @@ class TypeCompleter {
|
||||
same_as_object_rep);
|
||||
}
|
||||
|
||||
auto BuildTupleTypeValueRepr(SemIR::TypeId type_id,
|
||||
SemIR::TupleType tuple_type) const
|
||||
auto BuildValueReprForInst(SemIR::TypeId type_id,
|
||||
SemIR::TupleType tuple_type) const
|
||||
-> SemIR::ValueRepr {
|
||||
// TODO: Share more code with structs.
|
||||
auto elements = context_.type_blocks().Get(tuple_type.elements_id);
|
||||
@@ -964,78 +964,91 @@ class TypeCompleter {
|
||||
same_as_object_rep);
|
||||
}
|
||||
|
||||
auto BuildValueReprForInst(SemIR::TypeId type_id,
|
||||
SemIR::ArrayType /*inst*/) const
|
||||
-> SemIR::ValueRepr {
|
||||
// For arrays, it's convenient to always use a pointer representation,
|
||||
// even when the array has zero or one element, in order to support
|
||||
// indexing.
|
||||
return MakePointerValueRepr(type_id, SemIR::ValueRepr::ObjectAggregate);
|
||||
}
|
||||
|
||||
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/,
|
||||
SemIR::ClassType inst) const -> SemIR::ValueRepr {
|
||||
auto& class_info = context_.classes().Get(inst.class_id);
|
||||
// The value representation of an adapter is the value representation of
|
||||
// its adapted type.
|
||||
if (class_info.adapt_id.is_valid()) {
|
||||
return GetNestedValueRepr(class_info.object_repr_id);
|
||||
}
|
||||
// Otherwise, the value representation for a class is a pointer to the
|
||||
// object representation.
|
||||
// TODO: Support customized value representations for classes.
|
||||
// TODO: Pick a better value representation when possible.
|
||||
return MakePointerValueRepr(class_info.object_repr_id,
|
||||
SemIR::ValueRepr::ObjectAggregate);
|
||||
}
|
||||
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.template IsAnyOf<
|
||||
SemIR::AssociatedEntityType, SemIR::FunctionType,
|
||||
SemIR::GenericClassType, SemIR::GenericInterfaceType,
|
||||
SemIR::InterfaceType, SemIR::UnboundElementType>())
|
||||
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const
|
||||
-> SemIR::ValueRepr {
|
||||
// These types have no runtime operations, so we use an empty value
|
||||
// representation.
|
||||
//
|
||||
// TODO: There is information we could model here:
|
||||
// - For an interface, we could use a witness.
|
||||
// - For an associated entity, we could use an index into the witness.
|
||||
// - For an unbound element, we could use an index or offset.
|
||||
return MakeEmptyValueRepr();
|
||||
}
|
||||
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.template IsAnyOf<SemIR::BindSymbolicName,
|
||||
SemIR::InterfaceWitnessAccess>())
|
||||
auto BuildValueReprForInst(SemIR::TypeId type_id, InstT /*inst*/) const
|
||||
-> SemIR::ValueRepr {
|
||||
// For symbolic types, we arbitrarily pick a copy representation.
|
||||
return MakeCopyValueRepr(type_id);
|
||||
}
|
||||
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.template IsAnyOf<SemIR::FloatType, SemIR::IntType,
|
||||
SemIR::PointerType>())
|
||||
auto BuildValueReprForInst(SemIR::TypeId type_id, InstT /*inst*/) const
|
||||
-> SemIR::ValueRepr {
|
||||
return MakeCopyValueRepr(type_id);
|
||||
}
|
||||
|
||||
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/,
|
||||
SemIR::ConstType inst) const -> SemIR::ValueRepr {
|
||||
// The value representation of `const T` is the same as that of `T`.
|
||||
// Objects are not modifiable through their value representations.
|
||||
return GetNestedValueRepr(inst.inner_id);
|
||||
}
|
||||
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.is_type() == SemIR::InstIsType::Never)
|
||||
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT inst) const
|
||||
-> SemIR::ValueRepr {
|
||||
CARBON_FATAL() << "Type refers to non-type inst " << inst;
|
||||
}
|
||||
|
||||
// Builds and returns the value representation for the given type. All nested
|
||||
// types, as found by AddNestedIncompleteTypes, are known to be complete.
|
||||
auto BuildValueRepr(SemIR::TypeId type_id, SemIR::Inst inst) const
|
||||
-> SemIR::ValueRepr {
|
||||
// Use overload resolution to select the implementation, producing compile
|
||||
// errors when BuildTypeForInst isn't defined for a given instruction.
|
||||
CARBON_KIND_SWITCH(inst) {
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
case CARBON_KIND(SemIR::Name typed_inst): { \
|
||||
return BuildValueReprForInst(type_id, typed_inst); \
|
||||
}
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
CARBON_FATAL() << "Type refers to non-type inst " << inst;
|
||||
|
||||
case SemIR::ArrayType::Kind: {
|
||||
// For arrays, it's convenient to always use a pointer representation,
|
||||
// even when the array has zero or one element, in order to support
|
||||
// indexing.
|
||||
return MakePointerValueRepr(type_id, SemIR::ValueRepr::ObjectAggregate);
|
||||
}
|
||||
|
||||
case CARBON_KIND(SemIR::StructType struct_type): {
|
||||
return BuildStructTypeValueRepr(type_id, struct_type);
|
||||
}
|
||||
case CARBON_KIND(SemIR::TupleType tuple_type): {
|
||||
return BuildTupleTypeValueRepr(type_id, tuple_type);
|
||||
}
|
||||
case CARBON_KIND(SemIR::ClassType class_type): {
|
||||
auto& class_info = context_.classes().Get(class_type.class_id);
|
||||
// The value representation of an adapter is the value representation of
|
||||
// its adapted type.
|
||||
if (class_info.adapt_id.is_valid()) {
|
||||
return GetNestedValueRepr(class_info.object_repr_id);
|
||||
}
|
||||
// Otherwise, the value representation for a class is a pointer to the
|
||||
// object representation.
|
||||
// TODO: Support customized value representations for classes.
|
||||
// TODO: Pick a better value representation when possible.
|
||||
return MakePointerValueRepr(class_info.object_repr_id,
|
||||
SemIR::ValueRepr::ObjectAggregate);
|
||||
}
|
||||
case SemIR::AssociatedEntityType::Kind:
|
||||
case SemIR::FunctionType::Kind:
|
||||
case SemIR::GenericClassType::Kind:
|
||||
case SemIR::GenericInterfaceType::Kind:
|
||||
case SemIR::InterfaceType::Kind:
|
||||
case SemIR::UnboundElementType::Kind: {
|
||||
// These types have no runtime operations, so we use an empty value
|
||||
// representation.
|
||||
//
|
||||
// TODO: There is information we could model here:
|
||||
// - For an interface, we could use a witness.
|
||||
// - For an associated entity, we could use an index into the witness.
|
||||
// - For an unbound element, we could use an index or offset.
|
||||
return MakeEmptyValueRepr();
|
||||
}
|
||||
case CARBON_KIND(SemIR::BuiltinInst builtin): {
|
||||
return BuildBuiltinValueRepr(type_id, builtin);
|
||||
}
|
||||
|
||||
case SemIR::BindSymbolicName::Kind:
|
||||
case SemIR::InterfaceWitnessAccess::Kind:
|
||||
// For symbolic types, we arbitrarily pick a copy representation.
|
||||
return MakeCopyValueRepr(type_id);
|
||||
|
||||
case SemIR::FloatType::Kind:
|
||||
case SemIR::IntType::Kind:
|
||||
case SemIR::PointerType::Kind:
|
||||
return MakeCopyValueRepr(type_id);
|
||||
|
||||
case CARBON_KIND(SemIR::ConstType const_type): {
|
||||
// The value representation of `const T` is the same as that of `T`.
|
||||
// Objects are not modifiable through their value representations.
|
||||
return GetNestedValueRepr(const_type.inner_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,24 +82,6 @@ class ConstantContext {
|
||||
int32_t last_lowered_constant_index_ = -1;
|
||||
};
|
||||
|
||||
// For each instruction kind that can produce a constant, there is a function
|
||||
// below to convert it to an `llvm::Constant*`:
|
||||
//
|
||||
// auto Emit<InstKind>AsConstant(ConstantContext& context,
|
||||
// SemIR::<InstKind> inst) -> llvm::Constant*;
|
||||
|
||||
// For constants that are always of type `type`, produce the trivial runtime
|
||||
// representation of type `type`.
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
static auto Emit##Name##AsConstant( \
|
||||
ConstantContext& context, SemIR::Name /*inst*/) -> llvm::Constant* { \
|
||||
return context.GetTypeAsValue(); \
|
||||
}
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
|
||||
// Emits an aggregate constant of LLVM type `Type` whose elements are the
|
||||
// contents of `refs_id`.
|
||||
template <typename ConstantType, typename Type>
|
||||
@@ -116,16 +98,37 @@ static auto EmitAggregateConstant(ConstantContext& context,
|
||||
return ConstantType::get(llvm_type, elements);
|
||||
}
|
||||
|
||||
static auto EmitStructValueAsConstant(ConstantContext& context,
|
||||
SemIR::StructValue inst)
|
||||
// For each instruction InstT, there is a function below to convert it to an
|
||||
// `llvm::Constant*`:
|
||||
//
|
||||
// auto EmitAsConstant(ConstantContext& context, SemIR::InstT inst)
|
||||
// -> llvm::Constant*;
|
||||
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.constant_kind() == SemIR::InstConstantKind::Never ||
|
||||
InstT::Kind.constant_kind() == SemIR::InstConstantKind::SymbolicOnly)
|
||||
static auto EmitAsConstant(ConstantContext& /*context*/, InstT inst)
|
||||
-> llvm::Constant* {
|
||||
CARBON_FATAL() << "Unexpected constant instruction kind " << inst;
|
||||
}
|
||||
|
||||
// For constants that are always of type `type`, produce the trivial runtime
|
||||
// representation of type `type`.
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.is_type() == SemIR::InstIsType::Always)
|
||||
static auto EmitAsConstant(ConstantContext& context, InstT /*inst*/)
|
||||
-> llvm::Constant* {
|
||||
return context.GetTypeAsValue();
|
||||
}
|
||||
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::StructValue inst)
|
||||
-> llvm::Constant* {
|
||||
return EmitAggregateConstant<llvm::ConstantStruct>(
|
||||
context, inst.elements_id,
|
||||
cast<llvm::StructType>(context.GetType(inst.type_id)));
|
||||
}
|
||||
|
||||
static auto EmitTupleValueAsConstant(ConstantContext& context,
|
||||
SemIR::TupleValue inst)
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::TupleValue inst)
|
||||
-> llvm::Constant* {
|
||||
// TODO: Add an ArrayValue instruction and stop using TupleValues to represent
|
||||
// array constants.
|
||||
@@ -140,80 +143,72 @@ static auto EmitTupleValueAsConstant(ConstantContext& context,
|
||||
cast<llvm::StructType>(context.GetType(inst.type_id)));
|
||||
}
|
||||
|
||||
static auto EmitAddrOfAsConstant(ConstantContext& /*context*/,
|
||||
SemIR::AddrOf /*inst*/) -> llvm::Constant* {
|
||||
static auto EmitAsConstant(ConstantContext& /*context*/, SemIR::AddrOf /*inst*/)
|
||||
-> llvm::Constant* {
|
||||
// TODO: Constant lvalue support. For now we have no constant lvalues, so we
|
||||
// should never form a constant AddrOf.
|
||||
CARBON_FATAL() << "AddrOf constants not supported yet";
|
||||
}
|
||||
|
||||
static auto EmitAssociatedEntityAsConstant(ConstantContext& context,
|
||||
SemIR::AssociatedEntity inst)
|
||||
static auto EmitAsConstant(ConstantContext& context,
|
||||
SemIR::AssociatedEntity inst) -> llvm::Constant* {
|
||||
return context.GetUnusedConstant(inst.type_id);
|
||||
}
|
||||
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::BaseDecl inst)
|
||||
-> llvm::Constant* {
|
||||
return context.GetUnusedConstant(inst.type_id);
|
||||
}
|
||||
|
||||
static auto EmitBaseDeclAsConstant(ConstantContext& context,
|
||||
SemIR::BaseDecl inst) -> llvm::Constant* {
|
||||
return context.GetUnusedConstant(inst.type_id);
|
||||
}
|
||||
|
||||
static auto EmitBoolLiteralAsConstant(ConstantContext& context,
|
||||
SemIR::BoolLiteral inst)
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::BoolLiteral inst)
|
||||
-> llvm::Constant* {
|
||||
return llvm::ConstantInt::get(llvm::Type::getInt1Ty(context.llvm_context()),
|
||||
inst.value.index);
|
||||
}
|
||||
|
||||
static auto EmitBoundMethodAsConstant(ConstantContext& context,
|
||||
SemIR::BoundMethod inst)
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::BoundMethod inst)
|
||||
-> llvm::Constant* {
|
||||
// Propagate just the function; the object is separately provided to the
|
||||
// enclosing call as an implicit argument.
|
||||
return context.GetConstant(inst.function_id);
|
||||
}
|
||||
|
||||
static auto EmitFieldDeclAsConstant(ConstantContext& context,
|
||||
SemIR::FieldDecl inst) -> llvm::Constant* {
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::FieldDecl inst)
|
||||
-> llvm::Constant* {
|
||||
return context.GetUnusedConstant(inst.type_id);
|
||||
}
|
||||
|
||||
static auto EmitFloatLiteralAsConstant(ConstantContext& context,
|
||||
SemIR::FloatLiteral inst)
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::FloatLiteral inst)
|
||||
-> llvm::Constant* {
|
||||
const llvm::APFloat& value = context.sem_ir().floats().Get(inst.float_id);
|
||||
return llvm::ConstantFP::get(context.GetType(inst.type_id), value);
|
||||
}
|
||||
|
||||
static auto EmitInterfaceWitnessAsConstant(ConstantContext& context,
|
||||
SemIR::InterfaceWitness inst)
|
||||
-> llvm::Constant* {
|
||||
static auto EmitAsConstant(ConstantContext& context,
|
||||
SemIR::InterfaceWitness inst) -> llvm::Constant* {
|
||||
// TODO: For dynamic dispatch, we might want to lower witness tables as
|
||||
// constants.
|
||||
return context.GetUnusedConstant(inst.type_id);
|
||||
}
|
||||
|
||||
static auto EmitIntLiteralAsConstant(ConstantContext& context,
|
||||
SemIR::IntLiteral inst)
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::IntLiteral inst)
|
||||
-> llvm::Constant* {
|
||||
return llvm::ConstantInt::get(context.GetType(inst.type_id),
|
||||
context.sem_ir().ints().Get(inst.int_id));
|
||||
}
|
||||
|
||||
static auto EmitNamespaceAsConstant(ConstantContext& context,
|
||||
SemIR::Namespace inst) -> llvm::Constant* {
|
||||
static auto EmitAsConstant(ConstantContext& context, SemIR::Namespace inst)
|
||||
-> llvm::Constant* {
|
||||
return context.GetUnusedConstant(inst.type_id);
|
||||
}
|
||||
|
||||
static auto EmitStringLiteralAsConstant(ConstantContext& /*context*/,
|
||||
SemIR::StringLiteral inst)
|
||||
-> llvm::Constant* {
|
||||
static auto EmitAsConstant(ConstantContext& /*context*/,
|
||||
SemIR::StringLiteral inst) -> llvm::Constant* {
|
||||
CARBON_FATAL() << "TODO: Add support: " << inst;
|
||||
}
|
||||
|
||||
static auto EmitStructTypeFieldAsConstant(ConstantContext& /*context*/,
|
||||
SemIR::StructTypeField /*inst*/)
|
||||
-> llvm::Constant* {
|
||||
static auto EmitAsConstant(ConstantContext& /*context*/,
|
||||
SemIR::StructTypeField /*inst*/) -> llvm::Constant* {
|
||||
// A StructTypeField isn't a value, so this constant value won't ever be used.
|
||||
// It also doesn't even have a type, so we can't use GetUnusedConstant.
|
||||
return nullptr;
|
||||
@@ -240,21 +235,17 @@ auto LowerConstants(FileContext& file_context,
|
||||
auto inst = file_context.sem_ir().insts().Get(inst_id);
|
||||
llvm::Constant* value = nullptr;
|
||||
CARBON_KIND_SWITCH(inst) {
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
case CARBON_KIND(SemIR::Name const_inst): \
|
||||
value = Emit##Name##AsConstant(context, const_inst); \
|
||||
break;
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
case CARBON_KIND(SemIR::Name const_inst): { \
|
||||
value = EmitAsConstant(context, const_inst); \
|
||||
break; \
|
||||
}
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
|
||||
default:
|
||||
CARBON_FATAL() << "Unexpected constant instruction kind " << inst;
|
||||
}
|
||||
|
||||
constants[inst_id.index] = value;
|
||||
context.SetLastLoweredConstantIndex(inst_id.index);
|
||||
}
|
||||
}
|
||||
} // namespace Carbon::Lower
|
||||
|
||||
} // namespace Carbon::Lower
|
||||
|
||||
@@ -309,107 +309,143 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
|
||||
}
|
||||
}
|
||||
|
||||
auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
|
||||
CARBON_KIND_SWITCH(sem_ir_->insts().Get(inst_id)) {
|
||||
case CARBON_KIND(SemIR::ArrayType inst): {
|
||||
return llvm::ArrayType::get(GetType(inst.element_type_id),
|
||||
sem_ir_->GetArrayBoundValue(inst.bound_id));
|
||||
}
|
||||
case CARBON_KIND(SemIR::BuiltinInst inst): {
|
||||
switch (inst.builtin_inst_kind) {
|
||||
case SemIR::BuiltinInstKind::Invalid:
|
||||
case SemIR::BuiltinInstKind::Error:
|
||||
CARBON_FATAL() << "Unexpected builtin type in lowering.";
|
||||
case SemIR::BuiltinInstKind::TypeType:
|
||||
return GetTypeType();
|
||||
case SemIR::BuiltinInstKind::FloatType:
|
||||
return llvm::Type::getDoubleTy(*llvm_context_);
|
||||
case SemIR::BuiltinInstKind::IntType:
|
||||
return llvm::Type::getInt32Ty(*llvm_context_);
|
||||
case SemIR::BuiltinInstKind::BoolType:
|
||||
// TODO: We may want to have different representations for `bool`
|
||||
// storage
|
||||
// (`i8`) versus for `bool` values (`i1`).
|
||||
return llvm::Type::getInt1Ty(*llvm_context_);
|
||||
case SemIR::BuiltinInstKind::StringType:
|
||||
// TODO: Decide how we want to represent `StringType`.
|
||||
return llvm::PointerType::get(*llvm_context_, 0);
|
||||
case SemIR::BuiltinInstKind::BoundMethodType:
|
||||
case SemIR::BuiltinInstKind::NamespaceType:
|
||||
case SemIR::BuiltinInstKind::WitnessType:
|
||||
// Return an empty struct as a placeholder.
|
||||
return llvm::StructType::get(*llvm_context_);
|
||||
}
|
||||
}
|
||||
case CARBON_KIND(SemIR::ClassType inst): {
|
||||
auto object_repr_id =
|
||||
sem_ir_->classes().Get(inst.class_id).object_repr_id;
|
||||
return GetType(object_repr_id);
|
||||
}
|
||||
case CARBON_KIND(SemIR::ConstType inst): {
|
||||
return GetType(inst.inner_id);
|
||||
}
|
||||
case SemIR::FloatType::Kind: {
|
||||
// TODO: Handle different sizes.
|
||||
return llvm::Type::getDoubleTy(*llvm_context_);
|
||||
}
|
||||
case CARBON_KIND(SemIR::IntType inst): {
|
||||
auto width =
|
||||
sem_ir_->insts().TryGetAs<SemIR::IntLiteral>(inst.bit_width_id);
|
||||
CARBON_CHECK(width) << "Can't lower int type with symbolic width";
|
||||
return llvm::IntegerType::get(
|
||||
*llvm_context_, sem_ir_->ints().Get(width->int_id).getZExtValue());
|
||||
}
|
||||
case SemIR::PointerType::Kind: {
|
||||
return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0);
|
||||
}
|
||||
case CARBON_KIND(SemIR::StructType inst): {
|
||||
auto fields = sem_ir_->inst_blocks().Get(inst.fields_id);
|
||||
llvm::SmallVector<llvm::Type*> subtypes;
|
||||
subtypes.reserve(fields.size());
|
||||
for (auto field_id : fields) {
|
||||
auto field = sem_ir_->insts().GetAs<SemIR::StructTypeField>(field_id);
|
||||
subtypes.push_back(GetType(field.field_type_id));
|
||||
}
|
||||
return llvm::StructType::get(*llvm_context_, subtypes);
|
||||
}
|
||||
case CARBON_KIND(SemIR::TupleType inst): {
|
||||
// TODO: Investigate special-casing handling of empty tuples so that they
|
||||
// can be collectively replaced with LLVM's void, particularly around
|
||||
// function returns. LLVM doesn't allow declaring variables with a void
|
||||
// type, so that may require significant special casing.
|
||||
auto elements = sem_ir_->type_blocks().Get(inst.elements_id);
|
||||
llvm::SmallVector<llvm::Type*> subtypes;
|
||||
subtypes.reserve(elements.size());
|
||||
for (auto element_id : elements) {
|
||||
subtypes.push_back(GetType(element_id));
|
||||
}
|
||||
return llvm::StructType::get(*llvm_context_, subtypes);
|
||||
}
|
||||
case SemIR::AssociatedEntityType::Kind:
|
||||
case SemIR::InterfaceType::Kind:
|
||||
case SemIR::FunctionType::Kind:
|
||||
case SemIR::GenericClassType::Kind:
|
||||
case SemIR::GenericInterfaceType::Kind:
|
||||
case SemIR::UnboundElementType::Kind: {
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::ArrayType inst)
|
||||
-> llvm::Type* {
|
||||
return llvm::ArrayType::get(
|
||||
context.GetType(inst.element_type_id),
|
||||
context.sem_ir().GetArrayBoundValue(inst.bound_id));
|
||||
}
|
||||
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::BuiltinInst inst)
|
||||
-> llvm::Type* {
|
||||
switch (inst.builtin_inst_kind) {
|
||||
case SemIR::BuiltinInstKind::Invalid:
|
||||
case SemIR::BuiltinInstKind::Error:
|
||||
CARBON_FATAL() << "Unexpected builtin type in lowering.";
|
||||
case SemIR::BuiltinInstKind::TypeType:
|
||||
return context.GetTypeType();
|
||||
case SemIR::BuiltinInstKind::FloatType:
|
||||
return llvm::Type::getDoubleTy(context.llvm_context());
|
||||
case SemIR::BuiltinInstKind::IntType:
|
||||
return llvm::Type::getInt32Ty(context.llvm_context());
|
||||
case SemIR::BuiltinInstKind::BoolType:
|
||||
// TODO: We may want to have different representations for `bool`
|
||||
// storage
|
||||
// (`i8`) versus for `bool` values (`i1`).
|
||||
return llvm::Type::getInt1Ty(context.llvm_context());
|
||||
case SemIR::BuiltinInstKind::StringType:
|
||||
// TODO: Decide how we want to represent `StringType`.
|
||||
return llvm::PointerType::get(context.llvm_context(), 0);
|
||||
case SemIR::BuiltinInstKind::BoundMethodType:
|
||||
case SemIR::BuiltinInstKind::NamespaceType:
|
||||
case SemIR::BuiltinInstKind::WitnessType:
|
||||
// Return an empty struct as a placeholder.
|
||||
// TODO: Should we model an interface as a witness table, or an associated
|
||||
// entity as an index?
|
||||
return llvm::StructType::get(*llvm_context_);
|
||||
}
|
||||
return llvm::StructType::get(context.llvm_context());
|
||||
}
|
||||
}
|
||||
|
||||
// Treat non-monomorphized symbolic types as opaque.
|
||||
case SemIR::BindSymbolicName::Kind:
|
||||
case SemIR::InterfaceWitnessAccess::Kind: {
|
||||
return llvm::StructType::get(*llvm_context_);
|
||||
}
|
||||
// BuildTypeForInst is used to construct types for FileContext::BuildType below.
|
||||
// Implementations return the LLVM type for the instruction. This first overload
|
||||
// is the fallback handler for non-type instructions.
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.is_type() == SemIR::InstIsType::Never)
|
||||
static auto BuildTypeForInst(FileContext& /*context*/, InstT inst)
|
||||
-> llvm::Type* {
|
||||
CARBON_FATAL() << "Cannot use inst as type: " << inst;
|
||||
}
|
||||
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::ClassType inst)
|
||||
-> llvm::Type* {
|
||||
auto object_repr_id =
|
||||
context.sem_ir().classes().Get(inst.class_id).object_repr_id;
|
||||
return context.GetType(object_repr_id);
|
||||
}
|
||||
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::ConstType inst)
|
||||
-> llvm::Type* {
|
||||
return context.GetType(inst.inner_id);
|
||||
}
|
||||
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::FloatType /*inst*/)
|
||||
-> llvm::Type* {
|
||||
// TODO: Handle different sizes.
|
||||
return llvm::Type::getDoubleTy(context.llvm_context());
|
||||
}
|
||||
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::IntType inst)
|
||||
-> llvm::Type* {
|
||||
auto width =
|
||||
context.sem_ir().insts().TryGetAs<SemIR::IntLiteral>(inst.bit_width_id);
|
||||
CARBON_CHECK(width) << "Can't lower int type with symbolic width";
|
||||
return llvm::IntegerType::get(
|
||||
context.llvm_context(),
|
||||
context.sem_ir().ints().Get(width->int_id).getZExtValue());
|
||||
}
|
||||
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::PointerType /*inst*/)
|
||||
-> llvm::Type* {
|
||||
return llvm::PointerType::get(context.llvm_context(), /*AddressSpace=*/0);
|
||||
}
|
||||
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::StructType inst)
|
||||
-> llvm::Type* {
|
||||
auto fields = context.sem_ir().inst_blocks().Get(inst.fields_id);
|
||||
llvm::SmallVector<llvm::Type*> subtypes;
|
||||
subtypes.reserve(fields.size());
|
||||
for (auto field_id : fields) {
|
||||
auto field =
|
||||
context.sem_ir().insts().GetAs<SemIR::StructTypeField>(field_id);
|
||||
subtypes.push_back(context.GetType(field.field_type_id));
|
||||
}
|
||||
return llvm::StructType::get(context.llvm_context(), subtypes);
|
||||
}
|
||||
|
||||
static auto BuildTypeForInst(FileContext& context, SemIR::TupleType inst)
|
||||
-> llvm::Type* {
|
||||
// TODO: Investigate special-casing handling of empty tuples so that they
|
||||
// can be collectively replaced with LLVM's void, particularly around
|
||||
// function returns. LLVM doesn't allow declaring variables with a void
|
||||
// type, so that may require significant special casing.
|
||||
auto elements = context.sem_ir().type_blocks().Get(inst.elements_id);
|
||||
llvm::SmallVector<llvm::Type*> subtypes;
|
||||
subtypes.reserve(elements.size());
|
||||
for (auto element_id : elements) {
|
||||
subtypes.push_back(context.GetType(element_id));
|
||||
}
|
||||
return llvm::StructType::get(context.llvm_context(), subtypes);
|
||||
}
|
||||
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.template IsAnyOf<
|
||||
SemIR::AssociatedEntityType, SemIR::FunctionType,
|
||||
SemIR::GenericClassType, SemIR::GenericInterfaceType,
|
||||
SemIR::InterfaceType, SemIR::UnboundElementType>())
|
||||
static auto BuildTypeForInst(FileContext& context, InstT /*inst*/)
|
||||
-> llvm::Type* {
|
||||
// Return an empty struct as a placeholder.
|
||||
// TODO: Should we model an interface as a witness table, or an associated
|
||||
// entity as an index?
|
||||
return llvm::StructType::get(context.llvm_context());
|
||||
}
|
||||
|
||||
// Treat non-monomorphized symbolic types as opaque.
|
||||
template <typename InstT>
|
||||
requires(InstT::Kind.template IsAnyOf<SemIR::BindSymbolicName,
|
||||
SemIR::InterfaceWitnessAccess>())
|
||||
static auto BuildTypeForInst(FileContext& context, InstT /*inst*/)
|
||||
-> llvm::Type* {
|
||||
return llvm::StructType::get(context.llvm_context());
|
||||
}
|
||||
|
||||
auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
|
||||
// Use overload resolution to select the implementation, producing compile
|
||||
// errors when BuildTypeForInst isn't defined for a given instruction.
|
||||
CARBON_KIND_SWITCH(sem_ir_->insts().Get(inst_id)) {
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
case CARBON_KIND(SemIR::Name inst): { \
|
||||
return BuildTypeForInst(*this, inst); \
|
||||
}
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
CARBON_FATAL() << "Cannot use inst as type: " << inst_id << " "
|
||||
<< sem_ir_->insts().Get(inst_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,15 @@ class FileContext {
|
||||
// Returns a global value for the given instruction.
|
||||
auto GetGlobal(SemIR::InstId inst_id) -> llvm::Value*;
|
||||
|
||||
// Returns the empty LLVM struct type used to represent the type `type`.
|
||||
auto GetTypeType() -> llvm::StructType* {
|
||||
if (!type_type_) {
|
||||
// `type` is lowered to an empty LLVM StructType.
|
||||
type_type_ = llvm::StructType::create(*llvm_context_, {}, "type");
|
||||
}
|
||||
return type_type_;
|
||||
}
|
||||
|
||||
auto llvm_context() -> llvm::LLVMContext& { return *llvm_context_; }
|
||||
auto llvm_module() -> llvm::Module& { return *llvm_module_; }
|
||||
auto sem_ir() -> const SemIR::File& { return *sem_ir_; }
|
||||
@@ -67,15 +76,6 @@ class FileContext {
|
||||
// the caller.
|
||||
auto BuildType(SemIR::InstId inst_id) -> llvm::Type*;
|
||||
|
||||
// Returns the empty LLVM struct type used to represent the type `type`.
|
||||
auto GetTypeType() -> llvm::StructType* {
|
||||
if (!type_type_) {
|
||||
// `type` is lowered to an empty LLVM StructType.
|
||||
type_type_ = llvm::StructType::create(*llvm_context_, {}, "type");
|
||||
}
|
||||
return type_type_;
|
||||
}
|
||||
|
||||
// State for building the LLVM IR.
|
||||
llvm::LLVMContext* llvm_context_;
|
||||
std::unique_ptr<llvm::Module> llvm_module_;
|
||||
|
||||
@@ -60,6 +60,17 @@ static auto FatalErrorIfEncountered(InstT inst) -> void {
|
||||
<< inst;
|
||||
}
|
||||
|
||||
// For instructions that are always of type `type`, produce the trivial runtime
|
||||
// representation of type `type`.
|
||||
static auto SetTrivialType(FunctionContext& context, SemIR::InstId inst_id)
|
||||
-> void {
|
||||
context.SetLocal(inst_id, context.GetTypeAsValue());
|
||||
}
|
||||
|
||||
// TODO: Consider renaming Handle##Name, instead relying on typed_inst overload
|
||||
// resolution. That would allow putting the nonexistent handler implementations
|
||||
// in `requires`-style overloads.
|
||||
// NOLINTNEXTLINE(readability-function-size): The define confuses lint.
|
||||
auto FunctionContext::LowerInst(SemIR::InstId inst_id) -> void {
|
||||
// Skip over constants. `FileContext::GetGlobal` lowers them as needed.
|
||||
if (sem_ir().constant_values().Get(inst_id).is_constant()) {
|
||||
@@ -69,25 +80,29 @@ auto FunctionContext::LowerInst(SemIR::InstId inst_id) -> void {
|
||||
auto inst = sem_ir().insts().Get(inst_id);
|
||||
CARBON_VLOG() << "Lowering " << inst_id << ": " << inst << "\n";
|
||||
builder_.getInserter().SetCurrentInstId(inst_id);
|
||||
|
||||
CARBON_KIND_SWITCH(inst) {
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(Name)
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
case CARBON_KIND(SemIR::Name typed_inst): { \
|
||||
if constexpr (SemIR::Name::Kind.is_lowered()) { \
|
||||
Handle##Name(*this, inst_id, typed_inst); \
|
||||
} else { \
|
||||
FatalErrorIfEncountered(typed_inst); \
|
||||
} \
|
||||
break; \
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
case CARBON_KIND(SemIR::Name typed_inst): { \
|
||||
if constexpr (!SemIR::Name::Kind.is_lowered()) { \
|
||||
FatalErrorIfEncountered(typed_inst); \
|
||||
} else if constexpr (SemIR::Name::Kind.constant_kind() == \
|
||||
SemIR::InstConstantKind::Always) { \
|
||||
CARBON_FATAL() << "Missing constant value for constant instruction " \
|
||||
<< inst; \
|
||||
} else if constexpr (SemIR::Name::Kind.is_type() == \
|
||||
SemIR::InstIsType::Always) { \
|
||||
SetTrivialType(*this, inst_id); \
|
||||
} else { \
|
||||
Handle##Name(*this, inst_id, typed_inst); \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
|
||||
default:
|
||||
CARBON_FATAL() << "Missing constant value for constant instruction "
|
||||
<< inst;
|
||||
}
|
||||
|
||||
builder_.getInserter().SetCurrentInstId(SemIR::InstId::Invalid);
|
||||
} // namespace Carbon::Lower
|
||||
}
|
||||
|
||||
auto FunctionContext::GetBlockArg(SemIR::InstBlockId block_id,
|
||||
SemIR::TypeId type_id) -> llvm::PHINode* {
|
||||
|
||||
@@ -159,9 +159,8 @@ class FunctionContext {
|
||||
Map<SemIR::InstId, llvm::Value*> locals_;
|
||||
};
|
||||
|
||||
// Declare handlers for each SemIR::File instruction that is not always
|
||||
// constant.
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(Name)
|
||||
// Declare handlers for each SemIR::File instruction. Note that these aren't all
|
||||
// defined.
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
auto Handle##Name(FunctionContext& context, SemIR::InstId inst_id, \
|
||||
SemIR::Name inst) -> void;
|
||||
|
||||
@@ -160,6 +160,11 @@ auto HandleDeref(FunctionContext& context, SemIR::InstId inst_id,
|
||||
context.SetLocal(inst_id, context.GetValue(inst.pointer_id));
|
||||
}
|
||||
|
||||
auto HandleFacetTypeAccess(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::FacetTypeAccess /*inst*/) -> void {
|
||||
context.SetLocal(inst_id, context.GetTypeAsValue());
|
||||
}
|
||||
|
||||
auto HandleInitializeFrom(FunctionContext& context, SemIR::InstId /*inst_id*/,
|
||||
SemIR::InitializeFrom inst) -> void {
|
||||
auto storage_type_id = context.sem_ir().insts().Get(inst.dest_id).type_id();
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/lower/function_context.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::Lower {
|
||||
|
||||
// For instructions that are always of type `type`, produce the trivial runtime
|
||||
// representation of type `type`.
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...)
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
auto Handle##Name(FunctionContext& context, SemIR::InstId inst_id, \
|
||||
SemIR::Name /*inst*/) -> void { \
|
||||
context.SetLocal(inst_id, context.GetTypeAsValue()); \
|
||||
}
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
|
||||
auto HandleFacetTypeAccess(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::FacetTypeAccess /*inst*/) -> void {
|
||||
context.SetLocal(inst_id, context.GetTypeAsValue());
|
||||
}
|
||||
|
||||
} // namespace Carbon::Lower
|
||||
@@ -176,34 +176,15 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping {
|
||||
// precedence of that type's syntax. Higher numbers correspond to higher
|
||||
// precedence.
|
||||
static auto GetTypePrecedence(InstKind kind) -> int {
|
||||
switch (kind) {
|
||||
case ArrayType::Kind:
|
||||
case AssociatedEntityType::Kind:
|
||||
case BindSymbolicName::Kind:
|
||||
case BuiltinInst::Kind:
|
||||
case ClassType::Kind:
|
||||
case FloatType::Kind:
|
||||
case FunctionType::Kind:
|
||||
case GenericClassType::Kind:
|
||||
case GenericInterfaceType::Kind:
|
||||
case InterfaceType::Kind:
|
||||
case InterfaceWitnessAccess::Kind:
|
||||
case IntType::Kind:
|
||||
case StructType::Kind:
|
||||
case TupleType::Kind:
|
||||
case UnboundElementType::Kind:
|
||||
return 0;
|
||||
case ConstType::Kind:
|
||||
return -1;
|
||||
case PointerType::Kind:
|
||||
return -2;
|
||||
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
CARBON_FATAL() << "GetTypePrecedence for non-type inst kind " << kind;
|
||||
CARBON_CHECK(kind.is_type() != InstIsType::Never)
|
||||
<< "Only called for kinds which can define a type.";
|
||||
if (kind == ConstType::Kind) {
|
||||
return -1;
|
||||
}
|
||||
if (kind == PointerType::Kind) {
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements File::StringifyTypeExpr. Static to prevent accidental use of
|
||||
|
||||
@@ -13,12 +13,12 @@ CARBON_DEFINE_ENUM_CLASS_NAMES(InstKind) = {
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
};
|
||||
|
||||
auto InstKind::ir_name() const -> llvm::StringLiteral {
|
||||
static constexpr const llvm::StringLiteral Table[] = {
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) SemIR::Name::Kind.ir_name(),
|
||||
auto InstKind::definition_info(InstKind inst_kind) -> const DefinitionInfo& {
|
||||
static constexpr InstKind::DefinitionInfo DefinitionInfos[] = {
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) SemIR::Name::Kind.info_,
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
};
|
||||
return Table[AsInt()];
|
||||
return DefinitionInfos[inst_kind.AsInt()];
|
||||
}
|
||||
|
||||
auto InstKind::value_kind() const -> InstValueKind {
|
||||
@@ -31,26 +31,4 @@ auto InstKind::value_kind() const -> InstValueKind {
|
||||
return Table[AsInt()];
|
||||
}
|
||||
|
||||
auto InstKind::constant_kind() const -> InstConstantKind {
|
||||
static constexpr InstConstantKind Table[] = {
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...) InstConstantKind::Never,
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...) \
|
||||
InstConstantKind::SymbolicOnly,
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...) \
|
||||
InstConstantKind::Conditional,
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...) InstConstantKind::Always,
|
||||
#define CARBON_SEM_IR_INST_KIND(Name)
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
};
|
||||
return Table[AsInt()];
|
||||
}
|
||||
|
||||
auto InstKind::terminator_kind() const -> TerminatorKind {
|
||||
static constexpr const TerminatorKind Table[] = {
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) SemIR::Name::Kind.terminator_kind(),
|
||||
#include "toolchain/sem_ir/inst_kind.def"
|
||||
};
|
||||
return Table[AsInt()];
|
||||
}
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
+78
-148
@@ -10,160 +10,90 @@
|
||||
// This macro should be defined before including this header:
|
||||
// - CARBON_SEM_IR_INST_KIND(Name)
|
||||
// Invoked for each kind of semantic instruction.
|
||||
//
|
||||
// The invocation of the above macro will be wrapped in one macro from each of
|
||||
// the following sets, which by default expand to their argument:
|
||||
//
|
||||
// Whether the instruction can define a type:
|
||||
// - CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
|
||||
// Invoked for each instruction that is always of type `type`, and might
|
||||
// define a type constant.
|
||||
// - CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
|
||||
// Invoked for each instruction that is sometimes of type `type`, and might
|
||||
// define a type constant.
|
||||
// - CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...)
|
||||
// Invoked for each instruction that can never define a type constant. Note
|
||||
// that such instructions can still have type `type`, but are not the
|
||||
// canonical definition of any type.
|
||||
//
|
||||
// Whether the instruction can define a constant, see `InstConstantKind`:
|
||||
// - CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...)
|
||||
// Invoked when `constant_kind()` is `InstConstantKind::Never`.
|
||||
// - CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
|
||||
// Invoked when `constant_kind()` is `InstConstantKind::SymbolicOnly`.
|
||||
// - CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...)
|
||||
// Invoked when `constant_kind()` is `InstConstantKind::Conditional`.
|
||||
// - CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...)
|
||||
// Invoked when `constant_kind()` is `InstConstantKind::Always`.
|
||||
//
|
||||
// Defining these is optional.
|
||||
|
||||
#ifndef CARBON_SEM_IR_INST_KIND
|
||||
#error "Must define the x-macro to use this file."
|
||||
#endif
|
||||
|
||||
#ifndef CARBON_SEM_IR_INST_KIND_TYPE_NEVER
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...) __VA_ARGS__
|
||||
#endif
|
||||
#ifndef CARBON_SEM_IR_INST_KIND_TYPE_MAYBE
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...) __VA_ARGS__
|
||||
#endif
|
||||
#ifndef CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS
|
||||
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...) __VA_ARGS__
|
||||
#endif
|
||||
|
||||
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...) __VA_ARGS__
|
||||
#endif
|
||||
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...) __VA_ARGS__
|
||||
#endif
|
||||
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...) __VA_ARGS__
|
||||
#endif
|
||||
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS
|
||||
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...) __VA_ARGS__
|
||||
#endif
|
||||
|
||||
#define CARBON_SEM_IR_INST_KIND_IMPL(Name, IsType, IsConstant) \
|
||||
CARBON_SEM_IR_INST_KIND_##IsType( \
|
||||
CARBON_SEM_IR_INST_KIND_##IsConstant(CARBON_SEM_IR_INST_KIND(Name)))
|
||||
|
||||
// For each instruction kind declared here there is a matching definition in
|
||||
// `typed_insts.h`.
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(AdaptDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(AddrOf, TYPE_NEVER, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(AddrPattern, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ArrayIndex, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ArrayInit, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ArrayType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(AsCompatible, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Assign, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(AssociatedConstantDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntity, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntityType, TYPE_ALWAYS,
|
||||
CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BaseDecl, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BindAlias, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ExportDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BindName, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BindSymbolicName, TYPE_MAYBE,
|
||||
CONSTANT_SYMBOLIC_ONLY)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BindValue, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BlockArg, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BoolLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BoundMethod, TYPE_NEVER, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Branch, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BranchIf, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BranchWithArg, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(BuiltinInst, TYPE_ALWAYS, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Call, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ClassDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ClassElementAccess, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ClassInit, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ClassType, TYPE_ALWAYS, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ConstType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Converted, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Deref, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(FacetTypeAccess, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(FieldDecl, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(FloatLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(FloatType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(FunctionDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(FunctionType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(GenericClassType, TYPE_ALWAYS,
|
||||
CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(GenericInterfaceType, TYPE_ALWAYS,
|
||||
CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ImplDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ImportDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ImportRefUnloaded, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ImportRefLoaded, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(InitializeFrom, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceDecl, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceType, TYPE_ALWAYS, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitness, TYPE_NEVER, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitnessAccess, TYPE_MAYBE,
|
||||
CONSTANT_SYMBOLIC_ONLY)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(IntLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(IntType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(NameRef, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Namespace, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Param, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(PointerType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ReturnExpr, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Return, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(SpliceBlock, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(StringLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(StructAccess, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(StructInit, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(StructLiteral, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(StructType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(StructTypeField, TYPE_NEVER, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(StructValue, TYPE_NEVER, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(TemporaryStorage, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(Temporary, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(TupleAccess, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(TupleIndex, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(TupleInit, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(TupleLiteral, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(TupleType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(TupleValue, TYPE_NEVER, CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(UnaryOperatorNot, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(UnboundElementType, TYPE_ALWAYS,
|
||||
CONSTANT_CONDITIONAL)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ValueAsRef, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(ValueOfInitializer, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND_IMPL(VarStorage, TYPE_NEVER, CONSTANT_NEVER)
|
||||
CARBON_SEM_IR_INST_KIND(AdaptDecl)
|
||||
CARBON_SEM_IR_INST_KIND(AddrOf)
|
||||
CARBON_SEM_IR_INST_KIND(AddrPattern)
|
||||
CARBON_SEM_IR_INST_KIND(ArrayIndex)
|
||||
CARBON_SEM_IR_INST_KIND(ArrayInit)
|
||||
CARBON_SEM_IR_INST_KIND(ArrayType)
|
||||
CARBON_SEM_IR_INST_KIND(AsCompatible)
|
||||
CARBON_SEM_IR_INST_KIND(Assign)
|
||||
CARBON_SEM_IR_INST_KIND(AssociatedConstantDecl)
|
||||
CARBON_SEM_IR_INST_KIND(AssociatedEntity)
|
||||
CARBON_SEM_IR_INST_KIND(AssociatedEntityType)
|
||||
CARBON_SEM_IR_INST_KIND(BaseDecl)
|
||||
CARBON_SEM_IR_INST_KIND(BindAlias)
|
||||
CARBON_SEM_IR_INST_KIND(ExportDecl)
|
||||
CARBON_SEM_IR_INST_KIND(BindName)
|
||||
CARBON_SEM_IR_INST_KIND(BindSymbolicName)
|
||||
CARBON_SEM_IR_INST_KIND(BindValue)
|
||||
CARBON_SEM_IR_INST_KIND(BlockArg)
|
||||
CARBON_SEM_IR_INST_KIND(BoolLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(BoundMethod)
|
||||
CARBON_SEM_IR_INST_KIND(Branch)
|
||||
CARBON_SEM_IR_INST_KIND(BranchIf)
|
||||
CARBON_SEM_IR_INST_KIND(BranchWithArg)
|
||||
CARBON_SEM_IR_INST_KIND(BuiltinInst)
|
||||
CARBON_SEM_IR_INST_KIND(Call)
|
||||
CARBON_SEM_IR_INST_KIND(ClassDecl)
|
||||
CARBON_SEM_IR_INST_KIND(ClassElementAccess)
|
||||
CARBON_SEM_IR_INST_KIND(ClassInit)
|
||||
CARBON_SEM_IR_INST_KIND(ClassType)
|
||||
CARBON_SEM_IR_INST_KIND(ConstType)
|
||||
CARBON_SEM_IR_INST_KIND(Converted)
|
||||
CARBON_SEM_IR_INST_KIND(Deref)
|
||||
CARBON_SEM_IR_INST_KIND(FacetTypeAccess)
|
||||
CARBON_SEM_IR_INST_KIND(FieldDecl)
|
||||
CARBON_SEM_IR_INST_KIND(FloatLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(FloatType)
|
||||
CARBON_SEM_IR_INST_KIND(FunctionDecl)
|
||||
CARBON_SEM_IR_INST_KIND(FunctionType)
|
||||
CARBON_SEM_IR_INST_KIND(GenericClassType)
|
||||
CARBON_SEM_IR_INST_KIND(GenericInterfaceType)
|
||||
CARBON_SEM_IR_INST_KIND(ImplDecl)
|
||||
CARBON_SEM_IR_INST_KIND(ImportDecl)
|
||||
CARBON_SEM_IR_INST_KIND(ImportRefUnloaded)
|
||||
CARBON_SEM_IR_INST_KIND(ImportRefLoaded)
|
||||
CARBON_SEM_IR_INST_KIND(InitializeFrom)
|
||||
CARBON_SEM_IR_INST_KIND(InterfaceDecl)
|
||||
CARBON_SEM_IR_INST_KIND(InterfaceType)
|
||||
CARBON_SEM_IR_INST_KIND(InterfaceWitness)
|
||||
CARBON_SEM_IR_INST_KIND(InterfaceWitnessAccess)
|
||||
CARBON_SEM_IR_INST_KIND(IntLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(IntType)
|
||||
CARBON_SEM_IR_INST_KIND(NameRef)
|
||||
CARBON_SEM_IR_INST_KIND(Namespace)
|
||||
CARBON_SEM_IR_INST_KIND(Param)
|
||||
CARBON_SEM_IR_INST_KIND(PointerType)
|
||||
CARBON_SEM_IR_INST_KIND(ReturnExpr)
|
||||
CARBON_SEM_IR_INST_KIND(Return)
|
||||
CARBON_SEM_IR_INST_KIND(SpliceBlock)
|
||||
CARBON_SEM_IR_INST_KIND(StringLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(StructAccess)
|
||||
CARBON_SEM_IR_INST_KIND(StructInit)
|
||||
CARBON_SEM_IR_INST_KIND(StructLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(StructType)
|
||||
CARBON_SEM_IR_INST_KIND(StructTypeField)
|
||||
CARBON_SEM_IR_INST_KIND(StructValue)
|
||||
CARBON_SEM_IR_INST_KIND(TemporaryStorage)
|
||||
CARBON_SEM_IR_INST_KIND(Temporary)
|
||||
CARBON_SEM_IR_INST_KIND(TupleAccess)
|
||||
CARBON_SEM_IR_INST_KIND(TupleIndex)
|
||||
CARBON_SEM_IR_INST_KIND(TupleInit)
|
||||
CARBON_SEM_IR_INST_KIND(TupleLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(TupleType)
|
||||
CARBON_SEM_IR_INST_KIND(TupleValue)
|
||||
CARBON_SEM_IR_INST_KIND(UnaryOperatorNot)
|
||||
CARBON_SEM_IR_INST_KIND(UnboundElementType)
|
||||
CARBON_SEM_IR_INST_KIND(ValueAsRef)
|
||||
CARBON_SEM_IR_INST_KIND(ValueOfInitializer)
|
||||
CARBON_SEM_IR_INST_KIND(VarStorage)
|
||||
|
||||
#undef CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS
|
||||
#undef CARBON_SEM_IR_INST_KIND_TYPE_MAYBE
|
||||
#undef CARBON_SEM_IR_INST_KIND_TYPE_NEVER
|
||||
|
||||
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER
|
||||
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY
|
||||
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL
|
||||
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS
|
||||
|
||||
#undef CARBON_SEM_IR_INST_KIND_IMPL
|
||||
#undef CARBON_SEM_IR_INST_KIND
|
||||
|
||||
@@ -13,6 +13,17 @@
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
// Whether an instruction defines a type.
|
||||
enum class InstIsType : int8_t {
|
||||
// Always of type `type`, and might define a type constant.
|
||||
Always,
|
||||
// Sometimes of type `type`, and might define a type constant.
|
||||
Maybe,
|
||||
// Never defines a type constant. Note that such instructions can still have
|
||||
// type `type`, but are not the canonical definition of any type.
|
||||
Never,
|
||||
};
|
||||
|
||||
// Whether an instruction produces or represents a value, and if so, what kind
|
||||
// of value.
|
||||
enum class InstValueKind : int8_t {
|
||||
@@ -75,42 +86,62 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) {
|
||||
template <typename TypedNodeId>
|
||||
class Definition;
|
||||
|
||||
// Information about a definition. See associated accessors below for
|
||||
// comments.
|
||||
struct DefinitionInfo {
|
||||
llvm::StringLiteral ir_name;
|
||||
InstIsType is_type = InstIsType::Never;
|
||||
InstConstantKind constant_kind = InstConstantKind::Never;
|
||||
TerminatorKind terminator_kind = TerminatorKind::NotTerminator;
|
||||
bool is_lowered = true;
|
||||
};
|
||||
|
||||
// Provides a definition for this instruction kind. Should only be called
|
||||
// once, to construct the kind as part of defining it in `typed_insts.h`.
|
||||
template <typename TypedNodeId>
|
||||
constexpr auto Define(
|
||||
llvm::StringLiteral ir_name,
|
||||
TerminatorKind terminator_kind = TerminatorKind::NotTerminator) const
|
||||
-> Definition<TypedNodeId>;
|
||||
|
||||
// As above, but for instructions which are not lowered; `Define(ir_name)`
|
||||
// should be used instead of specifying `is_lowered = true`.
|
||||
template <typename TypedNodeId>
|
||||
constexpr auto Define(llvm::StringLiteral ir_name, bool is_lowered) const
|
||||
-> Definition<TypedNodeId>;
|
||||
constexpr auto Define(DefinitionInfo info) const -> Definition<TypedNodeId>;
|
||||
|
||||
using EnumBase::AsInt;
|
||||
using EnumBase::Make;
|
||||
|
||||
// Returns the name to use for this instruction kind in Semantics IR.
|
||||
auto ir_name() const -> llvm::StringLiteral;
|
||||
// Returns true if the kind matches any of the provided instructions' kinds.
|
||||
template <typename... InstT>
|
||||
constexpr auto IsAnyOf() const -> bool {
|
||||
return ((*this == InstT::Kind) || ...);
|
||||
}
|
||||
|
||||
// Returns whether this kind of instruction is expected to produce a value.
|
||||
// Returns the name to use for this instruction kind in Semantics IR.
|
||||
auto ir_name() const -> llvm::StringLiteral {
|
||||
return definition_info(*this).ir_name;
|
||||
}
|
||||
|
||||
// Returns whether this instruction kind defines a type.
|
||||
auto is_type() const -> InstIsType { return definition_info(*this).is_type; }
|
||||
|
||||
// Returns whether this instruction kind is expected to produce a value.
|
||||
auto value_kind() const -> InstValueKind;
|
||||
|
||||
// Returns whether this kind of instruction is able to define a constant.
|
||||
auto constant_kind() const -> InstConstantKind;
|
||||
// Returns this instruction kind's category of allowed constants.
|
||||
auto constant_kind() const -> InstConstantKind {
|
||||
return definition_info(*this).constant_kind;
|
||||
}
|
||||
|
||||
// Returns whether this instruction kind is a code block terminator, such as
|
||||
// an unconditional branch instruction, or part of the termination sequence,
|
||||
// such as a conditional branch instruction. The termination sequence of a
|
||||
// code block appears after all other instructions, and ends with a
|
||||
// terminator instruction.
|
||||
auto terminator_kind() const -> TerminatorKind;
|
||||
auto terminator_kind() const -> TerminatorKind {
|
||||
return definition_info(*this).terminator_kind;
|
||||
}
|
||||
|
||||
// Compute a fingerprint for this instruction kind, allowing its use as part
|
||||
// of the key in a `FoldingSet`.
|
||||
void Profile(llvm::FoldingSetNodeID& id) { id.AddInteger(AsInt()); }
|
||||
|
||||
private:
|
||||
// Returns the DefinitionInfo for the kind.
|
||||
static auto definition_info(InstKind kind) -> const DefinitionInfo&;
|
||||
};
|
||||
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
@@ -122,9 +153,9 @@ static_assert(sizeof(InstKind) == 1, "Kind objects include padding!");
|
||||
|
||||
// A definition of an instruction kind. This is an InstKind value, plus
|
||||
// ancillary data such as the name to use for the node kind in LLVM IR. These
|
||||
// are not copyable, and only one instance of this type is expected to exist per
|
||||
// instruction kind, specifically `TypedInst::Kind`. Use `InstKind` instead as a
|
||||
// thin wrapper around an instruction kind index.
|
||||
// are not copyable, and only one instance of this type is expected to exist
|
||||
// per instruction kind, specifically `TypedInst::Kind`. Use `InstKind`
|
||||
// instead as a thin wrapper around an instruction kind index.
|
||||
template <typename TypedNodeIdArg>
|
||||
class InstKind::Definition : public InstKind {
|
||||
public:
|
||||
@@ -135,47 +166,40 @@ class InstKind::Definition : public InstKind {
|
||||
auto operator=(const Definition&) -> Definition& = delete;
|
||||
|
||||
// Returns the name to use for this instruction kind in Semantics IR.
|
||||
constexpr auto ir_name() const -> llvm::StringLiteral { return ir_name_; }
|
||||
constexpr auto ir_name() const -> llvm::StringLiteral {
|
||||
return info_.ir_name;
|
||||
}
|
||||
|
||||
// Returns whether this instruction kind defines a type.
|
||||
constexpr auto is_type() const -> InstIsType { return info_.is_type; }
|
||||
|
||||
// Returns this instruction kind's category of allowed constants.
|
||||
constexpr auto constant_kind() const -> InstConstantKind {
|
||||
return info_.constant_kind;
|
||||
}
|
||||
|
||||
// Returns whether this instruction kind is a code block terminator. See
|
||||
// InstKind::terminator_kind().
|
||||
constexpr auto terminator_kind() const -> TerminatorKind {
|
||||
return terminator_kind_;
|
||||
return info_.terminator_kind;
|
||||
}
|
||||
|
||||
// Returns true if the instruction is lowered.
|
||||
constexpr auto is_lowered() const -> bool { return is_lowered_; }
|
||||
constexpr auto is_lowered() const -> bool { return info_.is_lowered; }
|
||||
|
||||
private:
|
||||
friend class InstKind;
|
||||
|
||||
constexpr Definition(InstKind kind, llvm::StringLiteral ir_name,
|
||||
TerminatorKind terminator_kind, bool is_lowered)
|
||||
: InstKind(kind),
|
||||
ir_name_(ir_name),
|
||||
terminator_kind_(terminator_kind),
|
||||
is_lowered_(is_lowered) {}
|
||||
constexpr Definition(InstKind kind, InstKind::DefinitionInfo info)
|
||||
: InstKind(kind), info_(info) {}
|
||||
|
||||
llvm::StringLiteral ir_name_;
|
||||
TerminatorKind terminator_kind_;
|
||||
bool is_lowered_;
|
||||
InstKind::DefinitionInfo info_;
|
||||
};
|
||||
|
||||
template <typename TypedNodeId>
|
||||
constexpr auto InstKind::Define(llvm::StringLiteral ir_name,
|
||||
TerminatorKind terminator_kind) const
|
||||
constexpr auto InstKind::Define(DefinitionInfo info) const
|
||||
-> Definition<TypedNodeId> {
|
||||
return Definition<TypedNodeId>(*this, ir_name, terminator_kind,
|
||||
/*is_lowered=*/true);
|
||||
}
|
||||
|
||||
template <typename TypedNodeId>
|
||||
constexpr auto InstKind::Define(llvm::StringLiteral ir_name,
|
||||
bool is_lowered) const
|
||||
-> Definition<TypedNodeId> {
|
||||
CARBON_CHECK(!is_lowered) << "Use Define(ir_name) instead";
|
||||
return Definition<TypedNodeId>(*this, ir_name, TerminatorKind::NotTerminator,
|
||||
/*is_lowered=*/is_lowered);
|
||||
return Definition<TypedNodeId>(*this, info);
|
||||
}
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
+161
-105
@@ -47,7 +47,7 @@ namespace Carbon::SemIR {
|
||||
// An adapted type declaration in a class, of the form `adapt T;`.
|
||||
struct AdaptDecl {
|
||||
static constexpr auto Kind = InstKind::AdaptDecl.Define<Parse::AdaptDeclId>(
|
||||
"adapt_decl", /*is_lowered=*/false);
|
||||
{.ir_name = "adapt_decl", .is_lowered = false});
|
||||
|
||||
// No type_id; this is not a value.
|
||||
TypeId adapted_type_id;
|
||||
@@ -56,8 +56,8 @@ struct AdaptDecl {
|
||||
// The `&` address-of operator, as in `&lvalue`.
|
||||
struct AddrOf {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::AddrOf.Define<Parse::NodeId>("addr_of");
|
||||
static constexpr auto Kind = InstKind::AddrOf.Define<Parse::NodeId>(
|
||||
{.ir_name = "addr_of", .constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
InstId lvalue_id;
|
||||
@@ -67,7 +67,7 @@ struct AddrOf {
|
||||
// generally be one of `AnyBindName`.
|
||||
struct AddrPattern {
|
||||
static constexpr auto Kind =
|
||||
InstKind::AddrPattern.Define<Parse::AddrId>("addr_pattern");
|
||||
InstKind::AddrPattern.Define<Parse::AddrId>({.ir_name = "addr_pattern"});
|
||||
|
||||
TypeId type_id;
|
||||
// The `self` binding.
|
||||
@@ -78,7 +78,7 @@ struct AddrPattern {
|
||||
struct ArrayIndex {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::ArrayIndex.Define<Parse::NodeId>("array_index");
|
||||
InstKind::ArrayIndex.Define<Parse::NodeId>({.ir_name = "array_index"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId array_id;
|
||||
@@ -138,7 +138,7 @@ struct AnyAggregateValue {
|
||||
struct ArrayInit {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::ArrayInit.Define<Parse::NodeId>("array_init");
|
||||
InstKind::ArrayInit.Define<Parse::NodeId>({.ir_name = "array_init"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId inits_id;
|
||||
@@ -147,8 +147,10 @@ struct ArrayInit {
|
||||
|
||||
// An array of `element_type_id` values, sized to `bound_id`.
|
||||
struct ArrayType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ArrayType.Define<Parse::ArrayExprId>("array_type");
|
||||
static constexpr auto Kind = InstKind::ArrayType.Define<Parse::ArrayExprId>(
|
||||
{.ir_name = "array_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
InstId bound_id;
|
||||
@@ -157,8 +159,8 @@ struct ArrayType {
|
||||
|
||||
// Perform a no-op conversion to a compatible type.
|
||||
struct AsCompatible {
|
||||
static constexpr auto Kind =
|
||||
InstKind::AsCompatible.Define<Parse::NodeId>("as_compatible");
|
||||
static constexpr auto Kind = InstKind::AsCompatible.Define<Parse::NodeId>(
|
||||
{.ir_name = "as_compatible"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId source_id;
|
||||
@@ -170,7 +172,7 @@ struct AsCompatible {
|
||||
struct Assign {
|
||||
static constexpr auto Kind = InstKind::Assign.Define<
|
||||
Parse::NodeIdOneOf<Parse::InfixOperatorEqualId, Parse::VariableDeclId>>(
|
||||
"assign");
|
||||
{.ir_name = "assign"});
|
||||
|
||||
// Assignments are statements, and so have no type.
|
||||
InstId lhs_id;
|
||||
@@ -181,7 +183,7 @@ struct Assign {
|
||||
struct AssociatedConstantDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::AssociatedConstantDecl.Define<Parse::NodeId>(
|
||||
"assoc_const_decl", /*is_lowered=*/false);
|
||||
{.ir_name = "assoc_const_decl", .is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
NameId name_id;
|
||||
@@ -192,8 +194,8 @@ struct AssociatedConstantDecl {
|
||||
// This represents the entity before impl lookup is performed, and identifies
|
||||
// the slot within a witness where the constant value will be found.
|
||||
struct AssociatedEntity {
|
||||
static constexpr auto Kind =
|
||||
InstKind::AssociatedEntity.Define<Parse::NodeId>("assoc_entity");
|
||||
static constexpr auto Kind = InstKind::AssociatedEntity.Define<Parse::NodeId>(
|
||||
{.ir_name = "assoc_entity", .constant_kind = InstConstantKind::Always});
|
||||
|
||||
// The type of the associated entity. This is an AssociatedEntityType.
|
||||
TypeId type_id;
|
||||
@@ -206,7 +208,9 @@ struct AssociatedEntity {
|
||||
struct AssociatedEntityType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::AssociatedEntityType.Define<Parse::InvalidNodeId>(
|
||||
"assoc_entity_type");
|
||||
{.ir_name = "assoc_entity_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
InterfaceId interface_id;
|
||||
@@ -217,8 +221,8 @@ struct AssociatedEntityType {
|
||||
// element of the derived class, and the type of the `BaseDecl` instruction is
|
||||
// an `UnboundElementType`.
|
||||
struct BaseDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::BaseDecl.Define<Parse::BaseDeclId>("base_decl");
|
||||
static constexpr auto Kind = InstKind::BaseDecl.Define<Parse::BaseDeclId>(
|
||||
{.ir_name = "base_decl", .constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
TypeId base_type_id;
|
||||
@@ -253,7 +257,7 @@ struct AnyBindNameOrExportDecl {
|
||||
// Binds a name as an alias.
|
||||
struct BindAlias {
|
||||
static constexpr auto Kind =
|
||||
InstKind::BindAlias.Define<Parse::NodeId>("bind_alias");
|
||||
InstKind::BindAlias.Define<Parse::NodeId>({.ir_name = "bind_alias"});
|
||||
|
||||
TypeId type_id;
|
||||
BindNameId bind_name_id;
|
||||
@@ -264,7 +268,7 @@ struct BindAlias {
|
||||
struct BindName {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::BindName.Define<Parse::NodeId>("bind_name");
|
||||
InstKind::BindName.Define<Parse::NodeId>({.ir_name = "bind_name"});
|
||||
|
||||
TypeId type_id;
|
||||
BindNameId bind_name_id;
|
||||
@@ -275,8 +279,10 @@ struct BindName {
|
||||
|
||||
// Binds a symbolic name, such as `x` in `let x:! i32 = 7;`.
|
||||
struct BindSymbolicName {
|
||||
static constexpr auto Kind =
|
||||
InstKind::BindSymbolicName.Define<Parse::NodeId>("bind_symbolic_name");
|
||||
static constexpr auto Kind = InstKind::BindSymbolicName.Define<Parse::NodeId>(
|
||||
{.ir_name = "bind_symbolic_name",
|
||||
.is_type = InstIsType::Maybe,
|
||||
.constant_kind = InstConstantKind::SymbolicOnly});
|
||||
|
||||
TypeId type_id;
|
||||
BindNameId bind_name_id;
|
||||
@@ -288,7 +294,7 @@ struct BindSymbolicName {
|
||||
struct BindValue {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::BindValue.Define<Parse::NodeId>("bind_value");
|
||||
InstKind::BindValue.Define<Parse::NodeId>({.ir_name = "bind_value"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId value_id;
|
||||
@@ -298,7 +304,7 @@ struct BindValue {
|
||||
struct BlockArg {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::BlockArg.Define<Parse::NodeId>("block_arg");
|
||||
InstKind::BlockArg.Define<Parse::NodeId>({.ir_name = "block_arg"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId block_id;
|
||||
@@ -306,8 +312,8 @@ struct BlockArg {
|
||||
|
||||
// A literal bool value, `true` or `false`.
|
||||
struct BoolLiteral {
|
||||
static constexpr auto Kind =
|
||||
InstKind::BoolLiteral.Define<Parse::NodeId>("bool_literal");
|
||||
static constexpr auto Kind = InstKind::BoolLiteral.Define<Parse::NodeId>(
|
||||
{.ir_name = "bool_literal", .constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
BoolValue value;
|
||||
@@ -316,8 +322,9 @@ struct BoolLiteral {
|
||||
// A bound method, that combines a function with the value to use for its
|
||||
// `self` parameter, such as `object.MethodName`.
|
||||
struct BoundMethod {
|
||||
static constexpr auto Kind =
|
||||
InstKind::BoundMethod.Define<Parse::NodeId>("bound_method");
|
||||
static constexpr auto Kind = InstKind::BoundMethod.Define<Parse::NodeId>(
|
||||
{.ir_name = "bound_method",
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
// The object argument in the bound method, which will be used to initialize
|
||||
@@ -342,8 +349,8 @@ struct AnyBranch {
|
||||
// Control flow to branch to the target block.
|
||||
struct Branch {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::Branch.Define<Parse::NodeId>("br", TerminatorKind::Terminator);
|
||||
static constexpr auto Kind = InstKind::Branch.Define<Parse::NodeId>(
|
||||
{.ir_name = "br", .terminator_kind = TerminatorKind::Terminator});
|
||||
|
||||
// Branches don't produce a value, so have no type.
|
||||
InstBlockId target_id;
|
||||
@@ -353,7 +360,7 @@ struct Branch {
|
||||
struct BranchIf {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind = InstKind::BranchIf.Define<Parse::NodeId>(
|
||||
"br", TerminatorKind::TerminatorSequence);
|
||||
{.ir_name = "br", .terminator_kind = TerminatorKind::TerminatorSequence});
|
||||
|
||||
// Branches don't produce a value, so have no type.
|
||||
InstBlockId target_id;
|
||||
@@ -365,7 +372,7 @@ struct BranchIf {
|
||||
struct BranchWithArg {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind = InstKind::BranchWithArg.Define<Parse::NodeId>(
|
||||
"br", TerminatorKind::Terminator);
|
||||
{.ir_name = "br", .terminator_kind = TerminatorKind::Terminator});
|
||||
|
||||
// Branches don't produce a value, so have no type.
|
||||
InstBlockId target_id;
|
||||
@@ -375,9 +382,12 @@ struct BranchWithArg {
|
||||
// A builtin instruction, corresponding to instructions like
|
||||
// InstId::BuiltinTypeType.
|
||||
struct BuiltinInst {
|
||||
// Builtin instructions don't have a parse node associated with them.
|
||||
// Builtins don't have a parse node associated with them.
|
||||
static constexpr auto Kind =
|
||||
InstKind::BuiltinInst.Define<Parse::InvalidNodeId>("builtin");
|
||||
InstKind::BuiltinInst.Define<Parse::InvalidNodeId>(
|
||||
{.ir_name = "builtin",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
BuiltinInstKind builtin_inst_kind;
|
||||
@@ -389,7 +399,8 @@ struct Call {
|
||||
// For a syntactic call, the parse node will be a CallExprStartId. However,
|
||||
// calls can arise from other syntaxes, such as operators and implicit
|
||||
// conversions.
|
||||
static constexpr auto Kind = InstKind::Call.Define<Parse::NodeId>("call");
|
||||
static constexpr auto Kind =
|
||||
InstKind::Call.Define<Parse::NodeId>({.ir_name = "call"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId callee_id;
|
||||
@@ -403,7 +414,8 @@ struct Call {
|
||||
// A class declaration.
|
||||
struct ClassDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ClassDecl.Define<Parse::AnyClassDeclId>("class_decl");
|
||||
InstKind::ClassDecl.Define<Parse::AnyClassDeclId>(
|
||||
{.ir_name = "class_decl"});
|
||||
|
||||
TypeId type_id;
|
||||
// TODO: For a generic class declaration, the name of the class declaration
|
||||
@@ -420,7 +432,7 @@ struct ClassElementAccess {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::ClassElementAccess.Define<Parse::NodeId>(
|
||||
"class_element_access");
|
||||
{.ir_name = "class_element_access"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId base_id;
|
||||
@@ -431,7 +443,7 @@ struct ClassElementAccess {
|
||||
struct ClassInit {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::ClassInit.Define<Parse::NodeId>("class_init");
|
||||
InstKind::ClassInit.Define<Parse::NodeId>({.ir_name = "class_init"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -440,8 +452,10 @@ struct ClassInit {
|
||||
|
||||
// The type for a class, either non-generic or parameterized generic instance.
|
||||
struct ClassType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ClassType.Define<Parse::NodeId>("class_type");
|
||||
static constexpr auto Kind = InstKind::ClassType.Define<Parse::NodeId>(
|
||||
{.ir_name = "class_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
ClassId class_id;
|
||||
@@ -451,7 +465,10 @@ struct ClassType {
|
||||
// Indicates `const` on a type, such as `var x: const i32`.
|
||||
struct ConstType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ConstType.Define<Parse::PrefixOperatorConstId>("const_type");
|
||||
InstKind::ConstType.Define<Parse::PrefixOperatorConstId>(
|
||||
{.ir_name = "const_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
TypeId inner_id;
|
||||
@@ -461,7 +478,7 @@ struct ConstType {
|
||||
// result.
|
||||
struct Converted {
|
||||
static constexpr auto Kind =
|
||||
InstKind::Converted.Define<Parse::NodeId>("converted");
|
||||
InstKind::Converted.Define<Parse::NodeId>({.ir_name = "converted"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId original_id;
|
||||
@@ -471,7 +488,8 @@ struct Converted {
|
||||
// The `*` dereference operator, as in `*pointer`.
|
||||
struct Deref {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind = InstKind::Deref.Define<Parse::NodeId>("deref");
|
||||
static constexpr auto Kind =
|
||||
InstKind::Deref.Define<Parse::NodeId>({.ir_name = "deref"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId pointer_id;
|
||||
@@ -480,7 +498,7 @@ struct Deref {
|
||||
// An `export bind_name` declaration.
|
||||
struct ExportDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ExportDecl.Define<Parse::NodeId>("export");
|
||||
InstKind::ExportDecl.Define<Parse::NodeId>({.ir_name = "export"});
|
||||
|
||||
TypeId type_id;
|
||||
BindNameId bind_name_id;
|
||||
@@ -491,8 +509,8 @@ struct ExportDecl {
|
||||
// Represents accessing the `type` field in a facet value, which is notionally a
|
||||
// pair of a type and a witness.
|
||||
struct FacetTypeAccess {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FacetTypeAccess.Define<Parse::NodeId>("facet_type_access");
|
||||
static constexpr auto Kind = InstKind::FacetTypeAccess.Define<Parse::NodeId>(
|
||||
{.ir_name = "facet_type_access"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId facet_id;
|
||||
@@ -502,7 +520,8 @@ struct FacetTypeAccess {
|
||||
// `FieldDecl` instruction is an `UnboundElementType`.
|
||||
struct FieldDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FieldDecl.Define<Parse::BindingPatternId>("field_decl");
|
||||
InstKind::FieldDecl.Define<Parse::BindingPatternId>(
|
||||
{.ir_name = "field_decl", .constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
NameId name_id;
|
||||
@@ -512,7 +531,9 @@ struct FieldDecl {
|
||||
// A literal floating point value.
|
||||
struct FloatLiteral {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FloatLiteral.Define<Parse::RealLiteralId>("float_literal");
|
||||
InstKind::FloatLiteral.Define<Parse::RealLiteralId>(
|
||||
{.ir_name = "float_literal",
|
||||
.constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
FloatId float_id;
|
||||
@@ -520,8 +541,10 @@ struct FloatLiteral {
|
||||
|
||||
// A floating point type.
|
||||
struct FloatType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FloatType.Define<Parse::NodeId>("float_type");
|
||||
static constexpr auto Kind = InstKind::FloatType.Define<Parse::NodeId>(
|
||||
{.ir_name = "float_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
// TODO: Consider adding a more compact way of representing either a small
|
||||
@@ -533,7 +556,7 @@ struct FloatType {
|
||||
struct FunctionDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FunctionDecl.Define<Parse::AnyFunctionDeclId>(
|
||||
"fn_decl", /*is_lowered=*/false);
|
||||
{.ir_name = "fn_decl", .is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
FunctionId function_id;
|
||||
@@ -545,7 +568,10 @@ struct FunctionDecl {
|
||||
// The type of a function.
|
||||
struct FunctionType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FunctionType.Define<Parse::AnyFunctionDeclId>("fn_type");
|
||||
InstKind::FunctionType.Define<Parse::AnyFunctionDeclId>(
|
||||
{.ir_name = "fn_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
FunctionId function_id;
|
||||
@@ -557,7 +583,9 @@ struct GenericClassType {
|
||||
// This is only ever created as a constant, so doesn't have a location.
|
||||
static constexpr auto Kind =
|
||||
InstKind::GenericClassType.Define<Parse::InvalidNodeId>(
|
||||
"generic_class_type");
|
||||
{.ir_name = "generic_class_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
ClassId class_id;
|
||||
@@ -569,7 +597,9 @@ struct GenericInterfaceType {
|
||||
// This is only ever created as a constant, so doesn't have a location.
|
||||
static constexpr auto Kind =
|
||||
InstKind::GenericInterfaceType.Define<Parse::InvalidNodeId>(
|
||||
"generic_interface_type");
|
||||
{.ir_name = "generic_interface_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
InterfaceId interface_id;
|
||||
@@ -578,7 +608,7 @@ struct GenericInterfaceType {
|
||||
// An `impl` declaration.
|
||||
struct ImplDecl {
|
||||
static constexpr auto Kind = InstKind::ImplDecl.Define<Parse::AnyImplDeclId>(
|
||||
"impl_decl", /*is_lowered=*/false);
|
||||
{.ir_name = "impl_decl", .is_lowered = false});
|
||||
|
||||
// No type: an impl declaration is not a value.
|
||||
ImplId impl_id;
|
||||
@@ -591,7 +621,7 @@ struct ImplDecl {
|
||||
// correspondence with actual `import`s isn't guaranteed.
|
||||
struct ImportDecl {
|
||||
static constexpr auto Kind = InstKind::ImportDecl.Define<Parse::ImportDeclId>(
|
||||
"import", /*is_lowered=*/false);
|
||||
{.ir_name = "import", .is_lowered = false});
|
||||
|
||||
NameId package_id;
|
||||
};
|
||||
@@ -613,7 +643,7 @@ struct ImportRefUnloaded {
|
||||
// No parse node: any parse node logic must use the referenced IR.
|
||||
static constexpr auto Kind =
|
||||
InstKind::ImportRefUnloaded.Define<Parse::InvalidNodeId>(
|
||||
"import_ref", /*is_lowered=*/false);
|
||||
{.ir_name = "import_ref", .is_lowered = false});
|
||||
|
||||
ImportIRInstId import_ir_inst_id;
|
||||
BindNameId bind_name_id;
|
||||
@@ -624,7 +654,7 @@ struct ImportRefLoaded {
|
||||
// No parse node: any parse node logic must use the referenced IR.
|
||||
static constexpr auto Kind =
|
||||
InstKind::ImportRefLoaded.Define<Parse::InvalidNodeId>(
|
||||
"import_ref", /*is_lowered=*/false);
|
||||
{.ir_name = "import_ref", .is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
ImportIRInstId import_ir_inst_id;
|
||||
@@ -636,8 +666,8 @@ struct ImportRefLoaded {
|
||||
// whose initialization is not in-place.
|
||||
struct InitializeFrom {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::InitializeFrom.Define<Parse::NodeId>("initialize_from");
|
||||
static constexpr auto Kind = InstKind::InitializeFrom.Define<Parse::NodeId>(
|
||||
{.ir_name = "initialize_from"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId src_id;
|
||||
@@ -648,7 +678,7 @@ struct InitializeFrom {
|
||||
struct InterfaceDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::InterfaceDecl.Define<Parse::AnyInterfaceDeclId>(
|
||||
"interface_decl", /*is_lowered=*/false);
|
||||
{.ir_name = "interface_decl", .is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
// TODO: For a generic interface declaration, the name of the interface
|
||||
@@ -662,8 +692,10 @@ struct InterfaceDecl {
|
||||
// The type for an interface, either non-generic or parameterized generic
|
||||
// instance.
|
||||
struct InterfaceType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::InterfaceType.Define<Parse::NodeId>("interface_type");
|
||||
static constexpr auto Kind = InstKind::InterfaceType.Define<Parse::NodeId>(
|
||||
{.ir_name = "interface_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
InterfaceId interface_id;
|
||||
@@ -674,7 +706,9 @@ struct InterfaceType {
|
||||
struct InterfaceWitness {
|
||||
static constexpr auto Kind =
|
||||
InstKind::InterfaceWitness.Define<Parse::InvalidNodeId>(
|
||||
"interface_witness", /*is_lowered=*/false);
|
||||
{.ir_name = "interface_witness",
|
||||
.constant_kind = InstConstantKind::Conditional,
|
||||
.is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -684,7 +718,10 @@ struct InterfaceWitness {
|
||||
struct InterfaceWitnessAccess {
|
||||
static constexpr auto Kind =
|
||||
InstKind::InterfaceWitnessAccess.Define<Parse::InvalidNodeId>(
|
||||
"interface_witness_access", /*is_lowered=*/false);
|
||||
{.ir_name = "interface_witness_access",
|
||||
.is_type = InstIsType::Maybe,
|
||||
.constant_kind = InstConstantKind::SymbolicOnly,
|
||||
.is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
InstId witness_id;
|
||||
@@ -694,8 +731,8 @@ struct InterfaceWitnessAccess {
|
||||
// A literal integer value.
|
||||
struct IntLiteral {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::IntLiteral.Define<Parse::NodeId>("int_literal");
|
||||
static constexpr auto Kind = InstKind::IntLiteral.Define<Parse::NodeId>(
|
||||
{.ir_name = "int_literal", .constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
IntId int_id;
|
||||
@@ -703,8 +740,10 @@ struct IntLiteral {
|
||||
|
||||
// An integer type.
|
||||
struct IntType {
|
||||
static constexpr auto Kind =
|
||||
InstKind::IntType.Define<Parse::NodeId>("int_type");
|
||||
static constexpr auto Kind = InstKind::IntType.Define<Parse::NodeId>(
|
||||
{.ir_name = "int_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
IntKind int_kind;
|
||||
@@ -718,7 +757,7 @@ struct IntType {
|
||||
struct NameRef {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::NameRef.Define<Parse::NodeId>("name_ref");
|
||||
InstKind::NameRef.Define<Parse::NodeId>({.ir_name = "name_ref"});
|
||||
|
||||
TypeId type_id;
|
||||
NameId name_id;
|
||||
@@ -728,7 +767,8 @@ struct NameRef {
|
||||
// A namespace declaration.
|
||||
struct Namespace {
|
||||
static constexpr auto Kind =
|
||||
InstKind::Namespace.Define<Parse::AnyNamespaceId>("namespace");
|
||||
InstKind::Namespace.Define<Parse::AnyNamespaceId>(
|
||||
{.ir_name = "namespace", .constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
NameScopeId name_scope_id;
|
||||
@@ -740,7 +780,8 @@ struct Namespace {
|
||||
// A parameter for a function or other parameterized block.
|
||||
struct Param {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind = InstKind::Param.Define<Parse::NodeId>("param");
|
||||
static constexpr auto Kind =
|
||||
InstKind::Param.Define<Parse::NodeId>({.ir_name = "param"});
|
||||
|
||||
TypeId type_id;
|
||||
NameId name_id;
|
||||
@@ -750,8 +791,10 @@ struct Param {
|
||||
// `x: i32*`, where `pointee_id` is `i32` and `type_id` is `type`.
|
||||
struct PointerType {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::PointerType.Define<Parse::NodeId>("ptr_type");
|
||||
static constexpr auto Kind = InstKind::PointerType.Define<Parse::NodeId>(
|
||||
{.ir_name = "ptr_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
TypeId pointee_id;
|
||||
@@ -761,7 +804,7 @@ struct Return {
|
||||
static constexpr auto Kind =
|
||||
InstKind::Return.Define<Parse::NodeIdOneOf<Parse::FunctionDefinitionId,
|
||||
Parse::ReturnStatementId>>(
|
||||
"return", TerminatorKind::Terminator);
|
||||
{.ir_name = "return", .terminator_kind = TerminatorKind::Terminator});
|
||||
|
||||
// This is a statement, so has no type.
|
||||
};
|
||||
@@ -770,7 +813,7 @@ struct Return {
|
||||
struct ReturnExpr {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ReturnExpr.Define<Parse::ReturnStatementId>(
|
||||
"return", TerminatorKind::Terminator);
|
||||
{.ir_name = "return", .terminator_kind = TerminatorKind::Terminator});
|
||||
|
||||
// This is a statement, so has no type.
|
||||
InstId expr_id;
|
||||
@@ -785,7 +828,7 @@ struct ReturnExpr {
|
||||
struct SpliceBlock {
|
||||
// TODO: Can we make Parse::NodeId more specific?
|
||||
static constexpr auto Kind =
|
||||
InstKind::SpliceBlock.Define<Parse::NodeId>("splice_block");
|
||||
InstKind::SpliceBlock.Define<Parse::NodeId>({.ir_name = "splice_block"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId block_id;
|
||||
@@ -795,7 +838,9 @@ struct SpliceBlock {
|
||||
// A literal string value.
|
||||
struct StringLiteral {
|
||||
static constexpr auto Kind =
|
||||
InstKind::StringLiteral.Define<Parse::StringLiteralId>("string_literal");
|
||||
InstKind::StringLiteral.Define<Parse::StringLiteralId>(
|
||||
{.ir_name = "string_literal",
|
||||
.constant_kind = InstConstantKind::Always});
|
||||
|
||||
TypeId type_id;
|
||||
StringLiteralValueId string_literal_id;
|
||||
@@ -804,8 +849,8 @@ struct StringLiteral {
|
||||
// Access to a struct type, with the index into the struct_id representation.
|
||||
struct StructAccess {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::StructAccess.Define<Parse::NodeId>("struct_access");
|
||||
static constexpr auto Kind = InstKind::StructAccess.Define<Parse::NodeId>(
|
||||
{.ir_name = "struct_access"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId struct_id;
|
||||
@@ -816,7 +861,7 @@ struct StructAccess {
|
||||
struct StructInit {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::StructInit.Define<Parse::NodeId>("struct_init");
|
||||
InstKind::StructInit.Define<Parse::NodeId>({.ir_name = "struct_init"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -826,7 +871,8 @@ struct StructInit {
|
||||
// A literal struct value, such as `{.a = 1, .b = 2}`.
|
||||
struct StructLiteral {
|
||||
static constexpr auto Kind =
|
||||
InstKind::StructLiteral.Define<Parse::StructLiteralId>("struct_literal");
|
||||
InstKind::StructLiteral.Define<Parse::StructLiteralId>(
|
||||
{.ir_name = "struct_literal"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -836,8 +882,10 @@ struct StructLiteral {
|
||||
struct StructType {
|
||||
// TODO: Make this more specific. It can be one of: ClassDefinitionId,
|
||||
// StructLiteralId, StructTypeLiteralId
|
||||
static constexpr auto Kind =
|
||||
InstKind::StructType.Define<Parse::NodeId>("struct_type");
|
||||
static constexpr auto Kind = InstKind::StructType.Define<Parse::NodeId>(
|
||||
{.ir_name = "struct_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId fields_id;
|
||||
@@ -850,8 +898,9 @@ struct StructType {
|
||||
// instruction has no type.
|
||||
struct StructTypeField {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::StructTypeField.Define<Parse::NodeId>("struct_type_field");
|
||||
static constexpr auto Kind = InstKind::StructTypeField.Define<Parse::NodeId>(
|
||||
{.ir_name = "struct_type_field",
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
NameId name_id;
|
||||
TypeId field_type_id;
|
||||
@@ -860,8 +909,9 @@ struct StructTypeField {
|
||||
// A struct value.
|
||||
struct StructValue {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::StructValue.Define<Parse::NodeId>("struct_value");
|
||||
static constexpr auto Kind = InstKind::StructValue.Define<Parse::NodeId>(
|
||||
{.ir_name = "struct_value",
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -871,7 +921,7 @@ struct StructValue {
|
||||
struct Temporary {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::Temporary.Define<Parse::NodeId>("temporary");
|
||||
InstKind::Temporary.Define<Parse::NodeId>({.ir_name = "temporary"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId storage_id;
|
||||
@@ -881,8 +931,8 @@ struct Temporary {
|
||||
// Storage for a temporary value.
|
||||
struct TemporaryStorage {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::TemporaryStorage.Define<Parse::NodeId>("temporary_storage");
|
||||
static constexpr auto Kind = InstKind::TemporaryStorage.Define<Parse::NodeId>(
|
||||
{.ir_name = "temporary_storage"});
|
||||
|
||||
TypeId type_id;
|
||||
};
|
||||
@@ -894,7 +944,7 @@ struct TemporaryStorage {
|
||||
struct TupleAccess {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::TupleAccess.Define<Parse::NodeId>("tuple_access");
|
||||
InstKind::TupleAccess.Define<Parse::NodeId>({.ir_name = "tuple_access"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId tuple_id;
|
||||
@@ -903,8 +953,8 @@ struct TupleAccess {
|
||||
|
||||
// Access to a tuple member by index, such as `tuple[index]`.
|
||||
struct TupleIndex {
|
||||
static constexpr auto Kind =
|
||||
InstKind::TupleIndex.Define<Parse::IndexExprId>("tuple_index");
|
||||
static constexpr auto Kind = InstKind::TupleIndex.Define<Parse::IndexExprId>(
|
||||
{.ir_name = "tuple_index"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId tuple_id;
|
||||
@@ -915,7 +965,7 @@ struct TupleIndex {
|
||||
struct TupleInit {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::TupleInit.Define<Parse::NodeId>("tuple_init");
|
||||
InstKind::TupleInit.Define<Parse::NodeId>({.ir_name = "tuple_init"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -925,7 +975,8 @@ struct TupleInit {
|
||||
// A literal tuple value.
|
||||
struct TupleLiteral {
|
||||
static constexpr auto Kind =
|
||||
InstKind::TupleLiteral.Define<Parse::TupleLiteralId>("tuple_literal");
|
||||
InstKind::TupleLiteral.Define<Parse::TupleLiteralId>(
|
||||
{.ir_name = "tuple_literal"});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -934,8 +985,10 @@ struct TupleLiteral {
|
||||
// The type of a tuple.
|
||||
struct TupleType {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::TupleType.Define<Parse::NodeId>("tuple_type");
|
||||
static constexpr auto Kind = InstKind::TupleType.Define<Parse::NodeId>(
|
||||
{.ir_name = "tuple_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
TypeBlockId elements_id;
|
||||
@@ -944,8 +997,9 @@ struct TupleType {
|
||||
// A tuple value.
|
||||
struct TupleValue {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::TupleValue.Define<Parse::NodeId>("tuple_value");
|
||||
static constexpr auto Kind = InstKind::TupleValue.Define<Parse::NodeId>(
|
||||
{.ir_name = "tuple_value",
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
InstBlockId elements_id;
|
||||
@@ -955,7 +1009,7 @@ struct TupleValue {
|
||||
struct UnaryOperatorNot {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::UnaryOperatorNot.Define<Parse::NodeId>("not");
|
||||
InstKind::UnaryOperatorNot.Define<Parse::NodeId>({.ir_name = "not"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId operand_id;
|
||||
@@ -967,7 +1021,9 @@ struct UnaryOperatorNot {
|
||||
struct UnboundElementType {
|
||||
static constexpr auto Kind = InstKind::UnboundElementType.Define<
|
||||
Parse::NodeIdOneOf<Parse::BaseDeclId, Parse::BindingPatternId>>(
|
||||
"unbound_element_type");
|
||||
{.ir_name = "unbound_element_type",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::Conditional});
|
||||
|
||||
TypeId type_id;
|
||||
// The class that a value of this type is an element of.
|
||||
@@ -981,8 +1037,8 @@ struct UnboundElementType {
|
||||
// example, when indexing a value expression of array type, this is used to
|
||||
// form a reference to the array object.
|
||||
struct ValueAsRef {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ValueAsRef.Define<Parse::IndexExprId>("value_as_ref");
|
||||
static constexpr auto Kind = InstKind::ValueAsRef.Define<Parse::IndexExprId>(
|
||||
{.ir_name = "value_as_ref"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId value_id;
|
||||
@@ -995,7 +1051,7 @@ struct ValueOfInitializer {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::ValueOfInitializer.Define<Parse::NodeId>(
|
||||
"value_of_initializer");
|
||||
{.ir_name = "value_of_initializer"});
|
||||
|
||||
TypeId type_id;
|
||||
InstId init_id;
|
||||
@@ -1005,7 +1061,7 @@ struct ValueOfInitializer {
|
||||
struct VarStorage {
|
||||
// TODO: Make Parse::NodeId more specific.
|
||||
static constexpr auto Kind =
|
||||
InstKind::VarStorage.Define<Parse::NodeId>("var");
|
||||
InstKind::VarStorage.Define<Parse::NodeId>({.ir_name = "var"});
|
||||
|
||||
TypeId type_id;
|
||||
NameId name_id;
|
||||
|
||||
Reference in New Issue
Block a user