Explorar el Código

Import support for tuple, struct and integer values. (#4042)

These can be imported as the argument values for a generic. Also, the
name of a generic is modeled as a `StructValue`.
Richard Smith hace 1 año
padre
commit
4aa34978eb

+ 61 - 8
toolchain/check/import_ref.cpp

@@ -385,7 +385,7 @@ class ImportRefResolver {
     inst_ids.reserve(import_block.size());
     for (auto import_inst_id : import_block) {
       auto const_id = GetLocalConstantId(import_inst_id);
-      inst_ids.push_back(const_id.inst_id());
+      inst_ids.push_back(const_id.inst_id_if_valid());
     }
 
     return inst_ids;
@@ -692,15 +692,24 @@ class ImportRefResolver {
       case CARBON_KIND(SemIR::InterfaceType inst): {
         return TryResolveTypedInst(inst);
       }
+      case CARBON_KIND(SemIR::IntLiteral inst): {
+        return TryResolveTypedInst(inst);
+      }
       case CARBON_KIND(SemIR::PointerType inst): {
         return TryResolveTypedInst(inst);
       }
       case CARBON_KIND(SemIR::StructType inst): {
         return TryResolveTypedInst(inst, inst_id);
       }
+      case CARBON_KIND(SemIR::StructValue inst): {
+        return TryResolveTypedInst(inst);
+      }
       case CARBON_KIND(SemIR::TupleType inst): {
         return TryResolveTypedInst(inst);
       }
+      case CARBON_KIND(SemIR::TupleValue inst): {
+        return TryResolveTypedInst(inst);
+      }
       case CARBON_KIND(SemIR::UnboundElementType inst): {
         return TryResolveTypedInst(inst);
       }
@@ -1314,11 +1323,25 @@ class ImportRefResolver {
         << "Failed to import an element without adding new work.";
 
     auto elements_id = context_.inst_blocks().Add(elements);
-    return {.const_id = TryEvalInst(
-                context_, SemIR::InstId::Invalid,
-                SemIR::InterfaceWitness{.type_id = context_.GetBuiltinType(
-                                            SemIR::BuiltinKind::WitnessType),
-                                        .elements_id = elements_id})};
+    SemIR::InterfaceWitness new_inst = {
+        .type_id = context_.GetBuiltinType(SemIR::BuiltinKind::WitnessType),
+        .elements_id = elements_id};
+    return {.const_id =
+                TryEvalInst(context_, SemIR::InstId::Invalid, new_inst)};
+  }
+
+  auto TryResolveTypedInst(SemIR::IntLiteral inst) -> ResolveResult {
+    auto initial_work = work_stack_.size();
+    auto type_id = GetLocalConstantId(inst.type_id);
+    if (HasNewWork(initial_work)) {
+      return ResolveResult::Retry();
+    }
+
+    SemIR::IntLiteral new_inst = {
+        .type_id = context_.GetTypeIdForTypeConstant(type_id),
+        .int_id = context_.ints().Add(import_ir_.ints().Get(inst.int_id))};
+    return {.const_id =
+                TryEvalInst(context_, SemIR::InstId::Invalid, new_inst)};
   }
 
   auto TryResolveTypedInst(SemIR::PointerType inst) -> ResolveResult {
@@ -1369,6 +1392,21 @@ class ImportRefResolver {
                 context_.GetStructType(context_.inst_blocks().Add(fields)))};
   }
 
+  auto TryResolveTypedInst(SemIR::StructValue inst) -> ResolveResult {
+    auto initial_work = work_stack_.size();
+    auto type_id = GetLocalConstantId(inst.type_id);
+    auto elems = GetLocalInstBlockContents(inst.elements_id);
+    if (HasNewWork(initial_work)) {
+      return ResolveResult::Retry();
+    }
+
+    SemIR::StructValue new_inst = {
+        .type_id = context_.GetTypeIdForTypeConstant(type_id),
+        .elements_id = GetLocalCanonicalInstBlockId(inst.elements_id, elems)};
+    return {.const_id =
+                TryEvalInst(context_, SemIR::InstId::Invalid, new_inst)};
+  }
+
   auto TryResolveTypedInst(SemIR::TupleType inst) -> ResolveResult {
     CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
 
@@ -1391,8 +1429,23 @@ class ImportRefResolver {
       elem_type_ids.push_back(context_.GetTypeIdForTypeConstant(elem_const_id));
     }
 
-    return {
-        context_.types().GetConstantId(context_.GetTupleType(elem_type_ids))};
+    return {.const_id = context_.types().GetConstantId(
+                context_.GetTupleType(elem_type_ids))};
+  }
+
+  auto TryResolveTypedInst(SemIR::TupleValue inst) -> ResolveResult {
+    auto initial_work = work_stack_.size();
+    auto type_id = GetLocalConstantId(inst.type_id);
+    auto elems = GetLocalInstBlockContents(inst.elements_id);
+    if (HasNewWork(initial_work)) {
+      return ResolveResult::Retry();
+    }
+
+    SemIR::TupleValue new_inst = {
+        .type_id = context_.GetTypeIdForTypeConstant(type_id),
+        .elements_id = GetLocalCanonicalInstBlockId(inst.elements_id, elems)};
+    return {.const_id =
+                TryEvalInst(context_, SemIR::InstId::Invalid, new_inst)};
   }
 
   auto TryResolveTypedInst(SemIR::UnboundElementType inst) -> ResolveResult {

+ 11 - 14
toolchain/check/testdata/interface/no_prelude/generic_import.carbon

@@ -16,13 +16,9 @@ interface AddWith(T:! type) {
   fn F();
 }
 
-// --- fail_todo_b.carbon
+// --- b.carbon
 
 library "b";
-// CHECK:STDERR: fail_todo_b.carbon:[[@LINE+4]]:1: In import.
-// CHECK:STDERR: import library "a";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: a.carbon: ERROR: Semantics TODO: `TryResolveInst on StructValue`.
 import library "a";
 
 class C {}
@@ -68,16 +64,17 @@ impl C as AddWith(C) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F();
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_b.carbon
+// CHECK:STDOUT: --- b.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
 // CHECK:STDOUT:   %AddWith.type: type = generic_interface_type @AddWith [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
-// CHECK:STDOUT:   %AddWith: %AddWith.type = struct_value () [template]
-// CHECK:STDOUT:   %Self: <error> = bind_symbolic_name Self 1 [symbolic]
-// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+14> [symbolic]
+// CHECK:STDOUT:   %AddWith.1: %AddWith.type = struct_value () [template]
+// CHECK:STDOUT:   %AddWith.2: %AddWith.type = struct_value () [template]
+// CHECK:STDOUT:   %Self: %AddWith.2 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+15> [symbolic]
 // CHECK:STDOUT:   %.3: type = interface_type @AddWith, (%C) [template]
 // CHECK:STDOUT:   %F.type.1: type = fn_type @F.1 [template]
 // CHECK:STDOUT:   %F.1: %F.type.1 = struct_value () [template]
@@ -91,16 +88,16 @@ impl C as AddWith(C) {
 // CHECK:STDOUT:     .AddWith = %import_ref.1
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.1: %AddWith.type = import_ref ir1, inst+4, loaded [template = constants.%AddWith]
+// CHECK:STDOUT:   %import_ref.1: %AddWith.type = import_ref ir1, inst+4, loaded [template = constants.%AddWith.1]
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+8, unloaded
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+14, unloaded
 // CHECK:STDOUT:   %import_ref.4: %F.type.2 = import_ref ir1, inst+10, loaded [template = constants.%F.2]
 // CHECK:STDOUT:   impl_decl @impl {
-// CHECK:STDOUT:     %C.ref.loc11_6: type = name_ref C, %C.decl [template = constants.%C]
-// CHECK:STDOUT:     %AddWith.ref: %AddWith.type = name_ref AddWith, %import_ref.1 [template = constants.%AddWith]
-// CHECK:STDOUT:     %C.ref.loc11_19: type = name_ref C, %C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc11: type = interface_type @AddWith, (%C.ref.loc11_19) [template = constants.%.3]
+// CHECK:STDOUT:     %C.ref.loc7_6: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %AddWith.ref: %AddWith.type = name_ref AddWith, %import_ref.1 [template = constants.%AddWith.1]
+// CHECK:STDOUT:     %C.ref.loc7_19: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc7: type = interface_type @AddWith, (%C.ref.loc7_19) [template = constants.%.3]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 247 - 4
toolchain/check/testdata/struct/import.carbon

@@ -16,12 +16,43 @@ var a_ref: {.a: i32} = {.a = 0};
 var b_ref: {.a: {.b: i32, .c: (i32,)}, .d: i32} =
     {.a = {.b = 0, .c = (0,)}, .d = 0};
 
+class C(S:! {.a: i32, .b: i32}) {}
+fn F() -> C({.a = 1, .b = 2});
+
 // --- implicit.impl.carbon
 
 impl package Implicit;
 
 var a: {.a: i32} = a_ref;
 var b: {.a: {.b: i32, .c: (i32,)}, .d: i32} = b_ref;
+var c: C({.a = 1, .b = 2}) = F();
+
+// --- fail_bad_type.impl.carbon
+
+impl package Implicit;
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+14]]:1: ERROR: Cannot implicitly convert from `C` to `C`.
+// CHECK:STDERR: var c_bad: C({.c = 1, .d = 2}) = F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+10]]:14: ERROR: Missing value for field `a` in struct initialization.
+// CHECK:STDERR: var c_bad: C({.c = 1, .d = 2}) = F();
+// CHECK:STDERR:              ^~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE-8]]:6: In import.
+// CHECK:STDERR: impl package Implicit;
+// CHECK:STDERR:      ^~~~~~~
+// CHECK:STDERR: implicit.carbon:8:1: Initializing parameter 1 of function declared here.
+// CHECK:STDERR: class C(S:! {.a: i32, .b: i32}) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var c_bad: C({.c = 1, .d = 2}) = F();
+
+// --- fail_bad_value.impl.carbon
+
+impl package Implicit;
+// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+3]]:1: ERROR: Cannot implicitly convert from `C` to `C`.
+// CHECK:STDERR: var c_bad: C({.a = 3, .b = 4}) = F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+var c_bad: C({.a = 3, .b = 4}) = F();
 
 // CHECK:STDOUT: --- implicit.carbon
 // CHECK:STDOUT:
@@ -42,6 +73,19 @@ var b: {.a: {.b: i32, .c: (i32,)}, .d: i32} = b_ref;
 // CHECK:STDOUT:   %tuple: %.5 = tuple_value (%.3) [template]
 // CHECK:STDOUT:   %struct.2: %.6 = struct_value (%.3, %tuple) [template]
 // CHECK:STDOUT:   %struct.3: %.7 = struct_value (%struct.2, %.3) [template]
+// CHECK:STDOUT:   %.11: type = struct_type {.a: i32, .b: i32} [template]
+// CHECK:STDOUT:   %S: %.11 = bind_symbolic_name S 0 [symbolic]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %.12: type = struct_type {} [template]
+// CHECK:STDOUT:   %.13: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.14: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.15: type = ptr_type %.11 [template]
+// CHECK:STDOUT:   %struct.4: %.11 = struct_value (%.13, %.14) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%struct.4) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -49,6 +93,8 @@ var b: {.a: {.b: i32, .c: (i32,)}, .d: i32} = b_ref;
 // CHECK:STDOUT:     .Core = %Core
 // CHECK:STDOUT:     .a_ref = %a_ref
 // CHECK:STDOUT:     .b_ref = %b_ref
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .F = %F.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
@@ -76,10 +122,40 @@ var b: {.a: {.b: i32, .c: (i32,)}, .d: i32} = b_ref;
 // CHECK:STDOUT:   %.loc5_47: type = struct_type {.a: %.6, .d: i32} [template = constants.%.7]
 // CHECK:STDOUT:   %b_ref.var: ref %.7 = var b_ref
 // CHECK:STDOUT:   %b_ref: ref %.7 = bind_name b_ref, %b_ref.var
+// CHECK:STDOUT:   %import_ref.5: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.6: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %C.decl: %C.type = class_decl @C [template = constants.%C.1] {
+// CHECK:STDOUT:     %int.make_type_32.loc8_18: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc8_18.1: type = value_of_initializer %int.make_type_32.loc8_18 [template = i32]
+// CHECK:STDOUT:     %.loc8_18.2: type = converted %int.make_type_32.loc8_18, %.loc8_18.1 [template = i32]
+// CHECK:STDOUT:     %int.make_type_32.loc8_27: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc8_27.1: type = value_of_initializer %int.make_type_32.loc8_27 [template = i32]
+// CHECK:STDOUT:     %.loc8_27.2: type = converted %int.make_type_32.loc8_27, %.loc8_27.1 [template = i32]
+// CHECK:STDOUT:     %.loc8_30: type = struct_type {.a: i32, .b: i32} [template = constants.%.11]
+// CHECK:STDOUT:     %S.loc8_9.1: %.11 = param S
+// CHECK:STDOUT:     %S.loc8_9.2: %.11 = bind_symbolic_name S 0, %S.loc8_9.1 [symbolic = constants.%S]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
+// CHECK:STDOUT:     %C.ref: %C.type = name_ref C, %C.decl [template = constants.%C.1]
+// CHECK:STDOUT:     %.loc9_19: i32 = int_literal 1 [template = constants.%.13]
+// CHECK:STDOUT:     %.loc9_27: i32 = int_literal 2 [template = constants.%.14]
+// CHECK:STDOUT:     %.loc9_28: %.11 = struct_literal (%.loc9_19, %.loc9_27)
+// CHECK:STDOUT:     %struct: %.11 = struct_value (%.loc9_19, %.loc9_27) [template = constants.%struct.4]
+// CHECK:STDOUT:     %.loc9_12: %.11 = converted %.loc9_28, %struct [template = constants.%struct.4]
+// CHECK:STDOUT:     %C: type = class_type @C, (%.loc9_12) [template = constants.%C.3]
+// CHECK:STDOUT:     @F.%return: ref %C.3 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.3;
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %.loc4_30: i32 = int_literal 0 [template = constants.%.3]
@@ -124,48 +200,84 @@ var b: {.a: {.b: i32, .c: (i32,)}, .d: i32} = b_ref;
 // CHECK:STDOUT:   %.7: type = ptr_type %.5 [template]
 // CHECK:STDOUT:   %.8: type = struct_type {.a: %.7, .d: i32} [template]
 // CHECK:STDOUT:   %.9: type = ptr_type %.8 [template]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %.10: type = struct_type {} [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %.11: type = struct_type {.a: i32, .b: i32} [template]
+// CHECK:STDOUT:   %S: %.11 = bind_symbolic_name S 0, <unexpected instref inst+101> [symbolic]
+// CHECK:STDOUT:   %.12: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.13: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.14: type = ptr_type %.11 [template]
+// CHECK:STDOUT:   %struct: %.11 = struct_value (%.12, %.13) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%struct) [template]
+// CHECK:STDOUT:   %.15: type = ptr_type %.10 [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .a_ref = %import_ref.1
 // CHECK:STDOUT:     .b_ref = %import_ref.2
+// CHECK:STDOUT:     .C = %import_ref.3
+// CHECK:STDOUT:     .F = %import_ref.4
 // CHECK:STDOUT:     .Core = %Core
 // CHECK:STDOUT:     .a = %a
 // CHECK:STDOUT:     .b = %b
+// CHECK:STDOUT:     .c = %c
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: ref %.2 = import_ref ir0, inst+16, loaded
 // CHECK:STDOUT:   %import_ref.2: ref %.6 = import_ref ir0, inst+59, loaded
+// CHECK:STDOUT:   %import_ref.3: %C.type = import_ref ir0, inst+103, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %import_ref.4: %F.type = import_ref ir0, inst+123, loaded [template = constants.%F]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %import_ref.3: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.5: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc4: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc4_13.1: type = value_of_initializer %int.make_type_32.loc4 [template = i32]
 // CHECK:STDOUT:   %.loc4_13.2: type = converted %int.make_type_32.loc4, %.loc4_13.1 [template = i32]
 // CHECK:STDOUT:   %.loc4_16: type = struct_type {.a: i32} [template = constants.%.2]
 // CHECK:STDOUT:   %a.var: ref %.2 = var a
 // CHECK:STDOUT:   %a: ref %.2 = bind_name a, %a.var
-// CHECK:STDOUT:   %import_ref.4: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.6: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc5_18: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc5_18.1: type = value_of_initializer %int.make_type_32.loc5_18 [template = i32]
 // CHECK:STDOUT:   %.loc5_18.2: type = converted %int.make_type_32.loc5_18, %.loc5_18.1 [template = i32]
-// CHECK:STDOUT:   %import_ref.5: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.7: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc5_28: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc5_32.1: %.3 = tuple_literal (%int.make_type_32.loc5_28)
 // CHECK:STDOUT:   %.loc5_32.2: type = value_of_initializer %int.make_type_32.loc5_28 [template = i32]
 // CHECK:STDOUT:   %.loc5_32.3: type = converted %int.make_type_32.loc5_28, %.loc5_32.2 [template = i32]
 // CHECK:STDOUT:   %.loc5_32.4: type = converted %.loc5_32.1, constants.%.4 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc5_33: type = struct_type {.b: i32, .c: %.4} [template = constants.%.5]
-// CHECK:STDOUT:   %import_ref.6: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.8: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc5_40: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc5_40.1: type = value_of_initializer %int.make_type_32.loc5_40 [template = i32]
 // CHECK:STDOUT:   %.loc5_40.2: type = converted %int.make_type_32.loc5_40, %.loc5_40.1 [template = i32]
 // CHECK:STDOUT:   %.loc5_43: type = struct_type {.a: %.5, .d: i32} [template = constants.%.6]
 // CHECK:STDOUT:   %b.var: ref %.6 = var b
 // CHECK:STDOUT:   %b: ref %.6 = bind_name b, %b.var
+// CHECK:STDOUT:   %import_ref.9 = import_ref ir0, inst+106, unloaded
+// CHECK:STDOUT:   %C.ref: %C.type = name_ref C, %import_ref.3 [template = constants.%C.1]
+// CHECK:STDOUT:   %.loc6_16: i32 = int_literal 1 [template = constants.%.12]
+// CHECK:STDOUT:   %.loc6_24: i32 = int_literal 2 [template = constants.%.13]
+// CHECK:STDOUT:   %.loc6_25: %.11 = struct_literal (%.loc6_16, %.loc6_24)
+// CHECK:STDOUT:   %struct: %.11 = struct_value (%.loc6_16, %.loc6_24) [template = constants.%struct]
+// CHECK:STDOUT:   %.loc6_9: %.11 = converted %.loc6_25, %struct [template = constants.%struct]
+// CHECK:STDOUT:   %C: type = class_type @C, (%.loc6_9) [template = constants.%C.3]
+// CHECK:STDOUT:   %c.var: ref %C.3 = var c
+// CHECK:STDOUT:   %c: ref %C.3 = bind_name c, %c.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.9
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.3;
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %a_ref.ref: ref %.2 = name_ref a_ref, file.%import_ref.1
@@ -197,6 +309,137 @@ var b: {.a: {.b: i32, .c: (i32,)}, .d: i32} = b_ref;
 // CHECK:STDOUT:   %.loc5_47.20: init %.6 = struct_init (%.loc5_47.15, %.loc5_47.19) to file.%b.var
 // CHECK:STDOUT:   %.loc5_52: init %.6 = converted %b_ref.ref, %.loc5_47.20
 // CHECK:STDOUT:   assign file.%b.var, %.loc5_52
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.4 [template = constants.%F]
+// CHECK:STDOUT:   %.loc6: ref %C.3 = splice_block file.%c.var {}
+// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc6
+// CHECK:STDOUT:   assign file.%c.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_bad_type.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %.3: type = struct_type {.a: i32, .b: i32} [template]
+// CHECK:STDOUT:   %S: %.3 = bind_symbolic_name S 0, <unexpected instref inst+19> [symbolic]
+// CHECK:STDOUT:   %.4: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.5: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.6: type = struct_type {.c: i32, .d: i32} [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.3 [template]
+// CHECK:STDOUT:   %.8: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %struct: %.3 = struct_value (%.4, %.5) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%struct) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .a_ref = %import_ref.1
+// CHECK:STDOUT:     .b_ref = %import_ref.2
+// CHECK:STDOUT:     .C = %import_ref.3
+// CHECK:STDOUT:     .F = %import_ref.4
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .c_bad = %c_bad
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir0, inst+16, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir0, inst+59, unloaded
+// CHECK:STDOUT:   %import_ref.3: %C.type = import_ref ir0, inst+103, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %import_ref.4: %F.type = import_ref ir0, inst+123, loaded [template = constants.%F]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir0, inst+106, unloaded
+// CHECK:STDOUT:   %C.ref: %C.type = name_ref C, %import_ref.3 [template = constants.%C.1]
+// CHECK:STDOUT:   %.loc17_20: i32 = int_literal 1 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc17_28: i32 = int_literal 2 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc17_29: %.6 = struct_literal (%.loc17_20, %.loc17_28)
+// CHECK:STDOUT:   %C: type = class_type @C [template = constants.%C.2]
+// CHECK:STDOUT:   %c_bad.var: ref %C.2 = var c_bad
+// CHECK:STDOUT:   %c_bad: ref %C.2 = bind_name c_bad, %c_bad.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.3;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.4 [template = constants.%F]
+// CHECK:STDOUT:   %.loc17: ref %C.3 = temporary_storage
+// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc17
+// CHECK:STDOUT:   assign file.%c_bad.var, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_bad_value.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %.3: type = struct_type {.a: i32, .b: i32} [template]
+// CHECK:STDOUT:   %S: %.3 = bind_symbolic_name S 0, <unexpected instref inst+19> [symbolic]
+// CHECK:STDOUT:   %.4: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.5: i32 = int_literal 4 [template]
+// CHECK:STDOUT:   %.6: type = ptr_type %.3 [template]
+// CHECK:STDOUT:   %struct.1: %.3 = struct_value (%.4, %.5) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%struct.1) [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.8: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.9: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %struct.2: %.3 = struct_value (%.9, %.8) [template]
+// CHECK:STDOUT:   %C.4: type = class_type @C, (%struct.2) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .a_ref = %import_ref.1
+// CHECK:STDOUT:     .b_ref = %import_ref.2
+// CHECK:STDOUT:     .C = %import_ref.3
+// CHECK:STDOUT:     .F = %import_ref.4
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .c_bad = %c_bad
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir0, inst+16, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir0, inst+59, unloaded
+// CHECK:STDOUT:   %import_ref.3: %C.type = import_ref ir0, inst+103, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %import_ref.4: %F.type = import_ref ir0, inst+123, loaded [template = constants.%F]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir0, inst+106, unloaded
+// CHECK:STDOUT:   %C.ref: %C.type = name_ref C, %import_ref.3 [template = constants.%C.1]
+// CHECK:STDOUT:   %.loc6_20: i32 = int_literal 3 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc6_28: i32 = int_literal 4 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc6_29: %.3 = struct_literal (%.loc6_20, %.loc6_28)
+// CHECK:STDOUT:   %struct: %.3 = struct_value (%.loc6_20, %.loc6_28) [template = constants.%struct.1]
+// CHECK:STDOUT:   %.loc6_13: %.3 = converted %.loc6_29, %struct [template = constants.%struct.1]
+// CHECK:STDOUT:   %C: type = class_type @C, (%.loc6_13) [template = constants.%C.3]
+// CHECK:STDOUT:   %c_bad.var: ref %C.3 = var c_bad
+// CHECK:STDOUT:   %c_bad: ref %C.3 = bind_name c_bad, %c_bad.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.4;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.4 [template = constants.%F]
+// CHECK:STDOUT:   %.loc6: ref %C.4 = temporary_storage
+// CHECK:STDOUT:   %F.call: init %C.4 = call %F.ref() to %.loc6
+// CHECK:STDOUT:   assign file.%c_bad.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 248 - 5
toolchain/check/testdata/tuples/import.carbon

@@ -15,12 +15,46 @@ package Implicit;
 var a_ref: (i32,) = (0,);
 var b_ref: (((i32,), i32), (i32, i32)) = (((0,), 1), (2, 3));
 
+class C(X:! (i32, i32)) {}
+
+fn F() -> C((1, 2));
+
 // --- implicit.impl.carbon
 
 impl package Implicit;
 
 var a: (i32,) = a_ref;
 var b: (((i32,), i32), (i32, i32)) = b_ref;
+var c: C((1, 2)) = F();
+
+// --- fail_bad_type.impl.carbon
+
+impl package Implicit;
+
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+14]]:1: ERROR: Cannot implicitly convert from `C` to `C`.
+// CHECK:STDERR: var c_bad: C((1, 2, 3)) = F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+10]]:14: ERROR: Cannot initialize tuple of 2 element(s) from tuple with 3 element(s).
+// CHECK:STDERR: var c_bad: C((1, 2, 3)) = F();
+// CHECK:STDERR:              ^~~~~~~~~
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE-9]]:6: In import.
+// CHECK:STDERR: impl package Implicit;
+// CHECK:STDERR:      ^~~~~~~
+// CHECK:STDERR: implicit.carbon:7:1: Initializing parameter 1 of function declared here.
+// CHECK:STDERR: class C(X:! (i32, i32)) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var c_bad: C((1, 2, 3)) = F();
+
+// --- fail_bad_value.impl.carbon
+
+impl package Implicit;
+
+// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+3]]:1: ERROR: Cannot implicitly convert from `C` to `C`.
+// CHECK:STDERR: var c_bad: C((3, 4)) = F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+var c_bad: C((3, 4)) = F();
 
 // CHECK:STDOUT: --- implicit.carbon
 // CHECK:STDOUT:
