Support import of typedefs. (#5787)

Unify code paths for importing classes by name and importing them
indirectly when their type is referenced. Switch to using the general
type import machinery to import all type declarations, which allows
typedef declarations naming importable types to be used too.

Fix up handling of error cases to consistently only produce an Error
InstId after actually producing an error message, so that we can produce
exactly one diagnostic in failure cases.

Remove TODO error for unions that previously was only produced when
importing them indirectly, not when importing them by name. Import of
unions is exactly as complete / incomplete as import of other class
types, so treating them differently doesn't seem necessary.
This commit is contained in:
Richard Smith
2025-07-10 20:46:46 +00:00
committed by GitHub
parent 6ca4e2e089
commit 26e23eac10
10 changed files with 848 additions and 570 deletions
+74 -97
View File
@@ -53,14 +53,7 @@ fn F() {
//@dump-sem-ir-begin
var s: Cpp.S = {};
var p: Cpp.S* = &s;
// CHECK:STDERR: fail_todo_import_double_pointer_param.carbon:[[@LINE+14]]:3: error: semantics TODO: `Unsupported: pointer to pointer type: S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(&p);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_double_pointer_param.carbon:[[@LINE+11]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: Cpp.foo(&p);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_import_double_pointer_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S * _Nonnull * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_double_pointer_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: pointer to pointer type: S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(&p);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_double_pointer_param.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -92,13 +85,12 @@ fn G() -> const Cpp.S;
fn F() {
//@dump-sem-ir-begin
var s: const Cpp.S = G();
// CHECK:STDERR: fail_todo_import_const_pointer_param.carbon:[[@LINE+8]]:11: error: cannot implicitly convert expression of type `const Cpp.S*` to `Cpp.S*` [ConversionFailure]
// CHECK:STDERR: fail_todo_import_const_pointer_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: const S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(&s);
// CHECK:STDERR: ^~
// CHECK:STDERR: fail_todo_import_const_pointer_param.carbon:[[@LINE+5]]:11: note: type `const Cpp.S*` does not implement interface `Core.ImplicitAs(Cpp.S*)` [MissingImplInMemberAccessNote]
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_const_pointer_param.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: Cpp.foo(&s);
// CHECK:STDERR: ^~
// CHECK:STDERR: fail_todo_import_const_pointer_param.carbon: note: initializing function parameter [InCallToFunctionParam]
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
Cpp.foo(&s);
//@dump-sem-ir-end
@@ -149,14 +141,7 @@ fn IngestDoublePointer(s: Cpp.S**);
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_import_double_pointer_return.carbon:[[@LINE+14]]:23: error: semantics TODO: `Unsupported: pointer to pointer type: S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: IngestDoublePointer(Cpp.foo());
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_double_pointer_return.carbon:[[@LINE+11]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: IngestDoublePointer(Cpp.foo());
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_import_double_pointer_return.carbon:[[@LINE+7]]:23: error: semantics TODO: `Unsupported: return type: S * _Nonnull * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_double_pointer_return.carbon:[[@LINE+7]]:23: error: semantics TODO: `Unsupported: pointer to pointer type: S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: IngestDoublePointer(Cpp.foo());
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_double_pointer_return.carbon:[[@LINE+4]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -188,15 +173,12 @@ fn IngestConstPointer(s: const Cpp.S*);
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_import_const_pointer_return.carbon:[[@LINE+10]]:22: error: cannot implicitly convert expression of type `Cpp.S*` to `const Cpp.S*` [ConversionFailure]
// CHECK:STDERR: fail_todo_import_const_pointer_return.carbon:[[@LINE+7]]:22: error: semantics TODO: `Unsupported: return type: const S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: IngestConstPointer(Cpp.foo());
// CHECK:STDERR: ^~~~~~~~~
// CHECK:STDERR: fail_todo_import_const_pointer_return.carbon:[[@LINE+7]]:22: note: type `Cpp.S*` does not implement interface `Core.ImplicitAs(const Cpp.S*)` [MissingImplInMemberAccessNote]
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_const_pointer_return.carbon:[[@LINE+4]]:22: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: IngestConstPointer(Cpp.foo());
// CHECK:STDERR: ^~~~~~~~~
// CHECK:STDERR: fail_todo_import_const_pointer_return.carbon:[[@LINE-10]]:23: note: initializing function parameter [InCallToFunctionParam]
// CHECK:STDERR: fn IngestConstPointer(s: const Cpp.S*);
// CHECK:STDERR: ^~~~~~~~~~~~~~~
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
IngestConstPointer(Cpp.foo());
Cpp.foo();
@@ -222,14 +204,7 @@ import Cpp library "nullable_pointer_param.h";
fn F() {
//@dump-sem-ir-begin
var s: Cpp.S = {};
// CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+14]]:3: error: semantics TODO: `Unsupported: nullable pointer: S *` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(&s);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+11]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: Cpp.foo(&s);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S *` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: nullable pointer: S *` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(&s);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -321,6 +296,8 @@ fn F() {
// CHECK:STDOUT: %S.val: %S = struct_value () [concrete]
// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete]
// CHECK:STDOUT: %pattern_type.259: type = pattern_type %ptr.5c7 [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
// CHECK:STDOUT: %ptr.dfe: type = ptr_type %ptr.5c7 [concrete]
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
// CHECK:STDOUT: %Op.type.bae: type = fn_type @Op.1 [concrete]
@@ -344,10 +321,15 @@ fn F() {
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .S = %S.decl
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: .foo = %foo.decl
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import_ref.0b9: @impl.%Op.type (%Op.type.bc9) = import_ref Core//prelude/parts/destroy, loc8_29, loaded [symbolic = @impl.%Op (constants.%Op.46f)]
// CHECK:STDOUT: %Destroy.impl_witness_table = impl_witness_table (%Core.import_ref.0b9), @impl [concrete]
// CHECK:STDOUT: }
@@ -383,10 +365,11 @@ fn F() {
// CHECK:STDOUT: %ptr: type = ptr_type %S.ref.loc9 [concrete = constants.%ptr.5c7]
// CHECK:STDOUT: }
// CHECK:STDOUT: %p: ref %ptr.5c7 = bind_name p, %p.var
// CHECK:STDOUT: %Cpp.ref.loc24: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %p.ref: ref %ptr.5c7 = name_ref p, %p
// CHECK:STDOUT: %addr.loc24: %ptr.dfe = addr_of %p.ref
// CHECK:STDOUT: %addr.loc17: %ptr.dfe = addr_of %p.ref
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref(<error>)
// CHECK:STDOUT: %impl.elem0.loc9: %.33b = impl_witness_access constants.%Destroy.impl_witness.79a, element0 [concrete = constants.%Op.64b]
// CHECK:STDOUT: %bound_method.loc9_3.1: <bound method> = bound_method %p.var, %impl.elem0.loc9
// CHECK:STDOUT: %specific_fn.loc9: <specific function> = specific_function %impl.elem0.loc9, @Op.2(constants.%ptr.5c7) [concrete = constants.%Op.specific_fn.131]
@@ -411,9 +394,6 @@ fn F() {
// CHECK:STDOUT: %G.type: type = fn_type @G [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %G: %G.type = struct_value () [concrete]
// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
// CHECK:STDOUT: %ptr.ff5: type = ptr_type %const [concrete]
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
// CHECK:STDOUT: %Op.type.bae: type = fn_type @Op.1 [concrete]
@@ -431,15 +411,10 @@ fn F() {
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .S = %S.decl
// CHECK:STDOUT: .foo = %foo.decl
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import_ref.0b9: @impl.%Op.type (%Op.type.bc9) = import_ref Core//prelude/parts/destroy, loc8_29, loaded [symbolic = @impl.%Op (constants.%Op.46f)]
// CHECK:STDOUT: %Destroy.impl_witness_table = impl_witness_table (%Core.import_ref.0b9), @impl [concrete]
// CHECK:STDOUT: }
@@ -461,12 +436,10 @@ fn F() {
// CHECK:STDOUT: %const: type = const_type %S.ref [concrete = constants.%const]
// CHECK:STDOUT: }
// CHECK:STDOUT: %s: ref %const = bind_name s, %s.var
// CHECK:STDOUT: %Cpp.ref.loc19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %s.ref: ref %const = name_ref s, %s
// CHECK:STDOUT: %addr.loc19: %ptr.ff5 = addr_of %s.ref
// CHECK:STDOUT: %.loc19: %ptr.5c7 = converted %addr.loc19, <error> [concrete = <error>]
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref(<error>)
// CHECK:STDOUT: %addr.loc18: %ptr.ff5 = addr_of %s.ref
// CHECK:STDOUT: %impl.elem0.loc10_3.1: %.af2 = impl_witness_access constants.%Destroy.impl_witness.4d8, element0 [concrete = constants.%Op.af7]
// CHECK:STDOUT: %bound_method.loc10_3.1: <bound method> = bound_method %.loc10_3, %impl.elem0.loc10_3.1
// CHECK:STDOUT: %specific_fn.loc10_3.1: <specific function> = specific_function %impl.elem0.loc10_3.1, @Op.2(constants.%const) [concrete = constants.%Op.specific_fn]
@@ -530,38 +503,6 @@ fn F() {
// CHECK:STDOUT: %IngestDoublePointer.type: type = fn_type @IngestDoublePointer [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %IngestDoublePointer: %IngestDoublePointer.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .S = %S.decl
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %IngestDoublePointer.ref: %IngestDoublePointer.type = name_ref IngestDoublePointer, file.%IngestDoublePointer.decl [concrete = constants.%IngestDoublePointer]
// CHECK:STDOUT: %Cpp.ref.loc24: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc24: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %IngestDoublePointer.call: init %empty_tuple.type = call %IngestDoublePointer.ref(<error>)
// CHECK:STDOUT: %Cpp.ref.loc25: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc25: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_import_const_pointer_return.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %S: type = class_type @S [concrete]
// CHECK:STDOUT: %const: type = const_type %S [concrete]
// CHECK:STDOUT: %ptr.ff5: type = ptr_type %const [concrete]
// CHECK:STDOUT: %IngestConstPointer.type: type = fn_type @IngestConstPointer [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %IngestConstPointer: %IngestConstPointer.type = struct_value () [concrete]
// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
// CHECK:STDOUT: }
@@ -582,15 +523,43 @@ fn F() {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %IngestDoublePointer.ref: %IngestDoublePointer.type = name_ref IngestDoublePointer, file.%IngestDoublePointer.decl [concrete = constants.%IngestDoublePointer]
// CHECK:STDOUT: %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc17: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %foo.call.loc17: init <error> = call %foo.ref.loc17()
// CHECK:STDOUT: %IngestDoublePointer.call: init %empty_tuple.type = call %IngestDoublePointer.ref(<error>)
// CHECK:STDOUT: %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc18: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %foo.call.loc18: init <error> = call %foo.ref.loc18()
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_import_const_pointer_return.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %S: type = class_type @S [concrete]
// CHECK:STDOUT: %IngestConstPointer.type: type = fn_type @IngestConstPointer [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %IngestConstPointer: %IngestConstPointer.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .S = %S.decl
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %IngestConstPointer.ref: %IngestConstPointer.type = name_ref IngestConstPointer, file.%IngestConstPointer.decl [concrete = constants.%IngestConstPointer]
// CHECK:STDOUT: %Cpp.ref.loc20: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc20: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %foo.call.loc20: init %ptr.5c7 = call %foo.ref.loc20()
// CHECK:STDOUT: %.loc20: %ptr.ff5 = converted %foo.call.loc20, <error> [concrete = <error>]
// CHECK:STDOUT: %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc17: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %IngestConstPointer.call: init %empty_tuple.type = call %IngestConstPointer.ref(<error>)
// CHECK:STDOUT: %Cpp.ref.loc21: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc21: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %foo.call.loc21: init %ptr.5c7 = call %foo.ref.loc21()
// CHECK:STDOUT: %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref.loc18: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -602,6 +571,8 @@ fn F() {
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %pattern_type.7da: type = pattern_type %S [concrete]
// CHECK:STDOUT: %S.val: %S = struct_value () [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete]
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
// CHECK:STDOUT: %Op.type.bae: type = fn_type @Op.1 [concrete]
@@ -619,10 +590,15 @@ fn F() {
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .S = %S.decl
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: .foo = %foo.decl
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import_ref.0b9: @impl.%Op.type (%Op.type.bc9) = import_ref Core//prelude/parts/destroy, loc8_29, loaded [symbolic = @impl.%Op (constants.%Op.46f)]
// CHECK:STDOUT: %Destroy.impl_witness_table = impl_witness_table (%Core.import_ref.0b9), @impl [concrete]
// CHECK:STDOUT: }
@@ -644,10 +620,11 @@ fn F() {
// CHECK:STDOUT: %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S]
// CHECK:STDOUT: }
// CHECK:STDOUT: %s: ref %S = bind_name s, %s.var
// CHECK:STDOUT: %Cpp.ref.loc23: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %Cpp.ref.loc16: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %s.ref: ref %S = name_ref s, %s
// CHECK:STDOUT: %addr.loc23: %ptr.5c7 = addr_of %s.ref
// CHECK:STDOUT: %addr.loc16: %ptr.5c7 = addr_of %s.ref
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref(<error>)
// CHECK:STDOUT: %impl.elem0: %.271 = impl_witness_access constants.%Destroy.impl_witness.2d2, element0 [concrete = constants.%Op.ab5]
// CHECK:STDOUT: %bound_method.loc8_3.1: <bound method> = bound_method %s.var, %impl.elem0
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Op.2(constants.%S) [concrete = constants.%Op.specific_fn]