Kaynağa Gözat

Change TypeId to be a thin wrapper around ConstantId. (#4140)

This better follows the principle that types are simply constants of
type `type`, and allows more uniform treatment of types as just another
kind of constant from generics handling.

Use a hash table to map from `TypeId` to information about the complete
type. This makes basic operations on types a bit simpler, and operations
that actually need to access the complete class information a bit more
complex.
Richard Smith 1 yıl önce
ebeveyn
işleme
dde0bd0ffe

+ 11 - 5
toolchain/check/context.cpp

@@ -734,7 +734,7 @@ class TypeCompleter {
 
       case Phase::BuildValueRepr: {
         auto value_rep = BuildValueRepr(type_id, inst);
-        context_.sem_ir().CompleteType(type_id, value_rep);
+        context_.types().SetValueRepr(type_id, value_rep);
         CARBON_CHECK(old_work_list_size == work_list_.size())
             << "BuildValueRepr should not change work items";
         work_list_.pop_back();
@@ -1107,10 +1107,16 @@ auto Context::GetTypeIdForTypeConstant(SemIR::ConstantId constant_id)
     -> SemIR::TypeId {
   CARBON_CHECK(constant_id.is_constant())
       << "Canonicalizing non-constant type: " << constant_id;
-
-  auto result = type_ids_for_type_constants_.Insert(
-      constant_id, [&]() { return types().Add({.constant_id = constant_id}); });
-  return result.value();
+  auto type_id =
+      insts().Get(constant_values().GetInstId(constant_id)).type_id();
+  // TODO: For now, we allow values of facet type to be used as types.
+  CARBON_CHECK(type_id == SemIR::TypeId::TypeType ||
+               types().Is<SemIR::InterfaceType>(type_id) ||
+               constant_id == SemIR::ConstantId::Error)
+      << "Forming type ID for non-type constant of type "
+      << types().GetAsInst(type_id);
+
+  return SemIR::TypeId::ForTypeConstant(constant_id);
 }
 
 // Gets or forms a type_id for a type, given the instruction kind and arguments.

+ 15 - 12
toolchain/check/testdata/basics/builtin_insts.carbon

@@ -24,7 +24,9 @@
 // CHECK:STDOUT:   generics:        {}
 // CHECK:STDOUT:   generic_instances: {}
 // CHECK:STDOUT:   types:
-// CHECK:STDOUT:     type0:           {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:     typeTypeType:    {kind: copy, type: typeTypeType}
+// CHECK:STDOUT:     typeError:       {kind: copy, type: typeError}
+// CHECK:STDOUT:     'type(instNamespaceType)': {kind: copy, type: type(instNamespaceType)}
 // CHECK:STDOUT:   type_blocks:     {}
 // CHECK:STDOUT:   insts:
 // CHECK:STDOUT:     instTypeType:    {kind: BuiltinInst, arg0: TypeType, type: typeTypeType}
@@ -36,18 +38,19 @@
 // CHECK:STDOUT:     instBoundMethodType: {kind: BuiltinInst, arg0: BoundMethodType, type: typeTypeType}
 // CHECK:STDOUT:     instNamespaceType: {kind: BuiltinInst, arg0: NamespaceType, type: typeTypeType}
 // CHECK:STDOUT:     instWitnessType: {kind: BuiltinInst, arg0: WitnessType, type: typeTypeType}
-// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
+// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
 // CHECK:STDOUT:   constant_values:
-// CHECK:STDOUT:     instTypeType:    template instTypeType
-// CHECK:STDOUT:     instError:       template instError
-// CHECK:STDOUT:     instBoolType:    template instBoolType
-// CHECK:STDOUT:     instIntType:     template instIntType
-// CHECK:STDOUT:     instFloatType:   template instFloatType
-// CHECK:STDOUT:     instStringType:  template instStringType
-// CHECK:STDOUT:     instBoundMethodType: template instBoundMethodType
-// CHECK:STDOUT:     instNamespaceType: template instNamespaceType
-// CHECK:STDOUT:     instWitnessType: template instWitnessType
-// CHECK:STDOUT:     'inst+0':          template inst+0
+// CHECK:STDOUT:     instTypeType:    templateConstant(instTypeType)
+// CHECK:STDOUT:     instError:       templateConstant(instError)
+// CHECK:STDOUT:     instBoolType:    templateConstant(instBoolType)
+// CHECK:STDOUT:     instIntType:     templateConstant(instIntType)
+// CHECK:STDOUT:     instFloatType:   templateConstant(instFloatType)
+// CHECK:STDOUT:     instStringType:  templateConstant(instStringType)
+// CHECK:STDOUT:     instBoundMethodType: templateConstant(instBoundMethodType)
+// CHECK:STDOUT:     instNamespaceType: templateConstant(instNamespaceType)
+// CHECK:STDOUT:     instWitnessType: templateConstant(instWitnessType)
+// CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)
+// CHECK:STDOUT:   symbolic_constants: {}
 // CHECK:STDOUT:   inst_blocks:
 // CHECK:STDOUT:     empty:           {}
 // CHECK:STDOUT:     exports:         {}

+ 43 - 37
toolchain/check/testdata/basics/no_prelude/multifile_raw_and_textual_ir.carbon

@@ -41,24 +41,27 @@ fn B() {
 // CHECK:STDOUT:   generics:        {}
 // CHECK:STDOUT:   generic_instances: {}
 // CHECK:STDOUT:   types:
-// CHECK:STDOUT:     type0:           {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type1:           {constant: template inst+2, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type2:           {constant: template inst+3, value_rep: {kind: none, type: type2}}
+// CHECK:STDOUT:     typeTypeType:    {kind: copy, type: typeTypeType}
+// CHECK:STDOUT:     typeError:       {kind: copy, type: typeError}
+// CHECK:STDOUT:     'type(instNamespaceType)': {kind: copy, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'type(inst+2)':    {kind: none, type: type(inst+3)}
+// CHECK:STDOUT:     'type(inst+3)':    {kind: none, type: type(inst+3)}
 // CHECK:STDOUT:   type_blocks:
 // CHECK:STDOUT:     type_block0:     {}
 // CHECK:STDOUT:   insts:
-// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
-// CHECK:STDOUT:     'inst+1':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
+// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'inst+1':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type(inst+2)}
 // CHECK:STDOUT:     'inst+2':          {kind: FunctionType, arg0: function0, type: typeTypeType}
 // CHECK:STDOUT:     'inst+3':          {kind: TupleType, arg0: type_block0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+4':          {kind: StructValue, arg0: empty, type: type1}
+// CHECK:STDOUT:     'inst+4':          {kind: StructValue, arg0: empty, type: type(inst+2)}
 // CHECK:STDOUT:     'inst+5':          {kind: Return}
 // CHECK:STDOUT:   constant_values:
-// CHECK:STDOUT:     'inst+0':          template inst+0
-// CHECK:STDOUT:     'inst+1':          template inst+4
-// CHECK:STDOUT:     'inst+2':          template inst+2
-// CHECK:STDOUT:     'inst+3':          template inst+3
-// CHECK:STDOUT:     'inst+4':          template inst+4
+// CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)
+// CHECK:STDOUT:     'inst+1':          templateConstant(inst+4)
+// CHECK:STDOUT:     'inst+2':          templateConstant(inst+2)
+// CHECK:STDOUT:     'inst+3':          templateConstant(inst+3)
+// CHECK:STDOUT:     'inst+4':          templateConstant(inst+4)
+// CHECK:STDOUT:   symbolic_constants: {}
 // CHECK:STDOUT:   inst_blocks:
 // CHECK:STDOUT:     empty:           {}
 // CHECK:STDOUT:     exports:
@@ -113,41 +116,44 @@ fn B() {
 // CHECK:STDOUT:   generics:        {}
 // CHECK:STDOUT:   generic_instances: {}
 // CHECK:STDOUT:   types:
-// CHECK:STDOUT:     type0:           {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type1:           {constant: template inst+4, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type2:           {constant: template inst+5, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type3:           {constant: template inst+10, value_rep: {kind: none, type: type2}}
+// CHECK:STDOUT:     typeTypeType:    {kind: copy, type: typeTypeType}
+// CHECK:STDOUT:     typeError:       {kind: copy, type: typeError}
+// CHECK:STDOUT:     'type(instNamespaceType)': {kind: copy, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'type(inst+4)':    {kind: none, type: type(inst+5)}
+// CHECK:STDOUT:     'type(inst+5)':    {kind: none, type: type(inst+5)}
+// CHECK:STDOUT:     'type(inst+10)':   {kind: none, type: type(inst+5)}
 // CHECK:STDOUT:   type_blocks:
 // CHECK:STDOUT:     type_block0:     {}
 // CHECK:STDOUT:   insts:
-// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
+// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
 // CHECK:STDOUT:     'inst+1':          {kind: ImportDecl, arg0: name1}
-// CHECK:STDOUT:     'inst+2':          {kind: Namespace, arg0: name_scope1, arg1: inst+1, type: type0}
-// CHECK:STDOUT:     'inst+3':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
+// CHECK:STDOUT:     'inst+2':          {kind: Namespace, arg0: name_scope1, arg1: inst+1, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'inst+3':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type(inst+4)}
 // CHECK:STDOUT:     'inst+4':          {kind: FunctionType, arg0: function0, type: typeTypeType}
 // CHECK:STDOUT:     'inst+5':          {kind: TupleType, arg0: type_block0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+6':          {kind: StructValue, arg0: empty, type: type1}
-// CHECK:STDOUT:     'inst+7':          {kind: NameRef, arg0: name1, arg1: inst+2, type: type0}
-// CHECK:STDOUT:     'inst+8':          {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: bind_name0, type: type3}
-// CHECK:STDOUT:     'inst+9':          {kind: FunctionDecl, arg0: function1, arg1: empty, type: type3}
+// CHECK:STDOUT:     'inst+6':          {kind: StructValue, arg0: empty, type: type(inst+4)}
+// CHECK:STDOUT:     'inst+7':          {kind: NameRef, arg0: name1, arg1: inst+2, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'inst+8':          {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: bind_name0, type: type(inst+10)}
+// CHECK:STDOUT:     'inst+9':          {kind: FunctionDecl, arg0: function1, arg1: empty, type: type(inst+10)}
 // CHECK:STDOUT:     'inst+10':         {kind: FunctionType, arg0: function1, type: typeTypeType}
-// CHECK:STDOUT:     'inst+11':         {kind: StructValue, arg0: empty, type: type3}
-// CHECK:STDOUT:     'inst+12':         {kind: NameRef, arg0: name1, arg1: inst+8, type: type3}
-// CHECK:STDOUT:     'inst+13':         {kind: Call, arg0: inst+12, arg1: block5, type: type2}
+// CHECK:STDOUT:     'inst+11':         {kind: StructValue, arg0: empty, type: type(inst+10)}
+// CHECK:STDOUT:     'inst+12':         {kind: NameRef, arg0: name1, arg1: inst+8, type: type(inst+10)}
+// CHECK:STDOUT:     'inst+13':         {kind: Call, arg0: inst+12, arg1: block5, type: type(inst+5)}
 // CHECK:STDOUT:     'inst+14':         {kind: Return}
 // CHECK:STDOUT:   constant_values:
-// CHECK:STDOUT:     'inst+0':          template inst+0
-// CHECK:STDOUT:     'inst+2':          template inst+2
-// CHECK:STDOUT:     'inst+3':          template inst+6
-// CHECK:STDOUT:     'inst+4':          template inst+4
-// CHECK:STDOUT:     'inst+5':          template inst+5
-// CHECK:STDOUT:     'inst+6':          template inst+6
-// CHECK:STDOUT:     'inst+7':          template inst+2
-// CHECK:STDOUT:     'inst+8':          template inst+11
-// CHECK:STDOUT:     'inst+9':          template inst+11
-// CHECK:STDOUT:     'inst+10':         template inst+10
-// CHECK:STDOUT:     'inst+11':         template inst+11
-// CHECK:STDOUT:     'inst+12':         template inst+11
+// CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)
+// CHECK:STDOUT:     'inst+2':          templateConstant(inst+2)
+// CHECK:STDOUT:     'inst+3':          templateConstant(inst+6)
+// CHECK:STDOUT:     'inst+4':          templateConstant(inst+4)
+// CHECK:STDOUT:     'inst+5':          templateConstant(inst+5)
+// CHECK:STDOUT:     'inst+6':          templateConstant(inst+6)
+// CHECK:STDOUT:     'inst+7':          templateConstant(inst+2)
+// CHECK:STDOUT:     'inst+8':          templateConstant(inst+11)
+// CHECK:STDOUT:     'inst+9':          templateConstant(inst+11)
+// CHECK:STDOUT:     'inst+10':         templateConstant(inst+10)
+// CHECK:STDOUT:     'inst+11':         templateConstant(inst+11)
+// CHECK:STDOUT:     'inst+12':         templateConstant(inst+11)
+// CHECK:STDOUT:   symbolic_constants: {}
 // CHECK:STDOUT:   inst_blocks:
 // CHECK:STDOUT:     empty:           {}
 // CHECK:STDOUT:     exports:

+ 43 - 37
toolchain/check/testdata/basics/no_prelude/multifile_raw_ir.carbon

@@ -41,24 +41,27 @@ fn B() {
 // CHECK:STDOUT:   generics:        {}
 // CHECK:STDOUT:   generic_instances: {}
 // CHECK:STDOUT:   types:
-// CHECK:STDOUT:     type0:           {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type1:           {constant: template inst+2, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type2:           {constant: template inst+3, value_rep: {kind: none, type: type2}}
+// CHECK:STDOUT:     typeTypeType:    {kind: copy, type: typeTypeType}
+// CHECK:STDOUT:     typeError:       {kind: copy, type: typeError}
+// CHECK:STDOUT:     'type(instNamespaceType)': {kind: copy, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'type(inst+2)':    {kind: none, type: type(inst+3)}
+// CHECK:STDOUT:     'type(inst+3)':    {kind: none, type: type(inst+3)}
 // CHECK:STDOUT:   type_blocks:
 // CHECK:STDOUT:     type_block0:     {}
 // CHECK:STDOUT:   insts:
-// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
-// CHECK:STDOUT:     'inst+1':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
+// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'inst+1':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type(inst+2)}
 // CHECK:STDOUT:     'inst+2':          {kind: FunctionType, arg0: function0, type: typeTypeType}
 // CHECK:STDOUT:     'inst+3':          {kind: TupleType, arg0: type_block0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+4':          {kind: StructValue, arg0: empty, type: type1}
+// CHECK:STDOUT:     'inst+4':          {kind: StructValue, arg0: empty, type: type(inst+2)}
 // CHECK:STDOUT:     'inst+5':          {kind: Return}
 // CHECK:STDOUT:   constant_values:
-// CHECK:STDOUT:     'inst+0':          template inst+0
-// CHECK:STDOUT:     'inst+1':          template inst+4
-// CHECK:STDOUT:     'inst+2':          template inst+2
-// CHECK:STDOUT:     'inst+3':          template inst+3
-// CHECK:STDOUT:     'inst+4':          template inst+4
+// CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)
+// CHECK:STDOUT:     'inst+1':          templateConstant(inst+4)
+// CHECK:STDOUT:     'inst+2':          templateConstant(inst+2)
+// CHECK:STDOUT:     'inst+3':          templateConstant(inst+3)
+// CHECK:STDOUT:     'inst+4':          templateConstant(inst+4)
+// CHECK:STDOUT:   symbolic_constants: {}
 // CHECK:STDOUT:   inst_blocks:
 // CHECK:STDOUT:     empty:           {}
 // CHECK:STDOUT:     exports:
@@ -92,41 +95,44 @@ fn B() {
 // CHECK:STDOUT:   generics:        {}
 // CHECK:STDOUT:   generic_instances: {}
 // CHECK:STDOUT:   types:
-// CHECK:STDOUT:     type0:           {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type1:           {constant: template inst+4, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type2:           {constant: template inst+5, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type3:           {constant: template inst+10, value_rep: {kind: none, type: type2}}
+// CHECK:STDOUT:     typeTypeType:    {kind: copy, type: typeTypeType}
+// CHECK:STDOUT:     typeError:       {kind: copy, type: typeError}
+// CHECK:STDOUT:     'type(instNamespaceType)': {kind: copy, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'type(inst+4)':    {kind: none, type: type(inst+5)}
+// CHECK:STDOUT:     'type(inst+5)':    {kind: none, type: type(inst+5)}
+// CHECK:STDOUT:     'type(inst+10)':   {kind: none, type: type(inst+5)}
 // CHECK:STDOUT:   type_blocks:
 // CHECK:STDOUT:     type_block0:     {}
 // CHECK:STDOUT:   insts:
-// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
+// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
 // CHECK:STDOUT:     'inst+1':          {kind: ImportDecl, arg0: name1}
-// CHECK:STDOUT:     'inst+2':          {kind: Namespace, arg0: name_scope1, arg1: inst+1, type: type0}
-// CHECK:STDOUT:     'inst+3':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
+// CHECK:STDOUT:     'inst+2':          {kind: Namespace, arg0: name_scope1, arg1: inst+1, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'inst+3':          {kind: FunctionDecl, arg0: function0, arg1: empty, type: type(inst+4)}
 // CHECK:STDOUT:     'inst+4':          {kind: FunctionType, arg0: function0, type: typeTypeType}
 // CHECK:STDOUT:     'inst+5':          {kind: TupleType, arg0: type_block0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+6':          {kind: StructValue, arg0: empty, type: type1}
-// CHECK:STDOUT:     'inst+7':          {kind: NameRef, arg0: name1, arg1: inst+2, type: type0}
-// CHECK:STDOUT:     'inst+8':          {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: bind_name0, type: type3}
-// CHECK:STDOUT:     'inst+9':          {kind: FunctionDecl, arg0: function1, arg1: empty, type: type3}
+// CHECK:STDOUT:     'inst+6':          {kind: StructValue, arg0: empty, type: type(inst+4)}
+// CHECK:STDOUT:     'inst+7':          {kind: NameRef, arg0: name1, arg1: inst+2, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'inst+8':          {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: bind_name0, type: type(inst+10)}
+// CHECK:STDOUT:     'inst+9':          {kind: FunctionDecl, arg0: function1, arg1: empty, type: type(inst+10)}
 // CHECK:STDOUT:     'inst+10':         {kind: FunctionType, arg0: function1, type: typeTypeType}
-// CHECK:STDOUT:     'inst+11':         {kind: StructValue, arg0: empty, type: type3}
-// CHECK:STDOUT:     'inst+12':         {kind: NameRef, arg0: name1, arg1: inst+8, type: type3}
-// CHECK:STDOUT:     'inst+13':         {kind: Call, arg0: inst+12, arg1: block5, type: type2}
+// CHECK:STDOUT:     'inst+11':         {kind: StructValue, arg0: empty, type: type(inst+10)}
+// CHECK:STDOUT:     'inst+12':         {kind: NameRef, arg0: name1, arg1: inst+8, type: type(inst+10)}
+// CHECK:STDOUT:     'inst+13':         {kind: Call, arg0: inst+12, arg1: block5, type: type(inst+5)}
 // CHECK:STDOUT:     'inst+14':         {kind: Return}
 // CHECK:STDOUT:   constant_values:
-// CHECK:STDOUT:     'inst+0':          template inst+0
-// CHECK:STDOUT:     'inst+2':          template inst+2
-// CHECK:STDOUT:     'inst+3':          template inst+6
-// CHECK:STDOUT:     'inst+4':          template inst+4
-// CHECK:STDOUT:     'inst+5':          template inst+5
-// CHECK:STDOUT:     'inst+6':          template inst+6
-// CHECK:STDOUT:     'inst+7':          template inst+2
-// CHECK:STDOUT:     'inst+8':          template inst+11
-// CHECK:STDOUT:     'inst+9':          template inst+11
-// CHECK:STDOUT:     'inst+10':         template inst+10
-// CHECK:STDOUT:     'inst+11':         template inst+11
-// CHECK:STDOUT:     'inst+12':         template inst+11
+// CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)
+// CHECK:STDOUT:     'inst+2':          templateConstant(inst+2)
+// CHECK:STDOUT:     'inst+3':          templateConstant(inst+6)
+// CHECK:STDOUT:     'inst+4':          templateConstant(inst+4)
+// CHECK:STDOUT:     'inst+5':          templateConstant(inst+5)
+// CHECK:STDOUT:     'inst+6':          templateConstant(inst+6)
+// CHECK:STDOUT:     'inst+7':          templateConstant(inst+2)
+// CHECK:STDOUT:     'inst+8':          templateConstant(inst+11)
+// CHECK:STDOUT:     'inst+9':          templateConstant(inst+11)
+// CHECK:STDOUT:     'inst+10':         templateConstant(inst+10)
+// CHECK:STDOUT:     'inst+11':         templateConstant(inst+11)
+// CHECK:STDOUT:     'inst+12':         templateConstant(inst+11)
+// CHECK:STDOUT:   symbolic_constants: {}
 // CHECK:STDOUT:   inst_blocks:
 // CHECK:STDOUT:     empty:           {}
 // CHECK:STDOUT:     exports:

+ 53 - 50
toolchain/check/testdata/basics/no_prelude/raw_and_textual_ir.carbon

@@ -32,69 +32,72 @@ fn Foo(n: ()) -> ((), ()) {
 // CHECK:STDOUT:   generics:        {}
 // CHECK:STDOUT:   generic_instances: {}
 // CHECK:STDOUT:   types:
-// CHECK:STDOUT:     type0:           {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type1:           {constant: template inst+1, value_rep: {kind: none, type: type1}}
-// CHECK:STDOUT:     type2:           {constant: template inst+8, value_rep: {kind: pointer, type: type4}}
-// CHECK:STDOUT:     type3:           {constant: template inst+15, value_rep: {kind: none, type: type1}}
-// CHECK:STDOUT:     type4:           {constant: template inst+17, value_rep: {kind: copy, type: type4}}
+// CHECK:STDOUT:     typeTypeType:    {kind: copy, type: typeTypeType}
+// CHECK:STDOUT:     typeError:       {kind: copy, type: typeError}
+// CHECK:STDOUT:     'type(instNamespaceType)': {kind: copy, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'type(inst+15)':   {kind: none, type: type(inst+1)}
+// CHECK:STDOUT:     'type(inst+1)':    {kind: none, type: type(inst+1)}
+// CHECK:STDOUT:     'type(inst+8)':    {kind: pointer, type: type(inst+17)}
+// CHECK:STDOUT:     'type(inst+17)':   {kind: copy, type: type(inst+17)}
 // CHECK:STDOUT:   type_blocks:
 // CHECK:STDOUT:     type_block0:     {}
 // CHECK:STDOUT:     type_block1:
-// CHECK:STDOUT:       0:               type1
-// CHECK:STDOUT:       1:               type1
+// CHECK:STDOUT:       0:               type(inst+1)
+// CHECK:STDOUT:       1:               type(inst+1)
 // CHECK:STDOUT:   insts:
-// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
+// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
 // CHECK:STDOUT:     'inst+1':          {kind: TupleType, arg0: type_block0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+2':          {kind: TupleLiteral, arg0: empty, type: type1}
+// CHECK:STDOUT:     'inst+2':          {kind: TupleLiteral, arg0: empty, type: type(inst+1)}
 // CHECK:STDOUT:     'inst+3':          {kind: Converted, arg0: inst+2, arg1: inst+1, type: typeTypeType}
-// CHECK:STDOUT:     'inst+4':          {kind: Param, arg0: name1, type: type1}
-// CHECK:STDOUT:     'inst+5':          {kind: BindName, arg0: bind_name0, arg1: inst+4, type: type1}
-// CHECK:STDOUT:     'inst+6':          {kind: TupleLiteral, arg0: empty, type: type1}
-// CHECK:STDOUT:     'inst+7':          {kind: TupleLiteral, arg0: empty, type: type1}
+// CHECK:STDOUT:     'inst+4':          {kind: Param, arg0: name1, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+5':          {kind: BindName, arg0: bind_name0, arg1: inst+4, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+6':          {kind: TupleLiteral, arg0: empty, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+7':          {kind: TupleLiteral, arg0: empty, type: type(inst+1)}
 // CHECK:STDOUT:     'inst+8':          {kind: TupleType, arg0: type_block1, type: typeTypeType}
-// CHECK:STDOUT:     'inst+9':          {kind: TupleLiteral, arg0: block5, type: type2}
+// CHECK:STDOUT:     'inst+9':          {kind: TupleLiteral, arg0: block5, type: type(inst+8)}
 // CHECK:STDOUT:     'inst+10':         {kind: Converted, arg0: inst+6, arg1: inst+1, type: typeTypeType}
 // CHECK:STDOUT:     'inst+11':         {kind: Converted, arg0: inst+7, arg1: inst+1, type: typeTypeType}
 // CHECK:STDOUT:     'inst+12':         {kind: Converted, arg0: inst+9, arg1: inst+8, type: typeTypeType}
-// CHECK:STDOUT:     'inst+13':         {kind: VarStorage, arg0: nameReturnSlot, type: type2}
-// CHECK:STDOUT:     'inst+14':         {kind: FunctionDecl, arg0: function0, arg1: block6, type: type3}
+// CHECK:STDOUT:     'inst+13':         {kind: VarStorage, arg0: nameReturnSlot, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+14':         {kind: FunctionDecl, arg0: function0, arg1: block6, type: type(inst+15)}
 // CHECK:STDOUT:     'inst+15':         {kind: FunctionType, arg0: function0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+16':         {kind: StructValue, arg0: empty, type: type3}
-// CHECK:STDOUT:     'inst+17':         {kind: PointerType, arg0: type2, type: typeTypeType}
-// CHECK:STDOUT:     'inst+18':         {kind: NameRef, arg0: name1, arg1: inst+5, type: type1}
-// CHECK:STDOUT:     'inst+19':         {kind: TupleLiteral, arg0: empty, type: type1}
-// CHECK:STDOUT:     'inst+20':         {kind: TupleLiteral, arg0: block8, type: type2}
-// CHECK:STDOUT:     'inst+21':         {kind: TupleAccess, arg0: inst+13, arg1: element0, type: type1}
-// CHECK:STDOUT:     'inst+22':         {kind: TupleInit, arg0: block9, arg1: inst+21, type: type1}
-// CHECK:STDOUT:     'inst+23':         {kind: TupleValue, arg0: block10, type: type1}
-// CHECK:STDOUT:     'inst+24':         {kind: Converted, arg0: inst+18, arg1: inst+22, type: type1}
-// CHECK:STDOUT:     'inst+25':         {kind: TupleAccess, arg0: inst+13, arg1: element1, type: type1}
-// CHECK:STDOUT:     'inst+26':         {kind: TupleInit, arg0: empty, arg1: inst+25, type: type1}
-// CHECK:STDOUT:     'inst+27':         {kind: Converted, arg0: inst+19, arg1: inst+26, type: type1}
-// CHECK:STDOUT:     'inst+28':         {kind: TupleInit, arg0: block11, arg1: inst+13, type: type2}
-// CHECK:STDOUT:     'inst+29':         {kind: TupleValue, arg0: block12, type: type2}
-// CHECK:STDOUT:     'inst+30':         {kind: Converted, arg0: inst+20, arg1: inst+28, type: type2}
+// CHECK:STDOUT:     'inst+16':         {kind: StructValue, arg0: empty, type: type(inst+15)}
+// CHECK:STDOUT:     'inst+17':         {kind: PointerType, arg0: type(inst+8), type: typeTypeType}
+// CHECK:STDOUT:     'inst+18':         {kind: NameRef, arg0: name1, arg1: inst+5, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+19':         {kind: TupleLiteral, arg0: empty, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+20':         {kind: TupleLiteral, arg0: block8, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+21':         {kind: TupleAccess, arg0: inst+13, arg1: element0, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+22':         {kind: TupleInit, arg0: block9, arg1: inst+21, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+23':         {kind: TupleValue, arg0: block10, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+24':         {kind: Converted, arg0: inst+18, arg1: inst+22, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+25':         {kind: TupleAccess, arg0: inst+13, arg1: element1, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+26':         {kind: TupleInit, arg0: empty, arg1: inst+25, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+27':         {kind: Converted, arg0: inst+19, arg1: inst+26, type: type(inst+1)}
+// CHECK:STDOUT:     'inst+28':         {kind: TupleInit, arg0: block11, arg1: inst+13, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+29':         {kind: TupleValue, arg0: block12, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+30':         {kind: Converted, arg0: inst+20, arg1: inst+28, type: type(inst+8)}
 // CHECK:STDOUT:     'inst+31':         {kind: ReturnExpr, arg0: inst+30, arg1: inst+13}
 // CHECK:STDOUT:   constant_values:
-// CHECK:STDOUT:     'inst+0':          template inst+0
-// CHECK:STDOUT:     'inst+1':          template inst+1
-// CHECK:STDOUT:     'inst+3':          template inst+1
-// CHECK:STDOUT:     'inst+8':          template inst+8
-// CHECK:STDOUT:     'inst+10':         template inst+1
-// CHECK:STDOUT:     'inst+11':         template inst+1
-// CHECK:STDOUT:     'inst+12':         template inst+8
-// CHECK:STDOUT:     'inst+14':         template inst+16
-// CHECK:STDOUT:     'inst+15':         template inst+15
-// CHECK:STDOUT:     'inst+16':         template inst+16
-// CHECK:STDOUT:     'inst+17':         template inst+17
-// CHECK:STDOUT:     'inst+22':         template inst+23
-// CHECK:STDOUT:     'inst+23':         template inst+23
-// CHECK:STDOUT:     'inst+24':         template inst+23
-// CHECK:STDOUT:     'inst+26':         template inst+23
-// CHECK:STDOUT:     'inst+27':         template inst+23
-// CHECK:STDOUT:     'inst+28':         template inst+29
-// CHECK:STDOUT:     'inst+29':         template inst+29
-// CHECK:STDOUT:     'inst+30':         template inst+29
+// CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)
+// CHECK:STDOUT:     'inst+1':          templateConstant(inst+1)
+// CHECK:STDOUT:     'inst+3':          templateConstant(inst+1)
+// CHECK:STDOUT:     'inst+8':          templateConstant(inst+8)
+// CHECK:STDOUT:     'inst+10':         templateConstant(inst+1)
+// CHECK:STDOUT:     'inst+11':         templateConstant(inst+1)
+// CHECK:STDOUT:     'inst+12':         templateConstant(inst+8)
+// CHECK:STDOUT:     'inst+14':         templateConstant(inst+16)
+// CHECK:STDOUT:     'inst+15':         templateConstant(inst+15)
+// CHECK:STDOUT:     'inst+16':         templateConstant(inst+16)
+// CHECK:STDOUT:     'inst+17':         templateConstant(inst+17)
+// CHECK:STDOUT:     'inst+22':         templateConstant(inst+23)
+// CHECK:STDOUT:     'inst+23':         templateConstant(inst+23)
+// CHECK:STDOUT:     'inst+24':         templateConstant(inst+23)
+// CHECK:STDOUT:     'inst+26':         templateConstant(inst+23)
+// CHECK:STDOUT:     'inst+27':         templateConstant(inst+23)
+// CHECK:STDOUT:     'inst+28':         templateConstant(inst+29)
+// CHECK:STDOUT:     'inst+29':         templateConstant(inst+29)
+// CHECK:STDOUT:     'inst+30':         templateConstant(inst+29)
+// CHECK:STDOUT:   symbolic_constants: {}
 // CHECK:STDOUT:   inst_blocks:
 // CHECK:STDOUT:     empty:           {}
 // CHECK:STDOUT:     exports:

+ 56 - 49
toolchain/check/testdata/basics/no_prelude/raw_ir.carbon

@@ -35,74 +35,81 @@ fn Foo[T:! type](n: T) -> (T, ()) {
 // CHECK:STDOUT:   generic_instances:
 // CHECK:STDOUT:     genericInstance0: {generic: generic0, args: block10}
 // CHECK:STDOUT:   types:
-// CHECK:STDOUT:     type0:           {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type1:           {constant: symbolic 0, value_rep: {kind: copy, type: type1}}
-// CHECK:STDOUT:     type2:           {constant: template inst+8, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type3:           {constant: template inst+10, value_rep: {kind: unknown, type: type<invalid>}}
-// CHECK:STDOUT:     type4:           {constant: symbolic 1, value_rep: {kind: pointer, type: type8}}
-// CHECK:STDOUT:     type5:           {constant: symbolic 2, value_rep: {kind: copy, type: type5}}
-// CHECK:STDOUT:     type6:           {constant: symbolic 3, value_rep: {kind: pointer, type: type8}}
-// CHECK:STDOUT:     type7:           {constant: template inst+17, value_rep: {kind: none, type: type2}}
-// CHECK:STDOUT:     type8:           {constant: symbolic 4, value_rep: {kind: copy, type: type8}}
+// CHECK:STDOUT:     typeTypeType:    {kind: copy, type: typeTypeType}
+// CHECK:STDOUT:     typeError:       {kind: copy, type: typeError}
+// CHECK:STDOUT:     'type(instNamespaceType)': {kind: copy, type: type(instNamespaceType)}
+// CHECK:STDOUT:     'type(inst+17)':   {kind: none, type: type(inst+8)}
+// CHECK:STDOUT:     'type(inst+8)':    {kind: none, type: type(inst+8)}
+// CHECK:STDOUT:     'type(symbolicConstant0)': {kind: copy, type: type(symbolicConstant0)}
+// CHECK:STDOUT:     'type(symbolicConstant1)': {kind: pointer, type: type(symbolicConstant4)}
+// CHECK:STDOUT:     'type(symbolicConstant4)': {kind: copy, type: type(symbolicConstant4)}
+// CHECK:STDOUT:     'type(symbolicConstant2)': {kind: copy, type: type(symbolicConstant2)}
+// CHECK:STDOUT:     'type(symbolicConstant3)': {kind: pointer, type: type(symbolicConstant4)}
 // CHECK:STDOUT:   type_blocks:
 // CHECK:STDOUT:     type_block0:     {}
 // CHECK:STDOUT:     type_block1:
 // CHECK:STDOUT:       0:               typeTypeType
-// CHECK:STDOUT:       1:               type2
+// CHECK:STDOUT:       1:               type(inst+8)
 // CHECK:STDOUT:     type_block2:
-// CHECK:STDOUT:       0:               type1
-// CHECK:STDOUT:       1:               type2
+// CHECK:STDOUT:       0:               type(symbolicConstant0)
+// CHECK:STDOUT:       1:               type(inst+8)
 // CHECK:STDOUT:   insts:
-// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
+// CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
 // CHECK:STDOUT:     'inst+1':          {kind: Param, arg0: name1, type: typeTypeType}
 // CHECK:STDOUT:     'inst+2':          {kind: BindSymbolicName, arg0: bind_name0, arg1: inst+1, type: typeTypeType}
 // CHECK:STDOUT:     'inst+3':          {kind: BindSymbolicName, arg0: bind_name0, arg1: inst<invalid>, type: typeTypeType}
 // CHECK:STDOUT:     'inst+4':          {kind: NameRef, arg0: name1, arg1: inst+2, type: typeTypeType}
-// CHECK:STDOUT:     'inst+5':          {kind: Param, arg0: name2, type: type5}
-// CHECK:STDOUT:     'inst+6':          {kind: BindName, arg0: bind_name1, arg1: inst+5, type: type5}
+// CHECK:STDOUT:     'inst+5':          {kind: Param, arg0: name2, type: type(symbolicConstant2)}
+// CHECK:STDOUT:     'inst+6':          {kind: BindName, arg0: bind_name1, arg1: inst+5, type: type(symbolicConstant2)}
 // CHECK:STDOUT:     'inst+7':          {kind: NameRef, arg0: name1, arg1: inst+2, type: typeTypeType}
 // CHECK:STDOUT:     'inst+8':          {kind: TupleType, arg0: type_block0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+9':          {kind: TupleLiteral, arg0: empty, type: type2}
+// CHECK:STDOUT:     'inst+9':          {kind: TupleLiteral, arg0: empty, type: type(inst+8)}
 // CHECK:STDOUT:     'inst+10':         {kind: TupleType, arg0: type_block1, type: typeTypeType}
-// CHECK:STDOUT:     'inst+11':         {kind: TupleLiteral, arg0: block6, type: type3}
+// CHECK:STDOUT:     'inst+11':         {kind: TupleLiteral, arg0: block6, type: type(inst+10)}
 // CHECK:STDOUT:     'inst+12':         {kind: Converted, arg0: inst+9, arg1: inst+8, type: typeTypeType}
 // CHECK:STDOUT:     'inst+13':         {kind: TupleType, arg0: type_block2, type: typeTypeType}
 // CHECK:STDOUT:     'inst+14':         {kind: Converted, arg0: inst+11, arg1: inst+13, type: typeTypeType}
-// CHECK:STDOUT:     'inst+15':         {kind: VarStorage, arg0: nameReturnSlot, type: type4}
-// CHECK:STDOUT:     'inst+16':         {kind: FunctionDecl, arg0: function0, arg1: block7, type: type7}
+// CHECK:STDOUT:     'inst+15':         {kind: VarStorage, arg0: nameReturnSlot, type: type(symbolicConstant1)}
+// CHECK:STDOUT:     'inst+16':         {kind: FunctionDecl, arg0: function0, arg1: block7, type: type(inst+17)}
 // CHECK:STDOUT:     'inst+17':         {kind: FunctionType, arg0: function0, type: typeTypeType}
-// CHECK:STDOUT:     'inst+18':         {kind: StructValue, arg0: empty, type: type7}
-// CHECK:STDOUT:     'inst+19':         {kind: PointerType, arg0: type4, type: typeTypeType}
-// CHECK:STDOUT:     'inst+20':         {kind: NameRef, arg0: name2, arg1: inst+6, type: type5}
-// CHECK:STDOUT:     'inst+21':         {kind: TupleLiteral, arg0: empty, type: type2}
-// CHECK:STDOUT:     'inst+22':         {kind: TupleLiteral, arg0: block13, type: type6}
-// CHECK:STDOUT:     'inst+23':         {kind: TupleAccess, arg0: inst+15, arg1: element0, type: type5}
-// CHECK:STDOUT:     'inst+24':         {kind: InitializeFrom, arg0: inst+20, arg1: inst+23, type: type5}
-// CHECK:STDOUT:     'inst+25':         {kind: TupleAccess, arg0: inst+15, arg1: element1, type: type2}
-// CHECK:STDOUT:     'inst+26':         {kind: TupleInit, arg0: empty, arg1: inst+25, type: type2}
-// CHECK:STDOUT:     'inst+27':         {kind: TupleValue, arg0: block15, type: type2}
-// CHECK:STDOUT:     'inst+28':         {kind: Converted, arg0: inst+21, arg1: inst+26, type: type2}
-// CHECK:STDOUT:     'inst+29':         {kind: TupleInit, arg0: block14, arg1: inst+15, type: type6}
-// CHECK:STDOUT:     'inst+30':         {kind: Converted, arg0: inst+22, arg1: inst+29, type: type6}
+// CHECK:STDOUT:     'inst+18':         {kind: StructValue, arg0: empty, type: type(inst+17)}
+// CHECK:STDOUT:     'inst+19':         {kind: PointerType, arg0: type(symbolicConstant1), type: typeTypeType}
+// CHECK:STDOUT:     'inst+20':         {kind: NameRef, arg0: name2, arg1: inst+6, type: type(symbolicConstant2)}
+// CHECK:STDOUT:     'inst+21':         {kind: TupleLiteral, arg0: empty, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+22':         {kind: TupleLiteral, arg0: block13, type: type(symbolicConstant3)}
+// CHECK:STDOUT:     'inst+23':         {kind: TupleAccess, arg0: inst+15, arg1: element0, type: type(symbolicConstant2)}
+// CHECK:STDOUT:     'inst+24':         {kind: InitializeFrom, arg0: inst+20, arg1: inst+23, type: type(symbolicConstant2)}
+// CHECK:STDOUT:     'inst+25':         {kind: TupleAccess, arg0: inst+15, arg1: element1, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+26':         {kind: TupleInit, arg0: empty, arg1: inst+25, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+27':         {kind: TupleValue, arg0: block15, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+28':         {kind: Converted, arg0: inst+21, arg1: inst+26, type: type(inst+8)}
+// CHECK:STDOUT:     'inst+29':         {kind: TupleInit, arg0: block14, arg1: inst+15, type: type(symbolicConstant3)}
+// CHECK:STDOUT:     'inst+30':         {kind: Converted, arg0: inst+22, arg1: inst+29, type: type(symbolicConstant3)}
 // CHECK:STDOUT:     'inst+31':         {kind: ReturnExpr, arg0: inst+30, arg1: inst+15}
 // CHECK:STDOUT:   constant_values:
-// CHECK:STDOUT:     'inst+0':          template inst+0
-// CHECK:STDOUT:     'inst+2':          symbolic 2
-// CHECK:STDOUT:     'inst+3':          symbolic 0
-// CHECK:STDOUT:     'inst+4':          symbolic 2
-// CHECK:STDOUT:     'inst+7':          symbolic 2
-// CHECK:STDOUT:     'inst+8':          template inst+8
-// CHECK:STDOUT:     'inst+10':         template inst+10
-// CHECK:STDOUT:     'inst+12':         template inst+8
-// CHECK:STDOUT:     'inst+13':         symbolic 1
-// CHECK:STDOUT:     'inst+14':         symbolic 3
-// CHECK:STDOUT:     'inst+16':         template inst+18
-// CHECK:STDOUT:     'inst+17':         template inst+17
-// CHECK:STDOUT:     'inst+18':         template inst+18
-// CHECK:STDOUT:     'inst+19':         symbolic 4
-// CHECK:STDOUT:     'inst+26':         template inst+27
-// CHECK:STDOUT:     'inst+27':         template inst+27
-// CHECK:STDOUT:     'inst+28':         template inst+27
+// CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)
+// CHECK:STDOUT:     'inst+2':          symbolicConstant2
+// CHECK:STDOUT:     'inst+3':          symbolicConstant0
+// CHECK:STDOUT:     'inst+4':          symbolicConstant2
+// CHECK:STDOUT:     'inst+7':          symbolicConstant2
+// CHECK:STDOUT:     'inst+8':          templateConstant(inst+8)
+// CHECK:STDOUT:     'inst+10':         templateConstant(inst+10)
+// CHECK:STDOUT:     'inst+12':         templateConstant(inst+8)
+// CHECK:STDOUT:     'inst+13':         symbolicConstant1
+// CHECK:STDOUT:     'inst+14':         symbolicConstant3
+// CHECK:STDOUT:     'inst+16':         templateConstant(inst+18)
+// CHECK:STDOUT:     'inst+17':         templateConstant(inst+17)
+// CHECK:STDOUT:     'inst+18':         templateConstant(inst+18)
+// CHECK:STDOUT:     'inst+19':         symbolicConstant4
+// CHECK:STDOUT:     'inst+26':         templateConstant(inst+27)
+// CHECK:STDOUT:     'inst+27':         templateConstant(inst+27)
+// CHECK:STDOUT:     'inst+28':         templateConstant(inst+27)
+// CHECK:STDOUT:   symbolic_constants:
+// CHECK:STDOUT:     symbolicConstant0: {inst: inst+3, generic: generic<invalid>, index: genericInst<invalid>}
+// CHECK:STDOUT:     symbolicConstant1: {inst: inst+13, generic: generic<invalid>, index: genericInst<invalid>}
+// CHECK:STDOUT:     symbolicConstant2: {inst: inst+3, generic: generic0, index: genericInstInDecl0}
+// CHECK:STDOUT:     symbolicConstant3: {inst: inst+13, generic: generic0, index: genericInstInDecl1}
+// CHECK:STDOUT:     symbolicConstant4: {inst: inst+19, generic: generic<invalid>, index: genericInst<invalid>}
 // CHECK:STDOUT:   inst_blocks:
 // CHECK:STDOUT:     empty:           {}
 // CHECK:STDOUT:     exports:

+ 1 - 1
toolchain/check/testdata/basics/no_prelude/verbose.carbon

@@ -9,7 +9,7 @@
 // SET-CHECK-SUBSET
 // CHECK:STDERR: Node Push 0: FunctionIntroducer -> <none>
 // CHECK:STDERR: AddPlaceholderInst: {kind: FunctionDecl, arg0: function<invalid>, arg1: empty}
-// CHECK:STDERR: ReplaceInst: inst+{{[0-9]+}} -> {kind: FunctionDecl, arg0: function{{[0-9]+}}, arg1: empty, type: type{{[0-9]+}}}
+// CHECK:STDERR: ReplaceInst: inst+{{[0-9]+}} -> {kind: FunctionDecl, arg0: function{{[0-9]+}}, arg1: empty, type: type(inst+{{[0-9]+}})}
 // CHECK:STDERR: inst_block_stack_ Push 1
 // CHECK:STDERR: AddInst: {kind: Return}
 // CHECK:STDERR: inst_block_stack_ Pop 1: block{{[0-9]+}}

+ 20 - 10
toolchain/lower/file_context.cpp

@@ -35,12 +35,12 @@ FileContext::FileContext(llvm::LLVMContext& llvm_context,
 auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
   CARBON_CHECK(llvm_module_) << "Run can only be called once.";
 
-  // Lower all types that were required to be complete. Note that this may
-  // leave some entries in `types_` null, if those types were mentioned but not
-  // used.
-  types_.resize(sem_ir_->types().size());
-  for (auto type_id : sem_ir_->complete_types()) {
-    types_[type_id.index] = BuildType(sem_ir_->types().GetInstId(type_id));
+  // Lower all types that were required to be complete.
+  types_.resize(sem_ir_->insts().size());
+  for (auto type_id : sem_ir_->types().complete_types()) {
+    if (type_id.index >= 0) {
+      types_[type_id.index] = BuildType(sem_ir_->types().GetInstId(type_id));
+    }
   }
 
   // Lower function declarations.
@@ -122,9 +122,11 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id)
     -> llvm::Function* {
   const auto& function = sem_ir().functions().Get(function_id);
 
-  // Don't lower associated functions.
-  // TODO: We shouldn't lower any function that has generic parameters.
-  if (sem_ir().insts().Is<SemIR::InterfaceDecl>(
+  // Don't lower generic functions or associated functions.
+  // TODO: Associated functions have `Self` in scope so should be treated as
+  // generic functions.
+  if (function.generic_id.is_valid() ||
+      sem_ir().insts().Is<SemIR::InterfaceDecl>(
           sem_ir().name_scopes().Get(function.parent_scope_id).inst_id)) {
     return nullptr;
   }
@@ -240,6 +242,12 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
   }
 
   llvm::Function* llvm_function = GetFunction(function_id);
+  if (!llvm_function) {
+    // We chose not to lower this function at all, for example because it's a
+    // generic function.
+    return;
+  }
+
   FunctionContext function_lowering(*this, llvm_function, vlog_stream_);
 
   const bool has_return_slot = function.has_return_slot();
@@ -320,8 +328,10 @@ static auto BuildTypeForInst(FileContext& context, SemIR::BuiltinInst inst)
     -> llvm::Type* {
   switch (inst.builtin_inst_kind) {
     case SemIR::BuiltinInstKind::Invalid:
-    case SemIR::BuiltinInstKind::Error:
       CARBON_FATAL() << "Unexpected builtin type in lowering.";
+    case SemIR::BuiltinInstKind::Error:
+      // This is a complete type but uses of it should never be lowered.
+      return nullptr;
     case SemIR::BuiltinInstKind::TypeType:
       return context.GetTypeType();
     case SemIR::BuiltinInstKind::FloatType:

+ 0 - 3
toolchain/lower/file_context.h

@@ -33,9 +33,6 @@ class FileContext {
   // Returns a lowered type for the given type_id.
   auto GetType(SemIR::TypeId type_id) -> llvm::Type* {
     // InvalidType should not be passed in.
-    if (type_id == SemIR::TypeId::TypeType) {
-      return GetTypeType();
-    }
     CARBON_CHECK(type_id.index >= 0) << type_id;
     CARBON_CHECK(types_[type_id.index]) << "Missing type " << type_id;
     return types_[type_id.index];

+ 0 - 10
toolchain/lower/testdata/function/generic/type_param.carbon

@@ -15,13 +15,3 @@ fn F(T:! type) {
 
 // CHECK:STDOUT: ; ModuleID = 'type_param.carbon'
 // CHECK:STDOUT: source_filename = "type_param.carbon"
-// CHECK:STDOUT:
-// CHECK:STDOUT: %type = type {}
-// CHECK:STDOUT:
-// CHECK:STDOUT: define void @F(%type %T) {
-// CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %p.var = alloca ptr, align 8
-// CHECK:STDOUT:   %.loc13_15 = load ptr, ptr %p.var, align 8
-// CHECK:STDOUT:   %.loc13_14.2 = load {}, ptr %.loc13_15, align 1
-// CHECK:STDOUT:   ret void
-// CHECK:STDOUT: }

+ 13 - 1
toolchain/sem_ir/constant.h

@@ -6,6 +6,7 @@
 #define CARBON_TOOLCHAIN_SEM_IR_CONSTANT_H_
 
 #include "common/map.h"
+#include "toolchain/base/yaml.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 
@@ -13,7 +14,7 @@ namespace Carbon::SemIR {
 
 // Information about a symbolic constant value. These are indexed by
 // `ConstantId`s for which `is_symbolic` is true.
-struct SymbolicConstant {
+struct SymbolicConstant : Printable<SymbolicConstant> {
   // The constant instruction that defines the value of this symbolic constant.
   InstId inst_id;
   // The enclosing generic. If this is invalid, then this is an abstract
@@ -23,6 +24,11 @@ struct SymbolicConstant {
   // The index of this symbolic constant within the generic's list of symbolic
   // constants, or invalid if `generic_id` is invalid.
   GenericInstIndex index;
+
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "{inst: " << inst_id << ", generic: " << generic_id
+        << ", index: " << index << "}";
+  }
 };
 
 // Provides a ValueStore wrapper for tracking the constant values of
@@ -109,6 +115,12 @@ class ConstantValueStore {
   // NotConstant.
   auto array_ref() const -> llvm::ArrayRef<ConstantId> { return values_; }
 
+  // Returns the symbolic constants mapping as an ArrayRef whose keys are
+  // symbolic indexes of constants.
+  auto symbolic_constants() const -> llvm::ArrayRef<SymbolicConstant> {
+    return symbolic_constants_;
+  }
+
  private:
   const ConstantId default_;
 

+ 20 - 5
toolchain/sem_ir/file.cpp

@@ -58,8 +58,8 @@ auto ValueRepr::Print(llvm::raw_ostream& out) const -> void {
   out << ", type: " << type_id << "}";
 }
 
-auto TypeInfo::Print(llvm::raw_ostream& out) const -> void {
-  out << "{constant: " << constant_id << ", value_rep: " << value_repr << "}";
+auto CompleteTypeInfo::Print(llvm::raw_ostream& out) const -> void {
+  out << "{value_rep: " << value_repr << "}";
 }
 
 File::File(CheckIRId check_ir_id, SharedValueStores& value_stores,
@@ -72,6 +72,12 @@ File::File(CheckIRId check_ir_id, SharedValueStores& value_stores,
       constant_values_(ConstantId::NotConstant),
       inst_blocks_(allocator_),
       constants_(*this, allocator_) {
+  // `type` and the error type are both complete types.
+  types_.SetValueRepr(TypeId::TypeType,
+                      {.kind = ValueRepr::Copy, .type_id = TypeId::TypeType});
+  types_.SetValueRepr(TypeId::Error,
+                      {.kind = ValueRepr::Copy, .type_id = TypeId::Error});
+
   insts_.Reserve(BuiltinInstKind::ValidCount);
 // Error uses a self-referential type so that it's not accidentally treated as
 // a normal type. Every other builtin is a type, including the
@@ -167,6 +173,16 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping {
                       }
                     }
                   }));
+          map.Add(
+              "symbolic_constants",
+              Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+                for (const auto& [i, symbolic] :
+                     llvm::enumerate(constant_values().symbolic_constants())) {
+                  map.Add(
+                      PrintToString(ConstantId::ForSymbolicConstantIndex(i)),
+                      Yaml::OutputScalar(symbolic));
+                }
+              }));
           map.Add("inst_blocks", inst_blocks_.OutputYaml());
         }));
   });
@@ -194,8 +210,6 @@ auto File::CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
   mem_usage.Collect(MemUsage::ConcatLabel(label, "inst_blocks_"), inst_blocks_);
   mem_usage.Collect(MemUsage::ConcatLabel(label, "constants_"), constants_);
   mem_usage.Collect(MemUsage::ConcatLabel(label, "types_"), types_);
-  mem_usage.Add(MemUsage::ConcatLabel(label, "complete_types_"),
-                complete_types_);
 }
 
 // Map an instruction kind representing a type into an integer describing the
@@ -706,7 +720,8 @@ auto GetInitRepr(const File& file, TypeId type_id) -> InitRepr {
 
     case ValueRepr::Unknown:
       CARBON_FATAL()
-          << "Attempting to perform initialization of incomplete type";
+          << "Attempting to perform initialization of incomplete type "
+          << file.types().GetAsInst(type_id);
   }
 }
 

+ 0 - 23
toolchain/sem_ir/file.h

@@ -64,19 +64,6 @@ class File : public Printable<File> {
         .getZExtValue();
   }
 
-  // Marks a type as complete, and sets its value representation.
-  auto CompleteType(TypeId object_type_id, ValueRepr value_repr) -> void {
-    if (object_type_id.index < 0) {
-      // We already know our builtin types are complete.
-      return;
-    }
-    CARBON_CHECK(types().Get(object_type_id).value_repr.kind ==
-                 ValueRepr::Unknown)
-        << "Type " << object_type_id << " completed more than once";
-    types().Get(object_type_id).value_repr = value_repr;
-    complete_types_.push_back(object_type_id);
-  }
-
   // Gets the pointee type of the given type, which must be a pointer type.
   auto GetPointeeType(TypeId pointer_id) const -> TypeId {
     return types().GetAs<PointerType>(pointer_id).pointee_id;
@@ -173,13 +160,6 @@ class File : public Printable<File> {
   auto constants() -> ConstantStore& { return constants_; }
   auto constants() const -> const ConstantStore& { return constants_; }
 
-  // A list of types that were completed in this file, in the order in which
-  // they were completed. Earlier types in this list cannot contain instances of
-  // later types.
-  auto complete_types() const -> llvm::ArrayRef<TypeId> {
-    return complete_types_;
-  }
-
   auto top_inst_block_id() const -> InstBlockId { return top_inst_block_id_; }
   auto set_top_inst_block_id(InstBlockId block_id) -> void {
     top_inst_block_id_ = block_id;
@@ -261,9 +241,6 @@ class File : public Printable<File> {
 
   // Descriptions of types used in this file.
   TypeStore types_ = TypeStore(&insts_, &constant_values_);
-
-  // Types that were completed in this file.
-  llvm::SmallVector<TypeId> complete_types_;
 };
 
 // The expression category of a sem_ir instruction. See /docs/design/values.md

+ 30 - 7
toolchain/sem_ir/ids.h

@@ -141,13 +141,23 @@ struct ConstantId : public IdBase, public Printable<ConstantId> {
     return index >= 0;
   }
 
-  auto Print(llvm::raw_ostream& out) const -> void {
+  // Prints this ID to the given output stream. `disambiguate` indicates whether
+  // template constants should be wrapped with "templateConstant(...)" so that
+  // they aren't printed the same as an InstId. This can be set to false if
+  // there is no risk of ambiguity.
+  auto Print(llvm::raw_ostream& out, bool disambiguate = true) const -> void {
     if (!is_valid()) {
       IdBase::Print(out);
     } else if (is_template()) {
-      out << "template " << template_inst_id();
+      if (disambiguate) {
+        out << "templateConstant(";
+      }
+      out << template_inst_id();
+      if (disambiguate) {
+        out << ")";
+      }
     } else if (is_symbolic()) {
-      out << "symbolic " << symbolic_index();
+      out << "symbolicConstant" << symbolic_index();
     } else {
       out << "runtime";
     }
@@ -615,7 +625,6 @@ constexpr InstBlockId InstBlockId::Unreachable = InstBlockId(InvalidIndex - 1);
 
 // The ID of a type.
 struct TypeId : public IdBase, public Printable<TypeId> {
-  using ValueType = TypeInfo;
   // StringifyType() is used for diagnostics.
   using DiagnosticType = DiagnosticTypeInfo<std::string>;
 
@@ -629,6 +638,17 @@ struct TypeId : public IdBase, public Printable<TypeId> {
   static const TypeId Invalid;
 
   using IdBase::IdBase;
+
+  // Returns the ID of the type corresponding to the constant `const_id`, which
+  // must be of type `type`. As an exception, the type `Error` is of type
+  // `Error`.
+  static constexpr auto ForTypeConstant(ConstantId const_id) -> TypeId {
+    return TypeId(const_id.index);
+  }
+
+  // Returns the constant ID that defines the type.
+  auto AsConstantId() const -> ConstantId { return ConstantId(index); }
+
   auto Print(llvm::raw_ostream& out) const -> void {
     out << "type";
     if (*this == TypeType) {
@@ -636,13 +656,16 @@ struct TypeId : public IdBase, public Printable<TypeId> {
     } else if (*this == Error) {
       out << "Error";
     } else {
-      IdBase::Print(out);
+      out << "(";
+      AsConstantId().Print(out, /*disambiguate=*/false);
+      out << ")";
     }
   }
 };
 
-constexpr TypeId TypeId::TypeType = TypeId(InvalidIndex - 2);
-constexpr TypeId TypeId::Error = TypeId(InvalidIndex - 1);
+constexpr TypeId TypeId::TypeType = TypeId::ForTypeConstant(
+    ConstantId::ForTemplateConstant(InstId::BuiltinTypeType));
+constexpr TypeId TypeId::Error = TypeId::ForTypeConstant(ConstantId::Error);
 constexpr TypeId TypeId::Invalid = TypeId(InvalidIndex);
 
 // The ID of a type block.

+ 45 - 19
toolchain/sem_ir/type.h

@@ -14,23 +14,18 @@
 namespace Carbon::SemIR {
 
 // Provides a ValueStore wrapper with an API specific to types.
-class TypeStore : public ValueStore<TypeId> {
+class TypeStore : public Yaml::Printable<TypeStore> {
  public:
   explicit TypeStore(InstStore* insts, ConstantValueStore* constants)
       : insts_(insts), constants_(constants) {}
 
   // Returns the ID of the constant used to define the specified type.
   auto GetConstantId(TypeId type_id) const -> ConstantId {
-    if (type_id == TypeId::TypeType) {
-      return ConstantId::ForTemplateConstant(InstId::BuiltinTypeType);
-    } else if (type_id == TypeId::Error) {
-      return ConstantId::Error;
-    } else if (!type_id.is_valid()) {
-      // TODO: Can we CHECK-fail on this?
+    if (!type_id.is_valid()) {
+      // TODO: Investigate replacing this with a CHECK or returning Invalid.
       return ConstantId::NotConstant;
-    } else {
-      return Get(type_id).constant_id;
     }
+    return type_id.AsConstantId();
   }
 
   // Returns the ID of the instruction used to define the specified type.
@@ -58,7 +53,7 @@ class TypeStore : public ValueStore<TypeId> {
       return GetAsInst(type_id).As<InstT>();
     } else {
       // The type is not a builtin, so no need to check for special values.
-      auto inst_id = constants_->GetInstId(Get(type_id).constant_id);
+      auto inst_id = constants_->GetInstId(GetConstantId(type_id));
       return insts_->GetAs<InstT>(inst_id);
     }
   }
@@ -80,17 +75,27 @@ class TypeStore : public ValueStore<TypeId> {
   // Gets the value representation to use for a type. This returns an
   // invalid type if the given type is not complete.
   auto GetValueRepr(TypeId type_id) const -> ValueRepr {
-    if (type_id.index < 0) {
-      // TypeType and InvalidType are their own value representation.
-      return {.kind = ValueRepr::Copy, .type_id = type_id};
+    if (auto type_info = complete_type_info_.Lookup(type_id)) {
+      return type_info.value().value_repr;
     }
-    return Get(type_id).value_repr;
+    return {.kind = ValueRepr::Unknown};
+  }
+
+  // Sets the value representation associated with a type.
+  auto SetValueRepr(TypeId type_id, ValueRepr value_repr) -> void {
+    CARBON_CHECK(value_repr.kind != ValueRepr::Unknown);
+    auto insert_info =
+        complete_type_info_.Insert(type_id, {.value_repr = value_repr});
+    CARBON_CHECK(insert_info.is_inserted())
+        << "Type " << type_id << " completed more than once";
+    complete_types_.push_back(type_id);
+    CARBON_CHECK(IsComplete(type_id));
   }
 
   // Determines whether the given type is known to be complete. This does not
   // determine whether the type could be completed, only whether it has been.
   auto IsComplete(TypeId type_id) const -> bool {
-    return GetValueRepr(type_id).kind != ValueRepr::Unknown;
+    return complete_type_info_.Contains(type_id);
   }
 
   // Determines whether the given type is a signed integer type.
@@ -103,14 +108,35 @@ class TypeStore : public ValueStore<TypeId> {
     return int_type && int_type->int_kind.is_signed();
   }
 
-  // This has no direct storage, so there's no interesting memory usage to
-  // collect.
-  auto CollectMemUsage(MemUsage& /*mem_usage*/, llvm::StringRef /*label*/) const
-      -> void {}
+  // Returns a list of types that were completed in this file, in the order in
+  // which they were completed. Earlier types in this list cannot contain
+  // instances of later types.
+  auto complete_types() const -> llvm::ArrayRef<TypeId> {
+    return complete_types_;
+  }
+
+  auto OutputYaml() const -> Yaml::OutputMapping {
+    return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+      for (auto type_id : complete_types_) {
+        map.Add(PrintToString(type_id),
+                Yaml::OutputScalar(GetValueRepr(type_id)));
+      }
+    });
+  }
+
+  auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
+      -> void {
+    mem_usage.Add(MemUsage::ConcatLabel(label, "complete_type_info_"),
+                  complete_type_info_);
+    mem_usage.Add(MemUsage::ConcatLabel(label, "complete_types_"),
+                  complete_types_);
+  }
 
  private:
   InstStore* insts_;
   ConstantValueStore* constants_;
+  Map<TypeId, CompleteTypeInfo> complete_type_info_;
+  llvm::SmallVector<TypeId> complete_types_;
 };
 
 }  // namespace Carbon::SemIR

+ 2 - 4
toolchain/sem_ir/type_info.h

@@ -62,12 +62,10 @@ struct ValueRepr : public Printable<ValueRepr> {
   TypeId type_id = TypeId::Invalid;
 };
 
-// Information stored about a TypeId.
-struct TypeInfo : public Printable<TypeInfo> {
+// Information stored about a TypeId corresponding to a complete type.
+struct CompleteTypeInfo : public Printable<CompleteTypeInfo> {
   auto Print(llvm::raw_ostream& out) const -> void;
 
-  // The constant type value that defines this type.
-  ConstantId constant_id;
   // The value representation for this type. Will be `Unknown` if the type is
   // not complete.
   ValueRepr value_repr = ValueRepr();

+ 4 - 5
toolchain/sem_ir/yaml_test.cpp

@@ -49,12 +49,10 @@ TEST(SemIRTest, YAML) {
   auto type_block_id = Yaml::Scalar(MatchesRegex(R"(type_block\d+)"));
   auto inst_id = Yaml::Scalar(MatchesRegex(R"(inst\+\d+)"));
   auto constant_id =
-      Yaml::Scalar(MatchesRegex(R"((template|symbolic) inst(\w+|\+\d+))"));
+      Yaml::Scalar(MatchesRegex(R"(templateConstant\(inst(\w+|\+\d+)\))"));
   auto inst_builtin = Yaml::Scalar(MatchesRegex(R"(inst\w+)"));
-  auto type_id = Yaml::Scalar(MatchesRegex(R"(type\d+)"));
-  auto type_builtin = Pair(
-      type_id, Yaml::Mapping(ElementsAre(Pair("constant", constant_id),
-                                         Pair("value_rep", Yaml::Mapping(_)))));
+  auto type_id = Yaml::Scalar(MatchesRegex(R"(type(\w+|\(inst(\w+|\+\d+)\)))"));
+  auto type_builtin = Pair(type_id, Yaml::Mapping(_));
 
   auto file = Yaml::Mapping(ElementsAre(
       Pair("import_irs", Yaml::Mapping(SizeIs(1))),
@@ -87,6 +85,7 @@ TEST(SemIRTest, YAML) {
                                                 Pair("arg1", inst_id)))))))),
       Pair("constant_values",
            Yaml::Mapping(AllOf(Each(Pair(inst_id, constant_id))))),
+      Pair("symbolic_constants", Yaml::Mapping(SizeIs(0))),
       // This production has only two instruction blocks.
       Pair("inst_blocks",
            Yaml::Mapping(ElementsAre(