Просмотр исходного кода

Implement some basic ImportRef handling for builtins. (#3663)

I'm basically just nudging down the path I think is right here. Adding a
small bit more support, but also more tests to capture cases that I
think will need to be verified as working.

With diagnostics like "Value of type `<function>` is not callable.",
that's because it expects a FunctionDecl but is instead finding a
ImportRefUsed. I'll need to work out the necessary support for a
callable function.
Jon Ross-Perkins 2 лет назад
Родитель
Сommit
e583493e9f

+ 1 - 0
toolchain/check/BUILD

@@ -61,6 +61,7 @@ cc_library(
         "//toolchain/parse:node_kind",
         "//toolchain/parse:tree",
         "//toolchain/parse:tree_node_location_translator",
+        "//toolchain/sem_ir:builtin_kind",
         "//toolchain/sem_ir:file",
         "//toolchain/sem_ir:ids",
         "//toolchain/sem_ir:inst",

+ 29 - 15
toolchain/check/context.cpp

@@ -15,6 +15,7 @@
 #include "toolchain/check/inst_block_stack.h"
 #include "toolchain/lex/tokenized_buffer.h"
 #include "toolchain/parse/node_kind.h"
+#include "toolchain/sem_ir/builtin_kind.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
@@ -227,22 +228,35 @@ auto Context::ResolveIfImportRefUnused(SemIR::InstId inst_id) -> void {
   }
   const SemIR::File& import_ir = *import_irs().Get(unused_inst->ir_id);
   auto import_inst = import_ir.insts().Get(unused_inst->inst_id);
-  switch (import_inst.kind()) {
-    default:
-      // TODO: We need more type support. For now we inject an arbitrary
-      // invalid node that's unrelated to the underlying value. The TODO
-      // diagnostic is used since this section shouldn't typically be able to
-      // error.
-      TODO(Parse::NodeId::Invalid,
-           (llvm::Twine("TODO: ResolveIfImportRefUnused for ") +
-            import_inst.kind().name())
-               .str());
-      ReplaceInstBeforeConstantUse(
-          inst_id,
-          {SemIR::ImportRefUsed{SemIR::TypeId::Error, unused_inst->ir_id,
-                                unused_inst->inst_id}});
-      break;
+
+  // If the type ID isn't a normal value, forward it directly.
+  if (!import_inst.type_id().is_valid()) {
+    ReplaceInstBeforeConstantUse(
+        inst_id,
+        {SemIR::ImportRefUsed{import_inst.type_id(), unused_inst->ir_id,
+                              unused_inst->inst_id}});
+    return;
   }
+
+  auto import_type_inst_id = import_ir.types().GetInstId(import_inst.type_id());
+  CARBON_CHECK(import_type_inst_id.is_valid());
+
+  // If the type of the instruction is a builtin, use it directly.
+  auto type_id = SemIR::TypeId::Invalid;
+  if (import_type_inst_id.is_builtin()) {
+    type_id = GetBuiltinType(import_type_inst_id.builtin_kind());
+  } else {
+    // TODO: This section probably needs to TryEvalInst for the type. Similar to
+    // GetTypeImpl, but in the context of import_ir.
+    TODO(Parse::NodeId::Invalid,
+         "TODO: ResolveIfImportRefUnused for non-builtin type");
+    type_id = SemIR::TypeId::Error;
+  }
+
+  // TODO: Add breadcrumbs for lowering.
+  ReplaceInstBeforeConstantUse(
+      inst_id, {SemIR::ImportRefUsed{type_id, unused_inst->ir_id,
+                                     unused_inst->inst_id}});
 }
 
 auto Context::LookupNameInDecl(Parse::NodeId /*parse_node*/,

+ 12 - 8
toolchain/check/testdata/class/fail_todo_import.carbon

@@ -3,8 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for ClassDecl`.
-// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for ClassDecl`.
 
 // --- a.carbon
 
@@ -26,7 +24,13 @@ library "b" api;
 import library "a";
 
 fn Run() {
+  // CHECK:STDERR: b.carbon:[[@LINE+3]]:10: ERROR: Expression cannot be used as a value.
+  // CHECK:STDERR:   var x: Empty = {};
+  // CHECK:STDERR:          ^~~~~
   var x: Empty = {};
+  // CHECK:STDERR: b.carbon:[[@LINE+3]]:10: ERROR: Expression cannot be used as a value.
+  // CHECK:STDERR:   var y: ForwardDeclared = {};
+  // CHECK:STDERR:          ^~~~~~~~~~~~~~~
   var y: ForwardDeclared = {};
   y.F();
 }
@@ -68,22 +72,22 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %import_ref.1, .ForwardDeclared = %import_ref.2, .Run = %Run} [template]
-// CHECK:STDOUT:   %import_ref.1: <error> = import_ref ir1, inst+1, used
-// CHECK:STDOUT:   %import_ref.2: <error> = import_ref ir1, inst+4, used
+// CHECK:STDOUT:   %import_ref.1: invalid = import_ref ir1, inst+1, used
+// CHECK:STDOUT:   %import_ref.2: invalid = import_ref ir1, inst+4, used
 // CHECK:STDOUT:   %Run: <function> = fn_decl @Run [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Empty.ref: <error> = name_ref Empty, file.%import_ref.1
+// CHECK:STDOUT:   %Empty.ref: invalid = name_ref Empty, file.%import_ref.1
 // CHECK:STDOUT:   %x.var: ref <error> = var x
 // CHECK:STDOUT:   %x: ref <error> = bind_name x, %x.var
-// CHECK:STDOUT:   %.loc7: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10: {} = struct_literal ()
 // CHECK:STDOUT:   assign %x.var, <error>
-// CHECK:STDOUT:   %ForwardDeclared.ref: <error> = name_ref ForwardDeclared, file.%import_ref.2
+// CHECK:STDOUT:   %ForwardDeclared.ref: invalid = name_ref ForwardDeclared, file.%import_ref.2
 // CHECK:STDOUT:   %y.var: ref <error> = var y
 // CHECK:STDOUT:   %y: ref <error> = bind_name y, %y.var
-// CHECK:STDOUT:   %.loc8: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc14: {} = struct_literal ()
 // CHECK:STDOUT:   assign %y.var, <error>
 // CHECK:STDOUT:   %y.ref: ref <error> = name_ref y, %y
 // CHECK:STDOUT:   return

+ 60 - 0
toolchain/check/testdata/const/fail_todo_import.carbon

@@ -0,0 +1,60 @@
+// 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
+//
+// When there are no more cases that can hit a TODO, remove this test. Until
+// then, update it whenever its target is implemented.
+//
+// AUTOUPDATE
+// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for non-builtin type`.
+
+// --- implicit.carbon
+
+package Implicit api;
+
+fn F() -> const i32;
+
+var a_ref: const i32 = F();
+
+// --- implicit.impl.carbon
+
+package Implicit impl;
+
+var a: const i32 = a_ref;
+
+// CHECK:STDOUT: --- implicit.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = const_type i32 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.F = %F, .a_ref = %a_ref} [template]
+// CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
+// CHECK:STDOUT:   %.loc6_12: type = const_type i32 [template = constants.%.1]
+// CHECK:STDOUT:   %a_ref.var: ref const i32 = var a_ref
+// CHECK:STDOUT:   %a_ref: ref const i32 = bind_name a_ref, %a_ref.var
+// CHECK:STDOUT:   %F.ref: <function> = name_ref F, %F [template = %F]
+// CHECK:STDOUT:   %.loc6_25: init const i32 = call %F.ref()
+// CHECK:STDOUT:   assign %a_ref.var, %.loc6_25
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> const i32;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- implicit.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = const_type i32 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.F = %import_ref.1, .a_ref = %import_ref.2, .a = %a} [template]
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+4, unused
+// CHECK:STDOUT:   %import_ref.2: ref <error> = import_ref ir1, inst+7, used
+// CHECK:STDOUT:   %.loc4: type = const_type i32 [template = constants.%.1]
+// CHECK:STDOUT:   %a.var: ref const i32 = var a
+// CHECK:STDOUT:   %a: ref const i32 = bind_name a, %a.var
+// CHECK:STDOUT:   %a_ref.ref: ref <error> = name_ref a_ref, %import_ref.2
+// CHECK:STDOUT:   assign %a.var, <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 5 - 3
toolchain/check/testdata/function/declaration/fail_todo_import.carbon

@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
 
 // --- a.carbon
 
@@ -18,6 +17,9 @@ library "b" api;
 import library "a";
 
 fn Run() {
+  // CHECK:STDERR: b.carbon:[[@LINE+3]]:3: ERROR: Value of type `<function>` is not callable.
+  // CHECK:STDERR:   F();
+  // CHECK:STDERR:   ^~
   F();
 }
 
@@ -34,13 +36,13 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace {.F = %import_ref, .Run = %Run} [template]
-// CHECK:STDOUT:   %import_ref: <error> = import_ref ir1, inst+1, used
+// CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used
 // CHECK:STDOUT:   %Run: <function> = fn_decl @Run [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %F.ref: <error> = name_ref F, file.%import_ref
+// CHECK:STDOUT:   %F.ref: <function> = name_ref F, file.%import_ref
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 5 - 3
toolchain/check/testdata/function/definition/fail_todo_import.carbon

@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
 
 // --- a.carbon
 
@@ -18,6 +17,9 @@ library "b" api;
 import library "a";
 
 fn Run() {
+  // CHECK:STDERR: b.carbon:[[@LINE+3]]:3: ERROR: Value of type `<function>` is not callable.
+  // CHECK:STDERR:   F();
+  // CHECK:STDERR:   ^~
   F();
 }
 
@@ -37,13 +39,13 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace {.F = %import_ref, .Run = %Run} [template]
-// CHECK:STDOUT:   %import_ref: <error> = import_ref ir1, inst+1, used
+// CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used
 // CHECK:STDOUT:   %Run: <function> = fn_decl @Run [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %F.ref: <error> = name_ref F, file.%import_ref
+// CHECK:STDOUT:   %F.ref: <function> = name_ref F, file.%import_ref
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 2
toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon

@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: conflict.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
 
 // --- fn.carbon
 
@@ -46,7 +45,7 @@ fn NS.Foo();
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace {.NS = %import_ref} [template]
-// CHECK:STDOUT:   %import_ref: <error> = import_ref ir1, inst+1, used
+// CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used
 // CHECK:STDOUT:   %.loc12: <namespace> = namespace {} [template]
 // CHECK:STDOUT:   %.loc20: <function> = fn_decl @.1 [template]
 // CHECK:STDOUT: }

+ 34 - 24
toolchain/check/testdata/namespace/fail_todo_imported.carbon

@@ -3,8 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
-// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
 
 // --- implicit.carbon
 
@@ -20,10 +18,22 @@ fn NS.ChildNS.B();
 
 package Implicit impl;
 
+// CHECK:STDERR: implicit.impl.carbon:[[@LINE+3]]:13: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var a: () = NS.A();
+// CHECK:STDERR:             ^~~~~
 var a: () = NS.A();
+// CHECK:STDERR: implicit.impl.carbon:[[@LINE+3]]:13: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var b: () = NS.ChildNS.B();
+// CHECK:STDERR:             ^~~~~~~~~~~~~
 var b: () = NS.ChildNS.B();
 
+// CHECK:STDERR: implicit.impl.carbon:[[@LINE+3]]:21: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var package_a: () = package.NS.A();
+// CHECK:STDERR:                     ^~~~~~~~~~~~~
 var package_a: () = package.NS.A();
+// CHECK:STDERR: implicit.impl.carbon:[[@LINE+3]]:21: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var package_b: () = package.NS.ChildNS.B();
+// CHECK:STDERR:                     ^~~~~~~~~~~~~~~~~~~~~
 var package_b: () = package.NS.ChildNS.B();
 
 // CHECK:STDOUT: --- implicit.carbon
@@ -52,39 +62,39 @@ var package_b: () = package.NS.ChildNS.B();
 // CHECK:STDOUT:   %.2: <namespace> = namespace {.ChildNS = %.3, .A = %import_ref.3}, %import_ref.1 [template]
 // CHECK:STDOUT:   %import_ref.2: <namespace> = import_ref ir1, inst+2, used
 // CHECK:STDOUT:   %.3: <namespace> = namespace {.B = %import_ref.4}, %import_ref.2 [template]
-// CHECK:STDOUT:   %import_ref.3: <error> = import_ref ir1, inst+3, used
-// CHECK:STDOUT:   %import_ref.4: <error> = import_ref ir1, inst+4, used
-// CHECK:STDOUT:   %.loc4_9.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc4_9.2: type = converted %.loc4_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+3, used
+// CHECK:STDOUT:   %import_ref.4: <function> = import_ref ir1, inst+4, used
+// CHECK:STDOUT:   %.loc7_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc7_9.2: type = converted %.loc7_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %a.var: ref () = var a
 // CHECK:STDOUT:   %a: ref () = bind_name a, %a.var
-// CHECK:STDOUT:   %NS.ref.loc4: <namespace> = name_ref NS, %.2 [template = %.2]
-// CHECK:STDOUT:   %A.ref.loc4: <error> = name_ref A, %import_ref.3
+// CHECK:STDOUT:   %NS.ref.loc7: <namespace> = name_ref NS, %.2 [template = %.2]
+// CHECK:STDOUT:   %A.ref.loc7: <function> = name_ref A, %import_ref.3
 // CHECK:STDOUT:   assign %a.var, <error>
-// CHECK:STDOUT:   %.loc5_9.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc5_9.2: type = converted %.loc5_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc11_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc11_9.2: type = converted %.loc11_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %b.var: ref () = var b
 // CHECK:STDOUT:   %b: ref () = bind_name b, %b.var
-// CHECK:STDOUT:   %NS.ref.loc5: <namespace> = name_ref NS, %.2 [template = %.2]
-// CHECK:STDOUT:   %ChildNS.ref.loc5: <namespace> = name_ref ChildNS, %.3 [template = %.3]
-// CHECK:STDOUT:   %B.ref.loc5: <error> = name_ref B, %import_ref.4
+// CHECK:STDOUT:   %NS.ref.loc11: <namespace> = name_ref NS, %.2 [template = %.2]
+// CHECK:STDOUT:   %ChildNS.ref.loc11: <namespace> = name_ref ChildNS, %.3 [template = %.3]
+// CHECK:STDOUT:   %B.ref.loc11: <function> = name_ref B, %import_ref.4
 // CHECK:STDOUT:   assign %b.var, <error>
-// CHECK:STDOUT:   %.loc7_17.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc7_17.2: type = converted %.loc7_17.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc16_17.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc16_17.2: type = converted %.loc16_17.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %package_a.var: ref () = var package_a
 // CHECK:STDOUT:   %package_a: ref () = bind_name package_a, %package_a.var
-// CHECK:STDOUT:   %package.ref.loc7: <namespace> = name_ref package, package [template = package]
-// CHECK:STDOUT:   %NS.ref.loc7: <namespace> = name_ref NS, %.2 [template = %.2]
-// CHECK:STDOUT:   %A.ref.loc7: <error> = name_ref A, %import_ref.3
+// CHECK:STDOUT:   %package.ref.loc16: <namespace> = name_ref package, package [template = package]
+// CHECK:STDOUT:   %NS.ref.loc16: <namespace> = name_ref NS, %.2 [template = %.2]
+// CHECK:STDOUT:   %A.ref.loc16: <function> = name_ref A, %import_ref.3
 // CHECK:STDOUT:   assign %package_a.var, <error>
-// CHECK:STDOUT:   %.loc8_17.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc8_17.2: type = converted %.loc8_17.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc20_17.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc20_17.2: type = converted %.loc20_17.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %package_b.var: ref () = var package_b
 // CHECK:STDOUT:   %package_b: ref () = bind_name package_b, %package_b.var
-// CHECK:STDOUT:   %package.ref.loc8: <namespace> = name_ref package, package [template = package]
-// CHECK:STDOUT:   %NS.ref.loc8: <namespace> = name_ref NS, %.2 [template = %.2]
-// CHECK:STDOUT:   %ChildNS.ref.loc8: <namespace> = name_ref ChildNS, %.3 [template = %.3]
-// CHECK:STDOUT:   %B.ref.loc8: <error> = name_ref B, %import_ref.4
+// CHECK:STDOUT:   %package.ref.loc20: <namespace> = name_ref package, package [template = package]
+// CHECK:STDOUT:   %NS.ref.loc20: <namespace> = name_ref NS, %.2 [template = %.2]
+// CHECK:STDOUT:   %ChildNS.ref.loc20: <namespace> = name_ref ChildNS, %.3 [template = %.3]
+// CHECK:STDOUT:   %B.ref.loc20: <function> = name_ref B, %import_ref.4
 // CHECK:STDOUT:   assign %package_b.var, <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 7 - 5
toolchain/check/testdata/namespace/fail_todo_imported_indirect.carbon

@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: e.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
 
 // --- a.carbon
 
@@ -37,6 +36,9 @@ fn A.B.C.D() {};
 package Same library "e" api;
 import library "d";
 
+// CHECK:STDERR: e.carbon:[[@LINE+3]]:13: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var e: () = A.B.C.D();
+// CHECK:STDERR:             ^~~~~~~~
 var e: () = A.B.C.D();
 
 // CHECK:STDOUT: --- a.carbon
@@ -98,15 +100,15 @@ var e: () = A.B.C.D();
 // CHECK:STDOUT:   %.3: <namespace> = namespace {.C = %.4}, %import_ref.2 [template]
 // CHECK:STDOUT:   %import_ref.3: <namespace> = import_ref ir1, inst+6, used
 // CHECK:STDOUT:   %.4: <namespace> = namespace {.D = %import_ref.4}, %import_ref.3 [template]
-// CHECK:STDOUT:   %import_ref.4: <error> = import_ref ir1, inst+7, used
-// CHECK:STDOUT:   %.loc5_9.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc5_9.2: type = converted %.loc5_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %import_ref.4: <function> = import_ref ir1, inst+7, used
+// CHECK:STDOUT:   %.loc8_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc8_9.2: type = converted %.loc8_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %e.var: ref () = var e
 // CHECK:STDOUT:   %e: ref () = bind_name e, %e.var
 // CHECK:STDOUT:   %A.ref: <namespace> = name_ref A, %.2 [template = %.2]
 // CHECK:STDOUT:   %B.ref: <namespace> = name_ref B, %.3 [template = %.3]
 // CHECK:STDOUT:   %C.ref: <namespace> = name_ref C, %.4 [template = %.4]
-// CHECK:STDOUT:   %D.ref: <error> = name_ref D, %import_ref.4
+// CHECK:STDOUT:   %D.ref: <function> = name_ref D, %import_ref.4
 // CHECK:STDOUT:   assign %e.var, <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 19 - 13
toolchain/check/testdata/namespace/fail_todo_merging.carbon

@@ -3,9 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: c.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
-// CHECK:STDERR: c.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
-// CHECK:STDERR: c.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
 
 // --- a.carbon
 
@@ -39,8 +36,17 @@ namespace NS;
 fn NS.C() {}
 
 fn Run() {
+  // CHECK:STDERR: c.carbon:[[@LINE+3]]:3: ERROR: Value of type `<function>` is not callable.
+  // CHECK:STDERR:   NS.A();
+  // CHECK:STDERR:   ^~~~~
   NS.A();
+  // CHECK:STDERR: c.carbon:[[@LINE+3]]:3: ERROR: Value of type `<function>` is not callable.
+  // CHECK:STDERR:   NS.B1();
+  // CHECK:STDERR:   ^~~~~~
   NS.B1();
+  // CHECK:STDERR: c.carbon:[[@LINE+3]]:3: ERROR: Value of type `<function>` is not callable.
+  // CHECK:STDERR:   NS.B2();
+  // CHECK:STDERR:   ^~~~~~
   NS.B2();
   NS.C();
 }
@@ -88,9 +94,9 @@ fn Run() {
 // CHECK:STDOUT:   package: <namespace> = namespace {.NS = %.loc7_13.1, .Run = %Run} [template]
 // CHECK:STDOUT:   %import_ref.1: <namespace> = import_ref ir1, inst+1, used
 // CHECK:STDOUT:   %.loc7_13.1: <namespace> = namespace {.A = %import_ref.2, .B1 = %import_ref.3, .B2 = %import_ref.4, .C = %C}, %import_ref.1 [template]
-// CHECK:STDOUT:   %import_ref.2: <error> = import_ref ir1, inst+2, used
-// CHECK:STDOUT:   %import_ref.3: <error> = import_ref ir2, inst+2, used
-// CHECK:STDOUT:   %import_ref.4: <error> = import_ref ir2, inst+5, used
+// CHECK:STDOUT:   %import_ref.2: <function> = import_ref ir1, inst+2, used
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir2, inst+2, used
+// CHECK:STDOUT:   %import_ref.4: <function> = import_ref ir2, inst+5, used
 // CHECK:STDOUT:   %.loc7_13.2: <namespace> = namespace {} [template]
 // CHECK:STDOUT:   %C: <function> = fn_decl @C [template]
 // CHECK:STDOUT:   %Run: <function> = fn_decl @Run [template]
@@ -103,15 +109,15 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %NS.ref.loc12: <namespace> = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1]
-// CHECK:STDOUT:   %A.ref: <error> = name_ref A, file.%import_ref.2
-// CHECK:STDOUT:   %NS.ref.loc13: <namespace> = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1]
-// CHECK:STDOUT:   %B1.ref: <error> = name_ref B1, file.%import_ref.3
-// CHECK:STDOUT:   %NS.ref.loc14: <namespace> = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1]
-// CHECK:STDOUT:   %B2.ref: <error> = name_ref B2, file.%import_ref.4
 // CHECK:STDOUT:   %NS.ref.loc15: <namespace> = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1]
+// CHECK:STDOUT:   %A.ref: <function> = name_ref A, file.%import_ref.2
+// CHECK:STDOUT:   %NS.ref.loc19: <namespace> = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1]
+// CHECK:STDOUT:   %B1.ref: <function> = name_ref B1, file.%import_ref.3
+// CHECK:STDOUT:   %NS.ref.loc23: <namespace> = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1]
+// CHECK:STDOUT:   %B2.ref: <function> = name_ref B2, file.%import_ref.4
+// CHECK:STDOUT:   %NS.ref.loc24: <namespace> = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1]
 // CHECK:STDOUT:   %C.ref: <function> = name_ref C, file.%C [template = file.%C]
-// CHECK:STDOUT:   %.loc15: init () = call %C.ref()
+// CHECK:STDOUT:   %.loc24: init () = call %C.ref()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 26 - 16
toolchain/check/testdata/packages/fail_todo_loaded_global.carbon

@@ -3,8 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // AUTOUPDATE
-// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
-// CHECK:STDERR: same_package_importer.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for FunctionDecl`.
 
 // --- implicit.carbon
 
@@ -16,8 +14,14 @@ fn A();
 
 package Implicit impl;
 
+// CHECK:STDERR: implicit.impl.carbon:[[@LINE+3]]:13: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var a: () = A();
+// CHECK:STDERR:             ^~
 var a: () = A();
 
+// CHECK:STDERR: implicit.impl.carbon:[[@LINE+3]]:21: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var package_a: () = package.A();
+// CHECK:STDERR:                     ^~~~~~~~~~
 var package_a: () = package.A();
 
 // --- same_package.carbon
@@ -32,8 +36,14 @@ package SamePackage library "importer" api;
 
 import library default;
 
+// CHECK:STDERR: same_package_importer.carbon:[[@LINE+3]]:13: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var b: () = B();
+// CHECK:STDERR:             ^~
 var b: () = B();
 
+// CHECK:STDERR: same_package_importer.carbon:[[@LINE+3]]:21: ERROR: Value of type `<function>` is not callable.
+// CHECK:STDERR: var package_b: () = package.B();
+// CHECK:STDERR:                     ^~~~~~~~~~
 var package_b: () = package.B();
 
 // CHECK:STDOUT: --- implicit.carbon
@@ -53,19 +63,19 @@ var package_b: () = package.B();
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace {.A = %import_ref, .a = %a, .package_a = %package_a} [template]
-// CHECK:STDOUT:   %import_ref: <error> = import_ref ir1, inst+1, used
-// CHECK:STDOUT:   %.loc4_9.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc4_9.2: type = converted %.loc4_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used
+// CHECK:STDOUT:   %.loc7_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc7_9.2: type = converted %.loc7_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %a.var: ref () = var a
 // CHECK:STDOUT:   %a: ref () = bind_name a, %a.var
-// CHECK:STDOUT:   %A.ref.loc4: <error> = name_ref A, %import_ref
+// CHECK:STDOUT:   %A.ref.loc7: <function> = name_ref A, %import_ref
 // CHECK:STDOUT:   assign %a.var, <error>
-// CHECK:STDOUT:   %.loc6_17.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc6_17.2: type = converted %.loc6_17.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc12_17.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc12_17.2: type = converted %.loc12_17.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %package_a.var: ref () = var package_a
 // CHECK:STDOUT:   %package_a: ref () = bind_name package_a, %package_a.var
 // CHECK:STDOUT:   %package.ref: <namespace> = name_ref package, package [template = package]
-// CHECK:STDOUT:   %A.ref.loc6: <error> = name_ref A, %import_ref
+// CHECK:STDOUT:   %A.ref.loc12: <function> = name_ref A, %import_ref
 // CHECK:STDOUT:   assign %package_a.var, <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -86,19 +96,19 @@ var package_b: () = package.B();
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace {.B = %import_ref, .b = %b, .package_b = %package_b} [template]
-// CHECK:STDOUT:   %import_ref: <error> = import_ref ir1, inst+1, used
-// CHECK:STDOUT:   %.loc6_9.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used
+// CHECK:STDOUT:   %.loc9_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc9_9.2: type = converted %.loc9_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %b.var: ref () = var b
 // CHECK:STDOUT:   %b: ref () = bind_name b, %b.var
-// CHECK:STDOUT:   %B.ref.loc6: <error> = name_ref B, %import_ref
+// CHECK:STDOUT:   %B.ref.loc9: <function> = name_ref B, %import_ref
 // CHECK:STDOUT:   assign %b.var, <error>
-// CHECK:STDOUT:   %.loc8_17.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc8_17.2: type = converted %.loc8_17.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc14_17.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc14_17.2: type = converted %.loc14_17.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %package_b.var: ref () = var package_b
 // CHECK:STDOUT:   %package_b: ref () = bind_name package_b, %package_b.var
 // CHECK:STDOUT:   %package.ref: <namespace> = name_ref package, package [template = package]
-// CHECK:STDOUT:   %B.ref.loc8: <error> = name_ref B, %import_ref
+// CHECK:STDOUT:   %B.ref.loc14: <function> = name_ref B, %import_ref
 // CHECK:STDOUT:   assign %package_b.var, <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 61 - 0
toolchain/check/testdata/pointer/fail_todo_import.carbon

@@ -0,0 +1,61 @@
+// 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
+//
+// When there are no more cases that can hit a TODO, remove this test. Until
+// then, update it whenever its target is implemented.
+//
+// AUTOUPDATE
+// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for non-builtin type`.
+
+// --- implicit.carbon
+
+package Implicit api;
+
+var a_orig: i32 = 0;
+var a_ref: i32* = &a_orig;
+
+// --- implicit.impl.carbon
+
+package Implicit impl;
+
+var a: i32* = a_ref;
+
+// CHECK:STDOUT: --- implicit.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.2: type = ptr_type i32 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.a_orig = %a_orig, .a_ref = %a_ref} [template]
+// CHECK:STDOUT:   %a_orig.var: ref i32 = var a_orig
+// CHECK:STDOUT:   %a_orig: ref i32 = bind_name a_orig, %a_orig.var
+// CHECK:STDOUT:   %.loc4: i32 = int_literal 0 [template = constants.%.1]
+// CHECK:STDOUT:   assign %a_orig.var, %.loc4
+// CHECK:STDOUT:   %.loc5_15: type = ptr_type i32 [template = constants.%.2]
+// CHECK:STDOUT:   %a_ref.var: ref i32* = var a_ref
+// CHECK:STDOUT:   %a_ref: ref i32* = bind_name a_ref, %a_ref.var
+// CHECK:STDOUT:   %a_orig.ref: ref i32 = name_ref a_orig, %a_orig
+// CHECK:STDOUT:   %.loc5_19: i32* = addr_of %a_orig.ref
+// CHECK:STDOUT:   assign %a_ref.var, %.loc5_19
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- implicit.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = ptr_type i32 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.a_orig = %import_ref.1, .a_ref = %import_ref.2, .a = %a} [template]
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+2, unused
+// CHECK:STDOUT:   %import_ref.2: ref <error> = import_ref ir1, inst+9, used
+// CHECK:STDOUT:   %.loc4: type = ptr_type i32 [template = constants.%.1]
+// CHECK:STDOUT:   %a.var: ref i32* = var a
+// CHECK:STDOUT:   %a: ref i32* = bind_name a, %a.var
+// CHECK:STDOUT:   %a_ref.ref: ref <error> = name_ref a_ref, %import_ref.2
+// CHECK:STDOUT:   assign %a.var, <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 58 - 0
toolchain/check/testdata/struct/fail_todo_import.carbon

