diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index e01bb8cd5e74..8f4594c43dde 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -199,6 +199,15 @@ auto Context::ReplaceInstBeforeConstantUse(SemIR::InstId inst_id, FinishInst(inst_id, inst); } +auto Context::ReplaceInstPreservingConstantValue(SemIR::InstId inst_id, + SemIR::Inst inst) -> void { + auto old_const_id = sem_ir().constant_values().Get(inst_id); + sem_ir().insts().Set(inst_id, inst); + CARBON_VLOG("ReplaceInst: {0} -> {1}\n", inst_id, inst); + auto new_const_id = TryEvalInst(*this, inst_id, inst); + CARBON_CHECK(old_const_id == new_const_id); +} + auto Context::DiagnoseDuplicateName(SemIRLoc dup_def, SemIRLoc prev_def) -> void { CARBON_DIAGNOSTIC(NameDeclDuplicate, Error, diff --git a/toolchain/check/context.h b/toolchain/check/context.h index a66d527e03a7..fc7f3385631e 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -194,6 +194,11 @@ class Context { auto ReplaceInstBeforeConstantUse(SemIR::InstId inst_id, SemIR::Inst inst) -> void; + // Replaces the instruction `inst_id` with `inst`, not affecting location. + // The instruction is required to not change its constant value. + auto ReplaceInstPreservingConstantValue(SemIR::InstId inst_id, + SemIR::Inst inst) -> void; + // Sets only the parse node of an instruction. This is only used when setting // the parse node of an imported namespace. Versus // ReplaceInstBeforeConstantUse, it is safe to use after the namespace is used @@ -492,6 +497,10 @@ class Context { return struct_type_fields_stack_; } + auto field_decls_stack() -> ArrayStack& { + return field_decls_stack_; + } + auto decl_name_stack() -> DeclNameStack& { return decl_name_stack_; } auto decl_introducer_state_stack() -> DeclIntroducerStateStack& { @@ -648,10 +657,12 @@ class Context { // arguments. InstBlockStack args_type_info_stack_; - // The stack of StructTypeFields for in-progress StructTypeLiterals and Class - // object representations. + // The stack of StructTypeFields for in-progress StructTypeLiterals. ArrayStack struct_type_fields_stack_; + // The stack of FieldDecls for in-progress Class definitions. + ArrayStack field_decls_stack_; + // The stack used for qualified declaration name construction. DeclNameStack decl_name_stack_; diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 5dc845637ec0..cd3a5e4049c3 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -128,15 +128,11 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, auto field_type_id = context.GetUnboundElementType( class_info.self_type_id, cast_type_id); auto field_id = context.AddInst( - binding_id, - {.type_id = field_type_id, - .name_id = name_id, - .index = SemIR::ElementIndex( - context.struct_type_fields_stack().PeekArray().size())}); + binding_id, {.type_id = field_type_id, + .name_id = name_id, + .index = SemIR::ElementIndex::Invalid}); + context.field_decls_stack().AppendToTop(field_id); - // Add a corresponding field to the object representation of the class. - context.struct_type_fields_stack().AppendToTop( - {.name_id = name_id, .type_id = cast_type_id}); context.node_stack().Push(node_id, field_id); break; } diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index dea13ca8d4c4..fe74b3f6249a 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -295,7 +295,7 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionStartId node_id) context.inst_block_stack().Push(); context.node_stack().Push(node_id, class_id); - context.struct_type_fields_stack().PushArray(); + context.field_decls_stack().PushArray(); // TODO: Handle the case where there's control flow in the class body. For // example: @@ -512,7 +512,7 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool { return true; } - if (!context.struct_type_fields_stack().PeekArray().empty()) { + if (!context.field_decls_stack().PeekArray().empty()) { // TODO: Add note that includes the first field location as an example. CARBON_DIAGNOSTIC( BaseDeclAfterFieldDecl, Error, @@ -523,6 +523,8 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool { auto base_info = CheckBaseType(context, base_type_node_id, base_type_expr_id); + // TODO: Should we diagnose if there are already any fields? + // The `base` value in the class scope has an unbound element type. Instance // binding will be performed when it's found by name lookup into an instance. auto field_type_id = @@ -530,8 +532,7 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool { class_info.base_id = context.AddInst( node_id, {.type_id = field_type_id, .base_type_id = base_info.type_id, - .index = SemIR::ElementIndex( - context.struct_type_fields_stack().PeekArray().size())}); + .index = SemIR::ElementIndex::Invalid}); if (base_info.type_id != SemIR::TypeId::Error) { auto base_class_info = context.classes().Get( @@ -539,12 +540,6 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool { class_info.is_dynamic |= base_class_info.is_dynamic; } - // Add a corresponding field to the object representation of the class. - // TODO: Consider whether we want to use `partial T` here. - // TODO: Should we diagnose if there are already any fields? - context.struct_type_fields_stack().AppendToTop( - {.name_id = SemIR::NameId::Base, .type_id = base_info.type_id}); - // Bind the name `base` in the class to the base field. context.decl_name_stack().AddNameOrDiagnoseDuplicate( context.decl_name_stack().MakeUnqualifiedName(node_id, @@ -567,8 +562,7 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool { // returns a corresponding complete type witness instruction. static auto CheckCompleteAdapterClassType(Context& context, Parse::NodeId node_id, - SemIR::ClassId class_id, - SemIR::StructTypeFieldsId fields_id) + SemIR::ClassId class_id) -> SemIR::InstId { const auto& class_info = context.classes().Get(class_id); if (class_info.base_id.is_valid()) { @@ -581,17 +575,14 @@ static auto CheckCompleteAdapterClassType(Context& context, return SemIR::InstId::BuiltinErrorInst; } - if (auto fields = context.struct_type_fields().Get(fields_id); - !fields.empty()) { - auto [first_field_inst_id, _] = context.LookupNameInExactScope( - node_id, fields.front().name_id, class_info.scope_id, - context.name_scopes().Get(class_info.scope_id)); + auto field_decls = context.field_decls_stack().PeekArray(); + if (!field_decls.empty()) { CARBON_DIAGNOSTIC(AdaptWithFields, Error, "adapter with fields"); CARBON_DIAGNOSTIC(AdaptWithFieldHere, Note, "first field declaration is here"); context.emitter() .Build(class_info.adapt_id, AdaptWithFields) - .Note(first_field_inst_id, AdaptWithFieldHere) + .Note(field_decls.front(), AdaptWithFieldHere) .Emit(); return SemIR::InstId::BuiltinErrorInst; } @@ -637,6 +628,31 @@ static auto CheckCompleteAdapterClassType(Context& context, {.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType), .object_repr_id = adapted_type_id}); } +static auto AddStructTypeFields( + Context& context, + llvm::SmallVector& struct_type_fields) + -> SemIR::StructTypeFieldsId { + for (auto field_decl_id : context.field_decls_stack().PeekArray()) { + auto field_decl = context.insts().GetAs(field_decl_id); + field_decl.index = + SemIR::ElementIndex{static_cast(struct_type_fields.size())}; + context.ReplaceInstPreservingConstantValue(field_decl_id, field_decl); + if (field_decl.type_id == SemIR::TypeId::Error) { + struct_type_fields.push_back( + {.name_id = field_decl.name_id, .type_id = SemIR::TypeId::Error}); + continue; + } + auto unbound_element_type = + context.sem_ir().types().GetAs( + field_decl.type_id); + struct_type_fields.push_back( + {.name_id = field_decl.name_id, + .type_id = unbound_element_type.element_type_id}); + } + auto fields_id = + context.struct_type_fields().AddCanonical(struct_type_fields); + return fields_id; +} // Checks that the specified finished class definition is valid and builds and // returns a corresponding complete type witness instruction. @@ -644,39 +660,47 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id, SemIR::ClassId class_id) -> SemIR::InstId { auto& class_info = context.classes().Get(class_id); if (class_info.adapt_id.is_valid()) { - auto fields_id = context.struct_type_fields().AddCanonical( - context.struct_type_fields_stack().PeekArray()); - context.struct_type_fields_stack().PopArray(); - - return CheckCompleteAdapterClassType(context, node_id, class_id, fields_id); + return CheckCompleteAdapterClassType(context, node_id, class_id); } - bool defining_vtable_ptr = class_info.is_dynamic; + bool defining_vptr = class_info.is_dynamic; if (class_info.base_id.is_valid()) { auto base_info = context.insts().GetAs(class_info.base_id); // TODO: If the base class is template dependent, we will need to decide // whether to add a vptr as part of instantiation. if (auto* base_class_info = TryGetAsClass(context, base_info.base_type_id); base_class_info && base_class_info->is_dynamic) { - defining_vtable_ptr = false; + defining_vptr = false; } } - if (defining_vtable_ptr) { - context.struct_type_fields_stack().PrependToTop( + auto field_decls = context.field_decls_stack().PeekArray(); + llvm::SmallVector struct_type_fields; + struct_type_fields.reserve(defining_vptr + class_info.base_id.is_valid() + + field_decls.size()); + if (defining_vptr) { + struct_type_fields.push_back( {.name_id = SemIR::NameId::Vptr, .type_id = context.GetPointerType( context.GetBuiltinType(SemIR::BuiltinInstKind::VtableType))}); } - - auto fields_id = context.struct_type_fields().AddCanonical( - context.struct_type_fields_stack().PeekArray()); - context.struct_type_fields_stack().PopArray(); + if (class_info.base_id.is_valid()) { + auto base_decl = context.insts().GetAs(class_info.base_id); + base_decl.index = + SemIR::ElementIndex{static_cast(struct_type_fields.size())}; + context.ReplaceInstPreservingConstantValue(class_info.base_id, base_decl); + struct_type_fields.push_back( + {.name_id = SemIR::NameId::Base, + .type_id = context.insts() + .GetAs(class_info.base_id) + .base_type_id}); + } return context.AddInst( node_id, {.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType), - .object_repr_id = context.GetStructType(fields_id)}); + .object_repr_id = context.GetStructType( + AddStructTypeFields(context, struct_type_fields))}); } auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id) @@ -691,6 +715,7 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id) class_info.complete_type_witness_id = complete_type_witness_id; context.inst_block_stack().Pop(); + context.field_decls_stack().PopArray(); FinishGenericDefinition(context, class_info.generic_id); diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 0541dc5fe0f2..c7e62ac65db5 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -212,8 +212,8 @@ static auto BuildFunctionDecl(Context& context, parent_scope_inst) { if (auto class_decl = parent_scope_inst->TryAs()) { auto& class_info = context.classes().Get(class_decl->class_id); - CARBON_CHECK(virtual_modifier != SemIR::Function::VirtualModifier::Impl || - class_info.is_dynamic); + // TODO: If this is an `impl` function, check there's a matching base + // function that's impl or virtual. class_info.is_dynamic = true; } } diff --git a/toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon b/toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon index 79301649031a..f9d821907c99 100644 --- a/toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon +++ b/toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon @@ -121,7 +121,7 @@ class AdaptWithBaseAndFields { // CHECK:STDOUT: %.loc10_12.2: type = converted %int.make_type_signed, %.loc10_12.1 [template = constants.%i32] // CHECK:STDOUT: adapt_decl %i32 // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base] -// CHECK:STDOUT: %.loc15: %.5 = base_decl %Base, element0 [template] +// CHECK:STDOUT: %.loc15: %.5 = base_decl %Base, element [template] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptWithBase @@ -171,7 +171,7 @@ class AdaptWithBaseAndFields { // CHECK:STDOUT: %int.make_type_signed.loc13: init type = call constants.%Int(%.loc13_10.1) [template = constants.%i32] // CHECK:STDOUT: %.loc13_10.2: type = value_of_initializer %int.make_type_signed.loc13 [template = constants.%i32] // CHECK:STDOUT: %.loc13_10.3: type = converted %int.make_type_signed.loc13, %.loc13_10.2 [template = constants.%i32] -// CHECK:STDOUT: %.loc13_8: %.2 = field_decl n, element0 [template] +// CHECK:STDOUT: %.loc13_8: %.2 = field_decl n, element [template] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptWithField @@ -188,17 +188,17 @@ class AdaptWithBaseAndFields { // CHECK:STDOUT: %int.make_type_signed.loc25: init type = call constants.%Int(%.loc25_10.1) [template = constants.%i32] // CHECK:STDOUT: %.loc25_10.2: type = value_of_initializer %int.make_type_signed.loc25 [template = constants.%i32] // CHECK:STDOUT: %.loc25_10.3: type = converted %int.make_type_signed.loc25, %.loc25_10.2 [template = constants.%i32] -// CHECK:STDOUT: %.loc25_8: %.3 = field_decl a, element0 [template] +// CHECK:STDOUT: %.loc25_8: %.3 = field_decl a, element [template] // CHECK:STDOUT: %.loc26_10.1: Core.IntLiteral = int_value 32 [template = constants.%.1] // CHECK:STDOUT: %int.make_type_signed.loc26: init type = call constants.%Int(%.loc26_10.1) [template = constants.%i32] // CHECK:STDOUT: %.loc26_10.2: type = value_of_initializer %int.make_type_signed.loc26 [template = constants.%i32] // CHECK:STDOUT: %.loc26_10.3: type = converted %int.make_type_signed.loc26, %.loc26_10.2 [template = constants.%i32] -// CHECK:STDOUT: %.loc26_8: %.3 = field_decl b, element1 [template] +// CHECK:STDOUT: %.loc26_8: %.3 = field_decl b, element [template] // CHECK:STDOUT: %.loc27_10.1: Core.IntLiteral = int_value 32 [template = constants.%.1] // CHECK:STDOUT: %int.make_type_signed.loc27: init type = call constants.%Int(%.loc27_10.1) [template = constants.%i32] // CHECK:STDOUT: %.loc27_10.2: type = value_of_initializer %int.make_type_signed.loc27 [template = constants.%i32] // CHECK:STDOUT: %.loc27_10.3: type = converted %int.make_type_signed.loc27, %.loc27_10.2 [template = constants.%i32] -// CHECK:STDOUT: %.loc27_8: %.3 = field_decl c, element2 [template] +// CHECK:STDOUT: %.loc27_8: %.3 = field_decl c, element [template] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptWithFields @@ -250,12 +250,12 @@ class AdaptWithBaseAndFields { // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptWithBaseAndFields { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base] -// CHECK:STDOUT: %.loc7: %.4 = base_decl %Base, element0 [template] +// CHECK:STDOUT: %.loc7: %.4 = base_decl %Base, element [template] // CHECK:STDOUT: %.loc8_10.1: Core.IntLiteral = int_value 32 [template = constants.%.5] // CHECK:STDOUT: %int.make_type_signed: init type = call constants.%Int(%.loc8_10.1) [template = constants.%i32] // CHECK:STDOUT: %.loc8_10.2: type = value_of_initializer %int.make_type_signed [template = constants.%i32] // CHECK:STDOUT: %.loc8_10.3: type = converted %int.make_type_signed, %.loc8_10.2 [template = constants.%i32] -// CHECK:STDOUT: %.loc8_8: %.6 = field_decl n, element1 [template] +// CHECK:STDOUT: %.loc8_8: %.6 = field_decl n, element [template] // CHECK:STDOUT: %.loc15_10: %.1 = struct_literal () // CHECK:STDOUT: %.loc15_11: type = converted %.loc15_10, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: adapt_decl %.1 diff --git a/toolchain/check/testdata/class/virtual_modifiers.carbon b/toolchain/check/testdata/class/virtual_modifiers.carbon index 9ddcfd6449cf..2a2eb3136614 100644 --- a/toolchain/check/testdata/class/virtual_modifiers.carbon +++ b/toolchain/check/testdata/class/virtual_modifiers.carbon @@ -52,13 +52,32 @@ fn F() { package InitMembers; base class Base { - virtual fn F(); var m1: i32; var m2: i32; + + virtual fn F(); } fn F() { - var v: Base = {.m2 = 3, .m1 = 5}; + var i: i32 = 3; + // TODO: These should initialize element1 (.m), not element0 (the vptr) + var b1: Base = {.m2 = i, .m1 = i}; + var b2: Base = {.m2 = 3, .m1 = 5}; + + // This one is good, though. + b1.m2 = 4; +} + +// --- todo_fail_impl_without_base_declaration.carbon + +package ImplWithoutBaseDeclaration; + +base class Base { +} + +class Derived { + extend base: Base; + impl fn F(); } // CHECK:STDOUT: --- modifiers.carbon @@ -253,31 +272,36 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [template] -// CHECK:STDOUT: %F.type.1: type = fn_type @F.1 [template] -// CHECK:STDOUT: %F.1: %F.type.1 = struct_value () [template] // CHECK:STDOUT: %.1: Core.IntLiteral = int_value 32 [template] // CHECK:STDOUT: %Int.type: type = fn_type @Int [template] // CHECK:STDOUT: %Int: %Int.type = struct_value () [template] // CHECK:STDOUT: %i32: type = int_type signed, %.1 [template] // CHECK:STDOUT: %.2: type = unbound_element_type %Base, %i32 [template] +// CHECK:STDOUT: %F.type.1: type = fn_type @F.1 [template] +// CHECK:STDOUT: %F.1: %F.type.1 = struct_value () [template] // CHECK:STDOUT: %.3: type = ptr_type [template] // CHECK:STDOUT: %.4: type = struct_type {.: %.3, .m1: %i32, .m2: %i32} [template] // CHECK:STDOUT: %.5: = complete_type_witness %.4 [template] // CHECK:STDOUT: %F.type.2: type = fn_type @F.2 [template] // CHECK:STDOUT: %F.2: %F.type.2 = struct_value () [template] -// CHECK:STDOUT: %.7: Core.IntLiteral = int_value 3 [template] -// CHECK:STDOUT: %.8: Core.IntLiteral = int_value 5 [template] -// CHECK:STDOUT: %.9: type = struct_type {.m2: Core.IntLiteral, .m1: Core.IntLiteral} [template] +// CHECK:STDOUT: %.6: Core.IntLiteral = int_value 3 [template] // CHECK:STDOUT: %Convert.type.2: type = fn_type @Convert.1, @ImplicitAs(%i32) [template] // CHECK:STDOUT: %Convert.type.14: type = fn_type @Convert.2, @impl.1(%.1) [template] // CHECK:STDOUT: %Convert.14: %Convert.type.14 = struct_value () [template] -// CHECK:STDOUT: %.33: = interface_witness (%Convert.14) [template] -// CHECK:STDOUT: %.34: = bound_method %.8, %Convert.14 [template] -// CHECK:STDOUT: %.35: = specific_function %.34, @Convert.2(%.1) [template] -// CHECK:STDOUT: %.36: %i32 = int_value 5 [template] -// CHECK:STDOUT: %.37: = bound_method %.7, %Convert.14 [template] -// CHECK:STDOUT: %.38: = specific_function %.37, @Convert.2(%.1) [template] -// CHECK:STDOUT: %.39: %i32 = int_value 3 [template] +// CHECK:STDOUT: %.30: = interface_witness (%Convert.14) [template] +// CHECK:STDOUT: %.31: = bound_method %.6, %Convert.14 [template] +// CHECK:STDOUT: %.32: = specific_function %.31, @Convert.2(%.1) [template] +// CHECK:STDOUT: %.33: %i32 = int_value 3 [template] +// CHECK:STDOUT: %.35: type = struct_type {.m2: %i32, .m1: %i32} [template] +// CHECK:STDOUT: %.36: Core.IntLiteral = int_value 5 [template] +// CHECK:STDOUT: %.37: type = struct_type {.m2: Core.IntLiteral, .m1: Core.IntLiteral} [template] +// CHECK:STDOUT: %.38: = bound_method %.36, %Convert.14 [template] +// CHECK:STDOUT: %.39: = specific_function %.38, @Convert.2(%.1) [template] +// CHECK:STDOUT: %.40: %i32 = int_value 5 [template] +// CHECK:STDOUT: %.41: Core.IntLiteral = int_value 4 [template] +// CHECK:STDOUT: %.42: = bound_method %.41, %Convert.14 [template] +// CHECK:STDOUT: %.43: = specific_function %.42, @Convert.2(%.1) [template] +// CHECK:STDOUT: %.44: %i32 = int_value 4 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -301,53 +325,146 @@ fn F() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { -// CHECK:STDOUT: %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {} {} +// CHECK:STDOUT: %.loc5_11.1: Core.IntLiteral = int_value 32 [template = constants.%.1] +// CHECK:STDOUT: %int.make_type_signed.loc5: init type = call constants.%Int(%.loc5_11.1) [template = constants.%i32] +// CHECK:STDOUT: %.loc5_11.2: type = value_of_initializer %int.make_type_signed.loc5 [template = constants.%i32] +// CHECK:STDOUT: %.loc5_11.3: type = converted %int.make_type_signed.loc5, %.loc5_11.2 [template = constants.%i32] +// CHECK:STDOUT: %.loc5_9: %.2 = field_decl m1, element1 [template] // CHECK:STDOUT: %.loc6_11.1: Core.IntLiteral = int_value 32 [template = constants.%.1] // CHECK:STDOUT: %int.make_type_signed.loc6: init type = call constants.%Int(%.loc6_11.1) [template = constants.%i32] // CHECK:STDOUT: %.loc6_11.2: type = value_of_initializer %int.make_type_signed.loc6 [template = constants.%i32] // CHECK:STDOUT: %.loc6_11.3: type = converted %int.make_type_signed.loc6, %.loc6_11.2 [template = constants.%i32] -// CHECK:STDOUT: %.loc6_9: %.2 = field_decl m1, element0 [template] -// CHECK:STDOUT: %.loc7_11.1: Core.IntLiteral = int_value 32 [template = constants.%.1] -// CHECK:STDOUT: %int.make_type_signed.loc7: init type = call constants.%Int(%.loc7_11.1) [template = constants.%i32] -// CHECK:STDOUT: %.loc7_11.2: type = value_of_initializer %int.make_type_signed.loc7 [template = constants.%i32] -// CHECK:STDOUT: %.loc7_11.3: type = converted %int.make_type_signed.loc7, %.loc7_11.2 [template = constants.%i32] -// CHECK:STDOUT: %.loc7_9: %.2 = field_decl m2, element1 [template] -// CHECK:STDOUT: %.loc8: = complete_type_witness %.4 [template = constants.%.5] +// CHECK:STDOUT: %.loc6_9: %.2 = field_decl m2, element2 [template] +// CHECK:STDOUT: %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {} {} +// CHECK:STDOUT: %.loc9: = complete_type_witness %.4 [template = constants.%.5] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base +// CHECK:STDOUT: .m1 = %.loc5_9 +// CHECK:STDOUT: .m2 = %.loc6_9 // CHECK:STDOUT: .F = %F.decl -// CHECK:STDOUT: .m1 = %.loc6_9 -// CHECK:STDOUT: .m2 = %.loc7_9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @F.1(); // CHECK:STDOUT: // CHECK:STDOUT: fn @F.2() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base] -// CHECK:STDOUT: %v.var: ref %Base = var v -// CHECK:STDOUT: %v: ref %Base = bind_name v, %v.var -// CHECK:STDOUT: %.loc11_24: Core.IntLiteral = int_value 3 [template = constants.%.7] -// CHECK:STDOUT: %.loc11_33: Core.IntLiteral = int_value 5 [template = constants.%.8] -// CHECK:STDOUT: %.loc11_34.1: %.9 = struct_literal (%.loc11_24, %.loc11_33) -// CHECK:STDOUT: %.loc11_34.2: %Convert.type.2 = interface_witness_access constants.%.33, element0 [template = constants.%Convert.14] -// CHECK:STDOUT: %.loc11_34.3: = bound_method %.loc11_33, %.loc11_34.2 [template = constants.%.34] -// CHECK:STDOUT: %.loc11_34.4: = specific_function %.loc11_34.3, @Convert.2(constants.%.1) [template = constants.%.35] -// CHECK:STDOUT: %int.convert_checked.loc11_34.1: init %i32 = call %.loc11_34.4(%.loc11_33) [template = constants.%.36] -// CHECK:STDOUT: %.loc11_34.5: init %i32 = converted %.loc11_33, %int.convert_checked.loc11_34.1 [template = constants.%.36] -// CHECK:STDOUT: %.loc11_34.6: ref %i32 = class_element_access %v.var, element2 -// CHECK:STDOUT: %.loc11_34.7: init %i32 = initialize_from %.loc11_34.5 to %.loc11_34.6 [template = constants.%.36] -// CHECK:STDOUT: %.loc11_34.8: %Convert.type.2 = interface_witness_access constants.%.33, element0 [template = constants.%Convert.14] -// CHECK:STDOUT: %.loc11_34.9: = bound_method %.loc11_24, %.loc11_34.8 [template = constants.%.37] -// CHECK:STDOUT: %.loc11_34.10: = specific_function %.loc11_34.9, @Convert.2(constants.%.1) [template = constants.%.38] -// CHECK:STDOUT: %int.convert_checked.loc11_34.2: init %i32 = call %.loc11_34.10(%.loc11_24) [template = constants.%.39] -// CHECK:STDOUT: %.loc11_34.11: init %i32 = converted %.loc11_24, %int.convert_checked.loc11_34.2 [template = constants.%.39] -// CHECK:STDOUT: %.loc11_34.12: ref %i32 = class_element_access %v.var, element1 -// CHECK:STDOUT: %.loc11_34.13: init %i32 = initialize_from %.loc11_34.11 to %.loc11_34.12 [template = constants.%.39] -// CHECK:STDOUT: %.loc11_34.14: init %Base = class_init (, %.loc11_34.7, %.loc11_34.13), %v.var [template = ] -// CHECK:STDOUT: %.loc11_35: init %Base = converted %.loc11_34.1, %.loc11_34.14 [template = ] -// CHECK:STDOUT: assign %v.var, %.loc11_35 +// CHECK:STDOUT: %.loc12_10.1: Core.IntLiteral = int_value 32 [template = constants.%.1] +// CHECK:STDOUT: %int.make_type_signed: init type = call constants.%Int(%.loc12_10.1) [template = constants.%i32] +// CHECK:STDOUT: %.loc12_10.2: type = value_of_initializer %int.make_type_signed [template = constants.%i32] +// CHECK:STDOUT: %.loc12_10.3: type = converted %int.make_type_signed, %.loc12_10.2 [template = constants.%i32] +// CHECK:STDOUT: %i.var: ref %i32 = var i +// CHECK:STDOUT: %i: ref %i32 = bind_name i, %i.var +// CHECK:STDOUT: %.loc12_16: Core.IntLiteral = int_value 3 [template = constants.%.6] +// CHECK:STDOUT: %.loc12_17.1: %Convert.type.2 = interface_witness_access constants.%.30, element0 [template = constants.%Convert.14] +// CHECK:STDOUT: %.loc12_17.2: = bound_method %.loc12_16, %.loc12_17.1 [template = constants.%.31] +// CHECK:STDOUT: %.loc12_17.3: = specific_function %.loc12_17.2, @Convert.2(constants.%.1) [template = constants.%.32] +// CHECK:STDOUT: %int.convert_checked.loc12: init %i32 = call %.loc12_17.3(%.loc12_16) [template = constants.%.33] +// CHECK:STDOUT: %.loc12_17.4: init %i32 = converted %.loc12_16, %int.convert_checked.loc12 [template = constants.%.33] +// CHECK:STDOUT: assign %i.var, %.loc12_17.4 +// CHECK:STDOUT: %Base.ref.loc14: type = name_ref Base, file.%Base.decl [template = constants.%Base] +// CHECK:STDOUT: %b1.var: ref %Base = var b1 +// CHECK:STDOUT: %b1: ref %Base = bind_name b1, %b1.var +// CHECK:STDOUT: %i.ref.loc14_25: ref %i32 = name_ref i, %i +// CHECK:STDOUT: %i.ref.loc14_34: ref %i32 = name_ref i, %i +// CHECK:STDOUT: %.loc14_35.1: %.35 = struct_literal (%i.ref.loc14_25, %i.ref.loc14_34) +// CHECK:STDOUT: %.loc14_34: %i32 = bind_value %i.ref.loc14_34 +// CHECK:STDOUT: %.loc14_35.2: ref %i32 = class_element_access %b1.var, element2 +// CHECK:STDOUT: %.loc14_35.3: init %i32 = initialize_from %.loc14_34 to %.loc14_35.2 +// CHECK:STDOUT: %.loc14_25: %i32 = bind_value %i.ref.loc14_25 +// CHECK:STDOUT: %.loc14_35.4: ref %i32 = class_element_access %b1.var, element1 +// CHECK:STDOUT: %.loc14_35.5: init %i32 = initialize_from %.loc14_25 to %.loc14_35.4 +// CHECK:STDOUT: %.loc14_35.6: init %Base = class_init (, %.loc14_35.3, %.loc14_35.5), %b1.var +// CHECK:STDOUT: %.loc14_36: init %Base = converted %.loc14_35.1, %.loc14_35.6 +// CHECK:STDOUT: assign %b1.var, %.loc14_36 +// CHECK:STDOUT: %Base.ref.loc15: type = name_ref Base, file.%Base.decl [template = constants.%Base] +// CHECK:STDOUT: %b2.var: ref %Base = var b2 +// CHECK:STDOUT: %b2: ref %Base = bind_name b2, %b2.var +// CHECK:STDOUT: %.loc15_25: Core.IntLiteral = int_value 3 [template = constants.%.6] +// CHECK:STDOUT: %.loc15_34: Core.IntLiteral = int_value 5 [template = constants.%.36] +// CHECK:STDOUT: %.loc15_35.1: %.37 = struct_literal (%.loc15_25, %.loc15_34) +// CHECK:STDOUT: %.loc15_35.2: %Convert.type.2 = interface_witness_access constants.%.30, element0 [template = constants.%Convert.14] +// CHECK:STDOUT: %.loc15_35.3: = bound_method %.loc15_34, %.loc15_35.2 [template = constants.%.38] +// CHECK:STDOUT: %.loc15_35.4: = specific_function %.loc15_35.3, @Convert.2(constants.%.1) [template = constants.%.39] +// CHECK:STDOUT: %int.convert_checked.loc15_35.1: init %i32 = call %.loc15_35.4(%.loc15_34) [template = constants.%.40] +// CHECK:STDOUT: %.loc15_35.5: init %i32 = converted %.loc15_34, %int.convert_checked.loc15_35.1 [template = constants.%.40] +// CHECK:STDOUT: %.loc15_35.6: ref %i32 = class_element_access %b2.var, element2 +// CHECK:STDOUT: %.loc15_35.7: init %i32 = initialize_from %.loc15_35.5 to %.loc15_35.6 [template = constants.%.40] +// CHECK:STDOUT: %.loc15_35.8: %Convert.type.2 = interface_witness_access constants.%.30, element0 [template = constants.%Convert.14] +// CHECK:STDOUT: %.loc15_35.9: = bound_method %.loc15_25, %.loc15_35.8 [template = constants.%.31] +// CHECK:STDOUT: %.loc15_35.10: = specific_function %.loc15_35.9, @Convert.2(constants.%.1) [template = constants.%.32] +// CHECK:STDOUT: %int.convert_checked.loc15_35.2: init %i32 = call %.loc15_35.10(%.loc15_25) [template = constants.%.33] +// CHECK:STDOUT: %.loc15_35.11: init %i32 = converted %.loc15_25, %int.convert_checked.loc15_35.2 [template = constants.%.33] +// CHECK:STDOUT: %.loc15_35.12: ref %i32 = class_element_access %b2.var, element1 +// CHECK:STDOUT: %.loc15_35.13: init %i32 = initialize_from %.loc15_35.11 to %.loc15_35.12 [template = constants.%.33] +// CHECK:STDOUT: %.loc15_35.14: init %Base = class_init (, %.loc15_35.7, %.loc15_35.13), %b2.var [template = ] +// CHECK:STDOUT: %.loc15_36: init %Base = converted %.loc15_35.1, %.loc15_35.14 [template = ] +// CHECK:STDOUT: assign %b2.var, %.loc15_36 +// CHECK:STDOUT: %b1.ref: ref %Base = name_ref b1, %b1 +// CHECK:STDOUT: %m2.ref: %.2 = name_ref m2, @Base.%.loc6_9 [template = @Base.%.loc6_9] +// CHECK:STDOUT: %.loc18_5: ref %i32 = class_element_access %b1.ref, element2 +// CHECK:STDOUT: %.loc18_11: Core.IntLiteral = int_value 4 [template = constants.%.41] +// CHECK:STDOUT: %.loc18_9.1: %Convert.type.2 = interface_witness_access constants.%.30, element0 [template = constants.%Convert.14] +// CHECK:STDOUT: %.loc18_9.2: = bound_method %.loc18_11, %.loc18_9.1 [template = constants.%.42] +// CHECK:STDOUT: %.loc18_9.3: = specific_function %.loc18_9.2, @Convert.2(constants.%.1) [template = constants.%.43] +// CHECK:STDOUT: %int.convert_checked.loc18: init %i32 = call %.loc18_9.3(%.loc18_11) [template = constants.%.44] +// CHECK:STDOUT: %.loc18_9.4: init %i32 = converted %.loc18_11, %int.convert_checked.loc18 [template = constants.%.44] +// CHECK:STDOUT: assign %.loc18_5, %.loc18_9.4 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- todo_fail_impl_without_base_declaration.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Base: type = class_type @Base [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: = complete_type_witness %.1 [template] +// CHECK:STDOUT: %Derived: type = class_type @Derived [template] +// CHECK:STDOUT: %.4: type = unbound_element_type %Derived, %Base [template] +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %.5: type = ptr_type [template] +// CHECK:STDOUT: %.6: type = struct_type {.: %.5, .base: %Base} [template] +// CHECK:STDOUT: %.7: = complete_type_witness %.6 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Base = %Base.decl +// CHECK:STDOUT: .Derived = %Derived.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Base.decl: type = class_decl @Base [template = constants.%Base] {} {} +// CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [template = constants.%Derived] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Base { +// CHECK:STDOUT: %.loc5: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Base +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Derived { +// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base] +// CHECK:STDOUT: %.loc8: %.4 = base_decl %Base, element1 [template] +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {} +// CHECK:STDOUT: %.loc10: = complete_type_witness %.6 [template = constants.%.7] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Derived +// CHECK:STDOUT: .base = %.loc8 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: extend %Base.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl fn @F(); +// CHECK:STDOUT: diff --git a/toolchain/lower/testdata/class/virtual.carbon b/toolchain/lower/testdata/class/virtual.carbon index 78a1d15f5540..68a8dfa6cab9 100644 --- a/toolchain/lower/testdata/class/virtual.carbon +++ b/toolchain/lower/testdata/class/virtual.carbon @@ -8,35 +8,75 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/class/virtual.carbon +// --- classes.carbon + +package Classes; + base class Base { } + base class Intermediate { extend base: Base; virtual fn Fn(); } + class Derived { extend base: Intermediate; impl fn Fn(); } +// --- create.carbon + +package Create; + +import Classes; + fn Create() { - var b: Base; - var i: Intermediate; - var d: Derived; + var b: Classes.Base; + var i: Classes.Intermediate; + var d: Classes.Derived; } -fn Use(v: Intermediate*) { +fn Use(v: Classes.Intermediate*) { v->Fn(); } -// CHECK:STDOUT: ; ModuleID = 'virtual.carbon' -// CHECK:STDOUT: source_filename = "virtual.carbon" +// --- member_init.carbon + +package MemberInit; + +base class Base { + var m: i32; + virtual fn Fn(); +} + +fn Fn() { + var i: i32 = 3; + // TODO: make `i` initialize `m` here (it currently initializes the vptr instead) + var v: Base = {.m = i}; + // TODO: fix crash here + // var v: Base = {.m = 3}; + v.m = 5; +} + +// CHECK:STDOUT: ; ModuleID = 'classes.carbon' +// CHECK:STDOUT: source_filename = "classes.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Intermediate.Main() +// CHECK:STDOUT: declare void @_CFn.Intermediate.Classes() // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Derived.Main() +// CHECK:STDOUT: declare void @_CFn.Derived.Classes() // CHECK:STDOUT: -// CHECK:STDOUT: define void @_CCreate.Main() !dbg !4 { +// 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, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "classes.carbon", directory: "") +// CHECK:STDOUT: ; ModuleID = 'create.carbon' +// CHECK:STDOUT: source_filename = "create.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CCreate.Create() !dbg !4 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %b.var = alloca {}, align 8, !dbg !7 // CHECK:STDOUT: %i.var = alloca { ptr, {} }, align 8, !dbg !8 @@ -44,9 +84,46 @@ fn Use(v: Intermediate*) { // CHECK:STDOUT: ret void, !dbg !10 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define void @_CUse.Main(ptr %v) !dbg !11 { +// CHECK:STDOUT: define void @_CUse.Create(ptr %v) !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CFn.Intermediate.Classes(), !dbg !12 +// CHECK:STDOUT: ret void, !dbg !13 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CFn.Intermediate.Classes() +// 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, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "create.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Create", linkageName: "_CCreate.Create", scope: null, file: !3, line: 6, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 7, column: 7, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 8, column: 7, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 9, column: 7, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 1, scope: !4) +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Create", scope: null, file: !3, line: 12, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 3, scope: !11) +// CHECK:STDOUT: !13 = !DILocation(line: 12, column: 1, scope: !11) +// CHECK:STDOUT: ; ModuleID = 'member_init.carbon' +// CHECK:STDOUT: source_filename = "member_init.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CFn.Base.MemberInit() +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CFn.MemberInit() !dbg !4 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @_CFn.Intermediate.Main(), !dbg !12 +// CHECK:STDOUT: %i.var = alloca i32, align 4, !dbg !7 +// CHECK:STDOUT: store i32 3, ptr %i.var, align 4, !dbg !8 +// CHECK:STDOUT: %v.var = alloca { ptr, i32 }, align 8, !dbg !9 +// CHECK:STDOUT: %.loc12_23 = load i32, ptr %i.var, align 4, !dbg !10 +// CHECK:STDOUT: %.loc12_24.2.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !11 +// CHECK:STDOUT: store i32 %.loc12_23, ptr %.loc12_24.2.m, align 4, !dbg !11 +// CHECK:STDOUT: %.loc15_4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !12 +// CHECK:STDOUT: store i32 5, ptr %.loc15_4.m, align 4, !dbg !12 // CHECK:STDOUT: ret void, !dbg !13 // CHECK:STDOUT: } // CHECK:STDOUT: @@ -56,14 +133,14 @@ fn Use(v: Intermediate*) { // 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, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) -// CHECK:STDOUT: !3 = !DIFile(filename: "virtual.carbon", directory: "") -// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Create", linkageName: "_CCreate.Main", scope: null, file: !3, line: 22, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !3 = !DIFile(filename: "member_init.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Fn", linkageName: "_CFn.MemberInit", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) // CHECK:STDOUT: !6 = !{} -// CHECK:STDOUT: !7 = !DILocation(line: 23, column: 7, scope: !4) -// CHECK:STDOUT: !8 = !DILocation(line: 24, column: 7, scope: !4) -// CHECK:STDOUT: !9 = !DILocation(line: 25, column: 7, scope: !4) -// CHECK:STDOUT: !10 = !DILocation(line: 22, column: 1, scope: !4) -// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Main", scope: null, file: !3, line: 28, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !12 = !DILocation(line: 29, column: 3, scope: !11) -// CHECK:STDOUT: !13 = !DILocation(line: 28, column: 1, scope: !11) +// CHECK:STDOUT: !7 = !DILocation(line: 10, column: 7, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 10, column: 3, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 12, column: 7, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 12, column: 23, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 17, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 15, column: 3, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 9, column: 1, scope: !4) diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 9fcc48d49cc5..c0e25d15ae2c 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -805,12 +805,17 @@ constexpr TypeBlockId TypeBlockId::Empty = TypeBlockId(0); struct ElementIndex : public IndexBase, public Printable { using IndexBase::IndexBase; + // An explicitly invalid ID. + static const ElementIndex Invalid; + auto Print(llvm::raw_ostream& out) const -> void { out << "element"; IndexBase::Print(out); } }; +constexpr ElementIndex ElementIndex::Invalid = ElementIndex(InvalidIndex); + // The ID of a library name. This is either a string literal or `default`. struct LibraryNameId : public IdBase, public Printable { using DiagnosticType = DiagnosticTypeInfo;