From 79ba184dabcda5003374df634a7481b639e00fdd Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 11 Dec 2024 16:44:07 -0800 Subject: [PATCH] Provide a location for monomorphization failures resulting from `TryToCompleteType`. (#4670) Almost all callers actually could never fail and nearly all of those already `CHECK`-failed on failure. Add a new overload for that case, and add a location parameter for the one remaining call. --- toolchain/check/context.cpp | 18 +- toolchain/check/context.h | 6 +- toolchain/check/convert.cpp | 15 +- toolchain/check/generic.cpp | 2 +- .../generic/complete_in_conversion.carbon | 216 ++++++++++++++++++ 5 files changed, 241 insertions(+), 16 deletions(-) create mode 100644 toolchain/check/testdata/class/generic/complete_in_conversion.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index adab63ebe771d..3b1c43c268f27 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -1261,9 +1261,15 @@ class TypeCompleter { }; } // namespace -auto Context::TryToCompleteType(SemIR::TypeId type_id) -> bool { - // TODO: We need a location here in case we need to instantiate a class type. - return TypeCompleter(*this, SemIR::LocId::Invalid, nullptr).Complete(type_id); +auto Context::TryToCompleteType(SemIR::TypeId type_id, SemIRLoc loc) -> bool { + return TypeCompleter(*this, loc, nullptr).Complete(type_id); +} + +auto Context::CompleteTypeOrCheckFail(SemIR::TypeId type_id) -> void { + bool complete = + TypeCompleter(*this, SemIR::LocId::Invalid, nullptr).Complete(type_id); + CARBON_CHECK(complete, "Expected {0} to be a complete type", + types().GetAsInst(type_id)); } auto Context::RequireCompleteType(SemIR::TypeId type_id, SemIR::LocId loc_id, @@ -1386,8 +1392,7 @@ template static auto GetCompleteTypeImpl(Context& context, EachArgT... each_arg) -> SemIR::TypeId { auto type_id = GetTypeImpl(context, each_arg...); - bool complete = context.TryToCompleteType(type_id); - CARBON_CHECK(complete, "Type completion should not fail"); + context.CompleteTypeOrCheckFail(type_id); return type_id; } @@ -1413,8 +1418,7 @@ auto Context::GetSingletonType(SemIR::InstId singleton_id) -> SemIR::TypeId { CARBON_CHECK(SemIR::IsSingletonInstId(singleton_id)); auto type_id = GetTypeIdForTypeInst(singleton_id); // To keep client code simpler, complete builtin types before returning them. - bool complete = TryToCompleteType(type_id); - CARBON_CHECK(complete, "Failed to complete builtin type"); + CompleteTypeOrCheckFail(type_id); return type_id; } diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 2b3f4afdb7ff4..b602adbb53e86 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -344,7 +344,11 @@ class Context { // symbolic. // // Avoid calling this where possible, as it can lead to coherence issues. - auto TryToCompleteType(SemIR::TypeId type_id) -> bool; + // TODO: Remove the remaining call to this and delete this function. + auto TryToCompleteType(SemIR::TypeId type_id, SemIRLoc loc) -> bool; + + // Completes the type `type_id`. CHECK-fails if it can't be completed. + auto CompleteTypeOrCheckFail(SemIR::TypeId type_id) -> void; // Like `TryToCompleteType`, but for cases where it is an error for the type // to be incomplete. diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 7f4141a6a0749..507548c3bccbc 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -596,13 +596,14 @@ using InheritancePath = // Computes the inheritance path from class `derived_id` to class `base_id`. // Returns nullopt if `derived_id` is not a class derived from `base_id`. -static auto ComputeInheritancePath(Context& context, SemIR::TypeId derived_id, +static auto ComputeInheritancePath(Context& context, SemIRLoc loc, + SemIR::TypeId derived_id, SemIR::TypeId base_id) -> std::optional { // We intend for NRVO to be applied to `result`. All `return` statements in // this function should `return result;`. std::optional result(std::in_place); - if (!context.TryToCompleteType(derived_id)) { + if (!context.TryToCompleteType(derived_id, loc)) { // TODO: Should we give an error here? If we don't, and there is an // inheritance path when the class is defined, we may have a coherence // problem. @@ -856,8 +857,8 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id, } // An expression of type T converts to U if T is a class derived from U. - if (auto path = - ComputeInheritancePath(context, value_type_id, target.type_id); + if (auto path = ComputeInheritancePath(context, loc_id, value_type_id, + target.type_id); path && !path->empty()) { return ConvertDerivedToBase(context, loc_id, value_id, *path); } @@ -867,9 +868,9 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id, if (auto target_pointer_type = target_type_inst.TryAs()) { if (auto src_pointer_type = sem_ir.types().TryGetAs(value_type_id)) { - if (auto path = - ComputeInheritancePath(context, src_pointer_type->pointee_id, - target_pointer_type->pointee_id); + if (auto path = ComputeInheritancePath(context, loc_id, + src_pointer_type->pointee_id, + target_pointer_type->pointee_id); path && !path->empty()) { return ConvertDerivedPointerToBasePointer( context, loc_id, *src_pointer_type, target.type_id, value_id, diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index ca37cc70d5cc8..e9abbba09dd2d 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -249,7 +249,7 @@ static auto MakeGenericEvalBlock(Context& context, SemIR::GenericId generic_id, // constraints on the generic rather than properties of the type. For now, // require the transformed type to be complete if the original was. if (context.types().IsComplete(inst.type_id())) { - context.TryToCompleteType(type_id); + context.CompleteTypeOrCheckFail(type_id); } inst.SetType(type_id); context.sem_ir().insts().Set(inst_id, inst); diff --git a/toolchain/check/testdata/class/generic/complete_in_conversion.carbon b/toolchain/check/testdata/class/generic/complete_in_conversion.carbon new file mode 100644 index 0000000000000..6ab05b239223f --- /dev/null +++ b/toolchain/check/testdata/class/generic/complete_in_conversion.carbon @@ -0,0 +1,216 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/complete_in_conversion.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/complete_in_conversion.carbon + +// --- fail_derived_to_base.carbon + +base class B {} + +class A(N:! i32) { + extend base: B; + + // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE+3]]:10: error: integer type width of 0 is not positive [IntWidthNotPositive] + // CHECK:STDERR: var n: Core.Int(N); + // CHECK:STDERR: ^~~~~~~~~~~ + var n: Core.Int(N); +} + +fn F(a: A(0)*) { + // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE+3]]:3: note: in `A(0)` used here [ResolvingSpecificHere] + // CHECK:STDERR: let b: B* = a; + // CHECK:STDERR: ^~~~~~~~~~~~~~ + let b: B* = a; +} + +// CHECK:STDOUT: --- fail_derived_to_base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %B: type = class_type @B [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type.1: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template] +// CHECK:STDOUT: %Int.type: type = fn_type @Int [template] +// CHECK:STDOUT: %Int: %Int.type = struct_value () [template] +// CHECK:STDOUT: %i32: type = int_type signed, %int_32 [template] +// CHECK:STDOUT: %N.1: %i32 = bind_symbolic_name N, 0 [symbolic] +// CHECK:STDOUT: %N.patt.1: %i32 = symbolic_binding_pattern N, 0 [symbolic] +// CHECK:STDOUT: %A.type: type = generic_class_type @A [template] +// CHECK:STDOUT: %A.generic: %A.type = struct_value () [template] +// CHECK:STDOUT: %A.1: type = class_type @A, @A(%N.1) [symbolic] +// CHECK:STDOUT: %A.elem.1: type = unbound_element_type %A.1, %B [symbolic] +// CHECK:STDOUT: %Convert.type.2: type = fn_type @Convert.1, @ImplicitAs(Core.IntLiteral) [template] +// CHECK:STDOUT: %Convert.type.13: type = fn_type @Convert.4, @impl.3(%int_32) [template] +// CHECK:STDOUT: %Convert.13: %Convert.type.13 = struct_value () [template] +// CHECK:STDOUT: %interface.9: = interface_witness (%Convert.13) [template] +// CHECK:STDOUT: %Convert.bound.1: = bound_method %N.1, %Convert.13 [symbolic] +// CHECK:STDOUT: %Convert.specific_fn.1: = specific_function %Convert.bound.1, @Convert.4(%int_32) [symbolic] +// CHECK:STDOUT: %int.convert_checked: init Core.IntLiteral = call %Convert.specific_fn.1(%N.1) [symbolic] +// CHECK:STDOUT: %iN.2: type = int_type signed, %int.convert_checked [symbolic] +// CHECK:STDOUT: %require_complete.7: = require_complete_type %iN.2 [symbolic] +// CHECK:STDOUT: %A.elem.2: type = unbound_element_type %A.1, %iN.2 [symbolic] +// CHECK:STDOUT: %struct_type.base.n: type = struct_type {.base: %B, .n: %iN.2} [symbolic] +// CHECK:STDOUT: %complete_type.3: = complete_type_witness %struct_type.base.n [symbolic] +// CHECK:STDOUT: %int_0.1: Core.IntLiteral = int_value 0 [template] +// CHECK:STDOUT: %Convert.type.14: type = fn_type @Convert.1, @ImplicitAs(%i32) [template] +// CHECK:STDOUT: %Convert.type.15: type = fn_type @Convert.2, @impl.1(%int_32) [template] +// CHECK:STDOUT: %Convert.15: %Convert.type.15 = struct_value () [template] +// CHECK:STDOUT: %interface.10: = interface_witness (%Convert.15) [template] +// CHECK:STDOUT: %Convert.bound.2: = bound_method %int_0.1, %Convert.15 [template] +// CHECK:STDOUT: %Convert.specific_fn.2: = specific_function %Convert.bound.2, @Convert.2(%int_32) [template] +// CHECK:STDOUT: %int_0.2: %i32 = int_value 0 [template] +// CHECK:STDOUT: %A.2: type = class_type @A, @A(%int_0.2) [template] +// CHECK:STDOUT: %ptr.2: type = ptr_type %A.2 [template] +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %ptr.3: type = ptr_type %B [template] +// CHECK:STDOUT: %A.elem.3: type = unbound_element_type %A.2, %B [template] +// CHECK:STDOUT: %Convert.bound.3: = bound_method %int_0.2, %Convert.13 [template] +// CHECK:STDOUT: %Convert.specific_fn.3: = specific_function %Convert.bound.3, @Convert.4(%int_32) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Int = %import_ref.1 +// CHECK:STDOUT: .ImplicitAs = %import_ref.2 +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: %Int.type = import_ref Core//prelude/types, Int, loaded [template = constants.%Int] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %B.decl: type = class_decl @B [template = constants.%B] {} {} +// CHECK:STDOUT: %A.decl: %A.type = class_decl @A [template = constants.%A.generic] { +// CHECK:STDOUT: %N.patt.loc4_9.1: %i32 = symbolic_binding_pattern N, 0 [symbolic = %N.patt.loc4_9.2 (constants.%N.patt.1)] +// CHECK:STDOUT: %N.param_patt: %i32 = value_param_pattern %N.patt.loc4_9.1, runtime_param [symbolic = %N.patt.loc4_9.2 (constants.%N.patt.1)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %int.make_type_signed.loc4: init type = call constants.%Int(%int_32) [template = constants.%i32] +// CHECK:STDOUT: %.loc4_13.1: type = value_of_initializer %int.make_type_signed.loc4 [template = constants.%i32] +// CHECK:STDOUT: %.loc4_13.2: type = converted %int.make_type_signed.loc4, %.loc4_13.1 [template = constants.%i32] +// CHECK:STDOUT: %N.param: %i32 = value_param runtime_param +// CHECK:STDOUT: %N.loc4_9.1: %i32 = bind_symbolic_name N, 0, %N.param [symbolic = %N.loc4_9.2 (constants.%N.1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { +// CHECK:STDOUT: %a.patt: %ptr.2 = binding_pattern a +// CHECK:STDOUT: %a.param_patt: %ptr.2 = value_param_pattern %a.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [template = constants.%A.generic] +// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [template = constants.%int_0.1] +// CHECK:STDOUT: %impl.elem0: %Convert.type.14 = interface_witness_access constants.%interface.10, element0 [template = constants.%Convert.15] +// CHECK:STDOUT: %Convert.bound: = bound_method %int_0, %impl.elem0 [template = constants.%Convert.bound.2] +// CHECK:STDOUT: %Convert.specific_fn: = specific_function %Convert.bound, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.2] +// CHECK:STDOUT: %int.convert_checked: init %i32 = call %Convert.specific_fn(%int_0) [template = constants.%int_0.2] +// CHECK:STDOUT: %.loc13_12.1: %i32 = value_of_initializer %int.convert_checked [template = constants.%int_0.2] +// CHECK:STDOUT: %.loc13_12.2: %i32 = converted %int_0, %.loc13_12.1 [template = constants.%int_0.2] +// CHECK:STDOUT: %A: type = class_type @A, @A(constants.%int_0.2) [template = constants.%A.2] +// CHECK:STDOUT: %ptr.loc13: type = ptr_type %A.2 [template = constants.%ptr.2] +// CHECK:STDOUT: %a.param: %ptr.2 = value_param runtime_param0 +// CHECK:STDOUT: %a: %ptr.2 = bind_name a, %a.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type.1] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%B +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @A(%N.loc4_9.1: %i32) { +// CHECK:STDOUT: %N.loc4_9.2: %i32 = bind_symbolic_name N, 0 [symbolic = %N.loc4_9.2 (constants.%N.1)] +// CHECK:STDOUT: %N.patt.loc4_9.2: %i32 = symbolic_binding_pattern N, 0 [symbolic = %N.patt.loc4_9.2 (constants.%N.patt.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %A: type = class_type @A, @A(%N.loc4_9.2) [symbolic = %A (constants.%A.1)] +// CHECK:STDOUT: %A.elem.loc5: type = unbound_element_type @A.%A (%A.1), %B [symbolic = %A.elem.loc5 (constants.%A.elem.1)] +// CHECK:STDOUT: %Convert.bound.loc10_19.2: = bound_method %N.loc4_9.2, constants.%Convert.13 [symbolic = %Convert.bound.loc10_19.2 (constants.%Convert.bound.1)] +// CHECK:STDOUT: %Convert.specific_fn.loc10_19.2: = specific_function %Convert.bound.loc10_19.2, @Convert.4(constants.%int_32) [symbolic = %Convert.specific_fn.loc10_19.2 (constants.%Convert.specific_fn.1)] +// CHECK:STDOUT: %int.convert_checked.loc10_19.2: init Core.IntLiteral = call %Convert.specific_fn.loc10_19.2(%N.loc4_9.2) [symbolic = %int.convert_checked.loc10_19.2 (constants.%int.convert_checked)] +// CHECK:STDOUT: %iN: type = int_type signed, %int.convert_checked.loc10_19.2 [symbolic = %iN (constants.%iN.2)] +// CHECK:STDOUT: %require_complete: = require_complete_type @A.%iN (%iN.2) [symbolic = %require_complete (constants.%require_complete.7)] +// CHECK:STDOUT: %A.elem.loc10: type = unbound_element_type @A.%A (%A.1), @A.%iN (%iN.2) [symbolic = %A.elem.loc10 (constants.%A.elem.2)] +// CHECK:STDOUT: %struct_type.base.n: type = struct_type {.base: %B, .n: @A.%iN (%iN.2)} [symbolic = %struct_type.base.n (constants.%struct_type.base.n)] +// CHECK:STDOUT: %complete_type.loc11_1.2: = complete_type_witness @A.%struct_type.base.n (%struct_type.base.n) [symbolic = %complete_type.loc11_1.2 (constants.%complete_type.3)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] +// CHECK:STDOUT: %.loc5: @A.%A.elem.loc5 (%A.elem.1) = base_decl %B.ref, element0 [template] +// CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] +// CHECK:STDOUT: %Int.ref: %Int.type = name_ref Int, imports.%import_ref.1 [template = constants.%Int] +// CHECK:STDOUT: %N.ref: %i32 = name_ref N, %N.loc4_9.1 [symbolic = %N.loc4_9.2 (constants.%N.1)] +// CHECK:STDOUT: %impl.elem0: %Convert.type.2 = interface_witness_access constants.%interface.9, element0 [template = constants.%Convert.13] +// CHECK:STDOUT: %Convert.bound.loc10_19.1: = bound_method %N.ref, %impl.elem0 [symbolic = %Convert.bound.loc10_19.2 (constants.%Convert.bound.1)] +// CHECK:STDOUT: %Convert.specific_fn.loc10_19.1: = specific_function %Convert.bound.loc10_19.1, @Convert.4(constants.%int_32) [symbolic = %Convert.specific_fn.loc10_19.2 (constants.%Convert.specific_fn.1)] +// CHECK:STDOUT: %int.convert_checked.loc10_19.1: init Core.IntLiteral = call %Convert.specific_fn.loc10_19.1(%N.ref) [symbolic = %int.convert_checked.loc10_19.2 (constants.%int.convert_checked)] +// CHECK:STDOUT: %.loc10_19.1: Core.IntLiteral = value_of_initializer %int.convert_checked.loc10_19.1 [symbolic = %int.convert_checked.loc10_19.2 (constants.%int.convert_checked)] +// CHECK:STDOUT: %.loc10_19.2: Core.IntLiteral = converted %N.ref, %.loc10_19.1 [symbolic = %int.convert_checked.loc10_19.2 (constants.%int.convert_checked)] +// CHECK:STDOUT: %int.make_type_signed.loc10: init type = call %Int.ref(%.loc10_19.2) [symbolic = %iN (constants.%iN.2)] +// CHECK:STDOUT: %.loc10_20.1: type = value_of_initializer %int.make_type_signed.loc10 [symbolic = %iN (constants.%iN.2)] +// CHECK:STDOUT: %.loc10_20.2: type = converted %int.make_type_signed.loc10, %.loc10_20.1 [symbolic = %iN (constants.%iN.2)] +// CHECK:STDOUT: %.loc10_8: @A.%A.elem.loc10 (%A.elem.2) = field_decl n, element1 [template] +// CHECK:STDOUT: %complete_type.loc11_1.1: = complete_type_witness %struct_type.base.n [symbolic = %complete_type.loc11_1.2 (constants.%complete_type.3)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%A.1 +// CHECK:STDOUT: .base = %.loc5 +// CHECK:STDOUT: .n = %.loc10_8 +// CHECK:STDOUT: extend %B.ref +// CHECK:STDOUT: complete_type_witness = %complete_type.loc11_1.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%a.param_patt: %ptr.2) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] +// CHECK:STDOUT: %ptr.loc17: type = ptr_type %B [template = constants.%ptr.3] +// CHECK:STDOUT: %a.ref: %ptr.2 = name_ref a, %a +// CHECK:STDOUT: %.loc17_16.1: ref %A.2 = deref %a.ref +// CHECK:STDOUT: %.loc17_16.2: ref %B = class_element_access %.loc17_16.1, element0 +// CHECK:STDOUT: %addr: %ptr.3 = addr_of %.loc17_16.2 +// CHECK:STDOUT: %.loc17_16.3: %ptr.3 = converted %a.ref, %addr +// CHECK:STDOUT: %b: %ptr.3 = bind_name b, %.loc17_16.3 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A(constants.%N.1) { +// CHECK:STDOUT: %N.loc4_9.2 => constants.%N.1 +// CHECK:STDOUT: %N.patt.loc4_9.2 => constants.%N.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A(%N.loc4_9.2) { +// CHECK:STDOUT: %N.loc4_9.2 => constants.%N.1 +// CHECK:STDOUT: %N.patt.loc4_9.2 => constants.%N.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A(constants.%int_0.2) { +// CHECK:STDOUT: %N.loc4_9.2 => constants.%int_0.2 +// CHECK:STDOUT: %N.patt.loc4_9.2 => constants.%int_0.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %A => constants.%A.2 +// CHECK:STDOUT: %A.elem.loc5 => constants.%A.elem.3 +// CHECK:STDOUT: %Convert.bound.loc10_19.2 => constants.%Convert.bound.3 +// CHECK:STDOUT: %Convert.specific_fn.loc10_19.2 => constants.%Convert.specific_fn.3 +// CHECK:STDOUT: %int.convert_checked.loc10_19.2 => constants.%int_0.1 +// CHECK:STDOUT: %iN => +// CHECK:STDOUT: %require_complete => +// CHECK:STDOUT: %A.elem.loc10 => +// CHECK:STDOUT: %struct_type.base.n => +// CHECK:STDOUT: %complete_type.loc11_1.2 => +// CHECK:STDOUT: } +// CHECK:STDOUT: