import_ref.cpp 11 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/import_ref.h"
  5. #include "common/check.h"
  6. #include "toolchain/check/context.h"
  7. #include "toolchain/check/eval.h"
  8. #include "toolchain/parse/node_ids.h"
  9. #include "toolchain/sem_ir/file.h"
  10. #include "toolchain/sem_ir/ids.h"
  11. #include "toolchain/sem_ir/inst.h"
  12. #include "toolchain/sem_ir/inst_kind.h"
  13. #include "toolchain/sem_ir/typed_insts.h"
  14. #include "toolchain/sem_ir/value_stores.h"
  15. namespace Carbon::Check {
  16. // Resolves an instruction from an imported IR into a constant referring to the
  17. // current IR.
  18. class ImportRefResolver {
  19. public:
  20. explicit ImportRefResolver(
  21. Context& context, const SemIR::File& import_ir,
  22. SemIR::ConstantValueStore& import_ir_constant_values)
  23. : context_(context),
  24. import_ir_(import_ir),
  25. import_ir_constant_values_(import_ir_constant_values) {}
  26. // Iteratively resolves an imported instruction's inner references until a
  27. // constant ID referencing the current IR is produced. When an outer
  28. // instruction has unresolved inner references, it will add them to the stack
  29. // for inner evaluation and reattempt outer evaluation after.
  30. auto Resolve(SemIR::InstId inst_id) -> SemIR::ConstantId {
  31. work_stack_.push_back(inst_id);
  32. while (!work_stack_.empty()) {
  33. auto inst_id = work_stack_.back();
  34. CARBON_CHECK(inst_id.is_valid());
  35. // Double-check that the constant still doesn't have a calculated value.
  36. // This should typically be checked before adding it, but a given constant
  37. // may be added multiple times before its constant is evaluated.
  38. if (auto current_const_id = import_ir_constant_values_.Get(inst_id);
  39. current_const_id.is_valid()) {
  40. work_stack_.pop_back();
  41. } else if (auto new_const_id = TryResolveInst(inst_id);
  42. new_const_id.is_valid()) {
  43. import_ir_constant_values_.Set(inst_id, new_const_id);
  44. work_stack_.pop_back();
  45. }
  46. }
  47. auto constant_id = import_ir_constant_values_.Get(inst_id);
  48. CARBON_CHECK(constant_id.is_valid());
  49. return constant_id;
  50. }
  51. // Wraps constant evaluation with logic to handle types.
  52. auto ResolveType(SemIR::TypeId import_type_id) -> SemIR::TypeId {
  53. if (!import_type_id.is_valid()) {
  54. return import_type_id;
  55. }
  56. auto import_type_inst_id = import_ir_.types().GetInstId(import_type_id);
  57. CARBON_CHECK(import_type_inst_id.is_valid());
  58. if (import_type_inst_id.is_builtin()) {
  59. // Builtins don't require constant resolution; we can use them directly.
  60. return context_.GetBuiltinType(import_type_inst_id.builtin_kind());
  61. } else {
  62. return context_.GetTypeIdForTypeConstant(Resolve(import_type_inst_id));
  63. }
  64. }
  65. private:
  66. // Returns the ConstantId for an instruction, or adds it to the stack and
  67. // returns Invalid if the ConstantId is not ready.
  68. auto GetConstantId(SemIR::TypeId type_id) -> SemIR::ConstantId {
  69. auto inst_id = import_ir_.types().GetInstId(type_id);
  70. auto const_id = import_ir_constant_values_.Get(inst_id);
  71. if (!const_id.is_valid()) {
  72. work_stack_.push_back(inst_id);
  73. }
  74. return const_id;
  75. }
  76. // Tries to resolve the InstId, returning a constant when ready, or Invalid if
  77. // more has been added to the stack. A similar API is followed for all
  78. // following TryResolveTypedInst helper functions.
  79. //
  80. // Logic for each TryResolveTypedInst will be in two phases:
  81. // 1. Gather all input constants.
  82. // 2. Produce an output constant.
  83. //
  84. // Although it's possible TryResolveTypedInst could complete in a single call
  85. // when all input constants are ready, a common scenario is that some inputs
  86. // will still be unresolved, and it'll return Invalid between phases. On the
  87. // second call, all previously unready constants will have been resolved, so
  88. // it should run to completion. As a consequence, it's important to reserve
  89. // all expensive logic for the second phase; this in particular includes
  90. // GetTypeIdForTypeConstant calls which do a hash table lookup.
  91. //
  92. // TODO: Error is returned when support is missing, but that should go away.
  93. auto TryResolveInst(SemIR::InstId inst_id) -> SemIR::ConstantId {
  94. if (inst_id.is_builtin()) {
  95. // Constants for builtins can be directly copied.
  96. return context_.constant_values().Get(inst_id);
  97. }
  98. auto inst = import_ir_.insts().Get(inst_id);
  99. switch (inst.kind()) {
  100. case SemIR::InstKind::ConstType:
  101. return TryResolveTypedInst(inst.As<SemIR::ConstType>());
  102. case SemIR::InstKind::PointerType:
  103. return TryResolveTypedInst(inst.As<SemIR::PointerType>());
  104. case SemIR::InstKind::StructType:
  105. return TryResolveTypedInst(inst.As<SemIR::StructType>());
  106. case SemIR::InstKind::TupleType:
  107. return TryResolveTypedInst(inst.As<SemIR::TupleType>());
  108. case SemIR::InstKind::BindName:
  109. case SemIR::InstKind::BindSymbolicName:
  110. // Can use TryEvalInst because the resulting constant doesn't really use
  111. // `inst`.
  112. return TryEvalInst(context_, inst_id, inst);
  113. case SemIR::InstKind::ClassDecl:
  114. case SemIR::InstKind::InterfaceDecl:
  115. // TODO: Not implemented.
  116. return SemIR::ConstantId::Error;
  117. case SemIR::InstKind::FunctionDecl:
  118. // TODO: Allowed to work for testing, but not really implemented.
  119. return SemIR::ConstantId::NotConstant;
  120. default:
  121. context_.TODO(
  122. Parse::NodeId::Invalid,
  123. llvm::formatv("TryResolveInst on {0}", inst.kind()).str());
  124. return SemIR::ConstantId::Error;
  125. }
  126. }
  127. auto TryResolveTypedInst(SemIR::ConstType inst) -> SemIR::ConstantId {
  128. CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
  129. auto inner_const_id = GetConstantId(inst.inner_id);
  130. if (!inner_const_id.is_valid()) {
  131. return SemIR::ConstantId::Invalid;
  132. }
  133. auto inner_type_id = context_.GetTypeIdForTypeConstant(inner_const_id);
  134. // TODO: Should ConstType have a wrapper for this similar to the others?
  135. return TryEvalInst(
  136. context_, SemIR::InstId::Invalid,
  137. SemIR::ConstType{SemIR::TypeId::TypeType, inner_type_id});
  138. }
  139. auto TryResolveTypedInst(SemIR::PointerType inst) -> SemIR::ConstantId {
  140. CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
  141. auto pointee_const_id = GetConstantId(inst.pointee_id);
  142. if (!pointee_const_id.is_valid()) {
  143. return SemIR::ConstantId::Invalid;
  144. }
  145. auto pointee_type_id = context_.GetTypeIdForTypeConstant(pointee_const_id);
  146. return context_.types().GetConstantId(
  147. context_.GetPointerType(pointee_type_id));
  148. }
  149. auto TryResolveTypedInst(SemIR::StructType inst) -> SemIR::ConstantId {
  150. CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
  151. // Collect all constants first, locating unresolved ones in a single pass.
  152. bool has_unresolved = false;
  153. auto orig_fields = import_ir_.inst_blocks().Get(inst.fields_id);
  154. llvm::SmallVector<SemIR::ConstantId> field_const_ids;
  155. field_const_ids.reserve(orig_fields.size());
  156. for (auto field_id : orig_fields) {
  157. auto field = import_ir_.insts().GetAs<SemIR::StructTypeField>(field_id);
  158. auto field_const_id = GetConstantId(field.field_type_id);
  159. if (field_const_id.is_valid()) {
  160. field_const_ids.push_back(field_const_id);
  161. } else {
  162. has_unresolved = true;
  163. }
  164. }
  165. if (has_unresolved) {
  166. return SemIR::ConstantId::Invalid;
  167. }
  168. // Prepare a vector of fields for GetStructType.
  169. // TODO: Should we have field constants so that we can deduplicate fields
  170. // without creating instructions here?
  171. llvm::SmallVector<SemIR::InstId> fields;
  172. fields.reserve(orig_fields.size());
  173. for (auto [field_id, field_const_id] :
  174. llvm::zip(orig_fields, field_const_ids)) {
  175. auto field = import_ir_.insts().GetAs<SemIR::StructTypeField>(field_id);
  176. auto name_str = import_ir_.names().GetAsStringIfIdentifier(field.name_id);
  177. auto name_id = name_str ? SemIR::NameId::ForIdentifier(
  178. context_.identifiers().Add(*name_str))
  179. : field.name_id;
  180. auto field_type_id = context_.GetTypeIdForTypeConstant(field_const_id);
  181. fields.push_back(context_.AddInstInNoBlock(
  182. {Parse::NodeId::Invalid,
  183. SemIR::StructTypeField{.name_id = name_id,
  184. .field_type_id = field_type_id}}));
  185. }
  186. return context_.types().GetConstantId(
  187. context_.GetStructType(context_.inst_blocks().Add(fields)));
  188. }
  189. auto TryResolveTypedInst(SemIR::TupleType inst) -> SemIR::ConstantId {
  190. CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
  191. // Collect all constants first, locating unresolved ones in a single pass.
  192. bool has_unresolved = false;
  193. auto orig_elem_type_ids = import_ir_.type_blocks().Get(inst.elements_id);
  194. llvm::SmallVector<SemIR::ConstantId> elem_const_ids;
  195. elem_const_ids.reserve(orig_elem_type_ids.size());
  196. for (auto elem_type_id : orig_elem_type_ids) {
  197. auto elem_const_id = GetConstantId(elem_type_id);
  198. if (elem_const_id.is_valid()) {
  199. elem_const_ids.push_back(elem_const_id);
  200. } else {
  201. has_unresolved = true;
  202. }
  203. }
  204. if (has_unresolved) {
  205. return SemIR::ConstantId::Invalid;
  206. }
  207. // Prepare a vector of the tuple types for GetTupleType.
  208. llvm::SmallVector<SemIR::TypeId> elem_type_ids;
  209. elem_type_ids.reserve(orig_elem_type_ids.size());
  210. for (auto elem_const_id : elem_const_ids) {
  211. elem_type_ids.push_back(context_.GetTypeIdForTypeConstant(elem_const_id));
  212. }
  213. return context_.types().GetConstantId(context_.GetTupleType(elem_type_ids));
  214. }
  215. Context& context_;
  216. const SemIR::File& import_ir_;
  217. SemIR::ConstantValueStore& import_ir_constant_values_;
  218. llvm::SmallVector<SemIR::InstId> work_stack_;
  219. };
  220. auto TryResolveImportRefUnused(Context& context, SemIR::InstId inst_id)
  221. -> void {
  222. auto inst = context.insts().Get(inst_id);
  223. auto import_ref = inst.TryAs<SemIR::ImportRefUnused>();
  224. if (!import_ref) {
  225. return;
  226. }
  227. const SemIR::File& import_ir = *context.import_irs().Get(import_ref->ir_id);
  228. auto& import_ir_constant_values =
  229. context.import_ir_constant_values()[import_ref->ir_id.index];
  230. auto import_inst = import_ir.insts().Get(import_ref->inst_id);
  231. ImportRefResolver resolver(context, import_ir, import_ir_constant_values);
  232. auto type_id = resolver.ResolveType(import_inst.type_id());
  233. auto constant_id = resolver.Resolve(import_ref->inst_id);
  234. // TODO: Once ClassDecl/InterfaceDecl are supported (no longer return Error),
  235. // remove this.
  236. if (constant_id == SemIR::ConstantId::Error) {
  237. type_id = SemIR::TypeId::Error;
  238. }
  239. // Replace the ImportRefUnused instruction with an ImportRefUsed. This doesn't
  240. // use ReplaceInstBeforeConstantUse because it would trigger TryEvalInst, and
  241. // we're instead doing constant evaluation here in order to minimize recursion
  242. // risks.
  243. context.sem_ir().insts().Set(
  244. inst_id,
  245. SemIR::ImportRefUsed{type_id, import_ref->ir_id, import_ref->inst_id});
  246. // Store the constant for both the ImportRefUsed and imported instruction.
  247. context.constant_values().Set(inst_id, constant_id);
  248. }
  249. } // namespace Carbon::Check