deferred_definition_worklist.h 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. #ifndef CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_WORKLIST_H_
  5. #define CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_WORKLIST_H_
  6. #include <optional>
  7. #include <variant>
  8. #include "common/ostream.h"
  9. #include "llvm/ADT/SmallVector.h"
  10. #include "toolchain/check/decl_name_stack.h"
  11. #include "toolchain/parse/tree.h"
  12. namespace Carbon::Check {
  13. // A worklist of pending tasks to perform to check deferred function definitions
  14. // in the right order.
  15. class DeferredDefinitionWorklist {
  16. public:
  17. // State saved for a function definition that has been suspended after
  18. // processing its declaration and before processing its body. This is used for
  19. // inline method handling.
  20. //
  21. // This type is large, so moves of this type should be avoided.
  22. struct SuspendedFunction : public MoveOnly<SuspendedFunction> {
  23. // The function that was declared.
  24. SemIR::FunctionId function_id;
  25. // The instruction ID of the FunctionDecl instruction.
  26. SemIR::InstId decl_id;
  27. // The declaration name information of the function. This includes the scope
  28. // information, such as parameter names.
  29. DeclNameStack::SuspendedName saved_name_state;
  30. };
  31. // A worklist task that indicates we should check a deferred function
  32. // definition that we previously skipped.
  33. //
  34. // This type is large, so moves of this type should be avoided.
  35. struct CheckSkippedDefinition : public MoveOnly<CheckSkippedDefinition> {
  36. // The definition that we skipped.
  37. Parse::DeferredDefinitionIndex definition_index;
  38. // The suspended function.
  39. SuspendedFunction suspended_fn;
  40. };
  41. // A description of a thunk.
  42. struct ThunkInfo {
  43. SemIR::FunctionId signature_id;
  44. SemIR::FunctionId function_id;
  45. SemIR::InstId decl_id;
  46. SemIR::InstId callee_id;
  47. };
  48. // A worklist task that indicates we should define a thunk that was previously
  49. // declared.
  50. //
  51. // This type is large, so moves of this type should be avoided.
  52. struct DefineThunk : public MoveOnly<DefineThunk> {
  53. ThunkInfo info;
  54. ScopeStack::SuspendedScope scope;
  55. };
  56. // A worklist task that indicates we should enter a nested deferred definition
  57. // scope. We delay processing the contents of nested deferred definition
  58. // scopes until we reach the end of the parent scope. For example:
  59. //
  60. // ```
  61. // class A {
  62. // class B {
  63. // fn F() -> A { return {}; }
  64. // }
  65. // } // A.B.F is type-checked here, with A complete.
  66. //
  67. // fn F() {
  68. // class C {
  69. // fn G() {}
  70. // } // C.G is type-checked here.
  71. // }
  72. // ```
  73. //
  74. // This type is large, so moves of this type should be avoided.
  75. struct EnterNestedDeferredDefinitionScope
  76. : public MoveOnly<EnterNestedDeferredDefinitionScope> {
  77. // The suspended scope. This is only set once we reach the end of the scope.
  78. std::optional<DeclNameStack::SuspendedName> suspended_name;
  79. };
  80. // A worklist task that indicates we should leave a nested deferred definition
  81. // scope.
  82. struct LeaveNestedDeferredDefinitionScope {};
  83. // A pending type-checking task.
  84. using Task = std::variant<CheckSkippedDefinition, DefineThunk,
  85. EnterNestedDeferredDefinitionScope,
  86. LeaveNestedDeferredDefinitionScope>;
  87. explicit DeferredDefinitionWorklist(llvm::raw_ostream* vlog_stream);
  88. // Suspends the current function definition and pushes a task onto the
  89. // worklist to finish it later.
  90. auto SuspendFunctionAndPush(
  91. Parse::DeferredDefinitionIndex index,
  92. llvm::function_ref<auto()->SuspendedFunction> suspend) -> void;
  93. // Suspends the current thunk scope and pushes a task onto the worklist to
  94. // define it later.
  95. auto SuspendThunkAndPush(Context& context, ThunkInfo info) -> void;
  96. // Pushes a task to re-enter a function scope, so that functions defined
  97. // within it are type-checked in the right context. Returns whether a
  98. // non-nested scope was entered.
  99. auto PushEnterDeferredDefinitionScope(Context& context) -> bool;
  100. // The kind of scope that we just finished.
  101. enum class FinishedScopeKind {
  102. // We finished a nested scope. No further action is taken at this point.
  103. Nested,
  104. // We finished a non-nested scope that has no further actions to perform.
  105. NonNestedEmpty,
  106. // We finished a non-nested scope that has further actions to perform.
  107. NonNestedWithWork,
  108. };
  109. // Suspends the current deferred definition scope, which is finished but still
  110. // on the decl_name_stack, and pushes a task to leave the scope when we're
  111. // type-checking deferred definitions. Returns `true` if the current list of
  112. // deferred definitions should be type-checked immediately.
  113. auto SuspendFinishedScopeAndPush(Context& context) -> FinishedScopeKind;
  114. // Returns the current size of the worklist.
  115. auto size() const -> size_t { return worklist_.size(); }
  116. // Truncates the worklist to the given size.
  117. auto truncate(int new_size) -> void { worklist_.truncate(new_size); }
  118. // Gets the given item on the worklist.
  119. auto operator[](int index) -> Task& { return worklist_[index]; }
  120. // CHECK that the work list has no further work.
  121. auto VerifyEmpty() {
  122. CARBON_CHECK(worklist_.empty() && entered_scopes_.empty(),
  123. "Tasks left behind on worklist.");
  124. }
  125. private:
  126. // A deferred definition scope that is currently still open.
  127. struct EnteredScope {
  128. // Whether this scope is nested immediately within the enclosing scope. If
  129. // so, deferred definitions are not processed at the end of this scope.
  130. bool nested;
  131. // The index in worklist_ of the first task in this scope. For a nested
  132. // scope, this is a EnterNestedDeferredDefinitionScope task.
  133. size_t worklist_start_index;
  134. // The corresponding lexical scope index.
  135. ScopeIndex scope_index;
  136. };
  137. llvm::raw_ostream* vlog_stream_;
  138. // A worklist of type-checking tasks we'll need to do later.
  139. //
  140. // Don't allocate any inline storage here. A Task is fairly large, so we never
  141. // want this to live on the stack. Instead, we reserve space in the
  142. // constructor for a fairly large number of deferred definitions.
  143. llvm::SmallVector<Task, 0> worklist_;
  144. // The deferred definition scopes for the current checking actions.
  145. llvm::SmallVector<EnteredScope> entered_scopes_;
  146. };
  147. } // namespace Carbon::Check
  148. #endif // CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_WORKLIST_H_