eval_inst.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. constexpr ConstantEvalResult ConstantEvalResult::Error =
  73. Existing(SemIR::ErrorInst::ConstantId);
  74. constexpr ConstantEvalResult ConstantEvalResult::NotConstant =
  75. ConstantEvalResult(SemIR::ConstantId::NotConstant);
  76. 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::InstAction:
  87. case SemIR::InstConstantKind::WheneverPossible:
  88. case SemIR::InstConstantKind::Always:
  89. case SemIR::InstConstantKind::Unique:
  90. return false;
  91. case SemIR::InstConstantKind::Indirect:
  92. case SemIR::InstConstantKind::SymbolicOnly:
  93. case SemIR::InstConstantKind::Conditional:
  94. return true;
  95. }
  96. }
  97. // Given an instruction kind, determines the type that should be used to declare
  98. // `EvalConstantInst` for that instruction.
  99. template <typename InstT, bool HasFn, bool HasInstId>
  100. struct FunctionTypeForEvalConstantInstImpl {
  101. // By default, we want no `EvalConstantInst` function at all. But we can't
  102. // express that, so use the type `auto () -> voic` as a placaeholder.
  103. using Type = auto() -> void;
  104. };
  105. template <typename InstT>
  106. struct FunctionTypeForEvalConstantInstImpl<InstT, true, false> {
  107. // Can be evaluated, evaluation doesn't need InstId.
  108. using Type = auto(Context& context, InstT inst) -> ConstantEvalResult;
  109. };
  110. template <typename InstT>
  111. struct FunctionTypeForEvalConstantInstImpl<InstT, true, true> {
  112. // Can be evaluated, evaluation needs InstId.
  113. using Type = auto(Context& context, SemIR::InstId inst_id, InstT inst)
  114. -> ConstantEvalResult;
  115. };
  116. template <typename InstT>
  117. using FunctionTypeForEvalConstantInst =
  118. typename FunctionTypeForEvalConstantInstImpl<
  119. InstT, ConstantKindHasEvalConstantInst(InstT::Kind.constant_kind()),
  120. InstT::Kind.constant_needs_inst_id() !=
  121. SemIR::InstConstantNeedsInstIdKind::No>::Type;
  122. } // namespace Internal
  123. // Explicitly delete the overload generated for non-evaluatable instructions.
  124. // These all produce the same signature, so we only need to delete it once.
  125. auto EvalConstantInst() -> void = delete;
  126. // `EvalConstantInst` evaluates an instruction whose operands are all constant,
  127. // in a context unrelated to the enclosing evaluation. The function is given the
  128. // instruction after its operands, including its type, are replaced by their
  129. // evaluated value, and returns a `ConstantEvalResult` describing the result of
  130. // evaluating the instruction.
  131. //
  132. // An overload is defined for each type whose constant kind is one of the
  133. // following:
  134. //
  135. // - InstConstantKind::Indirect
  136. // - InstConstantKind::SymbolicOnly
  137. // - InstConstantKind::Conditional
  138. //
  139. // ... except for cases where the result of evaluation depends on the evaluation
  140. // context itself. Those cases are handled by explicit specialization of
  141. // `TryEvalTypedInst` in `eval.cpp` instead.
  142. //
  143. // The signature of an overload is
  144. //
  145. // auto EvalConstantInst(Context& context, SemIR::InstId inst_id, InstT inst)
  146. // -> ConstantEvalResult;
  147. //
  148. // if `InstT::Kind.constant_needs_inst_id()` is true, and
  149. //
  150. // auto EvalConstantInst(Context& context, InstT inst) -> ConstantEvalResult;
  151. //
  152. // otherwise.
  153. //
  154. // Overloads are *declared* for all types, because there isn't a good way to
  155. // declare only the overloads we want here without duplicating the list of
  156. // types. Missing overloads will be diagnosed when linking. Excess overloads
  157. // map to a deleted signature to prevent accidental calls.
  158. #define CARBON_SEM_IR_INST_KIND(Kind) \
  159. Internal::FunctionTypeForEvalConstantInst<SemIR::Kind> EvalConstantInst;
  160. #include "toolchain/sem_ir/inst_kind.def"
  161. } // namespace Carbon::Check
  162. #endif // CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_