scope_stack.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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 "llvm/ADT/DenseSet.h"
  7. #include "llvm/ADT/SmallVector.h"
  8. #include "toolchain/check/lexical_lookup.h"
  9. #include "toolchain/check/scope_index.h"
  10. #include "toolchain/sem_ir/file.h"
  11. #include "toolchain/sem_ir/ids.h"
  12. namespace Carbon::Check {
  13. // A stack of lexical and semantic scopes that we are currently performing
  14. // checking within.
  15. class ScopeStack {
  16. public:
  17. explicit ScopeStack(const StringStoreWrapper<IdentifierId>& identifiers)
  18. : lexical_lookup_(identifiers) {}
  19. // A scope in which `break` and `continue` can be used.
  20. struct BreakContinueScope {
  21. SemIR::InstBlockId break_target;
  22. SemIR::InstBlockId continue_target;
  23. };
  24. // A scope in which `return` can be used.
  25. struct ReturnScope {
  26. // The declaration from which we can return. Inside a function, this will
  27. // be a `FunctionDecl`.
  28. SemIR::InstId decl_id;
  29. // The value corresponding to the current `returned var`, if any. Will be
  30. // set and unset as `returned var`s are declared and go out of scope.
  31. SemIR::InstId returned_var = SemIR::InstId::Invalid;
  32. };
  33. // A non-lexical scope in which unqualified lookup may be required.
  34. struct NonLexicalScope {
  35. // The index of the scope in the scope stack.
  36. ScopeIndex scope_index;
  37. // The corresponding name scope.
  38. SemIR::NameScopeId name_scope_id;
  39. };
  40. // Information about a scope that has been temporarily removed from the stack.
  41. struct SuspendedScope;
  42. // Pushes a scope onto scope_stack_. NameScopeId::Invalid is used for new
  43. // scopes. lexical_lookup_has_load_error is used to limit diagnostics when a
  44. // given namespace may contain a mix of both successful and failed name
  45. // imports.
  46. auto Push(SemIR::InstId scope_inst_id = SemIR::InstId::Invalid,
  47. SemIR::NameScopeId scope_id = SemIR::NameScopeId::Invalid,
  48. bool lexical_lookup_has_load_error = false) -> void;
  49. // Pops the top scope from scope_stack_, cleaning up names from
  50. // lexical_lookup_.
  51. auto Pop() -> void;
  52. // Pops the top scope from scope_stack_ if it contains no names.
  53. auto PopIfEmpty() -> void {
  54. if (scope_stack_.back().names.empty()) {
  55. Pop();
  56. }
  57. }
  58. // Pops scopes until we return to the specified scope index.
  59. auto PopTo(ScopeIndex index) -> void;
  60. // Returns the scope index associated with the current scope.
  61. auto PeekIndex() const -> ScopeIndex { return Peek().index; }
  62. // Returns the name scope associated with the current lexical scope, if any.
  63. auto PeekNameScopeId() const -> SemIR::NameScopeId { return Peek().scope_id; }
  64. // Returns the instruction associated with the current scope, or Invalid if
  65. // there is no such instruction, such as for a block scope.
  66. auto PeekInstId() const -> SemIR::InstId { return Peek().scope_inst_id; }
  67. // Returns the current scope, if it is of the specified kind. Otherwise,
  68. // returns nullopt.
  69. template <typename InstT>
  70. auto GetCurrentScopeAs(const SemIR::File& sem_ir) -> std::optional<InstT> {
  71. auto inst_id = PeekInstId();
  72. if (!inst_id.is_valid()) {
  73. return std::nullopt;
  74. }
  75. return sem_ir.insts().TryGetAs<InstT>(inst_id);
  76. }
  77. // If there is no `returned var` in scope, sets the given instruction to be
  78. // the current `returned var` and returns an invalid instruction ID. If there
  79. // is already a `returned var`, returns it instead.
  80. auto SetReturnedVarOrGetExisting(SemIR::InstId inst_id) -> SemIR::InstId;
  81. // Looks up the name `name_id` in the current scope. Returns the existing
  82. // lookup result, if any.
  83. auto LookupInCurrentScope(SemIR::NameId name_id) -> SemIR::InstId;
  84. // Looks up the name `name_id` in the current scope and its enclosing scopes.
  85. // Returns the innermost lexical lookup result, if any, along with a list of
  86. // non-lexical scopes in which lookup should also be performed, ordered from
  87. // outermost to innermost.
  88. auto LookupInEnclosingScopes(SemIR::NameId name_id)
  89. -> std::pair<SemIR::InstId, llvm::ArrayRef<NonLexicalScope>>;
  90. // Looks up the name `name_id` in the current scope. Returns the existing
  91. // instruction if any, and otherwise adds the name with the value `target_id`
  92. // and returns Invalid.
  93. auto LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id)
  94. -> SemIR::InstId;
  95. // Temporarily removes the top of the stack and its lexical lookup results.
  96. auto Suspend() -> SuspendedScope;
  97. // Restores a suspended scope stack entry.
  98. auto Restore(SuspendedScope scope) -> void;
  99. // Runs verification that the processing cleanly finished.
  100. auto VerifyOnFinish() -> void;
  101. auto return_scope_stack() -> llvm::SmallVector<ReturnScope>& {
  102. return return_scope_stack_;
  103. }
  104. auto break_continue_stack() -> llvm::SmallVector<BreakContinueScope>& {
  105. return break_continue_stack_;
  106. }
  107. private:
  108. // An entry in scope_stack_.
  109. struct ScopeStackEntry {
  110. // The sequential index of this scope entry within the file.
  111. ScopeIndex index;
  112. // The instruction associated with this entry, if any. This can be one of:
  113. //
  114. // - A `ClassDecl`, for a class definition scope.
  115. // - A `FunctionDecl`, for the outermost scope in a function
  116. // definition.
  117. // - Invalid, for any other scope.
  118. SemIR::InstId scope_inst_id;
  119. // The name scope associated with this entry, if any.
  120. SemIR::NameScopeId scope_id;
  121. // Whether lexical_lookup_ has load errors from this scope or an enclosing
  122. // scope.
  123. bool lexical_lookup_has_load_error;
  124. // Whether a `returned var` was introduced in this scope, and needs to be
  125. // unregistered when the scope ends.
  126. bool has_returned_var = false;
  127. // Names which are registered with lexical_lookup_, and will need to be
  128. // unregistered when the scope ends.
  129. llvm::DenseSet<SemIR::NameId> names;
  130. // TODO: This likely needs to track things which need to be destructed.
  131. };
  132. auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); }
  133. // Returns whether lexical lookup currently has any load errors.
  134. auto LexicalLookupHasLoadError() const -> bool {
  135. return !scope_stack_.empty() &&
  136. scope_stack_.back().lexical_lookup_has_load_error;
  137. }
  138. // A stack of scopes from which we can `return`.
  139. llvm::SmallVector<ReturnScope> return_scope_stack_;
  140. // A stack of `break` and `continue` targets.
  141. llvm::SmallVector<BreakContinueScope> break_continue_stack_;
  142. // A stack for scope context.
  143. llvm::SmallVector<ScopeStackEntry> scope_stack_;
  144. // Information about non-lexical scopes. This is a subset of the entries and
  145. // the information in scope_stack_.
  146. llvm::SmallVector<NonLexicalScope> non_lexical_scope_stack_;
  147. // The index of the next scope that will be pushed onto scope_stack_. The
  148. // first is always the package scope.
  149. ScopeIndex next_scope_index_ = ScopeIndex::Package;
  150. // Tracks lexical lookup results.
  151. LexicalLookup lexical_lookup_;
  152. };
  153. struct ScopeStack::SuspendedScope {
  154. // The suspended scope stack entry.
  155. ScopeStackEntry entry;
  156. // The lexical lookups for the suspended entry. The inline size is an attempt
  157. // to keep the size of a `SuspendedFunction` reasonable while avoiding heap
  158. // allocations most of the time.
  159. llvm::SmallVector<LexicalLookup::SuspendedResult, 8> suspended_lookups;
  160. };
  161. } // namespace Carbon::Check
  162. #endif // CARBON_TOOLCHAIN_CHECK_SCOPE_STACK_H_