Procházet zdrojové kódy

Refactor YAML handling to use the llvm::yaml API. (#3337)

Provides an adapter for the llvm::yaml API because it otherwise needs a
bunch of const/non-const definitions, and the traits are difficult to
diagnose issues with. The current approach is pretty simple to use, even
if it's not super efficient (which, yaml output is more of a debugging
thing so I'm not really expecting it to be an issue).

Changes the format of yaml output to provide more index information,
just as reminders when seeing something like `node+0`. Note this would
create more churn in deltas if we were reliant on the output yaml in
tests, but we aren't so it should be okay.
Jon Ross-Perkins před 2 roky
rodič
revize
3af7eb2672

+ 10 - 0
common/ostream.h

@@ -6,6 +6,7 @@
 #define CARBON_COMMON_OSTREAM_H_
 
 #include <ostream>
+#include <type_traits>
 
 #include "llvm/Support/raw_os_ostream.h"
 // Libraries should include this header instead of raw_ostream.
@@ -50,6 +51,15 @@ class Printable {
   }
 };
 
+// Returns the result of printing the value.
+template <typename T>
+inline auto PrintToString(const T& val) -> std::string {
+  std::string str;
+  llvm::raw_string_ostream stream(str);
+  stream << val;
+  return str;
+}
+
 }  // namespace Carbon
 
 namespace llvm {

+ 11 - 0
toolchain/base/BUILD

@@ -28,6 +28,7 @@ cc_library(
     hdrs = ["value_store.h"],
     deps = [
         ":index_base",
+        ":yaml",
         "//common:check",
         "//common:ostream",
         "@llvm-project//llvm:Support",
@@ -46,3 +47,13 @@ cc_test(
         "@com_google_googletest//:gtest",
     ],
 )
+
+cc_library(
+    name = "yaml",
+    hdrs = ["yaml.h"],
+    deps = [
+        "//common:check",
+        "//common:ostream",
+        "@llvm-project//llvm:Support",
+    ],
+)

+ 27 - 88
toolchain/base/value_store.h

@@ -12,10 +12,12 @@
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/YAMLParser.h"
 #include "toolchain/base/index_base.h"
+#include "toolchain/base/yaml.h"
 
 namespace Carbon {
 
@@ -86,73 +88,14 @@ namespace Internal {
 // Used as a parent class for non-printable types. This is just for
 // std::conditional, not as an API.
 class ValueStoreNotPrintable {};
-
-// Provides YAML printing of a value.
-template <typename ValueT>
-inline auto PrintValue(llvm::raw_ostream& out, const ValueT& val) {
-  out << val;
-};
-inline auto PrintValue(llvm::raw_ostream& out, const llvm::APInt& val) {
-  val.print(out, /*isSigned=*/false);
-};
-inline auto PrintValue(llvm::raw_ostream& out, const llvm::StringRef& val) {
-  out << "\"" << llvm::yaml::escape(val) << "\"";
-};
-
-struct DefaultPrinter {
-  template <typename ValueT>
-  void operator()(llvm::raw_ostream& out, const ValueT& value) {
-    PrintValue(out, value);
-  }
-};
-
 }  // namespace Internal
 
-// Provides YAML printing of a list. The first line indent applies even if there
-// is no label, in which case it applies to the first element.
-template <typename ValueT>
-inline auto PrintValueRange(
-    llvm::raw_ostream& out, llvm::iterator_range<const ValueT*> range,
-    std::optional<llvm::StringRef> label, int first_line_indent,
-    int later_indent, bool trailing_newline,
-    llvm::function_ref<void(llvm::raw_ostream&, const ValueT& val)> print =
-        Internal::DefaultPrinter()) {
-  out.indent(first_line_indent);
-  if (label) {
-    out << *label << ":";
-    if (range.empty()) {
-      // Add a space between the `:` and the `[]` printed below.
-      out << " ";
-    } else {
-      out << "\n";
-      out.indent(later_indent);
-    }
-  }
-  if (range.empty()) {
-    out << "[]";
-    if (trailing_newline) {
-      out << "\n";
-    }
-    return;
-  }
-  std::string sep_str = "\n";
-  sep_str.append(later_indent, ' ');
-  llvm::ListSeparator sep(sep_str);
-  for (const auto& val : range) {
-    out << sep << "- ";
-    print(out, val);
-  }
-  if (trailing_newline) {
-    out << "\n";
-  }
-}
-
 // A simple wrapper for accumulating values, providing IDs to later retrieve the
 // value. This does not do deduplication.
 template <typename IdT, typename ValueT = typename IdT::IndexedType>
 class ValueStore
     : public std::conditional<std::is_base_of_v<Printable<ValueT>, ValueT>,
-                              Printable<ValueStore<IdT, ValueT>>,
+                              Yaml::Printable<ValueStore<IdT, ValueT>>,
                               Internal::ValueStoreNotPrintable> {
  public:
   using PrintFn =
@@ -189,17 +132,13 @@ class ValueStore
   auto Reserve(size_t size) -> void { values_.reserve(size); }
 
   // These are to support printable structures, and are not guaranteed.
-  auto Print(llvm::raw_ostream& out) const -> void {
-    Print(out, std::nullopt, 0, 0);
-  }
-  auto Print(llvm::raw_ostream& out, std::optional<llvm::StringRef> label,
-             int first_line_indent, int later_indent,
-             // This decays so that `const llvm::APInt` printing catches the
-             // specialization.
-             PrintFn print = Internal::DefaultPrinter()) const -> void {
-    PrintValueRange(out, llvm::iterator_range(values_), label,
-                    first_line_indent, later_indent, /*trailing_newline=*/true,
-                    print);
+  auto OutputYaml() const -> Yaml::OutputMapping {
+    return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+      for (auto i : llvm::seq(values_.size())) {
+        auto id = IdT(i);
+        map.Add(PrintToString(id), Yaml::OutputScalar(Get(id)));
+      }
+    });
   }
 
   auto array_ref() const -> llvm::ArrayRef<ValueT> { return values_; }
@@ -212,7 +151,7 @@ class ValueStore
 // Storage for StringRefs. The caller is responsible for ensuring storage is
 // allocated.
 template <>
-class ValueStore<StringId> : public Printable<ValueStore<StringId>> {
+class ValueStore<StringId> : public Yaml::Printable<ValueStore<StringId>> {
  public:
   using PrintFn =
       llvm::function_ref<void(llvm::raw_ostream&, const llvm::StringRef& val)>;
@@ -234,15 +173,12 @@ class ValueStore<StringId> : public Printable<ValueStore<StringId>> {
     return values_[id.index];
   }
 
-  auto Print(llvm::raw_ostream& out) const -> void {
-    Print(out, std::nullopt, 0, 0);
-  }
-  auto Print(llvm::raw_ostream& out, std::optional<llvm::StringRef> label,
-             int first_line_indent, int later_indent,
-             PrintFn print = Internal::DefaultPrinter()) const -> void {
-    PrintValueRange(out, llvm::iterator_range(values_), label,
-                    first_line_indent, later_indent, /*trailing_newline=*/true,
-                    print);
+  auto OutputYaml() const -> Yaml::OutputMapping {
+    return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+      for (auto [i, val] : llvm::enumerate(values_)) {
+        map.Add(PrintToString(StringId(i)), val);
+      }
+    });
   }
 
  private:
@@ -252,7 +188,7 @@ class ValueStore<StringId> : public Printable<ValueStore<StringId>> {
 
 // Stores that will be used across compiler steps. This is provided mainly so
 // that they don't need to be passed separately.
-class SharedValueStores : public Printable<SharedValueStores> {
+class SharedValueStores : public Yaml::Printable<SharedValueStores> {
  public:
   auto integers() -> ValueStore<IntegerId>& { return integers_; }
   auto integers() const -> const ValueStore<IntegerId>& { return integers_; }
@@ -261,12 +197,15 @@ class SharedValueStores : public Printable<SharedValueStores> {
   auto strings() -> ValueStore<StringId>& { return strings_; }
   auto strings() const -> const ValueStore<StringId>& { return strings_; }
 
-  auto Print(llvm::raw_ostream& out) const -> void {
-    out << "shared_values:\n"
-        << "  - ";
-    integers_.Print(out, "integers", 0, 6);
-    reals_.Print(out, "reals", 4, 6);
-    strings_.Print(out, "strings", 4, 6);
+  auto OutputYaml() const -> Yaml::OutputMapping {
+    return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+      map.Add("shared_values",
+              Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+                map.Add("integers", integers_.OutputYaml());
+                map.Add("reals", reals_.OutputYaml());
+                map.Add("strings", strings_.OutputYaml());
+              }));
+    });
   }
 
  private:

+ 12 - 11
toolchain/base/value_store_test.cpp

@@ -78,14 +78,14 @@ TEST(ValueStore, String) {
   EXPECT_THAT(value_stores.strings().Add(b), Eq(b_id));
 }
 
-auto MatchSharedValues(testing::Matcher<Yaml::SequenceValue> integers,
-                       testing::Matcher<Yaml::SequenceValue> reals,
-                       testing::Matcher<Yaml::SequenceValue> strings) -> auto {
+auto MatchSharedValues(testing::Matcher<Yaml::MappingValue> integers,
+                       testing::Matcher<Yaml::MappingValue> reals,
+                       testing::Matcher<Yaml::MappingValue> strings) -> auto {
   return Yaml::IsYaml(Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre(Pair(
-      "shared_values", Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre(
-                           Pair("integers", Yaml::Sequence(integers)),
-                           Pair("reals", Yaml::Sequence(reals)),
-                           Pair("strings", Yaml::Sequence(strings))))))))))));
+      "shared_values",
+      Yaml::Mapping(ElementsAre(Pair("integers", Yaml::Mapping(integers)),
+                                Pair("reals", Yaml::Mapping(reals)),
+                                Pair("strings", Yaml::Mapping(strings))))))))));
 }
 
 TEST(ValueStore, PrintEmpty) {
@@ -106,10 +106,11 @@ TEST(ValueStore, PrintVals) {
   TestRawOstream out;
   value_stores.Print(out);
 
-  EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()),
-              MatchSharedValues(ElementsAre(Yaml::Scalar("8")),
-                                ElementsAre(Yaml::Scalar("8*10^8")),
-                                ElementsAre(Yaml::Scalar("foo'\"baz"))));
+  EXPECT_THAT(
+      Yaml::Value::FromText(out.TakeStr()),
+      MatchSharedValues(ElementsAre(Pair("int0", Yaml::Scalar("8"))),
+                        ElementsAre(Pair("real0", Yaml::Scalar("8*10^8"))),
+                        ElementsAre(Pair("str0", Yaml::Scalar("foo'\"baz")))));
 }
 
 }  // namespace

