From 3b0923c81d370354a10d66dd7c5e5195e46dbe28 Mon Sep 17 00:00:00 2001 From: josh11b Date: Fri, 8 Dec 2023 09:13:49 -0800 Subject: [PATCH] Add interface support to check (#3474) Largely copied from the `class` code --- toolchain/check/context.cpp | 1 + toolchain/check/context.h | 12 +- toolchain/check/handle_function.cpp | 2 + toolchain/check/handle_interface.cpp | 139 +++++++++++++++++- toolchain/check/modifiers.cpp | 13 +- toolchain/check/node_stack.h | 21 +++ .../check/testdata/interface/basic.carbon | 38 +++++ .../testdata/interface/fail_duplicate.carbon | 35 +++++ .../testdata/interface/fail_modifiers.carbon | 52 +++++++ .../interface/fail_todo_modifiers.carbon | 55 +++++++ toolchain/diagnostics/diagnostic_kind.def | 4 + toolchain/lower/handle.cpp | 6 + .../interface/associated_constants.carbon | 38 +++++ .../generics/interface/default_fn.carbon | 62 ++++++++ .../generics/interface/final_fn.carbon | 62 ++++++++ toolchain/sem_ir/file.cpp | 3 + toolchain/sem_ir/file.h | 41 ++++++ toolchain/sem_ir/formatter.cpp | 78 +++++++++- toolchain/sem_ir/ids.h | 18 +++ toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/typed_insts.h | 14 ++ 21 files changed, 677 insertions(+), 18 deletions(-) create mode 100644 toolchain/check/testdata/interface/basic.carbon create mode 100644 toolchain/check/testdata/interface/fail_duplicate.carbon create mode 100644 toolchain/check/testdata/interface/fail_modifiers.carbon create mode 100644 toolchain/check/testdata/interface/fail_todo_modifiers.carbon create mode 100644 toolchain/parse/testdata/generics/interface/associated_constants.carbon create mode 100644 toolchain/parse/testdata/generics/interface/default_fn.carbon create mode 100644 toolchain/parse/testdata/generics/interface/final_fn.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 321d08aae25e..1354f805c99c 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -848,6 +848,7 @@ class TypeCompleter { case SemIR::FunctionDecl::Kind: case SemIR::Import::Kind: case SemIR::InitializeFrom::Kind: + case SemIR::InterfaceDecl::Kind: case SemIR::IntLiteral::Kind: case SemIR::NameRef::Kind: case SemIR::Namespace::Kind: diff --git a/toolchain/check/context.h b/toolchain/check/context.h index b1f005d37b93..465aa3eb294f 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -126,13 +126,14 @@ class Context { // Returns true if currently at file scope. auto at_file_scope() const -> bool { return scope_stack_.size() == 1; } - // Returns the instruction kind associated with the current scope, if any. - auto current_scope_kind() const -> std::optional { + // Returns true if the current scope is of the specified kind. + template + auto CurrentScopeIs() -> bool { auto current_scope_inst_id = current_scope().scope_inst_id; if (!current_scope_inst_id.is_valid()) { - return std::nullopt; + return false; } - return sem_ir_->insts().Get(current_scope_inst_id).kind(); + return sem_ir_->insts().Get(current_scope_inst_id).kind() == InstT::Kind; } // Returns the current scope, if it is of the specified kind. Otherwise, @@ -336,6 +337,9 @@ class Context { return sem_ir().functions(); } auto classes() -> ValueStore& { return sem_ir().classes(); } + auto interfaces() -> ValueStore& { + return sem_ir().interfaces(); + } auto cross_ref_irs() -> ValueStore& { return sem_ir().cross_ref_irs(); } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index d80a708b09cb..6f19ed7e0944 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -100,6 +100,8 @@ static auto BuildFunctionDecl(Context& context, bool is_definition) "method modifier"); } if (!!(modifiers & KeywordModifierSet::Interface)) { + // TODO: Once we are saving the modifiers for a function, add check that + // the function may only be defined if it is marked `default` or `final`. context.TODO(context.decl_state_stack().innermost().saw_decl_modifier, "interface modifier"); } diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index 6d943d3d927e..a1d6e1cd596d 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -3,26 +3,151 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" +#include "toolchain/check/modifiers.h" namespace Carbon::Check { -auto HandleInterfaceDecl(Context& context, Parse::NodeId parse_node) -> bool { - return context.TODO(parse_node, "HandleInterfaceDecl"); +auto HandleInterfaceIntroducer(Context& context, Parse::NodeId parse_node) + -> bool { + // Create an instruction block to hold the instructions created as part of the + // interface signature, such as generic parameters. + context.inst_block_stack().Push(); + // Push the bracketing node. + context.node_stack().Push(parse_node); + // Optional modifiers and the name follow. + context.decl_state_stack().Push(DeclState::Interface, parse_node); + context.decl_name_stack().PushScopeAndStartName(); + return true; } -auto HandleInterfaceDefinition(Context& context, Parse::NodeId parse_node) +static auto BuildInterfaceDecl(Context& context) + -> std::tuple { + auto name_context = context.decl_name_stack().FinishName(); + context.node_stack() + .PopAndDiscardSoloParseNode(); + auto first_node = context.decl_state_stack().innermost().first_node; + + // Process modifiers. + CheckAccessModifiersOnDecl(context, Lex::TokenKind::Interface); + LimitModifiersOnDecl(context, KeywordModifierSet::Access, + Lex::TokenKind::Interface); + + auto modifiers = context.decl_state_stack().innermost().modifier_set; + if (!!(modifiers & KeywordModifierSet::Access)) { + context.TODO(context.decl_state_stack().innermost().saw_access_modifier, + "access modifier"); + } + + auto decl_block_id = context.inst_block_stack().Pop(); + + // Add the interface declaration. + auto interface_decl = SemIR::InterfaceDecl{ + first_node, SemIR::InterfaceId::Invalid, decl_block_id}; + auto interface_decl_id = context.AddInst(interface_decl); + + // Check whether this is a redeclaration. + auto existing_id = context.decl_name_stack().LookupOrAddName( + name_context, interface_decl_id); + if (existing_id.is_valid()) { + if (auto existing_interface_decl = + context.insts().Get(existing_id).TryAs()) { + // This is a redeclaration of an existing interface. + interface_decl.interface_id = existing_interface_decl->interface_id; + + // TODO: Check that the generic parameter list agrees with the prior + // declaration. + } else { + // This is a redeclaration of something other than a interface. + context.DiagnoseDuplicateName(name_context.parse_node, existing_id); + } + } + + // Create a new interface if this isn't a valid redeclaration. + if (!interface_decl.interface_id.is_valid()) { + // TODO: If this is an invalid redeclaration of a non-interface entity or + // there was an error in the qualifier, we will have lost track of the + // interface name here. We should keep track of it even if the name is + // invalid. + // TODO: should have a `Self` type id member + interface_decl.interface_id = context.interfaces().Add( + {.name_id = + name_context.state == DeclNameStack::NameContext::State::Unresolved + ? name_context.unresolved_name_id + : SemIR::NameId::Invalid, + .decl_id = interface_decl_id}); + } + + // Write the interface ID into the InterfaceDecl. + context.insts().Set(interface_decl_id, interface_decl); + + return {interface_decl.interface_id, interface_decl_id}; +} + +auto HandleInterfaceDecl(Context& context, Parse::NodeId /*parse_node*/) -> bool { - return context.TODO(parse_node, "HandleInterfaceDefinition"); + BuildInterfaceDecl(context); + context.decl_name_stack().PopScope(); + context.decl_state_stack().Pop(DeclState::Interface); + return true; } auto HandleInterfaceDefinitionStart(Context& context, Parse::NodeId parse_node) -> bool { - return context.TODO(parse_node, "HandleInterfaceDefinitionStart"); + auto [interface_id, interface_decl_id] = BuildInterfaceDecl(context); + auto& interface_info = context.interfaces().Get(interface_id); + + // Track that this declaration is the definition. + if (interface_info.definition_id.is_valid()) { + CARBON_DIAGNOSTIC(InterfaceRedefinition, Error, + "Redefinition of interface {0}.", std::string); + CARBON_DIAGNOSTIC(InterfacePreviousDefinition, Note, + "Previous definition was here."); + context.emitter() + .Build(parse_node, InterfaceRedefinition, + context.names().GetFormatted(interface_info.name_id).str()) + .Note(context.insts().Get(interface_info.definition_id).parse_node(), + InterfacePreviousDefinition) + .Emit(); + } else { + interface_info.definition_id = interface_decl_id; + interface_info.scope_id = context.name_scopes().Add(); + } + + // Enter the interface scope. + context.PushScope(interface_decl_id, interface_info.scope_id); + + // TODO: Introduce `Self`. + + 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. + + // TODO: Handle the case where there's control flow in the interface body. For + // example: + // + // interface C { + // let v: if true then i32 else f64; + // } + // + // We may need to track a list of instruction blocks here, as we do for a + // function. + interface_info.body_block_id = context.inst_block_stack().PeekOrAdd(); + return true; } -auto HandleInterfaceIntroducer(Context& context, Parse::NodeId parse_node) +auto HandleInterfaceDefinition(Context& context, Parse::NodeId /*parse_node*/) -> bool { - return context.TODO(parse_node, "HandleInterfaceIntroducer"); + auto interface_id = + context.node_stack().Pop(); + context.inst_block_stack().Pop(); + context.PopScope(); + context.decl_name_stack().PopScope(); + context.decl_state_stack().Pop(DeclState::Interface); + + // The interface type is now fully defined. + auto& interface_info = context.interfaces().Get(interface_id); + interface_info.defined = true; + return true; } } // namespace Carbon::Check diff --git a/toolchain/check/modifiers.cpp b/toolchain/check/modifiers.cpp index e4bdc101b9d0..3d314dff2764 100644 --- a/toolchain/check/modifiers.cpp +++ b/toolchain/check/modifiers.cpp @@ -71,11 +71,9 @@ auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind) return; } - if (auto kind = context.current_scope_kind()) { - if (*kind == SemIR::ClassDecl::Kind) { - // Both `private` and `protected` allowed in a class definition. - return; - } + if (context.CurrentScopeIs()) { + // Both `private` and `protected` allowed in a class definition. + return; } // Otherwise neither `private` nor `protected` allowed. @@ -88,7 +86,10 @@ auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind) auto RequireDefaultFinalOnlyInInterfaces(Context& context, Lex::TokenKind decl_kind) -> void { - // TODO: Skip this if *context.current_scope_kind() == SemIR::InterfaceDecl + if (context.CurrentScopeIs()) { + // Both `default` and `final` allowed in an interface definition. + return; + } ForbidModifiersOnDecl(context, KeywordModifierSet::Interface, decl_kind, " outside of an interface"); } diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 6e01d16a7a5d..4a77ffdbea93 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -153,6 +153,11 @@ class NodeStack { RequireParseKind(back.first); return back; } + if constexpr (RequiredIdKind == IdKind::InterfaceId) { + auto back = PopWithParseNode(); + RequireParseKind(back.first); + return back; + } if constexpr (RequiredIdKind == IdKind::NameId) { auto back = PopWithParseNode(); RequireParseKind(back.first); @@ -215,6 +220,9 @@ class NodeStack { if constexpr (RequiredIdKind == IdKind::ClassId) { return back.id(); } + if constexpr (RequiredIdKind == IdKind::InterfaceId) { + return back.id(); + } if constexpr (RequiredIdKind == IdKind::NameId) { return back.id(); } @@ -239,6 +247,7 @@ class NodeStack { InstBlockId, FunctionId, ClassId, + InterfaceId, NameId, TypeId, // No associated ID type. @@ -257,6 +266,8 @@ class NodeStack { : parse_node(parse_node), function_id(function_id) {} explicit Entry(Parse::NodeId parse_node, SemIR::ClassId class_id) : parse_node(parse_node), class_id(class_id) {} + explicit Entry(Parse::NodeId parse_node, SemIR::InterfaceId interface_id) + : parse_node(parse_node), interface_id(interface_id) {} explicit Entry(Parse::NodeId parse_node, SemIR::NameId name_id) : parse_node(parse_node), name_id(name_id) {} explicit Entry(Parse::NodeId parse_node, SemIR::TypeId type_id) @@ -277,6 +288,9 @@ class NodeStack { if constexpr (std::is_same()) { return class_id; } + if constexpr (std::is_same()) { + return interface_id; + } if constexpr (std::is_same()) { return name_id; } @@ -298,6 +312,7 @@ class NodeStack { SemIR::InstBlockId inst_block_id; SemIR::FunctionId function_id; SemIR::ClassId class_id; + SemIR::InterfaceId interface_id; SemIR::NameId name_id; SemIR::TypeId type_id; }; @@ -342,6 +357,8 @@ class NodeStack { return IdKind::FunctionId; case Parse::NodeKind::ClassDefinitionStart: return IdKind::ClassId; + case Parse::NodeKind::InterfaceDefinitionStart: + return IdKind::InterfaceId; case Parse::NodeKind::BaseName: case Parse::NodeKind::IdentifierName: return IdKind::NameId; @@ -351,6 +368,7 @@ class NodeStack { case Parse::NodeKind::FunctionIntroducer: case Parse::NodeKind::IfStatementElse: case Parse::NodeKind::ImplicitParamListStart: + case Parse::NodeKind::InterfaceIntroducer: case Parse::NodeKind::LetIntroducer: case Parse::NodeKind::ParamListStart: case Parse::NodeKind::ParenExprOrTupleLiteralStart: @@ -397,6 +415,9 @@ class NodeStack { if constexpr (std::is_same_v) { return IdKind::ClassId; } + if constexpr (std::is_same_v) { + return IdKind::InterfaceId; + } if constexpr (std::is_same_v) { return IdKind::NameId; } diff --git a/toolchain/check/testdata/interface/basic.carbon b/toolchain/check/testdata/interface/basic.carbon new file mode 100644 index 000000000000..b0075e7b9c14 --- /dev/null +++ b/toolchain/check/testdata/interface/basic.carbon @@ -0,0 +1,38 @@ +// 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 Empty { +} + +interface ForwardDeclared; + +interface ForwardDeclared { + fn F(); +} + +// CHECK:STDOUT: --- basic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Empty = %Empty.decl, .ForwardDeclared = %ForwardDeclared.decl.loc10} +// CHECK:STDOUT: %Empty.decl = interface_decl @Empty, () +// CHECK:STDOUT: %ForwardDeclared.decl.loc10 = interface_decl @ForwardDeclared, () +// CHECK:STDOUT: %ForwardDeclared.decl.loc12 = interface_decl @ForwardDeclared, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Empty { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @ForwardDeclared { +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_duplicate.carbon b/toolchain/check/testdata/interface/fail_duplicate.carbon new file mode 100644 index 000000000000..52c32e225b3b --- /dev/null +++ b/toolchain/check/testdata/interface/fail_duplicate.carbon @@ -0,0 +1,35 @@ +// 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_duplicate.carbon:[[@LINE+6]]:21: ERROR: Redefinition of interface Interface. +// CHECK:STDERR: interface Interface { +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_duplicate.carbon:[[@LINE-5]]:1: Previous definition was here. +// CHECK:STDERR: interface Interface { } +// CHECK:STDERR: ^~~~~~~~~ +interface Interface { + fn F(); +} + +// CHECK:STDOUT: --- fail_duplicate.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Interface = %Interface.decl.loc7} +// CHECK:STDOUT: %Interface.decl.loc7 = interface_decl @Interface, () +// CHECK:STDOUT: %Interface.decl.loc15 = interface_decl @Interface, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Interface { +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_modifiers.carbon b/toolchain/check/testdata/interface/fail_modifiers.carbon new file mode 100644 index 000000000000..ff8f2a23f0d8 --- /dev/null +++ b/toolchain/check/testdata/interface/fail_modifiers.carbon @@ -0,0 +1,52 @@ +// 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 + +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `abstract` not allowed on `interface` declaration. +// CHECK:STDERR: abstract interface Abstract { +// CHECK:STDERR: ^~~~~~~~ +abstract interface Abstract { +} + +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `default` not allowed on `interface` declaration. +// CHECK:STDERR: default interface Default; +// CHECK:STDERR: ^~~~~~~ +default interface Default; + +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `virtual` not allowed on `interface` declaration. +// CHECK:STDERR: virtual interface Virtual { +// CHECK:STDERR: ^~~~~~~ +virtual interface Virtual { +} + +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `protected` not allowed on `interface` declaration at file scope, `protected` is only allowed on class members. +// CHECK:STDERR: protected interface Protected; +// CHECK:STDERR: ^~~~~~~~~ +protected interface Protected; + +// CHECK:STDOUT: --- fail_modifiers.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Abstract = %Abstract.decl, .Default = %Default.decl, .Virtual = %Virtual.decl, .Protected = %Protected.decl} +// CHECK:STDOUT: %Abstract.decl = interface_decl @Abstract, () +// CHECK:STDOUT: %Default.decl = interface_decl @Default, () +// CHECK:STDOUT: %Virtual.decl = interface_decl @Virtual, () +// CHECK:STDOUT: %Protected.decl = interface_decl @Protected, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Abstract { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Default; +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Virtual { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Protected; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_todo_modifiers.carbon b/toolchain/check/testdata/interface/fail_todo_modifiers.carbon new file mode 100644 index 000000000000..f1ac02d0dce0 --- /dev/null +++ b/toolchain/check/testdata/interface/fail_todo_modifiers.carbon @@ -0,0 +1,55 @@ +// 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 Modifiers { + // CHECK:STDERR: fail_todo_modifiers.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. + // CHECK:STDERR: final fn Final() { } + // CHECK:STDERR: ^~~~~ + final fn Final() { } + // CHECK:STDERR: fail_todo_modifiers.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. + // CHECK:STDERR: default fn Default() { } + // CHECK:STDERR: ^~~~~~~ + default fn Default() { } +} + +// CHECK:STDERR: fail_todo_modifiers.carbon:[[@LINE+3]]:1: ERROR: Semantics TODO: `access modifier`. +// CHECK:STDERR: private interface Private { +// CHECK:STDERR: ^~~~~~~ +private interface Private { +} + +// CHECK:STDOUT: --- fail_todo_modifiers.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Modifiers = %Modifiers.decl, .Private = %Private.decl} +// CHECK:STDOUT: %Modifiers.decl = interface_decl @Modifiers, () +// CHECK:STDOUT: %Private.decl = interface_decl @Private, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Modifiers { +// CHECK:STDOUT: %Final: = fn_decl @Final +// CHECK:STDOUT: %Default: = fn_decl @Default +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Final = %Final +// CHECK:STDOUT: .Default = %Default +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Private { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Final() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Default() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index fe45df9fc923..6f897ddfb4e6 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -155,6 +155,10 @@ CARBON_DIAGNOSTIC_KIND(ClassRedefinition) CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition) CARBON_DIAGNOSTIC_KIND(ConstructionOfAbstractClass) +// Interface checking. +CARBON_DIAGNOSTIC_KIND(InterfacePreviousDefinition) +CARBON_DIAGNOSTIC_KIND(InterfaceRedefinition) + CARBON_DIAGNOSTIC_KIND(AddressOfEphemeralRef) CARBON_DIAGNOSTIC_KIND(AddressOfNonRef) CARBON_DIAGNOSTIC_KIND(AddrOnNonSelfParam) diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 299f09499279..6afaa3737f58 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -193,6 +193,12 @@ auto HandleInitializeFrom(FunctionContext& context, SemIR::InstId /*inst_id*/, context.FinishInit(storage_type_id, inst.dest_id, inst.src_id); } +auto HandleInterfaceDecl(FunctionContext& /*context*/, + SemIR::InstId /*inst_id*/, + SemIR::InterfaceDecl /*inst*/) -> void { + // No action to perform. +} + auto HandleIntLiteral(FunctionContext& context, SemIR::InstId inst_id, SemIR::IntLiteral inst) -> void { const llvm::APInt& i = context.sem_ir().ints().Get(inst.int_id); diff --git a/toolchain/parse/testdata/generics/interface/associated_constants.carbon b/toolchain/parse/testdata/generics/interface/associated_constants.carbon new file mode 100644 index 000000000000..423897aaef03 --- /dev/null +++ b/toolchain/parse/testdata/generics/interface/associated_constants.carbon @@ -0,0 +1,38 @@ +// 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 Foo { + // TODO: support `let T:! type;` + + final let I: i32 = 4; + default let D: bool = true; +} + +// CHECK:STDOUT: - filename: associated_constants.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'InterfaceIntroducer', text: 'interface'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'FinalModifier', text: 'final'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'I'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'LetInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '4'}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'DefaultModifier', text: 'default'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'D'}, +// CHECK:STDOUT: {kind: 'BoolTypeLiteral', text: 'bool'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'LetInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'BoolLiteralTrue', text: 'true'}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 20}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/generics/interface/default_fn.carbon b/toolchain/parse/testdata/generics/interface/default_fn.carbon new file mode 100644 index 000000000000..ff327c258d89 --- /dev/null +++ b/toolchain/parse/testdata/generics/interface/default_fn.carbon @@ -0,0 +1,62 @@ +// 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 Foo { + fn Scale[self: Self](s: i32) -> Self; + + // Default definition of `Invert` calls `Scale`. + default fn Invert[self: Self]() -> Self { + return self.Scale(-1); + } +} + +// CHECK:STDOUT: - filename: default_fn.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'InterfaceIntroducer', text: 'interface'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Scale'}, +// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, +// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 's'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParamList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'DefaultModifier', text: 'default'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Invert'}, +// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, +// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParamList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'SelfValueNameExpr', text: 'self'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Scale'}, +// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '1'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '-', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 23}, +// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 42}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/generics/interface/final_fn.carbon b/toolchain/parse/testdata/generics/interface/final_fn.carbon new file mode 100644 index 000000000000..9bbc5edad2ec --- /dev/null +++ b/toolchain/parse/testdata/generics/interface/final_fn.carbon @@ -0,0 +1,62 @@ +// 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 Order { + fn Less[self: Self](right: Self) -> bool; + final fn Greater[self: Self](right: Self) -> bool { + return right.Less(self); + } +} + +// CHECK:STDOUT: - filename: final_fn.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'InterfaceIntroducer', text: 'interface'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Order'}, +// CHECK:STDOUT: {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Less'}, +// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, +// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'right'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParamList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BoolTypeLiteral', text: 'bool'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'FinalModifier', text: 'final'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Greater'}, +// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, +// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'right'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParamList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BoolTypeLiteral', text: 'bool'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'right'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Less'}, +// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'SelfValueNameExpr', text: 'self'}, +// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 25}, +// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 44}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 0a3dd4e8fbca..670eede27077 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -206,6 +206,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { case FunctionDecl::Kind: case Import::Kind: case InitializeFrom::Kind: + case InterfaceDecl::Kind: case IntLiteral::Kind: case Namespace::Kind: case NoOp::Kind: @@ -408,6 +409,7 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string { case FunctionDecl::Kind: case Import::Kind: case InitializeFrom::Kind: + case InterfaceDecl::Kind: case IntLiteral::Kind: case Namespace::Kind: case NoOp::Kind: @@ -465,6 +467,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case FieldDecl::Kind: case FunctionDecl::Kind: case Import::Kind: + case InterfaceDecl::Kind: case Namespace::Kind: case NoOp::Kind: case Return::Kind: diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 4f9e644d9565..012b99605dea 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -116,6 +116,40 @@ struct Class : public Printable { TypeId object_repr_id = TypeId::Invalid; }; +// An interface. +struct Interface : public Printable { + auto Print(llvm::raw_ostream& out) const -> void { + out << "{name: " << name_id; + out << "}"; + } + + // 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; } + + // The following members always have values, and do not change throughout the + // lifetime of the interface. + + // The interface name. + NameId name_id; + // TODO: TypeId self_type_id; + // The first declaration of the interface. This is a InterfaceDecl. + InstId decl_id = InstId::Invalid; + + // The following members are set at the `{` of the interface definition. + + // The definition of the interface. This is a InterfaceDecl. + InstId definition_id = InstId::Invalid; + // The interface scope. + NameScopeId scope_id = NameScopeId::Invalid; + // The first block of the interface body. + // TODO: Handle control flow in the interface body, such as if-expressions. + InstBlockId body_block_id = InstBlockId::Invalid; + + // The following members are set at the `}` of the class definition. + bool defined = true; +}; + // The value representation to use when passing by value. struct ValueRepr : public Printable { auto Print(llvm::raw_ostream& out) const -> void; @@ -291,6 +325,10 @@ class File : public Printable { auto functions() const -> const ValueStore& { return functions_; } auto classes() -> ValueStore& { return classes_; } auto classes() const -> const ValueStore& { return classes_; } + auto interfaces() -> ValueStore& { return interfaces_; } + auto interfaces() const -> const ValueStore& { + return interfaces_; + } auto cross_ref_irs() -> ValueStore& { return cross_ref_irs_; } auto cross_ref_irs() const -> const ValueStore& { return cross_ref_irs_; @@ -350,6 +388,9 @@ class File : public Printable { // Storage for classes. ValueStore classes_; + // Storage for interfaces. + ValueStore interfaces_; + // Related IRs. There will always be at least 2 entries, the builtin IR (used // for references of builtins) followed by the current IR (used for references // crossing instruction blocks). diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index ada8f847461c..b315d1df1a36 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -43,7 +43,8 @@ class InstNamer { insts.resize(sem_ir.insts().size()); labels.resize(sem_ir.inst_blocks().size()); scopes.resize(static_cast(ScopeIndex::FirstFunction) + - sem_ir.functions().size() + sem_ir.classes().size()); + sem_ir.functions().size() + sem_ir.classes().size() + + sem_ir.interfaces().size()); // Build the constants scope. GetScopeInfo(ScopeIndex::Constants).name = @@ -96,6 +97,22 @@ class InstNamer { AddBlockLabel(class_scope, class_info.body_block_id, "class", class_loc); CollectNamesInBlock(class_scope, class_info.body_block_id); } + + // Build each interface scope. + for (auto [i, interface_info] : + llvm::enumerate(sem_ir.interfaces().array_ref())) { + auto interface_id = InterfaceId(i); + auto interface_scope = GetScopeFor(interface_id); + // TODO: Provide a location for the interface for use as a + // disambiguator. + auto interface_loc = Parse::NodeId::Invalid; + GetScopeInfo(interface_scope).name = globals.AllocateName( + *this, interface_loc, + sem_ir.names().GetIRBaseName(interface_info.name_id).str()); + AddBlockLabel(interface_scope, interface_info.body_block_id, "interface", + interface_loc); + CollectNamesInBlock(interface_scope, interface_info.body_block_id); + } } // Returns the scope index corresponding to a function. @@ -111,6 +128,14 @@ class InstNamer { sem_ir_.functions().size() + class_id.index); } + // Returns the scope index corresponding to an interface. + auto GetScopeFor(InterfaceId interface_id) -> ScopeIndex { + return static_cast( + static_cast(ScopeIndex::FirstFunction) + + sem_ir_.functions().size() + sem_ir_.classes().size() + + interface_id.index); + } + // Returns the IR name to use for a function. auto GetNameFor(FunctionId fn_id) -> llvm::StringRef { if (!fn_id.is_valid()) { @@ -127,6 +152,14 @@ class InstNamer { return GetScopeInfo(GetScopeFor(class_id)).name.str(); } + // Returns the IR name to use for an interface. + auto GetNameFor(InterfaceId interface_id) -> llvm::StringRef { + if (!interface_id.is_valid()) { + return "invalid"; + } + return GetScopeInfo(GetScopeFor(interface_id)).name.str(); + } + // Returns the IR name to use for an instruction, when referenced from a given // scope. auto GetNameFor(ScopeIndex scope_idx, InstId inst_id) -> std::string { @@ -456,6 +489,13 @@ class InstNamer { add_inst_name("import"); continue; } + case InterfaceDecl::Kind: { + add_inst_name_id(sem_ir_.interfaces() + .Get(inst.As().interface_id) + .name_id, + ".decl"); + continue; + } case NameRef::Kind: { add_inst_name_id(inst.As().name_id, ".ref"); continue; @@ -526,6 +566,10 @@ class Formatter { } out_ << "}\n"; + for (int i : llvm::seq(sem_ir_.interfaces().size())) { + FormatInterface(InterfaceId(i)); + } + for (int i : llvm::seq(sem_ir_.classes().size())) { FormatClass(ClassId(i)); } @@ -569,6 +613,25 @@ class Formatter { } } + auto FormatInterface(InterfaceId id) -> void { + const Interface& interface_info = sem_ir_.interfaces().Get(id); + + out_ << "\ninterface "; + FormatInterfaceName(id); + + llvm::SaveAndRestore interface_scope(scope_, inst_namer_.GetScopeFor(id)); + + if (interface_info.scope_id.is_valid()) { + out_ << " {\n"; + FormatCodeBlock(interface_info.body_block_id); + out_ << "\n!members:"; + FormatNameScope(interface_info.scope_id, "", "\n ."); + out_ << "\n}\n"; + } else { + out_ << ";\n"; + } + } + auto FormatFunction(FunctionId id) -> void { const Function& fn = sem_ir_.functions().Get(id); @@ -726,6 +789,13 @@ class Formatter { out_ << " = "; } + // Print InterfaceDecl with type-like semantics even though it lacks a + // type_id. + auto FormatInstructionLHS(InstId inst_id, InterfaceDecl /*inst*/) -> void { + FormatInstName(inst_id); + out_ << " = "; + } + template auto FormatInstructionRHS(InstT inst) -> void { // By default, an instruction has a comma-separated argument list. @@ -882,6 +952,8 @@ class Formatter { auto FormatArg(ClassId id) -> void { FormatClassName(id); } + auto FormatArg(InterfaceId id) -> void { FormatInterfaceName(id); } + auto FormatArg(CrossRefIRId id) -> void { out_ << id; } auto FormatArg(IntId id) -> void { @@ -961,6 +1033,10 @@ class Formatter { out_ << inst_namer_.GetNameFor(id); } + auto FormatInterfaceName(InterfaceId id) -> void { + out_ << inst_namer_.GetNameFor(id); + } + auto FormatType(TypeId id) -> void { if (!id.is_valid()) { out_ << "invalid"; diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index ce524d349a3a..6aa2b3051b6d 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -18,6 +18,7 @@ class File; class Inst; struct Class; struct Function; +struct Interface; struct TypeInfo; // The ID of an instruction. @@ -97,6 +98,23 @@ struct ClassId : public IdBase, public Printable { constexpr ClassId ClassId::Invalid = ClassId(ClassId::InvalidIndex); +// The ID of an interface. +struct InterfaceId : public IdBase, public Printable { + using ValueType = Interface; + + // An explicitly invalid interface ID. + static const InterfaceId Invalid; + + using IdBase::IdBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "interface"; + IdBase::Print(out); + } +}; + +constexpr InterfaceId InterfaceId::Invalid = + InterfaceId(InterfaceId::InvalidIndex); + // The ID of a cross-referenced IR. struct CrossRefIRId : public IdBase, public Printable { using ValueType = const File*; diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 1cc0baaddfde..2815cb9a0c5b 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -45,6 +45,7 @@ CARBON_SEM_IR_INST_KIND(FieldDecl) CARBON_SEM_IR_INST_KIND(FunctionDecl) CARBON_SEM_IR_INST_KIND(Import) CARBON_SEM_IR_INST_KIND(InitializeFrom) +CARBON_SEM_IR_INST_KIND(InterfaceDecl) CARBON_SEM_IR_INST_KIND(IntLiteral) CARBON_SEM_IR_INST_KIND(NameRef) CARBON_SEM_IR_INST_KIND(Namespace) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index e7bb20881d1e..8d207ed5a212 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -319,6 +319,20 @@ struct InitializeFrom { InstId dest_id; }; +struct InterfaceDecl { + static constexpr auto Kind = InstKind::InterfaceDecl.Define("interface_decl"); + + Parse::NodeId parse_node; + // No type: an interface declaration is not itself a value. The name of an + // interface declaration becomes a facet type value. + // TODO: For a generic interface declaration, the name of the interface + // declaration should become a parameterized entity name value. + InterfaceId interface_id; + // The declaration block, containing the interface name's qualifiers and the + // interface's generic parameters. + InstBlockId decl_block_id; +}; + struct IntLiteral { static constexpr auto Kind = InstKind::IntLiteral.Define("int_literal");