Finish refactoring Push/Pop for stronger type handling. (#2987)

This adds a distinction between Unused and SoloParseNode, rather than
equating the two. This is intended to help identify nodes which are
getting pushed but maybe don't need to be.

Not totally done because I want to adjust declaration name handling due
to a quirk with how it mixes Name with Expression, but almost done. Once
that's done the type punning will be completely gone.
This commit is contained in:
Jon Ross-Perkins
2023-07-14 23:16:16 +00:00
committed by GitHub
parent 6a2b9684fb
commit 43065a1257
13 changed files with 122 additions and 112 deletions
+100 -89
View File
@@ -38,7 +38,7 @@ class SemanticsNodeStack {
// IR generated by the node.
auto Push(ParseTree::Node parse_node) -> void {
CARBON_CHECK(ParseNodeKindToIdKind(parse_tree_->node_kind(parse_node)) ==
IdKind::Unused)
IdKind::SoloParseNode)
<< "Parse kind expects an Id: " << parse_tree_->node_kind(parse_node);
CARBON_VLOG() << "Node Push " << stack_.size() << ": "
<< parse_tree_->node_kind(parse_node) << " -> <none>\n";
@@ -67,100 +67,102 @@ class SemanticsNodeStack {
auto PopAndIgnore() -> void { PopEntry<SemanticsNodeId>(); }
// Pops the top of the stack and returns the parse_node.
template <ParseNodeKind::RawEnumType RequiredParseKind>
auto PopForSoloParseNode() -> ParseTree::Node {
Entry back = PopEntry<SemanticsNodeId>();
RequireSoloParseNode(back);
RequireIdKind(ParseNodeKind::Create(RequiredParseKind),
IdKind::SoloParseNode);
RequireParseKind<RequiredParseKind>(back.parse_node);
return back.parse_node;
}
// Pops the top of the stack and returns the parse_node.
template <ParseNodeKind::RawEnumType PopParseKind>
auto PopForSoloParseNode() -> ParseTree::Node {
auto parse_node = PopForSoloParseNode();
RequireParseKind(parse_node, ParseNodeKind::Create(PopParseKind));
return parse_node;
}
// Pops the top of the stack.
template <ParseNodeKind::RawEnumType PopParseKind>
template <ParseNodeKind::RawEnumType RequiredParseKind>
auto PopAndDiscardSoloParseNode() -> void {
PopForSoloParseNode<PopParseKind>();
PopForSoloParseNode<RequiredParseKind>();
}
// Pops the top of the stack and returns the parse_node and the ID.
template <typename IdT>
auto PopWithParseNode() -> std::pair<ParseTree::Node, IdT> {
Entry back = PopEntry<IdT>();
RequireValidId(back);
return {back.parse_node, back.id<IdT>()};
auto PopExpressionWithParseNode()
-> std::pair<ParseTree::Node, SemanticsNodeId> {
return PopWithParseNode<SemanticsNodeId>();
}
// Pops the top of the stack and returns the parse_node and the ID.
template <ParseNodeKind::RawEnumType PopParseKind>
template <ParseNodeKind::RawEnumType RequiredParseKind>
auto PopWithParseNode() -> auto {
if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) ==
IdKind::SemanticsNodeId) {
constexpr IdKind RequiredIdKind =
ParseNodeKindToIdKind(ParseNodeKind::Create(RequiredParseKind));
if constexpr (RequiredIdKind == IdKind::SemanticsNodeId) {
auto back = PopWithParseNode<SemanticsNodeId>();
RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind));
RequireParseKind<RequiredParseKind>(back.first);
return back;
}
if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) ==
IdKind::SemanticsNodeBlockId) {
if constexpr (RequiredIdKind == IdKind::SemanticsNodeBlockId) {
auto back = PopWithParseNode<SemanticsNodeBlockId>();
RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind));
RequireParseKind<RequiredParseKind>(back.first);
return back;
}
if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) ==
IdKind::SemanticsFunctionId) {
if constexpr (RequiredIdKind == IdKind::SemanticsFunctionId) {
auto back = PopWithParseNode<SemanticsFunctionId>();
RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind));
RequireParseKind<RequiredParseKind>(back.first);
return back;
}
if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) ==
IdKind::SemanticsStringId) {
if constexpr (RequiredIdKind == IdKind::SemanticsStringId) {
auto back = PopWithParseNode<SemanticsStringId>();
RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind));
RequireParseKind<RequiredParseKind>(back.first);
return back;
}
if constexpr (ParseNodeKindToIdKind(ParseNodeKind::Create(PopParseKind)) ==
IdKind::SemanticsTypeId) {
if constexpr (RequiredIdKind == IdKind::SemanticsTypeId) {
auto back = PopWithParseNode<SemanticsTypeId>();
RequireParseKind(back.first, ParseNodeKind::Create(PopParseKind));
RequireParseKind<RequiredParseKind>(back.first);
return back;
}
CARBON_FATAL() << "Unpoppable IdKind for parse kind: "
<< ParseNodeKind::Create(RequiredParseKind)
<< "; see value in ParseNodeKindToIdKind";
}
// Pops an expression from the top of the stack and returns the ID.
// Expressions map multiple ParseNodeKinds to SemanticsNodeId always.
auto PopExpression() -> SemanticsNodeId {
return PopExpressionWithParseNode().second;
}
// Pops the top of the stack and returns the ID.
template <typename IdT>
auto Pop() -> IdT {
return PopWithParseNode<IdT>().second;
}
// Pops the top of the stack and returns the ID.
template <ParseNodeKind::RawEnumType PopParseKind>
template <ParseNodeKind::RawEnumType RequiredParseKind>
auto Pop() -> auto {
return PopWithParseNode<PopParseKind>().second;
}
// Pops the top of the stack, and discards the ID.
auto PopAndDiscardId() -> void { PopWithParseNode<SemanticsNodeId>(); }
// Pops the top of the stack, and discards the ID.
template <ParseNodeKind::RawEnumType PopParseKind>
auto PopAndDiscardId() -> void {
PopWithParseNode<PopParseKind>();
return PopWithParseNode<RequiredParseKind>().second;
}
// Peeks at the parse_node of the top of the stack.
auto PeekParseNode() -> ParseTree::Node { return stack_.back().parse_node; }
// Peeks at the ID of the top of the stack.
template <typename IdT>
auto Peek(ParseNodeKind parse_kind) -> IdT {
template <ParseNodeKind::RawEnumType RequiredParseKind>
auto Peek() -> auto {
Entry back = stack_.back();
RequireParseKind(back.parse_node, parse_kind);
RequireValidId(back);
return back.id<IdT>();
RequireParseKind<RequiredParseKind>(back.parse_node);
constexpr IdKind RequiredIdKind =
ParseNodeKindToIdKind(ParseNodeKind::Create(RequiredParseKind));
if constexpr (RequiredIdKind == IdKind::SemanticsNodeId) {
return back.id<SemanticsNodeId>();
}
if constexpr (RequiredIdKind == IdKind::SemanticsNodeBlockId) {
return back.id<SemanticsNodeBlockId>();
}
if constexpr (RequiredIdKind == IdKind::SemanticsFunctionId) {
return back.id<SemanticsFunctionId>();
}
if constexpr (RequiredIdKind == IdKind::SemanticsStringId) {
return back.id<SemanticsStringId>();
}
if constexpr (RequiredIdKind == IdKind::SemanticsTypeId) {
return back.id<SemanticsTypeId>();
}
CARBON_FATAL() << "Unpeekable IdKind for parse kind: "
<< ParseNodeKind::Create(RequiredParseKind)
<< "; see value in ParseNodeKindToIdKind";
}
// Prints the stack for a stack dump.
@@ -170,12 +172,16 @@ class SemanticsNodeStack {
auto size() const -> size_t { return stack_.size(); }
private:
// Possible associated ID types.
enum class IdKind {
SemanticsNodeId,
SemanticsNodeBlockId,
SemanticsFunctionId,
SemanticsStringId,
SemanticsTypeId,
// No associated ID type.
SoloParseNode,
// Not expected in the node stack.
Unused,
};
@@ -228,21 +234,6 @@ class SemanticsNodeStack {
SemanticsStringId name_id;
SemanticsTypeId type_id;
};
// APIs rely on type punning. They read node_id.is_valid, even though that
// may not be the active union member. These asserts enforce standard layout
// in order to help ensure that works.
// TODO: Use is_layout_compatible in C++20.
static_assert(std::is_standard_layout_v<SemanticsNodeId>,
"Need standard layout for type punning");
static_assert(std::is_standard_layout_v<SemanticsNodeBlockId>,
"Need standard layout for type punning");
static_assert(std::is_standard_layout_v<SemanticsFunctionId>,
"Need standard layout for type punning");
static_assert(std::is_standard_layout_v<SemanticsStringId>,
"Need standard layout for type punning");
static_assert(std::is_standard_layout_v<SemanticsTypeId>,
"Need standard layout for type punning");
};
static_assert(sizeof(Entry) == 8, "Unexpected Entry size");
@@ -274,6 +265,19 @@ class SemanticsNodeStack {
return IdKind::SemanticsStringId;
case Carbon::ParseNodeKind::ReturnType:
return IdKind::SemanticsTypeId;
case Carbon::ParseNodeKind::CodeBlockStart:
case Carbon::ParseNodeKind::FunctionIntroducer:
case Carbon::ParseNodeKind::IfCondition:
case Carbon::ParseNodeKind::IfExpressionIf:
case Carbon::ParseNodeKind::ParameterListStart:
case Carbon::ParseNodeKind::ParenExpressionOrTupleLiteralStart:
case Carbon::ParseNodeKind::QualifiedDeclaration:
case Carbon::ParseNodeKind::ReturnStatementStart:
case Carbon::ParseNodeKind::StructFieldType:
case Carbon::ParseNodeKind::StructLiteralOrStructTypeLiteralStart:
case Carbon::ParseNodeKind::VariableInitializer:
case Carbon::ParseNodeKind::VariableIntroducer:
return IdKind::SoloParseNode;
default:
return IdKind::Unused;
}
@@ -310,27 +314,34 @@ class SemanticsNodeStack {
return back;
}
// Pops the top of the stack and returns the parse_node and the ID.
template <typename IdT>
auto PopWithParseNode() -> std::pair<ParseTree::Node, IdT> {
Entry back = PopEntry<IdT>();
RequireIdKind(parse_tree_->node_kind(back.parse_node),
IdTypeToIdKind<IdT>());
return {back.parse_node, back.id<IdT>()};
}
// Require a ParseNodeKind be mapped to a particular IdKind.
auto RequireIdKind(ParseNodeKind parse_kind, IdKind id_kind) -> void {
// TODO: Name can be popped as a node_id by declaration name handling. Will
// refactor to remove this quirk.
if (parse_kind == ParseNodeKind::Name &&
id_kind == IdKind::SemanticsNodeId) {
return;
}
CARBON_CHECK(ParseNodeKindToIdKind(parse_kind) == id_kind)
<< "Unexpected IdKind mapping for " << parse_kind;
}
// Require an entry to have the given ParseNodeKind.
auto RequireParseKind(ParseTree::Node parse_node, ParseNodeKind require_kind)
-> void {
template <ParseNodeKind::RawEnumType RequiredParseKind>
auto RequireParseKind(ParseTree::Node parse_node) -> void {
auto actual_kind = parse_tree_->node_kind(parse_node);
CARBON_CHECK(require_kind == actual_kind)
<< "Expected " << require_kind << ", found " << actual_kind;
}
// Requires an entry to have a invalid node_id.
auto RequireSoloParseNode(Entry entry) -> void {
// See above comment on type punning.
CARBON_CHECK(!entry.node_id.is_valid())
<< "Expected invalid id on " << parse_tree_->node_kind(entry.parse_node)
<< ", was " << entry.node_id << " (may not be node)";
}
// Requires an entry to have a valid id.
auto RequireValidId(Entry entry) -> void {
// See above comment on type punning.
CARBON_CHECK(entry.node_id.is_valid())
<< "Expected valid id on " << parse_tree_->node_kind(entry.parse_node);
CARBON_CHECK(RequiredParseKind == actual_kind)
<< "Expected " << ParseNodeKind::Create(RequiredParseKind) << ", found "
<< actual_kind;
}
// The file's parse tree.