[ir] Remove operator<<

The use of `operator<<` gets much more convoluted as things are changed
over to pointers and with inheritance. This CL switches the `operator<<`
methods to `ToString` functions.

Bug: tint:1718
Change-Id: I85fd25b870d82d995eb27014c767abe071e543b0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112046
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/ir/constant.cc b/src/tint/ir/constant.cc
index 78895bf..0138190 100644
--- a/src/tint/ir/constant.cc
+++ b/src/tint/ir/constant.cc
@@ -32,22 +32,22 @@
 
 Constant::~Constant() = default;
 
-std::ostream& operator<<(std::ostream& out, const Constant& r) {
-    switch (r.GetKind()) {
+std::ostream& Constant::ToString(std::ostream& out) const {
+    switch (GetKind()) {
         case Constant::Kind::kF32:
-            out << std::to_string(r.AsF32().value);
+            out << std::to_string(AsF32().value);
             break;
         case Constant::Kind::kF16:
-            out << std::to_string(r.AsF16().value);
+            out << std::to_string(AsF16().value);
             break;
         case Constant::Kind::kI32:
-            out << std::to_string(r.AsI32().value);
+            out << std::to_string(AsI32().value);
             break;
         case Constant::Kind::kU32:
-            out << std::to_string(r.AsU32().value);
+            out << std::to_string(AsU32().value);
             break;
         case Constant::Kind::kBool:
-            out << (r.AsBool() ? "true" : "false");
+            out << (AsBool() ? "true" : "false");
             break;
     }
     return out;
diff --git a/src/tint/ir/constant.h b/src/tint/ir/constant.h
index c06e121..23eeb02 100644
--- a/src/tint/ir/constant.h
+++ b/src/tint/ir/constant.h
@@ -100,6 +100,11 @@
     /// @note, must only be called if `IsBool()` is true
     bool AsBool() const { return std::get<bool>(data_); }
 
+    /// Write the constant to the given stream
+    /// @param out the stream to write to
+    /// @returns the stream
+    std::ostream& ToString(std::ostream& out) const override;
+
   private:
     /// The type of data stored in this constant
     Kind kind_;
@@ -107,8 +112,6 @@
     std::variant<f32, f16, u32, i32, bool> data_;
 };
 
-std::ostream& operator<<(std::ostream& out, const Constant& r);
-
 }  // namespace tint::ir
 
 #endif  // SRC_TINT_IR_CONSTANT_H_
diff --git a/src/tint/ir/constant_test.cc b/src/tint/ir/constant_test.cc
index 7b4e224..b20b424 100644
--- a/src/tint/ir/constant_test.cc
+++ b/src/tint/ir/constant_test.cc
@@ -32,7 +32,7 @@
     auto* val = b.builder.Constant(1.2_f);
     EXPECT_EQ(1.2_f, val->AsF32());
 
-    str << *val;
+    val->ToString(str);
     EXPECT_EQ("1.200000", str.str());
 
     EXPECT_TRUE(val->IsF32());
@@ -50,7 +50,7 @@
     auto* val = b.builder.Constant(1.1_h);
     EXPECT_EQ(1.1_h, val->AsF16());
 
-    str << *val;
+    val->ToString(str);
     EXPECT_EQ("1.099609", str.str());
 
     EXPECT_FALSE(val->IsF32());
@@ -68,7 +68,7 @@
     auto* val = b.builder.Constant(1_i);
     EXPECT_EQ(1_i, val->AsI32());
 
-    str << *val;
+    val->ToString(str);
     EXPECT_EQ("1", str.str());
 
     EXPECT_FALSE(val->IsF32());
@@ -86,7 +86,7 @@
     auto* val = b.builder.Constant(2_u);
     EXPECT_EQ(2_u, val->AsU32());
 
-    str << *val;
+    val->ToString(str);
     EXPECT_EQ("2", str.str());
 
     EXPECT_FALSE(val->IsF32());
@@ -104,14 +104,14 @@
     auto* val = b.builder.Constant(false);
     EXPECT_FALSE(val->AsBool());
 
-    str << *val;
+    val->ToString(str);
     EXPECT_EQ("false", str.str());
 
     str.str("");
     val = b.builder.Constant(true);
     EXPECT_TRUE(val->AsBool());
 
-    str << *val;
+    val->ToString(str);
     EXPECT_EQ("true", str.str());
 
     EXPECT_FALSE(val->IsF32());
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index e2749a1..d8ee4ba 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -63,7 +63,7 @@
 
 void Disassembler::EmitBlockInstructions(const Block* b) {
     for (const auto* instr : b->instructions) {
-        out_ << *instr << std::endl;
+        instr->ToString(out_) << std::endl;
     }
 }
 
diff --git a/src/tint/ir/instruction.cc b/src/tint/ir/instruction.cc
index 6c0f176..d8ce2dd 100644
--- a/src/tint/ir/instruction.cc
+++ b/src/tint/ir/instruction.cc
@@ -33,14 +33,14 @@
 
 Instruction& Instruction::operator=(Instruction&& instr) = default;
 
-std::ostream& operator<<(std::ostream& out, const Instruction& instr) {
-    out << *(instr.Result()) << " = ";
-    if (instr.HasLHS()) {
-        out << *(instr.LHS());
+std::ostream& Instruction::ToString(std::ostream& out) const {
+    Result()->ToString(out) << " = ";
+    if (HasLHS()) {
+        LHS()->ToString(out);
     }
     out << " ";
 
-    switch (instr.GetKind()) {
+    switch (GetKind()) {
         case Instruction::Kind::kAdd:
             out << "+";
             break;
@@ -97,8 +97,9 @@
             break;
     }
 
-    if (instr.HasRHS()) {
-        out << " " << *(instr.RHS());
+    if (HasRHS()) {
+        out << " ";
+        RHS()->ToString(out);
     }
 
     return out;
diff --git a/src/tint/ir/instruction.h b/src/tint/ir/instruction.h
index 0cb5f82..1ae8446 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -101,6 +101,11 @@
         return args_[1];
     }
 
+    /// Write the instructino to the given stream
+    /// @param out the stream to write to
+    /// @returns the stream
+    std::ostream& ToString(std::ostream& out) const;
+
   private:
     Kind kind_;
 
@@ -108,8 +113,6 @@
     utils::Vector<const Value*, 2> args_;
 };
 
-std::ostream& operator<<(std::ostream& out, const Instruction&);
-
 }  // namespace tint::ir
 
 #endif  // SRC_TINT_IR_INSTRUCTION_H_
diff --git a/src/tint/ir/instruction_test.cc b/src/tint/ir/instruction_test.cc
index 0957903..20bc1f0 100644
--- a/src/tint/ir/instruction_test.cc
+++ b/src/tint/ir/instruction_test.cc
@@ -46,7 +46,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 & 2");
 }
 
@@ -74,7 +74,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 | 2");
 }
 
@@ -102,7 +102,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 ^ 2");
 }
 
