Explorar o código

Add semantics for struct type and value literals. (#2709)

This handles the basics of type and value for structs. Structurally, these look like parameters and arguments (respectively) because expressions/generics may result in multiple IR nodes being generated.

Because `{}` needs to be cast to a type for storage, I'm also adding some validation that's not specific to `{}`, e.g. that `1` shouldn't be valid as a type for storage (previously, nothing errored for that).

This adds more stringification of types, particularly literals, because they come up in value errors now.

ImplicitAs is the result of me mulling whether I'm taking the right approach on type conversions. I think it needs to return a value so that if the implicit cast rewrites the value, the result is accessible to the caller. I may reorient the current TryTypeConversion logic to be more based on the ImplicitAs logic.
Jon Ross-Perkins %!s(int64=3) %!d(string=hai) anos
pai
achega
2eef8c751b
Modificáronse 32 ficheiros con 1177 adicións e 183 borrados
  1. 1 0
      toolchain/diagnostics/diagnostic_kind.def
  2. 20 0
      toolchain/lowering/lowering.cpp
  3. 6 0
      toolchain/semantics/semantics_builtin_kind.def
  4. 19 0
      toolchain/semantics/semantics_ir.cpp
  5. 6 0
      toolchain/semantics/semantics_ir.h
  6. 14 0
      toolchain/semantics/semantics_node.h
  7. 4 0
      toolchain/semantics/semantics_node_kind.def
  8. 5 4
      toolchain/semantics/semantics_node_stack.cpp
  9. 3 9
      toolchain/semantics/semantics_node_stack.h
  10. 250 122
      toolchain/semantics/semantics_parse_tree_handler.cpp
  11. 36 7
      toolchain/semantics/semantics_parse_tree_handler.h
  12. 2 0
      toolchain/semantics/testdata/basics/builtin_nodes.carbon
  13. 66 0
      toolchain/semantics/testdata/function/call/empty_struct.carbon
  14. 23 11
      toolchain/semantics/testdata/function/call/fail_param_count.carbon
  15. 4 2
      toolchain/semantics/testdata/function/call/fail_param_type.carbon
  16. 12 6
      toolchain/semantics/testdata/function/call/more_param_ir.carbon
  17. 5 3
      toolchain/semantics/testdata/function/call/params_one.carbon
  18. 10 6
      toolchain/semantics/testdata/function/call/params_one_comma.carbon
  19. 7 3
      toolchain/semantics/testdata/function/call/params_two.carbon
  20. 16 8
      toolchain/semantics/testdata/function/call/params_two_comma.carbon
  21. 42 0
      toolchain/semantics/testdata/struct/empty.carbon
  22. 45 0
      toolchain/semantics/testdata/struct/fail_assign_empty.carbon
  23. 54 0
      toolchain/semantics/testdata/struct/fail_assign_to_empty.carbon
  24. 64 0
      toolchain/semantics/testdata/struct/fail_field_name_mismatch.carbon
  25. 64 0
      toolchain/semantics/testdata/struct/fail_field_type_mismatch.carbon
  26. 67 0
      toolchain/semantics/testdata/struct/fail_too_few_values.carbon
  27. 54 0
      toolchain/semantics/testdata/struct/fail_type_assign.carbon
  28. 52 0
      toolchain/semantics/testdata/struct/fail_value_as_type.carbon
  29. 79 0
      toolchain/semantics/testdata/struct/one_entry.carbon
  30. 94 0
      toolchain/semantics/testdata/struct/two_entries.carbon
  31. 2 2
      toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon
  32. 51 0
      toolchain/semantics/testdata/var/fail_storage_is_literal.carbon

+ 1 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -102,6 +102,7 @@ CARBON_DIAGNOSTIC_KIND(ReturnStatementDisallowExpression)
 CARBON_DIAGNOSTIC_KIND(ReturnStatementImplicitNote)
 CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpression)
 CARBON_DIAGNOSTIC_KIND(ReturnStatementTypeMismatch)
+CARBON_DIAGNOSTIC_KIND(ImplicitAsConversionFailure)
 
 // ============================================================================
 // Other diagnostics

+ 20 - 0
toolchain/lowering/lowering.cpp

@@ -176,6 +176,26 @@ auto Lowering::HandleStringLiteralNode(SemanticsNodeId /*node_id*/,
   CARBON_FATAL() << "TODO: Add support: " << node;
 }
 
