浏览代码

Add interface support to check (#3474)

Largely copied from the `class` code
josh11b 2 年之前
父节点
当前提交
3b0923c81d

+ 1 - 0
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:

+ 8 - 4
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<SemIR::InstKind> {
+  // Returns true if the current scope is of the specified kind.
+  template <typename InstT>
+  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<SemIR::ClassId>& { return sem_ir().classes(); }
+  auto interfaces() -> ValueStore<SemIR::InterfaceId>& {
+    return sem_ir().interfaces();
+  }
   auto cross_ref_irs() -> ValueStore<SemIR::CrossRefIRId>& {
     return sem_ir().cross_ref_irs();
   }

+ 2 - 0
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");
   }

+ 132 - 7
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;
+}
+
+static auto BuildInterfaceDecl(Context& context)
+    -> std::tuple<SemIR::InterfaceId, SemIR::InstId> {
+  auto name_context = context.decl_name_stack().FinishName();
+  context.node_stack()
+      .PopAndDiscardSoloParseNode<Parse::NodeKind::InterfaceIntroducer>();
+  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<SemIR::InterfaceDecl>()) {
+      // 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 HandleInterfaceDefinition(Context& context, Parse::NodeId parse_node)
+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<Parse::NodeKind::InterfaceDefinitionStart>();
+  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

+ 7 - 6
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<SemIR::ClassDecl>()) {
+    // 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<SemIR::InterfaceDecl>()) {
+    // Both `default` and `final` allowed in an interface definition.
+    return;
+  }
   ForbidModifiersOnDecl(context, KeywordModifierSet::Interface, decl_kind,
                         " outside of an interface");
 }

+ 21 - 0
toolchain/check/node_stack.h

@@ -153,6 +153,11 @@ class NodeStack {
       RequireParseKind<RequiredParseKind>(back.first);
       return back;
     }
+    if constexpr (RequiredIdKind == IdKind::InterfaceId) {
+      auto back = PopWithParseNode<SemIR::InterfaceId>();
+      RequireParseKind<RequiredParseKind>(back.first);
+      return back;
+    }
     if constexpr (RequiredIdKind == IdKind::NameId) {
       auto back = PopWithParseNode<SemIR::NameId>();
       RequireParseKind<RequiredParseKind>(back.first);
@@ -215,6 +220,9 @@ class NodeStack {
     if constexpr (RequiredIdKind == IdKind::ClassId) {
       return back.id<SemIR::ClassId>();
     }
+    if constexpr (RequiredIdKind == IdKind::InterfaceId) {
+      return back.id<SemIR::InterfaceId>();
+    }
     if constexpr (RequiredIdKind == IdKind::NameId) {
       return back.id<SemIR::NameId>();
     }
@@ -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<T, SemIR::ClassId>()) {
         return class_id;
       }
+      if constexpr (std::is_same<T, SemIR::InterfaceId>()) {
+        return interface_id;
+      }
       if constexpr (std::is_same<T, SemIR::NameId>()) {
         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<IdT, SemIR::ClassId>) {
       return IdKind::ClassId;
     }
+    if constexpr (std::is_same_v<IdT, SemIR::InterfaceId>) {
+      return IdKind::InterfaceId;
+    }
     if constexpr (std::is_same_v<IdT, SemIR::NameId>) {
       return IdKind::NameId;
     }

+ 38 - 0
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> = 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: <function> = fn_decl @F
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:

+ 35 - 0
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> = 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: <function> = fn_decl @F
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:

+ 52 - 0
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> = 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:

+ 55 - 0
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> = 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: <function> = fn_decl @Final
+// CHECK:STDOUT:   %Default: <function> = 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:

+ 4 - 0
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)

+ 6 - 0
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);

+ 38 - 0
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:   ]

+ 62 - 0
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:   ]

+ 62 - 0
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:   ]

+ 3 - 0
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:

+ 41 - 0
toolchain/sem_ir/file.h

@@ -116,6 +116,40 @@ struct Class : public Printable<Class> {
   TypeId object_repr_id = TypeId::Invalid;
 };
 
+// An interface.
+struct Interface : public Printable<Interface> {
+  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<ValueRepr> {
   auto Print(llvm::raw_ostream& out) const -> void;
@@ -291,6 +325,10 @@ class File : public Printable<File> {
   auto functions() const -> const ValueStore<FunctionId>& { return functions_; }
   auto classes() -> ValueStore<ClassId>& { return classes_; }
   auto classes() const -> const ValueStore<ClassId>& { return classes_; }
+  auto interfaces() -> ValueStore<InterfaceId>& { return interfaces_; }
+  auto interfaces() const -> const ValueStore<InterfaceId>& {
+    return interfaces_;
+  }
   auto cross_ref_irs() -> ValueStore<CrossRefIRId>& { return cross_ref_irs_; }
   auto cross_ref_irs() const -> const ValueStore<CrossRefIRId>& {
     return cross_ref_irs_;
@@ -350,6 +388,9 @@ class File : public Printable<File> {
   // Storage for classes.
   ValueStore<ClassId> classes_;
 
+  // Storage for interfaces.
+  ValueStore<InterfaceId> 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).

+ 77 - 1
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<int32_t>(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<ScopeIndex>(
+        static_cast<int32_t>(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<InterfaceDecl>().interface_id)
+                               .name_id,
+                           ".decl");
+          continue;
+        }
         case NameRef::Kind: {
           add_inst_name_id(inst.As<NameRef>().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 <typename InstT>
   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";

+ 18 - 0
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<ClassId> {
 
 constexpr ClassId ClassId::Invalid = ClassId(ClassId::InvalidIndex);
 
+// The ID of an interface.
+struct InterfaceId : public IdBase, public Printable<InterfaceId> {
+  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<CrossRefIRId> {
   using ValueType = const File*;

+ 1 - 0
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)

+ 14 - 0
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");