@@ -131,7 +131,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 && 2");
 }
 
@@ -159,7 +159,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 || 2");
 }
 
@@ -187,7 +187,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 == 2");
 }
 
@@ -215,7 +215,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 != 2");
 }
 
@@ -243,7 +243,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 < 2");
 }
 
@@ -272,7 +272,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 > 2");
 }
 
@@ -301,7 +301,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 <= 2");
 }
 
@@ -330,7 +330,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 >= 2");
 }
 
@@ -358,7 +358,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 << 2");
 }
 
@@ -387,7 +387,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 >> 2");
 }
 
@@ -415,7 +415,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 + 2");
 }
 
@@ -443,7 +443,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 - 2");
 }
 
@@ -471,7 +471,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 * 2");
 }
 
@@ -499,7 +499,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 / 2");
 }
 
@@ -527,7 +527,7 @@
     EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
-    str << *instr;
+    instr->ToString(str);
     EXPECT_EQ(str.str(), "%42 = 4 % 2");
 }
 
diff --git a/src/tint/ir/temp.cc b/src/tint/ir/temp.cc
index de227ee..30efb86 100644
--- a/src/tint/ir/temp.cc
+++ b/src/tint/ir/temp.cc
@@ -24,8 +24,8 @@
 
 Temp::~Temp() = default;
 
-std::ostream& operator<<(std::ostream& out, const Temp& r) {
-    out << "%" << std::to_string(r.AsId());
+std::ostream& Temp::ToString(std::ostream& out) const {
+    out << "%" << std::to_string(AsId());
     return out;
 }
 
diff --git a/src/tint/ir/temp.h b/src/tint/ir/temp.h
index b16baf2..c621b5c 100644
--- a/src/tint/ir/temp.h
+++ b/src/tint/ir/temp.h
@@ -43,12 +43,15 @@
     /// @returns the value data as an `Id`.
     Id AsId() const { return id_; }
 
+    /// Write the temp to the given stream
+    /// @param out the stream to write to
+    /// @returns the stream
+    std::ostream& ToString(std::ostream& out) const override;
+
   private:
     Id id_ = 0;
 };
 
-std::ostream& operator<<(std::ostream& out, const Temp& r);
-
 }  // namespace tint::ir
 
 #endif  // SRC_TINT_IR_TEMP_H_
diff --git a/src/tint/ir/temp_test.cc b/src/tint/ir/temp_test.cc
index b5d817b..1dcae13 100644
--- a/src/tint/ir/temp_test.cc
+++ b/src/tint/ir/temp_test.cc
@@ -33,7 +33,7 @@
     auto* val = b.builder.Temp();
     EXPECT_EQ(4u, val->AsId());
 
-    str << *val;
+    val->ToString(str);
     EXPECT_EQ("%4", str.str());
 }
 
diff --git a/src/tint/ir/value.cc b/src/tint/ir/value.cc
index a1431e3..93040cf 100644
--- a/src/tint/ir/value.cc
+++ b/src/tint/ir/value.cc
@@ -25,17 +25,4 @@
 
 Value::~Value() = default;
 
-std::ostream& operator<<(std::ostream& out, const Value& v) {
-    const auto* ptr = &v;
-
-    if (auto* c = ptr->As<Constant>()) {
-        out << *c;
-    } else if (auto* t = ptr->As<Temp>()) {
-        out << *t;
-    } else {
-        out << "Unknown value";
-    }
-    return out;
-}
-
 }  // namespace tint::ir
diff --git a/src/tint/ir/value.h b/src/tint/ir/value.h
index 69ef8dd..9c41f52 100644
--- a/src/tint/ir/value.h
+++ b/src/tint/ir/value.h
@@ -33,13 +33,16 @@
     Value& operator=(const Value&) = delete;
     Value& operator=(Value&&) = delete;
 
+    /// Write the value to the given stream
+    /// @param out the stream to write to
+    /// @returns the stream
+    virtual std::ostream& ToString(std::ostream& out) const = 0;
+
   protected:
     /// Constructor
     Value();
 };
 
-std::ostream& operator<<(std::ostream& out, const Value& v);
-
 }  // namespace tint::ir
 
 #endif  // SRC_TINT_IR_VALUE_H_