+auto Lowering::HandleStructTypeNode(SemanticsNodeId /*node_id*/,
+                                    SemanticsNode node) -> void {
+  CARBON_FATAL() << "TODO: Add support: " << node;
+}
+
+auto Lowering::HandleStructTypeFieldNode(SemanticsNodeId /*node_id*/,
+                                         SemanticsNode node) -> void {
+  CARBON_FATAL() << "TODO: Add support: " << node;
+}
+
+auto Lowering::HandleStructValueNode(SemanticsNodeId /*node_id*/,
+                                     SemanticsNode node) -> void {
+  CARBON_FATAL() << "TODO: Add support: " << node;
+}
+
+auto Lowering::HandleStubReferenceNode(SemanticsNodeId /*node_id*/,
+                                       SemanticsNode node) -> void {
+  CARBON_FATAL() << "TODO: Add support: " << node;
+}
+
 auto Lowering::HandleVarStorageNode(SemanticsNodeId /*node_id*/,
                                     SemanticsNode node) -> void {
   CARBON_FATAL() << "TODO: Add support: " << node;

+ 6 - 0
toolchain/semantics/semantics_builtin_kind.def

@@ -60,6 +60,12 @@ CARBON_SEMANTICS_BUILTIN_KIND(FloatingPointType, TypeType, "f64")
 // The type of string values and String literals.
 CARBON_SEMANTICS_BUILTIN_KIND(StringType, TypeType, "String")
 
+// The canonical empty struct type.
+CARBON_SEMANTICS_BUILTIN_KIND(EmptyStructType, TypeType, "{} as Type")
+
+// The canonical empty struct.
+CARBON_SEMANTICS_BUILTIN_KIND(EmptyStruct, EmptyStructType, "{}")
+
 // The canonical empty tuple type.
 CARBON_SEMANTICS_BUILTIN_KIND(EmptyTupleType, TypeType, "() as Type")
 

+ 19 - 0
toolchain/semantics/semantics_ir.cpp

@@ -128,6 +128,23 @@ auto SemanticsIR::StringifyNodeImpl(llvm::raw_ostream& out,
 
   auto node = GetNode(node_id);
   switch (node.kind()) {
+    case SemanticsNodeKind::StructType: {
+      out << "{";
+      auto refs = GetNodeBlock(node.GetAsStructType().second);
+      llvm::ListSeparator sep;
+      for (const auto& ref_id : refs) {
+        out << sep;
+        // TODO: Bound recursion depth or remove recursive step.
+        StringifyNodeImpl(out, ref_id);
+      }
+      out << "}";
+      break;
+    }
+    case SemanticsNodeKind::StructTypeField: {
+      out << "." << GetString(node.GetAsStructTypeField()) << ": ";
+      StringifyNodeImpl(out, node.type_id());
+      break;
+    }
     case SemanticsNodeKind::Assign:
     case SemanticsNodeKind::BinaryOperatorAdd:
     case SemanticsNodeKind::BindName:
@@ -142,6 +159,8 @@ auto SemanticsIR::StringifyNodeImpl(llvm::raw_ostream& out,
     case SemanticsNodeKind::Return:
     case SemanticsNodeKind::ReturnExpression:
     case SemanticsNodeKind::StringLiteral:
+    case SemanticsNodeKind::StructValue:
+    case SemanticsNodeKind::StubReference:
     case SemanticsNodeKind::VarStorage:
       // We don't need to handle stringification for nodes that don't show up in
       // errors, but make it clear what's going on so that it's clearer when

+ 6 - 0
toolchain/semantics/semantics_ir.h

@@ -106,6 +106,12 @@ class SemanticsIR {
     return node_blocks_[block_id.index];
   }
 
+  // Returns the requested real literal.
+  auto GetRealLiteral(SemanticsRealLiteralId int_id) const
+      -> const SemanticsRealLiteral& {
+    return real_literals_[int_id.index];
+  }
+
   // Returns the requested string.
   auto GetString(SemanticsStringId string_id) const -> llvm::StringRef {
     return strings_[string_id.index];

+ 14 - 0
toolchain/semantics/semantics_node.h

@@ -299,6 +299,20 @@ class SemanticsNode {
                       SemanticsBuiltinKind::StringType.AsInt(),
                       SemanticsStringId /*string_id*/>;
 
+  using StructType = FactoryPreTyped<
+      SemanticsNodeKind::StructType, SemanticsBuiltinKind::TypeType.AsInt(),
+      SemanticsNodeBlockId /*ir_id*/, SemanticsNodeBlockId /*refs_id*/>;
+
+  using StructTypeField = Factory<SemanticsNodeKind::StructTypeField,
+                                  SemanticsStringId /*name_id*/>;
+
+  using StructValue =
+      Factory<SemanticsNodeKind::StructValue, SemanticsNodeBlockId /*ir_id*/,
+              SemanticsNodeBlockId /*refs_id*/>;
+
+  using StubReference =
+      Factory<SemanticsNodeKind::StubReference, SemanticsNodeId /*node_id*/>;
+
   using VarStorage = Factory<SemanticsNodeKind::VarStorage>;
 
   SemanticsNode()

+ 4 - 0
toolchain/semantics/semantics_node_kind.def

@@ -33,6 +33,10 @@ CARBON_SEMANTICS_NODE_KIND(RealLiteral)
 CARBON_SEMANTICS_NODE_KIND(Return)
 CARBON_SEMANTICS_NODE_KIND(ReturnExpression)
 CARBON_SEMANTICS_NODE_KIND(StringLiteral)
+CARBON_SEMANTICS_NODE_KIND(StructType)
+CARBON_SEMANTICS_NODE_KIND(StructTypeField)
+CARBON_SEMANTICS_NODE_KIND(StructValue)
+CARBON_SEMANTICS_NODE_KIND(StubReference)
 CARBON_SEMANTICS_NODE_KIND(VarStorage)
 
 #undef CARBON_SEMANTICS_NODE_KIND

+ 5 - 4
toolchain/semantics/semantics_node_stack.cpp

@@ -130,16 +130,17 @@ auto SemanticsNodeStack::PopForParseNodeAndNodeId(ParseNodeKind pop_parse_kind)
   return {back.parse_node, back.node_id};
 }
 
-auto SemanticsNodeStack::PopForParseNodeAndNameId()
+auto SemanticsNodeStack::PopForParseNodeAndNameId(ParseNodeKind pop_parse_kind)
     -> std::pair<ParseTree::Node, SemanticsStringId> {
-  auto back = PopEntry(ParseNodeKind::PatternBinding);
+  auto back = PopEntry(pop_parse_kind);
   RequireValidId(back);
   return {back.parse_node, back.name_id};
 }
 
-auto SemanticsNodeStack::PeekForNameId() -> SemanticsStringId {
+auto SemanticsNodeStack::PeekForNameId(ParseNodeKind parse_kind)
+    -> SemanticsStringId {
   auto back = stack_.back();
-  RequireParseKind(back, ParseNodeKind::PatternBinding);
+  RequireParseKind(back, parse_kind);
   RequireValidId(back);
   return back.name_id;
 }

+ 3 - 9
toolchain/semantics/semantics_node_stack.h

@@ -45,10 +45,8 @@ class SemanticsNodeStack {
     PushEntry({.parse_node = parse_node, .node_id = node_id}, DebugLog::NodeId);
   }
 
-  // Pushes a PatternBinding parse tree node onto the stack with its name.
+  // Pushes a parse tree node onto the stack with its name.
   auto Push(ParseTree::Node parse_node, SemanticsStringId name_id) -> void {
-    CARBON_CHECK(parse_tree_->node_kind(parse_node) ==
-                 ParseNodeKind::PatternBinding);
     PushEntry({.parse_node = parse_node, .name_id = name_id}, DebugLog::NameId);
   }
 
@@ -85,16 +83,14 @@ class SemanticsNodeStack {
   auto PopForNodeId() -> SemanticsNodeId;
 
   // Pops the top of the stack and returns the parse_node and name_id.
-  // Verifies that the parse_node is a PatternBinding.
-  auto PopForParseNodeAndNameId()
+  auto PopForParseNodeAndNameId(ParseNodeKind pop_parse_kind)
       -> std::pair<ParseTree::Node, SemanticsStringId>;
 
   // Peeks at the parse_node of the top of the stack.
   auto PeekParseNode() -> ParseTree::Node { return stack_.back().parse_node; }
 
   // Peeks at the name_id of the top of the stack.
-  // Verifies that the parse_node is a PatternBinding.
-  auto PeekForNameId() -> SemanticsStringId;
+  auto PeekForNameId(ParseNodeKind parse_kind) -> SemanticsStringId;
 
   // Prints the stack for a stack dump.
   auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
@@ -115,8 +111,6 @@ class SemanticsNodeStack {
     // is used based on the ParseNodeKind.
     union {
       SemanticsNodeId node_id;
-
-      // Right now name_id is exclusively for PatternBinding, which is enforced.
       SemanticsStringId name_id;
     };
   };

+ 250 - 122
toolchain/semantics/semantics_parse_tree_handler.cpp

@@ -232,68 +232,123 @@ auto SemanticsParseTreeHandler::TryTypeConversionOnArgs(
   return true;
 }
 
-auto SemanticsParseTreeHandler::ParamOrArgStart() -> void {
-  params_or_args_stack_.Push();
-  node_block_stack_.Push();
-}
+auto SemanticsParseTreeHandler::ImplicitAs(ParseTree::Node parse_node,
+                                           SemanticsNodeId value_id,
+                                           SemanticsNodeId as_type_id)
+    -> SemanticsNodeId {
+  // Start by making sure both sides are valid. If any part is invalid, the
+  // result is invalid and we shouldn't error.
+  if (value_id == SemanticsNodeId::BuiltinInvalidType ||
+      as_type_id == SemanticsNodeId::BuiltinInvalidType) {
+    return SemanticsNodeId::BuiltinInvalidType;
+  }
+  auto value_type_id = semantics_->GetType(value_id);
+  if (value_type_id == SemanticsNodeId::BuiltinInvalidType) {
+    return SemanticsNodeId::BuiltinInvalidType;
+  }
 
-auto SemanticsParseTreeHandler::ParamOrArgComma(ParseTree::Node parse_node)
-    -> bool {
-  node_stack_.Push(parse_node);
+  // If the type doesn't need to change, we can return the value directly.
+  if (value_type_id == as_type_id) {
+    return value_id;
+  }
 
-  // Copy the last node added to the IR block into the params block.
-  if (!ParamOrArgSave()) {
-    emitter_->Emit(
-        parse_node, SemanticsTodo,
-        "Should have a param before comma, will need error recovery");
-    return false;
+  // When converting to a Type, there are some automatic conversions that can be
+  // done.
+  if (as_type_id == SemanticsNodeId::BuiltinTypeType) {
+    if (value_id == SemanticsNodeId::BuiltinEmptyTuple) {
+      return SemanticsNodeId::BuiltinEmptyTupleType;
+    }
+    if (value_id == SemanticsNodeId::BuiltinEmptyStruct) {
+      return SemanticsNodeId::BuiltinEmptyStructType;
+    }
   }
 
-  return true;
+  auto value_type = semantics_->GetNode(value_type_id);
+  auto as_type = semantics_->GetNode(as_type_id);
+  if (CanImplicitAsStruct(value_type, as_type)) {
+    return value_id;
+  }
+
+  CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error,
+                    "Cannot implicitly convert from {0} to {1}.", std::string,
+                    std::string);
+  emitter_
+      ->Build(parse_node, ImplicitAsConversionFailure,
+              semantics_->StringifyNode(value_type_id),
+              semantics_->StringifyNode(as_type_id))
+      .Emit();
+  return SemanticsNodeId::BuiltinInvalidType;
 }
 
-auto SemanticsParseTreeHandler::ParamOrArgEnd(
-    ParseNodeKind start_kind, ParseNodeKind comma_kind,
-    std::function<bool(SemanticsNodeBlockId, SemanticsNodeBlockId)> on_start)
+auto SemanticsParseTreeHandler::CanImplicitAsStruct(SemanticsNode value_type,
+                                                    SemanticsNode as_type)
     -> bool {
-  // If there's a node in the IR block that has yet to be added to the params
-  // block, add it now.
-  ParamOrArgSave();
-
-  while (true) {
-    auto parse_kind = parse_tree_->node_kind(node_stack_.PeekParseNode());
-    if (parse_kind == start_kind) {
-      return on_start(node_block_stack_.Pop(), params_or_args_stack_.Pop());
-    } else if (parse_kind == comma_kind) {
-      node_stack_.PopAndDiscardSoloParseNode(comma_kind);
-    } else {
-      node_stack_.PopAndIgnore();
-    }
+  if (value_type.kind() != SemanticsNodeKind::StructType ||
+      as_type.kind() != SemanticsNodeKind::StructType) {
+    return false;
   }
-}
-
-auto SemanticsParseTreeHandler::ParamOrArgSave() -> bool {
-  // Copy the last node added to the IR block into the params block.
-  auto ir_id = node_block_stack_.Peek();
-  if (!ir_id.is_valid()) {
+  auto value_type_refs =
+      semantics_->GetNodeBlock(value_type.GetAsStructType().second);
+  auto as_type_refs =
+      semantics_->GetNodeBlock(as_type.GetAsStructType().second);
+  if (value_type_refs.size() != as_type_refs.size()) {
     return false;
   }
 
-  // We get params before ir because it may add a node block, which can
-  // invalidate the ir reference.
-  auto& params = semantics_->GetNodeBlock(params_or_args_stack_.PeekForAdd());
+  for (int i = 0; i < static_cast<int>(value_type_refs.size()); ++i) {
+    auto value_type_field = semantics_->GetNode(value_type_refs[i]);
+    auto as_type_field = semantics_->GetNode(as_type_refs[i]);
+    if (value_type_field.type_id() != as_type_field.type_id() ||
+        value_type_field.GetAsStructTypeField() !=
+            as_type_field.GetAsStructTypeField()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+auto SemanticsParseTreeHandler::ParamOrArgStart() -> void {
+  params_or_args_stack_.Push();
+  node_block_stack_.Push();
+}
 
-  auto& ir = semantics_->GetNodeBlock(ir_id);
-  CARBON_CHECK(!ir.empty())
-      << "Should only have a valid ID if a node was added";
-  auto& param = ir.back();
+auto SemanticsParseTreeHandler::ParamOrArgComma(bool for_args) -> void {
+  ParamOrArgSave(for_args);
+}
 
-  if (!params.empty() && param == params.back()) {
-    // The param was already added after a comma.
-    return false;
+auto SemanticsParseTreeHandler::ParamOrArgEnd(bool for_args,
+                                              ParseNodeKind start_kind)
+    -> std::pair<SemanticsNodeBlockId, SemanticsNodeBlockId> {
+  if (parse_tree_->node_kind(node_stack_.PeekParseNode()) != start_kind) {
+    ParamOrArgSave(for_args);
   }
-  params.push_back(ir.back());
-  return true;
+  return {node_block_stack_.Pop(), params_or_args_stack_.Pop()};
+}
+
+auto SemanticsParseTreeHandler::ParamOrArgSave(bool for_args) -> void {
+  SemanticsNodeId param_or_arg_id = SemanticsNodeId::Invalid;
+  if (for_args) {
+    // For an argument, we add a stub reference to the expression on the top of
+    // the stack. There may not be anything on the IR prior to this.
+    auto [entry_parse_node, entry_node_id] =
+        node_stack_.PopForParseNodeAndNodeId();
+    param_or_arg_id = AddNode(SemanticsNode::StubReference::Make(
+        entry_parse_node, semantics_->GetNode(entry_node_id).type_id(),
+        entry_node_id));
+  } else {
+    // For a parameter, there should always be something in the IR.
+    node_stack_.PopAndIgnore();
+    auto ir_id = node_block_stack_.Peek();
+    CARBON_CHECK(ir_id.is_valid());
+    auto& ir = semantics_->GetNodeBlock(ir_id);
+    CARBON_CHECK(!ir.empty()) << "Should have had a param";
+    param_or_arg_id = ir.back();
+  }
+
+  // Save the param or arg ID.
+  auto& params_or_args =
+      semantics_->GetNodeBlock(params_or_args_stack_.PeekForAdd());
+  params_or_args.push_back(param_or_arg_id);
 }
 
 auto SemanticsParseTreeHandler::HandleAddress(ParseTree::Node parse_node)
@@ -316,45 +371,44 @@ auto SemanticsParseTreeHandler::HandleBreakStatementStart(
 
 auto SemanticsParseTreeHandler::HandleCallExpression(ParseTree::Node parse_node)
     -> bool {
-  auto on_start = [&](SemanticsNodeBlockId ir_id,
-                      SemanticsNodeBlockId refs_id) -> bool {
-    // TODO: Convert to call expression.
-    auto [call_expr_parse_node, name_id] = node_stack_.PopForParseNodeAndNodeId(
-        ParseNodeKind::CallExpressionStart);
-    auto name_node = semantics_->GetNode(name_id);
-    if (name_node.kind() != SemanticsNodeKind::FunctionDeclaration) {
-      // TODO: Work on error.
-      emitter_->Emit(parse_node, SemanticsTodo, "Not a callable name");
-      node_stack_.Push(parse_node, name_id);
-      return true;
-    }
+  auto [ir_id, refs_id] =
+      ParamOrArgEnd(/*for_args=*/true, ParseNodeKind::CallExpressionStart);
+
+  // TODO: Convert to call expression.
+  auto [call_expr_parse_node, name_id] =
+      node_stack_.PopForParseNodeAndNodeId(ParseNodeKind::CallExpressionStart);
+  auto name_node = semantics_->GetNode(name_id);
+  if (name_node.kind() != SemanticsNodeKind::FunctionDeclaration) {
+    // TODO: Work on error.
+    emitter_->Emit(parse_node, SemanticsTodo, "Not a callable name");
+    node_stack_.Push(parse_node, name_id);
+    return true;
+  }
 
-    auto [_, callable_id] = name_node.GetAsFunctionDeclaration();
-    auto callable = semantics_->GetCallable(callable_id);
+  auto [_, callable_id] = name_node.GetAsFunctionDeclaration();
+  auto callable = semantics_->GetCallable(callable_id);
 
-    if (!TryTypeConversionOnArgs(call_expr_parse_node, ir_id, refs_id,
-                                 name_node.parse_node(),
-                                 callable.param_refs_id)) {
-      node_stack_.Push(parse_node, SemanticsNodeId::BuiltinInvalidType);
-      return true;
-    }
+  if (!TryTypeConversionOnArgs(call_expr_parse_node, ir_id, refs_id,
+                               name_node.parse_node(),
+                               callable.param_refs_id)) {
+    node_stack_.Push(parse_node, SemanticsNodeId::BuiltinInvalidType);
+    return true;
+  }
 
-    auto call_id = semantics_->AddCall({ir_id, refs_id});
-    // TODO: Propagate return types from callable.
-    auto call_node_id = AddNode(SemanticsNode::Call::Make(
-        call_expr_parse_node, SemanticsNodeId::BuiltinEmptyTuple, call_id,
-        callable_id));
+  auto call_id = semantics_->AddCall({ir_id, refs_id});
+  // TODO: Propagate return types from callable.
+  auto call_node_id = AddNode(SemanticsNode::Call::Make(
+      call_expr_parse_node, SemanticsNodeId::BuiltinEmptyTuple, call_id,
+      callable_id));
 
-    node_stack_.Push(parse_node, call_node_id);
-    return true;
-  };
-  return ParamOrArgEnd(ParseNodeKind::CallExpressionStart,
-                       ParseNodeKind::CallExpressionComma, on_start);
+  node_stack_.Push(parse_node, call_node_id);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleCallExpressionComma(
-    ParseTree::Node parse_node) -> bool {
-  return ParamOrArgComma(parse_node);
+    ParseTree::Node /*parse_node*/) -> bool {
+  ParamOrArgComma(/*for_args=*/true);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleCallExpressionStart(
@@ -434,8 +488,11 @@ auto SemanticsParseTreeHandler::HandleDeducedParameterListStart(
 
 auto SemanticsParseTreeHandler::HandleDesignatedName(ParseTree::Node parse_node)
     -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleDesignatedName");
-  return false;
+  auto name_str = parse_tree_->GetNodeText(parse_node);
+  auto name_id = semantics_->AddString(name_str);
+  // The parent is responsible for binding the name.
+  node_stack_.Push(parse_node, name_id);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleDesignatorExpression(
@@ -782,21 +839,20 @@ auto SemanticsParseTreeHandler::HandlePackageLibrary(ParseTree::Node parse_node)
 
 auto SemanticsParseTreeHandler::HandleParameterList(ParseTree::Node parse_node)
     -> bool {
-  auto on_start = [&](SemanticsNodeBlockId ir_id,
-                      SemanticsNodeBlockId refs_id) -> bool {
-    PopScope();
-    node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ParameterListStart);
-    finished_params_stack_.push_back({ir_id, refs_id});
-    node_stack_.Push(parse_node);
-    return true;
-  };
-  return ParamOrArgEnd(ParseNodeKind::ParameterListStart,
-                       ParseNodeKind::ParameterListComma, on_start);
+  auto [ir_id, refs_id] =
+      ParamOrArgEnd(/*for_args=*/false, ParseNodeKind::ParameterListStart);
+
+  PopScope();
+  node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ParameterListStart);
+  finished_params_stack_.push_back({ir_id, refs_id});
+  node_stack_.Push(parse_node);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleParameterListComma(
-    ParseTree::Node parse_node) -> bool {
-  return ParamOrArgComma(parse_node);
+    ParseTree::Node /*parse_node*/) -> bool {
+  ParamOrArgComma(/*for_args=*/false);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleParameterListStart(
@@ -822,16 +878,19 @@ auto SemanticsParseTreeHandler::HandleParenExpressionOrTupleLiteralStart(
 
 auto SemanticsParseTreeHandler::HandlePatternBinding(ParseTree::Node parse_node)
     -> bool {
-  auto type = node_stack_.PopForNodeId();
+  auto [type_node, parsed_type] = node_stack_.PopForParseNodeAndNodeId();
+  auto cast_type_id =
+      ImplicitAs(type_node, parsed_type, SemanticsNodeId::BuiltinTypeType);
 
   // Get the name.
   auto name_node = node_stack_.PopForSoloParseNode();
 
   // Allocate storage, linked to the name for error locations.
-  auto storage_id = AddNode(SemanticsNode::VarStorage::Make(name_node, type));
+  auto storage_id =
+      AddNode(SemanticsNode::VarStorage::Make(name_node, cast_type_id));
 
   // Bind the name to storage.
-  auto name_id = BindName(name_node, type, storage_id);
+  auto name_id = BindName(name_node, cast_type_id, storage_id);
 
   // If this node's result is used, it'll be for either the name or the storage
   // address. The storage address can be found through the name, so we push the
@@ -939,22 +998,35 @@ auto SemanticsParseTreeHandler::HandleSelfValueIdentifier(
   return false;
 }
 
-auto SemanticsParseTreeHandler::HandleStructComma(ParseTree::Node parse_node)
-    -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleStructComma");
-  return false;
+auto SemanticsParseTreeHandler::HandleStructComma(
+    ParseTree::Node /*parse_node*/) -> bool {
+  ParamOrArgComma(
+      /*for_args=*/parse_tree_->node_kind(node_stack_.PeekParseNode()) !=
+      ParseNodeKind::StructFieldType);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleStructFieldDesignator(
-    ParseTree::Node parse_node) -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleStructFieldDesignator");
-  return false;
+    ParseTree::Node /*parse_node*/) -> bool {
+  // This leaves the designated name on top because the `.` isn't interesting.
+  CARBON_CHECK(parse_tree_->node_kind(node_stack_.PeekParseNode()) ==
+               ParseNodeKind::DesignatedName);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleStructFieldType(
     ParseTree::Node parse_node) -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleStructFieldType");
-  return false;
+  auto [type_node, type_id] = node_stack_.PopForParseNodeAndNodeId();
+  auto cast_type_id =
+      ImplicitAs(type_node, type_id, SemanticsNodeId::BuiltinTypeType);
+
+  auto [name_node, name_id] =
+      node_stack_.PopForParseNodeAndNameId(ParseNodeKind::DesignatedName);
+
+  AddNode(
+      SemanticsNode::StructTypeField::Make(name_node, cast_type_id, name_id));
+  node_stack_.Push(parse_node);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleStructFieldUnknown(
@@ -965,27 +1037,81 @@ auto SemanticsParseTreeHandler::HandleStructFieldUnknown(
 
 auto SemanticsParseTreeHandler::HandleStructFieldValue(
     ParseTree::Node parse_node) -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleStructFieldValue");
-  return false;
+  auto [value_parse_node, value_node_id] =
+      node_stack_.PopForParseNodeAndNodeId();
+  auto [_, name_id] =
+      node_stack_.PopForParseNodeAndNameId(ParseNodeKind::DesignatedName);
+
+  // Store the name for the type.
+  auto type_block_id = args_type_info_stack_.PeekForAdd();
+  semantics_->AddNode(
+      type_block_id,
+      SemanticsNode::StructTypeField::Make(
+          parse_node, semantics_->GetNode(value_node_id).type_id(), name_id));
+
+  // Push the value back on the stack as an argument.
+  node_stack_.Push(parse_node, value_node_id);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleStructLiteral(ParseTree::Node parse_node)
     -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleStructLiteral");
-  return false;
+  auto [ir_id, refs_id] = ParamOrArgEnd(
+      /*for_args=*/true, ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
+
+  PopScope();
+  node_stack_.PopAndDiscardSoloParseNode(
+      ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
+  auto type_block_id = args_type_info_stack_.Pop();
+
+  // Special-case `{}`.
+  if (refs_id == SemanticsNodeBlockId::Empty) {
+    node_stack_.Push(parse_node, SemanticsNodeId::BuiltinEmptyStruct);
+    return true;
+  }
+
+  // Construct a type for the literal. Each field is one node, so ir_id and
+  // refs_id match.
+  auto refs = semantics_->GetNodeBlock(refs_id);
+  auto type_id = AddNode(SemanticsNode::StructType::Make(
+      parse_node, type_block_id, type_block_id));
+
+  auto value_id = AddNode(
+      SemanticsNode::StructValue::Make(parse_node, type_id, ir_id, refs_id));
+  node_stack_.Push(parse_node, value_id);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleStructLiteralOrStructTypeLiteralStart(
     ParseTree::Node parse_node) -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo,
-                 "HandleStructLiteralOrStructTypeLiteralStart");
-  return false;
+  PushScope();
+  node_stack_.Push(parse_node);
+  // At this point we aren't sure whether this will be a value or type literal,
+  // so we push onto args irrespective. It just won't be used for a type
+  // literal.
+  args_type_info_stack_.Push();
+  ParamOrArgStart();
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleStructTypeLiteral(
     ParseTree::Node parse_node) -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleStructTypeLiteral");
-  return false;
+  auto [ir_id, refs_id] = ParamOrArgEnd(
+      /*for_args=*/false, ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
+
+  PopScope();
+  node_stack_.PopAndDiscardSoloParseNode(
+      ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
+  // This is only used for value literals.
+  args_type_info_stack_.Pop();
+
+  CARBON_CHECK(refs_id != SemanticsNodeBlockId::Empty)
+      << "{} is handled by StructLiteral.";
+
+  auto type_id =
+      AddNode(SemanticsNode::StructType::Make(parse_node, ir_id, refs_id));
+  node_stack_.Push(parse_node, type_id);
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleTemplate(ParseTree::Node parse_node)
@@ -1015,16 +1141,17 @@ auto SemanticsParseTreeHandler::HandleVariableDeclaration(
     auto storage_id =
         node_stack_.PopForNodeId(ParseNodeKind::VariableInitializer);
 
-    auto binding = node_stack_.PopForParseNodeAndNameId();
+    auto binding =
+        node_stack_.PopForParseNodeAndNameId(ParseNodeKind::PatternBinding);
 
     // Restore the name now that the initializer is complete.
     ReaddNameToLookup(binding.second, storage_id);
 
-    auto storage_type =
-        TryTypeConversion(parse_node, storage_id, last_child.second,
-                          /*can_convert_lhs=*/false);
-    AddNode(SemanticsNode::Assign::Make(parse_node, storage_type, storage_id,
-                                        last_child.second));
+    auto cast_value_id = ImplicitAs(parse_node, last_child.second,
+                                    semantics_->GetType(storage_id));
+    AddNode(SemanticsNode::Assign::Make(parse_node,
+                                        semantics_->GetType(cast_value_id),
+                                        storage_id, cast_value_id));
   }
 
   node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::VariableIntroducer);
@@ -1046,7 +1173,8 @@ auto SemanticsParseTreeHandler::HandleVariableInitializer(
   // restored by `VariableDeclaration`.
 
   // Save the storage ID.
-  auto it = name_lookup_.find(node_stack_.PeekForNameId());
+  auto it = name_lookup_.find(
+      node_stack_.PeekForNameId(ParseNodeKind::PatternBinding));
   CARBON_CHECK(it != name_lookup_.end());
   CARBON_CHECK(!it->second.empty());
   auto storage_id = it->second.back();

+ 36 - 7
toolchain/semantics/semantics_parse_tree_handler.h

@@ -33,6 +33,8 @@ class SemanticsParseTreeHandler {
         node_block_stack_("node_block_stack_", semantics.node_blocks_,
                           vlog_stream),
         params_or_args_stack_("params_or_args_stack_", semantics.node_blocks_,
+                              vlog_stream),
+        args_type_info_stack_("args_type_info_stack_", semantics.node_blocks_,
                               vlog_stream) {}
 
   // Outputs the ParseTree information into SemanticsIR.
@@ -127,16 +129,37 @@ class SemanticsParseTreeHandler {
                                ParseTree::Node param_parse_node,
                                SemanticsNodeBlockId param_refs_id) -> bool;
 
-  auto ParamOrArgStart() -> void;
-  auto ParamOrArgComma(ParseTree::Node parse_node) -> bool;
-  auto ParamOrArgEnd(
-      ParseNodeKind start_kind, ParseNodeKind comma_kind,
-      std::function<bool(SemanticsNodeBlockId, SemanticsNodeBlockId)> on_start)
+  // Runs ImplicitAs behavior to convert `value` to `as_type`, returning the
+  // result type. The result will be the node to use to replace `value`. The
+  // result will be BuiltinInvalidType for errors; this handles printing
+  // diagnostics.
+  auto ImplicitAs(ParseTree::Node parse_node, SemanticsNodeId value,
+                  SemanticsNodeId as_type) -> SemanticsNodeId;
+
+  // Returns true if the ImplicitAs can use struct conversion.
+  // TODO: This currently only supports struct types that precisely match.
+  auto CanImplicitAsStruct(SemanticsNode value_type, SemanticsNode as_type)
       -> bool;
 
+  // Starts handling parameters or arguments.
+  auto ParamOrArgStart() -> void;
+
+  // On a comma, pushes the entry. On return, the top of node_stack_ will be
+  // start_kind.
+  auto ParamOrArgComma(bool for_args) -> void;
+
+  // Detects whether there's an entry to push. On return, the top of
+  // node_stack_ will be start_kind, and the caller should do type-specific
+  // processing. Returns a pair of {ir_id, refs_id}.
+  auto ParamOrArgEnd(bool for_args, ParseNodeKind start_kind)
+      -> std::pair<SemanticsNodeBlockId, SemanticsNodeBlockId>;
+
   // Saves a parameter from the top block in node_stack_ to the top block in
-  // params_or_args_stack_. Returns false if nothing is copied.
-  auto ParamOrArgSave() -> bool;
+  // params_or_args_stack_. If for_args, adds a StubReference of the previous
+  // node's result to the IR.
+  //
+  // This should only be called by other ParamOrArg functions, not directly.
+  auto ParamOrArgSave(bool for_args) -> void;
 
   // Parse node handlers. Returns false for unrecoverable errors.
 #define CARBON_PARSE_NODE_KIND(Name) \
@@ -172,6 +195,12 @@ class SemanticsParseTreeHandler {
   // node in blocks here.
   SemanticsNodeBlockStack params_or_args_stack_;
 
+  // The stack of node blocks being used for type information while processing
+  // arguments. This is used in parallel with params_or_args_stack_. It's
+  // currently only used for struct literals, where we need to track names
+  // for a type separate from the literal arguments.
+  SemanticsNodeBlockStack args_type_info_stack_;
+
   // Completed parameters that are held temporarily on a side-channel for a
   // function. This can't use node_stack_ because it has space for only one
   // value, whereas parameters return two values.

+ 2 - 0
toolchain/semantics/testdata/basics/builtin_nodes.carbon

@@ -22,6 +22,8 @@
 // CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: nodeIntegerType, type: nodeTypeType},
 // CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: nodeFloatingPointType, type: nodeTypeType},
 // CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: nodeStringType, type: nodeTypeType},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: nodeEmptyStructType, type: nodeTypeType},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: nodeEmptyStruct, type: nodeEmptyStructType},
 // CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: nodeEmptyTupleType, type: nodeTypeType},
 // CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: nodeEmptyTuple, type: nodeEmptyTupleType},
 // CHECK:STDOUT: ]

+ 66 - 0
toolchain/semantics/testdata/function/call/empty_struct.carbon

@@ -0,0 +1,66 @@
+// 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:   {arg_ir: block4, arg_refs: block5},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: callables: [
+// CHECK:STDOUT:   {param_ir: block1, param_refs: block2},
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   Echo,
+// CHECK:STDOUT:   Main,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: VarStorage, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+0, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str1, arg1: callable0},
+// CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+2, arg1: block0},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str2, arg1: callable1},
+// CHECK:STDOUT:   {kind: StubReference, arg0: nodeEmptyStruct, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: Call, arg0: call0, arg1: callable0, type: nodeEmptyTuple},
+// CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+4, arg1: block6},
+// 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+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+2,
+// CHECK:STDOUT:     node+3,
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+fn Echo(a: {}) {
+}
+
+fn Main() {
+  Echo({});
+}

+ 23 - 11
toolchain/semantics/testdata/function/call/fail_param_count.carbon

@@ -46,11 +46,17 @@
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+10, arg1: block0},
 // CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str5, arg1: callable3},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+13, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+15, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int2, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+17, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int3, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+19, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int4, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+21, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int5, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+23, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+12, arg1: block0},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: node_blocks: [
@@ -64,7 +70,7 @@
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:     node+11,
 // CHECK:STDOUT:     node+12,
-// CHECK:STDOUT:     node+19,
+// CHECK:STDOUT:     node+25,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+2,
@@ -85,31 +91,37 @@
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+13,
-// CHECK:STDOUT:   ],
-// CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+13,
-// CHECK:STDOUT:   ],
-// CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+14,
-// CHECK:STDOUT:     node+15,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+14,
-// CHECK:STDOUT:     node+15,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+15,
 // CHECK:STDOUT:     node+16,
 // CHECK:STDOUT:     node+17,
+// CHECK:STDOUT:     node+18,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+16,
-// CHECK:STDOUT:     node+17,
+// CHECK:STDOUT:     node+18,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+18,
+// CHECK:STDOUT:     node+19,
+// CHECK:STDOUT:     node+20,
+// CHECK:STDOUT:     node+21,
+// CHECK:STDOUT:     node+22,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+18,
+// CHECK:STDOUT:     node+20,
+// CHECK:STDOUT:     node+22,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+23,
+// CHECK:STDOUT:     node+24,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+24,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 

+ 4 - 2
toolchain/semantics/testdata/function/call/fail_param_type.carbon

@@ -28,6 +28,7 @@
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+2, arg1: block0},
 // CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str2, arg1: callable1},
 // CHECK:STDOUT:   {kind: RealLiteral, arg0: real0, type: nodeFloatingPointType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+5, type: nodeFloatingPointType},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+4, arg1: block0},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: node_blocks: [
@@ -44,13 +45,14 @@
 // CHECK:STDOUT:     node+2,
 // CHECK:STDOUT:     node+3,
 // CHECK:STDOUT:     node+4,
-// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 

+ 12 - 6
toolchain/semantics/testdata/function/call/more_param_ir.carbon

@@ -44,10 +44,13 @@
 // CHECK:STDOUT:   {kind: BinaryOperatorAdd, arg0: node+9, arg1: node+10, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int2, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: BinaryOperatorAdd, arg0: node+11, arg1: node+12, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+13, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int3, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int4, type: nodeIntegerType},
-// CHECK:STDOUT:   {kind: BinaryOperatorAdd, arg0: node+14, arg1: node+15, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: BinaryOperatorAdd, arg0: node+15, arg1: node+16, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+17, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int5, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+19, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: Call, arg0: call0, arg1: callable0, type: nodeEmptyTuple},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+8, arg1: block6},
 // CHECK:STDOUT: ]
@@ -71,7 +74,7 @@
 // CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:     node+8,
-// CHECK:STDOUT:     node+19,
+// CHECK:STDOUT:     node+22,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+9,
@@ -83,14 +86,17 @@
 // 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+13,
-// CHECK:STDOUT:     node+16,
-// CHECK:STDOUT:     node+17,
+// CHECK:STDOUT:     node+14,
+// CHECK:STDOUT:     node+18,
+// CHECK:STDOUT:     node+20,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+18,
+// CHECK:STDOUT:     node+21,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 

+ 5 - 3
toolchain/semantics/testdata/function/call/params_one.carbon

@@ -29,6 +29,7 @@
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+2, arg1: block0},
 // CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str2, arg1: callable1},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+5, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: Call, arg0: call0, arg1: callable0, type: nodeEmptyTuple},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+4, arg1: block6},
 // CHECK:STDOUT: ]
@@ -46,16 +47,17 @@
 // CHECK:STDOUT:     node+2,
 // CHECK:STDOUT:     node+3,
 // CHECK:STDOUT:     node+4,
-// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:     node+8,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 

+ 10 - 6
toolchain/semantics/testdata/function/call/params_one_comma.carbon

@@ -31,8 +31,10 @@
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+2, arg1: block0},
 // CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str2, arg1: callable1},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+5, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: Call, arg0: call0, arg1: callable0, type: nodeEmptyTuple},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+8, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: Call, arg0: call1, arg1: callable0, type: nodeEmptyTuple},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+4, arg1: block6},
 // CHECK:STDOUT: ]
@@ -50,23 +52,25 @@
 // CHECK:STDOUT:     node+2,
 // CHECK:STDOUT:     node+3,
 // CHECK:STDOUT:     node+4,
-// CHECK:STDOUT:     node+9,
-// CHECK:STDOUT:   ],
-// CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+11,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+6,
-// CHECK:STDOUT:     node+8,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+9,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 

+ 7 - 3
toolchain/semantics/testdata/function/call/params_two.carbon

@@ -33,7 +33,9 @@
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+4, arg1: block0},
 // CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str3, arg1: callable1},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+7, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+9, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: Call, arg0: call0, arg1: callable0, type: nodeEmptyTuple},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+6, arg1: block6},
 // CHECK:STDOUT: ]
@@ -54,18 +56,20 @@
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:     node+6,
-// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:     node+12,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:     node+11,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 

+ 16 - 8
toolchain/semantics/testdata/function/call/params_two_comma.carbon

@@ -36,10 +36,14 @@
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+4, arg1: block0},
 // CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str3, arg1: callable1},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+7, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+9, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: Call, arg0: call0, arg1: callable0, type: nodeEmptyTuple},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int2, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+12, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int3, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+14, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: Call, arg0: call1, arg1: callable0, type: nodeEmptyTuple},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+6, arg1: block6},
 // CHECK:STDOUT: ]
@@ -60,27 +64,31 @@
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:     node+6,
-// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:     node+17,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+9,
-// CHECK:STDOUT:     node+12,
+// CHECK:STDOUT:     node+11,
+// CHECK:STDOUT:     node+16,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT:   [
-// 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:   ],
 // CHECK:STDOUT:   [
-// CHECK:STDOUT:     node+10,
-// CHECK:STDOUT:     node+11,
+// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:     node+15,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 

+ 42 - 0
toolchain/semantics/testdata/struct/empty.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: %{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: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: VarStorage, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+0, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+0, arg1: nodeEmptyStruct, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: VarStorage, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+3, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+3, arg1: node+0, type: nodeEmptyStructType},
+// 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: ]
+
+var x: {} = {};
+var y: {} = x;

+ 45 - 0
toolchain/semantics/testdata/struct/fail_assign_empty.carbon

@@ -0,0 +1,45 @@
+// 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:   a,
+// CHECK:STDOUT:   x,
+// 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: 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:   ],
+// 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+4,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_assign_empty.carbon:[[@LINE+1]]:22: Cannot implicitly convert from {} as Type to {.a: i32}.
+var x: {.a: i32} = {};

+ 54 - 0
toolchain/semantics/testdata/struct/fail_assign_to_empty.carbon

@@ -0,0 +1,54 @@
+// 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:   1,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: VarStorage, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node+0, type: nodeEmptyStructType},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str1, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+2, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructType, arg0: block3, arg1: block3, type: nodeTypeType},
+// CHECK:STDOUT:   {kind: StructValue, arg0: block2, arg1: block4, type: node+5},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+0, 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+5,
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+2,
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+3,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_assign_to_empty.carbon:[[@LINE+1]]:21: Cannot implicitly convert from {.a: i32} to {} as Type.
+var x: {} = {.a = 1};

+ 64 - 0
toolchain/semantics/testdata/struct/fail_field_name_mismatch.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
+// 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:   1,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   x,
+// 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: str2, 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: 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:   ],
+// 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: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_field_name_mismatch.carbon:[[@LINE+1]]:28: Cannot implicitly convert from {.b: i32} to {.a: i32}.
+var x: {.a: i32} = {.b = 1};

+ 64 - 0
toolchain/semantics/testdata/struct/fail_field_type_mismatch.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
+// 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: 10, exponent: -1, is_decimal: 1},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   x,
+// 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: RealLiteral, arg0: real0, type: nodeFloatingPointType},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str2, 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: 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:   ],
+// 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: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_field_type_mismatch.carbon:[[@LINE+1]]:30: Cannot implicitly convert from {.b: f64} to {.a: i32}.
+var x: {.a: i32} = {.b = 1.0};

+ 67 - 0
toolchain/semantics/testdata/struct/fail_too_few_values.carbon

@@ -0,0 +1,67 @@
+// 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:   1,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   b,
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str0, type: nodeIntegerType},
+// 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: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+5, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructType, arg0: block5, arg1: block5, type: nodeTypeType},
+// CHECK:STDOUT:   {kind: StructValue, arg0: block4, arg1: block6, type: node+8},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+3, 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:   ],
+// 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+8,
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_too_few_values.carbon:[[@LINE+1]]:37: Cannot implicitly convert from {.a: i32} to {.a: i32, .b: i32}.
+var x: {.a: i32, .b: i32} = {.a = 1};

+ 54 - 0
toolchain/semantics/testdata/struct/fail_type_assign.carbon

@@ -0,0 +1,54 @@
+// 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:   a,
+// CHECK:STDOUT:   x,
+// 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: StructTypeField, arg0: str0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructType, arg0: block4, arg1: block5, type: nodeTypeType},
+// 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:   ],
+// 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+5,
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_type_assign.carbon:[[@LINE+1]]:29: Cannot implicitly convert from Type to {.a: i32}.
+var x: {.a: i32} = {.a: i32};

+ 52 - 0
toolchain/semantics/testdata/struct/fail_value_as_type.carbon

@@ -0,0 +1,52 @@
+// 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:   1,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructType, arg0: block2, arg1: block2, type: nodeTypeType},
+// CHECK:STDOUT:   {kind: StructValue, arg0: block1, arg1: block3, type: node+3},
+// CHECK:STDOUT:   {kind: VarStorage, type: nodeInvalidType},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+5, type: nodeInvalidType},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:     node+2,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+2,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+3,
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/struct/fail_value_as_type.carbon:[[@LINE+1]]:15: Cannot implicitly convert from {.a: i32} to Type.
+var x: {.a = 1};

+ 79 - 0
toolchain/semantics/testdata/struct/one_entry.carbon

@@ -0,0 +1,79 @@
+// 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:   4,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT:   y,
+// 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: StructTypeField, arg0: str0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructType, arg0: block7, arg1: block8, type: nodeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: node+11},
+// CHECK:STDOUT:   {kind: BindName, arg0: str2, arg1: node+12, type: node+11},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+12, arg1: node+2, type: node+1},
+// 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+11,
+// CHECK:STDOUT:     node+12,
+// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:     node+14,
+// 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:   [
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+var x: {.a: i32} = {.a = 4};
+var y: {.a: i32} = x;

+ 94 - 0
toolchain/semantics/testdata/struct/two_entries.carbon

@@ -0,0 +1,94 @@
+// 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:   2,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   a,
+// CHECK:STDOUT:   b,
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT:   y,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str0, type: nodeIntegerType},
+// 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: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StubReference, arg0: node+5, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, 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: StructTypeField, arg0: str0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructTypeField, arg0: str1, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: StructType, arg0: block7, arg1: block8, type: nodeTypeType},
+// CHECK:STDOUT:   {kind: VarStorage, type: node+16},
+// CHECK:STDOUT:   {kind: BindName, arg0: str3, arg1: node+17, type: node+16},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+17, arg1: node+3, type: node+2},
+// 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+16,
+// CHECK:STDOUT:     node+17,
+// CHECK:STDOUT:     node+18,
+// CHECK:STDOUT:     node+19,
+// 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:   [
+// CHECK:STDOUT:     node+14,
+// CHECK:STDOUT:     node+15,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+14,
+// CHECK:STDOUT:     node+15,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+var x: {.a: i32, .b: i32} = {.a = 1, .b = 2};
+var y: {.a: i32, .b: i32} = x;

+ 2 - 2
toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon

@@ -24,7 +24,7 @@
 // CHECK:STDOUT:   {kind: VarStorage, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+1, type: nodeIntegerType},
 // CHECK:STDOUT:   {kind: RealLiteral, arg0: real0, type: nodeFloatingPointType},
-// CHECK:STDOUT:   {kind: Assign, arg0: node+1, arg1: node+3, type: nodeInvalidType},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+1, arg1: nodeInvalidType, type: nodeInvalidType},
 // CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+0, arg1: block2},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: node_blocks: [
@@ -43,6 +43,6 @@
 // CHECK:STDOUT: ]
 
 fn Main() {
-  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon:[[@LINE+1]]:19: Type mismatch: lhs is i32, rhs is f64
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon:[[@LINE+1]]:19: Cannot implicitly convert from f64 to i32.
   var x: i32 = 1.0;
 }

+ 51 - 0
toolchain/semantics/testdata/var/fail_storage_is_literal.carbon

@@ -0,0 +1,51 @@
+// 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:   {param_ir: block0, param_refs: block0},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   1,
+// CHECK:STDOUT:   1,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   Main,
+// CHECK:STDOUT:   x,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: str0, arg1: callable0},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: VarStorage, type: nodeInvalidType},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+2, type: nodeInvalidType},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: nodeIntegerType},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+2, arg1: nodeInvalidType, type: nodeInvalidType},
+// CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node+0, arg1: block2},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+0,
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+1,
+// CHECK:STDOUT:     node+2,
+// CHECK:STDOUT:     node+3,
+// CHECK:STDOUT:     node+4,
+// CHECK:STDOUT:     node+5,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+fn Main() {
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_storage_is_literal.carbon:[[@LINE+1]]:10: Cannot implicitly convert from i32 to Type.
+  var x: 1 = 1;
+}