+ 115 - 0
toolchain/base/yaml.h

@@ -0,0 +1,115 @@
+// 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_BASE_YAML_H_
+#define CARBON_TOOLCHAIN_BASE_YAML_H_
+
+#include "common/check.h"
+#include "common/ostream.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/YAMLTraits.h"
+
+// This file provides adapters for outputting YAML using llvm::yaml's APIs. It
+// only supports output, not input. However, it addresses the mix of const and
+// non-const expectations of the llvm::yaml that make it difficult to otherwise
+// use the trait-based approach.
+
+namespace Carbon::Yaml {
+
+// Helper for printing YAML, to maintain a consistent configuration.
+template <typename T>
+inline auto Print(llvm::raw_ostream& out, T yaml) -> void {
+  llvm::yaml::Output yout(out, /*Ctxt=*/nullptr, /*WrapColumn=*/80);
+  yout << yaml;
+}
+
+// Similar to the standard Printable<T>, but relies on OutputYaml for printing.
+template <typename T>
+class Printable : public Carbon::Printable<T> {
+ public:
+  auto Print(llvm::raw_ostream& out) const -> void {
+    Carbon::Yaml::Print(out, static_cast<const T*>(this)->OutputYaml());
+  }
+};
+
+// Adapts a function for outputting YAML as a scalar. This currently assumes no
+// scalars passed through this should be quoted.
+class OutputScalar {
+ public:
+  template <typename T>
+  explicit OutputScalar(const T& val)
+      : output_([&](llvm::raw_ostream& out) -> void { out << val; }) {}
+
+  explicit OutputScalar(const llvm::APInt& val)
+      : output_([&](llvm::raw_ostream& out) -> void {
+          // Carbon's plain APInt storage is typically unsigned.
+          val.print(out, /*isSigned=*/false);
+        }) {}
+
+  explicit OutputScalar(std::function<void(llvm::raw_ostream&)> output)
+      : output_(std::move(output)) {}
+
+  auto Output(llvm::raw_ostream& out) const -> void { output_(out); }
+
+ private:
+  std::function<void(llvm::raw_ostream&)> output_;
+};
+
+// Adapts a function for outputting YAML as a mapping.
+class OutputMapping {
+ public:
+  class Map {
+   public:
+    explicit Map(llvm::yaml::IO& io) : io_(io) {}
+
+    // Maps a value. This mainly takes responsibility for copying the value,
+    // letting mapRequired take `&value`.
+    template <typename T>
+    auto Add(llvm::StringRef key, T value) -> void {
+      io_.mapRequired(key.data(), value);
+    }
+
+   private:
+    llvm::yaml::IO& io_;
+  };
+
+  explicit OutputMapping(std::function<void(OutputMapping::Map)> output)
+      : output_(std::move(output)) {}
+
+  auto Output(llvm::yaml::IO& io) -> void { output_(Map(io)); }
+
+ private:
+  std::function<void(OutputMapping::Map)> output_;
+};
+
+}  // namespace Carbon::Yaml
+
+// Link OutputScalar to the llvm::yaml::IO API.
+template <>
+struct llvm::yaml::ScalarTraits<Carbon::Yaml::OutputScalar> {
+  static auto output(const Carbon::Yaml::OutputScalar& value, void* /*ctxt*/,
+                     llvm::raw_ostream& out) -> void {
+    value.Output(out);
+  }
+  static auto input(StringRef /*scalar*/, void* /*ctxt*/,
+                    Carbon::Yaml::OutputScalar& /*value*/) -> StringRef {
+    CARBON_FATAL() << "Input is unsupported.";
+  }
+  static auto mustQuote(StringRef /*value*/) -> QuotingType {
+    return QuotingType::None;
+  }
+};
+static_assert(llvm::yaml::has_ScalarTraits<Carbon::Yaml::OutputScalar>::value);
+
+// Link OutputMapping to the llvm::yaml::IO API.
+template <>
+struct llvm::yaml::MappingTraits<Carbon::Yaml::OutputMapping> {
+  static auto mapping(IO& io, Carbon::Yaml::OutputMapping& mapping) -> void {
+    mapping.Output(io);
+  }
+};
+static_assert(llvm::yaml::has_MappingTraits<Carbon::Yaml::OutputMapping,
+                                            llvm::yaml::EmptyContext>::value);
+
+#endif  // CARBON_TOOLCHAIN_BASE_YAML_H_

