expr_info.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
  20. const File* ir = &file;
  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 = [&]<typename TypedInstT>(TypedInstT inst) {
  35. if constexpr (std::same_as<TypedInstT, ClassElementAccess>) {
  36. inst_id = inst.base_id;
  37. // A value of class type is a pointer to an object representation.
  38. // Therefore, if the base is a value, the result is an ephemeral
  39. // reference.
  40. value_category = ExprCategory::EphemeralRef;
  41. } else if constexpr (std::same_as<TypedInstT, ImportRefLoaded> ||
  42. std::same_as<TypedInstT, ImportRefUnloaded>) {
  43. auto import_ir_inst = ir->import_ir_insts().Get(inst.import_ir_inst_id);
  44. ir = ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir;
  45. inst_id = import_ir_inst.inst_id();
  46. } else {
  47. static_assert(
  48. TypedInstT::Kind.expr_category().TryAsComputedCategory() !=
  49. ComputedExprCategory::DependsOnOperands,
  50. "Missing expression category computation for type");
  51. }
  52. };
  53. // If the category depends on the operands of the instruction, determine it.
  54. // Usually this means the category is the same as the category of an
  55. // operand.
  56. switch (*category_from_kind.TryAsComputedCategory()) {
  57. case ComputedExprCategory::ValueIfHasType: {
  58. return untyped_inst.kind().has_type() ? value_category
  59. : ExprCategory::NotExpr;
  60. }
  61. case ComputedExprCategory::SameAsFirstOperand: {
  62. inst_id = AsAnyInstId(untyped_inst.arg0_and_kind());
  63. break;
  64. }
  65. case ComputedExprCategory::SameAsSecondOperand: {
  66. inst_id = AsAnyInstId(untyped_inst.arg1_and_kind());
  67. break;
  68. }
  69. case ComputedExprCategory::DependsOnOperands: {
  70. switch (untyped_inst.kind()) {
  71. #define CARBON_SEM_IR_INST_KIND(TypedInstT) \
  72. case TypedInstT::Kind: \
  73. handle_special_case(untyped_inst.As<TypedInstT>()); \
  74. break;
  75. #include "toolchain/sem_ir/inst_kind.def"
  76. }
  77. }
  78. }
  79. }
  80. }
  81. auto FindReturnSlotArgForInitializer(const File& sem_ir, InstId init_id)
  82. -> InstId {
  83. while (true) {
  84. Inst init_untyped = sem_ir.insts().Get(init_id);
  85. CARBON_KIND_SWITCH(init_untyped) {
  86. case CARBON_KIND(AsCompatible init): {
  87. init_id = init.source_id;
  88. continue;
  89. }
  90. case CARBON_KIND(Converted init): {
  91. init_id = init.result_id;
  92. continue;
  93. }
  94. case CARBON_KIND(ArrayInit init): {
  95. return init.dest_id;
  96. }
  97. case CARBON_KIND(ClassInit init): {
  98. return init.dest_id;
  99. }
  100. case CARBON_KIND(StructInit init): {
  101. return init.dest_id;
  102. }
  103. case CARBON_KIND(TupleInit init): {
  104. return init.dest_id;
  105. }
  106. case CARBON_KIND(InitializeFrom init): {
  107. return init.dest_id;
  108. }
  109. case CARBON_KIND(InPlaceInit init): {
  110. if (!InitRepr::ForType(sem_ir, init.type_id).MightBeInPlace()) {
  111. return InstId::None;
  112. }
  113. return init.dest_id;
  114. }
  115. case CARBON_KIND(Call call): {
  116. if (!ReturnTypeInfo::ForCallee(sem_ir, call.callee_id)
  117. .has_return_slot()) {
  118. return InstId::None;
  119. }
  120. if (!call.args_id.has_value()) {
  121. // Argument initialization failed, so we have no return slot.
  122. return InstId::None;
  123. }
  124. return sem_ir.inst_blocks().Get(call.args_id).back();
  125. }
  126. case CARBON_KIND(ErrorInst _): {
  127. return InstId::None;
  128. }
  129. default:
  130. CARBON_FATAL("Initialization from unexpected inst {0}", init_untyped);
  131. }
  132. }
  133. }
  134. } // namespace Carbon::SemIR