From 33c1e9ca95ad44b46d4ba4b7397166ff6a1b2996 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 27 Feb 2024 17:56:27 -0800 Subject: [PATCH] Add an associated entity instruction and corresponding type for interface elements. (#3730) When declaring an associated entity in an interface -- just associated functions for now -- create an associated entity value and corresponding type to represent a "slot in a witness table". Also track the list of associated entities on the interface so that we will eventually be able to check impls against them. Associated entities are represented as the integer index of their slot in a witness table. --------- Co-authored-by: Jon Ross-Perkins --- toolchain/check/BUILD | 15 ++++++ toolchain/check/context.cpp | 15 ++++-- toolchain/check/context.h | 4 ++ toolchain/check/eval.cpp | 4 ++ toolchain/check/handle_function.cpp | 21 +++++++- toolchain/check/handle_interface.cpp | 9 +++- toolchain/check/import_ref.cpp | 45 +++++++++++----- toolchain/check/interface.cpp | 36 +++++++++++++ toolchain/check/interface.h | 22 ++++++++ toolchain/check/testdata/impl/basic.carbon | 8 ++- .../check/testdata/impl/declaration.carbon | 1 + toolchain/check/testdata/impl/empty.carbon | 1 + .../impl/fail_extend_impl_forall.carbon | 10 ++-- .../impl/fail_extend_impl_scope.carbon | 1 + .../impl/fail_extend_impl_type_as.carbon | 1 + ..._extend_partially_defined_interface.carbon | 1 + .../testdata/impl/fail_impl_as_scope.carbon | 8 ++- .../testdata/impl/fail_impl_bad_type.carbon | 1 + .../testdata/impl/fail_redefinition.carbon | 1 + ...pl.carbon => fail_todo_extend_impl.carbon} | 32 +++++++----- toolchain/check/testdata/impl/impl_as.carbon | 10 ++-- .../check/testdata/impl/impl_forall.carbon | 8 ++- .../check/testdata/impl/redeclaration.carbon | 1 + .../check/testdata/interface/as_type.carbon | 1 + .../check/testdata/interface/basic.carbon | 7 ++- .../fail_add_member_outside_definition.carbon | 3 ++ .../interface/fail_as_type_of_type.carbon | 1 + .../testdata/interface/fail_duplicate.carbon | 4 +- .../interface/fail_lookup_undefined.carbon | 6 ++- ...ookup.carbon => fail_member_lookup.carbon} | 16 +++--- .../testdata/interface/fail_modifiers.carbon | 2 + .../interface/fail_redeclare_member.carbon | 44 ++++++++++++++++ .../fail_todo_define_out_of_line.carbon | 51 +++++++++++++++++++ .../interface/fail_todo_facet_lookup.carbon | 8 ++- .../interface/fail_todo_modifiers.carbon | 15 ++++-- .../check/testdata/interface/import.carbon | 24 ++++++--- toolchain/lower/file_context.cpp | 9 ++++ toolchain/lower/handle.cpp | 12 +++-- toolchain/lower/handle_type.cpp | 6 +++ .../lower/testdata/interface/assoc.carbon | 20 ++++++++ .../lower/testdata/interface/basic.carbon | 31 +++++++++++ toolchain/sem_ir/file.cpp | 19 +++++++ toolchain/sem_ir/formatter.cpp | 2 + toolchain/sem_ir/inst.h | 10 ++++ toolchain/sem_ir/inst_kind.def | 2 + toolchain/sem_ir/interface.h | 10 +++- toolchain/sem_ir/name_scope.h | 10 ++++ toolchain/sem_ir/typed_insts.h | 26 ++++++++++ 48 files changed, 522 insertions(+), 72 deletions(-) create mode 100644 toolchain/check/interface.cpp create mode 100644 toolchain/check/interface.h rename toolchain/check/testdata/impl/{extend_impl.carbon => fail_todo_extend_impl.carbon} (56%) rename toolchain/check/testdata/interface/{member_lookup.carbon => fail_member_lookup.carbon} (61%) create mode 100644 toolchain/check/testdata/interface/fail_redeclare_member.carbon create mode 100644 toolchain/check/testdata/interface/fail_todo_define_out_of_line.carbon create mode 100644 toolchain/lower/testdata/interface/assoc.carbon create mode 100644 toolchain/lower/testdata/interface/basic.carbon diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 1eb437c1ff77..161b1ec462b1 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -102,6 +102,7 @@ cc_library( deps = [ ":context", ":import", + ":interface", "//common:check", "//common:ostream", "//toolchain/base:pretty_stack_trace_function", @@ -147,6 +148,20 @@ cc_library( ], ) +cc_library( + name = "interface", + srcs = ["interface.cpp"], + hdrs = ["interface.h"], + deps = [ + ":context", + "//common:check", + "//toolchain/sem_ir:file", + "//toolchain/sem_ir:ids", + "//toolchain/sem_ir:inst", + "//toolchain/sem_ir:inst_kind", + ], +) + glob_sh_run( args = [ "$(location //toolchain/driver:carbon)", diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index f55c1d8ff5df..7a8fdd82f4f4 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -173,7 +173,7 @@ auto Context::NoteUndefinedInterface(SemIR::InterfaceId interface_id, DiagnosticBuilder& builder) -> void { const auto& interface_info = interfaces().Get(interface_id); CARBON_CHECK(!interface_info.is_defined()) << "Interface is not incomplete"; - if (interface_info.definition_id.is_valid()) { + if (interface_info.is_being_defined()) { CARBON_DIAGNOSTIC(InterfaceUndefinedWithinDefinition, Note, "Interface is currently being defined."); builder.Note(interface_info.definition_id, @@ -813,16 +813,13 @@ class TypeCompleter { // types, as found by AddNestedIncompleteTypes, are known to be complete. auto BuildValueRepr(SemIR::TypeId type_id, SemIR::Inst inst) const -> SemIR::ValueRepr { - // TODO: This can emit new SemIR instructions. Consider emitting them into a - // dedicated file-scope instruction block where possible, or somewhere else - // that better reflects the definition of the type, rather than wherever the - // type happens to first be required to be complete. switch (inst.kind()) { case SemIR::AddrOf::Kind: case SemIR::AddrPattern::Kind: case SemIR::ArrayIndex::Kind: case SemIR::ArrayInit::Kind: case SemIR::Assign::Kind: + case SemIR::AssociatedEntity::Kind: case SemIR::BaseDecl::Kind: case SemIR::BindAlias::Kind: case SemIR::BindName::Kind: @@ -908,6 +905,7 @@ class TypeCompleter { case SemIR::Builtin::Kind: return BuildBuiltinValueRepr(type_id, inst.As()); + case SemIR::AssociatedEntityType::Kind: case SemIR::BindSymbolicName::Kind: case SemIR::PointerType::Kind: case SemIR::UnboundElementType::Kind: @@ -980,6 +978,13 @@ auto Context::GetTupleType(llvm::ArrayRef type_ids) return GetTypeImpl(*this, type_blocks().Add(type_ids)); } +auto Context::GetAssociatedEntityType(SemIR::InterfaceId interface_id, + SemIR::TypeId entity_type_id) + -> SemIR::TypeId { + return GetTypeImpl(*this, interface_id, + entity_type_id); +} + auto Context::GetBuiltinType(SemIR::BuiltinKind kind) -> SemIR::TypeId { CARBON_CHECK(kind != SemIR::BuiltinKind::Invalid); auto type_id = GetTypeIdForTypeConstant( diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 134b5ff46ca1..b24759ee2bc6 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -234,6 +234,10 @@ class Context { // TODO: Consider moving these `Get*Type` functions to a separate class. + // Gets the type for the name of an associated entity. + auto GetAssociatedEntityType(SemIR::InterfaceId interface_id, + SemIR::TypeId entity_type_id) -> SemIR::TypeId; + // Gets a builtin type. The returned type will be complete. auto GetBuiltinType(SemIR::BuiltinKind kind) -> SemIR::TypeId; diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index d9f4722edd59..9ee1c427d5c6 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -322,6 +322,9 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) return true; }, &SemIR::ArrayType::bound_id, &SemIR::ArrayType::element_type_id); + case SemIR::AssociatedEntityType::Kind: + return RebuildIfFieldsAreConstant( + context, inst, &SemIR::AssociatedEntityType::entity_type_id); case SemIR::BoundMethod::Kind: return RebuildIfFieldsAreConstant(context, inst, &SemIR::BoundMethod::object_id, @@ -363,6 +366,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) case SemIR::TupleInit::Kind: return RebuildInitAsValue(context, inst, SemIR::TupleValue::Kind); + case SemIR::AssociatedEntity::Kind: case SemIR::Builtin::Kind: // Builtins are always template constants. return MakeConstantResult(context, inst, Phase::Template); diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index fa36daee5185..ce399b5e4c9e 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -5,6 +5,7 @@ #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/decl_name_stack.h" +#include "toolchain/check/interface.h" #include "toolchain/check/modifiers.h" #include "toolchain/parse/tree_node_location_translator.h" #include "toolchain/sem_ir/entry_point.h" @@ -126,9 +127,24 @@ static auto BuildFunctionDecl(Context& context, auto function_decl_id = context.AddPlaceholderInst({parse_node, function_decl}); + // At interface scope, a function declaration introduces an associated + // function. + auto lookup_result_id = function_decl_id; + if (name_context.enclosing_scope_id_for_new_inst().is_valid() && + !name_context.has_qualifiers) { + auto scope_inst_id = context.name_scopes().GetInstIdIfValid( + name_context.enclosing_scope_id_for_new_inst()); + if (auto interface_scope = + context.insts().TryGetAsIfValid( + scope_inst_id)) { + lookup_result_id = BuildAssociatedEntity( + context, interface_scope->interface_id, function_decl_id); + } + } + // Check whether this is a redeclaration. auto existing_id = - context.decl_name_stack().LookupOrAddName(name_context, function_decl_id); + context.decl_name_stack().LookupOrAddName(name_context, lookup_result_id); if (existing_id.is_valid()) { if (auto existing_function_decl = context.insts().Get(existing_id).TryAs()) { @@ -136,6 +152,7 @@ static auto BuildFunctionDecl(Context& context, function_decl.function_id = existing_function_decl->function_id; // TODO: Check that the signature matches! + // TODO: Disallow redeclarations within classes? // Track the signature from the definition, so that IDs in the body match // IDs in the signature. @@ -149,6 +166,8 @@ static auto BuildFunctionDecl(Context& context, } } else { // This is a redeclaration of something other than a function. + // This includes the case where an associated function redeclares another + // associated function. context.DiagnoseDuplicateName(function_decl_id, existing_id); } } diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index 8e643c9d4a7f..83005c24f925 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -130,7 +130,9 @@ auto HandleInterfaceDefinitionStart( context.inst_block_stack().Push(); context.node_stack().Push(parse_node, interface_id); - // TODO: Perhaps use the args_type_info_stack for a witness table. + + // We use the arg stack to build the witness table type. + context.args_type_info_stack().Push(); // TODO: Handle the case where there's control flow in the interface body. For // example: @@ -153,10 +155,13 @@ auto HandleInterfaceDefinition(Context& context, context.inst_block_stack().Pop(); context.scope_stack().Pop(); context.decl_name_stack().PopScope(); + auto associated_entities_id = context.args_type_info_stack().Pop(); // The interface type is now fully defined. auto& interface_info = context.interfaces().Get(interface_id); - interface_info.defined = true; + if (!interface_info.associated_entities_id.is_valid()) { + interface_info.associated_entities_id = associated_entities_id; + } return true; } diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 75c676652848..75b1f9916e08 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -237,13 +237,9 @@ class ImportRefResolver { } // Adds ImportRefUnused entries for members of the imported scope, for name - // lookup. Returns the block used for the refs, primarily for textual IR - // formatting. + // lookup. auto AddNameScopeImportRefs(const SemIR::NameScope& import_scope, - SemIR::NameScope& new_scope) - -> SemIR::InstBlockId { - // Push a block so that we can add scoped instructions to it. - context_.inst_block_stack().Push(); + SemIR::NameScope& new_scope) -> void { for (auto [entry_name_id, entry_inst_id] : import_scope.names) { auto ref_id = context_.AddPlaceholderInst( SemIR::ImportRefUnused{import_ir_id_, entry_inst_id}); @@ -251,7 +247,24 @@ class ImportRefResolver { new_scope.names.insert({GetLocalNameId(entry_name_id), ref_id}) .second); } - return context_.inst_block_stack().Pop(); + } + + // Given a block ID for a list of associated entities of a witness, returns a + // version localized to the current IR. + auto AddAssociatedEntities(SemIR::InstBlockId associated_entities_id) + -> SemIR::InstBlockId { + if (associated_entities_id == SemIR::InstBlockId::Empty) { + return SemIR::InstBlockId::Empty; + } + auto associated_entities = + import_ir_.inst_blocks().Get(associated_entities_id); + llvm::SmallVector new_associated_entities; + new_associated_entities.reserve(associated_entities.size()); + for (auto inst_id : associated_entities) { + new_associated_entities.push_back(context_.AddPlaceholderInst( + SemIR::ImportRefUnused{import_ir_id_, inst_id})); + } + return context_.inst_blocks().Add(new_associated_entities); } // Tries to resolve the InstId, returning a constant when ready, or Invalid if @@ -411,7 +424,11 @@ class ImportRefResolver { auto& new_scope = context_.name_scopes().Get(new_class.scope_id); const auto& import_scope = import_ir_.name_scopes().Get(import_class.scope_id); - new_class.body_block_id = AddNameScopeImportRefs(import_scope, new_scope); + + // Push a block so that we can add scoped instructions to it. + context_.inst_block_stack().Push(); + AddNameScopeImportRefs(import_scope, new_scope); + new_class.body_block_id = context_.inst_block_stack().Pop(); if (import_class.base_id.is_valid()) { new_class.base_id = base_const_id.inst_id(); @@ -591,12 +608,16 @@ class ImportRefResolver { auto& new_scope = context_.name_scopes().Get(new_interface.scope_id); const auto& import_scope = import_ir_.name_scopes().Get(import_interface.scope_id); - new_interface.body_block_id = - AddNameScopeImportRefs(import_scope, new_scope); + + // Push a block so that we can add scoped instructions to it. + context_.inst_block_stack().Push(); + AddNameScopeImportRefs(import_scope, new_scope); + new_interface.associated_entities_id = + AddAssociatedEntities(import_interface.associated_entities_id); + new_interface.body_block_id = context_.inst_block_stack().Pop(); + CARBON_CHECK(import_scope.extended_scopes.empty()) << "Interfaces don't currently have extended scopes to support."; - - new_interface.defined = true; } // Write the interface ID into the InterfaceDecl. diff --git a/toolchain/check/interface.cpp b/toolchain/check/interface.cpp new file mode 100644 index 000000000000..906ea8a100f5 --- /dev/null +++ b/toolchain/check/interface.cpp @@ -0,0 +1,36 @@ +// 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 "toolchain/check/interface.h" + +#include "toolchain/check/context.h" +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/typed_insts.h" + +namespace Carbon::Check { + +auto BuildAssociatedEntity(Context& context, SemIR::InterfaceId interface_id, + SemIR::InstId decl_id) -> SemIR::InstId { + auto& interface_info = context.interfaces().Get(interface_id); + if (!interface_info.is_being_defined()) { + // This should only happen if the interface is erroneously defined more than + // once. + // TODO: Find a way to CHECK this. + return SemIR::InstId::BuiltinError; + } + + // Register this declaration as declaring an associated entity. + auto index = SemIR::ElementIndex( + context.args_type_info_stack().PeekCurrentBlockContents().size()); + context.args_type_info_stack().AddInstId(decl_id); + + // Name lookup for the declaration's name should name the associated entity, + // not the declaration itself. + auto type_id = context.GetAssociatedEntityType( + interface_id, context.insts().Get(decl_id).type_id()); + return context.AddInst({context.insts().GetParseNode(decl_id), + SemIR::AssociatedEntity{type_id, index, decl_id}}); +} + +} // namespace Carbon::Check diff --git a/toolchain/check/interface.h b/toolchain/check/interface.h new file mode 100644 index 000000000000..ae4b9dd1e244 --- /dev/null +++ b/toolchain/check/interface.h @@ -0,0 +1,22 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ +#define CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ + +#include "toolchain/check/context.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::Check { + +// Builds and returns an associated entity for `interface_id` corresponding to +// the declaration `decl_id`, which can be an associated function or an +// associated constant. Registers the associated entity in the list for the +// interface. +auto BuildAssociatedEntity(Context& context, SemIR::InterfaceId interface_id, + SemIR::InstId decl_id) -> SemIR::InstId; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ diff --git a/toolchain/check/testdata/impl/basic.carbon b/toolchain/check/testdata/impl/basic.carbon index 3778fa4b9a7e..e7458d9429df 100644 --- a/toolchain/check/testdata/impl/basic.carbon +++ b/toolchain/check/testdata/impl/basic.carbon @@ -16,19 +16,23 @@ impl i32 as Simple { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Simple [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] +// CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.Simple = %Simple.decl} [template] // CHECK:STDOUT: %Simple.decl = interface_decl @Simple, () [template = constants.%.1] -// CHECK:STDOUT: impl_decl @impl, () +// CHECK:STDOUT: impl_decl @impl, () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %F: = fn_decl @F.1 [template] +// CHECK:STDOUT: %.loc8: in Simple> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as Simple { diff --git a/toolchain/check/testdata/impl/declaration.carbon b/toolchain/check/testdata/impl/declaration.carbon index b5874fb0f0a9..ba72a4234234 100644 --- a/toolchain/check/testdata/impl/declaration.carbon +++ b/toolchain/check/testdata/impl/declaration.carbon @@ -23,6 +23,7 @@ impl i32 as I; // CHECK:STDOUT: interface @I { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as I; diff --git a/toolchain/check/testdata/impl/empty.carbon b/toolchain/check/testdata/impl/empty.carbon index 8bc787c98cf5..f0f588c13482 100644 --- a/toolchain/check/testdata/impl/empty.carbon +++ b/toolchain/check/testdata/impl/empty.carbon @@ -25,6 +25,7 @@ impl i32 as Empty { // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as Empty { diff --git a/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon b/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon index 25efc3bfd7dd..050c5e784992 100644 --- a/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon +++ b/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon @@ -27,8 +27,10 @@ class C { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @GenericInterface [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @GenericInterface, [template] +// CHECK:STDOUT: %.3: in GenericInterface> = assoc_entity element0, @GenericInterface.%F [template] // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.4: type = struct_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -39,9 +41,11 @@ class C { // CHECK:STDOUT: // CHECK:STDOUT: interface @GenericInterface { // CHECK:STDOUT: %F: = fn_decl @F.1 [template] +// CHECK:STDOUT: %.loc11: in GenericInterface> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc11 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: C as { @@ -52,7 +56,7 @@ class C { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: impl_decl @impl, (, , , ) +// CHECK:STDOUT: impl_decl @impl, (, , , ) // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: has_error diff --git a/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon b/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon index 7c569c519941..fa3ece87cd93 100644 --- a/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon +++ b/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon @@ -26,6 +26,7 @@ extend impl i32 as I {} // CHECK:STDOUT: interface @I { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as I { diff --git a/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon b/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon index 2516664a6642..f5b93a86b9c7 100644 --- a/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon +++ b/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon @@ -54,6 +54,7 @@ class E { // CHECK:STDOUT: interface @I { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.1: i32 as I { diff --git a/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon b/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon index 64940b80c3fa..aeb451e5ccdd 100644 --- a/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon +++ b/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon @@ -34,6 +34,7 @@ interface I { // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: C as I; diff --git a/toolchain/check/testdata/impl/fail_impl_as_scope.carbon b/toolchain/check/testdata/impl/fail_impl_as_scope.carbon index 4469ce182c49..ec62923d1e51 100644 --- a/toolchain/check/testdata/impl/fail_impl_as_scope.carbon +++ b/toolchain/check/testdata/impl/fail_impl_as_scope.carbon @@ -19,19 +19,23 @@ impl as Simple { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Simple [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] +// CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.Simple = %Simple.decl} [template] // CHECK:STDOUT: %Simple.decl = interface_decl @Simple, () [template = constants.%.1] -// CHECK:STDOUT: impl_decl @impl, () +// CHECK:STDOUT: impl_decl @impl, () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %F: = fn_decl @F.1 [template] +// CHECK:STDOUT: %.loc8: in Simple> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: as Simple { diff --git a/toolchain/check/testdata/impl/fail_impl_bad_type.carbon b/toolchain/check/testdata/impl/fail_impl_bad_type.carbon index 76b4792d951d..5d3456d53f73 100644 --- a/toolchain/check/testdata/impl/fail_impl_bad_type.carbon +++ b/toolchain/check/testdata/impl/fail_impl_bad_type.carbon @@ -27,6 +27,7 @@ impl true as I {} // CHECK:STDOUT: interface @I { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: as I { diff --git a/toolchain/check/testdata/impl/fail_redefinition.carbon b/toolchain/check/testdata/impl/fail_redefinition.carbon index aba88d0a4ca9..b44c8ef20ff1 100644 --- a/toolchain/check/testdata/impl/fail_redefinition.carbon +++ b/toolchain/check/testdata/impl/fail_redefinition.carbon @@ -32,6 +32,7 @@ impl i32 as I {} // CHECK:STDOUT: interface @I { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as I { diff --git a/toolchain/check/testdata/impl/extend_impl.carbon b/toolchain/check/testdata/impl/fail_todo_extend_impl.carbon similarity index 56% rename from toolchain/check/testdata/impl/extend_impl.carbon rename to toolchain/check/testdata/impl/fail_todo_extend_impl.carbon index 06bbd0321dd3..dfc22bc3fe13 100644 --- a/toolchain/check/testdata/impl/extend_impl.carbon +++ b/toolchain/check/testdata/impl/fail_todo_extend_impl.carbon @@ -15,20 +15,28 @@ class C { } fn G(c: C) { - // TODO: These don't do impl lookup, and so refer to the interface members - // rather than to the impl members. + // TODO: These should do impl lookup. Because that's not implemented yet, they + // refer to the interface members rather than to the impl members. + // CHECK:STDERR: fail_todo_extend_impl.carbon:[[@LINE+3]]:3: ERROR: Value of type ` in HasF>` is not callable. + // CHECK:STDERR: C.F(); + // CHECK:STDERR: ^~~~ C.F(); + // CHECK:STDERR: fail_todo_extend_impl.carbon:[[@LINE+3]]:3: ERROR: Value of type ` in HasF>` is not callable. + // CHECK:STDERR: c.F(); + // CHECK:STDERR: ^~~~ c.F(); } -// CHECK:STDOUT: --- extend_impl.carbon +// CHECK:STDOUT: --- fail_todo_extend_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @HasF [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @HasF, [template] +// CHECK:STDOUT: %.3: in HasF> = assoc_entity element0, @HasF.%F [template] // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.2: type = struct_type {} [template] -// CHECK:STDOUT: %.3: type = tuple_type () [template] -// CHECK:STDOUT: %.4: type = ptr_type {} [template] +// CHECK:STDOUT: %.4: type = struct_type {} [template] +// CHECK:STDOUT: %.5: type = tuple_type () [template] +// CHECK:STDOUT: %.6: type = ptr_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -40,9 +48,11 @@ fn G(c: C) { // CHECK:STDOUT: // CHECK:STDOUT: interface @HasF { // CHECK:STDOUT: %F: = fn_decl @F.1 [template] +// CHECK:STDOUT: %.loc8: in HasF> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: C as HasF { @@ -53,7 +63,7 @@ fn G(c: C) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: impl_decl @impl, () +// CHECK:STDOUT: impl_decl @impl, () // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: extend name_scope1 @@ -69,11 +79,9 @@ fn G(c: C) { // CHECK:STDOUT: fn @G(%c: C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] -// CHECK:STDOUT: %F.ref.loc20: = name_ref F, @HasF.%F [template = @HasF.%F] -// CHECK:STDOUT: %.loc20: init () = call %F.ref.loc20() +// CHECK:STDOUT: %F.ref.loc23: in HasF> = name_ref F, @HasF.%.loc8 [template = constants.%.3] // CHECK:STDOUT: %c.ref: C = name_ref c, %c -// CHECK:STDOUT: %F.ref.loc21: = name_ref F, @HasF.%F [template = @HasF.%F] -// CHECK:STDOUT: %.loc21: init () = call %F.ref.loc21() +// CHECK:STDOUT: %F.ref.loc27: in HasF> = name_ref F, @HasF.%.loc8 [template = constants.%.3] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/impl_as.carbon b/toolchain/check/testdata/impl/impl_as.carbon index c479537b3aa8..bedee6e304cf 100644 --- a/toolchain/check/testdata/impl/impl_as.carbon +++ b/toolchain/check/testdata/impl/impl_as.carbon @@ -18,8 +18,10 @@ class C { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Simple [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] +// CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.4: type = struct_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -30,9 +32,11 @@ class C { // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %F: = fn_decl @F.1 [template] +// CHECK:STDOUT: %.loc8: in Simple> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: C as Simple { @@ -43,7 +47,7 @@ class C { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: impl_decl @impl, () +// CHECK:STDOUT: impl_decl @impl, () // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/impl/impl_forall.carbon b/toolchain/check/testdata/impl/impl_forall.carbon index 15122ac05e96..6746bb33cdbf 100644 --- a/toolchain/check/testdata/impl/impl_forall.carbon +++ b/toolchain/check/testdata/impl/impl_forall.carbon @@ -16,19 +16,23 @@ impl forall [T:! type] T as Simple { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Simple [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] +// CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.Simple = %Simple.decl} [template] // CHECK:STDOUT: %Simple.decl = interface_decl @Simple, () [template = constants.%.1] -// CHECK:STDOUT: impl_decl @impl, (, , , ) +// CHECK:STDOUT: impl_decl @impl, (, , , ) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %F: = fn_decl @F.1 [template] +// CHECK:STDOUT: %.loc8: in Simple> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: T as Simple { diff --git a/toolchain/check/testdata/impl/redeclaration.carbon b/toolchain/check/testdata/impl/redeclaration.carbon index 5b9016be7497..58e0949b7120 100644 --- a/toolchain/check/testdata/impl/redeclaration.carbon +++ b/toolchain/check/testdata/impl/redeclaration.carbon @@ -33,6 +33,7 @@ impl i32 as I {} // CHECK:STDOUT: interface @I { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as I { diff --git a/toolchain/check/testdata/interface/as_type.carbon b/toolchain/check/testdata/interface/as_type.carbon index 809821782a52..9c8c6e07f0e3 100644 --- a/toolchain/check/testdata/interface/as_type.carbon +++ b/toolchain/check/testdata/interface/as_type.carbon @@ -24,6 +24,7 @@ fn F(e: Empty) {} // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%e: Empty) { diff --git a/toolchain/check/testdata/interface/basic.carbon b/toolchain/check/testdata/interface/basic.carbon index 1cc4986d4cf9..f9397cc3116d 100644 --- a/toolchain/check/testdata/interface/basic.carbon +++ b/toolchain/check/testdata/interface/basic.carbon @@ -18,6 +18,8 @@ interface ForwardDeclared { // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Empty [template] // CHECK:STDOUT: %.2: type = interface_type @ForwardDeclared [template] +// CHECK:STDOUT: %.3: type = assoc_entity_type @ForwardDeclared, [template] +// CHECK:STDOUT: %.4: in ForwardDeclared> = assoc_entity element0, @ForwardDeclared.%F [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -30,13 +32,16 @@ interface ForwardDeclared { // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @ForwardDeclared { // CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: %.loc13: in ForwardDeclared> = assoc_entity element0, %F [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc13 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); diff --git a/toolchain/check/testdata/interface/fail_add_member_outside_definition.carbon b/toolchain/check/testdata/interface/fail_add_member_outside_definition.carbon index 51afa962d2ec..6e25f79615b7 100644 --- a/toolchain/check/testdata/interface/fail_add_member_outside_definition.carbon +++ b/toolchain/check/testdata/interface/fail_add_member_outside_definition.carbon @@ -44,6 +44,7 @@ interface Outer { // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = file.%F +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Outer { @@ -52,6 +53,7 @@ interface Outer { // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Inner = %Inner.decl +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Inner { @@ -59,6 +61,7 @@ interface Outer { // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = @Outer.%F +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.1() { diff --git a/toolchain/check/testdata/interface/fail_as_type_of_type.carbon b/toolchain/check/testdata/interface/fail_as_type_of_type.carbon index 0a90b75b77cf..71b1baa86acc 100644 --- a/toolchain/check/testdata/interface/fail_as_type_of_type.carbon +++ b/toolchain/check/testdata/interface/fail_as_type_of_type.carbon @@ -30,6 +30,7 @@ fn F(T:! Empty) { // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%T: Empty) { diff --git a/toolchain/check/testdata/interface/fail_duplicate.carbon b/toolchain/check/testdata/interface/fail_duplicate.carbon index 397c3fdfcda2..874ee6a98376 100644 --- a/toolchain/check/testdata/interface/fail_duplicate.carbon +++ b/toolchain/check/testdata/interface/fail_duplicate.carbon @@ -59,7 +59,8 @@ interface Class { } // CHECK:STDOUT: %F: = fn_decl @F [template] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @.1; @@ -67,6 +68,7 @@ interface Class { } // CHECK:STDOUT: interface @.2 { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class; diff --git a/toolchain/check/testdata/interface/fail_lookup_undefined.carbon b/toolchain/check/testdata/interface/fail_lookup_undefined.carbon index 137d4347e76f..725464e9b2a5 100644 --- a/toolchain/check/testdata/interface/fail_lookup_undefined.carbon +++ b/toolchain/check/testdata/interface/fail_lookup_undefined.carbon @@ -46,6 +46,8 @@ interface BeingDefined { // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Undefined [template] // CHECK:STDOUT: %.2: type = interface_type @BeingDefined [template] +// CHECK:STDOUT: %.3: type = assoc_entity_type @BeingDefined, [template] +// CHECK:STDOUT: %.4: in BeingDefined> = assoc_entity element0, @BeingDefined.%H [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -60,10 +62,12 @@ interface BeingDefined { // CHECK:STDOUT: // CHECK:STDOUT: interface @BeingDefined { // CHECK:STDOUT: %H: = fn_decl @H [template] +// CHECK:STDOUT: %.loc37: in BeingDefined> = assoc_entity element0, %H [template = constants.%.4] // CHECK:STDOUT: %.loc41: = fn_decl @.2 [template] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .H = %H +// CHECK:STDOUT: .H = %.loc37 +// CHECK:STDOUT: witness = (%H) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @.1(); diff --git a/toolchain/check/testdata/interface/member_lookup.carbon b/toolchain/check/testdata/interface/fail_member_lookup.carbon similarity index 61% rename from toolchain/check/testdata/interface/member_lookup.carbon rename to toolchain/check/testdata/interface/fail_member_lookup.carbon index b3e960b05586..42db38287a87 100644 --- a/toolchain/check/testdata/interface/member_lookup.carbon +++ b/toolchain/check/testdata/interface/fail_member_lookup.carbon @@ -7,15 +7,18 @@ interface Interface { fn F(); } fn F() { - // TODO: This should not be valid by itself. + // CHECK:STDERR: fail_member_lookup.carbon:[[@LINE+3]]:3: ERROR: Value of type ` in Interface>` is not callable. + // CHECK:STDERR: Interface.F(); + // CHECK:STDERR: ^~~~~~~~~~~~ Interface.F(); } -// CHECK:STDOUT: --- member_lookup.carbon +// CHECK:STDOUT: --- fail_member_lookup.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Interface [template] -// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Interface, [template] +// CHECK:STDOUT: %.3: in Interface> = assoc_entity element0, @Interface.%F [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -26,9 +29,11 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: interface @Interface { // CHECK:STDOUT: %F: = fn_decl @F.1 [template] +// CHECK:STDOUT: %.loc7: in Interface> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc7 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.1(); @@ -36,8 +41,7 @@ fn F() { // CHECK:STDOUT: fn @F.2() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Interface.ref: type = name_ref Interface, file.%Interface.decl [template = constants.%.1] -// CHECK:STDOUT: %F.ref: = name_ref F, @Interface.%F [template = @Interface.%F] -// CHECK:STDOUT: %.loc11: init () = call %F.ref() +// CHECK:STDOUT: %F.ref: in Interface> = name_ref F, @Interface.%.loc7 [template = constants.%.3] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_modifiers.carbon b/toolchain/check/testdata/interface/fail_modifiers.carbon index 4d4c96fbc6b1..5828c73284c8 100644 --- a/toolchain/check/testdata/interface/fail_modifiers.carbon +++ b/toolchain/check/testdata/interface/fail_modifiers.carbon @@ -46,6 +46,7 @@ protected interface Protected; // CHECK:STDOUT: interface @Abstract { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Default; @@ -53,6 +54,7 @@ protected interface Protected; // CHECK:STDOUT: interface @Virtual { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Protected; diff --git a/toolchain/check/testdata/interface/fail_redeclare_member.carbon b/toolchain/check/testdata/interface/fail_redeclare_member.carbon new file mode 100644 index 000000000000..3aa22a0dd6ce --- /dev/null +++ b/toolchain/check/testdata/interface/fail_redeclare_member.carbon @@ -0,0 +1,44 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +interface Interface { + fn F(); + // CHECK:STDERR: fail_redeclare_member.carbon:[[@LINE+6]]:3: ERROR: Duplicate name being declared in the same scope. + // CHECK:STDERR: fn F(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_redeclare_member.carbon:[[@LINE-4]]:3: Name is previously declared here. + // CHECK:STDERR: fn F(); + // CHECK:STDERR: ^~~~~~~ + fn F(); +} + +// CHECK:STDOUT: --- fail_redeclare_member.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @Interface [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Interface, [template] +// CHECK:STDOUT: %.3: in Interface> = assoc_entity element0, @Interface.%F [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Interface = %Interface.decl} [template] +// CHECK:STDOUT: %Interface.decl = interface_decl @Interface, () [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Interface { +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: %.loc8: in Interface> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: %.loc15: = fn_decl @.1 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @.1(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_todo_define_out_of_line.carbon b/toolchain/check/testdata/interface/fail_todo_define_out_of_line.carbon new file mode 100644 index 000000000000..05b4fbc39449 --- /dev/null +++ b/toolchain/check/testdata/interface/fail_todo_define_out_of_line.carbon @@ -0,0 +1,51 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +interface Interface { + // CHECK:STDERR: fail_todo_define_out_of_line.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. + // CHECK:STDERR: default fn F(); + // CHECK:STDERR: ^~~~~~~ + default fn F(); +} + +// CHECK:STDERR: fail_todo_define_out_of_line.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: fn Interface.F() {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_todo_define_out_of_line.carbon:[[@LINE-6]]:3: Name is previously declared here. +// CHECK:STDERR: default fn F(); +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +fn Interface.F() {} + +// CHECK:STDOUT: --- fail_todo_define_out_of_line.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @Interface [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Interface, [template] +// CHECK:STDOUT: %.3: in Interface> = assoc_entity element0, @Interface.%F [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Interface = %Interface.decl} [template] +// CHECK:STDOUT: %Interface.decl = interface_decl @Interface, () [template = constants.%.1] +// CHECK:STDOUT: %.loc20: = fn_decl @.1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Interface { +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: %.loc11: in Interface> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %.loc11 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @.1() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_todo_facet_lookup.carbon b/toolchain/check/testdata/interface/fail_todo_facet_lookup.carbon index 25dd4521f403..a06cede5b98a 100644 --- a/toolchain/check/testdata/interface/fail_todo_facet_lookup.carbon +++ b/toolchain/check/testdata/interface/fail_todo_facet_lookup.carbon @@ -17,7 +17,9 @@ fn CallStatic(T:! Interface) { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Interface [template] -// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Interface, [template] +// CHECK:STDOUT: %.3: in Interface> = assoc_entity element0, @Interface.%F [template] +// CHECK:STDOUT: %.4: type = tuple_type () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -28,9 +30,11 @@ fn CallStatic(T:! Interface) { // CHECK:STDOUT: // CHECK:STDOUT: interface @Interface { // CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: %.loc7: in Interface> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc7 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); diff --git a/toolchain/check/testdata/interface/fail_todo_modifiers.carbon b/toolchain/check/testdata/interface/fail_todo_modifiers.carbon index b297c3a8b7b3..d518a1ce6a10 100644 --- a/toolchain/check/testdata/interface/fail_todo_modifiers.carbon +++ b/toolchain/check/testdata/interface/fail_todo_modifiers.carbon @@ -25,27 +25,34 @@ private interface Private { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Modifiers [template] -// CHECK:STDOUT: %.2: type = interface_type @Private [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Modifiers, [template] +// CHECK:STDOUT: %.3: in Modifiers> = assoc_entity element0, @Modifiers.%Final [template] +// CHECK:STDOUT: %.4: in Modifiers> = assoc_entity element1, @Modifiers.%Default [template] +// CHECK:STDOUT: %.5: type = interface_type @Private [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.Modifiers = %Modifiers.decl, .Private = %Private.decl} [template] // CHECK:STDOUT: %Modifiers.decl = interface_decl @Modifiers, () [template = constants.%.1] -// CHECK:STDOUT: %Private.decl = interface_decl @Private, () [template = constants.%.2] +// CHECK:STDOUT: %Private.decl = interface_decl @Private, () [template = constants.%.5] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Modifiers { // CHECK:STDOUT: %Final: = fn_decl @Final [template] +// CHECK:STDOUT: %.loc11: in Modifiers> = assoc_entity element0, %Final [template = constants.%.3] // CHECK:STDOUT: %Default: = fn_decl @Default [template] +// CHECK:STDOUT: %.loc15: in Modifiers> = assoc_entity element1, %Default [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Final = %Final -// CHECK:STDOUT: .Default = %Default +// CHECK:STDOUT: .Final = %.loc11 +// CHECK:STDOUT: .Default = %.loc15 +// CHECK:STDOUT: witness = (%Final, %Default) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Private { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Final() { diff --git a/toolchain/check/testdata/interface/import.carbon b/toolchain/check/testdata/interface/import.carbon index f6e0a6b4f0f5..db881cf961fe 100644 --- a/toolchain/check/testdata/interface/import.carbon +++ b/toolchain/check/testdata/interface/import.carbon @@ -35,9 +35,11 @@ var f: ForwardDeclared* = &f_ref.f; // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Empty [template] // CHECK:STDOUT: %.2: type = interface_type @ForwardDeclared [template] -// CHECK:STDOUT: %.3: type = struct_type {.f: ForwardDeclared} [template] -// CHECK:STDOUT: %.4: type = tuple_type () [template] -// CHECK:STDOUT: %.5: type = struct_type {.f: ()} [template] +// CHECK:STDOUT: %.3: type = assoc_entity_type @ForwardDeclared, [template] +// CHECK:STDOUT: %.4: in ForwardDeclared> = assoc_entity element0, @ForwardDeclared.%F [template] +// CHECK:STDOUT: %.5: type = struct_type {.f: ForwardDeclared} [template] +// CHECK:STDOUT: %.6: type = tuple_type () [template] +// CHECK:STDOUT: %.7: type = struct_type {.f: ()} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -46,7 +48,7 @@ var f: ForwardDeclared* = &f_ref.f; // CHECK:STDOUT: %ForwardDeclared.decl.loc7 = interface_decl @ForwardDeclared, () [template = constants.%.2] // CHECK:STDOUT: %ForwardDeclared.decl.loc9 = interface_decl @ForwardDeclared, () [template = constants.%.2] // CHECK:STDOUT: %ForwardDeclared.ref: type = name_ref ForwardDeclared, %ForwardDeclared.decl.loc7 [template = constants.%.2] -// CHECK:STDOUT: %.loc13: type = struct_type {.f: ForwardDeclared} [template = constants.%.3] +// CHECK:STDOUT: %.loc13: type = struct_type {.f: ForwardDeclared} [template = constants.%.5] // CHECK:STDOUT: %f_ref.var: ref {.f: ForwardDeclared} = var f_ref // CHECK:STDOUT: %f_ref: ref {.f: ForwardDeclared} = bind_name f_ref, %f_ref.var // CHECK:STDOUT: } @@ -54,13 +56,16 @@ var f: ForwardDeclared* = &f_ref.f; // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @ForwardDeclared { // CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: %.loc10: in ForwardDeclared> = assoc_entity element0, %F [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .F = %.loc10 +// CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); @@ -80,7 +85,7 @@ var f: ForwardDeclared* = &f_ref.f; // CHECK:STDOUT: package: = namespace {.Empty = %import_ref.1, .ForwardDeclared = %import_ref.2, .f_ref = %import_ref.3, .UseEmpty = %UseEmpty, .UseForwardDeclared = %UseForwardDeclared, .f = %f} [template] // CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.1] // CHECK:STDOUT: %import_ref.2: type = import_ref ir1, inst+3, used [template = constants.%.3] -// CHECK:STDOUT: %import_ref.3: ref {.f: ForwardDeclared} = import_ref ir1, inst+16, used +// CHECK:STDOUT: %import_ref.3: ref {.f: ForwardDeclared} = import_ref ir1, inst+19, used // CHECK:STDOUT: %UseEmpty: = fn_decl @UseEmpty [template] // CHECK:STDOUT: %UseForwardDeclared: = fn_decl @UseForwardDeclared [template] // CHECK:STDOUT: %ForwardDeclared.ref: type = name_ref ForwardDeclared, %import_ref.2 [template = constants.%.3] @@ -92,13 +97,16 @@ var f: ForwardDeclared* = &f_ref.f; // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: // CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = () // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @ForwardDeclared { -// CHECK:STDOUT: %import_ref = import_ref ir1, inst+6, unused +// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+8, unused +// CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+6, unused // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %import_ref +// CHECK:STDOUT: .F = %import_ref.1 +// CHECK:STDOUT: witness = (%import_ref.2) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @UseEmpty(%e: Empty) { diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index eeaade664eb6..35b2fc648289 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -11,6 +11,7 @@ #include "toolchain/sem_ir/entry_point.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/inst.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Lower { @@ -66,6 +67,10 @@ auto FileContext::GetGlobal(SemIR::InstId inst_id) -> llvm::Value* { return GetFunction(function_decl->function_id); } + if (target.Is()) { + return llvm::ConstantStruct::getAnon(llvm_context(), {}); + } + if (target.type_id() == SemIR::TypeId::TypeType) { return GetTypeAsValue(); } @@ -274,6 +279,10 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* { GetType(array_type.element_type_id), sem_ir_->GetArrayBoundValue(array_type.bound_id)); } + case SemIR::AssociatedEntityType::Kind: + // No runtime operations are provided on an associated entity name, so use + // an empty representation. + return llvm::StructType::get(*llvm_context_); case SemIR::BindSymbolicName::Kind: // Treat non-monomorphized type bindings as opaque. return llvm::StructType::get(*llvm_context_); diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 5ab88a712bbd..290bfbafb96b 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -60,6 +60,12 @@ auto HandleAssign(FunctionContext& context, SemIR::InstId /*inst_id*/, context.FinishInit(storage_type_id, inst.lhs_id, inst.rhs_id); } +auto HandleAssociatedEntity(FunctionContext& /*context*/, + SemIR::InstId /*inst_id*/, + SemIR::AssociatedEntity inst) -> void { + FatalErrorIfEncountered(inst); +} + auto HandleBindAlias(FunctionContext& context, SemIR::InstId inst_id, SemIR::BindAlias inst) -> void { auto type_inst_id = context.sem_ir().types().GetInstId(inst.type_id); @@ -229,9 +235,9 @@ auto HandleInitializeFrom(FunctionContext& context, SemIR::InstId /*inst_id*/, } auto HandleInterfaceDecl(FunctionContext& /*context*/, - SemIR::InstId /*inst_id*/, - SemIR::InterfaceDecl /*inst*/) -> void { - // No action to perform. + SemIR::InstId /*inst_id*/, SemIR::InterfaceDecl inst) + -> void { + FatalErrorIfEncountered(inst); } auto HandleIntLiteral(FunctionContext& context, SemIR::InstId inst_id, diff --git a/toolchain/lower/handle_type.cpp b/toolchain/lower/handle_type.cpp index 96279ad2471d..1174736822bc 100644 --- a/toolchain/lower/handle_type.cpp +++ b/toolchain/lower/handle_type.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/lower/function_context.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Lower { @@ -11,6 +12,11 @@ auto HandleArrayType(FunctionContext& context, SemIR::InstId inst_id, context.SetLocal(inst_id, context.GetTypeAsValue()); } +auto HandleAssociatedEntityType(FunctionContext& context, SemIR::InstId inst_id, + SemIR::AssociatedEntityType /*inst*/) -> void { + context.SetLocal(inst_id, context.GetTypeAsValue()); +} + auto HandleClassType(FunctionContext& context, SemIR::InstId inst_id, SemIR::ClassType /*inst*/) -> void { context.SetLocal(inst_id, context.GetTypeAsValue()); diff --git a/toolchain/lower/testdata/interface/assoc.carbon b/toolchain/lower/testdata/interface/assoc.carbon new file mode 100644 index 000000000000..ae427a84623e --- /dev/null +++ b/toolchain/lower/testdata/interface/assoc.carbon @@ -0,0 +1,20 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +interface I { + fn Assoc(); +} + +fn F() { I.Assoc; } + +// CHECK:STDOUT: ; ModuleID = 'assoc.carbon' +// CHECK:STDOUT: source_filename = "assoc.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @Assoc() +// CHECK:STDOUT: +// CHECK:STDOUT: define void @F() { +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/interface/basic.carbon b/toolchain/lower/testdata/interface/basic.carbon new file mode 100644 index 000000000000..9fb909e13883 --- /dev/null +++ b/toolchain/lower/testdata/interface/basic.carbon @@ -0,0 +1,31 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +interface I { + fn Assoc(); +} + +// There are no interesting runtime operations here, but there's no rule saying +// you can't pass a facet around at runtime, so make sure it works. +fn F(T: I) -> I { return T; } + +interface J; + +// Declared-but-not-defined interfaces are still complete types. +fn G(T: J) {} + +// CHECK:STDOUT: ; ModuleID = 'basic.carbon' +// CHECK:STDOUT: source_filename = "basic.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @Assoc() +// CHECK:STDOUT: +// CHECK:STDOUT: define void @F() { +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @G() { +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index d7b5fa7c11cc..107c67615298 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -191,6 +191,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { static auto GetTypePrecedence(InstKind kind) -> int { switch (kind) { case ArrayType::Kind: + case AssociatedEntityType::Kind: case BindAlias::Kind: case BindSymbolicName::Kind: case Builtin::Kind: @@ -212,6 +213,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { case ArrayIndex::Kind: case ArrayInit::Kind: case Assign::Kind: + case AssociatedEntity::Kind: case BaseDecl::Kind: case BindName::Kind: case BindValue::Kind: @@ -316,6 +318,20 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, } break; } + case AssociatedEntityType::Kind: { + auto assoc = inst.As(); + if (step.index == 0) { + out << ""; + } + break; + } case BindAlias::Kind: case BindSymbolicName::Kind: { auto name_id = inst.As().bind_name_id; @@ -437,6 +453,7 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, case ArrayIndex::Kind: case ArrayInit::Kind: case Assign::Kind: + case AssociatedEntity::Kind: case BaseDecl::Kind: case BindName::Kind: case BindValue::Kind: @@ -552,6 +569,8 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case AddrOf::Kind: case AddrPattern::Kind: case ArrayType::Kind: + case AssociatedEntity::Kind: + case AssociatedEntityType::Kind: case BindSymbolicName::Kind: case BindValue::Kind: case BlockArg::Kind: diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index b5d38ae9f650..825da1f71dd9 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -649,6 +649,8 @@ class Formatter { FormatCodeBlock(interface_info.body_block_id); out_ << "\n!members:"; FormatNameScope(interface_info.scope_id, "", "\n "); + out_ << "\n witness = "; + FormatArg(interface_info.associated_entities_id); out_ << "\n}\n"; } else { out_ << ";\n"; diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index 0114325ba317..ae010c1ee7b8 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -343,6 +343,16 @@ class InstStore { return Get(inst_id).TryAs(); } + // Returns the requested instruction as the specified type, if it is valid and + // of that type. Otherwise returns nullopt. + template + auto TryGetAsIfValid(InstId inst_id) const -> std::optional { + if (!inst_id.is_valid()) { + return std::nullopt; + } + return TryGetAs(inst_id); + } + auto GetParseNode(InstId inst_id) const -> Parse::NodeId { return parse_nodes_[inst_id.index]; } diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 92abe2abb93d..be427faa6824 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -23,6 +23,8 @@ CARBON_SEM_IR_INST_KIND(ArrayIndex) CARBON_SEM_IR_INST_KIND(ArrayInit) CARBON_SEM_IR_INST_KIND(ArrayType) CARBON_SEM_IR_INST_KIND(Assign) +CARBON_SEM_IR_INST_KIND(AssociatedEntity) +CARBON_SEM_IR_INST_KIND(AssociatedEntityType) CARBON_SEM_IR_INST_KIND(BaseDecl) CARBON_SEM_IR_INST_KIND(BindAlias) CARBON_SEM_IR_INST_KIND(BindName) diff --git a/toolchain/sem_ir/interface.h b/toolchain/sem_ir/interface.h index de625009a2d9..9aecae23c2ff 100644 --- a/toolchain/sem_ir/interface.h +++ b/toolchain/sem_ir/interface.h @@ -18,7 +18,13 @@ struct Interface : public Printable { // Determines whether this interface has been fully defined. This is false // until we reach the `}` of the interface definition. - auto is_defined() const -> bool { return defined; } + auto is_defined() const -> bool { return associated_entities_id.is_valid(); } + + // Determines whether we're currently defining the interface. This is true + // between the braces of the interface. + auto is_being_defined() const -> bool { + return definition_id.is_valid() && !is_defined(); + } // The following members always have values, and do not change throughout the // lifetime of the interface. @@ -41,7 +47,7 @@ struct Interface : public Printable { InstBlockId body_block_id = InstBlockId::Invalid; // The following members are set at the `}` of the interface definition. - bool defined = false; + InstBlockId associated_entities_id = InstBlockId::Invalid; }; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/name_scope.h b/toolchain/sem_ir/name_scope.h index 3f73348d6681..cfc16aaa9859 100644 --- a/toolchain/sem_ir/name_scope.h +++ b/toolchain/sem_ir/name_scope.h @@ -95,6 +95,16 @@ class NameScopeStore { return values_.Get(scope_id); } + // Returns the instruction owning the requested name scope, or an invalid + // instruction if the scope is either invalid or has no associated + // instruction. + auto GetInstIdIfValid(NameScopeId scope_id) const -> InstId { + if (!scope_id.is_valid()) { + return InstId::Invalid; + } + return Get(scope_id).inst_id; + } + auto OutputYaml() const -> Yaml::OutputMapping { return values_.OutputYaml(); } diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 93a46fd375b4..f6d189587485 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -154,6 +154,32 @@ struct Assign { InstId rhs_id; }; +// An associated entity declared in an interface. This is either an associated +// function or a non-function associated constant such as an associated type. +// This represents the entity before impl lookup is performed, and identifies +// the slot within a witness where the constant value will be found. +struct AssociatedEntity { + static constexpr auto Kind = + InstKind::AssociatedEntity.Define("assoc_entity"); + + // The type of the associated entity. This is an AssociatedEntityType. + TypeId type_id; + ElementIndex index; + InstId decl_id; +}; + +// The type of an expression that names an associated entity, such as +// `InterfaceName.Function`. +struct AssociatedEntityType { + static constexpr auto Kind = + InstKind::AssociatedEntityType.Define( + "assoc_entity_type"); + + TypeId type_id; + InterfaceId interface_id; + TypeId entity_type_id; +}; + // A base in a class, of the form `base: base_type;`. A base class is an // element of the derived class, and the type of the `BaseDecl` instruction is // an `UnboundElementType`.