node_id_traversal.cpp 7.3 KB

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