mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
I'm looking at adding more significant logic to checking, particularly for interop. But check.cpp is getting large, and I think just adding more logic will make it harder to reason about, so I'm looking at splitting it up. This moves out NodeIdTraversal and DeferredDefinitionWorklist because they're already independent from the other code, and are reasonably sized to have their own files.
176 lines
6.3 KiB
C++
176 lines
6.3 KiB
C++
// 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/check/node_id_traversal.h"
|
|
|
|
#include "toolchain/check/handle.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
NodeIdTraversal::NodeIdTraversal(Context& context,
|
|
llvm::raw_ostream* vlog_stream)
|
|
: context_(context),
|
|
next_deferred_definition_(&context.parse_tree()),
|
|
worklist_(vlog_stream) {
|
|
auto range = context.parse_tree().postorder();
|
|
chunks_.push_back(
|
|
{.it = range.begin(),
|
|
.end = range.end(),
|
|
.next_definition = Parse::DeferredDefinitionIndex::Invalid});
|
|
}
|
|
|
|
auto NodeIdTraversal::Next() -> std::optional<Parse::NodeId> {
|
|
while (true) {
|
|
// If we're checking deferred definitions, find the next definition we
|
|
// should check, restore its suspended state, and add a corresponding
|
|
// `Chunk` to the top of the chunk list.
|
|
if (chunks_.back().checking_deferred_definitions) {
|
|
std::visit(
|
|
[&](auto&& task) { PerformTask(std::forward<decltype(task)>(task)); },
|
|
worklist_.Pop());
|
|
continue;
|
|
}
|
|
|
|
// If we're not checking deferred definitions, produce the next parse node
|
|
// for this chunk. If we've run out of parse nodes, we're done with this
|
|
// chunk of the parse tree.
|
|
if (chunks_.back().it == chunks_.back().end) {
|
|
auto old_chunk = chunks_.pop_back_val();
|
|
|
|
// If we're out of chunks, then we're done entirely.
|
|
if (chunks_.empty()) {
|
|
worklist_.VerifyEmpty();
|
|
return std::nullopt;
|
|
}
|
|
|
|
next_deferred_definition_.SkipTo(old_chunk.next_definition);
|
|
continue;
|
|
}
|
|
|
|
auto node_id = *chunks_.back().it;
|
|
|
|
// If we've reached the start of a deferred definition, skip to the end of
|
|
// it, and track that we need to check it later.
|
|
if (node_id == next_deferred_definition_.start_id()) {
|
|
const auto& definition_info =
|
|
context_.parse_tree().deferred_definitions().Get(
|
|
next_deferred_definition_.index());
|
|
worklist_.SuspendFunctionAndPush(context_,
|
|
next_deferred_definition_.index(),
|
|
definition_info.start_id);
|
|
|
|
// Continue type-checking the parse tree after the end of the definition.
|
|
chunks_.back().it =
|
|
Parse::Tree::PostorderIterator(definition_info.definition_id) + 1;
|
|
next_deferred_definition_.SkipTo(definition_info.next_definition_index);
|
|
continue;
|
|
}
|
|
|
|
++chunks_.back().it;
|
|
return node_id;
|
|
}
|
|
}
|
|
|
|
// Determines whether this node kind is the start of a deferred definition
|
|
// scope.
|
|
static auto IsStartOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
|
|
switch (kind) {
|
|
case Parse::NodeKind::ClassDefinitionStart:
|
|
case Parse::NodeKind::ImplDefinitionStart:
|
|
case Parse::NodeKind::InterfaceDefinitionStart:
|
|
case Parse::NodeKind::NamedConstraintDefinitionStart:
|
|
// TODO: Mixins.
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Determines whether this node kind is the end of a deferred definition scope.
|
|
static auto IsEndOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
|
|
switch (kind) {
|
|
case Parse::NodeKind::ClassDefinition:
|
|
case Parse::NodeKind::ImplDefinition:
|
|
case Parse::NodeKind::InterfaceDefinition:
|
|
case Parse::NodeKind::NamedConstraintDefinition:
|
|
// TODO: Mixins.
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// TODO: Investigate factoring out `IsStartOfDeferredDefinitionScope` and
|
|
// `IsEndOfDeferredDefinitionScope` in order to make `NodeIdTraversal`
|
|
// reusable.
|
|
auto NodeIdTraversal::Handle(Parse::NodeKind parse_kind) -> void {
|
|
// When we reach the start of a deferred definition scope, add a task to the
|
|
// worklist to check future skipped definitions in the new context.
|
|
if (IsStartOfDeferredDefinitionScope(parse_kind)) {
|
|
worklist_.PushEnterDeferredDefinitionScope(context_);
|
|
}
|
|
|
|
// When we reach the end of a deferred definition scope, add a task to the
|
|
// worklist to leave the scope. If this is not a nested scope, start
|
|
// checking the deferred definitions now.
|
|
if (IsEndOfDeferredDefinitionScope(parse_kind)) {
|
|
chunks_.back().checking_deferred_definitions =
|
|
worklist_.SuspendFinishedScopeAndPush(context_);
|
|
}
|
|
}
|
|
|
|
auto NodeIdTraversal::PerformTask(
|
|
DeferredDefinitionWorklist::EnterDeferredDefinitionScope&& enter) -> void {
|
|
CARBON_CHECK(enter.suspended_name,
|
|
"Entering a scope with no suspension information.");
|
|
context_.decl_name_stack().Restore(std::move(*enter.suspended_name));
|
|
}
|
|
|
|
auto NodeIdTraversal::PerformTask(
|
|
DeferredDefinitionWorklist::LeaveDeferredDefinitionScope&& leave) -> void {
|
|
if (!leave.in_deferred_definition_scope) {
|
|
// We're done with checking deferred definitions.
|
|
chunks_.back().checking_deferred_definitions = false;
|
|
}
|
|
context_.decl_name_stack().PopScope();
|
|
}
|
|
|
|
auto NodeIdTraversal::PerformTask(
|
|
DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition)
|
|
-> void {
|
|
auto& [definition_index, suspended_fn] = parse_definition;
|
|
const auto& definition_info =
|
|
context_.parse_tree().deferred_definitions().Get(definition_index);
|
|
HandleFunctionDefinitionResume(context_, definition_info.start_id,
|
|
std::move(suspended_fn));
|
|
auto range = Parse::Tree::PostorderIterator::MakeRange(
|
|
definition_info.start_id, definition_info.definition_id);
|
|
chunks_.push_back({.it = range.begin() + 1,
|
|
.end = range.end(),
|
|
.next_definition = next_deferred_definition_.index()});
|
|
++definition_index.index;
|
|
next_deferred_definition_.SkipTo(definition_index);
|
|
}
|
|
|
|
NodeIdTraversal::NextDeferredDefinitionCache::NextDeferredDefinitionCache(
|
|
const Parse::Tree* tree)
|
|
: tree_(tree) {
|
|
SkipTo(Parse::DeferredDefinitionIndex(0));
|
|
}
|
|
|
|
// Set the specified deferred definition index as being the next one that
|
|
// will be encountered.
|
|
auto NodeIdTraversal::NextDeferredDefinitionCache::SkipTo(
|
|
Parse::DeferredDefinitionIndex next_index) -> void {
|
|
index_ = next_index;
|
|
if (static_cast<size_t>(index_.index) ==
|
|
tree_->deferred_definitions().size()) {
|
|
start_id_ = Parse::NodeId::Invalid;
|
|
} else {
|
|
start_id_ = tree_->deferred_definitions().Get(index_).start_id;
|
|
}
|
|
}
|
|
|
|
} // namespace Carbon::Check
|