deferred_definition_worklist.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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 <variant>
  8. #include "common/emplace_by_calling.h"
  9. #include "common/vlog.h"
  10. #include "toolchain/base/kind_switch.h"
  11. #include "toolchain/check/context.h"
  12. namespace Carbon::Check {
  13. static constexpr llvm::StringLiteral VlogPrefix = "DeferredDefinitionWorklist ";
  14. DeferredDefinitionWorklist::DeferredDefinitionWorklist(
  15. llvm::raw_ostream* vlog_stream)
  16. : vlog_stream_(vlog_stream) {
  17. // See declaration of `worklist_`.
  18. worklist_.reserve(64);
  19. }
  20. auto DeferredDefinitionWorklist::SuspendFunctionAndPush(
  21. Parse::DeferredDefinitionIndex index,
  22. llvm::function_ref<auto()->SuspendedFunction> suspend) -> void {
  23. worklist_.emplace_back(EmplaceByCalling([&] {
  24. return CheckSkippedDefinition{.definition_index = index,
  25. .suspended_fn = suspend()};
  26. }));
  27. CARBON_VLOG("{0}Push CheckSkippedDefinition {1}\n", VlogPrefix, index.index);
  28. }
  29. auto DeferredDefinitionWorklist::SuspendThunkAndPush(Context& context,
  30. ThunkInfo info) -> void {
  31. worklist_.emplace_back(EmplaceByCalling([&] {
  32. return DefineThunk{.info = info, .scope = context.scope_stack().Suspend()};
  33. }));
  34. CARBON_VLOG("{0}Push DefineThunk {1}\n", VlogPrefix, info.function_id);
  35. }
  36. auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope(
  37. Context& context) -> bool {
  38. bool nested = !entered_scopes_.empty() &&
  39. entered_scopes_.back().scope_index ==
  40. context.decl_name_stack().PeekInitialScopeIndex();
  41. entered_scopes_.push_back({.nested = nested,
  42. .worklist_start_index = worklist_.size(),
  43. .scope_index = context.scope_stack().PeekIndex()});
  44. if (nested) {
  45. worklist_.emplace_back(EmplaceByCalling([&] {
  46. return EnterNestedDeferredDefinitionScope{.suspended_name = std::nullopt};
  47. }));
  48. CARBON_VLOG("{0}Push EnterDeferredDefinitionScope (nested)\n", VlogPrefix);
  49. } else {
  50. // Don't push a task to re-enter a non-nested scope. Instead,
  51. // SuspendFinishedScopeAndPush will remain in the scope when executing the
  52. // worklist tasks.
  53. CARBON_VLOG("{0}Entered non-nested deferred definition scope\n",
  54. VlogPrefix);
  55. }
  56. return !nested;
  57. }
  58. auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context)
  59. -> FinishedScopeKind {
  60. auto [nested, start_index, _] = entered_scopes_.pop_back_val();
  61. // If we've not found any tasks to perform in this scope, clean up the stack.
  62. // For non-nested scope, there will be no tasks on the worklist for this scope
  63. // in this case; for a nested scope, there will just be a task to re-enter the
  64. // nested scope.
  65. if (!nested && start_index == worklist_.size()) {
  66. context.decl_name_stack().PopScope();
  67. CARBON_VLOG("{0}Left non-nested empty deferred definition scope\n",
  68. VlogPrefix);
  69. return FinishedScopeKind::NonNestedEmpty;
  70. }
  71. if (nested && start_index == worklist_.size() - 1) {
  72. CARBON_CHECK(std::holds_alternative<EnterNestedDeferredDefinitionScope>(
  73. worklist_.back()));
  74. worklist_.pop_back();
  75. context.decl_name_stack().PopScope();
  76. CARBON_VLOG("{0}Pop EnterNestedDeferredDefinitionScope (empty)\n",
  77. VlogPrefix);
  78. return FinishedScopeKind::Nested;
  79. }
  80. // If we're finishing a nested deferred definition scope, keep track of that
  81. // but don't type-check deferred definitions now.
  82. if (nested) {
  83. auto& enter_scope =
  84. get<EnterNestedDeferredDefinitionScope>(worklist_[start_index]);
  85. // This is a nested deferred definition scope. Suspend the inner scope so we
  86. // can restore it when we come to type-check the deferred definitions.
  87. enter_scope.suspended_name.emplace(
  88. EmplaceByCalling([&] { return context.decl_name_stack().Suspend(); }));
  89. // Enqueue a task to leave the nested scope.
  90. worklist_.emplace_back(LeaveNestedDeferredDefinitionScope{});
  91. CARBON_VLOG("{0}Push LeaveNestedDeferredDefinitionScope\n", VlogPrefix);
  92. return FinishedScopeKind::Nested;
  93. }
  94. // We're at the end of a non-nested deferred definition scope. Start checking
  95. // deferred definitions.
  96. CARBON_VLOG("{0}Starting deferred definition processing\n", VlogPrefix);
  97. return FinishedScopeKind::NonNestedWithWork;
  98. }
  99. } // namespace Carbon::Check