mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 15:10:12 +01:00
Instead of building the definition of a thunk immediately when we generate the thunk declaration, wait until we reach the `}` of the outermost class, interface, etc. -- at the same time when we would parse the definition of the thunk if it were defined inline. This fixes issues where we fail to define the thunk because it requires an enclosing class to be complete, or its definition depends on something declared later in the enclosing class. Make the representation of a suspended function scope, and its constituent suspended components, be move-only, and switch to passing it around by rvalue reference instead of by value because it's expensive both to move and especially to copy. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
132 lines
5.3 KiB
C++
132 lines
5.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/deferred_definition_worklist.h"
|
|
|
|
#include <algorithm>
|
|
#include <optional>
|
|
|
|
#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) -> bool {
|
|
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)");
|
|
return !nested;
|
|
}
|
|
|
|
auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context)
|
|
-> FinishedScopeKind {
|
|
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();
|
|
auto enter_scope =
|
|
get<EnterDeferredDefinitionScope>(worklist_.pop_back_val());
|
|
CARBON_VLOG("{0}Pop EnterDeferredDefinitionScope (empty)\n", VlogPrefix);
|
|
return enter_scope.in_deferred_definition_scope
|
|
? FinishedScopeKind::Nested
|
|
: FinishedScopeKind::NonNestedEmpty;
|
|
}
|
|
|
|
// If we're finishing a nested deferred definition scope, keep track of that
|
|
// but don't type-check deferred definitions now.
|
|
if (auto& enter_scope =
|
|
get<EnterDeferredDefinitionScope>(worklist_[start_index]);
|
|
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 FinishedScopeKind::Nested;
|
|
}
|
|
|
|
// 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 FinishedScopeKind::NonNestedWithWork;
|
|
}
|
|
|
|
auto DeferredDefinitionWorklist::Pop(
|
|
llvm::function_ref<auto(Task&&)->void> handle_fn) -> void {
|
|
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)");
|
|
});
|
|
}
|
|
|
|
handle_fn(std::move(worklist_.back()));
|
|
worklist_.pop_back();
|
|
}
|
|
|
|
} // namespace Carbon::Check
|