+ 20 - 18
toolchain/check/testdata/basics/builtin_nodes.carbon

@@ -6,21 +6,23 @@
 //
 // AUTOUPDATE
 
-// CHECK:STDOUT: - filename: builtin_nodes.carbon
-// CHECK:STDOUT:   sem_ir:
-// CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: []
-// CHECK:STDOUT:     classes: []
-// CHECK:STDOUT:     types: []
-// CHECK:STDOUT:     type_blocks: []
-// CHECK:STDOUT:     nodes:
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeTypeType, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeError, type: typeError}
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeBoolType, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeIntegerType, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeFloatingPointType, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeStringType, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeFunctionType, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeNamespaceType, type: typeTypeType}
-// CHECK:STDOUT:     node_blocks:
-// CHECK:STDOUT:       - []
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        builtin_nodes.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   cross_reference_irs_size: 1
+// CHECK:STDOUT:   functions:       {}
+// CHECK:STDOUT:   classes:         {}
+// CHECK:STDOUT:   types:           {}
+// CHECK:STDOUT:   type_blocks:     {}
+// CHECK:STDOUT:   nodes:
+// CHECK:STDOUT:     nodeTypeType:    {kind: CrossReference, arg0: ir0, arg1: nodeTypeType, type: typeTypeType}
+// CHECK:STDOUT:     nodeError:       {kind: CrossReference, arg0: ir0, arg1: nodeError, type: typeError}
+// CHECK:STDOUT:     nodeBoolType:    {kind: CrossReference, arg0: ir0, arg1: nodeBoolType, type: typeTypeType}
+// CHECK:STDOUT:     nodeIntegerType: {kind: CrossReference, arg0: ir0, arg1: nodeIntegerType, type: typeTypeType}
+// CHECK:STDOUT:     nodeFloatingPointType: {kind: CrossReference, arg0: ir0, arg1: nodeFloatingPointType, type: typeTypeType}
+// CHECK:STDOUT:     nodeStringType:  {kind: CrossReference, arg0: ir0, arg1: nodeStringType, type: typeTypeType}
+// CHECK:STDOUT:     nodeFunctionType: {kind: CrossReference, arg0: ir0, arg1: nodeFunctionType, type: typeTypeType}
+// CHECK:STDOUT:     nodeNamespaceType: {kind: CrossReference, arg0: ir0, arg1: nodeNamespaceType, type: typeTypeType}
+// CHECK:STDOUT:   node_blocks:
+// CHECK:STDOUT:     block0:          {}
+// CHECK:STDOUT: ...

+ 40 - 32
toolchain/check/testdata/basics/multifile_raw_and_textual_ir.carbon

@@ -14,22 +14,26 @@ fn A() {}
 // --- b.carbon
 fn B() {}
 
-// CHECK:STDOUT: - filename: a.carbon
-// CHECK:STDOUT:   sem_ir:
-// CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions:
-// CHECK:STDOUT:       - {name: str0, param_refs: block0, body: [block1]}
-// CHECK:STDOUT:     classes: []
-// CHECK:STDOUT:     types:
-// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type_blocks: []
-// CHECK:STDOUT:     nodes:
-// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
-// CHECK:STDOUT:       - {kind: Return}
-// CHECK:STDOUT:     node_blocks:
-// CHECK:STDOUT:       - []
-// CHECK:STDOUT:       - - node+1
-// CHECK:STDOUT:       - - node+0
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        a.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   cross_reference_irs_size: 1
+// CHECK:STDOUT:   functions:
+// CHECK:STDOUT:     function0:       {name: str0, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:   classes:         {}
+// CHECK:STDOUT:   types:
+// CHECK:STDOUT:     type0:           {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:   type_blocks:     {}
+// CHECK:STDOUT:   nodes:
+// CHECK:STDOUT:     node+0:          {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:     node+1:          {kind: Return}
+// CHECK:STDOUT:   node_blocks:
+// CHECK:STDOUT:     block0:          {}
+// CHECK:STDOUT:     block1:
+// CHECK:STDOUT:       0:               node+1
+// CHECK:STDOUT:     block2:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT: ...
 // CHECK:STDOUT:
 // CHECK:STDOUT: file "a.carbon" {
 // CHECK:STDOUT:   %A: <function> = fn_decl @A