@@ -0,0 +1,58 @@
+// 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
+//
+// When there are no more cases that can hit a TODO, remove this test. Until
+// then, update it whenever its target is implemented.
+//
+// AUTOUPDATE
+// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for non-builtin type`.
+
+// --- implicit.carbon
+
+package Implicit api;
+
+var a_ref: {.a: i32} = {.a = 0};
+
+// --- implicit.impl.carbon
+
+package Implicit impl;
+
+var a: {.a: i32} = a_ref;
+
+// CHECK:STDOUT: --- implicit.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = struct_type {.a: i32} [template]
+// CHECK:STDOUT:   %.2: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.3: {.a: i32} = struct_value (%.2) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.a_ref = %a_ref} [template]
+// CHECK:STDOUT:   %.loc4_20: type = struct_type {.a: i32} [template = constants.%.1]
+// CHECK:STDOUT:   %a_ref.var: ref {.a: i32} = var a_ref
+// CHECK:STDOUT:   %a_ref: ref {.a: i32} = bind_name a_ref, %a_ref.var
+// CHECK:STDOUT:   %.loc4_30: i32 = int_literal 0 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc4_31.1: {.a: i32} = struct_literal (%.loc4_30)
+// CHECK:STDOUT:   %.loc4_31.2: init {.a: i32} = struct_init (%.loc4_30) to %a_ref.var [template = constants.%.3]
+// CHECK:STDOUT:   %.loc4_31.3: init {.a: i32} = converted %.loc4_31.1, %.loc4_31.2 [template = constants.%.3]
+// CHECK:STDOUT:   assign %a_ref.var, %.loc4_31.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- implicit.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = struct_type {.a: i32} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.a_ref = %import_ref, .a = %a} [template]
+// CHECK:STDOUT:   %import_ref: ref <error> = import_ref ir1, inst+6, used
+// CHECK:STDOUT:   %.loc4: type = struct_type {.a: i32} [template = constants.%.1]
+// CHECK:STDOUT:   %a.var: ref {.a: i32} = var a
+// CHECK:STDOUT:   %a: ref {.a: i32} = bind_name a, %a.var
+// CHECK:STDOUT:   %a_ref.ref: ref <error> = name_ref a_ref, %import_ref
+// CHECK:STDOUT:   assign %a.var, <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 62 - 0
toolchain/check/testdata/tuples/fail_todo_import.carbon

