expr_info.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. // NOLINTBEGIN(readability-function-size)
  20. auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
  21. const File* ir = &file;
  22. // The overall expression category if the current instruction is a value
  23. // expression.
  24. ExprCategory value_category = ExprCategory::Value;
  25. while (true) {
  26. auto untyped_inst = ir->insts().Get(inst_id);
  27. auto category_from_kind = untyped_inst.kind().expr_category();
  28. // If this instruction kind has a fixed category, return it.
  29. if (auto fixed_category = category_from_kind.TryAsFixedCategory()) {
  30. return *fixed_category == ExprCategory::Value ? value_category
  31. : *fixed_category;
  32. }
  33. // Handle any special cases that use
  34. // ComputedExprCategory::DependsOnOperands.
  35. auto handle_special_case =
  36. [&]<typename TypedInstT>(
  37. TypedInstT inst) -> std::optional<ExprCategory> {
  38. if constexpr (std::same_as<TypedInstT, ClassElementAccess>) {
  39. inst_id = inst.base_id;
  40. // A value of class type is a pointer to an object representation.
  41. // Therefore, if the base is a value, the result is an ephemeral
  42. // reference.
  43. value_category = ExprCategory::EphemeralRef;
  44. return std::nullopt;
  45. } else if constexpr (std::same_as<TypedInstT, ImportRefLoaded> ||
  46. std::same_as<TypedInstT, ImportRefUnloaded>) {
  47. auto import_ir_inst = ir->import_ir_insts().Get(inst.import_ir_inst_id);
  48. ir = ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir;
  49. inst_id = import_ir_inst.inst_id();
  50. return std::nullopt;
  51. } else if constexpr (std::same_as<TypedInstT, Call>) {
  52. auto callee = GetCallee(file, inst.callee_id);
  53. CARBON_KIND_SWITCH(callee) {
  54. case CARBON_KIND(SemIR::CalleeError _): {
  55. return ExprCategory::Error;
  56. }
  57. case CARBON_KIND(SemIR::CalleeFunction callee_function): {
  58. const auto& function =
  59. file.functions().Get(callee_function.function_id);
  60. auto return_form_id = function.GetDeclaredReturnForm(
  61. file, callee_function.resolved_specific_id);
  62. if (!return_form_id.has_value()) {
  63. // Treat as equivalent to `-> ()`.
  64. return ExprCategory::Initializing;
  65. }
  66. auto return_form = file.insts().Get(return_form_id);
  67. CARBON_KIND_SWITCH(return_form) {
  68. case CARBON_KIND(InitForm _):
  69. return ExprCategory::Initializing;
  70. case CARBON_KIND(RefForm _):
  71. return ExprCategory::DurableRef;
  72. case CARBON_KIND(ErrorInst _):
  73. return ExprCategory::Error;
  74. default:
  75. CARBON_FATAL("Unexpected inst kind: {0}", return_form);
  76. }
  77. }
  78. case CARBON_KIND(SemIR::CalleeNonFunction _): {
  79. return ExprCategory::NotExpr;
  80. }
  81. case CARBON_KIND(SemIR::CalleeCppOverloadSet _): {
  82. // TODO: support `ref` returns from C++.
  83. return ExprCategory::Initializing;
  84. }
  85. }
  86. } else {
  87. static_assert(
  88. TypedInstT::Kind.expr_category().TryAsComputedCategory() !=
  89. ComputedExprCategory::DependsOnOperands,
  90. "Missing expression category computation for type");
  91. }
  92. CARBON_FATAL("Unreachable");
  93. };
  94. // If the category depends on the operands of the instruction, determine it.
  95. // Usually this means the category is the same as the category of an
  96. // operand.
  97. switch (*category_from_kind.TryAsComputedCategory()) {
  98. case ComputedExprCategory::ValueIfHasType: {
  99. return untyped_inst.kind().has_type() ? value_category
  100. : ExprCategory::NotExpr;
  101. }
  102. case ComputedExprCategory::SameAsFirstOperand: {
  103. inst_id = AsAnyInstId(untyped_inst.arg0_and_kind());
  104. break;
  105. }
  106. case ComputedExprCategory::SameAsSecondOperand: {
  107. inst_id = AsAnyInstId(untyped_inst.arg1_and_kind());
  108. break;
  109. }
  110. case ComputedExprCategory::DependsOnOperands: {
  111. switch (untyped_inst.kind()) {
  112. #define CARBON_SEM_IR_INST_KIND(TypedInstT) \
  113. case TypedInstT::Kind: { \
  114. auto category = handle_special_case(untyped_inst.As<TypedInstT>()); \
  115. if (category.has_value()) { \
  116. return *category; \
  117. } \
  118. break; \
  119. }
  120. #include "toolchain/sem_ir/inst_kind.def"
  121. }
  122. }
  123. }
  124. }
  125. }
  126. // NOLINTEND(readability-function-size)
  127. auto FindReturnSlotArgForInitializer(const File& sem_ir, InstId init_id)
  128. -> InstId {
  129. while (true) {
  130. Inst init_untyped = sem_ir.insts().Get(init_id);
  131. CARBON_KIND_SWITCH(init_untyped) {
  132. case CARBON_KIND(AsCompatible init): {
  133. init_id = init.source_id;
  134. continue;
  135. }
  136. case CARBON_KIND(Converted init): {
  137. init_id = init.result_id;
  138. continue;
  139. }
  140. case CARBON_KIND(ArrayInit init): {
  141. return init.dest_id;
  142. }
  143. case CARBON_KIND(ClassInit init): {
  144. return init.dest_id;
  145. }
  146. case CARBON_KIND(StructInit init): {
  147. return init.dest_id;
  148. }
  149. case CARBON_KIND(TupleInit init): {
  150. return init.dest_id;
  151. }
  152. case CARBON_KIND(InitializeFrom init): {
  153. return init.dest_id;
  154. }
  155. case CARBON_KIND(InPlaceInit init): {
  156. if (!InitRepr::ForType(sem_ir, init.type_id).MightBeInPlace()) {
  157. return InstId::None;
  158. }
  159. return init.dest_id;
  160. }
  161. case CARBON_KIND(Call call): {
  162. auto callee_function = GetCalleeAsFunction(sem_ir, call.callee_id);
  163. const auto& function =
  164. sem_ir.functions().Get(callee_function.function_id);
  165. if (!function.return_form_inst_id.has_value()) {
  166. return InstId::None;
  167. }
  168. auto return_form_constant_id = GetConstantValueInSpecific(
  169. sem_ir, callee_function.resolved_specific_id,
  170. function.return_form_inst_id);
  171. auto return_form = sem_ir.insts().Get(
  172. sem_ir.constant_values().GetInstId(return_form_constant_id));
  173. CARBON_KIND_SWITCH(return_form) {
  174. case CARBON_KIND(InitForm init_form): {
  175. auto type_id = sem_ir.types().GetTypeIdForTypeInstId(
  176. init_form.type_component_inst_id);
  177. if (!InitRepr::ForType(sem_ir, type_id).MightBeInPlace()) {
  178. return InstId::None;
  179. }
  180. if (!call.args_id.has_value()) {
  181. // Argument initialization failed, so we have no return slot.
  182. return InstId::None;
  183. }
  184. return sem_ir.inst_blocks().Get(
  185. call.args_id)[init_form.index.index];
  186. }
  187. case CARBON_KIND(RefForm _): {
  188. return InstId::None;
  189. }
  190. default:
  191. CARBON_FATAL("Unexpected inst kind: {0}", return_form);
  192. }
  193. }
  194. case CARBON_KIND(ErrorInst _): {
  195. return InstId::None;
  196. }
  197. default:
  198. CARBON_FATAL("Initialization from unexpected inst {0}", init_untyped);
  199. }
  200. }
  201. }
  202. } // namespace Carbon::SemIR