custom_witness.cpp 12 KB

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