impl.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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_SEM_IR_IMPL_H_
  5. #define CARBON_TOOLCHAIN_SEM_IR_IMPL_H_
  6. #include <utility>
  7. #include "common/map.h"
  8. #include "toolchain/base/value_store.h"
  9. #include "toolchain/sem_ir/entity_with_params_base.h"
  10. #include "toolchain/sem_ir/facet_type_info.h"
  11. #include "toolchain/sem_ir/ids.h"
  12. namespace Carbon::SemIR {
  13. struct ImplFields {
  14. // This following members always have values and do not change.
  15. // The type for which the impl is implementing a constraint.
  16. TypeInstId self_id;
  17. // The constraint that the impl implements.
  18. TypeInstId constraint_id;
  19. // The single interface to implement from `constraint_id`.
  20. // The members are `None` if `constraint_id` isn't complete or doesn't
  21. // correspond to a single interface.
  22. SpecificInterface interface;
  23. // The witness for the impl. This can be `BuiltinErrorInst` or an import
  24. // reference. Note that the entries in the witness are updated at the end of
  25. // the impl definition.
  26. InstId witness_id = InstId::None;
  27. // The following members are set at the `{` of the impl definition.
  28. // The impl scope.
  29. NameScopeId scope_id = NameScopeId::None;
  30. // The first block of the impl body.
  31. // TODO: Handle control flow in the impl body, such as if-expressions.
  32. InstBlockId body_block_id = InstBlockId::None;
  33. // Whether the impl declaration is marked `final`.
  34. bool is_final;
  35. // The following members are set at the `}` of the impl definition.
  36. bool defined = false;
  37. };
  38. // An implementation of a constraint. See EntityWithParamsBase regarding the
  39. // inheritance here.
  40. struct Impl : public EntityWithParamsBase,
  41. public ImplFields,
  42. public Printable<Impl> {
  43. auto Print(llvm::raw_ostream& out) const -> void {
  44. out << "{self: " << self_id << ", constraint: " << constraint_id
  45. << ", witness: " << witness_id << "}";
  46. }
  47. // This is false until we reach the `}` of the impl definition.
  48. auto is_complete() const -> bool { return defined; }
  49. // Determines whether this impl's definition has begun but not yet ended.
  50. auto is_being_defined() const -> bool {
  51. return has_definition_started() && !is_complete();
  52. }
  53. };
  54. // A collection of `Impl`s, which can be accessed by the self type and
  55. // constraint implemented.
  56. class ImplStore {
  57. private:
  58. // An ID of either a single impl or a lookup bucket.
  59. class ImplOrLookupBucketId : public IdBase<ImplOrLookupBucketId> {
  60. public:
  61. static constexpr llvm::StringLiteral Label = "impl_or_lookup_bucket";
  62. // An ID with no value, corresponding to to ImplId::None.
  63. static const ImplOrLookupBucketId None;
  64. static auto ForImplId(ImplId impl_id) -> ImplOrLookupBucketId {
  65. return ImplOrLookupBucketId(impl_id.index);
  66. }
  67. static auto ForBucket(int bucket) -> ImplOrLookupBucketId {
  68. return ImplOrLookupBucketId(ImplId::NoneIndex - bucket - 1);
  69. }
  70. // Returns whether this ID represents a bucket index, rather than an ImplId.
  71. // `None` is not a bucket index.
  72. auto is_bucket() const { return index < ImplId::NoneIndex; }
  73. // Returns the bucket index represented by this ID. Requires is_bucket().
  74. auto bucket() const -> int {
  75. CARBON_CHECK(is_bucket());
  76. return ImplId::NoneIndex - index - 1;
  77. }
  78. // Returns the ImplId index represented by this ID. Requires !is_bucket().
  79. auto impl_id() const -> ImplId {
  80. CARBON_CHECK(!is_bucket());
  81. return ImplId(index);
  82. }
  83. private:
  84. explicit constexpr ImplOrLookupBucketId(int index) : IdBase(index) {}
  85. };
  86. public:
  87. // A reference to an impl lookup bucket. This represents a list of impls with
  88. // the same self and constraint type.
  89. //
  90. // The bucket is held indirectly as an `ImplOrLookupBucketId`, in one of three
  91. // states:
  92. //
  93. // - `ImplId::None` represents an empty bucket.
  94. // - An `ImplId` value represents a bucket with exactly one impl. This is
  95. // expected to be by far the most common case.
  96. // - A lookup bucket index represents an index within the `ImplStore`'s
  97. // array of variable-sized lookup buckets.
  98. class LookupBucketRef {
  99. public:
  100. LookupBucketRef(ImplStore& store, ImplOrLookupBucketId& id)
  101. : store_(&store), id_(&id), single_id_storage_(ImplId::None) {
  102. if (!id.is_bucket()) {
  103. single_id_storage_ = id.impl_id();
  104. }
  105. }
  106. auto begin() const -> const ImplId* {
  107. if (id_->is_bucket()) {
  108. return store_->lookup_buckets_[id_->bucket()].begin();
  109. }
  110. return &single_id_storage_;
  111. }
  112. auto end() const -> const ImplId* {
  113. if (id_->is_bucket()) {
  114. return store_->lookup_buckets_[id_->bucket()].end();
  115. }
  116. return &single_id_storage_ + (id_->has_value() ? 1 : 0);
  117. }
  118. // Adds an impl to this lookup bucket. Only impls from the current file and
  119. // its API file should be added in this way. Impls from other files do not
  120. // need to be found by impl redeclaration lookup so should not be added.
  121. auto push_back(ImplId impl_id) -> void {
  122. if (!id_->has_value()) {
  123. *id_ = ImplOrLookupBucketId::ForImplId(impl_id);
  124. single_id_storage_ = impl_id;
  125. } else if (!id_->is_bucket()) {
  126. auto first_id = id_->impl_id();
  127. *id_ = ImplOrLookupBucketId::ForBucket(store_->lookup_buckets_.size());
  128. store_->lookup_buckets_.push_back({first_id, impl_id});
  129. } else {
  130. store_->lookup_buckets_[id_->bucket()].push_back(impl_id);
  131. }
  132. }
  133. private:
  134. ImplStore* store_;
  135. ImplOrLookupBucketId* id_;
  136. // Storage for a single ImplId. Used to support iteration over the contents
  137. // of the bucket when it contains a single ImplId.
  138. ImplId single_id_storage_;
  139. };
  140. explicit ImplStore(File& sem_ir);
  141. // Returns a reference to the lookup bucket containing the list of impls with
  142. // this self type and constraint, or adds a new bucket if this is the first
  143. // time we've seen an impl of this kind. The lookup bucket reference remains
  144. // valid until this function is called again.
  145. auto GetOrAddLookupBucket(const Impl& impl) -> LookupBucketRef;
  146. // Adds the specified impl to the store. Does not add it to impl lookup.
  147. auto Add(Impl impl) -> ImplId { return values_.Add(impl); }
  148. // Returns a mutable value for an ID.
  149. auto Get(ImplId id) -> Impl& { return values_.Get(id); }
  150. // Returns the value for an ID.
  151. auto Get(ImplId id) const -> const Impl& { return values_.Get(id); }
  152. auto OutputYaml() const -> Yaml::OutputMapping {
  153. return values_.OutputYaml();
  154. }
  155. auto GetRawIndex(ImplId id) const -> int32_t {
  156. return values_.GetRawIndex(id);
  157. }
  158. // Collects memory usage of members.
  159. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
  160. -> void {
  161. mem_usage.Collect(MemUsage::ConcatLabel(label, "values_"), values_);
  162. mem_usage.Collect(MemUsage::ConcatLabel(label, "lookup_"), lookup_);
  163. }
  164. auto values() const [[clang::lifetimebound]]
  165. -> ValueStore<ImplId, Impl>::Range {
  166. return values_.values();
  167. }
  168. auto size() const -> size_t { return values_.size(); }
  169. auto enumerate() const [[clang::lifetimebound]] -> auto {
  170. return values_.enumerate();
  171. }
  172. private:
  173. File& sem_ir_;
  174. ValueStore<ImplId, Impl> values_;
  175. Map<std::pair<InstId, SpecificInterface>, ImplOrLookupBucketId> lookup_;
  176. // Buckets with at least 2 entries, which will be rare; see LookupBucketRef.
  177. llvm::SmallVector<llvm::SmallVector<ImplId, 2>> lookup_buckets_;
  178. };
  179. inline constexpr ImplStore::ImplOrLookupBucketId
  180. ImplStore::ImplOrLookupBucketId::None(NoneIndex);
  181. } // namespace Carbon::SemIR
  182. #endif // CARBON_TOOLCHAIN_SEM_IR_IMPL_H_