Explorar o código

Add formatted textual IR output (#3056)

Add a textual IR format to the toolchain.

The exact details of the format are somewhat arbitrary right now, and I
expect them to change as we refine the semantics IR model, but at the
moment they're somewhat directly following the current structure of the
IR.

Semantics tests currently test both the "raw" format, which shows the
details of the representation, and the textual format, which is somewhat
higher level. We may want to revisit that decision once the textual
format is a bit more stable, and test only the textual format in most of
these tests, but for now it seems prudent to keep both sets of tests.

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Richard Smith %!s(int64=2) %!d(string=hai) anos
pai
achega
6cbf280a68
Modificáronse 100 ficheiros con 2633 adicións e 64 borrados
  1. 1 0
      toolchain/driver/BUILD
  2. 22 3
      toolchain/driver/driver.cpp
  3. 17 0
      toolchain/semantics/BUILD
  4. 2 1
      toolchain/semantics/semantics_file_test.cpp
  5. 5 1
      toolchain/semantics/semantics_handle_function.cpp
  6. 13 7
      toolchain/semantics/semantics_ir.cpp
  7. 10 4
      toolchain/semantics/semantics_ir.h
  8. 671 0
      toolchain/semantics/semantics_ir_formatter.cpp
  9. 20 0
      toolchain/semantics/semantics_ir_formatter.h
  10. 1 1
      toolchain/semantics/semantics_ir_test.cpp
  11. 18 1
      toolchain/semantics/semantics_node_kind.cpp
  12. 72 43
      toolchain/semantics/semantics_node_kind.def
  13. 19 0
      toolchain/semantics/semantics_node_kind.h
  14. 1 1
      toolchain/semantics/testdata/basics/builtin_nodes.carbon
  15. 14 0
      toolchain/semantics/testdata/basics/builtin_types.carbon
  16. 3 0
      toolchain/semantics/testdata/basics/empty.carbon
  17. 3 0
      toolchain/semantics/testdata/basics/empty_decl.carbon
  18. 9 0
      toolchain/semantics/testdata/basics/fail_name_lookup.carbon
  19. 6 0
      toolchain/semantics/testdata/basics/fail_non_type_as_type.carbon
  20. 6 0
      toolchain/semantics/testdata/basics/fail_qualifier_unsupported.carbon
  21. 8 0
      toolchain/semantics/testdata/basics/parens.carbon
  22. 21 0
      toolchain/semantics/testdata/basics/textual_ir.carbon
  23. 1 1
      toolchain/semantics/testdata/basics/verbose.carbon
  24. 13 0
      toolchain/semantics/testdata/const/collapse.carbon
  25. 11 0
      toolchain/semantics/testdata/const/fail_collapse.carbon
  26. 19 0
      toolchain/semantics/testdata/function/call/empty_struct.carbon
  27. 19 0
      toolchain/semantics/testdata/function/call/empty_tuple.carbon
  28. 39 0
      toolchain/semantics/testdata/function/call/fail_param_count.carbon
  29. 17 0
      toolchain/semantics/testdata/function/call/fail_param_type.carbon
  30. 19 0
      toolchain/semantics/testdata/function/call/fail_return_type_mismatch.carbon
  31. 20 0
      toolchain/semantics/testdata/function/call/i32.carbon
  32. 29 0
      toolchain/semantics/testdata/function/call/more_param_ir.carbon
  33. 19 0
      toolchain/semantics/testdata/function/call/params_one.carbon
  34. 22 0
      toolchain/semantics/testdata/function/call/params_one_comma.carbon
  35. 21 0
      toolchain/semantics/testdata/function/call/params_two.carbon
  36. 26 0
      toolchain/semantics/testdata/function/call/params_two_comma.carbon
  37. 17 0
      toolchain/semantics/testdata/function/call/params_zero.carbon
  38. 20 0
      toolchain/semantics/testdata/function/call/return_implicit.carbon
  39. 14 0
      toolchain/semantics/testdata/function/declaration/simple.carbon
  40. 9 0
      toolchain/semantics/testdata/function/definition/fail_param_name_conflict.carbon
  41. 21 0
      toolchain/semantics/testdata/function/definition/order.carbon
  42. 9 0
      toolchain/semantics/testdata/function/definition/params_one.carbon
  43. 9 0
      toolchain/semantics/testdata/function/definition/params_one_comma.carbon
  44. 9 0
      toolchain/semantics/testdata/function/definition/params_two.carbon
  45. 9 0
      toolchain/semantics/testdata/function/definition/params_two_comma.carbon
  46. 9 0
      toolchain/semantics/testdata/function/definition/params_zero.carbon
  47. 15 0
      toolchain/semantics/testdata/function/definition/same_param_name.carbon
  48. 40 0
      toolchain/semantics/testdata/if/else.carbon
  49. 45 0
      toolchain/semantics/testdata/if/fail_reachable_fallthrough.carbon
  50. 18 0
      toolchain/semantics/testdata/if/fail_scope.carbon
  51. 30 0
      toolchain/semantics/testdata/if/no_else.carbon
  52. 17 0
      toolchain/semantics/testdata/if/unreachable_fallthrough.carbon
  53. 21 0
      toolchain/semantics/testdata/if_expression/basic.carbon
  54. 55 0
      toolchain/semantics/testdata/if_expression/constant_condition.carbon
  55. 35 0
      toolchain/semantics/testdata/if_expression/control_flow.carbon
  56. 43 0
      toolchain/semantics/testdata/if_expression/nested.carbon
  57. 20 0
      toolchain/semantics/testdata/index/element_access.carbon
  58. 18 0
      toolchain/semantics/testdata/index/fail_empty_tuple_access.carbon
  59. 19 0
      toolchain/semantics/testdata/index/fail_large_index.carbon
  60. 12 0
      toolchain/semantics/testdata/index/fail_name_not_found.carbon
  61. 3 0
      toolchain/semantics/testdata/index/fail_negative_indexing.carbon
  62. 20 0
      toolchain/semantics/testdata/index/fail_non_deterministic_type.carbon
  63. 18 0
      toolchain/semantics/testdata/index/fail_non_int_indexing.carbon
  64. 11 0
      toolchain/semantics/testdata/index/fail_non_tuple_access.carbon
  65. 18 0
      toolchain/semantics/testdata/index/fail_out_of_bound_access.carbon
  66. 25 0
      toolchain/semantics/testdata/index/return_value_access.carbon
  67. 92 0
      toolchain/semantics/testdata/ir/duplicate_name_same_line.carbon
  68. 17 1
      toolchain/semantics/testdata/namespace/fail_duplicate.carbon
  69. 9 0
      toolchain/semantics/testdata/namespace/fail_unresolved_scope.carbon
  70. 24 0
      toolchain/semantics/testdata/namespace/function.carbon
  71. 19 0
      toolchain/semantics/testdata/namespace/nested.carbon
  72. 33 0
      toolchain/semantics/testdata/operators/and.carbon
  73. 70 0
      toolchain/semantics/testdata/operators/assignment.carbon
  74. 12 0
      toolchain/semantics/testdata/operators/binary_op.carbon
  75. 85 0
      toolchain/semantics/testdata/operators/fail_assigment_to_non_assignable.carbon
  76. 12 0
      toolchain/semantics/testdata/operators/fail_type_mismatch.carbon
  77. 14 0
      toolchain/semantics/testdata/operators/fail_type_mismatch_assignment.carbon
  78. 14 0
      toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon
  79. 34 0
      toolchain/semantics/testdata/operators/or.carbon
  80. 10 0
      toolchain/semantics/testdata/operators/unary_op.carbon
  81. 17 0
      toolchain/semantics/testdata/pointer/address_of_deref.carbon
  82. 59 0
      toolchain/semantics/testdata/pointer/address_of_lvalue.carbon
  83. 17 0
      toolchain/semantics/testdata/pointer/basic.carbon
  84. 90 0
      toolchain/semantics/testdata/pointer/fail_address_of_value.carbon
  85. 16 0
      toolchain/semantics/testdata/pointer/fail_dereference_not_pointer.carbon
  86. 5 0
      toolchain/semantics/testdata/pointer/fail_dereference_type.carbon
  87. 12 0
      toolchain/semantics/testdata/pointer/fail_type_mismatch.carbon
  88. 12 0
      toolchain/semantics/testdata/pointer/nested_const.carbon
  89. 18 0
      toolchain/semantics/testdata/pointer/types.carbon
  90. 9 0
      toolchain/semantics/testdata/return/code_after_return.carbon
  91. 10 0
      toolchain/semantics/testdata/return/code_after_return_value.carbon
  92. 8 0
      toolchain/semantics/testdata/return/fail_missing_return.carbon
  93. 10 0
      toolchain/semantics/testdata/return/fail_missing_return_empty_tuple.carbon
  94. 10 0
      toolchain/semantics/testdata/return/fail_type_mismatch.carbon
  95. 10 0
      toolchain/semantics/testdata/return/fail_value_disallowed.carbon
  96. 9 0
      toolchain/semantics/testdata/return/fail_value_missing.carbon
  97. 9 0
      toolchain/semantics/testdata/return/missing_return_no_return_type.carbon
  98. 9 0
      toolchain/semantics/testdata/return/no_value.carbon
  99. 13 0
      toolchain/semantics/testdata/return/struct.carbon
  100. 19 0
      toolchain/semantics/testdata/return/tuple.carbon

+ 1 - 0
toolchain/driver/BUILD

@@ -22,6 +22,7 @@ cc_library(
         "//toolchain/lowering:lower_to_llvm",
         "//toolchain/parser:parse_tree",
         "//toolchain/semantics:semantics_ir",
+        "//toolchain/semantics:semantics_ir_formatter",
         "//toolchain/source:source_buffer",
         "@llvm-project//llvm:Core",
         "@llvm-project//llvm:Support",

+ 22 - 3
toolchain/driver/driver.cpp

@@ -19,6 +19,7 @@
 #include "toolchain/lowering/lower_to_llvm.h"
 #include "toolchain/parser/parse_tree.h"
 #include "toolchain/semantics/semantics_ir.h"
+#include "toolchain/semantics/semantics_ir_formatter.h"
 #include "toolchain/source/source_buffer.h"
 
 namespace Carbon {
@@ -116,6 +117,7 @@ auto Driver::RunHelpSubcommand(DiagnosticConsumer& /*consumer*/,
 enum class DumpMode {
   TokenizedBuffer,
   ParseTree,
+  RawSemanticsIR,
   SemanticsIR,
   LLVMIR,
   Assembly,
@@ -133,6 +135,7 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
   auto dump_mode = llvm::StringSwitch<DumpMode>(args.front())
                        .Case("tokens", DumpMode::TokenizedBuffer)
                        .Case("parse-tree", DumpMode::ParseTree)
+                       .Case("raw-semantics-ir", DumpMode::RawSemanticsIR)
                        .Case("semantics-ir", DumpMode::SemanticsIR)
                        .Case("llvm-ir", DumpMode::LLVMIR)
                        .Case("assembly", DumpMode::Assembly)
@@ -152,9 +155,16 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
     parse_tree_preorder = true;
   }
 
-  bool semantics_ir_include_builtins = false;
+  bool semantics_ir_include_raw = false;
   if (dump_mode == DumpMode::SemanticsIR && !args.empty() &&
-      args.front() == "--include_builtins") {
+      args.front() == "--include_raw") {
+    args = args.drop_front();
+    semantics_ir_include_raw = true;
+  }
+
+  bool semantics_ir_include_builtins = false;
+  if ((dump_mode == DumpMode::RawSemanticsIR || semantics_ir_include_raw) &&
+      !args.empty() && args.front() == "--include_builtins") {
     args = args.drop_front();
     semantics_ir_include_builtins = true;
   }
@@ -239,10 +249,19 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
       builtin_ir, tokenized_source, parse_tree, consumer, vlog_stream_);
   has_errors |= semantics_ir.has_errors();
   CARBON_VLOG() << "*** SemanticsIR::MakeFromParseTree done ***\n";
-  if (dump_mode == DumpMode::SemanticsIR) {
+  if (dump_mode == DumpMode::RawSemanticsIR) {
     semantics_ir.Print(output_stream_, semantics_ir_include_builtins);
     return !has_errors;
   }
+  if (dump_mode == DumpMode::SemanticsIR) {
+    if (semantics_ir_include_raw) {
+      semantics_ir.Print(output_stream_, semantics_ir_include_builtins);
+      output_stream_ << "\n";
+    }
+    FormatSemanticsIR(tokenized_source, parse_tree, semantics_ir,
+                      output_stream_);
+    return !has_errors;
+  }
   CARBON_VLOG() << "semantics_ir: " << semantics_ir;
 
   // Unlike previous steps, errors block further progress.

+ 17 - 0
toolchain/semantics/BUILD

@@ -95,6 +95,23 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "semantics_ir_formatter",
+    srcs = [
+        "semantics_ir_formatter.cpp",
+    ],
+    hdrs = [
+        "semantics_ir_formatter.h",
+    ],
+    deps = [
+        ":semantics_ir",
+        ":semantics_node_kind",
+        "//toolchain/lexer:tokenized_buffer",
+        "//toolchain/parser:parse_tree",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_test(
     name = "semantics_ir_test",
     size = "small",

+ 2 - 1
toolchain/semantics/semantics_file_test.cpp

@@ -15,7 +15,8 @@ class SemanticsFileTest : public DriverFileTestBase {
   using DriverFileTestBase::DriverFileTestBase;
 
   auto GetDefaultArgs() -> llvm::SmallVector<std::string> override {
-    return {"dump", "semantics-ir", "%s"};
+    // TODO: Remove the "--include_raw" once the textual IR format stabilizes.
+    return {"dump", "semantics-ir", "--include_raw", "%s"};
   }
 };
 

+ 5 - 1
toolchain/semantics/semantics_handle_function.cpp

@@ -27,7 +27,11 @@ static auto BuildFunctionDeclaration(SemanticsContext& context)
 
   // Add the callable.
   auto function_id = context.semantics_ir().AddFunction(
-      {.name_id = name_context.unresolved_name_id,
+      {.name_id =
+           name_context.state ==
+                   SemanticsDeclarationNameStack::Context::State::Unresolved
+               ? name_context.unresolved_name_id
+               : SemanticsStringId(SemanticsStringId::InvalidIndex),
        .param_refs_id = param_refs_id,
        .return_type_id = return_type_id,
        .body_block_ids = {}});

+ 13 - 7
toolchain/semantics/semantics_ir.cpp

@@ -5,6 +5,9 @@
 #include "toolchain/semantics/semantics_ir.h"
 
 #include "common/check.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include "toolchain/common/pretty_stack_trace_function.h"
 #include "toolchain/parser/parse_tree_node_location_translator.h"
 #include "toolchain/semantics/semantics_builtin_kind.h"
@@ -248,7 +251,8 @@ static auto GetTypePrecedence(SemanticsNodeKind kind) -> int {
   }
 }
 
-auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string {
+auto SemanticsIR::StringifyType(SemanticsTypeId type_id,
+                                bool in_type_context) const -> std::string {
   std::string str;
   llvm::raw_string_ostream out(str);
 
@@ -396,12 +400,14 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string {
   }
 
   // For `{}` or any tuple type, we've printed a non-type expression, so add a
-  // conversion to type `type`.
-  auto outer_node = GetNode(outer_node_id);
-  if (outer_node.kind() == SemanticsNodeKind::TupleType ||
-      (outer_node.kind() == SemanticsNodeKind::StructType &&
-       GetNodeBlock(outer_node.GetAsStructType()).empty())) {
-    out << " as type";
+  // conversion to type `type` if it's not implied by the context.
+  if (!in_type_context) {
+    auto outer_node = GetNode(outer_node_id);
+    if (outer_node.kind() == SemanticsNodeKind::TupleType ||
+        (outer_node.kind() == SemanticsNodeKind::StructType &&
+         GetNodeBlock(outer_node.GetAsStructType()).empty())) {
+      out << " as type";
+    }
   }
 
   return str;

+ 10 - 4
toolchain/semantics/semantics_ir.h

@@ -96,7 +96,8 @@ class SemanticsIR {
   }
 
   // Returns the requested callable.
-  auto GetFunction(SemanticsFunctionId function_id) const -> SemanticsFunction {
+  auto GetFunction(SemanticsFunctionId function_id) const
+      -> const SemanticsFunction& {
     return functions_[function_id.index];
   }
 
@@ -135,7 +136,7 @@ class SemanticsIR {
   }
 
   // Returns the requested name scope.
-  auto GetNameScope(SemanticsNameScopeId scope_id)
+  auto GetNameScope(SemanticsNameScopeId scope_id) const
       -> const llvm::DenseMap<SemanticsStringId, SemanticsNodeId>& {
     return name_scopes_[scope_id.index];
   }
@@ -266,11 +267,16 @@ class SemanticsIR {
     return type_blocks_[block_id.index];
   }
 
-  // Produces a string version of a type.
-  auto StringifyType(SemanticsTypeId type_id) -> std::string;
+  // Produces a string version of a type. If `in_type_context` is false, an
+  // explicit conversion to type `type` will be added in cases where the type
+  // expression would otherwise have a different type, such as a tuple or
+  // struct type.
+  auto StringifyType(SemanticsTypeId type_id,
+                     bool in_type_context = false) const -> std::string;
 
   auto functions_size() const -> int { return functions_.size(); }
   auto nodes_size() const -> int { return nodes_.size(); }
+  auto node_blocks_size() const -> int { return node_blocks_.size(); }
 
   auto types() const -> const llvm::SmallVector<SemanticsNodeId>& {
     return types_;

+ 671 - 0
toolchain/semantics/semantics_ir_formatter.cpp

@@ -0,0 +1,671 @@
+// 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
+
+#include "toolchain/semantics/semantics_ir_formatter.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/SaveAndRestore.h"
+#include "toolchain/lexer/tokenized_buffer.h"
+#include "toolchain/parser/parse_tree.h"
+
+namespace Carbon {
+
+namespace {
+// Assigns names to nodes, blocks, and scopes in the Semantics IR.
+//
+// TODOs / future work ideas:
+// - Add a documentation file for the textual format and link to the
+//   naming section here.
+// - Consider harmonizing `FooLiteral` vs. `foo_value` node names vs.
+//   IR names.
+//   - Also consider representing these as just `value` or `literal`
+//     in the IR and using the type to distinguish.
+// - Add block names based on the control flow construct names (`for`,
+//   `if`, `then`, `else`, ...). Either base this on the semantics or
+//   just on the keywords used to build that control flow -- little
+//   or no harm to getting it wrong. Tools will also want to be able
+//   to query the construct that resulted in control flow.
+class NodeNamer {
+ public:
+  enum class ScopeIndex : int {
+    None = -1,
+    Package = 0,
+  };
+
+  NodeNamer(const TokenizedBuffer& tokenized_buffer,
+            const ParseTree& parse_tree, const SemanticsIR& semantics_ir)
+      : tokenized_buffer_(tokenized_buffer),
+        parse_tree_(parse_tree),
+        semantics_ir_(semantics_ir) {
+    nodes.resize(semantics_ir.nodes_size());
+    labels.resize(semantics_ir.node_blocks_size());
+    scopes.resize(1 + semantics_ir.functions_size());
+
+    // Build the package scope.
+    GetScopeInfo(ScopeIndex::Package).name =
+        globals.AddNameUnchecked("package");
+    CollectNamesInBlock(ScopeIndex::Package, semantics_ir.top_node_block_id());
+
+    // Build each function scope.
+    for (int i = 0; i != semantics_ir.functions_size(); ++i) {
+      auto fn_id = SemanticsFunctionId(i);
+      auto fn_scope = GetScopeFor(fn_id);
+      const auto& fn = semantics_ir.GetFunction(fn_id);
+      GetScopeInfo(fn_scope).name = globals.AllocateName(
+          *this,
+          // TODO: Provide a location for the function for use as a
+          // disambiguator.
+          ParseTree::Node::Invalid,
+          fn.name_id.is_valid() ? semantics_ir.GetString(fn.name_id).str()
+                                : "");
+      CollectNamesInBlock(fn_scope, fn.param_refs_id);
+      for (auto block_id : fn.body_block_ids) {
+        AddBlockLabel(fn_scope, block_id,
+                      block_id == fn.body_block_ids.front() ? "entry" : "");
+        CollectNamesInBlock(fn_scope, block_id);
+      }
+    }
+  }
+
+  // Returns the scope index corresponding to a function.
+  auto GetScopeFor(SemanticsFunctionId fn_id) -> ScopeIndex {
+    return ScopeIndex(fn_id.index + 1);
+  }
+
+  // Returns the IR name to use for a function.
+  auto GetNameFor(SemanticsFunctionId fn_id) -> llvm::StringRef {
+    if (!fn_id.is_valid()) {
+      return "invalid";
+    }
+    return GetScopeInfo(GetScopeFor(fn_id)).name;
+  }
+
+  // Returns the IR name to use for a node, when referenced from a given scope.
+  auto GetNameFor(ScopeIndex scope_idx, SemanticsNodeId node_id)
+      -> std::string {
+    if (!node_id.is_valid()) {
+      return "invalid";
+    }
+
+    // Check for a builtin.
+    if (node_id.index < SemanticsBuiltinKind::ValidCount) {
+      return SemanticsBuiltinKind::FromInt(node_id.index).label().str();
+    }
+
+    auto& [node_scope, node_name] = nodes[node_id.index];
+    if (!node_name) {
+      // This should not happen in valid IR.
+      return "<unexpected noderef " + llvm::itostr(node_id.index) + ">";
+    }
+    if (node_scope == scope_idx) {
+      return node_name.str().str();
+    }
+    return (GetScopeInfo(node_scope).name.str() + "." + node_name.str()).str();
+  }
+
+  // Returns the IR name to use for a label, when referenced from a given scope.
+  auto GetLabelFor(ScopeIndex scope_idx, SemanticsNodeBlockId block_id)
+      -> std::string {
+    if (!block_id.is_valid()) {
+      return "!invalid";
+    }
+
+    auto& [label_scope, label_name] = labels[block_id.index];
+    if (!label_name) {
+      // This should not happen in valid IR.
+      return "<unexpected nodeblockref " + llvm::itostr(block_id.index) + ">";
+    }
+    if (label_scope == scope_idx) {
+      return label_name.str().str();
+    }
+    return (GetScopeInfo(label_scope).name.str() + "." + label_name.str())
+        .str();
+  }
+
+ private:
+  // A space in which unique names can be allocated.
+  struct Namespace {
+    // A result of a name lookup.
+    struct NameResult;
+
+    // A name in a namespace, which might be redirected to refer to another name
+    // for disambiguation purposes.
+    class Name {
+     public:
+      Name() : value_(nullptr) {}
+      explicit Name(llvm::StringMapIterator<NameResult> it) : value_(&*it) {}
+
+      explicit operator bool() const { return value_; }
+
+      auto str() const -> llvm::StringRef {
+        llvm::StringMapEntry<NameResult>* value = value_;
+        CARBON_CHECK(value) << "cannot print a null name";
+        while (value->second.ambiguous && value->second.fallback) {
+          value = value->second.fallback.value_;
+        }
+        return value->first();
+      }
+
+      operator llvm::StringRef() const { return str(); }
+
+      auto SetFallback(Name name) -> void { value_->second.fallback = name; }
+
+      auto SetAmbiguous() -> void { value_->second.ambiguous = true; }
+
+     private:
+      llvm::StringMapEntry<NameResult>* value_;
+    };
+
+    struct NameResult {
+      bool ambiguous = false;
+      Name fallback = Name();
+    };
+
+    llvm::StringRef prefix;
+    llvm::StringMap<NameResult> allocated = {};
+    int unnamed_count = 0;
+
+    auto AddNameUnchecked(llvm::StringRef name) -> Name {
+      return Name(allocated.insert({name, NameResult()}).first);
+    }
+
+    auto AllocateName(const NodeNamer& namer, ParseTree::Node node,
+                      std::string name = "") -> Name {
+      // The best (shortest) name for this node so far, and the current name
+      // for it.
+      Name best;
+      Name current;
+
+      // Add `name` as a name for this entity.
+      auto add_name = [&](bool mark_ambiguous = true) {
+        auto [it, added] = allocated.insert({name, NameResult()});
+        Name new_name = Name(it);
+
+        if (!added) {
+          if (mark_ambiguous) {
+            // This name was allocated for a different node. Mark it as
+            // ambiguous and keep looking for a name for this node.
+            new_name.SetAmbiguous();
+          }
+        } else {
+          if (!best) {
+            best = new_name;
+          } else {
+            CARBON_CHECK(current);
+            current.SetFallback(new_name);
+          }
+          current = new_name;
+        }
+        return added;
+      };
+
+      // All names start with the prefix.
+      name.insert(0, prefix);
+
+      // Use the given name if it's available and not just the prefix.
+      if (name.size() > prefix.size()) {
+        add_name();
+      }
+
+      // Append location information to try to disambiguate.
+      if (node.is_valid()) {
+        auto token = namer.parse_tree_.node_token(node);
+        llvm::raw_string_ostream(name)
+            << ".loc" << namer.tokenized_buffer_.GetLineNumber(token);
+        add_name();
+
+        llvm::raw_string_ostream(name)
+            << "_" << namer.tokenized_buffer_.GetColumnNumber(token);
+        add_name();
+      }
+
+      // Append numbers until we find an available name.
+      name += ".";
+      auto name_size_without_counter = name.size();
+      for (int counter = 1;; ++counter) {
+        name.resize(name_size_without_counter);
+        llvm::raw_string_ostream(name) << counter;
+        if (add_name(/*mark_ambiguous=*/false)) {
+          return best;
+        }
+      }
+    }
+  };
+
+  // A named scope that contains named entities.
+  struct Scope {
+    Namespace::Name name;
+    Namespace nodes = {.prefix = "%"};
+    Namespace labels = {.prefix = "!"};
+  };
+
+  auto GetScopeInfo(ScopeIndex scope_idx) -> Scope& {
+    return scopes[(int)scope_idx];
+  }
+
+  auto AddBlockLabel(ScopeIndex scope_idx, SemanticsNodeBlockId block_id,
+                     std::string name = "") -> void {
+    if (!block_id.is_valid()) {
+      return;
+    }
+
+    ParseTree::Node parse_node = ParseTree::Node::Invalid;
+    if (auto& block = semantics_ir_.GetNodeBlock(block_id); !block.empty()) {
+      parse_node = semantics_ir_.GetNode(block.front()).parse_node();
+    }
+
+    labels[block_id.index] = {scope_idx,
+                              GetScopeInfo(scope_idx).labels.AllocateName(
+                                  *this, parse_node, std::move(name))};
+  }
+
+  auto CollectNamesInBlock(ScopeIndex scope_idx, SemanticsNodeBlockId block_id)
+      -> void {
+    if (!block_id.is_valid()) {
+      return;
+    }
+
+    Scope& scope = GetScopeInfo(scope_idx);
+
+    // Use bound names where available. The BindName node appears after the node
+    // that it's giving a name to, so we need to do this before assigning
+    // fallback names.
+    for (auto node_id : semantics_ir_.GetNodeBlock(block_id)) {
+      auto node = semantics_ir_.GetNode(node_id);
+      if (node.kind() == SemanticsNodeKind::BindName) {
+        auto [name_id, named_node_id] = node.GetAsBindName();
+        nodes[named_node_id.index] = {
+            scope_idx,
+            scope.nodes.AllocateName(*this, node.parse_node(),
+                                     semantics_ir_.GetString(name_id).str())};
+      }
+    }
+
+    // Sequentially number all remaining values.
+    for (auto node_id : semantics_ir_.GetNodeBlock(block_id)) {
+      auto node = semantics_ir_.GetNode(node_id);
+      if (node.kind() != SemanticsNodeKind::BindName &&
+          node.kind().value_kind() != SemanticsNodeValueKind::None) {
+        auto& name = nodes[node_id.index];
+        if (!name.second) {
+          name = {scope_idx,
+                  scope.nodes.AllocateName(*this, node.parse_node())};
+        }
+      }
+    }
+  }
+
+  const TokenizedBuffer& tokenized_buffer_;
+  const ParseTree& parse_tree_;
+  const SemanticsIR& semantics_ir_;
+
+  Namespace globals = {.prefix = "@"};
+  std::vector<std::pair<ScopeIndex, Namespace::Name>> nodes;
+  std::vector<std::pair<ScopeIndex, Namespace::Name>> labels;
+  std::vector<Scope> scopes;
+};
+}  // namespace
+
+// Formatter for printing textual Semantics IR.
+class SemanticsIRFormatter {
+ public:
+  explicit SemanticsIRFormatter(const TokenizedBuffer& tokenized_buffer,
+                                const ParseTree& parse_tree,
+                                const SemanticsIR& semantics_ir,
+                                llvm::raw_ostream& out)
+      : semantics_ir_(semantics_ir),
+        out_(out),
+        node_namer_(tokenized_buffer, parse_tree, semantics_ir) {}
+
+  auto Format() -> void {
+    // TODO: Include information from the package declaration, once we fully
+    // support it.
+    out_ << "package {\n";
+    // TODO: Handle the case where there are multiple top-level node blocks.
+    // For example, there may be branching in the initializer of a global or a
+    // type expression.
+    if (auto block_id = semantics_ir_.top_node_block_id();
+        block_id.is_valid()) {
+      llvm::SaveAndRestore package_scope(scope_,
+                                         NodeNamer::ScopeIndex::Package);
+      FormatCodeBlock(block_id);
+    }
+    out_ << "}\n";
+
+    for (int i = 0; i != semantics_ir_.functions_size(); ++i) {
+      FormatFunction(SemanticsFunctionId(i));
+    }
+  }
+
+  auto FormatFunction(SemanticsFunctionId id) -> void {
+    const SemanticsFunction& fn = semantics_ir_.GetFunction(id);
+
+    out_ << "\nfn ";
+    FormatFunctionName(id);
+    out_ << "(";
+
+    llvm::SaveAndRestore function_scope(scope_, node_namer_.GetScopeFor(id));
+
+    llvm::ListSeparator sep;
+    for (const SemanticsNodeId param_id :
+         semantics_ir_.GetNodeBlock(fn.param_refs_id)) {
+      out_ << sep;
+      auto param = semantics_ir_.GetNode(param_id);
+      auto [name_id, node_id] = param.GetAsBindName();
+      FormatNodeName(node_id);
+      out_ << ": ";
+      FormatType(param.type_id());
+    }
+    out_ << ")";
+    if (fn.return_type_id.is_valid()) {
+      out_ << " -> ";
+      FormatType(fn.return_type_id);
+    }
+
+    if (!fn.body_block_ids.empty()) {
+      out_ << " {";
+
+      for (auto block_id : fn.body_block_ids) {
+        out_ << "\n";
+
+        FormatLabel(block_id);
+        out_ << ":\n";
+
+        FormatCodeBlock(block_id);
+      }
+
+      out_ << "}\n";
+    } else {
+      out_ << ";\n";
+    }
+  }
+
+  auto FormatCodeBlock(SemanticsNodeBlockId block_id) -> void {
+    if (!block_id.is_valid()) {
+      return;
+    }
+
+    for (const SemanticsNodeId node_id : semantics_ir_.GetNodeBlock(block_id)) {
+      FormatInstruction(node_id);
+    }
+  }
+
+  auto FormatInstruction(SemanticsNodeId node_id) -> void {
+    if (!node_id.is_valid()) {
+      out_ << "  " << SemanticsNodeKind::Invalid.ir_name() << "\n";
+      return;
+    }
+
+    FormatInstruction(node_id, semantics_ir_.GetNode(node_id));
+  }
+
+  auto FormatInstruction(SemanticsNodeId node_id, SemanticsNode node) -> void {
+    switch (node.kind()) {
+#define CARBON_SEMANTICS_NODE_KIND(Name)                   \
+  case SemanticsNodeKind::Name:                            \
+    FormatInstruction<SemanticsNode::Name>(node_id, node); \
+    break;
+#include "toolchain/semantics/semantics_node_kind.def"
+    }
+  }
+
+  template <typename Kind>
+  auto FormatInstruction(SemanticsNodeId node_id, SemanticsNode node) -> void {
+    out_ << "  ";
+    FormatInstructionLHS(node_id, node);
+    out_ << node.kind().ir_name();
+    FormatInstructionRHS<Kind>(node);
+    out_ << "\n";
+  }
+
+  auto FormatInstructionLHS(SemanticsNodeId node_id, SemanticsNode node)
+      -> void {
+    switch (node.kind().value_kind()) {
+      case SemanticsNodeValueKind::Typed:
+        FormatNodeName(node_id);
+        out_ << ": ";
+        FormatType(node.type_id());
+        out_ << " = ";
+        break;
+      case SemanticsNodeValueKind::Untyped:
+        FormatNodeName(node_id);
+        out_ << " = ";
+        break;
+      case SemanticsNodeValueKind::None:
+        break;
+    }
+  }
+
+  template <typename Kind>
+  auto FormatInstructionRHS(SemanticsNode node) -> void {
+    // By default, an instruction has a comma-separated argument list.
+    FormatArgs(Kind::Get(node));
+  }
+
+  // BindName is handled by the NodeNamer and doesn't appear in the output.
+  // These nodes are currently used simply to give a name to another node, and
+  // are never referenced themselves.
+  // TODO: Include BindName nodes in the output if we start referring to them.
+  template <>
+  auto FormatInstruction<SemanticsNode::BindName>(SemanticsNodeId,
+                                                  SemanticsNode) -> void {}
+
+  template <>
+  auto FormatInstructionRHS<SemanticsNode::BlockArg>(SemanticsNode node)
+      -> void {
+    out_ << " ";
+    FormatLabel(node.GetAsBlockArg());
+  }
+
+  template <>
+  auto FormatInstruction<SemanticsNode::BranchIf>(SemanticsNodeId,
+                                                  SemanticsNode node) -> void {
+    if (!in_terminator_sequence) {
+      out_ << "  ";
+    }
+    auto [label_id, cond_id] = node.GetAsBranchIf();
+    out_ << "if ";
+    FormatNodeName(cond_id);
+    out_ << " " << SemanticsNodeKind::Branch.ir_name() << " ";
+    FormatLabel(label_id);
+    out_ << " else ";
+    in_terminator_sequence = true;
+  }
+
+  template <>
+  auto FormatInstruction<SemanticsNode::BranchWithArg>(SemanticsNodeId,
+                                                       SemanticsNode node)
+      -> void {
+    if (!in_terminator_sequence) {
+      out_ << "  ";
+    }
+    auto [label_id, arg_id] = node.GetAsBranchWithArg();
+    out_ << SemanticsNodeKind::BranchWithArg.ir_name() << " ";
+    FormatLabel(label_id);
+    out_ << "(";
+    FormatNodeName(arg_id);
+    out_ << ")\n";
+    in_terminator_sequence = false;
+  }
+
+  template <>
+  auto FormatInstruction<SemanticsNode::Branch>(SemanticsNodeId,
+                                                SemanticsNode node) -> void {
+    if (!in_terminator_sequence) {
+      out_ << "  ";
+    }
+    out_ << SemanticsNodeKind::Branch.ir_name() << " ";
+    FormatLabel(node.GetAsBranch());
+    out_ << "\n";
+    in_terminator_sequence = false;
+  }
+
+  template <>
+  auto FormatInstructionRHS<SemanticsNode::Call>(SemanticsNode node) -> void {
+    out_ << " ";
+    auto [args_id, callee_id] = node.GetAsCall();
+    FormatArg(callee_id);
+    FormatArg(args_id);
+  }
+
+  template <>
+  auto FormatInstructionRHS<SemanticsNode::CrossReference>(SemanticsNode node)
+      -> void {
+    // TODO: Figure out a way to make this meaningful. We'll need some way to
+    // name cross-reference IRs, perhaps by the node ID of the import?
+    auto [xref_id, node_id] = node.GetAsCrossReference();
+    out_ << " " << xref_id << "." << node_id;
+  }
+
+  // StructTypeFields are formatted as part of their StructType.
+  template <>
+  auto FormatInstruction<SemanticsNode::StructTypeField>(SemanticsNodeId,
+                                                         SemanticsNode)
+      -> void {}
+
+  template <>
+  auto FormatInstructionRHS<SemanticsNode::StructType>(SemanticsNode node)
+      -> void {
+    out_ << " {";
+    llvm::ListSeparator sep;
+    for (auto field_id : semantics_ir_.GetNodeBlock(node.GetAsStructType())) {
+      out_ << sep << ".";
+      auto [field_name_id, field_type_id] =
+          semantics_ir_.GetNode(field_id).GetAsStructTypeField();
+      FormatString(field_name_id);
+      out_ << ": ";
+      FormatType(field_type_id);
+    }
+    out_ << "}";
+  }
+
+  auto FormatArgs(SemanticsNode::NoArgs) -> void {}
+
+  template <typename Arg1>
+  auto FormatArgs(Arg1 arg) -> void {
+    out_ << ' ';
+    FormatArg(arg);
+  }
+
+  template <typename Arg1, typename Arg2>
+  auto FormatArgs(std::pair<Arg1, Arg2> args) -> void {
+    out_ << ' ';
+    FormatArg(args.first);
+    out_ << ",";
+    FormatArgs(args.second);
+  }
+
+  auto FormatArg(SemanticsBoolValue v) -> void { out_ << v; }
+
+  auto FormatArg(SemanticsBuiltinKind kind) -> void { out_ << kind.label(); }
+
+  auto FormatArg(SemanticsFunctionId id) -> void { FormatFunctionName(id); }
+
+  auto FormatArg(SemanticsIntegerLiteralId id) -> void {
+    out_ << semantics_ir_.GetIntegerLiteral(id);
+  }
+
+  auto FormatArg(SemanticsMemberIndex index) -> void { out_ << index; }
+
+  // TODO: Should we be printing scopes inline, or should we have a separate
+  // step to print them like we do for functions?
+  auto FormatArg(SemanticsNameScopeId id) -> void {
+    // Name scopes aren't kept in any particular order. Sort the entries before
+    // we print them for stability and consistency.
+    std::vector<std::pair<SemanticsNodeId, SemanticsStringId>> entries;
+    for (auto [name_id, node_id] : semantics_ir_.GetNameScope(id)) {
+      entries.push_back({node_id, name_id});
+    }
+    llvm::sort(entries,
+               [](auto a, auto b) { return a.first.index < b.first.index; });
+
+    out_ << '{';
+    llvm::ListSeparator sep;
+    for (auto [node_id, name_id] : entries) {
+      out_ << sep << ".";
+      FormatString(name_id);
+      out_ << " = ";
+      FormatNodeName(node_id);
+    }
+    out_ << '}';
+  }
+
+  auto FormatArg(SemanticsNodeId id) -> void { FormatNodeName(id); }
+
+  auto FormatArg(SemanticsNodeBlockId id) -> void {
+    out_ << '(';
+    llvm::ListSeparator sep;
+    for (auto node_id : semantics_ir_.GetNodeBlock(id)) {
+      out_ << sep;
+      FormatArg(node_id);
+    }
+    out_ << ')';
+  }
+
+  auto FormatArg(SemanticsRealLiteralId id) -> void {
+    // TODO: Format with a `.` when the exponent is near zero.
+    const auto& real = semantics_ir_.GetRealLiteral(id);
+    out_ << real.mantissa << (real.is_decimal ? 'e' : 'p') << real.exponent;
+  }
+
+  auto FormatArg(SemanticsStringId id) -> void {
+    out_ << '"';
+    out_.write_escaped(semantics_ir_.GetString(id), /*UseHexEscapes=*/true);
+    out_ << '"';
+  }
+
+  auto FormatArg(SemanticsTypeId id) -> void { FormatType(id); }
+
+  auto FormatArg(SemanticsTypeBlockId id) -> void {
+    out_ << '(';
+    llvm::ListSeparator sep;
+    for (auto type_id : semantics_ir_.GetTypeBlock(id)) {
+      out_ << sep;
+      FormatArg(type_id);
+    }
+    out_ << ')';
+  }
+
+  auto FormatNodeName(SemanticsNodeId id) -> void {
+    out_ << node_namer_.GetNameFor(scope_, id);
+  }
+
+  auto FormatLabel(SemanticsNodeBlockId id) -> void {
+    out_ << node_namer_.GetLabelFor(scope_, id);
+  }
+
+  auto FormatString(SemanticsStringId id) -> void {
+    out_ << semantics_ir_.GetString(id);
+  }
+
+  auto FormatFunctionName(SemanticsFunctionId id) -> void {
+    out_ << node_namer_.GetNameFor(id);
+  }
+
+  auto FormatType(SemanticsTypeId id) -> void {
+    if (!id.is_valid()) {
+      out_ << "invalid";
+    } else {
+      out_ << semantics_ir_.StringifyType(id, /*in_type_context=*/true);
+    }
+  }
+
+ private:
+  const SemanticsIR& semantics_ir_;
+  llvm::raw_ostream& out_;
+  NodeNamer node_namer_;
+  NodeNamer::ScopeIndex scope_ = NodeNamer::ScopeIndex::None;
+  bool in_terminator_sequence = false;
+};
+
+auto FormatSemanticsIR(const TokenizedBuffer& tokenized_buffer,
+                       const ParseTree& parse_tree,
+                       const SemanticsIR& semantics_ir, llvm::raw_ostream& out)
+    -> void {
+  SemanticsIRFormatter(tokenized_buffer, parse_tree, semantics_ir, out)
+      .Format();
+}
+
+}  // namespace Carbon

+ 20 - 0
toolchain/semantics/semantics_ir_formatter.h

@@ -0,0 +1,20 @@
+// 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
+
+#ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_IR_FORMATTER_H_
+#define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_IR_FORMATTER_H_
+
+#include "llvm/Support/raw_ostream.h"
+#include "toolchain/semantics/semantics_ir.h"
+
+namespace Carbon {
+
+auto FormatSemanticsIR(const TokenizedBuffer& tokenized_buffer,
+                       const ParseTree& parse_tree,
+                       const SemanticsIR& semantics_ir, llvm::raw_ostream& out)
+    -> void;
+
+}  // namespace Carbon
+
+#endif  // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_IR_FORMATTER_H_

+ 1 - 1
toolchain/semantics/semantics_ir_test.cpp

@@ -32,7 +32,7 @@ TEST(SemanticsIRTest, YAML) {
                           llvm::MemoryBuffer::getMemBuffer("var x: i32 = 0;")));
   TestRawOstream print_stream;
   Driver d(fs, print_stream, llvm::errs());
-  d.RunFullCommand({"dump", "semantics-ir", "test.carbon"});
+  d.RunFullCommand({"dump", "raw-semantics-ir", "test.carbon"});
 
   // Matches the ID of a node. The numbers may change because of builtin
   // cross-references, so this code is only doing loose structural checks.

+ 18 - 1
toolchain/semantics/semantics_node_kind.cpp

@@ -11,9 +11,26 @@ CARBON_DEFINE_ENUM_CLASS_NAMES(SemanticsNodeKind) = {
 #include "toolchain/semantics/semantics_node_kind.def"
 };
 
+// Returns the name to use for this node kind in Semantics IR.
+[[nodiscard]] auto SemanticsNodeKind::ir_name() const -> llvm::StringRef {
+  static constexpr llvm::StringRef Table[] = {
+#define CARBON_SEMANTICS_NODE_KIND_WITH_IR_NAME(Name, IR_Name) IR_Name,
+#include "toolchain/semantics/semantics_node_kind.def"
+  };
+  return Table[AsInt()];
+}
+
+auto SemanticsNodeKind::value_kind() const -> SemanticsNodeValueKind {
+  static constexpr SemanticsNodeValueKind Table[] = {
+#define CARBON_SEMANTICS_NODE_KIND_WITH_VALUE_KIND(Name, ValueKind) \
+  SemanticsNodeValueKind::ValueKind,
+#include "toolchain/semantics/semantics_node_kind.def"
+  };
+  return Table[AsInt()];
+}
+
 auto SemanticsNodeKind::terminator_kind() const -> SemanticsTerminatorKind {
   static constexpr SemanticsTerminatorKind Table[] = {
-#define CARBON_SEMANTICS_NODE_KIND(Name) SemanticsTerminatorKind::NotTerminator,
 #define CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(Name, TerminatorKind) \
   SemanticsTerminatorKind::TerminatorKind,
 #include "toolchain/semantics/semantics_node_kind.def"

+ 72 - 43
toolchain/semantics/semantics_node_kind.def

@@ -7,57 +7,86 @@
 // inclusion to expand to the desired output. Macro definitions are cleaned up
 // at the end of this file.
 //
-// Supported x-macros are:
+// Exactly one of these macros should be defined before including this header:
 // - CARBON_SEMANTICS_NODE_KIND(Name)
-//   Defines a node kind.
+//   Invoked for each kind of semantic node.
+// - CARBON_SEMANTICS_NODE_KIND_WITH_VALUE_KIND(Name, TypeFieldKind)
+//   Invoked for each kind of semantic node, along with information about
+//   whether the node produces a value, and if so, what kind of value.
 // - CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(Name, TerminatorKind)
-//   Defines a node kind for a terminator node.
+//   Invoked for each kind of semantic node, along with information about
+//   whether the node is a terminator node.
+// - CARBON_SEMANTICS_NODE_KIND_WITH_IR_NAME(Name, IRName)
+//   Invoked for each kind of semantic node, along with the name that is used
+//   to denote this node in textual Semantics IR.
 
-#ifndef CARBON_SEMANTICS_NODE_KIND
-#error "Must define the x-macro to use this file."
-#endif
-
-#ifndef CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND
-#define CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(Name, TerminatorKind) \
+#if defined(CARBON_SEMANTICS_NODE_KIND)
+#define CARBON_SEMANTICS_NODE_KIND_IMPL(Name, IRName, ValueKind, \
+                                        TerminatorKind)          \
   CARBON_SEMANTICS_NODE_KIND(Name)
+#elif defined(CARBON_SEMANTICS_NODE_KIND_WITH_VALUE_KIND)
+#define CARBON_SEMANTICS_NODE_KIND_IMPL(Name, IRName, ValueKind, \
+                                        TerminatorKind)          \
+  CARBON_SEMANTICS_NODE_KIND_WITH_VALUE_KIND(Name, ValueKind)
+#elif defined(CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND)
+#define CARBON_SEMANTICS_NODE_KIND_IMPL(Name, IRName, ValueKind, \
+                                        TerminatorKind)          \
+  CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(Name, TerminatorKind)
+#elif defined(CARBON_SEMANTICS_NODE_KIND_WITH_IR_NAME)
+#define CARBON_SEMANTICS_NODE_KIND_IMPL(Name, IRName, ValueKind, \
+                                        TerminatorKind)          \
+  CARBON_SEMANTICS_NODE_KIND_WITH_IR_NAME(Name, IRName)
+#else
+#error "Must define the x-macro to use this file."
 #endif
 
-CARBON_SEMANTICS_NODE_KIND(Invalid)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Invalid, "invalid", None, NotTerminator)
 
 // A cross-reference between IRs.
-CARBON_SEMANTICS_NODE_KIND(CrossReference)
+CARBON_SEMANTICS_NODE_KIND_IMPL(CrossReference, "xref", Typed, NotTerminator)
 
-CARBON_SEMANTICS_NODE_KIND(AddressOf)
-CARBON_SEMANTICS_NODE_KIND(Assign)
-CARBON_SEMANTICS_NODE_KIND(BinaryOperatorAdd)
-CARBON_SEMANTICS_NODE_KIND(BindName)
-CARBON_SEMANTICS_NODE_KIND(BlockArg)
-CARBON_SEMANTICS_NODE_KIND(BoolLiteral)
-CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(Branch, Terminator)
-CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(BranchIf, TerminatorSequence)
-CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(BranchWithArg, Terminator)
-CARBON_SEMANTICS_NODE_KIND(Builtin)
-CARBON_SEMANTICS_NODE_KIND(Call)
-CARBON_SEMANTICS_NODE_KIND(ConstType)
-CARBON_SEMANTICS_NODE_KIND(Dereference)
-CARBON_SEMANTICS_NODE_KIND(FunctionDeclaration)
-CARBON_SEMANTICS_NODE_KIND(Index)
-CARBON_SEMANTICS_NODE_KIND(IntegerLiteral)
-CARBON_SEMANTICS_NODE_KIND(Namespace)
-CARBON_SEMANTICS_NODE_KIND(PointerType)
-CARBON_SEMANTICS_NODE_KIND(RealLiteral)
-CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(Return, Terminator)
-CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(ReturnExpression, Terminator)
-CARBON_SEMANTICS_NODE_KIND(StringLiteral)
-CARBON_SEMANTICS_NODE_KIND(StructMemberAccess)
-CARBON_SEMANTICS_NODE_KIND(StructType)
-CARBON_SEMANTICS_NODE_KIND(StructTypeField)
-CARBON_SEMANTICS_NODE_KIND(StructValue)
-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)
+CARBON_SEMANTICS_NODE_KIND_IMPL(AddressOf, "address_of", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Assign, "assign", None, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(BinaryOperatorAdd, "add", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(BindName, "bind_name", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(BlockArg, "block_arg", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(BoolLiteral, "bool_value", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Branch, "br", None, Terminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(BranchIf, "br", None, TerminatorSequence)
+CARBON_SEMANTICS_NODE_KIND_IMPL(BranchWithArg, "br", None, Terminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Builtin, "builtin", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Call, "call", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(ConstType, "const_type", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Dereference, "dereference", Typed,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(FunctionDeclaration, "fn_decl", Untyped,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Index, "tuple_index", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(IntegerLiteral, "int_value", Typed,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Namespace, "namespace", Untyped, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(PointerType, "ptr_type", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(RealLiteral, "real_value", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(Return, "return", None, Terminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(ReturnExpression, "return", None, Terminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(StringLiteral, "string_value", Typed,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(StructMemberAccess, "struct_access", Typed,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(StructType, "struct_type", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(StructTypeField, "struct_type_field", None,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(StructValue, "struct_value", Typed,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(StubReference, "stub_reference", Typed,
+                                NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(TupleType, "tuple_type", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(TupleValue, "tuple_value", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(UnaryOperatorNot, "not", Typed, NotTerminator)
+CARBON_SEMANTICS_NODE_KIND_IMPL(VarStorage, "var", Typed, NotTerminator)
 
-#undef CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND
 #undef CARBON_SEMANTICS_NODE_KIND
+#undef CARBON_SEMANTICS_NODE_KIND_WITH_VALUE_KIND
+#undef CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND
+#undef CARBON_SEMANTICS_NODE_KIND_WITH_IR_NAME
+#undef CARBON_SEMANTICS_NODE_KIND_IMPL

+ 19 - 0
toolchain/semantics/semantics_node_kind.h

@@ -17,6 +17,19 @@ CARBON_DEFINE_RAW_ENUM_CLASS(SemanticsNodeKind, uint8_t) {
 #include "toolchain/semantics/semantics_node_kind.def"
 };
 
+// Whether a node produces or represents a value, and if so, what kind of value.
+enum class SemanticsNodeValueKind {
+  // This node doesn't produce a value, and shouldn't be referenced by other
+  // nodes.
+  None,
+  // This node represents an untyped value. It may be referenced by other nodes
+  // expecting this kind of value.
+  Untyped,
+  // This node represents an expression or expression-like construct that
+  // produces a value of the type indicated by its `type_id` field.
+  Typed,
+};
+
 // Whether a node is a terminator or part of the terminator sequence. The nodes
 // in a block appear in the order NotTerminator, then TerminatorSequence, then
 // Terminator, which is also the numerical order of these values.
@@ -37,6 +50,12 @@ class SemanticsNodeKind : public CARBON_ENUM_BASE(SemanticsNodeKind) {
 
   using EnumBase::Create;
 
+  // Returns the name to use for this node kind in Semantics IR.
+  [[nodiscard]] auto ir_name() const -> llvm::StringRef;
+
+  // Returns whether this kind of node is expected to produce a value.
+  [[nodiscard]] auto value_kind() const -> SemanticsNodeValueKind;
+
   // Returns whether this node kind is a code block terminator, such as an
   // unconditional branch instruction, or part of the termination sequence,
   // such as a conditional branch instruction. The termination sequence of a

+ 1 - 1
toolchain/semantics/testdata/basics/builtin_nodes.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// ARGS: dump semantics-ir --include_builtins %s
+// ARGS: dump raw-semantics-ir --include_builtins %s
 //
 // AUTOUPDATE
 

+ 14 - 0
toolchain/semantics/testdata/basics/builtin_types.carbon

@@ -70,3 +70,17 @@ var test_type: type = i32;
 // CHECK:STDOUT:     node+14,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %test_i32: i32 = var
+// CHECK:STDOUT:   %.loc7: i32 = int_value 0
+// CHECK:STDOUT:   assign %test_i32, %.loc7
+// CHECK:STDOUT:   %test_f64: f64 = var
+// CHECK:STDOUT:   %.loc8: f64 = real_value 1e-1
+// CHECK:STDOUT:   assign %test_f64, %.loc8
+// CHECK:STDOUT:   %test_str: String = var
+// CHECK:STDOUT:   %.loc9: String = string_value "Test"
+// CHECK:STDOUT:   assign %test_str, %.loc9
+// CHECK:STDOUT:   %test_type: type = var
+// CHECK:STDOUT:   assign %test_type, i32
+// CHECK:STDOUT: }

+ 3 - 0
toolchain/semantics/testdata/basics/empty.carbon

@@ -23,3 +23,6 @@
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT: }

+ 3 - 0
toolchain/semantics/testdata/basics/empty_decl.carbon

@@ -25,3 +25,6 @@
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/basics/fail_name_lookup.carbon

@@ -41,3 +41,12 @@ fn Main() {
 // CHECK:STDOUT:     node+1,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 6 - 0
toolchain/semantics/testdata/basics/fail_non_type_as_type.carbon

@@ -41,3 +41,9 @@ var x: type = 42;
 // CHECK:STDOUT:     node+3,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %x: type = var
+// CHECK:STDOUT:   %.loc10: i32 = int_value 42
+// CHECK:STDOUT:   assign %x, <error>
+// CHECK:STDOUT: }

+ 6 - 0
toolchain/semantics/testdata/basics/fail_qualifier_unsupported.carbon

@@ -45,3 +45,9 @@ var y: i32 = x.b;
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %x: i32 = var
+// CHECK:STDOUT:   %y: i32 = var
+// CHECK:STDOUT:   assign %y, <error>
+// CHECK:STDOUT: }

+ 8 - 0
toolchain/semantics/testdata/basics/parens.carbon

@@ -43,3 +43,11 @@ var test_i32: i32 = ((1) + (2));
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %test_i32: i32 = var
+// CHECK:STDOUT:   %.loc7_23: i32 = int_value 1
+// CHECK:STDOUT:   %.loc7_29: i32 = int_value 2
+// CHECK:STDOUT:   %.loc7_26: i32 = add %.loc7_23, %.loc7_29
+// CHECK:STDOUT:   assign %test_i32, %.loc7_26
+// CHECK:STDOUT: }

+ 21 - 0
toolchain/semantics/testdata/basics/textual_ir.carbon

@@ -0,0 +1,21 @@
+// 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
+//
+// ARGS: dump semantics-ir %s
+//
+// Just check that the raw IR format isn't included in `dump semantics-ir` mode.
+// AUTOUPDATE
+
+fn Foo() {
+  return;
+}
+
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc10 = fn_decl @Foo
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 1 - 1
toolchain/semantics/testdata/basics/verbose.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// ARGS: -v dump semantics-ir %s
+// ARGS: -v dump raw-semantics-ir %s
 //
 // Only checks a couple statements in order to minimize manual update churn.
 // NOAUTOUPDATE

+ 13 - 0
toolchain/semantics/testdata/const/collapse.carbon

@@ -69,3 +69,16 @@ fn F(p: const i32**) -> const (const i32)** {
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc11_32: type = const_type i32
+// CHECK:STDOUT:   %.loc11_25: type = const_type const i32
+// CHECK:STDOUT:   %.loc11_42: type = ptr_type const i32
+// CHECK:STDOUT:   %.loc11_43: type = ptr_type const i32*
+// CHECK:STDOUT:   %.loc11_1 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%p: const i32**) -> const i32** {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return %p
+// CHECK:STDOUT: }

+ 11 - 0
toolchain/semantics/testdata/const/fail_collapse.carbon

@@ -71,3 +71,14 @@ fn G(p: const (const i32)**) -> i32** {
 // CHECK:STDOUT:     node+9,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc10_36: type = ptr_type i32
+// CHECK:STDOUT:   %.loc10_37: type = ptr_type i32*
+// CHECK:STDOUT:   %.loc10_1 = fn_decl @G
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G(%p: const i32**) -> i32** {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

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

@@ -75,3 +75,22 @@ fn Main() {
 // CHECK:STDOUT:     node+9,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_20: {} = struct_value ()
+// CHECK:STDOUT:   %.loc7_1 = fn_decl @Echo
+// CHECK:STDOUT:   %.loc11 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Echo(%a: {}) -> {} {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return %a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc12_9.1: {} = struct_value ()
+// CHECK:STDOUT:   %.loc12_9.2: {} = stub_reference %.loc12_9.1
+// CHECK:STDOUT:   %.loc12_7: {} = call @Echo(%.loc12_9.2)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

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

@@ -77,3 +77,22 @@ fn Main() {
 // CHECK:STDOUT:     node+9,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_20: () = tuple_value ()
+// CHECK:STDOUT:   %.loc7_1 = fn_decl @Echo
+// CHECK:STDOUT:   %.loc11 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Echo(%a: ()) -> () {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return %a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc12_9.1: () = tuple_value ()
+// CHECK:STDOUT:   %.loc12_9.2: () = stub_reference %.loc12_9.1
+// CHECK:STDOUT:   %.loc12_7: () = call @Echo(%.loc12_9.2)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 39 - 0
toolchain/semantics/testdata/function/call/fail_param_count.carbon

@@ -178,3 +178,42 @@ fn Main() {
 // CHECK:STDOUT:     node+24,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Run0
+// CHECK:STDOUT:   %.loc8 = fn_decl @Run1
+// CHECK:STDOUT:   %.loc9 = fn_decl @Run2
+// CHECK:STDOUT:   %.loc11 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run0() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run1(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run2(%a: i32, %b: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc18_8.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc18_8.2: i32 = stub_reference %.loc18_8.1
+// CHECK:STDOUT:   %.loc25_8.1: i32 = int_value 0
+// CHECK:STDOUT:   %.loc25_8.2: i32 = stub_reference %.loc25_8.1
+// CHECK:STDOUT:   %.loc25_11.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc25_11.2: i32 = stub_reference %.loc25_11.1
+// CHECK:STDOUT:   %.loc40_8.1: i32 = int_value 0
+// CHECK:STDOUT:   %.loc40_8.2: i32 = stub_reference %.loc40_8.1
+// CHECK:STDOUT:   %.loc40_11.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc40_11.2: i32 = stub_reference %.loc40_11.1
+// CHECK:STDOUT:   %.loc55_8.1: i32 = int_value 0
+// CHECK:STDOUT:   %.loc55_8.2: i32 = stub_reference %.loc55_8.1
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 17 - 0
toolchain/semantics/testdata/function/call/fail_param_type.carbon

@@ -73,3 +73,20 @@ fn Main() {
 // CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Run
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc16_7.1: f64 = real_value 10e-1
+// CHECK:STDOUT:   %.loc16_7.2: f64 = stub_reference %.loc16_7.1
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 19 - 0
toolchain/semantics/testdata/function/call/fail_return_type_mismatch.carbon

@@ -64,3 +64,22 @@ fn Run() {
 // CHECK:STDOUT:     node+8,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc9 = fn_decl @Run
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo() -> f64 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7: f64 = real_value 10e-1
+// CHECK:STDOUT:   return %.loc7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %x: i32 = var
+// CHECK:STDOUT:   %.loc13: f64 = call @Foo()
+// CHECK:STDOUT:   assign %x, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 20 - 0
toolchain/semantics/testdata/function/call/i32.carbon

@@ -77,3 +77,23 @@ fn Main() {
 // CHECK:STDOUT:     node+8,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Echo
+// CHECK:STDOUT:   %.loc11 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Echo(%a: i32) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return %a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %b: i32 = var
+// CHECK:STDOUT:   %.loc12_21.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc12_21.2: i32 = stub_reference %.loc12_21.1
+// CHECK:STDOUT:   %.loc12_20: i32 = call @Echo(%.loc12_21.2)
+// CHECK:STDOUT:   assign %b, %.loc12_20
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 29 - 0
toolchain/semantics/testdata/function/call/more_param_ir.carbon

@@ -113,3 +113,32 @@ fn Main() {
 // CHECK:STDOUT:     node+20,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32, %b: i32, %c: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_7: i32 = int_value 1
+// CHECK:STDOUT:   %.loc11_11: i32 = int_value 2
+// CHECK:STDOUT:   %.loc11_9: i32 = add %.loc11_7, %.loc11_11
+// CHECK:STDOUT:   %.loc11_15: i32 = int_value 3
+// CHECK:STDOUT:   %.loc11_13.1: i32 = add %.loc11_9, %.loc11_15
+// CHECK:STDOUT:   %.loc11_13.2: i32 = stub_reference %.loc11_13.1
+// CHECK:STDOUT:   %.loc11_18: i32 = int_value 4
+// CHECK:STDOUT:   %.loc11_22: i32 = int_value 5
+// CHECK:STDOUT:   %.loc11_20.1: i32 = add %.loc11_18, %.loc11_22
+// CHECK:STDOUT:   %.loc11_20.2: i32 = stub_reference %.loc11_20.1
+// CHECK:STDOUT:   %.loc11_25.1: i32 = int_value 6
+// CHECK:STDOUT:   %.loc11_25.2: i32 = stub_reference %.loc11_25.1
+// CHECK:STDOUT:   %.loc11_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc11_6.2: () = call @Foo(%.loc11_13.2, %.loc11_20.2, %.loc11_25.2)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 19 - 0
toolchain/semantics/testdata/function/call/params_one.carbon

@@ -73,3 +73,22 @@ fn Main() {
 // CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_7.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc10_7.2: i32 = stub_reference %.loc10_7.1
+// CHECK:STDOUT:   %.loc10_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc10_6.2: () = call @Foo(%.loc10_7.2)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 22 - 0
toolchain/semantics/testdata/function/call/params_one_comma.carbon

@@ -84,3 +84,25 @@ fn Main() {
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_7.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc10_7.2: i32 = stub_reference %.loc10_7.1
+// CHECK:STDOUT:   %.loc10_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc10_6.2: () = call @Foo(%.loc10_7.2)
+// CHECK:STDOUT:   %.loc11_7.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc11_7.2: i32 = stub_reference %.loc11_7.1
+// CHECK:STDOUT:   %.loc11_6: () = call @Foo(%.loc11_7.2)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 21 - 0
toolchain/semantics/testdata/function/call/params_two.carbon

@@ -85,3 +85,24 @@ fn Main() {
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32, %b: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_7.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc10_7.2: i32 = stub_reference %.loc10_7.1
+// CHECK:STDOUT:   %.loc10_10.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc10_10.2: i32 = stub_reference %.loc10_10.1
+// CHECK:STDOUT:   %.loc10_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc10_6.2: () = call @Foo(%.loc10_7.2, %.loc10_10.2)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 26 - 0
toolchain/semantics/testdata/function/call/params_two_comma.carbon

@@ -102,3 +102,29 @@ fn Main() {
 // CHECK:STDOUT:     node+16,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32, %b: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_7.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc10_7.2: i32 = stub_reference %.loc10_7.1
+// CHECK:STDOUT:   %.loc10_10.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc10_10.2: i32 = stub_reference %.loc10_10.1
+// CHECK:STDOUT:   %.loc10_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc10_6.2: () = call @Foo(%.loc10_7.2, %.loc10_10.2)
+// CHECK:STDOUT:   %.loc11_7.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc11_7.2: i32 = stub_reference %.loc11_7.1
+// CHECK:STDOUT:   %.loc11_10.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc11_10.2: i32 = stub_reference %.loc11_10.1
+// CHECK:STDOUT:   %.loc11_6: () = call @Foo(%.loc11_7.2, %.loc11_10.2)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 17 - 0
toolchain/semantics/testdata/function/call/params_zero.carbon

@@ -54,3 +54,20 @@ fn Main() {
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc10_6.2: () = call @Foo()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

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

@@ -64,3 +64,23 @@ fn Main() {
 // CHECK:STDOUT:     node+9,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @MakeImplicitEmptyTuple
+// CHECK:STDOUT:   %.loc10 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MakeImplicitEmptyTuple() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_11.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc11_11.2: () = tuple_value ()
+// CHECK:STDOUT:   %b: () = var
+// CHECK:STDOUT:   %.loc11_37: () = call @MakeImplicitEmptyTuple()
+// CHECK:STDOUT:   assign %b, %.loc11_37
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 14 - 0
toolchain/semantics/testdata/function/declaration/simple.carbon

@@ -48,3 +48,17 @@ fn G() { F(); }
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT:   %.loc9 = fn_decl @G
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc9_11.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc9_11.2: () = call @F()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/function/definition/fail_param_name_conflict.carbon

@@ -57,3 +57,12 @@ fn Bar(a: i32, a: i32) {}
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc13 = fn_decl @Bar
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Bar(%a.loc13_8: i32, %a.loc13_16: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 21 - 0
toolchain/semantics/testdata/function/definition/order.carbon

@@ -53,3 +53,24 @@ fn Baz() {}
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc8 = fn_decl @Bar
+// CHECK:STDOUT:   %.loc9 = fn_decl @Baz
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Bar() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Baz() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/function/definition/params_one.carbon

@@ -46,3 +46,12 @@ fn Foo(a: i32) {}
 // CHECK:STDOUT:     node+3,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/function/definition/params_one_comma.carbon

@@ -46,3 +46,12 @@ fn Foo(a: i32,) {}
 // CHECK:STDOUT:     node+3,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/function/definition/params_two.carbon

@@ -52,3 +52,12 @@ fn Foo(a: i32, b: i32) {}
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32, %b: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/function/definition/params_two_comma.carbon

@@ -52,3 +52,12 @@ fn Foo(a: i32, b: i32,) {}
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32, %b: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/function/definition/params_zero.carbon

@@ -35,3 +35,12 @@ fn Foo() {}
 // CHECK:STDOUT:     node+1,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 15 - 0
toolchain/semantics/testdata/function/definition/same_param_name.carbon

@@ -64,3 +64,18 @@ fn Bar(a: i32) {}
 // CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Foo
+// CHECK:STDOUT:   %.loc8 = fn_decl @Bar
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Foo(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Bar(%a: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 40 - 0
toolchain/semantics/testdata/if/else.carbon

@@ -106,3 +106,43 @@ fn If(b: bool) {
 // CHECK:STDOUT:     node+17,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT:   %.loc8 = fn_decl @G
+// CHECK:STDOUT:   %.loc9 = fn_decl @H
+// CHECK:STDOUT:   %.loc11 = fn_decl @If
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @H() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @If(%b: bool) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc13 else br !.loc15
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc13:
+// CHECK:STDOUT:   %.loc13_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc13_6.2: () = call @F()
+// CHECK:STDOUT:   br !.loc17
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc15:
+// CHECK:STDOUT:   %.loc15: () = call @G()
+// CHECK:STDOUT:   br !.loc17
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc17:
+// CHECK:STDOUT:   %.loc17: () = call @H()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 45 - 0
toolchain/semantics/testdata/if/fail_reachable_fallthrough.carbon

@@ -150,3 +150,48 @@ fn If3(b: bool) -> i32 {
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @If1
+// CHECK:STDOUT:   %.loc17 = fn_decl @If2
+// CHECK:STDOUT:   %.loc27 = fn_decl @If3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @If1(%b: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc9 else br !.loc8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc9:
+// CHECK:STDOUT:   %.loc9: i32 = int_value 1
+// CHECK:STDOUT:   return %.loc9
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8:
+// CHECK:STDOUT:   br !.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.1:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @If2(%b: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc18 else br !.loc20
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc18:
+// CHECK:STDOUT:   br !.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc20:
+// CHECK:STDOUT:   %.loc20: i32 = int_value 2
+// CHECK:STDOUT:   return %.loc20
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.1:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @If3(%b: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc29 else br !.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc29:
+// CHECK:STDOUT:   %.loc29: i32 = int_value 1
+// CHECK:STDOUT:   return %.loc29
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.1:
+// CHECK:STDOUT: }

+ 18 - 0
toolchain/semantics/testdata/if/fail_scope.carbon

@@ -77,3 +77,21 @@ fn VarScope(b: bool) -> i32 {
 // CHECK:STDOUT:     node+11,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @VarScope
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @VarScope(%b: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc9 else br !.loc15
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc9:
+// CHECK:STDOUT:   %n: i32 = var
+// CHECK:STDOUT:   %.loc9: i32 = int_value 2
+// CHECK:STDOUT:   assign %n, %.loc9
+// CHECK:STDOUT:   return %n
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc15:
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

+ 30 - 0
toolchain/semantics/testdata/if/no_else.carbon

@@ -89,3 +89,33 @@ fn If(b: bool) {
 // CHECK:STDOUT:     node+13,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT:   %.loc8 = fn_decl @G
+// CHECK:STDOUT:   %.loc10 = fn_decl @If
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @If(%b: bool) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc12 else br !.loc14
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc12:
+// CHECK:STDOUT:   %.loc12_6.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc12_6.2: () = call @F()
+// CHECK:STDOUT:   br !.loc14
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc14:
+// CHECK:STDOUT:   %.loc14: () = call @G()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 17 - 0
toolchain/semantics/testdata/if/unreachable_fallthrough.carbon

@@ -70,3 +70,20 @@ fn If(b: bool) -> i32 {
 // CHECK:STDOUT:     node+8,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @If
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @If(%b: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc9 else br !.loc11
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc9:
+// CHECK:STDOUT:   %.loc9: i32 = int_value 1
+// CHECK:STDOUT:   return %.loc9
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11:
+// CHECK:STDOUT:   %.loc11: i32 = int_value 2
+// CHECK:STDOUT:   return %.loc11
+// CHECK:STDOUT: }

+ 21 - 0
toolchain/semantics/testdata/if_expression/basic.carbon

@@ -81,3 +81,24 @@ fn F(b: bool, n: i32, m: i32) -> i32 {
 // CHECK:STDOUT:     node+14,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%b: bool, %n: i32, %m: i32) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc8_22 else br !.loc8_33
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_22:
+// CHECK:STDOUT:   %.loc8_22: i32 = add %n, %m
+// CHECK:STDOUT:   br !.loc8_10(%.loc8_22)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_33:
+// CHECK:STDOUT:   %.loc8_33: i32 = add %m, %n
+// CHECK:STDOUT:   br !.loc8_10(%.loc8_33)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_10:
+// CHECK:STDOUT:   %.loc8_10: i32 = block_arg !.loc8_10
+// CHECK:STDOUT:   return %.loc8_10
+// CHECK:STDOUT: }

+ 55 - 0
toolchain/semantics/testdata/if_expression/constant_condition.carbon

@@ -120,3 +120,58 @@ fn G() -> i32 {
 // CHECK:STDOUT:     node+25,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @A
+// CHECK:STDOUT:   %.loc8 = fn_decl @B
+// CHECK:STDOUT:   %.loc10 = fn_decl @F
+// CHECK:STDOUT:   %.loc14 = fn_decl @G
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7: i32 = int_value 1
+// CHECK:STDOUT:   return %.loc7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @B() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8: i32 = int_value 2
+// CHECK:STDOUT:   return %.loc8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_13: bool = bool_value true
+// CHECK:STDOUT:   if %.loc11_13 br !.loc11_24 else br !.loc11_33
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_24:
+// CHECK:STDOUT:   %.loc11_24: i32 = call @A()
+// CHECK:STDOUT:   br !.loc11_10(%.loc11_24)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_33:
+// CHECK:STDOUT:   %.loc11_33: i32 = call @B()
+// CHECK:STDOUT:   br !.loc11_10(%.loc11_33)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_10:
+// CHECK:STDOUT:   %.loc11_10: i32 = block_arg !.loc11_10
+// CHECK:STDOUT:   return %.loc11_10
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc15_13: bool = bool_value false
+// CHECK:STDOUT:   if %.loc15_13 br !.loc15_25 else br !.loc15_34
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc15_25:
+// CHECK:STDOUT:   %.loc15_25: i32 = call @A()
+// CHECK:STDOUT:   br !.loc15_10(%.loc15_25)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc15_34:
+// CHECK:STDOUT:   %.loc15_34: i32 = call @B()
+// CHECK:STDOUT:   br !.loc15_10(%.loc15_34)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc15_10:
+// CHECK:STDOUT:   %.loc15_10: i32 = block_arg !.loc15_10
+// CHECK:STDOUT:   return %.loc15_10
+// CHECK:STDOUT: }

+ 35 - 0
toolchain/semantics/testdata/if_expression/control_flow.carbon

@@ -94,3 +94,38 @@ fn F(b: bool) -> i32 {
 // CHECK:STDOUT:     node+16,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @A
+// CHECK:STDOUT:   %.loc8 = fn_decl @B
+// CHECK:STDOUT:   %.loc10 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7: i32 = int_value 1
+// CHECK:STDOUT:   return %.loc7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @B() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8: i32 = int_value 2
+// CHECK:STDOUT:   return %.loc8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%b: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %b br !.loc11_21 else br !.loc11_30
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_21:
+// CHECK:STDOUT:   %.loc11_21: i32 = call @A()
+// CHECK:STDOUT:   br !.loc11_10(%.loc11_21)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_30:
+// CHECK:STDOUT:   %.loc11_30: i32 = call @B()
+// CHECK:STDOUT:   br !.loc11_10(%.loc11_30)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_10:
+// CHECK:STDOUT:   %.loc11_10: i32 = block_arg !.loc11_10
+// CHECK:STDOUT:   return %.loc11_10
+// CHECK:STDOUT: }

+ 43 - 0
toolchain/semantics/testdata/if_expression/nested.carbon

@@ -121,3 +121,46 @@ fn F(a: bool, b: bool, c: bool) -> i32 {
 // CHECK:STDOUT:     node+26,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%a: bool, %b: bool, %c: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   if %a br !.loc8_20.1 else br !.loc8_44.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_20.1:
+// CHECK:STDOUT:   if %b br !.loc8_30 else br !.loc8_37
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_30:
+// CHECK:STDOUT:   %.loc8_30: i32 = int_value 1
+// CHECK:STDOUT:   br !.loc8_20.2(%.loc8_30)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_37:
+// CHECK:STDOUT:   %.loc8_37: i32 = int_value 2
+// CHECK:STDOUT:   br !.loc8_20.2(%.loc8_37)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_20.2:
+// CHECK:STDOUT:   %.loc8_20: i32 = block_arg !.loc8_20.2
+// CHECK:STDOUT:   br !.loc8_10(%.loc8_20)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_44.1:
+// CHECK:STDOUT:   if %c br !.loc8_54 else br !.loc8_61
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_54:
+// CHECK:STDOUT:   %.loc8_54: i32 = int_value 3
+// CHECK:STDOUT:   br !.loc8_44.2(%.loc8_54)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_61:
+// CHECK:STDOUT:   %.loc8_61: i32 = int_value 4
+// CHECK:STDOUT:   br !.loc8_44.2(%.loc8_61)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_44.2:
+// CHECK:STDOUT:   %.loc8_44: i32 = block_arg !.loc8_44.2
+// CHECK:STDOUT:   br !.loc8_10(%.loc8_44)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc8_10:
+// CHECK:STDOUT:   %.loc8_10: i32 = block_arg !.loc8_10
+// CHECK:STDOUT:   return %.loc8_10
+// CHECK:STDOUT: }

+ 20 - 0
toolchain/semantics/testdata/index/element_access.carbon

@@ -92,3 +92,23 @@ var c: i32 = b[0];
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_9: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_13.1: type = tuple_type (type)
+// CHECK:STDOUT:   %.loc7_13.2: (type,) = tuple_value (%.loc7_9)
+// CHECK:STDOUT:   %.loc7_13.3: type = tuple_type (i32)
+// CHECK:STDOUT:   %a: (i32,) = var
+// CHECK:STDOUT:   %.loc7_18.1: i32 = int_value 12
+// CHECK:STDOUT:   %.loc7_18.2: i32 = stub_reference %.loc7_18.1
+// CHECK:STDOUT:   %.loc7_21: (i32,) = tuple_value (%.loc7_18.2)
+// CHECK:STDOUT:   assign %a, %.loc7_21
+// CHECK:STDOUT:   %.loc8_9: type = stub_reference i32
+// CHECK:STDOUT:   %.loc8_13: (type,) = tuple_value (%.loc8_9)
+// CHECK:STDOUT:   %b: (i32,) = var
+// CHECK:STDOUT:   assign %b, %a
+// CHECK:STDOUT:   %c: i32 = var
+// CHECK:STDOUT:   %.loc9_16: i32 = int_value 0
+// CHECK:STDOUT:   %.loc9_17: i32 = tuple_index %b, %.loc9_16
+// CHECK:STDOUT:   assign %c, %.loc9_17
+// CHECK:STDOUT: }

+ 18 - 0
toolchain/semantics/testdata/index/fail_empty_tuple_access.carbon

@@ -61,3 +61,21 @@ fn Run() {
 // CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT:   %.loc9 = fn_decl @Run
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc13_4.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc13_4.2: () = call @F()
+// CHECK:STDOUT:   %.loc13_7: i32 = int_value 0
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 19 - 0
toolchain/semantics/testdata/index/fail_large_index.carbon

@@ -93,3 +93,22 @@ var c: i32 = b[0xFFFFFFFFFFFFFFFFF];
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_9: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_13.1: type = tuple_type (type)
+// CHECK:STDOUT:   %.loc7_13.2: (type,) = tuple_value (%.loc7_9)
+// CHECK:STDOUT:   %.loc7_13.3: type = tuple_type (i32)
+// CHECK:STDOUT:   %a: (i32,) = var
+// CHECK:STDOUT:   %.loc7_18.1: i32 = int_value 12
+// CHECK:STDOUT:   %.loc7_18.2: i32 = stub_reference %.loc7_18.1
+// CHECK:STDOUT:   %.loc7_21: (i32,) = tuple_value (%.loc7_18.2)
+// CHECK:STDOUT:   assign %a, %.loc7_21
+// CHECK:STDOUT:   %.loc8_9: type = stub_reference i32
+// CHECK:STDOUT:   %.loc8_13: (type,) = tuple_value (%.loc8_9)
+// CHECK:STDOUT:   %b: (i32,) = var
+// CHECK:STDOUT:   assign %b, %a
+// CHECK:STDOUT:   %c: i32 = var
+// CHECK:STDOUT:   %.loc12: i32 = int_value -1
+// CHECK:STDOUT:   assign %c, <error>
+// CHECK:STDOUT: }

+ 12 - 0
toolchain/semantics/testdata/index/fail_name_not_found.carbon

@@ -52,3 +52,15 @@ fn Main() {
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %b: i32 = var
+// CHECK:STDOUT:   %.loc11: i32 = int_value 0
+// CHECK:STDOUT:   assign %b, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 3 - 0
toolchain/semantics/testdata/index/fail_negative_indexing.carbon

@@ -87,3 +87,6 @@ var b: i32 = a[-10];
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT: }

+ 20 - 0
toolchain/semantics/testdata/index/fail_non_deterministic_type.carbon

@@ -97,3 +97,23 @@ var c: i32 = a[b];
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_9: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_14: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_17.1: type = tuple_type (type, type)
+// CHECK:STDOUT:   %.loc7_17.2: (type, type) = tuple_value (%.loc7_9, %.loc7_14)
+// CHECK:STDOUT:   %.loc7_17.3: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %a: (i32, i32) = var
+// CHECK:STDOUT:   %.loc7_22.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc7_22.2: i32 = stub_reference %.loc7_22.1
+// CHECK:STDOUT:   %.loc7_25.1: i32 = int_value 3
+// CHECK:STDOUT:   %.loc7_25.2: i32 = stub_reference %.loc7_25.1
+// CHECK:STDOUT:   %.loc7_26: (i32, i32) = tuple_value (%.loc7_22.2, %.loc7_25.2)
+// CHECK:STDOUT:   assign %a, %.loc7_26
+// CHECK:STDOUT:   %b: i32 = var
+// CHECK:STDOUT:   %.loc8: i32 = int_value 0
+// CHECK:STDOUT:   assign %b, %.loc8
+// CHECK:STDOUT:   %c: i32 = var
+// CHECK:STDOUT:   assign %c, <error>
+// CHECK:STDOUT: }

+ 18 - 0
toolchain/semantics/testdata/index/fail_non_int_indexing.carbon

@@ -90,3 +90,21 @@ var b: i32 = a[2.6];
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_9: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_14: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_17.1: type = tuple_type (type, type)
+// CHECK:STDOUT:   %.loc7_17.2: (type, type) = tuple_value (%.loc7_9, %.loc7_14)
+// CHECK:STDOUT:   %.loc7_17.3: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %a: (i32, i32) = var
+// CHECK:STDOUT:   %.loc7_22.1: i32 = int_value 12
+// CHECK:STDOUT:   %.loc7_22.2: i32 = stub_reference %.loc7_22.1
+// CHECK:STDOUT:   %.loc7_26.1: i32 = int_value 6
+// CHECK:STDOUT:   %.loc7_26.2: i32 = stub_reference %.loc7_26.1
+// CHECK:STDOUT:   %.loc7_27: (i32, i32) = tuple_value (%.loc7_22.2, %.loc7_26.2)
+// CHECK:STDOUT:   assign %a, %.loc7_27
+// CHECK:STDOUT:   %b: i32 = var
+// CHECK:STDOUT:   %.loc11: f64 = real_value 26e-1
+// CHECK:STDOUT:   assign %b, <error>
+// CHECK:STDOUT: }

+ 11 - 0
toolchain/semantics/testdata/index/fail_non_tuple_access.carbon

@@ -47,3 +47,14 @@ fn Main() {
 // CHECK:STDOUT:     node+3,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_3: i32 = int_value 0
+// CHECK:STDOUT:   %.loc11_5: i32 = int_value 1
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 18 - 0
toolchain/semantics/testdata/index/fail_out_of_bound_access.carbon

@@ -89,3 +89,21 @@ var b: i32 = a[2];
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_9: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_14: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_17.1: type = tuple_type (type, type)
+// CHECK:STDOUT:   %.loc7_17.2: (type, type) = tuple_value (%.loc7_9, %.loc7_14)
+// CHECK:STDOUT:   %.loc7_17.3: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %a: (i32, i32) = var
+// CHECK:STDOUT:   %.loc7_22.1: i32 = int_value 12
+// CHECK:STDOUT:   %.loc7_22.2: i32 = stub_reference %.loc7_22.1
+// CHECK:STDOUT:   %.loc7_26.1: i32 = int_value 6
+// CHECK:STDOUT:   %.loc7_26.2: i32 = stub_reference %.loc7_26.1
+// CHECK:STDOUT:   %.loc7_27: (i32, i32) = tuple_value (%.loc7_22.2, %.loc7_26.2)
+// CHECK:STDOUT:   assign %a, %.loc7_27
+// CHECK:STDOUT:   %b: i32 = var
+// CHECK:STDOUT:   %.loc11: i32 = int_value 2
+// CHECK:STDOUT:   assign %b, <error>
+// CHECK:STDOUT: }

+ 25 - 0
toolchain/semantics/testdata/index/return_value_access.carbon

@@ -84,3 +84,28 @@ fn Run() -> i32 {
 // CHECK:STDOUT:     node+13,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_12: type = stub_reference i32
+// CHECK:STDOUT:   %.loc7_16.1: type = tuple_type (type)
+// CHECK:STDOUT:   %.loc7_16.2: (type,) = tuple_value (%.loc7_12)
+// CHECK:STDOUT:   %.loc7_16.3: type = tuple_type (i32)
+// CHECK:STDOUT:   %.loc7_1 = fn_decl @F
+// CHECK:STDOUT:   %.loc9 = fn_decl @Run
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> (i32,) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7_28.1: i32 = int_value 0
+// CHECK:STDOUT:   %.loc7_28.2: i32 = stub_reference %.loc7_28.1
+// CHECK:STDOUT:   %.loc7_30: (i32,) = tuple_value (%.loc7_28.2)
+// CHECK:STDOUT:   return %.loc7_30
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_11: (i32,) = call @F()
+// CHECK:STDOUT:   %.loc10_14: i32 = int_value 0
+// CHECK:STDOUT:   %.loc10_15: i32 = tuple_index %.loc10_11, %.loc10_14
+// CHECK:STDOUT:   return %.loc10_15
+// CHECK:STDOUT: }

+ 92 - 0
toolchain/semantics/testdata/ir/duplicate_name_same_line.carbon

@@ -0,0 +1,92 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+
+fn A() { var n: i32 = 1; if (true) { var n: i32 = 2; } }
+
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT:   {name: str0, param_refs: block0, body: {block2, block3, block4}}},
+// 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:   n,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT:   nodeIntegerType,
+// CHECK:STDOUT:   nodeBoolType,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: type_blocks: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: function0},
+// CHECK:STDOUT:   {kind: VarStorage, type: type0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+1, type: type0},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: type0},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+1, arg1: node+3},
+// CHECK:STDOUT:   {kind: BoolLiteral, arg0: true, type: type1},
+// CHECK:STDOUT:   {kind: BranchIf, arg0: block3, arg1: node+5},
+// CHECK:STDOUT:   {kind: Branch, arg0: block4},
+// CHECK:STDOUT:   {kind: VarStorage, type: type0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str1, arg1: node+8, type: type0},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int1, type: type0},
+// CHECK:STDOUT:   {kind: Assign, arg0: node+8, arg1: node+10},
+// CHECK:STDOUT:   {kind: Branch, arg0: block4},
+// CHECK:STDOUT:   {kind: Return},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT:   [
+// 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:     node+5,
+// CHECK:STDOUT:     node+6,
+// CHECK:STDOUT:     node+7,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+8,
+// CHECK:STDOUT:     node+9,
+// CHECK:STDOUT:     node+10,
+// CHECK:STDOUT:     node+11,
+// CHECK:STDOUT:     node+12,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node+13,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @A
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %n.loc7_14: i32 = var
+// CHECK:STDOUT:   %.loc7_23: i32 = int_value 1
+// CHECK:STDOUT:   assign %n.loc7_14, %.loc7_23
+// CHECK:STDOUT:   %.loc7_30: bool = bool_value true
+// CHECK:STDOUT:   if %.loc7_30 br !.loc7_42 else br !.loc7_56
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc7_42:
+// CHECK:STDOUT:   %n.loc7_42: i32 = var
+// CHECK:STDOUT:   %.loc7_51: i32 = int_value 2
+// CHECK:STDOUT:   assign %n.loc7_42, %.loc7_51
+// CHECK:STDOUT:   br !.loc7_56
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc7_56:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 17 - 1
toolchain/semantics/testdata/namespace/fail_duplicate.carbon

@@ -21,7 +21,7 @@ fn Foo.Baz() {
 // CHECK:STDOUT: cross_reference_irs_size: 1
 // CHECK:STDOUT: functions: [
 // CHECK:STDOUT:   {name: str1, param_refs: block0, body: {block2}}},
-// CHECK:STDOUT:   {name: str7, param_refs: block0, body: {block3}}},
+// CHECK:STDOUT:   {name: str<invalid>, param_refs: block0, body: {block3}}},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: integer_literals: [
 // CHECK:STDOUT: ]
@@ -57,3 +57,19 @@ fn Foo.Baz() {
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = namespace {.Baz = %.loc9}
+// CHECK:STDOUT:   %.loc9 = fn_decl @Baz
+// CHECK:STDOUT:   %.loc18 = fn_decl @.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Baz() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/namespace/fail_unresolved_scope.carbon

@@ -40,3 +40,12 @@ fn Foo.Baz() {
 // CHECK:STDOUT:     node+1,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc10 = fn_decl @.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 24 - 0
toolchain/semantics/testdata/namespace/function.carbon

@@ -71,3 +71,27 @@ fn Bar() {
 // CHECK:STDOUT:     node+8,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = namespace {.Baz = %.loc13}
+// CHECK:STDOUT:   %.loc10 = fn_decl @Baz.1
+// CHECK:STDOUT:   %.loc13 = fn_decl @Baz.2
+// CHECK:STDOUT:   %.loc16 = fn_decl @Bar
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Baz.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Baz.2() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Bar() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc17_10.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc17_10.2: () = call @Baz.2()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 19 - 0
toolchain/semantics/testdata/namespace/nested.carbon

@@ -64,3 +64,22 @@ fn Foo.Bar.Baz() {
 // CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = namespace {.Bar = %.loc8}
+// CHECK:STDOUT:   %.loc8 = namespace {.Wiz = %.loc10, .Baz = %.loc13}
+// CHECK:STDOUT:   %.loc10 = fn_decl @Wiz
+// CHECK:STDOUT:   %.loc13 = fn_decl @Baz
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Wiz() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Baz() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc14_14.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc14_14.2: () = call @Wiz()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 33 - 0
toolchain/semantics/testdata/operators/and.carbon

@@ -79,3 +79,36 @@ fn And() -> bool {
 // CHECK:STDOUT:     node+14,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT:   %.loc8 = fn_decl @G
+// CHECK:STDOUT:   %.loc10 = fn_decl @And
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7: bool = bool_value true
+// CHECK:STDOUT:   return %.loc7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8: bool = bool_value true
+// CHECK:STDOUT:   return %.loc8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @And() -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_11: bool = call @F()
+// CHECK:STDOUT:   %.loc11_14.1: bool = bool_value false
+// CHECK:STDOUT:   if %.loc11_11 br !.loc11_19 else br !.loc11_14(%.loc11_14.1)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_19:
+// CHECK:STDOUT:   %.loc11_19: bool = call @G()
+// CHECK:STDOUT:   br !.loc11_14(%.loc11_19)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_14:
+// CHECK:STDOUT:   %.loc11_14.2: bool = block_arg !.loc11_14
+// CHECK:STDOUT:   return %.loc11_14.2
+// CHECK:STDOUT: }

+ 70 - 0
toolchain/semantics/testdata/operators/assignment.carbon

@@ -235,3 +235,73 @@ fn Main() {
 // CHECK:STDOUT:     node+65,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a: i32 = var
+// CHECK:STDOUT:   %.loc8: i32 = int_value 12
+// CHECK:STDOUT:   assign %a, %.loc8
+// CHECK:STDOUT:   %.loc9: i32 = int_value -7
+// CHECK:STDOUT:   assign %a, %.loc9
+// CHECK:STDOUT:   %.loc11_11: type = stub_reference i32
+// CHECK:STDOUT:   %.loc11_16: type = stub_reference i32
+// CHECK:STDOUT:   %.loc11_19.1: type = tuple_type (type, type)
+// CHECK:STDOUT:   %.loc11_19.2: (type, type) = tuple_value (%.loc11_11, %.loc11_16)
+// CHECK:STDOUT:   %.loc11_19.3: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %b: (i32, i32) = var
+// CHECK:STDOUT:   %.loc11_24.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc11_24.2: i32 = stub_reference %.loc11_24.1
+// CHECK:STDOUT:   %.loc11_27.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc11_27.2: i32 = stub_reference %.loc11_27.1
+// CHECK:STDOUT:   %.loc11_28: (i32, i32) = tuple_value (%.loc11_24.2, %.loc11_27.2)
+// CHECK:STDOUT:   assign %b, %.loc11_28
+// CHECK:STDOUT:   %.loc12_5: i32 = int_value 0
+// CHECK:STDOUT:   %.loc12_6: i32 = tuple_index %b, %.loc12_5
+// CHECK:STDOUT:   %.loc12_10: i32 = int_value 3
+// CHECK:STDOUT:   assign %.loc12_6, %.loc12_10
+// CHECK:STDOUT:   %.loc13_5: i32 = int_value 1
+// CHECK:STDOUT:   %.loc13_6: i32 = tuple_index %b, %.loc13_5
+// CHECK:STDOUT:   %.loc13_10: i32 = int_value 4
+// CHECK:STDOUT:   assign %.loc13_6, %.loc13_10
+// CHECK:STDOUT:   %.loc15_27: type = struct_type {.a: i32, .b: i32}
+// CHECK:STDOUT:   %c: {.a: i32, .b: i32} = var
+// CHECK:STDOUT:   %.loc15_37: i32 = int_value 1
+// CHECK:STDOUT:   %.loc15_35: i32 = stub_reference %.loc15_37
+// CHECK:STDOUT:   %.loc15_45: i32 = int_value 2
+// CHECK:STDOUT:   %.loc15_43: i32 = stub_reference %.loc15_45
+// CHECK:STDOUT:   %.loc15_46: {.a: i32, .b: i32} = struct_value (%.loc15_35, %.loc15_43)
+// CHECK:STDOUT:   assign %c, %.loc15_46
+// CHECK:STDOUT:   %.loc16_4: i32 = struct_access %c, member0
+// CHECK:STDOUT:   %.loc16_9: i32 = int_value 3
+// CHECK:STDOUT:   assign %.loc16_4, %.loc16_9
+// CHECK:STDOUT:   %.loc17_4: i32 = struct_access %c, member1
+// CHECK:STDOUT:   %.loc17_9: i32 = int_value 4
+// CHECK:STDOUT:   assign %.loc17_4, %.loc17_9
+// CHECK:STDOUT:   %.loc19_13: type = ptr_type i32
+// CHECK:STDOUT:   %p: i32* = var
+// CHECK:STDOUT:   %.loc19_17: i32* = address_of %a
+// CHECK:STDOUT:   assign %p, %.loc19_17
+// CHECK:STDOUT:   %.loc20_3: i32 = dereference %p
+// CHECK:STDOUT:   %.loc20_8: i32 = int_value 5
+// CHECK:STDOUT:   assign %.loc20_3, %.loc20_8
+// CHECK:STDOUT:   %.loc22_8: bool = bool_value true
+// CHECK:STDOUT:   if %.loc22_8 br !.loc22_5.1 else br !.loc22_25
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc22_5.1:
+// CHECK:STDOUT:   br !.loc22_5.2(%p)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc22_25:
+// CHECK:STDOUT:   %.loc22_25: i32* = address_of %a
+// CHECK:STDOUT:   br !.loc22_5.2(%.loc22_25)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc22_5.2:
+// CHECK:STDOUT:   %.loc22_5: i32* = block_arg !.loc22_5.2
+// CHECK:STDOUT:   %.loc22_3: i32 = dereference %.loc22_5
+// CHECK:STDOUT:   %.loc22_31: i32 = int_value 10
+// CHECK:STDOUT:   assign %.loc22_3, %.loc22_31
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 12 - 0
toolchain/semantics/testdata/operators/binary_op.carbon

@@ -46,3 +46,15 @@ fn Main() -> i32 {
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8_10: i32 = int_value 12
+// CHECK:STDOUT:   %.loc8_15: i32 = int_value 34
+// CHECK:STDOUT:   %.loc8_13: i32 = add %.loc8_10, %.loc8_15
+// CHECK:STDOUT:   return %.loc8_13
+// CHECK:STDOUT: }

+ 85 - 0
toolchain/semantics/testdata/operators/fail_assigment_to_non_assignable.carbon

@@ -286,3 +286,88 @@ fn Main() {
 // CHECK:STDOUT:     node+71,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT:   %.loc9 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> i32;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc13_3: i32 = int_value 1
+// CHECK:STDOUT:   %.loc13_7: i32 = int_value 2
+// CHECK:STDOUT:   assign %.loc13_3, %.loc13_7
+// CHECK:STDOUT:   %.loc17_4: i32 = call @F()
+// CHECK:STDOUT:   %.loc17_9: i32 = int_value 1
+// CHECK:STDOUT:   assign %.loc17_4, %.loc17_9
+// CHECK:STDOUT:   %.loc21_4.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc21_4.2: i32 = stub_reference %.loc21_4.1
+// CHECK:STDOUT:   %.loc21_7.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc21_7.2: i32 = stub_reference %.loc21_7.1
+// CHECK:STDOUT:   %.loc21_8.1: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %.loc21_8.2: (i32, i32) = tuple_value (%.loc21_4.2, %.loc21_7.2)
+// CHECK:STDOUT:   %.loc21_13.1: i32 = int_value 3
+// CHECK:STDOUT:   %.loc21_13.2: i32 = stub_reference %.loc21_13.1
+// CHECK:STDOUT:   %.loc21_16.1: i32 = int_value 4
+// CHECK:STDOUT:   %.loc21_16.2: i32 = stub_reference %.loc21_16.1
+// CHECK:STDOUT:   %.loc21_17: (i32, i32) = tuple_value (%.loc21_13.2, %.loc21_16.2)
+// CHECK:STDOUT:   assign %.loc21_8.2, %.loc21_17
+// CHECK:STDOUT:   %n: i32 = var
+// CHECK:STDOUT:   %.loc22: i32 = int_value 0
+// CHECK:STDOUT:   assign %n, %.loc22
+// CHECK:STDOUT:   %.loc26_4: i32 = stub_reference %n
+// CHECK:STDOUT:   %.loc26_7: i32 = stub_reference %n
+// CHECK:STDOUT:   %.loc26_8: (i32, i32) = tuple_value (%.loc26_4, %.loc26_7)
+// CHECK:STDOUT:   %.loc26_13.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc26_13.2: i32 = stub_reference %.loc26_13.1
+// CHECK:STDOUT:   %.loc26_16.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc26_16.2: i32 = stub_reference %.loc26_16.1
+// CHECK:STDOUT:   %.loc26_17: (i32, i32) = tuple_value (%.loc26_13.2, %.loc26_16.2)
+// CHECK:STDOUT:   assign %.loc26_8, %.loc26_17
+// CHECK:STDOUT:   %.loc30: type = ptr_type i32
+// CHECK:STDOUT:   assign i32, %.loc30
+// CHECK:STDOUT:   %.loc34_9: i32 = int_value 1
+// CHECK:STDOUT:   %.loc34_7: i32 = stub_reference %.loc34_9
+// CHECK:STDOUT:   %.loc34_17: i32 = int_value 2
+// CHECK:STDOUT:   %.loc34_15: i32 = stub_reference %.loc34_17
+// CHECK:STDOUT:   %.loc34_18.1: type = struct_type {.x: i32, .y: i32}
+// CHECK:STDOUT:   %.loc34_18.2: {.x: i32, .y: i32} = struct_value (%.loc34_7, %.loc34_15)
+// CHECK:STDOUT:   %.loc34_28: i32 = int_value 3
+// CHECK:STDOUT:   %.loc34_26: i32 = stub_reference %.loc34_28
+// CHECK:STDOUT:   %.loc34_36: i32 = int_value 4
+// CHECK:STDOUT:   %.loc34_34: i32 = stub_reference %.loc34_36
+// CHECK:STDOUT:   %.loc34_37: {.x: i32, .y: i32} = struct_value (%.loc34_26, %.loc34_34)
+// CHECK:STDOUT:   assign %.loc34_18.2, %.loc34_37
+// CHECK:STDOUT:   %.loc38_7: bool = bool_value true
+// CHECK:STDOUT:   if %.loc38_7 br !.loc38_17 else br !.loc38_24
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc38_17:
+// CHECK:STDOUT:   %.loc38_17: i32 = int_value 1
+// CHECK:STDOUT:   br !.loc38_4(%.loc38_17)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc38_24:
+// CHECK:STDOUT:   %.loc38_24: i32 = int_value 2
+// CHECK:STDOUT:   br !.loc38_4(%.loc38_24)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc38_4:
+// CHECK:STDOUT:   %.loc38_4: i32 = block_arg !.loc38_4
+// CHECK:STDOUT:   %.loc38_29: i32 = int_value 3
+// CHECK:STDOUT:   assign %.loc38_4, %.loc38_29
+// CHECK:STDOUT:   %a: i32 = var
+// CHECK:STDOUT:   %.loc45_7: bool = bool_value true
+// CHECK:STDOUT:   if %.loc45_7 br !.loc45_4.1 else br !.loc45_4.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc45_4.1:
+// CHECK:STDOUT:   br !.loc45_4.3(%a)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc45_4.2:
+// CHECK:STDOUT:   br !.loc45_4.3(%a)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc45_4.3:
+// CHECK:STDOUT:   %.loc45_4: i32 = block_arg !.loc45_4.3
+// CHECK:STDOUT:   %.loc45_29: i32 = int_value 10
+// CHECK:STDOUT:   assign %.loc45_4, %.loc45_29
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 12 - 0
toolchain/semantics/testdata/operators/fail_type_mismatch.carbon

@@ -50,3 +50,15 @@ fn Main() -> i32 {
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_10: i32 = int_value 12
+// CHECK:STDOUT:   %.loc11_15: f64 = real_value 34e-1
+// CHECK:STDOUT:   %.loc11_13: <error> = add <error>, %.loc11_15
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

+ 14 - 0
toolchain/semantics/testdata/operators/fail_type_mismatch_assignment.carbon

@@ -58,3 +58,17 @@ fn Main() {
 // CHECK:STDOUT:     node+7,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a: i32 = var
+// CHECK:STDOUT:   %.loc8: i32 = int_value 3
+// CHECK:STDOUT:   assign %a, %.loc8
+// CHECK:STDOUT:   %.loc12: f64 = real_value 56e-1
+// CHECK:STDOUT:   assign %a, %.loc12
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 14 - 0
toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon

@@ -57,3 +57,17 @@ fn Main() -> i32 {
 // CHECK:STDOUT:     node+6,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc13_10: i32 = int_value 12
+// CHECK:STDOUT:   %.loc13_15: f64 = real_value 34e-1
+// CHECK:STDOUT:   %.loc13_13: <error> = add <error>, %.loc13_15
+// CHECK:STDOUT:   %.loc13_21: i32 = int_value 12
+// CHECK:STDOUT:   %.loc13_19: <error> = add <error>, %.loc13_21
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

+ 34 - 0
toolchain/semantics/testdata/operators/or.carbon

@@ -81,3 +81,37 @@ fn Or() -> bool {
 // CHECK:STDOUT:     node+15,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT:   %.loc8 = fn_decl @G
+// CHECK:STDOUT:   %.loc10 = fn_decl @Or
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7: bool = bool_value true
+// CHECK:STDOUT:   return %.loc7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8: bool = bool_value true
+// CHECK:STDOUT:   return %.loc8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Or() -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_11: bool = call @F()
+// CHECK:STDOUT:   %.loc11_14.1: bool = not %.loc11_11
+// CHECK:STDOUT:   %.loc11_14.2: bool = bool_value true
+// CHECK:STDOUT:   if %.loc11_14.1 br !.loc11_18 else br !.loc11_14(%.loc11_14.2)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_18:
+// CHECK:STDOUT:   %.loc11_18: bool = call @G()
+// CHECK:STDOUT:   br !.loc11_14(%.loc11_18)
+// CHECK:STDOUT:
+// CHECK:STDOUT: !.loc11_14:
+// CHECK:STDOUT:   %.loc11_14.3: bool = block_arg !.loc11_14
+// CHECK:STDOUT:   return %.loc11_14.3
+// CHECK:STDOUT: }

+ 10 - 0
toolchain/semantics/testdata/operators/unary_op.carbon

@@ -50,3 +50,13 @@ fn Not(b: bool) -> bool {
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Not
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Not(%b: bool) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8: bool = not %b
+// CHECK:STDOUT:   return %.loc8
+// CHECK:STDOUT: }

+ 17 - 0
toolchain/semantics/testdata/pointer/address_of_deref.carbon

@@ -60,3 +60,20 @@ fn F() -> i32 {
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %n: i32 = var
+// CHECK:STDOUT:   %.loc8: i32 = int_value 0
+// CHECK:STDOUT:   assign %n, %.loc8
+// CHECK:STDOUT:   %.loc9_13.1: type = ptr_type i32
+// CHECK:STDOUT:   %.loc9_13.2: i32* = address_of %n
+// CHECK:STDOUT:   %.loc9_12: i32 = dereference %.loc9_13.2
+// CHECK:STDOUT:   %.loc9_11: i32* = address_of %.loc9_12
+// CHECK:STDOUT:   %.loc9_10: i32 = dereference %.loc9_11
+// CHECK:STDOUT:   return %.loc9_10
+// CHECK:STDOUT: }

+ 59 - 0
toolchain/semantics/testdata/pointer/address_of_lvalue.carbon

@@ -239,3 +239,62 @@ fn F(param: i32) {
 // CHECK:STDOUT:     node+45,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%param: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8_27: type = struct_type {.a: i32, .b: i32}
+// CHECK:STDOUT:   %s: {.a: i32, .b: i32} = var
+// CHECK:STDOUT:   %.loc8_37: i32 = int_value 1
+// CHECK:STDOUT:   %.loc8_35: i32 = stub_reference %.loc8_37
+// CHECK:STDOUT:   %.loc8_45: i32 = int_value 2
+// CHECK:STDOUT:   %.loc8_43: i32 = stub_reference %.loc8_45
+// CHECK:STDOUT:   %.loc8_46: {.a: i32, .b: i32} = struct_value (%.loc8_35, %.loc8_43)
+// CHECK:STDOUT:   assign %s, %.loc8_46
+// CHECK:STDOUT:   %.loc10_28: type = ptr_type {.a: i32, .b: i32}
+// CHECK:STDOUT:   %p: {.a: i32, .b: i32}* = var
+// CHECK:STDOUT:   %.loc10_32: {.a: i32, .b: i32}* = address_of %s
+// CHECK:STDOUT:   assign %p, %.loc10_32
+// CHECK:STDOUT:   %.loc11_13: type = ptr_type i32
+// CHECK:STDOUT:   %q: i32* = var
+// CHECK:STDOUT:   %.loc11_19: i32 = struct_access %s, member0
+// CHECK:STDOUT:   %.loc11_17: i32* = address_of %.loc11_19
+// CHECK:STDOUT:   assign %q, %.loc11_17
+// CHECK:STDOUT:   %.loc12_13: type = ptr_type i32
+// CHECK:STDOUT:   %r: i32* = var
+// CHECK:STDOUT:   %.loc12_19: i32 = struct_access %s, member1
+// CHECK:STDOUT:   %.loc12_17: i32* = address_of %.loc12_19
+// CHECK:STDOUT:   assign %r, %.loc12_17
+// CHECK:STDOUT:   %.loc14_11: type = stub_reference i32
+// CHECK:STDOUT:   %.loc14_16: type = stub_reference i32
+// CHECK:STDOUT:   %.loc14_19.1: type = tuple_type (type, type)
+// CHECK:STDOUT:   %.loc14_19.2: (type, type) = tuple_value (%.loc14_11, %.loc14_16)
+// CHECK:STDOUT:   %.loc14_19.3: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %t: (i32, i32) = var
+// CHECK:STDOUT:   %.loc14_24.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc14_24.2: i32 = stub_reference %.loc14_24.1
+// CHECK:STDOUT:   %.loc14_27.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc14_27.2: i32 = stub_reference %.loc14_27.1
+// CHECK:STDOUT:   %.loc14_28: (i32, i32) = tuple_value (%.loc14_24.2, %.loc14_27.2)
+// CHECK:STDOUT:   assign %t, %.loc14_28
+// CHECK:STDOUT:   %.loc15_14: type = ptr_type i32
+// CHECK:STDOUT:   %t0: i32* = var
+// CHECK:STDOUT:   %.loc15_21: i32 = int_value 0
+// CHECK:STDOUT:   %.loc15_22: i32 = tuple_index %t, %.loc15_21
+// CHECK:STDOUT:   %.loc15_18: i32* = address_of %.loc15_22
+// CHECK:STDOUT:   assign %t0, %.loc15_18
+// CHECK:STDOUT:   %.loc16_14: type = ptr_type i32
+// CHECK:STDOUT:   %t1: i32* = var
+// CHECK:STDOUT:   %.loc16_21: i32 = int_value 1
+// CHECK:STDOUT:   %.loc16_22: i32 = tuple_index %t, %.loc16_21
+// CHECK:STDOUT:   %.loc16_18: i32* = address_of %.loc16_22
+// CHECK:STDOUT:   assign %t1, %.loc16_18
+// CHECK:STDOUT:   %.loc20_22: type = ptr_type i32
+// CHECK:STDOUT:   %param_addr: i32* = var
+// CHECK:STDOUT:   %.loc20_26: i32* = address_of %param
+// CHECK:STDOUT:   assign %param_addr, %.loc20_26
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 17 - 0
toolchain/semantics/testdata/pointer/basic.carbon

@@ -65,3 +65,20 @@ fn F() -> i32 {
 // CHECK:STDOUT:     node+11,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %n: i32 = var
+// CHECK:STDOUT:   %.loc8: i32 = int_value 0
+// CHECK:STDOUT:   assign %n, %.loc8
+// CHECK:STDOUT:   %.loc9_13: type = ptr_type i32
+// CHECK:STDOUT:   %p: i32* = var
+// CHECK:STDOUT:   %.loc9_17: i32* = address_of %n
+// CHECK:STDOUT:   assign %p, %.loc9_17
+// CHECK:STDOUT:   %.loc11: i32 = dereference %p
+// CHECK:STDOUT:   return %.loc11
+// CHECK:STDOUT: }

+ 90 - 0
toolchain/semantics/testdata/pointer/fail_address_of_value.carbon

@@ -296,3 +296,93 @@ fn AddressOfTupleElementValue() {
 // CHECK:STDOUT:     node+59,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @G
+// CHECK:STDOUT:   %.loc9_19: type = struct_type {.a: i32}
+// CHECK:STDOUT:   %.loc9_1 = fn_decl @H
+// CHECK:STDOUT:   %.loc11 = fn_decl @AddressOfLiteral
+// CHECK:STDOUT:   %.loc38 = fn_decl @AddressOfOperator
+// CHECK:STDOUT:   %.loc53 = fn_decl @AddressOfCall
+// CHECK:STDOUT:   %.loc60 = fn_decl @AddressOfType
+// CHECK:STDOUT:   %.loc71 = fn_decl @AddressOfTupleElementValue
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() -> i32;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @H() -> {.a: i32};
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @AddressOfLiteral() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc15_4: i32 = int_value 0
+// CHECK:STDOUT:   %.loc15_3.1: type = ptr_type i32
+// CHECK:STDOUT:   %.loc15_3.2: i32* = address_of %.loc15_4
+// CHECK:STDOUT:   %.loc19_4: bool = bool_value true
+// CHECK:STDOUT:   %.loc19_3.1: type = ptr_type bool
+// CHECK:STDOUT:   %.loc19_3.2: bool* = address_of %.loc19_4
+// CHECK:STDOUT:   %.loc23_4: f64 = real_value 10e-1
+// CHECK:STDOUT:   %.loc23_3.1: type = ptr_type f64
+// CHECK:STDOUT:   %.loc23_3.2: f64* = address_of %.loc23_4
+// CHECK:STDOUT:   %.loc27_4: String = string_value "Hello"
+// CHECK:STDOUT:   %.loc27_3.1: type = ptr_type String
+// CHECK:STDOUT:   %.loc27_3.2: String* = address_of %.loc27_4
+// CHECK:STDOUT:   %.loc31_5.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc31_5.2: i32 = stub_reference %.loc31_5.1
+// CHECK:STDOUT:   %.loc31_8.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc31_8.2: i32 = stub_reference %.loc31_8.1
+// CHECK:STDOUT:   %.loc31_9.1: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %.loc31_9.2: (i32, i32) = tuple_value (%.loc31_5.2, %.loc31_8.2)
+// CHECK:STDOUT:   %.loc31_3.1: type = ptr_type (i32, i32)
+// CHECK:STDOUT:   %.loc31_3.2: (i32, i32)* = address_of %.loc31_9.2
+// CHECK:STDOUT:   %.loc35_10: i32 = int_value 5
+// CHECK:STDOUT:   %.loc35_8: i32 = stub_reference %.loc35_10
+// CHECK:STDOUT:   %.loc35_11: {.a: i32} = struct_value (%.loc35_8)
+// CHECK:STDOUT:   %.loc35_3.1: type = ptr_type {.a: i32}
+// CHECK:STDOUT:   %.loc35_3.2: {.a: i32}* = address_of %.loc35_11
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @AddressOfOperator() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc42_5: i32 = int_value 1
+// CHECK:STDOUT:   %.loc42_9: i32 = int_value 1
+// CHECK:STDOUT:   %.loc42_7: i32 = add %.loc42_5, %.loc42_9
+// CHECK:STDOUT:   %.loc42_3: i32* = address_of %.loc42_7
+// CHECK:STDOUT:   %.loc46_5: {.a: i32} = call @H()
+// CHECK:STDOUT:   %.loc46_7: i32 = struct_access %.loc46_5, member0
+// CHECK:STDOUT:   %.loc46_3: i32* = address_of %.loc46_7
+// CHECK:STDOUT:   %.loc50_9: bool = bool_value true
+// CHECK:STDOUT:   %.loc50_5: bool = not %.loc50_9
+// CHECK:STDOUT:   %.loc50_3: bool* = address_of %.loc50_5
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @AddressOfCall() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc57_5: i32 = call @G()
+// CHECK:STDOUT:   %.loc57_3: i32* = address_of %.loc57_5
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @AddressOfType() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc64_3.1: type = ptr_type type
+// CHECK:STDOUT:   %.loc64_3.2: type* = address_of i32
+// CHECK:STDOUT:   %.loc68_5: type = const_type i32
+// CHECK:STDOUT:   %.loc68_14: type = ptr_type const i32
+// CHECK:STDOUT:   %.loc68_3: type* = address_of %.loc68_14
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @AddressOfTupleElementValue() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc75_6.1: i32 = int_value 1
+// CHECK:STDOUT:   %.loc75_6.2: i32 = stub_reference %.loc75_6.1
+// CHECK:STDOUT:   %.loc75_9.1: i32 = int_value 2
+// CHECK:STDOUT:   %.loc75_9.2: i32 = stub_reference %.loc75_9.1
+// CHECK:STDOUT:   %.loc75_10: (i32, i32) = tuple_value (%.loc75_6.2, %.loc75_9.2)
+// CHECK:STDOUT:   %.loc75_12: i32 = int_value 0
+// CHECK:STDOUT:   %.loc75_13: i32 = tuple_index %.loc75_10, %.loc75_12
+// CHECK:STDOUT:   %.loc75_3: i32* = address_of %.loc75_13
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 16 - 0
toolchain/semantics/testdata/pointer/fail_dereference_not_pointer.carbon

@@ -77,3 +77,19 @@ fn Deref(n: i32) {
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Deref
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Deref(%n: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11: <error> = dereference %n
+// CHECK:STDOUT:   %.loc15_5.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc15_5.2: () = tuple_value ()
+// CHECK:STDOUT:   %.loc15_3: <error> = dereference %.loc15_5.2
+// CHECK:STDOUT:   %.loc19_5.1: type = struct_type {}
+// CHECK:STDOUT:   %.loc19_5.2: {} = struct_value ()
+// CHECK:STDOUT:   %.loc19_3: <error> = dereference %.loc19_5.2
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 5 - 0
toolchain/semantics/testdata/pointer/fail_dereference_type.carbon

@@ -40,3 +40,8 @@ var p: *i32;
 // CHECK:STDOUT:     node+2,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc13: <error> = dereference i32
+// CHECK:STDOUT:   %p: <error> = var
+// CHECK:STDOUT: }

+ 12 - 0
toolchain/semantics/testdata/pointer/fail_type_mismatch.carbon

@@ -69,3 +69,15 @@ fn ConstMismatch(p: const {}*) -> const ({}*) {
 // CHECK:STDOUT:     node+10,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_43: {} = struct_value ()
+// CHECK:STDOUT:   %.loc7_44: type = ptr_type {}
+// CHECK:STDOUT:   %.loc7_35: type = const_type {}*
+// CHECK:STDOUT:   %.loc7_1 = fn_decl @ConstMismatch
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @ConstMismatch(%p: const {}*) -> const ({}*) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

+ 12 - 0
toolchain/semantics/testdata/pointer/nested_const.carbon

@@ -70,3 +70,15 @@ fn F(p: const (const (const i32*)*)) -> const i32 {
 // CHECK:STDOUT:     node+11,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc8_41: type = const_type i32
+// CHECK:STDOUT:   %.loc8_1 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%p: const (const (const i32*)*)) -> const i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc9_11: const (const i32*) = dereference %p
+// CHECK:STDOUT:   %.loc9_10: const i32 = dereference %.loc9_11
+// CHECK:STDOUT:   return %.loc9_10
+// CHECK:STDOUT: }

+ 18 - 0
toolchain/semantics/testdata/pointer/types.carbon

@@ -84,3 +84,21 @@ fn ConstPtr(p: const i32*) -> (const i32)* {
 // CHECK:STDOUT:     node+13,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_23: type = ptr_type i32
+// CHECK:STDOUT:   %.loc7_1 = fn_decl @Ptr
+// CHECK:STDOUT:   %.loc11_32: type = const_type i32
+// CHECK:STDOUT:   %.loc11_42: type = ptr_type const i32
+// CHECK:STDOUT:   %.loc11_1 = fn_decl @ConstPtr
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Ptr(%p: i32*) -> i32* {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return %p
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @ConstPtr(%p: const i32*) -> const i32* {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return %p
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/return/code_after_return.carbon

@@ -48,3 +48,12 @@ fn Main() {
 // CHECK:STDOUT:     node+1,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 10 - 0
toolchain/semantics/testdata/return/code_after_return_value.carbon

@@ -77,3 +77,13 @@ fn F(b: bool) -> i32 {
 // CHECK:STDOUT:     node+4,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%b: bool) -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8: i32 = int_value 0
+// CHECK:STDOUT:   return %.loc8
+// CHECK:STDOUT: }

+ 8 - 0
toolchain/semantics/testdata/return/fail_missing_return.carbon

@@ -38,3 +38,11 @@ fn Main() -> i32 {
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT: }

+ 10 - 0
toolchain/semantics/testdata/return/fail_missing_return_empty_tuple.carbon

@@ -44,3 +44,13 @@ fn F() -> () {
 // CHECK:STDOUT:   [
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_12.1: type = tuple_type ()
+// CHECK:STDOUT:   %.loc7_12.2: () = tuple_value ()
+// CHECK:STDOUT:   %.loc7_1 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> () {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT: }

+ 10 - 0
toolchain/semantics/testdata/return/fail_type_mismatch.carbon

@@ -45,3 +45,13 @@ fn Main() -> i32 {
 // CHECK:STDOUT:     node+2,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11: f64 = real_value 10e-1
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

+ 10 - 0
toolchain/semantics/testdata/return/fail_value_disallowed.carbon

@@ -47,3 +47,13 @@ fn Main() {
 // CHECK:STDOUT:     node+2,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc14: i32 = int_value 0
+// CHECK:STDOUT:   return %.loc14
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/return/fail_value_missing.carbon

@@ -41,3 +41,12 @@ fn Main() -> i32 {
 // CHECK:STDOUT:     node+1,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/return/missing_return_no_return_type.carbon

@@ -36,3 +36,12 @@ fn F() {
 // CHECK:STDOUT:     node+1,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 9 - 0
toolchain/semantics/testdata/return/no_value.carbon

@@ -37,3 +37,12 @@ fn Main() {
 // CHECK:STDOUT:     node+1,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 13 - 0
toolchain/semantics/testdata/return/struct.carbon

@@ -61,3 +61,16 @@ fn Main() -> {.a: i32} {
 // CHECK:STDOUT:     node+5,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc7_22: type = struct_type {.a: i32}
+// CHECK:STDOUT:   %.loc7_1 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> {.a: i32} {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8_16: i32 = int_value 3
+// CHECK:STDOUT:   %.loc8_14: i32 = stub_reference %.loc8_16
+// CHECK:STDOUT:   %.loc8_17: {.a: i32} = struct_value (%.loc8_14)
+// CHECK:STDOUT:   return %.loc8_17
+// CHECK:STDOUT: }

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

@@ -79,3 +79,22 @@ fn Main() -> (i32, i32) {
 // CHECK:STDOUT:     node+9,
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
+// CHECK:STDOUT:
+// CHECK:STDOUT: package {
+// CHECK:STDOUT:   %.loc8_15: type = stub_reference i32
+// CHECK:STDOUT:   %.loc8_20: type = stub_reference i32
+// CHECK:STDOUT:   %.loc8_23.1: type = tuple_type (type, type)
+// CHECK:STDOUT:   %.loc8_23.2: (type, type) = tuple_value (%.loc8_15, %.loc8_20)
+// CHECK:STDOUT:   %.loc8_23.3: type = tuple_type (i32, i32)
+// CHECK:STDOUT:   %.loc8_1 = fn_decl @Main
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> (i32, i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc9_11.1: i32 = int_value 15
+// CHECK:STDOUT:   %.loc9_11.2: i32 = stub_reference %.loc9_11.1
+// CHECK:STDOUT:   %.loc9_15.1: i32 = int_value 35
+// CHECK:STDOUT:   %.loc9_15.2: i32 = stub_reference %.loc9_15.1
+// CHECK:STDOUT:   %.loc9_17: (i32, i32) = tuple_value (%.loc9_11.2, %.loc9_15.2)
+// CHECK:STDOUT:   return %.loc9_17
+// CHECK:STDOUT: }

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio