// 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 #include #include #include "common/emplace_by_calling.h" #include "common/vlog.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.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( Parse::DeferredDefinitionIndex index, llvm::function_refSuspendedFunction> suspend) -> void { worklist_.emplace_back(EmplaceByCalling([&] { return CheckSkippedDefinition{.definition_index = index, .suspended_fn = suspend()}; })); CARBON_VLOG("{0}Push CheckSkippedDefinition {1}\n", VlogPrefix, index.index); } auto DeferredDefinitionWorklist::SuspendThunkAndPush(Context& context, ThunkInfo info) -> void { worklist_.emplace_back(EmplaceByCalling([&] { return DefineThunk{.info = info, .scope = context.scope_stack().Suspend()}; })); CARBON_VLOG("{0}Push DefineThunk {1}\n", VlogPrefix, info.function_id); } 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({.nested = nested, .worklist_start_index = worklist_.size(), .scope_index = context.scope_stack().PeekIndex()}); if (nested) { worklist_.emplace_back(EmplaceByCalling([&] { return EnterNestedDeferredDefinitionScope{.suspended_name = std::nullopt}; })); CARBON_VLOG("{0}Push EnterDeferredDefinitionScope (nested)\n", VlogPrefix); } else { // Don't push a task to re-enter a non-nested scope. Instead, // SuspendFinishedScopeAndPush will remain in the scope when executing the // worklist tasks. CARBON_VLOG("{0}Entered non-nested deferred definition scope\n", VlogPrefix); } return !nested; } auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context) -> FinishedScopeKind { auto [nested, start_index, _] = entered_scopes_.pop_back_val(); // If we've not found any tasks to perform in this scope, clean up the stack. // For non-nested scope, there will be no tasks on the worklist for this scope // in this case; for a nested scope, there will just be a task to re-enter the // nested scope. if (!nested && start_index == worklist_.size()) { context.decl_name_stack().PopScope(); CARBON_VLOG("{0}Left non-nested empty deferred definition scope\n", VlogPrefix); return FinishedScopeKind::NonNestedEmpty; } if (nested && start_index == worklist_.size() - 1) { CARBON_CHECK(std::holds_alternative( worklist_.back())); worklist_.pop_back(); context.decl_name_stack().PopScope(); CARBON_VLOG("{0}Pop EnterNestedDeferredDefinitionScope (empty)\n", VlogPrefix); return FinishedScopeKind::Nested; } // If we're finishing a nested deferred definition scope, keep track of that // but don't type-check deferred definitions now. if (nested) { auto& enter_scope = get(worklist_[start_index]); // 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.emplace( EmplaceByCalling([&] { return context.decl_name_stack().Suspend(); })); // Enqueue a task to leave the nested scope. worklist_.emplace_back(LeaveNestedDeferredDefinitionScope{}); CARBON_VLOG("{0}Push LeaveNestedDeferredDefinitionScope\n", VlogPrefix); return FinishedScopeKind::Nested; } // We're at the end of a non-nested deferred definition scope. Start checking // deferred definitions. CARBON_VLOG("{0}Starting deferred definition processing\n", VlogPrefix); return FinishedScopeKind::NonNestedWithWork; } } // namespace Carbon::Check