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

WIP SemIR vtables #4732

Draft
wants to merge 1 commit into
base: trunk
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions toolchain/check/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Context::Context(DiagnosticEmitter* emitter,
args_type_info_stack_("args_type_info_stack_", *sem_ir, vlog_stream),
decl_name_stack_(this),
scope_stack_(sem_ir_->identifiers()),
vtable_stack_("vtable_stack_", *sem_ir, vlog_stream),
global_init_(this) {
// Prepare fields which relate to the number of IRs available for import.
import_irs().Reserve(imported_ir_count);
Expand Down
4 changes: 4 additions & 0 deletions toolchain/check/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ class Context {
return generic_region_stack_;
}

auto vtable_stack() -> InstBlockStack& { return vtable_stack_; }

auto import_ir_constant_values()
-> llvm::SmallVector<SemIR::ConstantValueStore, 0>& {
return import_ir_constant_values_;
Expand Down Expand Up @@ -700,6 +702,8 @@ class Context {
// The stack of generic regions we are currently within.
GenericRegionStack generic_region_stack_;

InstBlockStack vtable_stack_;

// Cache of reverse mapping from type constants to types.
//
// TODO: Instead of mapping to a dense `TypeId` space, we could make `TypeId`
Expand Down
3 changes: 3 additions & 0 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,9 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
case SemIR::TupleInit::Kind:
return RebuildInitAsValue(eval_context, inst, SemIR::TupleValue::Kind);

case SemIR::Vtable::Kind:
return RebuildIfFieldsAreConstant(eval_context, inst,
&SemIR::Vtable::virtual_functions_id);
case SemIR::AutoType::Kind:
case SemIR::BoolType::Kind:
case SemIR::BoundMethodType::Kind:
Expand Down
47 changes: 45 additions & 2 deletions toolchain/check/handle_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionStartId node_id)
context.inst_block_stack().Push();
context.node_stack().Push(node_id, class_id);
context.field_decls_stack().PushArray();
context.vtable_stack().Push();

// TODO: Handle the case where there's control flow in the class body. For
// example:
Expand Down Expand Up @@ -668,11 +669,12 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
bool defining_vptr = class_info.is_dynamic;
auto base_type_id =
class_info.GetBaseType(context.sem_ir(), SemIR::SpecificId::Invalid);
SemIR::Class* base_class_info = nullptr;
if (base_type_id.is_valid()) {
// TODO: If the base class is template dependent, we will need to decide
// whether to add a vptr as part of instantiation.
if (auto* base_class_info = TryGetAsClass(context, base_type_id);
base_class_info && base_class_info->is_dynamic) {
base_class_info = TryGetAsClass(context, base_type_id);
if (base_class_info && base_class_info->is_dynamic) {
defining_vptr = false;
}
}
Expand All @@ -696,6 +698,46 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
{.name_id = SemIR::NameId::Base, .type_id = base_type_id});
}

if (class_info.is_dynamic) {
llvm::SmallVector<SemIR::InstId> vtable;
if (!defining_vptr) {
auto base_vtable_inst_block = context.inst_blocks().Get(
context.insts()
.GetAs<SemIR::Vtable>(base_class_info->vtable_id)
.virtual_functions_id);
for (auto fn_decl_id : base_vtable_inst_block) {
/*
auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(fn_decl_id);
auto fn = context.functions().Get(fn_decl.function_id);
for (auto override_fn_decl_id :
context.vtable_stack().PeekCurrentBlockContents()) { auto
override_fn_decl =
context.insts().GetAs<SemIR::FunctionDecl>(override_fn_decl_id); auto
override_fn = context.functions().Get(override_fn_decl.function_id); if
(override_fn.virtual_modifier ==
SemIR::FunctionFields::VirtualModifier::Impl &&
override_fn.name_id == fn.name_id) {
fn_decl_id = override_fn_decl_id;
}
}
*/
vtable.push_back(fn_decl_id);
}
}

for (auto inst_id : context.vtable_stack().PeekCurrentBlockContents()) {
auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(inst_id);
auto fn = context.functions().Get(fn_decl.function_id);
if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Impl) {
vtable.push_back(inst_id);
}
}
class_info.vtable_id = context.AddInst<SemIR::Vtable>(
node_id, {.type_id = context.GetSingletonType(
SemIR::VtableType::SingletonInstId),
.virtual_functions_id = context.inst_blocks().Add(vtable)});
}

return context.AddInst<SemIR::CompleteTypeWitness>(
node_id,
{.type_id = context.GetSingletonType(SemIR::WitnessType::SingletonInstId),
Expand All @@ -716,6 +758,7 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id)

context.inst_block_stack().Pop();
context.field_decls_stack().PopArray();
context.vtable_stack().Pop();

FinishGenericDefinition(context, class_info.generic_id);

Expand Down
10 changes: 7 additions & 3 deletions toolchain/check/handle_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,18 +214,19 @@ static auto BuildFunctionDecl(Context& context,
.Case(KeywordModifierSet::Impl,
SemIR::Function::VirtualModifier::Impl)
.Default(SemIR::Function::VirtualModifier::None);
SemIR::Class* class_info = nullptr;
if (virtual_modifier != SemIR::Function::VirtualModifier::None &&
parent_scope_inst) {
if (auto class_decl = parent_scope_inst->TryAs<SemIR::ClassDecl>()) {
auto& class_info = context.classes().Get(class_decl->class_id);
class_info = &context.classes().Get(class_decl->class_id);
if (virtual_modifier == SemIR::Function::VirtualModifier::Impl &&
!class_info.base_id.is_valid()) {
!class_info->base_id.is_valid()) {
CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class");
context.emitter().Build(node_id, ImplWithoutBase).Emit();
}
// TODO: If this is an `impl` function, check there's a matching base
// function that's impl or virtual.
class_info.is_dynamic = true;
class_info->is_dynamic = true;
}
}
if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Interface)) {
Expand All @@ -252,6 +253,9 @@ static auto BuildFunctionDecl(Context& context,
if (is_definition) {
function_info.definition_id = decl_id;
}
if (class_info && class_info->is_dynamic) {
context.vtable_stack().AddInstId(decl_id);
}

TryMergeRedecl(context, node_id, name_context.prev_inst_id(), function_decl,
function_info, is_definition);
Expand Down
36 changes: 33 additions & 3 deletions toolchain/check/import_ref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,26 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
return ResolveResult::Done(resolver.local_constant_values().Get(inst_id));
}

static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Vtable inst,
SemIR::InstId import_inst_id) -> ResolveResult {
auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
auto virtual_functions =
GetLocalInstBlockContents(resolver, inst.virtual_functions_id);
if (resolver.HasNewWork()) {
return ResolveResult::Retry();
}

auto virtual_functions_id = GetLocalCanonicalInstBlockId(
resolver, inst.virtual_functions_id, virtual_functions);
auto inst_id = resolver.local_context().AddInstInNoBlock(
resolver.local_context().MakeImportedLocAndInst<SemIR::Vtable>(
AddImportIRInst(resolver, import_inst_id),
{.type_id =
resolver.local_context().GetTypeIdForTypeConstant(type_const_id),
.virtual_functions_id = virtual_functions_id}));
return ResolveResult::Done(resolver.local_constant_values().Get(inst_id));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inst_id comes from AddInstInNoBlock so looking it up in local_constant_values() looks wrong to me. Should it be local_insts()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried something like this, but didn't have any luck changing the small test case outcome (& autoupdate still crashes, which may or may not be related/the same crash - mostly ignoring that until I can get a small import-and-derive test case to work)

diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp
index 0a89777bf..b23c7ce81 100644
--- a/toolchain/check/import_ref.cpp
+++ b/toolchain/check/import_ref.cpp
@@ -1393,7 +1393,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 }

 static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Vtable inst,
-                                SemIR::InstId import_inst_id) -> ResolveResult {
+                                SemIR::InstId /*import_inst_id*/)
+    -> ResolveResult {
   auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
   auto virtual_functions =
       GetLocalInstBlockContents(resolver, inst.virtual_functions_id);
@@ -1403,13 +1404,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Vtable inst,

   auto virtual_functions_id = GetLocalCanonicalInstBlockId(
       resolver, inst.virtual_functions_id, virtual_functions);
-  auto inst_id = resolver.local_context().AddInstInNoBlock(
-      resolver.local_context().MakeImportedLocAndInst<SemIR::Vtable>(
-          AddImportIRInst(resolver, import_inst_id),
-          {.type_id =
-               resolver.local_context().GetTypeIdForTypeConstant(type_const_id),
-           .virtual_functions_id = virtual_functions_id}));
-  return ResolveResult::Done(resolver.local_constant_values().Get(inst_id));
+  return ResolveAs<SemIR::Vtable>(
+      resolver, {.type_id = resolver.local_context().GetTypeIdForTypeConstant(
+                     type_const_id),
+                 .virtual_functions_id = virtual_functions_id});
 }

