diff --git a/toolchain/common/index_base.h b/toolchain/common/index_base.h index 0d4a5eeb4547..6a623dc26879 100644 --- a/toolchain/common/index_base.h +++ b/toolchain/common/index_base.h @@ -27,7 +27,13 @@ struct IndexBase { constexpr IndexBase() : index(InvalidIndex) {} 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 << ""; + } + } auto is_valid() const -> bool { return index != InvalidIndex; } diff --git a/toolchain/semantics/BUILD b/toolchain/semantics/BUILD index c176a916c8a2..85ccdc9e8d50 100644 --- a/toolchain/semantics/BUILD +++ b/toolchain/semantics/BUILD @@ -20,21 +20,49 @@ cc_library( 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( name = "semantics_ir", srcs = [ "semantics_ir.cpp", - "semantics_node.cpp", "semantics_parse_tree_handler.cpp", ], hdrs = [ "semantics_ir.h", - "semantics_node.h", "semantics_parse_tree_handler.h", ], deps = [ ":semantics_builtin_kind", - ":semantics_node_kind", + ":semantics_node", + ":semantics_node_stack", "//common:check", "//common:ostream", "//common:vlog", diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index b29a711b73ab..9ba051ccc9f5 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -28,31 +28,46 @@ struct SemanticsNodeId : public IndexBase { static auto MakeInvalid() -> SemanticsNodeId { return SemanticsNodeId(); } 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. struct SemanticsCrossReferenceIRId : public 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. struct SemanticsIntegerLiteralId : public 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. struct SemanticsNodeBlockId : public 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. struct SemanticsStringId : public 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. diff --git a/toolchain/semantics/semantics_node_stack.cpp b/toolchain/semantics/semantics_node_stack.cpp new file mode 100644 index 000000000000..e494854b47e8 --- /dev/null +++ b/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 { + auto back = PopEntry(); + RequireNodeId(back); + return {back.parse_node, back.node_id}; +} + +auto SemanticsNodeStack::PopForParseNodeAndNodeId(ParseNodeKind pop_parse_kind) + -> std::pair { + auto back = PopEntry(pop_parse_kind); + RequireNodeId(back); + return {back.parse_node, back.node_id}; +} + +auto SemanticsNodeStack::PopForParseNodeAndNameId() + -> std::pair { + 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(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 diff --git a/toolchain/semantics/semantics_node_stack.h b/toolchain/semantics/semantics_node_stack.h new file mode 100644 index 000000000000..f261aed6062c --- /dev/null +++ b/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; + + // Pops the top of the stack and returns the parse_node and node_id. + auto PopForParseNodeAndNodeId(ParseNodeKind pop_parse_kind) + -> std::pair; + + // 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; + + // 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 stack_; +}; + +} // namespace Carbon + +#endif // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_NODE_STACK_H_ diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index bd0e40652d76..88710c0b8dec 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/toolchain/semantics/semantics_parse_tree_handler.cpp @@ -22,20 +22,7 @@ class SemanticsParseTreeHandler::PrettyStackTraceNodeStack ~PrettyStackTraceNodeStack() override = default; auto print(llvm::raw_ostream& output) const -> void override { - output << "node_stack_:\n"; - for (int i = 0; i < static_cast(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: @@ -95,6 +82,12 @@ auto SemanticsParseTreeHandler::AddNode(SemanticsNode node) -> SemanticsNodeId { 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, SemanticsNodeId type_id, SemanticsNodeId target_id) @@ -122,101 +115,6 @@ auto SemanticsParseTreeHandler::BindName(ParseTree::Node name_node, 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 { - 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 { scope_stack_.push_back({}); } @@ -311,7 +209,7 @@ auto SemanticsParseTreeHandler::HandleContinueStatementStart( auto SemanticsParseTreeHandler::HandleDeclaredName(ParseTree::Node parse_node) -> void { // The parent is responsible for binding the name. - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleDeducedParameterList( @@ -338,7 +236,7 @@ auto SemanticsParseTreeHandler::HandleEmptyDeclaration( ParseTree::Node parse_node) -> void { // Empty declarations have no actions associated, but we still balance the // tree. - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleExpressionStatement( @@ -346,8 +244,8 @@ auto SemanticsParseTreeHandler::HandleExpressionStatement( // Pop the expression without investigating its contents. // TODO: This will probably eventually need to do some "do not discard" // analysis. - PopWithResult(); - Push(parse_node); + node_stack_.PopAndIgnore(); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleFileEnd(ParseTree::Node /*parse_node*/) @@ -383,23 +281,23 @@ auto SemanticsParseTreeHandler::HandleFunctionDeclaration( auto SemanticsParseTreeHandler::HandleFunctionDefinition( ParseTree::Node parse_node) -> void { // 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) { - node_stack_.pop_back(); + node_stack_.PopAndIgnore(); } - Pop(ParseNodeKind::FunctionDefinitionStart); + node_stack_.PopAndIgnore(); + PopScope(); node_block_stack_.pop_back(); - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart( 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)); // TODO: Propagate the type of the function. @@ -408,13 +306,13 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart( AddNode(SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, block_id)); node_block_stack_.push_back(block_id); PushScope(); - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleFunctionIntroducer( ParseTree::Node parse_node) -> void { // No action, just a bracketing node. - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleIfCondition( @@ -439,8 +337,8 @@ auto SemanticsParseTreeHandler::HandleIfStatementElse( auto SemanticsParseTreeHandler::HandleInfixOperator(ParseTree::Node parse_node) -> void { - auto rhs_id = PopWithResult(); - auto lhs_id = PopWithResult(); + auto rhs_id = node_stack_.PopForNodeId(); + auto lhs_id = node_stack_.PopForNodeId(); SemanticsNodeId result_type = 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); switch (auto token_kind = tokens_->GetKind(token)) { 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; default: CARBON_FATAL() << "Unrecognized token kind: " << token_kind.name(); @@ -478,19 +376,20 @@ auto SemanticsParseTreeHandler::HandleLiteral(ParseTree::Node parse_node) case TokenKind::IntegerLiteral: { auto id = semantics_->AddIntegerLiteral(tokens_->GetIntegerLiteral(token)); - Push(parse_node, SemanticsNode::MakeIntegerLiteral(parse_node, id)); + AddNodeAndPush(parse_node, + SemanticsNode::MakeIntegerLiteral(parse_node, id)); break; } case TokenKind::RealLiteral: { // TODO: Add storage of the Real literal. - Push(parse_node, SemanticsNode::MakeRealLiteral(parse_node)); + AddNodeAndPush(parse_node, SemanticsNode::MakeRealLiteral(parse_node)); break; } case TokenKind::IntegerTypeLiteral: { auto text = tokens_->GetTokenText(token); 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; } default: @@ -506,8 +405,8 @@ auto SemanticsParseTreeHandler::HandleNameReference(ParseTree::Node parse_node) CARBON_DIAGNOSTIC(NameNotFound, Error, "Name {0} not found", llvm::StringRef); 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); @@ -524,7 +423,7 @@ auto SemanticsParseTreeHandler::HandleNameReference(ParseTree::Node parse_node) CARBON_CHECK(!it->second.empty()) << "Should have been erased: " << name_str; // 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*/) @@ -556,8 +455,8 @@ auto SemanticsParseTreeHandler::HandleParameterList(ParseTree::Node parse_node) -> void { // TODO: This should transform into a usable parameter list. For now // 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( @@ -568,7 +467,7 @@ auto SemanticsParseTreeHandler::HandleParameterListComma( auto SemanticsParseTreeHandler::HandleParameterListStart( ParseTree::Node parse_node) -> void { // TODO: See HandleParameterList. - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleParenExpression( @@ -583,23 +482,21 @@ auto SemanticsParseTreeHandler::HandleParenExpressionOrTupleLiteralStart( auto SemanticsParseTreeHandler::HandlePatternBinding(ParseTree::Node parse_node) -> void { - auto type = node_stack_.pop_back_val(); - CARBON_CHECK(type.result_id.is_valid()); + auto type = node_stack_.PopForNodeId(); // 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. - 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. - 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 // address. The storage address can be found through the name, so we push the // name. - Push(parse_node, name_id); + node_stack_.Push(parse_node, name_id); } auto SemanticsParseTreeHandler::HandlePostfixOperator( @@ -614,23 +511,23 @@ auto SemanticsParseTreeHandler::HandlePrefixOperator( auto SemanticsParseTreeHandler::HandleReturnStatement( 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) { - Pop(ParseNodeKind::ReturnStatementStart); - Push(parse_node, SemanticsNode::MakeReturn(parse_node)); + node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ReturnStatementStart); + AddNodeAndPush(parse_node, SemanticsNode::MakeReturn(parse_node)); } else { - auto arg = PopWithResult(); + auto arg = node_stack_.PopForNodeId(); 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( ParseTree::Node parse_node) -> void { // No action, just a bracketing node. - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleReturnType(ParseTree::Node /*parse_node*/) @@ -700,49 +597,45 @@ auto SemanticsParseTreeHandler::HandleTupleLiteralComma( auto SemanticsParseTreeHandler::HandleVariableDeclaration( 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) { - 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); - CARBON_CHECK(binding.name_id.is_valid()); + CARBON_CHECK(binding.second.is_valid()); // 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, - init_id)); + last_child.second)); } - Pop(ParseNodeKind::VariableIntroducer); - Push(parse_node); + node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::VariableIntroducer); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleVariableIntroducer( ParseTree::Node parse_node) -> void { // No action, just a bracketing node. - Push(parse_node); + node_stack_.Push(parse_node); } auto SemanticsParseTreeHandler::HandleVariableInitializer( ParseTree::Node parse_node) -> void { // Temporarily remove name lookup entries added by the `var`. These will be // 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. - 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->second.empty()); auto storage_id = it->second.back(); @@ -755,7 +648,7 @@ auto SemanticsParseTreeHandler::HandleVariableInitializer( it->second.pop_back(); } - Push(parse_node, storage_id); + node_stack_.Push(parse_node, storage_id); } auto SemanticsParseTreeHandler::HandleWhileCondition( diff --git a/toolchain/semantics/semantics_parse_tree_handler.h b/toolchain/semantics/semantics_parse_tree_handler.h index c1ca7fad7cae..3d5fbf2b1992 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.h +++ b/toolchain/semantics/semantics_parse_tree_handler.h @@ -11,6 +11,7 @@ #include "toolchain/parser/parse_tree.h" #include "toolchain/semantics/semantics_ir.h" #include "toolchain/semantics/semantics_node.h" +#include "toolchain/semantics/semantics_node_stack.h" namespace Carbon { @@ -26,7 +27,8 @@ class SemanticsParseTreeHandler { emitter_(&emitter), parse_tree_(&parse_tree), semantics_(&semantics), - vlog_stream_(vlog_stream) {} + vlog_stream_(vlog_stream), + node_stack_(parse_tree, vlog_stream) {} // Outputs the ParseTree information into SemanticsIR. 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_. struct ScopeStackEntry { // 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. 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 // can also be used to restore removed names. auto AddNameToLookup(SemanticsStringId name_id, SemanticsNodeId storage_id) @@ -91,36 +85,6 @@ class SemanticsParseTreeHandler { auto BindName(ParseTree::Node name_node, SemanticsNodeId type_id, 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; - // Pushes a new scope onto scope_stack_. auto PushScope() -> void; @@ -161,7 +125,7 @@ class SemanticsParseTreeHandler { llvm::raw_ostream* vlog_stream_; // The stack during Build. Will contain file-level parse nodes on return. - llvm::SmallVector node_stack_; + SemanticsNodeStack node_stack_; // The stack of node blocks during build. Only updated on ParseTree nodes that // affect the stack. diff --git a/toolchain/semantics/testdata/basics/verbose.carbon b/toolchain/semantics/testdata/basics/verbose.carbon index 426dc18e8980..8d7cda208d4a 100644 --- a/toolchain/semantics/testdata/basics/verbose.carbon +++ b/toolchain/semantics/testdata/basics/verbose.carbon @@ -6,7 +6,7 @@ // RUN: %{carbon} -v dump semantics-ir %s | %{FileCheck-allow-unmatched} // // Only checks a couple statements in order to minimize manual update churn. -// CHECK:STDERR: Push 0: FunctionIntroducer +// CHECK:STDERR: Push 0: FunctionIntroducer -> node // CHECK:STDERR: AddNode block0: BindName(str0, node{{[0-9]+}}) fn Foo() {