custom_witness.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. #include "toolchain/check/custom_witness.h"
  5. #include "toolchain/base/kind_switch.h"
  6. #include "toolchain/check/function.h"
  7. #include "toolchain/check/impl.h"
  8. #include "toolchain/check/import_ref.h"
  9. #include "toolchain/check/inst.h"
  10. #include "toolchain/check/name_lookup.h"
  11. #include "toolchain/check/type.h"
  12. #include "toolchain/sem_ir/typed_insts.h"
  13. namespace Carbon::Check {
  14. // Given a value whose type `IsFacetTypeOrError`, returns the corresponding
  15. // type.
  16. static auto GetFacetAsType(Context& context,
  17. SemIR::ConstantId facet_or_type_const_id)
  18. -> SemIR::TypeId {
  19. auto facet_or_type_id =
  20. context.constant_values().GetInstId(facet_or_type_const_id);
  21. auto type_type_id = context.insts().Get(facet_or_type_id).type_id();
  22. CARBON_CHECK(context.types().IsFacetTypeOrError(type_type_id));
  23. if (context.types().Is<SemIR::FacetType>(type_type_id)) {
  24. // It's a facet; access its type.
  25. facet_or_type_id = context.types().GetInstId(
  26. GetFacetAccessType(context, facet_or_type_id));
  27. }
  28. return context.types().GetTypeIdForTypeInstId(facet_or_type_id);
  29. }
  30. // Returns a manufactured no-op function with `self_const_id` as parameter.
  31. // TODO: This is somewhat temporary, but we may want to keep something similar
  32. // long-term where names are based on type structure (potentially also for
  33. // copy/move).
  34. static auto MakeNoOpFunction(Context& context, SemIR::LocId loc_id,
  35. SemIR::NameScopeId name_scope_id,
  36. SemIR::NameId name_id,
  37. SemIR::ConstantId self_const_id) -> SemIR::InstId {
  38. auto self_type_id = GetFacetAsType(context, self_const_id);
  39. return MakeBuiltinFunction(context, loc_id, SemIR::BuiltinFunctionKind::NoOp,
  40. name_scope_id, name_id,
  41. {.self_type_id = self_type_id});
  42. }
  43. auto BuildCustomWitness(Context& context, SemIR::LocId loc_id,
  44. SemIR::ConstantId query_self_const_id,
  45. SemIR::SpecificInterfaceId query_specific_interface_id,
  46. llvm::ArrayRef<SemIR::InstId> values) -> SemIR::InstId {
  47. const auto query_specific_interface =
  48. context.specific_interfaces().Get(query_specific_interface_id);
  49. const auto& interface =
  50. context.interfaces().Get(query_specific_interface.interface_id);
  51. auto assoc_entities =
  52. context.inst_blocks().GetOrEmpty(interface.associated_entities_id);
  53. if (assoc_entities.size() != values.size()) {
  54. context.TODO(loc_id, ("Unsupported definition of interface " +
  55. context.names().GetFormatted(interface.name_id))
  56. .str());
  57. return SemIR::ErrorInst::InstId;
  58. }
  59. llvm::SmallVector<SemIR::InstId> entries;
  60. // Build a witness with the current contents of the witness table. This will
  61. // grow as we progress through the impl. In theory this will build O(n^2)
  62. // table entries, but in practice n <= 2, so that's OK.
  63. //
  64. // This is necessary because later associated entities may refer to earlier
  65. // associated entities in their signatures. In particular, an associated
  66. // result type may be used as the return type of an associated function.
  67. auto make_witness = [&] {
  68. return context.constant_values().GetInstId(
  69. EvalOrAddInst<SemIR::CustomWitness>(
  70. context, loc_id,
  71. {.type_id =
  72. GetSingletonType(context, SemIR::WitnessType::TypeInstId),
  73. .elements_id = context.inst_blocks().Add(entries),
  74. .query_specific_interface_id = query_specific_interface_id}));
  75. };
  76. // Fill in the witness table.
  77. for (const auto& [assoc_entity_id, value_id] :
  78. llvm::zip_equal(assoc_entities, values)) {
  79. LoadImportRef(context, assoc_entity_id);
  80. auto decl_id =
  81. context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
  82. context.sem_ir(), query_specific_interface.specific_id,
  83. assoc_entity_id));
  84. CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity");
  85. auto decl = context.insts().Get(decl_id);
  86. CARBON_KIND_SWITCH(decl) {
  87. case CARBON_KIND(SemIR::StructValue struct_value): {
  88. if (struct_value.type_id == SemIR::ErrorInst::TypeId) {
  89. return SemIR::ErrorInst::InstId;
  90. }
  91. auto self_type_id = GetFacetAsType(context, query_self_const_id);
  92. // TODO: If a thunk is needed, this will build a different value each
  93. // time it's called, so we won't properly deduplicate repeated
  94. // witnesses.
  95. // TODO: Skip calling make_witness if this function signature doesn't
  96. // involve `Self`.
  97. entries.push_back(CheckAssociatedFunctionImplementation(
  98. context,
  99. context.types().GetAs<SemIR::FunctionType>(struct_value.type_id),
  100. query_specific_interface.specific_id, value_id, self_type_id,
  101. make_witness(),
  102. /*defer_thunk_definition=*/false));
  103. break;
  104. }
  105. case SemIR::AssociatedConstantDecl::Kind: {
  106. context.TODO(loc_id,
  107. "Associated constant in interface with synthesized impl");
  108. return SemIR::ErrorInst::InstId;
  109. }
  110. default:
  111. CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId,
  112. "Unexpected kind of associated entity {0}", decl);
  113. return SemIR::ErrorInst::InstId;
  114. }
  115. }
  116. // TODO: Consider building one witness after all associated constants, and
  117. // then a second after all associated functions, rather than building one in
  118. // each `StructValue`. Right now the code is written assuming at most one
  119. // function, though this CHECK can be removed as a temporary workaround.
  120. CARBON_CHECK(entries.size() <= 1,
  121. "TODO: Support multiple associated functions");
  122. return make_witness();
  123. }
  124. auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id)
  125. -> CoreInterface {
  126. const auto& interface = context.interfaces().Get(interface_id);
  127. if (!context.name_scopes().IsCorePackage(interface.parent_scope_id) ||
  128. !interface.name_id.AsIdentifierId().has_value()) {
  129. return CoreInterface::Unknown;
  130. }
  131. for (auto [core_identifier, core_interface] :
  132. {std::pair{CoreIdentifier::Copy, CoreInterface::Copy},
  133. std::pair{CoreIdentifier::Destroy, CoreInterface::Destroy}}) {
  134. if (interface.name_id ==
  135. context.core_identifiers().AddNameId(core_identifier)) {
  136. return core_interface;
  137. }
  138. }
  139. return CoreInterface::Unknown;
  140. }
  141. // Returns true if the `Self` should impl `Destroy`.
  142. static auto TypeCanDestroy(Context& context,
  143. SemIR::ConstantId query_self_const_id,
  144. SemIR::InterfaceId destroy_interface_id) -> bool {
  145. auto inst = context.insts().Get(context.constant_values().GetInstId(
  146. GetCanonicalFacetOrTypeValue(context, query_self_const_id)));
  147. // For facet values, look if the FacetType provides the same.
  148. if (auto facet_type =
  149. context.types().TryGetAs<SemIR::FacetType>(inst.type_id())) {
  150. const auto& info = context.facet_types().Get(facet_type->facet_type_id);
  151. for (auto interface : info.extend_constraints) {
  152. if (interface.interface_id == destroy_interface_id) {
  153. return true;
  154. }
  155. }
  156. }
  157. CARBON_KIND_SWITCH(inst) {
  158. case CARBON_KIND(SemIR::ClassType class_type): {
  159. auto class_info = context.classes().Get(class_type.class_id);
  160. // Incomplete and abstract classes can't be destroyed.
  161. // TODO: Return false if the object repr doesn't impl `Destroy`.
  162. // TODO: This should probably be skipped for all C++ types, but currently
  163. // must handle those for trivial destruction.
  164. return class_info.is_complete() &&
  165. class_info.inheritance_kind !=
  166. SemIR::Class::InheritanceKind::Abstract;
  167. }
  168. case SemIR::ArrayType::Kind:
  169. case SemIR::ConstType::Kind:
  170. case SemIR::MaybeUnformedType::Kind:
  171. case SemIR::PartialType::Kind:
  172. case SemIR::StructType::Kind:
  173. case SemIR::TupleType::Kind:
  174. // TODO: Return false for types that indirectly reference a type that
  175. // doesn't impl `Destroy`.
  176. return true;
  177. case SemIR::BoolType::Kind:
  178. case SemIR::PointerType::Kind:
  179. // Trivially destructible.
  180. return true;
  181. default:
  182. return false;
  183. }
  184. }
  185. auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
  186. CoreInterface core_interface,
  187. SemIR::ConstantId query_self_const_id,
  188. SemIR::SpecificInterfaceId query_specific_interface_id)
  189. -> SemIR::InstId {
  190. // TODO: Handle more interfaces, particularly copy, move, and conversion.
  191. if (core_interface != CoreInterface::Destroy) {
  192. return SemIR::InstId::None;
  193. }
  194. auto query_specific_interface =
  195. context.specific_interfaces().Get(query_specific_interface_id);
  196. if (!TypeCanDestroy(context, query_self_const_id,
  197. query_specific_interface.interface_id)) {
  198. return SemIR::InstId::None;
  199. }
  200. // TODO: This needs more complex logic to apply the correct behavior. Also, we
  201. // should avoid building a new function on each lookup since a similar query
  202. // could result in identical functions.
  203. auto noop_id = MakeNoOpFunction(
  204. context, loc_id, SemIR::NameScopeId::None,
  205. SemIR::NameId::ForIdentifier(context.identifiers().Add("DestroyOp")),
  206. query_self_const_id);
  207. return BuildCustomWitness(context, loc_id, query_self_const_id,
  208. query_specific_interface_id, {noop_id});
  209. }
  210. } // namespace Carbon::Check