| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #ifndef CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_
- #define CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_
- #include "toolchain/check/eval.h"
- #include "toolchain/sem_ir/inst_kind.h"
- namespace Carbon::Check {
- // The result of constant evaluation of an instruction.
- class ConstantEvalResult {
- public:
- // Produce a new constant as the result of an evaluation. The phase of the
- // produced constant must be the same as the greatest phase of the operands in
- // the evaluation. This will typically be the case if the evaluation uses all
- // of its operands.
- static auto NewSamePhase(SemIR::Inst inst) -> ConstantEvalResult {
- return ConstantEvalResult(inst, /*same_phase_as_inst=*/true);
- }
- // Produce a new constant as the result of an evaluation. The constant may
- // have any phase. Use `NewSamePhase` instead where possible, as it avoids a
- // phase recomputation.
- static auto NewAnyPhase(SemIR::Inst inst) -> ConstantEvalResult {
- return ConstantEvalResult(inst, /*same_phase_as_inst=*/false);
- }
- // Produce an existing constant as the result of an evaluation.
- static constexpr auto Existing(SemIR::ConstantId existing_id)
- -> ConstantEvalResult {
- CARBON_CHECK(existing_id.has_value());
- return ConstantEvalResult(existing_id);
- }
- // Indicates that an error was produced by evaluation.
- static const ConstantEvalResult Error;
- // Indicates that we encountered an instruction whose evaluation is
- // non-constant despite having constant operands. This should be rare;
- // usually we want to produce an error in this case.
- static const ConstantEvalResult NotConstant;
- // Indicates that we encountered an instruction for which we've not
- // implemented constant evaluation yet. Instruction is treated as not
- // constant.
- static const ConstantEvalResult TODO;
- // Returns whether the result of evaluation is that we should produce a new
- // constant described by `new_inst()` rather than an existing `ConstantId`
- // described by `existing()`.
- auto is_new() const -> bool { return !result_id_.has_value(); }
- // Returns the existing constant that this the instruction evaluates to, or
- // `None` if this is evaluation produces a new constant.
- auto existing() const -> SemIR::ConstantId { return result_id_; }
- // Returns the new constant instruction that is the result of evaluation.
- auto new_inst() const -> SemIR::Inst {
- CARBON_CHECK(is_new());
- return new_inst_;
- }
- // Whether the new constant instruction is known to have the same phase as the
- // evaluated instruction. Requires `is_new()`.
- auto same_phase_as_inst() const -> bool {
- CARBON_CHECK(is_new());
- return same_phase_as_inst_;
- }
- private:
- constexpr explicit ConstantEvalResult(SemIR::ConstantId raw_id)
- : result_id_(raw_id), same_phase_as_inst_(false) {}
- explicit ConstantEvalResult(SemIR::Inst inst, bool same_phase_as_inst)
- : result_id_(SemIR::ConstantId::None),
- new_inst_(inst),
- same_phase_as_inst_(same_phase_as_inst) {}
- SemIR::ConstantId result_id_;
- union {
- SemIR::Inst new_inst_;
- };
- bool same_phase_as_inst_;
- };
- inline constexpr ConstantEvalResult ConstantEvalResult::Error =
- Existing(SemIR::ErrorInst::ConstantId);
- inline constexpr ConstantEvalResult ConstantEvalResult::NotConstant =
- ConstantEvalResult(SemIR::ConstantId::NotConstant);
- inline constexpr ConstantEvalResult ConstantEvalResult::TODO = NotConstant;
- // Implementation details to compute the type of the `EvalConstantInst`
- // functions.
- namespace Internal {
- // Returns whether an `EvalConstantInst` overload is expected to exist for this
- // instruction constant kind.
- constexpr auto ConstantKindHasEvalConstantInst(SemIR::InstConstantKind kind)
- -> bool {
- switch (kind) {
- case SemIR::InstConstantKind::Never:
- case SemIR::InstConstantKind::ConstantInstAction:
- case SemIR::InstConstantKind::InstAction:
- case SemIR::InstConstantKind::WheneverPossible:
- case SemIR::InstConstantKind::Always:
- case SemIR::InstConstantKind::AlwaysUnique:
- return false;
- case SemIR::InstConstantKind::Indirect:
- case SemIR::InstConstantKind::SymbolicOnly:
- case SemIR::InstConstantKind::SymbolicOrReference:
- case SemIR::InstConstantKind::Conditional:
- case SemIR::InstConstantKind::ConditionalUnique:
- return true;
- }
- }
- // Given an instruction kind, determines the type that should be used to declare
- // `EvalConstantInst` for that instruction.
- template <typename InstT, bool HasFn, bool HasInstId>
- struct FunctionTypeForEvalConstantInstImpl {
- // By default, we want no `EvalConstantInst` function at all. But we can't
- // express that, so use the type `auto () -> void` as a placeholder.
- using Type = auto() -> void;
- };
- template <typename InstT>
- struct FunctionTypeForEvalConstantInstImpl<InstT, true, false> {
- // Can be evaluated, evaluation doesn't need InstId.
- using Type = auto(Context& context, InstT inst) -> ConstantEvalResult;
- };
- template <typename InstT>
- struct FunctionTypeForEvalConstantInstImpl<InstT, true, true> {
- // Can be evaluated, evaluation needs InstId.
- using Type = auto(Context& context, SemIR::InstId inst_id, InstT inst)
- -> ConstantEvalResult;
- };
- template <typename InstT>
- using FunctionTypeForEvalConstantInst =
- typename FunctionTypeForEvalConstantInstImpl<
- InstT, ConstantKindHasEvalConstantInst(InstT::Kind.constant_kind()),
- InstT::Kind.constant_needs_inst_id() !=
- SemIR::InstConstantNeedsInstIdKind::No>::Type;
- } // namespace Internal
- // Explicitly delete the overload generated for non-evaluatable instructions.
- // These all produce the same signature, so we only need to delete it once.
- auto EvalConstantInst() -> void = delete;
- // `EvalConstantInst` evaluates an instruction whose operands are all constant,
- // in a context unrelated to the enclosing evaluation. The function is given the
- // instruction after its operands, including its type, are replaced by their
- // evaluated value, and returns a `ConstantEvalResult` describing the result of
- // evaluating the instruction.
- //
- // An overload is defined for each type whose constant kind is one of the
- // following:
- //
- // - InstConstantKind::Indirect
- // - InstConstantKind::SymbolicOnly
- // - InstConstantKind::SymbolicOrReference
- // - InstConstantKind::Conditional
- // - InstConstantKind::ConditionalUnique
- //
- // ... except for cases where the result of evaluation depends on the evaluation
- // context itself. Those cases are handled by explicit specialization of
- // `TryEvalTypedInst` in `eval.cpp` instead.
- //
- // The signature of an overload is
- //
- // auto EvalConstantInst(Context& context, SemIR::InstId inst_id, InstT inst)
- // -> ConstantEvalResult;
- //
- // if `InstT::Kind.constant_needs_inst_id()` is true, and
- //
- // auto EvalConstantInst(Context& context, InstT inst) -> ConstantEvalResult;
- //
- // otherwise.
- //
- // Overloads are *declared* for all types, because there isn't a good way to
- // declare only the overloads we want here without duplicating the list of
- // types. Missing overloads will be diagnosed when linking. Excess overloads
- // map to a deleted signature to prevent accidental calls.
- #define CARBON_SEM_IR_INST_KIND(Kind) \
- Internal::FunctionTypeForEvalConstantInst<SemIR::Kind> EvalConstantInst;
- #include "toolchain/sem_ir/inst_kind.def"
- } // namespace Carbon::Check
- #endif // CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_
|