From cd7c10b8e2f2fef23508066ffa3711636d1dd7f0 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 16 Jul 2024 15:58:17 -0700 Subject: [PATCH] Rebuild the type of constants during evaluation. (#4138) When evaluating in a generic context, a constant with a symbolic type might evaluate to a constant with a concrete type (or a more specific symbolic type). This can't actually happen yet given the current state of the toolchain, as far as I can determine, so this is more just a refactoring for now, but will be relied upon by future generics work. --------- Co-authored-by: Jon Ross-Perkins Co-authored-by: Carbon Infra Bot --- toolchain/check/eval.cpp | 155 +++++++++++------- .../check/testdata/array/generic_empty.carbon | 1 - toolchain/check/testdata/eval/symbolic.carbon | 1 - .../index/fail_array_non_int_indexing.carbon | 2 +- .../index/fail_empty_tuple_access.carbon | 2 +- .../index/fail_negative_indexing.carbon | 2 +- .../index/fail_non_deterministic_type.carbon | 2 +- .../fail_out_of_bound_not_literal.carbon | 2 +- .../index/fail_tuple_index_error.carbon | 2 +- .../index/fail_tuple_large_index.carbon | 4 +- .../index/fail_tuple_non_int_indexing.carbon | 2 +- .../fail_tuple_out_of_bound_access.carbon | 2 +- 12 files changed, 105 insertions(+), 72 deletions(-) diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index bac7023ee73f..5535006979fe 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -70,6 +70,11 @@ struct EvalContext { return GetInContext(context.types().GetConstantId(type_id)); } + // Gets the constant value of the specified type in this context. + auto GetConstantValueAsType(SemIR::TypeId id) -> SemIR::TypeId { + return context.GetTypeIdForTypeConstant(GetConstantValue(id)); + } + // Gets the instruction describing the constant value of the specified type in // this context. auto GetConstantValueAsInst(SemIR::TypeId id) -> SemIR::Inst { @@ -150,16 +155,6 @@ static auto GetPhase(SemIR::ConstantId constant_id) -> Phase { } } -// Gets the earliest possible phase for a constant whose type is `type_id`. The -// type of a constant is effectively treated as an operand of that constant when -// determining its phase. For example, an empty struct with a symbolic type is a -// symbolic constant, not a template constant. -static auto GetTypePhase(EvalContext& eval_context, SemIR::TypeId type_id) - -> Phase { - CARBON_CHECK(type_id.is_valid()); - return GetPhase(eval_context.GetConstantValue(type_id)); -} - // Returns the later of two phases. static auto LatestPhase(Phase a, Phase b) -> Phase { return static_cast( @@ -227,12 +222,13 @@ static auto GetConstantValue(EvalContext& eval_context, SemIR::InstId inst_id, return eval_context.context.constant_values().GetInstId(const_id); } -// A type is always constant, but we still need to extract its phase. +// 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(const_id)); - return type_id; + return eval_context.context.GetTypeIdForTypeConstant(const_id); } // If the given instruction block contains only constants, returns a @@ -324,40 +320,62 @@ static auto ReplaceFieldWithConstantValue(EvalContext& eval_context, // // 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, a corresponding ConstantId for the new constant is +// 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. // `ConstantId::Error` is returned. -template -static auto RebuildAndValidateIfFieldsAreConstant( +template +static auto RebuildIfFieldsAreConstantImpl( EvalContext& eval_context, SemIR::Inst inst, ValidateFn validate_fn, - EachFieldIdT InstT::*... each_field_id) -> SemIR::ConstantId { + 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(); - // Some instruction kinds don't have a `type_id` field. For those that do, the - // type contributes to the phase. - Phase phase = inst.type_id().is_valid() - ? GetTypePhase(eval_context, inst.type_id()) - : Phase::Template; + Phase phase = Phase::Template; if ((ReplaceFieldWithConstantValue(eval_context, &typed_inst, each_field_id, &phase) && ...)) { if (phase == Phase::UnknownDueToError || !validate_fn(typed_inst)) { return SemIR::ConstantId::Error; } - return MakeConstantResult(eval_context.context, typed_inst, phase); + 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...); +} + // 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...); +} + +// 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 RebuildAndValidateIfFieldsAreConstant( - eval_context, inst, [](...) { return true; }, each_field_id...); + return RebuildIfFieldsAreConstantImpl( + eval_context, inst, [](...) { return true; }, std::identity{}, + each_field_id...); } // Rebuilds the given aggregate initialization instruction as a corresponding @@ -365,16 +383,14 @@ static auto RebuildIfFieldsAreConstant(EvalContext& eval_context, static auto RebuildInitAsValue(EvalContext& eval_context, SemIR::Inst inst, SemIR::InstKind value_kind) -> SemIR::ConstantId { - auto init_inst = inst.As(); - Phase phase = GetTypePhase(eval_context, init_inst.type_id); - auto elements_id = - GetConstantValue(eval_context, init_inst.elements_id, &phase); - return MakeConstantResult( - eval_context.context, - SemIR::AnyAggregateValue{.kind = value_kind, - .type_id = init_inst.type_id, - .elements_id = elements_id}, - phase); + 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); } // Performs an access into an aggregate, retrieving the specified element. @@ -409,8 +425,6 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst) -> SemIR::ConstantId { auto index_inst = inst.As(); Phase phase = Phase::Template; - auto aggregate_id = - GetConstantValue(eval_context, index_inst.aggregate_id, &phase); auto index_id = GetConstantValue(eval_context, index_inst.index_id, &phase); if (!index_id.is_valid()) { @@ -423,10 +437,11 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst) return MakeNonConstantResult(phase); } - // Array indexing is invalid if the index is constant and out of range. - auto aggregate_type_id = - eval_context.insts().Get(index_inst.aggregate_id).type_id(); + // Array indexing is invalid if the index is constant and out of range, + // regardless of whether the array itself is constant. const auto& index_val = eval_context.ints().Get(index->int_id); + auto aggregate_type_id = eval_context.GetConstantValueAsType( + eval_context.insts().Get(index_inst.aggregate_id).type_id()); if (auto array_type = eval_context.types().TryGetAs(aggregate_type_id)) { if (auto bound = eval_context.insts().TryGetAs( @@ -448,6 +463,8 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst) } } + auto aggregate_id = + GetConstantValue(eval_context, index_inst.aggregate_id, &phase); if (!aggregate_id.is_valid()) { return MakeNonConstantResult(phase); } @@ -986,9 +1003,11 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc, return SemIR::ConstantId::Error; } - // If the callee isn't constant, this is not a constant call. + // If the callee or return type isn't constant, this is not a constant call. if (!ReplaceFieldWithConstantValue(eval_context, &call, - &SemIR::Call::callee_id, &phase)) { + &SemIR::Call::callee_id, &phase) || + !ReplaceFieldWithConstantValue(eval_context, &call, &SemIR::Call::type_id, + &phase)) { return SemIR::ConstantId::NotConstant; } @@ -1071,6 +1090,7 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, // 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( @@ -1110,13 +1130,17 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, }, &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::entity_type_id); case SemIR::BoundMethod::Kind: - return RebuildIfFieldsAreConstant(eval_context, inst, - &SemIR::BoundMethod::object_id, - &SemIR::BoundMethod::function_id); + return RebuildIfFieldsAreConstant( + eval_context, inst, &SemIR::BoundMethod::type_id, + &SemIR::BoundMethod::object_id, &SemIR::BoundMethod::function_id); case SemIR::ClassType::Kind: return RebuildIfFieldsAreConstant(eval_context, inst, &SemIR::ClassType::instance_id); @@ -1155,12 +1179,14 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, &SemIR::StructTypeField::field_type_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( @@ -1181,7 +1207,6 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, case SemIR::TupleInit::Kind: return RebuildInitAsValue(eval_context, inst, SemIR::TupleValue::Kind); - case SemIR::AssociatedEntity::Kind: case SemIR::BuiltinInst::Kind: case SemIR::FunctionType::Kind: case SemIR::GenericClassType::Kind: @@ -1190,22 +1215,27 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, return MakeConstantResult(eval_context.context, inst, Phase::Template); case CARBON_KIND(SemIR::FunctionDecl fn_decl): { - return MakeConstantResult( - eval_context.context, - SemIR::StructValue{.type_id = fn_decl.type_id, - .elements_id = SemIR::InstBlockId::Empty}, - GetTypePhase(eval_context, fn_decl.type_id)); + 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); } 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).is_generic()) { - return MakeConstantResult( - eval_context.context, - SemIR::StructValue{.type_id = class_decl.type_id, - .elements_id = SemIR::InstBlockId::Empty}, - GetTypePhase(eval_context, class_decl.type_id)); + 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( @@ -1221,11 +1251,14 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, if (eval_context.interfaces() .Get(interface_decl.interface_id) .is_generic()) { - return MakeConstantResult( - eval_context.context, - SemIR::StructValue{.type_id = interface_decl.type_id, - .elements_id = SemIR::InstBlockId::Empty}, - GetTypePhase(eval_context, interface_decl.type_id)); + 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 the interface type. return MakeConstantResult( @@ -1261,6 +1294,8 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, // 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 IntLiteral or FloatLiteral be symbolic? If so, + // we may need to rebuild. return MakeConstantResult(eval_context.context, inst, Phase::Template); // The elements of a constant aggregate can be accessed. diff --git a/toolchain/check/testdata/array/generic_empty.carbon b/toolchain/check/testdata/array/generic_empty.carbon index f0248f884bc4..034a1bda4439 100644 --- a/toolchain/check/testdata/array/generic_empty.carbon +++ b/toolchain/check/testdata/array/generic_empty.carbon @@ -24,7 +24,6 @@ fn G(T:! type) { // CHECK:STDOUT: %.3: type = array_type %.2, %T [symbolic] // CHECK:STDOUT: %.4: type = ptr_type %.3 [symbolic] // CHECK:STDOUT: %array: %.3 = tuple_value () [symbolic] -// CHECK:STDOUT: %.5: type = ptr_type @G.%.loc13_17 (%.3) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { diff --git a/toolchain/check/testdata/eval/symbolic.carbon b/toolchain/check/testdata/eval/symbolic.carbon index 315a5386bd14..42c920d377db 100644 --- a/toolchain/check/testdata/eval/symbolic.carbon +++ b/toolchain/check/testdata/eval/symbolic.carbon @@ -32,7 +32,6 @@ fn F(T:! type) { // CHECK:STDOUT: %.9: i32 = int_literal 5 [template] // CHECK:STDOUT: %.10: type = array_type %.9, %T [symbolic] // CHECK:STDOUT: %.11: type = ptr_type %.10 [symbolic] -// CHECK:STDOUT: %.12: type = ptr_type @F.%.loc15_15 (%.10) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { 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 3076765839a3..2c92895903af 100644 --- a/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon +++ b/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon @@ -71,7 +71,7 @@ var b: i32 = a[2.6]; // CHECK:STDOUT: assign file.%a.var, %.loc11_24 // CHECK:STDOUT: %a.ref: ref %.3 = name_ref a, file.%a // CHECK:STDOUT: %.loc15_16: f64 = float_literal 2.6000000000000001 [template = constants.%.8] -// CHECK:STDOUT: %.loc15_19.1: ref i32 = array_index %a.ref, +// CHECK:STDOUT: %.loc15_19.1: ref i32 = array_index %a.ref, [template = ] // CHECK:STDOUT: %.loc15_19.2: i32 = bind_value %.loc15_19.1 // CHECK:STDOUT: assign file.%b.var, %.loc15_19.2 // CHECK:STDOUT: return diff --git a/toolchain/check/testdata/index/fail_empty_tuple_access.carbon b/toolchain/check/testdata/index/fail_empty_tuple_access.carbon index bc59ee29addf..0fc2632ca8d0 100644 --- a/toolchain/check/testdata/index/fail_empty_tuple_access.carbon +++ b/toolchain/check/testdata/index/fail_empty_tuple_access.carbon @@ -52,7 +52,7 @@ fn Run() { // CHECK:STDOUT: %.loc17_7: i32 = int_literal 0 [template = constants.%.2] // CHECK:STDOUT: %.loc17_4.1: ref %.1 = temporary_storage // CHECK:STDOUT: %.loc17_4.2: ref %.1 = temporary %.loc17_4.1, %F.call -// CHECK:STDOUT: %.loc17_8: ref = tuple_index %.loc17_4.2, +// CHECK:STDOUT: %.loc17_8: ref = tuple_index %.loc17_4.2, [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/index/fail_negative_indexing.carbon b/toolchain/check/testdata/index/fail_negative_indexing.carbon index 70fc42e8c64f..17d57999c008 100644 --- a/toolchain/check/testdata/index/fail_negative_indexing.carbon +++ b/toolchain/check/testdata/index/fail_negative_indexing.carbon @@ -96,7 +96,7 @@ var b: i32 = a[-10]; // CHECK:STDOUT: assign file.%a.var, %.loc11_28 // CHECK:STDOUT: %a.ref: ref %.3 = name_ref a, file.%a // CHECK:STDOUT: %.loc15_17: i32 = int_literal 10 [template = constants.%.7] -// CHECK:STDOUT: %.loc15_19: ref = tuple_index %a.ref, +// CHECK:STDOUT: %.loc15_19: ref = tuple_index %a.ref, [template = ] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_non_deterministic_type.carbon b/toolchain/check/testdata/index/fail_non_deterministic_type.carbon index de137b145690..9fec3db610eb 100644 --- a/toolchain/check/testdata/index/fail_non_deterministic_type.carbon +++ b/toolchain/check/testdata/index/fail_non_deterministic_type.carbon @@ -87,7 +87,7 @@ var c: i32 = a[b]; // CHECK:STDOUT: %a.ref: ref %.3 = name_ref a, file.%a // CHECK:STDOUT: %b.ref: ref i32 = name_ref b, file.%b // CHECK:STDOUT: %.loc16_16: i32 = bind_value %b.ref -// CHECK:STDOUT: %.loc16_17: ref = tuple_index %a.ref, +// CHECK:STDOUT: %.loc16_17: ref = tuple_index %a.ref, [template = ] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_out_of_bound_not_literal.carbon b/toolchain/check/testdata/index/fail_out_of_bound_not_literal.carbon index 039154ed17a7..9cb25996dcc7 100644 --- a/toolchain/check/testdata/index/fail_out_of_bound_not_literal.carbon +++ b/toolchain/check/testdata/index/fail_out_of_bound_not_literal.carbon @@ -82,7 +82,7 @@ var b: i32 = a[{.index = 2}.index]; // CHECK:STDOUT: %struct: %.8 = struct_value (%.loc15_26) [template = constants.%struct] // CHECK:STDOUT: %.loc15_27.2: %.8 = converted %.loc15_27.1, %struct [template = constants.%struct] // CHECK:STDOUT: %.loc15_28: i32 = struct_access %.loc15_27.2, element0 [template = constants.%.7] -// CHECK:STDOUT: %.loc15_34: ref = tuple_index %a.ref, +// CHECK:STDOUT: %.loc15_34: ref = tuple_index %a.ref, [template = ] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_tuple_index_error.carbon b/toolchain/check/testdata/index/fail_tuple_index_error.carbon index 6b624499962c..5cfe45c3a205 100644 --- a/toolchain/check/testdata/index/fail_tuple_index_error.carbon +++ b/toolchain/check/testdata/index/fail_tuple_index_error.carbon @@ -75,7 +75,7 @@ var b: i32 = a[oops]; // CHECK:STDOUT: assign file.%a.var, %.loc11_28 // CHECK:STDOUT: %a.ref: ref %.3 = name_ref a, file.%a // CHECK:STDOUT: %oops.ref: = name_ref oops, [template = ] -// CHECK:STDOUT: %.loc15: ref = tuple_index %a.ref, +// CHECK:STDOUT: %.loc15: ref = tuple_index %a.ref, [template = ] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_tuple_large_index.carbon b/toolchain/check/testdata/index/fail_tuple_large_index.carbon index 5541f996cd7c..822c6b44f4d8 100644 --- a/toolchain/check/testdata/index/fail_tuple_large_index.carbon +++ b/toolchain/check/testdata/index/fail_tuple_large_index.carbon @@ -94,11 +94,11 @@ var d: i32 = b[0x7FFF_FFFF]; // CHECK:STDOUT: assign file.%b.var, %.loc12_18 // CHECK:STDOUT: %b.ref.loc17: ref %.3 = name_ref b, file.%b // CHECK:STDOUT: %.loc17_16: i32 = int_literal 1 [template = constants.%.5] -// CHECK:STDOUT: %.loc17_17: ref = tuple_index %b.ref.loc17, +// CHECK:STDOUT: %.loc17_17: ref = tuple_index %b.ref.loc17, [template = ] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: %b.ref.loc21: ref %.3 = name_ref b, file.%b // CHECK:STDOUT: %.loc21_16: i32 = int_literal 2147483647 [template = constants.%.6] -// CHECK:STDOUT: %.loc21_27: ref = tuple_index %b.ref.loc21, +// CHECK:STDOUT: %.loc21_27: ref = tuple_index %b.ref.loc21, [template = ] // CHECK:STDOUT: assign file.%d.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon b/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon index c4f1a715de87..b0a3601801d5 100644 --- a/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon +++ b/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon @@ -76,7 +76,7 @@ var b: i32 = a[2.6]; // CHECK:STDOUT: assign file.%a.var, %.loc11_28 // CHECK:STDOUT: %a.ref: ref %.3 = name_ref a, file.%a // CHECK:STDOUT: %.loc15_16: f64 = float_literal 2.6000000000000001 [template = constants.%.7] -// CHECK:STDOUT: %.loc15_19: ref = tuple_index %a.ref, +// CHECK:STDOUT: %.loc15_19: ref = tuple_index %a.ref, [template = ] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon b/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon index d0416affb0eb..b1acf5ebf371 100644 --- a/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon +++ b/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon @@ -76,7 +76,7 @@ var b: i32 = a[2]; // CHECK:STDOUT: assign file.%a.var, %.loc11_28 // CHECK:STDOUT: %a.ref: ref %.3 = name_ref a, file.%a // CHECK:STDOUT: %.loc15_16: i32 = int_literal 2 [template = constants.%.7] -// CHECK:STDOUT: %.loc15_17: ref = tuple_index %a.ref, +// CHECK:STDOUT: %.loc15_17: ref = tuple_index %a.ref, [template = ] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: }