impl.h 7.8 KB

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