expr_info.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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/sem_ir/expr_info.h"
  5. #include <concepts>
  6. #include "common/check.h"
  7. #include "toolchain/base/kind_switch.h"
  8. #include "toolchain/sem_ir/ids.h"
  9. #include "toolchain/sem_ir/inst_kind.h"
  10. #include "toolchain/sem_ir/typed_insts.h"
  11. namespace Carbon::SemIR {
  12. // Returns the InstId represented by an instruction operand.
  13. static auto AsAnyInstId(Inst::ArgAndKind arg) -> InstId {
  14. if (auto inst_id = arg.TryAs<SemIR::InstId>()) {
  15. return *inst_id;
  16. }
  17. return arg.As<SemIR::AbsoluteInstId>();
  18. }
  19. static auto GetExprCategoryImpl(const File* ir, InstId inst_id)
  20. -> ExprCategory {
  21. // The overall expression category if the current instruction is a value
  22. // expression.
  23. ExprCategory value_category = ExprCategory::Value;
  24. while (true) {
  25. auto untyped_inst = ir->insts().Get(inst_id);
  26. auto category_from_kind = untyped_inst.kind().expr_category();
  27. // If this instruction kind has a fixed category, return it.
  28. if (auto fixed_category = category_from_kind.TryAsFixedCategory()) {
  29. return *fixed_category == ExprCategory::Value ? value_category
  30. : *fixed_category;
  31. }
  32. // Handle any special cases that use
  33. // ComputedExprCategory::DependsOnOperands.
  34. auto handle_special_case =
  35. [&]<typename TypedInstT>(
  36. TypedInstT inst) -> std::optional<ExprCategory> {
  37. if constexpr (std::same_as<TypedInstT, ClassElementAccess>) {
  38. inst_id = inst.base_id;
  39. // A value of class type is a pointer to an object representation.
  40. // Therefore, if the base is a value, the result is an ephemeral
  41. // reference.
  42. value_category = ExprCategory::EphemeralRef;
  43. return std::nullopt;
  44. } else if constexpr (std::same_as<TypedInstT, ImportRefLoaded> ||
  45. std::same_as<TypedInstT, ImportRefUnloaded>) {
  46. auto import_ir_inst = ir->import_ir_insts().Get(inst.import_ir_inst_id);
  47. ir = ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir;
  48. inst_id = import_ir_inst.inst_id();
  49. return std::nullopt;
  50. } else if constexpr (std::same_as<TypedInstT, Call>) {
  51. auto callee = GetCallee(*ir, inst.callee_id);
  52. CARBON_KIND_SWITCH(callee) {
  53. case CARBON_KIND(SemIR::CalleeError _): {
  54. return ExprCategory::Error;
  55. }
  56. case CARBON_KIND(SemIR::CalleeFunction callee_function): {
  57. const auto& function =
  58. ir->functions().Get(callee_function.function_id);
  59. auto return_form_id = function.GetDeclaredReturnForm(
  60. *ir, callee_function.resolved_specific_id);
  61. if (!return_form_id.has_value()) {
  62. // Treat as equivalent to `-> ()`.
  63. return ExprCategory::ReprInitializing;
  64. }
  65. auto return_form = ir->insts().Get(return_form_id);
  66. CARBON_KIND_SWITCH(return_form) {
  67. case CARBON_KIND(InitForm _):
  68. return ExprCategory::ReprInitializing;
  69. case CARBON_KIND(RefForm _):
  70. return ExprCategory::DurableRef;
  71. case CARBON_KIND(ValueForm _):
  72. return ExprCategory::Value;
  73. case CARBON_KIND(ErrorInst _):
  74. return ExprCategory::Error;
  75. default:
  76. CARBON_FATAL("Unexpected inst kind: {0}", return_form);
  77. }
  78. }
  79. case CARBON_KIND(SemIR::CalleeNonFunction _): {
  80. return ExprCategory::NotExpr;
  81. }
  82. case CARBON_KIND(SemIR::CalleeCppOverloadSet _): {
  83. // TODO: support `ref` returns from C++.
  84. return ExprCategory::ReprInitializing;
  85. }
  86. }
  87. } else if constexpr (std::same_as<TypedInstT, FormBinding>) {
  88. auto form_id = ir->entity_names().Get(inst.entity_name_id).form_id;
  89. if (form_id.is_symbolic()) {
  90. return ExprCategory::Dependent;
  91. }
  92. auto form_inst_id = ir->constant_values().GetInstId(form_id);
  93. auto form_inst = ir->insts().Get(form_inst_id);
  94. CARBON_KIND_SWITCH(form_inst) {
  95. case InitForm::Kind:
  96. // A `var` binding pattern produces a `ref` binding.
  97. case RefForm::Kind:
  98. return ExprCategory::DurableRef;
  99. case ValueForm::Kind:
  100. return ExprCategory::Value;
  101. case ErrorInst::Kind:
  102. return ExprCategory::Error;
  103. default:
  104. CARBON_FATAL("Unexpected kind for form inst {0}", form_inst);
  105. }
  106. } else {
  107. static_assert(
  108. TypedInstT::Kind.expr_category().TryAsComputedCategory() !=
  109. ComputedExprCategory::DependsOnOperands,
  110. "Missing expression category computation for type");
  111. }
  112. CARBON_FATAL("Unreachable");
  113. };
  114. // If the category depends on the operands of the instruction, determine it.
  115. // Usually this means the category is the same as the category of an
  116. // operand.
  117. switch (*category_from_kind.TryAsComputedCategory()) {
  118. case ComputedExprCategory::ValueIfHasType: {
  119. return untyped_inst.kind().has_type() ? value_category
  120. : ExprCategory::NotExpr;
  121. }
  122. case ComputedExprCategory::SameAsFirstOperand: {
  123. inst_id = AsAnyInstId(untyped_inst.arg0_and_kind());
  124. break;
  125. }
  126. case ComputedExprCategory::SameAsSecondOperand: {
  127. inst_id = AsAnyInstId(untyped_inst.arg1_and_kind());
  128. break;
  129. }
  130. case ComputedExprCategory::DependsOnOperands: {
  131. switch (untyped_inst.kind()) {
  132. #define CARBON_SEM_IR_INST_KIND(TypedInstT) \
  133. case TypedInstT::Kind: { \
  134. auto category = handle_special_case(untyped_inst.As<TypedInstT>()); \
  135. if (category.has_value()) { \
  136. return *category; \
  137. } \
  138. break; \
  139. }
  140. #include "toolchain/sem_ir/inst_kind.def"
  141. }
  142. }
  143. }
  144. }
  145. }
  146. auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
  147. return GetExprCategoryImpl(&file, inst_id);
  148. }
  149. auto FindStorageArgForInitializer(const File& sem_ir, InstId init_id,
  150. bool allow_transitive) -> InstId {
  151. while (true) {
  152. Inst init_untyped = sem_ir.insts().Get(init_id);
  153. CARBON_KIND_SWITCH(init_untyped) {
  154. case CARBON_KIND(AsCompatible init): {
  155. if (!allow_transitive) {
  156. return InstId::None;
  157. }
  158. init_id = init.source_id;
  159. continue;
  160. }
  161. case CARBON_KIND(Converted init): {
  162. if (!allow_transitive) {
  163. return InstId::None;
  164. }
  165. init_id = init.result_id;
  166. continue;
  167. }
  168. case CARBON_KIND(UpdateInit init): {
  169. if (!allow_transitive) {
  170. return InstId::None;
  171. }
  172. init_id = init.base_init_id;
  173. continue;
  174. }
  175. case CARBON_KIND(ArrayInit init): {
  176. return init.dest_id;
  177. }
  178. case CARBON_KIND(ClassInit init): {
  179. return init.dest_id;
  180. }
  181. case CARBON_KIND(StructInit init): {
  182. return init.dest_id;
  183. }
  184. case CARBON_KIND(TupleInit init): {
  185. return init.dest_id;
  186. }
  187. case CARBON_KIND(InPlaceInit init): {
  188. return init.dest_id;
  189. }
  190. case CARBON_KIND(MarkInPlaceInit init): {
  191. return init.dest_id;
  192. }
  193. case CARBON_KIND(Call call): {
  194. auto callee_function = GetCalleeAsFunction(sem_ir, call.callee_id);
  195. const auto& function =
  196. sem_ir.functions().Get(callee_function.function_id);
  197. if (!function.return_form_inst_id.has_value()) {
  198. return InstId::None;
  199. }
  200. auto return_form_constant_id = GetConstantValueInSpecific(
  201. sem_ir, callee_function.resolved_specific_id,
  202. function.return_form_inst_id);
  203. auto return_form = sem_ir.insts().Get(
  204. sem_ir.constant_values().GetInstId(return_form_constant_id));
  205. CARBON_KIND_SWITCH(return_form) {
  206. case CARBON_KIND(InitForm init_form): {
  207. auto type_id = sem_ir.types().GetTypeIdForTypeInstId(
  208. init_form.type_component_inst_id);
  209. if (!InitRepr::ForType(sem_ir, type_id).MightBeInPlace()) {
  210. return InstId::None;
  211. }
  212. if (!call.args_id.has_value()) {
  213. // Argument initialization failed, so we have no return slot.
  214. return InstId::None;
  215. }
  216. CARBON_CHECK(function.call_param_ranges.return_size() == 1,
  217. "Unexpected number of output parameters on function");
  218. return sem_ir.inst_blocks().Get(
  219. call.args_id)[function.call_param_ranges.return_begin().index];
  220. }
  221. case CARBON_KIND(RefForm _): {
  222. return InstId::None;
  223. }
  224. default:
  225. CARBON_FATAL("Unexpected inst kind: {0}", return_form);
  226. }
  227. }
  228. case CARBON_KIND(ErrorInst _): {
  229. return InstId::None;
  230. }
  231. default:
  232. CARBON_FATAL("Initialization from unexpected inst {0}", init_untyped);
  233. }
  234. }
  235. }
  236. } // namespace Carbon::SemIR