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`.