@@ -39,22 +43,26 @@ fn B() {}
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
-// CHECK:STDOUT: - filename: b.carbon
-// CHECK:STDOUT:   sem_ir:
-// CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions:
-// CHECK:STDOUT:       - {name: str1, param_refs: block0, body: [block1]}
-// CHECK:STDOUT:     classes: []
-// CHECK:STDOUT:     types:
-// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type_blocks: []
-// CHECK:STDOUT:     nodes:
-// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
-// CHECK:STDOUT:       - {kind: Return}
-// CHECK:STDOUT:     node_blocks:
-// CHECK:STDOUT:       - []
-// CHECK:STDOUT:       - - node+1
-// CHECK:STDOUT:       - - node+0
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        b.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   cross_reference_irs_size: 1
+// CHECK:STDOUT:   functions:
+// CHECK:STDOUT:     function0:       {name: str1, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:   classes:         {}
+// CHECK:STDOUT:   types:
+// CHECK:STDOUT:     type0:           {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:   type_blocks:     {}
+// CHECK:STDOUT:   nodes:
+// CHECK:STDOUT:     node+0:          {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:     node+1:          {kind: Return}
+// CHECK:STDOUT:   node_blocks:
+// CHECK:STDOUT:     block0:          {}
+// CHECK:STDOUT:     block1:
+// CHECK:STDOUT:       0:               node+1
+// CHECK:STDOUT:     block2:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT: ...
 // CHECK:STDOUT:
 // CHECK:STDOUT: file "b.carbon" {
 // CHECK:STDOUT:   %B: <function> = fn_decl @B

+ 40 - 32
toolchain/check/testdata/basics/multifile_raw_ir.carbon

@@ -14,35 +14,43 @@ fn A() {}
 // --- b.carbon
 fn B() {}
 
-// CHECK:STDOUT: - filename: a.carbon
-// CHECK:STDOUT:   sem_ir:
-// CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions:
-// CHECK:STDOUT:       - {name: str0, param_refs: block0, body: [block1]}
-// CHECK:STDOUT:     classes: []
-// CHECK:STDOUT:     types:
-// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type_blocks: []
-// CHECK:STDOUT:     nodes:
-// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
-// CHECK:STDOUT:       - {kind: Return}
-// CHECK:STDOUT:     node_blocks:
-// CHECK:STDOUT:       - []
-// CHECK:STDOUT:       - - node+1
-// CHECK:STDOUT:       - - node+0
-// CHECK:STDOUT: - filename: b.carbon
-// CHECK:STDOUT:   sem_ir:
-// CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions:
-// CHECK:STDOUT:       - {name: str1, param_refs: block0, body: [block1]}
-// CHECK:STDOUT:     classes: []
-// CHECK:STDOUT:     types:
-// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:     type_blocks: []
-// CHECK:STDOUT:     nodes:
-// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
-// CHECK:STDOUT:       - {kind: Return}
-// CHECK:STDOUT:     node_blocks:
-// CHECK:STDOUT:       - []
-// CHECK:STDOUT:       - - node+1
-// CHECK:STDOUT:       - - node+0
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        a.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   cross_reference_irs_size: 1
+// CHECK:STDOUT:   functions:
+// CHECK:STDOUT:     function0:       {name: str0, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:   classes:         {}
+// CHECK:STDOUT:   types:
+// CHECK:STDOUT:     type0:           {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:   type_blocks:     {}
+// CHECK:STDOUT:   nodes:
+// CHECK:STDOUT:     node+0:          {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:     node+1:          {kind: Return}
+// CHECK:STDOUT:   node_blocks:
+// CHECK:STDOUT:     block0:          {}
+// CHECK:STDOUT:     block1:
+// CHECK:STDOUT:       0:               node+1
+// CHECK:STDOUT:     block2:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT: ...
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        b.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   cross_reference_irs_size: 1
+// CHECK:STDOUT:   functions:
+// CHECK:STDOUT:     function0:       {name: str1, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:   classes:         {}
+// CHECK:STDOUT:   types:
+// CHECK:STDOUT:     type0:           {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:   type_blocks:     {}
+// CHECK:STDOUT:   nodes:
+// CHECK:STDOUT:     node+0:          {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:     node+1:          {kind: Return}
+// CHECK:STDOUT:   node_blocks:
+// CHECK:STDOUT:     block0:          {}
+// CHECK:STDOUT:     block1:
+// CHECK:STDOUT:       0:               node+1
+// CHECK:STDOUT:     block2:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT: ...

+ 75 - 64
toolchain/check/testdata/basics/raw_and_textual_ir.carbon

@@ -12,70 +12,81 @@ fn Foo(n: i32) -> (i32, f64) {
   return (n + 2, 3.4);
 }
 
