scope_stack.h 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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_SCOPE_STACK_H_
  5. #define CARBON_TOOLCHAIN_CHECK_SCOPE_STACK_H_
  6. #include "common/array_stack.h"
  7. #include "common/set.h"
  8. #include "llvm/ADT/SmallVector.h"
  9. #include "toolchain/check/lexical_lookup.h"
  10. #include "toolchain/check/scope_index.h"
  11. #include "toolchain/sem_ir/file.h"
  12. #include "toolchain/sem_ir/ids.h"
  13. namespace Carbon::Check {
  14. // A stack of lexical and semantic scopes that we are currently performing
  15. // checking within.
  16. class ScopeStack {
  17. public:
  18. explicit ScopeStack(const CanonicalValueStore<IdentifierId>& identifiers)
  19. : lexical_lookup_(identifiers) {}
  20. // A scope in which `break` and `continue` can be used.
  21. struct BreakContinueScope {
  22. SemIR::InstBlockId break_target;
  23. SemIR::InstBlockId continue_target;
  24. };
  25. // A scope in which `return` can be used.
  26. struct ReturnScope {
  27. // The declaration from which we can return. Inside a function, this will
  28. // be a `FunctionDecl`.
  29. SemIR::InstId decl_id;
  30. // The value corresponding to the current `returned var`, if any. Will be
  31. // set and unset as `returned var`s are declared and go out of scope.
  32. SemIR::InstId returned_var = SemIR::InstId::Invalid;
  33. };
  34. // A non-lexical scope in which unqualified lookup may be required.
  35. struct NonLexicalScope {
  36. // The index of the scope in the scope stack.
  37. ScopeIndex scope_index;
  38. // The corresponding name scope.
  39. SemIR::NameScopeId name_scope_id;
  40. // The corresponding specific.
  41. SemIR::SpecificId specific_id;
  42. };
  43. // Information about a scope that has been temporarily removed from the stack.
  44. struct SuspendedScope;
  45. // Pushes a scope onto scope_stack_. NameScopeId::Invalid is used for new
  46. // scopes. lexical_lookup_has_load_error is used to limit diagnostics when a
  47. // given namespace may contain a mix of both successful and failed name
  48. // imports.
  49. auto Push(SemIR::InstId scope_inst_id = SemIR::InstId::Invalid,
  50. SemIR::NameScopeId scope_id = SemIR::NameScopeId::Invalid,
  51. SemIR::SpecificId specific_id = SemIR::SpecificId::Invalid,
  52. bool lexical_lookup_has_load_error = false) -> void;
  53. // Pops the top scope from scope_stack_, cleaning up names from
  54. // lexical_lookup_.
  55. auto Pop() -> void;
  56. // Pops the top scope from scope_stack_ if it contains no names.
  57. auto PopIfEmpty() -> void {
  58. if (scope_stack_.back().num_names == 0) {
  59. Pop();
  60. }
  61. }
  62. // Pops scopes until we return to the specified scope index.
  63. auto PopTo(ScopeIndex index) -> void;
  64. // Returns the scope index associated with the current scope.
  65. auto PeekIndex() const -> ScopeIndex { return Peek().index; }
  66. // Returns the name scope associated with the current lexical scope, if any.
  67. auto PeekNameScopeId() const -> SemIR::NameScopeId { return Peek().scope_id; }
  68. // Returns the instruction associated with the current scope, or Invalid if
  69. // there is no such instruction, such as for a block scope.
  70. auto PeekInstId() const -> SemIR::InstId { return Peek().scope_inst_id; }
  71. // Returns the specific associated with the innermost enclosing scope that is
  72. // associated with a specific. This will generally be the self specific of the
  73. // innermost enclosing generic, as there is no way to enter any other specific
  74. // scope.
  75. auto PeekSpecificId() const -> SemIR::SpecificId {
  76. return Peek().specific_id;
  77. }
  78. // Returns the current scope, if it is of the specified kind. Otherwise,
  79. // returns nullopt.
  80. template <typename InstT>
  81. auto GetCurrentScopeAs(const SemIR::File& sem_ir) -> std::optional<InstT> {
  82. auto inst_id = PeekInstId();
  83. if (!inst_id.is_valid()) {
  84. return std::nullopt;
  85. }
  86. return sem_ir.insts().TryGetAs<InstT>(inst_id);
  87. }
  88. // If there is no `returned var` in scope, sets the given instruction to be
  89. // the current `returned var` and returns an invalid instruction ID. If there
  90. // is already a `returned var`, returns it instead.
  91. auto SetReturnedVarOrGetExisting(SemIR::InstId inst_id) -> SemIR::InstId;
  92. // Looks up the name `name_id` in the current scope. Returns the existing
  93. // lookup result, if any.
  94. auto LookupInCurrentScope(SemIR::NameId name_id) -> SemIR::InstId;
  95. // Looks up the name `name_id` in the current scope and related lexical
  96. // scopes. Returns the innermost lexical lookup result, if any, along with a
  97. // list of non-lexical scopes in which lookup should also be performed,
  98. // ordered from outermost to innermost.
  99. auto LookupInLexicalScopes(SemIR::NameId name_id)
  100. -> std::pair<SemIR::InstId, llvm::ArrayRef<NonLexicalScope>>;
  101. // Looks up the name `name_id` in the current scope. Returns the existing
  102. // instruction if any, and otherwise adds the name with the value `target_id`
  103. // and returns Invalid.
  104. auto LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id)
  105. -> SemIR::InstId;
  106. // Prepares to add a compile-time binding in the current scope, and returns
  107. // its index. The added binding must then be pushed using
  108. // `PushCompileTimeBinding`.
  109. auto AddCompileTimeBinding() -> SemIR::CompileTimeBindIndex {
  110. auto index = scope_stack_.back().next_compile_time_bind_index;
  111. ++scope_stack_.back().next_compile_time_bind_index.index;
  112. return index;
  113. }
  114. // Pushes a compile-time binding into the current scope.
  115. auto PushCompileTimeBinding(SemIR::InstId bind_id) -> void {
  116. compile_time_binding_stack_.AppendToTop(bind_id);
  117. }
  118. // Temporarily removes the top of the stack and its lexical lookup results.
  119. auto Suspend() -> SuspendedScope;
  120. // Restores a suspended scope stack entry.
  121. auto Restore(SuspendedScope scope) -> void;
  122. // Runs verification that the processing cleanly finished.
  123. auto VerifyOnFinish() -> void;
  124. auto return_scope_stack() -> llvm::SmallVector<ReturnScope>& {
  125. return return_scope_stack_;
  126. }
  127. auto break_continue_stack() -> llvm::SmallVector<BreakContinueScope>& {
  128. return break_continue_stack_;
  129. }
  130. auto compile_time_bindings_stack() -> ArrayStack<SemIR::InstId>& {
  131. return compile_time_binding_stack_;
  132. }
  133. private:
  134. // An entry in scope_stack_.
  135. struct ScopeStackEntry {
  136. // The sequential index of this scope entry within the file.
  137. ScopeIndex index;
  138. // The instruction associated with this entry, if any. This can be one of:
  139. //
  140. // - A `ClassDecl`, for a class definition scope.
  141. // - A `FunctionDecl`, for the outermost scope in a function
  142. // definition.
  143. // - Invalid, for any other scope.
  144. SemIR::InstId scope_inst_id;
  145. // The name scope associated with this entry, if any.
  146. SemIR::NameScopeId scope_id;
  147. // The specific associated with this entry, if any.
  148. SemIR::SpecificId specific_id;
  149. // The next compile-time binding index to allocate in this scope.
  150. SemIR::CompileTimeBindIndex next_compile_time_bind_index;
  151. // Whether lexical_lookup_ has load errors from this scope or an ancestor
  152. // scope.
  153. bool lexical_lookup_has_load_error;
  154. // Whether a `returned var` was introduced in this scope, and needs to be
  155. // unregistered when the scope ends.
  156. bool has_returned_var = false;
  157. // Whether there are any ids in the `names` set.
  158. int num_names = 0;
  159. // Names which are registered with lexical_lookup_, and will need to be
  160. // unregistered when the scope ends.
  161. Set<SemIR::NameId> names = {};
  162. // TODO: This likely needs to track things which need to be destructed.
  163. };
  164. auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); }
  165. // Returns whether lexical lookup currently has any load errors.
  166. auto LexicalLookupHasLoadError() const -> bool {
  167. return !scope_stack_.empty() &&
  168. scope_stack_.back().lexical_lookup_has_load_error;
  169. }
  170. // A stack of scopes from which we can `return`.
  171. llvm::SmallVector<ReturnScope> return_scope_stack_;
  172. // A stack of `break` and `continue` targets.
  173. llvm::SmallVector<BreakContinueScope> break_continue_stack_;
  174. // A stack for scope context.
  175. llvm::SmallVector<ScopeStackEntry> scope_stack_;
  176. // Information about non-lexical scopes. This is a subset of the entries and
  177. // the information in scope_stack_.
  178. llvm::SmallVector<NonLexicalScope> non_lexical_scope_stack_;
  179. // A stack of the current compile time bindings.
  180. ArrayStack<SemIR::InstId> compile_time_binding_stack_;
  181. // The index of the next scope that will be pushed onto scope_stack_. The
  182. // first is always the package scope.
  183. ScopeIndex next_scope_index_ = ScopeIndex::Package;
  184. // Tracks lexical lookup results.
  185. LexicalLookup lexical_lookup_;
  186. };
  187. struct ScopeStack::SuspendedScope {
  188. // An item that was suspended within this scope. This represents either a
  189. // lexical lookup entry in this scope, or a compile time binding entry in this
  190. // scope.
  191. //
  192. // TODO: For compile-time bindings, the common case is that they will both
  193. // have a suspended lexical lookup entry and a suspended compile time binding
  194. // entry. We should be able to store that as a single ScopeItem rather than
  195. // two.
  196. struct ScopeItem {
  197. static constexpr uint32_t IndexForCompileTimeBinding = -1;
  198. // The scope index for a LexicalLookup::SuspendedResult, or
  199. // CompileTimeBindingIndex for a suspended compile time binding.
  200. uint32_t index;
  201. // The instruction within the scope.
  202. SemIR::InstId inst_id;
  203. };
  204. // The suspended scope stack entry.
  205. ScopeStackEntry entry;
  206. // The list of items that were within this scope when it was suspended. The
  207. // inline size is an attempt to keep the size of a `SuspendedFunction`
  208. // reasonable while avoiding heap allocations most of the time.
  209. llvm::SmallVector<ScopeItem, 8> suspended_items;
  210. };
  211. } // namespace Carbon::Check
  212. #endif // CARBON_TOOLCHAIN_CHECK_SCOPE_STACK_H_