Преглед изворни кода

Rename `StringifyType` to reflect that it can stringify non-type constants. (#5285)

Use it to stringify associated constant values in diagnostics. In
passing, add missing support for stringifying bool literals. Note that
there are some cases that it doesn't stringify properly, but that's not
new here; such cases could already be observed when stringifying generic
arguments.
Richard Smith пре 1 година
родитељ
комит
47fa1b5991

+ 1 - 1
toolchain/check/BUILD

@@ -289,7 +289,7 @@ cc_library(
         "//toolchain/parse:tree",
         "//toolchain/sem_ir:absolute_node_id",
         "//toolchain/sem_ir:file",
-        "//toolchain/sem_ir:stringify_type",
+        "//toolchain/sem_ir:stringify",
         "//toolchain/sem_ir:typed_insts",
         "@llvm-project//llvm:Support",
     ],

+ 9 - 8
toolchain/check/diagnostic_emitter.cpp

@@ -6,7 +6,7 @@
 
 #include "common/raw_string_ostream.h"
 #include "toolchain/sem_ir/absolute_node_id.h"
-#include "toolchain/sem_ir/stringify_type.h"
+#include "toolchain/sem_ir/stringify.h"
 
 namespace Carbon::Check {
 
@@ -93,25 +93,26 @@ auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
     // TODO: Where possible, produce a better description of the type based on
     // the expression.
     return "`" +
-           StringifyTypeExpr(
+           StringifyConstantInst(
                *sem_ir_,
                sem_ir_->types().GetInstId(
                    sem_ir_->insts().Get(type_of_expr->inst_id).type_id())) +
            "`";
   }
-  if (auto* type_expr = llvm::any_cast<InstIdAsType>(&arg)) {
-    return "`" + StringifyTypeExpr(*sem_ir_, type_expr->inst_id) + "`";
+  if (auto* expr = llvm::any_cast<InstIdAsConstant>(&arg)) {
+    return "`" + StringifyConstantInst(*sem_ir_, expr->inst_id) + "`";
   }
   if (auto* type_expr = llvm::any_cast<InstIdAsRawType>(&arg)) {
-    return StringifyTypeExpr(*sem_ir_, type_expr->inst_id);
+    return StringifyConstantInst(*sem_ir_, type_expr->inst_id);
   }
   if (auto* type = llvm::any_cast<TypeIdAsRawType>(&arg)) {
-    return StringifyTypeExpr(*sem_ir_,
-                             sem_ir_->types().GetInstId(type->type_id));
+    return StringifyConstantInst(*sem_ir_,
+                                 sem_ir_->types().GetInstId(type->type_id));
   }
   if (auto* type_id = llvm::any_cast<SemIR::TypeId>(&arg)) {
     return "`" +
-           StringifyTypeExpr(*sem_ir_, sem_ir_->types().GetInstId(*type_id)) +
+           StringifyConstantInst(*sem_ir_,
+                                 sem_ir_->types().GetInstId(*type_id)) +
            "`";
   }
   if (auto* specific_id = llvm::any_cast<SemIR::SpecificId>(&arg)) {

+ 17 - 8
toolchain/check/diagnostic_helpers.h

@@ -61,6 +61,17 @@ inline auto TokenOnly(SemIR::LocId loc_id) -> SemIRLoc {
   return SemIRLoc(loc_id, true);
 }
 
+// An expression with a constant value, for rendering in a diagnostic. The
+// diagnostic rendering will include enclosing "`"s.
+struct InstIdAsConstant {
+  using DiagnosticType = Diagnostics::TypeInfo<std::string>;
+
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  InstIdAsConstant(SemIR::InstId inst_id) : inst_id(inst_id) {}
+
+  SemIR::InstId inst_id;
+};
+
 // An expression whose type should be rendered in a diagnostic. The diagnostic
 // rendering will include enclosing "`"s, and may also include extra information
 // about the type if it might otherwise be ambiguous or context-dependent, such
@@ -89,14 +100,12 @@ struct TypeOfInstId {
 //
 // This should be used when the source expression used to construct a type is
 // available.
-struct InstIdAsType {
-  using DiagnosticType = Diagnostics::TypeInfo<std::string>;
-
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  InstIdAsType(SemIR::InstId inst_id) : inst_id(inst_id) {}
-
-  SemIR::InstId inst_id;
-};
+//
+// Note that this is currently an alias for InstIdAsConstant. However, using
+// InstIdAsType is clearer when defining CARBON_DIAGNOSTICs, and we may wish to
+// distinguish type arguments in diagnostics from more general constants in some
+// way in the future.
+using InstIdAsType = InstIdAsConstant;
 
 // A type expression, for rendering in a diagnostic as a raw type. When
 // formatting as a raw type in a diagnostic, the type will be formatted as a

+ 14 - 13
toolchain/check/facet_type.cpp

@@ -169,14 +169,15 @@ auto InitialFacetTypeImplWitness(
         // TODO: Figure out how to print the two different values
         // `const_id` & `rewrite_inst_id` in the diagnostic
         // message.
-        CARBON_DIAGNOSTIC(AssociatedConstantWithDifferentValues, Error,
-                          "associated constant {0} given two different values",
-                          SemIR::NameId);
+        CARBON_DIAGNOSTIC(
+            AssociatedConstantWithDifferentValues, Error,
+            "associated constant {0} given two different values {1} and {2}",
+            SemIR::NameId, InstIdAsConstant, InstIdAsConstant);
         auto& assoc_const = context.associated_constants().Get(
             assoc_constant_decl->assoc_const_id);
-        context.emitter().Emit(facet_type_inst_id,
-                               AssociatedConstantWithDifferentValues,
-                               assoc_const.name_id);
+        context.emitter().Emit(
+            facet_type_inst_id, AssociatedConstantWithDifferentValues,
+            assoc_const.name_id, table_entry, rewrite_inst_id);
       }
       table_entry = SemIR::ErrorInst::SingletonInstId;
       continue;
@@ -205,17 +206,17 @@ auto InitialFacetTypeImplWitness(
       // value was constant.
       if (converted_inst_id.has_value()) {
         rewrite_inst_id = converted_inst_id;
-      } else if (rewrite_inst_id != SemIR::ErrorInst::SingletonInstId) {
+      } else {
         const auto& assoc_const = context.associated_constants().Get(
             assoc_constant_decl->assoc_const_id);
         CARBON_DIAGNOSTIC(
             AssociatedConstantNotConstantAfterConversion, Error,
-            "associated constant {0} given value that is not constant "
-            "after conversion to {1}",
-            SemIR::NameId, SemIR::TypeId);
-        context.emitter().Emit(facet_type_inst_id,
-                               AssociatedConstantNotConstantAfterConversion,
-                               assoc_const.name_id, assoc_const_type_id);
+            "associated constant {0} given value {1} that is not constant "
+            "after conversion to {2}",
+            SemIR::NameId, InstIdAsConstant, SemIR::TypeId);
+        context.emitter().Emit(
+            facet_type_inst_id, AssociatedConstantNotConstantAfterConversion,
+            assoc_const.name_id, rewrite_inst_id, assoc_const_type_id);
         rewrite_inst_id = SemIR::ErrorInst::SingletonInstId;
       }
     }

+ 1 - 1
toolchain/check/testdata/impl/assoc_const_self.carbon

@@ -51,7 +51,7 @@ impl () as Core.ImplicitAs(C) {
   fn Convert[self: ()]() -> C { return {}; }
 }
 
-// CHECK:STDERR: fail_assoc_const_not_constant_after_conversion.carbon:[[@LINE+4]]:11: error: associated constant V given value that is not constant after conversion to `C` [AssociatedConstantNotConstantAfterConversion]
+// CHECK:STDERR: fail_assoc_const_not_constant_after_conversion.carbon:[[@LINE+4]]:11: error: associated constant V given value `()` that is not constant after conversion to `C` [AssociatedConstantNotConstantAfterConversion]
 // CHECK:STDERR: impl C as I where .V = () {}
 // CHECK:STDERR:           ^~~~~~~~~~~~~~~
 // CHECK:STDERR:

+ 387 - 0
toolchain/check/testdata/impl/impl_assoc_const.carbon

@@ -0,0 +1,387 @@
+// 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
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_assoc_const.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_assoc_const.carbon
+
+// --- same_non_type.carbon
+library "[[@TEST_NAME]]";
+
+interface I {
+  let X:! {.a: bool, .b: (i32, i32)};
+}
+impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = not false, .b = (3 - 2, 4 / 2)} {}
+
+// --- fail_two_different_non_type.carbon
+library "[[@TEST_NAME]]";
+
+interface I {
+  let X:! {.a: bool, .b: (i32, i32)};
+}
+// CHECK:STDERR: fail_two_different_non_type.carbon:[[@LINE+4]]:12: error: associated constant X given two different values `{.a = true, .b = (1, 2)}` and `{.a = false, .b = (3, 4)}` [AssociatedConstantWithDifferentValues]
+// CHECK:STDERR: impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = false, .b = (3, 4)} {}
+// CHECK:STDERR:            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = false, .b = (3, 4)} {}
+
+// CHECK:STDOUT: --- same_non_type.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
+// CHECK:STDOUT:   %Self.826: %I.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete]
+// CHECK:STDOUT:   %struct_type.a.b.fe2: type = struct_type {.a: bool, .b: %tuple.type.d07} [concrete]
+// CHECK:STDOUT:   %I.assoc_type: type = assoc_entity_type @I [concrete]
+// CHECK:STDOUT:   %assoc0.e7f: %I.assoc_type = assoc_entity element0, @I.%X [concrete]
+// CHECK:STDOUT:   %.Self: %I.type = bind_symbolic_name .Self [symbolic_self]
+// CHECK:STDOUT:   %.Self.as_type: type = facet_access_type %.Self [symbolic_self]
+// CHECK:STDOUT:   %.Self.as_wit.iface0: <witness> = facet_access_witness %.Self, element0 [symbolic_self]
+// CHECK:STDOUT:   %I.facet: %I.type = facet_value %.Self.as_type, (%.Self.as_wit.iface0) [symbolic_self]
+// CHECK:STDOUT:   %impl.elem0: %struct_type.a.b.fe2 = impl_witness_access %.Self.as_wit.iface0, element0 [symbolic_self]
+// CHECK:STDOUT:   %true: bool = bool_literal true [concrete]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %int_2.ecc: Core.IntLiteral = int_value 2 [concrete]
+// CHECK:STDOUT:   %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete]
+// CHECK:STDOUT:   %struct_type.a.b.aa4: type = struct_type {.a: bool, .b: %tuple.type.f94} [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.205: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
+// CHECK:STDOUT:   %Convert.type.1b6: type = fn_type @Convert.1, @ImplicitAs(%i32) [concrete]
+// CHECK:STDOUT:   %Div.type: type = facet_type <@Div> [concrete]
+// CHECK:STDOUT:   %Sub.type: type = facet_type <@Sub> [concrete]
+// CHECK:STDOUT:   %impl_witness.d39: <witness> = impl_witness (imports.%Core.import_ref.a5b), @impl.4f9(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.type.035: type = fn_type @Convert.2, @impl.4f9(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.956: %Convert.type.035 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.205 = facet_value Core.IntLiteral, (%impl_witness.d39) [concrete]
+// CHECK:STDOUT:   %.be7: type = fn_type_with_self_type %Convert.type.1b6, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Convert.bound.ab5: <bound method> = bound_method %int_1.5b8, %Convert.956 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.956, @Convert.2(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method.9a1: <bound method> = bound_method %int_1.5b8, %Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_1.5d2: %i32 = int_value 1 [concrete]
+// CHECK:STDOUT:   %Convert.bound.ef9: <bound method> = bound_method %int_2.ecc, %Convert.956 [concrete]
+// CHECK:STDOUT:   %bound_method.b92: <bound method> = bound_method %int_2.ecc, %Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_2.ef8: %i32 = int_value 2 [concrete]
+// CHECK:STDOUT:   %tuple: %tuple.type.d07 = tuple_value (%int_1.5d2, %int_2.ef8) [concrete]
+// CHECK:STDOUT:   %struct: %struct_type.a.b.fe2 = struct_value (%true, %tuple) [concrete]
+// CHECK:STDOUT:   %false: bool = bool_literal false [concrete]
+// CHECK:STDOUT:   %int_3: Core.IntLiteral = int_value 3 [concrete]
+// CHECK:STDOUT:   %Op.type.111: type = fn_type @Op.13 [concrete]
+// CHECK:STDOUT:   %impl_witness.e4a: <witness> = impl_witness (imports.%Core.import_ref.6c4) [concrete]
+// CHECK:STDOUT:   %Sub.facet: %Sub.type = facet_value Core.IntLiteral, (%impl_witness.e4a) [concrete]
+// CHECK:STDOUT:   %.76a: type = fn_type_with_self_type %Op.type.111, %Sub.facet [concrete]
+// CHECK:STDOUT:   %Op.type.2b9: type = fn_type @Op.14 [concrete]
+// CHECK:STDOUT:   %Op.667: %Op.type.2b9 = struct_value () [concrete]
+// CHECK:STDOUT:   %Op.bound.645: <bound method> = bound_method %int_3, %Op.667 [concrete]
+// CHECK:STDOUT:   %int_4: Core.IntLiteral = int_value 4 [concrete]
+// CHECK:STDOUT:   %Op.type.784: type = fn_type @Op.15 [concrete]
+// CHECK:STDOUT:   %impl_witness.586: <witness> = impl_witness (imports.%Core.import_ref.dfd) [concrete]
+// CHECK:STDOUT:   %Div.facet: %Div.type = facet_value Core.IntLiteral, (%impl_witness.586) [concrete]
+// CHECK:STDOUT:   %.cf0: type = fn_type_with_self_type %Op.type.784, %Div.facet [concrete]
+// CHECK:STDOUT:   %Op.type.c38: type = fn_type @Op.16 [concrete]
+// CHECK:STDOUT:   %Op.553: %Op.type.c38 = struct_value () [concrete]
+// CHECK:STDOUT:   %Op.bound.2e7: <bound method> = bound_method %int_4, %Op.553 [concrete]
+// CHECK:STDOUT:   %I_where.type: type = facet_type <@I where %impl.elem0 = %struct> [concrete]
+// CHECK:STDOUT:   %impl_witness.f56: <witness> = impl_witness (file.%impl_witness_assoc_constant) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Bool = %Core.Bool
+// CHECK:STDOUT:     .Int = %Core.Int
+// CHECK:STDOUT:     .ImplicitAs = %Core.ImplicitAs
+// CHECK:STDOUT:     .Sub = %Core.Sub
+// CHECK:STDOUT:     .Div = %Core.Div
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {}
+// CHECK:STDOUT:   impl_decl @impl.540 [concrete] {} {
+// CHECK:STDOUT:     %.loc6_7.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:     %.loc6_7.2: type = converted %.loc6_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:     %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type]
+// CHECK:STDOUT:     %.Self: %I.type = bind_symbolic_name .Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:     %.Self.ref.loc6_20: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:     %X.ref.loc6_20: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.e7f]
+// CHECK:STDOUT:     %.Self.as_type.loc6_20: type = facet_access_type %.Self.ref.loc6_20 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.loc6_20: type = converted %.Self.ref.loc6_20, %.Self.as_type.loc6_20 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.Self.as_wit.iface0.loc6_20: <witness> = facet_access_witness %.Self.ref.loc6_20, element0 [symbolic_self = constants.%.Self.as_wit.iface0]
+// CHECK:STDOUT:     %impl.elem0.loc6_20: %struct_type.a.b.fe2 = impl_witness_access %.Self.as_wit.iface0.loc6_20, element0 [symbolic_self = constants.%impl.elem0]
+// CHECK:STDOUT:     %true: bool = bool_literal true [concrete = constants.%true]
+// CHECK:STDOUT:     %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:     %int_2.loc6_46: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:     %.loc6_47.1: %tuple.type.f94 = tuple_literal (%int_1, %int_2.loc6_46)
+// CHECK:STDOUT:     %.loc6_48.1: %struct_type.a.b.aa4 = struct_literal (%true, %.loc6_47.1)
+// CHECK:STDOUT:     %impl.elem0.loc6_47.1: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc6_47.1: <bound method> = bound_method %int_1, %impl.elem0.loc6_47.1 [concrete = constants.%Convert.bound.ab5]
+// CHECK:STDOUT:     %specific_fn.loc6_47.1: <specific function> = specific_function %impl.elem0.loc6_47.1, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc6_47.2: <bound method> = bound_method %int_1, %specific_fn.loc6_47.1 [concrete = constants.%bound_method.9a1]
+// CHECK:STDOUT:     %int.convert_checked.loc6_47.1: init %i32 = call %bound_method.loc6_47.2(%int_1) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %.loc6_47.2: %i32 = value_of_initializer %int.convert_checked.loc6_47.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %.loc6_47.3: %i32 = converted %int_1, %.loc6_47.2 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %impl.elem0.loc6_47.2: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc6_47.3: <bound method> = bound_method %int_2.loc6_46, %impl.elem0.loc6_47.2 [concrete = constants.%Convert.bound.ef9]
+// CHECK:STDOUT:     %specific_fn.loc6_47.2: <specific function> = specific_function %impl.elem0.loc6_47.2, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc6_47.4: <bound method> = bound_method %int_2.loc6_46, %specific_fn.loc6_47.2 [concrete = constants.%bound_method.b92]
+// CHECK:STDOUT:     %int.convert_checked.loc6_47.2: init %i32 = call %bound_method.loc6_47.4(%int_2.loc6_46) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %.loc6_47.4: %i32 = value_of_initializer %int.convert_checked.loc6_47.2 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %.loc6_47.5: %i32 = converted %int_2.loc6_46, %.loc6_47.4 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %tuple.loc6_47: %tuple.type.d07 = tuple_value (%.loc6_47.3, %.loc6_47.5) [concrete = constants.%tuple]
+// CHECK:STDOUT:     %.loc6_48.2: %tuple.type.d07 = converted %.loc6_47.1, %tuple.loc6_47 [concrete = constants.%tuple]
+// CHECK:STDOUT:     %struct.loc6_48: %struct_type.a.b.fe2 = struct_value (%true, %.loc6_48.2) [concrete = constants.%struct]
+// CHECK:STDOUT:     %.loc6_48.3: %struct_type.a.b.fe2 = converted %.loc6_48.1, %struct.loc6_48 [concrete = constants.%struct]
+// CHECK:STDOUT:     %.Self.ref.loc6_54: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:     %X.ref.loc6_54: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.e7f]
+// CHECK:STDOUT:     %.Self.as_type.loc6_54: type = facet_access_type %.Self.ref.loc6_54 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.loc6_54: type = converted %.Self.ref.loc6_54, %.Self.as_type.loc6_54 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.Self.as_wit.iface0.loc6_54: <witness> = facet_access_witness %.Self.ref.loc6_54, element0 [symbolic_self = constants.%.Self.as_wit.iface0]
+// CHECK:STDOUT:     %impl.elem0.loc6_54: %struct_type.a.b.fe2 = impl_witness_access %.Self.as_wit.iface0.loc6_54, element0 [symbolic_self = constants.%impl.elem0]
+// CHECK:STDOUT:     %false: bool = bool_literal false [concrete = constants.%false]
+// CHECK:STDOUT:     %.loc6_65: bool = not %false [concrete = constants.%true]
+// CHECK:STDOUT:     %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3]
+// CHECK:STDOUT:     %int_2.loc6_86: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:     %impl.elem0.loc6_84: %.76a = impl_witness_access constants.%impl_witness.e4a, element0 [concrete = constants.%Op.667]
+// CHECK:STDOUT:     %bound_method.loc6_84: <bound method> = bound_method %int_3, %impl.elem0.loc6_84 [concrete = constants.%Op.bound.645]
+// CHECK:STDOUT:     %int.ssub: init Core.IntLiteral = call %bound_method.loc6_84(%int_3, %int_2.loc6_86) [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:     %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4]
+// CHECK:STDOUT:     %int_2.loc6_93: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:     %impl.elem0.loc6_91: %.cf0 = impl_witness_access constants.%impl_witness.586, element0 [concrete = constants.%Op.553]
+// CHECK:STDOUT:     %bound_method.loc6_91: <bound method> = bound_method %int_4, %impl.elem0.loc6_91 [concrete = constants.%Op.bound.2e7]
+// CHECK:STDOUT:     %int.sdiv: init Core.IntLiteral = call %bound_method.loc6_91(%int_4, %int_2.loc6_93) [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:     %.loc6_94.1: %tuple.type.f94 = tuple_literal (%int.ssub, %int.sdiv)
+// CHECK:STDOUT:     %.loc6_95.1: %struct_type.a.b.aa4 = struct_literal (%.loc6_65, %.loc6_94.1)
+// CHECK:STDOUT:     %impl.elem0.loc6_94.1: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc6_94.1: <bound method> = bound_method %int.ssub, %impl.elem0.loc6_94.1 [concrete = constants.%Convert.bound.ab5]
+// CHECK:STDOUT:     %specific_fn.loc6_94.1: <specific function> = specific_function %impl.elem0.loc6_94.1, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc6_94.2: <bound method> = bound_method %int.ssub, %specific_fn.loc6_94.1 [concrete = constants.%bound_method.9a1]
+// CHECK:STDOUT:     %.loc6_84.1: Core.IntLiteral = value_of_initializer %int.ssub [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:     %.loc6_84.2: Core.IntLiteral = converted %int.ssub, %.loc6_84.1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:     %int.convert_checked.loc6_94.1: init %i32 = call %bound_method.loc6_94.2(%.loc6_84.2) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %.loc6_94.2: %i32 = value_of_initializer %int.convert_checked.loc6_94.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %.loc6_94.3: %i32 = converted %int.ssub, %.loc6_94.2 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %impl.elem0.loc6_94.2: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc6_94.3: <bound method> = bound_method %int.sdiv, %impl.elem0.loc6_94.2 [concrete = constants.%Convert.bound.ef9]
+// CHECK:STDOUT:     %specific_fn.loc6_94.2: <specific function> = specific_function %impl.elem0.loc6_94.2, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc6_94.4: <bound method> = bound_method %int.sdiv, %specific_fn.loc6_94.2 [concrete = constants.%bound_method.b92]
+// CHECK:STDOUT:     %.loc6_91.1: Core.IntLiteral = value_of_initializer %int.sdiv [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:     %.loc6_91.2: Core.IntLiteral = converted %int.sdiv, %.loc6_91.1 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:     %int.convert_checked.loc6_94.2: init %i32 = call %bound_method.loc6_94.4(%.loc6_91.2) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %.loc6_94.4: %i32 = value_of_initializer %int.convert_checked.loc6_94.2 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %.loc6_94.5: %i32 = converted %int.sdiv, %.loc6_94.4 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %tuple.loc6_94: %tuple.type.d07 = tuple_value (%.loc6_94.3, %.loc6_94.5) [concrete = constants.%tuple]
+// CHECK:STDOUT:     %.loc6_95.2: %tuple.type.d07 = converted %.loc6_94.1, %tuple.loc6_94 [concrete = constants.%tuple]
+// CHECK:STDOUT:     %struct.loc6_95: %struct_type.a.b.fe2 = struct_value (%.loc6_65, %.loc6_95.2) [concrete = constants.%struct]
+// CHECK:STDOUT:     %.loc6_95.3: %struct_type.a.b.fe2 = converted %.loc6_95.1, %struct.loc6_95 [concrete = constants.%struct]
+// CHECK:STDOUT:     %.loc6_14: type = where_expr %.Self [concrete = constants.%I_where.type] {
+// CHECK:STDOUT:       requirement_rewrite %impl.elem0.loc6_20, %.loc6_48.3
+// CHECK:STDOUT:       requirement_rewrite %impl.elem0.loc6_54, %.loc6_95.3
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness: <witness> = impl_witness (%impl_witness_assoc_constant) [concrete = constants.%impl_witness.f56]
+// CHECK:STDOUT:   %impl_witness_assoc_constant: %struct_type.a.b.fe2 = impl_witness_assoc_constant constants.%struct [concrete = constants.%struct]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @I {
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.826]
+// CHECK:STDOUT:   %X: %struct_type.a.b.fe2 = assoc_const_decl @X [concrete] {
+// CHECK:STDOUT:     %assoc0: %I.assoc_type = assoc_entity element0, @I.%X [concrete = constants.%assoc0.e7f]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .X = @X.%assoc0
+// CHECK:STDOUT:   witness = (%X)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic assoc_const @X(@I.%Self: %I.type) {
+// CHECK:STDOUT:   assoc_const X:! %struct_type.a.b.fe2;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.540: %.loc6_7.2 as %.loc6_14 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = file.%impl_witness
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @X(constants.%Self.826) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @X(constants.%I.facet) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_two_different_non_type.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
+// CHECK:STDOUT:   %Self.826: %I.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete]
+// CHECK:STDOUT:   %struct_type.a.b.fe2: type = struct_type {.a: bool, .b: %tuple.type.d07} [concrete]
+// CHECK:STDOUT:   %I.assoc_type: type = assoc_entity_type @I [concrete]
+// CHECK:STDOUT:   %assoc0.e7f: %I.assoc_type = assoc_entity element0, @I.%X [concrete]
+// CHECK:STDOUT:   %.Self: %I.type = bind_symbolic_name .Self [symbolic_self]
+// CHECK:STDOUT:   %.Self.as_type: type = facet_access_type %.Self [symbolic_self]
+// CHECK:STDOUT:   %.Self.as_wit.iface0: <witness> = facet_access_witness %.Self, element0 [symbolic_self]
+// CHECK:STDOUT:   %I.facet: %I.type = facet_value %.Self.as_type, (%.Self.as_wit.iface0) [symbolic_self]
+// CHECK:STDOUT:   %impl.elem0: %struct_type.a.b.fe2 = impl_witness_access %.Self.as_wit.iface0, element0 [symbolic_self]
+// CHECK:STDOUT:   %true: bool = bool_literal true [concrete]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %int_2.ecc: Core.IntLiteral = int_value 2 [concrete]
+// CHECK:STDOUT:   %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete]
+// CHECK:STDOUT:   %struct_type.a.b.aa4: type = struct_type {.a: bool, .b: %tuple.type.f94} [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.205: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
+// CHECK:STDOUT:   %Convert.type.1b6: type = fn_type @Convert.1, @ImplicitAs(%i32) [concrete]
+// CHECK:STDOUT:   %impl_witness.d39: <witness> = impl_witness (imports.%Core.import_ref.a5b), @impl.4f9(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.type.035: type = fn_type @Convert.2, @impl.4f9(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.956: %Convert.type.035 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.205 = facet_value Core.IntLiteral, (%impl_witness.d39) [concrete]
+// CHECK:STDOUT:   %.be7: type = fn_type_with_self_type %Convert.type.1b6, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Convert.bound.ab5: <bound method> = bound_method %int_1.5b8, %Convert.956 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.956, @Convert.2(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method.9a1: <bound method> = bound_method %int_1.5b8, %Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_1.5d2: %i32 = int_value 1 [concrete]
+// CHECK:STDOUT:   %Convert.bound.ef9: <bound method> = bound_method %int_2.ecc, %Convert.956 [concrete]
+// CHECK:STDOUT:   %bound_method.b92: <bound method> = bound_method %int_2.ecc, %Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_2.ef8: %i32 = int_value 2 [concrete]
+// CHECK:STDOUT:   %tuple.21c: %tuple.type.d07 = tuple_value (%int_1.5d2, %int_2.ef8) [concrete]
+// CHECK:STDOUT:   %struct.682: %struct_type.a.b.fe2 = struct_value (%true, %tuple.21c) [concrete]
+// CHECK:STDOUT:   %false: bool = bool_literal false [concrete]
+// CHECK:STDOUT:   %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
+// CHECK:STDOUT:   %int_4.0c1: Core.IntLiteral = int_value 4 [concrete]
+// CHECK:STDOUT:   %Convert.bound.b30: <bound method> = bound_method %int_3.1ba, %Convert.956 [concrete]
+// CHECK:STDOUT:   %bound_method.047: <bound method> = bound_method %int_3.1ba, %Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_3.822: %i32 = int_value 3 [concrete]
+// CHECK:STDOUT:   %Convert.bound.ac3: <bound method> = bound_method %int_4.0c1, %Convert.956 [concrete]
+// CHECK:STDOUT:   %bound_method.1da: <bound method> = bound_method %int_4.0c1, %Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_4.940: %i32 = int_value 4 [concrete]
+// CHECK:STDOUT:   %tuple.ffd: %tuple.type.d07 = tuple_value (%int_3.822, %int_4.940) [concrete]
+// CHECK:STDOUT:   %struct.68c: %struct_type.a.b.fe2 = struct_value (%false, %tuple.ffd) [concrete]
+// CHECK:STDOUT:   %I_where.type: type = facet_type <@I where %impl.elem0 = %struct.682 and %impl.elem0 = %struct.68c> [concrete]
+// CHECK:STDOUT:   %impl_witness.85b: <witness> = impl_witness (<error>) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Bool = %Core.Bool
+// CHECK:STDOUT:     .Int = %Core.Int
+// CHECK:STDOUT:     .ImplicitAs = %Core.ImplicitAs
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {}
+// CHECK:STDOUT:   impl_decl @impl.347 [concrete] {} {
+// CHECK:STDOUT:     %.loc10_7.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:     %.loc10_7.2: type = converted %.loc10_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:     %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type]
+// CHECK:STDOUT:     %.Self: %I.type = bind_symbolic_name .Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:     %.Self.ref.loc10_20: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:     %X.ref.loc10_20: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.e7f]
+// CHECK:STDOUT:     %.Self.as_type.loc10_20: type = facet_access_type %.Self.ref.loc10_20 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.loc10_20: type = converted %.Self.ref.loc10_20, %.Self.as_type.loc10_20 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.Self.as_wit.iface0.loc10_20: <witness> = facet_access_witness %.Self.ref.loc10_20, element0 [symbolic_self = constants.%.Self.as_wit.iface0]
+// CHECK:STDOUT:     %impl.elem0.loc10_20: %struct_type.a.b.fe2 = impl_witness_access %.Self.as_wit.iface0.loc10_20, element0 [symbolic_self = constants.%impl.elem0]
+// CHECK:STDOUT:     %true: bool = bool_literal true [concrete = constants.%true]
+// CHECK:STDOUT:     %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:     %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:     %.loc10_47.1: %tuple.type.f94 = tuple_literal (%int_1, %int_2)
+// CHECK:STDOUT:     %.loc10_48.1: %struct_type.a.b.aa4 = struct_literal (%true, %.loc10_47.1)
+// CHECK:STDOUT:     %impl.elem0.loc10_47.1: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc10_47.1: <bound method> = bound_method %int_1, %impl.elem0.loc10_47.1 [concrete = constants.%Convert.bound.ab5]
+// CHECK:STDOUT:     %specific_fn.loc10_47.1: <specific function> = specific_function %impl.elem0.loc10_47.1, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc10_47.2: <bound method> = bound_method %int_1, %specific_fn.loc10_47.1 [concrete = constants.%bound_method.9a1]
+// CHECK:STDOUT:     %int.convert_checked.loc10_47.1: init %i32 = call %bound_method.loc10_47.2(%int_1) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %.loc10_47.2: %i32 = value_of_initializer %int.convert_checked.loc10_47.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %.loc10_47.3: %i32 = converted %int_1, %.loc10_47.2 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:     %impl.elem0.loc10_47.2: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc10_47.3: <bound method> = bound_method %int_2, %impl.elem0.loc10_47.2 [concrete = constants.%Convert.bound.ef9]
+// CHECK:STDOUT:     %specific_fn.loc10_47.2: <specific function> = specific_function %impl.elem0.loc10_47.2, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc10_47.4: <bound method> = bound_method %int_2, %specific_fn.loc10_47.2 [concrete = constants.%bound_method.b92]
+// CHECK:STDOUT:     %int.convert_checked.loc10_47.2: init %i32 = call %bound_method.loc10_47.4(%int_2) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %.loc10_47.4: %i32 = value_of_initializer %int.convert_checked.loc10_47.2 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %.loc10_47.5: %i32 = converted %int_2, %.loc10_47.4 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:     %tuple.loc10_47: %tuple.type.d07 = tuple_value (%.loc10_47.3, %.loc10_47.5) [concrete = constants.%tuple.21c]
+// CHECK:STDOUT:     %.loc10_48.2: %tuple.type.d07 = converted %.loc10_47.1, %tuple.loc10_47 [concrete = constants.%tuple.21c]
+// CHECK:STDOUT:     %struct.loc10_48: %struct_type.a.b.fe2 = struct_value (%true, %.loc10_48.2) [concrete = constants.%struct.682]
+// CHECK:STDOUT:     %.loc10_48.3: %struct_type.a.b.fe2 = converted %.loc10_48.1, %struct.loc10_48 [concrete = constants.%struct.682]
+// CHECK:STDOUT:     %.Self.ref.loc10_54: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:     %X.ref.loc10_54: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.e7f]
+// CHECK:STDOUT:     %.Self.as_type.loc10_54: type = facet_access_type %.Self.ref.loc10_54 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.loc10_54: type = converted %.Self.ref.loc10_54, %.Self.as_type.loc10_54 [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:     %.Self.as_wit.iface0.loc10_54: <witness> = facet_access_witness %.Self.ref.loc10_54, element0 [symbolic_self = constants.%.Self.as_wit.iface0]
+// CHECK:STDOUT:     %impl.elem0.loc10_54: %struct_type.a.b.fe2 = impl_witness_access %.Self.as_wit.iface0.loc10_54, element0 [symbolic_self = constants.%impl.elem0]
+// CHECK:STDOUT:     %false: bool = bool_literal false [concrete = constants.%false]
+// CHECK:STDOUT:     %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
+// CHECK:STDOUT:     %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1]
+// CHECK:STDOUT:     %.loc10_82.1: %tuple.type.f94 = tuple_literal (%int_3, %int_4)
+// CHECK:STDOUT:     %.loc10_83.1: %struct_type.a.b.aa4 = struct_literal (%false, %.loc10_82.1)
+// CHECK:STDOUT:     %impl.elem0.loc10_82.1: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc10_82.1: <bound method> = bound_method %int_3, %impl.elem0.loc10_82.1 [concrete = constants.%Convert.bound.b30]
+// CHECK:STDOUT:     %specific_fn.loc10_82.1: <specific function> = specific_function %impl.elem0.loc10_82.1, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc10_82.2: <bound method> = bound_method %int_3, %specific_fn.loc10_82.1 [concrete = constants.%bound_method.047]
+// CHECK:STDOUT:     %int.convert_checked.loc10_82.1: init %i32 = call %bound_method.loc10_82.2(%int_3) [concrete = constants.%int_3.822]
+// CHECK:STDOUT:     %.loc10_82.2: %i32 = value_of_initializer %int.convert_checked.loc10_82.1 [concrete = constants.%int_3.822]
+// CHECK:STDOUT:     %.loc10_82.3: %i32 = converted %int_3, %.loc10_82.2 [concrete = constants.%int_3.822]
+// CHECK:STDOUT:     %impl.elem0.loc10_82.2: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:     %bound_method.loc10_82.3: <bound method> = bound_method %int_4, %impl.elem0.loc10_82.2 [concrete = constants.%Convert.bound.ac3]
+// CHECK:STDOUT:     %specific_fn.loc10_82.2: <specific function> = specific_function %impl.elem0.loc10_82.2, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn]
+// CHECK:STDOUT:     %bound_method.loc10_82.4: <bound method> = bound_method %int_4, %specific_fn.loc10_82.2 [concrete = constants.%bound_method.1da]
+// CHECK:STDOUT:     %int.convert_checked.loc10_82.2: init %i32 = call %bound_method.loc10_82.4(%int_4) [concrete = constants.%int_4.940]
+// CHECK:STDOUT:     %.loc10_82.4: %i32 = value_of_initializer %int.convert_checked.loc10_82.2 [concrete = constants.%int_4.940]
+// CHECK:STDOUT:     %.loc10_82.5: %i32 = converted %int_4, %.loc10_82.4 [concrete = constants.%int_4.940]
+// CHECK:STDOUT:     %tuple.loc10_82: %tuple.type.d07 = tuple_value (%.loc10_82.3, %.loc10_82.5) [concrete = constants.%tuple.ffd]
+// CHECK:STDOUT:     %.loc10_83.2: %tuple.type.d07 = converted %.loc10_82.1, %tuple.loc10_82 [concrete = constants.%tuple.ffd]
+// CHECK:STDOUT:     %struct.loc10_83: %struct_type.a.b.fe2 = struct_value (%false, %.loc10_83.2) [concrete = constants.%struct.68c]
+// CHECK:STDOUT:     %.loc10_83.3: %struct_type.a.b.fe2 = converted %.loc10_83.1, %struct.loc10_83 [concrete = constants.%struct.68c]
+// CHECK:STDOUT:     %.loc10_14: type = where_expr %.Self [concrete = constants.%I_where.type] {
+// CHECK:STDOUT:       requirement_rewrite %impl.elem0.loc10_20, %.loc10_48.3
+// CHECK:STDOUT:       requirement_rewrite %impl.elem0.loc10_54, %.loc10_83.3
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness: <witness> = impl_witness (<error>) [concrete = constants.%impl_witness.85b]
+// CHECK:STDOUT:   %impl_witness_assoc_constant: %struct_type.a.b.fe2 = impl_witness_assoc_constant constants.%struct.682 [concrete = constants.%struct.682]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @I {
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.826]
+// CHECK:STDOUT:   %X: %struct_type.a.b.fe2 = assoc_const_decl @X [concrete] {
+// CHECK:STDOUT:     %assoc0: %I.assoc_type = assoc_entity element0, @I.%X [concrete = constants.%assoc0.e7f]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .X = @X.%assoc0
+// CHECK:STDOUT:   witness = (%X)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic assoc_const @X(@I.%Self: %I.type) {
+// CHECK:STDOUT:   assoc_const X:! %struct_type.a.b.fe2;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.347: %.loc10_7.2 as %.loc10_14 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = file.%impl_witness
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @X(constants.%Self.826) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @X(constants.%I.facet) {}
+// CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/impl/no_prelude/impl_assoc_const.carbon

@@ -100,7 +100,7 @@ library "[[@TEST_NAME]]";
 
 interface L { let W:! type; }
 
-// CHECK:STDERR: fail_two_different.carbon:[[@LINE+4]]:12: error: associated constant W given two different values [AssociatedConstantWithDifferentValues]
+// CHECK:STDERR: fail_two_different.carbon:[[@LINE+4]]:12: error: associated constant W given two different values `()` and `{}` [AssociatedConstantWithDifferentValues]
 // CHECK:STDERR: impl () as L where .W = {} and .W = () {}
 // CHECK:STDERR:            ^~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:

+ 1 - 1
toolchain/check/testdata/impl/no_prelude/import_interface_assoc_const.carbon

@@ -117,7 +117,7 @@ import library "interface";
 
 class C7 { }
 
-// CHECK:STDERR: fail_two_different.carbon:[[@LINE+4]]:12: error: associated constant T given two different values [AssociatedConstantWithDifferentValues]
+// CHECK:STDERR: fail_two_different.carbon:[[@LINE+4]]:12: error: associated constant T given two different values `{}` and `()` [AssociatedConstantWithDifferentValues]
 // CHECK:STDERR: impl C7 as I where .T = {} and .T = () { }
 // CHECK:STDERR:            ^~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:

+ 4 - 4
toolchain/sem_ir/BUILD

@@ -145,9 +145,9 @@ cc_library(
 )
 
 cc_library(
-    name = "stringify_type",
-    srcs = ["stringify_type.cpp"],
-    hdrs = ["stringify_type.h"],
+    name = "stringify",
+    srcs = ["stringify.cpp"],
+    hdrs = ["stringify.h"],
     deps = [
         ":file",
         ":typed_insts",
@@ -241,7 +241,7 @@ cc_library(
     hdrs = ["dump.h"],
     deps = [
         ":file",
-        ":stringify_type",
+        ":stringify",
         "//common:ostream",
         "//common:raw_string_ostream",
     ],

+ 5 - 6
toolchain/sem_ir/dump.cpp

@@ -7,7 +7,7 @@
 #include "toolchain/sem_ir/dump.h"
 
 #include "common/raw_string_ostream.h"
-#include "toolchain/sem_ir/stringify_type.h"
+#include "toolchain/sem_ir/stringify.h"
 
 namespace Carbon::SemIR {
 
@@ -354,9 +354,8 @@ LLVM_DUMP_METHOD auto Dump(const File& file,
     for (auto field : block) {
       out << "\n  - " << field << DumpNameIfValid(file, field.name_id);
       if (field.type_id.has_value()) {
-        InstId inst_id =
-            file.constant_values().GetInstId(field.type_id.AsConstantId());
-        out << ": " << StringifyTypeExpr(file, inst_id);
+        InstId inst_id = file.types().GetInstId(field.type_id);
+        out << ": " << StringifyConstantInst(file, inst_id);
       }
     }
   }
@@ -386,8 +385,8 @@ LLVM_DUMP_METHOD auto Dump(const File& file, TypeId type_id) -> std::string {
     return out.TakeStr();
   }
 
-  InstId inst_id = file.constant_values().GetInstId(type_id.AsConstantId());
-  out << ": " << StringifyTypeExpr(file, inst_id) << "; "
+  InstId inst_id = file.types().GetInstId(type_id);
+  out << ": " << StringifyConstantInst(file, inst_id) << "; "
       << file.insts().Get(inst_id);
   return out.TakeStr();
 }

+ 2 - 2
toolchain/sem_ir/ids.h

@@ -707,8 +707,8 @@ constexpr StructTypeFieldsId StructTypeFieldsId::Empty = StructTypeFieldsId(0);
 struct TypeId : public IdBase<TypeId> {
   static constexpr llvm::StringLiteral Label = "type";
 
-  // `StringifyTypeExpr` is used for diagnostics. However, where possible, an
-  // `InstId` describing how the type was written should be preferred, using
+  // `StringifyConstantInst` is used for diagnostics. However, where possible,
+  // an `InstId` describing how the type was written should be preferred, using
   // `InstIdAsType` or `TypeOfInstId` as the diagnostic argument type.
   using DiagnosticType = Diagnostics::TypeInfo<std::string>;
 

+ 60 - 58
toolchain/sem_ir/stringify_type.cpp → toolchain/sem_ir/stringify.cpp

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "toolchain/sem_ir/stringify_type.h"
+#include "toolchain/sem_ir/stringify.h"
 
 #include "common/raw_string_ostream.h"
 #include "common/variant_helpers.h"
@@ -17,27 +17,27 @@
 
 namespace Carbon::SemIR {
 
-// Map an instruction kind representing a type expression into an integer
-// describing the precedence of that type's syntax. Higher numbers correspond to
+// Map an instruction kind representing an expression into an integer describing
+// the precedence of that expression's syntax. Higher numbers correspond to
 // higher precedence.
-static auto GetTypePrecedence(InstKind kind) -> int {
+static auto GetPrecedence(InstKind kind) -> int {
   if (kind == ConstType::Kind) {
     return -1;
   }
   if (kind == PointerType::Kind) {
     return -2;
   }
-  // TODO: Do any other kinds of type expression need precedence handling?
+  // TODO: Handle other kinds of expressions with precedence.
   return 0;
 }
 
 namespace {
 
-// Contains the stack of steps for `StringifyTypeExpr`.
+// Contains the stack of steps for `Stringify`.
 //
 // Note that when pushing items onto the stack, they're printed in the reverse
 // order of when they were pushed. All reference lifetimes must match the
-// lifetime of `StringifyTypeExpr`.
+// lifetime of `Stringify`.
 class StepStack {
  public:
   // An individual step in the stack, which stringifies some component of a type
@@ -185,7 +185,7 @@ class StepStack {
   llvm::SmallVector<Step> steps_;
 };
 
-// Provides `StringifyTypeInst` overloads for each instruction.
+// Provides `StringifyInst` overloads for each instruction.
 class Stringifier {
  public:
   explicit Stringifier(const SemIR::File* sem_ir, StepStack* step_stack,
@@ -194,7 +194,7 @@ class Stringifier {
 
   // By default try to print a constant, but otherwise may fail to
   // stringify.
-  auto StringifyTypeInstDefault(SemIR::InstId inst_id, Inst inst) -> void {
+  auto StringifyInstDefault(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);
@@ -210,28 +210,30 @@ class Stringifier {
   }
 
   template <typename InstT>
-  auto StringifyTypeInst(SemIR::InstId inst_id, InstT inst) -> void {
+  auto StringifyInst(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<InstT, WhereExpr>,
                   "Types should have a dedicated overload");
-    StringifyTypeInstDefault(inst_id, inst);
+    // TODO: We should have Stringify support for all types where
+    // InstT::Kind.constant_kind() is neither Never nor Indirect.
+    StringifyInstDefault(inst_id, inst);
   }
 
   // Singleton instructions use their IR name as a label.
   template <typename InstT>
     requires(IsSingletonInstKind(InstT::Kind))
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, InstT /*inst*/) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, InstT /*inst*/) -> void {
     *out_ << InstT::Kind.ir_name();
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ArrayType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, ArrayType inst) -> void {
     *out_ << "array(";
     step_stack_->Push(inst.element_type_inst_id, ", ", inst.bound_id, ")");
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, AssociatedConstantDecl inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, AssociatedConstantDecl inst)
       -> void {
     const auto& assoc_const =
         sem_ir_->associated_constants().Get(inst.assoc_const_id);
@@ -239,7 +241,7 @@ class Stringifier {
                                    assoc_const.name_id);
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, AssociatedEntityType inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, AssociatedEntityType inst)
       -> void {
     *out_ << "<associated entity in ";
     step_stack_->Push(">");
@@ -247,15 +249,19 @@ class Stringifier {
         SpecificInterface{inst.interface_id, inst.interface_specific_id});
   }
 
+  auto StringifyInst(SemIR::InstId /*inst_id*/, BoolLiteral inst) -> void {
+    step_stack_->Push(inst.value.ToBool() ? "true" : "false");
+  }
+
   template <typename InstT>
     requires(std::same_as<InstT, BindAlias> ||
              std::same_as<InstT, BindSymbolicName> ||
              std::same_as<InstT, ExportDecl>)
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, InstT inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, InstT inst) -> void {
     step_stack_->PushEntityNameId(inst.entity_name_id);
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ClassType inst) -> void {
+  auto StringifyInst(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()) {
@@ -265,12 +271,12 @@ class Stringifier {
     step_stack_->PushEntityName(class_info, inst.specific_id);
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ConstType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, ConstType inst) -> void {
     *out_ << "const ";
 
     // Add parentheses if required.
-    if (GetTypePrecedence(sem_ir_->insts().Get(inst.inner_id).kind()) <
-        GetTypePrecedence(SemIR::ConstType::Kind)) {
+    if (GetPrecedence(sem_ir_->insts().Get(inst.inner_id).kind()) <
+        GetPrecedence(SemIR::ConstType::Kind)) {
       *out_ << "(";
       // Note the `inst.inner_id` ends up here.
       step_stack_->PushString(")");
@@ -279,20 +285,19 @@ class Stringifier {
     step_stack_->PushInstId(inst.inner_id);
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FacetAccessType inst)
-      -> void {
+  auto StringifyInst(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)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, FacetAccessWitness inst)
       -> void {
     *out_ << "<witness for ";
     step_stack_->Push(inst.facet_value_inst_id, ", interface ", inst.index,
                       ">");
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FacetType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, FacetType inst) -> void {
     const FacetTypeInfo& facet_type_info =
         sem_ir_->facet_types().Get(inst.facet_type_id);
     // Output `where` restrictions.
@@ -335,12 +340,12 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FacetValue inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, FacetValue inst) -> void {
     // No need to output the witness.
     step_stack_->Push(inst.type_inst_id, " as ", inst.type_id);
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FloatType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, FloatType inst) -> void {
     // TODO: Is this okay?
     if (auto width_value =
             sem_ir_->insts().TryGetAs<IntValue>(inst.bit_width_id)) {
@@ -352,15 +357,15 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, FunctionType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, FunctionType inst) -> void {
     const auto& fn = sem_ir_->functions().Get(inst.function_id);
     *out_ << "<type of ";
     step_stack_->Push(
         StepStack::QualifiedNameItem{fn.parent_scope_id, fn.name_id}, ">");
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/,
-                         FunctionTypeWithSelfType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, FunctionTypeWithSelfType inst)
+      -> void {
     StepStack::PushItem fn_name = SemIR::InstId::None;
     if (auto fn_inst = sem_ir_->insts().TryGetAs<FunctionType>(
             inst.interface_function_type_id)) {
@@ -374,8 +379,7 @@ class Stringifier {
     step_stack_->Push(fn_name, " in ", inst.self_id, ">");
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, GenericClassType inst)
-      -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, GenericClassType inst) -> void {
     const auto& class_info = sem_ir_->classes().Get(inst.class_id);
     *out_ << "<type of ";
     step_stack_->Push(StepStack::QualifiedNameItem{class_info.parent_scope_id,
@@ -383,7 +387,7 @@ class Stringifier {
                       ">");
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, GenericInterfaceType inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, GenericInterfaceType inst)
       -> void {
     const auto& interface = sem_ir_->interfaces().Get(inst.interface_id);
     *out_ << "<type of ";
@@ -420,7 +424,7 @@ class Stringifier {
     return std::nullopt;
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ImplWitnessAccess inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, ImplWitnessAccess inst)
       -> void {
     auto witness_inst_id =
         sem_ir_->constant_values().GetConstantInstId(inst.witness_id);
@@ -475,7 +479,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, ImportRefUnloaded inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, ImportRefUnloaded inst)
       -> void {
     if (inst.entity_name_id.has_value()) {
       step_stack_->PushEntityNameId(inst.entity_name_id);
@@ -484,7 +488,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, IntType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, IntType inst) -> void {
     *out_ << "<builtin ";
     step_stack_->PushString(">");
     if (auto width_value =
@@ -497,33 +501,32 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, IntValue inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, IntValue inst) -> void {
     sem_ir_->ints().Get(inst.int_id).print(*out_, /*isSigned=*/true);
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, LookupImplWitness inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, LookupImplWitness inst)
       -> void {
     step_stack_->Push(
         inst.query_self_inst_id, " as ",
         sem_ir_->specific_interfaces().Get(inst.query_specific_interface_id));
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, NameRef inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, NameRef inst) -> void {
     *out_ << sem_ir_->names().GetFormatted(inst.name_id);
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, Namespace inst) -> void {
+  auto StringifyInst(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 {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, PointerType inst) -> void {
     step_stack_->Push(inst.pointee_id, "*");
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, SpecificFunction inst)
-      -> void {
+  auto StringifyInst(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),
@@ -533,7 +536,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, SpecificImplFunction inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, SpecificImplFunction inst)
       -> void {
     auto callee = SemIR::GetCalleeFunction(*sem_ir_, inst.callee_id);
     if (callee.function_id.has_value()) {
@@ -547,7 +550,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, StructType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, StructType inst) -> void {
     auto fields = sem_ir_->struct_type_fields().Get(inst.fields_id);
     if (fields.empty()) {
       *out_ << "{}";
@@ -561,7 +564,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, StructValue inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, StructValue inst) -> void {
     auto field_values = sem_ir_->inst_blocks().Get(inst.elements_id);
     if (field_values.empty()) {
       *out_ << "{}";
@@ -583,7 +586,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, TupleType inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, TupleType inst) -> void {
     auto refs = sem_ir_->type_blocks().Get(inst.elements_id);
     if (refs.empty()) {
       *out_ << "()";
@@ -602,7 +605,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, TupleValue inst) -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, TupleValue inst) -> void {
     auto refs = sem_ir_->inst_blocks().Get(inst.elements_id);
     if (refs.empty()) {
       *out_ << "()";
@@ -621,7 +624,7 @@ class Stringifier {
     }
   }
 
-  auto StringifyTypeInst(SemIR::InstId inst_id, TypeOfInst /*inst*/) -> void {
+  auto StringifyInst(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) {
@@ -631,14 +634,13 @@ class Stringifier {
     *out_ << "<dependent type>";
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, UnboundElementType inst)
+  auto StringifyInst(SemIR::InstId /*inst_id*/, UnboundElementType inst)
       -> void {
     *out_ << "<unbound element of class ";
     step_stack_->Push(inst.class_type_inst_id, ">");
   }
 
-  auto StringifyTypeInst(SemIR::InstId /*inst_id*/, VtablePtr /*inst*/)
-      -> void {
+  auto StringifyInst(SemIR::InstId /*inst_id*/, VtablePtr /*inst*/) -> void {
     *out_ << "<vtable ptr>";
   }
 
@@ -663,15 +665,15 @@ static auto Stringify(const SemIR::File& sem_ir, StepStack& step_stack)
         step,
         [&](InstId inst_id) {
           if (!inst_id.has_value()) {
-            out << "<invalid type>";
+            out << "<invalid>";
             return;
           }
           auto untyped_inst = sem_ir.insts().Get(inst_id);
           CARBON_KIND_SWITCH(untyped_inst) {
-#define CARBON_SEM_IR_INST_KIND(InstT)                  \
-  case CARBON_KIND(InstT typed_inst): {                 \
-    stringifier.StringifyTypeInst(inst_id, typed_inst); \
-    break;                                              \
+#define CARBON_SEM_IR_INST_KIND(InstT)              \
+  case CARBON_KIND(InstT typed_inst): {             \
+    stringifier.StringifyInst(inst_id, typed_inst); \
+    break;                                          \
   }
 #include "toolchain/sem_ir/inst_kind.def"
           }
@@ -684,7 +686,7 @@ static auto Stringify(const SemIR::File& sem_ir, StepStack& step_stack)
   return out.TakeStr();
 }
 
-auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id)
+auto StringifyConstantInst(const SemIR::File& sem_ir, InstId outer_inst_id)
     -> std::string {
   StepStack step_stack(&sem_ir);
   step_stack.PushInstId(outer_inst_id);
@@ -701,7 +703,7 @@ auto StringifySpecific(const File& sem_ir, SpecificId specific_id)
   CARBON_KIND_SWITCH(decl) {
     case CARBON_KIND(SemIR::ClassDecl class_decl): {
       // Print `Core.Int(N)` as `iN`.
-      // TODO: This duplicates work done in StringifyTypeInst for ClassType.
+      // TODO: This duplicates work done in StringifyInst for ClassType.
       const auto& class_info = sem_ir.classes().Get(class_decl.class_id);
       if (auto literal_info = NumericTypeLiteralInfo::ForType(
               sem_ir,

+ 30 - 0
toolchain/sem_ir/stringify.h

@@ -0,0 +1,30 @@
+// 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
+
+#ifndef CARBON_TOOLCHAIN_SEM_IR_STRINGIFY_H_
+#define CARBON_TOOLCHAIN_SEM_IR_STRINGIFY_H_
+
+#include "toolchain/sem_ir/file.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::SemIR {
+
+// Produces a string version of an instruction with a constant value. Generally,
+// this should not be called directly. To format a constant value into a
+// diagnostic, use a diagnostic parameter of type `InstIdAsConstant`. When the
+// constant value is a type, use `InstIdAsType`, `InstIdAsRawType`, or
+// `TypeOfInstId` where possible, or of type `TypeId` or `TypeIdAsRawType` if
+// you don't have an expression describing the type.
+auto StringifyConstantInst(const File& sem_ir, InstId outer_inst_id)
+    -> std::string;
+
+// Produces a string version of the name of a specific. Generally, this should
+// not be called directly. To format a string into a diagnostic, use a
+// diagnostic parameter of type `SemIR::SpecificId`.
+auto StringifySpecific(const File& sem_ir, SpecificId specific_id)
+    -> std::string;
+
+}  // namespace Carbon::SemIR
+
+#endif  // CARBON_TOOLCHAIN_SEM_IR_STRINGIFY_H_

+ 0 - 28
toolchain/sem_ir/stringify_type.h

@@ -1,28 +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
-
-#ifndef CARBON_TOOLCHAIN_SEM_IR_STRINGIFY_TYPE_H_
-#define CARBON_TOOLCHAIN_SEM_IR_STRINGIFY_TYPE_H_
-
-#include "toolchain/sem_ir/file.h"
-#include "toolchain/sem_ir/ids.h"
-
-namespace Carbon::SemIR {
-
-// Produces a string version of a type expression. Generally, this should not be
-// called directly. To format a string into a diagnostic, use a diagnostic
-// parameter of type `InstIdAsType`, `InstIdAsRawType`, or `TypeOfInstId` where
-// possible, or of type `TypeId` or `TypeIdAsRawType` if you don't have an
-// expression describing the type.
-auto StringifyTypeExpr(const File& sem_ir, InstId outer_inst_id) -> std::string;
-
-// Produces a string version of the name of a specific. Generally, this should
-// not be called directly. To format a string into a diagnostic, use a
-// diagnostic parameter of type `SemIR::SpecificId`.
-auto StringifySpecific(const File& sem_ir, SpecificId specific_id)
-    -> std::string;
-
-}  // namespace Carbon::SemIR
-
-#endif  // CARBON_TOOLCHAIN_SEM_IR_STRINGIFY_TYPE_H_