From 64bb4c7e0eb7e57213439eb548e6fc5c1187a333 Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 9 Dec 2024 12:59:07 -0500 Subject: [PATCH] Support builtin conversions of adaptor classes When doing a builtin conversion of an adaptor class: - if there's no conversion happening on the adaptor class itself, - if the conversion is from and to the same adaptor class type, Then we attempt the builtin conversion step on the foundation type as well. To do this, we must convert the value from its adaptor to its foundation type. Then perform the conversion. And convert the converted (foundation) value back to the adaptor class type. This might sound funny since they are the same type, so there can be no conversion happening. But in particular, this recursion allows copy construction of the elements of a tuple or struct with more than one element that is the foundation type of a class. A struct or tuple with more than one element is no longer initialized from a value by copy; its ValueRepr becomes a pointer. So it requires a builtin conversion to perform the copy on its interior elements. This already happens for these types in PerformBuiltinConversion, but now will also be done for adaptors of these types. --- toolchain/check/convert.cpp | 47 +++- .../testdata/as/adapter_conversion.carbon | 6 + .../testdata/class/adapter/adapt_copy.carbon | 219 +++++++++++++++--- toolchain/sem_ir/file.h | 6 +- 4 files changed, 234 insertions(+), 44 deletions(-) diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index c12ede807a10f..bbeb28fd0f2ab 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -707,7 +707,7 @@ static auto CanUseValueOfInitializer(const SemIR::File& sem_ir, } // Returns the non-adapter type that is compatible with the specified type. -static auto GetCompatibleBaseType(Context& context, SemIR::TypeId type_id) +static auto GetCompatibleFoundationType(Context& context, SemIR::TypeId type_id) -> SemIR::TypeId { // If the type is an adapter, its object representation type is its compatible // non-adapter type. @@ -782,9 +782,11 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id, // T explicitly converts to U if T is compatible with U. if (target.kind == ConversionTarget::Kind::ExplicitAs && target.type_id != value_type_id) { - auto target_base_id = GetCompatibleBaseType(context, target.type_id); - auto value_base_id = GetCompatibleBaseType(context, value_type_id); - if (target_base_id == value_base_id) { + auto target_foundation_id = + GetCompatibleFoundationType(context, target.type_id); + auto value_foundation_id = + GetCompatibleFoundationType(context, value_type_id); + if (target_foundation_id == value_foundation_id) { // For a struct or tuple literal, perform a category conversion if // necessary. if (SemIR::GetExprCategory(context.sem_ir(), value_id) == @@ -905,6 +907,43 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id, } } + // When converting between adaptor classes of the same type (we don't + // implicitly convert between different adaptors even if they adapt the same + // foundation type), and when no other conversion was available on the adaptor + // class itself, we do the builtin conversion step on their foundation type. + // To do so, we have to insert a conversion up to the foundation and back + // down. + if (value_type_id == target.type_id) { + auto foundation_value_type_id = + GetCompatibleFoundationType(context, value_type_id); + if (foundation_value_type_id != value_type_id) { + auto foundation_target_type_id = + GetCompatibleFoundationType(context, target.type_id); + CARBON_CHECK(foundation_value_type_id == foundation_target_type_id, + "Same type ids have different foundation type ids"); + + auto foundation_value_id = context.AddInst( + loc_id, {.type_id = foundation_value_type_id, .source_id = value_id}); + // We recurse here, but at most one time, since we have walked the full + // adapt chain to the root foundation type before recursing. + auto converted_value_id = + PerformBuiltinConversion(context, loc_id, foundation_value_id, + { + .kind = target.kind, + .type_id = foundation_target_type_id, + .init_id = target.init_id, + .init_block = target.init_block, + }); + auto target_value_id = context.AddInst( + loc_id, {.type_id = target.type_id, .source_id = converted_value_id}); + // If no builtin conversion actually happened, we orphan the two + // AsCompatible instructions and don't refer to them. If a conversion did + // happen, then we return the result of the second AsCompatible. + return converted_value_id == foundation_value_id ? value_id + : target_value_id; + } + } + // No builtin conversion applies. return value_id; } diff --git a/toolchain/check/testdata/as/adapter_conversion.carbon b/toolchain/check/testdata/as/adapter_conversion.carbon index 0273e44c1a813..f10e79237d5b1 100644 --- a/toolchain/check/testdata/as/adapter_conversion.carbon +++ b/toolchain/check/testdata/as/adapter_conversion.carbon @@ -565,6 +565,8 @@ var b: B = {.x = 1} as B; // CHECK:STDOUT: %B.ref.loc13: type = name_ref B, file.%B.decl [template = constants.%B] // CHECK:STDOUT: %.loc13_42.1: ref %B = as_compatible %.loc13_36 // CHECK:STDOUT: %.loc13_42.2: ref %B = converted %.loc13_36, %.loc13_42.1 +// CHECK:STDOUT: %.loc13_46.1: ref %A = as_compatible %.loc13_42.2 +// CHECK:STDOUT: %.loc13_46.2: ref %B = as_compatible %.loc13_46.1 // CHECK:STDOUT: %.loc13_42.3: %B = bind_value %.loc13_42.2 // CHECK:STDOUT: %b_value: %B = bind_name b_value, %.loc13_42.3 // CHECK:STDOUT: %int_1.loc24: Core.IntLiteral = int_value 1 [template = constants.%int_1.1] @@ -592,6 +594,8 @@ var b: B = {.x = 1} as B; // CHECK:STDOUT: %B.ref.loc24: type = name_ref B, file.%B.decl [template = constants.%B] // CHECK:STDOUT: %.loc24_41.1: ref %B = as_compatible %.loc24_35 // CHECK:STDOUT: %.loc24_41.2: ref %B = converted %.loc24_35, %.loc24_41.1 +// CHECK:STDOUT: %.loc24_45.1: ref %A = as_compatible %.loc24_41.2 +// CHECK:STDOUT: %.loc24_45.2: ref %B = as_compatible %.loc24_45.1 // CHECK:STDOUT: %.loc24_41.3: %B = bind_value %.loc24_41.2 // CHECK:STDOUT: assign file.%b_init.var, // CHECK:STDOUT: return @@ -667,6 +671,8 @@ var b: B = {.x = 1} as B; // CHECK:STDOUT: %.loc21_19: %struct_type.x.2 = struct_literal (%int_1) // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] // CHECK:STDOUT: %.loc21_21: %B = converted %.loc21_19, [template = ] +// CHECK:STDOUT: %.loc21_25.1: %A = as_compatible %.loc21_21 [template = ] +// CHECK:STDOUT: %.loc21_25.2: %B = as_compatible %.loc21_25.1 [template = ] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/class/adapter/adapt_copy.carbon b/toolchain/check/testdata/class/adapter/adapt_copy.carbon index f5b29484f8ee0..3d5691cb8c4c6 100644 --- a/toolchain/check/testdata/class/adapter/adapt_copy.carbon +++ b/toolchain/check/testdata/class/adapter/adapt_copy.carbon @@ -28,7 +28,7 @@ fn InTuple(c: (AdaptCopyable, i32)) -> (AdaptCopyable, i32) { return d; } -// --- fail_todo_adapt_copyable_tuple.carbon +// --- adapt_copyable_tuple.carbon library "[[@TEST_NAME]]"; @@ -36,18 +36,8 @@ class AdaptTuple { adapt (i32, i32); } -// TODO: Support copying in this case too, by performing the corresponding copy -// operation for the adapted type. fn F(c: AdaptTuple) -> AdaptTuple { - // CHECK:STDERR: fail_todo_adapt_copyable_tuple.carbon:[[@LINE+4]]:23: error: cannot copy value of type `AdaptTuple` [CopyOfUncopyableType] - // CHECK:STDERR: var d: AdaptTuple = c; - // CHECK:STDERR: ^ - // CHECK:STDERR: var d: AdaptTuple = c; - // CHECK:STDERR: fail_todo_adapt_copyable_tuple.carbon:[[@LINE+4]]:10: error: cannot copy value of type `AdaptTuple` [CopyOfUncopyableType] - // CHECK:STDERR: return d; - // CHECK:STDERR: ^ - // CHECK:STDERR: return d; } @@ -89,17 +79,30 @@ class AdaptNoncopyableIndirect { } fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { - // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+4]]:37: error: cannot copy value of type `AdaptNoncopyableIndirect` [CopyOfUncopyableType] + // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+4]]:3: error: cannot copy value of type `Noncopyable` [CopyOfUncopyableType] // CHECK:STDERR: var b: AdaptNoncopyableIndirect = a; - // CHECK:STDERR: ^ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var b: AdaptNoncopyableIndirect = a; - // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+3]]:10: error: cannot copy value of type `AdaptNoncopyableIndirect` [CopyOfUncopyableType] + // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+3]]:3: error: cannot copy value of type `Noncopyable` [CopyOfUncopyableType] // CHECK:STDERR: return b; - // CHECK:STDERR: ^ + // CHECK:STDERR: ^~~~~~~~~ return b; } +// --- adapt_copyable_struct.carbon + +library "[[@TEST_NAME]]"; + +class AdaptStruct { + adapt {.e: i32, .f: i32}; +} + +fn I(g: AdaptStruct) -> AdaptStruct { + var h: AdaptStruct = g; + return h; +} + // CHECK:STDOUT: --- adapt_copyable.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -193,10 +196,14 @@ fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { // CHECK:STDOUT: %d.var: ref %AdaptCopyable = var d // CHECK:STDOUT: %d: ref %AdaptCopyable = bind_name d, %d.var // CHECK:STDOUT: %c.ref: %AdaptCopyable = name_ref c, %c +// CHECK:STDOUT: %.loc11_27.1: %i32 = as_compatible %c.ref +// CHECK:STDOUT: %.loc11_27.2: %AdaptCopyable = as_compatible %.loc11_27.1 // CHECK:STDOUT: assign %d.var, %c.ref // CHECK:STDOUT: %d.ref: ref %AdaptCopyable = name_ref d, %d -// CHECK:STDOUT: %.loc12: %AdaptCopyable = bind_value %d.ref -// CHECK:STDOUT: return %.loc12 +// CHECK:STDOUT: %.loc12_11.1: ref %i32 = as_compatible %d.ref +// CHECK:STDOUT: %.loc12_11.2: ref %AdaptCopyable = as_compatible %.loc12_11.1 +// CHECK:STDOUT: %.loc12_10: %AdaptCopyable = bind_value %d.ref +// CHECK:STDOUT: return %.loc12_10 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InTuple(%c.param_patt: %tuple.type.2) -> %return.param_patt: %tuple.type.2 { @@ -212,29 +219,33 @@ fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { // CHECK:STDOUT: %d: ref %tuple.type.2 = bind_name d, %d.var // CHECK:STDOUT: %c.ref: %tuple.type.2 = name_ref c, %c // CHECK:STDOUT: %tuple.elem0.loc16_33.1: %AdaptCopyable = tuple_access %c.ref, element0 +// CHECK:STDOUT: %.loc16_33.1: %i32 = as_compatible %tuple.elem0.loc16_33.1 +// CHECK:STDOUT: %.loc16_33.2: %AdaptCopyable = as_compatible %.loc16_33.1 // CHECK:STDOUT: %tuple.elem0.loc16_33.2: ref %AdaptCopyable = tuple_access %d.var, element0 -// CHECK:STDOUT: %.loc16_33.1: init %AdaptCopyable = initialize_from %tuple.elem0.loc16_33.1 to %tuple.elem0.loc16_33.2 +// CHECK:STDOUT: %.loc16_33.3: init %AdaptCopyable = initialize_from %tuple.elem0.loc16_33.1 to %tuple.elem0.loc16_33.2 // CHECK:STDOUT: %tuple.elem1.loc16_33.1: %i32 = tuple_access %c.ref, element1 // CHECK:STDOUT: %tuple.elem1.loc16_33.2: ref %i32 = tuple_access %d.var, element1 -// CHECK:STDOUT: %.loc16_33.2: init %i32 = initialize_from %tuple.elem1.loc16_33.1 to %tuple.elem1.loc16_33.2 -// CHECK:STDOUT: %.loc16_33.3: init %tuple.type.2 = tuple_init (%.loc16_33.1, %.loc16_33.2) to %d.var -// CHECK:STDOUT: %.loc16_34: init %tuple.type.2 = converted %c.ref, %.loc16_33.3 +// CHECK:STDOUT: %.loc16_33.4: init %i32 = initialize_from %tuple.elem1.loc16_33.1 to %tuple.elem1.loc16_33.2 +// CHECK:STDOUT: %.loc16_33.5: init %tuple.type.2 = tuple_init (%.loc16_33.3, %.loc16_33.4) to %d.var +// CHECK:STDOUT: %.loc16_34: init %tuple.type.2 = converted %c.ref, %.loc16_33.5 // CHECK:STDOUT: assign %d.var, %.loc16_34 // CHECK:STDOUT: %d.ref: ref %tuple.type.2 = name_ref d, %d // CHECK:STDOUT: %tuple.elem0.loc17_10.1: ref %AdaptCopyable = tuple_access %d.ref, element0 -// CHECK:STDOUT: %.loc17_10.1: %AdaptCopyable = bind_value %tuple.elem0.loc17_10.1 +// CHECK:STDOUT: %.loc17_10.1: ref %i32 = as_compatible %tuple.elem0.loc17_10.1 +// CHECK:STDOUT: %.loc17_10.2: ref %AdaptCopyable = as_compatible %.loc17_10.1 +// CHECK:STDOUT: %.loc17_10.3: %AdaptCopyable = bind_value %tuple.elem0.loc17_10.1 // CHECK:STDOUT: %tuple.elem0.loc17_10.2: ref %AdaptCopyable = tuple_access %return, element0 -// CHECK:STDOUT: %.loc17_10.2: init %AdaptCopyable = initialize_from %.loc17_10.1 to %tuple.elem0.loc17_10.2 +// CHECK:STDOUT: %.loc17_10.4: init %AdaptCopyable = initialize_from %.loc17_10.3 to %tuple.elem0.loc17_10.2 // CHECK:STDOUT: %tuple.elem1.loc17_10.1: ref %i32 = tuple_access %d.ref, element1 -// CHECK:STDOUT: %.loc17_10.3: %i32 = bind_value %tuple.elem1.loc17_10.1 +// CHECK:STDOUT: %.loc17_10.5: %i32 = bind_value %tuple.elem1.loc17_10.1 // CHECK:STDOUT: %tuple.elem1.loc17_10.2: ref %i32 = tuple_access %return, element1 -// CHECK:STDOUT: %.loc17_10.4: init %i32 = initialize_from %.loc17_10.3 to %tuple.elem1.loc17_10.2 -// CHECK:STDOUT: %.loc17_10.5: init %tuple.type.2 = tuple_init (%.loc17_10.2, %.loc17_10.4) to %return -// CHECK:STDOUT: %.loc17_11: init %tuple.type.2 = converted %d.ref, %.loc17_10.5 +// CHECK:STDOUT: %.loc17_10.6: init %i32 = initialize_from %.loc17_10.5 to %tuple.elem1.loc17_10.2 +// CHECK:STDOUT: %.loc17_10.7: init %tuple.type.2 = tuple_init (%.loc17_10.4, %.loc17_10.6) to %return +// CHECK:STDOUT: %.loc17_11: init %tuple.type.2 = converted %d.ref, %.loc17_10.7 // CHECK:STDOUT: return %.loc17_11 to %return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_adapt_copyable_tuple.carbon +// CHECK:STDOUT: --- adapt_copyable_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %AdaptTuple: type = class_type @AdaptTuple [template] @@ -271,8 +282,8 @@ fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { // CHECK:STDOUT: %return.patt: %AdaptTuple = return_slot_pattern // CHECK:STDOUT: %return.param_patt: %AdaptTuple = out_param_pattern %return.patt, runtime_param1 // CHECK:STDOUT: } { -// CHECK:STDOUT: %AdaptTuple.ref.loc10_9: type = name_ref AdaptTuple, file.%AdaptTuple.decl [template = constants.%AdaptTuple] -// CHECK:STDOUT: %AdaptTuple.ref.loc10_24: type = name_ref AdaptTuple, file.%AdaptTuple.decl [template = constants.%AdaptTuple] +// CHECK:STDOUT: %AdaptTuple.ref.loc8_9: type = name_ref AdaptTuple, file.%AdaptTuple.decl [template = constants.%AdaptTuple] +// CHECK:STDOUT: %AdaptTuple.ref.loc8_24: type = name_ref AdaptTuple, file.%AdaptTuple.decl [template = constants.%AdaptTuple] // CHECK:STDOUT: %c.param: %AdaptTuple = value_param runtime_param0 // CHECK:STDOUT: %c: %AdaptTuple = bind_name c, %c.param // CHECK:STDOUT: %return.param: ref %AdaptTuple = out_param runtime_param1 @@ -301,14 +312,35 @@ fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param_patt: %AdaptTuple) -> %return.param_patt: %AdaptTuple { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %AdaptTuple.ref.loc15: type = name_ref AdaptTuple, file.%AdaptTuple.decl [template = constants.%AdaptTuple] +// CHECK:STDOUT: %AdaptTuple.ref.loc9: type = name_ref AdaptTuple, file.%AdaptTuple.decl [template = constants.%AdaptTuple] // CHECK:STDOUT: %d.var: ref %AdaptTuple = var d // CHECK:STDOUT: %d: ref %AdaptTuple = bind_name d, %d.var // CHECK:STDOUT: %c.ref: %AdaptTuple = name_ref c, %c -// CHECK:STDOUT: assign %d.var, +// CHECK:STDOUT: %.loc9_24.1: %tuple.type.2 = as_compatible %c.ref +// CHECK:STDOUT: %tuple.elem0.loc9_24.1: %i32 = tuple_access %.loc9_24.1, element0 +// CHECK:STDOUT: %tuple.elem0.loc9_24.2: ref %i32 = tuple_access %d.var, element0 +// CHECK:STDOUT: %.loc9_24.2: init %i32 = initialize_from %tuple.elem0.loc9_24.1 to %tuple.elem0.loc9_24.2 +// CHECK:STDOUT: %tuple.elem1.loc9_24.1: %i32 = tuple_access %.loc9_24.1, element1 +// CHECK:STDOUT: %tuple.elem1.loc9_24.2: ref %i32 = tuple_access %d.var, element1 +// CHECK:STDOUT: %.loc9_24.3: init %i32 = initialize_from %tuple.elem1.loc9_24.1 to %tuple.elem1.loc9_24.2 +// CHECK:STDOUT: %.loc9_24.4: init %tuple.type.2 = tuple_init (%.loc9_24.2, %.loc9_24.3) to %d.var +// CHECK:STDOUT: %.loc9_24.5: init %AdaptTuple = as_compatible %.loc9_24.4 +// CHECK:STDOUT: %.loc9_24.6: init %AdaptTuple = converted %c.ref, %.loc9_24.5 +// CHECK:STDOUT: assign %d.var, %.loc9_24.6 // CHECK:STDOUT: %d.ref: ref %AdaptTuple = name_ref d, %d -// CHECK:STDOUT: %.loc20: %AdaptTuple = bind_value %d.ref -// CHECK:STDOUT: return to %return +// CHECK:STDOUT: %.loc10_11.1: ref %tuple.type.2 = as_compatible %d.ref +// CHECK:STDOUT: %tuple.elem0.loc10_11.1: ref %i32 = tuple_access %.loc10_11.1, element0 +// CHECK:STDOUT: %.loc10_11.2: %i32 = bind_value %tuple.elem0.loc10_11.1 +// CHECK:STDOUT: %tuple.elem0.loc10_11.2: ref %i32 = tuple_access %return, element0 +// CHECK:STDOUT: %.loc10_11.3: init %i32 = initialize_from %.loc10_11.2 to %tuple.elem0.loc10_11.2 +// CHECK:STDOUT: %tuple.elem1.loc10_11.1: ref %i32 = tuple_access %.loc10_11.1, element1 +// CHECK:STDOUT: %.loc10_11.4: %i32 = bind_value %tuple.elem1.loc10_11.1 +// CHECK:STDOUT: %tuple.elem1.loc10_11.2: ref %i32 = tuple_access %return, element1 +// CHECK:STDOUT: %.loc10_11.5: init %i32 = initialize_from %.loc10_11.4 to %tuple.elem1.loc10_11.2 +// CHECK:STDOUT: %.loc10_11.6: init %tuple.type.2 = tuple_init (%.loc10_11.3, %.loc10_11.5) to %return +// CHECK:STDOUT: %.loc10_11.7: init %AdaptTuple = as_compatible %.loc10_11.6 +// CHECK:STDOUT: %.loc10_11.8: init %AdaptTuple = converted %d.ref, %.loc10_11.7 +// CHECK:STDOUT: return %.loc10_11.8 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_adapt_not_copyable.carbon @@ -378,9 +410,13 @@ fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { // CHECK:STDOUT: %b.var: ref %AdaptNoncopyable = var b // CHECK:STDOUT: %b: ref %AdaptNoncopyable = bind_name b, %b.var // CHECK:STDOUT: %a.ref: %AdaptNoncopyable = name_ref a, %a +// CHECK:STDOUT: %.loc17_30.1: %Noncopyable = as_compatible %a.ref +// CHECK:STDOUT: %.loc17_30.2: %AdaptNoncopyable = as_compatible %.loc17_30.1 // CHECK:STDOUT: assign %b.var, // CHECK:STDOUT: %b.ref: ref %AdaptNoncopyable = name_ref b, %b -// CHECK:STDOUT: %.loc22: %AdaptNoncopyable = bind_value %b.ref +// CHECK:STDOUT: %.loc22_11.1: ref %Noncopyable = as_compatible %b.ref +// CHECK:STDOUT: %.loc22_11.2: ref %AdaptNoncopyable = as_compatible %.loc22_11.1 +// CHECK:STDOUT: %.loc22_10: %AdaptNoncopyable = bind_value %b.ref // CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: @@ -469,9 +505,120 @@ fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { // CHECK:STDOUT: %b.var: ref %AdaptNoncopyableIndirect = var b // CHECK:STDOUT: %b: ref %AdaptNoncopyableIndirect = bind_name b, %b.var // CHECK:STDOUT: %a.ref: %AdaptNoncopyableIndirect = name_ref a, %a +// CHECK:STDOUT: %.loc17_38.1: %tuple.type.2 = as_compatible %a.ref +// CHECK:STDOUT: %tuple.elem0.loc17_38.1: %i32 = tuple_access %.loc17_38.1, element0 +// CHECK:STDOUT: %tuple.elem0.loc17_38.2: ref %i32 = tuple_access %b.var, element0 +// CHECK:STDOUT: %.loc17_38.2: init %i32 = initialize_from %tuple.elem0.loc17_38.1 to %tuple.elem0.loc17_38.2 +// CHECK:STDOUT: %tuple.elem1.loc17: %Noncopyable = tuple_access %.loc17_38.1, element1 +// CHECK:STDOUT: %.loc17_38.3: %AdaptNoncopyableIndirect = as_compatible [template = ] +// CHECK:STDOUT: %.loc17_38.4: %AdaptNoncopyableIndirect = converted %a.ref, %.loc17_38.3 [template = ] // CHECK:STDOUT: assign %b.var, // CHECK:STDOUT: %b.ref: ref %AdaptNoncopyableIndirect = name_ref b, %b -// CHECK:STDOUT: %.loc21: %AdaptNoncopyableIndirect = bind_value %b.ref +// CHECK:STDOUT: %.loc21_11.1: ref %tuple.type.2 = as_compatible %b.ref +// CHECK:STDOUT: %tuple.elem0.loc21_11.1: ref %i32 = tuple_access %.loc21_11.1, element0 +// CHECK:STDOUT: %.loc21_11.2: %i32 = bind_value %tuple.elem0.loc21_11.1 +// CHECK:STDOUT: %tuple.elem0.loc21_11.2: ref %i32 = tuple_access %return, element0 +// CHECK:STDOUT: %.loc21_11.3: init %i32 = initialize_from %.loc21_11.2 to %tuple.elem0.loc21_11.2 +// CHECK:STDOUT: %tuple.elem1.loc21: ref %Noncopyable = tuple_access %.loc21_11.1, element1 +// CHECK:STDOUT: %.loc21_11.4: %Noncopyable = bind_value %tuple.elem1.loc21 +// CHECK:STDOUT: %.loc21_11.5: %AdaptNoncopyableIndirect = as_compatible [template = ] +// CHECK:STDOUT: %.loc21_11.6: %AdaptNoncopyableIndirect = converted %b.ref, %.loc21_11.5 [template = ] // CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- adapt_copyable_struct.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %AdaptStruct: type = class_type @AdaptStruct [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: %struct_type.e.f: type = struct_type {.e: %i32, .f: %i32} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.e.f [template] +// CHECK:STDOUT: %I.type: type = fn_type @I [template] +// CHECK:STDOUT: %I: %I.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Int = %import_ref +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .AdaptStruct = %AdaptStruct.decl +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %AdaptStruct.decl: type = class_decl @AdaptStruct [template = constants.%AdaptStruct] {} {} +// CHECK:STDOUT: %I.decl: %I.type = fn_decl @I [template = constants.%I] { +// CHECK:STDOUT: %g.patt: %AdaptStruct = binding_pattern g +// CHECK:STDOUT: %g.param_patt: %AdaptStruct = value_param_pattern %g.patt, runtime_param0 +// CHECK:STDOUT: %return.patt: %AdaptStruct = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %AdaptStruct = out_param_pattern %return.patt, runtime_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %AdaptStruct.ref.loc8_9: type = name_ref AdaptStruct, file.%AdaptStruct.decl [template = constants.%AdaptStruct] +// CHECK:STDOUT: %AdaptStruct.ref.loc8_25: type = name_ref AdaptStruct, file.%AdaptStruct.decl [template = constants.%AdaptStruct] +// CHECK:STDOUT: %g.param: %AdaptStruct = value_param runtime_param0 +// CHECK:STDOUT: %g: %AdaptStruct = bind_name g, %g.param +// CHECK:STDOUT: %return.param: ref %AdaptStruct = out_param runtime_param1 +// CHECK:STDOUT: %return: ref %AdaptStruct = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptStruct { +// CHECK:STDOUT: %int_32.loc5_14: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %int.make_type_signed.loc5_14: init type = call constants.%Int(%int_32.loc5_14) [template = constants.%i32] +// CHECK:STDOUT: %.loc5_14.1: type = value_of_initializer %int.make_type_signed.loc5_14 [template = constants.%i32] +// CHECK:STDOUT: %.loc5_14.2: type = converted %int.make_type_signed.loc5_14, %.loc5_14.1 [template = constants.%i32] +// CHECK:STDOUT: %int_32.loc5_23: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %int.make_type_signed.loc5_23: init type = call constants.%Int(%int_32.loc5_23) [template = constants.%i32] +// CHECK:STDOUT: %.loc5_23.1: type = value_of_initializer %int.make_type_signed.loc5_23 [template = constants.%i32] +// CHECK:STDOUT: %.loc5_23.2: type = converted %int.make_type_signed.loc5_23, %.loc5_23.1 [template = constants.%i32] +// CHECK:STDOUT: %struct_type.e.f: type = struct_type {.e: %i32, .f: %i32} [template = constants.%struct_type.e.f] +// CHECK:STDOUT: adapt_decl %struct_type.e.f [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.e.f [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptStruct +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @I(%g.param_patt: %AdaptStruct) -> %return.param_patt: %AdaptStruct { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %AdaptStruct.ref.loc9: type = name_ref AdaptStruct, file.%AdaptStruct.decl [template = constants.%AdaptStruct] +// CHECK:STDOUT: %h.var: ref %AdaptStruct = var h +// CHECK:STDOUT: %h: ref %AdaptStruct = bind_name h, %h.var +// CHECK:STDOUT: %g.ref: %AdaptStruct = name_ref g, %g +// CHECK:STDOUT: %.loc9_25.1: %struct_type.e.f = as_compatible %g.ref +// CHECK:STDOUT: %.loc9_25.2: %i32 = struct_access %.loc9_25.1, element0 +// CHECK:STDOUT: %.loc9_25.3: ref %i32 = struct_access %h.var, element0 +// CHECK:STDOUT: %.loc9_25.4: init %i32 = initialize_from %.loc9_25.2 to %.loc9_25.3 +// CHECK:STDOUT: %.loc9_25.5: %i32 = struct_access %.loc9_25.1, element1 +// CHECK:STDOUT: %.loc9_25.6: ref %i32 = struct_access %h.var, element1 +// CHECK:STDOUT: %.loc9_25.7: init %i32 = initialize_from %.loc9_25.5 to %.loc9_25.6 +// CHECK:STDOUT: %.loc9_25.8: init %struct_type.e.f = struct_init (%.loc9_25.4, %.loc9_25.7) to %h.var +// CHECK:STDOUT: %.loc9_25.9: init %AdaptStruct = as_compatible %.loc9_25.8 +// CHECK:STDOUT: %.loc9_25.10: init %AdaptStruct = converted %g.ref, %.loc9_25.9 +// CHECK:STDOUT: assign %h.var, %.loc9_25.10 +// CHECK:STDOUT: %h.ref: ref %AdaptStruct = name_ref h, %h +// CHECK:STDOUT: %.loc10_11.1: ref %struct_type.e.f = as_compatible %h.ref +// CHECK:STDOUT: %.loc10_11.2: ref %i32 = struct_access %.loc10_11.1, element0 +// CHECK:STDOUT: %.loc10_11.3: %i32 = bind_value %.loc10_11.2 +// CHECK:STDOUT: %.loc10_11.4: ref %i32 = struct_access %return, element0 +// CHECK:STDOUT: %.loc10_11.5: init %i32 = initialize_from %.loc10_11.3 to %.loc10_11.4 +// CHECK:STDOUT: %.loc10_11.6: ref %i32 = struct_access %.loc10_11.1, element1 +// CHECK:STDOUT: %.loc10_11.7: %i32 = bind_value %.loc10_11.6 +// CHECK:STDOUT: %.loc10_11.8: ref %i32 = struct_access %return, element1 +// CHECK:STDOUT: %.loc10_11.9: init %i32 = initialize_from %.loc10_11.7 to %.loc10_11.8 +// CHECK:STDOUT: %.loc10_11.10: init %struct_type.e.f = struct_init (%.loc10_11.5, %.loc10_11.9) to %return +// CHECK:STDOUT: %.loc10_11.11: init %AdaptStruct = as_compatible %.loc10_11.10 +// CHECK:STDOUT: %.loc10_11.12: init %AdaptStruct = converted %h.ref, %.loc10_11.11 +// CHECK:STDOUT: return %.loc10_11.12 to %return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 528ff15e1eb93..427eff3ae118a 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -284,12 +284,10 @@ enum class ExprCategory : int8_t { // object that outlives the current full expression context. DurableRef, // This instruction represents an ephemeral reference expression, that denotes - // an - // object that does not outlive the current full expression context. + // an object that does not outlive the current full expression context. EphemeralRef, // This instruction represents an initializing expression, that describes how - // to - // initialize an object. + // to initialize an object. Initializing, // This instruction represents a syntactic combination of expressions that are // permitted to have different expression categories. This is used for tuple