Skip to content

Commit

Permalink
Use the correct type for Self in generic classes and generic interf…
Browse files Browse the repository at this point in the history
…aces (#4087)

In a `class C(T:! type)`, the type `Self` should be `C(T)`, not merely
`C`. Similarly, in an `interface I(T:! type)`, the type of self should
be `I(T)`, not merely `I`.
  • Loading branch information
zygoloid authored Jun 28, 2024
1 parent 19c5596 commit 10a198a
Show file tree
Hide file tree
Showing 24 changed files with 395 additions and 224 deletions.
9 changes: 1 addition & 8 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "toolchain/base/kind_switch.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/check/generic.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/sem_ir/builtin_function_kind.h"
#include "toolchain/sem_ir/function.h"
Expand All @@ -15,14 +16,6 @@

namespace Carbon::Check {

static auto MakeGenericInstance(Context& context, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id)
-> SemIR::GenericInstanceId {
auto instance_id = context.generic_instances().GetOrAdd(generic_id, args_id);
// TODO: Perform substitution into the generic declaration if needed.
return instance_id;
}

namespace {
// The evaluation phase for an expression, computed by evaluation. These are
// ordered so that the phase of an expression is the numerically highest phase
Expand Down
33 changes: 33 additions & 0 deletions toolchain/check/generic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,37 @@ auto FinishGenericDefinition(Context& /*context*/,
// TODO: Track contents of this generic definition.
}

auto MakeGenericInstance(Context& context, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id)
-> SemIR::GenericInstanceId {
auto instance_id = context.generic_instances().GetOrAdd(generic_id, args_id);
// TODO: Perform substitution into the generic declaration if needed.
return instance_id;
}

auto MakeGenericSelfInstance(Context& context, SemIR::GenericId generic_id)
-> SemIR::GenericInstanceId {
// TODO: Remove this once we import generics properly.
if (!generic_id.is_valid()) {
return SemIR::GenericInstanceId::Invalid;
}

auto& generic = context.generics().Get(generic_id);
auto args = context.inst_blocks().Get(generic.bindings_id);

// Form a canonical argument list for the generic.
llvm::SmallVector<SemIR::InstId> arg_ids;
arg_ids.reserve(args.size());
for (auto arg_id : args) {
arg_ids.push_back(context.constant_values().GetConstantInstId(arg_id));
}
auto args_id = context.inst_blocks().AddCanonical(arg_ids);

// Build a corresponding instance.
// TODO: This could be made more efficient. We don't need to perform
// substitution here; we know we want identity mappings for all constants and
// types. We could also consider not storing the mapping at all in this case.
return MakeGenericInstance(context, generic_id, args_id);
}

} // namespace Carbon::Check
14 changes: 14 additions & 0 deletions toolchain/check/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ auto FinishGenericRedecl(Context& context, SemIR::InstId decl_id,
auto FinishGenericDefinition(Context& context, SemIR::GenericId generic_id)
-> void;

// Builds a new generic instance, or finds an existing one if this instance of
// this generic has already been referenced. Performs substitution into the
// declaration, but not the definition, of the generic.
//
// `args_id` should be a canonical instruction block referring to constants.
auto MakeGenericInstance(Context& context, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id)
-> SemIR::GenericInstanceId;

// Builds the generic instance corresponding to the generic itself. For example,
// for a generic `G(T:! type)`, this is `G(T)`.
auto MakeGenericSelfInstance(Context& context, SemIR::GenericId generic_id)
-> SemIR::GenericInstanceId;

} // namespace Carbon::Check

#endif // CARBON_TOOLCHAIN_CHECK_GENERIC_H_
4 changes: 2 additions & 2 deletions toolchain/check/handle_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,8 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
// declaration.
auto& class_info = context.classes().Get(class_decl.class_id);
if (class_info.is_generic()) {
// TODO: Build generic arguments representing the parameters.
auto instance_id = SemIR::GenericInstanceId::Invalid;
auto instance_id =
MakeGenericSelfInstance(context, class_info.generic_id);
class_info.self_type_id = context.GetTypeIdForTypeConstant(
TryEvalInst(context, SemIR::InstId::Invalid,
SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
Expand Down
18 changes: 13 additions & 5 deletions toolchain/check/handle_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "toolchain/check/context.h"
#include "toolchain/check/eval.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/handle.h"
#include "toolchain/check/interface.h"
#include "toolchain/check/merge.h"
#include "toolchain/check/modifiers.h"
#include "toolchain/check/name_component.h"
Expand Down Expand Up @@ -152,10 +152,18 @@ auto HandleInterfaceDefinitionStart(Context& context,

// Declare and introduce `Self`.
if (!interface_info.is_defined()) {
// TODO: Once we support parameterized interfaces, this won't be the right
// type. For `interface X(T:! type)`, the type of `Self` is `X(T)`, whereas
// this will be simply `X`.
auto self_type_id = context.GetTypeIdForTypeInst(interface_decl_id);
SemIR::TypeId self_type_id = SemIR::TypeId::Invalid;
if (interface_info.is_generic()) {
auto instance_id =
MakeGenericSelfInstance(context, interface_info.generic_id);
self_type_id = context.GetTypeIdForTypeConstant(
TryEvalInst(context, SemIR::InstId::Invalid,
SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType,
.interface_id = interface_id,
.instance_id = instance_id}));
} else {
self_type_id = context.GetTypeIdForTypeInst(interface_decl_id);
}

// We model `Self` as a symbolic binding whose type is the interface.
// Because there is no equivalent non-symbolic value, we use `Invalid` as
Expand Down
21 changes: 21 additions & 0 deletions toolchain/check/subst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "toolchain/check/subst.h"

#include "toolchain/check/eval.h"
#include "toolchain/check/generic.h"
#include "toolchain/sem_ir/copy_on_write_block.h"
#include "toolchain/sem_ir/ids.h"

Expand Down Expand Up @@ -77,6 +78,13 @@ static auto PushOperand(Context& context, Worklist& worklist,
worklist.Push(context.types().GetInstId(type_id));
}
break;
case SemIR::IdKind::For<SemIR::GenericInstanceId>:
if (auto instance_id = static_cast<SemIR::GenericInstanceId>(arg);
instance_id.is_valid()) {
PushOperand(context, worklist, SemIR::IdKind::For<SemIR::InstBlockId>,
context.generic_instances().Get(instance_id).args_id.index);
}
break;
default:
break;
}
Expand Down Expand Up @@ -128,6 +136,19 @@ static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind,
}
return new_type_block.GetCanonical().index;
}
case SemIR::IdKind::For<SemIR::GenericInstanceId>: {
auto instance_id = static_cast<SemIR::GenericInstanceId>(arg);
if (!instance_id.is_valid()) {
return arg;
}
auto& instance = context.generic_instances().Get(instance_id);
auto args_id =
PopOperand(context, worklist, SemIR::IdKind::For<SemIR::InstBlockId>,
instance.args_id.index);
return MakeGenericInstance(context, instance.generic_id,
static_cast<SemIR::InstBlockId>(args_id))
.index;
}
default:
return arg;
}
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/testdata/class/fail_generic_method.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn Class(N:! i32).F[self: Self](n: T) {}
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic]
// CHECK:STDOUT: %.2: type = unbound_element_type %Class.2, %T [symbolic]
// CHECK:STDOUT: %F.type: type = fn_type @F [template]
// CHECK:STDOUT: %F: %F.type = struct_value () [template]
Expand Down Expand Up @@ -81,7 +81,7 @@ fn Class(N:! i32).F[self: Self](n: T) {}
// CHECK:STDOUT: %T.ref.loc12: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
// CHECK:STDOUT: %.loc12: %.2 = field_decl a, element0 [template]
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [template = constants.%Class.2]
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = constants.%Class.2]
// CHECK:STDOUT: %self.loc13_8.1: %Class.2 = param self
// CHECK:STDOUT: %self.loc13_8.2: %Class.2 = bind_name self, %self.loc13_8.1
// CHECK:STDOUT: %T.ref.loc13: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
Expand Down
10 changes: 5 additions & 5 deletions toolchain/check/testdata/class/generic/basic.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class Class(T:! type) {
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %.2: type = ptr_type %Class.2 [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic]
// CHECK:STDOUT: %.2: type = ptr_type %Class.2 [symbolic]
// CHECK:STDOUT: %.3: type = ptr_type %T [symbolic]
// CHECK:STDOUT: %GetAddr.type: type = fn_type @GetAddr [template]
// CHECK:STDOUT: %GetAddr: %GetAddr.type = struct_value () [template]
Expand All @@ -56,8 +56,8 @@ class Class(T:! type) {
// CHECK:STDOUT: class @Class
// CHECK:STDOUT: generic [file.%T.loc11_13.2: type] {
// CHECK:STDOUT: %GetAddr.decl: %GetAddr.type = fn_decl @GetAddr [template = constants.%GetAddr] {
// CHECK:STDOUT: %Self.ref.loc12: type = name_ref Self, constants.%Class.2 [template = constants.%Class.2]
// CHECK:STDOUT: %.loc12_29: type = ptr_type %Class.2 [template = constants.%.2]
// CHECK:STDOUT: %Self.ref.loc12: type = name_ref Self, constants.%Class.2 [symbolic = constants.%Class.2]
// CHECK:STDOUT: %.loc12_29: type = ptr_type %Class.2 [symbolic = constants.%.2]
// CHECK:STDOUT: %self.loc12_19.1: %.2 = param self
// CHECK:STDOUT: %self.loc12_19.3: %.2 = bind_name self, %self.loc12_19.1
// CHECK:STDOUT: %.loc12_14: %.2 = addr_pattern %self.loc12_19.3
Expand All @@ -66,7 +66,7 @@ class Class(T:! type) {
// CHECK:STDOUT: %return.var.loc12: ref %.3 = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %GetValue.decl: %GetValue.type = fn_decl @GetValue [template = constants.%GetValue] {
// CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, constants.%Class.2 [template = constants.%Class.2]
// CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, constants.%Class.2 [symbolic = constants.%Class.2]
// CHECK:STDOUT: %self.loc17_15.1: %Class.2 = param self
// CHECK:STDOUT: %self.loc17_15.2: %Class.2 = bind_name self, %self.loc17_15.1
// CHECK:STDOUT: %T.ref.loc17: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
Expand Down
8 changes: 4 additions & 4 deletions toolchain/check/testdata/class/generic/call.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ var a: Class(5, i32*);
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T, %N) [symbolic]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
// CHECK:STDOUT: %.4: i32 = int_literal 5 [template]
Expand Down Expand Up @@ -142,7 +142,7 @@ var a: Class(5, i32*);
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T, %N) [symbolic]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
// CHECK:STDOUT: }
Expand Down Expand Up @@ -195,7 +195,7 @@ var a: Class(5, i32*);
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T, %N) [symbolic]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
// CHECK:STDOUT: %.4: i32 = int_literal 1 [template]
Expand Down Expand Up @@ -252,7 +252,7 @@ var a: Class(5, i32*);
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T, %N) [symbolic]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: i32 = int_literal 5 [template]
// CHECK:STDOUT: %.4: type = ptr_type i32 [template]
Expand Down
8 changes: 4 additions & 4 deletions toolchain/check/testdata/class/generic/fail_todo_use.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ fn Run() -> i32 {
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %.2: type = ptr_type %Class.2 [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic]
// CHECK:STDOUT: %.2: type = ptr_type %Class.2 [symbolic]
// CHECK:STDOUT: %.3: type = ptr_type %T [symbolic]
// CHECK:STDOUT: %Get.type: type = fn_type @Get [template]
// CHECK:STDOUT: %Get: %Get.type = struct_value () [template]
Expand Down Expand Up @@ -85,8 +85,8 @@ fn Run() -> i32 {
// CHECK:STDOUT: class @Class
// CHECK:STDOUT: generic [file.%T.loc11_13.2: type] {
// CHECK:STDOUT: %Get.decl: %Get.type = fn_decl @Get [template = constants.%Get] {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [template = constants.%Class.2]
// CHECK:STDOUT: %.loc12_25: type = ptr_type %Class.2 [template = constants.%.2]
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = constants.%Class.2]
// CHECK:STDOUT: %.loc12_25: type = ptr_type %Class.2 [symbolic = constants.%.2]
// CHECK:STDOUT: %self.loc12_15.1: %.2 = param self
// CHECK:STDOUT: %self.loc12_15.3: %.2 = bind_name self, %self.loc12_15.1
// CHECK:STDOUT: %.loc12_10: %.2 = addr_pattern %self.loc12_15.3
Expand Down
22 changes: 11 additions & 11 deletions toolchain/check/testdata/class/generic/import.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ class Class(U:! type) {
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic]
// CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [template]
// CHECK:STDOUT: %CompleteClass.1: %CompleteClass.type = struct_value () [template]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass [template]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass, (%T) [symbolic]
// CHECK:STDOUT: %Int32.type: type = fn_type @Int32 [template]
// CHECK:STDOUT: %Int32: %Int32.type = struct_value () [template]
// CHECK:STDOUT: %.2: type = unbound_element_type %CompleteClass.2, i32 [template]
// CHECK:STDOUT: %.2: type = unbound_element_type %CompleteClass.2, i32 [symbolic]
// CHECK:STDOUT: %F.type.1: type = fn_type @F.1 [template]
// CHECK:STDOUT: %F.1: %F.type.1 = struct_value () [template]
// CHECK:STDOUT: %.3: type = struct_type {.n: i32} [template]
Expand Down Expand Up @@ -182,13 +182,13 @@ class Class(U:! type) {
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic]
// CHECK:STDOUT: %.2: type = unbound_element_type %Class.2, %T [symbolic]
// CHECK:STDOUT: %.3: type = struct_type {.x: %T} [symbolic]
// CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [template]
// CHECK:STDOUT: %CompleteClass.1: %CompleteClass.type = struct_value () [template]
// CHECK:STDOUT: %.4: type = struct_type {.n: i32} [template]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass [template]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass, (%T) [symbolic]
// CHECK:STDOUT: %Int32.type: type = fn_type @Int32 [template]
// CHECK:STDOUT: %Int32: %Int32.type = struct_value () [template]
// CHECK:STDOUT: %CompleteClass.3: type = class_type @CompleteClass, (i32) [template]
Expand Down Expand Up @@ -270,8 +270,8 @@ class Class(U:! type) {
// CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [template]
// CHECK:STDOUT: %CompleteClass.1: %CompleteClass.type = struct_value () [template]
// CHECK:STDOUT: %.2: type = struct_type {.n: i32} [template]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass [template]
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0, <unexpected instref inst+26> [symbolic]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass, (%T) [symbolic]
// CHECK:STDOUT: %CompleteClass.3: type = class_type @CompleteClass, (i32) [template]
// CHECK:STDOUT: %.3: type = ptr_type %.2 [template]
// CHECK:STDOUT: %F.type.1: type = fn_type @F.1 [template]
Expand Down Expand Up @@ -352,13 +352,13 @@ class Class(U:! type) {
// CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [template]
// CHECK:STDOUT: %CompleteClass.1: %CompleteClass.type = struct_value () [template]
// CHECK:STDOUT: %.2: type = struct_type {.n: i32} [template]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass [template]
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0, <unexpected instref inst+26> [symbolic]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass, (%T) [symbolic]
// CHECK:STDOUT: %CompleteClass.3: type = class_type @CompleteClass, (i32) [template]
// CHECK:STDOUT: %.3: type = ptr_type %.2 [template]
// CHECK:STDOUT: %F.type: type = fn_type @F [template]
// CHECK:STDOUT: %F: %F.type = struct_value () [template]
// CHECK:STDOUT: %.4: type = unbound_element_type %CompleteClass.2, i32 [template]
// CHECK:STDOUT: %.4: type = unbound_element_type %CompleteClass.2, i32 [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down Expand Up @@ -427,8 +427,8 @@ class Class(U:! type) {
// CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [template]
// CHECK:STDOUT: %CompleteClass.1: %CompleteClass.type = struct_value () [template]
// CHECK:STDOUT: %.2: type = struct_type {.n: i32} [template]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass [template]
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0, <unexpected instref inst+17> [symbolic]
// CHECK:STDOUT: %CompleteClass.2: type = class_type @CompleteClass, (%T) [symbolic]
// CHECK:STDOUT: %Int32.type: type = fn_type @Int32 [template]
// CHECK:STDOUT: %Int32: %Int32.type = struct_value () [template]
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
Expand Down Expand Up @@ -495,11 +495,11 @@ class Class(U:! type) {
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0, <unexpected instref inst+14> [symbolic]
// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic]
// CHECK:STDOUT: %.type: type = generic_class_type @.1 [template]
// CHECK:STDOUT: %.2: %.type = struct_value () [template]
// CHECK:STDOUT: %.3: type = class_type @.1 [template]
// CHECK:STDOUT: %.3: type = class_type @.1, (%U) [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down
Loading

0 comments on commit 10a198a

Please sign in to comment.