diff --git a/toolchain/check/context.h b/toolchain/check/context.h index cdab6891f5dd..93fe46db94ea 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -353,6 +353,9 @@ class Context { auto facet_types() -> SemIR::FacetTypeInfoStore& { return sem_ir().facet_types(); } + auto field_initializers() -> SemIR::File::FieldInitializerMap& { + return sem_ir().field_initializers(); + } auto identified_facet_types() -> SemIR::IdentifiedFacetTypeStore& { return sem_ir().identified_facet_types(); } diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index eb2c4277d5ce..dd6f4b6e8c56 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -609,10 +609,10 @@ static auto ConvertPartialInitializerToNonPartial( } // Common implementation for ConvertStructToStruct and ConvertStructToClass. -template +template static auto ConvertStructToStructOrClass( Context& context, SemIR::StructType src_type, SemIR::StructType dest_type, - SemIR::InstId value_id, ConversionTarget target, + SemIR::InstId value_id, ConversionTarget target, GetDefault get_default, SemIR::ClassType* vtable_class_type = nullptr) -> SemIR::InstId { static_assert(std::is_same_v || std::is_same_v); @@ -641,28 +641,38 @@ static auto ConvertStructToStructOrClass( value_id = MaterializeIfInitializer(context, value_id); } - // Check that the structs are the same size. - // TODO: If not, include the name of the first source field that doesn't - // exist in the destination or vice versa in the diagnostic. - if (src_elem_fields.size() != dest_elem_fields_size) { - if (target.diagnose) { - CARBON_DIAGNOSTIC( - StructInitElementCountMismatch, Error, - "cannot initialize {0:class|struct} with {1} field{1:s} from struct " - "with {2} field{2:s}", - Diagnostics::BoolAsSelect, Diagnostics::IntAsSelect, - Diagnostics::IntAsSelect); - context.emitter().Emit(value_loc_id, StructInitElementCountMismatch, - ToClass, dest_elem_fields_size, - src_elem_fields.size()); - } - return SemIR::ErrorInst::InstId; + Set dest_field_names; + for (auto field : dest_elem_fields) { + dest_field_names.Insert(field.name_id); } - // Prepare to look up fields in the source by index. + // Prepare to look up fields in the source by index. Also check for + // source fields that don't match any field in the destination. Map src_field_indexes; if (src_type.fields_id != dest_type.fields_id) { for (auto [i, field] : llvm::enumerate(src_elem_fields)) { + if (!dest_field_names.Lookup(field.name_id)) { + if (target.diagnose) { + if (literal_elems_id.has_value()) { + CARBON_DIAGNOSTIC(StructInitUnexpectedFieldInLiteral, Error, + "struct {0} has no field named `{1}`", + SemIR::TypeId, SemIR::NameId); + context.emitter().Emit(value_loc_id, + StructInitUnexpectedFieldInLiteral, + target.type_id, field.name_id); + } else { + CARBON_DIAGNOSTIC(StructInitUnexpectedFieldInConversion, Error, + "cannot convert from struct type {0} to {1}: " + "unexpected field `{2}` in source type", + TypeOfInstId, SemIR::TypeId, SemIR::NameId); + context.emitter().Emit(value_loc_id, + StructInitUnexpectedFieldInConversion, + value_id, target.type_id, field.name_id); + } + } + return SemIR::ErrorInst::InstId; + } + auto result = src_field_indexes.Insert(field.name_id, i); CARBON_CHECK(result.is_inserted(), "Duplicate field in source structure"); } @@ -675,7 +685,8 @@ 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_vptr_index.has_value() + literal_elems_id.has_value() && !dest_vptr_index.has_value() && + literal_elems.size() == dest_elem_fields_size ? SemIR::CopyOnWriteInstBlock(&sem_ir, literal_elems_id) : SemIR::CopyOnWriteInstBlock( &sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{ @@ -717,33 +728,14 @@ static auto ConvertStructToStructOrClass( // Find the matching source field. auto src_field_index = i; + bool found = true; if (src_type.fields_id != dest_type.fields_id) { if (auto lookup = src_field_indexes.Lookup(dest_field.name_id)) { src_field_index = lookup.value(); } else { - if (target.diagnose) { - if (literal_elems_id.has_value()) { - CARBON_DIAGNOSTIC( - StructInitMissingFieldInLiteral, Error, - "missing value for field `{0}` in struct initialization", - SemIR::NameId); - context.emitter().Emit(value_loc_id, - StructInitMissingFieldInLiteral, - dest_field.name_id); - } else { - CARBON_DIAGNOSTIC(StructInitMissingFieldInConversion, Error, - "cannot convert from struct type {0} to {1}: " - "missing field `{2}` in source type", - TypeOfInstId, SemIR::TypeId, SemIR::NameId); - context.emitter().Emit(value_loc_id, - StructInitMissingFieldInConversion, value_id, - target.type_id, dest_field.name_id); - } - } - return SemIR::ErrorInst::InstId; + found = false; } } - 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`. @@ -758,23 +750,51 @@ static auto ConvertStructToStructOrClass( 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; + SemIR::InstId init_id = SemIR::InstId::None; + if (found) { + auto src_field = src_elem_fields[src_field_index]; + + // 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; + } + 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, dest_field_index); + } else { + init_id = get_default(dest_field.name_id); } - 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, dest_field_index); + if (init_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } + if (!init_id.has_value()) { + if (target.diagnose) { + if (literal_elems_id.has_value()) { + CARBON_DIAGNOSTIC( + StructInitMissingFieldInLiteral, Error, + "missing value for field `{0}` in struct initialization", + SemIR::NameId); + context.emitter().Emit(value_loc_id, StructInitMissingFieldInLiteral, + dest_field.name_id); + } else { + CARBON_DIAGNOSTIC(StructInitMissingFieldInConversion, Error, + "cannot convert from struct type {0} to {1}: " + "missing field `{2}` in source type", + TypeOfInstId, SemIR::TypeId, SemIR::NameId); + context.emitter().Emit(value_loc_id, + StructInitMissingFieldInConversion, value_id, + target.type_id, dest_field.name_id); + } + } + 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 @@ -825,7 +845,8 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type, SemIR::InstId value_id, ConversionTarget target) -> SemIR::InstId { return ConvertStructToStructOrClass( - context, src_type, dest_type, value_id, target); + context, src_type, dest_type, value_id, target, + /*get_default=*/[](SemIR::NameId) { return SemIR::InstId::None; }); } // Performs a conversion from a struct to a class type. This function only @@ -863,8 +884,46 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, SemIR::LocId(value_id), {.type_id = target.type_id}); } + const auto& dest_class_scope = + context.name_scopes().Get(dest_class_info.scope_id); + + // Provide the default value for a field. Returns `InstId::None` if no + // default is available, or `ErrorInst::InstId` if a diagnosed error + // occurs. + auto get_default = [&](SemIR::NameId name_id) { + if (!context.insts().Is(value_id)) { + return SemIR::InstId::None; + } + + // Look up the field name in the class to get the corresponding + // `FieldDecl` `InstId`. + auto entry_id = dest_class_scope.Lookup(name_id); + if (!entry_id.has_value()) { + return SemIR::InstId::None; + } + + // Look up the initializer `InstId` for the field and eval as a + // constant. + auto field_inst_id = + dest_class_scope.GetEntry(*entry_id).result.target_inst_id(); + auto lookup = context.field_initializers().Lookup(field_inst_id); + if (!lookup) { + return SemIR::InstId::None; + } + auto initializer_id = lookup.value(); + SemIR::ConstantId const_id = SemIR::ConstantId::NotConstant; + const_id = GetConstantValueInSpecific( + context.sem_ir(), dest_type.specific_id, initializer_id); + if (const_id == SemIR::ConstantId::NotConstant) { + context.TODO(initializer_id, "field initializer is not constant"); + return SemIR::ErrorInst::InstId; + } + + return context.constant_values().GetInstId(const_id); + }; + return ConvertStructToStructOrClass( - context, src_type, dest_struct_type, value_id, target, + context, src_type, dest_struct_type, value_id, target, get_default, is_partial ? nullptr : &dest_type); } diff --git a/toolchain/check/handle_let_and_var.cpp b/toolchain/check/handle_let_and_var.cpp index 8947a6b2a2a5..f2b6b46c3905 100644 --- a/toolchain/check/handle_let_and_var.cpp +++ b/toolchain/check/handle_let_and_var.cpp @@ -126,12 +126,13 @@ auto HandleParseNode(Context& context, Parse::VariablePatternId node_id) pattern_id = AddInst( context, node_id, {.type_id = type_id, .subpattern_id = subpattern_id}); - break; } else { - // For non-static class fields, a `FieldDecl` has already been - // created; do not create a var pattern. - return true; + // For non-static class fields, a `FieldDecl` was created in + // `AddBindingPattern`. Use that as the `pattern_id` so that + // it's available when handling initializers. + pattern_id = context.field_decls_stack().PeekArray().back(); } + break; case FullPatternStack::Kind::NotInEitherParamList: CARBON_FATAL("Unreachable"); } @@ -209,11 +210,6 @@ auto HandleParseNode(Context& context, auto HandleParseNode(Context& context, Parse::VariableInitializerId node_id) -> bool { - if (InNonStaticFieldDecl(context)) { - context.TODO(node_id, "Field initializer"); - return false; - } - return HandleInitializer(context, node_id); } @@ -298,9 +294,7 @@ static auto HandleDecl(Context& context, Parse::NodeId node_id) -> DeclInfo { } } - if (!in_non_static_field_decl) { - decl_info.pattern_id = context.node_stack().PopPattern(); - } + decl_info.pattern_id = context.node_stack().PopPattern(); context.node_stack().PopAndDiscardSoloNodeId(); @@ -411,9 +405,7 @@ auto HandleParseNode(Context& context, Parse::VariableDeclId node_id) -> bool { KeywordModifierSet::Returned | KeywordModifierSet::Static); - if (!InNonStaticFieldDecl(context)) { - LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id); - } + LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id); context.full_pattern_stack().PopFullPattern(); context.decl_introducer_state_stack().Pop(); diff --git a/toolchain/check/pattern_match.cpp b/toolchain/check/pattern_match.cpp index 8d984b31e8d0..f1700ad09714 100644 --- a/toolchain/check/pattern_match.cpp +++ b/toolchain/check/pattern_match.cpp @@ -16,6 +16,7 @@ #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" +#include "toolchain/check/eval.h" #include "toolchain/check/pattern.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/format_providers.h" @@ -177,6 +178,8 @@ class MatchContext { SemIR::InstId scrutinee_id, WorkItem entry) -> void; auto DoPreWork(State state, SemIR::ExprPattern expr_pattern, SemIR::InstId scrutinee_id, WorkItem entry) -> void; + auto DoPreWork(State state, SemIR::FieldDecl field_decl, + SemIR::InstId scrutinee_id, WorkItem entry) -> void; auto DoPreWork(State state, SemIR::ReturnSlotPattern return_slot_pattern, SemIR::InstId scrutinee_id, WorkItem entry) -> void; auto DoPreWork(State state, SemIR::VarPattern var_pattern, @@ -537,6 +540,29 @@ auto MatchContext::DoPostWork(State /*state*/, SemIR::ExprPattern /*expr_pattern*/, WorkItem /*entry*/) -> void {} +auto MatchContext::DoPreWork(State /*state*/, SemIR::FieldDecl field_decl, + SemIR::InstId scrutinee_id, WorkItem entry) + -> void { + if (!scrutinee_id.has_value()) { + return; + } + + // Get the field initializer. + auto unbound_element_type = context_.insts().GetAs( + context_.types().GetTypeInstId(field_decl.type_id)); + auto element_type = context_.types().GetTypeIdForTypeInstId( + unbound_element_type.element_type_inst_id); + auto converted_id = ConvertToValueOfType(context_, SemIR::LocId(scrutinee_id), + scrutinee_id, element_type, + /*diagnose=*/true); + if (converted_id == SemIR::ErrorInst::InstId) { + return; + } + + // Store a mapping to the field's initializer. + context_.field_initializers().Insert(entry.pattern_id, converted_id); +} + auto MatchContext::DoPreWork(State state, SemIR::ReturnSlotPattern return_slot_pattern, SemIR::InstId scrutinee_id, WorkItem entry) @@ -838,6 +864,10 @@ auto MatchContext::Dispatch(State state, WorkItem entry) -> void { DoPreWork(state, expr_pattern, work.scrutinee_id, entry); break; } + case CARBON_KIND(SemIR::FieldDecl field_decl): { + DoPreWork(state, field_decl, work.scrutinee_id, entry); + break; + } case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): { DoPreWork(state, return_slot_pattern, work.scrutinee_id, entry); break; diff --git a/toolchain/check/testdata/choice/basic.carbon b/toolchain/check/testdata/choice/basic.carbon index 2ad2591ca379..7ec72369487a 100644 --- a/toolchain/check/testdata/choice/basic.carbon +++ b/toolchain/check/testdata/choice/basic.carbon @@ -51,7 +51,7 @@ library "[[@TEST_NAME]]"; choice Never {} // TODO: Can we produce a better diagnostic? -// CHECK:STDERR: fail_no_alternative_construct.carbon:[[@LINE+4]]:20: error: cannot initialize class with 1 field from struct with 0 fields [StructInitElementCountMismatch] +// CHECK:STDERR: fail_no_alternative_construct.carbon:[[@LINE+4]]:20: error: missing value for field `discriminant` in struct initialization [StructInitMissingFieldInLiteral] // CHECK:STDERR: let never: Never = {}; // CHECK:STDERR: ^~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/class/fail_init.carbon b/toolchain/check/testdata/class/fail_init.carbon index 3cd0a6701e2c..7c47a0a30846 100644 --- a/toolchain/check/testdata/class/fail_init.carbon +++ b/toolchain/check/testdata/class/fail_init.carbon @@ -16,17 +16,17 @@ class Class { } fn F() { - // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: cannot initialize class with 2 fields from struct with 1 field [StructInitElementCountMismatch] + // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: missing value for field `b` in struct initialization [StructInitMissingFieldInLiteral] // CHECK:STDERR: {.a = 1} as Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: {.a = 1} as Class; - // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: missing value for field `b` in struct initialization [StructInitMissingFieldInLiteral] + // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: struct `Class` has no field named `c` [StructInitUnexpectedFieldInLiteral] // CHECK:STDERR: {.a = 1, .c = 2} as Class; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: {.a = 1, .c = 2} as Class; - // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: cannot initialize class with 2 fields from struct with 3 fields [StructInitElementCountMismatch] + // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: struct `Class` has no field named `c` [StructInitUnexpectedFieldInLiteral] // CHECK:STDERR: {.a = 1, .b = 2, .c = 3} as Class; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon b/toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon deleted file mode 100644 index 6deb166948f6..000000000000 --- a/toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon +++ /dev/null @@ -1,19 +0,0 @@ -// 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/int.carbon -// -// AUTOUPDATE -// TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon - -class Class { - // CHECK:STDERR: fail_todo_field_initializer.carbon:[[@LINE+4]]:18: error: semantics TODO: `Field initializer` [SemanticsTodo] - // CHECK:STDERR: var field: i32 = 0; - // CHECK:STDERR: ^ - // CHECK:STDERR: - var field: i32 = 0; -} diff --git a/toolchain/check/testdata/class/field/field_initializer.carbon b/toolchain/check/testdata/class/field/field_initializer.carbon new file mode 100644 index 000000000000..a0c631a7c350 --- /dev/null +++ b/toolchain/check/testdata/class/field/field_initializer.carbon @@ -0,0 +1,163 @@ +// 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/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/field_initializer.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/field_initializer.carbon + +// --- field_initializer.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +class Class { + var field: i32 = 123; +} + +var C: Class = {}; +//@dump-sem-ir-end + +// --- fail_todo_field_initializer_import.carbon +library "[[@TEST_NAME]]"; + +import library "field_initializer"; + +// CHECK:STDERR: fail_todo_field_initializer_import.carbon:[[@LINE+4]]:17: error: missing value for field `field` in struct initialization [StructInitMissingFieldInLiteral] +// CHECK:STDERR: var C2: Class = {}; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var C2: Class = {}; + +// --- field_initializer_generic.carbon +library "[[@TEST_NAME]]"; + +interface I { + let Default:! Self; +} + +impl i32 as I where .Default = 123 {} + +class Class(T:! I) { + var x: T = T.Default; +} + +var C: Class(i32) = {}; + +// --- fail_field_initializer_invalid_name.carbon +library "[[@TEST_NAME]]"; + +class Class { + var field: i32 = 123; +} + +// CHECK:STDERR: fail_field_initializer_invalid_name.carbon:[[@LINE+4]]:16: error: struct `Class` has no field named `not_a_field` [StructInitUnexpectedFieldInLiteral] +// CHECK:STDERR: var C: Class = {.not_a_field = 456}; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +var C: Class = {.not_a_field = 456}; + +// --- fail_field_initializer_wrong_type.carbon +library "[[@TEST_NAME]]"; + +class Class { + // CHECK:STDERR: fail_field_initializer_wrong_type.carbon:[[@LINE+7]]:20: error: cannot implicitly convert expression of type `()` to `i32` [ConversionFailure] + // CHECK:STDERR: var field: i32 = (); + // CHECK:STDERR: ^~ + // CHECK:STDERR: fail_field_initializer_wrong_type.carbon:[[@LINE+4]]:20: note: type `()` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] + // CHECK:STDERR: var field: i32 = (); + // CHECK:STDERR: ^~ + // CHECK:STDERR: + var field: i32 = (); +} + +// --- fail_field_initializer_not_constant.carbon +library "[[@TEST_NAME]]"; + +var x: i32; +class Class { + // CHECK:STDERR: fail_field_initializer_not_constant.carbon:[[@LINE+4]]:20: error: semantics TODO: `field initializer is not constant` [SemanticsTodo] + // CHECK:STDERR: var field: i32 = x; + // CHECK:STDERR: ^ + // CHECK:STDERR: + var field: i32 = x; +} + +var C: Class = {}; + +// CHECK:STDOUT: --- field_initializer.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Class: type = class_type @Class [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] +// CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.544: 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.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.fb6: = impl_witness imports.%ImplicitAs.impl_witness_table.1a5, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.544 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.fb6) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.367: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.205: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.367, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_123.5a3: %i32 = int_value 123 [concrete] +// CHECK:STDOUT: %struct_type.field: type = struct_type {.field: %i32} [concrete] +// CHECK:STDOUT: %complete_type.97b: = complete_type_witness %struct_type.field [concrete] +// CHECK:STDOUT: %pattern_type.dd7: type = pattern_type %Class [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %Class.val: %Class = struct_value (%int_123.5a3) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.dd3: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1a5 = impl_witness_table (%Core.import_ref.dd3), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %C.patt: %pattern_type.dd7 = ref_binding_pattern C [concrete] +// CHECK:STDOUT: %C.var_patt: %pattern_type.dd7 = var_pattern %C.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.var: ref %Class = var %C.var_patt [concrete] +// CHECK:STDOUT: %Class.ref: type = name_ref Class, %Class.decl [concrete = constants.%Class] +// CHECK:STDOUT: %C: ref %Class = ref_binding C, %C.var [concrete = %C.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class { +// CHECK:STDOUT: %.loc5_12: %Class.elem = field_decl field, element0 [concrete] +// CHECK:STDOUT: %int_123: Core.IntLiteral = int_value 123 [concrete = constants.%int_123.fff] +// CHECK:STDOUT: %impl.elem0: %.205 = impl_witness_access constants.%ImplicitAs.impl_witness.fb6, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b] +// CHECK:STDOUT: %bound_method.loc5_20.1: = bound_method %int_123, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc5_20.2: = bound_method %int_123, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_20.2(%int_123) [concrete = constants.%int_123.5a3] +// CHECK:STDOUT: %.loc5_20.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_123.5a3] +// CHECK:STDOUT: %.loc5_20.2: %i32 = converted %int_123, %.loc5_20.1 [concrete = constants.%int_123.5a3] +// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.field [concrete = constants.%complete_type.97b] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Class +// CHECK:STDOUT: .field = %.loc5_12 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc8_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc8_17.2: init %Class to file.%C.var = class_init (constants.%int_123.5a3) [concrete = constants.%Class.val] +// CHECK:STDOUT: %.loc8_1: init %Class = converted %.loc8_17.1, %.loc8_17.2 [concrete = constants.%Class.val] +// CHECK:STDOUT: assign file.%C.var, %.loc8_1 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/use_assoc_entity.carbon b/toolchain/check/testdata/impl/use_assoc_entity.carbon index 4c443e738719..776d1efdbb16 100644 --- a/toolchain/check/testdata/impl/use_assoc_entity.carbon +++ b/toolchain/check/testdata/impl/use_assoc_entity.carbon @@ -398,7 +398,7 @@ library "[[@TEST_NAME]]"; interface K { let V:! type; - // CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+3]]:20: error: cannot convert from struct type `{.a: ()}` to `{.x: ()}`: missing field `x` in source type [StructInitMissingFieldInConversion] + // CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+3]]:20: error: cannot convert from struct type `{.a: ()}` to `{.x: ()}`: unexpected field `a` in source type [StructInitUnexpectedFieldInConversion] // CHECK:STDERR: fn F[self: Self](v: V) -> V; // CHECK:STDERR: ^~~~ fn F[self: Self](v: V) -> V; @@ -412,7 +412,7 @@ impl () as K where .V = {.a: ()} { // CHECK:STDERR: fn F[self: Self](v: V) -> V; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+7]]:3: error: cannot convert from struct type `{.x: ()}` to `{.a: ()}`: missing field `a` in source type [StructInitMissingFieldInConversion] + // CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+7]]:3: error: cannot convert from struct type `{.x: ()}` to `{.a: ()}`: unexpected field `x` in source type [StructInitUnexpectedFieldInConversion] // CHECK:STDERR: fn F[unused self: Self](v: {.x: ()}) -> {.x: ()} { return v; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE-14]]:3: note: while building thunk to match the signature of this function [ThunkSignature] diff --git a/toolchain/check/testdata/struct/fail_assign_empty.carbon b/toolchain/check/testdata/struct/fail_assign_empty.carbon index 64a454326bda..6cc1d90578fc 100644 --- a/toolchain/check/testdata/struct/fail_assign_empty.carbon +++ b/toolchain/check/testdata/struct/fail_assign_empty.carbon @@ -12,7 +12,7 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_assign_empty.carbon -// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+4]]:20: error: cannot initialize struct with 1 field from struct with 0 fields [StructInitElementCountMismatch] +// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+4]]:20: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral] // CHECK:STDERR: var x: {.a: i32} = {}; // CHECK:STDERR: ^~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/struct/fail_assign_nested.carbon b/toolchain/check/testdata/struct/fail_assign_nested.carbon index 6511089ce7e5..64459c61b40d 100644 --- a/toolchain/check/testdata/struct/fail_assign_nested.carbon +++ b/toolchain/check/testdata/struct/fail_assign_nested.carbon @@ -12,7 +12,7 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_assign_nested.carbon -// CHECK:STDERR: fail_assign_nested.carbon:[[@LINE+4]]:19: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral] +// CHECK:STDERR: fail_assign_nested.carbon:[[@LINE+4]]:19: error: struct `{.a: {}}` has no field named `b` [StructInitUnexpectedFieldInLiteral] // CHECK:STDERR: var x: {.a: {}} = {.b = {}}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/struct/fail_assign_to_empty.carbon b/toolchain/check/testdata/struct/fail_assign_to_empty.carbon index 752e31891c3f..4162cc5abfd5 100644 --- a/toolchain/check/testdata/struct/fail_assign_to_empty.carbon +++ b/toolchain/check/testdata/struct/fail_assign_to_empty.carbon @@ -12,7 +12,7 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_assign_to_empty.carbon -// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+4]]:13: error: cannot initialize struct with 0 fields from struct with 1 field [StructInitElementCountMismatch] +// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+4]]:13: error: struct `{}` has no field named `a` [StructInitUnexpectedFieldInLiteral] // CHECK:STDERR: var x: {} = {.a = 1}; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/struct/fail_field_name_mismatch.carbon b/toolchain/check/testdata/struct/fail_field_name_mismatch.carbon index 3cd1fd188635..1fa178cf7b1a 100644 --- a/toolchain/check/testdata/struct/fail_field_name_mismatch.carbon +++ b/toolchain/check/testdata/struct/fail_field_name_mismatch.carbon @@ -12,13 +12,20 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_field_name_mismatch.carbon -// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral] +var a: {.a: i32} = {.a = 1 }; +// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:29: error: cannot convert from struct type `{.a: i32}` to `{.a: i32, .b: i32}`: missing field `b` in source type [StructInitMissingFieldInConversion] +// CHECK:STDERR: var b: {.a: i32, .b: i32} = a; +// CHECK:STDERR: ^ +// CHECK:STDERR: +var b: {.a: i32, .b: i32} = a; + +// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: struct `{.a: i32}` has no field named `b` [StructInitUnexpectedFieldInLiteral] // CHECK:STDERR: var x: {.a: i32} = {.b = 1}; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var x: {.a: i32} = {.b = 1}; -// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: cannot convert from struct type `{.a: i32}` to `{.b: i32}`: missing field `b` in source type [StructInitMissingFieldInConversion] +// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: cannot convert from struct type `{.a: i32}` to `{.b: i32}`: unexpected field `a` in source type [StructInitUnexpectedFieldInConversion] // CHECK:STDERR: var y: {.b: i32} = x; // CHECK:STDERR: ^ // CHECK:STDERR: @@ -30,12 +37,46 @@ var y: {.b: i32} = x; // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] +// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] -// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete] -// CHECK:STDOUT: %pattern_type.79c: type = pattern_type %struct_type.a [concrete] -// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %struct_type.a.72d: type = struct_type {.a: %i32} [concrete] +// CHECK:STDOUT: %pattern_type.79c: type = pattern_type %struct_type.a.72d [concrete] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %struct_type.a.a6c: type = struct_type {.a: Core.IntLiteral} [concrete] +// CHECK:STDOUT: %struct.48c: %struct_type.a.a6c = struct_value (%int_1.5b8) [concrete] +// CHECK:STDOUT: %.17c: ref %i32 = struct_access file.%a.var, element0 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.649: type = generic_interface_type @ImplicitAs [concrete] +// CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.649 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.type.544: 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.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.fb6: = impl_witness imports.%ImplicitAs.impl_witness_table.1a5, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.544 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.fb6) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.367: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.205: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.367, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.d5e: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: %struct.096: %struct_type.a.72d = struct_value (%int_1.d5e) [concrete] +// CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] +// CHECK:STDOUT: %pattern_type.32c: type = pattern_type %struct_type.a.b [concrete] +// CHECK:STDOUT: %.568: ref %i32 = struct_access file.%b.var, element0 [concrete] +// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.b5d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.c85: %Int.as.Copy.impl.Op.type.b5d = struct_value () [symbolic] +// CHECK:STDOUT: %Copy.impl_witness.32d: = impl_witness imports.%Copy.impl_witness_table.07a, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.4a0: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.c4a: %Int.as.Copy.impl.Op.type.4a0 = struct_value () [concrete] +// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.32d) [concrete] +// CHECK:STDOUT: %Copy.WithSelf.Op.type.afe: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] +// CHECK:STDOUT: %.a5f: type = fn_type_with_self_type %Copy.WithSelf.Op.type.afe, %Copy.facet [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.c4a, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %struct_type.b.a15: type = struct_type {.b: Core.IntLiteral} [concrete] -// CHECK:STDOUT: %struct: %struct_type.b.a15 = struct_value (%int_1) [concrete] +// CHECK:STDOUT: %struct.62b: %struct_type.b.a15 = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %struct_type.b.177: type = struct_type {.b: %i32} [concrete] // CHECK:STDOUT: %pattern_type.3b8: type = pattern_type %struct_type.b.177 [concrete] // CHECK:STDOUT: } @@ -43,36 +84,67 @@ var y: {.b: i32} = x; // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int +// CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs +// CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] +// CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.649 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] +// CHECK:STDOUT: %Core.import_ref.dd3: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1a5 = impl_witness_table (%Core.import_ref.dd3), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] +// CHECK:STDOUT: %Core.import_ref.809: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.b5d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.c85)] +// CHECK:STDOUT: %Copy.impl_witness_table.07a = impl_witness_table (%Core.import_ref.809), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b // CHECK:STDOUT: .x = %x // CHECK:STDOUT: .y = %y // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.79c = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.79c = var_pattern %a.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a.var: ref %struct_type.a.72d = var %a.var_patt [concrete] +// CHECK:STDOUT: %.loc15: type = splice_block %struct_type.a.loc15 [concrete = constants.%struct_type.a.72d] { +// CHECK:STDOUT: %i32.loc15: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %struct_type.a.loc15: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a.72d] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: ref %struct_type.a.72d = ref_binding a, %a.var [concrete = %a.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.32c = ref_binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.32c = var_pattern %b.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b.var: ref %struct_type.a.b = var %b.var_patt [concrete] +// CHECK:STDOUT: %.loc20: type = splice_block %struct_type.a.b [concrete = constants.%struct_type.a.b] { +// CHECK:STDOUT: %i32.loc20_13: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %i32.loc20_22: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: ref %struct_type.a.b = ref_binding b, %b.var [concrete = %b.var] +// CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.79c = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type.79c = var_pattern %x.patt [concrete] // CHECK:STDOUT: } -// CHECK:STDOUT: %x.var: ref %struct_type.a = var %x.var_patt [concrete] -// CHECK:STDOUT: %.loc19: type = splice_block %struct_type.a [concrete = constants.%struct_type.a] { -// CHECK:STDOUT: %i32.loc19: type = type_literal constants.%i32 [concrete = constants.%i32] -// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a] +// CHECK:STDOUT: %x.var: ref %struct_type.a.72d = var %x.var_patt [concrete] +// CHECK:STDOUT: %.loc26: type = splice_block %struct_type.a.loc26 [concrete = constants.%struct_type.a.72d] { +// CHECK:STDOUT: %i32.loc26: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %struct_type.a.loc26: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a.72d] // CHECK:STDOUT: } -// CHECK:STDOUT: %x: ref %struct_type.a = ref_binding x, %x.var [concrete = %x.var] +// CHECK:STDOUT: %x: ref %struct_type.a.72d = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %y.patt: %pattern_type.3b8 = ref_binding_pattern y [concrete] // CHECK:STDOUT: %y.var_patt: %pattern_type.3b8 = var_pattern %y.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %y.var: ref %struct_type.b.177 = var %y.var_patt [concrete] -// CHECK:STDOUT: %.loc25: type = splice_block %struct_type.b [concrete = constants.%struct_type.b.177] { -// CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc32: type = splice_block %struct_type.b [concrete = constants.%struct_type.b.177] { +// CHECK:STDOUT: %i32.loc32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %i32} [concrete = constants.%struct_type.b.177] // CHECK:STDOUT: } // CHECK:STDOUT: %y: ref %struct_type.b.177 = ref_binding y, %y.var [concrete = %y.var] @@ -80,10 +152,32 @@ var y: {.b: i32} = x; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] -// CHECK:STDOUT: %.loc19: %struct_type.b.a15 = struct_literal (%int_1) [concrete = constants.%struct] +// CHECK:STDOUT: %int_1.loc15: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %.loc15_28.1: %struct_type.a.a6c = struct_literal (%int_1.loc15) [concrete = constants.%struct.48c] +// CHECK:STDOUT: %impl.elem0.loc15: %.205 = impl_witness_access constants.%ImplicitAs.impl_witness.fb6, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b] +// CHECK:STDOUT: %bound_method.loc15_28.1: = bound_method %int_1.loc15, %impl.elem0.loc15 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn.loc15: = specific_function %impl.elem0.loc15, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc15_28.2: = bound_method %int_1.loc15, %specific_fn.loc15 [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc15_28.2(%int_1.loc15) [concrete = constants.%int_1.d5e] +// CHECK:STDOUT: %.loc15_28.2: init %i32 = converted %int_1.loc15, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.d5e] +// CHECK:STDOUT: %.loc15_28.3: init %struct_type.a.72d to file.%a.var = struct_init (%.loc15_28.2) [concrete = constants.%struct.096] +// CHECK:STDOUT: %.loc15_1: init %struct_type.a.72d = converted %.loc15_28.1, %.loc15_28.3 [concrete = constants.%struct.096] +// CHECK:STDOUT: assign file.%a.var, %.loc15_1 +// CHECK:STDOUT: %a.ref: ref %struct_type.a.72d = name_ref a, file.%a [concrete = file.%a.var] +// CHECK:STDOUT: %.loc20_29.1: ref %i32 = struct_access %a.ref, element0 [concrete = constants.%.17c] +// CHECK:STDOUT: %.loc20_29.2: %i32 = acquire_value %.loc20_29.1 +// CHECK:STDOUT: %impl.elem0.loc20: %.a5f = impl_witness_access constants.%Copy.impl_witness.32d, element0 [concrete = constants.%Int.as.Copy.impl.Op.c4a] +// CHECK:STDOUT: %bound_method.loc20_29.1: = bound_method %.loc20_29.2, %impl.elem0.loc20 +// CHECK:STDOUT: %specific_fn.loc20: = specific_function %impl.elem0.loc20, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc20_29.2: = bound_method %.loc20_29.2, %specific_fn.loc20 +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc20_29.2(%.loc20_29.2) +// CHECK:STDOUT: %.loc20_29.3: ref %i32 = struct_access file.%b.var, element0 [concrete = constants.%.568] +// CHECK:STDOUT: %.loc20_29.4: init %i32 to %.loc20_29.3 = in_place_init %Int.as.Copy.impl.Op.call +// CHECK:STDOUT: assign file.%b.var, +// CHECK:STDOUT: %int_1.loc26: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %.loc26: %struct_type.b.a15 = struct_literal (%int_1.loc26) [concrete = constants.%struct.62b] // CHECK:STDOUT: assign file.%x.var, -// CHECK:STDOUT: %x.ref: ref %struct_type.a = name_ref x, file.%x [concrete = file.%x.var] +// CHECK:STDOUT: %x.ref: ref %struct_type.a.72d = name_ref x, file.%x [concrete = file.%x.var] // CHECK:STDOUT: assign file.%y.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/fail_field_type_mismatch.carbon b/toolchain/check/testdata/struct/fail_field_type_mismatch.carbon index da29daa57535..83d965eaf906 100644 --- a/toolchain/check/testdata/struct/fail_field_type_mismatch.carbon +++ b/toolchain/check/testdata/struct/fail_field_type_mismatch.carbon @@ -12,7 +12,7 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_field_type_mismatch.carbon -// CHECK:STDERR: fail_field_type_mismatch.carbon:[[@LINE+4]]:20: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral] +// CHECK:STDERR: fail_field_type_mismatch.carbon:[[@LINE+4]]:20: error: struct `{.a: i32}` has no field named `b` [StructInitUnexpectedFieldInLiteral] // CHECK:STDERR: var x: {.a: i32} = {.b = 1.0}; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/struct/fail_too_few_values.carbon b/toolchain/check/testdata/struct/fail_too_few_values.carbon index b325ae74c90d..94f92fa8dd7e 100644 --- a/toolchain/check/testdata/struct/fail_too_few_values.carbon +++ b/toolchain/check/testdata/struct/fail_too_few_values.carbon @@ -12,7 +12,7 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_too_few_values.carbon -// CHECK:STDERR: fail_too_few_values.carbon:[[@LINE+4]]:29: error: cannot initialize struct with 2 fields from struct with 1 field [StructInitElementCountMismatch] +// CHECK:STDERR: fail_too_few_values.carbon:[[@LINE+4]]:29: error: missing value for field `b` in struct initialization [StructInitMissingFieldInLiteral] // CHECK:STDERR: var x: {.a: i32, .b: i32} = {.a = 1}; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: @@ -27,18 +27,39 @@ var x: {.a: i32, .b: i32} = {.a = 1}; // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %pattern_type.32c: type = pattern_type %struct_type.a.b [concrete] -// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: Core.IntLiteral} [concrete] -// CHECK:STDOUT: %struct: %struct_type.a = struct_value (%int_1) [concrete] +// CHECK:STDOUT: %struct: %struct_type.a = struct_value (%int_1.5b8) [concrete] +// CHECK:STDOUT: %.2e9: ref %i32 = struct_access file.%x.var, element0 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.649: type = generic_interface_type @ImplicitAs [concrete] +// CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.649 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.type.544: 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.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.fb6: = impl_witness imports.%ImplicitAs.impl_witness_table.1a5, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.544 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.fb6) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.367: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.205: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.367, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.d5e: %i32 = int_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int +// CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] +// CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.649 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] +// CHECK:STDOUT: %Core.import_ref.dd3: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1a5 = impl_witness_table (%Core.import_ref.dd3), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -62,8 +83,16 @@ var x: {.a: i32, .b: i32} = {.a = 1}; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] -// CHECK:STDOUT: %.loc19: %struct_type.a = struct_literal (%int_1) [concrete = constants.%struct] +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %.loc19_36.1: %struct_type.a = struct_literal (%int_1) [concrete = constants.%struct] +// CHECK:STDOUT: %impl.elem0: %.205 = impl_witness_access constants.%ImplicitAs.impl_witness.fb6, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b] +// CHECK:STDOUT: %bound_method.loc19_36.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc19_36.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc19_36.2(%int_1) [concrete = constants.%int_1.d5e] +// CHECK:STDOUT: %.loc19_36.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.d5e] +// CHECK:STDOUT: %.loc19_36.3: ref %i32 = struct_access file.%x.var, element0 [concrete = constants.%.2e9] +// CHECK:STDOUT: %.loc19_36.4: init %i32 to %.loc19_36.3 = in_place_init %.loc19_36.2 [concrete = constants.%int_1.d5e] // CHECK:STDOUT: assign file.%x.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/import.carbon b/toolchain/check/testdata/struct/import.carbon index ca6abfcc0d5b..6c9a23189b8d 100644 --- a/toolchain/check/testdata/struct/import.carbon +++ b/toolchain/check/testdata/struct/import.carbon @@ -34,7 +34,7 @@ var c: C({.a = 1, .b = 2}) = F(); // --- fail_bad_type.impl.carbon impl package Implicit; -// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+8]]:14: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral] +// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+8]]:14: error: struct `{.a: i32, .b: i32}` has no field named `c` [StructInitUnexpectedFieldInLiteral] // CHECK:STDERR: var c_bad: C({.c = 1, .d = 2}) = F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE-4]]:1: in import [InImport] diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index 4430e3d918f9..7e718abbbd40 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -482,9 +482,10 @@ CARBON_DIAGNOSTIC_KIND(StringAtIndexNegative) CARBON_DIAGNOSTIC_KIND(StringLiteralTooLong) CARBON_DIAGNOSTIC_KIND(StringLiteralTypeIncomplete) CARBON_DIAGNOSTIC_KIND(StringLiteralTypeUnexpected) -CARBON_DIAGNOSTIC_KIND(StructInitElementCountMismatch) CARBON_DIAGNOSTIC_KIND(StructInitMissingFieldInLiteral) CARBON_DIAGNOSTIC_KIND(StructInitMissingFieldInConversion) +CARBON_DIAGNOSTIC_KIND(StructInitUnexpectedFieldInLiteral) +CARBON_DIAGNOSTIC_KIND(StructInitUnexpectedFieldInConversion) CARBON_DIAGNOSTIC_KIND(StructNameDuplicate) CARBON_DIAGNOSTIC_KIND(StructNamePrevious) CARBON_DIAGNOSTIC_KIND(TupleIndexNotConstant) diff --git a/toolchain/lower/testdata/class/field_initializer.carbon b/toolchain/lower/testdata/class/field_initializer.carbon new file mode 100644 index 000000000000..66c8a5d6c746 --- /dev/null +++ b/toolchain/lower/testdata/class/field_initializer.carbon @@ -0,0 +1,53 @@ +// 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/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/class/field_initializer.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/class/field_initializer.carbon + +// --- field_initializer.carbon +library "[[@TEST_NAME]]"; + +class C { + var a: i32 = 123; +} + +var c: C = {}; + +// CHECK:STDOUT: ; ModuleID = 'field_initializer.carbon' +// CHECK:STDOUT: source_filename = "field_initializer.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @_Cc.Main = global { i32 } zeroinitializer +// CHECK:STDOUT: @C.val.loc7_1 = internal constant { i32 } { i32 123 } +// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }] +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 @_Cc.Main, ptr align 4 @C.val.loc7_1, i64 4, i1 false), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync 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 nosync 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: "field_initializer.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "__global_init", linkageName: "_C__global_init.Main", scope: null, file: !3, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 7, column: 1, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 0, scope: !4) diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 026cece7d37f..240786d907a6 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -6,6 +6,7 @@ #define CARBON_TOOLCHAIN_SEM_IR_FILE_H_ #include "common/error.h" +#include "common/map.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Allocator.h" @@ -206,6 +207,12 @@ class File : public Printable { // TODO: Rename these to `facet_type_infos`. auto facet_types() -> FacetTypeInfoStore& { return facet_types_; } auto facet_types() const -> const FacetTypeInfoStore& { return facet_types_; } + + using FieldInitializerMap = Map; + auto field_initializers() -> FieldInitializerMap& { + return field_initializers_; + } + auto identified_facet_types() -> IdentifiedFacetTypeStore& { return identified_facet_types_; } @@ -349,6 +356,13 @@ class File : public Printable { // Storage for classes. ClassStore classes_; + // Map containing initializers for class fields. The map keys are + // `InstId`s corresponding to `FielDecl`s. + // + // TODO: consider replacing this map with a separate store for fields + // and tracking a new `FieldId` in the `FieldDecl`. + FieldInitializerMap field_initializers_; + // Storage for interfaces. InterfaceStore interfaces_;