-// CHECK:STDOUT: - filename: raw_and_textual_ir.carbon
-// CHECK:STDOUT:   sem_ir:
-// CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions:
-// CHECK:STDOUT:       - {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
-// CHECK:STDOUT:     classes: []
-// CHECK:STDOUT:     types:
-// CHECK:STDOUT:       - {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:       - {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
-// CHECK:STDOUT:       - {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
-// CHECK:STDOUT:       - {node: node+3, value_rep: {kind: pointer, type: type4}}
-// CHECK:STDOUT:       - {node: node+5, value_rep: {kind: copy, type: type4}}
-// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
-// CHECK:STDOUT:     type_blocks:
-// CHECK:STDOUT:       - - typeTypeType
-// CHECK:STDOUT:         - typeTypeType
-// CHECK:STDOUT:       - - type0
-// CHECK:STDOUT:         - type2
-// CHECK:STDOUT:     nodes:
-// CHECK:STDOUT:       - {kind: Parameter, arg0: str1, type: type0}
-// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block2, type: type1}
-// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: VarStorage, arg0: str2, type: type3}
-// CHECK:STDOUT:       - {kind: PointerType, arg0: type3, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type5}
-// CHECK:STDOUT:       - {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
-// CHECK:STDOUT:       - {kind: IntegerLiteral, arg0: int3, type: type0}
-// CHECK:STDOUT:       - {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
-// CHECK:STDOUT:       - {kind: RealLiteral, arg0: real0, type: type2}
-// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block5, type: type3}
-// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
-// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
-// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
-// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
-// CHECK:STDOUT:       - {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
-// CHECK:STDOUT:       - {kind: ReturnExpression, arg0: node+16}
-// CHECK:STDOUT:     node_blocks:
-// CHECK:STDOUT:       - []
-// CHECK:STDOUT:       - - node+0
-// CHECK:STDOUT:       - - nodeIntegerType
-// CHECK:STDOUT:         - nodeFloatingPointType
-// CHECK:STDOUT:       - - node+0
-// CHECK:STDOUT:         - node+1
-// CHECK:STDOUT:         - node+2
-// CHECK:STDOUT:         - node+3
-// CHECK:STDOUT:         - node+4
-// CHECK:STDOUT:       - - node+7
-// CHECK:STDOUT:         - node+8
-// CHECK:STDOUT:         - node+9
-// CHECK:STDOUT:         - node+10
-// CHECK:STDOUT:         - node+11
-// CHECK:STDOUT:         - node+12
-// CHECK:STDOUT:         - node+13
-// CHECK:STDOUT:         - node+14
-// CHECK:STDOUT:         - node+15
-// CHECK:STDOUT:         - node+16
-// CHECK:STDOUT:         - node+17
-// CHECK:STDOUT:       - - node+9
-// CHECK:STDOUT:         - node+10
-// CHECK:STDOUT:       - - node+13
-// CHECK:STDOUT:         - node+15
-// CHECK:STDOUT:       - - node+5
-// CHECK:STDOUT:         - node+6
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        raw_and_textual_ir.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   cross_reference_irs_size: 1
+// CHECK:STDOUT:   functions:
+// CHECK:STDOUT:     function0:       {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
+// CHECK:STDOUT:   classes:         {}
+// CHECK:STDOUT:   types:
+// CHECK:STDOUT:     type0:           {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:     type1:           {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
+// CHECK:STDOUT:     type2:           {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
+// CHECK:STDOUT:     type3:           {node: node+3, value_rep: {kind: pointer, type: type4}}
+// CHECK:STDOUT:     type4:           {node: node+5, value_rep: {kind: copy, type: type4}}
+// CHECK:STDOUT:     type5:           {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
+// CHECK:STDOUT:   type_blocks:
+// CHECK:STDOUT:     typeBlock0:
+// CHECK:STDOUT:       0:               typeTypeType
+// CHECK:STDOUT:       1:               typeTypeType
+// CHECK:STDOUT:     typeBlock1:
+// CHECK:STDOUT:       0:               type0
+// CHECK:STDOUT:       1:               type2
+// CHECK:STDOUT:   nodes:
+// CHECK:STDOUT:     node+0:          {kind: Parameter, arg0: str1, type: type0}
+// CHECK:STDOUT:     node+1:          {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
+// CHECK:STDOUT:     node+2:          {kind: TupleLiteral, arg0: block2, type: type1}
+// CHECK:STDOUT:     node+3:          {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
+// CHECK:STDOUT:     node+4:          {kind: VarStorage, arg0: str2, type: type3}
+// CHECK:STDOUT:     node+5:          {kind: PointerType, arg0: type3, type: typeTypeType}
+// CHECK:STDOUT:     node+6:          {kind: FunctionDeclaration, arg0: function0, type: type5}
+// CHECK:STDOUT:     node+7:          {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
+// CHECK:STDOUT:     node+8:          {kind: IntegerLiteral, arg0: int3, type: type0}
+// CHECK:STDOUT:     node+9:          {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
+// CHECK:STDOUT:     node+10:         {kind: RealLiteral, arg0: real0, type: type2}
+// CHECK:STDOUT:     node+11:         {kind: TupleLiteral, arg0: block5, type: type3}
+// CHECK:STDOUT:     node+12:         {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
+// CHECK:STDOUT:     node+13:         {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
+// CHECK:STDOUT:     node+14:         {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
+// CHECK:STDOUT:     node+15:         {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
+// CHECK:STDOUT:     node+16:         {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
+// CHECK:STDOUT:     node+17:         {kind: ReturnExpression, arg0: node+16}
+// CHECK:STDOUT:   node_blocks:
+// CHECK:STDOUT:     block0:          {}
+// CHECK:STDOUT:     block1:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT:     block2:
+// CHECK:STDOUT:       0:               nodeIntegerType
+// CHECK:STDOUT:       1:               nodeFloatingPointType
+// CHECK:STDOUT:     block3:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT:       1:               node+1
+// CHECK:STDOUT:       2:               node+2
+// CHECK:STDOUT:       3:               node+3
+// CHECK:STDOUT:       4:               node+4
+// CHECK:STDOUT:     block4:
+// CHECK:STDOUT:       0:               node+7
+// CHECK:STDOUT:       1:               node+8
+// CHECK:STDOUT:       2:               node+9
+// CHECK:STDOUT:       3:               node+10
+// CHECK:STDOUT:       4:               node+11
+// CHECK:STDOUT:       5:               node+12
+// CHECK:STDOUT:       6:               node+13
+// CHECK:STDOUT:       7:               node+14
+// CHECK:STDOUT:       8:               node+15
+// CHECK:STDOUT:       9:               node+16
+// CHECK:STDOUT:       10:              node+17
+// CHECK:STDOUT:     block5:
+// CHECK:STDOUT:       0:               node+9
+// CHECK:STDOUT:       1:               node+10
+// CHECK:STDOUT:     block6:
+// CHECK:STDOUT:       0:               node+13
+// CHECK:STDOUT:       1:               node+15
+// CHECK:STDOUT:     block7:
+// CHECK:STDOUT:       0:               node+5
+// CHECK:STDOUT:       1:               node+6
+// CHECK:STDOUT: ...
 // CHECK:STDOUT:
 // CHECK:STDOUT: file "raw_and_textual_ir.carbon" {
 // CHECK:STDOUT:   %.loc11: type = ptr_type (i32, f64)

+ 75 - 64
toolchain/check/testdata/basics/raw_ir.carbon

@@ -12,67 +12,78 @@ fn Foo(n: i32) -> (i32, f64) {
   return (n + 2, 3.4);
 }
 
