deferred_definition_worklist.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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/handle.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. Context& context, Parse::DeferredDefinitionIndex index,
  22. Parse::FunctionDefinitionStartId node_id) -> void {
  23. // TODO: Investigate factoring out `HandleFunctionDefinitionSuspend` to make
  24. // `DeferredDefinitionWorklist` reusable.
  25. worklist_.emplace_back(EmplaceByCalling([&] {
  26. return CheckSkippedDefinition{
  27. index, HandleFunctionDefinitionSuspend(context, node_id)};
  28. }));
  29. CARBON_VLOG("{0}Push CheckSkippedDefinition {1}\n", VlogPrefix, index.index);
  30. }
  31. auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope(
  32. Context& context) -> bool {
  33. bool nested = !entered_scopes_.empty() &&
  34. entered_scopes_.back().scope_index ==
  35. context.decl_name_stack().PeekInitialScopeIndex();
  36. entered_scopes_.push_back({.nested = nested,
  37. .worklist_start_index = worklist_.size(),
  38. .scope_index = context.scope_stack().PeekIndex()});
  39. if (nested) {
  40. worklist_.emplace_back(EmplaceByCalling([&] {
  41. return EnterNestedDeferredDefinitionScope{.suspended_name = std::nullopt};
  42. }));
  43. CARBON_VLOG("{0}Push EnterDeferredDefinitionScope (nested)\n", VlogPrefix);
  44. } else {
  45. // Don't push a task to re-enter a non-nested scope. Instead,
  46. // SuspendFinishedScopeAndPush will remain in the scope when executing the
  47. // worklist tasks.
  48. CARBON_VLOG("{0}Entered non-nested deferred definition scope\n",
  49. VlogPrefix);
  50. }
  51. return !nested;
  52. }
  53. auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context)
  54. -> FinishedScopeKind {
  55. auto [nested, start_index, _] = entered_scopes_.pop_back_val();
  56. // If we've not found any tasks to perform in this scope, clean up the stack.
  57. // For non-nested scope, there will be no tasks on the worklist for this scope
  58. // in this case; for a nested scope, there will just be a task to re-enter the
  59. // nested scope.
  60. if (!nested && start_index == worklist_.size()) {
  61. context.decl_name_stack().PopScope();
  62. CARBON_VLOG("{0}Left non-nested empty deferred definition scope\n",
  63. VlogPrefix);
  64. return FinishedScopeKind::NonNestedEmpty;
  65. }
  66. if (nested && start_index == worklist_.size() - 1) {
  67. CARBON_CHECK(std::holds_alternative<EnterNestedDeferredDefinitionScope>(
  68. worklist_.back()));
  69. worklist_.pop_back();
  70. context.decl_name_stack().PopScope();
  71. CARBON_VLOG("{0}Pop EnterNestedDeferredDefinitionScope (empty)\n",
  72. VlogPrefix);
  73. return FinishedScopeKind::Nested;
  74. }
  75. // If we're finishing a nested deferred definition scope, keep track of that
  76. // but don't type-check deferred definitions now.
  77. if (nested) {
  78. auto& enter_scope =
  79. get<EnterNestedDeferredDefinitionScope>(worklist_[start_index]);
  80. // This is a nested deferred definition scope. Suspend the inner scope so we
  81. // can restore it when we come to type-check the deferred definitions.
  82. enter_scope.suspended_name.emplace(
  83. EmplaceByCalling([&] { return context.decl_name_stack().Suspend(); }));
  84. // Enqueue a task to leave the nested scope.
  85. worklist_.emplace_back(LeaveNestedDeferredDefinitionScope{});
  86. CARBON_VLOG("{0}Push LeaveNestedDeferredDefinitionScope\n", VlogPrefix);
  87. return FinishedScopeKind::Nested;
  88. }
  89. // We're at the end of a non-nested deferred definition scope. Start checking
  90. // deferred definitions.
  91. CARBON_VLOG("{0}Starting deferred definition processing\n", VlogPrefix);
  92. return FinishedScopeKind::NonNestedWithWork;
  93. }
  94. } // namespace Carbon::Check