impl_lookup.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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/cpp/impl_lookup.h"
  5. #include "clang/Sema/Sema.h"
  6. #include "toolchain/base/kind_switch.h"
  7. #include "toolchain/check/cpp/import.h"
  8. #include "toolchain/check/cpp/location.h"
  9. #include "toolchain/check/cpp/overload_resolution.h"
  10. #include "toolchain/check/impl.h"
  11. #include "toolchain/check/impl_lookup.h"
  12. #include "toolchain/check/import_ref.h"
  13. #include "toolchain/check/inst.h"
  14. #include "toolchain/check/type.h"
  15. #include "toolchain/sem_ir/ids.h"
  16. #include "toolchain/sem_ir/typed_insts.h"
  17. namespace Carbon::Check {
  18. // If the given type is a C++ class type, returns the corresponding class
  19. // declaration. Otherwise returns nullptr.
  20. // TODO: Handle qualified types.
  21. static auto TypeAsClassDecl(Context& context, SemIR::TypeId type_id)
  22. -> clang::CXXRecordDecl* {
  23. auto class_type = context.types().TryGetAs<SemIR::ClassType>(type_id);
  24. if (!class_type) {
  25. // Not a class.
  26. return nullptr;
  27. }
  28. SemIR::NameScopeId class_scope_id =
  29. context.classes().Get(class_type->class_id).scope_id;
  30. if (!class_scope_id.has_value()) {
  31. return nullptr;
  32. }
  33. const auto& scope = context.name_scopes().Get(class_scope_id);
  34. auto decl_id = scope.clang_decl_context_id();
  35. if (!decl_id.has_value()) {
  36. return nullptr;
  37. }
  38. return dyn_cast<clang::CXXRecordDecl>(
  39. context.clang_decls().Get(decl_id).key.decl);
  40. }
  41. // Builds a witness that the given type implements the given interface,
  42. // populating it with the specified set of values. Returns a corresponding
  43. // lookup result. Produces a diagnostic and returns `None` if the specified
  44. // values aren't suitable for the interface.
  45. static auto BuildWitness(Context& context, SemIR::LocId loc_id,
  46. SemIR::TypeId self_type_id,
  47. SemIR::SpecificInterface specific_interface,
  48. llvm::ArrayRef<SemIR::InstId> values)
  49. -> SemIR::InstId {
  50. const auto& interface =
  51. context.interfaces().Get(specific_interface.interface_id);
  52. auto assoc_entities =
  53. context.inst_blocks().GetOrEmpty(interface.associated_entities_id);
  54. if (assoc_entities.size() != values.size()) {
  55. context.TODO(loc_id, ("Unsupported definition of interface " +
  56. context.names().GetFormatted(interface.name_id))
  57. .str());
  58. return SemIR::ErrorInst::InstId;
  59. }
  60. llvm::SmallVector<SemIR::InstId> entries;
  61. // Build a witness with the current contents of the witness table. This will
  62. // grow as we progress through the impl. In theory this will build O(n^2)
  63. // table entries, but in practice n <= 2, so that's OK.
  64. //
  65. // This is necessary because later associated entities may refer to earlier
  66. // associated entities in their signatures. In particular, an associated
  67. // result type may be used as the return type of an associated function.
  68. //
  69. // TODO: Consider building one witness after all associated constants, and
  70. // then a second after all associated functions, rather than building one at
  71. // each step. For now this doesn't really matter since we don't have more than
  72. // one of each anyway.
  73. auto make_witness = [&] {
  74. return context.constant_values().GetInstId(EvalOrAddInst<SemIR::CppWitness>(
  75. context, loc_id,
  76. {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
  77. .elements_id = context.inst_blocks().Add(entries)}));
  78. };
  79. // Fill in the witness table.
  80. for (const auto& [assoc_entity_id, value_id] :
  81. llvm::zip_equal(assoc_entities, values)) {
  82. LoadImportRef(context, assoc_entity_id);
  83. auto decl_id =
  84. context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
  85. context.sem_ir(), specific_interface.specific_id, assoc_entity_id));
  86. CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity");
  87. auto decl = context.insts().Get(decl_id);
  88. CARBON_KIND_SWITCH(decl) {
  89. case CARBON_KIND(SemIR::StructValue struct_value): {
  90. if (struct_value.type_id == SemIR::ErrorInst::TypeId) {
  91. return SemIR::ErrorInst::InstId;
  92. }
  93. // TODO: If a thunk is needed, this will build a different value each
  94. // time it's called, so we won't properly deduplicate repeated
  95. // witnesses.
  96. // TODO: Skip calling make_witness if this function signature doesn't
  97. // involve `Self`.
  98. entries.push_back(CheckAssociatedFunctionImplementation(
  99. context,
  100. context.types().GetAs<SemIR::FunctionType>(struct_value.type_id),
  101. value_id, self_type_id, 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. return make_witness();
  117. }
  118. static auto BuildSingleFunctionWitness(
  119. Context& context, SemIR::LocId loc_id, clang::FunctionDecl* cpp_fn,
  120. clang::DeclAccessPair found_decl, int num_params,
  121. SemIR::TypeId self_type_id, SemIR::SpecificInterface specific_interface)
  122. -> SemIR::InstId {
  123. auto fn_id = context.clang_sema().DiagnoseUseOfOverloadedDecl(
  124. cpp_fn, GetCppLocation(context, loc_id))
  125. ? SemIR::ErrorInst::InstId
  126. : ImportCppFunctionDecl(context, loc_id, cpp_fn, num_params);
  127. if (auto fn_decl =
  128. context.insts().TryGetAsWithId<SemIR::FunctionDecl>(fn_id)) {
  129. CheckCppOverloadAccess(context, loc_id, found_decl, fn_decl->inst_id);
  130. } else {
  131. CARBON_CHECK(fn_id == SemIR::ErrorInst::InstId);
  132. return SemIR::ErrorInst::InstId;
  133. }
  134. return BuildWitness(context, loc_id, self_type_id, specific_interface,
  135. {fn_id});
  136. }
  137. static auto LookupCopyImpl(Context& context, SemIR::LocId loc_id,
  138. SemIR::TypeId self_type_id,
  139. SemIR::SpecificInterface specific_interface)
  140. -> SemIR::InstId {
  141. auto* class_decl = TypeAsClassDecl(context, self_type_id);
  142. if (!class_decl) {
  143. // TODO: Should we also provide a `Copy` implementation for enumerations?
  144. return SemIR::InstId::None;
  145. }
  146. auto* ctor = context.clang_sema().LookupCopyingConstructor(
  147. class_decl, clang::Qualifiers::Const);
  148. if (!ctor) {
  149. // TODO: If the impl lookup failure is an error, we should produce a
  150. // diagnostic explaining why the class is not copyable.
  151. return SemIR::InstId::None;
  152. }
  153. return BuildSingleFunctionWitness(
  154. context, loc_id, ctor,
  155. clang::DeclAccessPair::make(ctor, ctor->getAccess()), /*num_params=*/1,
  156. self_type_id, specific_interface);
  157. }
  158. static auto LookupDestroyImpl(Context& context, SemIR::LocId loc_id,
  159. SemIR::TypeId self_type_id,
  160. SemIR::SpecificInterface specific_interface)
  161. -> SemIR::InstId {
  162. auto* class_decl = TypeAsClassDecl(context, self_type_id);
  163. if (!class_decl) {
  164. return SemIR::InstId::None;
  165. }
  166. auto* dtor = context.clang_sema().LookupDestructor(class_decl);
  167. if (!dtor) {
  168. // TODO: If the impl lookup failure is an error, we should produce a
  169. // diagnostic explaining why the class is not destructible.
  170. return SemIR::InstId::None;
  171. }
  172. return BuildSingleFunctionWitness(
  173. context, loc_id, dtor,
  174. clang::DeclAccessPair::make(dtor, dtor->getAccess()), /*num_params=*/0,
  175. self_type_id, specific_interface);
  176. }
  177. auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
  178. SemIR::TypeId self_type_id,
  179. SemIR::SpecificInterface specific_interface,
  180. const TypeStructure* best_impl_type_structure,
  181. SemIR::LocId best_impl_loc_id) -> SemIR::InstId {
  182. // Determine whether this is an interface that we have special knowledge of.
  183. auto& interface = context.interfaces().Get(specific_interface.interface_id);
  184. if (!context.name_scopes().IsCorePackage(interface.parent_scope_id)) {
  185. return SemIR::InstId::None;
  186. }
  187. if (!interface.name_id.AsIdentifierId().has_value()) {
  188. return SemIR::InstId::None;
  189. }
  190. if (context.identifiers().Get(interface.name_id.AsIdentifierId()) == "Copy") {
  191. return LookupCopyImpl(context, loc_id, self_type_id, specific_interface);
  192. }
  193. if (context.identifiers().Get(interface.name_id.AsIdentifierId()) ==
  194. "Destroy") {
  195. return LookupDestroyImpl(context, loc_id, self_type_id, specific_interface);
  196. }
  197. // TODO: Handle other interfaces.
  198. // TODO: Infer a C++ type structure and check whether it's less strict than
  199. // the best Carbon type structure.
  200. static_cast<void>(best_impl_type_structure);
  201. static_cast<void>(best_impl_loc_id);
  202. return SemIR::InstId::None;
  203. }
  204. } // namespace Carbon::Check