From aba3c47467879b9f413ff68a83ed4cc183e6dc54 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 19 Jan 2024 17:44:13 -0800 Subject: [PATCH] Constant evaluation for aggregate access. (#3625) Also, form `addr_of error` instead of `addr_of operand` when `operand` is not a reference expression, so that we don't try to constant-evaluate a meaningless expression. Also, expect a constant for an array bound rather than specifically an integer literal. This allows constant evaluation results to be more easily tested by inspecting array bounds. --- toolchain/check/eval.cpp | 114 +++++++++++++-- toolchain/check/handle_array.cpp | 32 +++-- toolchain/check/handle_operator.cpp | 7 +- .../testdata/array/fail_bound_overflow.carbon | 29 ++-- .../check/testdata/eval/aggregate.carbon | 133 ++++++++++++++++++ .../check/testdata/eval/fail_aggregate.carbon | 73 ++++++++++ .../testdata/index/fail_expr_category.carbon | 4 +- .../pointer/fail_address_of_error.carbon | 2 +- .../pointer/fail_address_of_value.carbon | 105 +++++++------- toolchain/sem_ir/typed_insts.h | 25 ++++ 10 files changed, 427 insertions(+), 97 deletions(-) create mode 100644 toolchain/check/testdata/eval/aggregate.carbon create mode 100644 toolchain/check/testdata/eval/fail_aggregate.carbon diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index b7572003ea04..54b50d2410b3 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -65,6 +65,12 @@ static auto MakeConstantResult(Context& context, SemIR::Inst inst, Phase phase) } } +// Forms a `constant_id` describing why an evaluation was not constant. +static auto MakeNonConstantResult(Phase phase) -> SemIR::ConstantId { + return phase == Phase::UnknownDueToError ? SemIR::ConstantId::Error + : SemIR::ConstantId::NotConstant; +} + // `GetConstantValue` checks to see whether the provided ID describes a value // with constant phase, and if so, returns the corresponding constant value. // Overloads are provided for different kinds of ID. @@ -120,22 +126,36 @@ static auto ReplaceFieldWithConstantValue(Context& context, InstT* inst, // 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 `SemIR::InstId::Invalid`. -template -static auto RebuildIfFieldsAreConstant(Context& context, SemIR::Inst inst, - EachFieldIdT InstT::*... each_field_id) - -> SemIR::ConstantId { +// constant value. 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 returned. Otherwise returns +// `ConstantId::NotConstant`. Returns `ConstantId::Error` if any subexpression +// is an error. +template +static auto RebuildAndValidateIfFieldsAreConstant( + Context& context, SemIR::Inst inst, ValidateFn validate_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::Template; if ((ReplaceFieldWithConstantValue(context, &typed_inst, each_field_id, &phase) && - ...)) { + ...) && + validate_fn(typed_inst)) { return MakeConstantResult(context, typed_inst, phase); } - return phase == Phase::UnknownDueToError ? SemIR::ConstantId::Error - : SemIR::ConstantId::NotConstant; + return MakeNonConstantResult(phase); +} + +// Same as above but with no validation step. +template +static auto RebuildIfFieldsAreConstant(Context& context, SemIR::Inst inst, + EachFieldIdT InstT::*... each_field_id) + -> SemIR::ConstantId { + return RebuildAndValidateIfFieldsAreConstant( + context, inst, [](...) { return true; }, each_field_id...); } // Rebuilds the given aggregate initialization instruction as a corresponding @@ -154,6 +174,63 @@ static auto RebuildInitAsValue(Context& context, SemIR::Inst inst, phase); } +// Performs an access into an aggregate, retrieving the specified element. +static auto PerformAggregateAccess(Context& context, SemIR::Inst inst) + -> SemIR::ConstantId { + auto access_inst = inst.As(); + Phase phase = Phase::Template; + if (auto aggregate_id = + GetConstantValue(context, access_inst.aggregate_id, &phase); + aggregate_id.is_valid()) { + if (auto aggregate = + context.insts().TryGetAs(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 template constant, then + // so is the result of indexing, even if the aggregate also contains a + // symbolic context. + return context.constant_values().Get(elements[index]); + } else { + CARBON_CHECK(phase != Phase::Template) + << "Failed to evaluate template constant " << inst; + } + } + return MakeNonConstantResult(phase); +} + +// Performs an index into a homogeneous aggregate, retrieving the specified +// element. +static auto PerformAggregateIndex(Context& context, SemIR::Inst inst) + -> SemIR::ConstantId { + auto index_inst = inst.As(); + Phase phase = Phase::Template; + auto aggregate_id = + GetConstantValue(context, index_inst.aggregate_id, &phase); + auto index_id = GetConstantValue(context, index_inst.index_id, &phase); + if (aggregate_id.is_valid() && index_id.is_valid()) { + auto aggregate = + context.insts().TryGetAs(aggregate_id); + auto index = context.insts().TryGetAs(index_id); + if (aggregate && index) { + const auto& index_val = context.ints().Get(index->int_id); + auto elements = context.inst_blocks().Get(aggregate->elements_id); + if (index_val.ult(elements.size())) { + return context.constant_values().Get( + elements[index_val.getZExtValue()]); + } else { + // TODO: In this case, this instruction is a constant, but it indexes an + // array with a statically out-of-bounds index. This should produce an + // error rather than just treating the instruction as non-constant. + } + } else { + CARBON_CHECK(phase != Phase::Template) + << "Failed to evaluate template constant " << inst; + } + } + return MakeNonConstantResult(phase); +} + auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { // TODO: Ensure we have test coverage for each of these cases that can result @@ -167,8 +244,18 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) return RebuildIfFieldsAreConstant(context, inst, &SemIR::AddrOf::lvalue_id); case SemIR::ArrayType::Kind: - return RebuildIfFieldsAreConstant(context, inst, - &SemIR::ArrayType::bound_id); + return RebuildAndValidateIfFieldsAreConstant( + context, inst, + [&](SemIR::ArrayType result) { + // TODO: If the bound doesn't fit in 64 bits or is negative, + // produce an error rather than a non-constant type. + // TODO: Support a symbolic bound here. This will require fixing + // callers of `GetArrayBoundValue`. + auto int_bound = + context.insts().TryGetAs(result.bound_id); + return context.ints().Get(int_bound->int_id).getActiveBits() <= 64; + }, + &SemIR::ArrayType::bound_id); case SemIR::BoundMethod::Kind: return RebuildIfFieldsAreConstant(context, inst, &SemIR::BoundMethod::object_id, @@ -227,13 +314,14 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) // by `APInt`s with different bit widths. return MakeConstantResult(context, inst, Phase::Template); - // TODO: Support subobject access. - case SemIR::ArrayIndex::Kind: + // The elements of a constant aggregate can be accessed. case SemIR::ClassElementAccess::Kind: case SemIR::StructAccess::Kind: case SemIR::TupleAccess::Kind: + return PerformAggregateAccess(context, inst); + case SemIR::ArrayIndex::Kind: case SemIR::TupleIndex::Kind: - break; + return PerformAggregateIndex(context, inst); // TODO: These need special handling. case SemIR::BindValue::Kind: diff --git a/toolchain/check/handle_array.cpp b/toolchain/check/handle_array.cpp index b20bd4dd9b8b..73530d9f30da 100644 --- a/toolchain/check/handle_array.cpp +++ b/toolchain/check/handle_array.cpp @@ -32,21 +32,25 @@ auto HandleArrayExpr(Context& context, Parse::ArrayExprId parse_node) -> bool { context.node_stack() .PopAndDiscardSoloParseNode(); auto element_type_inst_id = context.node_stack().PopExpr(); - auto bound_inst = context.insts().Get(bound_inst_id); - if (auto literal = bound_inst.TryAs()) { - const auto& bound_value = context.ints().Get(literal->int_id); - // TODO: Produce an error if the array type is too large. - if (bound_value.getActiveBits() <= 64) { - context.AddInstAndPush( - {parse_node, SemIR::ArrayType{SemIR::TypeId::TypeType, bound_inst_id, - ExprAsType(context, parse_node, - element_type_inst_id)}}); - return true; - } + + // The array bound must be a constant. + // + // TODO: Should we support runtime-phase bounds in cases such as: + // comptime fn F(n: i32) -> type { return [i32; n]; } + auto bound_inst = context.constant_values().Get(bound_inst_id); + if (!bound_inst.is_constant()) { + CARBON_DIAGNOSTIC(InvalidArrayExpr, Error, + "Array bound is not a constant."); + context.emitter().Emit(context.insts().GetParseNode(bound_inst_id), + InvalidArrayExpr); + context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError); + return true; } - CARBON_DIAGNOSTIC(InvalidArrayExpr, Error, "Invalid array expression."); - context.emitter().Emit(parse_node, InvalidArrayExpr); - context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError); + + context.AddInstAndPush( + {parse_node, SemIR::ArrayType{ + SemIR::TypeId::TypeType, bound_inst_id, + ExprAsType(context, parse_node, element_type_inst_id)}}); return true; } diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index 71965d847b98..ec1f200c4cbe 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -206,6 +206,7 @@ auto HandlePostfixOperatorStar(Context& context, auto HandlePrefixOperatorAmp(Context& context, Parse::PrefixOperatorAmpId parse_node) -> bool { auto value_id = context.node_stack().PopExpr(); + auto type_id = context.insts().Get(value_id).type_id(); // Only durable reference expressions can have their address taken. switch (SemIR::GetExprCategory(context.sem_ir(), value_id)) { case SemIR::ExprCategory::DurableRef: @@ -215,17 +216,17 @@ auto HandlePrefixOperatorAmp(Context& context, CARBON_DIAGNOSTIC(AddrOfEphemeralRef, Error, "Cannot take the address of a temporary object."); context.emitter().Emit(TokenOnly(parse_node), AddrOfEphemeralRef); + value_id = SemIR::InstId::BuiltinError; break; default: CARBON_DIAGNOSTIC(AddrOfNonRef, Error, "Cannot take the address of non-reference expression."); context.emitter().Emit(TokenOnly(parse_node), AddrOfNonRef); + value_id = SemIR::InstId::BuiltinError; break; } context.AddInstAndPush( - {parse_node, SemIR::AddrOf{context.GetPointerType( - context.insts().Get(value_id).type_id()), - value_id}}); + {parse_node, SemIR::AddrOf{context.GetPointerType(type_id), value_id}}); return true; } diff --git a/toolchain/check/testdata/array/fail_bound_overflow.carbon b/toolchain/check/testdata/array/fail_bound_overflow.carbon index ec324dbef5a3..24aec16c9149 100644 --- a/toolchain/check/testdata/array/fail_bound_overflow.carbon +++ b/toolchain/check/testdata/array/fail_bound_overflow.carbon @@ -4,23 +4,36 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:8: ERROR: Invalid array expression. -// CHECK:STDERR: var a: [1; 39999999999999999993]; +// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:8: ERROR: Cannot evaluate type expression. +// CHECK:STDERR: var a: [i32; 39999999999999999993]; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +var a: [i32; 39999999999999999993]; + +// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+6]]:8: ERROR: Cannot implicitly convert from `i32` to `type`. +// CHECK:STDERR: var b: [1; 39999999999999999993]; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ -var a: [1; 39999999999999999993]; +// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:8: ERROR: Cannot evaluate type expression. +// CHECK:STDERR: var b: [1; 39999999999999999993]; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +var b: [1; 39999999999999999993]; // CHECK:STDOUT: --- fail_bound_overflow.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] -// CHECK:STDOUT: %.2: i32 = int_literal 39999999999999999993 [template] +// CHECK:STDOUT: %.1: i32 = int_literal 39999999999999999993 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 1 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace package, {.a = %a} [template] -// CHECK:STDOUT: %.loc10_9: i32 = int_literal 1 [template = constants.%.1] -// CHECK:STDOUT: %.loc10_12: i32 = int_literal 39999999999999999993 [template = constants.%.2] +// CHECK:STDOUT: package: = namespace package, {.a = %a, .b = %b} [template] +// CHECK:STDOUT: %.loc10_14: i32 = int_literal 39999999999999999993 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_34: type = array_type %.loc10_14, i32 // CHECK:STDOUT: %a.var: ref = var a // CHECK:STDOUT: %a: ref = bind_name a, %a.var +// CHECK:STDOUT: %.loc18_9: i32 = int_literal 1 [template = constants.%.2] +// CHECK:STDOUT: %.loc18_12: i32 = int_literal 39999999999999999993 [template = constants.%.1] +// CHECK:STDOUT: %.loc18_32: type = array_type %.loc18_12, +// CHECK:STDOUT: %b.var: ref = var b +// CHECK:STDOUT: %b: ref = bind_name b, %b.var // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/eval/aggregate.carbon b/toolchain/check/testdata/eval/aggregate.carbon new file mode 100644 index 000000000000..39ec54032b4a --- /dev/null +++ b/toolchain/check/testdata/eval/aggregate.carbon @@ -0,0 +1,133 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +var tuple_copy: (i32, i32) = (1, 2) as (i32, i32); + +var struct_copy: {.a: i32, .b: i32, .c: i32} = {.c = 3, .b = 2, .a = 1} as {.b: i32, .a: i32, .c: i32}; + +var tuple_index: [i32; 1] = (0,) as [i32; (5, 7, 1, 9)[2]]; + +var struct_access: [i32; 1] = (0,) as [i32; {.a = 3, .b = 1}.b]; + +// CHECK:STDOUT: --- aggregate.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type (type, type) [template] +// CHECK:STDOUT: %.2: type = tuple_type (i32, i32) [template] +// CHECK:STDOUT: %.3: type = ptr_type (i32, i32) [template] +// CHECK:STDOUT: %.4: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.6: (i32, i32) = tuple_value (%.4, %.5) [template] +// CHECK:STDOUT: %.7: type = struct_type {.a: i32, .b: i32, .c: i32} [template] +// CHECK:STDOUT: %.8: type = ptr_type {.a: i32, .b: i32, .c: i32} [template] +// CHECK:STDOUT: %.9: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.10: type = struct_type {.c: i32, .b: i32, .a: i32} [template] +// CHECK:STDOUT: %.11: type = struct_type {.b: i32, .a: i32, .c: i32} [template] +// CHECK:STDOUT: %.12: type = ptr_type {.b: i32, .a: i32, .c: i32} [template] +// CHECK:STDOUT: %.13: {.b: i32, .a: i32, .c: i32} = struct_value (%.5, %.4, %.9) [template] +// CHECK:STDOUT: %.14: {.a: i32, .b: i32, .c: i32} = struct_value (%.4, %.5, %.9) [template] +// CHECK:STDOUT: %.15: type = array_type %.4, i32 [template] +// CHECK:STDOUT: %.16: type = ptr_type [i32; 1] [template] +// CHECK:STDOUT: %.17: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.18: type = tuple_type (i32) [template] +// CHECK:STDOUT: %.19: i32 = int_literal 5 [template] +// CHECK:STDOUT: %.20: i32 = int_literal 7 [template] +// CHECK:STDOUT: %.21: i32 = int_literal 9 [template] +// CHECK:STDOUT: %.22: type = tuple_type (i32, i32, i32, i32) [template] +// CHECK:STDOUT: %.23: type = ptr_type (i32, i32, i32, i32) [template] +// CHECK:STDOUT: %.24: (i32, i32, i32, i32) = tuple_value (%.19, %.20, %.4, %.21) [template] +// CHECK:STDOUT: %.25: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.26: [i32; 1] = tuple_value (%.17) [template] +// CHECK:STDOUT: %.27: type = struct_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.28: type = ptr_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.29: {.a: i32, .b: i32} = struct_value (%.9, %.4) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace package, {.tuple_copy = %tuple_copy, .struct_copy = %struct_copy, .tuple_index = %tuple_index, .struct_access = %struct_access} [template] +// CHECK:STDOUT: %.loc7_26.1: (type, type) = tuple_literal (i32, i32) +// CHECK:STDOUT: %.loc7_26.2: type = converted %.loc7_26.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %tuple_copy.var: ref (i32, i32) = var tuple_copy +// CHECK:STDOUT: %tuple_copy: ref (i32, i32) = bind_name tuple_copy, %tuple_copy.var +// CHECK:STDOUT: %.loc7_31: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc7_34: i32 = int_literal 2 [template = constants.%.5] +// CHECK:STDOUT: %.loc7_35.1: (i32, i32) = tuple_literal (%.loc7_31, %.loc7_34) +// CHECK:STDOUT: %.loc7_49.1: (type, type) = tuple_literal (i32, i32) +// CHECK:STDOUT: %.loc7_49.2: type = converted %.loc7_49.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %.loc7_35.2: ref i32 = tuple_access %tuple_copy.var, element0 +// CHECK:STDOUT: %.loc7_35.3: init i32 = initialize_from %.loc7_31 to %.loc7_35.2 [template = constants.%.4] +// CHECK:STDOUT: %.loc7_35.4: ref i32 = tuple_access %tuple_copy.var, element1 +// CHECK:STDOUT: %.loc7_35.5: init i32 = initialize_from %.loc7_34 to %.loc7_35.4 [template = constants.%.5] +// CHECK:STDOUT: %.loc7_35.6: init (i32, i32) = tuple_init (%.loc7_35.3, %.loc7_35.5) to %tuple_copy.var [template = constants.%.6] +// CHECK:STDOUT: %.loc7_35.7: init (i32, i32) = converted %.loc7_35.1, %.loc7_35.6 [template = constants.%.6] +// CHECK:STDOUT: assign %tuple_copy.var, %.loc7_35.7 +// CHECK:STDOUT: %.loc9_44: type = struct_type {.a: i32, .b: i32, .c: i32} [template = constants.%.7] +// CHECK:STDOUT: %struct_copy.var: ref {.a: i32, .b: i32, .c: i32} = var struct_copy +// CHECK:STDOUT: %struct_copy: ref {.a: i32, .b: i32, .c: i32} = bind_name struct_copy, %struct_copy.var +// CHECK:STDOUT: %.loc9_54: i32 = int_literal 3 [template = constants.%.9] +// CHECK:STDOUT: %.loc9_62: i32 = int_literal 2 [template = constants.%.5] +// CHECK:STDOUT: %.loc9_70: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_71.1: {.c: i32, .b: i32, .a: i32} = struct_literal (%.loc9_54, %.loc9_62, %.loc9_70) +// CHECK:STDOUT: %.loc9_102: type = struct_type {.b: i32, .a: i32, .c: i32} [template = constants.%.11] +// CHECK:STDOUT: %.loc9_71.2: {.b: i32, .a: i32, .c: i32} = struct_value (%.loc9_62, %.loc9_70, %.loc9_54) [template = constants.%.13] +// CHECK:STDOUT: %.loc9_71.3: {.b: i32, .a: i32, .c: i32} = converted %.loc9_71.1, %.loc9_71.2 [template = constants.%.13] +// CHECK:STDOUT: %.loc9_71.4: i32 = struct_access %.loc9_71.3, element1 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_71.5: ref i32 = struct_access %struct_copy.var, element1 +// CHECK:STDOUT: %.loc9_71.6: init i32 = initialize_from %.loc9_71.4 to %.loc9_71.5 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_71.7: i32 = struct_access %.loc9_71.3, element0 [template = constants.%.5] +// CHECK:STDOUT: %.loc9_71.8: ref i32 = struct_access %struct_copy.var, element0 +// CHECK:STDOUT: %.loc9_71.9: init i32 = initialize_from %.loc9_71.7 to %.loc9_71.8 [template = constants.%.5] +// CHECK:STDOUT: %.loc9_71.10: i32 = struct_access %.loc9_71.3, element2 [template = constants.%.9] +// CHECK:STDOUT: %.loc9_71.11: ref i32 = struct_access %struct_copy.var, element2 +// CHECK:STDOUT: %.loc9_71.12: init i32 = initialize_from %.loc9_71.10 to %.loc9_71.11 [template = constants.%.9] +// CHECK:STDOUT: %.loc9_71.13: init {.a: i32, .b: i32, .c: i32} = struct_init (%.loc9_71.6, %.loc9_71.9, %.loc9_71.12) to %struct_copy.var [template = constants.%.14] +// CHECK:STDOUT: %.loc9_71.14: init {.a: i32, .b: i32, .c: i32} = converted %.loc9_71.3, %.loc9_71.13 [template = constants.%.14] +// CHECK:STDOUT: assign %struct_copy.var, %.loc9_71.14 +// CHECK:STDOUT: %.loc11_24: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc11_25: type = array_type %.loc11_24, i32 [template = constants.%.15] +// CHECK:STDOUT: %tuple_index.var: ref [i32; 1] = var tuple_index +// CHECK:STDOUT: %tuple_index: ref [i32; 1] = bind_name tuple_index, %tuple_index.var +// CHECK:STDOUT: %.loc11_30: i32 = int_literal 0 [template = constants.%.17] +// CHECK:STDOUT: %.loc11_32.1: (i32,) = tuple_literal (%.loc11_30) +// CHECK:STDOUT: %.loc11_44: i32 = int_literal 5 [template = constants.%.19] +// CHECK:STDOUT: %.loc11_47: i32 = int_literal 7 [template = constants.%.20] +// CHECK:STDOUT: %.loc11_50: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc11_53: i32 = int_literal 9 [template = constants.%.21] +// CHECK:STDOUT: %.loc11_54.1: (i32, i32, i32, i32) = tuple_literal (%.loc11_44, %.loc11_47, %.loc11_50, %.loc11_53) +// CHECK:STDOUT: %.loc11_56: i32 = int_literal 2 [template = constants.%.5] +// CHECK:STDOUT: %.loc11_54.2: (i32, i32, i32, i32) = tuple_value (%.loc11_44, %.loc11_47, %.loc11_50, %.loc11_53) [template = constants.%.24] +// CHECK:STDOUT: %.loc11_54.3: (i32, i32, i32, i32) = converted %.loc11_54.1, %.loc11_54.2 [template = constants.%.24] +// CHECK:STDOUT: %.loc11_57: i32 = tuple_index %.loc11_54.3, %.loc11_56 [template = constants.%.4] +// CHECK:STDOUT: %.loc11_58: type = array_type %.loc11_57, i32 [template = constants.%.15] +// CHECK:STDOUT: %.loc11_5: ref [i32; 1] = splice_block %tuple_index.var {} +// CHECK:STDOUT: %.loc11_32.2: i32 = int_literal 0 [template = constants.%.25] +// CHECK:STDOUT: %.loc11_32.3: ref i32 = array_index %.loc11_5, %.loc11_32.2 +// CHECK:STDOUT: %.loc11_32.4: init i32 = initialize_from %.loc11_30 to %.loc11_32.3 [template = constants.%.17] +// CHECK:STDOUT: %.loc11_32.5: init [i32; 1] = array_init (%.loc11_32.4) to %.loc11_5 [template = constants.%.26] +// CHECK:STDOUT: %.loc11_32.6: init [i32; 1] = converted %.loc11_32.1, %.loc11_32.5 [template = constants.%.26] +// CHECK:STDOUT: assign %tuple_index.var, %.loc11_32.6 +// CHECK:STDOUT: %.loc13_26: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc13_27: type = array_type %.loc13_26, i32 [template = constants.%.15] +// CHECK:STDOUT: %struct_access.var: ref [i32; 1] = var struct_access +// CHECK:STDOUT: %struct_access: ref [i32; 1] = bind_name struct_access, %struct_access.var +// CHECK:STDOUT: %.loc13_32: i32 = int_literal 0 [template = constants.%.17] +// CHECK:STDOUT: %.loc13_34.1: (i32,) = tuple_literal (%.loc13_32) +// CHECK:STDOUT: %.loc13_51: i32 = int_literal 3 [template = constants.%.9] +// CHECK:STDOUT: %.loc13_59: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc13_60.1: {.a: i32, .b: i32} = struct_literal (%.loc13_51, %.loc13_59) +// CHECK:STDOUT: %.loc13_60.2: {.a: i32, .b: i32} = struct_value (%.loc13_51, %.loc13_59) [template = constants.%.29] +// CHECK:STDOUT: %.loc13_60.3: {.a: i32, .b: i32} = converted %.loc13_60.1, %.loc13_60.2 [template = constants.%.29] +// CHECK:STDOUT: %.loc13_61: i32 = struct_access %.loc13_60.3, element1 [template = constants.%.4] +// CHECK:STDOUT: %.loc13_63: type = array_type %.loc13_61, i32 [template = constants.%.15] +// CHECK:STDOUT: %.loc13_5: ref [i32; 1] = splice_block %struct_access.var {} +// CHECK:STDOUT: %.loc13_34.2: i32 = int_literal 0 [template = constants.%.25] +// CHECK:STDOUT: %.loc13_34.3: ref i32 = array_index %.loc13_5, %.loc13_34.2 +// CHECK:STDOUT: %.loc13_34.4: init i32 = initialize_from %.loc13_32 to %.loc13_34.3 [template = constants.%.17] +// CHECK:STDOUT: %.loc13_34.5: init [i32; 1] = array_init (%.loc13_34.4) to %.loc13_5 [template = constants.%.26] +// CHECK:STDOUT: %.loc13_34.6: init [i32; 1] = converted %.loc13_34.1, %.loc13_34.5 [template = constants.%.26] +// CHECK:STDOUT: assign %struct_access.var, %.loc13_34.6 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/eval/fail_aggregate.carbon b/toolchain/check/testdata/eval/fail_aggregate.carbon new file mode 100644 index 000000000000..8ea5f97ea1ae --- /dev/null +++ b/toolchain/check/testdata/eval/fail_aggregate.carbon @@ -0,0 +1,73 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// TODO: This creates an array temporary, which we don't yet support evaluating. + +// CHECK:STDERR: fail_aggregate.carbon:[[@LINE+3]]:43: ERROR: Array bound is not a constant. +// CHECK:STDERR: var array_index: [i32; 1] = (0,) as [i32; ((5, 7, 1, 9) as [i32; 4])[2]]; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var array_index: [i32; 1] = (0,) as [i32; ((5, 7, 1, 9) as [i32; 4])[2]]; + +// CHECK:STDOUT: --- fail_aggregate.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: type = array_type %.1, i32 [template] +// CHECK:STDOUT: %.3: type = ptr_type [i32; 1] [template] +// CHECK:STDOUT: %.4: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.5: type = tuple_type (i32) [template] +// CHECK:STDOUT: %.6: i32 = int_literal 5 [template] +// CHECK:STDOUT: %.7: i32 = int_literal 7 [template] +// CHECK:STDOUT: %.8: i32 = int_literal 9 [template] +// CHECK:STDOUT: %.9: type = tuple_type (i32, i32, i32, i32) [template] +// CHECK:STDOUT: %.10: i32 = int_literal 4 [template] +// CHECK:STDOUT: %.11: type = array_type %.10, i32 [template] +// CHECK:STDOUT: %.12: type = ptr_type [i32; 4] [template] +// CHECK:STDOUT: %.13: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.14: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.15: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.16: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.17: [i32; 4] = tuple_value (%.6, %.7, %.1, %.8) [template] +// CHECK:STDOUT: %.18: i32 = int_literal 2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace package, {.array_index = %array_index} [template] +// CHECK:STDOUT: %.loc12_24: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_25: type = array_type %.loc12_24, i32 [template = constants.%.2] +// CHECK:STDOUT: %array_index.var: ref [i32; 1] = var array_index +// CHECK:STDOUT: %array_index: ref [i32; 1] = bind_name array_index, %array_index.var +// CHECK:STDOUT: %.loc12_30: i32 = int_literal 0 [template = constants.%.4] +// CHECK:STDOUT: %.loc12_32: (i32,) = tuple_literal (%.loc12_30) +// CHECK:STDOUT: %.loc12_45: i32 = int_literal 5 [template = constants.%.6] +// CHECK:STDOUT: %.loc12_48: i32 = int_literal 7 [template = constants.%.7] +// CHECK:STDOUT: %.loc12_51: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_54: i32 = int_literal 9 [template = constants.%.8] +// CHECK:STDOUT: %.loc12_55.1: (i32, i32, i32, i32) = tuple_literal (%.loc12_45, %.loc12_48, %.loc12_51, %.loc12_54) +// CHECK:STDOUT: %.loc12_66: i32 = int_literal 4 [template = constants.%.10] +// CHECK:STDOUT: %.loc12_67: type = array_type %.loc12_66, i32 [template = constants.%.11] +// CHECK:STDOUT: %.loc12_55.2: ref [i32; 4] = temporary_storage +// CHECK:STDOUT: %.loc12_55.3: i32 = int_literal 0 [template = constants.%.13] +// CHECK:STDOUT: %.loc12_55.4: ref i32 = array_index %.loc12_55.2, %.loc12_55.3 +// CHECK:STDOUT: %.loc12_55.5: init i32 = initialize_from %.loc12_45 to %.loc12_55.4 [template = constants.%.6] +// CHECK:STDOUT: %.loc12_55.6: i32 = int_literal 1 [template = constants.%.14] +// CHECK:STDOUT: %.loc12_55.7: ref i32 = array_index %.loc12_55.2, %.loc12_55.6 +// CHECK:STDOUT: %.loc12_55.8: init i32 = initialize_from %.loc12_48 to %.loc12_55.7 [template = constants.%.7] +// CHECK:STDOUT: %.loc12_55.9: i32 = int_literal 2 [template = constants.%.15] +// CHECK:STDOUT: %.loc12_55.10: ref i32 = array_index %.loc12_55.2, %.loc12_55.9 +// CHECK:STDOUT: %.loc12_55.11: init i32 = initialize_from %.loc12_51 to %.loc12_55.10 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_55.12: i32 = int_literal 3 [template = constants.%.16] +// CHECK:STDOUT: %.loc12_55.13: ref i32 = array_index %.loc12_55.2, %.loc12_55.12 +// CHECK:STDOUT: %.loc12_55.14: init i32 = initialize_from %.loc12_54 to %.loc12_55.13 [template = constants.%.8] +// CHECK:STDOUT: %.loc12_55.15: init [i32; 4] = array_init (%.loc12_55.5, %.loc12_55.8, %.loc12_55.11, %.loc12_55.14) to %.loc12_55.2 [template = constants.%.17] +// CHECK:STDOUT: %.loc12_55.16: init [i32; 4] = converted %.loc12_55.1, %.loc12_55.15 [template = constants.%.17] +// CHECK:STDOUT: %.loc12_70: i32 = int_literal 2 [template = constants.%.18] +// CHECK:STDOUT: %.loc12_55.17: ref [i32; 4] = temporary %.loc12_55.2, %.loc12_55.16 +// CHECK:STDOUT: %.loc12_71.1: ref i32 = array_index %.loc12_55.17, %.loc12_70 +// CHECK:STDOUT: %.loc12_71.2: i32 = bind_value %.loc12_71.1 +// CHECK:STDOUT: assign %array_index.var, +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/index/fail_expr_category.carbon b/toolchain/check/testdata/index/fail_expr_category.carbon index cd0808e92880..237352bd47ff 100644 --- a/toolchain/check/testdata/index/fail_expr_category.carbon +++ b/toolchain/check/testdata/index/fail_expr_category.carbon @@ -58,7 +58,7 @@ fn G(b: [i32; 3]) { // CHECK:STDOUT: %.loc14_22.1: ref [i32; 3] = value_as_ref %b.ref.loc14 // CHECK:STDOUT: %.loc14_22.2: ref i32 = array_index %.loc14_22.1, %.loc14_21 // CHECK:STDOUT: %.loc14_22.3: i32 = bind_value %.loc14_22.2 -// CHECK:STDOUT: %.loc14_18: i32* = addr_of %.loc14_22.3 +// CHECK:STDOUT: %.loc14_18: i32* = addr_of [template = ] // CHECK:STDOUT: assign %pb.var, %.loc14_18 // CHECK:STDOUT: %b.ref.loc18: [i32; 3] = name_ref b, %b // CHECK:STDOUT: %.loc18_5: i32 = int_literal 0 [template = constants.%.5] @@ -77,7 +77,7 @@ fn G(b: [i32; 3]) { // CHECK:STDOUT: %.loc25_20.3: ref [i32; 3] = temporary %.loc25_20.1, %.loc25_20.2 // CHECK:STDOUT: %.loc25_24.1: ref i32 = array_index %.loc25_20.3, %.loc25_23 // CHECK:STDOUT: %.loc25_24.2: i32 = bind_value %.loc25_24.1 -// CHECK:STDOUT: %.loc25_18: i32* = addr_of %.loc25_24.2 +// CHECK:STDOUT: %.loc25_18: i32* = addr_of [template = ] // CHECK:STDOUT: assign %pf.var, %.loc25_18 // CHECK:STDOUT: %F.ref.loc29: = name_ref F, file.%F [template = file.%F] // CHECK:STDOUT: %.loc29_4.1: ref [i32; 3] = temporary_storage diff --git a/toolchain/check/testdata/pointer/fail_address_of_error.carbon b/toolchain/check/testdata/pointer/fail_address_of_error.carbon index 293f4f2fb132..128d4132bdd4 100644 --- a/toolchain/check/testdata/pointer/fail_address_of_error.carbon +++ b/toolchain/check/testdata/pointer/fail_address_of_error.carbon @@ -36,7 +36,7 @@ fn Test() { // CHECK:STDOUT: %.loc11: * = addr_of %undeclared.ref.loc11 [template = ] // CHECK:STDOUT: %undeclared.ref.loc18: = name_ref undeclared, [template = ] // CHECK:STDOUT: %.loc18_5: * = addr_of %undeclared.ref.loc18 [template = ] -// CHECK:STDOUT: %.loc18_3: ** = addr_of %.loc18_5 [template = ] +// CHECK:STDOUT: %.loc18_3: ** = addr_of [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/pointer/fail_address_of_value.carbon b/toolchain/check/testdata/pointer/fail_address_of_value.carbon index 5f0ee6a61d67..21dafa4ece3e 100644 --- a/toolchain/check/testdata/pointer/fail_address_of_value.carbon +++ b/toolchain/check/testdata/pointer/fail_address_of_value.carbon @@ -88,30 +88,23 @@ fn AddressOfParam(param: i32) { // CHECK:STDOUT: %.1: type = struct_type {.a: i32} [template] // CHECK:STDOUT: %.2: i32 = int_literal 0 [template] // CHECK:STDOUT: %.3: type = ptr_type i32 [template] -// CHECK:STDOUT: %.4: i32* = addr_of %.2 [template] -// CHECK:STDOUT: %.5: bool = bool_literal true [template] -// CHECK:STDOUT: %.6: type = ptr_type bool [template] -// CHECK:STDOUT: %.7: bool* = addr_of %.5 [template] -// CHECK:STDOUT: %.8: f64 = real_literal 10e-1 [template] -// CHECK:STDOUT: %.9: type = ptr_type f64 [template] -// CHECK:STDOUT: %.10: f64* = addr_of %.8 [template] -// CHECK:STDOUT: %.11: type = ptr_type String [template] -// CHECK:STDOUT: %.12: String = string_literal "Hello" [template] -// CHECK:STDOUT: %.13: String* = addr_of %.12 [template] -// CHECK:STDOUT: %.14: i32 = int_literal 1 [template] -// CHECK:STDOUT: %.15: i32 = int_literal 2 [template] -// CHECK:STDOUT: %.16: type = tuple_type (i32, i32) [template] -// CHECK:STDOUT: %.17: type = ptr_type (i32, i32) [template] -// CHECK:STDOUT: %.18: i32 = int_literal 5 [template] -// CHECK:STDOUT: %.19: type = ptr_type {.a: i32} [template] -// CHECK:STDOUT: %.20: bool = bool_literal false [template] -// CHECK:STDOUT: %.21: bool* = addr_of %.20 [template] -// CHECK:STDOUT: %.22: type = ptr_type type [template] -// CHECK:STDOUT: %.23: type* = addr_of i32 [template] -// CHECK:STDOUT: %.24: type = const_type i32 [template] -// CHECK:STDOUT: %.25: type = ptr_type const i32 [template] -// CHECK:STDOUT: %.26: type* = addr_of %.25 [template] -// CHECK:STDOUT: %.27: (i32, i32) = tuple_value (%.14, %.15) [template] +// CHECK:STDOUT: %.4: bool = bool_literal true [template] +// CHECK:STDOUT: %.5: type = ptr_type bool [template] +// CHECK:STDOUT: %.6: f64 = real_literal 10e-1 [template] +// CHECK:STDOUT: %.7: type = ptr_type f64 [template] +// CHECK:STDOUT: %.8: type = ptr_type String [template] +// CHECK:STDOUT: %.9: String = string_literal "Hello" [template] +// CHECK:STDOUT: %.10: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.11: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.12: type = tuple_type (i32, i32) [template] +// CHECK:STDOUT: %.13: type = ptr_type (i32, i32) [template] +// CHECK:STDOUT: %.14: i32 = int_literal 5 [template] +// CHECK:STDOUT: %.15: type = ptr_type {.a: i32} [template] +// CHECK:STDOUT: %.16: bool = bool_literal false [template] +// CHECK:STDOUT: %.17: type = ptr_type type [template] +// CHECK:STDOUT: %.18: type = const_type i32 [template] +// CHECK:STDOUT: %.19: type = ptr_type const i32 [template] +// CHECK:STDOUT: %.20: (i32, i32) = tuple_value (%.10, %.11) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -133,45 +126,45 @@ fn AddressOfParam(param: i32) { // CHECK:STDOUT: fn @AddressOfLiteral() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc15_4: i32 = int_literal 0 [template = constants.%.2] -// CHECK:STDOUT: %.loc15_3: i32* = addr_of %.loc15_4 [template = constants.%.4] -// CHECK:STDOUT: %.loc19_4: bool = bool_literal true [template = constants.%.5] -// CHECK:STDOUT: %.loc19_3: bool* = addr_of %.loc19_4 [template = constants.%.7] -// CHECK:STDOUT: %.loc23_4: f64 = real_literal 10e-1 [template = constants.%.8] -// CHECK:STDOUT: %.loc23_3: f64* = addr_of %.loc23_4 [template = constants.%.10] -// CHECK:STDOUT: %.loc27_4: String = string_literal "Hello" [template = constants.%.12] -// CHECK:STDOUT: %.loc27_3: String* = addr_of %.loc27_4 [template = constants.%.13] -// CHECK:STDOUT: %.loc31_5: i32 = int_literal 1 [template = constants.%.14] -// CHECK:STDOUT: %.loc31_8: i32 = int_literal 2 [template = constants.%.15] +// CHECK:STDOUT: %.loc15_3: i32* = addr_of [template = ] +// CHECK:STDOUT: %.loc19_4: bool = bool_literal true [template = constants.%.4] +// CHECK:STDOUT: %.loc19_3: bool* = addr_of [template = ] +// CHECK:STDOUT: %.loc23_4: f64 = real_literal 10e-1 [template = constants.%.6] +// CHECK:STDOUT: %.loc23_3: f64* = addr_of [template = ] +// CHECK:STDOUT: %.loc27_4: String = string_literal "Hello" [template = constants.%.9] +// CHECK:STDOUT: %.loc27_3: String* = addr_of [template = ] +// CHECK:STDOUT: %.loc31_5: i32 = int_literal 1 [template = constants.%.10] +// CHECK:STDOUT: %.loc31_8: i32 = int_literal 2 [template = constants.%.11] // CHECK:STDOUT: %.loc31_9: (i32, i32) = tuple_literal (%.loc31_5, %.loc31_8) -// CHECK:STDOUT: %.loc31_3: (i32, i32)* = addr_of %.loc31_9 -// CHECK:STDOUT: %.loc35_10: i32 = int_literal 5 [template = constants.%.18] +// CHECK:STDOUT: %.loc31_3: (i32, i32)* = addr_of [template = ] +// CHECK:STDOUT: %.loc35_10: i32 = int_literal 5 [template = constants.%.14] // CHECK:STDOUT: %.loc35_11: {.a: i32} = struct_literal (%.loc35_10) -// CHECK:STDOUT: %.loc35_3: {.a: i32}* = addr_of %.loc35_11 +// CHECK:STDOUT: %.loc35_3: {.a: i32}* = addr_of [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AddressOfOperator() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc42_5: bool = bool_literal true [template = constants.%.5] -// CHECK:STDOUT: %.loc42_10.1: bool = bool_literal false [template = constants.%.20] +// CHECK:STDOUT: %.loc42_5: bool = bool_literal true [template = constants.%.4] +// CHECK:STDOUT: %.loc42_10.1: bool = bool_literal false [template = constants.%.16] // CHECK:STDOUT: if %.loc42_5 br !and.rhs else br !and.result(%.loc42_10.1) // CHECK:STDOUT: // CHECK:STDOUT: !and.rhs: -// CHECK:STDOUT: %.loc42_14: bool = bool_literal false [template = constants.%.20] +// CHECK:STDOUT: %.loc42_14: bool = bool_literal false [template = constants.%.16] // CHECK:STDOUT: br !and.result(%.loc42_14) // CHECK:STDOUT: // CHECK:STDOUT: !and.result: // CHECK:STDOUT: %.loc42_10.2: bool = block_arg !and.result -// CHECK:STDOUT: %.loc42_3: bool* = addr_of %.loc42_10.2 +// CHECK:STDOUT: %.loc42_3: bool* = addr_of [template = ] // CHECK:STDOUT: %H.ref: = name_ref H, file.%H [template = file.%H] // CHECK:STDOUT: %.loc46_5.1: init {.a: i32} = call %H.ref() // CHECK:STDOUT: %.loc46_5.2: ref {.a: i32} = temporary_storage // CHECK:STDOUT: %.loc46_5.3: ref {.a: i32} = temporary %.loc46_5.2, %.loc46_5.1 // CHECK:STDOUT: %.loc46_7: ref i32 = struct_access %.loc46_5.3, element0 -// CHECK:STDOUT: %.loc46_3: i32* = addr_of %.loc46_7 -// CHECK:STDOUT: %.loc50_9: bool = bool_literal true [template = constants.%.5] -// CHECK:STDOUT: %.loc50_5: bool = not %.loc50_9 [template = constants.%.20] -// CHECK:STDOUT: %.loc50_3: bool* = addr_of %.loc50_5 [template = constants.%.21] +// CHECK:STDOUT: %.loc46_3: i32* = addr_of [template = ] +// CHECK:STDOUT: %.loc50_9: bool = bool_literal true [template = constants.%.4] +// CHECK:STDOUT: %.loc50_5: bool = not %.loc50_9 [template = constants.%.16] +// CHECK:STDOUT: %.loc50_3: bool* = addr_of [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: @@ -179,29 +172,29 @@ fn AddressOfParam(param: i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: = name_ref G, file.%G [template = file.%G] // CHECK:STDOUT: %.loc57_5: init i32 = call %G.ref() -// CHECK:STDOUT: %.loc57_3: i32* = addr_of %.loc57_5 +// CHECK:STDOUT: %.loc57_3: i32* = addr_of [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AddressOfType() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc64: type* = addr_of i32 [template = constants.%.23] -// CHECK:STDOUT: %.loc68_5: type = const_type i32 [template = constants.%.24] -// CHECK:STDOUT: %.loc68_14: type = ptr_type const i32 [template = constants.%.25] -// CHECK:STDOUT: %.loc68_3: type* = addr_of %.loc68_14 [template = constants.%.26] +// CHECK:STDOUT: %.loc64: type* = addr_of [template = ] +// CHECK:STDOUT: %.loc68_5: type = const_type i32 [template = constants.%.18] +// CHECK:STDOUT: %.loc68_14: type = ptr_type const i32 [template = constants.%.19] +// CHECK:STDOUT: %.loc68_3: type* = addr_of [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AddressOfTupleElementValue() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc75_6: i32 = int_literal 1 [template = constants.%.14] -// CHECK:STDOUT: %.loc75_9: i32 = int_literal 2 [template = constants.%.15] +// CHECK:STDOUT: %.loc75_6: i32 = int_literal 1 [template = constants.%.10] +// CHECK:STDOUT: %.loc75_9: i32 = int_literal 2 [template = constants.%.11] // CHECK:STDOUT: %.loc75_10.1: (i32, i32) = tuple_literal (%.loc75_6, %.loc75_9) // CHECK:STDOUT: %.loc75_12: i32 = int_literal 0 [template = constants.%.2] -// CHECK:STDOUT: %.loc75_10.2: (i32, i32) = tuple_value (%.loc75_6, %.loc75_9) [template = constants.%.27] -// CHECK:STDOUT: %.loc75_10.3: (i32, i32) = converted %.loc75_10.1, %.loc75_10.2 [template = constants.%.27] -// CHECK:STDOUT: %.loc75_13: i32 = tuple_index %.loc75_10.3, %.loc75_12 -// CHECK:STDOUT: %.loc75_3: i32* = addr_of %.loc75_13 +// CHECK:STDOUT: %.loc75_10.2: (i32, i32) = tuple_value (%.loc75_6, %.loc75_9) [template = constants.%.20] +// CHECK:STDOUT: %.loc75_10.3: (i32, i32) = converted %.loc75_10.1, %.loc75_10.2 [template = constants.%.20] +// CHECK:STDOUT: %.loc75_13: i32 = tuple_index %.loc75_10.3, %.loc75_12 [template = constants.%.10] +// CHECK:STDOUT: %.loc75_3: i32* = addr_of [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: @@ -211,7 +204,7 @@ fn AddressOfParam(param: i32) { // CHECK:STDOUT: %param_addr.var: ref i32* = var param_addr // CHECK:STDOUT: %param_addr: ref i32* = bind_name param_addr, %param_addr.var // CHECK:STDOUT: %param.ref: i32 = name_ref param, %param -// CHECK:STDOUT: %.loc82_26: i32* = addr_of %param.ref +// CHECK:STDOUT: %.loc82_26: i32* = addr_of [template = ] // CHECK:STDOUT: assign %param_addr.var, %.loc82_26 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 14a76c226a21..7ef9774e5d44 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -73,6 +73,31 @@ struct ArrayIndex { InstId index_id; }; +// Common representation for aggregate access nodes, which access a fixed +// element of an aggregate. +struct AnyAggregateAccess { + static constexpr InstKind Kinds[] = {InstKind::StructAccess, + InstKind::TupleAccess, + InstKind::ClassElementAccess}; + + InstKind kind; + TypeId type_id; + InstId aggregate_id; + ElementIndex index; +}; + +// Common representation for aggregate index nodes, which access an element +// determined by evaluating an expression. +struct AnyAggregateIndex { + static constexpr InstKind Kinds[] = {InstKind::ArrayIndex, + InstKind::TupleIndex}; + + InstKind kind; + TypeId type_id; + InstId aggregate_id; + InstId index_id; +}; + // Common representation for all kinds of aggregate initialization. struct AnyAggregateInit { static constexpr InstKind Kinds[] = {InstKind::ArrayInit, InstKind::ClassInit,