Files
carbon-lang/toolchain/check/deferred_definition_worklist.cpp
T
Jon Ross-Perkins 16bf3f710e Split deferred node traversal out from check.cpp (#4559)
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.
2024-11-20 20:58:58 +00:00

122 lines
4.9 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/deferred_definition_worklist.h"
#include "common/variant_helpers.h"
#include "common/vlog.h"
#include "toolchain/check/handle.h"
namespace Carbon::Check {
static constexpr llvm::StringLiteral VlogPrefix = "DeferredDefinitionWorklist ";
DeferredDefinitionWorklist::DeferredDefinitionWorklist(
llvm::raw_ostream* vlog_stream)
: vlog_stream_(vlog_stream) {
// See declaration of `worklist_`.
worklist_.reserve(64);
}
auto DeferredDefinitionWorklist::SuspendFunctionAndPush(
Context& context, Parse::DeferredDefinitionIndex index,
Parse::FunctionDefinitionStartId node_id) -> void {
// TODO: Investigate factoring out `HandleFunctionDefinitionSuspend` to make
// `DeferredDefinitionWorklist` reusable.
worklist_.push_back(CheckSkippedDefinition{
index, HandleFunctionDefinitionSuspend(context, node_id)});
CARBON_VLOG("{0}Push CheckSkippedDefinition {1}\n", VlogPrefix, index.index);
}
auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope(
Context& context) -> void {
bool nested = !entered_scopes_.empty() &&
entered_scopes_.back().scope_index ==
context.decl_name_stack().PeekInitialScopeIndex();
entered_scopes_.push_back({.worklist_start_index = worklist_.size(),
.scope_index = context.scope_stack().PeekIndex()});
worklist_.push_back(EnterDeferredDefinitionScope{
.suspended_name = std::nullopt, .in_deferred_definition_scope = nested});
CARBON_VLOG("{0}Push EnterDeferredDefinitionScope {1}\n", VlogPrefix,
nested ? "(nested)" : "(non-nested)");
}
auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context)
-> bool {
auto start_index = entered_scopes_.pop_back_val().worklist_start_index;
// If we've not found any deferred definitions in this scope, clean up the
// stack.
if (start_index == worklist_.size() - 1) {
context.decl_name_stack().PopScope();
worklist_.pop_back();
CARBON_VLOG("{0}Pop EnterDeferredDefinitionScope (empty)\n", VlogPrefix);
return false;
}
// If we're finishing a nested deferred definition scope, keep track of that
// but don't type-check deferred definitions now.
auto& enter_scope = get<EnterDeferredDefinitionScope>(worklist_[start_index]);
if (enter_scope.in_deferred_definition_scope) {
// This is a nested deferred definition scope. Suspend the inner scope so we
// can restore it when we come to type-check the deferred definitions.
enter_scope.suspended_name = context.decl_name_stack().Suspend();
// Enqueue a task to leave the nested scope.
worklist_.push_back(
LeaveDeferredDefinitionScope{.in_deferred_definition_scope = true});
CARBON_VLOG("{0}Push LeaveDeferredDefinitionScope (nested)\n", VlogPrefix);
return false;
}
// We're at the end of a non-nested deferred definition scope. Prepare to
// start checking deferred definitions. Enqueue a task to leave this outer
// scope and end checking deferred definitions.
worklist_.push_back(
LeaveDeferredDefinitionScope{.in_deferred_definition_scope = false});
CARBON_VLOG("{0}Push LeaveDeferredDefinitionScope (non-nested)\n",
VlogPrefix);
// We'll process the worklist in reverse index order, so reverse the part of
// it we're about to execute so we run our tasks in the order in which they
// were pushed.
std::reverse(worklist_.begin() + start_index, worklist_.end());
// Pop the `EnterDeferredDefinitionScope` that's now on the end of the
// worklist. We stay in that scope rather than suspending then immediately
// resuming it.
CARBON_CHECK(
holds_alternative<EnterDeferredDefinitionScope>(worklist_.back()),
"Unexpected task in worklist.");
worklist_.pop_back();
CARBON_VLOG("{0}Handle EnterDeferredDefinitionScope (non-nested)\n",
VlogPrefix);
return true;
}
auto DeferredDefinitionWorklist::Pop() -> Task {
if (vlog_stream_) {
VariantMatch(
worklist_.back(),
[&](CheckSkippedDefinition& definition) {
CARBON_VLOG("{0}Handle CheckSkippedDefinition {1}\n", VlogPrefix,
definition.definition_index.index);
},
[&](EnterDeferredDefinitionScope& enter) {
CARBON_CHECK(enter.in_deferred_definition_scope);
CARBON_VLOG("{0}Handle EnterDeferredDefinitionScope (nested)\n",
VlogPrefix);
},
[&](LeaveDeferredDefinitionScope& leave) {
bool nested = leave.in_deferred_definition_scope;
CARBON_VLOG("{0}Handle LeaveDeferredDefinitionScope {1}\n",
VlogPrefix, nested ? "(nested)" : "(non-nested)");
});
}
return worklist_.pop_back_val();
}
} // namespace Carbon::Check