-// CHECK:STDOUT: - filename: raw_ir.carbon
-// CHECK:STDOUT:   sem_ir:
-// CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions:
-// CHECK:STDOUT:       - {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
-// CHECK:STDOUT:     classes: []
-// CHECK:STDOUT:     types:
-// CHECK:STDOUT:       - {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
-// CHECK:STDOUT:       - {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
-// CHECK:STDOUT:       - {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
-// CHECK:STDOUT:       - {node: node+3, value_rep: {kind: pointer, type: type4}}
-// CHECK:STDOUT:       - {node: node+5, value_rep: {kind: copy, type: type4}}
-// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
-// CHECK:STDOUT:     type_blocks:
-// CHECK:STDOUT:       - - typeTypeType
-// CHECK:STDOUT:         - typeTypeType
-// CHECK:STDOUT:       - - type0
-// CHECK:STDOUT:         - type2
-// CHECK:STDOUT:     nodes:
-// CHECK:STDOUT:       - {kind: Parameter, arg0: str1, type: type0}
-// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block2, type: type1}
-// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: VarStorage, arg0: str2, type: type3}
-// CHECK:STDOUT:       - {kind: PointerType, arg0: type3, type: typeTypeType}
-// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type5}
-// CHECK:STDOUT:       - {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
-// CHECK:STDOUT:       - {kind: IntegerLiteral, arg0: int3, type: type0}
-// CHECK:STDOUT:       - {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
-// CHECK:STDOUT:       - {kind: RealLiteral, arg0: real0, type: type2}
-// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block5, type: type3}
-// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
-// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
-// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
-// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
-// CHECK:STDOUT:       - {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
-// CHECK:STDOUT:       - {kind: ReturnExpression, arg0: node+16}
-// CHECK:STDOUT:     node_blocks:
-// CHECK:STDOUT:       - []
-// CHECK:STDOUT:       - - node+0
-// CHECK:STDOUT:       - - nodeIntegerType
-// CHECK:STDOUT:         - nodeFloatingPointType
-// CHECK:STDOUT:       - - node+0
-// CHECK:STDOUT:         - node+1
-// CHECK:STDOUT:         - node+2
-// CHECK:STDOUT:         - node+3
-// CHECK:STDOUT:         - node+4
-// CHECK:STDOUT:       - - node+7
-// CHECK:STDOUT:         - node+8
-// CHECK:STDOUT:         - node+9
-// CHECK:STDOUT:         - node+10
-// CHECK:STDOUT:         - node+11
-// CHECK:STDOUT:         - node+12
-// CHECK:STDOUT:         - node+13
-// CHECK:STDOUT:         - node+14
-// CHECK:STDOUT:         - node+15
-// CHECK:STDOUT:         - node+16
-// CHECK:STDOUT:         - node+17
-// CHECK:STDOUT:       - - node+9
-// CHECK:STDOUT:         - node+10
-// CHECK:STDOUT:       - - node+13
-// CHECK:STDOUT:         - node+15
-// CHECK:STDOUT:       - - node+5
-// CHECK:STDOUT:         - node+6
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        raw_ir.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   cross_reference_irs_size: 1
+// CHECK:STDOUT:   functions:
+// CHECK:STDOUT:     function0:       {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
+// CHECK:STDOUT:   classes:         {}
+// CHECK:STDOUT:   types:
+// CHECK:STDOUT:     type0:           {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:     type1:           {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
+// CHECK:STDOUT:     type2:           {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
+// CHECK:STDOUT:     type3:           {node: node+3, value_rep: {kind: pointer, type: type4}}
+// CHECK:STDOUT:     type4:           {node: node+5, value_rep: {kind: copy, type: type4}}
+// CHECK:STDOUT:     type5:           {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
+// CHECK:STDOUT:   type_blocks:
+// CHECK:STDOUT:     typeBlock0:
+// CHECK:STDOUT:       0:               typeTypeType
+// CHECK:STDOUT:       1:               typeTypeType
+// CHECK:STDOUT:     typeBlock1:
+// CHECK:STDOUT:       0:               type0
+// CHECK:STDOUT:       1:               type2
+// CHECK:STDOUT:   nodes:
+// CHECK:STDOUT:     node+0:          {kind: Parameter, arg0: str1, type: type0}
+// CHECK:STDOUT:     node+1:          {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
+// CHECK:STDOUT:     node+2:          {kind: TupleLiteral, arg0: block2, type: type1}
+// CHECK:STDOUT:     node+3:          {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
+// CHECK:STDOUT:     node+4:          {kind: VarStorage, arg0: str2, type: type3}
+// CHECK:STDOUT:     node+5:          {kind: PointerType, arg0: type3, type: typeTypeType}
+// CHECK:STDOUT:     node+6:          {kind: FunctionDeclaration, arg0: function0, type: type5}
+// CHECK:STDOUT:     node+7:          {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
+// CHECK:STDOUT:     node+8:          {kind: IntegerLiteral, arg0: int3, type: type0}
+// CHECK:STDOUT:     node+9:          {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
+// CHECK:STDOUT:     node+10:         {kind: RealLiteral, arg0: real0, type: type2}
+// CHECK:STDOUT:     node+11:         {kind: TupleLiteral, arg0: block5, type: type3}
+// CHECK:STDOUT:     node+12:         {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
+// CHECK:STDOUT:     node+13:         {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
+// CHECK:STDOUT:     node+14:         {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
+// CHECK:STDOUT:     node+15:         {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
+// CHECK:STDOUT:     node+16:         {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
+// CHECK:STDOUT:     node+17:         {kind: ReturnExpression, arg0: node+16}
+// CHECK:STDOUT:   node_blocks:
+// CHECK:STDOUT:     block0:          {}
+// CHECK:STDOUT:     block1:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT:     block2:
+// CHECK:STDOUT:       0:               nodeIntegerType
+// CHECK:STDOUT:       1:               nodeFloatingPointType
+// CHECK:STDOUT:     block3:
+// CHECK:STDOUT:       0:               node+0
+// CHECK:STDOUT:       1:               node+1
+// CHECK:STDOUT:       2:               node+2
+// CHECK:STDOUT:       3:               node+3
+// CHECK:STDOUT:       4:               node+4
+// CHECK:STDOUT:     block4:
+// CHECK:STDOUT:       0:               node+7
+// CHECK:STDOUT:       1:               node+8
+// CHECK:STDOUT:       2:               node+9
+// CHECK:STDOUT:       3:               node+10
+// CHECK:STDOUT:       4:               node+11
+// CHECK:STDOUT:       5:               node+12
+// CHECK:STDOUT:       6:               node+13
+// CHECK:STDOUT:       7:               node+14
+// CHECK:STDOUT:       8:               node+15
+// CHECK:STDOUT:       9:               node+16
+// CHECK:STDOUT:       10:              node+17
+// CHECK:STDOUT:     block5:
+// CHECK:STDOUT:       0:               node+9
+// CHECK:STDOUT:       1:               node+10
+// CHECK:STDOUT:     block6:
+// CHECK:STDOUT:       0:               node+13
+// CHECK:STDOUT:       1:               node+15
+// CHECK:STDOUT:     block7:
+// CHECK:STDOUT:       0:               node+5
+// CHECK:STDOUT:       1:               node+6
+// CHECK:STDOUT: ...

+ 24 - 22
toolchain/driver/testdata/dump_shared_values.carbon

@@ -15,26 +15,28 @@ var real3: f64 = 0.8e9;
 var str1: String = "abc";
 var str2: String = "ab'\"c";
 
+// CHECK:STDOUT: ---
 // CHECK:STDOUT: shared_values:
