Browse Source

Support designator expressions on structs. (#2716)

Jon Ross-Perkins 3 years ago
parent
commit
706e611b6d

+ 2 - 0
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

+ 5 - 0
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;

+ 1 - 0
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:

+ 13 - 0
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<SemanticsNodeKind::StructMemberAccess,
+                                     SemanticsNodeId /*struct_id*/,
+                                     SemanticsMemberIndex /*ref_index*/>;
+
   using StructType = FactoryPreTyped<
       SemanticsNodeKind::StructType, SemanticsBuiltinKind::TypeType.AsInt(),
       SemanticsNodeBlockId /*ir_id*/, SemanticsNodeBlockId /*refs_id*/>;

+ 1 - 0
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)

+ 41 - 2
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<int>(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(

+ 42 - 0
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;

+ 72 - 0
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;

+ 72 - 0
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;

+ 92 - 0
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;