From 75bbfb3f908fe8f560b126dd973f9cd3e048e747 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Tue, 25 Mar 2025 13:31:08 -0700 Subject: [PATCH] Refactor StringifyTypeExpr to use overloads (#5180) StringifyTypeExpr has gotten a little long. I know there's some preference for the effects of explicitly handling cases, but maybe it's okay to adopt an overload pattern similar to what we do elsewhere? Note I'm trying to force types to provide overloads, as a case which are clearly intended to be handled. Also, perhaps subtly, the list of "singleton" instructions previously included Vtable, which is not a singleton. With this change, which instead directly handles singleton instructions by `requires(IsSingletonInstKind(InstT::Kind))`, Vtable will switch default handling. But it's not directly printed for types, so there is no net impact on IR. At present I have this split from `StepStack` because with these changes it'd no longer be a simple stack. It felt like maybe the type separation would help readers. --- toolchain/sem_ir/stringify_type.cpp | 967 +++++++++++++--------------- 1 file changed, 465 insertions(+), 502 deletions(-) diff --git a/toolchain/sem_ir/stringify_type.cpp b/toolchain/sem_ir/stringify_type.cpp index 40bc1d63d3fc..2c24ad8f67d0 100644 --- a/toolchain/sem_ir/stringify_type.cpp +++ b/toolchain/sem_ir/stringify_type.cpp @@ -8,6 +8,9 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/inst_kind.h" +#include "toolchain/sem_ir/singleton_insts.h" +#include "toolchain/sem_ir/struct_type_field.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" @@ -29,6 +32,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { } namespace { + // Contains the stack of steps for `StringifyTypeExpr`. class StepStack { public: @@ -151,6 +155,460 @@ class StepStack { // Remaining steps to take. llvm::SmallVector steps_; }; + +// Provides `StringifyTypeInst` overloads for each instruction. +class Stringifier { + public: + explicit Stringifier(const SemIR::File* sem_ir, StepStack* step_stack, + llvm::raw_ostream* out) + : sem_ir_(sem_ir), step_stack_(step_stack), out_(out) {} + + // By default try to print a constant, but otherwise may fail to + // stringify. + auto StringifyTypeInstDefault(SemIR::InstId inst_id, Inst inst) -> void { + // We don't know how to print this instruction, but it might have a + // constant value that we can print. + auto const_inst_id = sem_ir_->constant_values().GetConstantInstId(inst_id); + if (const_inst_id.has_value() && const_inst_id != inst_id) { + step_stack_->PushInstId(const_inst_id); + return; + } + + // We don't need to handle stringification for instructions that don't + // show up in errors, but make it clear what's going on so that it's + // clearer when stringification is needed. + *out_ << ""; + } + + template + auto StringifyTypeInst(SemIR::InstId inst_id, InstT inst) -> void { + // This doesn't use requires so that more specific overloads are chosen when + // provided. + static_assert(InstT::Kind.is_type() != InstIsType::Always || + std::same_as, + "Types should have a dedicated overload"); + StringifyTypeInstDefault(inst_id, inst); + } + + // Singleton instructions use their IR name as a label. + template + requires(IsSingletonInstKind(InstT::Kind)) + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, InstT /*inst*/) -> void { + *out_ << InstT::Kind.ir_name(); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ArrayType inst) -> void { + *out_ << "["; + step_stack_->PushString("]"); + step_stack_->PushInstId(inst.bound_id); + step_stack_->PushString("; "); + step_stack_->PushTypeId(inst.element_type_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, AssociatedConstantDecl inst) + -> void { + const auto& assoc_const = + sem_ir_->associated_constants().Get(inst.assoc_const_id); + step_stack_->PushQualifiedName(assoc_const.parent_scope_id, + assoc_const.name_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, AssociatedEntityType inst) + -> void { + *out_ << "PushString(">"); + step_stack_->PushTypeId(inst.interface_type_id); + } + + template + requires(std::same_as || + std::same_as || + std::same_as) + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, InstT inst) -> void { + auto name_id = inst.entity_name_id; + step_stack_->PushEntityName(name_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ClassType inst) -> void { + const auto& class_info = sem_ir_->classes().Get(inst.class_id); + if (auto literal_info = NumericTypeLiteralInfo::ForType(*sem_ir_, inst); + literal_info.is_valid()) { + literal_info.PrintLiteral(*sem_ir_, *out_); + return; + } + step_stack_->PushEntityName(class_info, inst.specific_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ConstType inst) -> void { + *out_ << "const "; + + // Add parentheses if required. + auto inner_type_inst_id = sem_ir_->types().GetInstId(inst.inner_id); + if (GetTypePrecedence(sem_ir_->insts().Get(inner_type_inst_id).kind()) < + GetTypePrecedence(SemIR::ConstType::Kind)) { + *out_ << "("; + step_stack_->PushString(")"); + } + + step_stack_->PushInstId(inner_type_inst_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FacetAccessType inst) + -> void { + // Given `T:! I`, print `T as type` as simply `T`. + step_stack_->PushInstId(inst.facet_value_inst_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FacetAccessWitness inst) + -> void { + *out_ << "PushString(">"); + step_stack_->PushElementIndex(inst.index); + step_stack_->PushString(", interface "); + step_stack_->PushInstId(inst.facet_value_inst_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FacetType inst) -> void { + const FacetTypeInfo& facet_type_info = + sem_ir_->facet_types().Get(inst.facet_type_id); + // Output `where` restrictions. + bool some_where = false; + if (facet_type_info.other_requirements) { + step_stack_->PushString("..."); + some_where = true; + } + for (auto rewrite : llvm::reverse(facet_type_info.rewrite_constraints)) { + if (some_where) { + step_stack_->PushString(" and"); + } + step_stack_->PushInstId( + sem_ir_->constant_values().GetInstId(rewrite.rhs_const_id)); + step_stack_->PushString(" = "); + step_stack_->PushInstId( + sem_ir_->constant_values().GetInstId(rewrite.lhs_const_id)); + step_stack_->PushString(" "); + some_where = true; + } + // TODO: Other restrictions from facet_type_info. + if (some_where) { + step_stack_->PushString(" where"); + } + + // Output interface requirements. + if (facet_type_info.impls_constraints.empty()) { + step_stack_->PushString("type"); + return; + } + for (auto index : + llvm::reverse(llvm::seq(facet_type_info.impls_constraints.size()))) { + const auto& impls = facet_type_info.impls_constraints[index]; + const auto& interface_info = + sem_ir_->interfaces().Get(impls.interface_id); + step_stack_->PushEntityName(interface_info, impls.specific_id); + if (index > 0) { + step_stack_->PushString(" & "); + } + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FacetValue inst) -> void { + // No need to output the witness. + step_stack_->PushTypeId(inst.type_id); + step_stack_->PushString(" as "); + step_stack_->PushInstId(inst.type_inst_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FloatType inst) -> void { + // TODO: Is this okay? + if (auto width_value = + sem_ir_->insts().TryGetAs(inst.bit_width_id)) { + *out_ << "f"; + sem_ir_->ints().Get(width_value->int_id).print(*out_, /*isSigned=*/false); + } else { + *out_ << "Core.Float("; + step_stack_->PushString(")"); + step_stack_->PushInstId(inst.bit_width_id); + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FunctionType inst) -> void { + const auto& fn = sem_ir_->functions().Get(inst.function_id); + *out_ << "PushString(">"); + step_stack_->PushQualifiedName(fn.parent_scope_id, fn.name_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, + FunctionTypeWithSelfType inst) -> void { + *out_ << "PushString(">"); + step_stack_->PushInstId(inst.self_id); + step_stack_->PushString(" in "); + if (auto fn_inst = sem_ir_->insts().TryGetAs( + inst.interface_function_type_id)) { + const auto& fn = sem_ir_->functions().Get(fn_inst->function_id); + step_stack_->PushQualifiedName(fn.parent_scope_id, fn.name_id); + } else { + step_stack_->PushInstId(inst.interface_function_type_id); + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, GenericClassType inst) + -> void { + const auto& class_info = sem_ir_->classes().Get(inst.class_id); + *out_ << "PushString(">"); + step_stack_->PushQualifiedName(class_info.parent_scope_id, + class_info.name_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, GenericInterfaceType inst) + -> void { + const auto& interface = sem_ir_->interfaces().Get(inst.interface_id); + *out_ << "PushString(">"); + step_stack_->PushQualifiedName(interface.parent_scope_id, + interface.name_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ImplWitnessAccess inst) + -> void { + auto witness_inst_id = + sem_ir_->constant_values().GetConstantInstId(inst.witness_id); + auto witness = sem_ir_->insts().GetAs(witness_inst_id); + auto witness_type_id = + sem_ir_->insts().Get(witness.facet_value_inst_id).type_id(); + auto facet_type = sem_ir_->types().GetAs(witness_type_id); + step_stack_->PushString(")"); + // TODO: Support != 1 interface better. + if (auto impls_constraint = sem_ir_->facet_types() + .Get(facet_type.facet_type_id) + .TryAsSingleInterface()) { + const auto& interface = + sem_ir_->interfaces().Get(impls_constraint->interface_id); + auto entities = + sem_ir_->inst_blocks().Get(interface.associated_entities_id); + size_t index = inst.index.index; + CARBON_CHECK(index < entities.size(), "Access out of bounds."); + auto entity_inst_id = entities[index]; + if (auto associated_const = + sem_ir_->insts().TryGetAs( + entity_inst_id)) { + step_stack_->PushNameId(sem_ir_->associated_constants() + .Get(associated_const->assoc_const_id) + .name_id); + } else if (auto function_decl = + sem_ir_->insts().TryGetAs(entity_inst_id)) { + const auto& function = + sem_ir_->functions().Get(function_decl->function_id); + step_stack_->PushNameId(function.name_id); + } else { + step_stack_->PushInstId(entity_inst_id); + } + step_stack_->PushString("."); + step_stack_->PushEntityName(interface, impls_constraint->specific_id); + step_stack_->PushString(".("); + } else { + step_stack_->PushTypeId(witness_type_id); + step_stack_->PushString(".(TODO: "); + } + + bool period_self = false; + if (auto sym_name = sem_ir_->insts().TryGetAs( + witness.facet_value_inst_id)) { + auto name_id = + sem_ir_->entity_names().Get(sym_name->entity_name_id).name_id; + period_self = (name_id == SemIR::NameId::PeriodSelf); + } + if (!period_self) { + step_stack_->PushInstId(witness.facet_value_inst_id); + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ImportRefUnloaded inst) + -> void { + if (inst.entity_name_id.has_value()) { + step_stack_->PushEntityName(inst.entity_name_id); + } else { + *out_ << ""; + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, IntType inst) -> void { + *out_ << "PushString(">"); + if (auto width_value = + sem_ir_->insts().TryGetAs(inst.bit_width_id)) { + *out_ << (inst.int_kind.is_signed() ? "i" : "u"); + sem_ir_->ints().Get(width_value->int_id).print(*out_, /*isSigned=*/false); + } else { + *out_ << (inst.int_kind.is_signed() ? "Int(" : "UInt("); + step_stack_->PushString(")"); + step_stack_->PushInstId(inst.bit_width_id); + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, IntValue inst) -> void { + sem_ir_->ints().Get(inst.int_id).print(*out_, /*isSigned=*/true); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, NameRef inst) -> void { + *out_ << sem_ir_->names().GetFormatted(inst.name_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, Namespace inst) -> void { + const auto& name_scope = sem_ir_->name_scopes().Get(inst.name_scope_id); + step_stack_->PushQualifiedName(name_scope.parent_scope_id(), + name_scope.name_id()); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, PointerType inst) -> void { + step_stack_->PushString("*"); + step_stack_->PushTypeId(inst.pointee_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, SpecificFunction inst) + -> void { + auto callee = SemIR::GetCalleeFunction(*sem_ir_, inst.callee_id); + if (callee.function_id.has_value()) { + step_stack_->PushEntityName(sem_ir_->functions().Get(callee.function_id), + inst.specific_id); + } else { + step_stack_->PushString(""); + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, SpecificImplFunction inst) + -> void { + auto callee = SemIR::GetCalleeFunction(*sem_ir_, inst.callee_id); + if (callee.function_id.has_value()) { + // TODO: The specific_id here is for the interface member, but the + // entity we're passing is the impl member. This might result in + // strange output once we render specific arguments properly. + step_stack_->PushEntityName(sem_ir_->functions().Get(callee.function_id), + inst.specific_id); + } else { + step_stack_->PushString(""); + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, StructType inst) -> void { + auto fields = sem_ir_->struct_type_fields().Get(inst.fields_id); + if (fields.empty()) { + *out_ << "{}"; + return; + } + *out_ << "{"; + step_stack_->PushString("}"); + for (auto index : llvm::reverse(llvm::seq(fields.size()))) { + const auto& field = fields[index]; + step_stack_->PushTypeId(field.type_id); + step_stack_->PushString(": "); + step_stack_->PushNameId(field.name_id); + step_stack_->PushString("."); + if (index > 0) { + step_stack_->PushString(", "); + } + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, StructValue inst) -> void { + auto field_values = sem_ir_->inst_blocks().Get(inst.elements_id); + if (field_values.empty()) { + *out_ << "{}"; + return; + } + auto struct_type = sem_ir_->types().GetAs( + sem_ir_->types().GetObjectRepr(inst.type_id)); + auto fields = sem_ir_->struct_type_fields().Get(struct_type.fields_id); + if (fields.size() != field_values.size()) { + *out_ << "{}"; + return; + } + *out_ << "{"; + step_stack_->PushString("}"); + for (auto index : llvm::reverse(llvm::seq(fields.size()))) { + SemIR::InstId value_inst_id = field_values[index]; + step_stack_->PushInstId(value_inst_id); + step_stack_->PushString(" = "); + step_stack_->PushNameId(fields[index].name_id); + step_stack_->PushString("."); + if (index > 0) { + step_stack_->PushString(", "); + } + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, TupleType inst) -> void { + auto refs = sem_ir_->type_blocks().Get(inst.elements_id); + if (refs.empty()) { + *out_ << "()"; + return; + } + *out_ << "("; + step_stack_->PushString(")"); + // A tuple of one element has a comma to disambiguate from an + // expression. + if (refs.size() == 1) { + step_stack_->PushString(","); + } + for (auto i : llvm::reverse(llvm::seq(refs.size()))) { + step_stack_->PushTypeId(refs[i]); + if (i > 0) { + step_stack_->PushString(", "); + } + } + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, TupleValue inst) -> void { + auto refs = sem_ir_->inst_blocks().Get(inst.elements_id); + if (refs.empty()) { + *out_ << "()"; + return; + } + *out_ << "("; + step_stack_->PushString(")"); + // A tuple of one element has a comma to disambiguate from an + // expression. + if (refs.size() == 1) { + step_stack_->PushString(","); + } + for (auto i : llvm::reverse(llvm::seq(refs.size()))) { + step_stack_->PushInstId(refs[i]); + if (i > 0) { + step_stack_->PushString(", "); + } + } + } + + auto StringifyTypeInst(SemIR::InstId inst_id, TypeOfInst /*inst*/) -> void { + // Print the constant value if we've already computed the inst. + auto const_inst_id = sem_ir_->constant_values().GetConstantInstId(inst_id); + if (const_inst_id.has_value() && const_inst_id != inst_id) { + step_stack_->PushInstId(const_inst_id); + return; + } + *out_ << ""; + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, UnboundElementType inst) + -> void { + *out_ << "PushString(">"); + step_stack_->PushTypeId(inst.class_type_id); + } + + auto StringifyTypeInst(SemIR::InstId /*inst_id*/, VtablePtr /*inst*/) + -> void { + *out_ << ""; + } + + private: + const SemIR::File* sem_ir_; + StepStack* step_stack_; + llvm::raw_ostream* out_; +}; + } // namespace auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) @@ -160,6 +618,7 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) // Note: Since this is a stack, work is resolved in the reverse order from the // order pushed. StepStack step_stack(&sem_ir, outer_inst_id); + Stringifier stringifier(&sem_ir, &step_stack, &out); while (!step_stack.empty()) { auto step = step_stack.Pop(); @@ -184,508 +643,12 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) auto untyped_inst = sem_ir.insts().Get(step.inst_id); CARBON_KIND_SWITCH(untyped_inst) { - case SemIR::AutoType::Kind: - case SemIR::BoolType::Kind: - case SemIR::BoundMethodType::Kind: - case SemIR::ErrorInst::Kind: - case SemIR::IntLiteralType::Kind: - case SemIR::InstType::Kind: - case SemIR::LegacyFloatType::Kind: - case SemIR::NamespaceType::Kind: - case SemIR::SpecificFunctionType::Kind: - case SemIR::StringType::Kind: - case SemIR::TypeType::Kind: - case SemIR::VtableType::Kind: - case SemIR::Vtable::Kind: - case SemIR::WitnessType::Kind: { - // Singleton instructions use their IR name as a label. - out << untyped_inst.kind().ir_name(); - break; - } - case CARBON_KIND(ArrayType inst): { - out << "["; - step_stack.PushString("]"); - step_stack.PushInstId(inst.bound_id); - step_stack.PushString("; "); - step_stack.PushTypeId(inst.element_type_id); - break; - } - case CARBON_KIND(AssociatedConstantDecl inst): { - const auto& assoc_const = - sem_ir.associated_constants().Get(inst.assoc_const_id); - step_stack.PushQualifiedName(assoc_const.parent_scope_id, - assoc_const.name_id); - break; - } - case CARBON_KIND(AssociatedEntityType inst): { - out << ""); - step_stack.PushTypeId(inst.interface_type_id); - break; - } - case BindAlias::Kind: - case BindSymbolicName::Kind: - case ExportDecl::Kind: { - auto name_id = - untyped_inst.As().entity_name_id; - step_stack.PushEntityName(name_id); - break; - } - case CARBON_KIND(ClassType inst): { - const auto& class_info = sem_ir.classes().Get(inst.class_id); - if (auto literal_info = NumericTypeLiteralInfo::ForType(sem_ir, inst); - literal_info.is_valid()) { - literal_info.PrintLiteral(sem_ir, out); - break; - } - step_stack.PushEntityName(class_info, inst.specific_id); - break; - } - case CARBON_KIND(ConstType inst): { - out << "const "; - - // Add parentheses if required. - auto inner_type_inst_id = sem_ir.types().GetInstId(inst.inner_id); - if (GetTypePrecedence(sem_ir.insts().Get(inner_type_inst_id).kind()) < - GetTypePrecedence(SemIR::ConstType::Kind)) { - out << "("; - step_stack.PushString(")"); - } - - step_stack.PushInstId(inner_type_inst_id); - break; - } - case CARBON_KIND(FacetAccessType inst): { - // Given `T:! I`, print `T as type` as simply `T`. - step_stack.PushInstId(inst.facet_value_inst_id); - break; - } - case CARBON_KIND(FacetAccessWitness inst): { - out << ""); - step_stack.PushElementIndex(inst.index); - step_stack.PushString(", interface "); - step_stack.PushInstId(inst.facet_value_inst_id); - break; - } - case CARBON_KIND(FacetType inst): { - const FacetTypeInfo& facet_type_info = - sem_ir.facet_types().Get(inst.facet_type_id); - // Output `where` restrictions. - bool some_where = false; - if (facet_type_info.other_requirements) { - step_stack.PushString("..."); - some_where = true; - } - for (auto rewrite : - llvm::reverse(facet_type_info.rewrite_constraints)) { - if (some_where) { - step_stack.PushString(" and"); - } - step_stack.PushInstId( - sem_ir.constant_values().GetInstId(rewrite.rhs_const_id)); - step_stack.PushString(" = "); - step_stack.PushInstId( - sem_ir.constant_values().GetInstId(rewrite.lhs_const_id)); - step_stack.PushString(" "); - some_where = true; - } - // TODO: Other restrictions from facet_type_info. - if (some_where) { - step_stack.PushString(" where"); - } - - // Output interface requirements. - if (facet_type_info.impls_constraints.empty()) { - step_stack.PushString("type"); - break; - } - for (auto index : llvm::reverse( - llvm::seq(facet_type_info.impls_constraints.size()))) { - const auto& impls = facet_type_info.impls_constraints[index]; - const auto& interface_info = - sem_ir.interfaces().Get(impls.interface_id); - step_stack.PushEntityName(interface_info, impls.specific_id); - if (index > 0) { - step_stack.PushString(" & "); - } - } - break; - } - case CARBON_KIND(FacetValue inst): { - // No need to output the witness. - step_stack.PushTypeId(inst.type_id); - step_stack.PushString(" as "); - step_stack.PushInstId(inst.type_inst_id); - break; - } - case CARBON_KIND(FloatType inst): { - // TODO: Is this okay? - if (auto width_value = - sem_ir.insts().TryGetAs(inst.bit_width_id)) { - out << "f"; - sem_ir.ints().Get(width_value->int_id).print(out, /*isSigned=*/false); - } else { - out << "Core.Float("; - step_stack.PushString(")"); - step_stack.PushInstId(inst.bit_width_id); - } - break; - } - case CARBON_KIND(FunctionType inst): { - const auto& fn = sem_ir.functions().Get(inst.function_id); - out << ""); - step_stack.PushQualifiedName(fn.parent_scope_id, fn.name_id); - break; - } - case CARBON_KIND(FunctionTypeWithSelfType inst): { - out << ""); - step_stack.PushInstId(inst.self_id); - step_stack.PushString(" in "); - if (auto fn_inst = sem_ir.insts().TryGetAs( - inst.interface_function_type_id)) { - const auto& fn = sem_ir.functions().Get(fn_inst->function_id); - step_stack.PushQualifiedName(fn.parent_scope_id, fn.name_id); - } else { - step_stack.PushInstId(inst.interface_function_type_id); - } - break; - } - case CARBON_KIND(GenericClassType inst): { - const auto& class_info = sem_ir.classes().Get(inst.class_id); - out << ""); - step_stack.PushQualifiedName(class_info.parent_scope_id, - class_info.name_id); - break; - } - case CARBON_KIND(GenericInterfaceType inst): { - const auto& interface = sem_ir.interfaces().Get(inst.interface_id); - out << ""); - step_stack.PushQualifiedName(interface.parent_scope_id, - interface.name_id); - break; - } - case CARBON_KIND(ImplWitnessAccess inst): { - auto witness_inst_id = - sem_ir.constant_values().GetConstantInstId(inst.witness_id); - auto witness = - sem_ir.insts().GetAs(witness_inst_id); - auto witness_type_id = - sem_ir.insts().Get(witness.facet_value_inst_id).type_id(); - auto facet_type = sem_ir.types().GetAs(witness_type_id); - step_stack.PushString(")"); - // TODO: Support != 1 interface better. - if (auto impls_constraint = sem_ir.facet_types() - .Get(facet_type.facet_type_id) - .TryAsSingleInterface()) { - const auto& interface = - sem_ir.interfaces().Get(impls_constraint->interface_id); - auto entities = - sem_ir.inst_blocks().Get(interface.associated_entities_id); - size_t index = inst.index.index; - CARBON_CHECK(index < entities.size(), "Access out of bounds."); - auto entity_inst_id = entities[index]; - if (auto associated_const = - sem_ir.insts().TryGetAs( - entity_inst_id)) { - step_stack.PushNameId(sem_ir.associated_constants() - .Get(associated_const->assoc_const_id) - .name_id); - } else if (auto function_decl = sem_ir.insts().TryGetAs( - entity_inst_id)) { - const auto& function = - sem_ir.functions().Get(function_decl->function_id); - step_stack.PushNameId(function.name_id); - } else { - step_stack.PushInstId(entity_inst_id); - } - step_stack.PushString("."); - step_stack.PushEntityName(interface, impls_constraint->specific_id); - step_stack.PushString(".("); - } else { - step_stack.PushTypeId(witness_type_id); - step_stack.PushString(".(TODO: "); - } - - bool period_self = false; - if (auto sym_name = sem_ir.insts().TryGetAs( - witness.facet_value_inst_id)) { - auto name_id = - sem_ir.entity_names().Get(sym_name->entity_name_id).name_id; - period_self = (name_id == SemIR::NameId::PeriodSelf); - } - if (!period_self) { - step_stack.PushInstId(witness.facet_value_inst_id); - } - break; - } - case CARBON_KIND(ImportRefUnloaded inst): { - if (inst.entity_name_id.has_value()) { - step_stack.PushEntityName(inst.entity_name_id); - } else { - out << ""; - } - break; - } - case CARBON_KIND(IntType inst): { - out << ""); - if (auto width_value = - sem_ir.insts().TryGetAs(inst.bit_width_id)) { - out << (inst.int_kind.is_signed() ? "i" : "u"); - sem_ir.ints().Get(width_value->int_id).print(out, /*isSigned=*/false); - } else { - out << (inst.int_kind.is_signed() ? "Int(" : "UInt("); - step_stack.PushString(")"); - step_stack.PushInstId(inst.bit_width_id); - } - break; - } - case CARBON_KIND(IntValue inst): { - sem_ir.ints().Get(inst.int_id).print(out, /*isSigned=*/true); - break; - } - case CARBON_KIND(NameRef inst): { - out << sem_ir.names().GetFormatted(inst.name_id); - break; - } - case CARBON_KIND(Namespace inst): { - const auto& name_scope = sem_ir.name_scopes().Get(inst.name_scope_id); - step_stack.PushQualifiedName(name_scope.parent_scope_id(), - name_scope.name_id()); - break; - } - case CARBON_KIND(PointerType inst): { - step_stack.PushString("*"); - step_stack.PushTypeId(inst.pointee_id); - break; - } - case CARBON_KIND(SpecificFunction inst): { - auto callee = SemIR::GetCalleeFunction(sem_ir, inst.callee_id); - if (callee.function_id.has_value()) { - step_stack.PushEntityName(sem_ir.functions().Get(callee.function_id), - inst.specific_id); - } else { - step_stack.PushString(""); - } - break; - } - case CARBON_KIND(SpecificImplFunction inst): { - auto callee = SemIR::GetCalleeFunction(sem_ir, inst.callee_id); - if (callee.function_id.has_value()) { - // TODO: The specific_id here is for the interface member, but the - // entity we're passing is the impl member. This might result in - // strange output once we render specific arguments properly. - step_stack.PushEntityName(sem_ir.functions().Get(callee.function_id), - inst.specific_id); - } else { - step_stack.PushString(""); - } - break; - } - case CARBON_KIND(StructType inst): { - auto fields = sem_ir.struct_type_fields().Get(inst.fields_id); - if (fields.empty()) { - out << "{}"; - break; - } - out << "{"; - step_stack.PushString("}"); - for (auto index : llvm::reverse(llvm::seq(fields.size()))) { - const auto& field = fields[index]; - step_stack.PushTypeId(field.type_id); - step_stack.PushString(": "); - step_stack.PushNameId(field.name_id); - step_stack.PushString("."); - if (index > 0) { - step_stack.PushString(", "); - } - } - break; - } - case CARBON_KIND(StructValue inst): { - auto field_values = sem_ir.inst_blocks().Get(inst.elements_id); - if (field_values.empty()) { - out << "{}"; - break; - } - auto struct_type = sem_ir.types().GetAs( - sem_ir.types().GetObjectRepr(inst.type_id)); - auto fields = sem_ir.struct_type_fields().Get(struct_type.fields_id); - if (fields.size() != field_values.size()) { - out << "{}"; - break; - } - out << "{"; - step_stack.PushString("}"); - for (auto index : llvm::reverse(llvm::seq(fields.size()))) { - SemIR::InstId value_inst_id = field_values[index]; - step_stack.PushInstId(value_inst_id); - step_stack.PushString(" = "); - step_stack.PushNameId(fields[index].name_id); - step_stack.PushString("."); - if (index > 0) { - step_stack.PushString(", "); - } - } - break; - } - case CARBON_KIND(TupleType inst): { - auto refs = sem_ir.type_blocks().Get(inst.elements_id); - if (refs.empty()) { - out << "()"; - break; - } - out << "("; - step_stack.PushString(")"); - // A tuple of one element has a comma to disambiguate from an - // expression. - if (refs.size() == 1) { - step_stack.PushString(","); - } - for (auto i : llvm::reverse(llvm::seq(refs.size()))) { - step_stack.PushTypeId(refs[i]); - if (i > 0) { - step_stack.PushString(", "); - } - } - break; - } - case CARBON_KIND(TupleValue inst): { - auto refs = sem_ir.inst_blocks().Get(inst.elements_id); - if (refs.empty()) { - out << "()"; - break; - } - out << "("; - step_stack.PushString(")"); - // A tuple of one element has a comma to disambiguate from an - // expression. - if (refs.size() == 1) { - step_stack.PushString(","); - } - for (auto i : llvm::reverse(llvm::seq(refs.size()))) { - step_stack.PushInstId(refs[i]); - if (i > 0) { - step_stack.PushString(", "); - } - } - break; - } - case SemIR::TypeOfInst::Kind: { - // Print the constant value if we've already computed the inst. - auto const_inst_id = - sem_ir.constant_values().GetConstantInstId(step.inst_id); - if (const_inst_id.has_value() && const_inst_id != step.inst_id) { - step_stack.PushInstId(const_inst_id); - break; - } - out << ""; - break; - } - case CARBON_KIND(UnboundElementType inst): { - out << ""); - step_stack.PushTypeId(inst.class_type_id); - break; - } - case VtablePtr::Kind: { - out << ""; - break; - } - case AccessMemberAction::Kind: - case AdaptDecl::Kind: - case AddrOf::Kind: - case AddrPattern::Kind: - case ArrayIndex::Kind: - case ArrayInit::Kind: - case AsCompatible::Kind: - case Assign::Kind: - case AssociatedEntity::Kind: - case BaseDecl::Kind: - case BindName::Kind: - case BindValue::Kind: - case BindingPattern::Kind: - case BlockArg::Kind: - case BoolLiteral::Kind: - case BoundMethod::Kind: - case Branch::Kind: - case BranchIf::Kind: - case BranchWithArg::Kind: - case Call::Kind: - case ClassDecl::Kind: - case ClassElementAccess::Kind: - case ClassInit::Kind: - case CompleteTypeWitness::Kind: - case ConvertToValueAction::Kind: - case Converted::Kind: - case Deref::Kind: - case FieldDecl::Kind: - case FloatLiteral::Kind: - case FunctionDecl::Kind: - case ImplDecl::Kind: - case ImplWitness::Kind: - case ImportCppDecl::Kind: - case ImportDecl::Kind: - case ImportRefLoaded::Kind: - case InitializeFrom::Kind: - case InstValue::Kind: - case InterfaceDecl::Kind: - case NameBindingDecl::Kind: - case OutParam::Kind: - case OutParamPattern::Kind: - case RefParam::Kind: - case RefParamPattern::Kind: - case RefineTypeAction::Kind: - case RequireCompleteType::Kind: - case RequirementEquivalent::Kind: - case RequirementImpls::Kind: - case RequirementRewrite::Kind: - case Return::Kind: - case ReturnExpr::Kind: - case ReturnSlot::Kind: - case ReturnSlotPattern::Kind: - case SpecificConstant::Kind: - case SpliceBlock::Kind: - case SpliceInst::Kind: - case StringLiteral::Kind: - case StructAccess::Kind: - case StructInit::Kind: - case StructLiteral::Kind: - case SymbolicBindingPattern::Kind: - case Temporary::Kind: - case TemporaryStorage::Kind: - case TupleAccess::Kind: - case TupleInit::Kind: - case TupleLiteral::Kind: - case TuplePattern::Kind: - case UnaryOperatorNot::Kind: - case ValueAsRef::Kind: - case ValueOfInitializer::Kind: - case ValueParam::Kind: - case ValueParamPattern::Kind: - case VarPattern::Kind: - case VarStorage::Kind: - case WhereExpr::Kind: - // We don't know how to print this instruction, but it might have a - // constant value that we can print. - auto const_inst_id = - sem_ir.constant_values().GetConstantInstId(step.inst_id); - if (const_inst_id.has_value() && const_inst_id != step.inst_id) { - step_stack.PushInstId(const_inst_id); - break; - } - - // We don't need to handle stringification for instructions that don't - // show up in errors, but make it clear what's going on so that it's - // clearer when stringification is needed. - out << ""; - break; +#define CARBON_SEM_IR_INST_KIND(InstT) \ + case CARBON_KIND(InstT typed_inst): { \ + stringifier.StringifyTypeInst(step.inst_id, typed_inst); \ + break; \ + } +#include "toolchain/sem_ir/inst_kind.def" } }