[tint] Make LHS of operator << generic.

Breaks a circular dependency in utils, and allows for << to work with
other stream types.

Change-Id: Ia001def89fb1db9dd4aa582ae516911ba8aa71f1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/143383
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/builtin/extension.h b/src/tint/lang/core/builtin/extension.h
index cb9f52d..a2640ed 100644
--- a/src/tint/lang/core/builtin/extension.h
+++ b/src/tint/lang/core/builtin/extension.h
@@ -24,7 +24,7 @@
 #define SRC_TINT_LANG_CORE_BUILTIN_EXTENSION_H_
 
 #include "src/tint/utils/containers/unique_vector.h"
-#include "src/tint/utils/text/string_stream.h"
+#include "src/tint/utils/traits/traits.h"
 
 namespace tint::builtin {
 
@@ -41,10 +41,17 @@
     kF16,
 };
 
+/// @param value the enum value
+/// @returns the string for the given enum value
+std::string_view ToString(Extension value);
+
 /// @param out the stream to write to
 /// @param value the Extension
-/// @returns `out` so calls can be chained
-StringStream& operator<<(StringStream& out, Extension value);
+/// @returns @p out so calls can be chained
+template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
+auto& operator<<(STREAM& out, Extension value) {
+    return out << ToString(value);
+}
 
 /// ParseExtension parses a Extension from a string.
 /// @param str the string to parse