diff --git a/.clang-format b/.clang-format index 15576c473583..2ac9b9c93ee2 100644 --- a/.clang-format +++ b/.clang-format @@ -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'] diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index cfaa2f38bc79..a78a59bc049e 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -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 + 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 + requires(InstT::Kind.template IsAnyOf()) + 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 + requires(InstT::Kind.template IsAnyOf()) + 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 + 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); - } } } diff --git a/toolchain/lower/constant.cpp b/toolchain/lower/constant.cpp index a85637aed606..0ec0f4cb2019 100644 --- a/toolchain/lower/constant.cpp +++ b/toolchain/lower/constant.cpp @@ -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 EmitAsConstant(ConstantContext& context, -// SemIR:: 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 @@ -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 + 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 + 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( context, inst.elements_id, cast(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(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 diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 2352fc5cade0..44cabf76fa2d 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -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(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 subtypes; - subtypes.reserve(fields.size()); - for (auto field_id : fields) { - auto field = sem_ir_->insts().GetAs(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 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 + 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(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 subtypes; + subtypes.reserve(fields.size()); + for (auto field_id : fields) { + auto field = + context.sem_ir().insts().GetAs(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 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 + 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 + requires(InstT::Kind.template IsAnyOf()) +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); } } diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index a6d45b33fdeb..e42332472bc2 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -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_; diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 7e7d720ae440..2259cca1e919 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -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* { diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index ed457f45aaa8..0900c29d98fc 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -159,9 +159,8 @@ class FunctionContext { Map 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; diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index b4a7cf3349d6..6ea8b8b19509 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -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(); diff --git a/toolchain/lower/handle_type.cpp b/toolchain/lower/handle_type.cpp deleted file mode 100644 index cae0dafb4c6d..000000000000 --- a/toolchain/lower/handle_type.cpp +++ /dev/null @@ -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 diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index acf5a5b58da3..274bbfe5b94f 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -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 diff --git a/toolchain/sem_ir/inst_kind.cpp b/toolchain/sem_ir/inst_kind.cpp index 8da678048866..119cb470935e 100644 --- a/toolchain/sem_ir/inst_kind.cpp +++ b/toolchain/sem_ir/inst_kind.cpp @@ -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 diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 514e420ce2ce..d12fd01ada0a 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -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 diff --git a/toolchain/sem_ir/inst_kind.h b/toolchain/sem_ir/inst_kind.h index 1adb4c2bb96f..0dbbb6463120 100644 --- a/toolchain/sem_ir/inst_kind.h +++ b/toolchain/sem_ir/inst_kind.h @@ -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 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 - constexpr auto Define( - llvm::StringLiteral ir_name, - TerminatorKind terminator_kind = TerminatorKind::NotTerminator) const - -> Definition; - - // As above, but for instructions which are not lowered; `Define(ir_name)` - // should be used instead of specifying `is_lowered = true`. - template - constexpr auto Define(llvm::StringLiteral ir_name, bool is_lowered) const - -> Definition; + constexpr auto Define(DefinitionInfo info) const -> Definition; 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 + 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 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 -constexpr auto InstKind::Define(llvm::StringLiteral ir_name, - TerminatorKind terminator_kind) const +constexpr auto InstKind::Define(DefinitionInfo info) const -> Definition { - return Definition(*this, ir_name, terminator_kind, - /*is_lowered=*/true); -} - -template -constexpr auto InstKind::Define(llvm::StringLiteral ir_name, - bool is_lowered) const - -> Definition { - CARBON_CHECK(!is_lowered) << "Use Define(ir_name) instead"; - return Definition(*this, ir_name, TerminatorKind::NotTerminator, - /*is_lowered=*/is_lowered); + return Definition(*this, info); } } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 3537baf50875..a5e9e93c7d2d 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -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( - "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("addr_of"); + static constexpr auto Kind = InstKind::AddrOf.Define( + {.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("addr_pattern"); + InstKind::AddrPattern.Define({.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("array_index"); + InstKind::ArrayIndex.Define({.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("array_init"); + InstKind::ArrayInit.Define({.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("array_type"); + static constexpr auto Kind = InstKind::ArrayType.Define( + {.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("as_compatible"); + static constexpr auto Kind = InstKind::AsCompatible.Define( + {.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>( - "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( - "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("assoc_entity"); + static constexpr auto Kind = InstKind::AssociatedEntity.Define( + {.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( - "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("base_decl"); + static constexpr auto Kind = InstKind::BaseDecl.Define( + {.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("bind_alias"); + InstKind::BindAlias.Define({.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("bind_name"); + InstKind::BindName.Define({.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("bind_symbolic_name"); + static constexpr auto Kind = InstKind::BindSymbolicName.Define( + {.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("bind_value"); + InstKind::BindValue.Define({.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("block_arg"); + InstKind::BlockArg.Define({.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("bool_literal"); + static constexpr auto Kind = InstKind::BoolLiteral.Define( + {.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("bound_method"); + static constexpr auto Kind = InstKind::BoundMethod.Define( + {.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("br", TerminatorKind::Terminator); + static constexpr auto Kind = InstKind::Branch.Define( + {.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( - "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( - "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("builtin"); + InstKind::BuiltinInst.Define( + {.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("call"); + static constexpr auto Kind = + InstKind::Call.Define({.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("class_decl"); + InstKind::ClassDecl.Define( + {.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( - "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("class_init"); + InstKind::ClassInit.Define({.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("class_type"); + static constexpr auto Kind = InstKind::ClassType.Define( + {.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("const_type"); + InstKind::ConstType.Define( + {.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("converted"); + InstKind::Converted.Define({.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("deref"); + static constexpr auto Kind = + InstKind::Deref.Define({.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("export"); + InstKind::ExportDecl.Define({.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("facet_type_access"); + static constexpr auto Kind = InstKind::FacetTypeAccess.Define( + {.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("field_decl"); + InstKind::FieldDecl.Define( + {.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("float_literal"); + InstKind::FloatLiteral.Define( + {.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("float_type"); + static constexpr auto Kind = InstKind::FloatType.Define( + {.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( - "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("fn_type"); + InstKind::FunctionType.Define( + {.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( - "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( - "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( - "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( - "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( - "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( - "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("initialize_from"); + static constexpr auto Kind = InstKind::InitializeFrom.Define( + {.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( - "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("interface_type"); + static constexpr auto Kind = InstKind::InterfaceType.Define( + {.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( - "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( - "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("int_literal"); + static constexpr auto Kind = InstKind::IntLiteral.Define( + {.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("int_type"); + static constexpr auto Kind = InstKind::IntType.Define( + {.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("name_ref"); + InstKind::NameRef.Define({.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("namespace"); + InstKind::Namespace.Define( + {.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("param"); + static constexpr auto Kind = + InstKind::Param.Define({.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("ptr_type"); + static constexpr auto Kind = InstKind::PointerType.Define( + {.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>( - "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( - "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("splice_block"); + InstKind::SpliceBlock.Define({.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("string_literal"); + InstKind::StringLiteral.Define( + {.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("struct_access"); + static constexpr auto Kind = InstKind::StructAccess.Define( + {.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("struct_init"); + InstKind::StructInit.Define({.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("struct_literal"); + InstKind::StructLiteral.Define( + {.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("struct_type"); + static constexpr auto Kind = InstKind::StructType.Define( + {.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("struct_type_field"); + static constexpr auto Kind = InstKind::StructTypeField.Define( + {.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("struct_value"); + static constexpr auto Kind = InstKind::StructValue.Define( + {.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("temporary"); + InstKind::Temporary.Define({.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("temporary_storage"); + static constexpr auto Kind = InstKind::TemporaryStorage.Define( + {.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("tuple_access"); + InstKind::TupleAccess.Define({.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("tuple_index"); + static constexpr auto Kind = InstKind::TupleIndex.Define( + {.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("tuple_init"); + InstKind::TupleInit.Define({.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("tuple_literal"); + InstKind::TupleLiteral.Define( + {.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("tuple_type"); + static constexpr auto Kind = InstKind::TupleType.Define( + {.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("tuple_value"); + static constexpr auto Kind = InstKind::TupleValue.Define( + {.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("not"); + InstKind::UnaryOperatorNot.Define({.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>( - "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("value_as_ref"); + static constexpr auto Kind = InstKind::ValueAsRef.Define( + {.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( - "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("var"); + InstKind::VarStorage.Define({.ir_name = "var"}); TypeId type_id; NameId name_id;