diff --git a/toolchain/sem_ir/stringify_type.cpp b/toolchain/sem_ir/stringify_type.cpp index 4b5ca5655784..b42013adfd17 100644 --- a/toolchain/sem_ir/stringify_type.cpp +++ b/toolchain/sem_ir/stringify_type.cpp @@ -24,18 +24,16 @@ static auto GetTypePrecedence(InstKind kind) -> int { return 0; } -auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) - -> std::string { - std::string str; - llvm::raw_string_ostream out(str); - +namespace { +class StepStack { + public: + enum Kind : uint8_t { + Inst, + FixedString, + ArrayBound, + Name, + }; struct Step { - // The instruction's file. - const File& sem_ir; - enum Kind : uint8_t { - Inst, - FixedString, - }; // The kind of step to perform. Kind kind; union { @@ -43,79 +41,100 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) InstId inst_id; // The fixed string to print, when kind is FixedString. const char* fixed_string; + // The array bound to print, when kind is ArrayBound. + InstId bound_id; + // The name to print, when kind is Name. + NameId name_id; }; - // The index within the current step. Not used by all kinds of step. - int index = 0; + }; - auto Next() const -> Step { - Step next = *this; - ++next.index; - return next; + explicit StepStack(const SemIR::File* file, InstId outer_inst_id) + : sem_ir(file) { + steps.push_back({.kind = Inst, .inst_id = outer_inst_id}); + } + + auto PushInstId(InstId inst_id) -> void { + steps.push_back({.kind = Inst, .inst_id = inst_id}); + } + auto PushString(const char* string) -> void { + steps.push_back({.kind = FixedString, .fixed_string = string}); + } + auto PushArrayBound(InstId bound_id) -> void { + steps.push_back({.kind = ArrayBound, .bound_id = bound_id}); + } + auto PushNameId(NameId name_id) -> void { + steps.push_back({.kind = Name, .name_id = name_id}); + } + auto PushTypeId(TypeId type_id) -> void { + PushInstId(sem_ir->types().GetInstId(type_id)); + } + auto PushSpecificId(const EntityWithParamsBase& entity, + SpecificId specific_id) -> void { + if (!entity.param_patterns_id.is_valid()) { + return; } - }; - llvm::SmallVector steps = {Step{ - .sem_ir = outer_sem_ir, .kind = Step::Inst, .inst_id = outer_inst_id}}; + int num_params = sem_ir->inst_blocks().Get(entity.param_patterns_id).size(); + if (!num_params) { + PushString("()"); + return; + } + if (!specific_id.is_valid()) { + // The name of the generic was used within the generic itself. + // TODO: Should we print the names of the generic parameters in this + // case? + return; + } + const auto& specific = sem_ir->specifics().Get(specific_id); + auto args = + sem_ir->inst_blocks().Get(specific.args_id).take_back(num_params); + bool last = true; + for (auto arg : llvm::reverse(args)) { + PushString(last ? ")" : ", "); + PushInstId(arg); + last = false; + } + PushString("("); + } - // Note: The `steps` worklist is a stack and so work is resolved in the - // reverse order from the order added. - auto push_string = [&](const char* string) { - steps.push_back({.sem_ir = outer_sem_ir, - .kind = Step::FixedString, - .fixed_string = string}); - }; + auto empty() const -> bool { return steps.empty(); } + auto Pop() -> Step { return steps.pop_back_val(); } - while (!steps.empty()) { - auto step = steps.pop_back_val(); + private: + const SemIR::File* sem_ir; + llvm::SmallVector steps; +}; +} // namespace - if (step.kind == Step::FixedString) { +auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) + -> std::string { + std::string str; + llvm::raw_string_ostream out(str); + + // 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); + + while (!step_stack.empty()) { + auto step = step_stack.Pop(); + + if (step.kind == StepStack::FixedString) { out << step.fixed_string; continue; } - - CARBON_CHECK(step.kind == Step::Inst); + if (step.kind == StepStack::ArrayBound) { + out << sem_ir.GetArrayBoundValue(step.bound_id); + continue; + } + if (step.kind == StepStack::Name) { + out << sem_ir.names().GetFormatted(step.name_id); + continue; + } + CARBON_CHECK(step.kind == StepStack::Inst); if (!step.inst_id.is_valid()) { out << ""; continue; } - const auto& sem_ir = step.sem_ir; - // Helper for instructions with the current sem_ir. - // Note: The `steps` worklist is a stack and so work is resolved in the - // reverse order from the order added. - auto push_inst_id = [&](InstId inst_id) { - steps.push_back( - {.sem_ir = sem_ir, .kind = Step::Inst, .inst_id = inst_id}); - }; - - auto push_specific_id = [&](const EntityWithParamsBase& entity, - SpecificId specific_id) { - if (!entity.param_patterns_id.is_valid()) { - return; - } - int num_params = - sem_ir.inst_blocks().Get(entity.param_patterns_id).size(); - if (!num_params) { - out << "()"; - return; - } - if (!specific_id.is_valid()) { - // The name of the generic was used within the generic itself. - // TODO: Should we print the names of the generic parameters in this - // case? - return; - } - out << "("; - const auto& specific = sem_ir.specifics().Get(specific_id); - auto args = - sem_ir.inst_blocks().Get(specific.args_id).take_back(num_params); - bool last = true; - for (auto arg : llvm::reverse(args)) { - push_string(last ? ")" : ", "); - push_inst_id(arg); - last = false; - } - }; - auto untyped_inst = sem_ir.insts().Get(step.inst_id); CARBON_KIND_SWITCH(untyped_inst) { case SemIR::AutoType::Kind: @@ -135,21 +154,19 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) break; } case CARBON_KIND(ArrayType inst): { - if (step.index == 0) { - out << "["; - steps.push_back(step.Next()); - push_inst_id(sem_ir.types().GetInstId(inst.element_type_id)); - } else if (step.index == 1) { - out << "; " << sem_ir.GetArrayBoundValue(inst.bound_id) << "]"; - } + out << "["; + step_stack.PushString("]"); + step_stack.PushArrayBound(inst.bound_id); + step_stack.PushString("; "); + step_stack.PushTypeId(inst.element_type_id); break; } case CARBON_KIND(AssociatedEntityType inst): { out << ""); - push_inst_id(sem_ir.types().GetInstId(inst.interface_type_id)); - push_string(" in "); - push_inst_id(sem_ir.types().GetInstId(inst.entity_type_id)); + step_stack.PushString(">"); + step_stack.PushTypeId(inst.interface_type_id); + step_stack.PushString(" in "); + step_stack.PushTypeId(inst.entity_type_id); break; } case BindAlias::Kind: @@ -164,7 +181,7 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) case CARBON_KIND(ClassType inst): { const auto& class_info = sem_ir.classes().Get(inst.class_id); out << sem_ir.names().GetFormatted(class_info.name_id); - push_specific_id(class_info, inst.specific_id); + step_stack.PushSpecificId(class_info, inst.specific_id); break; } case CARBON_KIND(ConstType inst): { @@ -175,53 +192,53 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) if (GetTypePrecedence(sem_ir.insts().Get(inner_type_inst_id).kind()) < GetTypePrecedence(SemIR::ConstType::Kind)) { out << "("; - push_string(")"); + step_stack.PushString(")"); } - push_inst_id(inner_type_inst_id); + step_stack.PushInstId(inner_type_inst_id); break; } case CARBON_KIND(FacetAccessType inst): { // Given `T:! I`, print `T as type` as simply `T`. - push_inst_id(inst.facet_value_inst_id); + step_stack.PushInstId(inst.facet_value_inst_id); break; } case CARBON_KIND(FacetAccessWitness inst): { out << ""); - push_inst_id(inst.facet_value_inst_id); + step_stack.PushString(">"); + 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); + // TODO: Also output other restrictions from facet_type_info. + if (facet_type_info.requirement_block_id.is_valid()) { + step_stack.PushString(" where..."); + } + if (facet_type_info.impls_constraints.empty()) { - out << "type"; - } else { - const auto& impls = facet_type_info.impls_constraints[step.index]; + 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); - out << sem_ir.names().GetFormatted(interface_info.name_id); - push_specific_id(interface_info, impls.specific_id); - if (step.index + 1 < - static_cast(facet_type_info.impls_constraints.size())) { - steps.push_back(step.Next()); - push_string(" & "); + step_stack.PushSpecificId(interface_info, impls.specific_id); + step_stack.PushNameId(interface_info.name_id); + if (index > 0) { + step_stack.PushString(" & "); } } - // TODO: Also output other restrictions from facet_type_info. - if (step.index + 1 >= - static_cast(facet_type_info.impls_constraints.size()) && - facet_type_info.requirement_block_id.is_valid()) { - out << " where..."; - } break; } case CARBON_KIND(FacetValue inst): { // No need to output the witness. - push_inst_id(sem_ir.types().GetInstId(inst.type_id)); - push_string(" as "); - push_inst_id(inst.type_inst_id); + step_stack.PushTypeId(inst.type_id); + step_stack.PushString(" as "); + step_stack.PushInstId(inst.type_inst_id); break; } case CARBON_KIND(FloatType inst): { @@ -232,8 +249,8 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) sem_ir.ints().Get(width_value->int_id).print(out, /*isSigned=*/false); } else { out << "Core.Float("; - push_string(")"); - push_inst_id(inst.bit_width_id); + step_stack.PushString(")"); + step_stack.PushInstId(inst.bit_width_id); } break; } @@ -261,8 +278,8 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) sem_ir.ints().Get(width_value->int_id).print(out, /*isSigned=*/false); } else { out << (inst.int_kind.is_signed() ? "Core.Int(" : "Core.UInt("); - push_string(")"); - push_inst_id(inst.bit_width_id); + step_stack.PushString(")"); + step_stack.PushInstId(inst.bit_width_id); } break; } @@ -271,8 +288,8 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) break; } case CARBON_KIND(PointerType inst): { - push_string("*"); - push_inst_id(sem_ir.types().GetInstId(inst.pointee_id)); + step_stack.PushString("*"); + step_stack.PushTypeId(inst.pointee_id); break; } case CARBON_KIND(StructType inst): { @@ -281,17 +298,18 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) out << "{}"; break; } - - if (step.index >= static_cast(fields.size())) { - 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(", "); + } } - - const auto& field = fields[step.index]; - out << (step.index == 0 ? "{" : ", ") << "." - << sem_ir.names().GetFormatted(field.name_id) << ": "; - steps.push_back(step.Next()); - push_inst_id(sem_ir.types().GetInstId(field.type_id)); break; } case CARBON_KIND(TupleType inst): { @@ -301,31 +319,31 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) break; } out << "("; - push_string(")"); + step_stack.PushString(")"); // A tuple of one element has a comma to disambiguate from an // expression. if (refs.size() == 1) { - push_string(","); + step_stack.PushString(","); } for (auto i : llvm::reverse(llvm::seq(refs.size()))) { - push_inst_id(sem_ir.types().GetInstId(refs[i])); + step_stack.PushTypeId(refs[i]); if (i > 0) { - push_string(", "); + step_stack.PushString(", "); } } break; } case CARBON_KIND(UnboundElementType inst): { out << ""); - push_inst_id(sem_ir.types().GetInstId(inst.class_type_id)); + step_stack.PushString(">"); + step_stack.PushTypeId(inst.class_type_id); break; } case CARBON_KIND(WhereExpr inst): { out << ""); + step_stack.PushString(">"); TypeId type_id = sem_ir.insts().Get(inst.period_self_id).type_id(); - push_inst_id(sem_ir.types().GetInstId(type_id)); + step_stack.PushTypeId(type_id); // TODO: Also output restrictions from the inst block // inst.requirements_id. break; @@ -404,7 +422,7 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id) auto const_inst_id = sem_ir.constant_values().GetConstantInstId(step.inst_id); if (const_inst_id.is_valid() && const_inst_id != step.inst_id) { - push_inst_id(const_inst_id); + step_stack.PushInstId(const_inst_id); break; }