@@ -48,6 +82,15 @@ var b: (((i32,), i32), (i32, i32)) = b_ref;
 // CHECK:STDOUT:   %tuple.2: %.8 = tuple_value (%tuple.1, %.15) [template]
 // CHECK:STDOUT:   %tuple.3: %.9 = tuple_value (%.16, %.17) [template]
 // CHECK:STDOUT:   %tuple.4: %.10 = tuple_value (%tuple.2, %tuple.3) [template]
+// CHECK:STDOUT:   %X: %.9 = bind_symbolic_name X 0 [symbolic]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %.18: type = struct_type {} [template]
+// CHECK:STDOUT:   %tuple.5: %.9 = tuple_value (%.15, %.16) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%tuple.5) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -55,6 +98,8 @@ var b: (((i32,), i32), (i32, i32)) = b_ref;
 // CHECK:STDOUT:     .Core = %Core
 // CHECK:STDOUT:     .a_ref = %a_ref
 // CHECK:STDOUT:     .b_ref = %b_ref
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .F = %F.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
@@ -91,10 +136,41 @@ var b: (((i32,), i32), (i32, i32)) = b_ref;
 // CHECK:STDOUT:   %.loc5_38.13: type = converted %.loc5_38.1, constants.%.10 [template = constants.%.10]
 // CHECK:STDOUT:   %b_ref.var: ref %.10 = var b_ref
 // CHECK:STDOUT:   %b_ref: ref %.10 = bind_name b_ref, %b_ref.var
