deferred_definition_worklist.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/check/deferred_definition_worklist.h"
  5. #include <algorithm>
  6. #include <optional>
  7. #include "common/variant_helpers.h"
  8. #include "common/vlog.h"
  9. #include "toolchain/check/handle.h"
  10. namespace Carbon::Check {
  11. static constexpr llvm::StringLiteral VlogPrefix = "DeferredDefinitionWorklist ";
  12. DeferredDefinitionWorklist::DeferredDefinitionWorklist(
  13. llvm::raw_ostream* vlog_stream)
  14. : vlog_stream_(vlog_stream) {
  15. // See declaration of `worklist_`.
  16. worklist_.reserve(64);
  17. }
  18. auto DeferredDefinitionWorklist::SuspendFunctionAndPush(
  19. Context& context, Parse::DeferredDefinitionIndex index,
  20. Parse::FunctionDefinitionStartId node_id) -> void {
  21. // TODO: Investigate factoring out `HandleFunctionDefinitionSuspend` to make
  22. // `DeferredDefinitionWorklist` reusable.
  23. worklist_.push_back(CheckSkippedDefinition{
  24. index, HandleFunctionDefinitionSuspend(context, node_id)});
  25. CARBON_VLOG("{0}Push CheckSkippedDefinition {1}\n", VlogPrefix, index.index);
  26. }
  27. auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope(
  28. Context& context) -> bool {
  29. bool nested = !entered_scopes_.empty() &&
  30. entered_scopes_.back().scope_index ==
  31. context.decl_name_stack().PeekInitialScopeIndex();
  32. entered_scopes_.push_back({.worklist_start_index = worklist_.size(),
  33. .scope_index = context.scope_stack().PeekIndex()});
  34. worklist_.push_back(EnterDeferredDefinitionScope{
  35. .suspended_name = std::nullopt, .in_deferred_definition_scope = nested});
  36. CARBON_VLOG("{0}Push EnterDeferredDefinitionScope {1}\n", VlogPrefix,
  37. nested ? "(nested)" : "(non-nested)");
  38. return !nested;
  39. }
  40. auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context)
  41. -> FinishedScopeKind {
  42. auto start_index = entered_scopes_.pop_back_val().worklist_start_index;
  43. // If we've not found any deferred definitions in this scope, clean up the
  44. // stack.
  45. if (start_index == worklist_.size() - 1) {
  46. context.decl_name_stack().PopScope();
  47. auto enter_scope =
  48. get<EnterDeferredDefinitionScope>(worklist_.pop_back_val());
  49. CARBON_VLOG("{0}Pop EnterDeferredDefinitionScope (empty)\n", VlogPrefix);
  50. return enter_scope.in_deferred_definition_scope
  51. ? FinishedScopeKind::Nested
  52. : FinishedScopeKind::NonNestedEmpty;
  53. }
  54. // If we're finishing a nested deferred definition scope, keep track of that
  55. // but don't type-check deferred definitions now.
  56. if (auto& enter_scope =
  57. get<EnterDeferredDefinitionScope>(worklist_[start_index]);
  58. enter_scope.in_deferred_definition_scope) {
  59. // This is a nested deferred definition scope. Suspend the inner scope so we
  60. // can restore it when we come to type-check the deferred definitions.
  61. enter_scope.suspended_name = context.decl_name_stack().Suspend();
  62. // Enqueue a task to leave the nested scope.
  63. worklist_.push_back(
  64. LeaveDeferredDefinitionScope{.in_deferred_definition_scope = true});
  65. CARBON_VLOG("{0}Push LeaveDeferredDefinitionScope (nested)\n", VlogPrefix);
  66. return FinishedScopeKind::Nested;
  67. }
  68. // We're at the end of a non-nested deferred definition scope. Prepare to
  69. // start checking deferred definitions. Enqueue a task to leave this outer
  70. // scope and end checking deferred definitions.
  71. worklist_.push_back(
  72. LeaveDeferredDefinitionScope{.in_deferred_definition_scope = false});
  73. CARBON_VLOG("{0}Push LeaveDeferredDefinitionScope (non-nested)\n",
  74. VlogPrefix);
  75. // We'll process the worklist in reverse index order, so reverse the part of
  76. // it we're about to execute so we run our tasks in the order in which they
  77. // were pushed.
  78. std::reverse(worklist_.begin() + start_index, worklist_.end());
  79. // Pop the `EnterDeferredDefinitionScope` that's now on the end of the
  80. // worklist. We stay in that scope rather than suspending then immediately
  81. // resuming it.
  82. CARBON_CHECK(
  83. holds_alternative<EnterDeferredDefinitionScope>(worklist_.back()),
  84. "Unexpected task in worklist.");
  85. worklist_.pop_back();
  86. CARBON_VLOG("{0}Handle EnterDeferredDefinitionScope (non-nested)\n",
  87. VlogPrefix);
  88. return FinishedScopeKind::NonNestedWithWork;
  89. }
  90. auto DeferredDefinitionWorklist::Pop(
  91. llvm::function_ref<auto(Task&&)->void> handle_fn) -> void {
  92. if (vlog_stream_) {
  93. VariantMatch(
  94. worklist_.back(),
  95. [&](CheckSkippedDefinition& definition) {
  96. CARBON_VLOG("{0}Handle CheckSkippedDefinition {1}\n", VlogPrefix,
  97. definition.definition_index.index);
  98. },
  99. [&](EnterDeferredDefinitionScope& enter) {
  100. CARBON_CHECK(enter.in_deferred_definition_scope);
  101. CARBON_VLOG("{0}Handle EnterDeferredDefinitionScope (nested)\n",
  102. VlogPrefix);
  103. },
  104. [&](LeaveDeferredDefinitionScope& leave) {
  105. bool nested = leave.in_deferred_definition_scope;
  106. CARBON_VLOG("{0}Handle LeaveDeferredDefinitionScope {1}\n",
  107. VlogPrefix, nested ? "(nested)" : "(non-nested)");
  108. });
  109. }
  110. handle_fn(std::move(worklist_.back()));
  111. worklist_.pop_back();
  112. }
  113. } // namespace Carbon::Check