From 41dd256d564edef4577339926b9ce4e986bbebb9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 26 Feb 2026 17:27:25 -0800 Subject: [PATCH] Support for initialization of classes with abstract base classes. (#6802) When initializing `.base` in class initialization, use `partial Base` as the destination type rather than `Base`. Treat `partial Base` as not being abstract even when `Base` is. Allow conversion from a `partial T` initializer to a `T` initializer. Store the vptr while performing the conversion. Do not store the vptr when performing a `partial T` initialization, only when performing a non-partial `T` initialization. --- toolchain/check/convert.cpp | 282 +++++++++++++++--- toolchain/check/convert.h | 8 +- toolchain/check/eval_inst.cpp | 7 + toolchain/check/testdata/as/partial.carbon | 81 ++--- .../{fail_abstract.carbon => abstract.carbon} | 79 ++--- .../testdata/class/inheritance/base.carbon | 19 +- .../class/inheritance/derived_to_base.carbon | 40 +-- .../class/inheritance/import_base.carbon | 13 +- .../check/testdata/class/partial/init.carbon | 67 +++++ .../testdata/class/virtual_modifiers.carbon | 86 ++++-- .../testdata/interop/cpp/class/access.carbon | 19 +- .../testdata/interop/cpp/impls/destroy.carbon | 38 ++- toolchain/check/type_completion.cpp | 9 +- toolchain/lower/function_context.cpp | 7 + toolchain/lower/function_context.h | 4 + toolchain/lower/handle.cpp | 12 + toolchain/lower/testdata/class/base.carbon | 2 +- toolchain/lower/testdata/class/generic.carbon | 2 +- toolchain/lower/testdata/class/virtual.carbon | 153 +++++++++- toolchain/sem_ir/expr_info.cpp | 7 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/typed_insts.h | 14 + 22 files changed, 717 insertions(+), 233 deletions(-) rename toolchain/check/testdata/class/abstract/{fail_abstract.carbon => abstract.carbon} (92%) create mode 100644 toolchain/check/testdata/class/partial/init.carbon diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index db672b6a04b9..441479e32b4d 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -21,6 +21,7 @@ #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" +#include "toolchain/check/member_access.h" #include "toolchain/check/operator.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" @@ -33,6 +34,7 @@ #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" +#include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" @@ -206,8 +208,7 @@ static auto ConvertAggregateElement( llvm::ArrayRef src_literal_elems, ConversionTarget::Kind kind, SemIR::InstId target_id, SemIR::TypeInstId target_elem_type_inst, PendingBlock* target_block, - size_t src_field_index, size_t target_field_index, - SemIR::ClassType* vtable_class_type = nullptr) -> SemIR::InstId { + size_t src_field_index, size_t target_field_index) -> SemIR::InstId { auto src_elem_type = context.types().GetTypeIdForTypeInstId(src_elem_type_inst); auto target_elem_type = @@ -235,7 +236,7 @@ static auto ConvertAggregateElement( target.storage_id = MakeElementAccessInst( context, loc_id, target_id, target_elem_type, *target_block, target_field_index); - return Convert(context, loc_id, src_elem_id, target, vtable_class_type); + return Convert(context, loc_id, src_elem_id, target); } // Performs a conversion from a tuple to an array type. This function only @@ -473,6 +474,136 @@ static auto ConvertTupleToType(Context& context, SemIR::LocId loc_id, return context.types().GetTypeInstId(tuple_type_id); } +// Create a reference to the vtable pointer for a class. Returns None if the +// class has no vptr. +static auto CreateVtablePtrRef(Context& context, SemIR::LocId loc_id, + SemIR::ClassType vtable_class_type) + -> SemIR::InstId { + auto vtable_decl_id = + context.classes().Get(vtable_class_type.class_id).vtable_decl_id; + if (!vtable_decl_id.has_value()) { + return SemIR::InstId::None; + } + + LoadImportRef(context, vtable_decl_id); + auto canonical_vtable_decl_id = + context.constant_values().GetConstantInstId(vtable_decl_id); + return AddInst( + context, loc_id, + {.type_id = GetPointerType(context, SemIR::VtableType::TypeInstId), + .vtable_id = context.insts() + .GetAs(canonical_vtable_decl_id) + .vtable_id, + .specific_id = vtable_class_type.specific_id}); +} + +// Returns whether the given expression performs in-place initialization (or is +// invalid). +static auto IsInPlaceInitializing(Context& context, SemIR::InstId result_id) { + auto category = SemIR::GetExprCategory(context.sem_ir(), result_id); + return category == SemIR::ExprCategory::InPlaceInitializing || + (category == SemIR::ExprCategory::ReprInitializing && + SemIR::InitRepr::ForType(context.sem_ir(), + context.insts().Get(result_id).type_id()) + .kind == SemIR::InitRepr::InPlace) || + category == SemIR::ExprCategory::Error; +} + +// Returns the index of the vptr field in the given struct type fields, or +// None if there is no vptr field. +static auto GetVptrFieldIndex(llvm::ArrayRef fields) + -> SemIR::ElementIndex { + // If the type introduces a vptr, it will always be the first field. + bool has_vptr = + !fields.empty() && fields.front().name_id == SemIR::NameId::Vptr; + return has_vptr ? SemIR::ElementIndex(0) : SemIR::ElementIndex::None; +} + +// Builds a member access expression naming the vptr field of the given class +// object. This is analogous to what `PerformMemberAccess` for `NameId::Vptr` +// would return if the vptr could be found by name lookup. +static auto PerformVptrAccess(Context& context, SemIR::LocId loc_id, + SemIR::InstId class_ref_id) -> SemIR::InstId { + auto class_type_id = context.insts().Get(class_ref_id).type_id(); + while (class_ref_id.has_value()) { + // The type of `ref_id` must be a class type. + if (class_type_id == SemIR::ErrorInst::TypeId) { + return SemIR::ErrorInst::InstId; + } + auto class_type = context.types().GetAs(class_type_id); + auto& class_info = context.classes().Get(class_type.class_id); + + // Get the object representation. + auto object_repr_id = + class_info.GetObjectRepr(context.sem_ir(), class_type.specific_id); + if (object_repr_id == SemIR::ErrorInst::TypeId) { + return SemIR::ErrorInst::InstId; + } + if (context.types().Is(object_repr_id)) { + context.TODO(loc_id, "accessing vptr of custom layout class"); + return SemIR::ErrorInst::InstId; + } + + // Check to see if this class introduces the vptr. + auto repr_struct_type = + context.types().GetAs(object_repr_id); + auto repr_fields = + context.struct_type_fields().Get(repr_struct_type.fields_id); + if (auto vptr_field_index = GetVptrFieldIndex(repr_fields); + vptr_field_index.has_value()) { + return AddInst( + context, loc_id, + {.type_id = context.types().GetTypeIdForTypeInstId( + repr_fields[vptr_field_index.index].type_inst_id), + .base_id = class_ref_id, + .index = vptr_field_index}); + } + + // Otherwise, step through to the base class and try again. + CARBON_CHECK(class_info.base_id.has_value(), + "Could not find vptr for dynamic class"); + auto base_decl = context.insts().GetAs(class_info.base_id); + class_type_id = context.types().GetTypeIdForTypeInstId( + repr_fields[base_decl.index.index].type_inst_id); + class_ref_id = + AddInst(context, loc_id, + {.type_id = class_type_id, + .base_id = class_ref_id, + .index = base_decl.index}); + } + return class_ref_id; +} + +// Converts an initializer for a type `partial T` to an initializer for `T` by +// initializing the vptr if necessary. +static auto ConvertPartialInitializerToNonPartial( + Context& context, ConversionTarget target, + SemIR::ClassType vtable_class_type, SemIR::InstId result_id) + -> SemIR::InstId { + auto loc_id = SemIR::LocId(result_id); + auto vptr_id = CreateVtablePtrRef(context, loc_id, vtable_class_type); + if (!vptr_id.has_value()) { + // No vtable pointer in this class, nothing to do. + return result_id; + } + + CARBON_CHECK( + IsInPlaceInitializing(context, result_id), + "Type with vptr should have in-place initializing representation"); + + target.storage_access_block->InsertHere(); + auto dest_id = PerformVptrAccess(context, loc_id, target.storage_id); + auto vptr_init_id = AddInst( + context, loc_id, + {.type_id = context.insts().Get(dest_id).type_id(), + .src_id = vptr_id, + .dest_id = dest_id}); + return AddInst(context, loc_id, + {.type_id = target.type_id, + .base_init_id = result_id, + .update_init_id = vptr_init_id}); +} + // Common implementation for ConvertStructToStruct and ConvertStructToClass. template static auto ConvertStructToStructOrClass( @@ -487,10 +618,9 @@ static auto ConvertStructToStructOrClass( auto& sem_ir = context.sem_ir(); auto src_elem_fields = sem_ir.struct_type_fields().Get(src_type.fields_id); auto dest_elem_fields = sem_ir.struct_type_fields().Get(dest_type.fields_id); - bool dest_has_vptr = !dest_elem_fields.empty() && - dest_elem_fields.front().name_id == SemIR::NameId::Vptr; - int dest_vptr_offset = (dest_has_vptr ? 1 : 0); - auto dest_elem_fields_size = dest_elem_fields.size() - dest_vptr_offset; + auto dest_vptr_index = GetVptrFieldIndex(dest_elem_fields); + auto dest_elem_fields_size = + dest_elem_fields.size() - (dest_vptr_index.has_value() ? 1 : 0); auto value = sem_ir.insts().Get(value_id); SemIR::LocId value_loc_id(value_id); @@ -541,7 +671,7 @@ static auto ConvertStructToStructOrClass( // of the source. // TODO: Annotate diagnostics coming from here with the element index. auto new_block = - literal_elems_id.has_value() && !dest_has_vptr + literal_elems_id.has_value() && !dest_vptr_index.has_value() ? SemIR::CopyOnWriteInstBlock(&sem_ir, literal_elems_id) : SemIR::CopyOnWriteInstBlock( &sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{ @@ -559,18 +689,20 @@ static auto ConvertStructToStructOrClass( {.type_id = vptr_type_id, .base_id = target.storage_id, .index = SemIR::ElementIndex(i)}); - auto vtable_decl_id = - context.classes().Get(vtable_class_type->class_id).vtable_decl_id; - LoadImportRef(context, vtable_decl_id); - auto canonical_vtable_decl_id = - context.constant_values().GetConstantInstId(vtable_decl_id); - auto vtable_ptr_id = AddInst( - context, value_loc_id, - {.type_id = GetPointerType(context, SemIR::VtableType::TypeInstId), - .vtable_id = context.insts() - .GetAs(canonical_vtable_decl_id) - .vtable_id, - .specific_id = vtable_class_type->specific_id}); + auto vtable_ptr_id = SemIR::InstId::None; + if (vtable_class_type) { + vtable_ptr_id = + CreateVtablePtrRef(context, value_loc_id, *vtable_class_type); + // Track that we initialized the vptr so we don't do it again. + vtable_class_type = nullptr; + } else { + // For a partial class type, we leave the vtable pointer uninitialized. + // TODO: Consider storing a specified value such as null for hardening. + vtable_ptr_id = AddInst( + context, value_loc_id, + {.type_id = + GetPointerType(context, SemIR::VtableType::TypeInstId)}); + } auto init_id = AddInst(context, value_loc_id, {.type_id = vptr_type_id, .src_id = vtable_ptr_id, @@ -609,18 +741,48 @@ static auto ConvertStructToStructOrClass( } auto src_field = src_elem_fields[src_field_index]; + // When initializing the `.base` field of a class, the destination type is + // `partial Base`, not `Base`. + // TODO: Skip this if the source field is an initializing expression of the + // non-partial type in order to produce smaller IR. + auto dest_field_type_inst_id = dest_field.type_inst_id; + if (dest_field.name_id == SemIR::NameId::Base) { + auto partial_type_id = GetQualifiedType( + context, + context.types().GetTypeIdForTypeInstId(dest_field.type_inst_id), + SemIR::TypeQualifiers::Partial); + dest_field_type_inst_id = context.types().GetTypeInstId(partial_type_id); + } + // TODO: This call recurses back into conversion. Switch to an iterative // approach. + auto dest_field_index = src_field_index; + if (dest_vptr_index.has_value() && + static_cast(src_field_index) >= dest_vptr_index.index) { + dest_field_index += 1; + } auto init_id = ConvertAggregateElement( context, value_loc_id, value_id, src_field.type_inst_id, literal_elems, inner_kind, target.storage_id, - dest_field.type_inst_id, target.storage_access_block, - src_field_index, src_field_index + dest_vptr_offset, - vtable_class_type); + dest_field_type_inst_id, target.storage_access_block, + src_field_index, dest_field_index); if (init_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } + + // When initializing the base, adjust the type of the initializer from + // `partial Base` to `Base`. This isn't strictly correct, since we haven't + // finished initializing a `Base` until we store to the vptr, but is better + // than having an inconsistent type for the struct field initializer. + if (dest_field_type_inst_id != dest_field.type_inst_id) { + init_id = AddInst( + context, value_loc_id, + {.type_id = + context.types().GetTypeIdForTypeInstId(dest_field.type_inst_id), + .source_id = init_id}); + } + new_block.Set(i, init_id); } @@ -629,10 +791,15 @@ static auto ConvertStructToStructOrClass( target.storage_access_block->InsertHere(); CARBON_CHECK(is_init, "Converting directly to a class value is not supported"); - return AddInst(context, value_loc_id, - {.type_id = target.type_id, - .elements_id = new_block.id(), - .dest_id = target.storage_id}); + auto result_id = AddInst(context, value_loc_id, + {.type_id = target.type_id, + .elements_id = new_block.id(), + .dest_id = target.storage_id}); + if (vtable_class_type) { + result_id = ConvertPartialInitializerToNonPartial( + context, target, *vtable_class_type, result_id); + } + return result_id; } else if (is_init) { target.storage_access_block->InsertHere(); return AddInst(context, value_loc_id, @@ -664,11 +831,11 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, SemIR::ClassType dest_type, SemIR::InstId value_id, ConversionTarget target, - SemIR::ClassType* vtable_class_type) - -> SemIR::InstId { + bool is_partial = false) -> SemIR::InstId { PendingBlock target_block(&context); auto& dest_class_info = context.classes().Get(dest_type.class_id); - CARBON_CHECK(dest_class_info.inheritance_kind != SemIR::Class::Abstract); + CARBON_CHECK(is_partial || + dest_class_info.inheritance_kind != SemIR::Class::Abstract); auto object_repr_id = dest_class_info.GetObjectRepr(context.sem_ir(), dest_type.specific_id); if (object_repr_id == SemIR::ErrorInst::TypeId) { @@ -693,7 +860,7 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, auto result_id = ConvertStructToStructOrClass( context, src_type, dest_struct_type, value_id, target, - vtable_class_type ? vtable_class_type : &dest_type); + is_partial ? nullptr : &dest_type); if (need_temporary) { target_block.InsertHere(); @@ -894,11 +1061,11 @@ static auto CanRemoveQualifiers(SemIR::TypeQualifiers quals, return false; } - if (quals.HasAnyOf(SemIR::TypeQualifiers::Partial) && - (!allow_unsafe || SemIR::IsInitializerCategory(cat))) { - // TODO: Allow removing `partial` for initializing expressions as a safe - // conversion. `PerformBuiltinConversion` will need to initialize the vptr - // as part of the conversion. + if (quals.HasAnyOf(SemIR::TypeQualifiers::Partial) && !allow_unsafe && + !SemIR::IsInitializerCategory(cat)) { + // Removing `partial` is an unsafe conversion for a non-initializing + // expression. But it's OK for an initializing expression because we will + // initialize the vptr as part of the conversion. return false; } @@ -939,10 +1106,9 @@ static auto DiagnoseConversionFailureToConstraintValue( } } -static auto PerformBuiltinConversion( - Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, - ConversionTarget target, SemIR::ClassType* vtable_class_type = nullptr) - -> SemIR::InstId { +static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id, + SemIR::InstId value_id, + ConversionTarget target) -> SemIR::InstId { auto& sem_ir = context.sem_ir(); auto value = sem_ir.insts().Get(value_id); auto value_type_id = value.type_id(); @@ -1112,6 +1278,19 @@ static auto PerformBuiltinConversion( } } + if ((removed_quals & SemIR::TypeQualifiers::Partial) != + SemIR::TypeQualifiers::None && + SemIR::IsInitializerCategory(category)) { + auto unqual_target_type_id = + context.types().GetUnqualifiedType(target.type_id); + if (auto target_class_type = + context.types().TryGetAs( + unqual_target_type_id)) { + value_id = ConvertPartialInitializerToNonPartial( + context, target, *target_class_type, value_id); + } + } + value_id = AddInst( context, loc_id, {.type_id = target.type_id, .source_id = value_id}); @@ -1167,19 +1346,28 @@ static auto PerformBuiltinConversion( } } + // Split the qualifiers off the target type. + // TODO: Most conversions should probably be looking at the unqualified target + // type. + auto [target_unqual_type_id, target_quals] = + context.types().GetUnqualifiedTypeAndQualifiers(target.type_id); + auto target_unqual_type_inst = + sem_ir.types().GetAsInst(target_unqual_type_id); + // A struct {.f_1: T_1, .f_2: T_2, ..., .f_n: T_n} converts to a class type // if it converts to the struct type that is the class's representation type // (a struct with the same fields as the class, plus a base field where // relevant). - if (auto target_class_type = target_type_inst.TryAs()) { + if (auto target_class_type = + target_unqual_type_inst.TryAs()) { if (auto src_struct_type = sem_ir.types().TryGetAs(value_type_id)) { if (!context.classes() .Get(target_class_type->class_id) .adapt_id.has_value()) { - return ConvertStructToClass(context, *src_struct_type, - *target_class_type, value_id, target, - vtable_class_type); + return ConvertStructToClass( + context, *src_struct_type, *target_class_type, value_id, target, + target_quals.HasAnyOf(SemIR::TypeQualifiers::Partial)); } } @@ -1697,8 +1885,7 @@ auto CategoryConverter::DoStep(const SemIR::InstId expr_id, } auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, - ConversionTarget target, SemIR::ClassType* vtable_class_type) - -> SemIR::InstId { + ConversionTarget target) -> SemIR::InstId { auto& sem_ir = context.sem_ir(); auto orig_expr_id = expr_id; @@ -1798,8 +1985,7 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, TryToCompleteType(context, context.insts().Get(expr_id).type_id(), loc_id); // Check whether any builtin conversion applies. - expr_id = PerformBuiltinConversion(context, loc_id, expr_id, target, - vtable_class_type); + expr_id = PerformBuiltinConversion(context, loc_id, expr_id, target); if (expr_id == SemIR::ErrorInst::InstId) { return expr_id; } diff --git a/toolchain/check/convert.h b/toolchain/check/convert.h index f5eba07a56cc..acae9d8f76f7 100644 --- a/toolchain/check/convert.h +++ b/toolchain/check/convert.h @@ -85,14 +85,8 @@ struct ConversionTarget { }; // Convert a value to another type and expression category. -// TODO: The `vtable_id` parameter is too much of a special case here, and -// should be removed - once partial classes are implemented, the vtable pointer -// initialization will be done not in this conversion, but during initialization -// of the object of non-partial class type from the object of partial class -// type. auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, - ConversionTarget target, - SemIR::ClassType* vtable_class_type = nullptr) -> SemIR::InstId; + ConversionTarget target) -> SemIR::InstId; // Converts `value_id` to an initializing expression of the type of // `storage_id`, and returns the possibly-converted initializing expression. diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index ac98d58896d6..832f4de65cd1 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -773,6 +773,13 @@ auto EvalConstantInst(Context& context, SemIR::UnaryOperatorNot inst) return ConstantEvalResult::NotConstant; } +auto EvalConstantInst(Context& /*context*/, SemIR::UpdateInit /*inst*/) + -> ConstantEvalResult { + // TODO: Support folding together a ClassInit with an update that sets the + // vptr. + return ConstantEvalResult::TODO; +} + auto EvalConstantInst(Context& context, SemIR::ValueOfInitializer inst) -> ConstantEvalResult { // Values of value expressions and initializing expressions are represented in diff --git a/toolchain/check/testdata/as/partial.carbon b/toolchain/check/testdata/as/partial.carbon index c172e74c3598..d8dcf8886307 100644 --- a/toolchain/check/testdata/as/partial.carbon +++ b/toolchain/check/testdata/as/partial.carbon @@ -31,7 +31,7 @@ fn Use() { //@dump-sem-ir-end } -// --- fail_todo_remove_partial_in_init.carbon +// --- remove_partial_in_init.carbon library "[[@TEST_NAME]]"; @@ -41,30 +41,8 @@ fn Init() -> partial X; fn Use() { //@dump-sem-ir-begin - // TODO: This should be valid, and should initialize the vptr. The explicit `as` should probably not be necessary. - // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `partial X` to `X` [ConversionFailure] - // CHECK:STDERR: var unused i: X = Init(); - // CHECK:STDERR: ^~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+4]]:3: note: type `partial X` does not implement interface `Core.ImplicitAs(X)` [MissingImplInMemberAccessInContext] - // CHECK:STDERR: var unused i: X = Init(); - // CHECK:STDERR: ^~~~~~~~~~~~~~~ - // CHECK:STDERR: var unused i: X = Init(); - // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+7]]:21: error: cannot convert expression of type `partial X` to `X` with `as` [ConversionFailure] - // CHECK:STDERR: var unused j: X = Init() as X; - // CHECK:STDERR: ^~~~~~~~~~~ - // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+4]]:21: note: type `partial X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessInContext] - // CHECK:STDERR: var unused j: X = Init() as X; - // CHECK:STDERR: ^~~~~~~~~~~ - // CHECK:STDERR: var unused j: X = Init() as X; - // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+7]]:21: error: cannot convert expression of type `partial X` to `X` with `unsafe as` [ConversionFailure] - // CHECK:STDERR: var unused k: X = Init() unsafe as X; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+4]]:21: note: type `partial X` does not implement interface `Core.UnsafeAs(X)` [MissingImplInMemberAccessInContext] - // CHECK:STDERR: var unused k: X = Init() unsafe as X; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: var unused k: X = Init() unsafe as X; //@dump-sem-ir-end } @@ -221,7 +199,7 @@ fn Use() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_remove_partial_in_init.carbon +// CHECK:STDOUT: --- remove_partial_in_init.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] @@ -241,45 +219,48 @@ fn Use() { // CHECK:STDOUT: %i.var_patt: %pattern_type.05f = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %X = var %i.var_patt -// CHECK:STDOUT: %Init.ref.loc18: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] -// CHECK:STDOUT: %.loc18_26: ref %.4b5 = temporary_storage -// CHECK:STDOUT: %Init.call.loc18: init %.4b5 to %.loc18_26 = call %Init.ref.loc18() -// CHECK:STDOUT: %.loc18_3: %X = converted %Init.call.loc18, [concrete = ] -// CHECK:STDOUT: assign %i.var, -// CHECK:STDOUT: %X.ref.loc18: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %Init.ref.loc10: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc10_3.1: ref %X = splice_block %i.var {} +// CHECK:STDOUT: %Init.call.loc10: init %.4b5 to %.loc10_3.1 = call %Init.ref.loc10() +// CHECK:STDOUT: %.loc10_3.2: init %X = as_compatible %Init.call.loc10 +// CHECK:STDOUT: %.loc10_3.3: init %X = converted %Init.call.loc10, %.loc10_3.2 +// CHECK:STDOUT: assign %i.var, %.loc10_3.3 +// CHECK:STDOUT: %X.ref.loc10: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %i: ref %X = ref_binding i, %i.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %j.patt: %pattern_type.05f = ref_binding_pattern j [concrete] // CHECK:STDOUT: %j.var_patt: %pattern_type.05f = var_pattern %j.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %j.var: ref %X = var %j.var_patt -// CHECK:STDOUT: %Init.ref.loc26: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] -// CHECK:STDOUT: %.loc26_26: ref %.4b5 = temporary_storage -// CHECK:STDOUT: %Init.call.loc26: init %.4b5 to %.loc26_26 = call %Init.ref.loc26() -// CHECK:STDOUT: %X.ref.loc26_31: type = name_ref X, file.%X.decl [concrete = constants.%X] -// CHECK:STDOUT: %.loc26_28: %X = converted %Init.call.loc26, [concrete = ] -// CHECK:STDOUT: assign %j.var, -// CHECK:STDOUT: %X.ref.loc26_17: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %Init.ref.loc11: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc11_3: ref %X = splice_block %j.var {} +// CHECK:STDOUT: %Init.call.loc11: init %.4b5 to %.loc11_3 = call %Init.ref.loc11() +// CHECK:STDOUT: %X.ref.loc11_31: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc11_28.1: init %X = as_compatible %Init.call.loc11 +// CHECK:STDOUT: %.loc11_28.2: init %X = converted %Init.call.loc11, %.loc11_28.1 +// CHECK:STDOUT: assign %j.var, %.loc11_28.2 +// CHECK:STDOUT: %X.ref.loc11_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %j: ref %X = ref_binding j, %j.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %k.patt: %pattern_type.05f = ref_binding_pattern k [concrete] // CHECK:STDOUT: %k.var_patt: %pattern_type.05f = var_pattern %k.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %k.var: ref %X = var %k.var_patt -// CHECK:STDOUT: %Init.ref.loc34: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] -// CHECK:STDOUT: %.loc34_26: ref %.4b5 = temporary_storage -// CHECK:STDOUT: %Init.call.loc34: init %.4b5 to %.loc34_26 = call %Init.ref.loc34() -// CHECK:STDOUT: %X.ref.loc34_38: type = name_ref X, file.%X.decl [concrete = constants.%X] -// CHECK:STDOUT: %.loc34_35: %X = converted %Init.call.loc34, [concrete = ] -// CHECK:STDOUT: assign %k.var, -// CHECK:STDOUT: %X.ref.loc34_17: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %Init.ref.loc12: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc12_3: ref %X = splice_block %k.var {} +// CHECK:STDOUT: %Init.call.loc12: init %.4b5 to %.loc12_3 = call %Init.ref.loc12() +// CHECK:STDOUT: %X.ref.loc12_38: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc12_35.1: init %X = as_compatible %Init.call.loc12 +// CHECK:STDOUT: %.loc12_35.2: init %X = converted %Init.call.loc12, %.loc12_35.1 +// CHECK:STDOUT: assign %k.var, %.loc12_35.2 +// CHECK:STDOUT: %X.ref.loc12_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %k: ref %X = ref_binding k, %k.var -// CHECK:STDOUT: %DestroyOp.bound.loc34: = bound_method %k.var, constants.%DestroyOp -// CHECK:STDOUT: %DestroyOp.call.loc34: init %empty_tuple.type = call %DestroyOp.bound.loc34(%k.var) -// CHECK:STDOUT: %DestroyOp.bound.loc26: = bound_method %j.var, constants.%DestroyOp -// CHECK:STDOUT: %DestroyOp.call.loc26: init %empty_tuple.type = call %DestroyOp.bound.loc26(%j.var) -// CHECK:STDOUT: %DestroyOp.bound.loc18: = bound_method %i.var, constants.%DestroyOp -// CHECK:STDOUT: %DestroyOp.call.loc18: init %empty_tuple.type = call %DestroyOp.bound.loc18(%i.var) +// CHECK:STDOUT: %DestroyOp.bound.loc12: = bound_method %k.var, constants.%DestroyOp +// CHECK:STDOUT: %DestroyOp.call.loc12: init %empty_tuple.type = call %DestroyOp.bound.loc12(%k.var) +// CHECK:STDOUT: %DestroyOp.bound.loc11: = bound_method %j.var, constants.%DestroyOp +// CHECK:STDOUT: %DestroyOp.call.loc11: init %empty_tuple.type = call %DestroyOp.bound.loc11(%j.var) +// CHECK:STDOUT: %DestroyOp.bound.loc10: = bound_method %i.var, constants.%DestroyOp +// CHECK:STDOUT: %DestroyOp.call.loc10: init %empty_tuple.type = call %DestroyOp.bound.loc10(%i.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/abstract/fail_abstract.carbon b/toolchain/check/testdata/class/abstract/abstract.carbon similarity index 92% rename from toolchain/check/testdata/class/abstract/fail_abstract.carbon rename to toolchain/check/testdata/class/abstract/abstract.carbon index 26fe95e31a7f..4695478be5e4 100644 --- a/toolchain/check/testdata/class/abstract/fail_abstract.carbon +++ b/toolchain/check/testdata/class/abstract/abstract.carbon @@ -8,9 +8,9 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/abstract/fail_abstract.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/abstract/abstract.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/abstract/fail_abstract.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/abstract/abstract.carbon // --- fail_abstract_field.carbon library "[[@TEST_NAME]]"; @@ -119,7 +119,7 @@ fn Call(p: Abstract) { Param(p); } -// --- fail_todo_return_nonabstract_derived.carbon +// --- return_nonabstract_derived.carbon library "[[@TEST_NAME]]"; abstract class Abstract { @@ -132,14 +132,6 @@ class Derived { } fn Make() -> Derived { - // TODO: This should be valid, and should construct an instance of `partial Abstract` as the base. - // CHECK:STDERR: fail_todo_return_nonabstract_derived.carbon:[[@LINE+7]]:10: error: initialization of abstract type `Abstract` [AbstractTypeInInit] - // CHECK:STDERR: return {.base = {}, .d = {}}; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_todo_return_nonabstract_derived.carbon:[[@LINE-14]]:1: note: class was declared abstract here [ClassAbstractHere] - // CHECK:STDERR: abstract class Abstract { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: return {.base = {}, .d = {}}; } @@ -222,17 +214,10 @@ fn F() { // to the `Abstract` value since `Abstract` and `Derived` have pointer value // representations. // - // CHECK:STDERR: fail_todo_abstract_let_temporary.carbon:[[@LINE+14]]:28: error: initialization of abstract type `Abstract` [AbstractTypeInInit] - // CHECK:STDERR: let unused l: Abstract = {.base = {}} as Derived; - // CHECK:STDERR: ^~~~~~~~~~~~ - // CHECK:STDERR: fail_todo_abstract_let_temporary.carbon:[[@LINE-15]]:1: note: class was declared abstract here [ClassAbstractHere] - // CHECK:STDERR: abstract class Abstract { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: // CHECK:STDERR: fail_todo_abstract_let_temporary.carbon:[[@LINE+7]]:28: error: initialization of abstract type `Abstract` [AbstractTypeInInit] // CHECK:STDERR: let unused l: Abstract = {.base = {}} as Derived; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_todo_abstract_let_temporary.carbon:[[@LINE-22]]:1: note: class was declared abstract here [ClassAbstractHere] + // CHECK:STDERR: fail_todo_abstract_let_temporary.carbon:[[@LINE-15]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -575,7 +560,7 @@ fn CallReturnAbstract() { // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_return_nonabstract_derived.carbon +// CHECK:STDOUT: --- return_nonabstract_derived.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] @@ -583,7 +568,7 @@ fn CallReturnAbstract() { // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem.032: type = unbound_element_type %Derived, %Abstract [concrete] -// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Derived.elem.87e: type = unbound_element_type %Derived, %empty_struct_type [concrete] // CHECK:STDOUT: %struct_type.base.d.be5: type = struct_type {.base: %Abstract, .d: %empty_struct_type} [concrete] // CHECK:STDOUT: %complete_type.840: = complete_type_witness %struct_type.base.d.be5 [concrete] @@ -592,7 +577,11 @@ fn CallReturnAbstract() { // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.d.e0f: type = struct_type {.base: %empty_struct_type, .d: %empty_struct_type} [concrete] -// CHECK:STDOUT: %struct: %struct_type.base.d.e0f = struct_value (%empty_struct, %empty_struct) [concrete] +// CHECK:STDOUT: %struct: %struct_type.base.d.e0f = struct_value (%empty_struct.a40, %empty_struct.a40) [concrete] +// CHECK:STDOUT: %.ec6: type = partial_type %Abstract [concrete] +// CHECK:STDOUT: %empty_struct.8eb: %.ec6 = struct_value () [concrete] +// CHECK:STDOUT: %Abstract.val: %Abstract = struct_value () [concrete] +// CHECK:STDOUT: %Derived.val: %Derived = struct_value (%Abstract.val, %empty_struct.a40) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -634,7 +623,7 @@ fn CallReturnAbstract() { // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc7: %Derived.elem.032 = base_decl %Abstract.ref, element0 [concrete] -// CHECK:STDOUT: %.loc9_11.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc9_11.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc9_11.2: type = converted %.loc9_11.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc9_8: %Derived.elem.87e = field_decl d, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d.be5 [concrete = constants.%complete_type.840] @@ -650,10 +639,19 @@ fn CallReturnAbstract() { // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> out %return.param: %Derived { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc21_20: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc21_29: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc21_30: %struct_type.base.d.e0f = struct_literal (%.loc21_20, %.loc21_29) [concrete = constants.%struct] -// CHECK:STDOUT: return to %return.param +// CHECK:STDOUT: %.loc13_20.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] +// CHECK:STDOUT: %.loc13_29.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] +// CHECK:STDOUT: %.loc13_30.1: %struct_type.base.d.e0f = struct_literal (%.loc13_20.1, %.loc13_29.1) [concrete = constants.%struct] +// CHECK:STDOUT: %.loc13_30.2: ref %.ec6 = class_element_access %return.param, element0 +// CHECK:STDOUT: %.loc13_20.2: init %.ec6 to %.loc13_30.2 = class_init () [concrete = constants.%empty_struct.8eb] +// CHECK:STDOUT: %.loc13_30.3: init %.ec6 = converted %.loc13_20.1, %.loc13_20.2 [concrete = constants.%empty_struct.8eb] +// CHECK:STDOUT: %.loc13_30.4: init %Abstract = as_compatible %.loc13_30.3 [concrete = constants.%Abstract.val] +// CHECK:STDOUT: %.loc13_30.5: ref %empty_struct_type = class_element_access %return.param, element1 +// CHECK:STDOUT: %.loc13_29.2: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct.a40] +// CHECK:STDOUT: %.loc13_30.6: init %empty_struct_type = converted %.loc13_29.1, %.loc13_29.2 [concrete = constants.%empty_struct.a40] +// CHECK:STDOUT: %.loc13_30.7: init %Derived to %return.param = class_init (%.loc13_30.4, %.loc13_30.6) [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc13_31: init %Derived = converted %.loc13_30.1, %.loc13_30.7 [concrete = constants.%Derived.val] +// CHECK:STDOUT: return %.loc13_31 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_return_abstract.carbon @@ -894,9 +892,13 @@ fn CallReturnAbstract() { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.a2e: type = pattern_type %Abstract [concrete] -// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] -// CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] +// CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct.a40) [concrete] +// CHECK:STDOUT: %.ec6: type = partial_type %Abstract [concrete] +// CHECK:STDOUT: %empty_struct.8eb: %.ec6 = struct_value () [concrete] +// CHECK:STDOUT: %Abstract.val: %Abstract = struct_value () [concrete] +// CHECK:STDOUT: %Derived.val: %Derived = struct_value (%Abstract.val) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete] // CHECK:STDOUT: %DestroyOp: %DestroyOp.type = struct_value () [concrete] @@ -950,16 +952,21 @@ fn CallReturnAbstract() { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %l.patt: %pattern_type.a2e = value_binding_pattern l [concrete] // CHECK:STDOUT: } -// CHECK:STDOUT: %.loc29_38: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc29_39.1: %struct_type.base.f5e = struct_literal (%.loc29_38) [concrete = constants.%struct] +// CHECK:STDOUT: %.loc22_38.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] +// CHECK:STDOUT: %.loc22_39.1: %struct_type.base.f5e = struct_literal (%.loc22_38.1) [concrete = constants.%struct] // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] -// CHECK:STDOUT: %.loc29_39.2: ref %Derived = temporary_storage -// CHECK:STDOUT: %.loc29_39.3: ref %Derived = temporary %.loc29_39.2, -// CHECK:STDOUT: %.loc29_41: ref %Derived = converted %.loc29_39.1, %.loc29_39.3 +// CHECK:STDOUT: %.loc22_39.2: ref %Derived = temporary_storage +// CHECK:STDOUT: %.loc22_39.3: ref %.ec6 = class_element_access %.loc22_39.2, element0 +// CHECK:STDOUT: %.loc22_38.2: init %.ec6 to %.loc22_39.3 = class_init () [concrete = constants.%empty_struct.8eb] +// CHECK:STDOUT: %.loc22_39.4: init %.ec6 = converted %.loc22_38.1, %.loc22_38.2 [concrete = constants.%empty_struct.8eb] +// CHECK:STDOUT: %.loc22_39.5: init %Abstract = as_compatible %.loc22_39.4 [concrete = constants.%Abstract.val] +// CHECK:STDOUT: %.loc22_39.6: init %Derived to %.loc22_39.2 = class_init (%.loc22_39.5) [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc22_39.7: ref %Derived = temporary %.loc22_39.2, %.loc22_39.6 +// CHECK:STDOUT: %.loc22_41: ref %Derived = converted %.loc22_39.1, %.loc22_39.7 // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %l: %Abstract = value_binding l, [concrete = ] -// CHECK:STDOUT: %DestroyOp.bound: = bound_method %.loc29_39.3, constants.%DestroyOp -// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc29_39.3) +// CHECK:STDOUT: %DestroyOp.bound: = bound_method %.loc22_39.7, constants.%DestroyOp +// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc22_39.7) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/inheritance/base.carbon b/toolchain/check/testdata/class/inheritance/base.carbon index ef63cc10f9ac..092cc4e79d87 100644 --- a/toolchain/check/testdata/class/inheritance/base.carbon +++ b/toolchain/check/testdata/class/inheritance/base.carbon @@ -76,6 +76,7 @@ class Derived { // CHECK:STDOUT: %int_7.29f: Core.IntLiteral = int_value 7 [concrete] // CHECK:STDOUT: %struct_type.base.d.a20: type = struct_type {.base: %struct_type.b.a15, .d: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.ab7: %struct_type.base.d.a20 = struct_value (%struct.a2e, %int_7.29f) [concrete] +// CHECK:STDOUT: %.dfa: type = partial_type %Base [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] @@ -92,6 +93,7 @@ class Derived { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.6d7: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] +// CHECK:STDOUT: %struct.5f8: %.dfa = struct_value (%int_4.940) [concrete] // CHECK:STDOUT: %Base.val: %Base = struct_value (%int_4.940) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1e0: = bound_method %int_7.29f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.bf2: = bound_method %int_7.29f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] @@ -211,21 +213,22 @@ class Derived { // CHECK:STDOUT: %bound_method.loc14_26.2: = bound_method %int_4, %specific_fn.loc14_26 [concrete = constants.%bound_method.6d7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_26: init %i32 = call %bound_method.loc14_26.2(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc14_26.2: init %i32 = converted %int_4, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_26 [concrete = constants.%int_4.940] -// CHECK:STDOUT: %.loc14_35.2: ref %Base = class_element_access %return.param, element0 +// CHECK:STDOUT: %.loc14_35.2: ref %.dfa = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc14_26.3: ref %i32 = class_element_access %.loc14_35.2, element0 // CHECK:STDOUT: %.loc14_26.4: init %i32 to %.loc14_26.3 = in_place_init %.loc14_26.2 [concrete = constants.%int_4.940] -// CHECK:STDOUT: %.loc14_26.5: init %Base to %.loc14_35.2 = class_init (%.loc14_26.4) [concrete = constants.%Base.val] -// CHECK:STDOUT: %.loc14_35.3: init %Base = converted %.loc14_26.1, %.loc14_26.5 [concrete = constants.%Base.val] +// CHECK:STDOUT: %.loc14_26.5: init %.dfa to %.loc14_35.2 = class_init (%.loc14_26.4) [concrete = constants.%struct.5f8] +// CHECK:STDOUT: %.loc14_35.3: init %.dfa = converted %.loc14_26.1, %.loc14_26.5 [concrete = constants.%struct.5f8] +// CHECK:STDOUT: %.loc14_35.4: init %Base = as_compatible %.loc14_35.3 [concrete = constants.%Base.val] // CHECK:STDOUT: %impl.elem0.loc14_35: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_35.1: = bound_method %int_7, %impl.elem0.loc14_35 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1e0] // CHECK:STDOUT: %specific_fn.loc14_35: = specific_function %impl.elem0.loc14_35, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_35.2: = bound_method %int_7, %specific_fn.loc14_35 [concrete = constants.%bound_method.bf2] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_35: init %i32 = call %bound_method.loc14_35.2(%int_7) [concrete = constants.%int_7.0b1] -// CHECK:STDOUT: %.loc14_35.4: init %i32 = converted %int_7, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_35 [concrete = constants.%int_7.0b1] -// CHECK:STDOUT: %.loc14_35.5: ref %i32 = class_element_access %return.param, element1 -// CHECK:STDOUT: %.loc14_35.6: init %i32 to %.loc14_35.5 = in_place_init %.loc14_35.4 [concrete = constants.%int_7.0b1] -// CHECK:STDOUT: %.loc14_35.7: init %Derived to %return.param = class_init (%.loc14_35.3, %.loc14_35.6) [concrete = constants.%Derived.val] -// CHECK:STDOUT: %.loc14_36: init %Derived = converted %.loc14_35.1, %.loc14_35.7 [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc14_35.5: init %i32 = converted %int_7, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_35 [concrete = constants.%int_7.0b1] +// CHECK:STDOUT: %.loc14_35.6: ref %i32 = class_element_access %return.param, element1 +// CHECK:STDOUT: %.loc14_35.7: init %i32 to %.loc14_35.6 = in_place_init %.loc14_35.5 [concrete = constants.%int_7.0b1] +// CHECK:STDOUT: %.loc14_35.8: init %Derived to %return.param = class_init (%.loc14_35.4, %.loc14_35.7) [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc14_36: init %Derived = converted %.loc14_35.1, %.loc14_35.8 [concrete = constants.%Derived.val] // CHECK:STDOUT: return %.loc14_36 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/inheritance/derived_to_base.carbon b/toolchain/check/testdata/class/inheritance/derived_to_base.carbon index e6a03d48a664..5b07d681df88 100644 --- a/toolchain/check/testdata/class/inheritance/derived_to_base.carbon +++ b/toolchain/check/testdata/class/inheritance/derived_to_base.carbon @@ -174,6 +174,8 @@ fn PassConstB(p: const B) { // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %struct_type.base.c.136: type = struct_type {.base: %struct_type.base.b.bf0, .c: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.2aa: %struct_type.base.c.136 = struct_value (%struct.ff9, %int_3.1ba) [concrete] +// CHECK:STDOUT: %.eaa: type = partial_type %B [concrete] +// CHECK:STDOUT: %.157: type = partial_type %A [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] @@ -188,10 +190,12 @@ fn PassConstB(p: const B) { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: %struct.e01: %.157 = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %A.val: %A = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] +// CHECK:STDOUT: %struct.bc2: %.eaa = struct_value (%A.val, %int_2.ef8) [concrete] // CHECK:STDOUT: %B.val: %B = struct_value (%A.val, %int_2.ef8) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] @@ -389,41 +393,43 @@ fn PassConstB(p: const B) { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_46: init %i32 = call %bound_method.loc32_46.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc32_46.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_46 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc32_64.2: ref %C = temporary_storage -// CHECK:STDOUT: %.loc32_64.3: ref %B = class_element_access %.loc32_64.2, element0 -// CHECK:STDOUT: %.loc32_55.2: ref %A = class_element_access %.loc32_64.3, element0 +// CHECK:STDOUT: %.loc32_64.3: ref %.eaa = class_element_access %.loc32_64.2, element0 +// CHECK:STDOUT: %.loc32_55.2: ref %.157 = class_element_access %.loc32_64.3, element0 // CHECK:STDOUT: %.loc32_46.3: ref %i32 = class_element_access %.loc32_55.2, element0 // CHECK:STDOUT: %.loc32_46.4: init %i32 to %.loc32_46.3 = in_place_init %.loc32_46.2 [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: %.loc32_46.5: init %A to %.loc32_55.2 = class_init (%.loc32_46.4) [concrete = constants.%A.val] -// CHECK:STDOUT: %.loc32_55.3: init %A = converted %.loc32_46.1, %.loc32_46.5 [concrete = constants.%A.val] +// CHECK:STDOUT: %.loc32_46.5: init %.157 to %.loc32_55.2 = class_init (%.loc32_46.4) [concrete = constants.%struct.e01] +// CHECK:STDOUT: %.loc32_55.3: init %.157 = converted %.loc32_46.1, %.loc32_46.5 [concrete = constants.%struct.e01] +// CHECK:STDOUT: %.loc32_55.4: init %A = as_compatible %.loc32_55.3 [concrete = constants.%A.val] // CHECK:STDOUT: %impl.elem0.loc32_55: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc32_55.1: = bound_method %int_2, %impl.elem0.loc32_55 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc32_55: = specific_function %impl.elem0.loc32_55, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc32_55.2: = bound_method %int_2, %specific_fn.loc32_55 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_55: init %i32 = call %bound_method.loc32_55.2(%int_2) [concrete = constants.%int_2.ef8] -// CHECK:STDOUT: %.loc32_55.4: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_55 [concrete = constants.%int_2.ef8] -// CHECK:STDOUT: %.loc32_55.5: ref %i32 = class_element_access %.loc32_64.3, element1 -// CHECK:STDOUT: %.loc32_55.6: init %i32 to %.loc32_55.5 = in_place_init %.loc32_55.4 [concrete = constants.%int_2.ef8] -// CHECK:STDOUT: %.loc32_55.7: init %B to %.loc32_64.3 = class_init (%.loc32_55.3, %.loc32_55.6) [concrete = constants.%B.val] -// CHECK:STDOUT: %.loc32_64.4: init %B = converted %.loc32_55.1, %.loc32_55.7 [concrete = constants.%B.val] +// CHECK:STDOUT: %.loc32_55.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_55 [concrete = constants.%int_2.ef8] +// CHECK:STDOUT: %.loc32_55.6: ref %i32 = class_element_access %.loc32_64.3, element1 +// CHECK:STDOUT: %.loc32_55.7: init %i32 to %.loc32_55.6 = in_place_init %.loc32_55.5 [concrete = constants.%int_2.ef8] +// CHECK:STDOUT: %.loc32_55.8: init %.eaa to %.loc32_64.3 = class_init (%.loc32_55.4, %.loc32_55.7) [concrete = constants.%struct.bc2] +// CHECK:STDOUT: %.loc32_64.4: init %.eaa = converted %.loc32_55.1, %.loc32_55.8 [concrete = constants.%struct.bc2] +// CHECK:STDOUT: %.loc32_64.5: init %B = as_compatible %.loc32_64.4 [concrete = constants.%B.val] // CHECK:STDOUT: %impl.elem0.loc32_64: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc32_64.1: = bound_method %int_3, %impl.elem0.loc32_64 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc32_64: = specific_function %impl.elem0.loc32_64, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc32_64.2: = bound_method %int_3, %specific_fn.loc32_64 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_64: init %i32 = call %bound_method.loc32_64.2(%int_3) [concrete = constants.%int_3.822] -// CHECK:STDOUT: %.loc32_64.5: init %i32 = converted %int_3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_64 [concrete = constants.%int_3.822] -// CHECK:STDOUT: %.loc32_64.6: ref %i32 = class_element_access %.loc32_64.2, element1 -// CHECK:STDOUT: %.loc32_64.7: init %i32 to %.loc32_64.6 = in_place_init %.loc32_64.5 [concrete = constants.%int_3.822] -// CHECK:STDOUT: %.loc32_64.8: init %C to %.loc32_64.2 = class_init (%.loc32_64.4, %.loc32_64.7) [concrete = constants.%C.val] -// CHECK:STDOUT: %.loc32_64.9: ref %C = temporary %.loc32_64.2, %.loc32_64.8 -// CHECK:STDOUT: %.loc32_66.1: ref %C = converted %.loc32_64.1, %.loc32_64.9 +// CHECK:STDOUT: %.loc32_64.6: init %i32 = converted %int_3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_64 [concrete = constants.%int_3.822] +// CHECK:STDOUT: %.loc32_64.7: ref %i32 = class_element_access %.loc32_64.2, element1 +// CHECK:STDOUT: %.loc32_64.8: init %i32 to %.loc32_64.7 = in_place_init %.loc32_64.6 [concrete = constants.%int_3.822] +// CHECK:STDOUT: %.loc32_64.9: init %C to %.loc32_64.2 = class_init (%.loc32_64.5, %.loc32_64.8) [concrete = constants.%C.val] +// CHECK:STDOUT: %.loc32_64.10: ref %C = temporary %.loc32_64.2, %.loc32_64.9 +// CHECK:STDOUT: %.loc32_66.1: ref %C = converted %.loc32_64.1, %.loc32_64.10 // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc32_66.2: ref %B = class_element_access %.loc32_66.1, element0 // CHECK:STDOUT: %.loc32_66.3: ref %A = class_element_access %.loc32_66.2, element0 // CHECK:STDOUT: %.loc32_66.4: ref %A = converted %.loc32_66.1, %.loc32_66.3 // CHECK:STDOUT: %.loc32_66.5: %A = acquire_value %.loc32_66.4 // CHECK:STDOUT: %a: %A = value_binding a, %.loc32_66.5 -// CHECK:STDOUT: %DestroyOp.bound: = bound_method %.loc32_64.9, constants.%DestroyOp -// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc32_64.9) +// CHECK:STDOUT: %DestroyOp.bound: = bound_method %.loc32_64.10, constants.%DestroyOp +// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc32_64.10) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/inheritance/import_base.carbon b/toolchain/check/testdata/class/inheritance/import_base.carbon index fa9e293226fe..0fdaf772aa1f 100644 --- a/toolchain/check/testdata/class/inheritance/import_base.carbon +++ b/toolchain/check/testdata/class/inheritance/import_base.carbon @@ -152,6 +152,7 @@ fn Run() { // CHECK:STDOUT: %struct.0bf: %struct_type.x.unused_y.76a = struct_value (%int_0.5c6, %int_1.5b8) [concrete] // CHECK:STDOUT: %struct_type.base.503: type = struct_type {.base: %struct_type.x.unused_y.76a} [concrete] // CHECK:STDOUT: %struct.cb5: %struct_type.base.503 = struct_value (%struct.0bf) [concrete] +// CHECK:STDOUT: %.7a5: type = partial_type %Base [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cf3: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] @@ -171,6 +172,7 @@ fn Run() { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f07: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %bound_method.307: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.47b: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: %struct.f56: %.7a5 = struct_value (%int_0.263, %int_1.47b) [concrete] // CHECK:STDOUT: %Base.val: %Base = struct_value (%int_0.263, %int_1.47b) [concrete] // CHECK:STDOUT: %Child.val: %Child = struct_value (%Base.val) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] @@ -262,7 +264,7 @@ fn Run() { // CHECK:STDOUT: %bound_method.loc7_49.2: = bound_method %int_0, %specific_fn.loc7_49.1 [concrete = constants.%bound_method.9f1] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_49.1: init %i32 = call %bound_method.loc7_49.2(%int_0) [concrete = constants.%int_0.263] // CHECK:STDOUT: %.loc7_49.2: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_49.1 [concrete = constants.%int_0.263] -// CHECK:STDOUT: %.loc7_50.2: ref %Base = class_element_access %a.var, element0 +// CHECK:STDOUT: %.loc7_50.2: ref %.7a5 = class_element_access %a.var, element0 // CHECK:STDOUT: %.loc7_49.3: ref %i32 = class_element_access %.loc7_50.2, element0 // CHECK:STDOUT: %.loc7_49.4: init %i32 to %.loc7_49.3 = in_place_init %.loc7_49.2 [concrete = constants.%int_0.263] // CHECK:STDOUT: %impl.elem0.loc7_49.2: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] @@ -273,10 +275,11 @@ fn Run() { // CHECK:STDOUT: %.loc7_49.5: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_49.2 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc7_49.6: ref %i32 = class_element_access %.loc7_50.2, element1 // CHECK:STDOUT: %.loc7_49.7: init %i32 to %.loc7_49.6 = in_place_init %.loc7_49.5 [concrete = constants.%int_1.47b] -// CHECK:STDOUT: %.loc7_49.8: init %Base to %.loc7_50.2 = class_init (%.loc7_49.4, %.loc7_49.7) [concrete = constants.%Base.val] -// CHECK:STDOUT: %.loc7_50.3: init %Base = converted %.loc7_49.1, %.loc7_49.8 [concrete = constants.%Base.val] -// CHECK:STDOUT: %.loc7_50.4: init %Child to %a.var = class_init (%.loc7_50.3) [concrete = constants.%Child.val] -// CHECK:STDOUT: %.loc7_3: init %Child = converted %.loc7_50.1, %.loc7_50.4 [concrete = constants.%Child.val] +// CHECK:STDOUT: %.loc7_49.8: init %.7a5 to %.loc7_50.2 = class_init (%.loc7_49.4, %.loc7_49.7) [concrete = constants.%struct.f56] +// CHECK:STDOUT: %.loc7_50.3: init %.7a5 = converted %.loc7_49.1, %.loc7_49.8 [concrete = constants.%struct.f56] +// CHECK:STDOUT: %.loc7_50.4: init %Base = as_compatible %.loc7_50.3 [concrete = constants.%Base.val] +// CHECK:STDOUT: %.loc7_50.5: init %Child to %a.var = class_init (%.loc7_50.4) [concrete = constants.%Child.val] +// CHECK:STDOUT: %.loc7_3: init %Child = converted %.loc7_50.1, %.loc7_50.5 [concrete = constants.%Child.val] // CHECK:STDOUT: assign %a.var, %.loc7_3 // CHECK:STDOUT: %Child.ref: type = name_ref Child, imports.%Main.Child [concrete = constants.%Child] // CHECK:STDOUT: %a: ref %Child = ref_binding a, %a.var diff --git a/toolchain/check/testdata/class/partial/init.carbon b/toolchain/check/testdata/class/partial/init.carbon new file mode 100644 index 000000000000..a20d5aa171eb --- /dev/null +++ b/toolchain/check/testdata/class/partial/init.carbon @@ -0,0 +1,67 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/partial/init.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/partial/init.carbon + +// --- base.carbon + +library "[[@TEST_NAME]]"; + +base class Base { + fn Make() -> partial Self { + return {}; + } +} + +class Derived { + extend base: Base; + fn Make() -> Self { + //@dump-sem-ir-begin + return {.base = Base.Make()}; + //@dump-sem-ir-end + } +} + +// CHECK:STDOUT: --- base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Base: type = class_type @Base [concrete] +// CHECK:STDOUT: %.7a5: type = partial_type %Base [concrete] +// CHECK:STDOUT: %Base.Make.type: type = fn_type @Base.Make [concrete] +// CHECK:STDOUT: %Base.Make: %Base.Make.type = struct_value () [concrete] +// CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] +// CHECK:STDOUT: %struct_type.base.cad: type = struct_type {.base: %.7a5} [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Derived { +// CHECK:STDOUT: +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Derived +// CHECK:STDOUT: .Base = +// CHECK:STDOUT: .base = %.loc11 +// CHECK:STDOUT: .Make = %Derived.Make.decl +// CHECK:STDOUT: extend %Base.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Derived.Make() -> out %return.param: %Derived { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] +// CHECK:STDOUT: %Make.ref: %Base.Make.type = name_ref Make, @Base.%Base.Make.decl [concrete = constants.%Base.Make] +// CHECK:STDOUT: %.loc14_32.1: ref %.7a5 = class_element_access %return.param, element0 +// CHECK:STDOUT: %Base.Make.call: init %.7a5 to %.loc14_32.1 = call %Make.ref() +// CHECK:STDOUT: %.loc14_32.2: %struct_type.base.cad = struct_literal (%Base.Make.call) +// CHECK:STDOUT: %.loc14_32.3: init %Base = as_compatible %Base.Make.call +// CHECK:STDOUT: %.loc14_32.4: init %Derived to %return.param = class_init (%.loc14_32.3) +// CHECK:STDOUT: %.loc14_33: init %Derived = converted %.loc14_32.2, %.loc14_32.4 +// CHECK:STDOUT: return %.loc14_33 to %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/virtual_modifiers.carbon b/toolchain/check/testdata/class/virtual_modifiers.carbon index 338e4e01275b..f9e523816ec6 100644 --- a/toolchain/check/testdata/class/virtual_modifiers.carbon +++ b/toolchain/check/testdata/class/virtual_modifiers.carbon @@ -640,10 +640,13 @@ class T2(G2:! type) { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] -// CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] -// CHECK:STDOUT: %Derived.vtable_ptr: ref %ptr.454 = vtable_ptr @Derived.vtable [concrete] -// CHECK:STDOUT: %Base.val: %Base = struct_value (%Derived.vtable_ptr) [concrete] +// CHECK:STDOUT: %struct.6b1: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] +// CHECK:STDOUT: %.a5d: type = partial_type %Base [concrete] +// CHECK:STDOUT: %uninit: %ptr.454 = uninitialized_value [concrete] +// CHECK:STDOUT: %struct.4dc: %.a5d = struct_value (%uninit) [concrete] +// CHECK:STDOUT: %Base.val: %Base = struct_value (%uninit) [concrete] // CHECK:STDOUT: %Derived.val: %Derived = struct_value (%Base.val) [concrete] +// CHECK:STDOUT: %Derived.vtable_ptr: ref %ptr.454 = vtable_ptr @Derived.vtable [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete] // CHECK:STDOUT: %DestroyOp: %DestroyOp.type = struct_value () [concrete] @@ -734,15 +737,21 @@ class T2(G2:! type) { // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %Derived = var %d.var_patt // CHECK:STDOUT: %.loc12_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc12_38.1: %struct_type.base.f5e = struct_literal (%.loc12_37.1) [concrete = constants.%struct] -// CHECK:STDOUT: %.loc12_38.2: ref %Base = class_element_access %d.var, element0 +// CHECK:STDOUT: %.loc12_38.1: %struct_type.base.f5e = struct_literal (%.loc12_37.1) [concrete = constants.%struct.6b1] +// CHECK:STDOUT: %.loc12_38.2: ref %.a5d = class_element_access %d.var, element0 // CHECK:STDOUT: %.loc12_37.2: ref %ptr.454 = class_element_access %.loc12_38.2, element0 +// CHECK:STDOUT: %uninit: %ptr.454 = uninitialized_value [concrete = constants.%uninit] +// CHECK:STDOUT: %.loc12_37.3: init %ptr.454 to %.loc12_37.2 = in_place_init %uninit [concrete = constants.%uninit] +// CHECK:STDOUT: %.loc12_37.4: init %.a5d to %.loc12_38.2 = class_init (%.loc12_37.3) [concrete = constants.%struct.4dc] +// CHECK:STDOUT: %.loc12_38.3: init %.a5d = converted %.loc12_37.1, %.loc12_37.4 [concrete = constants.%struct.4dc] +// CHECK:STDOUT: %.loc12_38.4: init %Base = as_compatible %.loc12_38.3 [concrete = constants.%Base.val] +// CHECK:STDOUT: %.loc12_38.5: init %Derived to %d.var = class_init (%.loc12_38.4) [concrete = constants.%Derived.val] // CHECK:STDOUT: %Derived.vtable_ptr: ref %ptr.454 = vtable_ptr @Derived.vtable [concrete = constants.%Derived.vtable_ptr] -// CHECK:STDOUT: %.loc12_37.3: init %ptr.454 to %.loc12_37.2 = in_place_init %Derived.vtable_ptr [concrete = constants.%Derived.vtable_ptr] -// CHECK:STDOUT: %.loc12_37.4: init %Base to %.loc12_38.2 = class_init (%.loc12_37.3) [concrete = constants.%Base.val] -// CHECK:STDOUT: %.loc12_38.3: init %Base = converted %.loc12_37.1, %.loc12_37.4 [concrete = constants.%Base.val] -// CHECK:STDOUT: %.loc12_38.4: init %Derived to %d.var = class_init (%.loc12_38.3) [concrete = constants.%Derived.val] -// CHECK:STDOUT: %.loc12_3: init %Derived = converted %.loc12_38.1, %.loc12_38.4 [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc12_38.6: ref %Base = class_element_access %d.var, element0 +// CHECK:STDOUT: %.loc12_38.7: ref %ptr.454 = class_element_access %.loc12_38.6, element0 +// CHECK:STDOUT: %.loc12_38.8: init %ptr.454 to %.loc12_38.7 = in_place_init %Derived.vtable_ptr [concrete = constants.%Derived.vtable_ptr] +// CHECK:STDOUT: %.loc12_38.9: init %Derived = update_init %.loc12_38.5, %.loc12_38.8 +// CHECK:STDOUT: %.loc12_3: init %Derived = converted %.loc12_38.1, %.loc12_38.9 // CHECK:STDOUT: assign %d.var, %.loc12_3 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d: ref %Derived = ref_binding d, %d.var @@ -1070,15 +1079,18 @@ class T2(G2:! type) { // CHECK:STDOUT: %B1.val.d17: %B1 = struct_value (%B1.vtable_ptr) [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] // CHECK:STDOUT: %struct.6b1: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] +// CHECK:STDOUT: %.5b3: type = partial_type %B1 [concrete] +// CHECK:STDOUT: %uninit: %ptr.454 = uninitialized_value [concrete] +// CHECK:STDOUT: %struct.571: %.5b3 = struct_value (%uninit) [concrete] +// CHECK:STDOUT: %B1.val.fd5: %B1 = struct_value (%uninit) [concrete] +// CHECK:STDOUT: %B2.val: %B2 = struct_value (%B1.val.fd5) [concrete] // CHECK:STDOUT: %B2.vtable_ptr: ref %ptr.454 = vtable_ptr @B2.vtable [concrete] -// CHECK:STDOUT: %B1.val.f0b: %B1 = struct_value (%B2.vtable_ptr) [concrete] -// CHECK:STDOUT: %B2.val.e28: %B2 = struct_value (%B1.val.f0b) [concrete] // CHECK:STDOUT: %struct_type.base.a0c: type = struct_type {.base: %struct_type.base.f5e} [concrete] // CHECK:STDOUT: %struct.83e: %struct_type.base.a0c = struct_value (%struct.6b1) [concrete] +// CHECK:STDOUT: %.312: type = partial_type %B2 [concrete] +// CHECK:STDOUT: %struct.0cd: %.312 = struct_value (%B1.val.fd5) [concrete] +// CHECK:STDOUT: %C.val: %C = struct_value (%B2.val) [concrete] // CHECK:STDOUT: %C.vtable_ptr: ref %ptr.454 = vtable_ptr @C.vtable [concrete] -// CHECK:STDOUT: %B1.val.dd4: %B1 = struct_value (%C.vtable_ptr) [concrete] -// CHECK:STDOUT: %B2.val.b52: %B2 = struct_value (%B1.val.dd4) [concrete] -// CHECK:STDOUT: %C.val: %C = struct_value (%B2.val.b52) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %DestroyOp.type.3e79c2.1: type = fn_type @DestroyOp.loc21 [concrete] // CHECK:STDOUT: %DestroyOp.b0ebf8.1: %DestroyOp.type.3e79c2.1 = struct_value () [concrete] @@ -1220,14 +1232,20 @@ class T2(G2:! type) { // CHECK:STDOUT: %b2.var: ref %B2 = var %b2.var_patt // CHECK:STDOUT: %.loc20_33.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc20_34.1: %struct_type.base.f5e = struct_literal (%.loc20_33.1) [concrete = constants.%struct.6b1] -// CHECK:STDOUT: %.loc20_34.2: ref %B1 = class_element_access %b2.var, element0 +// CHECK:STDOUT: %.loc20_34.2: ref %.5b3 = class_element_access %b2.var, element0 // CHECK:STDOUT: %.loc20_33.2: ref %ptr.454 = class_element_access %.loc20_34.2, element0 +// CHECK:STDOUT: %uninit.loc20: %ptr.454 = uninitialized_value [concrete = constants.%uninit] +// CHECK:STDOUT: %.loc20_33.3: init %ptr.454 to %.loc20_33.2 = in_place_init %uninit.loc20 [concrete = constants.%uninit] +// CHECK:STDOUT: %.loc20_33.4: init %.5b3 to %.loc20_34.2 = class_init (%.loc20_33.3) [concrete = constants.%struct.571] +// CHECK:STDOUT: %.loc20_34.3: init %.5b3 = converted %.loc20_33.1, %.loc20_33.4 [concrete = constants.%struct.571] +// CHECK:STDOUT: %.loc20_34.4: init %B1 = as_compatible %.loc20_34.3 [concrete = constants.%B1.val.fd5] +// CHECK:STDOUT: %.loc20_34.5: init %B2 to %b2.var = class_init (%.loc20_34.4) [concrete = constants.%B2.val] // CHECK:STDOUT: %B2.vtable_ptr: ref %ptr.454 = vtable_ptr @B2.vtable [concrete = constants.%B2.vtable_ptr] -// CHECK:STDOUT: %.loc20_33.3: init %ptr.454 to %.loc20_33.2 = in_place_init %B2.vtable_ptr [concrete = constants.%B2.vtable_ptr] -// CHECK:STDOUT: %.loc20_33.4: init %B1 to %.loc20_34.2 = class_init (%.loc20_33.3) [concrete = constants.%B1.val.f0b] -// CHECK:STDOUT: %.loc20_34.3: init %B1 = converted %.loc20_33.1, %.loc20_33.4 [concrete = constants.%B1.val.f0b] -// CHECK:STDOUT: %.loc20_34.4: init %B2 to %b2.var = class_init (%.loc20_34.3) [concrete = constants.%B2.val.e28] -// CHECK:STDOUT: %.loc20_3: init %B2 = converted %.loc20_34.1, %.loc20_34.4 [concrete = constants.%B2.val.e28] +// CHECK:STDOUT: %.loc20_34.6: ref %B1 = class_element_access %b2.var, element0 +// CHECK:STDOUT: %.loc20_34.7: ref %ptr.454 = class_element_access %.loc20_34.6, element0 +// CHECK:STDOUT: %.loc20_34.8: init %ptr.454 to %.loc20_34.7 = in_place_init %B2.vtable_ptr [concrete = constants.%B2.vtable_ptr] +// CHECK:STDOUT: %.loc20_34.9: init %B2 = update_init %.loc20_34.5, %.loc20_34.8 +// CHECK:STDOUT: %.loc20_3: init %B2 = converted %.loc20_34.1, %.loc20_34.9 // CHECK:STDOUT: assign %b2.var, %.loc20_3 // CHECK:STDOUT: %B2.ref: type = name_ref B2, file.%B2.decl [concrete = constants.%B2] // CHECK:STDOUT: %b2: ref %B2 = ref_binding b2, %b2.var @@ -1239,17 +1257,25 @@ class T2(G2:! type) { // CHECK:STDOUT: %.loc21_40.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc21_41.1: %struct_type.base.f5e = struct_literal (%.loc21_40.1) [concrete = constants.%struct.6b1] // CHECK:STDOUT: %.loc21_42.1: %struct_type.base.a0c = struct_literal (%.loc21_41.1) [concrete = constants.%struct.83e] -// CHECK:STDOUT: %.loc21_42.2: ref %B2 = class_element_access %c.var, element0 -// CHECK:STDOUT: %.loc21_41.2: ref %B1 = class_element_access %.loc21_42.2, element0 +// CHECK:STDOUT: %.loc21_42.2: ref %.312 = class_element_access %c.var, element0 +// CHECK:STDOUT: %.loc21_41.2: ref %.5b3 = class_element_access %.loc21_42.2, element0 // CHECK:STDOUT: %.loc21_40.2: ref %ptr.454 = class_element_access %.loc21_41.2, element0 +// CHECK:STDOUT: %uninit.loc21: %ptr.454 = uninitialized_value [concrete = constants.%uninit] +// CHECK:STDOUT: %.loc21_40.3: init %ptr.454 to %.loc21_40.2 = in_place_init %uninit.loc21 [concrete = constants.%uninit] +// CHECK:STDOUT: %.loc21_40.4: init %.5b3 to %.loc21_41.2 = class_init (%.loc21_40.3) [concrete = constants.%struct.571] +// CHECK:STDOUT: %.loc21_41.3: init %.5b3 = converted %.loc21_40.1, %.loc21_40.4 [concrete = constants.%struct.571] +// CHECK:STDOUT: %.loc21_41.4: init %B1 = as_compatible %.loc21_41.3 [concrete = constants.%B1.val.fd5] +// CHECK:STDOUT: %.loc21_41.5: init %.312 to %.loc21_42.2 = class_init (%.loc21_41.4) [concrete = constants.%struct.0cd] +// CHECK:STDOUT: %.loc21_42.3: init %.312 = converted %.loc21_41.1, %.loc21_41.5 [concrete = constants.%struct.0cd] +// CHECK:STDOUT: %.loc21_42.4: init %B2 = as_compatible %.loc21_42.3 [concrete = constants.%B2.val] +// CHECK:STDOUT: %.loc21_42.5: init %C to %c.var = class_init (%.loc21_42.4) [concrete = constants.%C.val] // CHECK:STDOUT: %C.vtable_ptr: ref %ptr.454 = vtable_ptr @C.vtable [concrete = constants.%C.vtable_ptr] -// CHECK:STDOUT: %.loc21_40.3: init %ptr.454 to %.loc21_40.2 = in_place_init %C.vtable_ptr [concrete = constants.%C.vtable_ptr] -// CHECK:STDOUT: %.loc21_40.4: init %B1 to %.loc21_41.2 = class_init (%.loc21_40.3) [concrete = constants.%B1.val.dd4] -// CHECK:STDOUT: %.loc21_41.3: init %B1 = converted %.loc21_40.1, %.loc21_40.4 [concrete = constants.%B1.val.dd4] -// CHECK:STDOUT: %.loc21_41.4: init %B2 to %.loc21_42.2 = class_init (%.loc21_41.3) [concrete = constants.%B2.val.b52] -// CHECK:STDOUT: %.loc21_42.3: init %B2 = converted %.loc21_41.1, %.loc21_41.4 [concrete = constants.%B2.val.b52] -// CHECK:STDOUT: %.loc21_42.4: init %C to %c.var = class_init (%.loc21_42.3) [concrete = constants.%C.val] -// CHECK:STDOUT: %.loc21_3: init %C = converted %.loc21_42.1, %.loc21_42.4 [concrete = constants.%C.val] +// CHECK:STDOUT: %.loc21_42.6: ref %B2 = class_element_access %c.var, element0 +// CHECK:STDOUT: %.loc21_42.7: ref %B1 = class_element_access %.loc21_42.6, element0 +// CHECK:STDOUT: %.loc21_42.8: ref %ptr.454 = class_element_access %.loc21_42.7, element0 +// CHECK:STDOUT: %.loc21_42.9: init %ptr.454 to %.loc21_42.8 = in_place_init %C.vtable_ptr [concrete = constants.%C.vtable_ptr] +// CHECK:STDOUT: %.loc21_42.10: init %C = update_init %.loc21_42.5, %.loc21_42.9 +// CHECK:STDOUT: %.loc21_3: init %C = converted %.loc21_42.1, %.loc21_42.10 // CHECK:STDOUT: assign %c.var, %.loc21_3 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var diff --git a/toolchain/check/testdata/interop/cpp/class/access.carbon b/toolchain/check/testdata/interop/cpp/class/access.carbon index b550e667d532..435a421d098a 100644 --- a/toolchain/check/testdata/interop/cpp/class/access.carbon +++ b/toolchain/check/testdata/interop/cpp/class/access.carbon @@ -2026,6 +2026,7 @@ fn Call(var instance: Cpp.PublicPrivate) { // CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete] // CHECK:STDOUT: %C__carbon_thunk.type.65f120.1: type = fn_type @C__carbon_thunk.1 [concrete] // CHECK:STDOUT: %C__carbon_thunk.d98342.1: %C__carbon_thunk.type.65f120.1 = struct_value () [concrete] +// CHECK:STDOUT: %.73a: type = partial_type %C [concrete] // CHECK:STDOUT: %PublicCall.cpp_destructor.type: type = fn_type @PublicCall.cpp_destructor [concrete] // CHECK:STDOUT: %PublicCall.cpp_destructor: %PublicCall.cpp_destructor.type = struct_value () [concrete] // CHECK:STDOUT: %ProtectedCall: type = class_type @ProtectedCall [concrete] @@ -2119,7 +2120,7 @@ fn Call(var instance: Cpp.PublicPrivate) { // CHECK:STDOUT: %addr.loc11_49: %ptr.dfa = addr_of %.loc11_49.1 // CHECK:STDOUT: %PublicCall__carbon_thunk.call: init %empty_tuple.type = call imports.%PublicCall__carbon_thunk.decl(%addr.loc11_49) // CHECK:STDOUT: %.loc11_49.2: init %PublicCall to %.loc11_49.1 = mark_in_place_init %PublicCall__carbon_thunk.call -// CHECK:STDOUT: %.loc11_51.1: ref %C = class_element_access %return.param, element0 +// CHECK:STDOUT: %.loc11_51.1: ref %.73a = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc11_49.3: ref %PublicCall = temporary %.loc11_49.1, %.loc11_49.2 // CHECK:STDOUT: %.loc11_49.4: %PublicCall = acquire_value %.loc11_49.3 // CHECK:STDOUT: %.loc11_49.5: ref %PublicCall = value_as_ref %.loc11_49.4 @@ -2128,8 +2129,11 @@ fn Call(var instance: Cpp.PublicPrivate) { // CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.1(%addr.loc11_50.1, %addr.loc11_50.2) // CHECK:STDOUT: %.loc11_50: init %C to %.loc11_51.1 = mark_in_place_init %C__carbon_thunk.call // CHECK:STDOUT: %.loc11_51.2: %struct_type.base.7c3 = struct_literal (%.loc11_50) -// CHECK:STDOUT: %.loc11_51.3: init %D to %return.param = class_init (%.loc11_50) -// CHECK:STDOUT: %.loc11_52: init %D = converted %.loc11_51.2, %.loc11_51.3 +// CHECK:STDOUT: %.loc11_51.3: init %.73a = as_compatible %.loc11_50 +// CHECK:STDOUT: %.loc11_51.4: init %.73a = converted %.loc11_50, %.loc11_51.3 +// CHECK:STDOUT: %.loc11_51.5: init %C = as_compatible %.loc11_51.4 +// CHECK:STDOUT: %.loc11_51.6: init %D to %return.param = class_init (%.loc11_51.5) +// CHECK:STDOUT: %.loc11_52: init %D = converted %.loc11_51.2, %.loc11_51.6 // CHECK:STDOUT: %PublicCall.cpp_destructor.bound: = bound_method %.loc11_49.3, constants.%PublicCall.cpp_destructor // CHECK:STDOUT: %PublicCall.cpp_destructor.call: init %empty_tuple.type = call %PublicCall.cpp_destructor.bound(%.loc11_49.3) // CHECK:STDOUT: return %.loc11_52 to %return.param @@ -2145,7 +2149,7 @@ fn Call(var instance: Cpp.PublicPrivate) { // CHECK:STDOUT: %addr.loc17_55: %ptr.8e6 = addr_of %.loc17_55.1 // CHECK:STDOUT: %ProtectedCall__carbon_thunk.call: init %empty_tuple.type = call imports.%ProtectedCall__carbon_thunk.decl(%addr.loc17_55) // CHECK:STDOUT: %.loc17_55.2: init %ProtectedCall to %.loc17_55.1 = mark_in_place_init %ProtectedCall__carbon_thunk.call -// CHECK:STDOUT: %.loc17_57.1: ref %C = class_element_access %return.param, element0 +// CHECK:STDOUT: %.loc17_57.1: ref %.73a = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc17_55.3: ref %ProtectedCall = temporary %.loc17_55.1, %.loc17_55.2 // CHECK:STDOUT: %.loc17_55.4: %ProtectedCall = acquire_value %.loc17_55.3 // CHECK:STDOUT: %.loc17_55.5: ref %ProtectedCall = value_as_ref %.loc17_55.4 @@ -2154,8 +2158,11 @@ fn Call(var instance: Cpp.PublicPrivate) { // CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.2(%addr.loc17_56.1, %addr.loc17_56.2) // CHECK:STDOUT: %.loc17_56: init %C to %.loc17_57.1 = mark_in_place_init %C__carbon_thunk.call // CHECK:STDOUT: %.loc17_57.2: %struct_type.base.7c3 = struct_literal (%.loc17_56) -// CHECK:STDOUT: %.loc17_57.3: init %D to %return.param = class_init (%.loc17_56) -// CHECK:STDOUT: %.loc17_58: init %D = converted %.loc17_57.2, %.loc17_57.3 +// CHECK:STDOUT: %.loc17_57.3: init %.73a = as_compatible %.loc17_56 +// CHECK:STDOUT: %.loc17_57.4: init %.73a = converted %.loc17_56, %.loc17_57.3 +// CHECK:STDOUT: %.loc17_57.5: init %C = as_compatible %.loc17_57.4 +// CHECK:STDOUT: %.loc17_57.6: init %D to %return.param = class_init (%.loc17_57.5) +// CHECK:STDOUT: %.loc17_58: init %D = converted %.loc17_57.2, %.loc17_57.6 // CHECK:STDOUT: %ProtectedCall.cpp_destructor.bound: = bound_method %.loc17_55.3, constants.%ProtectedCall.cpp_destructor // CHECK:STDOUT: %ProtectedCall.cpp_destructor.call: init %empty_tuple.type = call %ProtectedCall.cpp_destructor.bound(%.loc17_55.3) // CHECK:STDOUT: return %.loc17_58 to %return.param diff --git a/toolchain/check/testdata/interop/cpp/impls/destroy.carbon b/toolchain/check/testdata/interop/cpp/impls/destroy.carbon index 8575e3f02ec1..808ab7abf981 100644 --- a/toolchain/check/testdata/interop/cpp/impls/destroy.carbon +++ b/toolchain/check/testdata/interop/cpp/impls/destroy.carbon @@ -311,9 +311,11 @@ fn EqualWitnesses(p: Wrap(Cpp.PublicDestructor)*) -> Wrap(Cpp.PublicDestructor)* // CHECK:STDOUT: %ProtectedDestructor: type = class_type @ProtectedDestructor [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] -// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] -// CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] +// CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct.a40) [concrete] +// CHECK:STDOUT: %.ce2: type = partial_type %ProtectedDestructor [concrete] +// CHECK:STDOUT: %empty_struct.377: %.ce2 = struct_value () [concrete] // CHECK:STDOUT: %ProtectedDestructor.val: %ProtectedDestructor = struct_value () [concrete] // CHECK:STDOUT: %Derived.val: %Derived = struct_value (%ProtectedDestructor.val) [concrete] // CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete] @@ -327,13 +329,14 @@ fn EqualWitnesses(p: Wrap(Cpp.PublicDestructor)*) -> Wrap(Cpp.PublicDestructor)* // CHECK:STDOUT: %a.var_patt: %pattern_type.9f6 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %Derived = var %a.var_patt -// CHECK:STDOUT: %.loc12_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc12_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc12_38.1: %struct_type.base.f5e = struct_literal (%.loc12_37.1) [concrete = constants.%struct] -// CHECK:STDOUT: %.loc12_38.2: ref %ProtectedDestructor = class_element_access %a.var, element0 -// CHECK:STDOUT: %.loc12_37.2: init %ProtectedDestructor to %.loc12_38.2 = class_init () [concrete = constants.%ProtectedDestructor.val] -// CHECK:STDOUT: %.loc12_38.3: init %ProtectedDestructor = converted %.loc12_37.1, %.loc12_37.2 [concrete = constants.%ProtectedDestructor.val] -// CHECK:STDOUT: %.loc12_38.4: init %Derived to %a.var = class_init (%.loc12_38.3) [concrete = constants.%Derived.val] -// CHECK:STDOUT: %.loc12_3: init %Derived = converted %.loc12_38.1, %.loc12_38.4 [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc12_38.2: ref %.ce2 = class_element_access %a.var, element0 +// CHECK:STDOUT: %.loc12_37.2: init %.ce2 to %.loc12_38.2 = class_init () [concrete = constants.%empty_struct.377] +// CHECK:STDOUT: %.loc12_38.3: init %.ce2 = converted %.loc12_37.1, %.loc12_37.2 [concrete = constants.%empty_struct.377] +// CHECK:STDOUT: %.loc12_38.4: init %ProtectedDestructor = as_compatible %.loc12_38.3 [concrete = constants.%ProtectedDestructor.val] +// CHECK:STDOUT: %.loc12_38.5: init %Derived to %a.var = class_init (%.loc12_38.4) [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc12_3: init %Derived = converted %.loc12_38.1, %.loc12_38.5 [concrete = constants.%Derived.val] // CHECK:STDOUT: assign %a.var, %.loc12_3 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %a: ref %Derived = ref_binding a, %a.var @@ -352,9 +355,11 @@ fn EqualWitnesses(p: Wrap(Cpp.PublicDestructor)*) -> Wrap(Cpp.PublicDestructor)* // CHECK:STDOUT: %PrivateDestructor: type = class_type @PrivateDestructor [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] -// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] -// CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] +// CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct.a40) [concrete] +// CHECK:STDOUT: %.b20: type = partial_type %PrivateDestructor [concrete] +// CHECK:STDOUT: %empty_struct.361: %.b20 = struct_value () [concrete] // CHECK:STDOUT: %PrivateDestructor.val: %PrivateDestructor = struct_value () [concrete] // CHECK:STDOUT: %Derived.val: %Derived = struct_value (%PrivateDestructor.val) [concrete] // CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete] @@ -368,13 +373,14 @@ fn EqualWitnesses(p: Wrap(Cpp.PublicDestructor)*) -> Wrap(Cpp.PublicDestructor)* // CHECK:STDOUT: %a.var_patt: %pattern_type.9f6 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %Derived = var %a.var_patt -// CHECK:STDOUT: %.loc13_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc13_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc13_38.1: %struct_type.base.f5e = struct_literal (%.loc13_37.1) [concrete = constants.%struct] -// CHECK:STDOUT: %.loc13_38.2: ref %PrivateDestructor = class_element_access %a.var, element0 -// CHECK:STDOUT: %.loc13_37.2: init %PrivateDestructor to %.loc13_38.2 = class_init () [concrete = constants.%PrivateDestructor.val] -// CHECK:STDOUT: %.loc13_38.3: init %PrivateDestructor = converted %.loc13_37.1, %.loc13_37.2 [concrete = constants.%PrivateDestructor.val] -// CHECK:STDOUT: %.loc13_38.4: init %Derived to %a.var = class_init (%.loc13_38.3) [concrete = constants.%Derived.val] -// CHECK:STDOUT: %.loc13_3: init %Derived = converted %.loc13_38.1, %.loc13_38.4 [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc13_38.2: ref %.b20 = class_element_access %a.var, element0 +// CHECK:STDOUT: %.loc13_37.2: init %.b20 to %.loc13_38.2 = class_init () [concrete = constants.%empty_struct.361] +// CHECK:STDOUT: %.loc13_38.3: init %.b20 = converted %.loc13_37.1, %.loc13_37.2 [concrete = constants.%empty_struct.361] +// CHECK:STDOUT: %.loc13_38.4: init %PrivateDestructor = as_compatible %.loc13_38.3 [concrete = constants.%PrivateDestructor.val] +// CHECK:STDOUT: %.loc13_38.5: init %Derived to %a.var = class_init (%.loc13_38.4) [concrete = constants.%Derived.val] +// CHECK:STDOUT: %.loc13_3: init %Derived = converted %.loc13_38.1, %.loc13_38.5 [concrete = constants.%Derived.val] // CHECK:STDOUT: assign %a.var, %.loc13_3 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %a: ref %Derived = ref_binding a, %a.var diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 7b032f28b4da..6aeafa172c74 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -741,8 +741,13 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, SemIR::PartialType inst) const -> SemIR::CompleteTypeInfo { // The value representation of `partial T` is the same as that of `T`. - // Objects are not modifiable through their value representations. - return GetNestedInfo(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); + // Objects are not modifiable through their value representations. However, + // `partial T` is never abstract. + return { + .value_repr = + GetNestedInfo(context_->types().GetTypeIdForTypeInstId(inst.inner_id)) + .value_repr, + .abstract_class_id = SemIR::ClassId::None}; } auto TypeCompleter::BuildInfoForInst( diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 9ebc44ac6722..5469b914b027 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -8,6 +8,7 @@ #include "common/vlog.h" #include "toolchain/base/kind_switch.h" #include "toolchain/sem_ir/diagnostic_loc_converter.h" +#include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/generic.h" @@ -293,6 +294,12 @@ auto FunctionContext::InitializeStorage(TypeInFile type, SemIR::InstId dest_id, } } +auto FunctionContext::InitializeStorage(SemIR::InstId init_id) -> void { + InitializeStorage(GetTypeIdOfInst(init_id), + SemIR::FindStorageArgForInitializer(sem_ir(), init_id), + init_id); +} + auto FunctionContext::GetTypeIdOfInst(SemIR::InstId inst_id) -> TypeInFile { auto [file, type_id] = SemIR::GetTypeOfInstInSpecific( specific_sem_ir(), specific_id(), sem_ir(), inst_id); diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index 38d4f1b63e86..85d294490260 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -186,6 +186,10 @@ class FunctionContext { auto InitializeStorage(TypeInFile type, SemIR::InstId dest_id, SemIR::InstId source_id) -> void; + // Emits the instructions necessary to perform the initialization described by + // `init_id` in-place in its storage. + auto InitializeStorage(SemIR::InstId init_id) -> void; + // When fingerprinting for a specific, adds the call, found in the function // body, to . `function_id` and `specific_id` are // IDs within the file identified by `function_file_id`. diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 9fab2e912634..421573f90e10 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -350,6 +350,18 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, inst_id, context.builder().CreateNot(context.GetValue(inst.operand_id))); } +auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, + SemIR::UpdateInit inst) -> void { + // Ensure that our subordinate initializations have been performed. They may + // have been skipped if they were constant. + context.InitializeStorage(inst.base_init_id); + context.InitializeStorage(inst.update_init_id); + + // TODO: Add a helper to poison a value slot. + context.SetLocal(inst_id, + llvm::PoisonValue::get(context.GetTypeOfInst(inst_id))); +} + auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::VarStorage /* inst */) -> void { context.SetLocal(inst_id, diff --git a/toolchain/lower/testdata/class/base.carbon b/toolchain/lower/testdata/class/base.carbon index a4f6d6e34106..1ce92504bc94 100644 --- a/toolchain/lower/testdata/class/base.carbon +++ b/toolchain/lower/testdata/class/base.carbon @@ -42,7 +42,7 @@ fn Convert(p: Derived*) -> Base* { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc24_35.2.base = getelementptr inbounds nuw { { i32 }, i32 }, ptr %return, i32 0, i32 0, !dbg !8 // CHECK:STDOUT: %.loc24_26.3.b = getelementptr inbounds nuw { i32 }, ptr %.loc24_35.2.base, i32 0, i32 0, !dbg !9 -// CHECK:STDOUT: %.loc24_35.5.d = getelementptr inbounds nuw { { i32 }, i32 }, ptr %return, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: %.loc24_35.6.d = getelementptr inbounds nuw { { i32 }, i32 }, ptr %return, i32 0, i32 1, !dbg !8 // CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @Derived.val.loc24_36, i64 8, i1 false), !dbg !10 // CHECK:STDOUT: ret void, !dbg !10 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/class/generic.carbon b/toolchain/lower/testdata/class/generic.carbon index 4ab08d2e851a..8e62a5d83834 100644 --- a/toolchain/lower/testdata/class/generic.carbon +++ b/toolchain/lower/testdata/class/generic.carbon @@ -122,7 +122,7 @@ fn AccessTuple() -> (i32, i32, i32) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc7_35.2.base = getelementptr inbounds nuw { { i32 }, i32 }, ptr %return, i32 0, i32 0, !dbg !8 // CHECK:STDOUT: %.loc7_26.3.b = getelementptr inbounds nuw { i32 }, ptr %.loc7_35.2.base, i32 0, i32 0, !dbg !9 -// CHECK:STDOUT: %.loc7_35.5.d = getelementptr inbounds nuw { { i32 }, i32 }, ptr %return, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: %.loc7_35.6.d = getelementptr inbounds nuw { { i32 }, i32 }, ptr %return, i32 0, i32 1, !dbg !8 // CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @Derived.val.loc7_36, i64 8, i1 false), !dbg !10 // CHECK:STDOUT: ret void, !dbg !10 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/class/virtual.carbon b/toolchain/lower/testdata/class/virtual.carbon index bb8f2dd8f69c..f997bb1f2611 100644 --- a/toolchain/lower/testdata/class/virtual.carbon +++ b/toolchain/lower/testdata/class/virtual.carbon @@ -46,6 +46,36 @@ fn Use(v: Classes.Intermediate*) { v->Fn(); } +// --- create_named_ctor.carbon + +library "[[@TEST_NAME]]"; + +import Classes; + +fn MakePartialBase() -> partial Classes.Base { + return {}; +} + +fn MakePartialIntermediate() -> partial Classes.Intermediate { + return {.base = MakePartialBase()}; +} + +fn MakeBase() -> Classes.Base { + return MakePartialBase(); +} + +fn MakeIntermediate() -> Classes.Intermediate { + return MakePartialIntermediate(); +} + +fn MakeDerivedFromPartial() -> Classes.Derived { + return {.base = MakePartialIntermediate()}; +} + +fn MakeDerivedFromNonPartial() -> Classes.Derived { + return {.base = MakeIntermediate()}; +} + // --- member_init.carbon package MemberInit; @@ -199,7 +229,7 @@ fn Make() { // CHECK:STDOUT: @"_CDerived.Classes.$vtable" = external unnamed_addr constant ptr // CHECK:STDOUT: @Base.val.loc7_3 = internal constant {} zeroinitializer // CHECK:STDOUT: @Intermediate.val.ec2.loc8_3 = internal constant { ptr, {} } { ptr @"_CIntermediate.Classes.$vtable", {} zeroinitializer } -// CHECK:STDOUT: @Derived.val.loc9_3 = internal constant { { ptr, {} } } { { ptr, {} } { ptr @"_CDerived.Classes.$vtable", {} zeroinitializer } } +// CHECK:STDOUT: @Derived.val.loc9_49.5 = internal constant { { ptr, {} } } { { ptr, {} } { ptr poison, {} zeroinitializer } } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CCreate.Create() #0 !dbg !4 { @@ -218,7 +248,10 @@ fn Make() { // CHECK:STDOUT: %.loc9_49.2.base = getelementptr inbounds nuw { { ptr, {} } }, ptr %_.var.loc9, i32 0, i32 0, !dbg !12 // CHECK:STDOUT: %.loc9_48.2.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %.loc9_49.2.base, i32 0, i32 0, !dbg !13 // CHECK:STDOUT: %.loc9_48.4.base = getelementptr inbounds nuw { ptr, {} }, ptr %.loc9_49.2.base, i32 0, i32 1, !dbg !13 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %_.var.loc9, ptr align 8 @Derived.val.loc9_3, i64 8, i1 false), !dbg !9 +// CHECK:STDOUT: %.loc9_49.6.base = getelementptr inbounds nuw { { ptr, {} } }, ptr %_.var.loc9, i32 0, i32 0, !dbg !12 +// CHECK:STDOUT: %.loc9_49.7.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %.loc9_49.6.base, i32 0, i32 0, !dbg !12 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %_.var.loc9, ptr align 8 @Derived.val.loc9_49.5, i64 8, i1 false), !dbg !12 +// CHECK:STDOUT: store ptr @"_CDerived.Classes.$vtable", ptr %.loc9_49.7.vptr, align 8, !dbg !12 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc12), !dbg !10 // CHECK:STDOUT: ret void, !dbg !14 // CHECK:STDOUT: } @@ -280,6 +313,108 @@ fn Make() { // CHECK:STDOUT: !20 = !DILocalVariable(arg: 1, scope: !15, type: !18) // CHECK:STDOUT: !21 = !DILocation(line: 16, column: 3, scope: !15) // CHECK:STDOUT: !22 = !DILocation(line: 15, column: 1, scope: !15) +// CHECK:STDOUT: ; ModuleID = 'create_named_ctor.carbon' +// CHECK:STDOUT: source_filename = "create_named_ctor.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @"_CIntermediate.Classes.$vtable" = external unnamed_addr constant ptr +// CHECK:STDOUT: @"_CDerived.Classes.$vtable" = external unnamed_addr constant ptr +// CHECK:STDOUT: @empty_struct.d84.loc7_12 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMakePartialBase.Main(ptr sret({}) %return) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %return, ptr align 1 @empty_struct.d84.loc7_12, i64 0, i1 false), !dbg !8 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMakePartialIntermediate.Main(ptr sret({ ptr, {} }) %return) #0 !dbg !9 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc11_36.1.base = getelementptr inbounds nuw { ptr, {} }, ptr %return, i32 0, i32 1, !dbg !10 +// CHECK:STDOUT: call void @_CMakePartialBase.Main(ptr %.loc11_36.1.base), !dbg !11 +// CHECK:STDOUT: %.loc11_36.3.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %return, i32 0, i32 0, !dbg !10 +// CHECK:STDOUT: store ptr poison, ptr %.loc11_36.3.vptr, align 8, !dbg !10 +// CHECK:STDOUT: ret void, !dbg !12 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMakeBase.Main(ptr sret({}) %return) #0 !dbg !13 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CMakePartialBase.Main(ptr %return), !dbg !14 +// CHECK:STDOUT: ret void, !dbg !15 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMakeIntermediate.Main(ptr sret({ ptr, {} }) %return) #0 !dbg !16 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CMakePartialIntermediate.Main(ptr %return), !dbg !17 +// CHECK:STDOUT: %.loc19_34.1.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %return, i32 0, i32 0, !dbg !17 +// CHECK:STDOUT: store ptr @"_CIntermediate.Classes.$vtable", ptr %.loc19_34.1.vptr, align 8, !dbg !17 +// CHECK:STDOUT: ret void, !dbg !18 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CFn.Intermediate.Classes(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMakeDerivedFromPartial.Main(ptr sret({ { ptr, {} } }) %return) #0 !dbg !19 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc23_44.1.base = getelementptr inbounds nuw { { ptr, {} } }, ptr %return, i32 0, i32 0, !dbg !20 +// CHECK:STDOUT: call void @_CMakePartialIntermediate.Main(ptr %.loc23_44.1.base), !dbg !21 +// CHECK:STDOUT: %.loc23_44.5.base = getelementptr inbounds nuw { { ptr, {} } }, ptr %return, i32 0, i32 0, !dbg !20 +// CHECK:STDOUT: %.loc23_44.6.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %.loc23_44.5.base, i32 0, i32 0, !dbg !20 +// CHECK:STDOUT: store ptr @"_CDerived.Classes.$vtable", ptr %.loc23_44.6.vptr, align 8, !dbg !20 +// CHECK:STDOUT: ret void, !dbg !22 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CFn.Derived.Classes(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMakeDerivedFromNonPartial.Main(ptr sret({ { ptr, {} } }) %return) #0 !dbg !23 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc27_37.1.base = getelementptr inbounds nuw { { ptr, {} } }, ptr %return, i32 0, i32 0, !dbg !24 +// CHECK:STDOUT: call void @_CMakeIntermediate.Main(ptr %.loc27_37.1.base), !dbg !25 +// CHECK:STDOUT: %.loc27_37.7.base = getelementptr inbounds nuw { { ptr, {} } }, ptr %return, i32 0, i32 0, !dbg !24 +// CHECK:STDOUT: %.loc27_37.8.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %.loc27_37.7.base, i32 0, i32 0, !dbg !24 +// CHECK:STDOUT: store ptr @"_CDerived.Classes.$vtable", ptr %.loc27_37.8.vptr, align 8, !dbg !24 +// CHECK:STDOUT: ret void, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "create_named_ctor.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "MakePartialBase", linkageName: "_CMakePartialBase.Main", scope: null, file: !3, line: 6, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{!7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 8) +// CHECK:STDOUT: !8 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "MakePartialIntermediate", linkageName: "_CMakePartialIntermediate.Main", scope: null, file: !3, line: 10, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !10 = !DILocation(line: 11, column: 10, scope: !9) +// CHECK:STDOUT: !11 = !DILocation(line: 11, column: 19, scope: !9) +// CHECK:STDOUT: !12 = !DILocation(line: 11, column: 3, scope: !9) +// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "MakeBase", linkageName: "_CMakeBase.Main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !14 = !DILocation(line: 15, column: 10, scope: !13) +// CHECK:STDOUT: !15 = !DILocation(line: 15, column: 3, scope: !13) +// CHECK:STDOUT: !16 = distinct !DISubprogram(name: "MakeIntermediate", linkageName: "_CMakeIntermediate.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !17 = !DILocation(line: 19, column: 10, scope: !16) +// CHECK:STDOUT: !18 = !DILocation(line: 19, column: 3, scope: !16) +// CHECK:STDOUT: !19 = distinct !DISubprogram(name: "MakeDerivedFromPartial", linkageName: "_CMakeDerivedFromPartial.Main", scope: null, file: !3, line: 22, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !20 = !DILocation(line: 23, column: 10, scope: !19) +// CHECK:STDOUT: !21 = !DILocation(line: 23, column: 19, scope: !19) +// CHECK:STDOUT: !22 = !DILocation(line: 23, column: 3, scope: !19) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "MakeDerivedFromNonPartial", linkageName: "_CMakeDerivedFromNonPartial.Main", scope: null, file: !3, line: 26, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !24 = !DILocation(line: 27, column: 10, scope: !23) +// CHECK:STDOUT: !25 = !DILocation(line: 27, column: 19, scope: !23) +// CHECK:STDOUT: !26 = !DILocation(line: 27, column: 3, scope: !23) // CHECK:STDOUT: ; ModuleID = 'member_init.carbon' // CHECK:STDOUT: source_filename = "member_init.carbon" // CHECK:STDOUT: @@ -358,7 +493,7 @@ fn Make() { // CHECK:STDOUT: // CHECK:STDOUT: @"_CBase.Main.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CF.Base.Main to i64), i64 ptrtoint (ptr @"_CBase.Main.$vtable" to i64)) to i32)] // CHECK:STDOUT: @"_CDerived.Main.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CF.Base.Main to i64), i64 ptrtoint (ptr @"_CDerived.Main.$vtable" to i64)) to i32)] -// CHECK:STDOUT: @Derived.val.loc13_3 = internal constant { { ptr } } { { ptr } { ptr @"_CDerived.Main.$vtable" } } +// CHECK:STDOUT: @Derived.val.loc13_32.5 = internal constant { { ptr } } poison // CHECK:STDOUT: // CHECK:STDOUT: declare void @_CF.Base.Main(ptr) // CHECK:STDOUT: @@ -369,7 +504,10 @@ fn Make() { // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !7 // CHECK:STDOUT: %.loc13_32.2.base = getelementptr inbounds nuw { { ptr } }, ptr %_.var, i32 0, i32 0, !dbg !8 // CHECK:STDOUT: %.loc13_31.2.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc13_32.2.base, i32 0, i32 0, !dbg !9 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %_.var, ptr align 8 @Derived.val.loc13_3, i64 8, i1 false), !dbg !7 +// CHECK:STDOUT: %.loc13_32.6.base = getelementptr inbounds nuw { { ptr } }, ptr %_.var, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: %.loc13_32.7.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc13_32.6.base, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %_.var, ptr align 8 @Derived.val.loc13_32.5, i64 8, i1 false), !dbg !8 +// CHECK:STDOUT: store ptr @"_CDerived.Main.$vtable", ptr %.loc13_32.7.vptr, align 8, !dbg !8 // CHECK:STDOUT: ret void, !dbg !10 // CHECK:STDOUT: } // CHECK:STDOUT: @@ -439,7 +577,7 @@ fn Make() { // CHECK:STDOUT: // CHECK:STDOUT: @"_CBase.Main.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CF.Base.Main to i64), i64 ptrtoint (ptr @"_CBase.Main.$vtable" to i64)) to i32)] // CHECK:STDOUT: @"_CDerived.Main.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CF.Derived.Main to i64), i64 ptrtoint (ptr @"_CDerived.Main.$vtable" to i64)) to i32)] -// CHECK:STDOUT: @Derived.val.loc14_3 = internal constant { { ptr } } { { ptr } { ptr @"_CDerived.Main.$vtable" } } +// CHECK:STDOUT: @Derived.val.loc14_32.5 = internal constant { { ptr } } poison // CHECK:STDOUT: // CHECK:STDOUT: declare void @_CF.Base.Main(ptr) // CHECK:STDOUT: @@ -452,7 +590,10 @@ fn Make() { // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %v.var), !dbg !7 // CHECK:STDOUT: %.loc14_32.2.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !8 // CHECK:STDOUT: %.loc14_31.2.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc14_32.2.base, i32 0, i32 0, !dbg !9 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %v.var, ptr align 8 @Derived.val.loc14_3, i64 8, i1 false), !dbg !7 +// CHECK:STDOUT: %.loc14_32.6.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: %.loc14_32.7.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc14_32.6.base, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %v.var, ptr align 8 @Derived.val.loc14_32.5, i64 8, i1 false), !dbg !8 +// CHECK:STDOUT: store ptr @"_CDerived.Main.$vtable", ptr %.loc14_32.7.vptr, align 8, !dbg !8 // CHECK:STDOUT: %Derived.F.call.vtable = load ptr, ptr %v.var, align 8, !dbg !10 // CHECK:STDOUT: %Derived.F.call = call ptr @llvm.load.relative.i32(ptr %Derived.F.call.vtable, i32 0), !dbg !10 // CHECK:STDOUT: call void %Derived.F.call(ptr %v.var), !dbg !10 diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index d7d04f97c478..cfc7a2e17a23 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -178,6 +178,13 @@ auto FindStorageArgForInitializer(const File& sem_ir, InstId init_id, init_id = init.result_id; continue; } + case CARBON_KIND(UpdateInit init): { + if (!allow_transitive) { + return InstId::None; + } + init_id = init.base_init_id; + continue; + } case CARBON_KIND(ArrayInit init): { return init.dest_id; } diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 089459860592..86fe742ad6c5 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -161,6 +161,7 @@ CARBON_SEM_IR_INST_KIND(TypeType) CARBON_SEM_IR_INST_KIND(UnaryOperatorNot) CARBON_SEM_IR_INST_KIND(UnboundElementType) CARBON_SEM_IR_INST_KIND(UninitializedValue) +CARBON_SEM_IR_INST_KIND(UpdateInit) CARBON_SEM_IR_INST_KIND(ValueAsRef) CARBON_SEM_IR_INST_KIND(ValueBinding) CARBON_SEM_IR_INST_KIND(ValueBindingPattern) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 896a6650c4e6..1740c99e92de 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -2092,6 +2092,20 @@ struct UninitializedValue { TypeId type_id; }; +// Initializes an object by performing a base initialization followed by an +// update step. +struct UpdateInit { + static constexpr auto Kind = InstKind::UpdateInit.Define( + {.ir_name = "update_init", + .expr_category = ExprCategory::InPlaceInitializing}); + + TypeId type_id; + // The base initializer. Always an in-place initializer. + InstId base_init_id; + // The update expression. Always an in-place initializer. + InstId update_init_id; +}; + // Converts from a value expression to an ephemeral reference expression, in // the case where the value representation of the type is a pointer. For // example, when indexing a value expression of array type, this is used to