+// CHECK:STDOUT:   %import_ref.6: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.7: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %C.decl: %C.type = class_decl @C [template = constants.%C.1] {
+// CHECK:STDOUT:     %int.make_type_32.loc7_14: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %int.make_type_32.loc7_19: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc7_22.1: %.6 = tuple_literal (%int.make_type_32.loc7_14, %int.make_type_32.loc7_19)
+// CHECK:STDOUT:     %.loc7_22.2: type = value_of_initializer %int.make_type_32.loc7_14 [template = i32]
+// CHECK:STDOUT:     %.loc7_22.3: type = converted %int.make_type_32.loc7_14, %.loc7_22.2 [template = i32]
+// CHECK:STDOUT:     %.loc7_22.4: type = value_of_initializer %int.make_type_32.loc7_19 [template = i32]
+// CHECK:STDOUT:     %.loc7_22.5: type = converted %int.make_type_32.loc7_19, %.loc7_22.4 [template = i32]
+// CHECK:STDOUT:     %.loc7_22.6: type = converted %.loc7_22.1, constants.%.9 [template = constants.%.9]
+// CHECK:STDOUT:     %X.loc7_9.1: %.9 = param X
+// CHECK:STDOUT:     %X.loc7_9.2: %.9 = bind_symbolic_name X 0, %X.loc7_9.1 [symbolic = constants.%X]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
+// CHECK:STDOUT:     %C.ref: %C.type = name_ref C, %C.decl [template = constants.%C.1]
+// CHECK:STDOUT:     %.loc9_14: i32 = int_literal 1 [template = constants.%.15]
+// CHECK:STDOUT:     %.loc9_17: i32 = int_literal 2 [template = constants.%.16]
+// CHECK:STDOUT:     %.loc9_18: %.9 = tuple_literal (%.loc9_14, %.loc9_17)
+// CHECK:STDOUT:     %tuple: %.9 = tuple_value (%.loc9_14, %.loc9_17) [template = constants.%tuple.5]
+// CHECK:STDOUT:     %.loc9_12: %.9 = converted %.loc9_18, %tuple [template = constants.%tuple.5]
+// CHECK:STDOUT:     %C: type = class_type @C, (%.loc9_12) [template = constants.%C.3]
+// CHECK:STDOUT:     @F.%return: ref %C.3 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.3;
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %.loc4_22: i32 = int_literal 0 [template = constants.%.4]
@@ -150,20 +226,37 @@ var b: (((i32,), i32), (i32, i32)) = b_ref;
 // CHECK:STDOUT:   %.11: type = ptr_type %.7 [template]
 // CHECK:STDOUT:   %.12: type = tuple_type (%.11, %.10) [template]
 // CHECK:STDOUT:   %.13: type = ptr_type %.12 [template]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %.14: type = struct_type {} [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %X: %.8 = bind_symbolic_name X 0, <unexpected instref inst+103> [symbolic]
