custom_witness.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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/impl.h"
  7. #include "toolchain/check/import_ref.h"
  8. #include "toolchain/check/inst.h"
  9. #include "toolchain/check/name_lookup.h"
  10. #include "toolchain/check/pattern.h"
  11. #include "toolchain/check/pattern_match.h"
  12. #include "toolchain/check/type.h"
  13. namespace Carbon::Check {
  14. // Given a value whose type `IsFacetTypeOrError`, returns the corresponding
  15. // type.
  16. static auto GetFacetAsType(Context& context, SemIR::LocId loc_id,
  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 = GetOrAddInst<SemIR::FacetAccessType>(
  26. context, loc_id,
  27. {.type_id = SemIR::TypeType::TypeId,
  28. .facet_value_inst_id = facet_or_type_id});
  29. }
  30. return context.types().GetTypeIdForTypeInstId(facet_or_type_id);
  31. }
  32. // Returns a manufactured no-op function with `self_const_id` as parameter.
  33. // TODO: This is somewhat temporary, but we may want to keep something similar
  34. // long-term where names are based on type structure (potentially also for
  35. // copy/move). It'll probably be good to look at refactoring with function
  36. // construction in thunk.cpp and cpp/import.cpp.
  37. static auto MakeNoOpFunction(Context& context, SemIR::LocId loc_id,
  38. SemIR::ConstantId self_const_id,
  39. SemIR::SpecificId specific_id) -> SemIR::InstId {
  40. // Build the parameters, with `[ref self: Self]`
  41. context.scope_stack().PushForDeclName();
  42. context.inst_block_stack().Push();
  43. context.pattern_block_stack().Push();
  44. BeginSubpattern(context);
  45. auto type_id = GetFacetAsType(context, loc_id, self_const_id);
  46. SemIR::ExprRegionId type_expr_region_id =
  47. EndSubpatternAsExpr(context, context.types().GetInstId(type_id));
  48. auto self_param_id = AddParamPattern(
  49. context, loc_id, SemIR::NameId::SelfValue, type_expr_region_id, type_id,
  50. /*is_ref=*/true);
  51. auto implicit_param_patterns_id = context.inst_blocks().Add({self_param_id});
  52. auto call_params_id =
  53. CalleePatternMatch(context, implicit_param_patterns_id,
  54. /*param_patterns_id=*/SemIR::InstBlockId::Empty,
  55. /*return_patterns_id=*/SemIR::InstBlockId::None);
  56. auto pattern_block_id = context.pattern_block_stack().Pop();
  57. auto decl_block_id = context.inst_block_stack().Pop();
  58. context.scope_stack().Pop();
  59. // Add the function declaration.
  60. SemIR::FunctionDecl function_decl = {.type_id = SemIR::TypeId::None,
  61. .function_id = SemIR::FunctionId::None,
  62. .decl_block_id = decl_block_id};
  63. auto noop_id = AddPlaceholderInstInNoBlock(
  64. context, SemIR::LocIdAndInst::UncheckedLoc(loc_id, function_decl));
  65. auto noop_name_id =
  66. SemIR::NameId::ForIdentifier(context.identifiers().Add("DestroyOp"));
  67. // Build the function entity.
  68. auto noop_function = SemIR::Function{
  69. {
  70. .name_id = noop_name_id,
  71. .parent_scope_id = SemIR::NameScopeId::None,
  72. .generic_id = SemIR::GenericId::None,
  73. .first_param_node_id = Parse::NodeId::None,
  74. .last_param_node_id = Parse::NodeId::None,
  75. .pattern_block_id = pattern_block_id,
  76. .implicit_param_patterns_id = implicit_param_patterns_id,
  77. .param_patterns_id = SemIR::InstBlockId::Empty,
  78. .is_extern = false,
  79. .extern_library_id = SemIR::LibraryNameId::None,
  80. .non_owning_decl_id = SemIR::InstId::None,
  81. .first_owning_decl_id = noop_id,
  82. },
  83. {
  84. .call_params_id = call_params_id,
  85. .return_type_inst_id = SemIR::TypeInstId::None,
  86. .return_patterns_id = SemIR::InstBlockId::None,
  87. .self_param_id = self_param_id,
  88. }};
  89. noop_function.SetBuiltinFunction(SemIR::BuiltinFunctionKind::NoOp);
  90. function_decl.function_id = context.functions().Add(noop_function);
  91. function_decl.type_id =
  92. GetFunctionType(context, function_decl.function_id, specific_id);
  93. ReplaceInstBeforeConstantUse(context, noop_id, function_decl);
  94. return noop_id;
  95. }
  96. auto BuildCustomWitness(Context& context, SemIR::LocId loc_id,
  97. SemIR::ConstantId query_self_const_id,
  98. SemIR::SpecificInterfaceId query_specific_interface_id,
  99. llvm::ArrayRef<SemIR::InstId> values) -> SemIR::InstId {
  100. const auto query_specific_interface =
  101. context.specific_interfaces().Get(query_specific_interface_id);
  102. const auto& interface =
  103. context.interfaces().Get(query_specific_interface.interface_id);
  104. auto assoc_entities =
  105. context.inst_blocks().GetOrEmpty(interface.associated_entities_id);
  106. if (assoc_entities.size() != values.size()) {
  107. context.TODO(loc_id, ("Unsupported definition of interface " +
  108. context.names().GetFormatted(interface.name_id))
  109. .str());
  110. return SemIR::ErrorInst::InstId;
  111. }
  112. llvm::SmallVector<SemIR::InstId> entries;
  113. // Build a witness with the current contents of the witness table. This will
  114. // grow as we progress through the impl. In theory this will build O(n^2)
  115. // table entries, but in practice n <= 2, so that's OK.
  116. //
  117. // This is necessary because later associated entities may refer to earlier
  118. // associated entities in their signatures. In particular, an associated
  119. // result type may be used as the return type of an associated function.
  120. //
  121. // TODO: Consider building one witness after all associated constants, and
  122. // then a second after all associated functions, rather than building one at
  123. // each step. For now this doesn't really matter since we don't have more than
  124. // one of each anyway.
  125. auto make_witness = [&] {
  126. return context.constant_values().GetInstId(
  127. EvalOrAddInst<SemIR::CustomWitness>(
  128. context, loc_id,
  129. {.type_id =
  130. GetSingletonType(context, SemIR::WitnessType::TypeInstId),
  131. .elements_id = context.inst_blocks().Add(entries),
  132. .query_specific_interface_id = query_specific_interface_id}));
  133. };
  134. // Fill in the witness table.
  135. for (const auto& [assoc_entity_id, value_id] :
  136. llvm::zip_equal(assoc_entities, values)) {
  137. LoadImportRef(context, assoc_entity_id);
  138. auto decl_id =
  139. context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
  140. context.sem_ir(), query_specific_interface.specific_id,
  141. assoc_entity_id));
  142. CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity");
  143. auto decl = context.insts().Get(decl_id);
  144. CARBON_KIND_SWITCH(decl) {
  145. case CARBON_KIND(SemIR::StructValue struct_value): {
  146. if (struct_value.type_id == SemIR::ErrorInst::TypeId) {
  147. return SemIR::ErrorInst::InstId;
  148. }
  149. auto self_type_id =
  150. GetFacetAsType(context, loc_id, query_self_const_id);
  151. // TODO: If a thunk is needed, this will build a different value each
  152. // time it's called, so we won't properly deduplicate repeated
  153. // witnesses.
  154. // TODO: Skip calling make_witness if this function signature doesn't
  155. // involve `Self`.
  156. entries.push_back(CheckAssociatedFunctionImplementation(
  157. context,
  158. context.types().GetAs<SemIR::FunctionType>(struct_value.type_id),
  159. value_id, self_type_id, make_witness(),
  160. /*defer_thunk_definition=*/false));
  161. break;
  162. }
  163. case SemIR::AssociatedConstantDecl::Kind: {
  164. context.TODO(loc_id,
  165. "Associated constant in interface with synthesized impl");
  166. return SemIR::ErrorInst::InstId;
  167. }
  168. default:
  169. CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId,
  170. "Unexpected kind of associated entity {0}", decl);
  171. return SemIR::ErrorInst::InstId;
  172. }
  173. }
  174. return make_witness();
  175. }
  176. auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id)
  177. -> CoreInterface {
  178. const auto& interface = context.interfaces().Get(interface_id);
  179. if (!context.name_scopes().IsCorePackage(interface.parent_scope_id) ||
  180. !interface.name_id.AsIdentifierId().has_value()) {
  181. return CoreInterface::Unknown;
  182. }
  183. for (auto [core_identifier, core_interface] :
  184. {std::pair{CoreIdentifier::Copy, CoreInterface::Copy},
  185. std::pair{CoreIdentifier::Destroy, CoreInterface::Destroy}}) {
  186. if (interface.name_id ==
  187. context.core_identifiers().AddNameId(core_identifier)) {
  188. return core_interface;
  189. }
  190. }
  191. return CoreInterface::Unknown;
  192. }
  193. // Returns true if the `Self` should impl `Destroy`.
  194. static auto TypeCanDestroy(Context& context,
  195. SemIR::ConstantId query_self_const_id,
  196. SemIR::InterfaceId destroy_interface_id) -> bool {
  197. auto inst = context.insts().Get(context.constant_values().GetInstId(
  198. GetCanonicalFacetOrTypeValue(context, query_self_const_id)));
  199. // For facet values, look if the FacetType provides the same.
  200. if (auto facet_type =
  201. context.types().TryGetAs<SemIR::FacetType>(inst.type_id())) {
  202. const auto& info = context.facet_types().Get(facet_type->facet_type_id);
  203. for (auto interface : info.extend_constraints) {
  204. if (interface.interface_id == destroy_interface_id) {
  205. return true;
  206. }
  207. }
  208. }
  209. CARBON_KIND_SWITCH(inst) {
  210. case CARBON_KIND(SemIR::ClassType class_type): {
  211. auto class_info = context.classes().Get(class_type.class_id);
  212. // Incomplete and abstract classes can't be destroyed.
  213. // TODO: Return false if the object repr doesn't impl `Destroy`.
  214. // TODO: This should probably be skipped for all C++ types, but currently
  215. // must handle those for trivial destruction.
  216. return class_info.is_complete() &&
  217. class_info.inheritance_kind !=
  218. SemIR::Class::InheritanceKind::Abstract;
  219. }
  220. case SemIR::ArrayType::Kind:
  221. case SemIR::ConstType::Kind:
  222. case SemIR::MaybeUnformedType::Kind:
  223. case SemIR::PartialType::Kind:
  224. case SemIR::StructType::Kind:
  225. case SemIR::TupleType::Kind:
  226. // TODO: Return false for types that indirectly reference a type that
  227. // doesn't impl `Destroy`.
  228. return true;
  229. case SemIR::BoolType::Kind:
  230. case SemIR::PointerType::Kind:
  231. // Trivially destructible.
  232. return true;
  233. default:
  234. return false;
  235. }
  236. }
  237. auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
  238. CoreInterface core_interface,
  239. SemIR::ConstantId query_self_const_id,
  240. SemIR::SpecificInterfaceId query_specific_interface_id)
  241. -> SemIR::InstId {
  242. // TODO: Handle more interfaces, particularly copy, move, and conversion.
  243. if (core_interface != CoreInterface::Destroy) {
  244. return SemIR::InstId::None;
  245. }
  246. auto query_specific_interface =
  247. context.specific_interfaces().Get(query_specific_interface_id);
  248. if (!TypeCanDestroy(context, query_self_const_id,
  249. query_specific_interface.interface_id)) {
  250. return SemIR::InstId::None;
  251. }
  252. // TODO: This needs more complex logic to apply the correct behavior. Also, we
  253. // should avoid building a new function on each lookup since a similar query
  254. // could result in identical functions.
  255. auto noop_id = MakeNoOpFunction(context, loc_id, query_self_const_id,
  256. query_specific_interface.specific_id);
  257. return BuildCustomWitness(context, loc_id, query_self_const_id,
  258. query_specific_interface_id, {noop_id});
  259. }
  260. } // namespace Carbon::Check