From 2e97f27b8d029c78d1e68a50665d6b565088fc02 Mon Sep 17 00:00:00 2001 From: josh11b Date: Fri, 22 Dec 2023 14:14:11 -0800 Subject: [PATCH] Typed wrappers around parse tree nodes (#3534) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are intended to allow the structure of a parse tree node to be described more precisely in code, to support these use cases: - Automated checking that the parse tree conforms to the expected structure. (Added to `Tree::Verify`.) - Easier reading and understanding of the structure of the parse tree by toolchain developers. (See `parse/typed_nodes.h`.) - Easier navigation of the parse tree, for example for tooling uses and for use when forming diagnostics. On this last point, an object representing the file may be inspecting using `Tree::ExtractFile`, as in: ``` auto file = tree->ExtractFile(); for (AnyDeclId decl_id : file.decls) { // `decl_id` is convertible to a `NodeId`. if (std::optional fn_decl = tree->ExtractAs(decl_id)) { // fn_decl->params is a `TuplePatternId` (which extends `NodeId`) // that is guaranteed to reference a `TuplePattern`. std::optional params = tree->Extract(fn_decl->params); // `params` has a value unless there was an error in that node. } else if (auto class_def = tree->ExtractAs(decl_id)) { // ... } } ``` The `Extract...` functions collect the child nodes into the typed parse node's fields (internally using a `Tree::SiblingIterator`) for easy access. However, this is not as fast as directly observing the tree structure using the postorder strategy being used by the check stage. These functions rely on using struct reflection on the typed parse node definitions from `parse/typed_nodes.h` to get the expected structure of child nodes and then populate them. Note that validating these in `Tree::Verify` adds significant cost to it, and is currently included in the parsing stage. Without this change, a 10 mloc test case of lex & parse takes 4.129 s ± 0.041 s. With this change, it takes 5.768 s ± 0.036 s. This builds upon and completes #3393. Co-authored-by: Richard Smith --------- Co-authored-by: Richard Smith Co-authored-by: Chandler Carruth --- common/struct_reflection.h | 16 +- common/struct_reflection_test.cpp | 23 +- toolchain/check/check.cpp | 1 + toolchain/check/decl_state.h | 2 +- toolchain/check/node_stack.h | 5 + toolchain/parse/BUILD | 26 +- toolchain/parse/extract.cpp | 342 +++++++ toolchain/parse/handle_brace_expr.cpp | 1 + toolchain/parse/handle_let.cpp | 12 +- toolchain/parse/node_ids.h | 99 ++ toolchain/parse/node_kind.cpp | 13 + toolchain/parse/node_kind.def | 34 +- toolchain/parse/node_kind.h | 71 ++ .../parse/testdata/let/fail_empty.carbon | 2 +- .../testdata/struct/fail_comma_only.carbon | 2 +- .../struct/fail_comma_repeat_in_type.carbon | 2 +- .../struct/fail_comma_repeat_in_value.carbon | 2 +- .../testdata/struct/fail_dot_only.carbon | 2 +- .../struct/fail_dot_string_colon.carbon | 2 +- .../struct/fail_dot_string_equals.carbon | 2 +- .../struct/fail_identifier_colon.carbon | 2 +- .../struct/fail_identifier_equals.carbon | 2 +- .../struct/fail_identifier_only.carbon | 2 +- .../testdata/struct/fail_missing_type.carbon | 2 +- .../testdata/struct/fail_missing_value.carbon | 2 +- .../struct/fail_mix_type_and_value.carbon | 2 +- .../struct/fail_mix_value_and_type.carbon | 2 +- .../struct/fail_mix_with_unknown.carbon | 4 +- .../struct/fail_no_colon_or_equals.carbon | 2 +- .../struct/fail_type_no_designator.carbon | 2 +- toolchain/parse/tree.cpp | 23 + toolchain/parse/tree.h | 141 ++- toolchain/parse/typed_nodes.h | 909 ++++++++++++++++++ toolchain/parse/typed_nodes_test.cpp | 152 +++ toolchain/sem_ir/inst.h | 10 +- toolchain/sem_ir/inst_kind.cpp | 2 +- toolchain/sem_ir/typed_insts.h | 12 +- toolchain/sem_ir/typed_insts_test.cpp | 11 +- 38 files changed, 1865 insertions(+), 76 deletions(-) create mode 100644 toolchain/parse/extract.cpp create mode 100644 toolchain/parse/node_ids.h create mode 100644 toolchain/parse/typed_nodes.h create mode 100644 toolchain/parse/typed_nodes_test.cpp diff --git a/common/struct_reflection.h b/common/struct_reflection.h index 2583e24a0681..89ef79081367 100644 --- a/common/struct_reflection.h +++ b/common/struct_reflection.h @@ -21,7 +21,7 @@ // - Only simple aggregate structs are supported. Types with base classes, // non-public data members, constructors, or virtual functions are not // supported. -// - Structs with more than 5 fields are not supported. This limit is easy to +// - Structs with more than 6 fields are not supported. This limit is easy to // increase if needed, but removing it entirely is hard. // - Structs containing a reference to the same type are not supported. @@ -75,7 +75,8 @@ constexpr auto CountFields() -> int { if constexpr (CanListInitialize(0)) { return CountFields>(); } else if constexpr (AnyWorkedSoFar) { - static_assert(sizeof...(Fields) <= 5, + // Note: Compare against the maximum number of fields supported *PLUS 1*. + static_assert(sizeof...(Fields) <= 7, "Unsupported: too many fields in struct"); return sizeof...(Fields) - 1; } else if constexpr (sizeof...(Fields) > 32) { @@ -150,6 +151,17 @@ struct FieldAccessor<5> { } }; +template <> +struct FieldAccessor<6> { + template + static auto Get(T& value) -> auto { + auto& [field0, field1, field2, field3, field4, field5] = value; + return std::tuple( + field0, field1, field2, field3, field4, field5); + } +}; + } // namespace Internal // Get the fields of the struct `T` as a tuple. diff --git a/common/struct_reflection_test.cpp b/common/struct_reflection_test.cpp index 619cb231e9bf..8ea54a59866c 100644 --- a/common/struct_reflection_test.cpp +++ b/common/struct_reflection_test.cpp @@ -20,6 +20,15 @@ struct TwoFields { int y; }; +struct SixFields { + int one; + int two; + int three; + int four; + int five; + int six; +}; + struct ReferenceField { int& ref; }; @@ -60,6 +69,7 @@ TEST(StructReflectionTest, CountFields) { static_assert(Internal::CountFields() == 0); static_assert(Internal::CountFields() == 1); static_assert(Internal::CountFields() == 2); + static_assert(Internal::CountFields() == 6); static_assert(Internal::CountFields() == 1); static_assert(Internal::CountFields() == 1); } @@ -74,12 +84,23 @@ TEST(StructReflectionTest, OneField) { EXPECT_EQ(std::get<0>(fields), 1); } -TEST(StructReflectionTest, TwoField) { +TEST(StructReflectionTest, TwoFields) { std::tuple fields = AsTuple(TwoFields{.x = 1, .y = 2}); EXPECT_EQ(std::get<0>(fields), 1); EXPECT_EQ(std::get<1>(fields), 2); } +TEST(StructReflectionTest, SixFields) { + std::tuple fields = AsTuple(SixFields{ + .one = 1, .two = 2, .three = 3, .four = 4, .five = 5, .six = 6}); + EXPECT_EQ(std::get<0>(fields), 1); + EXPECT_EQ(std::get<1>(fields), 2); + EXPECT_EQ(std::get<2>(fields), 3); + EXPECT_EQ(std::get<3>(fields), 4); + EXPECT_EQ(std::get<4>(fields), 5); + EXPECT_EQ(std::get<5>(fields), 6); +} + TEST(StructReflectionTest, NoDefaultConstructor) { std::tuple fields = AsTuple(TwoFieldsNoDefaultConstructor{.x = NoDefaultConstructor(1), diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 5c2e2f2ed253..5534740b76f7 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -155,6 +155,7 @@ static auto ProcessParseNodes(Context& context, // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (auto parse_kind = context.parse_tree().node_kind(parse_node)) { + // TODO: Switch to `Parse::Name##Id(parse_node)` here. #define CARBON_PARSE_NODE_KIND(Name) \ case Parse::NodeKind::Name: { \ if (!Check::Handle##Name(context, parse_node)) { \ diff --git a/toolchain/check/decl_state.h b/toolchain/check/decl_state.h index 162d8f23c6d1..0813c164a528 100644 --- a/toolchain/check/decl_state.h +++ b/toolchain/check/decl_state.h @@ -43,7 +43,7 @@ enum class KeywordModifierSet : uint32_t { }; inline auto operator!(KeywordModifierSet k) -> bool { - return !static_cast(k); + return !static_cast(k); } // State stored for each declaration we are currently in: the kind of diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 9493ab5778ce..eaec94ba6145 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -92,6 +92,7 @@ class NodeStack { } // Pops the top of the stack and returns the parse_node. + // TODO: return a parse::NodeIdForKind instead. template auto PopForSoloParseNode() -> Parse::NodeId { Entry back = PopEntry(); @@ -102,6 +103,7 @@ class NodeStack { // Pops the top of the stack if it is the given kind, and returns the // parse_node. Otherwise, returns std::nullopt. + // TODO: Return a `Parse::NodeIdForKind` instead. template auto PopForSoloParseNodeIf() -> std::optional { if (PeekIs()) { @@ -200,6 +202,9 @@ class NodeStack { // Pops a name from the top of the stack and returns the ID. auto PopName() -> SemIR::NameId { return PopNameWithParseNode().second; } + // TODO: Can we add a `Pop<...>` that takes a parse node category? See + // https://github.com/carbon-language/carbon-lang/pull/3534/files#r1432067519 + // Pops the top of the stack and returns the ID. template auto Pop() -> auto { diff --git a/toolchain/parse/BUILD b/toolchain/parse/BUILD index 759784b349a7..55f146795919 100644 --- a/toolchain/parse/BUILD +++ b/toolchain/parse/BUILD @@ -16,12 +16,34 @@ filegroup( cc_library( name = "node_kind", srcs = ["node_kind.cpp"], - hdrs = ["node_kind.h"], + hdrs = [ + "node_ids.h", + "node_kind.h", + "typed_nodes.h", + ], textual_hdrs = ["node_kind.def"], deps = [ "//common:check", "//common:enum_base", + "//toolchain/base:index_base", "//toolchain/lex:token_kind", + "@llvm-project//llvm:Support", + ], +) + +cc_test( + name = "typed_nodes_test", + size = "small", + srcs = ["typed_nodes_test.cpp"], + deps = [ + ":node_kind", + ":tree", + "//testing/base:gtest_main", + "//toolchain/diagnostics:diagnostic_emitter", + "//toolchain/diagnostics:mocks", + "//toolchain/lex", + "//toolchain/lex:tokenized_buffer", + "@com_google_googletest//:gtest", ], ) @@ -38,6 +60,7 @@ cc_library( srcs = [ "context.cpp", "context.h", + "extract.cpp", "tree.cpp", ] + # Glob handler files to avoid missing any. @@ -52,6 +75,7 @@ cc_library( "//common:check", "//common:error", "//common:ostream", + "//common:struct_reflection", "//common:vlog", "//toolchain/base:pretty_stack_trace_function", "//toolchain/base:value_store", diff --git a/toolchain/parse/extract.cpp b/toolchain/parse/extract.cpp new file mode 100644 index 000000000000..3b6fd5fd8084 --- /dev/null +++ b/toolchain/parse/extract.cpp @@ -0,0 +1,342 @@ +// 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 +#include + +#include "common/error.h" +#include "common/struct_reflection.h" +#include "toolchain/parse/tree.h" +#include "toolchain/parse/typed_nodes.h" + +namespace Carbon::Parse { + +// A trait type that should be specialized by types that can be extracted +// from a parse tree. A specialization should provide the following API: +// +// ```cpp +// template<> +// struct Extractable { +// // Extract a value of this type from the sequence of nodes starting at +// // `it`, and increment `it` past this type. Returns `std::nullopt` if +// // the tree is malformed. If `trace != nullptr`, writes what actions +// // were taken to `*trace`. +// static auto Extract(Tree* tree, Tree::SiblingIterator& it, +// Tree::SiblingIterator end, +// ErrorBuilder* trace) -> std::optional; +// }; +// ``` +// +// Note that `Tree::SiblingIterator`s iterate in reverse order through the +// children of a node. +// +// This class is only in this file. +template +struct Extractable; + +// Extract a `NodeId` as a single child. +template <> +struct Extractable { + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional { + if (it == end) { + if (trace) { + *trace << "NodeId error: no more children\n"; + } + return std::nullopt; + } + if (trace) { + *trace << "NodeId: " << tree->node_kind(*it) << " consumed\n"; + } + return NodeId(*it++); + } +}; + +// Extract a `FooId`, which is the same as `NodeIdForKind`, +// as a single required child. +template +struct Extractable> { + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional> { + if (it == end || tree->node_kind(*it) != Kind) { + if (trace) { + if (it == end) { + *trace << "NodeIdForKind error: no more children, expected " << Kind + << "\n"; + } else { + *trace << "NodeIdForKind error: wrong kind " << tree->node_kind(*it) + << ", expected " << Kind << "\n"; + } + } + return std::nullopt; + } + if (trace) { + *trace << "NodeIdForKind: " << Kind << " consumed\n"; + } + return NodeIdForKind(*it++); + } +}; + +// Extract a `NodeIdInCategory` as a single child. +template +struct Extractable> { + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional> { + if (trace) { + *trace << "NodeIdInCategory"; + // TODO: Make NodeCategory printable instead. + if (!Category) { + *trace << " "; + } +#define CARBON_NODE_CATEGORY(Name) \ + if (!!(Category & NodeCategory::Name)) { \ + *trace << " " #Name; \ + } + CARBON_NODE_CATEGORY(Decl); + CARBON_NODE_CATEGORY(Expr); + CARBON_NODE_CATEGORY(Modifier); + CARBON_NODE_CATEGORY(NameComponent); + CARBON_NODE_CATEGORY(Pattern); + CARBON_NODE_CATEGORY(Statement); +#undef CARBON_NODE_CATEGORY + } + + if (it == end || !(tree->node_kind(*it).category() & Category)) { + if (trace) { + if (it == end) { + *trace << " error: no more children\n"; + } else { + *trace << " error: kind " << tree->node_kind(*it) + << " doesn't match\n"; + } + } + return std::nullopt; + } + if (trace) { + *trace << ": kind " << tree->node_kind(*it) << " consumed\n"; + } + return NodeIdInCategory(*it++); + } +}; + +// Extract a `NodeIdOneOf` as a single required child. +template +struct Extractable> { + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional> { + auto kind = tree->node_kind(*it); + if (it == end || (kind != T::Kind && kind != U::Kind)) { + if (trace) { + if (it == end) { + *trace << "NodeIdOneOf error: no more children, expected " << T::Kind + << " or " << U::Kind << "\n"; + } else { + *trace << "NodeIdOneOf error: wrong kind " << tree->node_kind(*it) + << ", expected " << T::Kind << " or " << U::Kind << "\n"; + } + } + return std::nullopt; + } + if (trace) { + *trace << "NodeIdOneOf " << T::Kind << " or " << U::Kind << ": " + << tree->node_kind(*it) << " consumed"; + } + return NodeIdOneOf(*it++); + } +}; + +// Extract a `NodeIdNot` as a single required child. +template +struct Extractable> { + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional> { + if (it == end || tree->node_kind(*it) == T::Kind) { + if (trace) { + if (it == end) { + *trace << "NodeIdNot " << T::Kind << " error: no more children\n"; + } else { + *trace << "NodeIdNot error: unexpected " << T::Kind << "\n"; + } + } + return std::nullopt; + } + if (trace) { + *trace << "NodeIdNot " << T::Kind << ": " << tree->node_kind(*it) + << " consumed\n"; + } + return NodeIdNot(*it++); + } +}; + +// Extract an `llvm::SmallVector` by extracting `T`s until we can't. +template +struct Extractable> { + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional> { + if (trace) { + *trace << "Vector: begin\n"; + } + llvm::SmallVector result; + while (it != end) { + auto old_it = it; + auto item = Extractable::Extract(tree, it, end, trace); + if (!item.has_value()) { + it = old_it; + break; + } + result.push_back(*item); + } + std::reverse(result.begin(), result.end()); + if (trace) { + *trace << "Vector: end\n"; + } + return result; + } +}; + +// Extract an `optional` from a list of child nodes by attempting to extract +// a `T`, and extracting nothing if that fails. +template +struct Extractable> { + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional> { + if (trace) { + *trace << "Optional" << typeid(T).name() << ": begin\n"; + } + auto old_it = it; + std::optional value = Extractable::Extract(tree, it, end, trace); + if (value) { + if (trace) { + *trace << "Optional" << typeid(T).name() << ": found\n"; + } + return value; + } + if (trace) { + *trace << "Optional" << typeid(T).name() << ": missing\n"; + } + it = old_it; + return value; + } +}; + +// Extract a `tuple` from a list of child nodes by extracting each `T` in +// reverse order. +template +struct Extractable> { + template + static auto ExtractImpl(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace, + std::index_sequence) + -> std::optional> { + std::tuple...> fields; + if (trace) { + *trace << "Tuple: begin\n"; + } + + // Use a fold over the `=` operator to parse fields from right to left. + [[maybe_unused]] int unused; + bool ok = true; + static_cast( + ((ok && (ok = (std::get(fields) = + Extractable::Extract(tree, it, end, trace)) + .has_value()), + unused) = ... = 0)); + + if (!ok) { + if (trace) { + *trace << "Tuple: error\n"; + } + return std::nullopt; + } + + if (trace) { + *trace << "Tuple: success\n"; + } + return std::tuple{std::move(std::get(fields).value())...}; + } + + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional> { + return ExtractImpl(tree, it, end, trace, + std::make_index_sequence()); + } +}; + +// Extract the fields of a simple aggregate type. +template +struct Extractable { + static_assert(std::is_aggregate_v, "Unsupported child type"); + static auto ExtractImpl(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional { + if (trace) { + *trace << "Aggregate " << typeid(T).name() << ": begin\n"; + } + // Extract the corresponding tuple type. + using TupleType = decltype(StructReflection::AsTuple(std::declval())); + auto tuple = Extractable::Extract(tree, it, end, trace); + if (!tuple.has_value()) { + if (trace) { + *trace << "Aggregate" << typeid(T).name() << ": error\n"; + } + return std::nullopt; + } + + if (trace) { + *trace << "Aggregate" << typeid(T).name() << ": success\n"; + } + // Convert the tuple to the struct type. + return std::apply( + [](auto&&... value) { + return T{std::forward(value)...}; + }, + *tuple); + } + + static auto Extract(const Tree* tree, Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) + -> std::optional { + static_assert(!HasKindMember, "Missing Id suffix"); + return ExtractImpl(tree, it, end, trace); + } +}; + +template +auto Tree::TryExtractNodeFromChildren( + llvm::iterator_range children, + ErrorBuilder* trace) const -> std::optional { + auto it = children.begin(); + auto result = Extractable::ExtractImpl(this, it, children.end(), trace); + if (it != children.end()) { + if (trace) { + *trace << "Error: " << node_kind(*it) << " node left unconsumed."; + } + return std::nullopt; + } + return result; +} + +// Manually instantiate Tree::TryExtractNodeFromChildren +#define CARBON_PARSE_NODE_KIND(KindName) \ + template auto Tree::TryExtractNodeFromChildren( \ + llvm::iterator_range children, \ + ErrorBuilder * trace) const -> std::optional; + +// Also instantiate for `File`, even though it isn't a parse node. +CARBON_PARSE_NODE_KIND(File) +#include "toolchain/parse/node_kind.def" + +auto Tree::ExtractFile() const -> File { + return ExtractNodeFromChildren(roots()); +} + +} // namespace Carbon::Parse diff --git a/toolchain/parse/handle_brace_expr.cpp b/toolchain/parse/handle_brace_expr.cpp index 20675c0247b7..44f6cd8e56c3 100644 --- a/toolchain/parse/handle_brace_expr.cpp +++ b/toolchain/parse/handle_brace_expr.cpp @@ -149,6 +149,7 @@ static auto HandleBraceExprParamFinish(Context& context, NodeKind node_kind, if (state.has_error) { context.AddLeafNode(NodeKind::InvalidParse, state.token, /*has_error=*/true); + context.ReturnErrorOnState(); } else { context.AddNode(node_kind, state.token, state.subtree_start, /*has_error=*/false); diff --git a/toolchain/parse/handle_let.cpp b/toolchain/parse/handle_let.cpp index 22330b016c76..4f3eb3f62b67 100644 --- a/toolchain/parse/handle_let.cpp +++ b/toolchain/parse/handle_let.cpp @@ -30,11 +30,13 @@ auto HandleLetAfterPattern(Context& context) -> void { if (auto equals = context.ConsumeIf(Lex::TokenKind::Equal)) { context.AddLeafNode(NodeKind::LetInitializer, *equals); context.PushState(State::Expr); - } else if (!state.has_error) { - CARBON_DIAGNOSTIC( - ExpectedInitializerAfterLet, Error, - "Expected `=`; `let` declaration must have an initializer."); - context.emitter().Emit(*context.position(), ExpectedInitializerAfterLet); + } else { + if (!state.has_error) { + CARBON_DIAGNOSTIC( + ExpectedInitializerAfterLet, Error, + "Expected `=`; `let` declaration must have an initializer."); + context.emitter().Emit(*context.position(), ExpectedInitializerAfterLet); + } context.ReturnErrorOnState(); } } diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h new file mode 100644 index 000000000000..f8ae31910d30 --- /dev/null +++ b/toolchain/parse/node_ids.h @@ -0,0 +1,99 @@ +// 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_PARSE_NODE_IDS_H_ +#define CARBON_TOOLCHAIN_PARSE_NODE_IDS_H_ + +#include "toolchain/base/index_base.h" +#include "toolchain/parse/node_kind.h" + +namespace Carbon::Parse { + +// A lightweight handle representing a node in the tree. +// +// Objects of this type are small and cheap to copy and store. They don't +// contain any of the information about the node, and serve as a handle that +// can be used with the underlying tree to query for detailed information. +struct NodeId : public IdBase { + // An explicitly invalid instance. + static const NodeId Invalid; + + using IdBase::IdBase; +}; + +constexpr NodeId NodeId::Invalid = NodeId(NodeId::InvalidIndex); + +// For looking up the type associated with a given id type. +template +struct NodeForId; + +// `Id` is a typed version of `NodeId` that references a node of kind +// ``: +template +struct NodeIdForKind : public NodeId { + static const NodeIdForKind Invalid; + + explicit NodeIdForKind(NodeId node_id) : NodeId(node_id) {} +}; +template +constexpr NodeIdForKind NodeIdForKind::Invalid = + NodeIdForKind(NodeId::Invalid.index); + +#define CARBON_PARSE_NODE_KIND(KindName) \ + using KindName##Id = NodeIdForKind; +#include "toolchain/parse/node_kind.def" + +// NodeId that matches any NodeKind whose `category()` overlaps with `Category`. +template +struct NodeIdInCategory : public NodeId { + // An explicitly invalid instance. + static const NodeIdInCategory Invalid; + + explicit NodeIdInCategory(NodeId node_id) : NodeId(node_id) {} +}; + +template +constexpr NodeIdInCategory NodeIdInCategory::Invalid = + NodeIdInCategory(NodeId::InvalidIndex); + +// Aliases for `NodeIdInCategory` to describe particular categories of nodes. +using AnyDeclId = NodeIdInCategory; +using AnyExprId = NodeIdInCategory; +using AnyModifierId = NodeIdInCategory; +using AnyNameComponentId = NodeIdInCategory; +using AnyPatternId = NodeIdInCategory; +using AnyStatementId = NodeIdInCategory; + +// NodeId with kind that matches either T::Kind or U::Kind. +template +struct NodeIdOneOf : public NodeId { + // An explicitly invalid instance. + static const NodeIdOneOf Invalid; + + explicit NodeIdOneOf(NodeId node_id) : NodeId(node_id) {} +}; + +template +constexpr NodeIdOneOf NodeIdOneOf::Invalid = + NodeIdOneOf(NodeId::InvalidIndex); + +// NodeId with kind that is anything but T::Kind. +template +struct NodeIdNot : public NodeId { + // An explicitly invalid instance. + static const NodeIdNot Invalid; + + explicit NodeIdNot(NodeId node_id) : NodeId(node_id) {} +}; + +template +constexpr NodeIdNot NodeIdNot::Invalid = + NodeIdNot(NodeId::InvalidIndex); + +// Note that the support for extracting these types using the `Tree::Extract*` +// functions is defined in `extract.cpp`. + +} // namespace Carbon::Parse + +#endif // CARBON_TOOLCHAIN_PARSE_NODE_IDS_H_ diff --git a/toolchain/parse/node_kind.cpp b/toolchain/parse/node_kind.cpp index 2000fd6d6c36..27acda93d7ea 100644 --- a/toolchain/parse/node_kind.cpp +++ b/toolchain/parse/node_kind.cpp @@ -5,6 +5,7 @@ #include "toolchain/parse/node_kind.h" #include "common/check.h" +#include "toolchain/parse/typed_nodes.h" namespace Carbon::Parse { @@ -76,4 +77,16 @@ auto NodeKind::CheckMatchesTokenKind(Lex::TokenKind token_kind, bool has_error) << ", but expected token kind " << expected_token_kind; } +auto NodeKind::category() const -> NodeCategory { + return definition().category(); +} + +auto NodeKind::definition() const -> const Definition& { + static constexpr const Definition* Table[] = { +#define CARBON_PARSE_NODE_KIND(Name) &Parse::Name::Kind, +#include "toolchain/parse/node_kind.def" + }; + return *Table[AsInt()]; +} + } // namespace Carbon::Parse diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 649fd4e75876..9884a0c16629 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -203,6 +203,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ImportDirective, ImportIntroducer, // `library` as directive: // LibraryIntroducer // DefaultLibrary or _external_: LibraryName +// PackageApi or PackageImpl // LibraryDirective CARBON_PARSE_NODE_KIND_CHILD_COUNT(DefaultLibrary, 0, Default) CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibraryIntroducer, 0, Library) @@ -210,14 +211,14 @@ CARBON_PARSE_NODE_KIND_BRACKET(LibraryDirective, LibraryIntroducer, CARBON_IF_VALID(Semi)) // `library` in `package` or `import`: -// _external_: LibraryName +// _external_: LibraryName or DefaultLibrary // LibrarySpecifier CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibrarySpecifier, 1, Library) // `namespace`: // NamespaceStart // _repeated_ _external_: modifier -// _external_: Name or QualifiedDecl +// _external_: IdentifierName or QualifiedDecl // Namespace CARBON_PARSE_NODE_KIND_CHILD_COUNT(NamespaceStart, 0, Namespace) CARBON_PARSE_NODE_KIND_BRACKET(Namespace, NamespaceStart, CARBON_IF_VALID(Semi)) @@ -234,7 +235,8 @@ CARBON_PARSE_NODE_KIND_BRACKET(CodeBlock, CodeBlockStart, // `fn`: // FunctionIntroducer // _repeated_ _external_: modifier -// _external_: Name or QualifiedDecl +// _external_: IdentifierName or QualifiedDecl +// _optional_ _external_: ImplicitParamList // _external_: TuplePattern // _external_: type expression // ReturnType @@ -291,7 +293,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(ArrayExprSemi, 2, CARBON_IF_VALID(Semi)) CARBON_PARSE_NODE_KIND_BRACKET(ArrayExpr, ArrayExprSemi, CloseSquareBracket) // A binding pattern, such as `name: Type`: -// Name or SelfValueName +// IdentifierName or SelfValueName // _external_: type expression // [Generic]BindingPattern // _optional_ Address @@ -446,8 +448,8 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExprComma, 0, Comma) CARBON_PARSE_NODE_KIND_BRACKET(CallExpr, CallExprStart, CloseParen) // A qualified declaration, such as `a.b`: -// _external_: Name or QualifiedDecl -// _external_: Name +// _external_: IdentifierName or QualifiedDecl +// _external_: IdentifierName // QualifiedDecl // // TODO: This will eventually more general expressions, for example with @@ -458,14 +460,14 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(QualifiedDecl, 2, Period) // `GetObject().(Interface.member)`: // _external_: lhs expression // _external_: rhs expression -// QualifiedExpr +// MemberAccessExpr CARBON_PARSE_NODE_KIND_CHILD_COUNT(MemberAccessExpr, 2, Period) // A pointer member access expression, such as `a->b` or // `GetObject()->(Interface.member)`: // _external_: lhs expression // _external_: rhs expression -// QualifiedExpr +// PointerMemberAccessExpr CARBON_PARSE_NODE_KIND_CHILD_COUNT(PointerMemberAccessExpr, 2, MinusGreater) // A value literal. @@ -558,7 +560,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExprElse, 3, CARBON_IF_VALID(Else)) // Struct literals, such as `{.a = 0}`: // StructLiteralOrStructTypeLiteralStart -// _external_: Name +// _external_: IdentifierName or BaseName // StructFieldDesignator // _external_: expression // StructFieldValue @@ -568,7 +570,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExprElse, 3, CARBON_IF_VALID(Else)) // // Struct type literals, such as `{.a: i32}`: // StructLiteralOrStructTypeLiteralStart -// _external_: Name +// _external_: IdentifierName or BaseName // StructFieldDesignator // _external_: type expression // StructFieldType @@ -609,7 +611,9 @@ CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Virtual) // `class`: // ClassIntroducer // _repeated_ _external_: modifier -// _external_: Name or QualifiedDecl +// _external_: IdentifierName or QualifiedDecl +// _optional_ _external_: ImplicitParamList +// _optional_ _external_: TuplePattern // ClassDefinitionStart // _external_: declarations // ClassDefinition @@ -638,7 +642,9 @@ CARBON_PARSE_NODE_KIND_BRACKET(BaseDecl, BaseIntroducer, CARBON_IF_VALID(Semi)) // `interface`: // InterfaceIntroducer // _repeated_ _external_: modifier -// _external_: Name or QualifiedDecl +// _external_: IdentifierName or QualifiedDecl +// _optional_ _external_: ImplicitParamList +// _optional_ _external_: TuplePattern // InterfaceDefinitionStart // _external_: declarations // InterfaceDefinition @@ -684,7 +690,9 @@ CARBON_PARSE_NODE_KIND_BRACKET(ImplDecl, ImplIntroducer, CARBON_IF_VALID(Semi)) // `constraint`: // NamedConstraintIntroducer // _repeated_ _external_: modifier -// _external_: Name or QualifiedDecl +// _external_: IdentifierName or QualifiedDecl +// _optional_ _external_: ImplicitParamList +// _optional_ _external_: TuplePattern // NamedConstraintDefinitionStart // _external_: declarations // NamedConstraintDefinition diff --git a/toolchain/parse/node_kind.h b/toolchain/parse/node_kind.h index 19b7ecaf0069..18c88659ec55 100644 --- a/toolchain/parse/node_kind.h +++ b/toolchain/parse/node_kind.h @@ -8,10 +8,33 @@ #include #include "common/enum_base.h" +#include "llvm/ADT/BitmaskEnum.h" #include "toolchain/lex/token_kind.h" namespace Carbon::Parse { +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +// Represents a set of keyword modifiers, using a separate bit per modifier. +// +// We expect this to grow, so are using a bigger size than needed. +// NOLINTNEXTLINE(performance-enum-size) +enum class NodeCategory : uint32_t { + Decl = 1 << 0, + Expr = 1 << 1, + Modifier = 1 << 2, + NameComponent = 1 << 3, + Pattern = 1 << 4, + Statement = 1 << 5, + None = 0, + + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Statement) +}; + +inline auto operator!(NodeCategory k) -> bool { + return !static_cast(k); +} + CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint8_t) { #define CARBON_PARSE_NODE_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name) #include "toolchain/parse/node_kind.def" @@ -40,7 +63,21 @@ class NodeKind : public CARBON_ENUM_BASE(NodeKind) { // that has_bracket is false. auto child_count() const -> int32_t; + // Returns which categories this node kind is in. + auto category() const -> NodeCategory; + using EnumBase::Create; + + class Definition; + + // Provides a definition for this parse node kind. Should only be called + // once, to construct the kind as part of defining it in `typed_nodes.h`. + constexpr auto Define(NodeCategory category = NodeCategory::None) const + -> Definition; + + private: + // Looks up the definition for this instruction kind. + auto definition() const -> const Definition&; }; #define CARBON_PARSE_NODE_KIND(Name) \ @@ -50,6 +87,40 @@ class NodeKind : public CARBON_ENUM_BASE(NodeKind) { // We expect the parse node kind to fit compactly into 8 bits. static_assert(sizeof(NodeKind) == 1, "Kind objects include padding!"); +// A definition of a parse node kind. This is a NodeKind value, plus +// ancillary data such as the name to use for the node kind in LLVM IR. These +// are not copyable, and only one instance of this type is expected to exist per +// parse node kind, specifically `TypedNode::Kind`. Use `NodeKind` instead as a +// thin wrapper around a parse node kind index. +class NodeKind::Definition : public NodeKind { + public: + // Not copyable. + Definition(const Definition&) = delete; + auto operator=(const Definition&) -> Definition& = delete; + + // Returns which categories this node kind is in. + constexpr auto category() const -> NodeCategory { return category_; } + + private: + friend class NodeKind; + + constexpr Definition(NodeKind kind, NodeCategory category) + : NodeKind(kind), category_(category) {} + + NodeCategory category_; +}; + +constexpr auto NodeKind::Define(NodeCategory category) const -> Definition { + return Definition(*this, category); +} + +// HasKindMember is true if T has a `static const NodeKind::Definition Kind` +// member. +template +inline constexpr bool HasKindMember = false; +template +inline constexpr bool HasKindMember = true; + } // namespace Carbon::Parse #endif // CARBON_TOOLCHAIN_PARSE_NODE_KIND_H_ diff --git a/toolchain/parse/testdata/let/fail_empty.carbon b/toolchain/parse/testdata/let/fail_empty.carbon index 8ed75545887f..4fade9f7ad75 100644 --- a/toolchain/parse/testdata/let/fail_empty.carbon +++ b/toolchain/parse/testdata/let/fail_empty.carbon @@ -16,6 +16,6 @@ let; // CHECK:STDOUT: {kind: 'IdentifierName', text: ';', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ';', has_error: yes, subtree_size: 3}, -// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/struct/fail_comma_only.carbon b/toolchain/parse/testdata/struct/fail_comma_only.carbon index a0b8a58f38ee..89ef26a19b59 100644 --- a/toolchain/parse/testdata/struct/fail_comma_only.carbon +++ b/toolchain/parse/testdata/struct/fail_comma_only.carbon @@ -17,7 +17,7 @@ var x: {,} = {}; // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ',', has_error: yes}, // CHECK:STDOUT: {kind: 'StructComma', text: ','}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 4}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 6}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_comma_repeat_in_type.carbon b/toolchain/parse/testdata/struct/fail_comma_repeat_in_type.carbon index a6fe2e1446f3..0efd14d8abbb 100644 --- a/toolchain/parse/testdata/struct/fail_comma_repeat_in_type.carbon +++ b/toolchain/parse/testdata/struct/fail_comma_repeat_in_type.carbon @@ -22,7 +22,7 @@ var x: {.a: i32,,} = {}; // CHECK:STDOUT: {kind: 'StructComma', text: ','}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ',', has_error: yes}, // CHECK:STDOUT: {kind: 'StructComma', text: ','}, -// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 9}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 11}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_comma_repeat_in_value.carbon b/toolchain/parse/testdata/struct/fail_comma_repeat_in_value.carbon index 21315a3bd34f..e36176e0a4cc 100644 --- a/toolchain/parse/testdata/struct/fail_comma_repeat_in_value.carbon +++ b/toolchain/parse/testdata/struct/fail_comma_repeat_in_value.carbon @@ -22,7 +22,7 @@ var x: {.a = 0,,} = {}; // CHECK:STDOUT: {kind: 'StructComma', text: ','}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ',', has_error: yes}, // CHECK:STDOUT: {kind: 'StructComma', text: ','}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 9}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 11}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_dot_only.carbon b/toolchain/parse/testdata/struct/fail_dot_only.carbon index f1e17964c957..31a5310c3757 100644 --- a/toolchain/parse/testdata/struct/fail_dot_only.carbon +++ b/toolchain/parse/testdata/struct/fail_dot_only.carbon @@ -18,7 +18,7 @@ var x: {.} = {}; // CHECK:STDOUT: {kind: 'IdentifierName', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '.', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 7}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_dot_string_colon.carbon b/toolchain/parse/testdata/struct/fail_dot_string_colon.carbon index 598e514023bf..41b5e47cb8f4 100644 --- a/toolchain/parse/testdata/struct/fail_dot_string_colon.carbon +++ b/toolchain/parse/testdata/struct/fail_dot_string_colon.carbon @@ -24,7 +24,7 @@ var x: {."hello": i32, .y: i32} = {}; // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, // CHECK:STDOUT: {kind: 'StructFieldType', text: ':', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 11}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 13}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_dot_string_equals.carbon b/toolchain/parse/testdata/struct/fail_dot_string_equals.carbon index ffeafafae49f..60312cba14c6 100644 --- a/toolchain/parse/testdata/struct/fail_dot_string_equals.carbon +++ b/toolchain/parse/testdata/struct/fail_dot_string_equals.carbon @@ -24,7 +24,7 @@ var x: {."hello" = 0, .y = 4} = {}; // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'IntLiteral', text: '4'}, // CHECK:STDOUT: {kind: 'StructFieldValue', text: '=', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 11}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 13}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_identifier_colon.carbon b/toolchain/parse/testdata/struct/fail_identifier_colon.carbon index b1bc833b0437..31174a149749 100644 --- a/toolchain/parse/testdata/struct/fail_identifier_colon.carbon +++ b/toolchain/parse/testdata/struct/fail_identifier_colon.carbon @@ -16,7 +16,7 @@ var x: {a:} = {}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'InvalidParse', text: 'a', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 5}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_identifier_equals.carbon b/toolchain/parse/testdata/struct/fail_identifier_equals.carbon index 3f0ee3c16c72..ab3099b32480 100644 --- a/toolchain/parse/testdata/struct/fail_identifier_equals.carbon +++ b/toolchain/parse/testdata/struct/fail_identifier_equals.carbon @@ -16,7 +16,7 @@ var x: {a=} = {}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'InvalidParse', text: 'a', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 5}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_identifier_only.carbon b/toolchain/parse/testdata/struct/fail_identifier_only.carbon index 632b4386893a..7e326c33f7ab 100644 --- a/toolchain/parse/testdata/struct/fail_identifier_only.carbon +++ b/toolchain/parse/testdata/struct/fail_identifier_only.carbon @@ -16,7 +16,7 @@ var x: {a} = {}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'InvalidParse', text: 'a', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 5}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_missing_type.carbon b/toolchain/parse/testdata/struct/fail_missing_type.carbon index c86608dbe568..f09551e8f594 100644 --- a/toolchain/parse/testdata/struct/fail_missing_type.carbon +++ b/toolchain/parse/testdata/struct/fail_missing_type.carbon @@ -19,7 +19,7 @@ var x: {.a:} = {}; // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ':', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 6}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 8}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_missing_value.carbon b/toolchain/parse/testdata/struct/fail_missing_value.carbon index 20763f7a1f06..5688750db069 100644 --- a/toolchain/parse/testdata/struct/fail_missing_value.carbon +++ b/toolchain/parse/testdata/struct/fail_missing_value.carbon @@ -19,7 +19,7 @@ var x: {.a=} = {}; // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '=', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 6}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 8}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_mix_type_and_value.carbon b/toolchain/parse/testdata/struct/fail_mix_type_and_value.carbon index c52ac3024932..9e44b9bc6905 100644 --- a/toolchain/parse/testdata/struct/fail_mix_type_and_value.carbon +++ b/toolchain/parse/testdata/struct/fail_mix_type_and_value.carbon @@ -23,7 +23,7 @@ var x: {.a: i32, .b = 0} = {}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'b'}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '.', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 10}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 12}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_mix_value_and_type.carbon b/toolchain/parse/testdata/struct/fail_mix_value_and_type.carbon index d91f8a7fcdad..c289520cd7fa 100644 --- a/toolchain/parse/testdata/struct/fail_mix_value_and_type.carbon +++ b/toolchain/parse/testdata/struct/fail_mix_value_and_type.carbon @@ -21,7 +21,7 @@ var x: {.a = 0, b: i32} = {}; // CHECK:STDOUT: {kind: 'StructFieldValue', text: '=', subtree_size: 4}, // CHECK:STDOUT: {kind: 'StructComma', text: ','}, // CHECK:STDOUT: {kind: 'InvalidParse', text: 'b', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 8}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 10}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_mix_with_unknown.carbon b/toolchain/parse/testdata/struct/fail_mix_with_unknown.carbon index 46edbf2c3cd8..60d5ab25dce8 100644 --- a/toolchain/parse/testdata/struct/fail_mix_with_unknown.carbon +++ b/toolchain/parse/testdata/struct/fail_mix_with_unknown.carbon @@ -41,7 +41,7 @@ var x: i32 = {.a: i32, .b, .c = 1}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'c'}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '.', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 14}, // CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 20}, // CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, @@ -61,7 +61,7 @@ var x: i32 = {.a: i32, .b, .c = 1}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'c'}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '.', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 14}, // CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 20}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/struct/fail_no_colon_or_equals.carbon b/toolchain/parse/testdata/struct/fail_no_colon_or_equals.carbon index f053680851f1..5b3c25328ba2 100644 --- a/toolchain/parse/testdata/struct/fail_no_colon_or_equals.carbon +++ b/toolchain/parse/testdata/struct/fail_no_colon_or_equals.carbon @@ -18,7 +18,7 @@ var x: {.a} = {}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '.', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 7}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/testdata/struct/fail_type_no_designator.carbon b/toolchain/parse/testdata/struct/fail_type_no_designator.carbon index 2f1187fd2724..2dc5dc9d9aeb 100644 --- a/toolchain/parse/testdata/struct/fail_type_no_designator.carbon +++ b/toolchain/parse/testdata/struct/fail_type_no_designator.carbon @@ -16,7 +16,7 @@ var x: {i32} = {}; // CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, // CHECK:STDOUT: {kind: 'InvalidParse', text: 'i32', has_error: yes}, -// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 5}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, // CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'}, diff --git a/toolchain/parse/tree.cpp b/toolchain/parse/tree.cpp index 206f33312ae8..a5d0ee5733e8 100644 --- a/toolchain/parse/tree.cpp +++ b/toolchain/parse/tree.cpp @@ -12,6 +12,7 @@ #include "toolchain/lex/tokenized_buffer.h" #include "toolchain/parse/context.h" #include "toolchain/parse/node_kind.h" +#include "toolchain/parse/typed_nodes.h" namespace Carbon::Parse { @@ -219,6 +220,16 @@ auto Tree::Print(llvm::raw_ostream& output, bool preorder) const -> void { output << " ]\n"; } +static auto TestExtract(const Tree* tree, NodeId node_id, NodeKind kind, + ErrorBuilder* trace) -> bool { + switch (kind) { +#define CARBON_PARSE_NODE_KIND(Name) \ + case NodeKind::Name: \ + return tree->VerifyExtractAs(node_id, trace).has_value(); +#include "toolchain/parse/node_kind.def" + } +} + auto Tree::Verify() const -> ErrorOr { llvm::SmallVector nodes; // Traverse the tree in postorder. @@ -235,6 +246,18 @@ auto Tree::Verify() const -> ErrorOr { return Error(llvm::formatv( "Node #{0} is a placeholder node that wasn't replaced.", n.index)); } + // Should extract successfully if node not marked as having an error. + // Without this code, a 10 mloc test case of lex & parse takes + // 4.129 s ± 0.041 s. With this additional verification, it takes + // 5.768 s ± 0.036 s. + if (!n_impl.has_error && !TestExtract(this, n, n_impl.kind, nullptr)) { + ErrorBuilder trace; + trace << llvm::formatv( + "NodeId #{0} couldn't be extracted as a {1}. Trace:\n", n, + n_impl.kind); + TestExtract(this, n, n_impl.kind, &trace); + return trace; + } int subtree_size = 1; if (n_impl.kind.has_bracket()) { diff --git a/toolchain/parse/tree.h b/toolchain/parse/tree.h index 8b8584294b00..811da401e8e0 100644 --- a/toolchain/parse/tree.h +++ b/toolchain/parse/tree.h @@ -7,6 +7,7 @@ #include +#include "common/check.h" #include "common/error.h" #include "common/ostream.h" #include "llvm/ADT/SmallVector.h" @@ -14,23 +15,13 @@ #include "llvm/ADT/iterator_range.h" #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/lex/tokenized_buffer.h" +#include "toolchain/parse/node_ids.h" #include "toolchain/parse/node_kind.h" namespace Carbon::Parse { -// A lightweight handle representing a node in the tree. -// -// Objects of this type are small and cheap to copy and store. They don't -// contain any of the information about the node, and serve as a handle that -// can be used with the underlying tree to query for detailed information. -struct NodeId : public IdBase { - // An explicitly invalid instance. - static const NodeId Invalid; - - using IdBase::IdBase; -}; - -constexpr NodeId NodeId::Invalid = NodeId(NodeId::InvalidIndex); +// Defined in typed_nodes.h. Include that to call `Tree::ExtractFile()`. +struct File; // A tree of parsed tokens based on the language grammar. // @@ -120,6 +111,19 @@ class Tree : public Printable { auto node_subtree_size(NodeId n) const -> int32_t; + // Returns whether this node is a valid node of the specified type. + template + auto IsValid(NodeId node_id) const -> bool { + return node_kind(node_id) == T::Kind && !node_has_error(node_id); + } + + template + auto IsValid(IdT id) const -> bool { + using T = typename NodeForId::TypedNode; + CARBON_DCHECK(node_kind(id) == T::Kind); + return !node_has_error(id); + } + auto packaging_directive() const -> const std::optional& { return packaging_directive_; } @@ -168,14 +172,56 @@ class Tree : public Printable { // line-oriented shell tools from `grep` to `awk`. auto Print(llvm::raw_ostream& output, bool preorder) const -> void; + // The following `Extract*` function provide an alternative way of accessing + // the nodes of a tree. It is intended to be more convenient and type-safe, + // but slower and can't be used on nodes that are marked as having an error. + // It is appropriate for uses that are less performance sensitive, like + // diagnostics. Example usage: + // ``` + // auto file = tree->ExtractFile(); + // for (AnyDeclId decl_id : file.decls) { + // // `decl_id` is convertible to a `NodeId`. + // if (std::optional fn_decl = + // tree->ExtractAs(decl_id)) { + // // fn_decl->params is a `TuplePatternId` (which extends `NodeId`) + // // that is guaranteed to reference a `TuplePattern`. + // std::optional params = tree->Extract(fn_decl->params); + // // `params` has a value unless there was an error in that node. + // } else if (auto class_def = tree->ExtractAs(decl_id)) { + // // ... + // } + // } + // ``` + + // Extract a `File` object representing the parse tree for the whole file. + // #include "toolchain/parse/typed_nodes.h" to get the definition of `File` + // and the types representing its children nodes. + auto ExtractFile() const -> File; + + // Converts this node_id to a typed node of a specified type, if it is a valid + // node of that kind. + template + auto ExtractAs(NodeId node_id) const -> std::optional; + + // Converts to a typed node, if it is not an error. + template + auto Extract(IdT id) const + -> std::optional::TypedNode>; + // Verifies the parse tree structure. Checks invariants of the parse tree // structure and returns verification errors. // - // 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. + // This is fairly slow, and 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. auto Verify() const -> ErrorOr; + // Like ExtractAs(), but malformed tree errors are not fatal. Should only be + // used by `Verify()`. + template + auto VerifyExtractAs(NodeId node_id, ErrorBuilder* trace) const + -> std::optional; + private: friend class Context; @@ -245,6 +291,20 @@ class Tree : public Printable { auto PrintNode(llvm::raw_ostream& output, NodeId n, int depth, bool preorder) const -> bool; + // Extract a node of type `T` from a sibling range. This is expected to + // consume the complete sibling range. Malformed tree errors are written + // to `*trace`, if `trace != nullptr`. + template + auto TryExtractNodeFromChildren( + llvm::iterator_range children, + ErrorBuilder* trace) const -> std::optional; + + // Extract a node of type `T` from a sibling range. This is expected to + // consume the complete sibling range. Malformed tree errors are fatal. + template + auto ExtractNodeFromChildren( + llvm::iterator_range children) const -> T; + // Depth-first postorder sequence of node implementation data. llvm::SmallVector node_impls_; @@ -270,7 +330,7 @@ class Tree : public Printable { class Tree::PostorderIterator : public llvm::iterator_facade_base, + int, const NodeId*, NodeId>, public Printable { public: PostorderIterator() = delete; @@ -322,7 +382,7 @@ class Tree::PostorderIterator class Tree::SiblingIterator : public llvm::iterator_facade_base, + const NodeId*, NodeId>, public Printable { public: explicit SiblingIterator() = delete; @@ -353,6 +413,51 @@ class Tree::SiblingIterator NodeId node_; }; +template +auto Tree::ExtractNodeFromChildren( + llvm::iterator_range children) const -> T { + auto result = TryExtractNodeFromChildren(children, nullptr); + if (!result.has_value()) { + // On error try again, this time capturing a trace. + ErrorBuilder trace; + TryExtractNodeFromChildren(children, &trace); + CARBON_FATAL() << "Malformed parse node:\n" << Error(trace).message(); + } + return *result; +} + +template +auto Tree::ExtractAs(NodeId node_id) const -> std::optional { + static_assert(HasKindMember, "Not a parse node type"); + if (!IsValid(node_id)) { + return std::nullopt; + } + + return ExtractNodeFromChildren(children(node_id)); +} + +template +auto Tree::VerifyExtractAs(NodeId node_id, ErrorBuilder* trace) const + -> std::optional { + static_assert(HasKindMember, "Not a parse node type"); + if (!IsValid(node_id)) { + return std::nullopt; + } + + return TryExtractNodeFromChildren(children(node_id), trace); +} + +template +auto Tree::Extract(IdT id) const + -> std::optional::TypedNode> { + if (!IsValid(id)) { + return std::nullopt; + } + + using T = typename NodeForId::TypedNode; + return ExtractNodeFromChildren(children(id)); +} + } // namespace Carbon::Parse #endif // CARBON_TOOLCHAIN_PARSE_TREE_H_ diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h new file mode 100644 index 000000000000..d7d242f9eca5 --- /dev/null +++ b/toolchain/parse/typed_nodes.h @@ -0,0 +1,909 @@ +// 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_PARSE_TYPED_NODES_H_ +#define CARBON_TOOLCHAIN_PARSE_TYPED_NODES_H_ + +#include "toolchain/parse/node_ids.h" +#include "toolchain/parse/node_kind.h" + +namespace Carbon::Parse { + +// Helpers for defining different kinds of parse nodes. +// ---------------------------------------------------- + +// A pair of a list item and its optional following comma. +template +struct ListItem { + Element value; + std::optional comma; +}; + +// A list of items, parameterized by the kind of the elements and comma. +template +using CommaSeparatedList = llvm::SmallVector>; + +// This class provides a shorthand for defining parse node kinds for leaf nodes. +template +struct LeafNode { + static constexpr auto Kind = KindT.Define(Category); +}; + +// ---------------------------------------------------------------------------- +// Each node kind (in node_kind.def) should have a corresponding type defined +// here which describes the expected child structure of that parse node. +// +// Each of these types should start with a `static constexpr Kind` member +// initialized by calling `Define` on the corresponding `NodeKind`, and passing +// in the `NodeCategory` of that kind. This will both associate the category +// with the node kind and create the necessary kind object for the typed node. +// +// This should be followed by field declarations that describe the child nodes, +// in order, that occur in the parse tree. The `Extract...` functions on the +// parse tree use struct reflection on these fields to guide the extraction of +// the child nodes from the tree into an object of this type with these fields +// for convenient access. +// +// The types of these fields are special and describe the specific child node +// structure of the parse node. Many of these types are defined in `node_ids.h`. +// +// Valid primitive types here are: +// - `NodeId` to match any single child node +// - `FooId` to require that child to have kind `NodeKind::Foo` +// - `AnyCatId` to require that child to have a kind in category `Cat` +// - `NodeIdOneOf` to require the child to have kind `NodeKind::A` or +// `NodeKind::B` +// - `NodeIdNot` to match any single child whose kind is not `NodeKind::A` +// +// There a few, restricted composite field types allowed that compose types in +// various ways, where all of the `T`s and `U`s below are themselves valid field +// types: +// - `llvm::SmallVector` to match any number of children matching `T` +// - `std::optional` to match 0 or 1 children matching `T` +// - `std::tuple` to match children matching `T...` +// - Any provided `Aggregate` type that is a simple aggregate type such as +// `struct Aggregate { T x; U y; }`, +// to match children with types `T` and `U`. +// ---------------------------------------------------------------------------- + +// Error nodes +// ----------- + +// An invalid parse. Used to balance the parse tree. This type is here only to +// ensure we have a type for each parse node kind. This node kind always has an +// error, so can never be extracted. +using InvalidParse = + LeafNode; + +// An invalid subtree. Always has an error so can never be extracted. +using InvalidParseStart = LeafNode; +struct InvalidParseSubtree { + static constexpr auto Kind = + NodeKind::InvalidParseSubtree.Define(NodeCategory::Decl); + + InvalidParseStartId start; + llvm::SmallVector> extra; +}; + +// A placeholder node to be replaced; it will never exist in a valid parse tree. +// Its token kind is not enforced even when valid. +using Placeholder = LeafNode; + +// File nodes +// ---------- + +// The start of the file. +using FileStart = LeafNode; + +// The end of the file. +using FileEnd = LeafNode; + +// General-purpose nodes +// --------------------- + +// An empty declaration, such as `;`. +using EmptyDecl = + LeafNode; + +// A name in a non-expression context, such as a declaration. +using IdentifierName = + LeafNode; + +// A name in an expression context. +using IdentifierNameExpr = + LeafNode; + +// The `self` value and `Self` type identifier keywords. Typically of the form +// `self: Self`. +using SelfValueName = LeafNode; +using SelfValueNameExpr = + LeafNode; +using SelfTypeNameExpr = + LeafNode; + +// The `base` value keyword, introduced by `base: B`. Typically referenced in +// an expression, as in `x.base` or `{.base = ...}`, but can also be used as a +// declared name, as in `{.base: partial B}`. +using BaseName = LeafNode; + +// A qualified name: `A.B`. +// +// TODO: This is not a declaration. Rename this parse node. +struct QualifiedDecl { + static constexpr auto Kind = + NodeKind::QualifiedDecl.Define(NodeCategory::NameComponent); + + // For now, this is either an IdentifierName or a QualifiedDecl. + AnyNameComponentId lhs; + + // TODO: This will eventually need to support more general expressions, for + // example `GenericType(type_args).ChildType(child_type_args).Name`. + IdentifierNameId rhs; +}; + +// Library, package, import +// ------------------------ + +// The `package` keyword in an expression. +using PackageExpr = LeafNode; + +// The name of a package or library for `package`, `import`, and `library`. +using PackageName = LeafNode; +using LibraryName = LeafNode; +using DefaultLibrary = LeafNode; + +using PackageIntroducer = LeafNode; +using PackageApi = LeafNode; +using PackageImpl = LeafNode; + +// `library` in `package` or `import`. +struct LibrarySpecifier { + static constexpr auto Kind = NodeKind::LibrarySpecifier.Define(); + + NodeIdOneOf name; +}; + +// First line of the file, such as: +// `package MyPackage library "MyLibrary" impl;` +struct PackageDirective { + static constexpr auto Kind = NodeKind::PackageDirective.Define(); + + PackageIntroducerId introducer; + std::optional name; + std::optional library; + NodeIdOneOf api_or_impl; +}; + +// `import TheirPackage library "TheirLibrary";` +using ImportIntroducer = LeafNode; +struct ImportDirective { + static constexpr auto Kind = NodeKind::ImportDirective.Define(); + + ImportIntroducerId introducer; + std::optional name; + std::optional library; +}; + +// `library` as directive. +using LibraryIntroducer = LeafNode; +struct LibraryDirective { + static constexpr auto Kind = NodeKind::LibraryDirective.Define(); + + LibraryIntroducerId introducer; + NodeIdOneOf library_name; + NodeIdOneOf api_or_impl; +}; + +// Namespace nodes +// --------------- + +using NamespaceStart = LeafNode; + +// A namespace: `namespace N;`. +struct Namespace { + static constexpr auto Kind = NodeKind::Namespace.Define(NodeCategory::Decl); + + NamespaceStartId introducer; + llvm::SmallVector modifiers; + NodeIdOneOf name; +}; + +// Pattern nodes +// ------------- + +// A pattern binding, such as `name: Type`. +struct BindingPattern { + static constexpr auto Kind = + NodeKind::BindingPattern.Define(NodeCategory::Pattern); + + NodeIdOneOf name; + AnyExprId type; +}; + +// `name:! Type` +struct GenericBindingPattern { + static constexpr auto Kind = + NodeKind::GenericBindingPattern.Define(NodeCategory::Pattern); + + NodeIdOneOf name; + AnyExprId type; +}; + +// An address-of binding: `addr self: Self*`. +struct Address { + static constexpr auto Kind = NodeKind::Address.Define(NodeCategory::Pattern); + + AnyPatternId inner; +}; + +// A template binding: `template T:! type`. +struct Template { + static constexpr auto Kind = NodeKind::Template.Define(NodeCategory::Pattern); + + // This is a GenericBindingPatternId in any valid program. + // TODO: Should the parser enforce that? + AnyPatternId inner; +}; + +using TuplePatternStart = LeafNode; +using PatternListComma = LeafNode; + +// A parameter list or tuple pattern: `(a: i32, b: i32)`. +struct TuplePattern { + static constexpr auto Kind = + NodeKind::TuplePattern.Define(NodeCategory::Pattern); + + TuplePatternStartId left_paren; + CommaSeparatedList params; +}; + +using ImplicitParamListStart = LeafNode; + +// An implicit parameter list: `[T:! type, self: Self]`. +struct ImplicitParamList { + static constexpr auto Kind = NodeKind::ImplicitParamList.Define(); + + ImplicitParamListStartId left_square; + CommaSeparatedList params; +}; + +// Function nodes +// -------------- + +using FunctionIntroducer = LeafNode; + +// A return type: `-> i32`. +struct ReturnType { + static constexpr auto Kind = NodeKind::ReturnType.Define(); + + AnyExprId type; +}; + +// A function signature: `fn F() -> i32`. +template +struct FunctionSignature { + static constexpr auto Kind = KindT.Define(NodeCategory::Decl); + + FunctionIntroducerId introducer; + llvm::SmallVector modifiers; + // For now, this is either an IdentifierName or a QualifiedDecl. + AnyNameComponentId name; + std::optional implicit_params; + TuplePatternId params; + std::optional return_type; +}; + +using FunctionDecl = FunctionSignature; +using FunctionDefinitionStart = + FunctionSignature; + +// A function definition: `fn F() -> i32 { ... }`. +struct FunctionDefinition { + static constexpr auto Kind = + NodeKind::FunctionDefinition.Define(NodeCategory::Decl); + + FunctionDefinitionStartId signature; + llvm::SmallVector body; +}; + +// `let` nodes +// ----------- + +using LetIntroducer = LeafNode; +using LetInitializer = LeafNode; + +// A `let` declaration: `let a: i32 = 5;`. +struct LetDecl { + static constexpr auto Kind = + NodeKind::LetDecl.Define(NodeCategory::Decl | NodeCategory::Statement); + + LetIntroducerId introducer; + llvm::SmallVector modifiers; + AnyPatternId pattern; + LetInitializerId equals; + AnyExprId initializer; +}; + +// `var` nodes +// ----------- + +using VariableIntroducer = LeafNode; +using ReturnedModifier = LeafNode; +using VariableInitializer = LeafNode; + +// A `var` declaration: `var a: i32;` or `var a: i32 = 5;`. +struct VariableDecl { + static constexpr auto Kind = NodeKind::VariableDecl.Define( + NodeCategory::Decl | NodeCategory::Statement); + + VariableIntroducerId introducer; + llvm::SmallVector modifiers; + std::optional returned; + AnyPatternId pattern; + + struct Initializer { + VariableInitializerId equals; + AnyExprId value; + }; + std::optional initializer; +}; + +// Statement nodes +// --------------- + +using CodeBlockStart = LeafNode; + +// A code block: `{ statement; statement; ... }`. +struct CodeBlock { + static constexpr auto Kind = NodeKind::CodeBlock.Define(); + + CodeBlockStartId left_brace; + llvm::SmallVector statements; +}; + +// An expression statement: `F(x);`. +struct ExprStatement { + static constexpr auto Kind = + NodeKind::ExprStatement.Define(NodeCategory::Statement); + + AnyExprId expr; +}; + +using BreakStatementStart = LeafNode; + +// A break statement: `break;`. +struct BreakStatement { + static constexpr auto Kind = + NodeKind::BreakStatement.Define(NodeCategory::Statement); + + BreakStatementStartId introducer; +}; + +using ContinueStatementStart = LeafNode; + +// A continue statement: `continue;`. +struct ContinueStatement { + static constexpr auto Kind = + NodeKind::ContinueStatement.Define(NodeCategory::Statement); + + ContinueStatementStartId introducer; +}; + +using ReturnStatementStart = LeafNode; +using ReturnVarModifier = LeafNode; + +// A return statement: `return;` or `return expr;` or `return var;`. +struct ReturnStatement { + static constexpr auto Kind = + NodeKind::ReturnStatement.Define(NodeCategory::Statement); + + ReturnStatementStartId introducer; + std::optional expr; + std::optional var; +}; + +using ForHeaderStart = LeafNode; + +// The `var ... in` portion of a `for` statement. +struct ForIn { + static constexpr auto Kind = NodeKind::ForIn.Define(); + + VariableIntroducerId introducer; + AnyPatternId pattern; +}; + +// The `for (var ... in ...)` portion of a `for` statement. +struct ForHeader { + static constexpr auto Kind = NodeKind::ForHeader.Define(); + + ForHeaderStartId introducer; + ForInId var; + AnyExprId range; +}; + +// A complete `for (...) { ... }` statement. +struct ForStatement { + static constexpr auto Kind = + NodeKind::ForStatement.Define(NodeCategory::Statement); + + ForHeaderId header; + CodeBlockId body; +}; + +using IfConditionStart = LeafNode; + +// The condition portion of an `if` statement: `(expr)`. +struct IfCondition { + static constexpr auto Kind = NodeKind::IfCondition.Define(); + + IfConditionStartId left_paren; + AnyExprId condition; +}; + +using IfStatementElse = LeafNode; + +// An `if` statement: `if (expr) { ... } else { ... }`. +struct IfStatement { + static constexpr auto Kind = + NodeKind::IfStatement.Define(NodeCategory::Statement); + + IfConditionId head; + CodeBlockId then; + + struct Else { + IfStatementElseId else_token; + NodeIdOneOf body; + }; + std::optional else_clause; +}; + +using WhileConditionStart = LeafNode; + +// The condition portion of a `while` statement: `(expr)`. +struct WhileCondition { + static constexpr auto Kind = NodeKind::WhileCondition.Define(); + + WhileConditionStartId left_paren; + AnyExprId condition; +}; + +// A `while` statement: `while (expr) { ... }`. +struct WhileStatement { + static constexpr auto Kind = + NodeKind::WhileStatement.Define(NodeCategory::Statement); + + WhileConditionId head; + CodeBlockId body; +}; + +// Expression nodes +// ---------------- + +using ArrayExprStart = LeafNode; + +// The start of an array type, `[i32;`. +// +// TODO: Consider flattening this into `ArrayExpr`. +struct ArrayExprSemi { + static constexpr auto Kind = NodeKind::ArrayExprSemi.Define(); + + ArrayExprStartId left_square; + AnyExprId type; +}; + +// An array type, such as `[i32; 3]` or `[i32;]`. +struct ArrayExpr { + static constexpr auto Kind = NodeKind::ArrayExpr.Define(NodeCategory::Expr); + + ArrayExprSemiId start; + std::optional bound; +}; + +// The opening portion of an indexing expression: `a[`. +// +// TODO: Consider flattening this into `IndexExpr`. +struct IndexExprStart { + static constexpr auto Kind = NodeKind::IndexExprStart.Define(); + + AnyExprId sequence; +}; + +// An indexing expression, such as `a[1]`. +struct IndexExpr { + static constexpr auto Kind = NodeKind::IndexExpr.Define(NodeCategory::Expr); + + IndexExprStartId start; + AnyExprId index; +}; + +using ExprOpenParen = LeafNode; + +// A parenthesized expression: `(a)`. +struct ParenExpr { + static constexpr auto Kind = NodeKind::ParenExpr.Define(NodeCategory::Expr); + + ExprOpenParenId left_paren; + AnyExprId expr; +}; + +using TupleLiteralComma = LeafNode; + +// A tuple literal: `()`, `(a, b, c)`, or `(a,)`. +struct TupleLiteral { + static constexpr auto Kind = + NodeKind::TupleLiteral.Define(NodeCategory::Expr); + + ExprOpenParenId left_paren; + CommaSeparatedList elements; +}; + +// The opening portion of a call expression: `F(`. +// +// TODO: Consider flattening this into `CallExpr`. +struct CallExprStart { + static constexpr auto Kind = NodeKind::CallExprStart.Define(); + + AnyExprId callee; +}; + +using CallExprComma = LeafNode; + +// A call expression: `F(a, b, c)`. +struct CallExpr { + static constexpr auto Kind = NodeKind::CallExpr.Define(NodeCategory::Expr); + + CallExprStartId start; + CommaSeparatedList arguments; +}; + +// A simple member access expression: `a.b`. +struct MemberAccessExpr { + static constexpr auto Kind = + NodeKind::MemberAccessExpr.Define(NodeCategory::Expr); + + AnyExprId lhs; + // TODO: Figure out which nodes can appear here + NodeId rhs; +}; + +// A simple indirect member access expression: `a->b`. +struct PointerMemberAccessExpr { + static constexpr auto Kind = + NodeKind::PointerMemberAccessExpr.Define(NodeCategory::Expr); + + AnyExprId lhs; + // TODO: Figure out which nodes can appear here + NodeId rhs; +}; + +// A prefix operator expression. +template +struct PrefixOperator { + static constexpr auto Kind = KindT.Define(NodeCategory::Expr); + + AnyExprId operand; +}; + +// An infix operator expression. +template +struct InfixOperator { + static constexpr auto Kind = KindT.Define(NodeCategory::Expr); + + AnyExprId lhs; + AnyExprId rhs; +}; + +// A postfix operator expression. +template +struct PostfixOperator { + static constexpr auto Kind = KindT.Define(NodeCategory::Expr); + + AnyExprId operand; +}; + +// Literals, operators, and modifiers + +#define CARBON_PARSE_NODE_KIND(...) +#define CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(Name, ...) \ + using Name = LeafNode; +#define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name, ...) \ + using Name##Modifier = \ + LeafNode; +#define CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Name, ...) \ + using PrefixOperator##Name = PrefixOperator; +#define CARBON_PARSE_NODE_KIND_INFIX_OPERATOR(Name, ...) \ + using InfixOperator##Name = InfixOperator; +#define CARBON_PARSE_NODE_KIND_POSTFIX_OPERATOR(Name, ...) \ + using PostfixOperator##Name = \ + PostfixOperator; +#include "toolchain/parse/node_kind.def" + +// The first operand of a short-circuiting infix operator: `a and` or `a or`. +// The complete operator expression will be an InfixOperator with this as the +// `lhs`. +// TODO: Make this be a template if we ever need to write generic code to cover +// both cases at once, say in check. +struct ShortCircuitOperandAnd { + static constexpr auto Kind = NodeKind::ShortCircuitOperandAnd.Define(); + + AnyExprId operand; +}; + +struct ShortCircuitOperandOr { + static constexpr auto Kind = NodeKind::ShortCircuitOperandOr.Define(); + + AnyExprId operand; +}; + +struct ShortCircuitOperatorAnd { + static constexpr auto Kind = + NodeKind::ShortCircuitOperatorAnd.Define(NodeCategory::Expr); + + ShortCircuitOperandAndId lhs; + AnyExprId rhs; +}; + +struct ShortCircuitOperatorOr { + static constexpr auto Kind = + NodeKind::ShortCircuitOperatorOr.Define(NodeCategory::Expr); + + ShortCircuitOperandOrId lhs; + AnyExprId rhs; +}; + +// The `if` portion of an `if` expression: `if expr`. +struct IfExprIf { + static constexpr auto Kind = NodeKind::IfExprIf.Define(); + + AnyExprId condition; +}; + +// The `then` portion of an `if` expression: `then expr`. +struct IfExprThen { + static constexpr auto Kind = NodeKind::IfExprThen.Define(); + + AnyExprId result; +}; + +// A full `if` expression: `if expr then expr else expr`. +struct IfExprElse { + static constexpr auto Kind = NodeKind::IfExprElse.Define(NodeCategory::Expr); + + IfExprIfId start; + IfExprThenId then; + AnyExprId else_result; +}; + +// Struct literals and struct type literals +// ---------------------------------------- + +// `{` +using StructLiteralOrStructTypeLiteralStart = + LeafNode; +// `,` +using StructComma = LeafNode; + +// `.a` +struct StructFieldDesignator { + static constexpr auto Kind = NodeKind::StructFieldDesignator.Define(); + + NodeIdOneOf name; +}; + +// `.a = 0` +struct StructFieldValue { + static constexpr auto Kind = NodeKind::StructFieldValue.Define(); + + StructFieldDesignatorId designator; + AnyExprId expr; +}; + +// `.a: i32` +struct StructFieldType { + static constexpr auto Kind = NodeKind::StructFieldType.Define(); + + StructFieldDesignatorId designator; + AnyExprId type_expr; +}; + +// Struct literals, such as `{.a = 0}`. +struct StructLiteral { + static constexpr auto Kind = + NodeKind::StructLiteral.Define(NodeCategory::Expr); + + StructLiteralOrStructTypeLiteralStartId introducer; + CommaSeparatedList fields; +}; + +// Struct type literals, such as `{.a: i32}`. +struct StructTypeLiteral { + static constexpr auto Kind = + NodeKind::StructTypeLiteral.Define(NodeCategory::Expr); + + StructLiteralOrStructTypeLiteralStartId introducer; + CommaSeparatedList fields; +}; + +// `class` declarations and definitions +// ------------------------------------ + +// `class` +using ClassIntroducer = LeafNode; + +// A class signature `class C` +template +struct ClassSignature { + static constexpr auto Kind = KindT.Define(Category); + + ClassIntroducerId introducer; + llvm::SmallVector modifiers; + AnyNameComponentId name; + std::optional implicit_params; + std::optional params; +}; + +// `class C;` +using ClassDecl = ClassSignature; +// `class C {` +using ClassDefinitionStart = + ClassSignature; + +// `class C { ... }` +struct ClassDefinition { + static constexpr auto Kind = + NodeKind::ClassDefinition.Define(NodeCategory::Decl); + + ClassDefinitionStartId signature; + llvm::SmallVector members; +}; + +// Base class declaration +// ---------------------- + +// `base` +using BaseIntroducer = LeafNode; +using BaseColon = LeafNode; +// `extend base: BaseClass;` +struct BaseDecl { + static constexpr auto Kind = NodeKind::BaseDecl.Define(NodeCategory::Decl); + + BaseIntroducerId introducer; + llvm::SmallVector modifiers; + BaseColonId colon; + AnyExprId base_class; +}; + +// Interface declarations and definitions +// -------------------------------------- + +// `interface` +using InterfaceIntroducer = LeafNode; + +// `interface I` +template +struct InterfaceSignature { + static constexpr auto Kind = KindT.Define(Category); + + InterfaceIntroducerId introducer; + llvm::SmallVector modifiers; + AnyNameComponentId name; + std::optional implicit_params; + std::optional params; +}; + +// `interface I;` +using InterfaceDecl = + InterfaceSignature; +// `interface I {` +using InterfaceDefinitionStart = + InterfaceSignature; + +// `interface I { ... }` +struct InterfaceDefinition { + static constexpr auto Kind = + NodeKind::InterfaceDefinition.Define(NodeCategory::Decl); + + InterfaceDefinitionStartId signature; + llvm::SmallVector members; +}; + +// `impl`...`as` declarations and definitions +// ------------------------------------------ + +// `impl` +using ImplIntroducer = LeafNode; +// `as` +using ImplAs = LeafNode; + +// `forall [...]` +struct ImplForall { + static constexpr auto Kind = NodeKind::ImplForall.Define(); + + ImplicitParamListId params; +}; + +// `impl T as I` +template +struct ImplSignature { + static constexpr auto Kind = KindT.Define(Category); + + ImplIntroducerId introducer; + llvm::SmallVector modifiers; + std::optional forall; + std::optional type_expr; + ImplAsId as; + AnyExprId interface; +}; + +// `impl T as I;` +using ImplDecl = ImplSignature; +// `impl T as I {` +using ImplDefinitionStart = + ImplSignature; + +// `impl T as I { ... }` +struct ImplDefinition { + static constexpr auto Kind = + NodeKind::ImplDefinition.Define(NodeCategory::Decl); + + ImplDefinitionStartId signature; + llvm::SmallVector members; +}; + +// Named constraint declarations and definitions +// --------------------------------------------- + +// `constraint` +using NamedConstraintIntroducer = LeafNode; + +// `constraint NC` +template +struct NamedConstraintSignature { + static constexpr auto Kind = KindT.Define(Category); + + NamedConstraintIntroducerId introducer; + llvm::SmallVector modifiers; + AnyNameComponentId name; + std::optional implicit_params; + std::optional params; +}; + +// `constraint NC;` +using NamedConstraintDecl = + NamedConstraintSignature; +// `constraint NC {` +using NamedConstraintDefinitionStart = + NamedConstraintSignature; + +// `constraint NC { ... }` +struct NamedConstraintDefinition { + static constexpr auto Kind = + NodeKind::NamedConstraintDefinition.Define(NodeCategory::Decl); + + NamedConstraintDefinitionStartId signature; + llvm::SmallVector members; +}; + +// --------------------------------------------------------------------------- + +// A complete source file. Note that there is no corresponding parse node for +// the file. The file is instead the complete contents of the parse tree. +struct File { + FileStartId start; + llvm::SmallVector decls; + FileEndId end; +}; + +// Define `Foo` as the node type for the ID type `FooId`. +#define CARBON_PARSE_NODE_KIND(KindName) \ + template <> \ + struct NodeForId { \ + using TypedNode = KindName; \ + }; +#include "toolchain/parse/node_kind.def" + +} // namespace Carbon::Parse + +#endif // CARBON_TOOLCHAIN_PARSE_TYPED_NODES_H_ diff --git a/toolchain/parse/typed_nodes_test.cpp b/toolchain/parse/typed_nodes_test.cpp new file mode 100644 index 000000000000..6094d0fb41c1 --- /dev/null +++ b/toolchain/parse/typed_nodes_test.cpp @@ -0,0 +1,152 @@ +// 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/parse/typed_nodes.h" + +#include +#include + +#include + +#include "toolchain/diagnostics/mocks.h" +#include "toolchain/lex/lex.h" +#include "toolchain/lex/tokenized_buffer.h" +#include "toolchain/parse/tree.h" + +namespace Carbon::Parse { +namespace { + +// Check that each node kind defines a Kind member using the correct +// NodeKind enumerator. +#define CARBON_PARSE_NODE_KIND(Name) \ + static_assert(Name::Kind == NodeKind::Name, #Name); +#include "toolchain/parse/node_kind.def" + +class TypedNodeTest : public ::testing::Test { + protected: + auto GetSourceBuffer(llvm::StringRef t) -> SourceBuffer& { + CARBON_CHECK(fs_.addFile("test.carbon", /*ModificationTime=*/0, + llvm::MemoryBuffer::getMemBuffer(t))); + source_storage_.push_front(std::move( + *SourceBuffer::CreateFromFile(fs_, "test.carbon", consumer_))); + return source_storage_.front(); + } + + auto GetTokenizedBuffer(llvm::StringRef t) -> Lex::TokenizedBuffer& { + token_storage_.push_front( + Lex::Lex(value_stores_, GetSourceBuffer(t), consumer_)); + return token_storage_.front(); + } + + auto GetTree(llvm::StringRef t) -> Tree& { + tree_storage_.push_front(Tree::Parse(GetTokenizedBuffer(t), consumer_, + /*vlog_stream=*/nullptr)); + return tree_storage_.front(); + } + + SharedValueStores value_stores_; + llvm::vfs::InMemoryFileSystem fs_; + std::forward_list source_storage_; + std::forward_list token_storage_; + std::forward_list tree_storage_; + DiagnosticConsumer& consumer_ = ConsoleDiagnosticConsumer(); +}; + +TEST_F(TypedNodeTest, Empty) { + auto* tree = &GetTree(""); + auto file = tree->ExtractFile(); + + EXPECT_TRUE(tree->IsValid(file.start)); + EXPECT_TRUE(tree->ExtractAs(file.start).has_value()); + EXPECT_TRUE(tree->Extract(file.start).has_value()); + + EXPECT_TRUE(tree->IsValid(file.end)); + EXPECT_TRUE(tree->ExtractAs(file.end).has_value()); + EXPECT_TRUE(tree->Extract(file.end).has_value()); + + EXPECT_FALSE(tree->IsValid(file.start)); + EXPECT_FALSE(tree->ExtractAs(file.start).has_value()); +} + +TEST_F(TypedNodeTest, Function) { + auto* tree = &GetTree(R"carbon( + fn F() {} + virtual fn G() -> i32; + )carbon"); + auto file = tree->ExtractFile(); + + ASSERT_EQ(file.decls.size(), 2); + + auto f_fn = tree->ExtractAs(file.decls[0]); + ASSERT_TRUE(f_fn.has_value()); + auto f_sig = tree->Extract(f_fn->signature); + ASSERT_TRUE(f_sig.has_value()); + EXPECT_FALSE(f_sig->return_type.has_value()); + EXPECT_TRUE(f_sig->modifiers.empty()); + + auto g_fn = tree->ExtractAs(file.decls[1]); + ASSERT_TRUE(g_fn.has_value()); + EXPECT_TRUE(g_fn->return_type.has_value()); + EXPECT_FALSE(g_fn->modifiers.empty()); +} + +TEST_F(TypedNodeTest, ModifierOrder) { + auto* tree = &GetTree(R"carbon( + private abstract virtual default interface I; + )carbon"); + auto file = tree->ExtractFile(); + + ASSERT_EQ(file.decls.size(), 1); + + auto decl = tree->ExtractAs(file.decls[0]); + ASSERT_TRUE(decl.has_value()); + ASSERT_EQ(decl->modifiers.size(), 4); + // Note that the order here matches the source order, but is reversed from + // sibling iteration order. + ASSERT_TRUE(tree->ExtractAs(decl->modifiers[0]).has_value()); + ASSERT_TRUE( + tree->ExtractAs(decl->modifiers[1]).has_value()); + ASSERT_TRUE(tree->ExtractAs(decl->modifiers[2]).has_value()); + ASSERT_TRUE(tree->ExtractAs(decl->modifiers[3]).has_value()); +} + +TEST_F(TypedNodeTest, For) { + auto* tree = &GetTree(R"carbon( + fn F(arr: [i32; 5]) { + for (var v: i32 in arr) { + Print(v); + } + } + )carbon"); + auto file = tree->ExtractFile(); + + ASSERT_EQ(file.decls.size(), 1); + auto fn = tree->ExtractAs(file.decls[0]); + ASSERT_TRUE(fn.has_value()); + ASSERT_EQ(fn->body.size(), 1); + auto for_stmt = tree->ExtractAs(fn->body[0]); + ASSERT_TRUE(for_stmt.has_value()); + auto for_header = tree->Extract(for_stmt->header); + ASSERT_TRUE(for_header.has_value()); + auto for_var = tree->Extract(for_header->var); + ASSERT_TRUE(for_var.has_value()); + auto for_var_binding = tree->ExtractAs(for_var->pattern); + ASSERT_TRUE(for_var_binding.has_value()); + auto for_var_name = tree->ExtractAs(for_var_binding->name); + ASSERT_TRUE(for_var_name.has_value()); +} + +auto CategoryMatches(const NodeKind::Definition& def, NodeKind kind, + const char* name) { + EXPECT_EQ(def.category(), kind.category()) << name; +} + +TEST_F(TypedNodeTest, CategoryMatches) { +#define CARBON_PARSE_NODE_KIND(Name) \ + CategoryMatches(Name::Kind, NodeKind::Name, #Name); +#include "toolchain/parse/node_kind.def" +} + +} // namespace +} // namespace Carbon::Parse diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index 6f20f75a866c..e005f2985922 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -27,7 +27,7 @@ struct TypedInstArgsInfo { using Tuple = decltype(StructReflection::AsTuple(std::declval())); static constexpr int FirstArgField = - HasParseNode + HasTypeId; + HasParseNodeMember + HasTypeIdMember; static constexpr int NumArgs = std::tuple_size_v - FirstArgField; static_assert(NumArgs <= 2, @@ -73,10 +73,10 @@ class Inst : public Printable { type_id_(TypeId::Invalid), arg0_(InstId::InvalidIndex), arg1_(InstId::InvalidIndex) { - if constexpr (HasParseNode) { + if constexpr (HasParseNodeMember) { parse_node_ = typed_inst.parse_node; } - if constexpr (HasTypeId) { + if constexpr (HasTypeIdMember) { type_id_ = typed_inst.type_id; } if constexpr (Info::NumArgs > 0) { @@ -100,7 +100,7 @@ class Inst : public Printable { CARBON_CHECK(Is()) << "Casting inst of kind " << kind() << " to wrong kind " << TypedInst::Kind; auto build_with_type_id_and_args = [&](auto... type_id_and_args) { - if constexpr (HasParseNode) { + if constexpr (HasParseNodeMember) { return TypedInst{parse_node(), type_id_and_args...}; } else { return TypedInst{type_id_and_args...}; @@ -108,7 +108,7 @@ class Inst : public Printable { }; auto build_with_args = [&](auto... args) { - if constexpr (HasTypeId) { + if constexpr (HasTypeIdMember) { return build_with_type_id_and_args(type_id(), args...); } else { return build_with_type_id_and_args(args...); diff --git a/toolchain/sem_ir/inst_kind.cpp b/toolchain/sem_ir/inst_kind.cpp index 8cd8b774c056..56193b27f945 100644 --- a/toolchain/sem_ir/inst_kind.cpp +++ b/toolchain/sem_ir/inst_kind.cpp @@ -20,7 +20,7 @@ auto InstKind::ir_name() const -> llvm::StringLiteral { auto InstKind::value_kind() const -> InstValueKind { static constexpr InstValueKind Table[] = { #define CARBON_SEM_IR_INST_KIND(Name) \ - HasTypeId ? InstValueKind::Typed : InstValueKind::None, + HasTypeIdMember ? InstValueKind::Typed : InstValueKind::None, #include "toolchain/sem_ir/inst_kind.def" }; return Table[AsInt()]; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index c56de856f68b..76d0b89c700c 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -616,17 +616,17 @@ struct VarStorage { NameId name_id; }; -// HasParseNode is true if T has a `Parse::NodeId parse_node` field. +// HasParseNodeMember is true if T has a `Parse::NodeId parse_node` field. template -inline constexpr bool HasParseNode = false; +inline constexpr bool HasParseNodeMember = false; template -inline constexpr bool HasParseNode = true; +inline constexpr bool HasParseNodeMember = true; -// HasTypeId is true if T has a `TypeId type_id` field. +// HasTypeIdMember is true if T has a `TypeId type_id` field. template -inline constexpr bool HasTypeId = false; +inline constexpr bool HasTypeIdMember = false; template -inline constexpr bool HasTypeId = true; +inline constexpr bool HasTypeIdMember = true; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/typed_insts_test.cpp b/toolchain/sem_ir/typed_insts_test.cpp index f1673431108b..df83c74dbd95 100644 --- a/toolchain/sem_ir/typed_insts_test.cpp +++ b/toolchain/sem_ir/typed_insts_test.cpp @@ -43,10 +43,10 @@ template auto CommonFieldOrder() -> void { Inst inst = MakeInstWithNumberedFields(TypedInst::Kind); auto typed = inst.As(); - if constexpr (HasParseNode) { + if constexpr (HasParseNodeMember) { EXPECT_EQ(typed.parse_node, Parse::NodeId(1)); } - if constexpr (HasTypeId) { + if constexpr (HasTypeIdMember) { EXPECT_EQ(typed.type_id, TypeId(2)); } } @@ -77,7 +77,8 @@ auto RoundTrip() -> void { auto typed1 = inst1.As(); Inst inst2 = typed1; - ExpectEqInsts(inst1, inst2, HasParseNode, HasTypeId); + ExpectEqInsts(inst1, inst2, HasParseNodeMember, + HasTypeIdMember); // If the typed instruction has no padding, we should get exactly the same // thing if we convert back from an instruction. @@ -129,8 +130,8 @@ auto StructLayout() -> void { if constexpr (std::has_unique_object_representations_v) { auto typed = MakeInstWithNumberedFields(TypedInst::Kind).template As(); - StructLayoutHelper(&typed, sizeof(typed), HasParseNode, - HasTypeId); + StructLayoutHelper(&typed, sizeof(typed), HasParseNodeMember, + HasTypeIdMember); } }