+// CHECK:STDOUT:   %.15: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.16: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %tuple: %.8 = tuple_value (%.15, %.16) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%tuple) [template]
+// CHECK:STDOUT:   %.17: type = ptr_type %.14 [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .a_ref = %import_ref.1
 // CHECK:STDOUT:     .b_ref = %import_ref.2
+// CHECK:STDOUT:     .C = %import_ref.3
+// CHECK:STDOUT:     .F = %import_ref.4
 // CHECK:STDOUT:     .Core = %Core
 // CHECK:STDOUT:     .a = %a
 // CHECK:STDOUT:     .b = %b
+// CHECK:STDOUT:     .c = %c
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: ref %.3 = import_ref ir0, inst+16, loaded
 // CHECK:STDOUT:   %import_ref.2: ref %.9 = import_ref ir0, inst+60, loaded
+// CHECK:STDOUT:   %import_ref.3: %C.type = import_ref ir0, inst+108, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %import_ref.4: %F.type = import_ref ir0, inst+123, loaded [template = constants.%F]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %import_ref.3: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.5: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc4: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc4_13.1: %.2 = tuple_literal (%int.make_type_32.loc4)
 // CHECK:STDOUT:   %.loc4_13.2: type = value_of_initializer %int.make_type_32.loc4 [template = i32]