-// CHECK:STDOUT:   - integers:
-// CHECK:STDOUT:       - 32
-// CHECK:STDOUT:       - 1
-// CHECK:STDOUT:       - 32
-// CHECK:STDOUT:       - 8
-// CHECK:STDOUT:       - 64
-// CHECK:STDOUT:       - 64
-// CHECK:STDOUT:       - 64
-// CHECK:STDOUT:     reals:
-// CHECK:STDOUT:       - 10*10^-1
-// CHECK:STDOUT:       - 8*10^7
-// CHECK:STDOUT:       - 8*10^8
-// CHECK:STDOUT:     strings:
-// CHECK:STDOUT:       - "int1"
-// CHECK:STDOUT:       - "int2"
-// CHECK:STDOUT:       - "real1"
-// CHECK:STDOUT:       - "real2"
-// CHECK:STDOUT:       - "real3"
-// CHECK:STDOUT:       - "str1"
-// CHECK:STDOUT:       - "abc"
-// CHECK:STDOUT:       - "str2"
-// CHECK:STDOUT:       - "ab'\"c"
+// CHECK:STDOUT:   integers:
+// CHECK:STDOUT:     int0:            32
+// CHECK:STDOUT:     int1:            1
+// CHECK:STDOUT:     int2:            32
+// CHECK:STDOUT:     int3:            8
+// CHECK:STDOUT:     int4:            64
+// CHECK:STDOUT:     int5:            64
+// CHECK:STDOUT:     int6:            64
+// CHECK:STDOUT:   reals:
+// CHECK:STDOUT:     real0:           10*10^-1
+// CHECK:STDOUT:     real1:           8*10^7
+// CHECK:STDOUT:     real2:           8*10^8
+// CHECK:STDOUT:   strings:
+// CHECK:STDOUT:     str0:            int1
+// CHECK:STDOUT:     str1:            int2
+// CHECK:STDOUT:     str2:            real1
+// CHECK:STDOUT:     str3:            real2
+// CHECK:STDOUT:     str4:            real3
+// CHECK:STDOUT:     str5:            str1
+// CHECK:STDOUT:     str6:            abc
+// CHECK:STDOUT:     str7:            str2
+// CHECK:STDOUT:     str8:            'ab''"c'
+// CHECK:STDOUT: ...

+ 1 - 1
toolchain/lex/tokenized_buffer_test.cpp

@@ -1057,7 +1057,7 @@ TEST_F(LexerTest, DiagnosticUnrecognizedChar) {
   Lex("\b", consumer);
 }
 
-TEST_F(LexerTest, PrintingAsYaml) {
+TEST_F(LexerTest, PrintingOutputYaml) {
   // Test that we can parse this into YAML and verify line and indent data.
   auto buffer = Lex("\n ;\n\n\n; ;\n\n\n\n\n\n\n\n\n\n\n");
   ASSERT_FALSE(buffer.has_errors());

+ 2 - 0
toolchain/sem_ir/BUILD

@@ -69,6 +69,7 @@ cc_library(
         ":value_stores",
         "//common:check",
         "//toolchain/base:value_store",
+        "//toolchain/base:yaml",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -102,6 +103,7 @@ cc_library(
     deps = [
         ":node",
         "//toolchain/base:value_store",
+        "//toolchain/base:yaml",
         "@llvm-project//llvm:Support",
     ],
 )

+ 25 - 21
toolchain/sem_ir/file.cpp

@@ -8,6 +8,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "toolchain/base/value_store.h"
+#include "toolchain/base/yaml.h"
 #include "toolchain/sem_ir/builtin_kind.h"
 #include "toolchain/sem_ir/node.h"
 #include "toolchain/sem_ir/node_kind.h"
@@ -129,27 +130,30 @@ auto File::Verify() const -> ErrorOr<Success> {
   return Success();
 }
 
-auto File::Print(llvm::raw_ostream& out, bool include_builtins) const -> void {
-  out << "- filename: " << filename_ << "\n"
-      << "  sem_ir:\n"
-      << "  - cross_reference_irs_size: " << cross_reference_irs_.size()
-      << "\n";
-
-  static constexpr int FirstLineIndent = 4;
-  static constexpr int LaterIndent = 6;
-  functions_.Print(out, "functions", FirstLineIndent, LaterIndent);
-  classes_.Print(out, "classes", FirstLineIndent, LaterIndent);
-  types_.Print(out, "types", FirstLineIndent, LaterIndent);
-  type_blocks_.Print(out, "type_blocks", FirstLineIndent, LaterIndent);
-
-  auto nodes = nodes_.array_ref();
-  if (!include_builtins) {
-    nodes = nodes.drop_front(BuiltinKind::ValidCount);
-  }
-  PrintValueRange(out, llvm::iterator_range(nodes), "nodes", FirstLineIndent,
-                  LaterIndent, /*trailing_newline=*/true);
-
-  node_blocks_.Print(out, "node_blocks", FirstLineIndent, LaterIndent);
+auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping {
+  return Yaml::OutputMapping([this,
+                              include_builtins](Yaml::OutputMapping::Map map) {
+    map.Add("filename", filename_);
+    map.Add("sem_ir", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+              map.Add("cross_reference_irs_size",
+                      Yaml::OutputScalar(cross_reference_irs_.size()));
+              map.Add("functions", functions_.OutputYaml());
+              map.Add("classes", classes_.OutputYaml());
+              map.Add("types", types_.OutputYaml());
+              map.Add("type_blocks", type_blocks_.OutputYaml());
+              map.Add("nodes",
+                      Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+                        int start =
+                            include_builtins ? 0 : BuiltinKind::ValidCount;
+                        for (int i : llvm::seq(start, nodes_.size())) {
+                          auto id = NodeId(i);
+                          map.Add(PrintToString(id),
+                                  Yaml::OutputScalar(nodes_.Get(id)));
+                        }
+                      }));
+              map.Add("node_blocks", node_blocks_.OutputYaml());
+            }));
+  });
 }
 
 // Map a node kind representing a type into an integer describing the

+ 5 - 4
toolchain/sem_ir/file.h

@@ -10,6 +10,7 @@
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "toolchain/base/value_store.h"
+#include "toolchain/base/yaml.h"
 #include "toolchain/sem_ir/node.h"
 #include "toolchain/sem_ir/value_stores.h"
 