@@ -0,0 +1,62 @@
+// 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
+//
+// When there are no more cases that can hit a TODO, remove this test. Until
+// then, update it whenever its target is implemented.
+//
+// AUTOUPDATE
+// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for non-builtin type`.
+
+// --- implicit.carbon
+
+package Implicit api;
+
+var a_ref: (i32,) = (0,);
+
+// --- implicit.impl.carbon
+
+package Implicit impl;
+
+var a: (i32,) = a_ref;
+
+// CHECK:STDOUT: --- implicit.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type (type) [template]
+// CHECK:STDOUT:   %.2: type = tuple_type (i32) [template]
+// CHECK:STDOUT:   %.3: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.4: (i32,) = tuple_value (%.3) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.a_ref = %a_ref} [template]
+// CHECK:STDOUT:   %.loc4_17.1: (type,) = tuple_literal (i32)
+// CHECK:STDOUT:   %.loc4_17.2: type = converted %.loc4_17.1, constants.%.2 [template = constants.%.2]
+// CHECK:STDOUT:   %a_ref.var: ref (i32,) = var a_ref
+// CHECK:STDOUT:   %a_ref: ref (i32,) = bind_name a_ref, %a_ref.var
+// CHECK:STDOUT:   %.loc4_22: i32 = int_literal 0 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc4_24.1: (i32,) = tuple_literal (%.loc4_22)
+// CHECK:STDOUT:   %.loc4_24.2: init (i32,) = tuple_init (%.loc4_22) to %a_ref.var [template = constants.%.4]
+// CHECK:STDOUT:   %.loc4_24.3: init (i32,) = converted %.loc4_24.1, %.loc4_24.2 [template = constants.%.4]
+// CHECK:STDOUT:   assign %a_ref.var, %.loc4_24.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- implicit.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type (type) [template]
+// CHECK:STDOUT:   %.2: type = tuple_type (i32) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.a_ref = %import_ref, .a = %a} [template]
+// CHECK:STDOUT:   %import_ref: ref <error> = import_ref ir1, inst+6, used
+// CHECK:STDOUT:   %.loc4_13.1: (type,) = tuple_literal (i32)
+// CHECK:STDOUT:   %.loc4_13.2: type = converted %.loc4_13.1, constants.%.2 [template = constants.%.2]
+// CHECK:STDOUT:   %a.var: ref (i32,) = var a
+// CHECK:STDOUT:   %a: ref (i32,) = bind_name a, %a.var
+// CHECK:STDOUT:   %a_ref.ref: ref <error> = name_ref a_ref, %import_ref
+// CHECK:STDOUT:   assign %a.var, <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 7 - 13
toolchain/check/testdata/var/fail_todo_import.carbon → toolchain/check/testdata/var/import.carbon