@@ -171,15 +264,15 @@ var b: (((i32,), i32), (i32, i32)) = b_ref;
 // CHECK:STDOUT:   %.loc4_13.4: type = converted %.loc4_13.1, constants.%.3 [template = constants.%.3]
 // CHECK:STDOUT:   %a.var: ref %.3 = var a
 // CHECK:STDOUT:   %a: ref %.3 = bind_name a, %a.var
-// CHECK:STDOUT:   %import_ref.4: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.6: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc5_11: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc5_15: %.2 = tuple_literal (%int.make_type_32.loc5_11)
-// CHECK:STDOUT:   %import_ref.5: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.7: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc5_18: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc5_21: %.4 = tuple_literal (%.loc5_15, %int.make_type_32.loc5_18)
-// CHECK:STDOUT:   %import_ref.6: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.8: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc5_25: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %import_ref.7: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.9: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %int.make_type_32.loc5_30: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc5_33: %.5 = tuple_literal (%int.make_type_32.loc5_25, %int.make_type_32.loc5_30)
 // CHECK:STDOUT:   %.loc5_34.1: %.6 = tuple_literal (%.loc5_21, %.loc5_33)
@@ -197,10 +290,27 @@ var b: (((i32,), i32), (i32, i32)) = b_ref;
 // CHECK:STDOUT:   %.loc5_34.13: type = converted %.loc5_34.1, constants.%.9 [template = constants.%.9]
 // CHECK:STDOUT:   %b.var: ref %.9 = var b
 // CHECK:STDOUT:   %b: ref %.9 = bind_name b, %b.var