@@ -174,11 +175,11 @@ class File : public Printable<File> {
   // TODO: In the future, the things to print may change, for example by adding
   // preludes. We may then want the ability to omit other things similar to
   // builtins.
-  auto Print(llvm::raw_ostream& out, bool include_builtins) const -> void;
-
-  auto Print(llvm::raw_ostream& out) const -> void {
-    Print(out, /*include_builtins=*/false);
+  auto Print(llvm::raw_ostream& out, bool include_builtins = false) const
+      -> void {
+    Yaml::Print(out, OutputYaml(include_builtins));
   }
+  auto OutputYaml(bool include_builtins) const -> Yaml::OutputMapping;
 
   // Returns array bound value from the bound node.
   auto GetArrayBoundValue(NodeId bound_id) const -> uint64_t {

+ 15 - 13
toolchain/sem_ir/value_stores.h

@@ -7,6 +7,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "toolchain/base/value_store.h"
+#include "toolchain/base/yaml.h"
 #include "toolchain/sem_ir/node.h"
 
 namespace Carbon::SemIR {
@@ -72,7 +73,7 @@ class NameScopeStore {
 // BlockValueStore is used as-is, but there are also children that expose the
 // protected members for type-specific functionality.
 template <typename IdT, typename ValueT>
-class BlockValueStore {
+class BlockValueStore : public Yaml::Printable<BlockValueStore<IdT, ValueT>> {
  public:
   explicit BlockValueStore(llvm::BumpPtrAllocator& allocator)
       : allocator_(&allocator) {}
@@ -88,18 +89,19 @@ class BlockValueStore {
   // Returns the requested block.
   auto Get(IdT id) -> llvm::MutableArrayRef<ValueT> { return values_.Get(id); }
 
-  auto Print(llvm::raw_ostream& out) const -> void {
-    Print(out, std::nullopt, 0, 0);
-  }
-  auto Print(llvm::raw_ostream& out, std::optional<llvm::StringRef> label,
-             int first_line_indent, int later_indent) const -> void {
-    values_.Print(out, label, first_line_indent, later_indent,
-                  [&](llvm::raw_ostream& out,
-                      const llvm::MutableArrayRef<ValueT>& value) {
-                    PrintValueRange<ValueT>(out, llvm::iterator_range(value),
-                                            std::nullopt, 0, later_indent + 2,
-                                            /*trailing_newline=*/false);
-                  });
+  auto OutputYaml() const -> Yaml::OutputMapping {
+    return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+      for (auto block_index : llvm::seq(values_.size())) {
+        auto block_id = IdT(block_index);
+        map.Add(PrintToString(block_id),
+                Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
+                  auto block = Get(block_id);
+                  for (auto i : llvm::seq(block.size())) {
+                    map.Add(llvm::itostr(i), Yaml::OutputScalar(block[i]));
+                  }
+                }));
+      }
+    });
   }
 
   auto size() const -> int { return values_.size(); }

+ 26 - 21
toolchain/sem_ir/yaml_test.cpp

@@ -44,40 +44,45 @@ TEST(SemIRTest, YAML) {
   auto node_id = Yaml::Scalar(MatchesRegex(R"(node\+\d+)"));
   auto node_builtin = Yaml::Scalar(MatchesRegex(R"(node\w+)"));
   auto type_id = Yaml::Scalar(MatchesRegex(R"(type\d+)"));
-  auto type_builtin = Yaml::Mapping(ElementsAre(
-      Pair("node", node_builtin), Pair("value_rep", Yaml::Mapping(_))));
+  auto type_builtin = Pair(
+      type_id, Yaml::Mapping(ElementsAre(Pair("node", node_builtin),
+                                         Pair("value_rep", Yaml::Mapping(_)))));
 
-  auto file = Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre(
+  auto file = Yaml::Mapping(ElementsAre(
       Pair("cross_reference_irs_size", "1"),
-      Pair("functions", Yaml::Sequence(SizeIs(1))),
-      Pair("classes", Yaml::Sequence(SizeIs(0))),
-      Pair("types", Yaml::Sequence(Each(type_builtin))),
-      Pair("type_blocks", Yaml::Sequence(IsEmpty())),
+      Pair("functions", Yaml::Mapping(SizeIs(1))),
+      Pair("classes", Yaml::Mapping(SizeIs(0))),
+      Pair("types", Yaml::Mapping(Each(type_builtin))),
+      Pair("type_blocks", Yaml::Mapping(IsEmpty())),
       Pair("nodes",
-           Yaml::Sequence(AllOf(
+           Yaml::Mapping(AllOf(
+               Each(Key(node_id)),
                // kind is required, other parts are optional.
-               Each(Yaml::Mapping(Contains(Pair("kind", _)))),
+               Each(Pair(_, Yaml::Mapping(Contains(Pair("kind", _))))),
                // A 0-arg node.
-               Contains(Yaml::Mapping(ElementsAre(Pair("kind", "Return")))),
+               Contains(
+                   Pair(_, Yaml::Mapping(ElementsAre(Pair("kind", "Return"))))),
                // A 1-arg node.
-               Contains(Yaml::Mapping(ElementsAre(
-                   Pair("kind", "IntegerLiteral"), Pair("arg0", integer_id),
-                   Pair("type", type_id)))),
+               Contains(Pair(
+                   _, Yaml::Mapping(ElementsAre(Pair("kind", "IntegerLiteral"),
+                                                Pair("arg0", integer_id),
+                                                Pair("type", type_id))))),
                // A 2-arg node.
-               Contains(Yaml::Mapping(ElementsAre(Pair("kind", "Assign"),
-                                                  Pair("arg0", node_id),
-                                                  Pair("arg1", node_id))))))),
+               Contains(Pair(
+                   _, Yaml::Mapping(ElementsAre(Pair("kind", "Assign"),
+                                                Pair("arg0", node_id),
+                                                Pair("arg1", node_id)))))))),
       // This production has only two node blocks.
       Pair("node_blocks",
-           Yaml::Sequence(ElementsAre(Yaml::Sequence(IsEmpty()),
-                                      Yaml::Sequence(Each(node_id)),
-                                      Yaml::Sequence(Each(node_id)))))))));
+           Yaml::Mapping(ElementsAre(
+               Pair("block0", Yaml::Mapping(IsEmpty())),
+               Pair("block1", Yaml::Mapping(Each(Pair(_, node_id)))),
+               Pair("block2", Yaml::Mapping(Each(Pair(_, node_id)))))))));
 
   auto root = Yaml::Sequence(ElementsAre(Yaml::Mapping(
       ElementsAre(Pair("filename", "test.carbon"), Pair("sem_ir", file)))));
 
-  EXPECT_THAT(Yaml::Value::FromText(print_stream.TakeStr()),
-              IsYaml(ElementsAre(root)));
+  EXPECT_THAT(Yaml::Value::FromText(print_stream.TakeStr()), IsYaml(root));
 }
 
 }  // namespace