From 706e611b6df24636a28e1a5f93b6a31acfdbc2e4 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Thu, 30 Mar 2023 14:19:46 -0700 Subject: [PATCH] Support designator expressions on structs. (#2716) --- toolchain/diagnostics/diagnostic_kind.def | 2 + toolchain/lowering/lowering.cpp | 5 + toolchain/semantics/semantics_ir.cpp | 1 + toolchain/semantics/semantics_node.h | 13 +++ toolchain/semantics/semantics_node_kind.def | 1 + .../semantics_parse_tree_handler.cpp | 43 ++++++++- .../designators/fail_unsupported.carbon | 42 +++++++++ .../struct/fail_member_access_type.carbon | 72 +++++++++++++++ .../struct/fail_non_member_access.carbon | 72 +++++++++++++++ .../testdata/struct/member_access.carbon | 92 +++++++++++++++++++ 10 files changed, 341 insertions(+), 2 deletions(-) create mode 100644 toolchain/semantics/testdata/designators/fail_unsupported.carbon create mode 100644 toolchain/semantics/testdata/struct/fail_member_access_type.carbon create mode 100644 toolchain/semantics/testdata/struct/fail_non_member_access.carbon create mode 100644 toolchain/semantics/testdata/struct/member_access.carbon diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index a3a2c3cad9e4..d225bfed2791 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -103,6 +103,8 @@ CARBON_DIAGNOSTIC_KIND(ReturnStatementImplicitNote) CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpression) CARBON_DIAGNOSTIC_KIND(ReturnStatementTypeMismatch) CARBON_DIAGNOSTIC_KIND(ImplicitAsConversionFailure) +CARBON_DIAGNOSTIC_KIND(DesignatorExpressionUnsupported) +CARBON_DIAGNOSTIC_KIND(DesignatorExpressionNameNotFound) // ============================================================================ // Other diagnostics diff --git a/toolchain/lowering/lowering.cpp b/toolchain/lowering/lowering.cpp index 7803e379aeeb..a17c8930e382 100644 --- a/toolchain/lowering/lowering.cpp +++ b/toolchain/lowering/lowering.cpp @@ -176,6 +176,11 @@ auto Lowering::HandleStringLiteralNode(SemanticsNodeId /*node_id*/, CARBON_FATAL() << "TODO: Add support: " << node; } +auto Lowering::HandleStructMemberAccessNode(SemanticsNodeId /*node_id*/, + SemanticsNode node) -> void { + CARBON_FATAL() << "TODO: Add support: " << node; +} + auto Lowering::HandleStructTypeNode(SemanticsNodeId /*node_id*/, SemanticsNode node) -> void { CARBON_FATAL() << "TODO: Add support: " << node; diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 5649935e4cd4..cb98c33bfc97 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -159,6 +159,7 @@ auto SemanticsIR::StringifyNodeImpl(llvm::raw_ostream& out, case SemanticsNodeKind::Return: case SemanticsNodeKind::ReturnExpression: case SemanticsNodeKind::StringLiteral: + case SemanticsNodeKind::StructMemberAccess: case SemanticsNodeKind::StructValue: case SemanticsNodeKind::StubReference: case SemanticsNodeKind::VarStorage: diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 504c51b8eda6..1a54a52eedcb 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -124,6 +124,15 @@ struct SemanticsStringId : public IndexBase { } }; +// An index for member access. +struct SemanticsMemberIndex : public IndexBase { + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "member"; + IndexBase::Print(out); + } +}; + // The standard structure for SemanticsNode. This is trying to provide a minimal // amount of information for a node: // @@ -299,6 +308,10 @@ class SemanticsNode { SemanticsBuiltinKind::StringType.AsInt(), SemanticsStringId /*string_id*/>; + using StructMemberAccess = Factory; + using StructType = FactoryPreTyped< SemanticsNodeKind::StructType, SemanticsBuiltinKind::TypeType.AsInt(), SemanticsNodeBlockId /*ir_id*/, SemanticsNodeBlockId /*refs_id*/>; diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index d9cac417859f..e1ec3d7fe701 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -33,6 +33,7 @@ CARBON_SEMANTICS_NODE_KIND(RealLiteral) CARBON_SEMANTICS_NODE_KIND(Return) CARBON_SEMANTICS_NODE_KIND(ReturnExpression) CARBON_SEMANTICS_NODE_KIND(StringLiteral) +CARBON_SEMANTICS_NODE_KIND(StructMemberAccess) CARBON_SEMANTICS_NODE_KIND(StructType) CARBON_SEMANTICS_NODE_KIND(StructTypeField) CARBON_SEMANTICS_NODE_KIND(StructValue) diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index f1a7ac1a9450..7071616a2949 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/toolchain/semantics/semantics_parse_tree_handler.cpp @@ -495,8 +495,47 @@ auto SemanticsParseTreeHandler::HandleDesignatedName(ParseTree::Node parse_node) auto SemanticsParseTreeHandler::HandleDesignatorExpression( ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleDesignatorExpression"); - return false; + auto [_, name_id] = + node_stack_.PopForParseNodeAndNameId(ParseNodeKind::DesignatedName); + + auto base_id = node_stack_.PopForNodeId(); + auto base = semantics_->GetNode(base_id); + auto base_type = semantics_->GetNode(base.type_id()); + + switch (base_type.kind()) { + case SemanticsNodeKind::StructType: { + auto refs = semantics_->GetNodeBlock(base_type.GetAsStructType().second); + // TODO: Do we need to optimize this with a lookup table for O(1)? + for (int i = 0; i < static_cast(refs.size()); ++i) { + auto ref = semantics_->GetNode(refs[i]); + if (name_id == ref.GetAsStructTypeField()) { + AddNodeAndPush(parse_node, SemanticsNode::StructMemberAccess::Make( + parse_node, ref.type_id(), base_id, + SemanticsMemberIndex(i))); + return true; + } + } + CARBON_DIAGNOSTIC(DesignatorExpressionNameNotFound, Error, + "Type `{0}` does not have a member `{1}`.", std::string, + llvm::StringRef); + emitter_->Emit(parse_node, DesignatorExpressionNameNotFound, + semantics_->StringifyNode(base.type_id()), + semantics_->GetString(name_id)); + break; + } + default: { + CARBON_DIAGNOSTIC(DesignatorExpressionUnsupported, Error, + "Type `{0}` does not support designator expressions.", + std::string); + emitter_->Emit(parse_node, DesignatorExpressionUnsupported, + semantics_->StringifyNode(base.type_id())); + break; + } + } + + // Should only be reached on error. + node_stack_.Push(parse_node, SemanticsNodeId::BuiltinInvalidType); + return true; } auto SemanticsParseTreeHandler::HandleEmptyDeclaration( diff --git a/toolchain/semantics/testdata/designators/fail_unsupported.carbon b/toolchain/semantics/testdata/designators/fail_unsupported.carbon new file mode 100644 index 000000000000..aebc7fa910c1 --- /dev/null +++ b/toolchain/semantics/testdata/designators/fail_unsupported.carbon @@ -0,0 +1,42 @@ +// 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 +// RUN: %{not} %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: calls: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: callables: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: x, +// CHECK:STDOUT: y, +// CHECK:STDOUT: b, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: VarStorage, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+0, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: VarStorage, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+2, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: Assign, arg0: node+2, arg1: nodeInvalidType, type: nodeInvalidType}, +// 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: ], +// CHECK:STDOUT: ] + +var x: i32; +// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/designators/fail_unsupported.carbon:[[@LINE+1]]:15: Type `i32` does not support designator expressions. +var y: i32 = x.b; diff --git a/toolchain/semantics/testdata/struct/fail_member_access_type.carbon b/toolchain/semantics/testdata/struct/fail_member_access_type.carbon new file mode 100644 index 000000000000..73d15c3ca9bf --- /dev/null +++ b/toolchain/semantics/testdata/struct/fail_member_access_type.carbon @@ -0,0 +1,72 @@ +// 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 +// RUN: %{not} %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: calls: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: callables: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: {mantissa: 40, exponent: -1, is_decimal: 1}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: a, +// CHECK:STDOUT: x, +// CHECK:STDOUT: y, +// CHECK:STDOUT: b, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: StructTypeField, arg0: str0, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: StructType, arg0: block1, arg1: block2, type: nodeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: node+1}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+2, type: node+1}, +// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str0, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+4, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: StructType, arg0: block5, arg1: block5, type: nodeTypeType}, +// CHECK:STDOUT: {kind: StructValue, arg0: block4, arg1: block6, type: node+7}, +// CHECK:STDOUT: {kind: Assign, arg0: node+2, arg1: node+8, type: node+7}, +// CHECK:STDOUT: {kind: VarStorage, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+10, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: Assign, arg0: node+10, arg1: nodeInvalidType, type: nodeInvalidType}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// 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: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +var x: {.a: f64} = {.a = 4.0}; +// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_member_access_type.carbon:[[@LINE+1]]:15: Type `{.a: f64}` does not have a member `b`. +var y: i32 = x.b; diff --git a/toolchain/semantics/testdata/struct/fail_non_member_access.carbon b/toolchain/semantics/testdata/struct/fail_non_member_access.carbon new file mode 100644 index 000000000000..3c24a00b8915 --- /dev/null +++ b/toolchain/semantics/testdata/struct/fail_non_member_access.carbon @@ -0,0 +1,72 @@ +// 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 +// RUN: %{not} %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: calls: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: callables: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 4, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: a, +// CHECK:STDOUT: x, +// CHECK:STDOUT: y, +// CHECK:STDOUT: b, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: StructTypeField, arg0: str0, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StructType, arg0: block1, arg1: block2, type: nodeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: node+1}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+2, type: node+1}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str0, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+4, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StructType, arg0: block5, arg1: block5, type: nodeTypeType}, +// CHECK:STDOUT: {kind: StructValue, arg0: block4, arg1: block6, type: node+7}, +// CHECK:STDOUT: {kind: Assign, arg0: node+2, arg1: node+8, type: node+7}, +// CHECK:STDOUT: {kind: VarStorage, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+10, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: Assign, arg0: node+10, arg1: nodeInvalidType, type: nodeInvalidType}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// 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: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +var x: {.a: i32} = {.a = 4}; +// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_non_member_access.carbon:[[@LINE+1]]:15: Type `{.a: i32}` does not have a member `b`. +var y: i32 = x.b; diff --git a/toolchain/semantics/testdata/struct/member_access.carbon b/toolchain/semantics/testdata/struct/member_access.carbon new file mode 100644 index 000000000000..19f8f238994b --- /dev/null +++ b/toolchain/semantics/testdata/struct/member_access.carbon @@ -0,0 +1,92 @@ +// 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 +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: calls: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: callables: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 1, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: {mantissa: 0, exponent: -1, is_decimal: 1}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: a, +// CHECK:STDOUT: b, +// CHECK:STDOUT: x, +// CHECK:STDOUT: y, +// CHECK:STDOUT: z, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: StructTypeField, arg0: str0, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str1, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StructType, arg0: block1, arg1: block2, type: nodeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: node+2}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+3, type: node+2}, +// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str0, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+5, type: nodeFloatingPointType}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str1, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+8, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StructType, arg0: block5, arg1: block5, type: nodeTypeType}, +// CHECK:STDOUT: {kind: StructValue, arg0: block4, arg1: block6, type: node+11}, +// CHECK:STDOUT: {kind: Assign, arg0: node+3, arg1: node+12, type: node+11}, +// CHECK:STDOUT: {kind: VarStorage, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: BindName, arg0: str3, arg1: node+14, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: StructMemberAccess, arg0: node+3, arg1: member1, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: Assign, arg0: node+14, arg1: node+16, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: VarStorage, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: BindName, arg0: str4, arg1: node+18, type: nodeIntegerType}, +// CHECK:STDOUT: {kind: Assign, arg0: node+18, arg1: node+14, type: nodeIntegerType}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +var x: {.a: f64, .b: i32} = {.a = 0.0, .b = 1}; +var y: i32 = x.b; +var z: i32 = y;