+// CHECK:STDOUT:   %import_ref.10 = import_ref ir0, inst+111, unloaded
+// CHECK:STDOUT:   %C.ref: %C.type = name_ref C, %import_ref.3 [template = constants.%C.1]
+// CHECK:STDOUT:   %.loc6_11: i32 = int_literal 1 [template = constants.%.15]
+// CHECK:STDOUT:   %.loc6_14: i32 = int_literal 2 [template = constants.%.16]
+// CHECK:STDOUT:   %.loc6_15: %.8 = tuple_literal (%.loc6_11, %.loc6_14)
+// CHECK:STDOUT:   %tuple: %.8 = tuple_value (%.loc6_11, %.loc6_14) [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc6_9: %.8 = converted %.loc6_15, %tuple [template = constants.%tuple]
+// CHECK:STDOUT:   %C: type = class_type @C, (%.loc6_9) [template = constants.%C.3]
+// CHECK:STDOUT:   %c.var: ref %C.3 = var c
+// CHECK:STDOUT:   %c: ref %C.3 = bind_name c, %c.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.10
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.3;
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %a_ref.ref: ref %.3 = name_ref a_ref, file.%import_ref.1
@@ -240,6 +350,139 @@ var b: (((i32,), i32), (i32, i32)) = b_ref;
 // CHECK:STDOUT:   %.loc5_38.28: init %.9 = tuple_init (%.loc5_38.15, %.loc5_38.27) to file.%b.var
 // CHECK:STDOUT:   %.loc5_43: init %.9 = converted %b_ref.ref, %.loc5_38.28
 // CHECK:STDOUT:   assign file.%b.var, %.loc5_43
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.4 [template = constants.%F]
+// CHECK:STDOUT:   %.loc6: ref %C.3 = splice_block file.%c.var {}
+// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc6
+// CHECK:STDOUT:   assign file.%c.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_bad_type.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %.3: type = tuple_type (i32, i32) [template]
+// CHECK:STDOUT:   %X: %.3 = bind_symbolic_name X 0, <unexpected instref inst+15> [symbolic]
+// CHECK:STDOUT:   %.4: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.5: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.7: type = tuple_type (i32, i32, i32) [template]
+// CHECK:STDOUT:   %.8: type = ptr_type %.3 [template]
+// CHECK:STDOUT:   %.9: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %tuple: %.3 = tuple_value (%.4, %.5) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%tuple) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .a_ref = %import_ref.1
+// CHECK:STDOUT:     .b_ref = %import_ref.2
+// CHECK:STDOUT:     .C = %import_ref.3
+// CHECK:STDOUT:     .F = %import_ref.4
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .c_bad = %c_bad
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir0, inst+16, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir0, inst+60, unloaded
+// CHECK:STDOUT:   %import_ref.3: %C.type = import_ref ir0, inst+108, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %import_ref.4: %F.type = import_ref ir0, inst+123, loaded [template = constants.%F]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir0, inst+111, unloaded
+// CHECK:STDOUT:   %C.ref: %C.type = name_ref C, %import_ref.3 [template = constants.%C.1]
+// CHECK:STDOUT:   %.loc18_15: i32 = int_literal 1 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc18_18: i32 = int_literal 2 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc18_21: i32 = int_literal 3 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc18_22: %.7 = tuple_literal (%.loc18_15, %.loc18_18, %.loc18_21)
+// CHECK:STDOUT:   %C: type = class_type @C [template = constants.%C.2]
+// CHECK:STDOUT:   %c_bad.var: ref %C.2 = var c_bad
+// CHECK:STDOUT:   %c_bad: ref %C.2 = bind_name c_bad, %c_bad.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.3;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.4 [template = constants.%F]
+// CHECK:STDOUT:   %.loc18: ref %C.3 = temporary_storage
+// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc18
+// CHECK:STDOUT:   assign file.%c_bad.var, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_bad_value.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C [template]
+// CHECK:STDOUT:   %.3: type = tuple_type (i32, i32) [template]
+// CHECK:STDOUT:   %X: %.3 = bind_symbolic_name X 0, <unexpected instref inst+15> [symbolic]
+// CHECK:STDOUT:   %.4: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.5: i32 = int_literal 4 [template]
+// CHECK:STDOUT:   %.6: type = ptr_type %.3 [template]
+// CHECK:STDOUT:   %tuple.1: %.3 = tuple_value (%.4, %.5) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, (%tuple.1) [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.8: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.9: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %tuple.2: %.3 = tuple_value (%.9, %.8) [template]
+// CHECK:STDOUT:   %C.4: type = class_type @C, (%tuple.2) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .a_ref = %import_ref.1
+// CHECK:STDOUT:     .b_ref = %import_ref.2
+// CHECK:STDOUT:     .C = %import_ref.3
+// CHECK:STDOUT:     .F = %import_ref.4
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .c_bad = %c_bad
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir0, inst+16, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir0, inst+60, unloaded
+// CHECK:STDOUT:   %import_ref.3: %C.type = import_ref ir0, inst+108, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %import_ref.4: %F.type = import_ref ir0, inst+123, loaded [template = constants.%F]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir0, inst+111, unloaded
+// CHECK:STDOUT:   %C.ref: %C.type = name_ref C, %import_ref.3 [template = constants.%C.1]
+// CHECK:STDOUT:   %.loc7_15: i32 = int_literal 3 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc7_18: i32 = int_literal 4 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc7_19: %.3 = tuple_literal (%.loc7_15, %.loc7_18)
+// CHECK:STDOUT:   %tuple: %.3 = tuple_value (%.loc7_15, %.loc7_18) [template = constants.%tuple.1]
+// CHECK:STDOUT:   %.loc7_13: %.3 = converted %.loc7_19, %tuple [template = constants.%tuple.1]
+// CHECK:STDOUT:   %C: type = class_type @C, (%.loc7_13) [template = constants.%C.3]
+// CHECK:STDOUT:   %c_bad.var: ref %C.3 = var c_bad
+// CHECK:STDOUT:   %c_bad: ref %C.3 = bind_name c_bad, %c_bad.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %C.4;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.4 [template = constants.%F]
+// CHECK:STDOUT:   %.loc7: ref %C.4 = temporary_storage
+// CHECK:STDOUT:   %F.call: init %C.4 = call %F.ref() to %.loc7
+// CHECK:STDOUT:   assign file.%c_bad.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 1
toolchain/parse/node_kind.def

@@ -356,4 +356,3 @@ CARBON_PARSE_NODE_KIND(MatchDefault)
 #undef CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR
 #undef CARBON_PARSE_NODE_KIND_TOKEN_LITERAL
 #undef CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER
-#undef CARBON_IF_VALID

+ 6 - 0
toolchain/sem_ir/ids.h

@@ -141,6 +141,12 @@ struct ConstantId : public IdBase, public Printable<ConstantId> {
     return InstId(Abs(index) - IndexOffset);
   }
 
+  // Returns the instruction that describes this constant value, or
+  // InstId::Invalid if this is invalid or a runtime value.
+  constexpr auto inst_id_if_valid() const -> InstId {
+    return is_valid() ? inst_id() : InstId::Invalid;
+  }
+
   auto Print(llvm::raw_ostream& out) const -> void {
     if (!is_valid()) {
       IdBase::Print(out);