From 5cd05efc62b9125fd7613588713818251ff6dabe Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Fri, 18 Nov 2022 16:45:59 -0800 Subject: [PATCH] Introduce verification checks on subtree_size (#2414) This switches the Verify method to walk postorder so that we can see how much subtree_size is really used, and shift towards removing it. It also starts calling Verify. Also, I think I'd lost the reserve/size check during Parser refactoring, so I'm putting that back in as part of Verify. Co-authored-by: Chandler Carruth --- toolchain/parser/BUILD | 2 + toolchain/parser/parse_node_kind.cpp | 36 ++++++ toolchain/parser/parse_node_kind.def | 160 ++++++++++++++++----------- toolchain/parser/parse_node_kind.h | 14 +++ toolchain/parser/parse_tree.cpp | 127 +++++++++++---------- toolchain/parser/parse_tree.h | 20 ++-- 6 files changed, 230 insertions(+), 129 deletions(-) diff --git a/toolchain/parser/BUILD b/toolchain/parser/BUILD index f8844eef4a9c..457f3c171301 100644 --- a/toolchain/parser/BUILD +++ b/toolchain/parser/BUILD @@ -12,6 +12,7 @@ cc_library( hdrs = ["parse_node_kind.h"], textual_hdrs = ["parse_node_kind.def"], deps = [ + "//common:check", "//common:ostream", "@llvm-project//llvm:Support", ], @@ -65,6 +66,7 @@ cc_library( ":parser_state", ":precedence", "//common:check", + "//common:error", "//common:ostream", "//toolchain/diagnostics:diagnostic_emitter", "//toolchain/lexer:token_kind", diff --git a/toolchain/parser/parse_node_kind.cpp b/toolchain/parser/parse_node_kind.cpp index d26eb33acf36..d20c42298d8b 100644 --- a/toolchain/parser/parse_node_kind.cpp +++ b/toolchain/parser/parse_node_kind.cpp @@ -4,6 +4,7 @@ #include "toolchain/parser/parse_node_kind.h" +#include "common/check.h" #include "llvm/ADT/StringRef.h" namespace Carbon { @@ -16,4 +17,39 @@ auto ParseNodeKind::name() const -> llvm::StringRef { return Names[static_cast(kind_)]; } +auto ParseNodeKind::has_bracket() const -> bool { + static constexpr bool HasBracket[] = { +#define CARBON_PARSE_NODE_KIND_BRACKET(...) true, +#define CARBON_PARSE_NODE_KIND_CHILD_COUNT(...) false, +#include "toolchain/parser/parse_node_kind.def" + }; + return HasBracket[static_cast(kind_)]; +} + +auto ParseNodeKind::bracket() const -> ParseNodeKind { + // Nodes are never self-bracketed, so we use that for nodes that instead set + // child_count. + static constexpr ParseNodeKind Bracket[] = { +#define CARBON_PARSE_NODE_KIND_BRACKET(Name, BracketName) \ + ParseNodeKind::BracketName(), +#define CARBON_PARSE_NODE_KIND_CHILD_COUNT(Name, ...) ParseNodeKind::Name(), +#include "toolchain/parser/parse_node_kind.def" + }; + auto bracket = Bracket[static_cast(kind_)]; + CARBON_CHECK(bracket != kind_); + return bracket; +} + +auto ParseNodeKind::child_count() const -> int32_t { + static constexpr int32_t ChildCount[] = { +#define CARBON_PARSE_NODE_KIND_BRACKET(...) -1, +#define CARBON_PARSE_NODE_KIND_CHILD_COUNT(Name, Size) Size, +#include "toolchain/parser/parse_node_kind.def" + }; + auto child_count = ChildCount[static_cast(kind_)]; + // TODO: Allows TodoFixParseNode; see parse_node_kind.def. + CARBON_CHECK(child_count != -1); + return child_count; +} + } // namespace Carbon diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index af6bfaa49b8c..2586c62b0fca 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/toolchain/parser/parse_node_kind.def @@ -6,81 +6,113 @@ // // It does not use `#include` guards, and instead is designed to be `#include`ed // after the x-macro is defined in order for its inclusion to expand to the -// desired output. The x-macro for this header is `CARBON_PARSE_NODE_KIND`. The -// definition provided will be removed at the end of this file to clean up. +// desired output. +// +// x-macros come in three forms: +// CARBON_PARSE_NODE_KIND(Name) +// Used as a fallback if other macros are missing. +// CARBON_PARSE_NODE_KIND_BRACKET(Name, BracketName) +// Defines a bracketed node kind. BracketName should refer to the node kind +// that is the _start_ of the bracketed range. +// CARBON_PARSE_NODE_KIND_CHILD_COUNT(Name, ChildCount) +// Defines a parse node with a set number of children, often 0. This count +// must be correct even when the node contains errors. +// +// Macro definitions will be removed at the end of this file to clean up. +// +// TODO: Using CHILD_COUNT(..., TodoFixParseNode) to indicate nodes which need to be fixed, +// sometimes due to inconsistent output in error modes. This is also handled in +// ParseNodeKind::child_count() and ParseTree::Verify(). All of that can be +// cleaned up once remainining nodes are fixed. -#ifndef CARBON_PARSE_NODE_KIND -#error "Must define the x-macro to use this file." +#if !(defined(CARBON_PARSE_NODE_KIND) || \ + (defined(CARBON_PARSE_NODE_KIND_BRACKET) && \ + defined(CARBON_PARSE_NODE_KIND_CHILD_COUNT))) +#error "Must define CARBON_PARSE_NODE_KIND family x-macros to use this file." +#endif + +// The BRACKET and CHILD_COUNT macros will use CARBON_PARSE_NODE_KIND by default +// when undefined. +#ifndef CARBON_PARSE_NODE_KIND_BRACKET +#define CARBON_PARSE_NODE_KIND_BRACKET(Name, ...) CARBON_PARSE_NODE_KIND(Name) +#endif +#ifndef CARBON_PARSE_NODE_KIND_CHILD_COUNT +#define CARBON_PARSE_NODE_KIND_CHILD_COUNT(Name, ...) \ + CARBON_PARSE_NODE_KIND(Name) #endif // Declarations. -CARBON_PARSE_NODE_KIND(DeclarationEnd) -CARBON_PARSE_NODE_KIND(EmptyDeclaration) -CARBON_PARSE_NODE_KIND(DeclaredName) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(DeclarationEnd, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(EmptyDeclaration, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(DeclaredName, 0) -CARBON_PARSE_NODE_KIND(PackageDirective) -CARBON_PARSE_NODE_KIND(PackageApi) -CARBON_PARSE_NODE_KIND(PackageImpl) -CARBON_PARSE_NODE_KIND(PackageLibrary) -CARBON_PARSE_NODE_KIND(PackageEnd) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageDirective, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageApi, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageImpl, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageLibrary, 1) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageEnd, 0) -CARBON_PARSE_NODE_KIND(FunctionIntroducer) -CARBON_PARSE_NODE_KIND(FunctionDeclaration) -CARBON_PARSE_NODE_KIND(FunctionDefinition) -CARBON_PARSE_NODE_KIND(FunctionDefinitionStart) -CARBON_PARSE_NODE_KIND(ParameterList) -CARBON_PARSE_NODE_KIND(ParameterListStart) -CARBON_PARSE_NODE_KIND(ParameterListComma) -CARBON_PARSE_NODE_KIND(PatternBinding) -CARBON_PARSE_NODE_KIND(ReturnType) -CARBON_PARSE_NODE_KIND(VariableDeclaration) -CARBON_PARSE_NODE_KIND(VariableInitializer) -CARBON_PARSE_NODE_KIND(FileEnd) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(FunctionIntroducer, 0) +CARBON_PARSE_NODE_KIND_BRACKET(FunctionDeclaration, FunctionIntroducer) +CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinition, FunctionDefinitionStart) +CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinitionStart, FunctionIntroducer) +CARBON_PARSE_NODE_KIND_BRACKET(ParameterList, ParameterListStart) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParameterListStart, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParameterListComma, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PatternBinding, 2) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnType, 1) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableDeclaration, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableInitializer, 1) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(FileEnd, 0) // Statements. -CARBON_PARSE_NODE_KIND(CodeBlock) -CARBON_PARSE_NODE_KIND(CodeBlockStart) -CARBON_PARSE_NODE_KIND(ExpressionStatement) -CARBON_PARSE_NODE_KIND(IfStatement) -CARBON_PARSE_NODE_KIND(IfStatementElse) -CARBON_PARSE_NODE_KIND(WhileStatement) -CARBON_PARSE_NODE_KIND(Condition) -CARBON_PARSE_NODE_KIND(ConditionEnd) -CARBON_PARSE_NODE_KIND(ContinueStatement) -CARBON_PARSE_NODE_KIND(ContinueStatementStart) -CARBON_PARSE_NODE_KIND(BreakStatement) -CARBON_PARSE_NODE_KIND(BreakStatementStart) -CARBON_PARSE_NODE_KIND(ReturnStatement) -CARBON_PARSE_NODE_KIND(ReturnStatementStart) -CARBON_PARSE_NODE_KIND(ForStatement) -CARBON_PARSE_NODE_KIND(ForHeader) -CARBON_PARSE_NODE_KIND(ForHeaderEnd) -CARBON_PARSE_NODE_KIND(ForIn) +CARBON_PARSE_NODE_KIND_BRACKET(CodeBlock, CodeBlockStart) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(CodeBlockStart, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExpressionStatement, 1) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfStatement, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfStatementElse, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(WhileStatement, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(Condition, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ConditionEnd, 0) +CARBON_PARSE_NODE_KIND_BRACKET(ContinueStatement, ContinueStatementStart) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ContinueStatementStart, 0) +CARBON_PARSE_NODE_KIND_BRACKET(BreakStatement, BreakStatementStart) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(BreakStatementStart, 0) +CARBON_PARSE_NODE_KIND_BRACKET(ReturnStatement, ReturnStatementStart) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnStatementStart, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForStatement, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForHeader, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForHeaderEnd, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForIn, 0) // Expressions. -CARBON_PARSE_NODE_KIND(Literal) -CARBON_PARSE_NODE_KIND(TupleLiteral) -CARBON_PARSE_NODE_KIND(TupleLiteralComma) -CARBON_PARSE_NODE_KIND(TupleLiteralEnd) -CARBON_PARSE_NODE_KIND(NameReference) -CARBON_PARSE_NODE_KIND(ParenExpression) -CARBON_PARSE_NODE_KIND(ParenExpressionEnd) -CARBON_PARSE_NODE_KIND(DesignatorExpression) -CARBON_PARSE_NODE_KIND(DesignatedName) -CARBON_PARSE_NODE_KIND(CallExpression) -CARBON_PARSE_NODE_KIND(CallExpressionComma) -CARBON_PARSE_NODE_KIND(CallExpressionEnd) -CARBON_PARSE_NODE_KIND(PrefixOperator) -CARBON_PARSE_NODE_KIND(InfixOperator) -CARBON_PARSE_NODE_KIND(PostfixOperator) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(Literal, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteral, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteralComma, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteralEnd, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(NameReference, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParenExpression, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParenExpressionEnd, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(DesignatorExpression, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(DesignatedName, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExpression, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExpressionComma, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExpressionEnd, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PrefixOperator, 1) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PostfixOperator, 1) // Struct literals. -CARBON_PARSE_NODE_KIND(StructLiteral) -CARBON_PARSE_NODE_KIND(StructTypeLiteral) -CARBON_PARSE_NODE_KIND(StructLiteralOrStructTypeLiteralStart) -CARBON_PARSE_NODE_KIND(StructFieldDesignator) -CARBON_PARSE_NODE_KIND(StructFieldValue) -CARBON_PARSE_NODE_KIND(StructFieldType) -CARBON_PARSE_NODE_KIND(StructComma) +CARBON_PARSE_NODE_KIND_BRACKET(StructLiteral, + StructLiteralOrStructTypeLiteralStart) +CARBON_PARSE_NODE_KIND_BRACKET(StructTypeLiteral, + StructLiteralOrStructTypeLiteralStart) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructLiteralOrStructTypeLiteralStart, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldDesignator, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldValue, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldType, TodoFixParseNode) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructComma, 0) #undef CARBON_PARSE_NODE_KIND +#undef CARBON_PARSE_NODE_KIND_BRACKET +#undef CARBON_PARSE_NODE_KIND_CHILD_COUNT diff --git a/toolchain/parser/parse_node_kind.h b/toolchain/parser/parse_node_kind.h index 57a1c142e97e..b108f1f646bc 100644 --- a/toolchain/parser/parse_node_kind.h +++ b/toolchain/parser/parse_node_kind.h @@ -33,6 +33,9 @@ class ParseNodeKind { }; public: + // TODO: See parse_node_kind.def. + static constexpr int32_t TodoFixParseNode = -2; + // `clang-format` has a bug with spacing around `->` returns in macros. See // https://bugs.llvm.org/show_bug.cgi?id=48320 for details. #define CARBON_PARSE_NODE_KIND(Name) \ @@ -64,6 +67,17 @@ class ParseNodeKind { void Print(llvm::raw_ostream& out) const { out << name(); } + // Returns true if the node is bracketed; otherwise, child_count is used. + auto has_bracket() const -> bool; + + // Returns the bracketing node kind for the current node kind. Requires that + // has_bracket is true. + auto bracket() const -> ParseNodeKind; + + // Returns the number of children that the node must have, often 0. Requires + // that has_bracket is false. + auto child_count() const -> int32_t; + private: constexpr explicit ParseNodeKind(KindEnum k) : kind_(k) {} diff --git a/toolchain/parser/parse_tree.cpp b/toolchain/parser/parse_tree.cpp index a0f7bf48f07a..6e4c1598edc7 100644 --- a/toolchain/parser/parse_tree.cpp +++ b/toolchain/parser/parse_tree.cpp @@ -7,14 +7,11 @@ #include #include "common/check.h" -#include "llvm/ADT/ArrayRef.h" +#include "common/error.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/Sequence.h" -#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/iterator.h" -#include "llvm/Support/raw_ostream.h" -#include "toolchain/lexer/token_kind.h" +#include "toolchain/lexer/tokenized_buffer.h" #include "toolchain/parser/parse_node_kind.h" #include "toolchain/parser/parser.h" @@ -27,7 +24,10 @@ auto ParseTree::Parse(TokenizedBuffer& tokens, DiagnosticConsumer& consumer) TokenDiagnosticEmitter emitter(translator, consumer); // Delegate to the parser. - return Parser::Parse(tokens, emitter); + auto tree = Parser::Parse(tokens, emitter); + auto verify_error = tree.Verify(); + CARBON_CHECK(!verify_error) << *verify_error; + return tree; } auto ParseTree::postorder() const -> llvm::iterator_range { @@ -188,68 +188,83 @@ auto ParseTree::Print(llvm::raw_ostream& output, bool preorder) const -> void { output << "]\n"; } -auto ParseTree::Verify() const -> bool { - // Verify basic tree structure invariants. - llvm::SmallVector ancestors; - for (Node n : llvm::reverse(postorder())) { +auto ParseTree::Verify() const -> llvm::Optional { + llvm::SmallVector nodes; + // Traverse the tree in postorder. + for (Node n : postorder()) { const auto& n_impl = node_impls_[n.index()]; if (n_impl.has_error && !has_errors_) { - llvm::errs() - << "Node #" << n.index() - << " has errors, but the tree is not marked as having any.\n"; - return false; + return Error(llvm::formatv( + "Node #{0} has errors, but the tree is not marked as having any.", + n.index())); } - if (n_impl.subtree_size > 1) { - if (!ancestors.empty()) { - auto parent_n = ancestors.back(); - const auto& parent_n_impl = node_impls_[parent_n.index()]; - int end_index = n.index() - n_impl.subtree_size; - int parent_end_index = parent_n.index() - parent_n_impl.subtree_size; - if (parent_end_index > end_index) { - llvm::errs() << "Node #" << n.index() << " has a subtree size of " - << n_impl.subtree_size - << " which extends beyond its parent's (node #" - << parent_n.index() << ") subtree (size " - << parent_n_impl.subtree_size << ")\n"; - return false; + int subtree_size = 1; + if (n_impl.kind.has_bracket()) { + while (true) { + if (nodes.empty()) { + return Error( + llvm::formatv("Node #{0} is a {1} with bracket {2}, but didn't " + "find the bracket.", + n.index(), n_impl.kind, n_impl.kind.bracket())); + } + auto child_impl = node_impls_[nodes.pop_back_val().index()]; + subtree_size += child_impl.subtree_size; + if (n_impl.kind.bracket() == child_impl.kind) { + break; } } - // Has children, so we descend. - ancestors.push_back(n); - continue; - } - - if (n_impl.subtree_size < 1) { - llvm::errs() << "Node #" << n.index() - << " has an invalid subtree size of " << n_impl.subtree_size - << "!\n"; - return false; - } - - // We're going to pop off some levels of the tree. Check each ancestor to - // make sure the offsets are correct. - int next_index = n.index() - 1; - while (!ancestors.empty()) { - ParseTree::Node parent_n = ancestors.back(); - if ((parent_n.index() - node_impls_[parent_n.index()].subtree_size) != - next_index) { - break; + } else if (n_impl.kind.child_count() == ParseNodeKind::TodoFixParseNode) { + while (subtree_size < n_impl.subtree_size && !nodes.empty()) { + auto child_impl = node_impls_[nodes.pop_back_val().index()]; + subtree_size += child_impl.subtree_size; + } + } else { + for (int i = 0; i < n_impl.kind.child_count(); ++i) { + if (nodes.empty()) { + return Error(llvm::formatv( + "Node #{0} is a {1} with child_count {2}, but only had {3} " + "nodes to consume.", + n.index(), n_impl.kind, n_impl.kind.child_count(), i)); + } + auto child_impl = node_impls_[nodes.pop_back_val().index()]; + subtree_size += child_impl.subtree_size; } - ancestors.pop_back(); } - } - if (!ancestors.empty()) { - llvm::errs() - << "Finished walking the parse tree and there are still ancestors:\n"; - for (Node ancestor_n : ancestors) { - llvm::errs() << " Node #" << ancestor_n.index() << "\n"; + if (n_impl.subtree_size != subtree_size) { + return Error(llvm::formatv( + "Node #{0} is a {1} with subtree_size of {2}, but calculated {3}.", + n.index(), n_impl.kind, n_impl.subtree_size, subtree_size)); } - return false; + nodes.push_back(n); } - return true; + // Remaining nodes should all be roots in the tree; make sure they line up. + CARBON_CHECK(nodes.back().index() == + static_cast(node_impls_.size()) - 1) + << nodes.back().index() << " " << node_impls_.size() - 1; + int prev_index = -1; + for (const auto& n : nodes) { + const auto& n_impl = node_impls_[n.index()]; + + if (n.index() - n_impl.subtree_size != prev_index) { + return Error(llvm::formatv( + "Node #{0} is a root {1} with subtree_size {2}, but " + "previous root was at #{3}.", + n.index(), n_impl.kind, n_impl.subtree_size, prev_index)); + } + prev_index = n.index(); + } + + if (!has_errors_ && + static_cast(node_impls_.size()) != tokens_->size()) { + return Error( + llvm::formatv("ParseTree has {0} nodes and no errors, but " + "TokenizedBuffer has {1} tokens.", + node_impls_.size(), tokens_->size())); + } + return llvm::None; } auto ParseTree::Node::Print(llvm::raw_ostream& output) const -> void { diff --git a/toolchain/parser/parse_tree.h b/toolchain/parser/parse_tree.h index 84430213eece..840e262dd1cc 100644 --- a/toolchain/parser/parse_tree.h +++ b/toolchain/parser/parse_tree.h @@ -7,12 +7,12 @@ #include +#include "common/error.h" #include "common/ostream.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" -#include "llvm/Support/raw_ostream.h" #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/lexer/tokenized_buffer.h" #include "toolchain/parser/parse_node_kind.h" @@ -140,14 +140,13 @@ class ParseTree { // line-oriented shell tools from `grep` to `awk`. auto Print(llvm::raw_ostream& output, bool preorder) const -> void; - // Verifies the parse tree structure. + // Verifies the parse tree structure. Checks invariants of the parse tree + // structure and returns verification errors. // - // This tries to check any invariants of the parse tree structure and write - // out information about it to stderr. Returns false if anything fails to - // verify. This is primarily intended to be used as a debugging aid. A typical - // usage is to `assert` on the result. This routine doesn't directly assert so - // that it can be used even when asserts are disabled or within a debugger. - [[nodiscard]] auto Verify() const -> bool; + // This is primarily intended to be used as a + // debugging aid. This routine doesn't directly CHECK so that it can be used + // within a debugger. + [[nodiscard]] auto Verify() const -> llvm::Optional; private: friend class Parser; @@ -208,7 +207,10 @@ class ParseTree { // Wires up the reference to the tokenized buffer. The global `parse` routine // should be used to actually parse the tokens into a tree. - explicit ParseTree(TokenizedBuffer& tokens_arg) : tokens_(&tokens_arg) {} + explicit ParseTree(TokenizedBuffer& tokens_arg) : tokens_(&tokens_arg) { + // If the tree is valid, there will be one node per token, so reserve once. + node_impls_.reserve(tokens_->size()); + } // Prints a single node for Print(). Returns true when preorder and there are // children.