mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:20:10 +01:00
196 lines
7.4 KiB
C++
196 lines
7.4 KiB
C++
// 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::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::TemplateOnly:
|
|
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 = 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::TemplateOnly
|
|
// - 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_
|