From 0d2f364f39b408be6c7295eb068731dd34c9e3a4 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 26 Feb 2025 17:31:26 -0800 Subject: [PATCH] Split evaluation up into one function per instruction kind (#5008) Replace the large and growing `TryEvalInstInContext` function with one function per kind. While we still have special-case handling for a small number of instruction kinds, most instructions are now handled either fully automatically or use a common codepath that evaluates the instruction operands and then performs an eval-context-independent evaluation of the instruction. To support this, `InstConstantKind` is expanded to describe more fine-grained details about how each kind of instruction interacts with constant evaluation. Also, the operand kinds of instructions become slightly more fine-grained: we now distinguish between operands that describe the destination of an initializing expression (`DestInstId`) from other `InstId` operands, because `DestInstId` operands need different treatment during constant evaluation. In particular, an initializing expression can have a constant value even if its destination is non-constant or has not yet been set, because evaluation of an initializing expression doesn't include the store to the destination. Some minor test changes: - We now more consistently propagate errors into the results of constant evaluation, so more instructions that depend on errors have a constant value of ``. - Diagnostic location for invalid array types now point at the whole array type rather than the array index expression, because `EvalConstantinst` doesn't have access to the original expression. - Diagnostic for failed `RequireCompleteType` doesn't print the original type any more because `EvalConstantInst` doesn't have access to the original expression. As a follow-up, some of this -- in particular, the `EvalConstantInst` overloads -- will be moved to a separate file, in an effort to split the overall constant evaluation machinery apart from the logic to evaluate each individual kind of instruction. --- toolchain/check/eval.cpp | 1449 +++++++++-------- .../testdata/array/fail_bound_negative.carbon | 4 +- .../testdata/array/fail_bound_overflow.carbon | 4 +- .../array/fail_incomplete_element.carbon | 2 +- .../fail_out_of_bound_non_literal.carbon | 2 +- ..._facet_value_to_narrowed_facet_type.carbon | 6 +- .../value_with_type_through_access.carbon | 4 +- .../check/testdata/choice/fail_invalid.carbon | 4 +- .../testdata/class/fail_generic_method.carbon | 4 +- .../check/testdata/class/fail_init.carbon | 12 +- .../testdata/class/fail_self_param.carbon | 2 +- .../class/no_prelude/import_access.carbon | 4 +- .../class/no_prelude/name_poisoning.carbon | 4 +- .../check/testdata/deduce/int_float.carbon | 2 +- .../declaration/fail_param_in_type.carbon | 2 +- .../fail_decl_param_mismatch.carbon | 2 +- .../testdata/generic/complete_type.carbon | 4 +- .../testdata/impl/assoc_const_self.carbon | 7 +- .../testdata/impl/fail_call_invalid.carbon | 2 +- .../impl/fail_self_type_mismatch.carbon | 2 +- .../impl/fail_todo_use_assoc_const.carbon | 2 +- .../impl/no_prelude/name_poisoning.carbon | 6 +- .../index/fail_array_large_index.carbon | 4 +- .../index/fail_array_non_int_indexing.carbon | 2 +- .../fail_array_out_of_bound_access.carbon | 2 +- .../index/fail_negative_indexing.carbon | 2 +- .../fail_assoc_const_not_constant.carbon | 4 +- .../interface/no_prelude/import_access.carbon | 14 +- .../check/testdata/let/generic_import.carbon | 4 +- .../builtin/fail_assignment_to_error.carbon | 2 +- .../packages/fail_import_type_error.carbon | 8 +- .../no_prelude/core_name_poisoning.carbon | 2 +- .../testdata/pointer/fail_deref_error.carbon | 4 +- .../pointer/fail_deref_function.carbon | 4 +- .../pointer/fail_deref_namespace.carbon | 4 +- .../pointer/fail_deref_not_pointer.carbon | 12 +- .../testdata/pointer/fail_deref_type.carbon | 4 +- .../no_prelude/fail_nested_incomplete.carbon | 2 +- .../tuple/fail_nested_incomplete.carbon | 2 +- .../testdata/where_expr/designator.carbon | 2 +- .../testdata/where_expr/equal_rewrite.carbon | 2 +- .../testdata/where_expr/fail_not_facet.carbon | 4 +- toolchain/lower/constant.cpp | 2 + toolchain/lower/function_context.cpp | 4 +- toolchain/sem_ir/formatter.cpp | 4 + toolchain/sem_ir/id_kind.h | 2 +- toolchain/sem_ir/ids.h | 28 +- toolchain/sem_ir/inst.h | 3 +- toolchain/sem_ir/inst_kind.h | 64 +- toolchain/sem_ir/typed_insts.h | 230 +-- 50 files changed, 1036 insertions(+), 915 deletions(-) diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 5747dcbb9e7f0..29ac9e51c8fc9 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -16,6 +16,7 @@ #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/generic.h" +#include "toolchain/sem_ir/id_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/typed_insts.h" @@ -223,15 +224,20 @@ enum class Phase : uint8_t { }; } // namespace +// Returns whether the specified phase is a constant phase. +static auto IsConstant(Phase phase) -> bool { + return phase < Phase::UnknownDueToError; +} + // Gets the phase in which the value of a constant will become available. -static auto GetPhase(EvalContext& eval_context, SemIR::ConstantId constant_id) - -> Phase { +static auto GetPhase(const SemIR::ConstantValueStore& constant_values, + SemIR::ConstantId constant_id) -> Phase { if (!constant_id.is_constant()) { return Phase::Runtime; } else if (constant_id == SemIR::ErrorInst::SingletonConstantId) { return Phase::UnknownDueToError; } - switch (eval_context.constant_values().GetDependence(constant_id)) { + switch (constant_values.GetDependence(constant_id)) { case SemIR::ConstantDependence::None: return Phase::Concrete; case SemIR::ConstantDependence::PeriodSelf: @@ -256,7 +262,7 @@ static auto LatestPhase(Phase a, Phase b) -> Phase { static auto UpdatePhaseIgnorePeriodSelf(EvalContext& eval_context, SemIR::ConstantId constant_id, Phase* phase) { - Phase constant_phase = GetPhase(eval_context, constant_id); + Phase constant_phase = GetPhase(eval_context.constant_values(), constant_id); // Since LatestPhase(x, Phase::Concrete) == x, this is equivalent to replacing // Phase::PeriodSelfSymbolic with Phase::Concrete. if (constant_phase != Phase::PeriodSelfSymbolic) { @@ -333,16 +339,26 @@ static auto MakeFloatResult(Context& context, SemIR::TypeId type_id, static auto GetConstantValue(EvalContext& eval_context, SemIR::InstId inst_id, Phase* phase) -> SemIR::InstId { auto const_id = eval_context.GetConstantValue(inst_id); - *phase = LatestPhase(*phase, GetPhase(eval_context, const_id)); + *phase = + LatestPhase(*phase, GetPhase(eval_context.constant_values(), const_id)); return eval_context.constant_values().GetInstId(const_id); } +// Explicitly discard a `DestInstId`, because we should not be using the +// destination as part of evaluation. +static auto GetConstantValue(EvalContext& /*eval_context*/, + SemIR::DestInstId /*inst_id*/, Phase* /*phase*/) + -> SemIR::DestInstId { + return SemIR::InstId::None; +} + // Given a type which may refer to a generic parameter, returns the // corresponding type in the evaluation context. static auto GetConstantValue(EvalContext& eval_context, SemIR::TypeId type_id, Phase* phase) -> SemIR::TypeId { auto const_id = eval_context.GetConstantValue(type_id); - *phase = LatestPhase(*phase, GetPhase(eval_context, const_id)); + *phase = + LatestPhase(*phase, GetPhase(eval_context.constant_values(), const_id)); return eval_context.context().types().GetTypeIdForTypeConstantId(const_id); } @@ -505,6 +521,16 @@ static auto GetConstantFacetTypeInfo(EvalContext& eval_context, return info; } +static auto GetConstantValue(EvalContext& eval_context, + SemIR::FacetTypeId facet_type_id, Phase* phase) + -> SemIR::FacetTypeId { + SemIR::FacetTypeInfo info = + GetConstantFacetTypeInfo(eval_context, facet_type_id, phase); + info.Canonicalize(); + // TODO: Return `facet_type_id` if we can detect nothing has changed. + return eval_context.facet_types().Add(info); +} + // Replaces the specified field of the given typed instruction with its constant // value, if it has constant phase. Returns true on success, false if the value // has runtime phase. @@ -520,112 +546,81 @@ static auto ReplaceFieldWithConstantValue(EvalContext& eval_context, return true; } -// If the specified fields of the given typed instruction have constant values, -// replaces the fields with their constant values and builds a corresponding -// constant value. Otherwise returns `ConstantId::NotConstant`. Returns -// `ErrorInst::SingletonConstantId` if any subexpression is an error. -// -// The constant value is then checked by calling `validate_fn(typed_inst)`, -// which should return a `bool` indicating whether the new constant is valid. If -// validation passes, `transform_fn(typed_inst)` is called to produce the final -// constant instruction, and a corresponding ConstantId for the new constant is -// returned. If validation fails, it should produce a suitable error message. -// `ErrorInst::SingletonConstantId` is returned. -template -static auto RebuildIfFieldsAreConstantImpl( - EvalContext& eval_context, SemIR::Inst inst, ValidateFn validate_fn, - TransformFn transform_fn, EachFieldIdT InstT::*... each_field_id) - -> SemIR::ConstantId { - // Build a constant instruction by replacing each non-constant operand with - // its constant value. - auto typed_inst = inst.As(); - Phase phase = Phase::Concrete; - if ((ReplaceFieldWithConstantValue(eval_context, &typed_inst, each_field_id, - &phase) && - ...)) { - if (phase == Phase::UnknownDueToError || !validate_fn(typed_inst)) { - return SemIR::ErrorInst::SingletonConstantId; - } - return MakeConstantResult(eval_context.context(), transform_fn(typed_inst), - phase); - } - return MakeNonConstantResult(phase); -} - -// Same as above but with an identity transform function. -template -static auto RebuildAndValidateIfFieldsAreConstant( - EvalContext& eval_context, SemIR::Inst inst, ValidateFn validate_fn, - EachFieldIdT InstT::*... each_field_id) -> SemIR::ConstantId { - return RebuildIfFieldsAreConstantImpl(eval_context, inst, validate_fn, - std::identity{}, each_field_id...); -} +// Function template that can be called with an argument of type `T`. Used below +// to detect which overloads of `GetConstantValue` exist. +template +static void Accept(T /*arg*/) {} + +// Determines whether a `GetConstantValue` overload exists for a given ID type. +// Note that we do not check whether `GetConstantValue` is *callable* with a +// given ID type, because that would use the `InstId` overload for +// `AbsoluteInstId` and similar wrapper types, which should be left alone. +template +static constexpr bool HasGetConstantValueOverload = requires { + AcceptIdT>(GetConstantValue); +}; -// Same as above but with no validation step. -template -static auto TransformIfFieldsAreConstant(EvalContext& eval_context, - SemIR::Inst inst, - TransformFn transform_fn, - EachFieldIdT InstT::*... each_field_id) - -> SemIR::ConstantId { - return RebuildIfFieldsAreConstantImpl( - eval_context, inst, [](...) { return true; }, transform_fn, - each_field_id...); +// Given the stored value `arg` of an instruction field and its corresponding +// kind `kind`, returns the constant value to use for that field, if it has a +// constant phase. `*phase` is updated to include the new constant value. If +// the resulting phase is not constant, the returned value is not useful and +// will typically be `NoneIndex`. +template +static auto GetConstantValueForArg(EvalContext& eval_context, + SemIR::TypeEnum kind, int32_t arg, + Phase* phase) -> int32_t { + using Handler = auto(EvalContext&, int32_t arg, Phase * phase)->int32_t; + static constexpr Handler* Handlers[] = { + [](EvalContext& eval_context, int32_t arg, Phase* phase) -> int32_t { + auto id = SemIR::Inst::FromRaw(arg); + if constexpr (HasGetConstantValueOverload) { + // If we have a custom `GetConstantValue` overload, call it. + return SemIR::Inst::ToRaw(GetConstantValue(eval_context, id, phase)); + } else { + // Otherwise, we assume the value is already constant. + return arg; + } + }..., + [](EvalContext&, int32_t, Phase*) -> int32_t { + // Handler for IdKind::Invalid is next. + CARBON_FATAL("Instruction has argument with invalid IdKind"); + }, + [](EvalContext&, int32_t arg, Phase*) -> int32_t { + // Handler for IdKind::None is last. + return arg; + }}; + return Handlers[kind.ToIndex()](eval_context, arg, phase); } -// Same as above but with no validation or transform step. -template -static auto RebuildIfFieldsAreConstant(EvalContext& eval_context, - SemIR::Inst inst, - EachFieldIdT InstT::*... each_field_id) - -> SemIR::ConstantId { - return RebuildIfFieldsAreConstantImpl( - eval_context, inst, [](...) { return true; }, std::identity{}, - each_field_id...); -} +// Given an instruction, replaces its type and operands with their constant +// values from the specified evaluation context. `*phase` is updated to describe +// the constant phase of the result. Returns whether `*phase` is a constant +// phase; if not, `inst` may not be fully updated and should not be used. +static auto ReplaceAllFieldsWithConstantValues(EvalContext& eval_context, + SemIR::Inst* inst, Phase* phase) + -> bool { + auto type_id = SemIR::TypeId( + GetConstantValueForArg(eval_context, SemIR::IdKind::For, + inst->type_id().index, phase)); + inst->SetType(type_id); + if (!IsConstant(*phase)) { + return false; + } -// Rebuilds the given aggregate initialization instruction as a corresponding -// constant aggregate value, if its elements are all constants. -static auto RebuildInitAsValue(EvalContext& eval_context, SemIR::Inst inst, - SemIR::InstKind value_kind) - -> SemIR::ConstantId { - return TransformIfFieldsAreConstant( - eval_context, inst, - [&](SemIR::AnyAggregateInit result) { - return SemIR::AnyAggregateValue{.kind = value_kind, - .type_id = result.type_id, - .elements_id = result.elements_id}; - }, - &SemIR::AnyAggregateInit::type_id, &SemIR::AnyAggregateInit::elements_id); -} + auto kinds = inst->ArgKinds(); + auto arg0 = + GetConstantValueForArg(eval_context, kinds.first, inst->arg0(), phase); + if (!IsConstant(*phase)) { + return false; + } -// Performs an access into an aggregate, retrieving the specified element. -static auto PerformAggregateAccess(EvalContext& eval_context, SemIR::Inst inst) - -> SemIR::ConstantId { - auto access_inst = inst.As(); - Phase phase = Phase::Concrete; - if (ReplaceFieldWithConstantValue(eval_context, &access_inst, - &SemIR::AnyAggregateAccess::aggregate_id, - &phase)) { - if (auto aggregate = - eval_context.insts().TryGetAs( - access_inst.aggregate_id)) { - auto elements = eval_context.inst_blocks().Get(aggregate->elements_id); - auto index = static_cast(access_inst.index.index); - CARBON_CHECK(index < elements.size(), "Access out of bounds."); - // `Phase` is not used here. If this element is a concrete constant, then - // so is the result of indexing, even if the aggregate also contains a - // symbolic context. - return eval_context.GetConstantValue(elements[index]); - } else { - CARBON_CHECK(phase != Phase::Concrete, - "Failed to evaluate template constant {0} arg0: {1}", inst, - eval_context.insts().Get(access_inst.aggregate_id)); - } - return MakeConstantResult(eval_context.context(), access_inst, phase); + auto arg1 = + GetConstantValueForArg(eval_context, kinds.second, inst->arg1(), phase); + if (!IsConstant(*phase)) { + return false; } - return MakeNonConstantResult(phase); + inst->SetArgs(arg0, arg1); + return true; } // Performs an index into a homogeneous aggregate, retrieving the specified @@ -1549,641 +1544,675 @@ static auto MakeFacetTypeResult(Context& context, phase); } -// Implementation for `TryEvalInst`, wrapping `Context` with `EvalContext`. +// 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 New(SemIR::Inst inst) -> ConstantEvalResult { + return ConstantEvalResult(inst); + } + + // Produce an existing constant as the result of an evaluation. + static constexpr auto Existing(SemIR::ConstantId existing_id) + -> ConstantEvalResult { + CARBON_CHECK(existing_id.is_constant()); + 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_; + } + + private: + constexpr explicit ConstantEvalResult(SemIR::ConstantId raw_id) + : result_id_(raw_id) {} + + explicit ConstantEvalResult(SemIR::Inst inst) + : result_id_(SemIR::ConstantId::None), new_inst_(inst) {} + + SemIR::ConstantId result_id_; + union { + SemIR::Inst new_inst_; + }; +}; + +constexpr ConstantEvalResult ConstantEvalResult::Error = + Existing(SemIR::ErrorInst::SingletonConstantId); + +constexpr ConstantEvalResult ConstantEvalResult::NotConstant = + ConstantEvalResult(SemIR::ConstantId::NotConstant); + +constexpr ConstantEvalResult ConstantEvalResult::TODO = NotConstant; + +// `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. // -// Tail call should not be diagnosed as recursion. -// https://github.com/llvm/llvm-project/issues/125724 -// NOLINTNEXTLINE(misc-no-recursion): Tail call. -static auto TryEvalInstInContext(EvalContext& eval_context, - SemIR::InstId inst_id, SemIR::Inst inst) - -> SemIR::ConstantId { - // TODO: Ensure we have test coverage for each of these cases that can result - // in a constant, once those situations are all reachable. - CARBON_KIND_SWITCH(inst) { - // These cases are constants if their operands are. - case SemIR::AddrOf::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::AddrOf::type_id, - &SemIR::AddrOf::lvalue_id); - case CARBON_KIND(SemIR::ArrayType array_type): { - return RebuildAndValidateIfFieldsAreConstant( - eval_context, inst, - [&](SemIR::ArrayType result) { - auto bound_id = array_type.bound_id; - auto bound_inst = eval_context.insts().Get(result.bound_id); - auto int_bound = bound_inst.TryAs(); - if (!int_bound) { - CARBON_CHECK(eval_context.constant_values() - .Get(result.bound_id) - .is_symbolic(), - "Unexpected inst {0} for template constant int", - bound_inst); - return true; - } - // TODO: We should check that the size of the resulting array type - // fits in 64 bits, not just that the bound does. Should we use a - // 32-bit limit for 32-bit targets? - const auto& bound_val = eval_context.ints().Get(int_bound->int_id); - if (eval_context.types().IsSignedInt(int_bound->type_id) && - bound_val.isNegative()) { - CARBON_DIAGNOSTIC(ArrayBoundNegative, Error, - "array bound of {0} is negative", TypedInt); - eval_context.emitter().Emit( - eval_context.GetDiagnosticLoc(bound_id), ArrayBoundNegative, - {.type = int_bound->type_id, .value = bound_val}); - return false; - } - if (bound_val.getActiveBits() > 64) { - CARBON_DIAGNOSTIC(ArrayBoundTooLarge, Error, - "array bound of {0} is too large", TypedInt); - eval_context.emitter().Emit( - eval_context.GetDiagnosticLoc(bound_id), ArrayBoundTooLarge, - {.type = int_bound->type_id, .value = bound_val}); - return false; - } - return true; - }, - &SemIR::ArrayType::bound_id, &SemIR::ArrayType::element_type_id); - } - case SemIR::AssociatedEntity::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::AssociatedEntity::type_id); - case SemIR::AssociatedEntityType::Kind: - return RebuildIfFieldsAreConstant( - eval_context, inst, &SemIR::AssociatedEntityType::interface_type_id); - case SemIR::BoundMethod::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::BoundMethod::type_id, - &SemIR::BoundMethod::object_id, - &SemIR::BoundMethod::function_decl_id); - case SemIR::ClassType::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::ClassType::specific_id); - case SemIR::CompleteTypeWitness::Kind: - return RebuildIfFieldsAreConstant( - eval_context, inst, &SemIR::CompleteTypeWitness::object_repr_id); - case SemIR::FacetValue::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::FacetValue::type_id, - &SemIR::FacetValue::type_inst_id, - &SemIR::FacetValue::witness_inst_id); - case SemIR::FunctionType::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::FunctionType::specific_id); - case SemIR::FunctionTypeWithSelfType::Kind: - return RebuildIfFieldsAreConstant( - eval_context, inst, - &SemIR::FunctionTypeWithSelfType::interface_function_type_id, - &SemIR::FunctionTypeWithSelfType::self_id); - case SemIR::GenericClassType::Kind: - return RebuildIfFieldsAreConstant( - eval_context, inst, &SemIR::GenericClassType::enclosing_specific_id); - case SemIR::GenericInterfaceType::Kind: - return RebuildIfFieldsAreConstant( - eval_context, inst, - &SemIR::GenericInterfaceType::enclosing_specific_id); - case SemIR::ImplWitness::Kind: - // We intentionally don't replace the `elements_id` field here. We want to - // track that specific InstBlock in particular, not coalesce blocks with - // the same members. That block may get updated, and we want to pick up - // those changes. - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::ImplWitness::specific_id); - case CARBON_KIND(SemIR::IntType int_type): { - return RebuildAndValidateIfFieldsAreConstant( - eval_context, inst, - [&](SemIR::IntType result) { - return ValidateIntType( - eval_context.context(), - eval_context.GetDiagnosticLoc({inst_id, int_type.bit_width_id}), - result); - }, - &SemIR::IntType::bit_width_id); - } - case SemIR::PointerType::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::PointerType::pointee_id); - case CARBON_KIND(SemIR::FloatType float_type): { - return RebuildAndValidateIfFieldsAreConstant( - eval_context, inst, - [&](SemIR::FloatType result) { - return ValidateFloatType(eval_context.context(), - eval_context.GetDiagnosticLoc( - {inst_id, float_type.bit_width_id}), - result); - }, - &SemIR::FloatType::bit_width_id); - } - case SemIR::SpecificFunction::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::SpecificFunction::callee_id, - &SemIR::SpecificFunction::specific_id); - case SemIR::StructType::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::StructType::fields_id); - case SemIR::StructValue::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::StructValue::type_id, - &SemIR::StructValue::elements_id); - case SemIR::TupleType::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::TupleType::elements_id); - case SemIR::TupleValue::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::TupleValue::type_id, - &SemIR::TupleValue::elements_id); - case SemIR::UnboundElementType::Kind: - return RebuildIfFieldsAreConstant( - eval_context, inst, &SemIR::UnboundElementType::class_type_id, - &SemIR::UnboundElementType::element_type_id); - - // Initializers evaluate to a value of the object representation. - case SemIR::ArrayInit::Kind: - // TODO: Add an `ArrayValue` to represent a constant array object - // representation instead of using a `TupleValue`. - return RebuildInitAsValue(eval_context, inst, SemIR::TupleValue::Kind); - case SemIR::ClassInit::Kind: - // TODO: Add a `ClassValue` to represent a constant class object - // representation instead of using a `StructValue`. - return RebuildInitAsValue(eval_context, inst, SemIR::StructValue::Kind); - case SemIR::StructInit::Kind: - return RebuildInitAsValue(eval_context, inst, SemIR::StructValue::Kind); - case SemIR::TupleInit::Kind: - return RebuildInitAsValue(eval_context, inst, SemIR::TupleValue::Kind); - - case SemIR::Vtable::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::Vtable::virtual_functions_id); - case SemIR::AutoType::Kind: - case SemIR::BoolType::Kind: - case SemIR::BoundMethodType::Kind: - case SemIR::ErrorInst::Kind: - case SemIR::IntLiteralType::Kind: - case SemIR::LegacyFloatType::Kind: - case SemIR::NamespaceType::Kind: - case SemIR::SpecificFunctionType::Kind: - case SemIR::StringType::Kind: - case SemIR::TypeType::Kind: - case SemIR::VtableType::Kind: - case SemIR::WitnessType::Kind: - // Builtins are always concrete constants. - return MakeConstantResult(eval_context.context(), inst, Phase::Concrete); - - case CARBON_KIND(SemIR::FunctionDecl fn_decl): { - return TransformIfFieldsAreConstant( - eval_context, fn_decl, - [&](SemIR::FunctionDecl result) { - return SemIR::StructValue{.type_id = result.type_id, - .elements_id = SemIR::InstBlockId::Empty}; - }, - &SemIR::FunctionDecl::type_id); - } +// An overload is provided for each type whose constant kind is one of the +// following: +// +// - InstConstantKind::Indirect +// - InstConstantKind::SymbolicOnly +// - InstConstantKind::Conditional +// +// ... except for cases where the result of evaluation depends on the evaluation +// context itself. Those cases are handled by explicit specialization of +// `TryEvalTypedInst`. + +static auto EvalConstantInst(Context& context, SemIRLoc loc, + SemIR::ArrayType inst) -> ConstantEvalResult { + auto bound_inst = context.insts().Get(inst.bound_id); + auto int_bound = bound_inst.TryAs(); + if (!int_bound) { + CARBON_CHECK(context.constant_values().Get(inst.bound_id).is_symbolic(), + "Unexpected inst {0} for template constant int", bound_inst); + return ConstantEvalResult::New(inst); + } + // TODO: We should check that the size of the resulting array type + // fits in 64 bits, not just that the bound does. Should we use a + // 32-bit limit for 32-bit targets? + const auto& bound_val = context.ints().Get(int_bound->int_id); + if (context.types().IsSignedInt(int_bound->type_id) && + bound_val.isNegative()) { + CARBON_DIAGNOSTIC(ArrayBoundNegative, Error, + "array bound of {0} is negative", TypedInt); + context.emitter().Emit(loc, ArrayBoundNegative, + {.type = int_bound->type_id, .value = bound_val}); + return ConstantEvalResult::Error; + } + if (bound_val.getActiveBits() > 64) { + CARBON_DIAGNOSTIC(ArrayBoundTooLarge, Error, + "array bound of {0} is too large", TypedInt); + context.emitter().Emit(loc, ArrayBoundTooLarge, + {.type = int_bound->type_id, .value = bound_val}); + return ConstantEvalResult::Error; + } + return ConstantEvalResult::New(inst); +} - case CARBON_KIND(SemIR::ClassDecl class_decl): { - // If the class has generic parameters, we don't produce a class type, but - // a callable whose return value is a class type. - if (eval_context.classes().Get(class_decl.class_id).has_parameters()) { - return TransformIfFieldsAreConstant( - eval_context, class_decl, - [&](SemIR::ClassDecl result) { - return SemIR::StructValue{ - .type_id = result.type_id, - .elements_id = SemIR::InstBlockId::Empty}; - }, - &SemIR::ClassDecl::type_id); - } - // A non-generic class declaration evaluates to the class type. - return MakeConstantResult( - eval_context.context(), - SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId, - .class_id = class_decl.class_id, - .specific_id = SemIR::SpecificId::None}, - Phase::Concrete); - } +static auto EvalConstantInst(Context& context, SemIRLoc loc, + SemIR::IntType inst) -> ConstantEvalResult { + return ValidateIntType(context, loc, inst) ? ConstantEvalResult::New(inst) + : ConstantEvalResult::Error; +} - case CARBON_KIND(SemIR::FacetType facet_type): { - Phase phase = Phase::Concrete; - SemIR::FacetTypeInfo info = GetConstantFacetTypeInfo( - eval_context, facet_type.facet_type_id, &phase); - info.Canonicalize(); - // TODO: Reuse `inst` if we can detect that nothing has changed. - return MakeFacetTypeResult(eval_context.context(), info, phase); - } +static auto EvalConstantInst(Context& context, SemIRLoc loc, + SemIR::FloatType inst) -> ConstantEvalResult { + return ValidateFloatType(context, loc, inst) ? ConstantEvalResult::New(inst) + : ConstantEvalResult::Error; +} - case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { - // If the interface has generic parameters, we don't produce an interface - // type, but a callable whose return value is an interface type. - if (eval_context.interfaces() - .Get(interface_decl.interface_id) - .has_parameters()) { - return TransformIfFieldsAreConstant( - eval_context, interface_decl, - [&](SemIR::InterfaceDecl result) { - return SemIR::StructValue{ - .type_id = result.type_id, - .elements_id = SemIR::InstBlockId::Empty}; - }, - &SemIR::InterfaceDecl::type_id); - } - // A non-generic interface declaration evaluates to a facet type. - return MakeConstantResult( - eval_context.context(), - FacetTypeFromInterface(eval_context.context(), - interface_decl.interface_id, - SemIR::SpecificId::None), - Phase::Concrete); - } +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::ArrayInit init) -> ConstantEvalResult { + // TODO: Add an `ArrayValue` to represent a constant array object + // representation instead of using a `TupleValue`. + return ConstantEvalResult::New( + SemIR::TupleValue{.type_id = init.type_id, .elements_id = init.inits_id}); +} - case CARBON_KIND(SemIR::SpecificConstant specific): { - // Pull the constant value out of the specific. - return SemIR::GetConstantValueInSpecific( - eval_context.sem_ir(), specific.specific_id, specific.inst_id); - } +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::ClassInit init) -> ConstantEvalResult { + // TODO: Add a `ClassValue` to represent a constant class object + // representation instead of using a `StructValue`. + return ConstantEvalResult::New(SemIR::StructValue{ + .type_id = init.type_id, .elements_id = init.elements_id}); +} - // These cases are treated as being the unique canonical definition of the - // corresponding constant value. - // TODO: This doesn't properly handle redeclarations. Consider adding a - // corresponding `Value` inst for each of these cases, or returning the - // first declaration. - case SemIR::AdaptDecl::Kind: - case SemIR::AssociatedConstantDecl::Kind: - case SemIR::BaseDecl::Kind: - case SemIR::FieldDecl::Kind: - case SemIR::ImplDecl::Kind: - case SemIR::Namespace::Kind: - return SemIR::ConstantId::ForConcreteConstant(inst_id); - - case SemIR::BoolLiteral::Kind: - case SemIR::FloatLiteral::Kind: - case SemIR::IntValue::Kind: - case SemIR::StringLiteral::Kind: - // Promote literals to the constant block. - // TODO: Convert literals into a canonical form. Currently we can form two - // different `i32` constants with the same value if they are represented - // by `APInt`s with different bit widths. - // TODO: Can the type of an IntValue or FloatLiteral be symbolic? If so, - // we may need to rebuild. - return MakeConstantResult(eval_context.context(), inst, Phase::Concrete); - - // The elements of a constant aggregate can be accessed. - case SemIR::ClassElementAccess::Kind: - case SemIR::StructAccess::Kind: - case SemIR::TupleAccess::Kind: - return PerformAggregateAccess(eval_context, inst); - - case CARBON_KIND(SemIR::ImplWitnessAccess access_inst): { - // This is PerformAggregateAccess followed by GetConstantInSpecific. - Phase phase = Phase::Concrete; - if (ReplaceFieldWithConstantValue(eval_context, &access_inst, - &SemIR::ImplWitnessAccess::witness_id, - &phase)) { - if (auto witness = eval_context.insts().TryGetAs( - access_inst.witness_id)) { - auto elements = eval_context.inst_blocks().Get(witness->elements_id); - auto index = static_cast(access_inst.index.index); - CARBON_CHECK(index < elements.size(), "Access out of bounds."); - // `Phase` is not used here. If this element is a concrete constant, - // then so is the result of indexing, even if the aggregate also - // contains a symbolic context. - - auto element = elements[index]; - if (!element.has_value()) { - // TODO: Perhaps this should be a `{}` value with incomplete type? - CARBON_DIAGNOSTIC(ImplAccessMemberBeforeComplete, Error, - "accessing member from impl before the end of " - "its definition"); - // TODO: Add note pointing to the impl declaration. - eval_context.emitter().Emit(eval_context.GetDiagnosticLoc(inst_id), - ImplAccessMemberBeforeComplete); - return SemIR::ErrorInst::SingletonConstantId; - } - LoadImportRef(eval_context.context(), element); - return GetConstantValueInSpecific(eval_context.sem_ir(), - witness->specific_id, element); - } else { - CARBON_CHECK(phase != Phase::Concrete, - "Failed to evaluate template constant {0} arg0: {1}", - inst, eval_context.insts().Get(access_inst.witness_id)); - } - return MakeConstantResult(eval_context.context(), access_inst, phase); - } - return MakeNonConstantResult(phase); - } - case CARBON_KIND(SemIR::ArrayIndex index): { - return PerformArrayIndex(eval_context, index); - } +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::StructInit init) -> ConstantEvalResult { + return ConstantEvalResult::New(SemIR::StructValue{ + .type_id = init.type_id, .elements_id = init.elements_id}); +} - case CARBON_KIND(SemIR::Call call): { - return MakeConstantForCall(eval_context, - eval_context.GetDiagnosticLoc(inst_id), call); - } +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::TupleInit init) -> ConstantEvalResult { + return ConstantEvalResult::New(SemIR::TupleValue{ + .type_id = init.type_id, .elements_id = init.elements_id}); +} - // TODO: These need special handling. - case SemIR::BindValue::Kind: - case SemIR::Deref::Kind: - case SemIR::ImportRefLoaded::Kind: - case SemIR::ReturnSlot::Kind: - case SemIR::Temporary::Kind: - case SemIR::TemporaryStorage::Kind: - case SemIR::ValueAsRef::Kind: - case SemIR::VtablePtr::Kind: - break; +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::FunctionDecl inst) -> ConstantEvalResult { + return ConstantEvalResult::New(SemIR::StructValue{ + .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); +} - case CARBON_KIND(SemIR::SymbolicBindingPattern bind): { - // TODO: Disable constant evaluation of SymbolicBindingPattern once - // DeduceGenericCallArguments no longer needs implicit params to have - // constant values. - const auto& bind_name = - eval_context.entity_names().Get(bind.entity_name_id); - - // If we know which specific we're evaluating within and this is an - // argument of that specific, its constant value is the corresponding - // argument value. - if (auto value = - eval_context.GetCompileTimeBindValue(bind_name.bind_index()); - value.has_value()) { - return value; - } +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::ClassDecl inst) -> ConstantEvalResult { + // If the class has generic parameters, we don't produce a class type, but a + // callable whose return value is a class type. + if (context.classes().Get(inst.class_id).has_parameters()) { + return ConstantEvalResult::New(SemIR::StructValue{ + .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); + } - // The constant form of a symbolic binding is an idealized form of the - // original, with no equivalent value. - bind.entity_name_id = - eval_context.entity_names().MakeCanonical(bind.entity_name_id); - return MakeConstantResult(eval_context.context(), bind, - bind_name.is_template ? Phase::TemplateSymbolic - : Phase::CheckedSymbolic); - } - case CARBON_KIND(SemIR::BindSymbolicName bind): { - const auto& bind_name = - eval_context.entity_names().Get(bind.entity_name_id); + // A non-generic class declaration evaluates to the class type. + return ConstantEvalResult::New( + SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId, + .class_id = inst.class_id, + .specific_id = SemIR::SpecificId::None}); +} - Phase phase; - if (bind_name.name_id == SemIR::NameId::PeriodSelf) { - phase = Phase::PeriodSelfSymbolic; - } else { - // If we know which specific we're evaluating within and this is an - // argument of that specific, its constant value is the corresponding - // argument value. - if (auto value = - eval_context.GetCompileTimeBindValue(bind_name.bind_index()); - value.has_value()) { - return value; - } - phase = bind_name.is_template ? Phase::TemplateSymbolic - : Phase::CheckedSymbolic; - } - // The constant form of a symbolic binding is an idealized form of the - // original, with no equivalent value. - bind.entity_name_id = - eval_context.entity_names().MakeCanonical(bind.entity_name_id); - bind.value_id = SemIR::InstId::None; - if (!ReplaceFieldWithConstantValue( - eval_context, &bind, &SemIR::BindSymbolicName::type_id, &phase)) { - return MakeNonConstantResult(phase); - } - return MakeConstantResult(eval_context.context(), bind, phase); - } +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::InterfaceDecl inst) -> ConstantEvalResult { + // If the interface has generic parameters, we don't produce an interface + // type, but a callable whose return value is an interface type. + if (context.interfaces().Get(inst.interface_id).has_parameters()) { + return ConstantEvalResult::New(SemIR::StructValue{ + .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); + } - // AsCompatible changes the type of the source instruction; its constant - // value, if there is one, needs to be modified to be of the same type. - case CARBON_KIND(SemIR::AsCompatible inst): { - auto value = eval_context.GetConstantValue(inst.source_id); - if (!value.is_constant()) { - return value; - } + // A non-generic interface declaration evaluates to a facet type. + return ConstantEvalResult::New(FacetTypeFromInterface( + context, inst.interface_id, SemIR::SpecificId::None)); +} - auto from_phase = Phase::Concrete; - auto value_inst_id = - GetConstantValue(eval_context, inst.source_id, &from_phase); +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::SpecificConstant inst) + -> ConstantEvalResult { + // Pull the constant value out of the specific. + return ConstantEvalResult::Existing(SemIR::GetConstantValueInSpecific( + context.sem_ir(), inst.specific_id, inst.inst_id)); +} - auto to_phase = Phase::Concrete; - auto type_id = GetConstantValue(eval_context, inst.type_id, &to_phase); +// Performs an access into an aggregate, retrieving the specified element. +static auto PerformAggregateAccess(Context& context, SemIR::Inst inst) + -> ConstantEvalResult { + auto access_inst = inst.As(); + if (auto aggregate = context.insts().TryGetAs( + access_inst.aggregate_id)) { + auto elements = context.inst_blocks().Get(aggregate->elements_id); + auto index = static_cast(access_inst.index.index); + CARBON_CHECK(index < elements.size(), "Access out of bounds."); + // `Phase` is not used here. If this element is a concrete constant, then + // so is the result of indexing, even if the aggregate also contains a + // symbolic context. + return ConstantEvalResult::Existing( + context.constant_values().Get(elements[index])); + } + + return ConstantEvalResult::New(inst); +} - auto value_inst = eval_context.insts().Get(value_inst_id); - value_inst.SetType(type_id); +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::ClassElementAccess inst) + -> ConstantEvalResult { + return PerformAggregateAccess(context, inst); +} - if (to_phase >= from_phase) { - // If moving from a concrete constant value to a symbolic type, the new - // constant value takes on the phase of the new type. We're adding the - // symbolic bit to the new constant value due to the presence of a - // symbolic type. - return MakeConstantResult(eval_context.context(), value_inst, to_phase); - } else { - // If moving from a symbolic constant value to a concrete type, the new - // constant value has a phase that depends on what is in the value. If - // there is anything symbolic within the value, then it's symbolic. We - // can't easily determine that here without evaluating a new constant - // value. See - // https://github.com/carbon-language/carbon-lang/pull/4881#discussion_r1939961372 - [[clang::musttail]] return TryEvalInstInContext( - eval_context, SemIR::InstId::None, value_inst); - } - } +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::StructAccess inst) -> ConstantEvalResult { + return PerformAggregateAccess(context, inst); +} - // These semantic wrappers don't change the constant value. - case CARBON_KIND(SemIR::BindAlias typed_inst): { - return eval_context.GetConstantValue(typed_inst.value_id); - } - case CARBON_KIND(SemIR::ExportDecl typed_inst): { - return eval_context.GetConstantValue(typed_inst.value_id); - } - case CARBON_KIND(SemIR::NameRef typed_inst): { - return eval_context.GetConstantValue(typed_inst.value_id); - } - case CARBON_KIND(SemIR::ValueParamPattern param_pattern): { - // TODO: Treat this as a non-expression (here and in GetExprCategory) - // once generic deduction doesn't need patterns to have constant values. - return eval_context.GetConstantValue(param_pattern.subpattern_id); - } - case CARBON_KIND(SemIR::Converted typed_inst): { - return eval_context.GetConstantValue(typed_inst.result_id); - } - case CARBON_KIND(SemIR::InitializeFrom typed_inst): { - return eval_context.GetConstantValue(typed_inst.src_id); - } - case CARBON_KIND(SemIR::SpliceBlock typed_inst): { - return eval_context.GetConstantValue(typed_inst.result_id); - } - case CARBON_KIND(SemIR::ValueOfInitializer typed_inst): { - return eval_context.GetConstantValue(typed_inst.init_id); +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::TupleAccess inst) -> ConstantEvalResult { + return PerformAggregateAccess(context, inst); +} + +static auto EvalConstantInst(Context& context, SemIRLoc loc, + SemIR::ImplWitnessAccess inst) + -> ConstantEvalResult { + // This is PerformAggregateAccess followed by GetConstantInSpecific. + if (auto witness = + context.insts().TryGetAs(inst.witness_id)) { + auto elements = context.inst_blocks().Get(witness->elements_id); + auto index = static_cast(inst.index.index); + CARBON_CHECK(index < elements.size(), "Access out of bounds."); + auto element = elements[index]; + if (!element.has_value()) { + // TODO: Perhaps this should be a `{}` value with incomplete type? + CARBON_DIAGNOSTIC(ImplAccessMemberBeforeComplete, Error, + "accessing member from impl before the end of " + "its definition"); + // TODO: Add note pointing to the impl declaration. + context.emitter().Emit(loc, ImplAccessMemberBeforeComplete); + return ConstantEvalResult::Error; } - case CARBON_KIND(SemIR::FacetAccessType typed_inst): { - Phase phase = Phase::Concrete; - if (ReplaceFieldWithConstantValue( - eval_context, &typed_inst, - &SemIR::FacetAccessType::facet_value_inst_id, &phase)) { - if (auto facet_value = eval_context.insts().TryGetAs( - typed_inst.facet_value_inst_id)) { - return eval_context.constant_values().Get(facet_value->type_inst_id); - } - return MakeConstantResult(eval_context.context(), typed_inst, phase); - } else { - return MakeNonConstantResult(phase); - } + + LoadImportRef(context, element); + return ConstantEvalResult::Existing(GetConstantValueInSpecific( + context.sem_ir(), witness->specific_id, element)); + } + + return ConstantEvalResult::New(inst); +} + +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::BindValue /*inst*/) -> ConstantEvalResult { + // TODO: Handle this once we've decided how to represent constant values of + // reference expressions. + return ConstantEvalResult::TODO; +} + +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::Deref /*inst*/) -> ConstantEvalResult { + // TODO: Handle this. + return ConstantEvalResult::TODO; +} + +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::Temporary /*inst*/) -> ConstantEvalResult { + // TODO: Handle this. Can we just return the value of `init_id`? + return ConstantEvalResult::TODO; +} + +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::VtablePtr /*inst*/) -> ConstantEvalResult { + // TODO: Handle this. + return ConstantEvalResult::TODO; +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::AsCompatible inst) -> ConstantEvalResult { + // AsCompatible changes the type of the source instruction; its constant + // value, if there is one, needs to be modified to be of the same type. + auto value_id = context.constant_values().Get(inst.source_id); + CARBON_CHECK(value_id.is_constant()); + + auto value_inst = + context.insts().Get(context.constant_values().GetInstId(value_id)); + auto phase = GetPhase(context.constant_values(), + context.types().GetConstantId(inst.type_id)); + value_inst.SetType(inst.type_id); + + // Finish computing the new phase by incorporating the phases of the + // arguments. + EvalContext eval_context(context, SemIR::InstId::None); + auto kinds = value_inst.ArgKinds(); + GetConstantValueForArg(eval_context, kinds.first, value_inst.arg0(), &phase); + GetConstantValueForArg(eval_context, kinds.second, value_inst.arg1(), &phase); + CARBON_CHECK(IsConstant(phase)); + + // We can't use `ConstantEvalResult::New` because it would use the wrong + // phase, so manually build a new constant. + return ConstantEvalResult::Existing( + MakeConstantResult(context, value_inst, phase)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::BindAlias inst) -> ConstantEvalResult { + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.value_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::ExportDecl inst) -> ConstantEvalResult { + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.value_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::NameRef inst) -> ConstantEvalResult { + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.value_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::ValueParamPattern inst) + -> ConstantEvalResult { + // TODO: Treat this as a non-expression (here and in GetExprCategory) + // once generic deduction doesn't need patterns to have constant values. + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.subpattern_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::Converted inst) -> ConstantEvalResult { + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.result_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::InitializeFrom inst) -> ConstantEvalResult { + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.src_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::SpliceBlock inst) -> ConstantEvalResult { + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.result_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::ValueOfInitializer inst) + -> ConstantEvalResult { + return ConstantEvalResult::Existing( + context.constant_values().Get(inst.init_id)); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::FacetAccessType inst) + -> ConstantEvalResult { + if (auto facet_value = context.insts().TryGetAs( + inst.facet_value_inst_id)) { + return ConstantEvalResult::Existing( + context.constant_values().Get(facet_value->type_inst_id)); + } + return ConstantEvalResult::New(inst); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::FacetAccessWitness inst) + -> ConstantEvalResult { + if (auto facet_value = context.insts().TryGetAs( + inst.facet_value_inst_id)) { + return ConstantEvalResult::Existing( + context.constant_values().Get(facet_value->witness_inst_id)); + } + return ConstantEvalResult::New(inst); +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::UnaryOperatorNot inst) + -> ConstantEvalResult { + // `not true` -> `false`, `not false` -> `true`. + // All other uses of unary `not` are non-constant. + auto const_id = context.constant_values().Get(inst.operand_id); + if (const_id.is_concrete()) { + auto value = context.insts().GetAs( + context.constant_values().GetInstId(const_id)); + value.value = SemIR::BoolValue::From(!value.value.ToBool()); + return ConstantEvalResult::New(value); + } + return ConstantEvalResult::NotConstant; +} + +static auto EvalConstantInst(Context& context, SemIRLoc /*loc*/, + SemIR::ConstType inst) -> ConstantEvalResult { + // `const (const T)` evaluates to `const T`. + if (context.types().Is(inst.inner_id)) { + return ConstantEvalResult::Existing( + context.types().GetConstantId(inst.inner_id)); + } + // Otherwise, `const T` evaluates to itself. + return ConstantEvalResult::New(inst); +} + +static auto EvalConstantInst(Context& context, SemIRLoc loc, + SemIR::RequireCompleteType inst) + -> ConstantEvalResult { + auto witness_type_id = + GetSingletonType(context, SemIR::WitnessType::SingletonInstId); + + // If the type is a concrete constant, require it to be complete now. + auto complete_type_id = inst.complete_type_id; + if (context.types().GetConstantId(complete_type_id).is_concrete()) { + if (!TryToCompleteType(context, complete_type_id, loc, [&] { + // TODO: It'd be nice to report the original type prior to + // evaluation here. + CARBON_DIAGNOSTIC(IncompleteTypeInMonomorphization, Error, + "type {0} is incomplete", SemIR::TypeId); + return context.emitter().Build(loc, IncompleteTypeInMonomorphization, + complete_type_id); + })) { + return ConstantEvalResult::Error; } - case CARBON_KIND(SemIR::FacetAccessWitness typed_inst): { - Phase phase = Phase::Concrete; - if (ReplaceFieldWithConstantValue( - eval_context, &typed_inst, - &SemIR::FacetAccessWitness::facet_value_inst_id, &phase)) { - if (auto facet_value = eval_context.insts().TryGetAs( - typed_inst.facet_value_inst_id)) { - return eval_context.constant_values().Get( - facet_value->witness_inst_id); - } - return MakeConstantResult(eval_context.context(), typed_inst, phase); - } else { - return MakeNonConstantResult(phase); + return ConstantEvalResult::New(SemIR::CompleteTypeWitness{ + .type_id = witness_type_id, + .object_repr_id = context.types().GetObjectRepr(complete_type_id)}); + } + + // If it's not a concrete constant, require it to be complete once it + // becomes one. + return ConstantEvalResult::New(inst); +} + +static auto EvalConstantInst(Context& /*context*/, SemIRLoc /*loc*/, + SemIR::ImportRefUnloaded inst) + -> ConstantEvalResult { + CARBON_FATAL("ImportRefUnloaded should be loaded before TryEvalInst: {0}", + inst); +} + +// Evaluates an instruction of a known type in an evaluation context. The +// default behavior of this function depends on the constant kind of the +// instruction: +// +// - InstConstantKind::Never: returns ConstantId::NotConstant. +// - InstConstantKind::Indirect, SymbolicOnly, Conditional: evaluates all the +// operands of the instruction, and calls `EvalConstantInst` to evaluate the +// resulting constant instruction. +// - InstConstantKind::WheneverPossible, Always: evaluates all the operands of +// the instruction, and produces the resulting constant instruction as the +// result. +// - InstConstantKind::Unique: returns the `inst_id` as the resulting +// constant. +// +// Returns an error constant ID if any of the nested evaluations fail, and +// returns NotConstant if any of the nested evaluations is non-constant. +// +// This template is explicitly specialized for instructions that need special +// handling. +template +static auto TryEvalTypedInst(EvalContext& eval_context, SemIR::InstId inst_id, + SemIR::Inst inst) -> SemIR::ConstantId { + constexpr auto ConstantKind = InstT::Kind.constant_kind(); + if constexpr (ConstantKind == SemIR::InstConstantKind::Never) { + return SemIR::ConstantId::NotConstant; + } else if constexpr (ConstantKind == SemIR::InstConstantKind::Unique) { + CARBON_CHECK(inst_id.has_value()); + return SemIR::ConstantId::ForConcreteConstant(inst_id); + } else { + // Build a constant instruction by replacing each non-constant operand with + // its constant value. + Phase phase = Phase::Concrete; + if (!ReplaceAllFieldsWithConstantValues(eval_context, &inst, &phase)) { + if constexpr (ConstantKind == SemIR::InstConstantKind::Always) { + CARBON_CHECK(phase == Phase::UnknownDueToError, + "{0} should always be constant", InstT::Kind); } + return MakeNonConstantResult(phase); } - case CARBON_KIND(SemIR::WhereExpr typed_inst): { - Phase phase = Phase::Concrete; - SemIR::TypeId base_facet_type_id = - eval_context.insts().Get(typed_inst.period_self_id).type_id(); - SemIR::Inst base_facet_inst = - eval_context.GetConstantValueAsInst(base_facet_type_id); - SemIR::FacetTypeInfo info = {.other_requirements = false}; - // `where` provides that the base facet is an error, `type`, or a facet - // type. - if (auto facet_type = base_facet_inst.TryAs()) { - info = GetConstantFacetTypeInfo(eval_context, facet_type->facet_type_id, - &phase); - } else if (base_facet_type_id == SemIR::ErrorInst::SingletonTypeId) { - return SemIR::ErrorInst::SingletonConstantId; - } else { - CARBON_CHECK(base_facet_type_id == SemIR::TypeType::SingletonTypeId, - "Unexpected type_id: {0}, inst: {1}", base_facet_type_id, - base_facet_inst); - } - if (typed_inst.requirements_id.has_value()) { - auto insts = eval_context.inst_blocks().Get(typed_inst.requirements_id); - for (auto inst_id : insts) { - if (auto rewrite = - eval_context.insts().TryGetAs( - inst_id)) { - SemIR::ConstantId lhs = - eval_context.GetConstantValue(rewrite->lhs_id); - SemIR::ConstantId rhs = - eval_context.GetConstantValue(rewrite->rhs_id); - // `where` requirements using `.Self` should not be considered - // symbolic - UpdatePhaseIgnorePeriodSelf(eval_context, lhs, &phase); - UpdatePhaseIgnorePeriodSelf(eval_context, rhs, &phase); - info.rewrite_constraints.push_back( - {.lhs_const_id = lhs, .rhs_const_id = rhs}); - } else { - // TODO: Handle other requirements - info.other_requirements = true; - } - } + if constexpr (ConstantKind == SemIR::InstConstantKind::Always || + ConstantKind == SemIR::InstConstantKind::WheneverPossible) { + return MakeConstantResult(eval_context.context(), inst, phase); + } else { + ConstantEvalResult result = EvalConstantInst( + eval_context.context(), eval_context.GetDiagnosticLoc({inst_id}), + inst.As()); + if (result.is_new()) { + return MakeConstantResult(eval_context.context(), result.new_inst(), + phase); } - info.Canonicalize(); - return MakeFacetTypeResult(eval_context.context(), info, phase); + return result.existing(); } + } +} - // `not true` -> `false`, `not false` -> `true`. - // All other uses of unary `not` are non-constant. - case CARBON_KIND(SemIR::UnaryOperatorNot typed_inst): { - auto const_id = eval_context.GetConstantValue(typed_inst.operand_id); - auto phase = GetPhase(eval_context, const_id); - if (phase == Phase::Concrete) { - auto value = eval_context.insts().GetAs( - eval_context.constant_values().GetInstId(const_id)); - return MakeBoolResult(eval_context.context(), value.type_id, - !value.value.ToBool()); - } - if (phase == Phase::UnknownDueToError) { - return SemIR::ErrorInst::SingletonConstantId; - } - break; - } +// Specialize evaluation for array indexing because we want to check the index +// expression even if the array expression is non-constant. +template <> +auto TryEvalTypedInst(EvalContext& eval_context, + SemIR::InstId /*inst_id*/, + SemIR::Inst inst) + -> SemIR::ConstantId { + return PerformArrayIndex(eval_context, inst.As()); +} - // `const (const T)` evaluates to `const T`. Otherwise, `const T` evaluates - // to itself. - case CARBON_KIND(SemIR::ConstType typed_inst): { - auto phase = Phase::Concrete; - auto inner_id = - GetConstantValue(eval_context, typed_inst.inner_id, &phase); - if (eval_context.context().types().Is(inner_id)) { - return eval_context.context().types().GetConstantId(inner_id); - } - typed_inst.inner_id = inner_id; - return MakeConstantResult(eval_context.context(), typed_inst, phase); - } +// Specialize evaluation for function calls because we want to check the callee +// expression even if an argument expression is non-constant, and because we +// will eventually want to perform control flow handling here. +template <> +auto TryEvalTypedInst(EvalContext& eval_context, + SemIR::InstId inst_id, SemIR::Inst inst) + -> SemIR::ConstantId { + return MakeConstantForCall(eval_context, + eval_context.GetDiagnosticLoc(inst_id), + inst.As()); +} - case CARBON_KIND(SemIR::RequireCompleteType require_complete): { - auto phase = Phase::Concrete; - auto witness_type_id = GetSingletonType( - eval_context.context(), SemIR::WitnessType::SingletonInstId); - auto complete_type_id = GetConstantValue( - eval_context, require_complete.complete_type_id, &phase); - - // If the type is a concrete constant, require it to be complete now. - if (phase == Phase::Concrete) { - if (!TryToCompleteType( - eval_context.context(), complete_type_id, - eval_context.GetDiagnosticLoc(inst_id), [&] { - CARBON_DIAGNOSTIC(IncompleteTypeInMonomorphization, Error, - "{0} evaluates to incomplete type {1}", - SemIR::TypeId, SemIR::TypeId); - return eval_context.emitter().Build( - eval_context.GetDiagnosticLoc(inst_id), - IncompleteTypeInMonomorphization, - require_complete.complete_type_id, complete_type_id); - })) { - return SemIR::ErrorInst::SingletonConstantId; - } - return MakeConstantResult( - eval_context.context(), - SemIR::CompleteTypeWitness{ - .type_id = witness_type_id, - .object_repr_id = - eval_context.types().GetObjectRepr(complete_type_id)}, - phase); - } +// ImportRefLoaded can have a constant value, but it's owned and maintained by +// `import_ref.cpp`, not by us. +// TODO: Rearrange how `ImportRefLoaded` instructions are created so we never +// call this. +template <> +auto TryEvalTypedInst(EvalContext& /*eval_context*/, + SemIR::InstId /*inst_id*/, + SemIR::Inst /*inst*/) + -> SemIR::ConstantId { + return SemIR::ConstantId::NotConstant; +} + +// TODO: Disable constant evaluation of SymbolicBindingPattern once +// DeduceGenericCallArguments no longer needs implicit params to have constant +// values. +template <> +auto TryEvalTypedInst(EvalContext& eval_context, + SemIR::InstId /*inst_id*/, + SemIR::Inst inst) + -> SemIR::ConstantId { + auto bind = inst.As(); + + const auto& bind_name = eval_context.entity_names().Get(bind.entity_name_id); + + // If we know which specific we're evaluating within and this is an + // argument of that specific, its constant value is the corresponding + // argument value. + if (auto value = eval_context.GetCompileTimeBindValue(bind_name.bind_index()); + value.has_value()) { + return value; + } - // If it's not a concrete constant, require it to be complete once it - // becomes one. - return MakeConstantResult( - eval_context.context(), - SemIR::RequireCompleteType{.type_id = witness_type_id, - .complete_type_id = complete_type_id}, - phase); + // The constant form of a symbolic binding is an idealized form of the + // original, with no equivalent value. + bind.entity_name_id = + eval_context.entity_names().MakeCanonical(bind.entity_name_id); + return MakeConstantResult( + eval_context.context(), bind, + bind_name.is_template ? Phase::TemplateSymbolic : Phase::CheckedSymbolic); +} + +// Symbolic bindings are a special case because they can reach into the eval +// context and produce a context-specific value. +template <> +auto TryEvalTypedInst(EvalContext& eval_context, + SemIR::InstId /*inst_id*/, + SemIR::Inst inst) + -> SemIR::ConstantId { + auto bind = inst.As(); + + const auto& bind_name = eval_context.entity_names().Get(bind.entity_name_id); + + Phase phase; + if (bind_name.name_id == SemIR::NameId::PeriodSelf) { + phase = Phase::PeriodSelfSymbolic; + } else { + // If we know which specific we're evaluating within and this is an + // argument of that specific, its constant value is the corresponding + // argument value. + if (auto value = + eval_context.GetCompileTimeBindValue(bind_name.bind_index()); + value.has_value()) { + return value; } + phase = bind_name.is_template ? Phase::TemplateSymbolic + : Phase::CheckedSymbolic; + } + // The constant form of a symbolic binding is an idealized form of the + // original, with no equivalent value. + bind.entity_name_id = + eval_context.entity_names().MakeCanonical(bind.entity_name_id); + bind.value_id = SemIR::InstId::None; + if (!ReplaceFieldWithConstantValue( + eval_context, &bind, &SemIR::BindSymbolicName::type_id, &phase)) { + return MakeNonConstantResult(phase); + } + return MakeConstantResult(eval_context.context(), bind, phase); +} - // These cases are either not expressions or not constant. - case SemIR::AddrPattern::Kind: - case SemIR::Assign::Kind: - case SemIR::BindName::Kind: - case SemIR::BindingPattern::Kind: - case SemIR::BlockArg::Kind: - case SemIR::Branch::Kind: - case SemIR::BranchIf::Kind: - case SemIR::BranchWithArg::Kind: - case SemIR::ImportCppDecl::Kind: - case SemIR::ImportDecl::Kind: - case SemIR::NameBindingDecl::Kind: - case SemIR::OutParam::Kind: - case SemIR::OutParamPattern::Kind: - case SemIR::RequirementEquivalent::Kind: - case SemIR::RequirementImpls::Kind: - case SemIR::RequirementRewrite::Kind: - case SemIR::Return::Kind: - case SemIR::ReturnExpr::Kind: - case SemIR::ReturnSlotPattern::Kind: - case SemIR::StructLiteral::Kind: - case SemIR::TupleLiteral::Kind: - case SemIR::TuplePattern::Kind: - case SemIR::ValueParam::Kind: - case SemIR::VarPattern::Kind: - case SemIR::VarStorage::Kind: - break; +// TODO: Convert this to an EvalConstantInst instruction. This will require +// providing a `GetConstantValue` overload for a requirement block. +template <> +auto TryEvalTypedInst(EvalContext& eval_context, + SemIR::InstId /*inst_id*/, + SemIR::Inst inst) -> SemIR::ConstantId { + auto typed_inst = inst.As(); - case SemIR::ImportRefUnloaded::Kind: - CARBON_FATAL("ImportRefUnloaded should be loaded before TryEvalInst: {0}", - inst); + Phase phase = Phase::Concrete; + SemIR::TypeId base_facet_type_id = + eval_context.insts().Get(typed_inst.period_self_id).type_id(); + SemIR::Inst base_facet_inst = + eval_context.GetConstantValueAsInst(base_facet_type_id); + SemIR::FacetTypeInfo info = {.other_requirements = false}; + // `where` provides that the base facet is an error, `type`, or a facet + // type. + if (auto facet_type = base_facet_inst.TryAs()) { + info = GetConstantFacetTypeInfo(eval_context, facet_type->facet_type_id, + &phase); + } else if (base_facet_type_id == SemIR::ErrorInst::SingletonTypeId) { + return SemIR::ErrorInst::SingletonConstantId; + } else { + CARBON_CHECK(base_facet_type_id == SemIR::TypeType::SingletonTypeId, + "Unexpected type_id: {0}, inst: {1}", base_facet_type_id, + base_facet_inst); + } + if (typed_inst.requirements_id.has_value()) { + auto insts = eval_context.inst_blocks().Get(typed_inst.requirements_id); + for (auto inst_id : insts) { + if (auto rewrite = + eval_context.insts().TryGetAs( + inst_id)) { + SemIR::ConstantId lhs = eval_context.GetConstantValue(rewrite->lhs_id); + SemIR::ConstantId rhs = eval_context.GetConstantValue(rewrite->rhs_id); + // `where` requirements using `.Self` should not be considered + // symbolic + UpdatePhaseIgnorePeriodSelf(eval_context, lhs, &phase); + UpdatePhaseIgnorePeriodSelf(eval_context, rhs, &phase); + info.rewrite_constraints.push_back( + {.lhs_const_id = lhs, .rhs_const_id = rhs}); + } else { + // TODO: Handle other requirements + info.other_requirements = true; + } + } } - return SemIR::ConstantId::NotConstant; + info.Canonicalize(); + return MakeFacetTypeResult(eval_context.context(), info, phase); +} + +// Implementation for `TryEvalInst`, wrapping `Context` with `EvalContext`. +static auto TryEvalInstInContext(EvalContext& eval_context, + SemIR::InstId inst_id, SemIR::Inst inst) + -> SemIR::ConstantId { + using EvalInstFn = + auto(EvalContext & eval_context, SemIR::InstId inst_id, SemIR::Inst inst) + ->SemIR::ConstantId; + static constexpr EvalInstFn* EvalInstFns[] = { +#define CARBON_SEM_IR_INST_KIND(Kind) &TryEvalTypedInst, +#include "toolchain/sem_ir/inst_kind.def" + }; + [[clang::musttail]] return EvalInstFns[inst.kind().AsInt()](eval_context, + inst_id, inst); } auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) diff --git a/toolchain/check/testdata/array/fail_bound_negative.carbon b/toolchain/check/testdata/array/fail_bound_negative.carbon index 579066cb95689..c33143622b503 100644 --- a/toolchain/check/testdata/array/fail_bound_negative.carbon +++ b/toolchain/check/testdata/array/fail_bound_negative.carbon @@ -10,9 +10,9 @@ fn Negate(n: i32) -> i32 = "int.snegate"; -// CHECK:STDERR: fail_bound_negative.carbon:[[@LINE+4]]:19: error: array bound of -1 is negative [ArrayBoundNegative] +// CHECK:STDERR: fail_bound_negative.carbon:[[@LINE+4]]:8: error: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: var a: array(i32, Negate(1)); -// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: array(i32, Negate(1)); diff --git a/toolchain/check/testdata/array/fail_bound_overflow.carbon b/toolchain/check/testdata/array/fail_bound_overflow.carbon index d13f939b2798c..502963fc72511 100644 --- a/toolchain/check/testdata/array/fail_bound_overflow.carbon +++ b/toolchain/check/testdata/array/fail_bound_overflow.carbon @@ -8,9 +8,9 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/fail_bound_overflow.carbon -// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+4]]:19: error: array bound of 39999999999999999993 is too large [ArrayBoundTooLarge] +// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+4]]:8: error: array bound of 39999999999999999993 is too large [ArrayBoundTooLarge] // CHECK:STDERR: var a: array(i32, 39999999999999999993); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: array(i32, 39999999999999999993); diff --git a/toolchain/check/testdata/array/fail_incomplete_element.carbon b/toolchain/check/testdata/array/fail_incomplete_element.carbon index 012834259a82f..ae2a36b2cf9b0 100644 --- a/toolchain/check/testdata/array/fail_incomplete_element.carbon +++ b/toolchain/check/testdata/array/fail_incomplete_element.carbon @@ -74,7 +74,7 @@ var p: Incomplete* = &a[0]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %a.ref: = name_ref a, file.%a +// CHECK:STDOUT: %a.ref: = name_ref a, file.%a [concrete = ] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %addr: = addr_of [concrete = ] // CHECK:STDOUT: assign file.%p.var, diff --git a/toolchain/check/testdata/array/fail_out_of_bound_non_literal.carbon b/toolchain/check/testdata/array/fail_out_of_bound_non_literal.carbon index 62331a2fe71eb..6e346d737bbd0 100644 --- a/toolchain/check/testdata/array/fail_out_of_bound_non_literal.carbon +++ b/toolchain/check/testdata/array/fail_out_of_bound_non_literal.carbon @@ -135,7 +135,7 @@ var b: i32 = a[{.index = 3}.index]; // CHECK:STDOUT: %.loc16_28.2: %i32 = value_of_initializer %int.convert_checked.loc16 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc16_28.3: %i32 = converted %.loc16_28.1, %.loc16_28.2 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc16_34.1: ref %i32 = array_index %a.ref, %.loc16_28.3 [concrete = ] -// CHECK:STDOUT: %.loc16_34.2: %i32 = bind_value %.loc16_34.1 +// CHECK:STDOUT: %.loc16_34.2: %i32 = bind_value %.loc16_34.1 [concrete = ] // CHECK:STDOUT: assign file.%b.var, %.loc16_34.2 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/builtin_conversions/no_prelude/fail_todo_convert_facet_value_to_narrowed_facet_type.carbon b/toolchain/check/testdata/builtin_conversions/no_prelude/fail_todo_convert_facet_value_to_narrowed_facet_type.carbon index 86a17654876fd..35471add57d31 100644 --- a/toolchain/check/testdata/builtin_conversions/no_prelude/fail_todo_convert_facet_value_to_narrowed_facet_type.carbon +++ b/toolchain/check/testdata/builtin_conversions/no_prelude/fail_todo_convert_facet_value_to_narrowed_facet_type.carbon @@ -184,9 +184,9 @@ fn HandleAnimal[T:! Animal & Eats](a: T) { Feed(a); } // CHECK:STDOUT: } // CHECK:STDOUT: %HandleAnimal.decl: %HandleAnimal.type = fn_decl @HandleAnimal [concrete = constants.%HandleAnimal] { // CHECK:STDOUT: %T.patt.loc15_17.1: = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc15_17.2 (constants.%T.patt.e01)] -// CHECK:STDOUT: %T.param_patt: = value_param_pattern %T.patt.loc15_17.1, runtime_param [symbolic = %T.patt.loc15_17.2 (constants.%T.patt.e01)] +// CHECK:STDOUT: %T.param_patt: = value_param_pattern %T.patt.loc15_17.1, runtime_param [concrete = ] // CHECK:STDOUT: %a.patt: = binding_pattern a -// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param0 +// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.param: = value_param runtime_param // CHECK:STDOUT: %.1: = splice_block [concrete = ] { @@ -238,7 +238,7 @@ fn HandleAnimal[T:! Animal & Eats](a: T) { Feed(a); } // CHECK:STDOUT: fn[%T.param_patt: ](%a.param_patt: ) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Feed.ref: %Feed.type = name_ref Feed, file.%Feed.decl [concrete = constants.%Feed] -// CHECK:STDOUT: %a.ref: = name_ref a, %a +// CHECK:STDOUT: %a.ref: = name_ref a, %a [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/builtin_conversions/value_with_type_through_access.carbon b/toolchain/check/testdata/builtin_conversions/value_with_type_through_access.carbon index 98e2c218939dc..864e599315129 100644 --- a/toolchain/check/testdata/builtin_conversions/value_with_type_through_access.carbon +++ b/toolchain/check/testdata/builtin_conversions/value_with_type_through_access.carbon @@ -524,7 +524,7 @@ fn G() { // CHECK:STDOUT: %x.patt: @F.%HoldsType.loc21_31.2 (%HoldsType.f95cf2.1) = binding_pattern x // CHECK:STDOUT: %x.param_patt: @F.%HoldsType.loc21_31.2 (%HoldsType.f95cf2.1) = value_param_pattern %x.patt, runtime_param0 // CHECK:STDOUT: %a.patt: = binding_pattern a -// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param1 +// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param1 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.param: %Class = value_param runtime_param // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] @@ -723,7 +723,7 @@ fn G() { // CHECK:STDOUT: %x.patt: @F.%HoldsType.loc12_40.2 (%HoldsType) = binding_pattern x // CHECK:STDOUT: %x.param_patt: @F.%HoldsType.loc12_40.2 (%HoldsType) = value_param_pattern %x.patt, runtime_param0 // CHECK:STDOUT: %a.patt: = binding_pattern a -// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param1 +// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param1 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.param: %array_type = value_param runtime_param // CHECK:STDOUT: %.loc12_23: type = splice_block %array_type [concrete = constants.%array_type] { diff --git a/toolchain/check/testdata/choice/fail_invalid.carbon b/toolchain/check/testdata/choice/fail_invalid.carbon index 4bc692bd3e26e..3f843eb74c349 100644 --- a/toolchain/check/testdata/choice/fail_invalid.carbon +++ b/toolchain/check/testdata/choice/fail_invalid.carbon @@ -68,8 +68,8 @@ fn F() { // CHECK:STDOUT: %.loc11_23.1: %empty_struct_type = struct_literal () // CHECK:STDOUT: %Never.ref: type = name_ref Never, file.%Never.decl [concrete = constants.%Never] // CHECK:STDOUT: %.loc11_23.2: ref %Never = temporary_storage -// CHECK:STDOUT: %.loc11_23.3: ref %Never = temporary %.loc11_23.2, -// CHECK:STDOUT: %.loc11_23.4: ref %Never = converted %.loc11_23.1, %.loc11_23.3 +// CHECK:STDOUT: %.loc11_23.3: ref %Never = temporary %.loc11_23.2, [concrete = ] +// CHECK:STDOUT: %.loc11_23.4: ref %Never = converted %.loc11_23.1, %.loc11_23.3 [concrete = ] // CHECK:STDOUT: %never: ref %Never = bind_name never, %.loc11_23.4 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/class/fail_generic_method.carbon b/toolchain/check/testdata/class/fail_generic_method.carbon index 76dbb80f285a6..7b42c53f53b28 100644 --- a/toolchain/check/testdata/class/fail_generic_method.carbon +++ b/toolchain/check/testdata/class/fail_generic_method.carbon @@ -78,9 +78,9 @@ fn Class(N:! i32).F[self: Self](n: T) {} // CHECK:STDOUT: } // CHECK:STDOUT: %.decl: %.type = fn_decl @.1 [concrete = constants.%.d85] { // CHECK:STDOUT: %self.patt: = binding_pattern self -// CHECK:STDOUT: %self.param_patt: = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: %self.param_patt: = value_param_pattern %self.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: %n.patt: = binding_pattern n -// CHECK:STDOUT: %n.param_patt: = value_param_pattern %n.patt, runtime_param1 +// CHECK:STDOUT: %n.param_patt: = value_param_pattern %n.patt, runtime_param1 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %N.param: %i32 = value_param runtime_param // CHECK:STDOUT: %.loc33: type = splice_block %i32 [concrete = constants.%i32] { diff --git a/toolchain/check/testdata/class/fail_init.carbon b/toolchain/check/testdata/class/fail_init.carbon index 3db86a91287ed..c9608c3df1553 100644 --- a/toolchain/check/testdata/class/fail_init.carbon +++ b/toolchain/check/testdata/class/fail_init.carbon @@ -106,8 +106,8 @@ fn F() { // CHECK:STDOUT: %.loc21_10.1: %struct_type.a = struct_literal (%int_1.loc21) // CHECK:STDOUT: %Class.ref.loc21: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc21_10.2: ref %Class = temporary_storage -// CHECK:STDOUT: %.loc21_10.3: ref %Class = temporary %.loc21_10.2, -// CHECK:STDOUT: %.loc21_12: ref %Class = converted %.loc21_10.1, %.loc21_10.3 +// CHECK:STDOUT: %.loc21_10.3: ref %Class = temporary %.loc21_10.2, [concrete = ] +// CHECK:STDOUT: %.loc21_12: ref %Class = converted %.loc21_10.1, %.loc21_10.3 [concrete = ] // CHECK:STDOUT: %int_1.loc26: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2.loc26: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc26_18.1: %struct_type.a.c = struct_literal (%int_1.loc26, %int_2.loc26) @@ -120,16 +120,16 @@ fn F() { // CHECK:STDOUT: %.loc26_18.3: ref %Class = temporary_storage // CHECK:STDOUT: %.loc26_18.4: ref %i32 = class_element_access %.loc26_18.3, element0 // CHECK:STDOUT: %.loc26_18.5: init %i32 = initialize_from %.loc26_18.2 to %.loc26_18.4 [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: %.loc26_18.6: ref %Class = temporary %.loc26_18.3, -// CHECK:STDOUT: %.loc26_20: ref %Class = converted %.loc26_18.1, %.loc26_18.6 +// CHECK:STDOUT: %.loc26_18.6: ref %Class = temporary %.loc26_18.3, [concrete = ] +// CHECK:STDOUT: %.loc26_20: ref %Class = converted %.loc26_18.1, %.loc26_18.6 [concrete = ] // CHECK:STDOUT: %int_1.loc31: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2.loc31: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %.loc31_26.1: %struct_type.a.b.c = struct_literal (%int_1.loc31, %int_2.loc31, %int_3) // CHECK:STDOUT: %Class.ref.loc31: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc31_26.2: ref %Class = temporary_storage -// CHECK:STDOUT: %.loc31_26.3: ref %Class = temporary %.loc31_26.2, -// CHECK:STDOUT: %.loc31_28: ref %Class = converted %.loc31_26.1, %.loc31_26.3 +// CHECK:STDOUT: %.loc31_26.3: ref %Class = temporary %.loc31_26.2, [concrete = ] +// CHECK:STDOUT: %.loc31_28: ref %Class = converted %.loc31_26.1, %.loc31_26.3 [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_self_param.carbon b/toolchain/check/testdata/class/fail_self_param.carbon index 8bd2fb9056a1d..deee11cda40e7 100644 --- a/toolchain/check/testdata/class/fail_self_param.carbon +++ b/toolchain/check/testdata/class/fail_self_param.carbon @@ -42,7 +42,7 @@ var v: C(0); // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %x.patt.loc15_22.1: = symbolic_binding_pattern x, 0 [symbolic = %x.patt.loc15_22.2 (constants.%x.patt)] -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc15_22.1, runtime_param [symbolic = %x.patt.loc15_22.2 (constants.%x.patt)] +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc15_22.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param // CHECK:STDOUT: %self.ref: = name_ref self, [concrete = ] diff --git a/toolchain/check/testdata/class/no_prelude/import_access.carbon b/toolchain/check/testdata/class/no_prelude/import_access.carbon index 0881d3b02f890..5dca8b54768a1 100644 --- a/toolchain/check/testdata/class/no_prelude/import_access.carbon +++ b/toolchain/check/testdata/class/no_prelude/import_access.carbon @@ -488,7 +488,7 @@ private class Redecl {} // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: = binding_pattern c -// CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt, runtime_param0 +// CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: = value_param runtime_param0 // CHECK:STDOUT: %.loc10: type = splice_block %ptr [concrete = ] { @@ -526,7 +526,7 @@ private class Redecl {} // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: = binding_pattern c -// CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt, runtime_param0 +// CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: = value_param runtime_param0 // CHECK:STDOUT: %.loc10: type = splice_block %ptr [concrete = ] { diff --git a/toolchain/check/testdata/class/no_prelude/name_poisoning.carbon b/toolchain/check/testdata/class/no_prelude/name_poisoning.carbon index ea59e93894b13..2a75c4fd5421e 100644 --- a/toolchain/check/testdata/class/no_prelude/name_poisoning.carbon +++ b/toolchain/check/testdata/class/no_prelude/name_poisoning.carbon @@ -481,7 +481,7 @@ class C { // CHECK:STDOUT: %C.decl.loc18: type = class_decl @C.2 [concrete = constants.%C.9f4] {} {} // CHECK:STDOUT: %F2.decl: %F2.type = fn_decl @F2 [concrete = constants.%F2] { // CHECK:STDOUT: %x.patt: = binding_pattern x -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param0 // CHECK:STDOUT: %.1: = splice_block [concrete = ] { @@ -680,7 +680,7 @@ class C { // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: = binding_pattern x -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param0 // CHECK:STDOUT: %C.ref: = name_ref C, [concrete = ] diff --git a/toolchain/check/testdata/deduce/int_float.carbon b/toolchain/check/testdata/deduce/int_float.carbon index bc10213db7e76..53999784cb346 100644 --- a/toolchain/check/testdata/deduce/int_float.carbon +++ b/toolchain/check/testdata/deduce/int_float.carbon @@ -211,7 +211,7 @@ fn G(a: f64) -> Core.IntLiteral() { // CHECK:STDOUT: %N.patt.loc9_6.1: Core.IntLiteral = symbolic_binding_pattern N, 0 [symbolic = %N.patt.loc9_6.2 (constants.%N.patt)] // CHECK:STDOUT: %N.param_patt: Core.IntLiteral = value_param_pattern %N.patt.loc9_6.1, runtime_param [symbolic = %N.patt.loc9_6.2 (constants.%N.patt)] // CHECK:STDOUT: %n.patt: = binding_pattern n -// CHECK:STDOUT: %n.param_patt: = value_param_pattern %n.patt, runtime_param0 +// CHECK:STDOUT: %n.param_patt: = value_param_pattern %n.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: %return.patt: Core.IntLiteral = return_slot_pattern // CHECK:STDOUT: %return.param_patt: Core.IntLiteral = out_param_pattern %return.patt, runtime_param1 // CHECK:STDOUT: } { diff --git a/toolchain/check/testdata/function/declaration/fail_param_in_type.carbon b/toolchain/check/testdata/function/declaration/fail_param_in_type.carbon index 9e27d38b916ab..bb11f13e53310 100644 --- a/toolchain/check/testdata/function/declaration/fail_param_in_type.carbon +++ b/toolchain/check/testdata/function/declaration/fail_param_in_type.carbon @@ -41,7 +41,7 @@ fn F(n: i32, a: array(i32, n)*); // CHECK:STDOUT: %n.patt: %i32 = binding_pattern n // CHECK:STDOUT: %n.param_patt: %i32 = value_param_pattern %n.patt, runtime_param0 // CHECK:STDOUT: %a.patt: = binding_pattern a -// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param1 +// CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt, runtime_param1 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %n.param: %i32 = value_param runtime_param0 // CHECK:STDOUT: %.loc15_9: type = splice_block %i32.loc15_9 [concrete = constants.%i32] { diff --git a/toolchain/check/testdata/function/definition/no_prelude/fail_decl_param_mismatch.carbon b/toolchain/check/testdata/function/definition/no_prelude/fail_decl_param_mismatch.carbon index 1d0b963910fc8..547190bdae6d4 100644 --- a/toolchain/check/testdata/function/definition/no_prelude/fail_decl_param_mismatch.carbon +++ b/toolchain/check/testdata/function/definition/no_prelude/fail_decl_param_mismatch.carbon @@ -144,7 +144,7 @@ fn K() -> {} { return {}; } // CHECK:STDOUT: } // CHECK:STDOUT: %.decl.loc36: %.type.b6a92a.3 = fn_decl @.3 [concrete = constants.%.d852be.3] { // CHECK:STDOUT: %x.patt: = binding_pattern x -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param0 // CHECK:STDOUT: %x: = bind_name x, %x.param diff --git a/toolchain/check/testdata/generic/complete_type.carbon b/toolchain/check/testdata/generic/complete_type.carbon index 0cd27c5437228..aedf9ec9c8d84 100644 --- a/toolchain/check/testdata/generic/complete_type.carbon +++ b/toolchain/check/testdata/generic/complete_type.carbon @@ -15,7 +15,7 @@ library "[[@TEST_NAME]]"; class B; class A(T:! type) { - // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+6]]:10: error: `T` evaluates to incomplete type `B` [IncompleteTypeInMonomorphization] + // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+6]]:10: error: type `B` is incomplete [IncompleteTypeInMonomorphization] // CHECK:STDERR: var v: T; // CHECK:STDERR: ^ // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE-6]]:1: note: class was forward declared here [ClassForwardDeclaredHere] @@ -61,7 +61,7 @@ library "[[@TEST_NAME]]"; class B; fn F(T:! type) { - // CHECK:STDERR: fail_incomplete_in_function_at_eof.carbon:[[@LINE+6]]:10: error: `T` evaluates to incomplete type `B` [IncompleteTypeInMonomorphization] + // CHECK:STDERR: fail_incomplete_in_function_at_eof.carbon:[[@LINE+6]]:10: error: type `B` is incomplete [IncompleteTypeInMonomorphization] // CHECK:STDERR: var v: T; // CHECK:STDERR: ^ // CHECK:STDERR: fail_incomplete_in_function_at_eof.carbon:[[@LINE-6]]:1: note: class was forward declared here [ClassForwardDeclaredHere] diff --git a/toolchain/check/testdata/impl/assoc_const_self.carbon b/toolchain/check/testdata/impl/assoc_const_self.carbon index 0a8d37ec5fcb8..d8237d03c07f5 100644 --- a/toolchain/check/testdata/impl/assoc_const_self.carbon +++ b/toolchain/check/testdata/impl/assoc_const_self.carbon @@ -62,9 +62,9 @@ impl C as I where .V = () {} library "[[@TEST_NAME]]"; interface I(N:! Core.IntLiteral()) { - // CHECK:STDERR: fail_monomorphization_failure.carbon:[[@LINE+3]]:17: error: array bound of -1 is negative [ArrayBoundNegative] + // CHECK:STDERR: fail_monomorphization_failure.carbon:[[@LINE+3]]:11: error: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: let V:! array(Self, N); - // CHECK:STDERR: ^~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~~ let V:! array(Self, N); } @@ -563,7 +563,6 @@ fn CallF() { // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self [symbolic_self] // CHECK:STDOUT: %.Self.as_wit: = facet_access_witness %.Self [symbolic_self] // CHECK:STDOUT: %I.facet: %I.type.057 = facet_value %.Self.as_type, %.Self.as_wit [symbolic_self] -// CHECK:STDOUT: %impl.elem0: = impl_witness_access %.Self.as_wit, element0 [symbolic_self] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -614,7 +613,7 @@ fn CallF() { // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.as_type] // CHECK:STDOUT: %.loc15_24.2: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.as_type] // CHECK:STDOUT: %.Self.as_wit: = facet_access_witness %.Self.ref [symbolic_self = constants.%.Self.as_wit] -// CHECK:STDOUT: %impl.elem0.loc15_24: = impl_witness_access %.Self.as_wit, element0 [symbolic_self = constants.%impl.elem0] +// CHECK:STDOUT: %impl.elem0.loc15_24: = impl_witness_access %.Self.as_wit, element0 [concrete = ] // CHECK:STDOUT: %.loc15_30: %empty_tuple.type = tuple_literal () // CHECK:STDOUT: %.loc15_18: type = where_expr %.Self [concrete = ] { // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc15_24, diff --git a/toolchain/check/testdata/impl/fail_call_invalid.carbon b/toolchain/check/testdata/impl/fail_call_invalid.carbon index 8686608e0eaa0..23564caf7b646 100644 --- a/toolchain/check/testdata/impl/fail_call_invalid.carbon +++ b/toolchain/check/testdata/impl/fail_call_invalid.carbon @@ -106,7 +106,7 @@ fn InstanceCall(n: i32) { // CHECK:STDOUT: impl @impl.006: %i32 as %Simple.ref { // CHECK:STDOUT: %G.decl: %G.type.c98 = fn_decl @G.2 [concrete = constants.%G.e73] { // CHECK:STDOUT: %self.patt: = binding_pattern self -// CHECK:STDOUT: %self.param_patt: = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: %self.param_patt: = value_param_pattern %self.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: = value_param runtime_param0 // CHECK:STDOUT: %Undeclared.ref: = name_ref Undeclared, [concrete = ] diff --git a/toolchain/check/testdata/impl/fail_self_type_mismatch.carbon b/toolchain/check/testdata/impl/fail_self_type_mismatch.carbon index 43b6cd0271cfd..26c87c2ee345f 100644 --- a/toolchain/check/testdata/impl/fail_self_type_mismatch.carbon +++ b/toolchain/check/testdata/impl/fail_self_type_mismatch.carbon @@ -100,7 +100,7 @@ impl i32 as I { // CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %F.decl: %F.type.cf0 = fn_decl @F.1 [concrete = constants.%F.bc6] { // CHECK:STDOUT: %c.patt: = binding_pattern c -// CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt, runtime_param0 +// CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: = value_param runtime_param0 // CHECK:STDOUT: %.loc29: type = splice_block %C [concrete = ] { diff --git a/toolchain/check/testdata/impl/fail_todo_use_assoc_const.carbon b/toolchain/check/testdata/impl/fail_todo_use_assoc_const.carbon index cb1f72386fb8d..effa4190de375 100644 --- a/toolchain/check/testdata/impl/fail_todo_use_assoc_const.carbon +++ b/toolchain/check/testdata/impl/fail_todo_use_assoc_const.carbon @@ -177,7 +177,7 @@ impl () as I where .N = 2 { // CHECK:STDOUT: %self.patt: @F.1.%Self.as_type.loc19_14.1 (%Self.as_type.3df) = binding_pattern self // CHECK:STDOUT: %self.param_patt: @F.1.%Self.as_type.loc19_14.1 (%Self.as_type.3df) = value_param_pattern %self.patt, runtime_param0 // CHECK:STDOUT: %u.patt: = binding_pattern u -// CHECK:STDOUT: %u.param_patt: = value_param_pattern %u.patt, runtime_param1 +// CHECK:STDOUT: %u.param_patt: = value_param_pattern %u.patt, runtime_param1 [concrete = ] // CHECK:STDOUT: %return.patt: = return_slot_pattern // CHECK:STDOUT: %return.param_patt: = out_param_pattern %return.patt, runtime_param2 // CHECK:STDOUT: } { diff --git a/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon b/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon index 7b234a90ad251..15e56a682d709 100644 --- a/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon +++ b/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon @@ -472,7 +472,7 @@ class N.C { // CHECK:STDOUT: } // CHECK:STDOUT: %F2.decl: %F2.type = fn_decl @F2 [concrete = constants.%F2] { // CHECK:STDOUT: %x.patt.loc14_9.1: = symbolic_binding_pattern x, 0 [symbolic = %x.patt.loc14_9.2 (constants.%x.patt.e01)] -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc14_9.1, runtime_param [symbolic = %x.patt.loc14_9.2 (constants.%x.patt.e01)] +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc14_9.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param // CHECK:STDOUT: %.1: = splice_block [concrete = ] { @@ -544,7 +544,7 @@ class N.C { // CHECK:STDOUT: %I.decl.loc17: type = interface_decl @I.2 [concrete = constants.%I.type.4da] {} {} // CHECK:STDOUT: %F2.decl: %F2.type = fn_decl @F2 [concrete = constants.%F2] { // CHECK:STDOUT: %x.patt.loc23_9.1: = symbolic_binding_pattern x, 0 [symbolic = %x.patt.loc23_9.2 (constants.%x.patt.e01)] -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc23_9.1, runtime_param [symbolic = %x.patt.loc23_9.2 (constants.%x.patt.e01)] +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc23_9.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param // CHECK:STDOUT: %.1: = splice_block [concrete = ] { @@ -811,7 +811,7 @@ class N.C { // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt.loc13_8.1: = symbolic_binding_pattern x, 0 [symbolic = %x.patt.loc13_8.2 (constants.%x.patt)] -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc13_8.1, runtime_param [symbolic = %x.patt.loc13_8.2 (constants.%x.patt)] +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt.loc13_8.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param // CHECK:STDOUT: %I.ref: = name_ref I, [concrete = ] diff --git a/toolchain/check/testdata/index/fail_array_large_index.carbon b/toolchain/check/testdata/index/fail_array_large_index.carbon index f566be1ce638c..0c3dd9d7013b2 100644 --- a/toolchain/check/testdata/index/fail_array_large_index.carbon +++ b/toolchain/check/testdata/index/fail_array_large_index.carbon @@ -129,7 +129,7 @@ var c: i32 = a[0x7FFF_FFFF]; // CHECK:STDOUT: %.loc17_16.1: %i32 = value_of_initializer %int.convert_checked.loc17 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc17_16.2: %i32 = converted %int_1, %.loc17_16.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc17_17.1: ref %i32 = array_index %a.ref.loc17, %.loc17_16.2 [concrete = ] -// CHECK:STDOUT: %.loc17_17.2: %i32 = bind_value %.loc17_17.1 +// CHECK:STDOUT: %.loc17_17.2: %i32 = bind_value %.loc17_17.1 [concrete = ] // CHECK:STDOUT: assign file.%b.var, %.loc17_17.2 // CHECK:STDOUT: %a.ref.loc23: ref %array_type = name_ref a, file.%a // CHECK:STDOUT: %int_2147483647: Core.IntLiteral = int_value 2147483647 [concrete = constants.%int_2147483647.d89] @@ -142,7 +142,7 @@ var c: i32 = a[0x7FFF_FFFF]; // CHECK:STDOUT: %.loc23_16.1: %i32 = value_of_initializer %int.convert_checked.loc23 [concrete = constants.%int_2147483647.a74] // CHECK:STDOUT: %.loc23_16.2: %i32 = converted %int_2147483647, %.loc23_16.1 [concrete = constants.%int_2147483647.a74] // CHECK:STDOUT: %.loc23_27.1: ref %i32 = array_index %a.ref.loc23, %.loc23_16.2 [concrete = ] -// CHECK:STDOUT: %.loc23_27.2: %i32 = bind_value %.loc23_27.1 +// CHECK:STDOUT: %.loc23_27.2: %i32 = bind_value %.loc23_27.1 [concrete = ] // CHECK:STDOUT: assign file.%c.var, %.loc23_27.2 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon b/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon index ec1b314bd05eb..68240846b0d13 100644 --- a/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon +++ b/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon @@ -103,7 +103,7 @@ var b: i32 = a[2.6]; // CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %.loc19_16: %i32 = converted %float, [concrete = ] // CHECK:STDOUT: %.loc19_19.1: ref %i32 = array_index %a.ref, [concrete = ] -// CHECK:STDOUT: %.loc19_19.2: %i32 = bind_value %.loc19_19.1 +// CHECK:STDOUT: %.loc19_19.2: %i32 = bind_value %.loc19_19.1 [concrete = ] // CHECK:STDOUT: assign file.%b.var, %.loc19_19.2 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon b/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon index c8eb8000e8fbb..bffad62875a23 100644 --- a/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon +++ b/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon @@ -107,7 +107,7 @@ var b: i32 = a[1]; // CHECK:STDOUT: %.loc16_16.1: %i32 = value_of_initializer %int.convert_checked.loc16 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc16_16.2: %i32 = converted %int_1, %.loc16_16.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc16_17.1: ref %i32 = array_index %a.ref, %.loc16_16.2 [concrete = ] -// CHECK:STDOUT: %.loc16_17.2: %i32 = bind_value %.loc16_17.1 +// CHECK:STDOUT: %.loc16_17.2: %i32 = bind_value %.loc16_17.1 [concrete = ] // CHECK:STDOUT: assign file.%b.var, %.loc16_17.2 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_negative_indexing.carbon b/toolchain/check/testdata/index/fail_negative_indexing.carbon index a67dcad948b66..9314c80a8c80c 100644 --- a/toolchain/check/testdata/index/fail_negative_indexing.carbon +++ b/toolchain/check/testdata/index/fail_negative_indexing.carbon @@ -134,7 +134,7 @@ var d: i32 = c[-10]; // CHECK:STDOUT: %.loc16_16.3: %i32 = value_of_initializer %int.convert_checked.loc16 [concrete = constants.%int_-10.c17] // CHECK:STDOUT: %.loc16_16.4: %i32 = converted %int.snegate, %.loc16_16.3 [concrete = constants.%int_-10.c17] // CHECK:STDOUT: %.loc16_19.1: ref %i32 = array_index %c.ref, %.loc16_16.4 [concrete = ] -// CHECK:STDOUT: %.loc16_19.2: %i32 = bind_value %.loc16_19.1 +// CHECK:STDOUT: %.loc16_19.2: %i32 = bind_value %.loc16_19.1 [concrete = ] // CHECK:STDOUT: assign file.%d.var, %.loc16_19.2 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon index 3fabe3691503d..eee7fecfa9834 100644 --- a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon +++ b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon @@ -41,8 +41,8 @@ alias UseOther = I.other; // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %I.ref.loc24: type = name_ref I, %I.decl [concrete = constants.%I.type] -// CHECK:STDOUT: %a.ref: = name_ref a, .inst19.loc20_7 -// CHECK:STDOUT: %UseA: = bind_alias UseA, .inst19.loc20_7 +// CHECK:STDOUT: %a.ref: = name_ref a, .inst19.loc20_7 [concrete = ] +// CHECK:STDOUT: %UseA: = bind_alias UseA, .inst19.loc20_7 [concrete = ] // CHECK:STDOUT: %I.ref.loc27: type = name_ref I, %I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %other.ref: = name_ref other, [concrete = ] // CHECK:STDOUT: %UseOther: = bind_alias UseOther, [concrete = ] diff --git a/toolchain/check/testdata/interface/no_prelude/import_access.carbon b/toolchain/check/testdata/interface/no_prelude/import_access.carbon index 844acd6d6ecc7..a231e67fb9309 100644 --- a/toolchain/check/testdata/interface/no_prelude/import_access.carbon +++ b/toolchain/check/testdata/interface/no_prelude/import_access.carbon @@ -255,7 +255,7 @@ private interface Redecl {} // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %i.patt: = binding_pattern i -// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 +// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %i.param: = value_param runtime_param0 // CHECK:STDOUT: %Def.ref: = name_ref Def, [concrete = ] @@ -290,7 +290,7 @@ private interface Redecl {} // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %i.patt: = binding_pattern i -// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 +// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %i.param: = value_param runtime_param0 // CHECK:STDOUT: %.1: = splice_block [concrete = ] { @@ -362,7 +362,7 @@ private interface Redecl {} // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %i.patt: = binding_pattern i -// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 +// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %i.param: = value_param runtime_param0 // CHECK:STDOUT: %ForwardWithDef.ref: = name_ref ForwardWithDef, [concrete = ] @@ -397,7 +397,7 @@ private interface Redecl {} // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %i.patt: = binding_pattern i -// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 +// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %i.param: = value_param runtime_param0 // CHECK:STDOUT: %.1: = splice_block [concrete = ] { @@ -431,7 +431,7 @@ private interface Redecl {} // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %i.patt: = binding_pattern i -// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 +// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %i.param: = value_param runtime_param0 // CHECK:STDOUT: %.loc11: type = splice_block %ptr [concrete = ] { @@ -471,7 +471,7 @@ private interface Redecl {} // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %i.patt: = binding_pattern i -// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 +// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %i.param: = value_param runtime_param0 // CHECK:STDOUT: %.loc10: type = splice_block %ptr [concrete = ] { @@ -509,7 +509,7 @@ private interface Redecl {} // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %i.patt: = binding_pattern i -// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 +// CHECK:STDOUT: %i.param_patt: = value_param_pattern %i.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %i.param: = value_param runtime_param0 // CHECK:STDOUT: %.loc10: type = splice_block %ptr [concrete = ] { diff --git a/toolchain/check/testdata/let/generic_import.carbon b/toolchain/check/testdata/let/generic_import.carbon index 777ece5d2dd9e..293eae70508b6 100644 --- a/toolchain/check/testdata/let/generic_import.carbon +++ b/toolchain/check/testdata/let/generic_import.carbon @@ -108,8 +108,8 @@ var b: T = *a; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %a.ref: = name_ref a, file.%a -// CHECK:STDOUT: %.loc13: ref = deref +// CHECK:STDOUT: %a.ref: = name_ref a, file.%a [concrete = ] +// CHECK:STDOUT: %.loc13: ref = deref [concrete = ] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/operators/builtin/fail_assignment_to_error.carbon b/toolchain/check/testdata/operators/builtin/fail_assignment_to_error.carbon index 119045502bf7d..6fd6dda0b95f3 100644 --- a/toolchain/check/testdata/operators/builtin/fail_assignment_to_error.carbon +++ b/toolchain/check/testdata/operators/builtin/fail_assignment_to_error.carbon @@ -53,7 +53,7 @@ fn Main() { // CHECK:STDOUT: %int_42.loc16: Core.IntLiteral = int_value 42 [concrete = constants.%int_42] // CHECK:STDOUT: assign %undeclared.ref, // CHECK:STDOUT: %also_undeclared.ref: = name_ref also_undeclared, [concrete = ] -// CHECK:STDOUT: %.loc21: ref = deref +// CHECK:STDOUT: %.loc21: ref = deref [concrete = ] // CHECK:STDOUT: %int_42.loc21: Core.IntLiteral = int_value 42 [concrete = constants.%int_42] // CHECK:STDOUT: assign %.loc21, // CHECK:STDOUT: return diff --git a/toolchain/check/testdata/packages/fail_import_type_error.carbon b/toolchain/check/testdata/packages/fail_import_type_error.carbon index 35dcd0384d53d..03badbee8f5bb 100644 --- a/toolchain/check/testdata/packages/fail_import_type_error.carbon +++ b/toolchain/check/testdata/packages/fail_import_type_error.carbon @@ -180,13 +180,13 @@ var d: i32 = d_ref; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %a_ref.ref: = name_ref a_ref, imports.%Implicit.a_ref +// CHECK:STDOUT: %a_ref.ref: = name_ref a_ref, imports.%Implicit.a_ref [concrete = ] // CHECK:STDOUT: assign file.%a.var, -// CHECK:STDOUT: %b_ref.ref: = name_ref b_ref, imports.%Implicit.b_ref +// CHECK:STDOUT: %b_ref.ref: = name_ref b_ref, imports.%Implicit.b_ref [concrete = ] // CHECK:STDOUT: assign file.%b.var, -// CHECK:STDOUT: %c_ref.ref: = name_ref c_ref, imports.%Implicit.c_ref +// CHECK:STDOUT: %c_ref.ref: = name_ref c_ref, imports.%Implicit.c_ref [concrete = ] // CHECK:STDOUT: assign file.%c.var, -// CHECK:STDOUT: %d_ref.ref: = name_ref d_ref, imports.%Implicit.d_ref +// CHECK:STDOUT: %d_ref.ref: = name_ref d_ref, imports.%Implicit.d_ref [concrete = ] // CHECK:STDOUT: assign file.%d.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/packages/no_prelude/core_name_poisoning.carbon b/toolchain/check/testdata/packages/no_prelude/core_name_poisoning.carbon index 8043d2096b215..f5d455834c4a6 100644 --- a/toolchain/check/testdata/packages/no_prelude/core_name_poisoning.carbon +++ b/toolchain/check/testdata/packages/no_prelude/core_name_poisoning.carbon @@ -35,7 +35,7 @@ class r#Core {} // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: = binding_pattern x -// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt, runtime_param0 [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param runtime_param0 // CHECK:STDOUT: %x: = bind_name x, %x.param diff --git a/toolchain/check/testdata/pointer/fail_deref_error.carbon b/toolchain/check/testdata/pointer/fail_deref_error.carbon index 9f7ba435971f3..14bfce43ef263 100644 --- a/toolchain/check/testdata/pointer/fail_deref_error.carbon +++ b/toolchain/check/testdata/pointer/fail_deref_error.carbon @@ -63,9 +63,9 @@ let n2: i32 = undeclared->foo; // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %undeclared.ref.loc15: = name_ref undeclared, [concrete = ] -// CHECK:STDOUT: %.loc15: ref = deref +// CHECK:STDOUT: %.loc15: ref = deref [concrete = ] // CHECK:STDOUT: %undeclared.ref.loc20: = name_ref undeclared, [concrete = ] -// CHECK:STDOUT: %.loc20: ref = deref +// CHECK:STDOUT: %.loc20: ref = deref [concrete = ] // CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/pointer/fail_deref_function.carbon b/toolchain/check/testdata/pointer/fail_deref_function.carbon index fb905879cd513..fb734e44a1a54 100644 --- a/toolchain/check/testdata/pointer/fail_deref_function.carbon +++ b/toolchain/check/testdata/pointer/fail_deref_function.carbon @@ -47,9 +47,9 @@ fn A() { // CHECK:STDOUT: fn @A() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref.loc16: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] -// CHECK:STDOUT: %.loc16: ref = deref +// CHECK:STDOUT: %.loc16: ref = deref [concrete = ] // CHECK:STDOUT: %A.ref.loc21: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] -// CHECK:STDOUT: %.loc21: ref = deref +// CHECK:STDOUT: %.loc21: ref = deref [concrete = ] // CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/pointer/fail_deref_namespace.carbon b/toolchain/check/testdata/pointer/fail_deref_namespace.carbon index 84d93e3d56f76..664ab76ce6c08 100644 --- a/toolchain/check/testdata/pointer/fail_deref_namespace.carbon +++ b/toolchain/check/testdata/pointer/fail_deref_namespace.carbon @@ -51,9 +51,9 @@ fn F() { // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref.loc18: = name_ref A, file.%A [concrete = file.%A] -// CHECK:STDOUT: %.loc18: ref = deref +// CHECK:STDOUT: %.loc18: ref = deref [concrete = ] // CHECK:STDOUT: %A.ref.loc23: = name_ref A, file.%A [concrete = file.%A] -// CHECK:STDOUT: %.loc23: ref = deref +// CHECK:STDOUT: %.loc23: ref = deref [concrete = ] // CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon b/toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon index bdc8983eb9be0..e25edda350ace 100644 --- a/toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon +++ b/toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon @@ -84,27 +84,27 @@ fn Deref(n: i32) { // CHECK:STDOUT: fn @Deref(%n.param_patt: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref.loc16: %i32 = name_ref n, %n -// CHECK:STDOUT: %.loc16: ref = deref %n.ref.loc16 +// CHECK:STDOUT: %.loc16: ref = deref %n.ref.loc16 [concrete = ] // CHECK:STDOUT: %n.ref.loc21: %i32 = name_ref n, %n -// CHECK:STDOUT: %.loc21: ref = deref %n.ref.loc21 +// CHECK:STDOUT: %.loc21: ref = deref %n.ref.loc21 [concrete = ] // CHECK:STDOUT: %foo.ref.loc21: = name_ref foo, [concrete = ] // CHECK:STDOUT: %.loc26_5.1: %empty_tuple.type = tuple_literal () // CHECK:STDOUT: %empty_tuple.loc26: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc26_5.2: %empty_tuple.type = converted %.loc26_5.1, %empty_tuple.loc26 [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc26_3: ref = deref %.loc26_5.2 +// CHECK:STDOUT: %.loc26_3: ref = deref %.loc26_5.2 [concrete = ] // CHECK:STDOUT: %.loc31_4.1: %empty_tuple.type = tuple_literal () // CHECK:STDOUT: %empty_tuple.loc31: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc31_4.2: %empty_tuple.type = converted %.loc31_4.1, %empty_tuple.loc31 [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc31_5: ref = deref %.loc31_4.2 +// CHECK:STDOUT: %.loc31_5: ref = deref %.loc31_4.2 [concrete = ] // CHECK:STDOUT: %foo.ref.loc31: = name_ref foo, [concrete = ] // CHECK:STDOUT: %.loc36_5.1: %empty_struct_type = struct_literal () // CHECK:STDOUT: %empty_struct.loc36: %empty_struct_type = struct_value () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc36_5.2: %empty_struct_type = converted %.loc36_5.1, %empty_struct.loc36 [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc36_3: ref = deref %.loc36_5.2 +// CHECK:STDOUT: %.loc36_3: ref = deref %.loc36_5.2 [concrete = ] // CHECK:STDOUT: %.loc41_4.1: %empty_struct_type = struct_literal () // CHECK:STDOUT: %empty_struct.loc41: %empty_struct_type = struct_value () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc41_4.2: %empty_struct_type = converted %.loc41_4.1, %empty_struct.loc41 [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc41_5: ref = deref %.loc41_4.2 +// CHECK:STDOUT: %.loc41_5: ref = deref %.loc41_4.2 [concrete = ] // CHECK:STDOUT: %foo.ref.loc41: = name_ref foo, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/pointer/fail_deref_type.carbon b/toolchain/check/testdata/pointer/fail_deref_type.carbon index cd548f2de7a64..237ce6a1da394 100644 --- a/toolchain/check/testdata/pointer/fail_deref_type.carbon +++ b/toolchain/check/testdata/pointer/fail_deref_type.carbon @@ -52,7 +52,7 @@ var p2: i32->foo; // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %int_32.loc18: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %i32.loc18: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] -// CHECK:STDOUT: %.loc18_8: ref = deref %i32.loc18 +// CHECK:STDOUT: %.loc18_8: ref = deref %i32.loc18 [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %p: = bind_name p, // CHECK:STDOUT: name_binding_decl { @@ -63,7 +63,7 @@ var p2: i32->foo; // CHECK:STDOUT: %.2: = splice_block [concrete = ] { // CHECK:STDOUT: %int_32.loc23: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %i32.loc23: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] -// CHECK:STDOUT: %.loc23_12: ref = deref %i32.loc23 +// CHECK:STDOUT: %.loc23_12: ref = deref %i32.loc23 [concrete = ] // CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %p2: = bind_name p2, diff --git a/toolchain/check/testdata/struct/no_prelude/fail_nested_incomplete.carbon b/toolchain/check/testdata/struct/no_prelude/fail_nested_incomplete.carbon index 444313e31695e..be26739ac23fb 100644 --- a/toolchain/check/testdata/struct/no_prelude/fail_nested_incomplete.carbon +++ b/toolchain/check/testdata/struct/no_prelude/fail_nested_incomplete.carbon @@ -62,7 +62,7 @@ var p: Incomplete* = &s.a; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %s.ref: = name_ref s, file.%s +// CHECK:STDOUT: %s.ref: = name_ref s, file.%s [concrete = ] // CHECK:STDOUT: %a.ref: = name_ref a, [concrete = ] // CHECK:STDOUT: %addr: = addr_of %a.ref [concrete = ] // CHECK:STDOUT: assign file.%p.var, diff --git a/toolchain/check/testdata/tuple/fail_nested_incomplete.carbon b/toolchain/check/testdata/tuple/fail_nested_incomplete.carbon index 6756fde7dd514..16e376eb28f4a 100644 --- a/toolchain/check/testdata/tuple/fail_nested_incomplete.carbon +++ b/toolchain/check/testdata/tuple/fail_nested_incomplete.carbon @@ -79,7 +79,7 @@ var p: Incomplete* = &t[1]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %t.ref: = name_ref t, file.%t +// CHECK:STDOUT: %t.ref: = name_ref t, file.%t [concrete = ] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %addr: = addr_of [concrete = ] // CHECK:STDOUT: assign file.%p.var, diff --git a/toolchain/check/testdata/where_expr/designator.carbon b/toolchain/check/testdata/where_expr/designator.carbon index bd4a41158f7cf..bd55ce4f3913c 100644 --- a/toolchain/check/testdata/where_expr/designator.carbon +++ b/toolchain/check/testdata/where_expr/designator.carbon @@ -275,7 +275,7 @@ class D { // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %PeriodMismatch.decl: %PeriodMismatch.type = fn_decl @PeriodMismatch [concrete = constants.%PeriodMismatch] { // CHECK:STDOUT: %W.patt.loc12_19.1: = symbolic_binding_pattern W, 0 [symbolic = %W.patt.loc12_19.2 (constants.%W.patt)] -// CHECK:STDOUT: %W.param_patt: = value_param_pattern %W.patt.loc12_19.1, runtime_param [symbolic = %W.patt.loc12_19.2 (constants.%W.patt)] +// CHECK:STDOUT: %W.param_patt: = value_param_pattern %W.patt.loc12_19.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %W.param: = value_param runtime_param // CHECK:STDOUT: %.loc12_25.1: type = splice_block %.loc12_25.2 [concrete = ] { diff --git a/toolchain/check/testdata/where_expr/equal_rewrite.carbon b/toolchain/check/testdata/where_expr/equal_rewrite.carbon index b80237072d58e..8f205a5450820 100644 --- a/toolchain/check/testdata/where_expr/equal_rewrite.carbon +++ b/toolchain/check/testdata/where_expr/equal_rewrite.carbon @@ -1335,7 +1335,7 @@ let K: (E where .F = .Self.G) = bool; // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %RewriteTypeMismatch.decl: %RewriteTypeMismatch.type = fn_decl @RewriteTypeMismatch [concrete = constants.%RewriteTypeMismatch] { // CHECK:STDOUT: %X.patt.loc16_24.1: = symbolic_binding_pattern X, 0 [symbolic = %X.patt.loc16_24.2 (constants.%X.patt)] -// CHECK:STDOUT: %X.param_patt: = value_param_pattern %X.patt.loc16_24.1, runtime_param [symbolic = %X.patt.loc16_24.2 (constants.%X.patt)] +// CHECK:STDOUT: %X.param_patt: = value_param_pattern %X.patt.loc16_24.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %X.param: = value_param runtime_param // CHECK:STDOUT: %.loc16_30.1: type = splice_block %.loc16_30.2 [concrete = ] { diff --git a/toolchain/check/testdata/where_expr/fail_not_facet.carbon b/toolchain/check/testdata/where_expr/fail_not_facet.carbon index 552e0d4d3cee5..c295827b1a634 100644 --- a/toolchain/check/testdata/where_expr/fail_not_facet.carbon +++ b/toolchain/check/testdata/where_expr/fail_not_facet.carbon @@ -67,7 +67,7 @@ var v: e where .x = 3; // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt.loc8_6.1: = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc8_6.2 (constants.%T.patt)] -// CHECK:STDOUT: %T.param_patt: = value_param_pattern %T.patt.loc8_6.1, runtime_param [symbolic = %T.patt.loc8_6.2 (constants.%T.patt)] +// CHECK:STDOUT: %T.param_patt: = value_param_pattern %T.patt.loc8_6.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.param: = value_param runtime_param // CHECK:STDOUT: %.loc8_14.1: type = splice_block %.loc8_14.2 [concrete = ] { @@ -121,7 +121,7 @@ var v: e where .x = 3; // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %U.patt.loc8_6.1: = symbolic_binding_pattern U, 0 [symbolic = %U.patt.loc8_6.2 (constants.%U.patt)] -// CHECK:STDOUT: %U.param_patt: = value_param_pattern %U.patt.loc8_6.1, runtime_param [symbolic = %U.patt.loc8_6.2 (constants.%U.patt)] +// CHECK:STDOUT: %U.param_patt: = value_param_pattern %U.patt.loc8_6.1, runtime_param [concrete = ] // CHECK:STDOUT: } { // CHECK:STDOUT: %U.param: = value_param runtime_param // CHECK:STDOUT: %.loc8_23.1: type = splice_block %.loc8_23.2 [concrete = ] { diff --git a/toolchain/lower/constant.cpp b/toolchain/lower/constant.cpp index 8d81bcc82de1a..3047b714ab498 100644 --- a/toolchain/lower/constant.cpp +++ b/toolchain/lower/constant.cpp @@ -226,6 +226,8 @@ template static auto MaybeEmitAsConstant(ConstantContext& context, InstT inst) -> llvm::Constant* { if constexpr (InstT::Kind.constant_kind() == SemIR::InstConstantKind::Never || + InstT::Kind.constant_kind() == + SemIR::InstConstantKind::Indirect || InstT::Kind.constant_kind() == SemIR::InstConstantKind::SymbolicOnly) { CARBON_FATAL("Unexpected constant instruction kind {0}", inst); diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 64d6c79dad441..2ae307314e806 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -72,7 +72,9 @@ static auto LowerInstHelper(FunctionContext& context, SemIR::InstId inst_id, "instruction in lowered contexts. Instruction: {0}", inst); } else if constexpr (InstT::Kind.constant_kind() == - SemIR::InstConstantKind::Always) { + SemIR::InstConstantKind::Always || + InstT::Kind.constant_kind() == + SemIR::InstConstantKind::Unique) { CARBON_FATAL("Missing constant value for constant instruction {0}", inst); } else if constexpr (InstT::Kind.is_type() == SemIR::InstIsType::Always) { // For instructions that are always of type `type`, produce the trivial diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 47fb2201786a0..7c25a7e9dd10c 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -1349,6 +1349,10 @@ class FormatterImpl { FormatName(static_cast(id)); } + auto FormatName(DestInstId id) -> void { + FormatName(static_cast(id)); + } + auto FormatName(SpecificId id) -> void { const auto& specific = sem_ir_->specifics().Get(id); FormatName(specific.generic_id); diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index 603338c353454..a19642f24e9a0 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -139,7 +139,7 @@ using IdKind = TypeEnum< // From base/value_store.h. IntId, RealId, FloatId, StringLiteralValueId, // From sem_ir/ids.h. - InstId, AbsoluteInstId, AnyRawId, ConstantId, EntityNameId, + InstId, AbsoluteInstId, DestInstId, AnyRawId, ConstantId, EntityNameId, CompileTimeBindIndex, RuntimeParamIndex, FacetTypeId, FunctionId, ClassId, InterfaceId, AssociatedConstantId, ImplId, GenericId, SpecificId, ImportIRId, ImportIRInstId, LocId, BoolValue, IntKind, NameId, NameScopeId, diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 0d0a16a4caecf..b1a949d2c8b6c 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -74,6 +74,28 @@ class AbsoluteInstId : public InstId { using InstId::InstId; }; +// An ID of an instruction that is used as the destination of an initializing +// expression. This should only be used as the type of a field within a typed +// instruction class. +// +// This behaves in most respects like an InstId field, but constant evaluation +// of an instruction with a destination field will not evaluate this field, and +// substitution will not substitute into it. +// +// TODO: Decide on how substitution should handle this. Multiple instructions +// can refer to the same destination, so these don't have the tree structure +// that substitution expects, but we might need to substitute into the result of +// an instruction. +class DestInstId : public InstId { + public: + // Support implicit conversion from InstId so that InstId and DestInstId + // have the same interface. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr DestInstId(InstId inst_id) : InstId(inst_id) {} + + using InstId::InstId; +}; + // The ID of a constant value of an expression. An expression is either: // // - a concrete constant, whose value does not depend on any generic parameters, @@ -110,17 +132,17 @@ struct ConstantId : public IdBase { using IdBase::IdBase; // Returns whether this represents a constant. Requires has_value. - auto is_constant() const -> bool { + constexpr auto is_constant() const -> bool { CARBON_DCHECK(has_value()); return *this != ConstantId::NotConstant; } // Returns whether this represents a symbolic constant. Requires has_value. - auto is_symbolic() const -> bool { + constexpr auto is_symbolic() const -> bool { CARBON_DCHECK(has_value()); return index <= FirstSymbolicIndex; } // Returns whether this represents a concrete constant. Requires has_value. - auto is_concrete() const -> bool { + constexpr auto is_concrete() const -> bool { CARBON_DCHECK(has_value()); return index >= 0; } diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index 2393263ab1653..abd90cb7befb7 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -286,7 +286,8 @@ class Inst : public Printable { // Raw constructor, used for testing. explicit Inst(InstKind kind, TypeId type_id, int32_t arg0, int32_t arg1) : Inst(kind.AsInt(), type_id, arg0, arg1) {} - explicit Inst(int32_t kind, TypeId type_id, int32_t arg0, int32_t arg1) + explicit constexpr Inst(int32_t kind, TypeId type_id, int32_t arg0, + int32_t arg1) : kind_(kind), type_id_(type_id), arg0_(arg0), arg1_(arg1) {} int32_t kind_; diff --git a/toolchain/sem_ir/inst_kind.h b/toolchain/sem_ir/inst_kind.h index 79a416a74a213..d85ee87b6989f 100644 --- a/toolchain/sem_ir/inst_kind.h +++ b/toolchain/sem_ir/inst_kind.h @@ -7,10 +7,7 @@ #include -#include "common/check.h" #include "common/enum_base.h" -#include "llvm/ADT/FoldingSet.h" - namespace Carbon::SemIR { // Whether an instruction defines a type. @@ -35,28 +32,51 @@ enum class InstValueKind : int8_t { Typed, }; -// Whether an instruction can be used to define a constant value. This specifies -// whether the instruction can be added to the `constants()` list. Note that -// even instructions that cannot define a constant value can still have an -// associated `constant_value()`, but the constant value will be a different -// kind of instruction. +// Whether an instruction can have a constant value, and whether it can be used +// to define a constant value. +// +// This specifies whether an instruction of this kind can have a corresponding +// constant value in the `constant_values()` list, and whether an instruction of +// this kind can be added to the `constants()` list. enum class InstConstantKind : int8_t { - // This instruction never defines a constant value. For example, - // `UnaryOperatorNot` never defines a constant value; if its operand is a - // concrete constant, its constant value will instead be a `BoolLiteral`. This - // is also used for instructions that don't produce a value at all. + // This instruction is never constant. Its constant value is always + // `NotConstant`. This is also used for instructions that don't produce a + // value at all and aren't used as constants. Never, - // This instruction may be a symbolic constant, depending on its operands, but - // is never a concrete constant. For example, a `Call` instruction can be a - // symbolic constant but never a concrete constant. + // This instruction never defines a constant value, but can evaluate to a + // constant value of a different kind. For example, `UnaryOperatorNot` never + // defines a constant value; if its operand is a concrete constant, its + // constant value will instead be a `BoolLiteral`, and if its operand is not a + // concrete constant, the result is non-constant. This is the default. + Indirect, + // This instruction may define a symbolic constant, depending on its operands, + // but never a concrete constant. For example, a `Call` instruction can define + // a symbolic constant but never a concrete constant. The instruction may have + // a concrete constant value of a different kind. SymbolicOnly, // This instruction can define a symbolic or concrete constant, but might not - // have a constant value, depending on its operands. For example, a - // `TupleValue` can define a constant if its operands are constants. + // have a constant value, might have a constant value that is not defined by + // itself, or might result in a compile-time error, depending on its operands. + // For example, `ArrayType` is a compile-time constant if its operands are + // constant and its array bound is within a valid range. Conditional, - // This instruction always has a constant value of the same kind. For example, - // `IntValue`. + // This instruction defines a symbolic or concrete constant whenever its + // operands are constant. Otherwise, it is non-constant. For example, a + // `TupleValue` defines a constant if and only if its operands are constants. + // Constant evaluation support for types with this constant kind is provided + // automatically. + WheneverPossible, + // This instruction always has a constant value of the same kind. This is the + // same as `WheneverPossible`, except that the operands are known in advance + // to always be constant. For example, `IntValue`. Always, + // This instruction is itself a unique constant. This is used for declarations + // whose constant identity is simply themselves. The `ConstantId` for this + // instruction will always be a concrete constant whose `InstId` refers + // directly back to the instruction, rather than to a separate instrinction in + // the constants block. + // TODO: Decide if this is the model we want for these cases. + Unique, }; // Whether an instruction is a terminator or part of the terminator sequence. @@ -91,7 +111,7 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) { struct DefinitionInfo { llvm::StringLiteral ir_name; InstIsType is_type = InstIsType::Never; - InstConstantKind constant_kind = InstConstantKind::Never; + InstConstantKind constant_kind = InstConstantKind::Indirect; TerminatorKind terminator_kind = TerminatorKind::NotTerminator; bool is_lowered = true; bool deduce_through = false; @@ -143,10 +163,6 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) { return definition_info(*this).deduce_through; } - // Compute a fingerprint for this instruction kind, allowing its use as part - // of the key in a `FoldingSet`. - auto Profile(llvm::FoldingSetNodeID& id) -> void { id.AddInteger(AsInt()); } - private: // Returns the DefinitionInfo for the kind. static auto definition_info(InstKind kind) -> const DefinitionInfo&; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 186bc24e9b657..1d61789350940 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -48,32 +48,6 @@ namespace Carbon::SemIR { -// Used for the type of patterns that do not match a fixed type. -struct AutoType { - static constexpr auto Kind = InstKind::AutoType.Define( - {.ir_name = "auto", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - static constexpr auto SingletonInstId = MakeSingletonInstId(); - static constexpr auto SingletonTypeId = - TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(SingletonInstId)); - - TypeId type_id; -}; - -// The type of bool literals and branch conditions, bool. -struct BoolType { - static constexpr auto Kind = InstKind::BoolType.Define( - {.ir_name = "bool", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto SingletonInstId = MakeSingletonInstId(); - - TypeId type_id; -}; - // Common representation for declarations describing the foundation type of a // class -- either its adapted type or its base class. struct AnyFoundationDecl { @@ -89,7 +63,7 @@ struct AnyFoundationDecl { struct AdaptDecl { static constexpr auto Kind = InstKind::AdaptDecl.Define( {.ir_name = "adapt_decl", - .constant_kind = InstConstantKind::Always, + .constant_kind = InstConstantKind::Unique, .is_lowered = false}); // No type_id; this is not a value. @@ -101,7 +75,8 @@ struct AdaptDecl { struct AddrOf { // Parse node is usually Parse::PrefixOperatorAmpId. static constexpr auto Kind = InstKind::AddrOf.Define( - {.ir_name = "addr_of", .constant_kind = InstConstantKind::Conditional}); + {.ir_name = "addr_of", + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; InstId lvalue_id; @@ -111,7 +86,9 @@ struct AddrOf { // generally be a pattern inst. struct AddrPattern { static constexpr auto Kind = InstKind::AddrPattern.Define( - {.ir_name = "addr_pattern", .is_lowered = false}); + {.ir_name = "addr_pattern", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); TypeId type_id; // The `self` binding. @@ -153,7 +130,7 @@ struct AnyAggregateInit { InstKind kind; TypeId type_id; InstBlockId elements_id; - InstId dest_id; + DestInstId dest_id; }; // Common representation for all kinds of aggregate value. @@ -175,7 +152,7 @@ struct ArrayInit { TypeId type_id; InstBlockId inits_id; - InstId dest_id; + DestInstId dest_id; }; // An array of `element_type_id` values, sized to `bound_id`. @@ -205,8 +182,8 @@ struct AsCompatible { // `InitializeFrom`. struct Assign { // TODO: Make Parse::NodeId more specific. - static constexpr auto Kind = - InstKind::Assign.Define({.ir_name = "assign"}); + static constexpr auto Kind = InstKind::Assign.Define( + {.ir_name = "assign", .constant_kind = InstConstantKind::Never}); // Assignments are statements, and so have no type. InstId lhs_id; @@ -218,7 +195,9 @@ struct AssociatedConstantDecl { static constexpr auto Kind = InstKind::AssociatedConstantDecl .Define( - {.ir_name = "assoc_const_decl", .is_lowered = false}); + {.ir_name = "assoc_const_decl", + .constant_kind = InstConstantKind::Unique, + .is_lowered = false}); TypeId type_id; AssociatedConstantId assoc_const_id; @@ -246,7 +225,7 @@ struct AssociatedEntityType { InstKind::AssociatedEntityType.Define( {.ir_name = "assoc_entity_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; // The interface in which the entity was declared. @@ -254,12 +233,25 @@ struct AssociatedEntityType { TypeId interface_type_id; }; +// Used for the type of patterns that do not match a fixed type. +struct AutoType { + static constexpr auto Kind = InstKind::AutoType.Define( + {.ir_name = "auto", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::Always}); + static constexpr auto SingletonInstId = MakeSingletonInstId(); + static constexpr auto SingletonTypeId = + TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(SingletonInstId)); + + TypeId type_id; +}; + // A base in a class, of the form `base: base_type;`. A base class is an // element of the derived class, and the type of the `BaseDecl` instruction is // an `UnboundElementType`. struct BaseDecl { static constexpr auto Kind = InstKind::BaseDecl.Define( - {.ir_name = "base_decl", .constant_kind = InstConstantKind::Always}); + {.ir_name = "base_decl", .constant_kind = InstConstantKind::Unique}); TypeId type_id; InstId base_type_inst_id; @@ -304,8 +296,8 @@ struct BindAlias { // Binds a name, such as `x` in `var x: i32`. struct BindName { // TODO: Make Parse::NodeId more specific. - static constexpr auto Kind = - InstKind::BindName.Define({.ir_name = "bind_name"}); + static constexpr auto Kind = InstKind::BindName.Define( + {.ir_name = "bind_name", .constant_kind = InstConstantKind::Never}); TypeId type_id; EntityNameId entity_name_id; @@ -350,7 +342,9 @@ struct AnyBindingPattern { // Represents a non-symbolic binding pattern. struct BindingPattern { static constexpr auto Kind = InstKind::BindingPattern.Define( - {.ir_name = "binding_pattern", .is_lowered = false}); + {.ir_name = "binding_pattern", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); TypeId type_id; EntityNameId entity_name_id; @@ -371,8 +365,8 @@ struct SymbolicBindingPattern { // Reads an argument from `BranchWithArg`. struct BlockArg { - static constexpr auto Kind = - InstKind::BlockArg.Define({.ir_name = "block_arg"}); + static constexpr auto Kind = InstKind::BlockArg.Define( + {.ir_name = "block_arg", .constant_kind = InstConstantKind::Never}); TypeId type_id; LabelId block_id; @@ -388,13 +382,26 @@ struct BoolLiteral { BoolValue value; }; +// The type of bool literals and branch conditions, bool. +struct BoolType { + static constexpr auto Kind = InstKind::BoolType.Define( + {.ir_name = "bool", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); + + TypeId type_id; +}; + // For member access such as `object.MethodName`, combines a member function // with the value to use for `self`. This is a callable structure; `Call` will // handle the argument assignment. struct BoundMethod { static constexpr auto Kind = InstKind::BoundMethod.Define( {.ir_name = "bound_method", - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; // The object argument in the bound method, which will be used to initialize @@ -435,7 +442,9 @@ struct AnyBranch { struct Branch { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind = InstKind::Branch.Define( - {.ir_name = "br", .terminator_kind = TerminatorKind::Terminator}); + {.ir_name = "br", + .constant_kind = InstConstantKind::Never, + .terminator_kind = TerminatorKind::Terminator}); // Branches don't produce a value, so have no type. LabelId target_id; @@ -445,7 +454,9 @@ struct Branch { struct BranchIf { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind = InstKind::BranchIf.Define( - {.ir_name = "br", .terminator_kind = TerminatorKind::TerminatorSequence}); + {.ir_name = "br", + .constant_kind = InstConstantKind::Never, + .terminator_kind = TerminatorKind::TerminatorSequence}); // Branches don't produce a value, so have no type. LabelId target_id; @@ -457,7 +468,9 @@ struct BranchIf { struct BranchWithArg { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind = InstKind::BranchWithArg.Define( - {.ir_name = "br", .terminator_kind = TerminatorKind::Terminator}); + {.ir_name = "br", + .constant_kind = InstConstantKind::Never, + .terminator_kind = TerminatorKind::Terminator}); // Branches don't produce a value, so have no type. LabelId target_id; @@ -517,7 +530,7 @@ struct ClassInit { TypeId type_id; InstBlockId elements_id; - InstId dest_id; + DestInstId dest_id; }; // The type for a class, either non-generic or specific. @@ -572,7 +585,9 @@ struct Converted { InstKind::Converted.Define({.ir_name = "converted"}); TypeId type_id; - InstId original_id; + // The operand prior to being converted. This is tracked only for tooling + // purposes and has no associated semantics. + AbsoluteInstId original_id; InstId result_id; }; @@ -674,7 +689,7 @@ struct FacetValue { struct FieldDecl { static constexpr auto Kind = InstKind::FieldDecl.Define( - {.ir_name = "field_decl", .constant_kind = InstConstantKind::Always}); + {.ir_name = "field_decl", .constant_kind = InstConstantKind::Unique}); TypeId type_id; NameId name_id; @@ -741,7 +756,7 @@ struct FunctionType { InstKind::FunctionType.Define( {.ir_name = "fn_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; FunctionId function_id; @@ -756,7 +771,7 @@ struct FunctionTypeWithSelfType { InstKind::FunctionTypeWithSelfType.Define( {.ir_name = "fn_type_with_self_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional, + .constant_kind = InstConstantKind::WheneverPossible, .is_lowered = false}); TypeId type_id; @@ -776,7 +791,7 @@ struct GenericClassType { InstKind::GenericClassType.Define( {.ir_name = "generic_class_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; ClassId class_id; @@ -791,7 +806,7 @@ struct GenericInterfaceType { InstKind::GenericInterfaceType.Define( {.ir_name = "generic_interface_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; InterfaceId interface_id; @@ -802,7 +817,9 @@ struct GenericInterfaceType { struct ImplDecl { static constexpr auto Kind = InstKind::ImplDecl.Define( {.ir_name = "impl_decl", - .constant_kind = InstConstantKind::Always, + // TODO: Modeling impls as unique doesn't properly handle impl + // redeclarations. + .constant_kind = InstConstantKind::Unique, .is_lowered = false}); // No type: an impl declaration is not a value. @@ -816,7 +833,7 @@ struct ImplDecl { struct ImplWitness { static constexpr auto Kind = InstKind::ImplWitness.Define( {.ir_name = "impl_witness", - .constant_kind = InstConstantKind::Conditional, + .constant_kind = InstConstantKind::WheneverPossible, // TODO: For dynamic dispatch, we might want to lower witness tables as // constants. .is_lowered = false}); @@ -845,14 +862,18 @@ struct ImplWitnessAccess { struct ImportCppDecl { static constexpr auto Kind = InstKind::ImportCppDecl.Define( - {.ir_name = "import_cpp", .is_lowered = false}); + {.ir_name = "import_cpp", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); }; // An `import` declaration. This is mainly for `import` diagnostics, and a 1:1 // correspondence with actual `import`s isn't guaranteed. struct ImportDecl { static constexpr auto Kind = InstKind::ImportDecl.Define( - {.ir_name = "import", .is_lowered = false}); + {.ir_name = "import", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); NameId package_id; }; @@ -901,7 +922,7 @@ struct InitializeFrom { TypeId type_id; InstId src_id; - InstId dest_id; + DestInstId dest_id; }; // An interface declaration. @@ -968,7 +989,8 @@ struct IntType { struct NameBindingDecl { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind = InstKind::NameBindingDecl.Define( - {.ir_name = "name_binding_decl"}); + {.ir_name = "name_binding_decl", + .constant_kind = InstConstantKind::Never}); InstBlockId pattern_block_id; }; @@ -989,7 +1011,10 @@ struct NameRef { struct Namespace { static constexpr auto Kind = InstKind::Namespace.Define( - {.ir_name = "namespace", .constant_kind = InstConstantKind::Always}); + {.ir_name = "namespace", + // TODO: Modeling namespaces as unique doesn't properly handle + // namespace redeclarations. + .constant_kind = InstConstantKind::Unique}); // The file's package namespace is a well-known instruction to help `package.` // qualified names. It will always be immediately after singletons. static constexpr InstId PackageInstId = InstId(SingletonInstKinds.size()); @@ -1035,8 +1060,8 @@ struct AnyParam { // An output parameter. See AnyParam for member documentation. struct OutParam { // TODO: Make Parse::NodeId more specific. - static constexpr auto Kind = - InstKind::OutParam.Define({.ir_name = "out_param"}); + static constexpr auto Kind = InstKind::OutParam.Define( + {.ir_name = "out_param", .constant_kind = InstConstantKind::Never}); TypeId type_id; RuntimeParamIndex runtime_index; @@ -1046,8 +1071,8 @@ struct OutParam { // A by-value parameter. See AnyParam for member documentation. struct ValueParam { // TODO: Make Parse::NodeId more specific. - static constexpr auto Kind = - InstKind::ValueParam.Define({.ir_name = "value_param"}); + static constexpr auto Kind = InstKind::ValueParam.Define( + {.ir_name = "value_param", .constant_kind = InstConstantKind::Never}); TypeId type_id; RuntimeParamIndex runtime_index; @@ -1071,7 +1096,9 @@ struct AnyParamPattern { struct OutParamPattern { static constexpr auto Kind = InstKind::OutParamPattern.Define( - {.ir_name = "out_param_pattern", .is_lowered = false}); + {.ir_name = "out_param_pattern", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); TypeId type_id; InstId subpattern_id; @@ -1097,7 +1124,7 @@ struct PointerType { InstKind::PointerType.Define( {.ir_name = "ptr_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional, + .constant_kind = InstConstantKind::WheneverPossible, .deduce_through = true}); TypeId type_id; @@ -1126,7 +1153,9 @@ struct Return { static constexpr auto Kind = InstKind::Return.Define>( - {.ir_name = "return", .terminator_kind = TerminatorKind::Terminator}); + {.ir_name = "return", + .constant_kind = InstConstantKind::Never, + .terminator_kind = TerminatorKind::Terminator}); // This is a statement, so has no type. }; @@ -1135,20 +1164,22 @@ struct Return { struct ReturnExpr { static constexpr auto Kind = InstKind::ReturnExpr.Define( - {.ir_name = "return", .terminator_kind = TerminatorKind::Terminator}); + {.ir_name = "return", + .constant_kind = InstConstantKind::Never, + .terminator_kind = TerminatorKind::Terminator}); // This is a statement, so has no type. InstId expr_id; // The return slot, if any. `None` if we're not returning through memory. - InstId dest_id; + DestInstId dest_id; }; // The return slot of a function declaration, as exposed in the function body. // This acts as an output parameter, analogous to `BindName` for input // parameters. struct ReturnSlot { - static constexpr auto Kind = - InstKind::ReturnSlot.Define({.ir_name = "return_slot"}); + static constexpr auto Kind = InstKind::ReturnSlot.Define( + {.ir_name = "return_slot", .constant_kind = InstConstantKind::Never}); // The type of the value that will be stored in this slot (i.e. the return // type of the function). @@ -1169,7 +1200,9 @@ struct ReturnSlot { struct ReturnSlotPattern { static constexpr auto Kind = InstKind::ReturnSlotPattern.Define( - {.ir_name = "return_slot_pattern", .is_lowered = false}); + {.ir_name = "return_slot_pattern", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); // The type of the value that will be stored in this slot (i.e. the return // type of the function). @@ -1185,7 +1218,9 @@ struct ReturnSlotPattern { struct RequirementEquivalent { static constexpr auto Kind = InstKind::RequirementEquivalent.Define( - {.ir_name = "requirement_equivalent", .is_lowered = false}); + {.ir_name = "requirement_equivalent", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); // No type since not an expression InstId lhs_id; @@ -1196,7 +1231,9 @@ struct RequirementEquivalent { struct RequirementImpls { static constexpr auto Kind = InstKind::RequirementImpls.Define( - {.ir_name = "requirement_impls", .is_lowered = false}); + {.ir_name = "requirement_impls", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); // No type since not an expression InstId lhs_id; @@ -1207,7 +1244,9 @@ struct RequirementImpls { struct RequirementRewrite { static constexpr auto Kind = InstKind::RequirementRewrite.Define( - {.ir_name = "requirement_rewrite", .is_lowered = false}); + {.ir_name = "requirement_rewrite", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); // No type since not an expression InstId lhs_id; @@ -1241,7 +1280,7 @@ struct SpecificConstant { struct SpecificFunction { static constexpr auto Kind = InstKind::SpecificFunction.Define( {.ir_name = "specific_function", - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); // Always the builtin SpecificFunctionType. TypeId type_id; @@ -1275,7 +1314,7 @@ struct SpliceBlock { InstKind::SpliceBlock.Define({.ir_name = "splice_block"}); TypeId type_id; - InstBlockId block_id; + AbsoluteInstBlockId block_id; InstId result_id; }; @@ -1323,14 +1362,15 @@ struct StructInit { TypeId type_id; InstBlockId elements_id; - InstId dest_id; + DestInstId dest_id; }; // A literal struct value, such as `{.a = 1, .b = 2}`. struct StructLiteral { static constexpr auto Kind = InstKind::StructLiteral.Define( - {.ir_name = "struct_literal"}); + {.ir_name = "struct_literal", + .constant_kind = InstConstantKind::Never}); TypeId type_id; InstBlockId elements_id; @@ -1342,7 +1382,7 @@ struct StructType { InstKind::StructType.Define( {.ir_name = "struct_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional, + .constant_kind = InstConstantKind::WheneverPossible, .deduce_through = true}); TypeId type_id; @@ -1353,7 +1393,7 @@ struct StructType { struct StructValue { static constexpr auto Kind = InstKind::StructValue.Define( {.ir_name = "struct_value", - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; InstBlockId elements_id; @@ -1365,7 +1405,7 @@ struct Temporary { InstKind::Temporary.Define({.ir_name = "temporary"}); TypeId type_id; - InstId storage_id; + DestInstId storage_id; InstId init_id; }; @@ -1373,7 +1413,8 @@ struct Temporary { struct TemporaryStorage { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind = InstKind::TemporaryStorage.Define( - {.ir_name = "temporary_storage"}); + {.ir_name = "temporary_storage", + .constant_kind = InstConstantKind::Never}); TypeId type_id; }; @@ -1398,14 +1439,15 @@ struct TupleInit { TypeId type_id; InstBlockId elements_id; - InstId dest_id; + DestInstId dest_id; }; // A literal tuple value. struct TupleLiteral { static constexpr auto Kind = InstKind::TupleLiteral.Define( - {.ir_name = "tuple_literal"}); + {.ir_name = "tuple_literal", + .constant_kind = InstConstantKind::Never}); TypeId type_id; InstBlockId elements_id; @@ -1415,7 +1457,9 @@ struct TupleLiteral { struct TuplePattern { static constexpr auto Kind = InstKind::TuplePattern.Define( - {.ir_name = "tuple_pattern", .is_lowered = false}); + {.ir_name = "tuple_pattern", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); TypeId type_id; InstBlockId elements_id; @@ -1426,7 +1470,7 @@ struct TupleType { static constexpr auto Kind = InstKind::TupleType.Define( {.ir_name = "tuple_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional, + .constant_kind = InstConstantKind::WheneverPossible, .deduce_through = true}); TypeId type_id; @@ -1437,7 +1481,7 @@ struct TupleType { struct TupleValue { static constexpr auto Kind = InstKind::TupleValue.Define( {.ir_name = "tuple_value", - .constant_kind = InstConstantKind::Conditional, + .constant_kind = InstConstantKind::WheneverPossible, .deduce_through = true}); TypeId type_id; @@ -1476,7 +1520,7 @@ struct UnboundElementType { Parse::NodeIdOneOf>( {.ir_name = "unbound_element_type", .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Conditional}); + .constant_kind = InstConstantKind::WheneverPossible}); TypeId type_id; // The class that a value of this type is an element of. @@ -1491,7 +1535,7 @@ struct UnboundElementType { // form a reference to the array object. struct ValueAsRef { static constexpr auto Kind = InstKind::ValueAsRef.Define( - {.ir_name = "value_as_ref"}); + {.ir_name = "value_as_ref", .constant_kind = InstConstantKind::Never}); TypeId type_id; InstId value_id; @@ -1514,7 +1558,9 @@ struct ValueOfInitializer { struct VarPattern { static constexpr auto Kind = InstKind::VarPattern.Define( - {.ir_name = "var_pattern", .is_lowered = false}); + {.ir_name = "var_pattern", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); TypeId type_id; InstId subpattern_id; @@ -1523,8 +1569,8 @@ struct VarPattern { // Tracks storage for a `var` pattern. struct VarStorage { // TODO: Make Parse::NodeId more specific. - static constexpr auto Kind = - InstKind::VarStorage.Define({.ir_name = "var"}); + static constexpr auto Kind = InstKind::VarStorage.Define( + {.ir_name = "var", .constant_kind = InstConstantKind::Never}); TypeId type_id;