| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- // 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 <type_traits>
- #include "common/vlog.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::Check {
- // Wraps the stack of nodes for Context.
- //
- // 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 NodeStack {
- public:
- explicit NodeStack(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 {
- CARBON_CHECK(ParseNodeKindToIdKind(parse_tree_->node_kind(parse_node)) ==
- IdKind::SoloParseNode)
- << "Parse kind expects an Id: " << parse_tree_->node_kind(parse_node);
- CARBON_VLOG() << "Node Push " << stack_.size() << ": "
- << parse_tree_->node_kind(parse_node) << " -> <none>\n";
- CARBON_CHECK(stack_.size() < (1 << 20))
- << "Excessive stack size: likely infinite loop";
- stack_.push_back(Entry(parse_node, SemIR::NodeId::Invalid));
- }
- // Pushes a parse tree node onto the stack with an ID.
- template <typename IdT>
- auto Push(ParseTree::Node parse_node, IdT id) -> void {
- CARBON_CHECK(ParseNodeKindToIdKind(parse_tree_->node_kind(parse_node)) ==
- IdTypeToIdKind<IdT>())
- << "Parse kind expected a different IdT: "
- << parse_tree_->node_kind(parse_node) << " -> " << id << "\n";
- CARBON_CHECK(id.is_valid()) << "Push called with invalid id: "
- << parse_tree_->node_kind(parse_node);
- CARBON_VLOG() << "Node Push " << stack_.size() << ": "
- << parse_tree_->node_kind(parse_node) << " -> " << id << "\n";
- CARBON_CHECK(stack_.size() < (1 << 20))
- << "Excessive stack size: likely infinite loop";
- stack_.push_back(Entry(parse_node, id));
- }
- // Pops the top of the stack without any verification.
- auto PopAndIgnore() -> void { PopEntry<SemIR::NodeId>(); }
- // Pops the top of the stack and returns the parse_node.
- template <ParseNodeKind::RawEnumType RequiredParseKind>
- auto PopForSoloParseNode() -> ParseTree::Node {
- Entry back = PopEntry<SemIR::NodeId>();
- RequireIdKind(ParseNodeKind::Create(RequiredParseKind),
- IdKind::SoloParseNode);
- RequireParseKind<RequiredParseKind>(back.parse_node);
- return back.parse_node;
- }
- // Pops the top of the stack.
- template <ParseNodeKind::RawEnumType RequiredParseKind>
- auto PopAndDiscardSoloParseNode() -> void {
- PopForSoloParseNode<RequiredParseKind>();
- }
- // Pops an expression from the top of the stack and returns the parse_node and
- // the ID.
- auto PopExpressionWithParseNode()
- -> std::pair<ParseTree::Node, SemIR::NodeId> {
- return PopWithParseNode<SemIR::NodeId>();
- }
- // Pops the top of the stack and returns the parse_node and the ID.
- template <ParseNodeKind::RawEnumType RequiredParseKind>
- auto PopWithParseNode() -> auto {
- constexpr IdKind RequiredIdKind =
- ParseNodeKindToIdKind(ParseNodeKind::Create(RequiredParseKind));
- if constexpr (RequiredIdKind == IdKind::NodeId) {
- auto back = PopWithParseNode<SemIR::NodeId>();
- RequireParseKind<RequiredParseKind>(back.first);
- return back;
- }
- if constexpr (RequiredIdKind == IdKind::NodeBlockId) {
- auto back = PopWithParseNode<SemIR::NodeBlockId>();
- RequireParseKind<RequiredParseKind>(back.first);
- return back;
- }
- if constexpr (RequiredIdKind == IdKind::FunctionId) {
- auto back = PopWithParseNode<SemIR::FunctionId>();
- RequireParseKind<RequiredParseKind>(back.first);
- return back;
- }
- if constexpr (RequiredIdKind == IdKind::StringId) {
- auto back = PopWithParseNode<SemIR::StringId>();
- RequireParseKind<RequiredParseKind>(back.first);
- return back;
- }
- if constexpr (RequiredIdKind == IdKind::TypeId) {
- auto back = PopWithParseNode<SemIR::TypeId>();
- RequireParseKind<RequiredParseKind>(back.first);
- return back;
- }
- CARBON_FATAL() << "Unpoppable IdKind for parse kind: "
- << ParseNodeKind::Create(RequiredParseKind)
- << "; see value in ParseNodeKindToIdKind";
- }
- // Pops an expression from the top of the stack and returns the ID.
- // Expressions map multiple ParseNodeKinds to SemIR::NodeId always.
- auto PopExpression() -> SemIR::NodeId {
- return PopExpressionWithParseNode().second;
- }
- // Pops the top of the stack and returns the ID.
- template <ParseNodeKind::RawEnumType RequiredParseKind>
- auto Pop() -> auto {
- return PopWithParseNode<RequiredParseKind>().second;
- }
- // Peeks at the parse_node of the top of the stack.
- auto PeekParseNode() -> ParseTree::Node { return stack_.back().parse_node; }
- // Peeks at the ID of the top of the stack.
- template <ParseNodeKind::RawEnumType RequiredParseKind>
- auto Peek() -> auto {
- Entry back = stack_.back();
- RequireParseKind<RequiredParseKind>(back.parse_node);
- constexpr IdKind RequiredIdKind =
- ParseNodeKindToIdKind(ParseNodeKind::Create(RequiredParseKind));
- if constexpr (RequiredIdKind == IdKind::NodeId) {
- return back.id<SemIR::NodeId>();
- }
- if constexpr (RequiredIdKind == IdKind::NodeBlockId) {
- return back.id<SemIR::NodeBlockId>();
- }
- if constexpr (RequiredIdKind == IdKind::FunctionId) {
- return back.id<SemIR::FunctionId>();
- }
- if constexpr (RequiredIdKind == IdKind::StringId) {
- return back.id<SemIR::StringId>();
- }
- if constexpr (RequiredIdKind == IdKind::TypeId) {
- return back.id<SemIR::TypeId>();
- }
- CARBON_FATAL() << "Unpeekable IdKind for parse kind: "
- << ParseNodeKind::Create(RequiredParseKind)
- << "; see value in ParseNodeKindToIdKind";
- }
- // Prints the stack for a stack dump.
- auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
- auto empty() const -> bool { return stack_.empty(); }
- auto size() const -> size_t { return stack_.size(); }
- private:
- // Possible associated ID types.
- enum class IdKind {
- NodeId,
- NodeBlockId,
- FunctionId,
- StringId,
- TypeId,
- // No associated ID type.
- SoloParseNode,
- // Not expected in the node stack.
- Unused,
- };
- // An entry in stack_.
- struct Entry {
- explicit Entry(ParseTree::Node parse_node, SemIR::NodeId node_id)
- : parse_node(parse_node), node_id(node_id) {}
- explicit Entry(ParseTree::Node parse_node, SemIR::NodeBlockId node_block_id)
- : parse_node(parse_node), node_block_id(node_block_id) {}
- explicit Entry(ParseTree::Node parse_node, SemIR::FunctionId function_id)
- : parse_node(parse_node), function_id(function_id) {}
- explicit Entry(ParseTree::Node parse_node, SemIR::StringId name_id)
- : parse_node(parse_node), name_id(name_id) {}
- explicit Entry(ParseTree::Node parse_node, SemIR::TypeId type_id)
- : parse_node(parse_node), type_id(type_id) {}
- // Returns the appropriate ID basaed on type.
- template <typename T>
- auto id() -> T& {
- if constexpr (std::is_same<T, SemIR::NodeId>()) {
- return node_id;
- }
- if constexpr (std::is_same<T, SemIR::NodeBlockId>()) {
- return node_block_id;
- }
- if constexpr (std::is_same<T, SemIR::FunctionId>()) {
- return function_id;
- }
- if constexpr (std::is_same<T, SemIR::StringId>()) {
- return name_id;
- }
- if constexpr (std::is_same<T, SemIR::TypeId>()) {
- return type_id;
- }
- }
- // The node associated with the stack entry.
- ParseTree::Node parse_node;
- // The entries will evaluate as invalid if and only if they're a solo
- // parse_node. Invalid is used instead of optional to save space.
- //
- // A discriminator isn't needed because the caller can determine which field
- // is used based on the ParseNodeKind.
- union {
- SemIR::NodeId node_id;
- SemIR::NodeBlockId node_block_id;
- SemIR::FunctionId function_id;
- SemIR::StringId name_id;
- SemIR::TypeId type_id;
- };
- };
- static_assert(sizeof(Entry) == 8, "Unexpected Entry size");
- // Translate a parse node kind to the enum ID kind it should always provide.
- static constexpr auto ParseNodeKindToIdKind(ParseNodeKind kind) -> IdKind {
- switch (kind) {
- case ParseNodeKind::ArrayExpression:
- case ParseNodeKind::CallExpression:
- case ParseNodeKind::CallExpressionStart:
- case ParseNodeKind::IfExpressionElse:
- case ParseNodeKind::IndexExpression:
- case ParseNodeKind::InfixOperator:
- case ParseNodeKind::Literal:
- case ParseNodeKind::MemberAccessExpression:
- case ParseNodeKind::NameExpression:
- case ParseNodeKind::ParenExpression:
- case ParseNodeKind::PatternBinding:
- case ParseNodeKind::PostfixOperator:
- case ParseNodeKind::PrefixOperator:
- case ParseNodeKind::ShortCircuitOperand:
- case ParseNodeKind::StructFieldValue:
- case ParseNodeKind::StructLiteral:
- case ParseNodeKind::StructFieldType:
- case ParseNodeKind::StructTypeLiteral:
- case ParseNodeKind::TupleLiteral:
- return IdKind::NodeId;
- case ParseNodeKind::IfExpressionThen:
- case ParseNodeKind::IfStatementElse:
- case ParseNodeKind::ParameterList:
- return IdKind::NodeBlockId;
- case ParseNodeKind::FunctionDefinitionStart:
- return IdKind::FunctionId;
- case ParseNodeKind::Name:
- return IdKind::StringId;
- case ParseNodeKind::ReturnType:
- return IdKind::TypeId;
- case ParseNodeKind::ArrayExpressionSemi:
- case ParseNodeKind::CodeBlockStart:
- case ParseNodeKind::FunctionIntroducer:
- case ParseNodeKind::IfCondition:
- case ParseNodeKind::IfExpressionIf:
- case ParseNodeKind::ParameterListStart:
- case ParseNodeKind::ParenExpressionOrTupleLiteralStart:
- case ParseNodeKind::QualifiedDeclaration:
- case ParseNodeKind::ReturnStatementStart:
- case ParseNodeKind::StructLiteralOrStructTypeLiteralStart:
- case ParseNodeKind::VariableInitializer:
- case ParseNodeKind::VariableIntroducer:
- return IdKind::SoloParseNode;
- default:
- return IdKind::Unused;
- }
- }
- // Translates an ID type to the enum ID kind for comparison with
- // ParseNodeKindToIdKind.
- template <typename IdT>
- static constexpr auto IdTypeToIdKind() -> IdKind {
- if constexpr (std::is_same_v<IdT, SemIR::NodeId>) {
- return IdKind::NodeId;
- }
- if constexpr (std::is_same_v<IdT, SemIR::NodeBlockId>) {
- return IdKind::NodeBlockId;
- }
- if constexpr (std::is_same_v<IdT, SemIR::FunctionId>) {
- return IdKind::FunctionId;
- }
- if constexpr (std::is_same_v<IdT, SemIR::StringId>) {
- return IdKind::StringId;
- }
- if constexpr (std::is_same_v<IdT, SemIR::TypeId>) {
- return IdKind::TypeId;
- }
- }
- // Pops an entry.
- template <typename IdT>
- auto PopEntry() -> Entry {
- Entry back = stack_.pop_back_val();
- CARBON_VLOG() << "Node Pop " << stack_.size() << ": "
- << parse_tree_->node_kind(back.parse_node) << " -> "
- << back.id<IdT>() << "\n";
- return back;
- }
- // Pops the top of the stack and returns the parse_node and the ID.
- template <typename IdT>
- auto PopWithParseNode() -> std::pair<ParseTree::Node, IdT> {
- Entry back = PopEntry<IdT>();
- RequireIdKind(parse_tree_->node_kind(back.parse_node),
- IdTypeToIdKind<IdT>());
- return {back.parse_node, back.id<IdT>()};
- }
- // Require a ParseNodeKind be mapped to a particular IdKind.
- auto RequireIdKind(ParseNodeKind parse_kind, IdKind id_kind) -> void {
- CARBON_CHECK(ParseNodeKindToIdKind(parse_kind) == id_kind)
- << "Unexpected IdKind mapping for " << parse_kind;
- }
- // Require an entry to have the given ParseNodeKind.
- template <ParseNodeKind::RawEnumType RequiredParseKind>
- auto RequireParseKind(ParseTree::Node parse_node) -> void {
- auto actual_kind = parse_tree_->node_kind(parse_node);
- CARBON_CHECK(RequiredParseKind == actual_kind)
- << "Expected " << ParseNodeKind::Create(RequiredParseKind) << ", found "
- << actual_kind;
- }
- // 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::Check
- #endif // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_NODE_STACK_H_
|