@@ -6,7 +6,6 @@
 // then, update it whenever its target is implemented.
 //
 // AUTOUPDATE
-// CHECK:STDERR: implicit.impl.carbon: ERROR: Semantics TODO: `TODO: ResolveIfImportRefUnused for BindName`.
 
 // --- implicit.carbon
 
@@ -18,7 +17,7 @@ var a_ref: i32 = 0;
 
 package Implicit impl;
 
-var a: () = a_ref;
+var a: i32 = a_ref;
 
 // CHECK:STDOUT: --- implicit.carbon
 // CHECK:STDOUT:
@@ -36,18 +35,13 @@ var a: () = a_ref;
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- implicit.impl.carbon
 // CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.1: type = tuple_type () [template]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace {.a_ref = %import_ref, .a = %a} [template]
-// CHECK:STDOUT:   %import_ref: ref <error> = import_ref ir1, inst+2, used
-// CHECK:STDOUT:   %.loc4_9.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc4_9.2: type = converted %.loc4_9.1, constants.%.1 [template = constants.%.1]
-// CHECK:STDOUT:   %a.var: ref () = var a
-// CHECK:STDOUT:   %a: ref () = bind_name a, %a.var
-// CHECK:STDOUT:   %a_ref.ref: ref <error> = name_ref a_ref, %import_ref
-// CHECK:STDOUT:   assign %a.var, <error>
+// CHECK:STDOUT:   %import_ref: ref i32 = import_ref ir1, inst+2, used
+// CHECK:STDOUT:   %a.var: ref i32 = var a
+// CHECK:STDOUT:   %a: ref i32 = bind_name a, %a.var
+// CHECK:STDOUT:   %a_ref.ref: ref i32 = name_ref a_ref, %import_ref
+// CHECK:STDOUT:   %.loc4: i32 = bind_value %a_ref.ref
+// CHECK:STDOUT:   assign %a.var, %.loc4
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 0
toolchain/sem_ir/BUILD

