node_id_traversal.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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/node_id_traversal.h"
  5. #include <optional>
  6. #include <utility>
  7. #include <variant>
  8. #include "toolchain/check/deferred_definition_worklist.h"
  9. #include "toolchain/check/handle.h"
  10. #include "toolchain/check/thunk.h"
  11. namespace Carbon::Check {
  12. NodeIdTraversal::NodeIdTraversal(Context* context)
  13. : context_(context),
  14. next_deferred_definition_(&context->parse_tree()),
  15. worklist_(&context->deferred_definition_worklist()) {
  16. auto range = context->parse_tree().postorder();
  17. chunks_.push_back({.it = range.begin(),
  18. .end = range.end(),
  19. .next_definition = Parse::DeferredDefinitionIndex::None});
  20. }
  21. auto NodeIdTraversal::Next() -> std::optional<Parse::NodeId> {
  22. while (true) {
  23. // If we're checking deferred definitions, find the next definition we
  24. // should check, restore its suspended state, and add a corresponding
  25. // `Chunk` to the top of the chunk list.
  26. if (chunks_.back().checking_deferred_definitions) {
  27. if (chunks_.back().next_worklist_index < worklist().size()) {
  28. std::visit([&](auto& task) { PerformTask(std::move(task)); },
  29. worklist()[chunks_.back().next_worklist_index++]);
  30. continue;
  31. }
  32. // Worklist is empty: discard the worklist items associated with this
  33. // chunk, and leave the scope.
  34. worklist().truncate(chunks_.back().first_worklist_index);
  35. // We reach here when
  36. // `DeferredDefinitionScope::SuspendFinishedScopeAndPush` returns
  37. // `NonNestedWithWork`. In this case it's our responsibility to pop the
  38. // scope left behind by the `Handle*Definition` function for the
  39. // non-nested definition.
  40. context_->decl_name_stack().PopScope();
  41. chunks_.back().checking_deferred_definitions = false;
  42. }
  43. // If we're not checking deferred definitions, produce the next parse node
  44. // for this chunk. If we've run out of parse nodes, we're done with this
  45. // chunk of the parse tree.
  46. if (chunks_.back().it == chunks_.back().end) {
  47. auto old_chunk = chunks_.pop_back_val();
  48. // If we're out of chunks, then we're done entirely.
  49. if (chunks_.empty()) {
  50. worklist().VerifyEmpty();
  51. return std::nullopt;
  52. }
  53. next_deferred_definition_.SkipTo(old_chunk.next_definition);
  54. continue;
  55. }
  56. auto node_id = *chunks_.back().it;
  57. // If we've reached the start of a deferred definition, skip to the end of
  58. // it, and track that we need to check it later.
  59. if (node_id == next_deferred_definition_.start_id()) {
  60. const auto& definition_info =
  61. context_->parse_tree().deferred_definitions().Get(
  62. next_deferred_definition_.index());
  63. worklist().SuspendFunctionAndPush(next_deferred_definition_.index(), [&] {
  64. return HandleFunctionDefinitionSuspend(*context_,
  65. definition_info.start_id);
  66. });
  67. // Continue type-checking the parse tree after the end of the definition.
  68. chunks_.back().it =
  69. Parse::Tree::PostorderIterator(definition_info.definition_id) + 1;
  70. next_deferred_definition_.SkipTo(definition_info.next_definition_index);
  71. continue;
  72. }
  73. ++chunks_.back().it;
  74. return node_id;
  75. }
  76. }
  77. // Determines whether this node kind is the start of a deferred definition
  78. // scope.
  79. static auto IsStartOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
  80. switch (kind) {
  81. case Parse::NodeKind::ClassDefinitionStart:
  82. case Parse::NodeKind::ImplDefinitionStart:
  83. case Parse::NodeKind::InterfaceDefinitionStart:
  84. case Parse::NodeKind::NamedConstraintDefinitionStart:
  85. // TODO: Mixins.
  86. return true;
  87. default:
  88. return false;
  89. }
  90. }
  91. // Determines whether this node kind is the end of a deferred definition scope.
  92. static auto IsEndOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
  93. switch (kind) {
  94. case Parse::NodeKind::ClassDefinition:
  95. case Parse::NodeKind::ImplDefinition:
  96. case Parse::NodeKind::InterfaceDefinition:
  97. case Parse::NodeKind::NamedConstraintDefinition:
  98. // TODO: Mixins.
  99. return true;
  100. default:
  101. return false;
  102. }
  103. }
  104. // TODO: Investigate factoring out `IsStartOfDeferredDefinitionScope` and
  105. // `IsEndOfDeferredDefinitionScope` in order to make `NodeIdTraversal`
  106. // reusable.
  107. auto NodeIdTraversal::Handle(Parse::NodeKind parse_kind) -> void {
  108. // When we reach the start of a deferred definition scope, add a task to the
  109. // worklist to check future skipped definitions in the new context.
  110. if (IsStartOfDeferredDefinitionScope(parse_kind)) {
  111. worklist().PushEnterDeferredDefinitionScope(*context_);
  112. }
  113. // When we reach the end of a deferred definition scope, add a task to the
  114. // worklist to leave the scope. If this is not a nested scope, start
  115. // checking the deferred definitions now.
  116. if (IsEndOfDeferredDefinitionScope(parse_kind)) {
  117. auto scope_kind = worklist().SuspendFinishedScopeAndPush(*context_);
  118. // If we have deferred tasks in this scope, perform them next.
  119. if (scope_kind ==
  120. DeferredDefinitionWorklist::FinishedScopeKind::NonNestedWithWork) {
  121. chunks_.back().checking_deferred_definitions = true;
  122. chunks_.back().next_worklist_index = chunks_.back().first_worklist_index;
  123. }
  124. }
  125. }
  126. auto NodeIdTraversal::PerformTask(
  127. DeferredDefinitionWorklist::EnterNestedDeferredDefinitionScope&& enter)
  128. -> void {
  129. CARBON_CHECK(enter.suspended_name,
  130. "Entering a scope with no suspension information.");
  131. context_->decl_name_stack().Restore(std::move(*enter.suspended_name));
  132. }
  133. auto NodeIdTraversal::PerformTask(
  134. DeferredDefinitionWorklist::LeaveNestedDeferredDefinitionScope&& /*leave*/)
  135. -> void {
  136. context_->decl_name_stack().PopScope();
  137. }
  138. auto NodeIdTraversal::PerformTask(
  139. DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition)
  140. -> void {
  141. auto& [definition_index, suspended_fn] = parse_definition;
  142. const auto& definition_info =
  143. context_->parse_tree().deferred_definitions().Get(definition_index);
  144. HandleFunctionDefinitionResume(*context_, definition_info.start_id,
  145. std::move(suspended_fn));
  146. auto range = Parse::Tree::PostorderIterator::MakeRange(
  147. definition_info.start_id, definition_info.definition_id);
  148. chunks_.push_back({.it = range.begin() + 1,
  149. .end = range.end(),
  150. .next_definition = next_deferred_definition_.index(),
  151. .checking_deferred_definitions = false,
  152. .first_worklist_index = worklist().size(),
  153. .next_worklist_index = worklist().size()});
  154. ++definition_index.index;
  155. next_deferred_definition_.SkipTo(definition_index);
  156. }
  157. auto NodeIdTraversal::PerformTask(
  158. DeferredDefinitionWorklist::DefineThunk&& define_thunk) -> void {
  159. BuildThunkDefinition(*context_, std::move(define_thunk));
  160. }
  161. NodeIdTraversal::NextDeferredDefinitionCache::NextDeferredDefinitionCache(
  162. const Parse::Tree* tree)
  163. : tree_(tree) {
  164. SkipTo(Parse::DeferredDefinitionIndex(0));
  165. }
  166. // Set the specified deferred definition index as being the next one that
  167. // will be encountered.
  168. auto NodeIdTraversal::NextDeferredDefinitionCache::SkipTo(
  169. Parse::DeferredDefinitionIndex next_index) -> void {
  170. index_ = next_index;
  171. if (static_cast<size_t>(index_.index) ==
  172. tree_->deferred_definitions().size()) {
  173. start_id_ = Parse::NodeId::None;
  174. } else {
  175. start_id_ = tree_->deferred_definitions().Get(index_).start_id;
  176. }
  177. }
  178. } // namespace Carbon::Check