impl.h 7.0 KB

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