diff --git a/common/enum_base.h b/common/enum_base.h index 3ffbaa5bfdc4..d36d9eadc440 100644 --- a/common/enum_base.h +++ b/common/enum_base.h @@ -53,12 +53,11 @@ namespace Carbon::Internal { // ``` template class EnumBase { - protected: + public: // An alias for the raw enum type. This is an implementation detail and - // shouldn't be used, but we need it for a signature so it is declared early. + // should rarely be used directly, only when an actual enum type is needed. using RawEnumType = EnumT; - public: using EnumType = DerivedT; using UnderlyingType = std::underlying_type_t; @@ -92,7 +91,8 @@ class EnumBase { // the base itself. This should only be used in the `Create` function below. constexpr EnumBase() = default; - // Create an instance from the raw enumerator, for internal use. + // Create an instance from the raw enumerator. Mainly used internally, but may + // be exposed for unusual use cases. static constexpr auto Create(RawEnumType value) -> EnumType { EnumType result; result.value_ = value; diff --git a/toolchain/parser/parse_node_kind.h b/toolchain/parser/parse_node_kind.h index 90e52d681582..8b042c168e60 100644 --- a/toolchain/parser/parse_node_kind.h +++ b/toolchain/parser/parse_node_kind.h @@ -33,6 +33,8 @@ class ParseNodeKind : public CARBON_ENUM_BASE(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; + + using EnumBase::Create; }; #define CARBON_PARSE_NODE_KIND(Name) \ diff --git a/toolchain/semantics/semantics_context.cpp b/toolchain/semantics/semantics_context.cpp index 38e3b5319ea6..25a7da23a132 100644 --- a/toolchain/semantics/semantics_context.cpp +++ b/toolchain/semantics/semantics_context.cpp @@ -307,7 +307,8 @@ auto SemanticsContext::PopDeclarationName() -> DeclarationNameContext { ParseNodeKind::QualifiedDeclaration) { // Any parts from a QualifiedDeclaration will already have been processed // into the name. - node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::QualifiedDeclaration); + node_stack_ + .PopAndDiscardSoloParseNode(); } else { // The name had no qualifiers, so we need to process the node now. auto [parse_node, node_or_name_id] = diff --git a/toolchain/semantics/semantics_handle_call_expression.cpp b/toolchain/semantics/semantics_handle_call_expression.cpp index 5efe59d361ea..b1bb43844f58 100644 --- a/toolchain/semantics/semantics_handle_call_expression.cpp +++ b/toolchain/semantics/semantics_handle_call_expression.cpp @@ -13,8 +13,8 @@ auto SemanticsHandleCallExpression(SemanticsContext& context, // TODO: Convert to call expression. auto [call_expr_parse_node, name_id] = - context.node_stack().PopWithParseNode( - ParseNodeKind::CallExpressionStart); + context.node_stack() + .PopWithParseNode(); auto name_node = context.semantics_ir().GetNode(name_id); if (name_node.kind() != SemanticsNodeKind::FunctionDeclaration) { // TODO: Work on error. diff --git a/toolchain/semantics/semantics_handle_codeblock.cpp b/toolchain/semantics/semantics_handle_codeblock.cpp index c9eea8405021..19e137896473 100644 --- a/toolchain/semantics/semantics_handle_codeblock.cpp +++ b/toolchain/semantics/semantics_handle_codeblock.cpp @@ -17,7 +17,7 @@ auto SemanticsHandleCodeBlockStart(SemanticsContext& context, auto SemanticsHandleCodeBlock(SemanticsContext& context, ParseTree::Node /*parse_node*/) -> bool { context.PopScope(); - context.node_stack().PopForSoloParseNode(ParseNodeKind::CodeBlockStart); + context.node_stack().PopForSoloParseNode(); return true; } diff --git a/toolchain/semantics/semantics_handle_function.cpp b/toolchain/semantics/semantics_handle_function.cpp index fda64ee3acdf..bacb205f0baa 100644 --- a/toolchain/semantics/semantics_handle_function.cpp +++ b/toolchain/semantics/semantics_handle_function.cpp @@ -14,17 +14,16 @@ static auto BuildFunctionDeclaration(SemanticsContext& context) SemanticsTypeId return_type_id = SemanticsTypeId::Invalid; if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == ParseNodeKind::ReturnType) { - return_type_id = - context.node_stack().Pop(ParseNodeKind::ReturnType); + return_type_id = context.node_stack().Pop(); } else { // Canonicalize the empty tuple for the implicit return. context.CanonicalizeType(SemanticsNodeId::BuiltinEmptyTupleType); } - auto param_refs_id = context.node_stack().Pop( - ParseNodeKind::ParameterList); + SemanticsNodeBlockId param_refs_id = + context.node_stack().Pop(); auto name_context = context.PopDeclarationName(); - auto fn_node = context.node_stack().PopForSoloParseNode( - ParseNodeKind::FunctionIntroducer); + auto fn_node = context.node_stack() + .PopForSoloParseNode(); // TODO: Support out-of-line definitions, which will have a resolved // name_context. Right now, those become errors in AddNameToLookup. @@ -50,8 +49,8 @@ auto SemanticsHandleFunctionDeclaration(SemanticsContext& context, auto SemanticsHandleFunctionDefinition(SemanticsContext& context, ParseTree::Node parse_node) -> bool { - auto function_id = context.node_stack().Pop( - ParseNodeKind::FunctionDefinitionStart); + SemanticsFunctionId function_id = + context.node_stack().Pop(); // If the `}` of the function is reachable, reject if we need a return value // and otherwise add an implicit `return;`. diff --git a/toolchain/semantics/semantics_handle_if_expression.cpp b/toolchain/semantics/semantics_handle_if_expression.cpp index 25cb8ad8eb8b..521bd1f5638b 100644 --- a/toolchain/semantics/semantics_handle_if_expression.cpp +++ b/toolchain/semantics/semantics_handle_if_expression.cpp @@ -37,11 +37,10 @@ auto SemanticsHandleIfExpressionElse(SemanticsContext& context, ParseTree::Node else_node) -> bool { auto else_value_id = context.node_stack().Pop(); auto [then_node, then_end_block_id] = - context.node_stack().PopWithParseNode( - ParseNodeKind::IfExpressionThen); + context.node_stack().PopWithParseNode(); auto then_value_id = context.node_stack().Pop(); auto if_node = - context.node_stack().PopForSoloParseNode(ParseNodeKind::IfExpressionIf); + context.node_stack().PopForSoloParseNode(); // Convert the `else` value to the `then` value's type, and finish the `else` // block. @@ -58,7 +57,7 @@ auto SemanticsHandleIfExpressionElse(SemanticsContext& context, context.AddCurrentCodeBlockToFunction(); // Push the result value. - context.node_stack().Push(if_node, chosen_value_id); + context.node_stack().Push(else_node, chosen_value_id); return true; } diff --git a/toolchain/semantics/semantics_handle_if_statement.cpp b/toolchain/semantics/semantics_handle_if_statement.cpp index 0cb8d938dec1..2a300c2cd78e 100644 --- a/toolchain/semantics/semantics_handle_if_statement.cpp +++ b/toolchain/semantics/semantics_handle_if_statement.cpp @@ -37,7 +37,7 @@ auto SemanticsHandleIfCondition(SemanticsContext& context, auto SemanticsHandleIfStatementElse(SemanticsContext& context, ParseTree::Node parse_node) -> bool { - context.node_stack().PopAndDiscardSoloParseNode(ParseNodeKind::IfCondition); + context.node_stack().PopAndDiscardSoloParseNode(); // Switch to emitting the else block. auto then_block_id = context.node_block_stack().PopForAdd(); @@ -56,8 +56,8 @@ auto SemanticsHandleIfStatement(SemanticsContext& context, context.node_stack().PeekParseNode())) { case ParseNodeKind::IfCondition: { // Branch from then block to else block. - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::IfCondition); + context.node_stack() + .PopAndDiscardSoloParseNode(); context.AddNodeToBlock( sub_block_id, SemanticsNode::Branch::Make(parse_node, @@ -67,8 +67,8 @@ auto SemanticsHandleIfStatement(SemanticsContext& context, case ParseNodeKind::IfStatementElse: { // Branch from the then and else blocks to a new resumption block. - auto then_block_id = context.node_stack().Pop( - ParseNodeKind::IfStatementElse); + SemanticsNodeBlockId then_block_id = + context.node_stack().Pop(); context.AddConvergenceBlockAndPush(parse_node, {then_block_id, sub_block_id}); break; diff --git a/toolchain/semantics/semantics_handle_name.cpp b/toolchain/semantics/semantics_handle_name.cpp index f0e83bf2aae7..ccbee0aedcb5 100644 --- a/toolchain/semantics/semantics_handle_name.cpp +++ b/toolchain/semantics/semantics_handle_name.cpp @@ -9,8 +9,7 @@ namespace Carbon { auto SemanticsHandleMemberAccessExpression(SemanticsContext& context, ParseTree::Node parse_node) -> bool { - auto name_id = - context.node_stack().Pop(ParseNodeKind::Name); + SemanticsStringId name_id = context.node_stack().Pop(); auto base_id = context.node_stack().Pop(); auto base = context.semantics_ir().GetNode(base_id); diff --git a/toolchain/semantics/semantics_handle_parameter_list.cpp b/toolchain/semantics/semantics_handle_parameter_list.cpp index 91256ff347cc..b97b7ff82697 100644 --- a/toolchain/semantics/semantics_handle_parameter_list.cpp +++ b/toolchain/semantics/semantics_handle_parameter_list.cpp @@ -30,8 +30,8 @@ auto SemanticsHandleParameterList(SemanticsContext& context, context.node_block_stack().Pop(); context.PopScope(); - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::ParameterListStart); + context.node_stack() + .PopAndDiscardSoloParseNode(); context.node_stack().Push(parse_node, refs_id); return true; } diff --git a/toolchain/semantics/semantics_handle_paren.cpp b/toolchain/semantics/semantics_handle_paren.cpp index 3e31402e7be1..602fe46d3afe 100644 --- a/toolchain/semantics/semantics_handle_paren.cpp +++ b/toolchain/semantics/semantics_handle_paren.cpp @@ -9,8 +9,9 @@ namespace Carbon { auto SemanticsHandleParenExpression(SemanticsContext& context, ParseTree::Node parse_node) -> bool { auto value_id = context.node_stack().Pop(); - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::ParenExpressionOrTupleLiteralStart); + context.node_stack() + .PopAndDiscardSoloParseNode< + ParseNodeKind::ParenExpressionOrTupleLiteralStart>(); context.node_stack().Push(parse_node, value_id); return true; } diff --git a/toolchain/semantics/semantics_handle_pattern_binding.cpp b/toolchain/semantics/semantics_handle_pattern_binding.cpp index 7e6390374e69..e72845553482 100644 --- a/toolchain/semantics/semantics_handle_pattern_binding.cpp +++ b/toolchain/semantics/semantics_handle_pattern_binding.cpp @@ -25,8 +25,7 @@ auto SemanticsHandlePatternBinding(SemanticsContext& context, // Get the name. auto [name_node, name_id] = - context.node_stack().PopWithParseNode( - ParseNodeKind::Name); + context.node_stack().PopWithParseNode(); // Allocate storage, linked to the name for error locations. auto storage_id = diff --git a/toolchain/semantics/semantics_handle_statement.cpp b/toolchain/semantics/semantics_handle_statement.cpp index 2811e7680122..c84c58353db2 100644 --- a/toolchain/semantics/semantics_handle_statement.cpp +++ b/toolchain/semantics/semantics_handle_statement.cpp @@ -27,8 +27,8 @@ auto SemanticsHandleReturnStatement(SemanticsContext& context, if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == ParseNodeKind::ReturnStatementStart) { - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::ReturnStatementStart); + context.node_stack() + .PopAndDiscardSoloParseNode(); if (callable.return_type_id.is_valid()) { // TODO: Add a note pointing at the return type's parse node. @@ -43,8 +43,8 @@ auto SemanticsHandleReturnStatement(SemanticsContext& context, context.AddNode(SemanticsNode::Return::Make(parse_node)); } else { auto arg = context.node_stack().Pop(); - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::ReturnStatementStart); + context.node_stack() + .PopAndDiscardSoloParseNode(); if (!callable.return_type_id.is_valid()) { CARBON_DIAGNOSTIC( diff --git a/toolchain/semantics/semantics_handle_struct.cpp b/toolchain/semantics/semantics_handle_struct.cpp index 9a253ae9d043..ea08616a9787 100644 --- a/toolchain/semantics/semantics_handle_struct.cpp +++ b/toolchain/semantics/semantics_handle_struct.cpp @@ -32,8 +32,7 @@ auto SemanticsHandleStructFieldType(SemanticsContext& context, SemanticsTypeId cast_type_id = context.ExpressionAsType(type_node, type_id); auto [name_node, name_id] = - context.node_stack().PopWithParseNode( - ParseNodeKind::Name); + context.node_stack().PopWithParseNode(); context.AddNode( SemanticsNode::StructTypeField::Make(name_node, cast_type_id, name_id)); @@ -50,8 +49,7 @@ auto SemanticsHandleStructFieldValue(SemanticsContext& context, ParseTree::Node parse_node) -> bool { auto [value_parse_node, value_node_id] = context.node_stack().PopWithParseNode(); - auto name_id = - context.node_stack().Pop(ParseNodeKind::Name); + SemanticsStringId name_id = context.node_stack().Pop(); // Store the name for the type. auto type_block_id = context.args_type_info_stack().PeekForAdd(); @@ -72,8 +70,9 @@ auto SemanticsHandleStructLiteral(SemanticsContext& context, /*for_args=*/true, ParseNodeKind::StructLiteralOrStructTypeLiteralStart); context.PopScope(); - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::StructLiteralOrStructTypeLiteralStart); + context.node_stack() + .PopAndDiscardSoloParseNode< + ParseNodeKind::StructLiteralOrStructTypeLiteralStart>(); auto type_block_id = context.args_type_info_stack().Pop(); auto type_id = context.CanonicalizeStructType(parse_node, type_block_id); @@ -102,8 +101,9 @@ auto SemanticsHandleStructTypeLiteral(SemanticsContext& context, /*for_args=*/false, ParseNodeKind::StructLiteralOrStructTypeLiteralStart); context.PopScope(); - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::StructLiteralOrStructTypeLiteralStart); + context.node_stack() + .PopAndDiscardSoloParseNode< + ParseNodeKind::StructLiteralOrStructTypeLiteralStart>(); // This is only used for value literals. context.args_type_info_stack().Pop(); diff --git a/toolchain/semantics/semantics_handle_variable.cpp b/toolchain/semantics/semantics_handle_variable.cpp index 87c2409685f7..4d380a2403bc 100644 --- a/toolchain/semantics/semantics_handle_variable.cpp +++ b/toolchain/semantics/semantics_handle_variable.cpp @@ -16,13 +16,13 @@ auto SemanticsHandleVariableDeclaration(SemanticsContext& context, ParseNodeKind::PatternBinding; if (has_init) { expr_node_id = context.node_stack().Pop(); - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::VariableInitializer); + context.node_stack() + .PopAndDiscardSoloParseNode(); } // Get the storage and add it to name lookup. - auto binding_id = - context.node_stack().Pop(ParseNodeKind::PatternBinding); + SemanticsNodeId binding_id = + context.node_stack().Pop(); auto binding = context.semantics_ir().GetNode(binding_id); auto [name_id, storage_id] = binding.GetAsBindName(); context.AddNameToLookup(binding.parse_node(), name_id, storage_id); @@ -37,8 +37,8 @@ auto SemanticsHandleVariableDeclaration(SemanticsContext& context, storage_id, cast_value_id)); } - context.node_stack().PopAndDiscardSoloParseNode( - ParseNodeKind::VariableIntroducer); + context.node_stack() + .PopAndDiscardSoloParseNode(); return true; } diff --git a/toolchain/semantics/semantics_node_stack.h b/toolchain/semantics/semantics_node_stack.h index a8d96250d8f5..1995efceebd4 100644 --- a/toolchain/semantics/semantics_node_stack.h +++ b/toolchain/semantics/semantics_node_stack.h @@ -37,6 +37,9 @@ class SemanticsNodeStack { // Pushes a solo parse tree node onto the stack. Used when there is no // IR generated by the node. auto Push(ParseTree::Node parse_node) -> void { + CARBON_CHECK(ParseNodeKindToIdKind(parse_tree_->node_kind(parse_node)) == + IdKind::Unused) + << "Parse kind expects an Id: " << parse_tree_->node_kind(parse_node); CARBON_VLOG() << "Node Push " << stack_.size() << ": " << parse_tree_->node_kind(parse_node) << " -> \n"; CARBON_CHECK(stack_.size() < (1 << 20)) @@ -47,6 +50,12 @@ class SemanticsNodeStack { // Pushes a parse tree node onto the stack with an ID. template auto Push(ParseTree::Node parse_node, IdT id) -> void { + CARBON_CHECK(ParseNodeKindToIdKind(parse_tree_->node_kind(parse_node)) == + IdTypeToIdKind()) + << "Parse kind expected a different IdT: " + << parse_tree_->node_kind(parse_node) << " -> " << id << "\n"; + CARBON_CHECK(id.is_valid()) << "Push called with invalid id: " + << parse_tree_->node_kind(parse_node); CARBON_VLOG() << "Node Push " << stack_.size() << ": " << parse_tree_->node_kind(parse_node) << " -> " << id << "\n"; CARBON_CHECK(stack_.size() < (1 << 20)) @@ -65,15 +74,17 @@ class SemanticsNodeStack { } // Pops the top of the stack and returns the parse_node. - auto PopForSoloParseNode(ParseNodeKind pop_parse_kind) -> ParseTree::Node { + template + auto PopForSoloParseNode() -> ParseTree::Node { auto parse_node = PopForSoloParseNode(); - RequireParseKind(parse_node, pop_parse_kind); + RequireParseKind(parse_node, ParseNodeKind::Create(PopParseKind)); return parse_node; } // Pops the top of the stack. - auto PopAndDiscardSoloParseNode(ParseNodeKind pop_parse_kind) -> void { - PopForSoloParseNode(pop_parse_kind); + template + auto PopAndDiscardSoloParseNode() -> void { + PopForSoloParseNode(); } // Pops the top of the stack and returns the parse_node and the ID. @@ -85,12 +96,38 @@ class SemanticsNodeStack { } // Pops the top of the stack and returns the parse_node and the ID. - template - auto PopWithParseNode(ParseNodeKind pop_parse_kind) - -> std::pair { - auto back = PopWithParseNode(); - RequireParseKind(back.first, pop_parse_kind); - return back; + template + auto PopWithParseNode() -> auto { + if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) == + IdKind::SemanticsNodeId) { + auto back = PopWithParseNode(); + RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind)); + return back; + } + if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) == + IdKind::SemanticsNodeBlockId) { + auto back = PopWithParseNode(); + RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind)); + return back; + } + if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) == + IdKind::SemanticsFunctionId) { + auto back = PopWithParseNode(); + RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind)); + return back; + } + if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) == + IdKind::SemanticsStringId) { + auto back = PopWithParseNode(); + RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind)); + return back; + } + if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) == + IdKind::SemanticsTypeId) { + auto back = PopWithParseNode(); + RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind)); + return back; + } } // Pops the top of the stack and returns the ID. @@ -100,17 +137,18 @@ class SemanticsNodeStack { } // Pops the top of the stack and returns the ID. - template - auto Pop(ParseNodeKind pop_parse_kind) -> IdT { - return PopWithParseNode(pop_parse_kind).second; + template + auto Pop() -> auto { + return PopWithParseNode().second; } // Pops the top of the stack, and discards the ID. auto PopAndDiscardId() -> void { PopWithParseNode(); } // Pops the top of the stack, and discards the ID. - auto PopAndDiscardId(ParseNodeKind pop_parse_kind) -> void { - PopWithParseNode(pop_parse_kind); + template + auto PopAndDiscardId() -> void { + PopWithParseNode(); } // Peeks at the parse_node of the top of the stack. @@ -132,6 +170,15 @@ class SemanticsNodeStack { auto size() const -> size_t { return stack_.size(); } private: + enum class IdKind { + SemanticsNodeId, + SemanticsNodeBlockId, + SemanticsFunctionId, + SemanticsStringId, + SemanticsTypeId, + Unused, + }; + // An entry in stack_. struct Entry { explicit Entry(ParseTree::Node parse_node, SemanticsNodeId node_id) @@ -199,6 +246,60 @@ class SemanticsNodeStack { }; static_assert(sizeof(Entry) == 8, "Unexpected Entry size"); + // Translate a parse node kind to the enum ID kind it should always provide. + static constexpr auto ParseNodeKindToIdKind(ParseNodeKind kind) -> IdKind { + switch (kind) { + case Carbon::ParseNodeKind::CallExpression: + case Carbon::ParseNodeKind::CallExpressionStart: + case Carbon::ParseNodeKind::IfExpressionElse: + case Carbon::ParseNodeKind::InfixOperator: + case Carbon::ParseNodeKind::Literal: + case Carbon::ParseNodeKind::MemberAccessExpression: + case Carbon::ParseNodeKind::NameExpression: + case Carbon::ParseNodeKind::ParenExpression: + case Carbon::ParseNodeKind::PatternBinding: + case Carbon::ParseNodeKind::PrefixOperator: + case Carbon::ParseNodeKind::ShortCircuitOperand: + case Carbon::ParseNodeKind::StructFieldValue: + case Carbon::ParseNodeKind::StructLiteral: + case Carbon::ParseNodeKind::StructTypeLiteral: + return IdKind::SemanticsNodeId; + case Carbon::ParseNodeKind::IfExpressionThen: + case Carbon::ParseNodeKind::IfStatementElse: + case Carbon::ParseNodeKind::ParameterList: + return IdKind::SemanticsNodeBlockId; + case Carbon::ParseNodeKind::FunctionDefinitionStart: + return IdKind::SemanticsFunctionId; + case Carbon::ParseNodeKind::Name: + return IdKind::SemanticsStringId; + case Carbon::ParseNodeKind::ReturnType: + return IdKind::SemanticsTypeId; + default: + return IdKind::Unused; + } + } + + // Translates an ID type to the enum ID kind for comparison with + // ParseNodeKindToIdKind. + template + static constexpr auto IdTypeToIdKind() -> IdKind { + if constexpr (std::is_same_v) { + return IdKind::SemanticsNodeId; + } + if constexpr (std::is_same_v) { + return IdKind::SemanticsNodeBlockId; + } + if constexpr (std::is_same_v) { + return IdKind::SemanticsFunctionId; + } + if constexpr (std::is_same_v) { + return IdKind::SemanticsStringId; + } + if constexpr (std::is_same_v) { + return IdKind::SemanticsTypeId; + } + } + // Pops an entry. template auto PopEntry() -> Entry {