Explorar o código

Integrates tuple to semantic analysis (#2992)

Handles tuples (including nested tuples) in the semantic phase of the
tool chain. Does not handle tuple element access yet.

---------

Co-authored-by: Farzana Ahmed Siddique <fasiddique@google.com>
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Farzana Ahmed Siddique %!s(int64=2) %!d(string=hai) anos
pai
achega
0594a1aac7
Modificáronse 30 ficheiros con 1139 adicións e 37 borrados
  1. 0 3
      toolchain/common/index_base.h
  2. 12 0
      toolchain/lowering/lowering_handle.cpp
  3. 3 3
      toolchain/parser/parse_node_kind.def
  4. 1 0
      toolchain/semantics/BUILD
  5. 64 7
      toolchain/semantics/semantics_context.cpp
  6. 27 9
      toolchain/semantics/semantics_context.h
  7. 0 1
      toolchain/semantics/semantics_handle_codeblock.cpp
  8. 30 5
      toolchain/semantics/semantics_handle_paren.cpp
  9. 0 1
      toolchain/semantics/semantics_handle_variable.cpp
  10. 25 0
      toolchain/semantics/semantics_ir.cpp
  11. 25 4
      toolchain/semantics/semantics_ir.h
  12. 0 2
      toolchain/semantics/semantics_node.cpp
  13. 15 1
      toolchain/semantics/semantics_node.h
  14. 2 0
      toolchain/semantics/semantics_node_kind.def
  15. 2 0
      toolchain/semantics/semantics_node_stack.h
  16. 75 0
      toolchain/semantics/testdata/function/call/empty_tuple.carbon
  17. 27 0
      toolchain/semantics/testdata/function/call/return_implicit.carbon
  18. 13 1
      toolchain/semantics/testdata/return/fail_missing_return_empty_tuple.carbon
  19. 70 0
      toolchain/semantics/testdata/return/tuple.carbon
  20. 112 0
      toolchain/semantics/testdata/struct/tuple_as_element.carbon
  21. 50 0
      toolchain/semantics/testdata/tuples/empty.carbon
  22. 53 0
      toolchain/semantics/testdata/tuples/fail_assign_empty.carbon
  23. 43 0
      toolchain/semantics/testdata/tuples/fail_assign_to_empty.carbon
  24. 71 0
      toolchain/semantics/testdata/tuples/fail_element_type_mismatch.carbon
  25. 64 0
      toolchain/semantics/testdata/tuples/fail_too_few_element.carbon
  26. 55 0
      toolchain/semantics/testdata/tuples/fail_type_assign.carbon
  27. 49 0
      toolchain/semantics/testdata/tuples/fail_value_as_type.carbon
  28. 95 0
      toolchain/semantics/testdata/tuples/nested_tuple.carbon
  29. 72 0
      toolchain/semantics/testdata/tuples/one_element.carbon
  30. 84 0
      toolchain/semantics/testdata/tuples/two_elements.carbon

+ 0 - 3
toolchain/common/index_base.h

@@ -8,9 +8,6 @@
 #include <type_traits>
 
 #include "common/ostream.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/iterator.h"
-#include "llvm/Support/Format.h"
 
 namespace Carbon {
 

+ 12 - 0
toolchain/lowering/lowering_handle.cpp

@@ -215,6 +215,18 @@ auto LoweringHandleStructType(LoweringFunctionContext& /*context*/,
   // No action to take.
 }
 
+auto LoweringHandleTupleValue(LoweringFunctionContext& /*context*/,
+                              SemanticsNodeId /*node_id*/, SemanticsNode node)
+    -> void {
+  CARBON_FATAL() << "TODO: Add support: " << node;
+}
+
+auto LoweringHandleTupleType(LoweringFunctionContext& /*context*/,
+                             SemanticsNodeId /*node_id*/,
+                             SemanticsNode /*node*/) -> void {
+  // No action to take.
+}
+
 auto LoweringHandleStructTypeField(LoweringFunctionContext& /*context*/,
                                    SemanticsNodeId /*node_id*/,
                                    SemanticsNode /*node*/) -> void {

+ 3 - 3
toolchain/parser/parse_node_kind.def

@@ -208,12 +208,12 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(WhileConditionStart, 0)
 CARBON_PARSE_NODE_KIND_BRACKET(WhileCondition, WhileConditionStart)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(WhileStatement, 2)
 
-// Parenthesized expressions:
+// Parenthesized expressions, such as `(2)`:
 //   ParenExpressionOrTupleLiteralStart
 //   _external_: expression
 // ParenExpression
-
-// Tuples:
+//
+// Tuples, such as `(1, 2)`:
 //   ParenExpressionOrTupleLiteralStart
 //   _external_: expression
 //   TupleLiteralComma

+ 1 - 0
toolchain/semantics/BUILD

@@ -74,6 +74,7 @@ cc_library(
     deps = [
         ":semantics_builtin_kind",
         ":semantics_node",
+        ":semantics_node_kind",
         ":semantics_node_stack",
         "//common:check",
         "//common:ostream",

+ 64 - 7
toolchain/semantics/semantics_context.cpp

@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "common/check.h"
 #include "common/vlog.h"
 #include "toolchain/diagnostics/diagnostic_kind.h"
 #include "toolchain/lexer/tokenized_buffer.h"
@@ -14,6 +15,7 @@
 #include "toolchain/semantics/semantics_ir.h"
 #include "toolchain/semantics/semantics_node.h"
 #include "toolchain/semantics/semantics_node_block_stack.h"
+#include "toolchain/semantics/semantics_node_kind.h"
 
 namespace Carbon {
 
@@ -372,11 +374,29 @@ auto SemanticsContext::ImplicitAsImpl(SemanticsNodeId value_id,
     // Type doesn't need to change.
     return ImplicitAsKind::Identical;
   }
-
   if (as_type_id == SemanticsTypeId::TypeType) {
-    // TODO: When converting `()` to a type, the result is `() as Type`.
-    // Right now there is no tuple value support.
-
+    if (value.kind() == SemanticsNodeKind::TupleValue) {
+      auto tuple_block_id = value.GetAsTupleValue();
+      llvm::SmallVector<SemanticsTypeId> type_ids;
+      // If it is empty tuple type, we don't fetch anything.
+      if (tuple_block_id != SemanticsNodeBlockId::Empty) {
+        const auto& tuple_block = semantics_ir_->GetNodeBlock(tuple_block_id);
+        for (auto tuple_node_id : tuple_block) {
+          // TODO: Eventually ExpressionAsType will insert implicit cast
+          // instructions. When that happens, this will need to verify the full
+          // tuple conversion will work before calling it.
+          type_ids.push_back(
+              ExpressionAsType(value.parse_node(), tuple_node_id));
+        }
+      }
+      auto tuple_type_id =
+          CanonicalizeTupleType(value.parse_node(), std::move(type_ids));
+      if (output_value_id != nullptr) {
+        *output_value_id =
+            semantics_ir_->GetTypeAllowBuiltinTypes(tuple_type_id);
+      }
+      return ImplicitAsKind::Compatible;
+    }
     // When converting `{}` to a type, the result is `{} as Type`.
     if (value.kind() == SemanticsNodeKind::StructValue &&
         value.GetAsStructValue() == SemanticsNodeBlockId::Empty) {
@@ -439,6 +459,14 @@ auto SemanticsContext::ParamOrArgSave(bool for_args) -> void {
 
 auto SemanticsContext::CanonicalizeType(SemanticsNodeId node_id)
     -> SemanticsTypeId {
+  auto node = semantics_ir_->GetNode(node_id);
+  if (node.kind() == SemanticsNodeKind::StubReference) {
+    node_id = node.GetAsStubReference();
+    CARBON_CHECK(semantics_ir_->GetNode(node_id).kind() !=
+                 SemanticsNodeKind::StubReference)
+        << "Stub reference should not point to another stub reference";
+  }
+
   auto it = canonical_types_.find(node_id);
   if (it != canonical_types_.end()) {
     return it->second;
@@ -474,13 +502,42 @@ auto SemanticsContext::CanonicalizeStructType(ParseTree::Node parse_node,
       parse_node, SemanticsTypeId::TypeType, refs_id));
   auto type_id = semantics_ir_->AddType(node_id);
   CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second);
-  canonical_struct_types_nodes_.push_back(
-      std::make_unique<StructTypeNode>(canonical_id, type_id));
-  canonical_struct_types_.InsertNode(canonical_struct_types_nodes_.back().get(),
+  canonical_types_nodes_.push_back(
+      std::make_unique<TypeNode>(canonical_id, type_id));
+  canonical_struct_types_.InsertNode(canonical_types_nodes_.back().get(),
                                      insert_pos);
   return type_id;
 }
 
+auto SemanticsContext::CanonicalizeTupleType(
+    ParseTree::Node parse_node, llvm::SmallVector<SemanticsTypeId>&& type_ids)
+    -> SemanticsTypeId {
+  llvm::FoldingSetNodeID canonical_id;
+  for (const auto& type_id : type_ids) {
+    canonical_id.AddInteger(type_id.index);
+  }
+  // If a tuple with matching fields was already created, reuse it.
+  void* insert_pos;
+  auto* node =
+      canonical_tuple_types_.FindNodeOrInsertPos(canonical_id, insert_pos);
+  if (node != nullptr) {
+    return node->type_id();
+  }
+  // The tuple type doesn't already exist, so create and store it as canonical.
+  auto type_block_id = semantics_ir_->AddTypeBlock();
+  auto& type_block = semantics_ir_->GetTypeBlock(type_block_id);
+  type_block = std::move(type_ids);
+  auto node_id = AddNode(SemanticsNode::TupleType::Make(
+      parse_node, SemanticsTypeId::TypeType, type_block_id));
+  auto type_id = semantics_ir_->AddType(node_id);
+  CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second);
+  canonical_types_nodes_.push_back(
+      std::make_unique<TypeNode>(canonical_id, type_id));
+  canonical_tuple_types_.InsertNode(canonical_types_nodes_.back().get(),
+                                    insert_pos);
+  return type_id;
+}
+
 auto SemanticsContext::PrintForStackDump(llvm::raw_ostream& output) const
     -> void {
   node_stack_.PrintForStackDump(output);

+ 27 - 9
toolchain/semantics/semantics_context.h

@@ -147,10 +147,24 @@ class SemanticsContext {
   auto CanonicalizeStructType(ParseTree::Node parse_node,
                               SemanticsNodeBlockId refs_id) -> SemanticsTypeId;
 
+  // Handles canonicalization of tuple types. This may create a new tuple type
+  // if the `type_ids` doesn't match an existing tuple type.
+  auto CanonicalizeTupleType(ParseTree::Node parse_node,
+                             llvm::SmallVector<SemanticsTypeId>&& type_ids)
+      -> SemanticsTypeId;
+
   // Converts an expression for use as a type.
   // TODO: This should eventually return a type ID.
   auto ExpressionAsType(ParseTree::Node parse_node, SemanticsNodeId value_id)
       -> SemanticsTypeId {
+    auto node = semantics_ir_->GetNode(value_id);
+    if (node.kind() == SemanticsNodeKind::StubReference) {
+      value_id = node.GetAsStubReference();
+      CARBON_CHECK(semantics_ir_->GetNode(value_id).kind() !=
+                   SemanticsNodeKind::StubReference)
+          << "Stub reference should not point to another stub reference";
+    }
+
     return CanonicalizeType(
         ImplicitAsRequired(parse_node, value_id, SemanticsTypeId::TypeType));
   }
@@ -215,11 +229,11 @@ class SemanticsContext {
     Compatible,
   };
 
-  // A FoldingSet node for a struct type.
-  class StructTypeNode : public llvm::FastFoldingSetNode {
+  // A FoldingSet node for a struct or tuple type.
+  class TypeNode : public llvm::FastFoldingSetNode {
    public:
-    explicit StructTypeNode(const llvm::FoldingSetNodeID& node_id,
-                            SemanticsTypeId type_id)
+    explicit TypeNode(const llvm::FoldingSetNodeID& node_id,
+                      SemanticsTypeId type_id)
         : llvm::FastFoldingSetNode(node_id), type_id_(type_id) {}
 
     auto type_id() -> SemanticsTypeId { return type_id_; }
@@ -308,12 +322,16 @@ class SemanticsContext {
 
   // Tracks struct type literals which have been defined, so that they aren't
   // repeatedly redefined.
-  llvm::FoldingSet<StructTypeNode> canonical_struct_types_;
+  llvm::FoldingSet<TypeNode> canonical_struct_types_;
+
+  // Tracks tuple type literals which have been defined, so that they aren't
+  // repeatedly redefined.
+  llvm::FoldingSet<TypeNode> canonical_tuple_types_;
 
-  // Storage for the nodes in canonical_struct_types_. This stores in pointers
-  // so that canonical_struct_types_ can have stable pointers.
-  llvm::SmallVector<std::unique_ptr<StructTypeNode>>
-      canonical_struct_types_nodes_;
+  // Storage for the nodes in canonical_struct_types_ and
+  // canonical_tuple_types_. This stores in pointers so that FoldingSet can have
+  // stable pointers.
+  llvm::SmallVector<std::unique_ptr<TypeNode>> canonical_types_nodes_;
 };
 
 // Parse node handlers. Returns false for unrecoverable errors.

+ 0 - 1
toolchain/semantics/semantics_handle_codeblock.cpp

@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/semantics/semantics_context.h"
-#include "toolchain/semantics/semantics_node.h"
 
 namespace Carbon {
 

+ 30 - 5
toolchain/semantics/semantics_handle_paren.cpp

@@ -2,6 +2,8 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
+#include <utility>
+
 #include "toolchain/semantics/semantics_context.h"
 
 namespace Carbon {
@@ -9,6 +11,10 @@ namespace Carbon {
 auto SemanticsHandleParenExpression(SemanticsContext& context,
                                     ParseTree::Node parse_node) -> bool {
   auto value_id = context.node_stack().PopExpression();
+  // ParamOrArgStart was called for tuple handling; clean up the ParamOrArg
+  // support for non-tuple cases.
+  context.ParamOrArgEnd(
+      /*for_args=*/true, ParseNodeKind::ParenExpressionOrTupleLiteralStart);
   context.node_stack()
       .PopAndDiscardSoloParseNode<
           ParseNodeKind::ParenExpressionOrTupleLiteralStart>();
@@ -19,17 +25,36 @@ auto SemanticsHandleParenExpression(SemanticsContext& context,
 auto SemanticsHandleParenExpressionOrTupleLiteralStart(
     SemanticsContext& context, ParseTree::Node parse_node) -> bool {
   context.node_stack().Push(parse_node);
+  context.ParamOrArgStart();
+  return true;
+}
+
+auto SemanticsHandleTupleLiteralComma(SemanticsContext& context,
+                                      ParseTree::Node /*parse_node*/) -> bool {
+  context.ParamOrArgComma(/*for_args=*/true);
   return true;
 }
 
 auto SemanticsHandleTupleLiteral(SemanticsContext& context,
                                  ParseTree::Node parse_node) -> bool {
-  return context.TODO(parse_node, "HandleTupleLiteral");
-}
+  auto refs_id = context.ParamOrArgEnd(
+      /*for_args=*/true, ParseNodeKind::ParenExpressionOrTupleLiteralStart);
 
-auto SemanticsHandleTupleLiteralComma(SemanticsContext& context,
-                                      ParseTree::Node parse_node) -> bool {
-  return context.TODO(parse_node, "HandleTupleLiteralComma");
+  context.node_stack()
+      .PopAndDiscardSoloParseNode<
+          ParseNodeKind::ParenExpressionOrTupleLiteralStart>();
+  const auto& node_block = context.semantics_ir().GetNodeBlock(refs_id);
+  llvm::SmallVector<SemanticsTypeId> type_ids;
+  type_ids.reserve(node_block.size());
+  for (auto node : node_block) {
+    type_ids.push_back(context.semantics_ir().GetNode(node).type_id());
+  }
+  auto type_id = context.CanonicalizeTupleType(parse_node, std::move(type_ids));
+
+  auto value_id = context.AddNode(
+      SemanticsNode::TupleValue::Make(parse_node, type_id, refs_id));
+  context.node_stack().Push(parse_node, value_id);
+  return true;
 }
 
 }  // namespace Carbon

+ 0 - 1
toolchain/semantics/semantics_handle_variable.cpp

@@ -26,7 +26,6 @@ auto SemanticsHandleVariableDeclaration(SemanticsContext& context,
   auto binding = context.semantics_ir().GetNode(binding_id);
   auto [name_id, storage_id] = binding.GetAsBindName();
   context.AddNameToLookup(binding.parse_node(), name_id, storage_id);
-
   // If there was an initializer, assign it to storage.
   if (has_init) {
     auto cast_value_id = context.ImplicitAsRequired(

+ 25 - 0
toolchain/semantics/semantics_ir.cpp

@@ -10,6 +10,7 @@
 #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"
 
 namespace Carbon {
 
@@ -241,6 +242,29 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string {
         steps.push_back({.node_id = GetTypeAllowBuiltinTypes(node.type_id())});
         break;
       }
+      case SemanticsNodeKind::TupleType: {
+        auto refs = GetTypeBlock(node.GetAsTupleType());
+        if (refs.empty()) {
+          out << "() as type";
+          break;
+        } else if (step.index == 0) {
+          out << "(";
+        } else if (step.index < static_cast<int>(refs.size())) {
+          out << ", ";
+        } else {
+          // A tuple of one element has a comma to disambiguate from an
+          // expression.
+          if (step.index == 1) {
+            out << ",";
+          }
+          out << ") as type";
+          break;
+        }
+        steps.push_back({.node_id = step.node_id, .index = step.index + 1});
+        steps.push_back(
+            {.node_id = GetTypeAllowBuiltinTypes(refs[step.index])});
+        break;
+      }
       case SemanticsNodeKind::Assign:
       case SemanticsNodeKind::BinaryOperatorAdd:
       case SemanticsNodeKind::BindName:
@@ -262,6 +286,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string {
       case SemanticsNodeKind::StructMemberAccess:
       case SemanticsNodeKind::StructValue:
       case SemanticsNodeKind::StubReference:
+      case SemanticsNodeKind::TupleValue:
       case SemanticsNodeKind::UnaryOperatorNot:
       case SemanticsNodeKind::VarStorage:
         // We don't need to handle stringification for nodes that don't show up

+ 25 - 4
toolchain/semantics/semantics_ir.h

@@ -90,8 +90,7 @@ class SemanticsIR {
   }
 
   // Returns the requested callable.
-  auto GetFunction(SemanticsFunctionId function_id) const
-      -> const SemanticsFunction& {
+  auto GetFunction(SemanticsFunctionId function_id) const -> SemanticsFunction {
     return functions_[function_id.index];
   }
 
@@ -225,11 +224,11 @@ class SemanticsIR {
     return type_id;
   }
 
-  // Gets the node ID for a type. This doesn't handle TypeType or Error in
+  // Gets the node ID for a type. This doesn't handle TypeType or InvalidType in
   // order to avoid a check; callers that need that should use
   // GetTypeAllowBuiltinTypes.
   auto GetType(SemanticsTypeId type_id) const -> SemanticsNodeId {
-    // Double-check it's not called with TypeType or Error.
+    // Double-check it's not called with TypeType or InvalidType.
     CARBON_CHECK(type_id.index >= 0)
         << "Invalid argument for GetType: " << type_id;
     return types_[type_id.index];
@@ -246,6 +245,25 @@ class SemanticsIR {
     }
   }
 
+  // Adds an empty type block, returning an ID to reference it.
+  auto AddTypeBlock() -> SemanticsTypeBlockId {
+    SemanticsTypeBlockId id(type_blocks_.size());
+    type_blocks_.push_back({});
+    return id;
+  }
+
+  // Returns the requested type block.
+  auto GetTypeBlock(SemanticsTypeBlockId block_id) const
+      -> const llvm::SmallVector<SemanticsTypeId>& {
+    return type_blocks_[block_id.index];
+  }
+
+  // Returns the requested type block.
+  auto GetTypeBlock(SemanticsTypeBlockId block_id)
+      -> llvm::SmallVector<SemanticsTypeId>& {
+    return type_blocks_[block_id.index];
+  }
+
   // Produces a string version of a type.
   auto StringifyType(SemanticsTypeId type_id) -> std::string;
 
@@ -308,6 +326,9 @@ class SemanticsIR {
   // by lowering.
   llvm::SmallVector<SemanticsNodeId> types_;
 
+  // Storage for blocks within the IR. These reference entries in types_.
+  llvm::SmallVector<llvm::SmallVector<SemanticsTypeId>> type_blocks_;
+
   // The type of the empty tuple. This is special-cased due to its use in
   // implicit function returns.
   SemanticsTypeId empty_tuple_type_id_ = SemanticsTypeId::Invalid;

+ 0 - 2
toolchain/semantics/semantics_node.cpp

@@ -4,8 +4,6 @@
 
 #include "toolchain/semantics/semantics_node.h"
 
-#include "toolchain/semantics/semantics_builtin_kind.h"
-
 namespace Carbon {
 
 static auto PrintArgs(llvm::raw_ostream& /*out*/,

+ 15 - 1
toolchain/semantics/semantics_node.h

@@ -194,6 +194,15 @@ constexpr SemanticsTypeId SemanticsTypeId::Error =
 constexpr SemanticsTypeId SemanticsTypeId::Invalid =
     SemanticsTypeId(SemanticsTypeId::InvalidIndex);
 
+// The ID of a type block.
+struct SemanticsTypeBlockId : public IndexBase {
+  using IndexBase::IndexBase;
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "typeBlock";
+    IndexBase::Print(out);
+  }
+};
+
 // An index for member access.
 struct SemanticsMemberIndex : public IndexBase {
   using IndexBase::IndexBase;
@@ -400,11 +409,16 @@ class SemanticsNode {
   using StubReference =
       Factory<SemanticsNodeKind::StubReference, SemanticsNodeId /*node_id*/>;
 
+  using TupleType =
+      Factory<SemanticsNodeKind::TupleType, SemanticsTypeBlockId /*refs_id*/>;
+
+  using TupleValue =
+      Factory<SemanticsNodeKind::TupleValue, SemanticsNodeBlockId /*refs_id*/>;
+
   using UnaryOperatorNot = Factory<SemanticsNodeKind::UnaryOperatorNot,
                                    SemanticsNodeId /*operand_id*/>;
 
   using VarStorage = Factory<SemanticsNodeKind::VarStorage>;
-
   SemanticsNode()
       : SemanticsNode(ParseTree::Node::Invalid, SemanticsNodeKind::Invalid,
                       SemanticsTypeId::Invalid) {}

+ 2 - 0
toolchain/semantics/semantics_node_kind.def

@@ -49,6 +49,8 @@ CARBON_SEMANTICS_NODE_KIND(StructType)
 CARBON_SEMANTICS_NODE_KIND(StructTypeField)
 CARBON_SEMANTICS_NODE_KIND(StructValue)
 CARBON_SEMANTICS_NODE_KIND(StubReference)
+CARBON_SEMANTICS_NODE_KIND(TupleType)
+CARBON_SEMANTICS_NODE_KIND(TupleValue)
 CARBON_SEMANTICS_NODE_KIND(UnaryOperatorNot)
 CARBON_SEMANTICS_NODE_KIND(VarStorage)
 

+ 2 - 0
toolchain/semantics/semantics_node_stack.h

@@ -256,6 +256,8 @@ class SemanticsNodeStack {
       case Carbon::ParseNodeKind::StructLiteral:
       case Carbon::ParseNodeKind::StructTypeLiteral:
         return IdKind::SemanticsNodeId;
+      case Carbon::ParseNodeKind::TupleLiteral:
+        return IdKind::SemanticsNodeId;
       case Carbon::ParseNodeKind::IfExpressionThen:
       case Carbon::ParseNodeKind::IfStatementElse:
       case Carbon::ParseNodeKind::ParameterList:

+ 75 - 0
toolchain/semantics/testdata/function/call/empty_tuple.carbon

@@ -0,0 +1,75 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT:   {name: str0, param_refs: block2, return_type: type0, body: {block4}}},
+// CHECK:STDOUT:   {name: str2, param_refs: block0, body: {block5}}},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   Echo,
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   Main,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+0,
+// CHECK:STDOUT:   nodeEmptyTupleType,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: VarStorage, type: type0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+2, type: type0},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: function0},
+// CHECK:STDOUT:   {kind: ReturnExpression, arg0: node+2, type: type0},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: function1},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+8, type: type0},
+// CHECK:STDOUT:   {kind: Call, arg0: block6, arg1: function0, type: type0},
+// CHECK:STDOUT:   {kind: Return},
+// 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+3,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:     node+11,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+fn Echo(a: ()) -> () {
+  return a;
+}
+
+fn Main() {
+  Echo(());
+}

+ 27 - 0
toolchain/semantics/testdata/function/call/return_implicit.carbon

@@ -0,0 +1,27 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// TODO test if builtin implicit empty tuple type is treated as being the same type as an explicitly-created empty tuple type.
+// Currently crashes due to call expression.
+// fn MakeImplicitEmptyTuple() {}
+// var v: () = MakeImplicitEmptyTuple();

+ 13 - 1
toolchain/semantics/testdata/return/fail_missing_return_empty_tuple.carbon

@@ -5,6 +5,7 @@
 // AUTOUPDATE
 // CHECK:STDOUT: cross_reference_irs_size: 1
 // CHECK:STDOUT: functions: [
+// CHECK:STDOUT:   {name: str0, param_refs: block0, return_type: type0, body: {block2}}},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: integer_literals: [
 // CHECK:STDOUT: ]
@@ -14,14 +15,25 @@
 // CHECK:STDOUT:   F,
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+0,
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: function0},
 // 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 
-// CHECK:STDERR: fail_missing_return_empty_tuple.carbon:[[@LINE+1]]:12: Semantics TODO: HandleTupleLiteral
 fn F() -> () {
+// CHECK:STDERR: fail_missing_return_empty_tuple.carbon:[[@LINE+1]]:1: Missing `return` at end of function with declared return type.
 }

+ 70 - 0
toolchain/semantics/testdata/return/tuple.carbon

@@ -0,0 +1,70 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT:   {name: str0, param_refs: block0, return_type: type2, body: {block3}}},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   15,
+// CHECK:STDOUT:   35,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   Main,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+2,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+4,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: function0},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+6, type: type1},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: type1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+8, type: type1},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block4, type: type2},
+// CHECK:STDOUT:   {kind: ReturnExpression, arg0: node+10, type: type2},
+// 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+
+fn Main() -> (i32, i32) {
+  return (15, 35);
+}

+ 112 - 0
toolchain/semantics/testdata/struct/tuple_as_element.carbon

@@ -0,0 +1,112 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   1,
+// CHECK:STDOUT:   2,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   b,
+// CHECK:STDOUT:   y,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+2,
+// CHECK:STDOUT:   node+4,
+// CHECK:STDOUT:   node+6,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str1, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block3, type: type1},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str2, type: type2},
+// CHECK:STDOUT:   {kind: StructType, arg0: block2, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type3},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+7, type: type3},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type0},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str1, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+9, type: type0},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+12, type: type0},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block6, type: type2},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str2, type: type2},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+14, type: type2},
+// CHECK:STDOUT:   {kind: StructValue, arg0: block5, type: type3},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+7, arg1: node+17, type: type3},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str1, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block8, type: type1},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str2, type: type2},
+// CHECK:STDOUT:   {kind: VarStorage, type: type3},
+// CHECK:STDOUT:   {kind: BindName, arg0: str3, arg1: node+23, type: type3},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+23, arg1: node+7, type: type3},
+// 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+11,
+// CHECK:STDOUT:     node+12,
+// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:     node+14,
+// CHECK:STDOUT:     node+16,
+// CHECK:STDOUT:     node+17,
+// CHECK:STDOUT:     node+18,
+// CHECK:STDOUT:     node+19,
+// CHECK:STDOUT:     node+20,
+// CHECK:STDOUT:     node+21,
+// CHECK:STDOUT:     node+22,
+// CHECK:STDOUT:     node+23,
+// CHECK:STDOUT:     node+24,
+// CHECK:STDOUT:     node+25,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:     node+15,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+11,
+// CHECK:STDOUT:     node+16,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+19,
+// CHECK:STDOUT:     node+22,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+20,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+var x: {.a: i32, .b: (i32,)} = {.a = 1, .b = (2,)};
+var y: {.a: i32, .b: (i32,)} = x;

+ 50 - 0
toolchain/semantics/testdata/tuples/empty.carbon

@@ -0,0 +1,50 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// 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: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+0,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: VarStorage, type: type0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+2, type: type0},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+2, arg1: node+4, type: type0},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: VarStorage, type: type0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+7, type: type0},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+7, arg1: node+2, type: type0},
+// 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:   ],
+// CHECK:STDOUT: ]
+
+var x: () = ();
+var y: () = x;

+ 53 - 0
toolchain/semantics/testdata/tuples/fail_assign_empty.carbon

@@ -0,0 +1,53 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+1,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+3,
+// CHECK:STDOUT:   node+6,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+4, type: type2},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock2, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type3},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+4, arg1: nodeError, type: typeError},
+// 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+1]]:19: Cannot implicitly convert from `() as type` to `(i32,) as type`.
+var x: (i32,) = ();

+ 43 - 0
toolchain/semantics/testdata/tuples/fail_assign_to_empty.carbon

@@ -0,0 +1,43 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   66,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+0,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block0, type: type0},
+// CHECK:STDOUT:   {kind: VarStorage, type: type0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+2, type: type0},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type1},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+2, arg1: nodeError, type: typeError},
+// 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:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+1]]:17: Cannot implicitly convert from `i32` to `() as type`.
+var x: () = (66);

+ 71 - 0
toolchain/semantics/testdata/tuples/fail_element_type_mismatch.carbon

@@ -0,0 +1,71 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   2,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT:   {mantissa: 6589, exponent: -2, is_decimal: 1},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+2,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+4,
+// CHECK:STDOUT:   nodeFloatingPointType,
+// CHECK:STDOUT:   node+11,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+5, type: type2},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+7, type: type1},
+// CHECK:STDOUT:   {kind: RealLiteral, arg0: real0, type: type3},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+9, type: type3},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock2, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block3, type: type4},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+5, arg1: nodeError, type: typeError},
+// 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+0,
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: fail_element_type_mismatch.carbon:[[@LINE+1]]:31: Cannot implicitly convert from `(i32, f64) as type` to `(i32, i32) as type`.
+var x: (i32, i32) = (2, 65.89);

+ 64 - 0
toolchain/semantics/testdata/tuples/fail_too_few_element.carbon

@@ -0,0 +1,64 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   2,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+2,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+4,
+// CHECK:STDOUT:   node+9,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+5, type: type2},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+7, type: type1},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock2, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block3, type: type3},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+5, arg1: nodeError, type: typeError},
+// 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: fail_too_few_element.carbon:[[@LINE+1]]:26: Cannot implicitly convert from `(i32,) as type` to `(i32, i32) as type`.
+var x: (i32, i32) = (2, );

+ 55 - 0
toolchain/semantics/testdata/tuples/fail_type_assign.carbon

@@ -0,0 +1,55 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+1,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+3,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+4, type: type2},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block3, type: type0},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+4, arg1: nodeError, type: typeError},
+// 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: fail_type_assign.carbon:[[@LINE+1]]:25: Cannot implicitly convert from `(Type,) as type` to `(i32,) as type`.
+var x: (i32, ) = (i32, );

+ 49 - 0
toolchain/semantics/testdata/tuples/fail_value_as_type.carbon

@@ -0,0 +1,49 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   1,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+2,
+// CHECK:STDOUT:   node+4,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+0, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type1},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+5, type: type2},
+// 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:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: fail_value_as_type.carbon:[[@LINE+1]]:12: Cannot implicitly convert from `i32` to `Type`.
+var x: (1, );

+ 95 - 0
toolchain/semantics/testdata/tuples/nested_tuple.carbon

@@ -0,0 +1,95 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   12,
+// CHECK:STDOUT:   76,
+// CHECK:STDOUT:   6,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+2,
+// CHECK:STDOUT:   node+6,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+8,
+// CHECK:STDOUT:   node+9,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+3, type: type0},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block3, type: type1},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock2, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock3, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type4},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+10, type: type4},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type2},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+12, type: type2},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: type2},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+14, type: type2},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block4, type: type3},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+16, type: type3},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int2, type: type2},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+18, type: type2},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block5, type: type4},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+10, arg1: node+20, type: type4},
+// 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:     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:     node+21,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:     node+15,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+17,
+// CHECK:STDOUT:     node+19,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+var x: ((i32, i32), i32) = ((12, 76), 6);

+ 72 - 0
toolchain/semantics/testdata/tuples/one_element.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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   4,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT:   y,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+1,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+3,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+4, type: type2},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+6, type: type1},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block3, type: type2},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+4, arg1: node+8, type: type2},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block4, type: type0},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+12, type: type2},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+12, arg1: node+4, type: type2},
+// 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:     node+14,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+var x: (i32,) = (4,);
+var y: (i32,) = x;

+ 84 - 0
toolchain/semantics/testdata/tuples/two_elements.carbon

@@ -0,0 +1,84 @@
+// 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
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   4,
+// CHECK:STDOUT:   102,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT:   y,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   node+2,
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   node+4,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block2, type: type0},
+// CHECK:STDOUT:   {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+5, type: type2},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+7, type: type1},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: type1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+9, type: type1},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block3, type: type2},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+5, arg1: node+11, type: type2},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
+// CHECK:STDOUT:   {kind: TupleValue, arg0: block4, type: type0},
+// CHECK:STDOUT:   {kind: VarStorage, type: type2},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+16, type: type2},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+16, arg1: node+5, type: type2},
+// 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:     node+14,
+// CHECK:STDOUT:     node+15,
+// CHECK:STDOUT:     node+16,
+// CHECK:STDOUT:     node+17,
+// CHECK:STDOUT:     node+18,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:     node+14,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+var x: (i32, i32) = (4, 102);
+var y: (i32, i32) = x;