Browse Source

Refactor the node stack into its own class. (#2505)

Currently, there's a mix of accessing node_stack_ both directly and indirectly, and there are already several Push/Pop methods to help wrap the behavior. However, there's also direct access because of shifting over time, as well as variations in _how_ the stack is used.

This migrates to a separate class in order to make a more specific contract for the API. It cleans up existing uses and adds APIs where needed.
Jon Ross-Perkins 3 năm trước cách đây
mục cha
commit
2a163ca6cd

+ 7 - 1
toolchain/common/index_base.h

@@ -27,7 +27,13 @@ struct IndexBase {
   constexpr IndexBase() : index(InvalidIndex) {}
   constexpr IndexBase() : index(InvalidIndex) {}
   constexpr explicit IndexBase(int index) : index(index) {}
   constexpr explicit IndexBase(int index) : index(index) {}
 
 
-  auto Print(llvm::raw_ostream& output) const -> void { output << index; }
+  auto Print(llvm::raw_ostream& output) const -> void {
+    if (is_valid()) {
+      output << index;
+    } else {
+      output << "<invalid>";
+    }
+  }
 
 
   auto is_valid() const -> bool { return index != InvalidIndex; }
   auto is_valid() const -> bool { return index != InvalidIndex; }
 
 

+ 31 - 3
toolchain/semantics/BUILD

@@ -20,21 +20,49 @@ cc_library(
     deps = ["//toolchain/common:enum_base"],
     deps = ["//toolchain/common:enum_base"],
 )
 )
 
 
+cc_library(
+    name = "semantics_node",
+    srcs = ["semantics_node.cpp"],
+    hdrs = ["semantics_node.h"],
+    deps = [
+        ":semantics_builtin_kind",
+        ":semantics_node_kind",
+        "//common:check",
+        "//common:ostream",
+        "//toolchain/parser:parse_tree",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_library(
+    name = "semantics_node_stack",
+    srcs = ["semantics_node_stack.cpp"],
+    hdrs = ["semantics_node_stack.h"],
+    deps = [
+        ":semantics_node",
+        "//common:check",
+        "//common:ostream",
+        "//common:vlog",
+        "//toolchain/parser:parse_node_kind",
+        "//toolchain/parser:parse_tree",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_library(
 cc_library(
     name = "semantics_ir",
     name = "semantics_ir",
     srcs = [
     srcs = [
         "semantics_ir.cpp",
         "semantics_ir.cpp",
-        "semantics_node.cpp",
         "semantics_parse_tree_handler.cpp",
         "semantics_parse_tree_handler.cpp",
     ],
     ],
     hdrs = [
     hdrs = [
         "semantics_ir.h",
         "semantics_ir.h",
-        "semantics_node.h",
         "semantics_parse_tree_handler.h",
         "semantics_parse_tree_handler.h",
     ],
     ],
     deps = [
     deps = [
         ":semantics_builtin_kind",
         ":semantics_builtin_kind",
-        ":semantics_node_kind",
+        ":semantics_node",
+        ":semantics_node_stack",
         "//common:check",
         "//common:check",
         "//common:ostream",
         "//common:ostream",
         "//common:vlog",
         "//common:vlog",

+ 20 - 5
toolchain/semantics/semantics_node.h

@@ -28,31 +28,46 @@ struct SemanticsNodeId : public IndexBase {
   static auto MakeInvalid() -> SemanticsNodeId { return SemanticsNodeId(); }
   static auto MakeInvalid() -> SemanticsNodeId { return SemanticsNodeId(); }
 
 
   using IndexBase::IndexBase;
   using IndexBase::IndexBase;
-  auto Print(llvm::raw_ostream& out) const -> void { out << "node" << index; }
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "node";
+    IndexBase::Print(out);
+  }
 };
 };
 
 
 // The ID of a cross-referenced IR.
 // The ID of a cross-referenced IR.
 struct SemanticsCrossReferenceIRId : public IndexBase {
 struct SemanticsCrossReferenceIRId : public IndexBase {
   using IndexBase::IndexBase;
   using IndexBase::IndexBase;
-  auto Print(llvm::raw_ostream& out) const -> void { out << "ir" << index; }
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "ir";
+    IndexBase::Print(out);
+  }
 };
 };
 
 
 // Type-safe storage of integer literals.
 // Type-safe storage of integer literals.
 struct SemanticsIntegerLiteralId : public IndexBase {
 struct SemanticsIntegerLiteralId : public IndexBase {
   using IndexBase::IndexBase;
   using IndexBase::IndexBase;
-  auto Print(llvm::raw_ostream& out) const -> void { out << "int" << index; }
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "int";
+    IndexBase::Print(out);
+  }
 };
 };
 
 
 // Type-safe storage of node blocks.
 // Type-safe storage of node blocks.
 struct SemanticsNodeBlockId : public IndexBase {
 struct SemanticsNodeBlockId : public IndexBase {
   using IndexBase::IndexBase;
   using IndexBase::IndexBase;
-  auto Print(llvm::raw_ostream& out) const -> void { out << "block" << index; }
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "block";
+    IndexBase::Print(out);
+  }
 };
 };
 
 
 // Type-safe storage of strings.
 // Type-safe storage of strings.
 struct SemanticsStringId : public IndexBase {
 struct SemanticsStringId : public IndexBase {
   using IndexBase::IndexBase;
   using IndexBase::IndexBase;
-  auto Print(llvm::raw_ostream& out) const -> void { out << "str" << index; }
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "str";
+    IndexBase::Print(out);
+  }
 };
 };
 
 
 // The standard structure for nodes.
 // The standard structure for nodes.

+ 140 - 0
toolchain/semantics/semantics_node_stack.cpp

@@ -0,0 +1,140 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/semantics/semantics_node_stack.h"
+
+#include "common/vlog.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "toolchain/semantics/semantics_node.h"
+
+namespace Carbon {
+
+auto SemanticsNodeStack::PushEntry(Entry entry, bool is_node_id) -> void {
+  CARBON_VLOG() << "Push " << stack_.size() << ": "
+                << parse_tree_->node_kind(entry.parse_node) << " -> ";
+  if (is_node_id) {
+    CARBON_VLOG() << entry.node_id;
+  } else {
+    CARBON_VLOG() << entry.name_id;
+  }
+  CARBON_VLOG() << "\n";
+  CARBON_CHECK(stack_.size() < (1 << 20))
+      << "Excessive stack size: likely infinite loop";
+  stack_.push_back(entry);
+}
+
+auto SemanticsNodeStack::PopEntry() -> Entry {
+  auto back = stack_.pop_back_val();
+  CARBON_VLOG() << "Pop " << stack_.size() << ": any ("
+                << parse_tree_->node_kind(back.parse_node) << ") -> "
+                << back.node_id << "\n";
+  return back;
+}
+
+auto SemanticsNodeStack::PopEntry(ParseNodeKind pop_parse_kind) -> Entry {
+  auto back = stack_.pop_back_val();
+  CARBON_VLOG() << "Pop " << stack_.size() << ": " << pop_parse_kind << " -> "
+                << back.node_id << "\n";
+  RequireParseKind(back, pop_parse_kind);
+  return back;
+}
+
+auto SemanticsNodeStack::RequireParseKind(Entry entry,
+                                          ParseNodeKind require_kind) -> void {
+  auto actual_kind = parse_tree_->node_kind(entry.parse_node);
+  CARBON_CHECK(require_kind == actual_kind)
+      << "Expected " << require_kind << ", found " << actual_kind;
+}
+
+auto SemanticsNodeStack::RequireSoloParseNode(Entry entry) -> void {
+  CARBON_CHECK(!entry.node_id.is_valid())
+      << "Expected invalid node_id on "
+      << parse_tree_->node_kind(entry.parse_node) << ", was " << entry.node_id;
+}
+
+auto SemanticsNodeStack::RequireNodeId(Entry entry) -> void {
+  CARBON_CHECK(entry.node_id.is_valid())
+      << "Expected valid node_id on "
+      << parse_tree_->node_kind(entry.parse_node);
+}
+
+auto SemanticsNodeStack::PopAndDiscardSoloParseNode(
+    ParseNodeKind pop_parse_kind) -> void {
+  auto back = PopEntry(pop_parse_kind);
+  RequireSoloParseNode(back);
+}
+
+auto SemanticsNodeStack::PopForSoloParseNode() -> ParseTree::Node {
+  auto back = PopEntry();
+  RequireSoloParseNode(back);
+  return back.parse_node;
+}
+
+auto SemanticsNodeStack::PopForSoloParseNode(ParseNodeKind pop_parse_kind)
+    -> ParseTree::Node {
+  auto back = PopEntry(pop_parse_kind);
+  RequireSoloParseNode(back);
+  return back.parse_node;
+}
+
+auto SemanticsNodeStack::PopForNodeId() -> SemanticsNodeId {
+  auto back = PopEntry();
+  RequireNodeId(back);
+  return back.node_id;
+}
+
+auto SemanticsNodeStack::PopForNodeId(ParseNodeKind pop_parse_kind)
+    -> SemanticsNodeId {
+  auto back = PopEntry(pop_parse_kind);
+  RequireNodeId(back);
+  return back.node_id;
+}
+
+auto SemanticsNodeStack::PopForParseNodeAndNodeId()
+    -> std::pair<ParseTree::Node, SemanticsNodeId> {
+  auto back = PopEntry();
+  RequireNodeId(back);
+  return {back.parse_node, back.node_id};
+}
+
+auto SemanticsNodeStack::PopForParseNodeAndNodeId(ParseNodeKind pop_parse_kind)
+    -> std::pair<ParseTree::Node, SemanticsNodeId> {
+  auto back = PopEntry(pop_parse_kind);
+  RequireNodeId(back);
+  return {back.parse_node, back.node_id};
+}
+
+auto SemanticsNodeStack::PopForParseNodeAndNameId()
+    -> std::pair<ParseTree::Node, SemanticsStringId> {
+  auto back = PopEntry(ParseNodeKind::PatternBinding);
+  RequireNodeId(back);
+  return {back.parse_node, back.name_id};
+}
+
+auto SemanticsNodeStack::PeekForNameId() -> SemanticsStringId {
+  auto back = stack_.back();
+  RequireParseKind(back, ParseNodeKind::PatternBinding);
+  RequireNodeId(back);
+  return back.name_id;
+}
+
+auto SemanticsNodeStack::PrintForStackDump(llvm::raw_ostream& output) const
+    -> void {
+  output << "node_stack_:\n";
+  for (int i = 0; i < static_cast<int>(stack_.size()); ++i) {
+    const auto& entry = stack_[i];
+    auto parse_node_kind = parse_tree_->node_kind(entry.parse_node);
+    output << "\t" << i << ".\t" << parse_node_kind;
+    if (parse_node_kind == ParseNodeKind::PatternBinding) {
+      output << " -> " << entry.name_id;
+    } else {
+      if (entry.node_id.is_valid()) {
+        output << " -> " << entry.node_id;
+      }
+    }
+    output << "\n";
+  }
+}
+
+}  // namespace Carbon

+ 145 - 0
toolchain/semantics/semantics_node_stack.h

@@ -0,0 +1,145 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_NODE_STACK_H_
+#define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_NODE_STACK_H_
+
+#include "llvm/ADT/SmallVector.h"
+#include "toolchain/parser/parse_node_kind.h"
+#include "toolchain/parser/parse_tree.h"
+#include "toolchain/semantics/semantics_node.h"
+
+namespace Carbon {
+
+// Wraps the stack of nodes for SemanticsParseTreeHandler.
+//
+// All pushes and pops will be vlogged.
+//
+// Pop APIs will run basic verification:
+//
+// - If receiving a pop_parse_kind, verify that the parse_node being popped is
+//   of pop_parse_kind.
+// - Validates presence of node_id based on whether it's a solo
+//   parse_node.
+//
+// These should be assumed API constraints unless otherwise mentioned on a
+// method. The main exception is PopAndIgnore, which doesn't do verification.
+class SemanticsNodeStack {
+ public:
+  SemanticsNodeStack(const ParseTree& parse_tree,
+                     llvm::raw_ostream* vlog_stream)
+      : parse_tree_(&parse_tree), vlog_stream_(vlog_stream) {}
+
+  // Pushes a solo parse tree node onto the stack. Used when there is no
+  // IR generated by the node.
+  auto Push(ParseTree::Node parse_node) -> void {
+    PushEntry(
+        {.parse_node = parse_node, .node_id = SemanticsNodeId::MakeInvalid()},
+        /*is_node_id=*/true);
+  }
+
+  // Pushes a parse tree node onto the stack.
+  auto Push(ParseTree::Node parse_node, SemanticsNodeId node_id) -> void {
+    PushEntry({.parse_node = parse_node, .node_id = node_id},
+              /*is_node_id=*/true);
+  }
+
+  // Pushes a PatternBinding parse tree node onto the stack with its name.
+  auto Push(ParseTree::Node parse_node, SemanticsStringId name_id) -> void {
+    CARBON_CHECK(parse_tree_->node_kind(parse_node) ==
+                 ParseNodeKind::PatternBinding);
+    PushEntry({.parse_node = parse_node, .name_id = name_id},
+              /*is_node_id=*/false);
+  }
+
+  // Pops the top of the stack without any verification.
+  auto PopAndIgnore() -> void { PopEntry(); }
+
+  // Pops the top of the stack.
+  auto PopAndDiscardSoloParseNode(ParseNodeKind pop_parse_kind) -> void;
+
+  // Pops the top of the stack and returns the parse_node.
+  auto PopForSoloParseNode() -> ParseTree::Node;
+
+  // Pops the top of the stack and returns the parse_node.
+  auto PopForSoloParseNode(ParseNodeKind pop_parse_kind) -> ParseTree::Node;
+
+  // Pops the top of the stack and returns the node_id.
+  auto PopForNodeId(ParseNodeKind pop_parse_kind) -> SemanticsNodeId;
+
+  // Pops the top of the stack and returns the parse_node and node_id.
+  auto PopForParseNodeAndNodeId()
+      -> std::pair<ParseTree::Node, SemanticsNodeId>;
+
+  // Pops the top of the stack and returns the parse_node and node_id.
+  auto PopForParseNodeAndNodeId(ParseNodeKind pop_parse_kind)
+      -> std::pair<ParseTree::Node, SemanticsNodeId>;
+
+  // Pops the top of the stack and returns the node_id.
+  auto PopForNodeId() -> SemanticsNodeId;
+
+  // Pops the top of the stack and returns the parse_node and name_id.
+  // Verifies that the parse_node is a PatternBinding.
+  auto PopForParseNodeAndNameId()
+      -> std::pair<ParseTree::Node, SemanticsStringId>;
+
+  // Peeks at the parse_node of the top of the stack.
+  auto PeekParseNode() -> ParseTree::Node { return stack_.back().parse_node; }
+
+  // Peeks at the name_id of the top of the stack.
+  // Verifies that the parse_node is a PatternBinding.
+  auto PeekForNameId() -> SemanticsStringId;
+
+  // Prints the stack for a stack dump.
+  auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
+
+ private:
+  // An entry in node_stack_.
+  struct Entry {
+    ParseTree::Node parse_node;
+    union {
+      // The node_id will be invalid if and only if it's a solo parse_node.
+      SemanticsNodeId node_id;
+      // The name_id is provided for PatternBindings.
+      SemanticsStringId name_id;
+    };
+  };
+  static_assert(sizeof(Entry) == 8, "Unexpected Entry size");
+
+  // Pushes an entry onto the stack. is_node_id is provided for debug output
+  // only.
+  auto PushEntry(Entry entry, bool is_node_id) -> void;
+
+  // Pops an entry.
+  auto PopEntry() -> Entry;
+
+  // Pops an entry, requiring the specific kind.
+  auto PopEntry(ParseNodeKind pop_parse_kind) -> Entry;
+
+  // Require an entry to have the given ParseNodeKind.
+  auto RequireParseKind(Entry entry, ParseNodeKind require_kind) -> void;
+
+  // Requires an entry to have a invalid node_id.
+  // Also works with name_id in the union due to type compatibility.
+  auto RequireSoloParseNode(Entry entry) -> void;
+
+  // Requires an entry to have a valid node_id.
+  // Also works with name_id in the union due to type compatibility.
+  auto RequireNodeId(Entry entry) -> void;
+
+  // The file's parse tree.
+  const ParseTree* parse_tree_;
+
+  // Whether to print verbose output.
+  llvm::raw_ostream* vlog_stream_;
+
+  // The actual stack.
+  // PushEntry and PopEntry control modification in order to centralize
+  // vlogging.
+  llvm::SmallVector<Entry> stack_;
+};
+
+}  // namespace Carbon
+
+#endif  // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_NODE_STACK_H_

+ 67 - 174
toolchain/semantics/semantics_parse_tree_handler.cpp

@@ -22,20 +22,7 @@ class SemanticsParseTreeHandler::PrettyStackTraceNodeStack
   ~PrettyStackTraceNodeStack() override = default;
   ~PrettyStackTraceNodeStack() override = default;
 
 
   auto print(llvm::raw_ostream& output) const -> void override {
   auto print(llvm::raw_ostream& output) const -> void override {
-    output << "node_stack_:\n";
-    for (int i = 0; i < static_cast<int>(handler_->node_stack_.size()); ++i) {
-      const auto& entry = handler_->node_stack_[i];
-      auto parse_node_kind = handler_->parse_tree_->node_kind(entry.parse_node);
-      output << "\t" << i << ".\t" << parse_node_kind;
-      if (parse_node_kind == ParseNodeKind::PatternBinding) {
-        output << " -> " << entry.name_id;
-      } else {
-        if (entry.result_id.is_valid()) {
-          output << " -> " << entry.result_id;
-        }
-      }
-      output << "\n";
-    }
+    handler_->node_stack_.PrintForStackDump(output);
   }
   }
 
 
  private:
  private:
@@ -95,6 +82,12 @@ auto SemanticsParseTreeHandler::AddNode(SemanticsNode node) -> SemanticsNodeId {
   return semantics_->AddNode(current_block_id(), node);
   return semantics_->AddNode(current_block_id(), node);
 }
 }
 
 
+auto SemanticsParseTreeHandler::AddNodeAndPush(ParseTree::Node parse_node,
+                                               SemanticsNode node) -> void {
+  auto node_id = AddNode(node);
+  node_stack_.Push(parse_node, node_id);
+}
+
 auto SemanticsParseTreeHandler::BindName(ParseTree::Node name_node,
 auto SemanticsParseTreeHandler::BindName(ParseTree::Node name_node,
                                          SemanticsNodeId type_id,
                                          SemanticsNodeId type_id,
                                          SemanticsNodeId target_id)
                                          SemanticsNodeId target_id)
@@ -122,101 +115,6 @@ auto SemanticsParseTreeHandler::BindName(ParseTree::Node name_node,
   return name_id;
   return name_id;
 }
 }
 
 
-auto SemanticsParseTreeHandler::Push(ParseTree::Node parse_node) -> void {
-  CARBON_VLOG() << "Push " << node_stack_.size() << ": "
-                << parse_tree_->node_kind(parse_node) << "\n";
-  CARBON_CHECK(node_stack_.size() < (1 << 20))
-      << "Excessive stack size: likely infinite loop";
-  node_stack_.push_back(
-      {.parse_node = parse_node, .result_id = SemanticsNodeId::MakeInvalid()});
-}
-
-auto SemanticsParseTreeHandler::Push(ParseTree::Node parse_node,
-                                     SemanticsNode node) -> void {
-  CARBON_VLOG() << "Push " << node_stack_.size() << ": "
-                << parse_tree_->node_kind(parse_node) << " -> " << node.kind()
-                << "\n";
-  CARBON_CHECK(node_stack_.size() < (1 << 20))
-      << "Excessive stack size: likely infinite loop";
-  auto node_id = AddNode(node);
-  node_stack_.push_back({.parse_node = parse_node, .result_id = node_id});
-}
-
-auto SemanticsParseTreeHandler::Push(ParseTree::Node parse_node,
-                                     SemanticsNodeId node_id) -> void {
-  CARBON_VLOG() << "Push " << node_stack_.size() << ": "
-                << parse_tree_->node_kind(parse_node) << " -> " << node_id
-                << "\n";
-  CARBON_CHECK(node_stack_.size() < (1 << 20))
-      << "Excessive stack size: likely infinite loop";
-  node_stack_.push_back({.parse_node = parse_node, .result_id = node_id});
-}
-
-auto SemanticsParseTreeHandler::Push(ParseTree::Node parse_node,
-                                     SemanticsStringId name_id) -> void {
-  CARBON_CHECK(parse_tree_->node_kind(parse_node) ==
-               ParseNodeKind::PatternBinding);
-  CARBON_VLOG() << "Push " << node_stack_.size() << ": "
-                << parse_tree_->node_kind(parse_node) << " -> " << name_id
-                << "\n";
-  CARBON_CHECK(node_stack_.size() < (1 << 20))
-      << "Excessive stack size: likely infinite loop";
-  node_stack_.push_back({.parse_node = parse_node, .name_id = name_id});
-}
-
-auto SemanticsParseTreeHandler::Pop(ParseNodeKind pop_parse_kind) -> void {
-  auto back = node_stack_.pop_back_val();
-  auto parse_kind = parse_tree_->node_kind(back.parse_node);
-  CARBON_VLOG() << "Pop " << node_stack_.size() << ": " << pop_parse_kind
-                << "\n";
-  CARBON_CHECK(parse_kind == pop_parse_kind)
-      << "Expected " << pop_parse_kind << ", found " << parse_kind;
-  CARBON_CHECK(!back.result_id.is_valid())
-      << "Expected no result ID on " << parse_kind << ", was "
-      << back.result_id;
-}
-
-auto SemanticsParseTreeHandler::PopWithResult() -> SemanticsNodeId {
-  auto back = node_stack_.pop_back_val();
-  auto node_id = back.result_id;
-  CARBON_VLOG() << "Pop " << node_stack_.size() << ": any ("
-                << parse_tree_->node_kind(back.parse_node) << ") -> " << node_id
-                << "\n";
-  CARBON_CHECK(node_id.is_valid())
-      << "Invalid PopWithResult on " << parse_tree_->node_kind(back.parse_node);
-  return node_id;
-}
-
-auto SemanticsParseTreeHandler::PopWithResult(ParseNodeKind pop_parse_kind)
-    -> SemanticsNodeId {
-  auto back = node_stack_.pop_back_val();
-  auto parse_kind = parse_tree_->node_kind(back.parse_node);
-  auto node_id = back.result_id;
-  CARBON_VLOG() << "Pop " << node_stack_.size() << ": " << pop_parse_kind
-                << ") -> " << node_id << "\n";
-  CARBON_CHECK(parse_kind == pop_parse_kind)
-      << "Expected " << pop_parse_kind << ", found " << parse_kind;
-  CARBON_CHECK(node_id.is_valid())
-      << "Invalid PopWithResult with " << parse_kind;
-  return node_id;
-}
-
-auto SemanticsParseTreeHandler::PopWithResultIf(ParseNodeKind pop_parse_kind)
-    -> std::optional<SemanticsNodeId> {
-  auto parse_kind = parse_tree_->node_kind(node_stack_.back().parse_node);
-  if (parse_kind != pop_parse_kind) {
-    return std::nullopt;
-  }
-
-  auto back = node_stack_.pop_back_val();
-  auto node_id = back.result_id;
-  CARBON_VLOG() << "Pop " << node_stack_.size() << ": " << pop_parse_kind
-                << ") -> " << node_id << "\n";
-  CARBON_CHECK(node_id.is_valid())
-      << "Invalid PopWithResult with " << parse_kind;
-  return node_id;
-}
-
 auto SemanticsParseTreeHandler::PushScope() -> void {
 auto SemanticsParseTreeHandler::PushScope() -> void {
   scope_stack_.push_back({});
   scope_stack_.push_back({});
 }
 }
@@ -311,7 +209,7 @@ auto SemanticsParseTreeHandler::HandleContinueStatementStart(
 auto SemanticsParseTreeHandler::HandleDeclaredName(ParseTree::Node parse_node)
 auto SemanticsParseTreeHandler::HandleDeclaredName(ParseTree::Node parse_node)
     -> void {
     -> void {
   // The parent is responsible for binding the name.
   // The parent is responsible for binding the name.
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleDeducedParameterList(
 auto SemanticsParseTreeHandler::HandleDeducedParameterList(
@@ -338,7 +236,7 @@ auto SemanticsParseTreeHandler::HandleEmptyDeclaration(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
   // Empty declarations have no actions associated, but we still balance the
   // Empty declarations have no actions associated, but we still balance the
   // tree.
   // tree.
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleExpressionStatement(
 auto SemanticsParseTreeHandler::HandleExpressionStatement(
@@ -346,8 +244,8 @@ auto SemanticsParseTreeHandler::HandleExpressionStatement(
   // Pop the expression without investigating its contents.
   // Pop the expression without investigating its contents.
   // TODO: This will probably eventually need to do some "do not discard"
   // TODO: This will probably eventually need to do some "do not discard"
   // analysis.
   // analysis.
-  PopWithResult();
-  Push(parse_node);
+  node_stack_.PopAndIgnore();
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleFileEnd(ParseTree::Node /*parse_node*/)
 auto SemanticsParseTreeHandler::HandleFileEnd(ParseTree::Node /*parse_node*/)
@@ -383,23 +281,23 @@ auto SemanticsParseTreeHandler::HandleFunctionDeclaration(
 auto SemanticsParseTreeHandler::HandleFunctionDefinition(
 auto SemanticsParseTreeHandler::HandleFunctionDefinition(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
   // Merges code block children up under the FunctionDefinitionStart.
   // Merges code block children up under the FunctionDefinitionStart.
-  while (parse_tree_->node_kind(node_stack_.back().parse_node) !=
+  while (parse_tree_->node_kind(node_stack_.PeekParseNode()) !=
          ParseNodeKind::FunctionDefinitionStart) {
          ParseNodeKind::FunctionDefinitionStart) {
-    node_stack_.pop_back();
+    node_stack_.PopAndIgnore();
   }
   }
-  Pop(ParseNodeKind::FunctionDefinitionStart);
+  node_stack_.PopAndIgnore();
+
   PopScope();
   PopScope();
   node_block_stack_.pop_back();
   node_block_stack_.pop_back();
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart(
 auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
-  Pop(ParseNodeKind::ParameterList);
-  auto name_node = node_stack_.back().parse_node;
-  node_stack_.pop_back();
-  auto fn_node = node_stack_.back().parse_node;
-  Pop(ParseNodeKind::FunctionIntroducer);
+  node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ParameterList);
+  auto name_node = node_stack_.PopForSoloParseNode(ParseNodeKind::DeclaredName);
+  auto fn_node =
+      node_stack_.PopForSoloParseNode(ParseNodeKind::FunctionIntroducer);
 
 
   auto decl_id = AddNode(SemanticsNode::MakeFunctionDeclaration(fn_node));
   auto decl_id = AddNode(SemanticsNode::MakeFunctionDeclaration(fn_node));
   // TODO: Propagate the type of the function.
   // TODO: Propagate the type of the function.
@@ -408,13 +306,13 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart(
   AddNode(SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, block_id));
   AddNode(SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, block_id));
   node_block_stack_.push_back(block_id);
   node_block_stack_.push_back(block_id);
   PushScope();
   PushScope();
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleFunctionIntroducer(
 auto SemanticsParseTreeHandler::HandleFunctionIntroducer(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
   // No action, just a bracketing node.
   // No action, just a bracketing node.
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleIfCondition(
 auto SemanticsParseTreeHandler::HandleIfCondition(
@@ -439,8 +337,8 @@ auto SemanticsParseTreeHandler::HandleIfStatementElse(
 
 
 auto SemanticsParseTreeHandler::HandleInfixOperator(ParseTree::Node parse_node)
 auto SemanticsParseTreeHandler::HandleInfixOperator(ParseTree::Node parse_node)
     -> void {
     -> void {
-  auto rhs_id = PopWithResult();
-  auto lhs_id = PopWithResult();
+  auto rhs_id = node_stack_.PopForNodeId();
+  auto lhs_id = node_stack_.PopForNodeId();
   SemanticsNodeId result_type =
   SemanticsNodeId result_type =
       TryTypeConversion(parse_node, lhs_id, rhs_id, /*can_convert_lhs=*/true);
       TryTypeConversion(parse_node, lhs_id, rhs_id, /*can_convert_lhs=*/true);
 
 
@@ -448,8 +346,8 @@ auto SemanticsParseTreeHandler::HandleInfixOperator(ParseTree::Node parse_node)
   auto token = parse_tree_->node_token(parse_node);
   auto token = parse_tree_->node_token(parse_node);
   switch (auto token_kind = tokens_->GetKind(token)) {
   switch (auto token_kind = tokens_->GetKind(token)) {
     case TokenKind::Plus:
     case TokenKind::Plus:
-      Push(parse_node, SemanticsNode::MakeBinaryOperatorAdd(
-                           parse_node, result_type, lhs_id, rhs_id));
+      AddNodeAndPush(parse_node, SemanticsNode::MakeBinaryOperatorAdd(
+                                     parse_node, result_type, lhs_id, rhs_id));
       break;
       break;
     default:
     default:
       CARBON_FATAL() << "Unrecognized token kind: " << token_kind.name();
       CARBON_FATAL() << "Unrecognized token kind: " << token_kind.name();
@@ -478,19 +376,20 @@ auto SemanticsParseTreeHandler::HandleLiteral(ParseTree::Node parse_node)
     case TokenKind::IntegerLiteral: {
     case TokenKind::IntegerLiteral: {
       auto id =
       auto id =
           semantics_->AddIntegerLiteral(tokens_->GetIntegerLiteral(token));
           semantics_->AddIntegerLiteral(tokens_->GetIntegerLiteral(token));
-      Push(parse_node, SemanticsNode::MakeIntegerLiteral(parse_node, id));
+      AddNodeAndPush(parse_node,
+                     SemanticsNode::MakeIntegerLiteral(parse_node, id));
       break;
       break;
     }
     }
     case TokenKind::RealLiteral: {
     case TokenKind::RealLiteral: {
       // TODO: Add storage of the Real literal.
       // TODO: Add storage of the Real literal.
-      Push(parse_node, SemanticsNode::MakeRealLiteral(parse_node));
+      AddNodeAndPush(parse_node, SemanticsNode::MakeRealLiteral(parse_node));
       break;
       break;
     }
     }
     case TokenKind::IntegerTypeLiteral: {
     case TokenKind::IntegerTypeLiteral: {
       auto text = tokens_->GetTokenText(token);
       auto text = tokens_->GetTokenText(token);
       CARBON_CHECK(text == "i32") << "Currently only i32 is allowed";
       CARBON_CHECK(text == "i32") << "Currently only i32 is allowed";
-      Push(parse_node, SemanticsNodeId::MakeBuiltinReference(
-                           SemanticsBuiltinKind::IntegerType));
+      node_stack_.Push(parse_node, SemanticsNodeId::MakeBuiltinReference(
+                                       SemanticsBuiltinKind::IntegerType));
       break;
       break;
     }
     }
     default:
     default:
@@ -506,8 +405,8 @@ auto SemanticsParseTreeHandler::HandleNameReference(ParseTree::Node parse_node)
     CARBON_DIAGNOSTIC(NameNotFound, Error, "Name {0} not found",
     CARBON_DIAGNOSTIC(NameNotFound, Error, "Name {0} not found",
                       llvm::StringRef);
                       llvm::StringRef);
     emitter_->Emit(parse_node, NameNotFound, name_str);
     emitter_->Emit(parse_node, NameNotFound, name_str);
-    Push(parse_node, SemanticsNodeId::MakeBuiltinReference(
-                         SemanticsBuiltinKind::InvalidType));
+    node_stack_.Push(parse_node, SemanticsNodeId::MakeBuiltinReference(
+                                     SemanticsBuiltinKind::InvalidType));
   };
   };
 
 
   auto name_id = semantics_->GetString(name_str);
   auto name_id = semantics_->GetString(name_str);
@@ -524,7 +423,7 @@ auto SemanticsParseTreeHandler::HandleNameReference(ParseTree::Node parse_node)
   CARBON_CHECK(!it->second.empty()) << "Should have been erased: " << name_str;
   CARBON_CHECK(!it->second.empty()) << "Should have been erased: " << name_str;
 
 
   // TODO: Check for ambiguous lookups.
   // TODO: Check for ambiguous lookups.
-  Push(parse_node, it->second.back());
+  node_stack_.Push(parse_node, it->second.back());
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandlePackageApi(ParseTree::Node /*parse_node*/)
 auto SemanticsParseTreeHandler::HandlePackageApi(ParseTree::Node /*parse_node*/)
@@ -556,8 +455,8 @@ auto SemanticsParseTreeHandler::HandleParameterList(ParseTree::Node parse_node)
     -> void {
     -> void {
   // TODO: This should transform into a usable parameter list. For now
   // TODO: This should transform into a usable parameter list. For now
   // it's unused and only stored so that node counts match.
   // it's unused and only stored so that node counts match.
-  Pop(ParseNodeKind::ParameterListStart);
-  Push(parse_node);
+  node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ParameterListStart);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleParameterListComma(
 auto SemanticsParseTreeHandler::HandleParameterListComma(
@@ -568,7 +467,7 @@ auto SemanticsParseTreeHandler::HandleParameterListComma(
 auto SemanticsParseTreeHandler::HandleParameterListStart(
 auto SemanticsParseTreeHandler::HandleParameterListStart(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
   // TODO: See HandleParameterList.
   // TODO: See HandleParameterList.
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleParenExpression(
 auto SemanticsParseTreeHandler::HandleParenExpression(
@@ -583,23 +482,21 @@ auto SemanticsParseTreeHandler::HandleParenExpressionOrTupleLiteralStart(
 
 
 auto SemanticsParseTreeHandler::HandlePatternBinding(ParseTree::Node parse_node)
 auto SemanticsParseTreeHandler::HandlePatternBinding(ParseTree::Node parse_node)
     -> void {
     -> void {
-  auto type = node_stack_.pop_back_val();
-  CARBON_CHECK(type.result_id.is_valid());
+  auto type = node_stack_.PopForNodeId();
 
 
   // Get the name.
   // Get the name.
-  auto name_node = node_stack_.pop_back_val().parse_node;
+  auto name_node = node_stack_.PopForSoloParseNode();
 
 
   // Allocate storage, linked to the name for error locations.
   // Allocate storage, linked to the name for error locations.
-  auto storage_id =
-      AddNode(SemanticsNode::MakeVarStorage(name_node, type.result_id));
+  auto storage_id = AddNode(SemanticsNode::MakeVarStorage(name_node, type));
 
 
   // Bind the name to storage.
   // Bind the name to storage.
-  auto name_id = BindName(name_node, type.result_id, storage_id);
+  auto name_id = BindName(name_node, type, storage_id);
 
 
   // If this node's result is used, it'll be for either the name or the storage
   // If this node's result is used, it'll be for either the name or the storage
   // address. The storage address can be found through the name, so we push the
   // address. The storage address can be found through the name, so we push the
   // name.
   // name.
-  Push(parse_node, name_id);
+  node_stack_.Push(parse_node, name_id);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandlePostfixOperator(
 auto SemanticsParseTreeHandler::HandlePostfixOperator(
@@ -614,23 +511,23 @@ auto SemanticsParseTreeHandler::HandlePrefixOperator(
 
 
 auto SemanticsParseTreeHandler::HandleReturnStatement(
 auto SemanticsParseTreeHandler::HandleReturnStatement(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
-  if (parse_tree_->node_kind(node_stack_.back().parse_node) ==
+  if (parse_tree_->node_kind(node_stack_.PeekParseNode()) ==
       ParseNodeKind::ReturnStatementStart) {
       ParseNodeKind::ReturnStatementStart) {
-    Pop(ParseNodeKind::ReturnStatementStart);
-    Push(parse_node, SemanticsNode::MakeReturn(parse_node));
+    node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ReturnStatementStart);
+    AddNodeAndPush(parse_node, SemanticsNode::MakeReturn(parse_node));
   } else {
   } else {
-    auto arg = PopWithResult();
+    auto arg = node_stack_.PopForNodeId();
     auto arg_type = semantics_->GetType(arg);
     auto arg_type = semantics_->GetType(arg);
-    Pop(ParseNodeKind::ReturnStatementStart);
-    Push(parse_node,
-         SemanticsNode::MakeReturnExpression(parse_node, arg_type, arg));
+    node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ReturnStatementStart);
+    AddNodeAndPush(parse_node, SemanticsNode::MakeReturnExpression(
+                                   parse_node, arg_type, arg));
   }
   }
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleReturnStatementStart(
 auto SemanticsParseTreeHandler::HandleReturnStatementStart(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
   // No action, just a bracketing node.
   // No action, just a bracketing node.
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleReturnType(ParseTree::Node /*parse_node*/)
 auto SemanticsParseTreeHandler::HandleReturnType(ParseTree::Node /*parse_node*/)
@@ -700,49 +597,45 @@ auto SemanticsParseTreeHandler::HandleTupleLiteralComma(
 
 
 auto SemanticsParseTreeHandler::HandleVariableDeclaration(
 auto SemanticsParseTreeHandler::HandleVariableDeclaration(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
-  auto last_child = node_stack_.pop_back_val();
-  CARBON_CHECK(last_child.result_id.is_valid());
+  auto last_child = node_stack_.PopForParseNodeAndNodeId();
 
 
-  if (parse_tree_->node_kind(last_child.parse_node) !=
+  if (parse_tree_->node_kind(last_child.first) !=
       ParseNodeKind::PatternBinding) {
       ParseNodeKind::PatternBinding) {
-    SemanticsNodeId init_id = last_child.result_id;
-    auto storage_id = PopWithResult(ParseNodeKind::VariableInitializer);
+    auto storage_id =
+        node_stack_.PopForNodeId(ParseNodeKind::VariableInitializer);
 
 
-    auto binding = node_stack_.pop_back_val();
-    CARBON_CHECK(parse_tree_->node_kind(binding.parse_node) ==
+    auto binding = node_stack_.PopForParseNodeAndNameId();
+    CARBON_CHECK(parse_tree_->node_kind(binding.first) ==
                  ParseNodeKind::PatternBinding);
                  ParseNodeKind::PatternBinding);
-    CARBON_CHECK(binding.name_id.is_valid());
+    CARBON_CHECK(binding.second.is_valid());
 
 
     // Restore the name now that the initializer is complete.
     // Restore the name now that the initializer is complete.
-    AddNameToLookup(binding.name_id, storage_id);
+    AddNameToLookup(binding.second, storage_id);
 
 
-    auto storage_type = TryTypeConversion(parse_node, storage_id, init_id,
-                                          /*can_convert_lhs=*/false);
+    auto storage_type =
+        TryTypeConversion(parse_node, storage_id, last_child.second,
+                          /*can_convert_lhs=*/false);
     AddNode(SemanticsNode::MakeAssign(parse_node, storage_type, storage_id,
     AddNode(SemanticsNode::MakeAssign(parse_node, storage_type, storage_id,
-                                      init_id));
+                                      last_child.second));
   }
   }
 
 
-  Pop(ParseNodeKind::VariableIntroducer);
-  Push(parse_node);
+  node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::VariableIntroducer);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleVariableIntroducer(
 auto SemanticsParseTreeHandler::HandleVariableIntroducer(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
   // No action, just a bracketing node.
   // No action, just a bracketing node.
-  Push(parse_node);
+  node_stack_.Push(parse_node);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleVariableInitializer(
 auto SemanticsParseTreeHandler::HandleVariableInitializer(
     ParseTree::Node parse_node) -> void {
     ParseTree::Node parse_node) -> void {
   // Temporarily remove name lookup entries added by the `var`. These will be
   // Temporarily remove name lookup entries added by the `var`. These will be
   // restored by `VariableDeclaration`.
   // restored by `VariableDeclaration`.
-  auto back = node_stack_.back();
-  CARBON_CHECK(parse_tree_->node_kind(back.parse_node) ==
-               ParseNodeKind::PatternBinding)
-      << parse_tree_->node_kind(back.parse_node);
 
 
   // Save the storage ID.
   // Save the storage ID.
-  auto it = name_lookup_.find(back.name_id);
+  auto it = name_lookup_.find(node_stack_.PeekForNameId());
   CARBON_CHECK(it != name_lookup_.end());
   CARBON_CHECK(it != name_lookup_.end());
   CARBON_CHECK(!it->second.empty());
   CARBON_CHECK(!it->second.empty());
   auto storage_id = it->second.back();
   auto storage_id = it->second.back();
@@ -755,7 +648,7 @@ auto SemanticsParseTreeHandler::HandleVariableInitializer(
     it->second.pop_back();
     it->second.pop_back();
   }
   }
 
 
-  Push(parse_node, storage_id);
+  node_stack_.Push(parse_node, storage_id);
 }
 }
 
 
 auto SemanticsParseTreeHandler::HandleWhileCondition(
 auto SemanticsParseTreeHandler::HandleWhileCondition(

+ 8 - 44
toolchain/semantics/semantics_parse_tree_handler.h

@@ -11,6 +11,7 @@
 #include "toolchain/parser/parse_tree.h"
 #include "toolchain/parser/parse_tree.h"
 #include "toolchain/semantics/semantics_ir.h"
 #include "toolchain/semantics/semantics_ir.h"
 #include "toolchain/semantics/semantics_node.h"
 #include "toolchain/semantics/semantics_node.h"
+#include "toolchain/semantics/semantics_node_stack.h"
 
 
 namespace Carbon {
 namespace Carbon {
 
 
@@ -26,7 +27,8 @@ class SemanticsParseTreeHandler {
         emitter_(&emitter),
         emitter_(&emitter),
         parse_tree_(&parse_tree),
         parse_tree_(&parse_tree),
         semantics_(&semantics),
         semantics_(&semantics),
-        vlog_stream_(vlog_stream) {}
+        vlog_stream_(vlog_stream),
+        node_stack_(parse_tree, vlog_stream) {}
 
 
   // Outputs the ParseTree information into SemanticsIR.
   // Outputs the ParseTree information into SemanticsIR.
   auto Build() -> void;
   auto Build() -> void;
@@ -56,18 +58,6 @@ class SemanticsParseTreeHandler {
     }
     }
   };
   };
 
 
-  // An entry in node_stack_.
-  struct NodeStackEntry {
-    ParseTree::Node parse_node;
-    union {
-      // The result_id may be invalid if there's no result.
-      SemanticsNodeId result_id;
-      // The name_id is provided for PatternBindings.
-      SemanticsStringId name_id;
-    };
-  };
-  static_assert(sizeof(NodeStackEntry) == 8, "Unexpected NodeStackEntry size");
-
   // An entry in scope_stack_.
   // An entry in scope_stack_.
   struct ScopeStackEntry {
   struct ScopeStackEntry {
     // Names which are registered with name_lookup_, and will need to be
     // Names which are registered with name_lookup_, and will need to be
@@ -80,6 +70,10 @@ class SemanticsParseTreeHandler {
   // Adds a node to the current block, returning the produced ID.
   // Adds a node to the current block, returning the produced ID.
   auto AddNode(SemanticsNode node) -> SemanticsNodeId;
   auto AddNode(SemanticsNode node) -> SemanticsNodeId;
 
 
+  // Pushes a parse tree node onto the stack, storing the SemanticsNode as the
+  // result.
+  auto AddNodeAndPush(ParseTree::Node parse_node, SemanticsNode node) -> void;
+
   // Adds a name to name lookup. This is typically done through BindName, but
   // Adds a name to name lookup. This is typically done through BindName, but
   // can also be used to restore removed names.
   // can also be used to restore removed names.
   auto AddNameToLookup(SemanticsStringId name_id, SemanticsNodeId storage_id)
   auto AddNameToLookup(SemanticsStringId name_id, SemanticsNodeId storage_id)
@@ -91,36 +85,6 @@ class SemanticsParseTreeHandler {
   auto BindName(ParseTree::Node name_node, SemanticsNodeId type_id,
   auto BindName(ParseTree::Node name_node, SemanticsNodeId type_id,
                 SemanticsNodeId target_id) -> SemanticsStringId;
                 SemanticsNodeId target_id) -> SemanticsStringId;
 
 
-  // Pushes a parse tree node onto the stack. Used when there is no IR generated
-  // by the node.
-  auto Push(ParseTree::Node parse_node) -> void;
-
-  // Pushes a parse tree node onto the stack, storing the SemanticsNode as the
-  // result.
-  auto Push(ParseTree::Node parse_node, SemanticsNode node) -> void;
-
-  // Pushes a parse tree node onto the stack with an already-built node ID.
-  auto Push(ParseTree::Node parse_node, SemanticsNodeId node_id) -> void;
-
-  // Pushes a PatternBinding parse tree node onto the stack with its name.
-  auto Push(ParseTree::Node parse_node, SemanticsStringId name_id) -> void;
-
-  // Pops the top of the stack, verifying that it's the expected kind.
-  auto Pop(ParseNodeKind pop_parse_kind) -> void;
-
-  // Pops the top of the stack, returning the result_id. Must only be called for
-  // nodes that have results.
-  auto PopWithResult() -> SemanticsNodeId;
-
-  // Pops the top of the stack, verifying that it's the expected kind and
-  // returning the result_id. Must only be called for nodes that have results.
-  auto PopWithResult(ParseNodeKind pop_parse_kind) -> SemanticsNodeId;
-
-  // Pops the top of the stack, verifying that it's the expected kind and
-  // returning the result_id. Must only be called for nodes that have results.
-  auto PopWithResultIf(ParseNodeKind pop_parse_kind)
-      -> std::optional<SemanticsNodeId>;
-
   // Pushes a new scope onto scope_stack_.
   // Pushes a new scope onto scope_stack_.
   auto PushScope() -> void;
   auto PushScope() -> void;
 
 
@@ -161,7 +125,7 @@ class SemanticsParseTreeHandler {
   llvm::raw_ostream* vlog_stream_;
   llvm::raw_ostream* vlog_stream_;
 
 
   // The stack during Build. Will contain file-level parse nodes on return.
   // The stack during Build. Will contain file-level parse nodes on return.
-  llvm::SmallVector<NodeStackEntry> node_stack_;
+  SemanticsNodeStack node_stack_;
 
 
   // The stack of node blocks during build. Only updated on ParseTree nodes that
   // The stack of node blocks during build. Only updated on ParseTree nodes that
   // affect the stack.
   // affect the stack.

+ 1 - 1
toolchain/semantics/testdata/basics/verbose.carbon

@@ -6,7 +6,7 @@
 // RUN: %{carbon} -v dump semantics-ir %s | %{FileCheck-allow-unmatched}
 // RUN: %{carbon} -v dump semantics-ir %s | %{FileCheck-allow-unmatched}
 //
 //
 // Only checks a couple statements in order to minimize manual update churn.
 // Only checks a couple statements in order to minimize manual update churn.
-// CHECK:STDERR: Push 0: FunctionIntroducer
+// CHECK:STDERR: Push 0: FunctionIntroducer -> node<invalid>
 // CHECK:STDERR: AddNode block0: BindName(str0, node{{[0-9]+}})
 // CHECK:STDERR: AddNode block0: BindName(str0, node{{[0-9]+}})
 
 
 fn Foo() {
 fn Foo() {