but, yeah, throwing things in the dark here a bit :/

Comment on lines +1406 to +1412
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally only add a new instruction when importing declaration-like things where the declaration and its constant value might be separate, it might be found by name lookup, it might be redeclared, that sort of thing. If you only want to import a constant value for the vtable (which I think is what we want here), you can directly produce a constant value instead of adding an instruction and then getting its constant value:

Suggested change
auto inst_id = resolver.local_context().AddInstInNoBlock(
resolver.local_context().MakeImportedLocAndInst<SemIR::Vtable>(
AddImportIRInst(resolver, import_inst_id),
{.type_id =
resolver.local_context().GetTypeIdForTypeConstant(type_const_id),
.virtual_functions_id = virtual_functions_id}));
return ResolveResult::Done(resolver.local_constant_values().Get(inst_id));
return ResolveAs<SemIR::Vtable>(
resolver,
{.type_id =
resolver.local_context().GetTypeIdForTypeConstant(type_const_id),
.virtual_functions_id = virtual_functions_id}));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right - I guess I tried something like this to start, encountered problems, then went down the rabbit hole of ResolveResult, etc, trying to find something that works.

So stepping back (based on the suggested edit you provided) to this simpler implementation, I should describe the problem I encounter (oh, right, I did describe it over here: #4671 (reply in thread) - sorry about the bifurcated discussion, repeating that here):

package Modifiers;
base class B1 { virtual fn H(); }
import Modifiers;
class D1 { extend base: Modifiers.B1; }
base class B2 { virtual fn H(); }
class D2 { extend base: B2; }

where D2 has the correct vtable:

%H.decl: %H.type.2 = fn_decl @H.2 [template = constants.%H.2] {} {}
 ...
%.loc4_29: <vtable> = vtable (@B2.%H.decl) [template = constants.%.2]

but D1 has this weird vtable:

 %H.1: %H.type.1 = struct_value () [template]
 ...
 %.loc2_39: <vtable> = vtable (constants.%H.1) [template = constants.%.1]

Which then lead to a CHECK-fail in the commented out vtable initialization code in this PR, here:

       auto base_vtable_inst_block = context.inst_blocks().Get(
           context.insts()
               .GetAs<SemIR::Vtable>(base_class_info->vtable_id)
               .virtual_functions_id);
       for (auto fn_decl_id : base_vtable_inst_block) {
───────> auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(fn_decl_id);
CHECK failure at ./toolchain/sem_ir/inst.h:182: Is<TypedInst>(): Casting inst {kind: StructValue, arg0: inst_block_empty, type: type(inst22)} to wrong kind FunctionDecl

}

static auto TryResolveTypedInst(ImportRefResolver& resolver,
SemIR::BindAlias inst) -> ResolveResult {
auto value_id = GetLocalConstantId(resolver, inst.value_id);
Expand Down Expand Up @@ -1513,8 +1533,8 @@ static auto AddClassDefinition(ImportContext& context,
const SemIR::Class& import_class,
SemIR::Class& new_class,
SemIR::InstId complete_type_witness_id,
SemIR::InstId base_id, SemIR::InstId adapt_id)
-> void {
SemIR::InstId base_id, SemIR::InstId adapt_id,
SemIR::InstId vtable_id) -> void {
new_class.definition_id = new_class.first_owning_decl_id;

new_class.complete_type_witness_id = complete_type_witness_id;
Expand All @@ -1537,6 +1557,9 @@ static auto AddClassDefinition(ImportContext& context,
if (import_class.adapt_id.is_valid()) {
new_class.adapt_id = adapt_id;
}
if (import_class.vtable_id.is_valid()) {
new_class.vtable_id = vtable_id;
}
}

static auto TryResolveTypedInst(ImportRefResolver& resolver,
Expand Down Expand Up @@ -1602,6 +1625,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
auto adapt_id = import_class.adapt_id.is_valid()
? GetLocalConstantInstId(resolver, import_class.adapt_id)
: SemIR::InstId::Invalid;
auto vtable_id =
import_class.vtable_id.is_valid()
? GetLocalConstantInstId(resolver, import_class.vtable_id)
: SemIR::InstId::Invalid;

if (resolver.HasNewWork()) {
return ResolveResult::Retry(class_const_id);
Expand All @@ -1625,7 +1652,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
SemIR::WitnessType::SingletonInstId),
import_class.complete_type_witness_id, complete_type_witness_const_id);
AddClassDefinition(resolver, import_class, new_class,
complete_type_witness_id, base_id, adapt_id);
complete_type_witness_id, base_id, adapt_id, vtable_id);
}

return ResolveResult::Done(class_const_id);
Expand Down Expand Up @@ -2674,6 +2701,9 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
case CARBON_KIND(SemIR::UnboundElementType inst): {
return TryResolveTypedInst(resolver, inst);
}
case CARBON_KIND(SemIR::Vtable inst): {
return TryResolveTypedInst(resolver, inst, inst_id);
}
default: {
// This instruction might have a constant value of a different kind.
auto constant_inst_id =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ let d: c = {};
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
Expand Down
5 changes: 5 additions & 0 deletions toolchain/check/testdata/alias/no_prelude/export_name.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ var d: D* = &c;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- export.carbon
Expand Down Expand Up @@ -122,6 +123,7 @@ var d: D* = &c;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.4
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.3
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- export_orig.carbon
Expand Down Expand Up @@ -152,6 +154,7 @@ var d: D* = &c;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.4
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.3
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_export.carbon
Expand Down Expand Up @@ -184,6 +187,7 @@ var d: D* = &c;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.3
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.2
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
Expand Down Expand Up @@ -261,6 +265,7 @@ var d: D* = &c;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.4
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.3
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ let c_var: c = d;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @D {
Expand All @@ -61,6 +62,7 @@ let c_var: c = d;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%D
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@ extern alias C = Class;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Class
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ alias b = C;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
Expand Down
3 changes: 3 additions & 0 deletions toolchain/check/testdata/alias/no_prelude/import.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- class2.carbon
Expand Down Expand Up @@ -136,6 +137,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.5
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.4
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- class3.carbon
Expand Down Expand Up @@ -171,6 +173,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.4
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.3
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- var1.carbon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ var inst: Test.A = {};
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- def.impl.carbon
Expand Down Expand Up @@ -111,6 +112,7 @@ var inst: Test.A = {};
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.4
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.3
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
Expand Down
2 changes: 2 additions & 0 deletions toolchain/check/testdata/alias/no_prelude/import_order.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ var a_val: a = {.v = b_val.v};
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: .v = %.loc4_16
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- b.carbon
Expand Down Expand Up @@ -126,6 +127,7 @@ var a_val: a = {.v = b_val.v};
// CHECK:STDOUT: .Self = imports.%import_ref.7
// CHECK:STDOUT: .v = imports.%import_ref.8
// CHECK:STDOUT: complete_type_witness = imports.%import_ref.6
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ fn F() -> NS.a {
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: .v = %.loc11_16
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_id = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> %return.param_patt: %C {
Expand Down
Loading
Loading