eval_inst.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. #ifndef CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_
  5. #define CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_
  6. #include "toolchain/check/eval.h"
  7. #include "toolchain/sem_ir/inst_kind.h"
  8. namespace Carbon::Check {
  9. // The result of constant evaluation of an instruction.
  10. class ConstantEvalResult {
  11. public:
  12. // Produce a new constant as the result of an evaluation. The phase of the
  13. // produced constant must be the same as the greatest phase of the operands in
  14. // the evaluation. This will typically be the case if the evaluation uses all
  15. // of its operands.
  16. static auto NewSamePhase(SemIR::Inst inst) -> ConstantEvalResult {
  17. return ConstantEvalResult(inst, /*same_phase_as_inst=*/true);
  18. }
  19. // Produce a new constant as the result of an evaluation. The constant may
  20. // have any phase. Use `NewSamePhase` instead where possible, as it avoids a
  21. // phase recomputation.
  22. static auto NewAnyPhase(SemIR::Inst inst) -> ConstantEvalResult {
  23. return ConstantEvalResult(inst, /*same_phase_as_inst=*/false);
  24. }
  25. // Produce an existing constant as the result of an evaluation.
  26. static constexpr auto Existing(SemIR::ConstantId existing_id)
  27. -> ConstantEvalResult {
  28. CARBON_CHECK(existing_id.has_value());
  29. return ConstantEvalResult(existing_id);
  30. }
  31. // Indicates that an error was produced by evaluation.
  32. static const ConstantEvalResult Error;
  33. // Indicates that we encountered an instruction whose evaluation is
  34. // non-constant despite having constant operands. This should be rare;
  35. // usually we want to produce an error in this case.
  36. static const ConstantEvalResult NotConstant;
  37. // Indicates that we encountered an instruction for which we've not
  38. // implemented constant evaluation yet. Instruction is treated as not
  39. // constant.
  40. static const ConstantEvalResult TODO;
  41. // Returns whether the result of evaluation is that we should produce a new
  42. // constant described by `new_inst()` rather than an existing `ConstantId`
  43. // described by `existing()`.
  44. auto is_new() const -> bool { return !result_id_.has_value(); }
  45. // Returns the existing constant that this the instruction evaluates to, or
  46. // `None` if this is evaluation produces a new constant.
  47. auto existing() const -> SemIR::ConstantId { return result_id_; }
  48. // Returns the new constant instruction that is the result of evaluation.
  49. auto new_inst() const -> SemIR::Inst {
  50. CARBON_CHECK(is_new());
  51. return new_inst_;
  52. }
  53. // Whether the new constant instruction is known to have the same phase as the
  54. // evaluated instruction. Requires `is_new()`.
  55. auto same_phase_as_inst() const -> bool {
  56. CARBON_CHECK(is_new());
  57. return same_phase_as_inst_;
  58. }
  59. private:
  60. constexpr explicit ConstantEvalResult(SemIR::ConstantId raw_id)
  61. : result_id_(raw_id), same_phase_as_inst_(false) {}
  62. explicit ConstantEvalResult(SemIR::Inst inst, bool same_phase_as_inst)
  63. : result_id_(SemIR::ConstantId::None),
  64. new_inst_(inst),
  65. same_phase_as_inst_(same_phase_as_inst) {}
  66. SemIR::ConstantId result_id_;
  67. union {
  68. SemIR::Inst new_inst_;
  69. };
  70. bool same_phase_as_inst_;
  71. };
  72. inline constexpr ConstantEvalResult ConstantEvalResult::Error =
  73. Existing(SemIR::ErrorInst::ConstantId);
  74. inline constexpr ConstantEvalResult ConstantEvalResult::NotConstant =
  75. ConstantEvalResult(SemIR::ConstantId::NotConstant);
  76. inline constexpr ConstantEvalResult ConstantEvalResult::TODO = NotConstant;
  77. // Implementation details to compute the type of the `EvalConstantInst`
  78. // functions.
  79. namespace Internal {
  80. // Returns whether an `EvalConstantInst` overload is expected to exist for this
  81. // instruction constant kind.
  82. constexpr auto ConstantKindHasEvalConstantInst(SemIR::InstConstantKind kind)
  83. -> bool {
  84. switch (kind) {
  85. case SemIR::InstConstantKind::Never:
  86. case SemIR::InstConstantKind::ConstantInstAction:
  87. case SemIR::InstConstantKind::InstAction:
  88. case SemIR::InstConstantKind::WheneverPossible:
  89. case SemIR::InstConstantKind::Always:
  90. case SemIR::InstConstantKind::AlwaysUnique:
  91. return false;
  92. case SemIR::InstConstantKind::Indirect:
  93. case SemIR::InstConstantKind::SymbolicOnly:
  94. case SemIR::InstConstantKind::SymbolicOrReference:
  95. case SemIR::InstConstantKind::Conditional:
  96. case SemIR::InstConstantKind::ConditionalUnique:
  97. return true;
  98. }
  99. }
  100. // Given an instruction kind, determines the type that should be used to declare
  101. // `EvalConstantInst` for that instruction.
  102. template <typename InstT, bool HasFn, bool HasInstId>
  103. struct FunctionTypeForEvalConstantInstImpl {
  104. // By default, we want no `EvalConstantInst` function at all. But we can't
  105. // express that, so use the type `auto () -> void` as a placeholder.
  106. using Type = auto() -> void;
  107. };
  108. template <typename InstT>
  109. struct FunctionTypeForEvalConstantInstImpl<InstT, true, false> {
  110. // Can be evaluated, evaluation doesn't need InstId.
  111. using Type = auto(Context& context, InstT inst) -> ConstantEvalResult;
  112. };
  113. template <typename InstT>
  114. struct FunctionTypeForEvalConstantInstImpl<InstT, true, true> {
  115. // Can be evaluated, evaluation needs InstId.
  116. using Type = auto(Context& context, SemIR::InstId inst_id, InstT inst)
  117. -> ConstantEvalResult;
  118. };
  119. template <typename InstT>
  120. using FunctionTypeForEvalConstantInst =
  121. typename FunctionTypeForEvalConstantInstImpl<
  122. InstT, ConstantKindHasEvalConstantInst(InstT::Kind.constant_kind()),
  123. InstT::Kind.constant_needs_inst_id() !=
  124. SemIR::InstConstantNeedsInstIdKind::No>::Type;
  125. } // namespace Internal
  126. // Explicitly delete the overload generated for non-evaluatable instructions.
  127. // These all produce the same signature, so we only need to delete it once.
  128. auto EvalConstantInst() -> void = delete;
  129. // `EvalConstantInst` evaluates an instruction whose operands are all constant,
  130. // in a context unrelated to the enclosing evaluation. The function is given the
  131. // instruction after its operands, including its type, are replaced by their
  132. // evaluated value, and returns a `ConstantEvalResult` describing the result of
  133. // evaluating the instruction.
  134. //
  135. // An overload is defined for each type whose constant kind is one of the
  136. // following:
  137. //
  138. // - InstConstantKind::Indirect
  139. // - InstConstantKind::SymbolicOnly
  140. // - InstConstantKind::SymbolicOrReference
  141. // - InstConstantKind::Conditional
  142. // - InstConstantKind::ConditionalUnique
  143. //
  144. // ... except for cases where the result of evaluation depends on the evaluation
  145. // context itself. Those cases are handled by explicit specialization of
  146. // `TryEvalTypedInst` in `eval.cpp` instead.
  147. //
  148. // The signature of an overload is
  149. //
  150. // auto EvalConstantInst(Context& context, SemIR::InstId inst_id, InstT inst)
  151. // -> ConstantEvalResult;
  152. //
  153. // if `InstT::Kind.constant_needs_inst_id()` is true, and
  154. //
  155. // auto EvalConstantInst(Context& context, InstT inst) -> ConstantEvalResult;
  156. //
  157. // otherwise.
  158. //
  159. // Overloads are *declared* for all types, because there isn't a good way to
  160. // declare only the overloads we want here without duplicating the list of
  161. // types. Missing overloads will be diagnosed when linking. Excess overloads
  162. // map to a deleted signature to prevent accidental calls.
  163. #define CARBON_SEM_IR_INST_KIND(Kind) \
  164. Internal::FunctionTypeForEvalConstantInst<SemIR::Kind> EvalConstantInst;
  165. #include "toolchain/sem_ir/inst_kind.def"
  166. } // namespace Carbon::Check
  167. #endif // CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_