mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Add tracking of function parameters (#2552)
For parameters (and in the future, arguments too; generally comma-separated lists) track two node blocks: 1. param_ir: The complete IR. 2. param_refs: Nodes within the IR that are the "root" parameter. param_refs should allow quick counting of the # of parameters, and more efficient comparison of call args with function parameters. param_ir should be necessary to generate the actual signature. In order to construct this, this refactors the node_block_stack into its own class, which is reused in params_stack. These carry references to the underlying SmallVector for lazy modification in order to avoid a dependency cycle with SemanticsIR (also see notes on empty node blocks below). When finalized, the block pair is pushed onto finished_params_stack. That's because node_stack only has space for one thing, and this is two things -- so I'm essentially choosing a trade-off of adding another stack in order to avoid consuming more space in the expectation that most parse nodes have 0 or 1 things to return, and 2 will be very rare. As factored, this currently consolidates most empty node blocks into a single canonical empty node block. This is because I think empty blocks, i.e. `()`, will be very common. In order to achieve this, SemanticsNodeBlockStack does lazy creation. An alternative approach would have been to use 1 node block per parameter. We decided against this in order to reduce the number of vectors being created.
This commit is contained in:
@@ -4,58 +4,43 @@
|
||||
|
||||
#include "toolchain/semantics/semantics_parse_tree_handler.h"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "common/vlog.h"
|
||||
#include "llvm/Support/PrettyStackTrace.h"
|
||||
#include "toolchain/lexer/token_kind.h"
|
||||
#include "toolchain/lexer/tokenized_buffer.h"
|
||||
#include "toolchain/parser/parse_node_kind.h"
|
||||
#include "toolchain/semantics/semantics_builtin_kind.h"
|
||||
#include "toolchain/semantics/semantics_ir.h"
|
||||
#include "toolchain/semantics/semantics_node.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
class SemanticsParseTreeHandler::PrettyStackTraceNodeStack
|
||||
: public llvm::PrettyStackTraceEntry {
|
||||
class PrettyStackTraceFunction : public llvm::PrettyStackTraceEntry {
|
||||
public:
|
||||
explicit PrettyStackTraceNodeStack(const SemanticsParseTreeHandler* handler)
|
||||
: handler_(handler) {}
|
||||
~PrettyStackTraceNodeStack() override = default;
|
||||
explicit PrettyStackTraceFunction(std::function<void(llvm::raw_ostream&)> fn)
|
||||
: fn_(std::move(fn)) {}
|
||||
~PrettyStackTraceFunction() override = default;
|
||||
|
||||
auto print(llvm::raw_ostream& output) const -> void override {
|
||||
handler_->node_stack_.PrintForStackDump(output);
|
||||
}
|
||||
auto print(llvm::raw_ostream& output) const -> void override { fn_(output); }
|
||||
|
||||
private:
|
||||
const SemanticsParseTreeHandler* handler_;
|
||||
};
|
||||
|
||||
class SemanticsParseTreeHandler::PrettyStackTraceNodeBlockStack
|
||||
: public llvm::PrettyStackTraceEntry {
|
||||
public:
|
||||
explicit PrettyStackTraceNodeBlockStack(
|
||||
const SemanticsParseTreeHandler* handler)
|
||||
: handler_(handler) {}
|
||||
~PrettyStackTraceNodeBlockStack() override = default;
|
||||
|
||||
auto print(llvm::raw_ostream& output) const -> void override {
|
||||
output << "node_block_stack_:\n";
|
||||
for (int i = 0; i < static_cast<int>(handler_->node_block_stack_.size());
|
||||
++i) {
|
||||
const auto& entry = handler_->node_block_stack_[i];
|
||||
output << "\t" << i << ".\t" << entry << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const SemanticsParseTreeHandler* handler_;
|
||||
const std::function<void(llvm::raw_ostream&)> fn_;
|
||||
};
|
||||
|
||||
auto SemanticsParseTreeHandler::Build() -> void {
|
||||
PrettyStackTraceNodeStack pretty_node_stack(this);
|
||||
PrettyStackTraceNodeBlockStack pretty_node_block_stack(this);
|
||||
PrettyStackTraceFunction pretty_node_stack([&](llvm::raw_ostream& output) {
|
||||
node_stack_.PrintForStackDump(output);
|
||||
});
|
||||
PrettyStackTraceFunction pretty_node_block_stack(
|
||||
[&](llvm::raw_ostream& output) {
|
||||
node_block_stack_.PrintForStackDump(output);
|
||||
});
|
||||
|
||||
// Add a block for the ParseTree.
|
||||
node_block_stack_.push_back(semantics_->AddNodeBlock());
|
||||
node_block_stack_.Push();
|
||||
PushScope();
|
||||
|
||||
for (auto parse_node : parse_tree_->postorder()) {
|
||||
@@ -69,8 +54,7 @@ auto SemanticsParseTreeHandler::Build() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
node_block_stack_.pop_back();
|
||||
CARBON_CHECK(node_block_stack_.empty()) << node_block_stack_.size();
|
||||
node_block_stack_.Pop();
|
||||
|
||||
PopScope();
|
||||
CARBON_CHECK(name_lookup_.empty()) << name_lookup_.size();
|
||||
@@ -78,8 +62,9 @@ auto SemanticsParseTreeHandler::Build() -> void {
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::AddNode(SemanticsNode node) -> SemanticsNodeId {
|
||||
CARBON_VLOG() << "AddNode " << current_block_id() << ": " << node << "\n";
|
||||
return semantics_->AddNode(current_block_id(), node);
|
||||
auto block = node_block_stack_.PeekForAdd();
|
||||
CARBON_VLOG() << "AddNode " << block << ": " << node << "\n";
|
||||
return semantics_->AddNode(block, node);
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::AddNodeAndPush(ParseTree::Node parse_node,
|
||||
@@ -244,7 +229,7 @@ auto SemanticsParseTreeHandler::HandleExpressionStatement(
|
||||
// Pop the expression without investigating its contents.
|
||||
// TODO: This will probably eventually need to do some "do not discard"
|
||||
// analysis.
|
||||
node_stack_.PopAndIgnore();
|
||||
node_stack_.PopAndDiscardId();
|
||||
node_stack_.Push(parse_node);
|
||||
}
|
||||
|
||||
@@ -288,23 +273,35 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinition(
|
||||
node_stack_.PopAndIgnore();
|
||||
|
||||
PopScope();
|
||||
node_block_stack_.pop_back();
|
||||
node_block_stack_.Pop();
|
||||
node_stack_.Push(parse_node);
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart(
|
||||
ParseTree::Node parse_node) -> void {
|
||||
node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ParameterList);
|
||||
node_stack_.PopForSoloParseNode(ParseNodeKind::ParameterList);
|
||||
auto [param_ir_id, param_refs_id] = finished_params_stack_.pop_back_val();
|
||||
auto name_node = node_stack_.PopForSoloParseNode(ParseNodeKind::DeclaredName);
|
||||
auto fn_node =
|
||||
node_stack_.PopForSoloParseNode(ParseNodeKind::FunctionIntroducer);
|
||||
|
||||
auto decl_id = AddNode(SemanticsNode::MakeFunctionDeclaration(fn_node));
|
||||
SemanticsCallable callable;
|
||||
callable.param_ir_id = param_ir_id;
|
||||
callable.param_refs_id = param_refs_id;
|
||||
auto callable_id = semantics_->AddCallable(callable);
|
||||
auto decl_id =
|
||||
AddNode(SemanticsNode::MakeFunctionDeclaration(fn_node, callable_id));
|
||||
// TODO: Propagate the type of the function.
|
||||
BindName(name_node, SemanticsNodeId::MakeInvalid(), decl_id);
|
||||
auto block_id = semantics_->AddNodeBlock();
|
||||
AddNode(SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, block_id));
|
||||
node_block_stack_.push_back(block_id);
|
||||
|
||||
// TODO: Consider approaches that allow lazy creation of the definition block.
|
||||
auto outer_block = node_block_stack_.PeekForAdd();
|
||||
auto def_block = node_block_stack_.PushWithUnconditionalAlloc();
|
||||
auto node =
|
||||
SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, def_block);
|
||||
CARBON_VLOG() << "AddNode " << outer_block << ": " << node << "\n";
|
||||
semantics_->AddNode(outer_block, node);
|
||||
|
||||
PushScope();
|
||||
node_stack_.Push(parse_node);
|
||||
}
|
||||
@@ -451,23 +448,76 @@ auto SemanticsParseTreeHandler::HandlePackageLibrary(
|
||||
CARBON_FATAL() << "TODO";
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::SaveParam() -> bool {
|
||||
// Copy the last node added to the IR block into the params block.
|
||||
auto ir_id = node_block_stack_.Peek();
|
||||
if (!ir_id.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
auto& ir = semantics_->GetNodeBlock(ir_id);
|
||||
CARBON_CHECK(!ir.empty())
|
||||
<< "Should only have a valid ID if a node was added";
|
||||
auto& param = ir.back();
|
||||
auto& params = semantics_->GetNodeBlock(params_stack_.PeekForAdd());
|
||||
if (!params.empty() && param == params.back()) {
|
||||
// The param was already added after a comma.
|
||||
return false;
|
||||
}
|
||||
params.push_back(ir.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::HandleParameterList(ParseTree::Node parse_node)
|
||||
-> void {
|
||||
// TODO: This should transform into a usable parameter list. For now
|
||||
// it's unused and only stored so that node counts match.
|
||||
node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ParameterListStart);
|
||||
node_stack_.Push(parse_node);
|
||||
// If there's a node in the IR block that has yet to be added to the params
|
||||
// block, add it now.
|
||||
SaveParam();
|
||||
|
||||
while (true) {
|
||||
switch (auto parse_kind =
|
||||
parse_tree_->node_kind(node_stack_.PeekParseNode())) {
|
||||
case ParseNodeKind::ParameterListStart:
|
||||
node_stack_.PopAndDiscardSoloParseNode(
|
||||
ParseNodeKind::ParameterListStart);
|
||||
finished_params_stack_.push_back(
|
||||
{node_block_stack_.Pop(), params_stack_.Pop()});
|
||||
node_stack_.Push(parse_node);
|
||||
return;
|
||||
|
||||
case ParseNodeKind::ParameterListComma:
|
||||
node_stack_.PopAndDiscardSoloParseNode(
|
||||
ParseNodeKind::ParameterListComma);
|
||||
break;
|
||||
|
||||
case ParseNodeKind::PatternBinding:
|
||||
node_stack_.PopAndDiscardId(ParseNodeKind::PatternBinding);
|
||||
break;
|
||||
|
||||
default:
|
||||
// This should only occur for invalid parse trees.
|
||||
CARBON_FATAL() << "TODO: " << parse_kind;
|
||||
}
|
||||
}
|
||||
|
||||
llvm_unreachable("loop always exits");
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::HandleParameterListComma(
|
||||
ParseTree::Node /*parse_node*/) -> void {
|
||||
CARBON_FATAL() << "TODO";
|
||||
ParseTree::Node parse_node) -> void {
|
||||
node_stack_.Push(parse_node);
|
||||
|
||||
// Copy the last node added to the IR block into the params block.
|
||||
bool had_param_before_comma = SaveParam();
|
||||
CARBON_CHECK(had_param_before_comma)
|
||||
<< "TODO: Should have a param before comma, will need error recovery";
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::HandleParameterListStart(
|
||||
ParseTree::Node parse_node) -> void {
|
||||
// TODO: See HandleParameterList.
|
||||
node_stack_.Push(parse_node);
|
||||
|
||||
params_stack_.Push();
|
||||
node_block_stack_.Push();
|
||||
}
|
||||
|
||||
auto SemanticsParseTreeHandler::HandleParenExpression(
|
||||
|
||||
Reference in New Issue
Block a user