Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the correct type for Self in generic classes and generic interfaces #4087

Merged
merged 7 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading