Przeglądaj źródła

Start using typed parse node ids in the check stage (#3547)

Goal is to increase type safety, though more work needs to be done (see
added TODOs).

Note that, after this change, check handlers corresponding to deleted
parse node kinds will no longer compile.

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
josh11b 2 lat temu
rodzic
commit
48c986f52d
33 zmienionych plików z 340 dodań i 249 usunięć
  1. 1 2
      toolchain/check/check.cpp
  2. 1 1
      toolchain/check/context.h
  3. 5 4
      toolchain/check/handle_array.cpp
  4. 6 4
      toolchain/check/handle_binding_pattern.cpp
  5. 5 4
      toolchain/check/handle_call_expr.cpp
  6. 11 8
      toolchain/check/handle_class.cpp
  7. 6 3
      toolchain/check/handle_codeblock.cpp
  8. 2 2
      toolchain/check/handle_expr_statement.cpp
  9. 3 2
      toolchain/check/handle_file.cpp
  10. 10 7
      toolchain/check/handle_function.cpp
  11. 5 3
      toolchain/check/handle_if_expr.cpp
  12. 8 5
      toolchain/check/handle_if_statement.cpp
  13. 10 6
      toolchain/check/handle_impl.cpp
  14. 23 21
      toolchain/check/handle_import_and_package.cpp
  15. 3 3
      toolchain/check/handle_index.cpp
  16. 8 5
      toolchain/check/handle_interface.cpp
  17. 5 4
      toolchain/check/handle_let.cpp
  18. 22 14
      toolchain/check/handle_literal.cpp
  19. 20 12
      toolchain/check/handle_loop_statement.cpp
  20. 4 4
      toolchain/check/handle_modifier.cpp
  21. 19 14
      toolchain/check/handle_name.cpp
  22. 9 7
      toolchain/check/handle_named_constraint.cpp
  23. 3 3
      toolchain/check/handle_namespace.cpp
  24. 9 7
      toolchain/check/handle_noop.cpp
  25. 86 67
      toolchain/check/handle_operator.cpp
  26. 7 4
      toolchain/check/handle_paren.cpp
  27. 10 8
      toolchain/check/handle_pattern_list.cpp
  28. 6 4
      toolchain/check/handle_return_statement.cpp
  29. 14 10
      toolchain/check/handle_struct.cpp
  30. 8 6
      toolchain/check/handle_variable.cpp
  31. 4 5
      toolchain/check/node_stack.h
  32. 6 0
      toolchain/parse/node_ids.h
  33. 1 0
      toolchain/sem_ir/typed_insts.h

+ 1 - 2
toolchain/check/check.cpp

@@ -155,10 +155,9 @@ static auto ProcessParseNodes(Context& context,
     // clang warns on unhandled enum values; clang-tidy is incorrect here.
     // NOLINTNEXTLINE(bugprone-switch-missing-default-case)
     switch (auto parse_kind = context.parse_tree().node_kind(parse_node)) {
-      // TODO: Switch to `Parse::Name##Id(parse_node)` here.
 #define CARBON_PARSE_NODE_KIND(Name)                                         \
   case Parse::NodeKind::Name: {                                              \
-    if (!Check::Handle##Name(context, parse_node)) {                         \
+    if (!Check::Handle##Name(context, Parse::Name##Id(parse_node))) {        \
       CARBON_CHECK(err_tracker.seen_error())                                 \
           << "Handle" #Name " returned false without printing a diagnostic"; \
       return false;                                                          \

+ 1 - 1
toolchain/check/context.h

@@ -534,7 +534,7 @@ class Context {
 
 // Parse node handlers. Returns false for unrecoverable errors.
 #define CARBON_PARSE_NODE_KIND(Name) \
-  auto Handle##Name(Context& context, Parse::NodeId parse_node) -> bool;
+  auto Handle##Name(Context& context, Parse::Name##Id parse_node) -> bool;
 #include "toolchain/parse/node_kind.def"
 
 }  // namespace Carbon::Check

+ 5 - 4
toolchain/check/handle_array.cpp

@@ -9,17 +9,18 @@
 
 namespace Carbon::Check {
 
-auto HandleArrayExprStart(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleArrayExprStart(Context& /*context*/,
+                          Parse::ArrayExprStartId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleArrayExprSemi(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleArrayExprSemi(Context& context, Parse::ArrayExprSemiId parse_node)
+    -> bool {
   context.node_stack().Push(parse_node);
   return true;
 }
 
-auto HandleArrayExpr(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleArrayExpr(Context& context, Parse::ArrayExprId parse_node) -> bool {
   // TODO: Handle array type with undefined bound.
   if (context.node_stack()
           .PopAndDiscardSoloParseNodeIf<Parse::NodeKind::ArrayExprSemi>()) {

+ 6 - 4
toolchain/check/handle_binding_pattern.cpp

@@ -9,7 +9,7 @@
 
 namespace Carbon::Check {
 
-auto HandleAddress(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleAddress(Context& context, Parse::AddressId parse_node) -> bool {
   auto self_param_id =
       context.node_stack().Pop<Parse::NodeKind::BindingPattern>();
   auto self_param = context.insts().TryGetAs<SemIR::BindName>(self_param_id);
@@ -28,12 +28,14 @@ auto HandleAddress(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleGenericBindingPattern(Context& context, Parse::NodeId parse_node)
+auto HandleGenericBindingPattern(Context& context,
+                                 Parse::GenericBindingPatternId parse_node)
     -> bool {
   return context.TODO(parse_node, "GenericBindingPattern");
 }
 
-auto HandleBindingPattern(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node)
+    -> bool {
   auto [type_node, parsed_type_id] =
       context.node_stack().PopExprWithParseNode();
   auto type_node_copy = type_node;
@@ -158,7 +160,7 @@ auto HandleBindingPattern(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleTemplate(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleTemplate(Context& context, Parse::TemplateId parse_node) -> bool {
   // TODO: diagnose if this occurs in a `var` context.
   return context.TODO(parse_node, "HandleTemplate");
 }

+ 5 - 4
toolchain/check/handle_call_expr.cpp

@@ -9,7 +9,7 @@
 
 namespace Carbon::Check {
 
-auto HandleCallExpr(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleCallExpr(Context& context, Parse::CallExprId parse_node) -> bool {
   // Process the final explicit call argument now, but leave the arguments
   // block on the stack until the end of this function.
   context.ParamOrArgEndNoPop(Parse::NodeKind::CallExprStart);
@@ -83,13 +83,14 @@ auto HandleCallExpr(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleCallExprComma(Context& context, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleCallExprComma(Context& context,
+                         Parse::CallExprCommaId /*parse_node*/) -> bool {
   context.ParamOrArgComma();
   return true;
 }
 
-auto HandleCallExprStart(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleCallExprStart(Context& context, Parse::CallExprStartId parse_node)
+    -> bool {
   auto name_id = context.node_stack().PopExpr();
   context.node_stack().Push(parse_node, name_id);
   context.ParamOrArgStart();

+ 11 - 8
toolchain/check/handle_class.cpp

@@ -8,7 +8,8 @@
 
 namespace Carbon::Check {
 
-auto HandleClassIntroducer(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleClassIntroducer(Context& context,
+                           Parse::ClassIntroducerId parse_node) -> bool {
   // Create an instruction block to hold the instructions created as part of the
   // class signature, such as generic parameters.
   context.inst_block_stack().Push();
@@ -111,13 +112,14 @@ static auto BuildClassDecl(Context& context, Parse::NodeId parse_node)
   return {class_decl.class_id, class_decl_id};
 }
 
-auto HandleClassDecl(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleClassDecl(Context& context, Parse::ClassDeclId parse_node) -> bool {
   BuildClassDecl(context, parse_node);
   context.decl_name_stack().PopScope();
   return true;
 }
 
-auto HandleClassDefinitionStart(Context& context, Parse::NodeId parse_node)
+auto HandleClassDefinitionStart(Context& context,
+                                Parse::ClassDefinitionStartId parse_node)
     -> bool {
   auto [class_id, class_decl_id] = BuildClassDecl(context, parse_node);
   auto& class_info = context.classes().Get(class_id);
@@ -163,13 +165,13 @@ auto HandleClassDefinitionStart(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleBaseIntroducer(Context& context, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleBaseIntroducer(Context& context,
+                          Parse::BaseIntroducerId /*parse_node*/) -> bool {
   context.decl_state_stack().Push(DeclState::Base);
   return true;
 }
 
-auto HandleBaseColon(Context& /*context*/, Parse::NodeId /*parse_node*/)
+auto HandleBaseColon(Context& /*context*/, Parse::BaseColonId /*parse_node*/)
     -> bool {
   return true;
 }
@@ -245,7 +247,7 @@ static auto CheckBaseType(Context& context, Parse::NodeId parse_node,
   return {.type_id = base_type_id, .scope_id = base_class_info->scope_id};
 }
 
-auto HandleBaseDecl(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleBaseDecl(Context& context, Parse::BaseDeclId parse_node) -> bool {
   auto base_type_expr_id = context.node_stack().PopExpr();
 
   // Process modifiers. `extend` is required, none others are allowed.
@@ -318,7 +320,8 @@ auto HandleBaseDecl(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleClassDefinition(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleClassDefinition(Context& context,
+                           Parse::ClassDefinitionId parse_node) -> bool {
   auto fields_id = context.args_type_info_stack().Pop();
   auto class_id =
       context.node_stack().Pop<Parse::NodeKind::ClassDefinitionStart>();

+ 6 - 3
toolchain/check/handle_codeblock.cpp

@@ -6,15 +6,18 @@
 
 namespace Carbon::Check {
 
-auto HandleCodeBlockStart(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleCodeBlockStart(Context& context, Parse::CodeBlockStartId parse_node)
+    -> bool {
   context.node_stack().Push(parse_node);
   context.PushScope();
   return true;
 }
 
-auto HandleCodeBlock(Context& context, Parse::NodeId /*parse_node*/) -> bool {
+auto HandleCodeBlock(Context& context, Parse::CodeBlockId /*parse_node*/)
+    -> bool {
   context.PopScope();
-  context.node_stack().PopForSoloParseNode<Parse::NodeKind::CodeBlockStart>();
+  context.node_stack()
+      .PopAndDiscardSoloParseNode<Parse::NodeKind::CodeBlockStart>();
   return true;
 }
 

+ 2 - 2
toolchain/check/handle_expr_statement.cpp

@@ -21,8 +21,8 @@ static auto HandleDiscardedExpr(Context& context, SemIR::InstId expr_id)
   // TODO: This will eventually need to do some "do not discard" analysis.
 }
 
-auto HandleExprStatement(Context& context, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleExprStatement(Context& context,
+                         Parse::ExprStatementId /*parse_node*/) -> bool {
   HandleDiscardedExpr(context, context.node_stack().PopExpr());
   return true;
 }

+ 3 - 2
toolchain/check/handle_file.cpp

@@ -6,7 +6,7 @@
 
 namespace Carbon::Check {
 
-auto HandleFileStart(Context& /*context*/, Parse::NodeId /*parse_node*/)
+auto HandleFileStart(Context& /*context*/, Parse::FileStartId /*parse_node*/)
     -> bool {
   // No action to perform.
   // TODO: We may want to push `FileStart` as a sentinel so that `Peek`s can't
@@ -14,7 +14,8 @@ auto HandleFileStart(Context& /*context*/, Parse::NodeId /*parse_node*/)
   return true;
 }
 
-auto HandleFileEnd(Context& /*context*/, Parse::NodeId /*parse_node*/) -> bool {
+auto HandleFileEnd(Context& /*context*/, Parse::FileEndId /*parse_node*/)
+    -> bool {
   // No action to perform.
   return true;
 }

+ 10 - 7
toolchain/check/handle_function.cpp

@@ -174,14 +174,15 @@ static auto BuildFunctionDecl(Context& context, Parse::NodeId parse_node,
   return {function_decl.function_id, function_decl_id};
 }
 
-auto HandleFunctionDecl(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleFunctionDecl(Context& context, Parse::FunctionDeclId parse_node)
+    -> bool {
   BuildFunctionDecl(context, parse_node, /*is_definition=*/false);
   context.decl_name_stack().PopScope();
   return true;
 }
 
-auto HandleFunctionDefinition(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleFunctionDefinition(Context& context,
+                              Parse::FunctionDefinitionId parse_node) -> bool {
   SemIR::FunctionId function_id =
       context.node_stack().Pop<Parse::NodeKind::FunctionDefinitionStart>();
 
@@ -205,7 +206,8 @@ auto HandleFunctionDefinition(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleFunctionDefinitionStart(Context& context, Parse::NodeId parse_node)
+auto HandleFunctionDefinitionStart(Context& context,
+                                   Parse::FunctionDefinitionStartId parse_node)
     -> bool {
   // Process the declaration portion of the function.
   auto [function_id, decl_id] =
@@ -271,8 +273,8 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleFunctionIntroducer(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleFunctionIntroducer(Context& context,
+                              Parse::FunctionIntroducerId parse_node) -> bool {
   // Create an instruction block to hold the instructions created as part of the
   // function signature, such as parameter and return types.
   context.inst_block_stack().Push();
@@ -284,7 +286,8 @@ auto HandleFunctionIntroducer(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleReturnType(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleReturnType(Context& context, Parse::ReturnTypeId parse_node)
+    -> bool {
   // Propagate the type expression.
   auto [type_parse_node, type_inst_id] =
       context.node_stack().PopExprWithParseNode();

+ 5 - 3
toolchain/check/handle_if_expr.cpp

@@ -7,7 +7,7 @@
 
 namespace Carbon::Check {
 
-auto HandleIfExprIf(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIfExprIf(Context& context, Parse::IfExprIfId parse_node) -> bool {
   // Alias parse_node for if/then/else consistency.
   auto& if_node = parse_node;
 
@@ -28,7 +28,8 @@ auto HandleIfExprIf(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleIfExprThen(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIfExprThen(Context& context, Parse::IfExprThenId parse_node)
+    -> bool {
   auto then_value_id = context.node_stack().PopExpr();
   auto else_block_id = context.node_stack().Peek<Parse::NodeKind::IfExprIf>();
 
@@ -43,7 +44,8 @@ auto HandleIfExprThen(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleIfExprElse(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIfExprElse(Context& context, Parse::IfExprElseId parse_node)
+    -> bool {
   // Alias parse_node for if/then/else consistency.
   auto& else_node = parse_node;
 

+ 8 - 5
toolchain/check/handle_if_statement.cpp

@@ -8,12 +8,13 @@
 
 namespace Carbon::Check {
 
-auto HandleIfConditionStart(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleIfConditionStart(Context& /*context*/,
+                            Parse::IfConditionStartId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleIfCondition(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIfCondition(Context& context, Parse::IfConditionId parse_node)
+    -> bool {
   // Convert the condition to `bool`.
   auto cond_value_id = context.node_stack().PopExpr();
   cond_value_id = ConvertToBoolValue(context, parse_node, cond_value_id);
@@ -34,7 +35,8 @@ auto HandleIfCondition(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleIfStatementElse(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIfStatementElse(Context& context,
+                           Parse::IfStatementElseId parse_node) -> bool {
   auto else_block_id = context.node_stack().Pop<Parse::NodeKind::IfCondition>();
 
   // Switch to emitting the `else` block.
@@ -45,7 +47,8 @@ auto HandleIfStatementElse(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleIfStatement(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIfStatement(Context& context, Parse::IfStatementId parse_node)
+    -> bool {
   switch (auto kind = context.node_stack().PeekParseNodeKind()) {
     case Parse::NodeKind::IfCondition: {
       // Branch from then block to else block, and start emitting the else

+ 10 - 6
toolchain/check/handle_impl.cpp

@@ -6,28 +6,32 @@
 
 namespace Carbon::Check {
 
-auto HandleImplIntroducer(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleImplIntroducer(Context& context, Parse::ImplIntroducerId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleImplIntroducer");
 }
 
-auto HandleImplForall(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleImplForall(Context& context, Parse::ImplForallId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleImplForall");
 }
 
-auto HandleImplAs(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleImplAs(Context& context, Parse::ImplAsId parse_node) -> bool {
   return context.TODO(parse_node, "HandleImplAs");
 }
 
-auto HandleImplDecl(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleImplDecl(Context& context, Parse::ImplDeclId parse_node) -> bool {
   return context.TODO(parse_node, "HandleImplDecl");
 }
 
-auto HandleImplDefinitionStart(Context& context, Parse::NodeId parse_node)
+auto HandleImplDefinitionStart(Context& context,
+                               Parse::ImplDefinitionStartId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleImplDefinitionStart");
 }
 
-auto HandleImplDefinition(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleImplDefinition(Context& context, Parse::ImplDefinitionId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleImplDefinition");
 }
 

+ 23 - 21
toolchain/check/handle_import_and_package.cpp

@@ -9,63 +9,65 @@ namespace Carbon::Check {
 // `import` and `package` are structured by parsing. As a consequence, no
 // checking logic is needed here.
 
-auto HandleImportIntroducer(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleImportIntroducer(Context& /*context*/,
+                            Parse::ImportIntroducerId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleImportDirective(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleImportDirective(Context& /*context*/,
+                           Parse::ImportDirectiveId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleLibraryIntroducer(Context& /*context*/, Parse::NodeId /*parse_node*/)
+auto HandleLibraryIntroducer(Context& /*context*/,
+                             Parse::LibraryIntroducerId /*parse_node*/)
     -> bool {
   return true;
 }
 
-auto HandleLibraryDirective(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleLibraryDirective(Context& /*context*/,
+                            Parse::LibraryDirectiveId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandlePackageIntroducer(Context& /*context*/, Parse::NodeId /*parse_node*/)
+auto HandlePackageIntroducer(Context& /*context*/,
+                             Parse::PackageIntroducerId /*parse_node*/)
     -> bool {
   return true;
 }
 
-auto HandlePackageDirective(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandlePackageDirective(Context& /*context*/,
+                            Parse::PackageDirectiveId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleLibrarySpecifier(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleLibrarySpecifier(Context& /*context*/,
+                            Parse::LibrarySpecifierId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandlePackageName(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandlePackageName(Context& /*context*/,
+                       Parse::PackageNameId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleLibraryName(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleLibraryName(Context& /*context*/,
+                       Parse::LibraryNameId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleDefaultLibrary(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleDefaultLibrary(Context& /*context*/,
+                          Parse::DefaultLibraryId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandlePackageApi(Context& /*context*/, Parse::NodeId /*parse_node*/)
+auto HandlePackageApi(Context& /*context*/, Parse::PackageApiId /*parse_node*/)
     -> bool {
   return true;
 }
 
-auto HandlePackageImpl(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandlePackageImpl(Context& /*context*/,
+                       Parse::PackageImplId /*parse_node*/) -> bool {
   return true;
 }
 

+ 3 - 3
toolchain/check/handle_index.cpp

@@ -9,8 +9,8 @@
 
 namespace Carbon::Check {
 
-auto HandleIndexExprStart(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleIndexExprStart(Context& /*context*/,
+                          Parse::IndexExprStartId /*parse_node*/) -> bool {
   // Leave the expression on the stack for IndexExpr.
   return true;
 }
@@ -35,7 +35,7 @@ static auto ValidateIntLiteralBound(Context& context, Parse::NodeId parse_node,
   return &index_val;
 }
 
-auto HandleIndexExpr(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIndexExpr(Context& context, Parse::IndexExprId parse_node) -> bool {
   auto index_inst_id = context.node_stack().PopExpr();
   auto index_inst = context.insts().Get(index_inst_id);
   auto operand_inst_id = context.node_stack().PopExpr();

+ 8 - 5
toolchain/check/handle_interface.cpp

@@ -7,7 +7,8 @@
 
 namespace Carbon::Check {
 
-auto HandleInterfaceIntroducer(Context& context, Parse::NodeId parse_node)
+auto HandleInterfaceIntroducer(Context& context,
+                               Parse::InterfaceIntroducerId parse_node)
     -> bool {
   // Create an instruction block to hold the instructions created as part of the
   // interface signature, such as generic parameters.
@@ -83,14 +84,15 @@ static auto BuildInterfaceDecl(Context& context, Parse::NodeId parse_node)
   return {interface_decl.interface_id, interface_decl_id};
 }
 
-auto HandleInterfaceDecl(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleInterfaceDecl(Context& context, Parse::InterfaceDeclId parse_node)
+    -> bool {
   BuildInterfaceDecl(context, parse_node);
   context.decl_name_stack().PopScope();
   return true;
 }
 
-auto HandleInterfaceDefinitionStart(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInterfaceDefinitionStart(
+    Context& context, Parse::InterfaceDefinitionStartId parse_node) -> bool {
   auto [interface_id, interface_decl_id] =
       BuildInterfaceDecl(context, parse_node);
   auto& interface_info = context.interfaces().Get(interface_id);
@@ -134,7 +136,8 @@ auto HandleInterfaceDefinitionStart(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleInterfaceDefinition(Context& context, Parse::NodeId /*parse_node*/)
+auto HandleInterfaceDefinition(Context& context,
+                               Parse::InterfaceDefinitionId /*parse_node*/)
     -> bool {
   auto interface_id =
       context.node_stack().Pop<Parse::NodeKind::InterfaceDefinitionStart>();

+ 5 - 4
toolchain/check/handle_let.cpp

@@ -9,7 +9,7 @@
 
 namespace Carbon::Check {
 
-auto HandleLetDecl(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleLetDecl(Context& context, Parse::LetDeclId parse_node) -> bool {
   auto value_id = context.node_stack().PopExpr();
   if (context.node_stack().PeekIs<Parse::NodeKind::TuplePattern>()) {
     return context.TODO(parse_node, "tuple pattern in let");
@@ -56,15 +56,16 @@ auto HandleLetDecl(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleLetIntroducer(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleLetIntroducer(Context& context, Parse::LetIntroducerId parse_node)
+    -> bool {
   context.decl_state_stack().Push(DeclState::Let);
   // Push a bracketing node to establish the pattern context.
   context.node_stack().Push(parse_node);
   return true;
 }
 
-auto HandleLetInitializer(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleLetInitializer(Context& /*context*/,
+                          Parse::LetInitializerId /*parse_node*/) -> bool {
   return true;
 }
 

+ 22 - 14
toolchain/check/handle_literal.cpp

@@ -6,8 +6,8 @@
 
 namespace Carbon::Check {
 
-auto HandleBoolLiteralFalse(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleBoolLiteralFalse(Context& context,
+                            Parse::BoolLiteralFalseId parse_node) -> bool {
   context.AddInstAndPush(
       parse_node,
       SemIR::BoolLiteral{parse_node,
@@ -16,7 +16,8 @@ auto HandleBoolLiteralFalse(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleBoolLiteralTrue(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleBoolLiteralTrue(Context& context,
+                           Parse::BoolLiteralTrueId parse_node) -> bool {
   context.AddInstAndPush(
       parse_node,
       SemIR::BoolLiteral{parse_node,
@@ -25,7 +26,8 @@ auto HandleBoolLiteralTrue(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleIntLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIntLiteral(Context& context, Parse::IntLiteralId parse_node)
+    -> bool {
   context.AddInstAndPush(
       parse_node,
       SemIR::IntLiteral{parse_node,
@@ -35,7 +37,8 @@ auto HandleIntLiteral(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleRealLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleRealLiteral(Context& context, Parse::RealLiteralId parse_node)
+    -> bool {
   context.AddInstAndPush(
       parse_node,
       SemIR::RealLiteral{parse_node,
@@ -45,7 +48,8 @@ auto HandleRealLiteral(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleStringLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleStringLiteral(Context& context, Parse::StringLiteralId parse_node)
+    -> bool {
   context.AddInstAndPush(
       parse_node,
       SemIR::StringLiteral{
@@ -55,12 +59,14 @@ auto HandleStringLiteral(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleBoolTypeLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleBoolTypeLiteral(Context& context,
+                           Parse::BoolTypeLiteralId parse_node) -> bool {
   context.node_stack().Push(parse_node, SemIR::InstId::BuiltinBoolType);
   return true;
 }
 
-auto HandleIntTypeLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIntTypeLiteral(Context& context, Parse::IntTypeLiteralId parse_node)
+    -> bool {
   auto text = context.tokens().GetTokenText(
       context.parse_tree().node_token(parse_node));
   if (text != "i32") {
@@ -70,13 +76,14 @@ auto HandleIntTypeLiteral(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleUnsignedIntTypeLiteral(Context& context, Parse::NodeId parse_node)
+auto HandleUnsignedIntTypeLiteral(Context& context,
+                                  Parse::UnsignedIntTypeLiteralId parse_node)
     -> bool {
   return context.TODO(parse_node, "Need to support unsigned type literals");
 }
 
-auto HandleFloatTypeLiteral(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleFloatTypeLiteral(Context& context,
+                            Parse::FloatTypeLiteralId parse_node) -> bool {
   auto text = context.tokens().GetTokenText(
       context.parse_tree().node_token(parse_node));
   if (text != "f64") {
@@ -86,13 +93,14 @@ auto HandleFloatTypeLiteral(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleStringTypeLiteral(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleStringTypeLiteral(Context& context,
+                             Parse::StringTypeLiteralId parse_node) -> bool {
   context.node_stack().Push(parse_node, SemIR::InstId::BuiltinStringType);
   return true;
 }
 
-auto HandleTypeTypeLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleTypeTypeLiteral(Context& context,
+                           Parse::TypeTypeLiteralId parse_node) -> bool {
   context.node_stack().Push(parse_node, SemIR::InstId::BuiltinTypeType);
   return true;
 }

+ 20 - 12
toolchain/check/handle_loop_statement.cpp

@@ -7,12 +7,13 @@
 
 namespace Carbon::Check {
 
-auto HandleBreakStatement(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleBreakStatement(Context& /*context*/,
+                          Parse::BreakStatementId /*parse_node*/) -> bool {
   return true;
 }
 
-auto HandleBreakStatementStart(Context& context, Parse::NodeId parse_node)
+auto HandleBreakStatementStart(Context& context,
+                               Parse::BreakStatementStartId parse_node)
     -> bool {
   auto& stack = context.break_continue_stack();
   if (stack.empty()) {
@@ -28,12 +29,14 @@ auto HandleBreakStatementStart(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleContinueStatement(Context& /*context*/, Parse::NodeId /*parse_node*/)
+auto HandleContinueStatement(Context& /*context*/,
+                             Parse::ContinueStatementId /*parse_node*/)
     -> bool {
   return true;
 }
 
-auto HandleContinueStatementStart(Context& context, Parse::NodeId parse_node)
+auto HandleContinueStatementStart(Context& context,
+                                  Parse::ContinueStatementStartId parse_node)
     -> bool {
   auto& stack = context.break_continue_stack();
   if (stack.empty()) {
@@ -49,24 +52,27 @@ auto HandleContinueStatementStart(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleForHeader(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleForHeader(Context& context, Parse::ForHeaderId parse_node) -> bool {
   return context.TODO(parse_node, "HandleForHeader");
 }
 
-auto HandleForHeaderStart(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleForHeaderStart(Context& context, Parse::ForHeaderStartId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleForHeaderStart");
 }
 
-auto HandleForIn(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleForIn(Context& context, Parse::ForInId parse_node) -> bool {
   context.decl_state_stack().Pop(DeclState::Var);
   return context.TODO(parse_node, "HandleForIn");
 }
 
-auto HandleForStatement(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleForStatement(Context& context, Parse::ForStatementId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleForStatement");
 }
 
-auto HandleWhileConditionStart(Context& context, Parse::NodeId parse_node)
+auto HandleWhileConditionStart(Context& context,
+                               Parse::WhileConditionStartId parse_node)
     -> bool {
   // Branch to the loop header block. Note that we create a new block here even
   // if the current block is empty; this ensures that the loop always has a
@@ -82,7 +88,8 @@ auto HandleWhileConditionStart(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleWhileCondition(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleWhileCondition(Context& context, Parse::WhileConditionId parse_node)
+    -> bool {
   auto cond_value_id = context.node_stack().PopExpr();
   auto loop_header_id =
       context.node_stack().Peek<Parse::NodeKind::WhileConditionStart>();
@@ -104,7 +111,8 @@ auto HandleWhileCondition(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleWhileStatement(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleWhileStatement(Context& context, Parse::WhileStatementId parse_node)
+    -> bool {
   auto loop_exit_id =
       context.node_stack().Pop<Parse::NodeKind::WhileCondition>();
   auto loop_header_id =

+ 4 - 4
toolchain/check/handle_modifier.cpp

@@ -62,10 +62,10 @@ static auto HandleModifier(Context& context, Parse::NodeId parse_node,
 }
 
 #define CARBON_PARSE_NODE_KIND(...)
-#define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name, ...)                  \
-  auto Handle##Name##Modifier(Context& context, Parse::NodeId parse_node) \
-      -> bool {                                                           \
-    return HandleModifier(context, parse_node, KeywordModifierSet::Name); \
+#define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name, ...)                    \
+  auto Handle##Name##Modifier(Context& context,                             \
+                              Parse::Name##ModifierId parse_node) -> bool { \
+    return HandleModifier(context, parse_node, KeywordModifierSet::Name);   \
   }
 #include "toolchain/parse/node_kind.def"
 

+ 19 - 14
toolchain/check/handle_name.cpp

@@ -89,8 +89,8 @@ static auto IsInstanceMethod(const SemIR::File& sem_ir,
   return false;
 }
 
-auto HandleMemberAccessExpr(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleMemberAccessExpr(Context& context,
+                            Parse::MemberAccessExprId parse_node) -> bool {
   SemIR::NameId name_id = context.node_stack().PopName();
   auto base_id = context.node_stack().PopExpr();
 
@@ -246,7 +246,8 @@ auto HandleMemberAccessExpr(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandlePointerMemberAccessExpr(Context& context, Parse::NodeId parse_node)
+auto HandlePointerMemberAccessExpr(Context& context,
+                                   Parse::PointerMemberAccessExprId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandlePointerMemberAccessExpr");
 }
@@ -276,7 +277,8 @@ static auto HandleNameAsExpr(Context& context, Parse::NodeId parse_node,
   return true;
 }
 
-auto HandleIdentifierName(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleIdentifierName(Context& context, Parse::IdentifierNameId parse_node)
+    -> bool {
   // The parent is responsible for binding the name.
   auto name_id = GetIdentifierAsName(context, parse_node);
   if (!name_id) {
@@ -286,8 +288,8 @@ auto HandleIdentifierName(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleIdentifierNameExpr(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleIdentifierNameExpr(Context& context,
+                              Parse::IdentifierNameExprId parse_node) -> bool {
   auto name_id = GetIdentifierAsName(context, parse_node);
   if (!name_id) {
     return context.TODO(parse_node, "Error recovery from keyword name.");
@@ -295,27 +297,29 @@ auto HandleIdentifierNameExpr(Context& context, Parse::NodeId parse_node)
   return HandleNameAsExpr(context, parse_node, *name_id);
 }
 
-auto HandleBaseName(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleBaseName(Context& context, Parse::BaseNameId parse_node) -> bool {
   context.node_stack().Push(parse_node, SemIR::NameId::Base);
   return true;
 }
 
-auto HandleSelfTypeNameExpr(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleSelfTypeNameExpr(Context& context,
+                            Parse::SelfTypeNameExprId parse_node) -> bool {
   return HandleNameAsExpr(context, parse_node, SemIR::NameId::SelfType);
 }
 
-auto HandleSelfValueName(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleSelfValueName(Context& context, Parse::SelfValueNameId parse_node)
+    -> bool {
   context.node_stack().Push(parse_node, SemIR::NameId::SelfValue);
   return true;
 }
 
-auto HandleSelfValueNameExpr(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleSelfValueNameExpr(Context& context,
+                             Parse::SelfValueNameExprId parse_node) -> bool {
   return HandleNameAsExpr(context, parse_node, SemIR::NameId::SelfValue);
 }
 
-auto HandleQualifiedName(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleQualifiedName(Context& context, Parse::QualifiedNameId parse_node)
+    -> bool {
   auto [parse_node2, name_id2] = context.node_stack().PopNameWithParseNode();
 
   Parse::NodeId parse_node1 = context.node_stack().PeekParseNode();
@@ -346,7 +350,8 @@ auto HandleQualifiedName(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandlePackageExpr(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandlePackageExpr(Context& context, Parse::PackageExprId parse_node)
+    -> bool {
   context.AddInstAndPush(
       parse_node,
       SemIR::NameRef{

+ 9 - 7
toolchain/check/handle_named_constraint.cpp

@@ -6,23 +6,25 @@
 
 namespace Carbon::Check {
 
-auto HandleNamedConstraintDecl(Context& context, Parse::NodeId parse_node)
+auto HandleNamedConstraintDecl(Context& context,
+                               Parse::NamedConstraintDeclId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleNamedConstraintDecl");
 }
 
-auto HandleNamedConstraintDefinition(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleNamedConstraintDefinition(
+    Context& context, Parse::NamedConstraintDefinitionId parse_node) -> bool {
   return context.TODO(parse_node, "HandleNamedConstraintDefinition");
 }
 
-auto HandleNamedConstraintDefinitionStart(Context& context,
-                                          Parse::NodeId parse_node) -> bool {
+auto HandleNamedConstraintDefinitionStart(
+    Context& context, Parse::NamedConstraintDefinitionStartId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleNamedConstraintDefinitionStart");
 }
 
-auto HandleNamedConstraintIntroducer(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleNamedConstraintIntroducer(
+    Context& context, Parse::NamedConstraintIntroducerId parse_node) -> bool {
   return context.TODO(parse_node, "HandleNamedConstraintIntroducer");
 }
 

+ 3 - 3
toolchain/check/handle_namespace.cpp

@@ -10,15 +10,15 @@
 
 namespace Carbon::Check {
 
-auto HandleNamespaceStart(Context& context, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandleNamespaceStart(Context& context,
+                          Parse::NamespaceStartId /*parse_node*/) -> bool {
   // Optional modifiers and the name follow.
   context.decl_state_stack().Push(DeclState::Namespace);
   context.decl_name_stack().PushScopeAndStartName();
   return true;
 }
 
-auto HandleNamespace(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleNamespace(Context& context, Parse::NamespaceId parse_node) -> bool {
   auto name_context = context.decl_name_stack().FinishName();
   LimitModifiersOnDecl(context, KeywordModifierSet::None,
                        Lex::TokenKind::Namespace);

+ 9 - 7
toolchain/check/handle_noop.cpp

@@ -6,28 +6,30 @@
 
 namespace Carbon::Check {
 
-auto HandleEmptyDecl(Context& /*context*/, Parse::NodeId /*parse_node*/)
+auto HandleEmptyDecl(Context& /*context*/, Parse::EmptyDeclId /*parse_node*/)
     -> bool {
   // Empty declarations have no actions associated.
   return true;
 }
 
-auto HandleInvalidParse(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleInvalidParse(Context& context, Parse::InvalidParseId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleInvalidParse");
 }
 
-auto HandleInvalidParseStart(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInvalidParseStart(Context& context,
+                             Parse::InvalidParseStartId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInvalidParseStart");
 }
 
-auto HandleInvalidParseSubtree(Context& context, Parse::NodeId parse_node)
+auto HandleInvalidParseSubtree(Context& context,
+                               Parse::InvalidParseSubtreeId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInvalidParseSubtree");
 }
 
-auto HandlePlaceholder(Context& /*context*/, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandlePlaceholder(Context& /*context*/,
+                       Parse::PlaceholderId /*parse_node*/) -> bool {
   CARBON_FATAL()
       << "Placeholder node should always be replaced before parse completes";
 }

+ 86 - 67
toolchain/check/handle_operator.cpp

@@ -7,17 +7,19 @@
 
 namespace Carbon::Check {
 
-auto HandleInfixOperatorAmp(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorAmp(Context& context,
+                            Parse::InfixOperatorAmpId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorAmp");
 }
 
-auto HandleInfixOperatorAmpEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorAmpEqual(Context& context,
+                                 Parse::InfixOperatorAmpEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorAmpEqual");
 }
 
-auto HandleInfixOperatorAs(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleInfixOperatorAs(Context& context,
+                           Parse::InfixOperatorAsId parse_node) -> bool {
   auto [rhs_node, rhs_id] = context.node_stack().PopExprWithParseNode();
   auto [lhs_node, lhs_id] = context.node_stack().PopExprWithParseNode();
 
@@ -28,18 +30,19 @@ auto HandleInfixOperatorAs(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleInfixOperatorCaret(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorCaret(Context& context,
+                              Parse::InfixOperatorCaretId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorCaret");
 }
 
-auto HandleInfixOperatorCaretEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorCaretEqual(Context& context,
+                                   Parse::InfixOperatorCaretEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorCaretEqual");
 }
 
-auto HandleInfixOperatorEqual(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorEqual(Context& context,
+                              Parse::InfixOperatorEqualId parse_node) -> bool {
   auto [rhs_node, rhs_id] = context.node_stack().PopExprWithParseNode();
   auto [lhs_node, lhs_id] = context.node_stack().PopExprWithParseNode();
 
@@ -63,122 +66,135 @@ auto HandleInfixOperatorEqual(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleInfixOperatorEqualEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorEqualEqual(Context& context,
+                                   Parse::InfixOperatorEqualEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorEqualEqual");
 }
 
-auto HandleInfixOperatorExclaimEqual(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorExclaimEqual(
+    Context& context, Parse::InfixOperatorExclaimEqualId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorExclaimEqual");
 }
 
-auto HandleInfixOperatorGreater(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorGreater(Context& context,
+                                Parse::InfixOperatorGreaterId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorGreater");
 }
 
-auto HandleInfixOperatorGreaterEqual(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorGreaterEqual(
+    Context& context, Parse::InfixOperatorGreaterEqualId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorGreaterEqual");
 }
 
-auto HandleInfixOperatorGreaterGreater(Context& context,
-                                       Parse::NodeId parse_node) -> bool {
+auto HandleInfixOperatorGreaterGreater(
+    Context& context, Parse::InfixOperatorGreaterGreaterId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorGreaterGreater");
 }
 
-auto HandleInfixOperatorGreaterGreaterEqual(Context& context,
-                                            Parse::NodeId parse_node) -> bool {
+auto HandleInfixOperatorGreaterGreaterEqual(
+    Context& context, Parse::InfixOperatorGreaterGreaterEqualId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorGreaterGreaterEqual");
 }
 
-auto HandleInfixOperatorLess(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorLess(Context& context,
+                             Parse::InfixOperatorLessId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorLess");
 }
 
-auto HandleInfixOperatorLessEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorLessEqual(Context& context,
+                                  Parse::InfixOperatorLessEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorLessEqual");
 }
 
-auto HandleInfixOperatorLessEqualGreater(Context& context,
-                                         Parse::NodeId parse_node) -> bool {
+auto HandleInfixOperatorLessEqualGreater(
+    Context& context, Parse::InfixOperatorLessEqualGreaterId parse_node)
+    -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorLessEqualGreater");
 }
 
-auto HandleInfixOperatorLessLess(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorLessLess(Context& context,
+                                 Parse::InfixOperatorLessLessId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorLessLess");
 }
 
-auto HandleInfixOperatorLessLessEqual(Context& context,
-                                      Parse::NodeId parse_node) -> bool {
+auto HandleInfixOperatorLessLessEqual(
+    Context& context, Parse::InfixOperatorLessLessEqualId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorLessLessEqual");
 }
 
-auto HandleInfixOperatorMinus(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorMinus(Context& context,
+                              Parse::InfixOperatorMinusId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorMinus");
 }
 
-auto HandleInfixOperatorMinusEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorMinusEqual(Context& context,
+                                   Parse::InfixOperatorMinusEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorMinusEqual");
 }
 
-auto HandleInfixOperatorPercent(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorPercent(Context& context,
+                                Parse::InfixOperatorPercentId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorPercent");
 }
 
-auto HandleInfixOperatorPercentEqual(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorPercentEqual(
+    Context& context, Parse::InfixOperatorPercentEqualId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorPercentEqual");
 }
 
-auto HandleInfixOperatorPipe(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorPipe(Context& context,
+                             Parse::InfixOperatorPipeId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorPipe");
 }
 
-auto HandleInfixOperatorPipeEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorPipeEqual(Context& context,
+                                  Parse::InfixOperatorPipeEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorPipeEqual");
 }
 
-auto HandleInfixOperatorPlus(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorPlus(Context& context,
+                             Parse::InfixOperatorPlusId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorPlus");
 }
 
-auto HandleInfixOperatorPlusEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorPlusEqual(Context& context,
+                                  Parse::InfixOperatorPlusEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorPlusEqual");
 }
 
-auto HandleInfixOperatorSlash(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorSlash(Context& context,
+                              Parse::InfixOperatorSlashId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorSlash");
 }
 
-auto HandleInfixOperatorSlashEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorSlashEqual(Context& context,
+                                   Parse::InfixOperatorSlashEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorSlashEqual");
 }
 
-auto HandleInfixOperatorStar(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleInfixOperatorStar(Context& context,
+                             Parse::InfixOperatorStarId parse_node) -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorStar");
 }
 
-auto HandleInfixOperatorStarEqual(Context& context, Parse::NodeId parse_node)
+auto HandleInfixOperatorStarEqual(Context& context,
+                                  Parse::InfixOperatorStarEqualId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandleInfixOperatorStarEqual");
 }
 
-auto HandlePostfixOperatorStar(Context& context, Parse::NodeId parse_node)
+auto HandlePostfixOperatorStar(Context& context,
+                               Parse::PostfixOperatorStarId parse_node)
     -> bool {
   auto value_id = context.node_stack().PopExpr();
   auto inner_type_id = ExprAsType(context, parse_node, value_id);
@@ -188,8 +204,8 @@ auto HandlePostfixOperatorStar(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandlePrefixOperatorAmp(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandlePrefixOperatorAmp(Context& context,
+                             Parse::PrefixOperatorAmpId parse_node) -> bool {
   auto value_id = context.node_stack().PopExpr();
   // Only durable reference expressions can have their address taken.
   switch (SemIR::GetExprCategory(context.sem_ir(), value_id)) {
@@ -216,12 +232,14 @@ auto HandlePrefixOperatorAmp(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandlePrefixOperatorCaret(Context& context, Parse::NodeId parse_node)
+auto HandlePrefixOperatorCaret(Context& context,
+                               Parse::PrefixOperatorCaretId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandlePrefixOperatorCaret");
 }
 
-auto HandlePrefixOperatorConst(Context& context, Parse::NodeId parse_node)
+auto HandlePrefixOperatorConst(Context& context,
+                               Parse::PrefixOperatorConstId parse_node)
     -> bool {
   auto value_id = context.node_stack().PopExpr();
 
@@ -241,18 +259,19 @@ auto HandlePrefixOperatorConst(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandlePrefixOperatorMinus(Context& context, Parse::NodeId parse_node)
+auto HandlePrefixOperatorMinus(Context& context,
+                               Parse::PrefixOperatorMinusId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandlePrefixOperatorMinus");
 }
 
-auto HandlePrefixOperatorMinusMinus(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandlePrefixOperatorMinusMinus(
+    Context& context, Parse::PrefixOperatorMinusMinusId parse_node) -> bool {
   return context.TODO(parse_node, "HandlePrefixOperatorMinusMinus");
 }
 
-auto HandlePrefixOperatorNot(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandlePrefixOperatorNot(Context& context,
+                             Parse::PrefixOperatorNotId parse_node) -> bool {
   auto value_id = context.node_stack().PopExpr();
   value_id = ConvertToBoolValue(context, parse_node, value_id);
   context.AddInstAndPush(
@@ -262,18 +281,14 @@ auto HandlePrefixOperatorNot(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandlePrefixOperatorPlus(Context& context, Parse::NodeId parse_node)
-    -> bool {
-  return context.TODO(parse_node, "HandlePrefixOperatorPlus");
-}
-
-auto HandlePrefixOperatorPlusPlus(Context& context, Parse::NodeId parse_node)
+auto HandlePrefixOperatorPlusPlus(Context& context,
+                                  Parse::PrefixOperatorPlusPlusId parse_node)
     -> bool {
   return context.TODO(parse_node, "HandlePrefixOperatorPlusPlus");
 }
 
-auto HandlePrefixOperatorStar(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandlePrefixOperatorStar(Context& context,
+                              Parse::PrefixOperatorStarId parse_node) -> bool {
   auto value_id = context.node_stack().PopExpr();
   value_id = ConvertToValueExpr(context, value_id);
   auto type_id =
@@ -339,12 +354,14 @@ static auto HandleShortCircuitOperand(Context& context,
   return true;
 }
 
-auto HandleShortCircuitOperandAnd(Context& context, Parse::NodeId parse_node)
+auto HandleShortCircuitOperandAnd(Context& context,
+                                  Parse::ShortCircuitOperandAndId parse_node)
     -> bool {
   return HandleShortCircuitOperand(context, parse_node, /*is_or=*/false);
 }
 
-auto HandleShortCircuitOperandOr(Context& context, Parse::NodeId parse_node)
+auto HandleShortCircuitOperandOr(Context& context,
+                                 Parse::ShortCircuitOperandOrId parse_node)
     -> bool {
   return HandleShortCircuitOperand(context, parse_node, /*is_or=*/true);
 }
@@ -375,12 +392,14 @@ static auto HandleShortCircuitOperator(Context& context,
   return true;
 }
 
-auto HandleShortCircuitOperatorAnd(Context& context, Parse::NodeId parse_node)
+auto HandleShortCircuitOperatorAnd(Context& context,
+                                   Parse::ShortCircuitOperatorAndId parse_node)
     -> bool {
   return HandleShortCircuitOperator(context, parse_node);
 }
 
-auto HandleShortCircuitOperatorOr(Context& context, Parse::NodeId parse_node)
+auto HandleShortCircuitOperatorOr(Context& context,
+                                  Parse::ShortCircuitOperatorOrId parse_node)
     -> bool {
   return HandleShortCircuitOperator(context, parse_node);
 }

+ 7 - 4
toolchain/check/handle_paren.cpp

@@ -6,7 +6,7 @@
 
 namespace Carbon::Check {
 
-auto HandleParenExpr(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleParenExpr(Context& context, Parse::ParenExprId parse_node) -> bool {
   auto value_id = context.node_stack().PopExpr();
   // ParamOrArgStart was called for tuple handling; clean up the ParamOrArg
   // support for non-tuple cases.
@@ -17,19 +17,22 @@ auto HandleParenExpr(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleExprOpenParen(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleExprOpenParen(Context& context, Parse::ExprOpenParenId parse_node)
+    -> bool {
   context.node_stack().Push(parse_node);
   context.ParamOrArgStart();
   return true;
 }
 
-auto HandleTupleLiteralComma(Context& context, Parse::NodeId /*parse_node*/)
+auto HandleTupleLiteralComma(Context& context,
+                             Parse::TupleLiteralCommaId /*parse_node*/)
     -> bool {
   context.ParamOrArgComma();
   return true;
 }
 
-auto HandleTupleLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleTupleLiteral(Context& context, Parse::TupleLiteralId parse_node)
+    -> bool {
   auto refs_id = context.ParamOrArgEnd(Parse::NodeKind::ExprOpenParen);
 
   context.node_stack()

+ 10 - 8
toolchain/check/handle_pattern_list.cpp

@@ -6,8 +6,8 @@
 
 namespace Carbon::Check {
 
-auto HandleImplicitParamList(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleImplicitParamList(Context& context,
+                             Parse::ImplicitParamListId parse_node) -> bool {
   auto refs_id = context.ParamOrArgEnd(Parse::NodeKind::ImplicitParamListStart);
   context.node_stack()
       .PopAndDiscardSoloParseNode<Parse::NodeKind::ImplicitParamListStart>();
@@ -17,7 +17,8 @@ auto HandleImplicitParamList(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleImplicitParamListStart(Context& context, Parse::NodeId parse_node)
+auto HandleImplicitParamListStart(Context& context,
+                                  Parse::ImplicitParamListStartId parse_node)
     -> bool {
   context.PushScope();
   context.node_stack().Push(parse_node);
@@ -25,7 +26,8 @@ auto HandleImplicitParamListStart(Context& context, Parse::NodeId parse_node)
   return true;
 }
 
-auto HandleTuplePattern(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleTuplePattern(Context& context, Parse::TuplePatternId parse_node)
+    -> bool {
   auto refs_id = context.ParamOrArgEnd(Parse::NodeKind::TuplePatternStart);
   context.PopScope();
   context.node_stack()
@@ -34,14 +36,14 @@ auto HandleTuplePattern(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandlePatternListComma(Context& context, Parse::NodeId /*parse_node*/)
-    -> bool {
+auto HandlePatternListComma(Context& context,
+                            Parse::PatternListCommaId /*parse_node*/) -> bool {
   context.ParamOrArgComma();
   return true;
 }
 
-auto HandleTuplePatternStart(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleTuplePatternStart(Context& context,
+                             Parse::TuplePatternStartId parse_node) -> bool {
   // A tuple pattern following an implicit parameter list shares the same
   // scope.
   //

+ 6 - 4
toolchain/check/handle_return_statement.cpp

@@ -7,21 +7,23 @@
 
 namespace Carbon::Check {
 
-auto HandleReturnStatementStart(Context& context, Parse::NodeId parse_node)
+auto HandleReturnStatementStart(Context& context,
+                                Parse::ReturnStatementStartId parse_node)
     -> bool {
   // No action, just a bracketing node.
   context.node_stack().Push(parse_node);
   return true;
 }
 
-auto HandleReturnVarModifier(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleReturnVarModifier(Context& context,
+                             Parse::ReturnVarModifierId parse_node) -> bool {
   // No action, just a bracketing node.
   context.node_stack().Push(parse_node);
   return true;
 }
 
-auto HandleReturnStatement(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleReturnStatement(Context& context,
+                           Parse::ReturnStatementId parse_node) -> bool {
   switch (context.node_stack().PeekParseNodeKind()) {
     case Parse::NodeKind::ReturnStatementStart:
       // This is a `return;` statement.

+ 14 - 10
toolchain/check/handle_struct.cpp

@@ -7,19 +7,22 @@
 
 namespace Carbon::Check {
 
-auto HandleStructComma(Context& context, Parse::NodeId /*parse_node*/) -> bool {
+auto HandleStructComma(Context& context, Parse::StructCommaId /*parse_node*/)
+    -> bool {
   context.ParamOrArgComma();
   return true;
 }
 
-auto HandleStructFieldDesignator(Context& context, Parse::NodeId /*parse_node*/)
+auto HandleStructFieldDesignator(Context& context,
+                                 Parse::StructFieldDesignatorId /*parse_node*/)
     -> bool {
   // This leaves the designated name on top because the `.` isn't interesting.
   CARBON_CHECK(context.node_stack().PeekIsName());
   return true;
 }
 
-auto HandleStructFieldType(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleStructFieldType(Context& context,
+                           Parse::StructFieldTypeId parse_node) -> bool {
   auto [type_node, type_id] = context.node_stack().PopExprWithParseNode();
   SemIR::TypeId cast_type_id = ExprAsType(context, type_node, type_id);
 
@@ -30,8 +33,8 @@ auto HandleStructFieldType(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleStructFieldValue(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleStructFieldValue(Context& context,
+                            Parse::StructFieldValueId parse_node) -> bool {
   auto value_inst_id = context.node_stack().PopExpr();
   auto [name_node, name_id] = context.node_stack().PopNameWithParseNode();
 
@@ -72,7 +75,8 @@ static auto DiagnoseDuplicateNames(Context& context,
   return false;
 }
 
-auto HandleStructLiteral(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleStructLiteral(Context& context, Parse::StructLiteralId parse_node)
+    -> bool {
   auto refs_id = context.ParamOrArgEnd(
       Parse::NodeKind::StructLiteralOrStructTypeLiteralStart);
 
@@ -94,8 +98,8 @@ auto HandleStructLiteral(Context& context, Parse::NodeId parse_node) -> bool {
   return true;
 }
 
-auto HandleStructLiteralOrStructTypeLiteralStart(Context& context,
-                                                 Parse::NodeId parse_node)
+auto HandleStructLiteralOrStructTypeLiteralStart(
+    Context& context, Parse::StructLiteralOrStructTypeLiteralStartId parse_node)
     -> bool {
   context.PushScope();
   context.node_stack().Push(parse_node);
@@ -107,8 +111,8 @@ auto HandleStructLiteralOrStructTypeLiteralStart(Context& context,
   return true;
 }
 
-auto HandleStructTypeLiteral(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleStructTypeLiteral(Context& context,
+                             Parse::StructTypeLiteralId parse_node) -> bool {
   auto refs_id = context.ParamOrArgEnd(
       Parse::NodeKind::StructLiteralOrStructTypeLiteralStart);
 

+ 8 - 6
toolchain/check/handle_variable.cpp

@@ -9,29 +9,31 @@
 
 namespace Carbon::Check {
 
-auto HandleVariableIntroducer(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleVariableIntroducer(Context& context,
+                              Parse::VariableIntroducerId parse_node) -> bool {
   // No action, just a bracketing node.
   context.node_stack().Push(parse_node);
   context.decl_state_stack().Push(DeclState::Var);
   return true;
 }
 
-auto HandleReturnedModifier(Context& context, Parse::NodeId parse_node)
-    -> bool {
+auto HandleReturnedModifier(Context& context,
+                            Parse::ReturnedModifierId parse_node) -> bool {
   // No action, just a bracketing node.
   context.node_stack().Push(parse_node);
   return true;
 }
 
-auto HandleVariableInitializer(Context& context, Parse::NodeId parse_node)
+auto HandleVariableInitializer(Context& context,
+                               Parse::VariableInitializerId parse_node)
     -> bool {
   // No action, just a bracketing node.
   context.node_stack().Push(parse_node);
   return true;
 }
 
-auto HandleVariableDecl(Context& context, Parse::NodeId parse_node) -> bool {
+auto HandleVariableDecl(Context& context, Parse::VariableDeclId parse_node)
+    -> bool {
   // Handle the optional initializer.
   auto init_id = SemIR::InstId::Invalid;
   Parse::NodeKind next_kind = context.node_stack().PeekParseNodeKind();

+ 4 - 5
toolchain/check/node_stack.h

@@ -92,20 +92,19 @@ class NodeStack {
   }
 
   // Pops the top of the stack and returns the parse_node.
-  // TODO: return a parse::NodeIdForKind<RequiredParseKind> instead.
   template <const Parse::NodeKind& RequiredParseKind>
-  auto PopForSoloParseNode() -> Parse::NodeId {
+  auto PopForSoloParseNode() -> Parse::NodeIdForKind<RequiredParseKind> {
     Entry back = PopEntry<SemIR::InstId>();
     RequireIdKind(RequiredParseKind, IdKind::SoloParseNode);
     RequireParseKind<RequiredParseKind>(back.parse_node);
-    return back.parse_node;
+    return Parse::NodeIdForKind<RequiredParseKind>(back.parse_node);
   }
 
   // Pops the top of the stack if it is the given kind, and returns the
   // parse_node. Otherwise, returns std::nullopt.
-  // TODO: Return a `Parse::NodeIdForKind<RequiredParseKind>` instead.
   template <const Parse::NodeKind& RequiredParseKind>
-  auto PopForSoloParseNodeIf() -> std::optional<Parse::NodeId> {
+  auto PopForSoloParseNodeIf()
+      -> std::optional<Parse::NodeIdForKind<RequiredParseKind>> {
     if (PeekIs<RequiredParseKind>()) {
       return PopForSoloParseNode<RequiredParseKind>();
     }

+ 6 - 0
toolchain/parse/node_ids.h

@@ -50,6 +50,9 @@ struct NodeIdInCategory : public NodeId {
   // An explicitly invalid instance.
   static const NodeIdInCategory<Category> Invalid;
 
+  // TODO: Support conversion from `NodeIdForKind<Kind>` if `Kind::category()`
+  // overlaps with `Category`.
+
   explicit NodeIdInCategory(NodeId node_id) : NodeId(node_id) {}
 };
 
@@ -72,6 +75,9 @@ struct NodeIdOneOf : public NodeId {
   // An explicitly invalid instance.
   static const NodeIdOneOf<T, U> Invalid;
 
+  // TODO: Support conversion from `NodeIdForKind<Kind>` if `Kind` is
+  // `T::Kind` or `U::Kind`.
+
   explicit NodeIdOneOf(NodeId node_id) : NodeId(node_id) {}
 };
 

+ 1 - 0
toolchain/sem_ir/typed_insts.h

@@ -18,6 +18,7 @@
 //   associated location. Almost all instructions should have this, with
 //   exceptions being things that are generated internally, without any relation
 //   to source syntax, such as predeclared builtins.
+//   TODO: Make these typed parse node id types.
 // - Optionally, a `TypeId type_id;` member, for instructions that produce a
 //   value. This includes instructions that produce an abstract value, such as a
 //   `Namespace`, for which a placeholder type should be used.