@@ -94,6 +94,7 @@ cc_library(
         ":file",
         ":ids",
         ":inst_kind",
+        "//common:ostream",
         "//toolchain/base:value_store",
         "//toolchain/lex:tokenized_buffer",
         "//toolchain/parse:tree",

+ 1 - 1
toolchain/sem_ir/file.cpp

@@ -296,7 +296,7 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
 
     // Builtins have designated labels.
     if (step.inst_id.is_builtin()) {
-      out << BuiltinKind::FromInt(step.inst_id.index).label();
+      out << step.inst_id.builtin_kind().label();
       continue;
     }
 

+ 2 - 1
toolchain/sem_ir/formatter.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/sem_ir/formatter.h"
 
+#include "common/ostream.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringMap.h"
@@ -170,7 +171,7 @@ class InstNamer {
 
     // Check for a builtin.
     if (inst_id.is_builtin()) {
-      return BuiltinKind::FromInt(inst_id.index).label().str();
+      return inst_id.builtin_kind().label().str();
     }
 
     if (inst_id == InstId::PackageNamespace) {

+ 7 - 1
toolchain/sem_ir/ids.h

@@ -51,12 +51,18 @@ struct InstId : public IdBase, public Printable<InstId> {
     return index < BuiltinKind::ValidCount;
   }
 
+  // Returns the BuiltinKind. Requires is_builtin.
+  auto builtin_kind() const -> BuiltinKind {
+    CARBON_CHECK(is_builtin());
+    return BuiltinKind::FromInt(index);
+  }
+
   auto Print(llvm::raw_ostream& out) const -> void {
     out << "inst";
     if (!is_valid()) {
       IdBase::Print(out);
     } else if (is_builtin()) {
-      out << BuiltinKind::FromInt(index);
+      out << builtin_kind();
     } else {
       // Use the `+` as a small reminder that this is a delta, rather than an
       // absolute index.