diff --git a/toolchain/lowering/lowering_handle.cpp b/toolchain/lowering/lowering_handle.cpp index 122ae8afd955..0a33da490327 100644 --- a/toolchain/lowering/lowering_handle.cpp +++ b/toolchain/lowering/lowering_handle.cpp @@ -24,6 +24,12 @@ auto LoweringHandleAddressOf(LoweringFunctionContext& context, context.SetLocal(node_id, context.GetLocal(node.GetAsAddressOf())); } +auto LoweringHandleArrayIndex(LoweringFunctionContext& /*context*/, + SemanticsNodeId /*node_id*/, SemanticsNode node) + -> void { + CARBON_FATAL() << "TODO: Add support: " << node; +} + auto LoweringHandleArrayType(LoweringFunctionContext& /*context*/, SemanticsNodeId /*node_id*/, SemanticsNode node) -> void { diff --git a/toolchain/semantics/semantics_handle_index.cpp b/toolchain/semantics/semantics_handle_index.cpp index 8d8a682b61a8..d8a470ea6508 100644 --- a/toolchain/semantics/semantics_handle_index.cpp +++ b/toolchain/semantics/semantics_handle_index.cpp @@ -2,8 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include - +#include "toolchain/semantics/semantics_builtin_kind.h" #include "toolchain/semantics/semantics_context.h" #include "toolchain/semantics/semantics_node.h" #include "toolchain/semantics/semantics_node_kind.h" @@ -19,6 +18,10 @@ auto SemanticsHandleIndexExpressionStart(SemanticsContext& /*context*/, auto SemanticsHandleIndexExpression(SemanticsContext& context, ParseTree::Node parse_node) -> bool { + CARBON_DIAGNOSTIC(OutOfBoundsAccess, Error, + "Index `{0}` is past the end of `{1}`.", llvm::APSInt, + std::string); + auto index_node_id = context.node_stack().PopExpression(); auto index_node = context.semantics_ir().GetNode(index_node_id); auto name_node_id = context.node_stack().PopExpression(); @@ -27,34 +30,57 @@ auto SemanticsHandleIndexExpression(SemanticsContext& context, context.semantics_ir().GetTypeAllowBuiltinTypes(name_node.type_id()); auto name_type_node = context.semantics_ir().GetNode(name_type_id); - if (name_type_node.kind() == SemanticsNodeKind::TupleType && - index_node.kind() == SemanticsNodeKind::IntegerLiteral) { - const auto& index_val = context.semantics_ir().GetIntegerLiteral( - index_node.GetAsIntegerLiteral()); - auto type_block = - context.semantics_ir().GetTypeBlock(name_type_node.GetAsTupleType()); - - if (index_val.uge(static_cast(type_block.size()))) { - CARBON_DIAGNOSTIC(OutOfBoundsAccess, Error, - "Index `{0}` is past the end of `{1}`.", llvm::APSInt, - std::string); - context.emitter().Emit( - parse_node, OutOfBoundsAccess, - llvm::APSInt(index_val, /*isUnsigned=*/true), - context.semantics_ir().StringifyType(name_node.type_id())); - } else { + if (name_type_node.kind() == SemanticsNodeKind::ArrayType) { + auto [bound_id, type_id] = name_type_node.GetAsArrayType(); + if (index_node.kind() == SemanticsNodeKind::IntegerLiteral) { + const auto& index_val = context.semantics_ir().GetIntegerLiteral( + index_node.GetAsIntegerLiteral()); + if (index_val.uge(context.semantics_ir().GetArrayBoundValue(bound_id))) { + context.emitter().Emit( + parse_node, OutOfBoundsAccess, + llvm::APSInt(index_val, /*isUnsigned=*/true), + context.semantics_ir().StringifyType(name_node.type_id())); + } else { + context.AddNodeAndPush( + parse_node, SemanticsNode::ArrayIndex::Make( + parse_node, type_id, name_node_id, index_node_id)); + return true; + } + } else if (context.ImplicitAsRequired( + index_node.parse_node(), index_node_id, + context.CanonicalizeType( + SemanticsNodeId::BuiltinIntegerType)) != + SemanticsNodeId::BuiltinError) { context.AddNodeAndPush( - parse_node, SemanticsNode::TupleIndex::Make( - parse_node, type_block[index_val.getZExtValue()], - name_node_id, index_node_id)); + parse_node, SemanticsNode::ArrayIndex::Make( + parse_node, type_id, name_node_id, index_node_id)); return true; } - } else if (index_node.kind() != SemanticsNodeKind::IntegerLiteral) { - CARBON_DIAGNOSTIC(NondeterministicType, Error, - "Type cannot be determined at compile time."); - context.emitter().Emit(parse_node, NondeterministicType); - } else if (name_type_node.kind() != SemanticsNodeKind::TupleType && - name_type_id != SemanticsNodeId::BuiltinError) { + } else if (name_type_node.kind() == SemanticsNodeKind::TupleType) { + if (index_node.kind() == SemanticsNodeKind::IntegerLiteral) { + const auto& index_val = context.semantics_ir().GetIntegerLiteral( + index_node.GetAsIntegerLiteral()); + auto type_block = + context.semantics_ir().GetTypeBlock(name_type_node.GetAsTupleType()); + + if (index_val.uge(static_cast(type_block.size()))) { + context.emitter().Emit( + parse_node, OutOfBoundsAccess, + llvm::APSInt(index_val, /*isUnsigned=*/true), + context.semantics_ir().StringifyType(name_node.type_id())); + } else { + context.AddNodeAndPush( + parse_node, SemanticsNode::TupleIndex::Make( + parse_node, type_block[index_val.getZExtValue()], + name_node_id, index_node_id)); + return true; + } + } else { + CARBON_DIAGNOSTIC(NondeterministicType, Error, + "Type cannot be determined at compile time."); + context.emitter().Emit(parse_node, NondeterministicType); + } + } else if (name_type_id != SemanticsNodeId::BuiltinError) { CARBON_DIAGNOSTIC(InvalidIndexExpression, Error, "Invalid index expression."); context.emitter().Emit(parse_node, InvalidIndexExpression); diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 24502fbe90ea..d69b9f62259b 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -222,6 +222,7 @@ static auto GetTypePrecedence(SemanticsNodeKind kind) -> int { return 0; case SemanticsNodeKind::AddressOf: + case SemanticsNodeKind::ArrayIndex: case SemanticsNodeKind::ArrayValue: case SemanticsNodeKind::Assign: case SemanticsNodeKind::BinaryOperatorAdd: @@ -376,6 +377,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id, break; } case SemanticsNodeKind::AddressOf: + case SemanticsNodeKind::ArrayIndex: case SemanticsNodeKind::ArrayValue: case SemanticsNodeKind::Assign: case SemanticsNodeKind::BinaryOperatorAdd: @@ -479,6 +481,12 @@ auto GetSemanticsExpressionCategory(const SemanticsIR& semantics_ir, case SemanticsNodeKind::UnaryOperatorNot: return SemanticsExpressionCategory::Value; + case SemanticsNodeKind::ArrayIndex: { + auto [base_id, index_id] = node.GetAsArrayIndex(); + node_id = base_id; + continue; + } + case SemanticsNodeKind::StructAccess: { auto [base_id, member_index] = node.GetAsStructAccess(); node_id = base_id; diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 1b93608c1d0b..f098659e7131 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -310,6 +310,10 @@ class SemanticsNode { using AddressOf = SemanticsNode::Factory; + using ArrayIndex = + Factory; + using ArrayType = SemanticsNode::Factory +// CHECK:STDOUT: } diff --git a/toolchain/semantics/testdata/index/fail_array_non_int_indexing.carbon b/toolchain/semantics/testdata/index/fail_array_non_int_indexing.carbon new file mode 100644 index 000000000000..634ac04a5783 --- /dev/null +++ b/toolchain/semantics/testdata/index/fail_array_non_int_indexing.carbon @@ -0,0 +1,91 @@ +// 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 a: [i32; 1] = (12,); +// CHECK:STDERR: fail_array_non_int_indexing.carbon:[[@LINE+3]]:16: Cannot implicitly convert from `f64` to `i32`. +// CHECK:STDERR: var b: i32 = a[2.6]; +// CHECK:STDERR: ^ +var b: i32 = a[2.6]; + +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 12, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: {mantissa: 26, exponent: -1, is_decimal: 1}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: a, +// CHECK:STDOUT: b, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: nodeFloatingPointType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: type0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, +// CHECK:STDOUT: {kind: ArrayType, arg0: node+0, arg1: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+2, type: type1}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+4, type: type0}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block2, type: type2}, +// CHECK:STDOUT: {kind: ArrayValue, arg0: node+7, type: type1}, +// CHECK:STDOUT: {kind: Assign, arg0: node+2, arg1: node+8}, +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+10, type: type0}, +// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type3}, +// CHECK:STDOUT: {kind: Assign, arg0: node+10, arg1: nodeError}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] +// CHECK:STDOUT: +// CHECK:STDOUT: package { +// CHECK:STDOUT: %.loc7_14: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_15: type = array_type %.loc7_14, i32 +// CHECK:STDOUT: %a: [i32; 1] = var +// CHECK:STDOUT: %.loc7_20.1: i32 = int_literal 12 +// CHECK:STDOUT: %.loc7_20.2: i32 = stub_reference %.loc7_20.1 +// CHECK:STDOUT: %.loc7_23.1: type = tuple_type (i32) +// CHECK:STDOUT: %.loc7_23.2: (i32,) = tuple_value (%.loc7_20.2) +// CHECK:STDOUT: %.loc7_23.3: [i32; 1] = array_value %.loc7_23.2 +// CHECK:STDOUT: assign %a, %.loc7_23.3 +// CHECK:STDOUT: %b: i32 = var +// CHECK:STDOUT: %.loc11: f64 = real_literal 26e-1 +// CHECK:STDOUT: assign %b, +// CHECK:STDOUT: } diff --git a/toolchain/semantics/testdata/index/fail_array_out_of_bound_access.carbon b/toolchain/semantics/testdata/index/fail_array_out_of_bound_access.carbon new file mode 100644 index 000000000000..6594e9db2e39 --- /dev/null +++ b/toolchain/semantics/testdata/index/fail_array_out_of_bound_access.carbon @@ -0,0 +1,90 @@ +// 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 a: [i32; 1] = (12,); +// CHECK:STDERR: fail_array_out_of_bound_access.carbon:[[@LINE+3]]:17: Index `2` is past the end of `[i32; 1]`. +// CHECK:STDERR: var b: i32 = a[2]; +// CHECK:STDERR: ^ +var b: i32 = a[2]; + +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 12, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: a, +// CHECK:STDOUT: b, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: type0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, +// CHECK:STDOUT: {kind: ArrayType, arg0: node+0, arg1: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+2, type: type1}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+4, type: type0}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block2, type: type2}, +// CHECK:STDOUT: {kind: ArrayValue, arg0: node+7, type: type1}, +// CHECK:STDOUT: {kind: Assign, arg0: node+2, arg1: node+8}, +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+10, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type0}, +// CHECK:STDOUT: {kind: Assign, arg0: node+10, arg1: nodeError}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] +// CHECK:STDOUT: +// CHECK:STDOUT: package { +// CHECK:STDOUT: %.loc7_14: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_15: type = array_type %.loc7_14, i32 +// CHECK:STDOUT: %a: [i32; 1] = var +// CHECK:STDOUT: %.loc7_20.1: i32 = int_literal 12 +// CHECK:STDOUT: %.loc7_20.2: i32 = stub_reference %.loc7_20.1 +// CHECK:STDOUT: %.loc7_23.1: type = tuple_type (i32) +// CHECK:STDOUT: %.loc7_23.2: (i32,) = tuple_value (%.loc7_20.2) +// CHECK:STDOUT: %.loc7_23.3: [i32; 1] = array_value %.loc7_23.2 +// CHECK:STDOUT: assign %a, %.loc7_23.3 +// CHECK:STDOUT: %b: i32 = var +// CHECK:STDOUT: %.loc11: i32 = int_literal 2 +// CHECK:STDOUT: assign %b, +// CHECK:STDOUT: } diff --git a/toolchain/semantics/testdata/index/fail_large_index.carbon b/toolchain/semantics/testdata/index/fail_tuple_large_index.carbon similarity index 97% rename from toolchain/semantics/testdata/index/fail_large_index.carbon rename to toolchain/semantics/testdata/index/fail_tuple_large_index.carbon index 786ce8e90e84..0e22252892ae 100644 --- a/toolchain/semantics/testdata/index/fail_large_index.carbon +++ b/toolchain/semantics/testdata/index/fail_tuple_large_index.carbon @@ -6,7 +6,7 @@ var a: (i32,) = (12,); var b: (i32,) = a; -// CHECK:STDERR: fail_large_index.carbon:[[@LINE+3]]:35: Index `295147905179352825855` is past the end of `(i32,) as type`. +// CHECK:STDERR: fail_tuple_large_index.carbon:[[@LINE+3]]:35: Index `295147905179352825855` is past the end of `(i32,) as type`. // CHECK:STDERR: var c: i32 = b[0xFFFFFFFFFFFFFFFFF]; // CHECK:STDERR: ^ var c: i32 = b[0xFFFFFFFFFFFFFFFFF]; diff --git a/toolchain/semantics/testdata/index/fail_non_int_indexing.carbon b/toolchain/semantics/testdata/index/fail_tuple_non_int_indexing.carbon similarity index 97% rename from toolchain/semantics/testdata/index/fail_non_int_indexing.carbon rename to toolchain/semantics/testdata/index/fail_tuple_non_int_indexing.carbon index e3d2fc94f554..f1b89b65d0df 100644 --- a/toolchain/semantics/testdata/index/fail_non_int_indexing.carbon +++ b/toolchain/semantics/testdata/index/fail_tuple_non_int_indexing.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE var a: (i32, i32) = (12, 6); -// CHECK:STDERR: fail_non_int_indexing.carbon:[[@LINE+3]]:19: Type cannot be determined at compile time. +// CHECK:STDERR: fail_tuple_non_int_indexing.carbon:[[@LINE+3]]:19: Type cannot be determined at compile time. // CHECK:STDERR: var b: i32 = a[2.6]; // CHECK:STDERR: ^ var b: i32 = a[2.6]; diff --git a/toolchain/semantics/testdata/index/fail_out_of_bound_access.carbon b/toolchain/semantics/testdata/index/fail_tuple_out_of_bound_access.carbon similarity index 97% rename from toolchain/semantics/testdata/index/fail_out_of_bound_access.carbon rename to toolchain/semantics/testdata/index/fail_tuple_out_of_bound_access.carbon index ef0e1eb62af7..2d7a0ebb4817 100644 --- a/toolchain/semantics/testdata/index/fail_out_of_bound_access.carbon +++ b/toolchain/semantics/testdata/index/fail_tuple_out_of_bound_access.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE var a: (i32, i32) = (12, 6); -// CHECK:STDERR: fail_out_of_bound_access.carbon:[[@LINE+3]]:17: Index `2` is past the end of `(i32, i32) as type`. +// CHECK:STDERR: fail_tuple_out_of_bound_access.carbon:[[@LINE+3]]:17: Index `2` is past the end of `(i32, i32) as type`. // CHECK:STDERR: var b: i32 = a[2]; // CHECK:STDERR: ^ var b: i32 = a[2]; diff --git a/toolchain/semantics/testdata/index/element_access.carbon b/toolchain/semantics/testdata/index/tuple_element_access.carbon similarity index 100% rename from toolchain/semantics/testdata/index/element_access.carbon rename to toolchain/semantics/testdata/index/tuple_element_access.carbon diff --git a/toolchain/semantics/testdata/index/return_value_access.carbon b/toolchain/semantics/testdata/index/tuple_return_value_access.carbon similarity index 100% rename from toolchain/semantics/testdata/index/return_value_access.carbon rename to toolchain/semantics/testdata/index/tuple_return_value_access.carbon