Преглед на файлове

Switch `CARBON_CHECK` to a format string API (#4285)

This switches `DCHECK` and `FATAL` as well.

The goal is to reduce the code size impact of these assertions so that
we can keep more of them enabled. Currently, the largest cost I see from
`CHECK` is not the actual check or the cold code itself, but actually
the failure to inline trivial functions due to the presence of the cold
code. This means that our goal isn't to reduce apparent code size in the
final binary but the LLVM IR cost assessed for these routines in the
inliner, which closely correlates with code size but is a bit different.

As discussed in #4283, experimentation shows that a single function call
with a minimal number of arguments is the lowest cost model for these.
This is easily achieved with a format-string API that internally uses
`llvm::formatv`. This PR is essentially the `CHECK` version of #4283.

However, the check macros are substantially harder to make work with
both format strings and streaming because they also take a condition.
Also, unexpectedly, I was very successful at devising a regular
expression based automated rewrite from the streaming to the format
string form with only low 10s of manual fixes. This includes compacting
strings broken up across lines, etc. Given how well that went, I've
prepared this PR which just directly switches to the format string API
and migrate everything to use it.

One nice side-effect is that the format string approach ends up greatly
simplifying the implementation here as well.

This is ... *shockingly* effective. Parsing speeds up by more than 3%
with just this change. And checking speeds up by **8%** with this change
alone:
```
BM_CompileAPIFileDenseDecls<Phase::Parse>/256      86.3µs ± 1%  82.9µs ± 1%  -3.94%  (p=0.000 n=17+19)
BM_CompileAPIFileDenseDecls<Phase::Parse>/1024      431µs ± 1%   415µs ± 1%  -3.76%  (p=0.000 n=18+19)
BM_CompileAPIFileDenseDecls<Phase::Parse>/4096     1.77ms ± 1%  1.71ms ± 1%  -3.18%  (p=0.000 n=18+19)
BM_CompileAPIFileDenseDecls<Phase::Parse>/16384    7.44ms ± 1%  7.17ms ± 2%  -3.56%  (p=0.000 n=18+20)
BM_CompileAPIFileDenseDecls<Phase::Parse>/65536    30.7ms ± 1%  29.7ms ± 1%  -3.15%  (p=0.000 n=18+20)
BM_CompileAPIFileDenseDecls<Phase::Parse>/262144    131ms ± 1%   127ms ± 1%  -2.81%  (p=0.000 n=18+18)
BM_CompileAPIFileDenseDecls<Phase::Check>/256       878µs ± 2%   800µs ± 1%  -8.91%  (p=0.000 n=19+20)
BM_CompileAPIFileDenseDecls<Phase::Check>/1024     1.88ms ± 2%  1.72ms ± 1%  -8.56%  (p=0.000 n=19+20)
BM_CompileAPIFileDenseDecls<Phase::Check>/4096     5.78ms ± 2%  5.28ms ± 1%  -8.70%  (p=0.000 n=20+18)
BM_CompileAPIFileDenseDecls<Phase::Check>/16384    21.9ms ± 1%  20.1ms ± 1%  -8.02%  (p=0.000 n=18+20)
BM_CompileAPIFileDenseDecls<Phase::Check>/65536    90.4ms ± 2%  83.1ms ± 1%  -8.04%  (p=0.000 n=19+20)
BM_CompileAPIFileDenseDecls<Phase::Check>/262144    381ms ± 2%   352ms ± 1%  -7.79%  (p=0.000 n=19+19)
```

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
Chandler Carruth преди 1 година
родител
ревизия
4845f40dff
променени са 100 файла, в които са добавени 982 реда и са изтрити 913 реда
  1. 2 0
      common/BUILD
  2. 4 4
      common/array_stack.h
  3. 26 16
      common/check.h
  4. 14 16
      common/check_internal.cpp
  5. 94 61
      common/check_internal.h
  6. 15 6
      common/check_test.cpp
  7. 77 71
      common/command_line.cpp
  8. 4 4
      common/command_line.h
  9. 1 1
      common/error.h
  10. 1 1
      common/hashing_test.cpp
  11. 2 2
      common/map.h
  12. 3 3
      common/map_benchmark.cpp
  13. 24 22
      common/raw_hashtable.h
  14. 4 3
      common/raw_hashtable_benchmark_helpers.cpp
  15. 53 48
      common/raw_hashtable_metadata_group.h
  16. 1 1
      common/set.h
  17. 3 3
      common/set_benchmark.cpp
  18. 3 3
      explorer/ast/bindings.cpp
  19. 5 5
      explorer/ast/clone_context.cpp
  20. 1 1
      explorer/ast/clone_context.h
  21. 1 1
      explorer/ast/declaration.cpp
  22. 3 3
      explorer/ast/declaration.h
  23. 2 2
      explorer/ast/element_path.h
  24. 1 2
      explorer/ast/expression.cpp
  25. 4 3
      explorer/ast/expression.h
  26. 3 3
      explorer/ast/impl_binding.h
  27. 2 2
      explorer/ast/pattern.h
  28. 3 3
      explorer/ast/statement.h
  29. 2 2
      explorer/ast/static_scope.cpp
  30. 22 24
      explorer/ast/value.cpp
  31. 10 10
      explorer/ast/value.h
  32. 2 2
      explorer/file_test.cpp
  33. 9 9
      explorer/fuzzing/ast_to_proto.cpp
  34. 2 2
      explorer/fuzzing/fuzzer_util.cpp
  35. 8 8
      explorer/interpreter/action.cpp
  36. 1 1
      explorer/interpreter/action.h
  37. 7 7
      explorer/interpreter/action_stack.cpp
  38. 2 2
      explorer/interpreter/heap.cpp
  39. 7 7
      explorer/interpreter/impl_scope.cpp
  40. 70 68
      explorer/interpreter/interpreter.cpp
  41. 1 1
      explorer/interpreter/matching_impl_set.cpp
  42. 12 13
      explorer/interpreter/pattern_match.cpp
  43. 7 7
      explorer/interpreter/resolve_names.cpp
  44. 2 2
      explorer/interpreter/resolve_unformed.cpp
  45. 5 5
      explorer/interpreter/stack.h
  46. 67 65
      explorer/interpreter/type_checker.cpp
  47. 2 2
      explorer/interpreter/type_utils.cpp
  48. 2 2
      explorer/syntax/parse.cpp
  49. 1 2
      explorer/syntax/prelude.cpp
  50. 2 2
      migrate_cpp/rewriter_test.cpp
  51. 4 3
      testing/base/global_exe_path.cpp
  52. 21 21
      testing/base/source_gen.cpp
  53. 13 13
      testing/file_test/autoupdate.cpp
  54. 6 5
      testing/file_test/autoupdate.h
  55. 3 3
      testing/file_test/file_test_base.cpp
  56. 3 3
      toolchain/base/value_store.h
  57. 1 1
      toolchain/base/yaml.h
  58. 19 20
      toolchain/check/check.cpp
  59. 1 1
      toolchain/check/check_fuzzer.cpp
  60. 31 30
      toolchain/check/context.cpp
  61. 1 2
      toolchain/check/context.h
  62. 13 16
      toolchain/check/convert.cpp
  63. 2 3
      toolchain/check/decl_introducer_state.h
  64. 24 24
      toolchain/check/decl_name_stack.cpp
  65. 4 4
      toolchain/check/deduce.cpp
  66. 22 21
      toolchain/check/eval.cpp
  67. 26 24
      toolchain/check/generic.cpp
  68. 4 4
      toolchain/check/handle_binding_pattern.cpp
  69. 4 4
      toolchain/check/handle_class.cpp
  70. 1 1
      toolchain/check/handle_if_statement.cpp
  71. 2 2
      toolchain/check/handle_interface.cpp
  72. 2 2
      toolchain/check/handle_let_and_var.cpp
  73. 1 1
      toolchain/check/handle_name.cpp
  74. 2 2
      toolchain/check/handle_noop.cpp
  75. 2 2
      toolchain/check/handle_struct.cpp
  76. 10 10
      toolchain/check/impl.cpp
  77. 6 6
      toolchain/check/import.cpp
  78. 18 18
      toolchain/check/import_ref.cpp
  79. 5 5
      toolchain/check/inst_block_stack.cpp
  80. 3 3
      toolchain/check/inst_block_stack.h
  81. 12 11
      toolchain/check/lexical_lookup.h
  82. 5 6
      toolchain/check/member_access.cpp
  83. 6 6
      toolchain/check/merge.cpp
  84. 1 1
      toolchain/check/node_stack.cpp
  85. 19 20
      toolchain/check/node_stack.h
  86. 4 4
      toolchain/check/return.cpp
  87. 32 30
      toolchain/check/scope_stack.cpp
  88. 6 6
      toolchain/check/sem_ir_diagnostic_converter.cpp
  89. 6 6
      toolchain/check/subst.cpp
  90. 9 8
      toolchain/diagnostics/diagnostic_emitter.h
  91. 2 2
      toolchain/diagnostics/sorting_diagnostic_consumer.h
  92. 1 1
      toolchain/docs/idioms.md
  93. 1 1
      toolchain/driver/clang_runner.cpp
  94. 1 1
      toolchain/driver/clang_runner_test.cpp
  95. 2 2
      toolchain/driver/driver.cpp
  96. 1 1
      toolchain/driver/driver_fuzzer.cpp
  97. 10 9
      toolchain/driver/driver_test.cpp
  98. 3 3
      toolchain/install/install_paths.cpp
  99. 5 5
      toolchain/install/install_paths_test.cpp
  100. 3 3
      toolchain/install/install_paths_test_helpers.cpp

+ 2 - 0
common/BUILD

@@ -68,6 +68,7 @@ cc_library(
     hdrs = ["check.h"],
     deps = [
         ":ostream",
+        ":template_string",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -329,6 +330,7 @@ cc_library(
     hdrs = ["raw_hashtable_metadata_group.h"],
     deps = [
         ":check",
+        ":ostream",
         "@llvm-project//llvm:Support",
     ],
 )

+ 4 - 4
common/array_stack.h

@@ -62,15 +62,15 @@ class ArrayStack {
 
   // Appends a value to the top array on the stack.
   auto AppendToTop(ValueT value) -> void {
-    CARBON_CHECK(!array_offsets_.empty())
-        << "Must call PushArray before PushValue.";
+    CARBON_CHECK(!array_offsets_.empty(),
+                 "Must call PushArray before PushValue.");
     values_.push_back(value);
   }
 
   // Adds multiple values to the top array on the stack.
   auto AppendToTop(llvm::ArrayRef<ValueT> values) -> void {
-    CARBON_CHECK(!array_offsets_.empty())
-        << "Must call PushArray before PushValues.";
+    CARBON_CHECK(!array_offsets_.empty(),
+                 "Must call PushArray before PushValues.");
     values_.append(values.begin(), values.end());
   }
 

+ 26 - 16
common/check.h

@@ -14,30 +14,40 @@ namespace Carbon {
 // a bug in the application.
 //
 // For example:
-//   CARBON_CHECK(is_valid) << "Data is not valid!";
-#define CARBON_CHECK(...)                                                   \
-  (__VA_ARGS__) ? (void)0                                                   \
-                : CARBON_CHECK_INTERNAL_STREAM()                            \
-                      << "CHECK failure at " << __FILE__ << ":" << __LINE__ \
-                      << ": " #__VA_ARGS__                                  \
-                      << Carbon::Internal::ExitingStream::AddSeparator()
+//   CARBON_CHECK(is_valid, "Data is not valid!");
+//
+// The condition must be parenthesized if it contains top-level commas, for
+// example in a template argument list:
+//   CARBON_CHECK((inst.IsOneOf<Call, TupleLiteral>()),
+//                "Unexpected inst {0}", inst);
+#define CARBON_CHECK(condition, ...) \
+  (condition) ? (void)0              \
+              : CARBON_INTERNAL_CHECK(condition __VA_OPT__(, ) __VA_ARGS__)
 
 // DCHECK calls CHECK in debug mode, and does nothing otherwise.
 #ifndef NDEBUG
-#define CARBON_DCHECK(...) CARBON_CHECK(__VA_ARGS__)
+#define CARBON_DCHECK(condition, ...) \
+  CARBON_CHECK(condition __VA_OPT__(, ) __VA_ARGS__)
 #else
-#define CARBON_DCHECK(...) CARBON_CHECK(true || (__VA_ARGS__))
+// When in a debug build we want to preserve as much as we can of how the
+// parameters are used, other than making them be trivially in dead code and
+// eliminated by the optimizer. As a consequence we preserve the condition but
+// prefix it with a short-circuit operator, and we still emit the (dead) call to
+// the check implementation. But we use a special implementation that reduces
+// the compile time cost.
+#define CARBON_DCHECK(condition, ...) \
+  (true || (condition))               \
+      ? (void)0                       \
+      : CARBON_INTERNAL_DEAD_DCHECK(condition __VA_OPT__(, ) __VA_ARGS__)
 #endif
 
-// This is similar to CHECK, but is unconditional. Writing CARBON_FATAL() is
-// clearer than CARBON_CHECK(false) because it avoids confusion about control
-// flow.
+// This is similar to CHECK, but is unconditional. Writing
+// `CARBON_FATAL("message")` is clearer than `CARBON_CHECK(false, "message")
+// because it avoids confusion about control flow.
 //
 // For example:
-//   CARBON_FATAL() << "Unreachable!";
-#define CARBON_FATAL()           \
-  CARBON_CHECK_INTERNAL_STREAM() \
-      << "FATAL failure at " << __FILE__ << ":" << __LINE__ << ": "
+//   CARBON_FATAL("Unreachable!");
+#define CARBON_FATAL(...) CARBON_INTERNAL_FATAL(__VA_ARGS__)
 
 }  // namespace Carbon
 

+ 14 - 16
common/check_internal.cpp

@@ -4,7 +4,7 @@
 
 #include "common/check_internal.h"
 
-#include "llvm/Support/ErrorHandling.h"
+#include "common/ostream.h"
 #include "llvm/Support/Signals.h"
 
 namespace Carbon::Internal {
@@ -14,22 +14,20 @@ static auto PrintAfterStackTrace(void* str) -> void {
   llvm::errs() << reinterpret_cast<char*>(str);
 }
 
-ExitingStream::~ExitingStream() {
-  llvm_unreachable(
-      "Exiting streams should only be constructed by check.h macros that "
-      "ensure the special operator| exits the program prior to their "
-      "destruction!");
-}
-
-auto ExitingStream::Done() -> void {
-  buffer_ << "\n";
-  buffer_.flush();
-
-  // Register another signal handler to print the buffered message. This is
-  // because we want it at the bottom of output, after LLVM's builtin stack
-  // output, rather than the top.
+auto CheckFailImpl(const char* kind, const char* file, int line,
+                   const char* condition_str, llvm::StringRef extra_message)
+    -> void {
+  // Render the final check string here.
+  std::string message = llvm::formatv(
+      "{0} failure at {1}:{2}{3}{4}{5}{6}\n", kind, file, line,
+      llvm::StringRef(condition_str).empty() ? "" : ": ", condition_str,
+      extra_message.empty() ? "" : ": ", extra_message);
+
+  // Register another signal handler to print the message. This is because we
+  // want it at the bottom of output, after LLVM's builtin stack output, rather
+  // than the top.
   llvm::sys::AddSignalHandler(PrintAfterStackTrace,
-                              const_cast<char*>(buffer_str_.c_str()));
+                              const_cast<char*>(message.c_str()));
   // It's useful to exit the program with `std::abort()` for integration with
   // debuggers and other tools. We also assume LLVM's exit handling is
   // installed, which will stack trace on `std::abort()`.

+ 94 - 61
common/check_internal.h

@@ -5,73 +5,106 @@
 #ifndef CARBON_COMMON_CHECK_INTERNAL_H_
 #define CARBON_COMMON_CHECK_INTERNAL_H_
 
-#include "common/ostream.h"
+#include "common/template_string.h"
+#include "llvm/Support/FormatVariadic.h"
 
 namespace Carbon::Internal {
 
-// Wraps a stream and exiting for fatal errors. Should only be used by check.h
-// macros.
-class ExitingStream {
- public:
-  // A tag type that renders as ": " in an ExitingStream, but only if it is
-  // followed by additional output. Otherwise, it renders as "". Primarily used
-  // when building macros around these streams.
-  struct AddSeparator {};
-
-  // Internal type used in macros to dispatch to the `operator|` overload.
-  struct Helper {};
-
-  ExitingStream()
-      // Prefix the buffer with the current bug report message.
-      : buffer_(buffer_str_) {}
-
-  // Never called.
-  [[noreturn]] ~ExitingStream();
-
-  // If the bool cast occurs, it's because the condition is false. This supports
-  // && short-circuiting the creation of ExitingStream.
-  explicit operator bool() const { return true; }
-
-  // Forward output to llvm::errs.
-  template <typename T>
-  auto operator<<(const T& message) -> ExitingStream& {
-    if (separator_) {
-      buffer_ << ": ";
-      separator_ = false;
-    }
-    buffer_ << message;
-    return *this;
-  }
-
-  auto operator<<(AddSeparator /*add_separator*/) -> ExitingStream& {
-    separator_ = true;
-    return *this;
-  }
-
-  // Low-precedence binary operator overload used in check.h macros to flush the
-  // output and exit the program. We do this in a binary operator rather than
-  // the destructor to ensure good debug info and backtraces for errors.
-  [[noreturn]] friend auto operator|(Helper /*helper*/, ExitingStream& stream)
-      -> void {
-    stream.Done();
+// Implements the check failure message printing.
+//
+// This is out-of-line and will arrange to stop the program, print any debugging
+// information and this string.
+//
+// This API uses `const char*` C string arguments rather than `llvm::StringRef`
+// because we know that these are available as C strings and passing them that
+// way lets the code size of calling it be smaller: it only needs to materialize
+// a single pointer argument for each. The runtime cost of re-computing the size
+// should be minimal. The extra message however might not be compile-time
+// guaranteed to be a C string so we use a normal `StringRef` there.
+[[noreturn]] auto CheckFailImpl(const char* kind, const char* file, int line,
+                                const char* condition_str,
+                                llvm::StringRef extra_message) -> void;
+
+// Prints a check failure, including rendering any user-provided message using
+// a format string.
+//
+// Most of the parameters are passed as compile-time template strings to avoid
+// runtime cost of parameter setup in optimized builds. Each of these are passed
+// along to the underlying implementation to include in the final printed
+// message.
+//
+// Any user-provided format string and values are directly passed to
+// `llvm::formatv` which handles all of the formatting of output.
+template <TemplateString Kind, TemplateString File, int Line,
+          TemplateString ConditionStr, TemplateString FormatStr, typename... Ts>
+[[noreturn, gnu::cold, clang::noinline, clang::preserve_most]] auto CheckFail(
+    Ts&&... values) -> void {
+  if constexpr (llvm::StringRef(FormatStr).empty()) {
+    // Skip the format string rendering if empty. Note that we don't skip it
+    // even if there are no values as we want to have consistent handling of
+    // `{}`s in the format string. This case is about when there is no message
+    // at all, just the condition.
+    CheckFailImpl(Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(), "");
+  } else {
+    CheckFailImpl(
+        Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(),
+        llvm::formatv(FormatStr.c_str(), std::forward<Ts>(values)...).str());
   }
-
- private:
-  [[noreturn]] auto Done() -> void;
-
-  // Whether a separator should be printed if << is used again.
-  bool separator_ = false;
-
-  std::string buffer_str_;
-  llvm::raw_string_ostream buffer_;
-};
+}
 
 }  // namespace Carbon::Internal
 
-// Raw exiting stream. This should be used when building forms of exiting
-// macros. It evaluates to a temporary `ExitingStream` object that can be
-// manipulated, streamed into, and then will exit the program.
-#define CARBON_CHECK_INTERNAL_STREAM() \
-  Carbon::Internal::ExitingStream::Helper() | Carbon::Internal::ExitingStream()
+// Implements check messages without any formatted values.
+//
+// Passes each of the provided components of the message to the template
+// parameters of the check failure printing function above, including an empty
+// string for the format string. Because there are multiple template arguments,
+// the entire call is wrapped in parentheses.
+#define CARBON_INTERNAL_CHECK_IMPL(kind, file, line, condition_str) \
+  (Carbon::Internal::CheckFail<kind, file, line, condition_str, "">())
+
+// Implements check messages with a format string and potentially formatted
+// values.
+//
+// Each of the main components is passed as a template arguments, and then any
+// formatted values are passed as arguments. Because there are multiple template
+// arguments, the entire call is wrapped in parentheses.
+#define CARBON_INTERNAL_CHECK_IMPL_FORMAT(kind, file, line, condition_str,   \
+                                          format_str, ...)                   \
+  (Carbon::Internal::CheckFail<kind, file, line, condition_str, format_str>( \
+      __VA_ARGS__))
+
+// Implements the failure of a check.
+//
+// Collects all the metadata about the failure to be printed, such as source
+// location and stringified condition, and passes those, any format string and
+// formatted arguments to the correct implementation macro above.
+#define CARBON_INTERNAL_CHECK(condition, ...)      \
+  CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \
+      "CHECK", __FILE__, __LINE__, #condition __VA_OPT__(, ) __VA_ARGS__)
+
+// Implements the fatal macro.
+//
+// Similar to the check failure macro, but tags the message as a fatal one and
+// leaves the stringified condition empty.
+#define CARBON_INTERNAL_FATAL(...)                 \
+  CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \
+      "FATAL", __FILE__, __LINE__, "" __VA_OPT__(, ) __VA_ARGS__)
+
+#ifdef NDEBUG
+// For `DCHECK` in optimized builds we have a dead check that we want to
+// potentially "use" arguments, but otherwise have the minimal overhead. We
+// avoid forming interesting format strings here so that we don't have to
+// repeatedly instantiate the `Check` function above. This format string would
+// be an error if actually used.
+#define CARBON_INTERNAL_DEAD_DCHECK(condition, ...) \
+  CARBON_INTERNAL_DEAD_DCHECK_IMPL##__VA_OPT__(_FORMAT)(__VA_ARGS__)
+
+#define CARBON_INTERNAL_DEAD_DCHECK_IMPL() \
+  Carbon::Internal::CheckFail<"", "", 0, "", "">()
+
+#define CARBON_INTERNAL_DEAD_DCHECK_IMPL_FORMAT(format_str, ...) \
+  Carbon::Internal::CheckFail<"", "", 0, "", "">(__VA_ARGS__)
+#endif
 
 #endif  // CARBON_COMMON_CHECK_INTERNAL_H_

+ 15 - 6
common/check_test.cpp

@@ -26,32 +26,41 @@ TEST(CheckTest, CheckTrueCallbackNotUsed) {
     called = true;
     return "called";
   };
-  CARBON_CHECK(true) << callback();
+  CARBON_CHECK(true, "{0}", callback());
   EXPECT_FALSE(called);
 }
 
 TEST(CheckTest, CheckFalseMessage) {
-  ASSERT_DEATH({ CARBON_CHECK(false) << "msg"; },
+  ASSERT_DEATH({ CARBON_CHECK(false, "msg"); },
                "\nCHECK failure at common/check_test.cpp:.+: false: msg\n");
 }
 
+TEST(CheckTest, CheckFalseFormattedMessage) {
+  const char msg[] = "msg";
+  std::string str = "str";
+  int i = 1;
+  ASSERT_DEATH(
+      { CARBON_CHECK(false, "{0} {1} {2} {3}", msg, str, i, 0); },
+      "\nCHECK failure at common/check_test.cpp:.+: false: msg str 1 0\n");
+}
+
 TEST(CheckTest, CheckOutputForms) {
   const char msg[] = "msg";
   std::string str = "str";
   int i = 1;
-  CARBON_CHECK(true) << msg << str << i << 0;
+  CARBON_CHECK(true, "{0} {1} {2} {3}", msg, str, i, 0);
 }
 
 TEST(CheckTest, Fatal) {
-  ASSERT_DEATH({ CARBON_FATAL() << "msg"; },
+  ASSERT_DEATH({ CARBON_FATAL("msg"); },
                "\nFATAL failure at common/check_test.cpp:.+: msg\n");
 }
 
 TEST(CheckTest, FatalHasStackDump) {
-  ASSERT_DEATH({ CARBON_FATAL() << "msg"; }, "\nStack dump:\n");
+  ASSERT_DEATH({ CARBON_FATAL("msg"); }, "\nStack dump:\n");
 }
 
-auto FatalNoReturnRequired() -> int { CARBON_FATAL() << "msg"; }
+auto FatalNoReturnRequired() -> int { CARBON_FATAL("msg"); }
 
 TEST(ErrorTest, FatalNoReturnRequired) {
   ASSERT_DEATH({ FatalNoReturnRequired(); },

+ 77 - 71
common/command_line.cpp

@@ -22,7 +22,7 @@ auto operator<<(llvm::raw_ostream& output, ParseResult result)
     case ParseResult::Success:
       return output << "Success";
   }
-  CARBON_FATAL() << "Corrupt parse result!";
+  CARBON_FATAL("Corrupt parse result!");
 }
 
 auto operator<<(llvm::raw_ostream& output, ArgKind kind) -> llvm::raw_ostream& {
@@ -40,7 +40,7 @@ auto operator<<(llvm::raw_ostream& output, ArgKind kind) -> llvm::raw_ostream& {
     case ArgKind::Invalid:
       return output << "Invalid";
   }
-  CARBON_FATAL() << "Corrupt argument kind!";
+  CARBON_FATAL("Corrupt argument kind!");
 }
 
 auto operator<<(llvm::raw_ostream& output, CommandKind kind)
@@ -55,7 +55,7 @@ auto operator<<(llvm::raw_ostream& output, CommandKind kind)
     case CommandKind::MetaAction:
       return output << "MetaAction";
   }
-  CARBON_FATAL() << "Corrupt command kind!";
+  CARBON_FATAL("Corrupt command kind!");
 }
 Arg::Arg(const ArgInfo& info) : info(info) {}
 
@@ -315,8 +315,9 @@ void MetaPrinter::PrintHelpForSubcommandName(
 }
 
 void MetaPrinter::PrintVersion(const Command& command) const {
-  CARBON_CHECK(!command.info.version.empty())
-      << "Printing should not be enabled without a version string configured.";
+  CARBON_CHECK(
+      !command.info.version.empty(),
+      "Printing should not be enabled without a version string configured.");
   PrintRawVersion(command, /*indent=*/"");
   if (!command.info.build_info.empty()) {
     out_ << "\n";
@@ -463,13 +464,14 @@ void MetaPrinter::PrintOptionUsage(const Arg& option) const {
 }
 
 void MetaPrinter::PrintOptionShortName(const Arg& arg) const {
-  CARBON_CHECK(!arg.info.short_name.empty()) << "No short name to use.";
+  CARBON_CHECK(!arg.info.short_name.empty(), "No short name to use.");
   out_ << "-" << arg.info.short_name;
 }
 
 void MetaPrinter::PrintArgShortValues(const Arg& arg) const {
-  CARBON_CHECK(arg.kind == Arg::Kind::OneOf)
-      << "Only one-of arguments have interesting value snippets to print.";
+  CARBON_CHECK(
+      arg.kind == Arg::Kind::OneOf,
+      "Only one-of arguments have interesting value snippets to print.");
   llvm::ListSeparator sep;
   for (llvm::StringRef value_string : arg.value_strings) {
     out_ << sep << value_string;
@@ -517,7 +519,7 @@ void MetaPrinter::PrintArgHelp(const Arg& arg, llvm::StringRef indent) const {
       // No value help.
       break;
     case Arg::Kind::Invalid:
-      CARBON_FATAL() << "Argument configured without any action or kind!";
+      CARBON_FATAL("Argument configured without any action or kind!");
   }
 }
 
@@ -793,11 +795,11 @@ void Parser::PopulateMaps(const Command& command) {
     if (option->info.short_name.empty()) {
       continue;
     }
-    CARBON_CHECK(option->info.short_name.size() == 1)
-        << "Short option names must have exactly one character.";
+    CARBON_CHECK(option->info.short_name.size() == 1,
+                 "Short option names must have exactly one character.");
     unsigned char short_char = option->info.short_name[0];
-    CARBON_CHECK(short_char < short_option_table_.size())
-        << "Short option name outside of the expected range.";
+    CARBON_CHECK(short_char < short_option_table_.size(),
+                 "Short option name outside of the expected range.");
     short_option_table_[short_char] = &map_entry.second;
   }
   subcommand_map_.clear();
@@ -807,7 +809,7 @@ void Parser::PopulateMaps(const Command& command) {
 }
 
 void Parser::SetOptionDefault(const Arg& option) {
-  CARBON_CHECK(option.has_default) << "No default value available!";
+  CARBON_CHECK(option.has_default, "No default value available!");
   switch (option.kind) {
     case Arg::Kind::Flag:
       *option.flag_storage = option.default_flag;
@@ -822,9 +824,9 @@ void Parser::SetOptionDefault(const Arg& option) {
       option.default_action(option);
       break;
     case Arg::Kind::MetaActionOnly:
-      CARBON_FATAL() << "Can't set a default value for a meta action!";
+      CARBON_FATAL("Can't set a default value for a meta action!");
     case Arg::Kind::Invalid:
-      CARBON_FATAL() << "Option configured without any action or kind!";
+      CARBON_FATAL("Option configured without any action or kind!");
   }
 }
 
@@ -846,7 +848,7 @@ auto Parser::ParseNegatedFlag(const Arg& flag,
 
 auto Parser::ParseFlag(const Arg& flag, std::optional<llvm::StringRef> value)
     -> bool {
-  CARBON_CHECK(flag.kind == Arg::Kind::Flag) << "Incorrect kind: " << flag.kind;
+  CARBON_CHECK(flag.kind == Arg::Kind::Flag, "Incorrect kind: {0}", flag.kind);
   if (!value || *value == "true") {
     *flag.flag_storage = true;
   } else if (*value == "false") {
@@ -862,8 +864,7 @@ auto Parser::ParseFlag(const Arg& flag, std::optional<llvm::StringRef> value)
 
 auto Parser::ParseIntegerArgValue(const Arg& arg, llvm::StringRef value)
     -> bool {
-  CARBON_CHECK(arg.kind == Arg::Kind::Integer)
-      << "Incorrect kind: " << arg.kind;
+  CARBON_CHECK(arg.kind == Arg::Kind::Integer, "Incorrect kind: {0}", arg.kind);
   int integer_value;
   // Note that this method returns *true* on error!
   if (value.getAsInteger(/*Radix=*/0, integer_value)) {
@@ -881,7 +882,7 @@ auto Parser::ParseIntegerArgValue(const Arg& arg, llvm::StringRef value)
 
 auto Parser::ParseStringArgValue(const Arg& arg, llvm::StringRef value)
     -> bool {
-  CARBON_CHECK(arg.kind == Arg::Kind::String) << "Incorrect kind: " << arg.kind;
+  CARBON_CHECK(arg.kind == Arg::Kind::String, "Incorrect kind: {0}", arg.kind);
   if (!arg.is_append) {
     *arg.string_storage = value;
   } else {
@@ -891,7 +892,7 @@ auto Parser::ParseStringArgValue(const Arg& arg, llvm::StringRef value)
 }
 
 auto Parser::ParseOneOfArgValue(const Arg& arg, llvm::StringRef value) -> bool {
-  CARBON_CHECK(arg.kind == Arg::Kind::OneOf) << "Incorrect kind: " << arg.kind;
+  CARBON_CHECK(arg.kind == Arg::Kind::OneOf, "Incorrect kind: {0}", arg.kind);
   if (!arg.value_action(arg, value)) {
     errors_ << "ERROR: Option '--" << arg.info.name << "=";
     llvm::printEscapedString(value, errors_);
@@ -967,7 +968,7 @@ auto Parser::ParseArg(const Arg& arg, bool short_spelling,
       return false;
     case Arg::Kind::Flag:
     case Arg::Kind::Invalid:
-      CARBON_FATAL() << "Invalid kind!";
+      CARBON_FATAL("Invalid kind!");
   }
 }
 
@@ -984,8 +985,8 @@ auto Parser::SplitValue(llvm::StringRef& unparsed_arg)
 }
 
 auto Parser::ParseLongOption(llvm::StringRef unparsed_arg) -> bool {
-  CARBON_CHECK(unparsed_arg.starts_with("--") && unparsed_arg.size() > 2)
-      << "Must only be called on a potential long option.";
+  CARBON_CHECK(unparsed_arg.starts_with("--") && unparsed_arg.size() > 2,
+               "Must only be called on a potential long option.");
 
   // Walk past the double dash.
   unparsed_arg = unparsed_arg.drop_front(2);
@@ -1009,8 +1010,8 @@ auto Parser::ParseLongOption(llvm::StringRef unparsed_arg) -> bool {
 }
 
 auto Parser::ParseShortOptionSeq(llvm::StringRef unparsed_arg) -> bool {
-  CARBON_CHECK(unparsed_arg.starts_with("-") && unparsed_arg.size() > 1)
-      << "Must only be called on a potential short option sequence.";
+  CARBON_CHECK(unparsed_arg.starts_with("-") && unparsed_arg.size() > 1,
+               "Must only be called on a potential short option sequence.");
 
   unparsed_arg = unparsed_arg.drop_front();
   std::optional<llvm::StringRef> value = SplitValue(unparsed_arg);
@@ -1143,10 +1144,10 @@ auto Parser::FinalizeParse() -> ParseResult {
   // If we were appending to a positional argument, mark that as complete.
   llvm::ArrayRef positional_args = command_->positional_args;
   if (appending_to_positional_arg_) {
-    CARBON_CHECK(static_cast<size_t>(positional_arg_index_) <
-                 positional_args.size())
-        << "Appending to a positional argument with an invalid index: "
-        << positional_arg_index_;
+    CARBON_CHECK(
+        static_cast<size_t>(positional_arg_index_) < positional_args.size(),
+        "Appending to a positional argument with an invalid index: {0}",
+        positional_arg_index_);
     ++positional_arg_index_;
   }
 
@@ -1162,15 +1163,15 @@ auto Parser::FinalizeParse() -> ParseResult {
       return ParseResult::Error;
     }
     for (const auto& arg_ptr : unparsed_positional_args) {
-      CARBON_CHECK(!arg_ptr->is_required)
-          << "Cannot have required positional parameters after an optional "
-             "one.";
+      CARBON_CHECK(
+          !arg_ptr->is_required,
+          "Cannot have required positional parameters after an optional one.");
     }
   }
 
   switch (command_->kind) {
     case Command::Kind::Invalid:
-      CARBON_FATAL() << "Should never have a parser with an invalid command!";
+      CARBON_FATAL("Should never have a parser with an invalid command!");
     case Command::Kind::RequiresSubcommand:
       errors_ << "ERROR: No subcommand specified. Available subcommands: ";
       error_meta_printer_.PrintSubcommands(*command_);
@@ -1189,11 +1190,13 @@ auto Parser::FinalizeParse() -> ParseResult {
 
 auto Parser::ParsePositionalSuffix(
     llvm::ArrayRef<llvm::StringRef> unparsed_args) -> bool {
-  CARBON_CHECK(!command_->positional_args.empty())
-      << "Cannot do positional suffix parsing without positional arguments!";
-  CARBON_CHECK(!unparsed_args.empty() && unparsed_args.front() == "--")
-      << "Must be called with a suffix of arguments starting with a `--` that "
-         "switches to positional suffix parsing.";
+  CARBON_CHECK(
+      !command_->positional_args.empty(),
+      "Cannot do positional suffix parsing without positional arguments!");
+  CARBON_CHECK(
+      !unparsed_args.empty() && unparsed_args.front() == "--",
+      "Must be called with a suffix of arguments starting with a `--` that "
+      "switches to positional suffix parsing.");
   // Once we're in the positional suffix, we can track empty positional
   // arguments.
   bool empty_positional = false;
@@ -1291,9 +1294,9 @@ auto Parser::Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args)
       continue;
     }
 
-    CARBON_CHECK(command_->positional_args.empty() ||
-                 command_->subcommands.empty())
-        << "Cannot have both positional arguments and subcommands!";
+    CARBON_CHECK(
+        command_->positional_args.empty() || command_->subcommands.empty(),
+        "Cannot have both positional arguments and subcommands!");
     if (command_->positional_args.empty() && command_->subcommands.empty()) {
       errors_ << "ERROR: Found unexpected positional argument or subcommand: '"
               << unparsed_arg << "'\n";
@@ -1439,12 +1442,13 @@ void CommandBuilder::AddOneOfPositionalArg(
 
 void CommandBuilder::AddSubcommand(
     const CommandInfo& info, llvm::function_ref<void(CommandBuilder&)> build) {
-  CARBON_CHECK(IsValidName(info.name))
-      << "Invalid subcommand name: " << info.name;
-  CARBON_CHECK(subcommand_names_.insert(info.name).second)
-      << "Added a duplicate subcommand: " << info.name;
-  CARBON_CHECK(command_.positional_args.empty())
-      << "Cannot add subcommands to a command with a positional argument.";
+  CARBON_CHECK(IsValidName(info.name), "Invalid subcommand name: {0}",
+               info.name);
+  CARBON_CHECK(subcommand_names_.insert(info.name).second,
+               "Added a duplicate subcommand: {0}", info.name);
+  CARBON_CHECK(
+      command_.positional_args.empty(),
+      "Cannot add subcommands to a command with a positional argument.");
 
   command_.subcommands.emplace_back(new Command(info, &command_));
   CommandBuilder builder(*command_.subcommands.back(), meta_printer_);
@@ -1457,25 +1461,28 @@ void CommandBuilder::HelpHidden(bool is_help_hidden) {
 }
 
 void CommandBuilder::RequiresSubcommand() {
-  CARBON_CHECK(!command_.subcommands.empty())
-      << "Cannot require subcommands unless there are subcommands.";
-  CARBON_CHECK(command_.positional_args.empty())
-      << "Cannot require subcommands and have a positional argument.";
-  CARBON_CHECK(command_.kind == Kind::Invalid)
-      << "Already established the kind of this command as: " << command_.kind;
+  CARBON_CHECK(!command_.subcommands.empty(),
+               "Cannot require subcommands unless there are subcommands.");
+  CARBON_CHECK(command_.positional_args.empty(),
+               "Cannot require subcommands and have a positional argument.");
+  CARBON_CHECK(command_.kind == Kind::Invalid,
+               "Already established the kind of this command as: {0}",
+               command_.kind);
   command_.kind = Kind::RequiresSubcommand;
 }
 
 void CommandBuilder::Do(ActionT action) {
-  CARBON_CHECK(command_.kind == Kind::Invalid)
-      << "Already established the kind of this command as: " << command_.kind;
+  CARBON_CHECK(command_.kind == Kind::Invalid,
+               "Already established the kind of this command as: {0}",
+               command_.kind);
   command_.kind = Kind::Action;
   command_.action = std::move(action);
 }
 
 void CommandBuilder::Meta(ActionT action) {
-  CARBON_CHECK(command_.kind == Kind::Invalid)
-      << "Already established the kind of this command as: " << command_.kind;
+  CARBON_CHECK(command_.kind == Kind::Invalid,
+               "Already established the kind of this command as: {0}",
+               command_.kind);
   command_.kind = Kind::MetaAction;
   command_.action = std::move(action);
 }
@@ -1484,10 +1491,9 @@ CommandBuilder::CommandBuilder(Command& command, MetaPrinter& meta_printer)
     : command_(command), meta_printer_(meta_printer) {}
 
 auto CommandBuilder::AddArgImpl(const ArgInfo& info, Arg::Kind kind) -> Arg& {
-  CARBON_CHECK(IsValidName(info.name))
-      << "Invalid argument name: " << info.name;
-  CARBON_CHECK(arg_names_.insert(info.name).second)
-      << "Added a duplicate argument name: " << info.name;
+  CARBON_CHECK(IsValidName(info.name), "Invalid argument name: {0}", info.name);
+  CARBON_CHECK(arg_names_.insert(info.name).second,
+               "Added a duplicate argument name: {0}", info.name);
 
   command_.options.emplace_back(new Arg(info));
   Arg& arg = *command_.options.back();
@@ -1497,23 +1503,23 @@ auto CommandBuilder::AddArgImpl(const ArgInfo& info, Arg::Kind kind) -> Arg& {
 
 void CommandBuilder::AddPositionalArgImpl(
     const ArgInfo& info, Arg::Kind kind, llvm::function_ref<void(Arg&)> build) {
-  CARBON_CHECK(IsValidName(info.name))
-      << "Invalid argument name: " << info.name;
-  CARBON_CHECK(command_.subcommands.empty())
-      << "Cannot add a positional argument to a command with subcommands.";
+  CARBON_CHECK(IsValidName(info.name), "Invalid argument name: {0}", info.name);
+  CARBON_CHECK(
+      command_.subcommands.empty(),
+      "Cannot add a positional argument to a command with subcommands.");
 
   command_.positional_args.emplace_back(new Arg(info));
   Arg& arg = *command_.positional_args.back();
   arg.kind = kind;
   build(arg);
 
-  CARBON_CHECK(!arg.is_help_hidden)
-      << "Cannot have a help-hidden positional argument.";
+  CARBON_CHECK(!arg.is_help_hidden,
+               "Cannot have a help-hidden positional argument.");
 
   if (arg.is_required && command_.positional_args.size() > 1) {
-    CARBON_CHECK((*std::prev(command_.positional_args.end(), 2))->is_required)
-        << "A required positional argument cannot be added after an optional "
-           "one.";
+    CARBON_CHECK((*std::prev(command_.positional_args.end(), 2))->is_required,
+                 "A required positional argument cannot be added after an "
+                 "optional one.");
   }
 }
 

+ 4 - 4
common/command_line.h

@@ -735,7 +735,7 @@ struct Command {
 
 template <typename T>
 void ArgBuilder::MetaAction(T action) {
-  CARBON_CHECK(!arg_.meta_action) << "Cannot set a meta action twice!";
+  CARBON_CHECK(!arg_.meta_action, "Cannot set a meta action twice!");
   arg_.meta_action = std::move(action);
 }
 
@@ -814,9 +814,9 @@ void OneOfArgBuilder::OneOfImpl(const OneOfValueT<U> (&input_values)[N],
 
   // Fold over all the input values to see if there is a default.
   if ((input_values[Indices].is_default || ...)) {
-    CARBON_CHECK(!arg_.is_append) << "Can't append default.";
-    CARBON_CHECK((input_values[Indices].is_default + ... + 0) == 1)
-        << "Cannot default more than one value.";
+    CARBON_CHECK(!arg_.is_append, "Can't append default.");
+    CARBON_CHECK((input_values[Indices].is_default + ... + 0) == 1,
+                 "Cannot default more than one value.");
 
     arg_.has_default = true;
 

+ 1 - 1
common/error.h

@@ -27,7 +27,7 @@ class [[nodiscard]] Error : public Printable<Error> {
   // Represents an error state.
   explicit Error(llvm::Twine location, llvm::Twine message)
       : location_(location.str()), message_(message.str()) {
-    CARBON_CHECK(!message_.empty()) << "Errors must have a message.";
+    CARBON_CHECK(!message_.empty(), "Errors must have a message.");
   }
 
   // Represents an error with no associated location.

+ 1 - 1
common/hashing_test.cpp

@@ -653,7 +653,7 @@ auto FindBitRangeCollisions(llvm::ArrayRef<HashedValue<T>> hashes)
 auto CheckNoDuplicateValues(llvm::ArrayRef<HashedString> hashes) -> void {
   for (int i = 0, size = hashes.size(); i < size - 1; ++i) {
     const auto& [_, value] = hashes[i];
-    CARBON_CHECK(value != hashes[i + 1].v) << "Duplicate value: " << value;
+    CARBON_CHECK(value != hashes[i + 1].v, "Duplicate value: {0}", value);
   }
 }
 

+ 2 - 2
common/map.h

@@ -472,7 +472,7 @@ MapBase<InputKeyT, InputValueT, InputKeyContextT>::Insert(
            std::invocable<InsertCallbackT, LookupKeyT, void*, void*>)
 {
   auto [entry, inserted] = this->InsertImpl(lookup_key, key_context);
-  CARBON_DCHECK(entry) << "Should always result in a valid index.";
+  CARBON_DCHECK(entry, "Should always result in a valid index.");
 
   if (LLVM_LIKELY(!inserted)) {
     return InsertKVResult(false, *entry);
@@ -538,7 +538,7 @@ MapBase<InputKeyT, InputValueT, InputKeyContextT>::Update(
            std::invocable<UpdateCallbackT, KeyT&, ValueT&>)
 {
   auto [entry, inserted] = this->InsertImpl(lookup_key, key_context);
-  CARBON_DCHECK(entry) << "Should always result in a valid index.";
+  CARBON_DCHECK(entry, "Should always result in a valid index.");
 
   if (LLVM_LIKELY(!inserted)) {
     update_cb(entry->key(), entry->value());

+ 3 - 3
common/map_benchmark.cpp

@@ -460,12 +460,12 @@ static void BM_MapInsertSeq(benchmark::State& state) {
     MapWrapperT m;
     for (auto k : keys) {
       bool inserted = m.BenchInsert(k, MakeValue<VT>());
-      CARBON_DCHECK(inserted) << "Must be a successful insert!";
+      CARBON_DCHECK(inserted, "Must be a successful insert!");
     }
 
     // Now insert a final random repeated key.
     bool inserted = m.BenchInsert(lookup_keys[i], MakeValue2<VT>());
-    CARBON_DCHECK(!inserted) << "Must already be in the map!";
+    CARBON_DCHECK(!inserted, "Must already be in the map!");
 
     // Rotate through the shuffled keys.
     i = (i + static_cast<ssize_t>(!inserted)) & (LookupKeysSize - 1);
@@ -484,7 +484,7 @@ static void BM_MapInsertSeq(benchmark::State& state) {
     MapWrapperT m;
     for (auto k : keys) {
       bool inserted = m.BenchInsert(k, MakeValue<VT>());
-      CARBON_DCHECK(inserted) << "Must be a successful insert!";
+      CARBON_DCHECK(inserted, "Must be a successful insert!");
     }
 
     ReportMetrics(m, state);

+ 24 - 22
common/raw_hashtable.h

@@ -381,8 +381,8 @@ class ViewImpl {
   // given size. This is trivial, but we use this routine to enforce invariants
   // on the sizes.
   static constexpr auto EntriesOffset(ssize_t alloc_size) -> ssize_t {
-    CARBON_DCHECK(llvm::isPowerOf2_64(alloc_size))
-        << "Size must be a power of two for a hashed buffer!";
+    CARBON_DCHECK(llvm::isPowerOf2_64(alloc_size),
+                  "Size must be a power of two for a hashed buffer!");
     // The size is always a power of two. We prevent any too-small sizes so it
     // being a power of two provides the needed alignment. As a result, the
     // offset is exactly the size. We validate this here to catch alignment bugs
@@ -615,8 +615,8 @@ inline auto ComputeSeed() -> uint64_t {
 }
 
 inline auto ComputeProbeMaskFromSize(ssize_t size) -> size_t {
-  CARBON_DCHECK(llvm::isPowerOf2_64(size))
-      << "Size must be a power of two for a hashed buffer!";
+  CARBON_DCHECK(llvm::isPowerOf2_64(size),
+                "Size must be a power of two for a hashed buffer!");
   // Since `size` is a power of two, we can make sure the probes are less
   // than `size` by making the mask `size - 1`. We also mask off the low
   // bits so the probes are a multiple of the size of the groups of entries.
@@ -659,12 +659,14 @@ class ProbeSequence {
     // everything down by `GroupSize`.
     CARBON_DCHECK(
         (p_ / GroupSize) ==
-        ((start_ / GroupSize +
-          (step_ / GroupSize + (step_ / GroupSize) * (step_ / GroupSize)) / 2) %
-         (size_ / GroupSize)))
-        << "Index in probe sequence does not match the expected formula.";
-    CARBON_DCHECK(step_ < size_) << "We necessarily visit all groups, so we "
-                                    "can't have more probe steps than groups.";
+            ((start_ / GroupSize +
+              (step_ / GroupSize + (step_ / GroupSize) * (step_ / GroupSize)) /
+                  2) %
+             (size_ / GroupSize)),
+        "Index in probe sequence does not match the expected formula.");
+    CARBON_DCHECK(step_ < size_,
+                  "We necessarily visit all groups, so we can't have more "
+                  "probe steps than groups.");
 #endif
   }
 
@@ -924,13 +926,14 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::InsertImpl(
     }
 
     --growth_budget_;
-    CARBON_DCHECK(growth_budget() >= 0)
-        << "Growth budget shouldn't have gone negative!";
+    CARBON_DCHECK(growth_budget() >= 0,
+                  "Growth budget shouldn't have gone negative!");
     return return_insert_at_index(group_index + empty_match.index());
   }
 
-  CARBON_FATAL() << "We should never finish probing without finding the entry "
-                    "or an empty slot.";
+  CARBON_FATAL(
+      "We should never finish probing without finding the entry or an empty "
+      "slot.");
 }
 
 template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
@@ -1264,11 +1267,11 @@ BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::InsertIntoEmpty(
 template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
 auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::ComputeNextAllocSize(
     ssize_t old_alloc_size) -> ssize_t {
-  CARBON_DCHECK(llvm::isPowerOf2_64(old_alloc_size))
-      << "Expected a power of two!";
+  CARBON_DCHECK(llvm::isPowerOf2_64(old_alloc_size),
+                "Expected a power of two!");
   ssize_t new_alloc_size;
   bool overflow = __builtin_mul_overflow(old_alloc_size, 2, &new_alloc_size);
-  CARBON_CHECK(!overflow) << "Computing the new size overflowed `ssize_t`!";
+  CARBON_CHECK(!overflow, "Computing the new size overflowed `ssize_t`!");
   return new_alloc_size;
 }
 
@@ -1340,11 +1343,10 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::GrowToNextAllocSize(
       llvm::count(llvm::ArrayRef(old_metadata, old_size), MetadataGroup::Empty);
   ssize_t debug_deleted_count = llvm::count(
       llvm::ArrayRef(old_metadata, old_size), MetadataGroup::Deleted);
-  CARBON_DCHECK(debug_empty_count >=
-                (old_size - GrowthThresholdForAllocSize(old_size)))
-      << "debug_empty_count: " << debug_empty_count
-      << ", debug_deleted_count: " << debug_deleted_count
-      << ", size: " << old_size;
+  CARBON_DCHECK(
+      debug_empty_count >= (old_size - GrowthThresholdForAllocSize(old_size)),
+      "debug_empty_count: {0}, debug_deleted_count: {1}, size: {2}",
+      debug_empty_count, debug_deleted_count, old_size);
 #endif
 
   // Configure for the new size and allocate the new storage.

+ 4 - 3
common/raw_hashtable_benchmark_helpers.cpp

@@ -43,8 +43,9 @@ static auto MakeChars() -> llvm::OwningArrayRef<char> {
       ++i;
     }
   }
-  CARBON_CHECK(i == NumChars) << "Expected exactly " << NumChars
-                              << " characters, got " << i << " instead!";
+  CARBON_CHECK(i == NumChars,
+               "Expected exactly {0} characters, got {1} instead!", NumChars,
+               i);
   return characters;
 }
 
@@ -344,7 +345,7 @@ auto DumpHashStatistics(llvm::ArrayRef<T> keys) -> void {
                                                     GroupShift);
   for (auto [i, k] : llvm::enumerate(keys)) {
     ssize_t hash_index = get_hash_index(k);
-    CARBON_CHECK(hash_index < (expected_size >> GroupShift)) << hash_index;
+    CARBON_CHECK(hash_index < (expected_size >> GroupShift), "{0}", hash_index);
     grouped_key_indices[hash_index].push_back(i);
   }
   ssize_t max_group_index =

+ 53 - 48
common/raw_hashtable_metadata_group.h

@@ -10,6 +10,7 @@
 #include <iterator>
 
 #include "common/check.h"
+#include "common/ostream.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/bit.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -80,14 +81,14 @@ class BitIndex
 
   // Returns true when there are no matches for the tag.
   auto empty() const -> bool {
-    CARBON_DCHECK((bits_ & ZeroMask) == 0) << "Unexpected non-zero bits!";
+    CARBON_DCHECK((bits_ & ZeroMask) == 0, "Unexpected non-zero bits!");
     __builtin_assume((bits_ & ZeroMask) == 0);
     return bits_ == 0;
   }
 
   // Returns the index of the first matched tag.
   auto index() -> ssize_t {
-    CARBON_DCHECK(bits_ != 0) << "Cannot get an index from zero bits!";
+    CARBON_DCHECK(bits_ != 0, "Cannot get an index from zero bits!");
     __builtin_assume(bits_ != 0);
     ssize_t index = unscaled_index();
 
@@ -102,7 +103,7 @@ class BitIndex
   // Optimized tool to index a pointer `p` by `index()`.
   template <typename T>
   auto index_ptr(T* pointer) -> T* {
-    CARBON_DCHECK(bits_ != 0) << "Cannot get an index from zero bits!";
+    CARBON_DCHECK(bits_ != 0, "Cannot get an index from zero bits!");
     __builtin_assume(bits_ != 0);
     if constexpr (!ByteEncoding) {
       return &pointer[unscaled_index()];
@@ -191,7 +192,7 @@ class BitIndexRange : public Printable<BitIndexRange<BitIndexT>> {
     }
 
     auto operator*() -> ssize_t& {
-      CARBON_DCHECK(bits_ != 0) << "Cannot get an index from zero bits!";
+      CARBON_DCHECK(bits_ != 0, "Cannot get an index from zero bits!");
       __builtin_assume(bits_ != 0);
       index_ = BitIndexT(bits_).index();
       // Note that we store the index in a member so we can return a reference
@@ -205,7 +206,7 @@ class BitIndexRange : public Printable<BitIndexRange<BitIndexT>> {
     }
 
     auto operator++() -> Iterator& {
-      CARBON_DCHECK(bits_ != 0) << "Must not increment past the end!";
+      CARBON_DCHECK(bits_ != 0, "Must not increment past the end!");
       __builtin_assume(bits_ != 0);
       // Clears the least significant set bit, effectively stepping to the next
       // match.
@@ -270,7 +271,7 @@ class BitIndexRange : public Printable<BitIndexRange<BitIndexT>> {
 //   }
 //   if (UseSIMD || DebugSIMD) {
 //     simd_result = SIMDOperation(...)
-//     CARBON_DCHECK(result == portable_result) << ...;
+//     CARBON_DCHECK(result == portable_result, "{0}", ...);
 //   }
 //   return UseSIMD ? simd_result : portable_result;
 // }
@@ -533,9 +534,8 @@ inline auto MetadataGroup::Store(uint8_t* metadata, ssize_t index) const
 }
 
 inline auto MetadataGroup::ClearByte(ssize_t byte_index) -> void {
-  CARBON_DCHECK(FastByteClear) << "Only use byte clearing when fast!";
-  CARBON_DCHECK(Size == 8)
-      << "The clear implementation assumes an 8-byte group.";
+  CARBON_DCHECK(FastByteClear, "Only use byte clearing when fast!");
+  CARBON_DCHECK(Size == 8, "The clear implementation assumes an 8-byte group.");
 
   metadata_ints[0] &= ~(static_cast<uint64_t>(0xff) << (byte_index * 8));
 }
@@ -548,9 +548,10 @@ inline auto MetadataGroup::ClearDeleted() -> void {
   }
   if constexpr (UseSIMD || DebugSIMD) {
     simd_g.SIMDClearDeleted();
-    CARBON_DCHECK(simd_g == portable_g)
-        << "SIMD cleared group '" << simd_g
-        << "' doesn't match portable cleared group '" << portable_g << "'";
+    CARBON_DCHECK(
+        simd_g == portable_g,
+        "SIMD cleared group '{0}' doesn't match portable cleared group '{1}'",
+        simd_g, portable_g);
   }
   *this = UseSIMD ? simd_g : portable_g;
 }
@@ -559,7 +560,7 @@ inline auto MetadataGroup::Match(uint8_t tag) const -> MatchRange {
   // The caller should provide us with the present byte hash, and not set any
   // present bit tag on it so that this layer can manage tagging the high bit of
   // a present byte.
-  CARBON_DCHECK((tag & PresentMask) == 0) << llvm::formatv("{0:x}", tag);
+  CARBON_DCHECK((tag & PresentMask) == 0, "{0:x}", tag);
 
   MatchRange portable_result;
   MatchRange simd_result;
@@ -568,9 +569,9 @@ inline auto MetadataGroup::Match(uint8_t tag) const -> MatchRange {
   }
   if constexpr (UseSIMD || DebugSIMD) {
     simd_result = SIMDMatch(tag);
-    CARBON_DCHECK(simd_result == portable_result)
-        << "SIMD result '" << simd_result << "' doesn't match portable result '"
-        << portable_result << "'";
+    CARBON_DCHECK(simd_result == portable_result,
+                  "SIMD result '{0}' doesn't match portable result '{1}'",
+                  simd_result, portable_result);
   }
   return UseSIMD ? simd_result : portable_result;
 }
@@ -583,9 +584,9 @@ inline auto MetadataGroup::MatchPresent() const -> MatchRange {
   }
   if constexpr (UseSIMD || DebugSIMD) {
     simd_result = SIMDMatchPresent();
-    CARBON_DCHECK(simd_result == portable_result)
-        << "SIMD result '" << simd_result << "' doesn't match portable result '"
-        << portable_result << "'";
+    CARBON_DCHECK(simd_result == portable_result,
+                  "SIMD result '{0}' doesn't match portable result '{1}'",
+                  simd_result, portable_result);
   }
   return UseSIMD ? simd_result : portable_result;
 }
@@ -598,9 +599,9 @@ inline auto MetadataGroup::MatchEmpty() const -> MatchIndex {
   }
   if constexpr (UseSIMD || DebugSIMD) {
     simd_result = SIMDMatchEmpty();
-    CARBON_DCHECK(simd_result == portable_result)
-        << "SIMD result '" << simd_result << "' doesn't match portable result '"
-        << portable_result << "'";
+    CARBON_DCHECK(simd_result == portable_result,
+                  "SIMD result '{0}' doesn't match portable result '{1}'",
+                  simd_result, portable_result);
   }
   return UseSIMD ? simd_result : portable_result;
 }
@@ -613,9 +614,9 @@ inline auto MetadataGroup::MatchDeleted() const -> MatchIndex {
   }
   if constexpr (UseSIMD || DebugSIMD) {
     simd_result = SIMDMatchDeleted();
-    CARBON_DCHECK(simd_result == portable_result)
-        << "SIMD result '" << simd_result << "' doesn't match portable result '"
-        << portable_result << "'";
+    CARBON_DCHECK(simd_result == portable_result,
+                  "SIMD result '{0}' doesn't match portable result '{1}'",
+                  simd_result, portable_result);
   }
   return UseSIMD ? simd_result : portable_result;
 }
@@ -640,29 +641,31 @@ inline auto MetadataGroup::VerifyIndexBits(
   for (ssize_t byte_index : llvm::seq<ssize_t>(0, Size)) {
     if constexpr (!ByteEncoding) {
       if (byte_match(metadata_bytes[byte_index])) {
-        CARBON_CHECK(((index_bits >> byte_index) & 1) == 1)
-            << "Bit not set at matching byte index: " << byte_index;
+        CARBON_CHECK(((index_bits >> byte_index) & 1) == 1,
+                     "Bit not set at matching byte index: {0}", byte_index);
         // Only the first match is needed, so stop scanning once found.
         break;
       }
 
-      CARBON_CHECK(((index_bits >> byte_index) & 1) == 0)
-          << "Bit set at non-matching byte index: " << byte_index;
+      CARBON_CHECK(((index_bits >> byte_index) & 1) == 0,
+                   "Bit set at non-matching byte index: {0}", byte_index);
     } else {
       // `index_bits` is byte-encoded rather than bit encoded, so extract a
       // byte.
       uint8_t index_byte = (index_bits >> (byte_index * 8)) & 0xFF;
       if (byte_match(metadata_bytes[byte_index])) {
-        CARBON_CHECK((index_byte & 0x80) == 0x80)
-            << "Should have the high bit set for a matching byte, found: "
-            << llvm::formatv("{0:x}", index_byte);
+        CARBON_CHECK(
+            (index_byte & 0x80) == 0x80,
+            "Should have the high bit set for a matching byte, found: {0:x}",
+            index_byte);
         // Only the first match is needed so stop scanning once found.
         break;
       }
 
-      CARBON_CHECK(index_byte == 0)
-          << "Should have no bits set for an unmatched byte, found: "
-          << llvm::formatv("{0:x}", index_byte);
+      CARBON_CHECK(
+          index_byte == 0,
+          "Should have no bits set for an unmatched byte, found: {0:x}",
+          index_byte);
     }
   }
   return true;
@@ -674,24 +677,26 @@ inline auto MetadataGroup::VerifyRangeBits(
   for (ssize_t byte_index : llvm::seq<ssize_t>(0, Size)) {
     if constexpr (!ByteEncoding) {
       if (byte_match(metadata_bytes[byte_index])) {
-        CARBON_CHECK(((range_bits >> byte_index) & 1) == 1)
-            << "Bit not set at matching byte index: " << byte_index;
+        CARBON_CHECK(((range_bits >> byte_index) & 1) == 1,
+                     "Bit not set at matching byte index: {0}", byte_index);
       } else {
-        CARBON_CHECK(((range_bits >> byte_index) & 1) == 0)
-            << "Bit set at non-matching byte index: " << byte_index;
+        CARBON_CHECK(((range_bits >> byte_index) & 1) == 0,
+                     "Bit set at non-matching byte index: {0}", byte_index);
       }
     } else {
       // `range_bits` is byte-encoded rather than bit encoded, so extract a
       // byte.
       uint8_t range_byte = (range_bits >> (byte_index * 8)) & 0xFF;
       if (byte_match(metadata_bytes[byte_index])) {
-        CARBON_CHECK(range_byte == 0x80)
-            << "Should just have the high bit set for a matching byte, found: "
-            << llvm::formatv("{0:x}", range_byte);
+        CARBON_CHECK(range_byte == 0x80,
+                     "Should just have the high bit set for a matching byte, "
+                     "found: {0:x}",
+                     range_byte);
       } else {
-        CARBON_CHECK(range_byte == 0)
-            << "Should have no bits set for an unmatched byte, found: "
-            << llvm::formatv("{0:x}", range_byte);
+        CARBON_CHECK(
+            range_byte == 0,
+            "Should have no bits set for an unmatched byte, found: {0:x}",
+            range_byte);
       }
     }
   }
@@ -730,7 +735,7 @@ inline auto MetadataGroup::PortableMatch(uint8_t tag) const -> MatchRange {
   // The caller should provide us with the present byte hash, and not set any
   // present bit tag on it so that this layer can manage tagging the high bit of
   // a present byte.
-  CARBON_DCHECK((tag & PresentMask) == 0) << llvm::formatv("{0:x}", tag);
+  CARBON_DCHECK((tag & PresentMask) == 0, "{0:x}", tag);
 
   // Use a simple fallback approach for sizes beyond 8.
   // TODO: Instead of a simple fallback, we should generalize the below
@@ -783,8 +788,8 @@ inline auto MetadataGroup::PortableMatch(uint8_t tag) const -> MatchRange {
   // know that the add cannot carry, and this way it can be lowered using
   // combined multiply-add instructions if available.
   uint64_t broadcast = LSBs * tag + MSBs;
-  CARBON_DCHECK(broadcast == (LSBs * tag | MSBs))
-      << "Unexpected carry from addition!";
+  CARBON_DCHECK(broadcast == (LSBs * tag | MSBs),
+                "Unexpected carry from addition!");
 
   // Xor the broadcast byte pattern. This makes bytes with matches become 0, and
   // clears the high-bits of non-matches. Note that if we are looking for a tag

+ 1 - 1
common/set.h

@@ -372,7 +372,7 @@ auto SetBase<InputKeyT, InputKeyContextT>::Insert(LookupKeyT lookup_key,
   requires std::invocable<InsertCallbackT, LookupKeyT, void*>
 {
   auto [entry, inserted] = this->InsertImpl(lookup_key, key_context);
-  CARBON_DCHECK(entry) << "Should always result in a valid index.";
+  CARBON_DCHECK(entry, "Should always result in a valid index.");
 
   if (LLVM_LIKELY(!inserted)) {
     return InsertResult(false, entry->key());

+ 3 - 3
common/set_benchmark.cpp

@@ -336,12 +336,12 @@ static void BM_SetInsertSeq(benchmark::State& state) {
     SetWrapperT s;
     for (auto k : keys) {
       bool inserted = s.BenchInsert(k);
-      CARBON_DCHECK(inserted) << "Must be a successful insert!";
+      CARBON_DCHECK(inserted, "Must be a successful insert!");
     }
 
     // Now insert a final random repeated key.
     bool inserted = s.BenchInsert(lookup_keys[i]);
-    CARBON_DCHECK(!inserted) << "Must already be in the map!";
+    CARBON_DCHECK(!inserted, "Must already be in the map!");
 
     // Rotate through the shuffled keys.
     i = (i + static_cast<ssize_t>(!inserted)) & (LookupKeysSize - 1);
@@ -360,7 +360,7 @@ static void BM_SetInsertSeq(benchmark::State& state) {
     SetT s;
     for (auto k : keys) {
       bool inserted = s.Insert(k).is_inserted();
-      CARBON_DCHECK(inserted) << "Must be a successful insert!";
+      CARBON_DCHECK(inserted, "Must be a successful insert!");
     }
 
     ReportTableMetrics(s, state);

+ 3 - 3
explorer/ast/bindings.cpp

@@ -25,15 +25,15 @@ void Bindings::Add(Nonnull<const GenericBinding*> binding,
                    Nonnull<const Value*> value,
                    std::optional<Nonnull<const Value*>> witness) {
   bool added_value = args_.insert({binding, value}).second;
-  CARBON_CHECK(added_value) << "Add of already-existing binding";
+  CARBON_CHECK(added_value, "Add of already-existing binding");
 
   if (witness) {
     // TODO: Eventually we should check that we have a witness if and only if
     // the binding has an impl binding.
     auto impl_binding = binding->impl_binding();
-    CARBON_CHECK(impl_binding) << "Given witness but have no impl binding";
+    CARBON_CHECK(impl_binding, "Given witness but have no impl binding");
     bool added_witness = witnesses_.insert({*impl_binding, *witness}).second;
-    CARBON_CHECK(added_witness) << "Add of already-existing binding";
+    CARBON_CHECK(added_witness, "Add of already-existing binding");
   }
 }
 

+ 5 - 5
explorer/ast/clone_context.cpp

@@ -13,10 +13,10 @@ namespace Carbon {
 auto CloneContext::CloneBase(Nonnull<const AstNode*> node)
     -> Nonnull<AstNode*> {
   auto [it, added] = nodes_.insert({node, nullptr});
-  CARBON_CHECK(added) << (it->second
-                              ? "node was cloned multiple times: "
-                              : "node was remapped before it was cloned: ")
-                      << *node;
+  CARBON_CHECK(
+      added, "node was {0}: {1}",
+      it->second ? "cloned multiple times" : "remapped before it was cloned",
+      *node);
 
   // TODO: Generate a Visit member on AstNode and use it here to avoid these
   // macros.
@@ -34,7 +34,7 @@ auto CloneContext::CloneBase(Nonnull<const AstNode*> node)
 
   // Cloning may have invalidated our iterator; redo lookup.
   auto* result = nodes_[node];
-  CARBON_CHECK(result) << "CloneImpl didn't set the result pointer";
+  CARBON_CHECK(result, "CloneImpl didn't set the result pointer");
   return result;
 }
 

+ 1 - 1
explorer/ast/clone_context.h

@@ -131,7 +131,7 @@ class CloneContext {
   template <typename T>
   auto GetExistingClone(Nonnull<const T*> node) -> Nonnull<T*> {
     AstNode* cloned = nodes_.lookup(node);
-    CARBON_CHECK(cloned) << "expected node to be cloned";
+    CARBON_CHECK(cloned, "expected node to be cloned");
     return llvm::cast<T>(cloned);
   }
 

+ 1 - 1
explorer/ast/declaration.cpp

@@ -413,7 +413,7 @@ auto FunctionDeclaration::Create(Nonnull<Arena*> arena,
 void CallableDeclaration::PrintIndent(int indent_num_spaces,
                                       llvm::raw_ostream& out) const {
   auto name = GetName(*this);
-  CARBON_CHECK(name) << "Unexpected missing name for `" << *this << "`.";
+  CARBON_CHECK(name, "Unexpected missing name for `{0}`.", *this);
   out.indent(indent_num_spaces) << "fn " << *name << " ";
   if (!deduced_parameters_.empty() || self_pattern_) {
     out << "[";

+ 3 - 3
explorer/ast/declaration.h

@@ -101,7 +101,7 @@ class Declaration : public AstNode {
   // Set that this node is declared. Should only be called once, by the
   // type-checker, once the node is ready to be named and used.
   void set_is_declared() {
-    CARBON_CHECK(!is_declared_) << "should not be declared twice";
+    CARBON_CHECK(!is_declared_, "should not be declared twice");
     is_declared_ = true;
   }
 
@@ -111,7 +111,7 @@ class Declaration : public AstNode {
   // Set that this node is type-checked. Should only be called once, by the
   // type-checker, once full type-checking is complete.
   void set_is_type_checked() {
-    CARBON_CHECK(!is_type_checked_) << "should not be type-checked twice";
+    CARBON_CHECK(!is_type_checked_, "should not be type-checked twice");
     is_type_checked_ = true;
   }
 
@@ -674,7 +674,7 @@ class VariableDeclaration : public Declaration {
 
   // Can only be called by type-checking, if a conversion was required.
   void set_initializer(Nonnull<Expression*> initializer) {
-    CARBON_CHECK(has_initializer()) << "should not add a new initializer";
+    CARBON_CHECK(has_initializer(), "should not add a new initializer");
     initializer_ = initializer;
   }
 

+ 2 - 2
explorer/ast/element_path.h

@@ -113,8 +113,8 @@ class ElementPath : public Printable<ElementPath> {
   // Removes all trailing `BaseElement`s, errors if there are no base elements.
   auto RemoveTrailingBaseElements() -> void {
     CARBON_CHECK(!components_.empty() && components_.back().element()->kind() ==
-                                             ElementKind::BaseElement)
-        << "No base elements to remove.";
+                                             ElementKind::BaseElement,
+                 "No base elements to remove.");
     const auto r_it = std::find_if(
         components_.rbegin(), components_.rend(), [](const Component& c) {
           return c.element()->kind() != ElementKind::BaseElement;

+ 1 - 2
explorer/ast/expression.cpp

@@ -231,8 +231,7 @@ void Expression::Print(llvm::raw_ostream& out) const {
               << *op.arguments()[1];
           break;
         default:
-          CARBON_FATAL() << "Unexpected argument count: "
-                         << op.arguments().size();
+          CARBON_FATAL("Unexpected argument count: {0}", op.arguments().size());
       }
       out << ")";
       break;

+ 4 - 3
explorer/ast/expression.h

@@ -113,7 +113,7 @@ class RewritableMixin : public Base {
   // Set the rewritten form of this expression. Can only be called during type
   // checking.
   auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
-    CARBON_CHECK(!rewritten_form_.has_value()) << "rewritten form set twice";
+    CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
     rewritten_form_ = rewritten_form;
     this->set_static_type(&rewritten_form->static_type());
     this->set_expression_category(rewritten_form->expression_category());
@@ -660,8 +660,9 @@ class StructTypeLiteral : public ConstantValueLiteral {
                              std::vector<FieldInitializer> fields)
       : ConstantValueLiteral(AstNodeKind::StructTypeLiteral, loc),
         fields_(std::move(fields)) {
-    CARBON_CHECK(!fields_.empty())
-        << "`{}` is represented as a StructLiteral, not a StructTypeLiteral.";
+    CARBON_CHECK(
+        !fields_.empty(),
+        "`{}` is represented as a StructLiteral, not a StructTypeLiteral.");
   }
 
   explicit StructTypeLiteral(CloneContext& context,

+ 3 - 3
explorer/ast/impl_binding.h

@@ -49,14 +49,14 @@ class ImplBinding : public AstNode {
   // The constraint being implemented.
   // TODO: Rename this to `constraint`.
   auto interface() const -> Nonnull<const Value*> {
-    CARBON_CHECK(iface_) << "interface has not been set yet";
+    CARBON_CHECK(iface_, "interface has not been set yet");
     return *iface_;
   }
 
   // Set the interface being implemented, if not set by the constructor. Should
   // only be called by typechecking.
   void set_interface(Nonnull<const Value*> iface) {
-    CARBON_CHECK(!iface_) << "interface set twice";
+    CARBON_CHECK(!iface_, "interface set twice");
     iface_ = iface;
   }
 
@@ -75,7 +75,7 @@ class ImplBinding : public AstNode {
   // These functions exist only so that an `ImplBinding` can be used as a
   // `ValueNodeView` as a key in a `StaticScope`.
   auto static_type() const -> const Value& {
-    CARBON_FATAL() << "an ImplBinding has no type";
+    CARBON_FATAL("an ImplBinding has no type");
   }
   auto expression_category() const -> ExpressionCategory {
     return ExpressionCategory::Value;

+ 2 - 2
explorer/ast/pattern.h

@@ -79,7 +79,7 @@ class Pattern : public AstNode {
   // Sets the value of this pattern. Can only be called once, during
   // typechecking.
   void set_value(Nonnull<const Value*> value) {
-    CARBON_CHECK(!value_) << "set_value called more than once";
+    CARBON_CHECK(!value_, "set_value called more than once");
     value_ = value;
   }
 
@@ -307,7 +307,7 @@ class GenericBinding : public Pattern {
 
   // Set the index of this binding. Should be called only during type-checking.
   void set_index(int index) {
-    CARBON_CHECK(!index_) << "should only set depth and index once";
+    CARBON_CHECK(!index_, "should only set depth and index once");
     index_ = index;
   }
 

+ 3 - 3
explorer/ast/statement.h

@@ -150,7 +150,7 @@ class Assign : public Statement {
   // Set the rewritten form of this statement. Can only be called during type
   // checking.
   auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
-    CARBON_CHECK(!rewritten_form_.has_value()) << "rewritten form set twice";
+    CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
     rewritten_form_ = rewritten_form;
   }
 
@@ -196,7 +196,7 @@ class IncrementDecrement : public Statement {
   // Set the rewritten form of this statement. Can only be called during type
   // checking.
   auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
-    CARBON_CHECK(!rewritten_form_.has_value()) << "rewritten form set twice";
+    CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
     rewritten_form_ = rewritten_form;
   }
 
@@ -256,7 +256,7 @@ class VariableDefinition : public Statement {
 
   // Can only be called by type-checking, if a conversion was required.
   void set_init(Nonnull<Expression*> init) {
-    CARBON_CHECK(has_init()) << "should not add a new initializer";
+    CARBON_CHECK(has_init(), "should not add a new initializer");
     init_ = init;
   }
 

+ 2 - 2
explorer/ast/static_scope.cpp

@@ -54,7 +54,7 @@ void StaticScope::PrintID(llvm::raw_ostream& out) const {
 
 void StaticScope::MarkDeclared(std::string_view name) {
   auto it = declared_names_.find(name);
-  CARBON_CHECK(it != declared_names_.end()) << name << " not found";
+  CARBON_CHECK(it != declared_names_.end(), "{0} not found", name);
   if (it->second.status == NameStatus::KnownButNotDeclared) {
     it->second.status = NameStatus::DeclaredButNotUsable;
     if (trace_stream_->is_enabled()) {
@@ -67,7 +67,7 @@ void StaticScope::MarkDeclared(std::string_view name) {
 
 void StaticScope::MarkUsable(std::string_view name) {
   auto it = declared_names_.find(name);
-  CARBON_CHECK(it != declared_names_.end()) << name << " not found";
+  CARBON_CHECK(it != declared_names_.end(), "{0} not found", name);
   it->second.status = NameStatus::Usable;
   if (trace_stream_->is_enabled()) {
     trace_stream_->Result()

+ 22 - 24
explorer/ast/value.cpp

@@ -159,8 +159,8 @@ static auto GetPositionalElement(Nonnull<const TupleValue*> tuple,
                                  const ElementPath::Component& path_comp,
                                  SourceLocation source_loc)
     -> ErrorOr<Nonnull<const Value*>> {
-  CARBON_CHECK(path_comp.element()->kind() == ElementKind::PositionalElement)
-      << "Invalid non-tuple member";
+  CARBON_CHECK(path_comp.element()->kind() == ElementKind::PositionalElement,
+               "Invalid non-tuple member");
   const auto* tuple_element = cast<PositionalElement>(path_comp.element());
   const size_t index = tuple_element->index();
   if (index < 0 || index >= tuple->elements().size()) {
@@ -175,8 +175,8 @@ static auto GetNamedElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
                             SourceLocation source_loc,
                             std::optional<Nonnull<const Value*>> me_value)
     -> ErrorOr<Nonnull<const Value*>> {
-  CARBON_CHECK(field.element()->kind() == ElementKind::NamedElement)
-      << "Invalid element, expecting NamedElement";
+  CARBON_CHECK(field.element()->kind() == ElementKind::NamedElement,
+               "Invalid element, expecting NamedElement");
   const auto* member = cast<NamedElement>(field.element());
   const auto f = member->name();
   if (field.witness().has_value()) {
@@ -186,7 +186,7 @@ static auto GetNamedElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
     if (const auto* assoc_const =
             dyn_cast_or_null<AssociatedConstantDeclaration>(
                 member->declaration().value_or(nullptr))) {
-      CARBON_CHECK(field.interface()) << "have witness but no interface";
+      CARBON_CHECK(field.interface(), "have witness but no interface");
       // TODO: Use witness to find the value of the constant.
       return arena->New<AssociatedConstant>(v, *field.interface(), assoc_const,
                                             witness);
@@ -259,8 +259,8 @@ static auto GetNamedElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
           // Get class value matching the virtual method, and turn it into a
           // bound method.
           for (int i = 0; i < level_diff; ++i) {
-            CARBON_CHECK(m_class_value->base())
-                << "Error trying to access function class value";
+            CARBON_CHECK(m_class_value->base(),
+                         "Error trying to access function class value");
             m_class_value = *m_class_value->base();
           }
           return arena->New<BoundMethodValue>(
@@ -299,7 +299,7 @@ static auto GetNamedElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
                                        &class_type.bindings());
     }
     default:
-      CARBON_FATAL() << "named element access not supported for value " << *v;
+      CARBON_FATAL("named element access not supported for value {0}", *v);
   }
 }
 
@@ -315,7 +315,7 @@ static auto GetElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
       if (const auto* tuple = dyn_cast<TupleValue>(v)) {
         return GetPositionalElement(tuple, path_comp, source_loc);
       } else {
-        CARBON_FATAL() << "Invalid value for positional element";
+        CARBON_FATAL("Invalid value for positional element");
       }
     }
     case ElementKind::BaseElement:
@@ -328,7 +328,7 @@ static auto GetElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
               ptr->address().ElementAddress(path_comp.element()));
         }
         default:
-          CARBON_FATAL() << "Invalid value for base element";
+          CARBON_FATAL("Invalid value for base element");
       }
   }
 }
@@ -404,9 +404,9 @@ static auto SetFieldImpl(
     }
     case Value::Kind::TupleType:
     case Value::Kind::TupleValue: {
-      CARBON_CHECK((*path_begin).element()->kind() ==
-                   ElementKind::PositionalElement)
-          << "Invalid non-positional member for tuple";
+      CARBON_CHECK(
+          (*path_begin).element()->kind() == ElementKind::PositionalElement,
+          "Invalid non-positional member for tuple");
       std::vector<Nonnull<const Value*>> elements =
           cast<TupleValueBase>(*value).elements();
       const size_t index =
@@ -425,7 +425,7 @@ static auto SetFieldImpl(
       }
     }
     default:
-      CARBON_FATAL() << "field access not allowed for value " << *value;
+      CARBON_FATAL("field access not allowed for value {0}", *value);
   }
 }
 
@@ -841,7 +841,7 @@ void IntrinsicConstraint::Print(llvm::raw_ostream& out) const {
 static auto BindingMapEqual(
     const BindingMap& map1, const BindingMap& map2,
     std::optional<Nonnull<const EqualityContext*>> equality_ctx) -> bool {
-  CARBON_CHECK(map1.size() == map2.size()) << "maps should have same keys";
+  CARBON_CHECK(map1.size() == map2.size(), "maps should have same keys");
   for (const auto& [key, value] : map1) {
     if (!ValueEqual(value, map2.at(key), equality_ctx)) {
       return false;
@@ -1025,17 +1025,16 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
     case Value::Kind::MixinPseudoType:
     case Value::Kind::TypeOfMixinPseudoType:
     case Value::Kind::TypeOfNamespaceName:
-      CARBON_FATAL() << "TypeEqual used to compare non-type values\n"
-                     << *t1 << "\n"
-                     << *t2;
+      CARBON_FATAL("TypeEqual used to compare non-type values\n{0}\n{1}", *t1,
+                   *t2);
     case Value::Kind::ImplWitness:
     case Value::Kind::BindingWitness:
     case Value::Kind::ConstraintWitness:
     case Value::Kind::ConstraintImplWitness:
-      CARBON_FATAL() << "TypeEqual: unexpected Witness";
+      CARBON_FATAL("TypeEqual: unexpected Witness");
       break;
     case Value::Kind::AutoType:
-      CARBON_FATAL() << "TypeEqual: unexpected AutoType";
+      CARBON_FATAL("TypeEqual: unexpected AutoType");
       break;
   }
 }
@@ -1123,8 +1122,8 @@ static auto ValueStructurallyEqual(
           GetName(cast<ParameterizedEntityName>(v1)->declaration());
       std::optional<std::string_view> name2 =
           GetName(cast<ParameterizedEntityName>(v2)->declaration());
-      CARBON_CHECK(name1.has_value() && name2.has_value())
-          << "parameterized name refers to unnamed declaration";
+      CARBON_CHECK(name1.has_value() && name2.has_value(),
+                   "parameterized name refers to unnamed declaration");
       return *name1 == *name2;
     }
     case Value::Kind::AssociatedConstant: {
@@ -1171,8 +1170,7 @@ static auto ValueStructurallyEqual(
     case Value::Kind::MemberName:
       // TODO: support pointer comparisons once we have a clearer distinction
       // between pointers and lvalues.
-      CARBON_FATAL() << "ValueEqual does not support this kind of value: "
-                     << *v1;
+      CARBON_FATAL("ValueEqual does not support this kind of value: {0}", *v1);
   }
 }
 

+ 10 - 10
explorer/ast/value.h

@@ -924,8 +924,8 @@ class MixinPseudoType : public Value {
  public:
   explicit MixinPseudoType(Nonnull<const MixinDeclaration*> declaration)
       : Value(Kind::MixinPseudoType), declaration_(declaration) {
-    CARBON_CHECK(!declaration->params().has_value())
-        << "missing arguments for parameterized mixin type";
+    CARBON_CHECK(!declaration->params().has_value(),
+                 "missing arguments for parameterized mixin type");
   }
   explicit MixinPseudoType(Nonnull<const MixinDeclaration*> declaration,
                            Nonnull<const Bindings*> bindings)
@@ -982,8 +982,8 @@ class InterfaceType : public Value {
  public:
   explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration)
       : Value(Kind::InterfaceType), declaration_(declaration) {
-    CARBON_CHECK(!declaration->params().has_value())
-        << "missing arguments for parameterized interface type";
+    CARBON_CHECK(!declaration->params().has_value(),
+                 "missing arguments for parameterized interface type");
   }
   explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration,
                          Nonnull<const Bindings*> bindings)
@@ -1334,8 +1334,8 @@ class ConstraintImplWitness : public Witness {
   // element.
   static auto Make(Nonnull<Arena*> arena, Nonnull<const Witness*> witness,
                    int index) -> Nonnull<const Witness*> {
-    CARBON_CHECK(!llvm::isa<ImplWitness>(witness))
-        << "impl witness has no components to access";
+    CARBON_CHECK(!llvm::isa<ImplWitness>(witness),
+                 "impl witness has no components to access");
     if (const auto* constraint_witness =
             llvm::dyn_cast<ConstraintWitness>(witness)) {
       return constraint_witness->witnesses()[index];
@@ -1348,8 +1348,8 @@ class ConstraintImplWitness : public Witness {
       : Witness(Kind::ConstraintImplWitness),
         constraint_witness_(constraint_witness),
         index_(index) {
-    CARBON_CHECK(!llvm::isa<ConstraintWitness>(constraint_witness))
-        << "should have resolved element from constraint witness";
+    CARBON_CHECK(!llvm::isa<ConstraintWitness>(constraint_witness),
+                 "should have resolved element from constraint witness");
   }
 
   static auto classof(const Value* value) -> bool {
@@ -1480,8 +1480,8 @@ class MemberName : public Value, public Printable<MemberName> {
         base_type_(base_type),
         interface_(interface),
         member_(member) {
-    CARBON_CHECK(base_type || interface)
-        << "member name must be in a type, an interface, or both";
+    CARBON_CHECK(base_type || interface,
+                 "member name must be in a type, an interface, or both");
   }
 
   static auto classof(const Value* value) -> bool {

+ 2 - 2
explorer/file_test.cpp

@@ -23,8 +23,8 @@ class ExplorerFileTest : public FileTestBase {
       : FileTestBase(test_name),
         prelude_line_re_(R"(prelude.carbon:(\d+))"),
         timing_re_(R"((Time elapsed in \w+: )\d+(ms))") {
-    CARBON_CHECK(prelude_line_re_.ok()) << prelude_line_re_.error();
-    CARBON_CHECK(timing_re_.ok()) << timing_re_.error();
+    CARBON_CHECK(prelude_line_re_.ok(), "{0}", prelude_line_re_.error());
+    CARBON_CHECK(timing_re_.ok(), "{0}", timing_re_.error());
   }
 
   auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,

+ 9 - 9
explorer/fuzzing/ast_to_proto.cpp

@@ -638,10 +638,10 @@ static auto DeclarationToProto(const Declaration& declaration)
           default:
             // Parser shouldn't allow self_pattern to be anything other than
             // AddrPattern or BindingPattern
-            CARBON_FATAL()
-                << "self_pattern in method declaration can be either "
-                   "AddrPattern or BindingPattern. Actual pattern: "
-                << function.self_pattern();
+            CARBON_FATAL(
+                "self_pattern in method declaration can be either AddrPattern "
+                "or BindingPattern. Actual pattern: {0}",
+                function.self_pattern());
             break;
         }
       }
@@ -674,10 +674,10 @@ static auto DeclarationToProto(const Declaration& declaration)
           default:
             // Parser shouldn't allow self_pattern to be anything other than
             // AddrPattern or BindingPattern
-            CARBON_FATAL()
-                << "self_pattern in method declaration can be either "
-                   "AddrPattern or BindingPattern. Actual pattern: "
-                << function.self_pattern();
+            CARBON_FATAL(
+                "self_pattern in method declaration can be either AddrPattern "
+                "or BindingPattern. Actual pattern: {0}",
+                function.self_pattern());
             break;
         }
       }
@@ -833,7 +833,7 @@ static auto DeclarationToProto(const Declaration& declaration)
     }
 
     case DeclarationKind::SelfDeclaration: {
-      CARBON_FATAL() << "Unreachable SelfDeclaration in DeclarationToProto().";
+      CARBON_FATAL("Unreachable SelfDeclaration in DeclarationToProto().");
     }
 
     case DeclarationKind::AliasDeclaration: {

+ 2 - 2
explorer/fuzzing/fuzzer_util.cpp

@@ -38,10 +38,10 @@ auto ParseAndExecuteProto(const Fuzzing::Carbon& carbon) -> ErrorOr<int> {
   const ErrorOr<std::string> prelude_path =
       GetRunfilesFile("carbon/explorer/data/prelude.carbon");
   // Can't do anything without a prelude, so it's a fatal error.
-  CARBON_CHECK(prelude_path.ok()) << prelude_path.error();
+  CARBON_CHECK(prelude_path.ok(), "{0}", prelude_path.error());
   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> prelude =
       llvm::MemoryBuffer::getFile(*prelude_path);
-  CARBON_CHECK(!prelude.getError()) << prelude.getError().message();
+  CARBON_CHECK(!prelude.getError(), "{0}", prelude.getError().message());
   CARBON_CHECK(fs.addFile("prelude.carbon", /*ModificationTime=*/0,
                           std::move(*prelude)));
 

+ 8 - 8
explorer/interpreter/action.cpp

@@ -56,19 +56,19 @@ void RuntimeScope::Bind(ValueNodeView value_node, Address address) {
   bool success =
       locals_.insert({value_node, heap_->arena().New<LocationValue>(address)})
           .second;
-  CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
+  CARBON_CHECK(success, "Duplicate definition of {0}", value_node.base());
 }
 
 void RuntimeScope::BindAndPin(ValueNodeView value_node, Address address) {
   Bind(value_node, address);
   bool success = bound_values_.insert(&value_node.base()).second;
-  CARBON_CHECK(success) << "Duplicate pinned node for " << value_node.base();
+  CARBON_CHECK(success, "Duplicate pinned node for {0}", value_node.base());
   heap_->BindValueToReference(value_node, address);
 }
 
 void RuntimeScope::BindLifetimeToScope(Address address) {
-  CARBON_CHECK(address.element_path_.IsEmpty())
-      << "Cannot extend lifetime of a specific sub-element";
+  CARBON_CHECK(address.element_path_.IsEmpty(),
+               "Cannot extend lifetime of a specific sub-element");
   allocations_.push_back(address.allocation_);
 }
 
@@ -77,7 +77,7 @@ void RuntimeScope::BindValue(ValueNodeView value_node,
   CARBON_CHECK(!value_node.constant_value().has_value());
   CARBON_CHECK(value->kind() != Value::Kind::LocationValue);
   bool success = locals_.insert({value_node, value}).second;
-  CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
+  CARBON_CHECK(success, "Duplicate definition of {0}", value_node.base());
 }
 
 auto RuntimeScope::Initialize(ValueNodeView value_node,
@@ -89,7 +89,7 @@ auto RuntimeScope::Initialize(ValueNodeView value_node,
   const auto* location =
       heap_->arena().New<LocationValue>(Address(allocations_.back()));
   bool success = locals_.insert({value_node, location}).second;
-  CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
+  CARBON_CHECK(success, "Duplicate definition of {0}", value_node.base());
   return location;
 }
 
@@ -97,11 +97,11 @@ void RuntimeScope::Merge(RuntimeScope other) {
   CARBON_CHECK(heap_ == other.heap_);
   for (auto& element : other.locals_) {
     bool success = locals_.insert(element).second;
-    CARBON_CHECK(success) << "Duplicate definition of " << element.first;
+    CARBON_CHECK(success, "Duplicate definition of {0}", element.first);
   }
   for (const auto* element : other.bound_values_) {
     bool success = bound_values_.insert(element).second;
-    CARBON_CHECK(success) << "Duplicate bound value.";
+    CARBON_CHECK(success, "Duplicate bound value.");
   }
   allocations_.insert(allocations_.end(), other.allocations_.begin(),
                       other.allocations_.end());

+ 1 - 1
explorer/interpreter/action.h

@@ -346,7 +346,7 @@ class StatementAction : public Action {
 
   // Sets the location provided to an initializing expression.
   auto set_location_created(AllocationId location_created) {
-    CARBON_CHECK(!location_created_) << "location created set twice";
+    CARBON_CHECK(!location_created_, "location created set twice");
     location_created_ = location_created;
   }
   // Returns the location provided to an initializing expression, if any.

+ 7 - 7
explorer/interpreter/action_stack.cpp

@@ -92,7 +92,7 @@ void ActionStack::MergeScope(RuntimeScope scope) {
     globals_->Merge(std::move(scope));
     return;
   }
-  CARBON_FATAL() << "No current scope";
+  CARBON_FATAL("No current scope");
 }
 
 namespace {
@@ -132,9 +132,9 @@ auto ActionStack::FinishAction() -> ErrorOr<Success> {
   std::unique_ptr<Action> act = Pop();
   switch (FinishActionKindFor(act->kind())) {
     case FinishActionKind::Value:
-      CARBON_FATAL() << "This kind of action must produce a result: " << *act;
+      CARBON_FATAL("This kind of action must produce a result: {0}", *act);
     case FinishActionKind::NeverCalled:
-      CARBON_FATAL() << "Should not call FinishAction for: " << *act;
+      CARBON_FATAL("Should not call FinishAction for: {0}", *act);
     case FinishActionKind::NoValue:
       PopScopes(scopes_to_destroy);
       break;
@@ -150,9 +150,9 @@ auto ActionStack::FinishAction(Nonnull<const Value*> result)
   std::unique_ptr<Action> act = Pop();
   switch (FinishActionKindFor(act->kind())) {
     case FinishActionKind::NoValue:
-      CARBON_FATAL() << "This kind of action cannot produce results: " << *act;
+      CARBON_FATAL("This kind of action cannot produce results: {0}", *act);
     case FinishActionKind::NeverCalled:
-      CARBON_FATAL() << "Should not call FinishAction for: " << *act;
+      CARBON_FATAL("Should not call FinishAction for: {0}", *act);
     case FinishActionKind::Value:
       PopScopes(scopes_to_destroy);
       SetResult(result);
@@ -183,8 +183,8 @@ auto ActionStack::ReplaceWith(std::unique_ptr<Action> replacement)
     -> ErrorOr<Success> {
   std::unique_ptr<Action> old = Pop();
   CARBON_CHECK(FinishActionKindFor(old->kind()) ==
-               FinishActionKindFor(replacement->kind()))
-      << "Can't replace action " << *old << " with " << *replacement;
+                   FinishActionKindFor(replacement->kind()),
+               "Can't replace action {0} with {1}", *old, *replacement);
   Push(std::move(replacement));
   return Success();
 }

+ 2 - 2
explorer/interpreter/heap.cpp

@@ -116,8 +116,8 @@ auto Heap::Deallocate(AllocationId allocation) -> ErrorOr<Success> {
   if (states_[allocation.index_] != ValueState::Dead) {
     states_[allocation.index_] = ValueState::Dead;
   } else {
-    CARBON_FATAL() << "deallocating an already dead value: "
-                   << *values_[allocation.index_];
+    CARBON_FATAL("deallocating an already dead value: {0}",
+                 *values_[allocation.index_]);
   }
 
   if (trace_stream_->is_enabled()) {

+ 7 - 7
explorer/interpreter/impl_scope.cpp

@@ -28,8 +28,8 @@ void ImplScope::Add(Nonnull<const Value*> iface,
                     const TypeChecker& type_checker,
                     std::optional<TypeStructureSortKey> sort_key) {
   if (const auto* constraint = dyn_cast<ConstraintType>(iface)) {
-    CARBON_CHECK(!sort_key)
-        << "should only be given a sort key for an impl of an interface";
+    CARBON_CHECK(!sort_key,
+                 "should only be given a sort key for an impl of an interface");
     // The caller should have substituted `.Self` for `type` already.
     Add(constraint->impls_constraints(), deduced, impl_bindings, witness,
         type_checker);
@@ -85,8 +85,8 @@ static auto DiagnoseUnequalValues(SourceLocation source_loc,
                                   Nonnull<const Value*> b_evaluated,
                                   Nonnull<const EqualityContext*> equality_ctx)
     -> Error {
-  CARBON_CHECK(!ValueEqual(a_evaluated, b_evaluated, equality_ctx))
-      << "expected unequal values";
+  CARBON_CHECK(!ValueEqual(a_evaluated, b_evaluated, equality_ctx),
+               "expected unequal values");
   auto error = ProgramError(source_loc);
   error << "constraint requires that " << *a_written;
   if (!ValueEqual(a_written, a_evaluated, std::nullopt)) {
@@ -110,7 +110,7 @@ auto ImplScope::Resolve(Nonnull<const Value*> constraint_type,
       std::optional<Nonnull<const Witness*>> witness,
       TryResolve(constraint_type, impl_type, source_loc, type_checker, bindings,
                  /*diagnose_missing_impl=*/true));
-  CARBON_CHECK(witness) << "should have diagnosed missing impl";
+  CARBON_CHECK(witness, "should have diagnosed missing impl");
   return *witness;
 }
 
@@ -238,7 +238,7 @@ auto ImplScope::TryResolve(Nonnull<const Value*> constraint_type,
     }
     return {type_checker.MakeConstraintWitness(std::move(witnesses))};
   }
-  CARBON_FATAL() << "expected a constraint, not " << *constraint_type;
+  CARBON_FATAL("expected a constraint, not {0}", *constraint_type);
 }
 
 auto ImplScope::VisitEqualValues(
@@ -333,7 +333,7 @@ static auto CombineResults(Nonnull<const InterfaceType*> iface_type,
       return b;
     }
   }
-  CARBON_CHECK(impl_a && impl_b) << "non-final impl should not be symbolic";
+  CARBON_CHECK(impl_a && impl_b, "non-final impl should not be symbolic");
 
   // At this point, we're comparing two `impl` declarations, and either they're
   // both final or neither of them is.

+ 70 - 68
explorer/interpreter/interpreter.cpp

@@ -292,8 +292,8 @@ auto Interpreter::EvalPrim(Operator op, Nonnull<const Value*> /*static_type*/,
     case Operator::BitShiftLeft:
     case Operator::BitShiftRight:
     case Operator::Complement:
-      CARBON_FATAL() << "operator " << OperatorToString(op)
-                     << " should always be rewritten";
+      CARBON_FATAL("operator {0} should always be rewritten",
+                   OperatorToString(op));
   }
 }
 
@@ -320,7 +320,7 @@ auto Interpreter::StepLocation() -> ErrorOr<Success> {
           Nonnull<const Value*> value,
           todo_.ValueOfNode(cast<IdentifierExpression>(exp).value_node(),
                             exp.source_loc()));
-      CARBON_CHECK(isa<LocationValue>(value)) << *value;
+      CARBON_CHECK(isa<LocationValue>(value), "{0}", *value);
       return todo_.FinishAction(value);
     }
     case ExpressionKind::SimpleMemberAccessExpression: {
@@ -361,8 +361,8 @@ auto Interpreter::StepLocation() -> ErrorOr<Success> {
         if (constant_value) {
           return todo_.FinishAction(act.results().back());
         }
-        CARBON_CHECK(!access.member().interface().has_value())
-            << "unexpected location interface member";
+        CARBON_CHECK(!access.member().interface().has_value(),
+                     "unexpected location interface member");
         CARBON_ASSIGN_OR_RETURN(
             Nonnull<const Value*> val,
             Convert(act.results()[0], *access.member().base_type(),
@@ -411,8 +411,8 @@ auto Interpreter::StepLocation() -> ErrorOr<Success> {
         return todo_.ReplaceWith(std::make_unique<LocationAction>(*rewrite));
       }
       if (op.op() != Operator::Deref) {
-        CARBON_FATAL()
-            << "Can't treat primitive operator expression as location: " << exp;
+        CARBON_FATAL(
+            "Can't treat primitive operator expression as location: {0}", exp);
       }
       if (act.pos() == 0) {
         return todo_.Spawn(
@@ -442,9 +442,9 @@ auto Interpreter::StepLocation() -> ErrorOr<Success> {
     case ExpressionKind::DotSelfExpression:
     case ExpressionKind::ArrayTypeLiteral:
     case ExpressionKind::BuiltinConvertExpression:
-      CARBON_FATAL() << "Can't treat expression as location: " << exp;
+      CARBON_FATAL("Can't treat expression as location: {0}", exp);
     case ExpressionKind::UnimplementedExpression:
-      CARBON_FATAL() << "Unimplemented: " << exp;
+      CARBON_FATAL("Unimplemented: {0}", exp);
   }
 }
 
@@ -478,8 +478,8 @@ auto Interpreter::EvalAssociatedConstant(
 
   const auto* impl_witness = dyn_cast<ImplWitness>(witness);
   if (!impl_witness) {
-    CARBON_CHECK(phase() == Phase::CompileTime)
-        << "symbolic witnesses should only be formed at compile time";
+    CARBON_CHECK(phase() == Phase::CompileTime,
+                 "symbolic witnesses should only be formed at compile time");
     CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> base,
                             InstantiateType(&assoc->base(), source_loc));
     return arena_->New<AssociatedConstant>(base, cast<InterfaceType>(interface),
@@ -500,10 +500,11 @@ auto Interpreter::EvalAssociatedConstant(
     }
   }
   if (!result) {
-    CARBON_FATAL() << impl_witness->declaration() << " with constraint "
-                   << *constraint
-                   << " is missing value for associated constant "
-                   << *interface << "." << assoc->constant().binding().name();
+    CARBON_FATAL(
+        "{0} with constraint {1} is missing value for associated constant "
+        "{2}.{3}",
+        impl_witness->declaration(), *constraint, *interface,
+        assoc->constant().binding().name());
   }
   return *result;
 }
@@ -615,9 +616,9 @@ auto Interpreter::ConvertStructToClass(
                           InstantiateType(class_type, source_loc));
   for (const auto& field : init_struct->elements()) {
     if (field.name == NominalClassValue::BaseField) {
-      CARBON_CHECK(class_type->base().has_value())
-          << "Invalid 'base' field for class '"
-          << class_type->declaration().name() << "' without base class.";
+      CARBON_CHECK(class_type->base().has_value(),
+                   "Invalid 'base' field for class '{0}' without base class.",
+                   class_type->declaration().name());
       CARBON_ASSIGN_OR_RETURN(
           auto base,
           Convert(field.value, class_type->base().value(), source_loc));
@@ -626,8 +627,8 @@ auto Interpreter::ConvertStructToClass(
       struct_values.push_back(field);
     }
   }
-  CARBON_CHECK(!cast<NominalClassType>(inst_class)->base() || base_instance)
-      << "Invalid conversion for `" << *inst_class << "`: base class missing";
+  CARBON_CHECK(!cast<NominalClassType>(inst_class)->base() || base_instance,
+               "Invalid conversion for `{0}`: base class missing", *inst_class);
   auto* converted_init_struct =
       arena_->New<StructValue>(std::move(struct_values));
   Nonnull<const NominalClassValue** const> class_value_ptr =
@@ -714,15 +715,15 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
         case Value::Kind::ConstraintType:
         case Value::Kind::NamedConstraintType:
         case Value::Kind::InterfaceType: {
-          CARBON_CHECK(struct_val.elements().empty())
-              << "only empty structs convert to `type`";
+          CARBON_CHECK(struct_val.elements().empty(),
+                       "only empty structs convert to `type`");
           return arena_->New<StructType>();
         }
         default: {
           CARBON_CHECK(IsValueKindDependent(destination_type) ||
-                       isa<TypeType, ConstraintType>(destination_type))
-              << "Can't convert value " << *value << " to type "
-              << *destination_type;
+                           (isa<TypeType, ConstraintType>(destination_type)),
+                       "Can't convert value {0} to type {1}", *value,
+                       *destination_type);
           return value;
         }
       }
@@ -757,9 +758,9 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
         }
         default: {
           CARBON_CHECK(IsValueKindDependent(destination_type) ||
-                       isa<TypeType, ConstraintType>(destination_type))
-              << "Can't convert value " << *value << " to type "
-              << *destination_type;
+                           (isa<TypeType, ConstraintType>(destination_type)),
+                       "Can't convert value {0} to type {1}", *value,
+                       *destination_type);
           return value;
         }
       }
@@ -832,8 +833,8 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
       const auto* src_ptr = cast<PointerValue>(value);
       CARBON_ASSIGN_OR_RETURN(const auto* pointee,
                               heap_.Read(src_ptr->address(), source_loc))
-      CARBON_CHECK(pointee->kind() == Value::Kind::NominalClassValue)
-          << "Unexpected pointer type";
+      CARBON_CHECK(pointee->kind() == Value::Kind::NominalClassValue,
+                   "Unexpected pointer type");
 
       // Conversion logic for subtyping for function arguments only.
       // TODO: Drop when able to rewrite subtyping in TypeChecker for arguments.
@@ -945,7 +946,7 @@ auto Interpreter::CallFunction(const CallExpression& call,
                                   ExpressionResult::Value(converted_args),
                                   call.source_loc(), &function_scope,
                                   generic_args, trace_stream_, this->arena_);
-      CARBON_CHECK(success) << "Failed to bind arguments to parameters";
+      CARBON_CHECK(success, "Failed to bind arguments to parameters");
       return todo_.Spawn(std::make_unique<StatementAction>(*function.body(),
                                                            location_received),
                          std::move(function_scope));
@@ -977,7 +978,7 @@ auto Interpreter::CallFunction(const CallExpression& call,
           return todo_.FinishAction(arena_->New<ChoiceType>(
               &cast<ChoiceDeclaration>(decl), bindings));
         default:
-          CARBON_FATAL() << "unknown kind of ParameterizedEntityName " << decl;
+          CARBON_FATAL("unknown kind of ParameterizedEntityName {0}", decl);
       }
     }
     default:
@@ -997,8 +998,8 @@ auto Interpreter::CallDestructor(Nonnull<const DestructorDeclaration*> fun,
   BindSelfIfPresent(fun, receiver, method_scope, generic_args,
                     SourceLocation::DiagnosticsIgnored());
 
-  CARBON_CHECK(method.body().has_value())
-      << "Calling a method that's missing a body";
+  CARBON_CHECK(method.body().has_value(),
+               "Calling a method that's missing a body");
 
   auto act = std::make_unique<StatementAction>(*method.body(), std::nullopt);
   return todo_.Spawn(std::unique_ptr<Action>(std::move(act)),
@@ -1019,7 +1020,7 @@ void Interpreter::BindSelfIfPresent(Nonnull<const CallableDeclaration*> decl,
       bool success =
           PatternMatch(placeholder, receiver, source_location, &method_scope,
                        generic_args, trace_stream_, this->arena_);
-      CARBON_CHECK(success) << "Failed to bind self";
+      CARBON_CHECK(success, "Failed to bind self");
     }
   } else {
     // Mutable self with `[addr self: Self*]`
@@ -1036,7 +1037,7 @@ void Interpreter::BindSelfIfPresent(Nonnull<const CallableDeclaration*> decl,
     }
     bool success = PatternMatch(self_pattern, v, source_location, &method_scope,
                                 generic_args, trace_stream_, this->arena_);
-    CARBON_CHECK(success) << "Failed to bind addr self";
+    CARBON_CHECK(success, "Failed to bind addr self");
   }
 }
 
@@ -1245,8 +1246,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
                        dyn_cast<TypeOfMemberName>(&access.static_type())) {
           // The result is a member name, such as in `Type.field_name`. Form a
           // suitable member name value.
-          CARBON_CHECK(phase() == Phase::CompileTime)
-              << "should not form MemberNames at runtime";
+          CARBON_CHECK(phase() == Phase::CompileTime,
+                       "should not form MemberNames at runtime");
           auto found_in_interface = access.found_in_interface();
           if (act.pos() == 1 && found_in_interface) {
             return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
@@ -1365,8 +1366,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
             return todo_.FinishAction(act.results().back());
           }
         } else if (forming_member_name) {
-          CARBON_CHECK(phase() == Phase::CompileTime)
-              << "should not form MemberNames at runtime";
+          CARBON_CHECK(phase() == Phase::CompileTime,
+                       "should not form MemberNames at runtime");
           if (auto found_in_interface = access.member().interface();
               found_in_interface && act.pos() == 1) {
             return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
@@ -1378,9 +1379,9 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
             if (found_in_interface) {
               found_in_interface = cast<InterfaceType>(act.results().back());
             }
-            CARBON_CHECK(!access.member().base_type().has_value())
-                << "compound member access forming a member name should be "
-                   "performing impl lookup";
+            CARBON_CHECK(!access.member().base_type().has_value(),
+                         "compound member access forming a member name should "
+                         "be performing impl lookup");
             auto* member_name =
                 arena_->New<MemberName>(act.results()[0], found_in_interface,
                                         &access.member().member());
@@ -1427,8 +1428,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
             if (access.impl().has_value()) {
               witness = cast<Witness>(act.results()[1]);
             } else {
-              CARBON_CHECK(access.member().base_type().has_value())
-                  << "compound access should have base type or impl";
+              CARBON_CHECK(access.member().base_type().has_value(),
+                           "compound access should have base type or impl");
               CARBON_ASSIGN_OR_RETURN(
                   object, Convert(object, *access.member().base_type(),
                                   exp.source_loc()));
@@ -1582,7 +1583,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
           return todo_.FinishAction(act.results()[function_call_pos]);
         }
       } else {
-        CARBON_FATAL() << "in StepValueExp with Call pos " << act.pos();
+        CARBON_FATAL("in StepValueExp with Call pos {0}", act.pos());
       }
     }
     case ExpressionKind::IntrinsicExpression: {
@@ -1622,7 +1623,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
               break;
             }
             default:
-              CARBON_FATAL() << "Too many format args: " << num_format_args;
+              CARBON_FATAL("Too many format args: {0}", num_format_args);
           }
           // Implicit newline; currently no way to disable it.
           *print_stream_ << "\n";
@@ -1712,9 +1713,9 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
           // reproducible across builds/platforms.
           int64_t r = (generator() % range) + low;
           CARBON_CHECK(r >= std::numeric_limits<int32_t>::min() &&
-                       r <= std::numeric_limits<int32_t>::max())
-              << "Non-int32 result: " << r;
-          CARBON_CHECK(r >= low && r <= high) << "Out-of-range result: " << r;
+                           r <= std::numeric_limits<int32_t>::max(),
+                       "Non-int32 result: {0}", r);
+          CARBON_CHECK(r >= low && r <= high, "Out-of-range result: {0}", r);
           return todo_.FinishAction(arena_->New<IntValue>(r));
         }
         case IntrinsicExpression::Intrinsic::ImplicitAs: {
@@ -1747,8 +1748,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
           return todo_.FinishAction(result);
         }
         case IntrinsicExpression::Intrinsic::ImplicitAsConvert: {
-          CARBON_FATAL()
-              << "__intrinsic_implicit_as_convert should have been rewritten";
+          CARBON_FATAL(
+              "__intrinsic_implicit_as_convert should have been rewritten");
         }
         case IntrinsicExpression::Intrinsic::IntEq: {
           CARBON_CHECK(args.size() == 2);
@@ -1891,7 +1892,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
     }
     case ExpressionKind::WhereExpression: {
       auto rewrite = cast<WhereExpression>(exp).rewritten_form();
-      CARBON_CHECK(rewrite) << "where expression should be rewritten";
+      CARBON_CHECK(rewrite, "where expression should be rewritten");
       return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
           *rewrite, act.preserve_nested_categories(), act.location_received()));
     }
@@ -1919,7 +1920,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
       }
     }
     case ExpressionKind::UnimplementedExpression:
-      CARBON_FATAL() << "Unimplemented: " << exp;
+      CARBON_FATAL("Unimplemented: {0}", exp);
   }  // switch (exp->kind)
 }
 
@@ -1982,7 +1983,7 @@ auto Interpreter::StepWitness() -> ErrorOr<Success> {
     }
 
     default:
-      CARBON_FATAL() << "unexpected kind of witness " << *witness;
+      CARBON_FATAL("unexpected kind of witness {0}", *witness);
   }
 }
 
@@ -2183,11 +2184,12 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
           const auto init_location = act.location_created();
           v = v_expr ? (*v_expr)->value() : result;
           if (expr_category == ExpressionCategory::Reference) {
-            CARBON_CHECK(v_expr) << "Expecting ReferenceExpressionValue from "
-                                    "reference expression";
+            CARBON_CHECK(
+                v_expr,
+                "Expecting ReferenceExpressionValue from reference expression");
             v_location = (*v_expr)->address();
-            CARBON_CHECK(v_location)
-                << "Expecting a valid address from reference expression";
+            CARBON_CHECK(v_location,
+                         "Expecting a valid address from reference expression");
           } else if (has_initializing_expr && init_location &&
                      heap_.is_initialized(*init_location)) {
             // Bind even if a conversion is necessary.
@@ -2228,9 +2230,10 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
               PatternMatch(p, ExpressionResult(v, v_location, expr_category),
                            stmt.source_loc(), &scope, generic_args,
                            trace_stream_, this->arena_);
-          CARBON_CHECK(matched)
-              << stmt.source_loc()
-              << ": internal error in variable definition, match failed";
+          CARBON_CHECK(
+              matched,
+              "{0}: internal error in variable definition, match failed",
+              stmt.source_loc());
         }
         todo_.MergeScope(std::move(scope));
         return todo_.FinishAction();
@@ -2435,9 +2438,8 @@ auto Interpreter::StepDestroy() -> ErrorOr<Success> {
           const Address var_addr =
               object.ElementAddress(arena_->New<NamedElement>(var));
           const auto v = heap_.Read(var_addr, var->source_loc());
-          CARBON_CHECK(v.ok())
-              << "Failed to read member `" << var->binding().name()
-              << "` from class `" << class_decl.name() << "`";
+          CARBON_CHECK(v.ok(), "Failed to read member `{0}` from class `{1}`",
+                       var->binding().name(), class_decl.name());
           return todo_.Spawn(std::make_unique<DestroyAction>(
               arena_->New<LocationValue>(var_addr), *v));
         } else {
@@ -2487,7 +2489,7 @@ auto Interpreter::StepDestroy() -> ErrorOr<Success> {
       todo_.Pop();
       return Success();
   }
-  CARBON_FATAL() << "Unreachable";
+  CARBON_FATAL("Unreachable");
 }
 
 auto Interpreter::StepCleanUp() -> ErrorOr<Success> {
@@ -2577,9 +2579,9 @@ auto Interpreter::Step() -> ErrorOr<Success> {
       CARBON_RETURN_IF_ERROR(StepInstantiateType());
       break;
     case Action::Kind::ScopeAction:
-      CARBON_FATAL() << "ScopeAction escaped ActionStack";
+      CARBON_FATAL("ScopeAction escaped ActionStack");
     case Action::Kind::RecursiveAction:
-      CARBON_FATAL() << "Tried to step a RecursiveAction";
+      CARBON_FATAL("Tried to step a RecursiveAction");
   }  // switch
   return Success();
 }

+ 1 - 1
explorer/interpreter/matching_impl_set.cpp

@@ -118,7 +118,7 @@ MatchingImplSet::Match::Match(Nonnull<MatchingImplSet*> parent,
 }
 
 MatchingImplSet::Match::~Match() {
-  CARBON_CHECK(parent_->matches_.back() == this) << "match stack broken";
+  CARBON_CHECK(parent_->matches_.back() == this, "match stack broken");
   parent_->matches_.pop_back();
 }
 

+ 12 - 13
explorer/interpreter/pattern_match.cpp

@@ -30,8 +30,8 @@ static auto InitializePlaceholderValue(const ValueNodeView& value_node,
       } else {
         // Location initialized by initializing expression, bind node to
         // address.
-        CARBON_CHECK(v.address())
-            << "Missing location from initializing expression";
+        CARBON_CHECK(v.address(),
+                     "Missing location from initializing expression");
         bindings->Bind(value_node, *v.address());
       }
       break;
@@ -41,19 +41,18 @@ static auto InitializePlaceholderValue(const ValueNodeView& value_node,
         bindings->BindValue(value_node, v.value());
       } else if (v.expression_category() == ExpressionCategory::Reference) {
         // Bind the reference expression value directly.
-        CARBON_CHECK(v.address())
-            << "Missing location from reference expression";
+        CARBON_CHECK(v.address(), "Missing location from reference expression");
         bindings->BindAndPin(value_node, *v.address());
       } else {
         // Location initialized by initializing expression, bind node to
         // address.
-        CARBON_CHECK(v.address())
-            << "Missing location from initializing expression";
+        CARBON_CHECK(v.address(),
+                     "Missing location from initializing expression");
         bindings->Bind(value_node, *v.address());
       }
       break;
     case ExpressionCategory::Initializing:
-      CARBON_FATAL() << "Cannot pattern match an initializing expression";
+      CARBON_FATAL("Cannot pattern match an initializing expression");
       break;
   }
 }
@@ -135,8 +134,8 @@ auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
           return true;
         }
         default:
-          CARBON_FATAL() << "expected a tuple value in pattern, not "
-                         << *v.value();
+          CARBON_FATAL("expected a tuple value in pattern, not {0}",
+                       *v.value());
       }
     case Value::Kind::StructValue: {
       const auto& p_struct = cast<StructValue>(*p);
@@ -172,12 +171,12 @@ auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
               source_loc, bindings, generic_args, trace_stream, arena);
         }
         default:
-          CARBON_FATAL() << "expected a choice alternative in pattern, not "
-                         << *v.value();
+          CARBON_FATAL("expected a choice alternative in pattern, not {0}",
+                       *v.value());
       }
     case Value::Kind::UninitializedValue:
-      CARBON_FATAL() << "uninitialized value is not allowed in pattern "
-                     << *v.value();
+      CARBON_FATAL("uninitialized value is not allowed in pattern {0}",
+                   *v.value());
     case Value::Kind::FunctionType:
       switch (v.value()->kind()) {
         case Value::Kind::FunctionType: {

+ 7 - 7
explorer/interpreter/resolve_names.cpp

@@ -325,8 +325,8 @@ auto NameResolver::ResolveNamesImpl(Expression& expression,
       }
       if (const auto* namespace_decl = dyn_cast<NamespaceDeclaration>(base)) {
         auto ns_it = namespace_scopes_.find(namespace_decl);
-        CARBON_CHECK(ns_it != namespace_scopes_.end())
-            << "name resolved to undeclared namespace";
+        CARBON_CHECK(ns_it != namespace_scopes_.end(),
+                     "name resolved to undeclared namespace");
         CARBON_ASSIGN_OR_RETURN(
             const auto value_node,
             ns_it->second.ResolveHere(scope, access.member_name(),
@@ -459,7 +459,7 @@ auto NameResolver::ResolveNamesImpl(Expression& expression,
     case ExpressionKind::ValueLiteral:
     case ExpressionKind::BuiltinConvertExpression:
     case ExpressionKind::BaseAccessExpression:
-      CARBON_FATAL() << "should not exist before type checking";
+      CARBON_FATAL("should not exist before type checking");
     case ExpressionKind::UnimplementedExpression:
       return ProgramError(expression.source_loc()) << "Unimplemented";
   }
@@ -603,9 +603,9 @@ auto NameResolver::ResolveNamesImpl(Statement& statement,
       }
       CARBON_RETURN_IF_ERROR(ResolveNames(def.pattern(), enclosing_scope));
       if (def.is_returned()) {
-        CARBON_CHECK(def.pattern().kind() == PatternKind::BindingPattern)
-            << def.pattern().source_loc()
-            << "returned var definition can only be a binding pattern";
+        CARBON_CHECK(def.pattern().kind() == PatternKind::BindingPattern,
+                     "{0}returned var definition can only be a binding pattern",
+                     def.pattern().source_loc());
         CARBON_RETURN_IF_ERROR(enclosing_scope.AddReturnedVar(
             ValueNodeView(&cast<BindingPattern>(def.pattern()))));
       }
@@ -924,7 +924,7 @@ auto NameResolver::ResolveNamesImpl(Declaration& declaration,
     }
 
     case DeclarationKind::SelfDeclaration: {
-      CARBON_FATAL() << "Unreachable: resolving names for `Self` declaration";
+      CARBON_FATAL("Unreachable: resolving names for `Self` declaration");
     }
 
     case DeclarationKind::AliasDeclaration: {

+ 2 - 2
explorer/interpreter/resolve_unformed.cpp

@@ -140,8 +140,8 @@ static auto ResolveUnformedImpl(Nonnull<TraceStream*> trace_stream,
     case ExpressionKind::OperatorExpression: {
       const auto& opt_exp = cast<OperatorExpression>(*expression);
       if (opt_exp.op() == Operator::AddressOf) {
-        CARBON_CHECK(opt_exp.arguments().size() == 1)
-            << "OperatorExpression with op & can only have 1 argument";
+        CARBON_CHECK(opt_exp.arguments().size() == 1,
+                     "OperatorExpression with op & can only have 1 argument");
         CARBON_RETURN_IF_ERROR(
             // When a variable is taken address of, defer the unformed check to
             // runtime. A more sound analysis can be implemented when a

+ 5 - 5
explorer/interpreter/stack.h

@@ -32,7 +32,7 @@ struct Stack {
   //
   // - Requires: !this->IsEmpty()
   auto Pop() -> T {
-    CARBON_CHECK(!empty()) << "Can't pop from empty stack.";
+    CARBON_CHECK(!empty(), "Can't pop from empty stack.");
     auto r = std::move(elements_.back());
     elements_.pop_back();
     return r;
@@ -42,9 +42,9 @@ struct Stack {
   //
   // - Requires: n >= 0 && n <= Count()
   void Pop(int n) {
-    CARBON_CHECK(n >= 0) << "Negative pop count disallowed.";
-    CARBON_CHECK(static_cast<size_t>(n) <= elements_.size())
-        << "Can only pop as many elements as stack has.";
+    CARBON_CHECK(n >= 0, "Negative pop count disallowed.");
+    CARBON_CHECK(static_cast<size_t>(n) <= elements_.size(),
+                 "Can only pop as many elements as stack has.");
     elements_.erase(elements_.end() - n, elements_.end());
   }
 
@@ -52,7 +52,7 @@ struct Stack {
   //
   // - Requires: !this->IsEmpty()
   auto Top() const -> const T& {
-    CARBON_CHECK(!empty()) << "Empty stack has no Top().";
+    CARBON_CHECK(!empty(), "Empty stack has no Top().");
     return elements_.back();
   }
 

+ 67 - 65
explorer/interpreter/type_checker.cpp

@@ -109,7 +109,7 @@ static auto ExpectCompleteType(SourceLocation source_loc,
     case Value::Kind::ParameterizedEntityName:
     case Value::Kind::MemberName:
     case Value::Kind::MixinPseudoType:
-      CARBON_FATAL() << "should not see non-type values";
+      CARBON_FATAL("should not see non-type values");
 
     case Value::Kind::IntType:
     case Value::Kind::BoolType:
@@ -195,7 +195,7 @@ static auto ExpectConcreteType(SourceLocation source_loc,
 // Returns whether `type` is a placeholder type, which is a second-class type
 // that cannot be the type of a binding but can be the type of an expression.
 static auto IsPlaceholderType(Nonnull<const Value*> type) -> bool {
-  CARBON_CHECK(IsType(type)) << "expected a type, but found " << *type;
+  CARBON_CHECK(IsType(type), "expected a type, but found {0}", *type);
   return isa<TypeOfParameterizedEntityName, TypeOfMemberName,
              TypeOfMixinPseudoType, TypeOfNamespaceName>(type);
 }
@@ -568,14 +568,14 @@ auto TypeChecker::BuildSubtypeConversion(Nonnull<Expression*> source,
   while (!TypeEqual(cur_class, dest_class, std::nullopt)) {
     const auto src = src_class->declaration().name();
     const auto base_class = cur_class->base();
-    CARBON_CHECK(base_class) << "Invalid subtyping conversion";
+    CARBON_CHECK(base_class, "Invalid subtyping conversion");
     auto* base_expr = arena_->New<BaseAccessExpression>(
         source->source_loc(), last_expr,
         arena_->New<BaseElement>(arena_->New<PointerType>(*base_class)));
     last_expr = base_expr;
     cur_class = *base_class;
   }
-  CARBON_CHECK(last_expr) << "Error, no conversion was needed";
+  CARBON_CHECK(last_expr, "Error, no conversion was needed");
   return last_expr;
 }
 
@@ -758,7 +758,7 @@ auto TypeChecker::BuildBuiltinConversion(Nonnull<Expression*> source,
       return conversion_failed();
   }
 
-  CARBON_FATAL() << "unreachable";
+  CARBON_FATAL("unreachable");
 }
 
 auto TypeChecker::ImplicitlyConvert(std::string_view context,
@@ -892,8 +892,8 @@ auto TypeChecker::IsIntrinsicConstraintSatisfied(
   // TODO: Check to see if this constraint is known in the current impl scope.
   switch (constraint.kind) {
     case IntrinsicConstraint::ImplicitAs:
-      CARBON_CHECK(constraint.arguments.size() == 1)
-          << "wrong number of arguments for `__intrinsic_implicit_as`";
+      CARBON_CHECK(constraint.arguments.size() == 1,
+                   "wrong number of arguments for `__intrinsic_implicit_as`");
       CARBON_ASSIGN_OR_RETURN(
           bool convertible,
           IsBuiltinConversion(source_loc, constraint.type,
@@ -1007,7 +1007,7 @@ auto TypeChecker::ExpectNonPlaceholderType(SourceLocation source_loc,
     return ProgramError(source_loc)
            << "expected `.member_name` after name of " << *namespace_type;
   }
-  CARBON_FATAL() << "unknown kind of placeholder type " << *type;
+  CARBON_FATAL("unknown kind of placeholder type {0}", *type);
 }
 
 // Argument deduction matches two values and attempts to find a set of
@@ -1204,8 +1204,9 @@ auto TypeChecker::ArgumentDeduction::Deduce(Nonnull<const Value*> param,
               return diagnose_missing_field(param_struct, arg_field, false);
             }
           }
-          CARBON_FATAL() << "field count mismatch but no missing field; "
-                         << "duplicate field name?";
+          CARBON_FATAL(
+              "field count mismatch but no missing field; duplicate field "
+              "name?");
         }
       } else {
         for (const auto [param_field, arg_field] :
@@ -1350,7 +1351,7 @@ auto TypeChecker::ArgumentDeduction::Deduce(Nonnull<const Value*> param,
     }
     case Value::Kind::MixinPseudoType:
     case Value::Kind::TypeOfMixinPseudoType:
-      CARBON_CHECK(false) << "Type expression must not contain Mixin types";
+      CARBON_CHECK(false, "Type expression must not contain Mixin types");
   }
 }
 
@@ -2515,13 +2516,13 @@ auto TypeChecker::DeduceCallBindings(
                            /*allow_implicit_conversion=*/true));
     }
   }
-  CARBON_CHECK(generic_params.empty())
-      << "did not find all generic parameters in parameter list";
+  CARBON_CHECK(generic_params.empty(),
+               "did not find all generic parameters in parameter list");
 
   CARBON_ASSIGN_OR_RETURN(
       std::optional<Bindings> bindings,
       deduction.Finish(*this, impl_scope, /*diagnose_deduction_failure=*/true));
-  CARBON_CHECK(bindings) << "should have diagnosed deduction failure";
+  CARBON_CHECK(bindings, "should have diagnosed deduction failure");
   call.set_bindings(std::move(*bindings));
 
   // Convert the arguments to the deduced and substituted parameter type.
@@ -2791,8 +2792,9 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
     case ExpressionKind::ValueLiteral:
     case ExpressionKind::BuiltinConvertExpression:
     case ExpressionKind::BaseAccessExpression:
-      CARBON_FATAL() << "attempting to type check node " << *e
-                     << " generated during type checking";
+      CARBON_FATAL(
+          "attempting to type check node {0} generated during type checking",
+          *e);
     case ExpressionKind::IndexExpression: {
       auto& index = cast<IndexExpression>(*e);
       CARBON_RETURN_IF_ERROR(TypeCheckExp(&index.object(), impl_scope));
@@ -2943,8 +2945,8 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
                 return ProgramError(access.source_loc())
                        << "Member access to aliases is not yet supported.";
               default:
-                CARBON_FATAL() << "member " << access.member_name()
-                               << " is not a field or method";
+                CARBON_FATAL("member {0} is not a field or method",
+                             access.member_name());
                 break;
             }
             return Success();
@@ -3344,8 +3346,8 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
             // This should not be possible: the name of a static member
             // function should have function type not member name type.
             CARBON_CHECK(!has_instance || is_instance_member ||
-                         !member_name.base_type().has_value())
-                << "vacuous compound member access";
+                             !member_name.base_type().has_value(),
+                         "vacuous compound member access");
             // If this is instance access, remove self bound from function type
             if (has_instance && is_instance_member) {
               CARBON_RETURN_IF_ERROR(set_static_type_remove_self());
@@ -3365,8 +3367,7 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
           access.set_expression_category(access.object().expression_category());
           return Success();
         default:
-          CARBON_FATAL() << "member " << member_name
-                         << " is not a field or method";
+          CARBON_FATAL("member {0} is not a field or method", member_name);
           break;
       }
 
@@ -3674,10 +3675,11 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
 
           // Currently the only kinds of parameterized entities we support are
           // types.
-          CARBON_CHECK(
-              isa<ClassDeclaration, InterfaceDeclaration, ConstraintDeclaration,
-                  ChoiceDeclaration>(param_name.declaration()))
-              << "unknown type of ParameterizedEntityName for " << param_name;
+          CARBON_CHECK((isa<ClassDeclaration, InterfaceDeclaration,
+                            ConstraintDeclaration, ChoiceDeclaration>(
+                           param_name.declaration())),
+                       "unknown type of ParameterizedEntityName for {0}",
+                       param_name);
           call.set_static_type(arena_->New<TypeType>());
           call.set_expression_category(ExpressionCategory::Value);
           return Success();
@@ -4109,7 +4111,7 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
       return Success();
     }
     case ExpressionKind::UnimplementedExpression:
-      CARBON_FATAL() << "Unimplemented: " << *e;
+      CARBON_FATAL("Unimplemented: {0}", *e);
     case ExpressionKind::ArrayTypeLiteral: {
       auto& array_literal = cast<ArrayTypeLiteral>(*e);
       CARBON_ASSIGN_OR_RETURN(
@@ -4200,18 +4202,18 @@ auto TypeChecker::TypeCheckTypeExp(Nonnull<Expression*> type_expression,
                         arena_->New<TypeType>()));
   CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
                           InterpExp(type_expression));
-  CARBON_CHECK(IsType(type))
-      << "type expression did not produce a type, got " << *type;
+  CARBON_CHECK(IsType(type), "type expression did not produce a type, got {0}",
+               *type);
   if (concrete) {
     if (TypeIsDeduceable(type)) {
       return ProgramError(type_expression->source_loc())
              << "`auto` is not permitted in this context";
     }
-    CARBON_CHECK(IsNonDeduceableType(type))
-        << "unknown kind of non-concrete type " << *type;
+    CARBON_CHECK(IsNonDeduceableType(type),
+                 "unknown kind of non-concrete type {0}", *type);
   }
-  CARBON_CHECK(!IsPlaceholderType(type))
-      << "should be no way to write a placeholder type";
+  CARBON_CHECK(!IsPlaceholderType(type),
+               "should be no way to write a placeholder type");
   return type;
 }
 
@@ -4325,9 +4327,10 @@ auto TypeChecker::TypeCheckPattern(
                               arena_->New<TypeType>()));
         CARBON_ASSIGN_OR_RETURN(type, InterpExp(converted));
       }
-      CARBON_CHECK(IsType(type))
-          << "conversion to type succeeded but didn't produce a type, got "
-          << *type;
+      CARBON_CHECK(
+          IsType(type),
+          "conversion to type succeeded but didn't produce a type, got {0}",
+          *type);
       if (expected) {
         // TODO: Per proposal #2188, we should be performing conversions at
         // this level rather than on the overall initializer.
@@ -4348,11 +4351,11 @@ auto TypeChecker::TypeCheckPattern(
         CARBON_RETURN_IF_ERROR(ExpectResolvedBindingType(binding, type));
       }
 
-      CARBON_CHECK(IsNonDeduceableType(type))
-          << "did not resolve " << binding << " to concrete type, got "
-          << *type;
-      CARBON_CHECK(!IsPlaceholderType(type))
-          << "should be no way to write a placeholder type";
+      CARBON_CHECK(IsNonDeduceableType(type),
+                   "did not resolve {0} to concrete type, got {1}", binding,
+                   *type);
+      CARBON_CHECK(!IsPlaceholderType(type),
+                   "should be no way to write a placeholder type");
       binding.set_static_type(type);
       binding.set_value(binding.name() != AnonymousName
                             ? arena_->New<BindingPlaceholderValue>(&binding)
@@ -4897,7 +4900,7 @@ auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
                                              const ScopeInfo& scope_info)
     -> ErrorOr<Success> {
   const auto name = GetName(*f);
-  CARBON_CHECK(name) << "Unexpected missing name for `" << *f << "`.";
+  CARBON_CHECK(name, "Unexpected missing name for `{0}`.", *f);
   if (trace_stream_->is_enabled()) {
     trace_stream_->Start() << "declaring function `" << *name << "` ("
                            << f->source_loc() << ")\n";
@@ -4997,7 +5000,7 @@ auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
           arena_->New<DestructorValue>(cast<DestructorDeclaration>(f)));
       break;
     default:
-      CARBON_FATAL() << "f is not a callable declaration";
+      CARBON_FATAL("f is not a callable declaration");
   }
 
   if (name == "Main") {
@@ -5027,7 +5030,7 @@ auto TypeChecker::TypeCheckCallableDeclaration(Nonnull<CallableDeclaration*> f,
                                                const ImplScope& impl_scope)
     -> ErrorOr<Success> {
   auto name = GetName(*f);
-  CARBON_CHECK(name) << "Unexpected missing name for `" << *f << "`.";
+  CARBON_CHECK(name, "Unexpected missing name for `{0}`.", *f);
   if (trace_stream_->is_enabled()) {
     trace_stream_->Start() << "checking function `" << *name << "` ("
                            << f->source_loc() << ")\n";
@@ -5165,8 +5168,8 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
              << "Error declaring `" << fun->name() << "`"
              << ": class functions cannot be virtual.";
     }
-    CARBON_CHECK(!fun->name().is_qualified())
-        << "qualified function name not permitted in class scope";
+    CARBON_CHECK(!fun->name().is_qualified(),
+                 "qualified function name not permitted in class scope");
 
     if (fun->virt_override() == VirtualOverride::Abstract &&
         fun->body().has_value()) {
@@ -5312,8 +5315,8 @@ auto TypeChecker::TypeCheckClassDeclaration(
   }
   auto [it, inserted] =
       collected_members_.insert({class_decl, CollectedMembersMap()});
-  CARBON_CHECK(inserted) << "Adding class " << class_decl->name()
-                         << " to collected_members_ must not fail";
+  CARBON_CHECK(inserted, "Adding class {0} to collected_members_ must not fail",
+               class_decl->name());
   for (Nonnull<Declaration*> m : class_decl->members()) {
     CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, class_scope, class_decl));
     CARBON_RETURN_IF_ERROR(CollectMember(class_decl, m));
@@ -5459,8 +5462,8 @@ auto TypeChecker::DeclareConstraintTypeDeclaration(
     Nonnull<ConstraintTypeDeclaration*> constraint_decl,
     const ScopeInfo& scope_info) -> ErrorOr<Success> {
   CARBON_CHECK(
-      isa<InterfaceDeclaration, ConstraintDeclaration>(constraint_decl))
-      << "unexpected kind of constraint type declaration";
+      (isa<InterfaceDeclaration, ConstraintDeclaration>(constraint_decl)),
+      "unexpected kind of constraint type declaration");
   bool is_interface = isa<InterfaceDeclaration>(constraint_decl);
 
   if (trace_stream_->is_enabled()) {
@@ -5635,9 +5638,8 @@ auto TypeChecker::DeclareConstraintTypeDeclaration(
       }
 
       default: {
-        CARBON_FATAL()
-            << "unexpected declaration in constraint type declaration:\n"
-            << *m;
+        CARBON_FATAL(
+            "unexpected declaration in constraint type declaration:\n{0}", *m);
         break;
       }
     }
@@ -5728,7 +5730,7 @@ auto TypeChecker::CheckImplIsComplete(Nonnull<const InterfaceType*> iface_type,
     } else {
       // Every member function must be declared.
       std::optional<std::string_view> mem_name = GetName(*m);
-      CARBON_CHECK(mem_name.has_value()) << "unnamed interface member " << *m;
+      CARBON_CHECK(mem_name.has_value(), "unnamed interface member {0}", *m);
 
       std::optional<Nonnull<const Declaration*>> mem =
           FindMember(*mem_name, impl_decl->members());
@@ -6129,11 +6131,11 @@ static auto IsValidTypeForAliasTarget(Nonnull<const Value*> type) -> bool {
     case Value::Kind::AlternativeConstructorValue:
     case Value::Kind::StringValue:
     case Value::Kind::UninitializedValue:
-      CARBON_FATAL() << "type of alias target is not a type: " << *type;
+      CARBON_FATAL("type of alias target is not a type: {0}", *type);
 
     case Value::Kind::AutoType:
     case Value::Kind::VariableType:
-      CARBON_FATAL() << "pattern type in alias target: " << *type;
+      CARBON_FATAL("pattern type in alias target: {0}", *type);
 
     case Value::Kind::IntType:
     case Value::Kind::BoolType:
@@ -6291,7 +6293,7 @@ auto TypeChecker::TypeCheckDeclaration(
       break;
     }
     case DeclarationKind::SelfDeclaration: {
-      CARBON_FATAL() << "Unreachable TypeChecker `Self` declaration";
+      CARBON_FATAL("Unreachable TypeChecker `Self` declaration");
     }
     case DeclarationKind::AliasDeclaration: {
       break;
@@ -6418,7 +6420,7 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
     }
 
     case DeclarationKind::SelfDeclaration: {
-      CARBON_FATAL() << "Unreachable TypeChecker declare `Self` declaration";
+      CARBON_FATAL("Unreachable TypeChecker declare `Self` declaration");
     }
 
     case DeclarationKind::AliasDeclaration: {
@@ -6490,8 +6492,8 @@ auto TypeChecker::CollectMember(Nonnull<const Declaration*> enclosing_decl,
                                 Nonnull<const Declaration*> member_decl)
     -> ErrorOr<Success> {
   CARBON_CHECK(isa<MixinDeclaration>(enclosing_decl) ||
-               isa<ClassDeclaration>(enclosing_decl))
-      << "Can't collect members for " << *enclosing_decl;
+                   isa<ClassDeclaration>(enclosing_decl),
+               "Can't collect members for {0}", *enclosing_decl);
   auto member_name = GetName(*member_decl);
   if (!member_name.has_value()) {
     // No need to collect members without a name
@@ -6532,7 +6534,7 @@ auto TypeChecker::FindCollectedMembers(Nonnull<const Declaration*> decl)
       return it->second;
     }
     default:
-      CARBON_FATAL() << "Can't collect members for " << *decl;
+      CARBON_FATAL("Can't collect members for {0}", *decl);
   }
 }
 
@@ -6585,8 +6587,8 @@ auto TypeChecker::InstantiateImplDeclaration(
         std::optional<Nonnull<const Value*>> witness;
         if (auto impl = param->impl_binding()) {
           auto it = bindings->witnesses().find(*impl);
-          CARBON_CHECK(it != bindings->witnesses().end())
-              << "no witness for generic binding";
+          CARBON_CHECK(it != bindings->witnesses().end(),
+                       "no witness for generic binding");
           witness = it->second;
         }
         new_bindings.Add(clone, value, witness);
@@ -6604,8 +6606,8 @@ auto TypeChecker::InstantiateImplDeclaration(
   // TODO: It's probably not correct to use the top-level impl scope here. It's
   // not obvious what we should use, though -- which impls are in scope in
   // template instantiation?
-  CARBON_CHECK(top_level_impl_scope_)
-      << "can't perform template instantiation with no top-level scope";
+  CARBON_CHECK(top_level_impl_scope_,
+               "can't perform template instantiation with no top-level scope");
   ImplScope scope(*top_level_impl_scope_);
 
   // Bring all impls from any checked generic bindings in the template

+ 2 - 2
explorer/interpreter/type_utils.cpp

@@ -81,7 +81,7 @@ auto IsType(Nonnull<const Value*> value) -> bool {
 }
 
 auto TypeIsDeduceable(Nonnull<const Value*> type) -> bool {
-  CARBON_CHECK(IsType(type)) << "expected a type, but found " << *type;
+  CARBON_CHECK(IsType(type), "expected a type, but found {0}", *type);
 
   switch (type->kind()) {
     case Value::Kind::IntValue:
@@ -108,7 +108,7 @@ auto TypeIsDeduceable(Nonnull<const Value*> type) -> bool {
     case Value::Kind::ParameterizedEntityName:
     case Value::Kind::MemberName:
     case Value::Kind::MixinPseudoType:
-      CARBON_FATAL() << "non-type value";
+      CARBON_FATAL("non-type value");
     case Value::Kind::IntType:
     case Value::Kind::BoolType:
     case Value::Kind::TypeType:

+ 2 - 2
explorer/syntax/parse.cpp

@@ -36,8 +36,8 @@ static auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
   }
 
   // Return parse results.
-  CARBON_CHECK(ast != std::nullopt)
-      << "parser validated syntax yet didn't produce an AST.";
+  CARBON_CHECK(ast != std::nullopt,
+               "parser validated syntax yet didn't produce an AST.");
   return *ast;
 }
 

+ 1 - 2
explorer/syntax/prelude.cpp

@@ -19,8 +19,7 @@ void AddPrelude(llvm::vfs::FileSystem& fs, std::string_view prelude_file_name,
     // Try again with tracing, to help diagnose the problem.
     ErrorOr<AST> trace_parse_result =
         Parse(fs, arena, prelude_file_name, FileKind::Prelude, true);
-    CARBON_FATAL() << "Failed to parse prelude:\n"
-                   << trace_parse_result.error();
+    CARBON_FATAL("Failed to parse prelude:\n{0}", trace_parse_result.error());
   }
   const auto& prelude = *parse_result;
   declarations->insert(declarations->begin(), prelude.declarations.begin(),

+ 2 - 2
migrate_cpp/rewriter_test.cpp

@@ -27,8 +27,8 @@ class Annotations {
     }
     start_ = index;
     end_ = annotated_source.find("]]$", index);
-    CARBON_CHECK(end_ != llvm::StringRef::npos)
-        << "Found `$[[` but no matching `]]$`";
+    CARBON_CHECK(end_ != llvm::StringRef::npos,
+                 "Found `$[[` but no matching `]]$`");
     source_code_ = (llvm::Twine(annotated_source.substr(0, start_)) +
                     annotated_source.substr(start_ + 3, end_ - start_ - 3) +
                     annotated_source.substr(end_ + 3))

+ 4 - 3
testing/base/global_exe_path.cpp

@@ -14,13 +14,14 @@ static constinit std::optional<std::string> exe_path = {};
 namespace Carbon::Testing {
 
 auto GetExePath() -> llvm::StringRef {
-  CARBON_CHECK(exe_path)
-      << "Must not query the executable path until after it has been set!";
+  CARBON_CHECK(
+      exe_path,
+      "Must not query the executable path until after it has been set!");
   return *exe_path;
 }
 
 auto SetExePath(const char* argv_zero) -> void {
-  CARBON_CHECK(!exe_path) << "Must not call `SetExePath` more than once!";
+  CARBON_CHECK(!exe_path, "Must not call `SetExePath` more than once!");
   exe_path.emplace(Carbon::FindExecutablePath(argv_zero));
 }
 

+ 21 - 21
testing/base/source_gen.cpp

@@ -152,10 +152,11 @@ auto SourceGen::ClassGenState::GetValidTypeName() -> llvm::StringRef {
       return type_names_.pop_back_val();
     }
 
-    CARBON_CHECK(last_type_name_index_ != initial_last_type_name_index)
-        << "Failed to find a valid type name with " << type_names_.size()
-        << " candidates, an initial index of " << initial_last_type_name_index
-        << ", and with " << class_names_.size() << " classes left to emit!";
+    CARBON_CHECK(last_type_name_index_ != initial_last_type_name_index,
+                 "Failed to find a valid type name with {0} candidates, an "
+                 "initial index of {1}, and with {2} classes left to emit!",
+                 type_names_.size(), initial_last_type_name_index,
+                 class_names_.size());
   }
 }
 
@@ -319,8 +320,8 @@ auto SourceGen::GenAPIFileDenseDecls(int target_lines,
   // needs a blank line.
   constexpr int NumFileCommentLines = 4;
   double avg_class_lines = EstimateAvgClassDefLines(params.class_params);
-  CARBON_CHECK(target_lines > NumFileCommentLines + avg_class_lines)
-      << "Not enough target lines to generate a single class!";
+  CARBON_CHECK(target_lines > NumFileCommentLines + avg_class_lines,
+               "Not enough target lines to generate a single class!");
   int num_classes = static_cast<double>(target_lines - NumFileCommentLines) /
                     (avg_class_lines + 1);
   int expected_lines =
@@ -372,9 +373,9 @@ auto SourceGen::GetShuffledIdentifiers(int number, int min_length,
 auto SourceGen::GetShuffledUniqueIdentifiers(int number, int min_length,
                                              int max_length, bool uniform)
     -> llvm::SmallVector<llvm::StringRef> {
-  CARBON_CHECK(min_length >= 4)
-      << "Cannot trivially guarantee enough distinct, unique identifiers for "
-         "lengths <= 3";
+  CARBON_CHECK(min_length >= 4,
+               "Cannot trivially guarantee enough distinct, unique identifiers "
+               "for lengths <= 3");
   llvm::SmallVector<llvm::StringRef> idents =
       GetUniqueIdentifiers(number, min_length, max_length, uniform);
   std::shuffle(idents.begin(), idents.end(), rng_);
@@ -398,9 +399,9 @@ auto SourceGen::GetIdentifiers(int number, int min_length, int max_length,
 auto SourceGen::GetUniqueIdentifiers(int number, int min_length, int max_length,
                                      bool uniform)
     -> llvm::SmallVector<llvm::StringRef> {
-  CARBON_CHECK(min_length >= 4)
-      << "Cannot trivially guarantee enough distinct, unique identifiers for "
-         "lengths <= 3";
+  CARBON_CHECK(min_length >= 4,
+               "Cannot trivially guarantee enough distinct, unique identifiers "
+               "for lengths <= 3");
   llvm::SmallVector<llvm::StringRef> idents =
       GetIdentifiersImpl(number, min_length, max_length, uniform,
                          [this](int length, int length_count,
@@ -634,10 +635,10 @@ auto SourceGen::GetIdentifiersImpl(int number, int min_length, int max_length,
                                    llvm::function_ref<AppendFn> append)
     -> llvm::SmallVector<llvm::StringRef> {
   CARBON_CHECK(min_length <= max_length);
-  CARBON_CHECK(uniform || max_length <= 64)
-      << "Cannot produce a meaningful non-uniform distribution of lengths "
-         "longer than 64 as those are exceedingly rare in our observed data "
-         "sets.";
+  CARBON_CHECK(
+      uniform || max_length <= 64,
+      "Cannot produce a meaningful non-uniform distribution of lengths longer "
+      "than 64 as those are exceedingly rare in our observed data sets.");
 
   llvm::SmallVector<llvm::StringRef> idents;
   idents.reserve(number);
@@ -667,11 +668,10 @@ auto SourceGen::GetIdentifiersImpl(int number, int min_length, int max_length,
     }
     append(length, length_count, idents);
   }
-  CARBON_CHECK(number_rem == 0)
-      << "Unexpected number remaining: " << number_rem;
-  CARBON_CHECK(static_cast<int>(idents.size()) == number)
-      << "Ended up with " << idents.size()
-      << " identifiers instead of the requested " << number;
+  CARBON_CHECK(number_rem == 0, "Unexpected number remaining: {0}", number_rem);
+  CARBON_CHECK(static_cast<int>(idents.size()) == number,
+               "Ended up with {0} identifiers instead of the requested {1}",
+               idents.size(), number);
 
   return idents;
 }

+ 13 - 13
testing/file_test/autoupdate.cpp

@@ -21,8 +21,8 @@ static auto ParseLineNumber(absl::string_view matched_line_number) -> int {
   trimmed = trimmed.trim();
   // NOLINTNEXTLINE(google-runtime-int): API requirement.
   long long val;
-  CARBON_CHECK(!llvm::getAsSignedInteger(trimmed, 10, val))
-      << matched_line_number;
+  CARBON_CHECK(!llvm::getAsSignedInteger(trimmed, 10, val), "{0}",
+               matched_line_number);
   return val;
 }
 
@@ -60,7 +60,7 @@ auto FileTestAutoupdater::CheckLine::RemapLineNumbers(
       RE2::PartialMatch(line_cursor, *replacement_->re, &matched_line_number);
     }
     if (matched_line_number.empty()) {
-      CARBON_CHECK(found_one) << line_;
+      CARBON_CHECK(found_one, "{0}", line_);
       return;
     }
     found_one = true;
@@ -195,9 +195,9 @@ auto FileTestAutoupdater::BuildCheckLines(llvm::StringRef output,
       absl::string_view filename;
       if (RE2::PartialMatch(line, *default_file_re_, &filename)) {
         auto it = file_to_number_map.find(filename);
-        CARBON_CHECK(it != file_to_number_map.end())
-            << "default_file_re had unexpected match in '" << line << "' (`"
-            << default_file_re_->pattern() << "`)";
+        CARBON_CHECK(it != file_to_number_map.end(),
+                     "default_file_re had unexpected match in '{0}' (`{1}`)",
+                     line, default_file_re_->pattern());
         default_file_number = it->second;
       }
     }
@@ -218,7 +218,7 @@ auto FileTestAutoupdater::AddRemappedNonCheckLine() -> void {
 }
 
 auto FileTestAutoupdater::AddTips() -> void {
-  CARBON_CHECK(tips_.empty()) << "Should only add tips once";
+  CARBON_CHECK(tips_.empty(), "Should only add tips once");
 
   tips_.reserve(4);
   // This puts commands on a single line so that they can be easily copied.
@@ -278,12 +278,12 @@ auto FileTestAutoupdater::StartSplitFile() -> void {
   // Advance the file.
   ++output_file_number_;
   output_line_number_ = 0;
-  CARBON_CHECK(output_file_number_ == non_check_line_->file_number())
-      << "Non-sequential file: " << non_check_line_->file_number();
+  CARBON_CHECK(output_file_number_ == non_check_line_->file_number(),
+               "Non-sequential file: {0}", non_check_line_->file_number());
 
   // Each following file has precisely one split line.
-  CARBON_CHECK(non_check_line_->line_number() < 1)
-      << "Expected a split line, got " << *non_check_line_;
+  CARBON_CHECK(non_check_line_->line_number() < 1,
+               "Expected a split line, got {0}", *non_check_line_);
   // The split line is ignored when calculating line counts.
   new_lines_.push_back(non_check_line_);
 
@@ -300,8 +300,8 @@ auto FileTestAutoupdater::Run(bool dry_run) -> bool {
   // Print everything until the autoupdate line.
   while (non_check_line_->line_number() != autoupdate_line_number_) {
     CARBON_CHECK(non_check_line_ != non_check_lines_.end() &&
-                 non_check_line_->file_number() == 0)
-        << "Missed autoupdate?";
+                     non_check_line_->file_number() == 0,
+                 "Missed autoupdate?");
     AddRemappedNonCheckLine();
     ++non_check_line_;
   }

+ 6 - 5
testing/file_test/autoupdate.h

@@ -62,11 +62,12 @@ class FileTestAutoupdater {
             [&](const CheckLine& line) { return line.line_number() != -1; })),
         non_check_line_(non_check_lines_.begin()) {
     for (const auto& replacement : line_number_replacements_) {
-      CARBON_CHECK(replacement.has_file || default_file_re_)
-          << "For replacement with pattern `" << replacement.re->pattern()
-          << "` to have has_file=false, override GetDefaultFileRE.";
-      CARBON_CHECK(replacement.re->ok())
-          << "Invalid line replacement RE2: " << replacement.re->error();
+      CARBON_CHECK(replacement.has_file || default_file_re_,
+                   "For replacement with pattern `{0}` to have has_file=false, "
+                   "override GetDefaultFileRE.",
+                   replacement.re->pattern());
+      CARBON_CHECK(replacement.re->ok(), "Invalid line replacement RE2: {0}",
+                   replacement.re->error());
     }
   }
 

+ 3 - 3
testing/file_test/file_test_base.cpp

@@ -900,10 +900,10 @@ static auto GetTests() -> llvm::SmallVector<std::string> {
   }
 
   // Extracts tests from the target file.
-  CARBON_CHECK(!absl::GetFlag(FLAGS_test_targets_file).empty())
-      << "Missing --test_targets_file.";
+  CARBON_CHECK(!absl::GetFlag(FLAGS_test_targets_file).empty(),
+               "Missing --test_targets_file.");
   auto content = ReadFile(absl::GetFlag(FLAGS_test_targets_file));
-  CARBON_CHECK(content.ok()) << content.error();
+  CARBON_CHECK(content.ok(), "{0}", content.error());
   llvm::SmallVector<std::string> all_tests;
   for (llvm::StringRef file_ref : llvm::split(*content, "\n")) {
     if (file_ref.empty()) {

+ 3 - 3
toolchain/base/value_store.h

@@ -151,7 +151,7 @@ class ValueStore
   // Stores the value and returns an ID to reference it.
   auto Add(ValueType value) -> IdT {
     IdT id(values_.size());
-    CARBON_CHECK(id.index >= 0) << "Id overflow";
+    CARBON_CHECK(id.index >= 0, "Id overflow");
     values_.push_back(std::move(value));
     return id;
   }
@@ -165,13 +165,13 @@ class ValueStore
 
   // Returns a mutable value for an ID.
   auto Get(IdT id) -> RefType {
-    CARBON_DCHECK(id.index >= 0) << id;
+    CARBON_DCHECK(id.index >= 0, "{0}", id);
     return values_[id.index];
   }
 
   // Returns the value for an ID.
   auto Get(IdT id) const -> ConstRefType {
-    CARBON_DCHECK(id.index >= 0) << id;
+    CARBON_DCHECK(id.index >= 0, "{0}", id);
     return values_[id.index];
   }
 

+ 1 - 1
toolchain/base/yaml.h

@@ -94,7 +94,7 @@ struct llvm::yaml::ScalarTraits<Carbon::Yaml::OutputScalar> {
   }
   static auto input(StringRef /*scalar*/, void* /*ctxt*/,
                     Carbon::Yaml::OutputScalar& /*value*/) -> StringRef {
-    CARBON_FATAL() << "Input is unsupported.";
+    CARBON_FATAL("Input is unsupported.");
   }
   static auto mustQuote(StringRef /*value*/) -> QuotingType {
     return QuotingType::None;

+ 19 - 20
toolchain/check/check.cpp

@@ -555,8 +555,8 @@ class DeferredDefinitionWorklist {
 
   // CHECK that the work list has no further work.
   auto VerifyEmpty() {
-    CARBON_CHECK(worklist_.empty() && entered_scopes_.empty())
-        << "Tasks left behind on worklist.";
+    CARBON_CHECK(worklist_.empty() && entered_scopes_.empty(),
+                 "Tasks left behind on worklist.");
   }
 
  private:
@@ -627,8 +627,8 @@ auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context)
   // worklist. We stay in that scope rather than suspending then immediately
   // resuming it.
   CARBON_CHECK(
-      holds_alternative<EnterDeferredDefinitionScope>(worklist_.back()))
-      << "Unexpected task in worklist.";
+      holds_alternative<EnterDeferredDefinitionScope>(worklist_.back()),
+      "Unexpected task in worklist.");
   worklist_.pop_back();
   CARBON_VLOG("{0}Handle EnterDeferredDefinitionScope (non-nested)\n",
               VlogPrefix);
@@ -690,8 +690,8 @@ class NodeIdTraversal {
   auto PerformTask(
       DeferredDefinitionWorklist::EnterDeferredDefinitionScope&& enter)
       -> void {
-    CARBON_CHECK(enter.suspended_name)
-        << "Entering a scope with no suspension information.";
+    CARBON_CHECK(enter.suspended_name,
+                 "Entering a scope with no suspension information.");
     context_.decl_name_stack().Restore(std::move(*enter.suspended_name));
   }
 
@@ -815,12 +815,10 @@ static auto DiagnoseMissingDefinitions(Context& context,
       case SemIR::InterfaceDecl::Kind: {
         // TODO: handle `interface` as well, once we can test it without
         // triggering https://github.com/carbon-language/carbon-lang/issues/4071
-        CARBON_FATAL()
-            << "TODO: Support interfaces in DiagnoseMissingDefinitions";
+        CARBON_FATAL("TODO: Support interfaces in DiagnoseMissingDefinitions");
       }
       default: {
-        CARBON_FATAL() << "Unexpected inst in definitions_required: "
-                       << decl_inst;
+        CARBON_FATAL("Unexpected inst in definitions_required: {0}", decl_inst);
       }
     }
   }
@@ -851,14 +849,15 @@ static auto ProcessNodeIds(Context& context, llvm::raw_ostream* vlog_stream,
     auto parse_kind = context.parse_tree().node_kind(node_id);
 
     switch (parse_kind) {
-#define CARBON_PARSE_NODE_KIND(Name)                                         \
-  case Parse::NodeKind::Name: {                                              \
-    if (!HandleParseNode(context, Parse::Name##Id(node_id))) {               \
-      CARBON_CHECK(err_tracker.seen_error())                                 \
-          << "Handle" #Name " returned false without printing a diagnostic"; \
-      return false;                                                          \
-    }                                                                        \
-    break;                                                                   \
+#define CARBON_PARSE_NODE_KIND(Name)                                 \
+  case Parse::NodeKind::Name: {                                      \
+    if (!HandleParseNode(context, Parse::Name##Id(node_id))) {       \
+      CARBON_CHECK(err_tracker.seen_error(),                         \
+                   "Handle" #Name                                    \
+                   " returned false without printing a diagnostic"); \
+      return false;                                                  \
+    }                                                                \
+    break;                                                           \
   }
 #include "toolchain/parse/node_kind.def"
     }
@@ -919,8 +918,8 @@ static auto CheckParseTree(
 
 #ifndef NDEBUG
   if (auto verify = sem_ir.Verify(); !verify.ok()) {
-    CARBON_FATAL() << sem_ir << "Built invalid semantics IR: " << verify.error()
-                   << "\n";
+    CARBON_FATAL("{0}Built invalid semantics IR: {1}\n", sem_ir,
+                 verify.error());
   }
 #endif
 }

+ 1 - 1
toolchain/check/check_fuzzer.cpp

@@ -15,7 +15,7 @@ static const InstallPaths* install_paths = nullptr;
 
 // NOLINTNEXTLINE(readability-non-const-parameter): External API required types.
 extern "C" auto LLVMFuzzerInitialize(int* argc, char*** argv) -> int {
-  CARBON_CHECK(*argc >= 1) << "Need the `argv[0]` value to initialize!";
+  CARBON_CHECK(*argc >= 1, "Need the `argv[0]` value to initialize!");
   install_paths = new InstallPaths(
       InstallPaths::MakeForBazelRunfiles(FindExecutablePath((*argv)[0])));
   return 0;

+ 31 - 30
toolchain/check/context.cpp

@@ -142,9 +142,10 @@ auto Context::CheckCompatibleImportedNodeKind(
   auto& import_ir_inst = import_ir_insts().Get(imported_loc_id);
   const auto* import_ir = import_irs().Get(import_ir_inst.ir_id).sem_ir;
   auto imported_kind = import_ir->insts().Get(import_ir_inst.inst_id).kind();
-  CARBON_CHECK(HasCompatibleImportedNodeKind(imported_kind, kind))
-      << "Node of kind " << kind
-      << " created with location of imported node of kind " << imported_kind;
+  CARBON_CHECK(
+      HasCompatibleImportedNodeKind(imported_kind, kind),
+      "Node of kind {0} created with location of imported node of kind {1}",
+      kind, imported_kind);
 }
 
 auto Context::AddInstInNoBlock(SemIR::LocIdAndInst loc_id_and_inst)
@@ -218,7 +219,7 @@ auto Context::DiagnoseNameNotFound(SemIRLoc loc, SemIR::NameId name_id)
 auto Context::NoteIncompleteClass(SemIR::ClassId class_id,
                                   DiagnosticBuilder& builder) -> void {
   const auto& class_info = classes().Get(class_id);
-  CARBON_CHECK(!class_info.is_defined()) << "Class is not incomplete";
+  CARBON_CHECK(!class_info.is_defined(), "Class is not incomplete");
   if (class_info.definition_id.is_valid()) {
     CARBON_DIAGNOSTIC(ClassIncompleteWithinDefinition, Note,
                       "Class is incomplete within its definition.");
@@ -233,7 +234,7 @@ auto Context::NoteIncompleteClass(SemIR::ClassId class_id,
 auto Context::NoteUndefinedInterface(SemIR::InterfaceId interface_id,
                                      DiagnosticBuilder& builder) -> void {
   const auto& interface_info = interfaces().Get(interface_id);
-  CARBON_CHECK(!interface_info.is_defined()) << "Interface is not incomplete";
+  CARBON_CHECK(!interface_info.is_defined(), "Interface is not incomplete");
   if (interface_info.is_being_defined()) {
     CARBON_DIAGNOSTIC(InterfaceUndefinedWithinDefinition, Note,
                       "Interface is currently being defined.");
@@ -389,7 +390,7 @@ static auto DiagnoseInvalidQualifiedNameAccess(Context& context, SemIRLoc loc,
                        class_info.adapt_id)) {
       parent_type_id = adapt_decl->adapted_type_id;
     } else {
-      CARBON_FATAL() << "Expected parent for parent access";
+      CARBON_FATAL("Expected parent for parent access");
     }
   }
 
@@ -615,7 +616,7 @@ auto Context::AddDominatedBlockAndBranchIf(Parse::NodeId node_id,
 
 auto Context::AddConvergenceBlockAndPush(Parse::NodeId node_id, int num_blocks)
     -> void {
-  CARBON_CHECK(num_blocks >= 2) << "no convergence";
+  CARBON_CHECK(num_blocks >= 2, "no convergence");
 
   SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
   for ([[maybe_unused]] auto _ : llvm::seq(num_blocks)) {
@@ -633,7 +634,7 @@ auto Context::AddConvergenceBlockAndPush(Parse::NodeId node_id, int num_blocks)
 auto Context::AddConvergenceBlockWithArgAndPush(
     Parse::NodeId node_id, std::initializer_list<SemIR::InstId> block_args)
     -> SemIR::InstId {
-  CARBON_CHECK(block_args.size() >= 2) << "no convergence";
+  CARBON_CHECK(block_args.size() >= 2, "no convergence");
 
   SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
   for (auto arg_id : block_args) {
@@ -671,8 +672,8 @@ auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
     const_id = constant_values().Get(literal.value().value.ToBool() ? if_true
                                                                     : if_false);
   } else {
-    CARBON_CHECK(cond_const_id == SemIR::ConstantId::Error)
-        << "Unexpected constant branch condition.";
+    CARBON_CHECK(cond_const_id == SemIR::ConstantId::Error,
+                 "Unexpected constant branch condition.");
     const_id = SemIR::ConstantId::Error;
   }
 
@@ -684,11 +685,11 @@ auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
 }
 
 auto Context::AddCurrentCodeBlockToFunction(Parse::NodeId node_id) -> void {
-  CARBON_CHECK(!inst_block_stack().empty()) << "no current code block";
+  CARBON_CHECK(!inst_block_stack().empty(), "no current code block");
 
   if (return_scope_stack().empty()) {
-    CARBON_CHECK(node_id.is_valid())
-        << "No current function, but node_id not provided";
+    CARBON_CHECK(node_id.is_valid(),
+                 "No current function, but node_id not provided");
     TODO(node_id,
          "Control flow expressions are currently only supported inside "
          "functions.");
@@ -798,16 +799,16 @@ class TypeCompleter {
         if (!AddNestedIncompleteTypes(inst)) {
           return false;
         }
-        CARBON_CHECK(work_list_.size() >= old_work_list_size)
-            << "AddNestedIncompleteTypes should not remove work items";
+        CARBON_CHECK(work_list_.size() >= old_work_list_size,
+                     "AddNestedIncompleteTypes should not remove work items");
         work_list_[old_work_list_size - 1].phase = Phase::BuildValueRepr;
         break;
 
       case Phase::BuildValueRepr: {
         auto value_rep = BuildValueRepr(type_id, inst);
         context_.types().SetValueRepr(type_id, value_rep);
-        CARBON_CHECK(old_work_list_size == work_list_.size())
-            << "BuildValueRepr should not change work items";
+        CARBON_CHECK(old_work_list_size == work_list_.size(),
+                     "BuildValueRepr should not change work items");
         work_list_.pop_back();
 
         // Also complete the value representation type, if necessary. This
@@ -919,11 +920,11 @@ class TypeCompleter {
   // Gets the value representation of a nested type, which should already be
   // complete.
   auto GetNestedValueRepr(SemIR::TypeId nested_type_id) const {
-    CARBON_CHECK(context_.types().IsComplete(nested_type_id))
-        << "Nested type should already be complete";
+    CARBON_CHECK(context_.types().IsComplete(nested_type_id),
+                 "Nested type should already be complete");
     auto value_rep = context_.types().GetValueRepr(nested_type_id);
-    CARBON_CHECK(value_rep.kind != SemIR::ValueRepr::Unknown)
-        << "Complete type should have a value representation";
+    CARBON_CHECK(value_rep.kind != SemIR::ValueRepr::Unknown,
+                 "Complete type should have a value representation");
     return value_rep;
   }
 
@@ -1105,7 +1106,7 @@ class TypeCompleter {
     requires(InstT::Kind.is_type() == SemIR::InstIsType::Never)
   auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT inst) const
       -> SemIR::ValueRepr {
-    CARBON_FATAL() << "Type refers to non-type inst " << inst;
+    CARBON_FATAL("Type refers to non-type inst {0}", inst);
   }
 
   // Builds and returns the value representation for the given type. All nested
@@ -1173,16 +1174,16 @@ auto Context::TryToDefineType(SemIR::TypeId type_id,
 
 auto Context::GetTypeIdForTypeConstant(SemIR::ConstantId constant_id)
     -> SemIR::TypeId {
-  CARBON_CHECK(constant_id.is_constant())
-      << "Canonicalizing non-constant type: " << constant_id;
+  CARBON_CHECK(constant_id.is_constant(),
+               "Canonicalizing non-constant type: {0}", constant_id);
   auto type_id =
       insts().Get(constant_values().GetInstId(constant_id)).type_id();
   // TODO: For now, we allow values of facet type to be used as types.
   CARBON_CHECK(type_id == SemIR::TypeId::TypeType ||
-               types().Is<SemIR::InterfaceType>(type_id) ||
-               constant_id == SemIR::ConstantId::Error)
-      << "Forming type ID for non-type constant of type "
-      << types().GetAsInst(type_id);
+                   types().Is<SemIR::InterfaceType>(type_id) ||
+                   constant_id == SemIR::ConstantId::Error,
+               "Forming type ID for non-type constant of type {0}",
+               types().GetAsInst(type_id));
 
   return SemIR::TypeId::ForTypeConstant(constant_id);
 }
@@ -1205,7 +1206,7 @@ static auto GetCompleteTypeImpl(Context& context, EachArgT... each_arg)
     -> SemIR::TypeId {
   auto type_id = GetTypeImpl<InstT>(context, each_arg...);
   bool complete = context.TryToCompleteType(type_id);
-  CARBON_CHECK(complete) << "Type completion should not fail";
+  CARBON_CHECK(complete, "Type completion should not fail");
   return type_id;
 }
 
@@ -1231,7 +1232,7 @@ auto Context::GetBuiltinType(SemIR::BuiltinInstKind kind) -> SemIR::TypeId {
   auto type_id = GetTypeIdForTypeInst(SemIR::InstId::ForBuiltin(kind));
   // To keep client code simpler, complete builtin types before returning them.
   bool complete = TryToCompleteType(type_id);
-  CARBON_CHECK(complete) << "Failed to complete builtin type";
+  CARBON_CHECK(complete, "Failed to complete builtin type");
   return type_id;
 }
 

+ 1 - 2
toolchain/check/context.h

@@ -363,8 +363,7 @@ class Context {
   // Sets the total number of IRs which exist. This is used to prepare a map
   // from IR to imported IR.
   auto SetTotalIRCount(int num_irs) -> void {
-    CARBON_CHECK(check_ir_map_.empty())
-        << "SetTotalIRCount is only called once";
+    CARBON_CHECK(check_ir_map_.empty(), "SetTotalIRCount is only called once");
     check_ir_map_.resize(num_irs, SemIR::ImportIRId::Invalid);
   }
 

+ 13 - 16
toolchain/check/convert.cpp

@@ -66,8 +66,7 @@ static auto FindReturnSlotForInitializer(SemIR::File& sem_ir,
         return sem_ir.inst_blocks().Get(call.args_id).back();
       }
       default:
-        CARBON_FATAL() << "Initialization from unexpected inst "
-                       << init_untyped;
+        CARBON_FATAL("Initialization from unexpected inst {0}", init_untyped);
     }
   }
 }
@@ -80,10 +79,10 @@ static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::InstId init_id,
   if (return_slot_id.is_valid()) {
     // Replace the temporary in the return slot with a reference to our target.
     CARBON_CHECK(sem_ir.insts().Get(return_slot_id).kind() ==
-                 SemIR::TemporaryStorage::Kind)
-        << "Return slot for initializer does not contain a temporary; "
-        << "initialized multiple times? Have "
-        << sem_ir.insts().Get(return_slot_id);
+                     SemIR::TemporaryStorage::Kind,
+                 "Return slot for initializer does not contain a temporary; "
+                 "initialized multiple times? Have {0}",
+                 sem_ir.insts().Get(return_slot_id));
     target_block.MergeReplacing(return_slot_id, target_id);
   }
 }
@@ -100,10 +99,10 @@ static auto FinalizeTemporary(Context& context, SemIR::InstId init_id,
   if (return_slot_id.is_valid()) {
     // The return slot should already have a materialized temporary in it.
     CARBON_CHECK(sem_ir.insts().Get(return_slot_id).kind() ==
-                 SemIR::TemporaryStorage::Kind)
-        << "Return slot for initializer does not contain a temporary; "
-        << "initialized multiple times? Have "
-        << sem_ir.insts().Get(return_slot_id);
+                     SemIR::TemporaryStorage::Kind,
+                 "Return slot for initializer does not contain a temporary; "
+                 "initialized multiple times? Have {0}",
+                 sem_ir.insts().Get(return_slot_id));
     auto init = sem_ir.insts().Get(init_id);
     return context.AddInst<SemIR::Temporary>(sem_ir.insts().GetLocId(init_id),
                                              {.type_id = init.type_id(),
@@ -423,8 +422,7 @@ static auto ConvertStructToStructOrClass(Context& context,
     for (auto [i, field_id] : llvm::enumerate(src_elem_fields)) {
       auto result = src_field_indexes.Insert(
           context.insts().GetAs<SemIR::StructTypeField>(field_id).name_id, i);
-      CARBON_CHECK(result.is_inserted())
-          << "Duplicate field in source structure";
+      CARBON_CHECK(result.is_inserted(), "Duplicate field in source structure");
     }
   }
 
@@ -496,8 +494,8 @@ static auto ConvertStructToStructOrClass(Context& context,
 
   if (is_class) {
     target.init_block->InsertHere();
-    CARBON_CHECK(is_init)
-        << "Converting directly to a class value is not supported";
+    CARBON_CHECK(is_init,
+                 "Converting directly to a class value is not supported");
     return context.AddInst<SemIR::ClassInit>(value_loc_id,
                                              {.type_id = target.type_id,
                                               .elements_id = new_block.id(),
@@ -1004,8 +1002,7 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
   switch (SemIR::GetExprCategory(sem_ir, expr_id)) {
     case SemIR::ExprCategory::NotExpr:
     case SemIR::ExprCategory::Mixed:
-      CARBON_FATAL() << "Unexpected expression " << expr
-                     << " after builtin conversions";
+      CARBON_FATAL("Unexpected expression {0} after builtin conversions", expr);
 
     case SemIR::ExprCategory::Error:
       return SemIR::InstId::BuiltinError;

+ 2 - 3
toolchain/check/decl_introducer_state.h

@@ -74,9 +74,8 @@ class DeclIntroducerStateStack {
   template <Lex::TokenKind::RawEnumType Kind>
     requires(IsDeclIntroducer<Kind>())
   auto Pop() -> DeclIntroducerState {
-    CARBON_CHECK(stack_.back().kind == Kind)
-        << "Found: " << stack_.back().kind
-        << " expected: " << Lex::TokenKind::Make(Kind);
+    CARBON_CHECK(stack_.back().kind == Kind, "Found: {0} expected: {1}",
+                 stack_.back().kind, Lex::TokenKind::Make(Kind));
     return stack_.pop_back_val();
   }
 

+ 24 - 24
toolchain/check/decl_name_stack.cpp

@@ -23,9 +23,9 @@ auto DeclNameStack::NameContext::prev_inst_id() -> SemIR::InstId {
       return SemIR::InstId::Invalid;
 
     case NameContext::State::Empty:
-      CARBON_FATAL()
-          << "Name is missing, not expected to call existing_inst_id (but "
-             "that may change based on error handling).";
+      CARBON_FATAL(
+          "Name is missing, not expected to call existing_inst_id (but that "
+          "may change based on error handling).");
 
     case NameContext::State::Resolved:
       return resolved_inst_id;
@@ -34,7 +34,7 @@ auto DeclNameStack::NameContext::prev_inst_id() -> SemIR::InstId {
       return SemIR::InstId::Invalid;
 
     case NameContext::State::Finished:
-      CARBON_FATAL() << "Finished state should only be used internally";
+      CARBON_FATAL("Finished state should only be used internally");
   }
 }
 
@@ -59,8 +59,8 @@ auto DeclNameStack::PushScopeAndStartName() -> void {
 }
 
 auto DeclNameStack::FinishName(const NameComponent& name) -> NameContext {
-  CARBON_CHECK(decl_name_stack_.back().state != NameContext::State::Finished)
-      << "Finished name twice";
+  CARBON_CHECK(decl_name_stack_.back().state != NameContext::State::Finished,
+               "Finished name twice");
 
   ApplyAndLookupName(decl_name_stack_.back(), name.name_loc_id, name.name_id);
 
@@ -70,8 +70,8 @@ auto DeclNameStack::FinishName(const NameComponent& name) -> NameContext {
 }
 
 auto DeclNameStack::FinishImplName() -> NameContext {
-  CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Empty)
-      << "Impl has a name";
+  CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Empty,
+               "Impl has a name");
 
   NameContext result = decl_name_stack_.back();
   decl_name_stack_.back().state = NameContext::State::Finished;
@@ -79,15 +79,15 @@ auto DeclNameStack::FinishImplName() -> NameContext {
 }
 
 auto DeclNameStack::PopScope() -> void {
-  CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Finished)
-      << "Missing call to FinishName before PopScope";
+  CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Finished,
+               "Missing call to FinishName before PopScope");
   context_->scope_stack().PopTo(decl_name_stack_.back().initial_scope_index);
   decl_name_stack_.pop_back();
 }
 
 auto DeclNameStack::Suspend() -> SuspendedName {
-  CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Finished)
-      << "Missing call to FinishName before Suspend";
+  CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Finished,
+               "Missing call to FinishName before Suspend");
   SuspendedName result = {.name_context = decl_name_stack_.pop_back_val(),
                           .scopes = {}};
   auto scope_index = result.name_context.initial_scope_index;
@@ -95,17 +95,17 @@ auto DeclNameStack::Suspend() -> SuspendedName {
   while (scope_stack.PeekIndex() > scope_index) {
     result.scopes.push_back(scope_stack.Suspend());
   }
-  CARBON_CHECK(scope_stack.PeekIndex() == scope_index)
-      << "Scope index " << scope_index << " does not enclose the current scope "
-      << scope_stack.PeekIndex();
+  CARBON_CHECK(scope_stack.PeekIndex() == scope_index,
+               "Scope index {0} does not enclose the current scope {1}",
+               scope_index, scope_stack.PeekIndex());
   return result;
 }
 
 auto DeclNameStack::Restore(SuspendedName sus) -> void {
   // The parent state must be the same when a name is restored.
   CARBON_CHECK(context_->scope_stack().PeekIndex() ==
-               sus.name_context.initial_scope_index)
-      << "Name restored at the wrong position in the name stack.";
+                   sus.name_context.initial_scope_index,
+               "Name restored at the wrong position in the name stack.");
 
   // clang-tidy warns that the `std::move` below has no effect. While that's
   // true, this `move` defends against `NameContext` growing more state later.
@@ -164,15 +164,15 @@ auto DeclNameStack::AddName(NameContext name_context, SemIR::InstId target_id,
         };
         auto result = name_scope.name_map.Insert(
             name_context.unresolved_name_id, add_scope);
-        CARBON_CHECK(result.is_inserted())
-            << "Duplicate names should have been resolved previously: "
-            << name_context.unresolved_name_id << " in "
-            << name_context.parent_scope_id;
+        CARBON_CHECK(
+            result.is_inserted(),
+            "Duplicate names should have been resolved previously: {0} in {1}",
+            name_context.unresolved_name_id, name_context.parent_scope_id);
       }
       break;
 
     default:
-      CARBON_FATAL() << "Should not be calling AddName";
+      CARBON_FATAL("Should not be calling AddName");
       break;
   }
 }
@@ -284,7 +284,7 @@ static auto CheckQualifierIsResolved(
     Context& context, const DeclNameStack::NameContext& name_context) -> bool {
   switch (name_context.state) {
     case DeclNameStack::NameContext::State::Empty:
-      CARBON_FATAL() << "No qualifier to resolve";
+      CARBON_FATAL("No qualifier to resolve");
 
     case DeclNameStack::NameContext::State::Resolved:
       return true;
@@ -297,7 +297,7 @@ static auto CheckQualifierIsResolved(
       return false;
 
     case DeclNameStack::NameContext::State::Finished:
-      CARBON_FATAL() << "Added a qualifier after calling FinishName";
+      CARBON_FATAL("Added a qualifier after calling FinishName");
 
     case DeclNameStack::NameContext::State::Error:
       // Already in an error state, so return without examining.

+ 4 - 4
toolchain/check/deduce.cpp

@@ -156,10 +156,10 @@ auto DeduceGenericCallArguments(
         auto& entity_name = context.entity_names().Get(bind.entity_name_id);
         auto index = entity_name.bind_index;
         if (index.is_valid() && index >= first_deduced_index) {
-          CARBON_CHECK(static_cast<size_t>(index.index) < result_arg_ids.size())
-              << "Deduced value for unexpected index " << index
-              << "; expected to deduce " << result_arg_ids.size()
-              << " arguments.";
+          CARBON_CHECK(static_cast<size_t>(index.index) < result_arg_ids.size(),
+                       "Deduced value for unexpected index {0}; expected to "
+                       "deduce {1} arguments.",
+                       index, result_arg_ids.size());
           auto arg_const_inst_id =
               context.constant_values().GetConstantInstId(arg_id);
           if (arg_const_inst_id.is_valid()) {

+ 22 - 21
toolchain/check/eval.cpp

@@ -77,9 +77,10 @@ class EvalContext {
               specifics().Get(specific_id_).generic_id &&
           symbolic_info.index.region() == specific_eval_info_->region) {
         auto inst_id = specific_eval_info_->values[symbolic_info.index.index()];
-        CARBON_CHECK(inst_id.is_valid())
-            << "Forward reference in eval block: index "
-            << symbolic_info.index.index() << " referenced before evaluation";
+        CARBON_CHECK(inst_id.is_valid(),
+                     "Forward reference in eval block: index {0} referenced "
+                     "before evaluation",
+                     symbolic_info.index.index());
         return constant_values().Get(inst_id);
       }
     }
@@ -464,14 +465,14 @@ static auto PerformAggregateAccess(EvalContext& eval_context, SemIR::Inst inst)
                 aggregate_id)) {
       auto elements = eval_context.inst_blocks().Get(aggregate->elements_id);
       auto index = static_cast<size_t>(access_inst.index.index);
-      CARBON_CHECK(index < elements.size()) << "Access out of bounds.";
+      CARBON_CHECK(index < elements.size(), "Access out of bounds.");
       // `Phase` is not used here. If this element is a template constant, then
       // so is the result of indexing, even if the aggregate also contains a
       // symbolic context.
       return eval_context.GetConstantValue(elements[index]);
     } else {
-      CARBON_CHECK(phase != Phase::Template)
-          << "Failed to evaluate template constant " << inst;
+      CARBON_CHECK(phase != Phase::Template,
+                   "Failed to evaluate template constant {0}", inst);
     }
   }
   return MakeNonConstantResult(phase);
@@ -490,8 +491,8 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst)
   }
   auto index = eval_context.insts().TryGetAs<SemIR::IntLiteral>(index_id);
   if (!index) {
-    CARBON_CHECK(phase != Phase::Template)
-        << "Template constant integer should be a literal";
+    CARBON_CHECK(phase != Phase::Template,
+                 "Template constant integer should be a literal");
     return MakeNonConstantResult(phase);
   }
 
@@ -529,15 +530,15 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst)
   auto aggregate =
       eval_context.insts().TryGetAs<SemIR::AnyAggregateValue>(aggregate_id);
   if (!aggregate) {
-    CARBON_CHECK(phase != Phase::Template)
-        << "Unexpected representation for template constant aggregate";
+    CARBON_CHECK(phase != Phase::Template,
+                 "Unexpected representation for template constant aggregate");
     return MakeNonConstantResult(phase);
   }
 
   auto elements = eval_context.inst_blocks().Get(aggregate->elements_id);
   // We checked this for the array case above.
-  CARBON_CHECK(index_val.ult(elements.size()))
-      << "Index out of bounds in tuple indexing";
+  CARBON_CHECK(index_val.ult(elements.size()),
+               "Index out of bounds in tuple indexing");
   return eval_context.GetConstantValue(elements[index_val.getZExtValue()]);
 }
 
@@ -649,7 +650,7 @@ static auto PerformBuiltinUnaryIntOp(Context& context, SemIRLoc loc,
       op_val.flipAllBits();
       break;
     default:
-      CARBON_FATAL() << "Unexpected builtin kind";
+      CARBON_FATAL("Unexpected builtin kind");
   }
 
   return MakeIntResult(context, op.type_id, std::move(op_val));
@@ -774,7 +775,7 @@ static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc loc,
       break;
 
     default:
-      CARBON_FATAL() << "Unexpected operation kind.";
+      CARBON_FATAL("Unexpected operation kind.");
   }
 
   if (overflow) {
@@ -823,7 +824,7 @@ static auto PerformBuiltinIntComparison(Context& context,
       result = is_signed ? lhs_val.sge(rhs_val) : lhs_val.sge(rhs_val);
       break;
     default:
-      CARBON_FATAL() << "Unexpected operation kind.";
+      CARBON_FATAL("Unexpected operation kind.");
   }
 
   return MakeBoolResult(context, bool_type_id, result);
@@ -842,7 +843,7 @@ static auto PerformBuiltinUnaryFloatOp(Context& context,
       op_val.changeSign();
       break;
     default:
-      CARBON_FATAL() << "Unexpected builtin kind";
+      CARBON_FATAL("Unexpected builtin kind");
   }
 
   return MakeFloatResult(context, op.type_id, std::move(op_val));
@@ -875,7 +876,7 @@ static auto PerformBuiltinBinaryFloatOp(Context& context,
       result_val = lhs_val / rhs_val;
       break;
     default:
-      CARBON_FATAL() << "Unexpected operation kind.";
+      CARBON_FATAL("Unexpected operation kind.");
   }
 
   return MakeFloatResult(context, lhs.type_id, std::move(result_val));
@@ -912,7 +913,7 @@ static auto PerformBuiltinFloatComparison(
       result = lhs_val >= rhs_val;
       break;
     default:
-      CARBON_FATAL() << "Unexpected operation kind.";
+      CARBON_FATAL("Unexpected operation kind.");
   }
 
   return MakeBoolResult(context, bool_type_id, result);
@@ -926,7 +927,7 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc,
                                        Phase phase) -> SemIR::ConstantId {
   switch (builtin_kind) {
     case SemIR::BuiltinFunctionKind::None:
-      CARBON_FATAL() << "Not a builtin function.";
+      CARBON_FATAL("Not a builtin function.");
 
     case SemIR::BuiltinFunctionKind::PrintInt: {
       // Providing a constant result would allow eliding the function call.
@@ -1483,8 +1484,8 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id,
       break;
 
     case SemIR::ImportRefUnloaded::Kind:
-      CARBON_FATAL()
-          << "ImportRefUnloaded should be loaded before TryEvalInst: " << inst;
+      CARBON_FATAL("ImportRefUnloaded should be loaded before TryEvalInst: {0}",
+                   inst);
   }
   return SemIR::ConstantId::NotConstant;
 }

+ 26 - 24
toolchain/check/generic.cpp

@@ -73,9 +73,10 @@ class RebuildGenericConstantInEvalBlockCallbacks final
     if (!const_id.is_valid()) {
       // An unloaded import ref should never contain anything we need to
       // substitute into. Don't trigger loading it here.
-      CARBON_CHECK(context_.insts().Is<SemIR::ImportRefUnloaded>(inst_id))
-          << "Substituting into instruction with invalid constant ID: "
-          << context_.insts().Get(inst_id);
+      CARBON_CHECK(
+          context_.insts().Is<SemIR::ImportRefUnloaded>(inst_id),
+          "Substituting into instruction with invalid constant ID: {0}",
+          context_.insts().Get(inst_id));
       return true;
     }
     if (!const_id.is_symbolic()) {
@@ -174,9 +175,9 @@ static auto AddGenericConstantToEvalBlock(
       SubstInst(context, const_inst_id,
                 RebuildGenericConstantInEvalBlockCallbacks(
                     context, generic_id, region, constants_in_generic));
-  CARBON_CHECK(new_inst_id != const_inst_id)
-      << "Did not apply any substitutions to symbolic constant "
-      << context.insts().Get(const_inst_id);
+  CARBON_CHECK(new_inst_id != const_inst_id,
+               "Did not apply any substitutions to symbolic constant {0}",
+               context.insts().Get(const_inst_id));
   return context.constant_values().Get(new_inst_id);
 }
 
@@ -192,9 +193,9 @@ static auto PopulateConstantsFromDeclaration(
   for (auto inst_id : decl_eval_block) {
     auto const_inst_id = context.constant_values().GetConstantInstId(inst_id);
     auto result = constants_in_generic.Insert(const_inst_id, inst_id);
-    CARBON_CHECK(result.is_inserted())
-        << "Duplicate constant in generic decl eval block: "
-        << context.insts().Get(const_inst_id);
+    CARBON_CHECK(result.is_inserted(),
+                 "Duplicate constant in generic decl eval block: {0}",
+                 context.insts().Get(const_inst_id));
   }
 }
 
@@ -252,12 +253,13 @@ static auto MakeGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
     }
   }
 
-  CARBON_CHECK(num_dependent_insts ==
-               context.generic_region_stack().PeekDependentInsts().size())
-      << "Building eval block added new dependent insts, for example "
-      << context.insts().Get(context.generic_region_stack()
-                                 .PeekDependentInsts()[num_dependent_insts]
-                                 .inst_id);
+  CARBON_CHECK(
+      num_dependent_insts ==
+          context.generic_region_stack().PeekDependentInsts().size(),
+      "Building eval block added new dependent insts, for example {0}",
+      context.insts().Get(context.generic_region_stack()
+                              .PeekDependentInsts()[num_dependent_insts]
+                              .inst_id));
 
   return context.inst_block_stack().Pop();
 }
@@ -283,11 +285,11 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
     // Build a constant in the inst block.
     AddGenericConstantToEvalBlock(context, generic_id, region,
                                   constants_in_generic, inst_id);
-    CARBON_CHECK(context.inst_block_stack().PeekCurrentBlockContents().size() ==
-                 i + 1)
-        << "Produced "
-        << (context.inst_block_stack().PeekCurrentBlockContents().size() - i)
-        << " instructions when importing " << context.insts().Get(inst_id);
+    CARBON_CHECK(
+        context.inst_block_stack().PeekCurrentBlockContents().size() == i + 1,
+        "Produced {0} instructions when importing {1}",
+        (context.inst_block_stack().PeekCurrentBlockContents().size() - i),
+        context.insts().Get(inst_id));
   }
 
   return context.inst_block_stack().Pop();
@@ -299,9 +301,9 @@ auto FinishGenericDecl(Context& context, SemIR::InstId decl_id)
       context.scope_stack().compile_time_bindings_stack().PeekAllValues();
 
   if (all_bindings.empty()) {
-    CARBON_CHECK(context.generic_region_stack().PeekDependentInsts().empty())
-        << "Have dependent instructions but no compile time bindings are in "
-           "scope.";
+    CARBON_CHECK(context.generic_region_stack().PeekDependentInsts().empty(),
+                 "Have dependent instructions but no compile time bindings are "
+                 "in scope.");
     context.generic_region_stack().Pop();
     return SemIR::GenericId::Invalid;
   }
@@ -396,7 +398,7 @@ auto ResolveSpecificDefinition(Context& context, SemIR::SpecificId specific_id)
     -> bool {
   auto& specific = context.specifics().Get(specific_id);
   auto generic_id = specific.generic_id;
-  CARBON_CHECK(generic_id.is_valid()) << "Specific with no generic ID";
+  CARBON_CHECK(generic_id.is_valid(), "Specific with no generic ID");
 
   if (!specific.definition_block_id.is_valid()) {
     // Evaluate the eval block for the definition of the generic.

+ 4 - 4
toolchain/check/handle_binding_pattern.cpp

@@ -108,8 +108,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
                                        cast_type_id);
       });
       if (parent_class_decl) {
-        CARBON_CHECK(context_node_kind == Parse::NodeKind::VariableIntroducer)
-            << "`returned var` at class scope";
+        CARBON_CHECK(context_node_kind == Parse::NodeKind::VariableIntroducer,
+                     "`returned var` at class scope");
         auto& class_info = context.classes().Get(parent_class_decl->class_id);
         auto field_type_id = context.GetUnboundElementType(
             class_info.self_type_id, cast_type_id);
@@ -185,8 +185,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
     }
 
     default:
-      CARBON_FATAL() << "Found a pattern binding in unexpected context "
-                     << context_node_kind;
+      CARBON_FATAL("Found a pattern binding in unexpected context {0}",
+                   context_node_kind);
   }
   return true;
 }

+ 4 - 4
toolchain/check/handle_class.cpp

@@ -408,8 +408,8 @@ auto HandleParseNode(Context& context, Parse::AdaptDeclId node_id) -> bool {
     } else if (auto* adapted_class_info =
                    TryGetAsClass(context, adapted_type_id)) {
       extended_scope_id = adapted_class_info->scope_id;
-      CARBON_CHECK(adapted_class_info->scope_id.is_valid())
-          << "Complete class should have a scope";
+      CARBON_CHECK(adapted_class_info->scope_id.is_valid(),
+                   "Complete class should have a scope");
     } else {
       // TODO: Accept any type that has a scope.
       context.TODO(node_id, "extending non-class type");
@@ -489,8 +489,8 @@ static auto CheckBaseType(Context& context, Parse::NodeId node_id,
     DiagnoseBaseIsFinal(context, node_id, base_type_id);
   }
 
-  CARBON_CHECK(base_class_info->scope_id.is_valid())
-      << "Complete class should have a scope";
+  CARBON_CHECK(base_class_info->scope_id.is_valid(),
+               "Complete class should have a scope");
   return {.type_id = base_type_id, .scope_id = base_class_info->scope_id};
 }
 

+ 1 - 1
toolchain/check/handle_if_statement.cpp

@@ -68,7 +68,7 @@ auto HandleParseNode(Context& context, Parse::IfStatementId node_id) -> bool {
     }
 
     default: {
-      CARBON_FATAL() << "Unexpected parse node at start of `if`: " << kind;
+      CARBON_FATAL("Unexpected parse node at start of `if`: {0}", kind);
     }
   }
 

+ 2 - 2
toolchain/check/handle_interface.cpp

@@ -137,8 +137,8 @@ auto HandleParseNode(Context& context,
   auto& interface_info = context.interfaces().Get(interface_id);
 
   // Track that this declaration is the definition.
-  CARBON_CHECK(!interface_info.is_defined())
-      << "Can't merge with defined interfaces.";
+  CARBON_CHECK(!interface_info.is_defined(),
+               "Can't merge with defined interfaces.");
   interface_info.definition_id = interface_decl_id;
   interface_info.scope_id =
       context.name_scopes().Add(interface_decl_id, SemIR::NameId::Invalid,

+ 2 - 2
toolchain/check/handle_let_and_var.cpp

@@ -237,8 +237,8 @@ auto HandleParseNode(Context& context, Parse::LetDeclId node_id) -> bool {
   // the computation of the value.
   // TODO: Support other kinds of pattern here.
   auto bind_name = pattern.inst.As<SemIR::AnyBindName>();
-  CARBON_CHECK(!bind_name.value_id.is_valid())
-      << "Binding should not already have a value!";
+  CARBON_CHECK(!bind_name.value_id.is_valid(),
+               "Binding should not already have a value!");
   bind_name.value_id =
       decl_info->init_id ? *decl_info->init_id : SemIR::InstId::BuiltinError;
   context.ReplaceInstBeforeConstantUse(decl_info->pattern_id, bind_name);

+ 1 - 1
toolchain/check/handle_name.cpp

@@ -104,7 +104,7 @@ static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id,
   auto value = context.insts().Get(result.inst_id);
   auto type_id = SemIR::GetTypeInSpecific(context.sem_ir(), result.specific_id,
                                           value.type_id());
-  CARBON_CHECK(type_id.is_valid()) << "Missing type for " << value;
+  CARBON_CHECK(type_id.is_valid(), "Missing type for {0}", value);
 
   // If the named entity has a constant value that depends on its specific,
   // store the specific too.

+ 2 - 2
toolchain/check/handle_noop.cpp

@@ -29,8 +29,8 @@ auto HandleParseNode(Context& context, Parse::InvalidParseSubtreeId node_id)
 
 auto HandleParseNode(Context& /*context*/, Parse::PlaceholderId /*node_id*/)
     -> bool {
-  CARBON_FATAL()
-      << "Placeholder node should always be replaced before parse completes";
+  CARBON_FATAL(
+      "Placeholder node should always be replaced before parse completes");
 }
 
 }  // namespace Carbon::Check

+ 2 - 2
toolchain/check/handle_struct.cpp

@@ -125,8 +125,8 @@ auto HandleParseNode(Context& context, Parse::StructTypeLiteralId node_id)
   context.node_stack()
       .PopAndDiscardSoloNodeId<Parse::NodeKind::StructTypeLiteralStart>();
 
-  CARBON_CHECK(refs_id != SemIR::InstBlockId::Empty)
-      << "{} is handled by StructLiteral.";
+  CARBON_CHECK(refs_id != SemIR::InstBlockId::Empty,
+               "{{}} is handled by StructLiteral.");
 
   if (DiagnoseDuplicateNames(context, refs_id, "struct type literal")) {
     context.node_stack().Push(node_id, SemIR::InstId::BuiltinError);

+ 10 - 10
toolchain/check/impl.cpp

@@ -54,12 +54,12 @@ static auto GetSelfSpecificForInterfaceMemberWithSelfType(
   // Add the `Self` argument.
   CARBON_CHECK(
       context.entity_names()
-          .Get(context.insts()
-                   .GetAs<SemIR::BindSymbolicName>(bindings[arg_ids.size()])
-                   .entity_name_id)
-          .name_id == SemIR::NameId::SelfType)
-      << "Expected a Self binding, found "
-      << context.insts().Get(bindings[arg_ids.size()]);
+              .Get(context.insts()
+                       .GetAs<SemIR::BindSymbolicName>(bindings[arg_ids.size()])
+                       .entity_name_id)
+              .name_id == SemIR::NameId::SelfType,
+      "Expected a Self binding, found {0}",
+      context.insts().Get(bindings[arg_ids.size()]));
   arg_ids.push_back(context.types().GetInstId(self_type_id));
 
   // Take any trailing argument values from the self specific.
@@ -150,7 +150,7 @@ static auto BuildInterfaceWitness(
     decl_id =
         context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific(
             context.sem_ir(), interface_type.specific_id, decl_id));
-    CARBON_CHECK(decl_id.is_valid()) << "Non-constant associated entity";
+    CARBON_CHECK(decl_id.is_valid(), "Non-constant associated entity");
     auto decl = context.insts().Get(decl_id);
     CARBON_KIND_SWITCH(decl) {
       case CARBON_KIND(SemIR::StructValue struct_value): {
@@ -160,7 +160,7 @@ static auto BuildInterfaceWitness(
         auto type_inst = context.types().GetAsInst(struct_value.type_id);
         auto fn_type = type_inst.TryAs<SemIR::FunctionType>();
         if (!fn_type) {
-          CARBON_FATAL() << "Unexpected type: " << type_inst;
+          CARBON_FATAL("Unexpected type: {0}", type_inst);
         }
         auto& fn = context.functions().Get(fn_type->function_id);
         auto [impl_decl_id, _] = context.LookupNameInExactScope(
@@ -190,8 +190,8 @@ static auto BuildInterfaceWitness(
                      "impl of interface with associated constant");
         return SemIR::InstId::BuiltinError;
       default:
-        CARBON_CHECK(decl_id == SemIR::InstId::BuiltinError)
-            << "Unexpected kind of associated entity " << decl;
+        CARBON_CHECK(decl_id == SemIR::InstId::BuiltinError,
+                     "Unexpected kind of associated entity {0}", decl);
         table.push_back(SemIR::InstId::BuiltinError);
         break;
     }

+ 6 - 6
toolchain/check/import.cpp

@@ -63,7 +63,7 @@ static auto GetImportName(const SemIR::File& import_sem_ir,
     }
 
     default:
-      CARBON_FATAL() << "Unsupported export kind: " << import_inst;
+      CARBON_FATAL("Unsupported export kind: {0}", import_inst);
   }
 }
 
@@ -152,9 +152,9 @@ static auto CacheCopiedNamespace(
     SemIR::NameScopeId import_scope_id, SemIR::NameScopeId to_scope_id)
     -> void {
   auto result = copied_namespaces.Insert(import_scope_id, to_scope_id);
-  CARBON_CHECK(result.is_inserted() || result.value() == to_scope_id)
-      << "Copy result for namespace changed from " << import_scope_id << " to "
-      << to_scope_id;
+  CARBON_CHECK(result.is_inserted() || result.value() == to_scope_id,
+               "Copy result for namespace changed from {0} to {1}",
+               import_scope_id, to_scope_id);
 }
 
 // Copies a namespace from the import IR, returning its ID. This may diagnose
@@ -431,8 +431,8 @@ auto ImportLibrariesFromOtherPackage(Context& context,
                                      IdentifierId package_id,
                                      llvm::ArrayRef<SemIR::ImportIR> import_irs,
                                      bool has_load_error) -> void {
-  CARBON_CHECK(has_load_error || !import_irs.empty())
-      << "There should be either a load error or at least one IR.";
+  CARBON_CHECK(has_load_error || !import_irs.empty(),
+               "There should be either a load error or at least one IR.");
 
   auto name_id = SemIR::NameId::ForIdentifier(package_id);
 

+ 18 - 18
toolchain/check/import_ref.cpp

@@ -36,8 +36,8 @@ auto SetApiImportIR(Context& context, SemIR::ImportIR import_ir) -> void {
     // We don't have a check_ir_id, so add without touching check_ir_map.
     ir_id = InternalAddImportIR(context, import_ir);
   }
-  CARBON_CHECK(ir_id == SemIR::ImportIRId::ApiForImpl)
-      << "ApiForImpl must be the first IR";
+  CARBON_CHECK(ir_id == SemIR::ImportIRId::ApiForImpl,
+               "ApiForImpl must be the first IR");
 }
 
 auto AddImportIR(Context& context, SemIR::ImportIR import_ir)
@@ -244,9 +244,9 @@ class ImportRefResolver {
       auto [new_const_id, retry] =
           TryResolveInst(work.inst_id, existing.const_id);
 
-      CARBON_CHECK(!existing.const_id.is_valid() ||
-                   existing.const_id == new_const_id)
-          << "Constant value changed in third phase.";
+      CARBON_CHECK(
+          !existing.const_id.is_valid() || existing.const_id == new_const_id,
+          "Constant value changed in third phase.");
       if (!existing.const_id.is_valid()) {
         SetResolvedConstId(work.inst_id, existing.indirect_insts, new_const_id);
       }
@@ -394,8 +394,8 @@ class ImportRefResolver {
       }
       cursor_inst_id = ir_inst.inst_id;
 
-      CARBON_CHECK(cursor_ir != prev_ir || cursor_inst_id != prev_inst_id)
-          << cursor_ir->insts().Get(cursor_inst_id);
+      CARBON_CHECK(cursor_ir != prev_ir || cursor_inst_id != prev_inst_id,
+                   "{0}", cursor_ir->insts().Get(cursor_inst_id));
 
       if (auto const_id =
               context_.import_ir_constant_values()[cursor_ir_id.index].Get(
@@ -426,8 +426,8 @@ class ImportRefResolver {
   // Returns true if new unresolved constants were found as part of this
   // `Resolve` step.
   auto HasNewWork() -> bool {
-    CARBON_CHECK(initial_work_ <= work_stack_.size())
-        << "Work shouldn't decrease";
+    CARBON_CHECK(initial_work_ <= work_stack_.size(),
+                 "Work shouldn't decrease");
     return initial_work_ < work_stack_.size();
   }
 
@@ -586,7 +586,7 @@ class ImportRefResolver {
             .generic_id;
       }
       default: {
-        CARBON_FATAL() << "Unexpected type for generic declaration: " << type;
+        CARBON_FATAL("Unexpected type for generic declaration: {0}", type);
       }
     }
   }
@@ -736,7 +736,7 @@ class ImportRefResolver {
             break;
           }
           default: {
-            CARBON_FATAL() << "Unexpected kind: " << bind_inst->kind;
+            CARBON_FATAL("Unexpected kind: {0}", bind_inst->kind);
           }
         }
       }
@@ -829,8 +829,8 @@ class ImportRefResolver {
         break;
       }
     }
-    CARBON_FATAL() << "Unexpected instruction kind for name scope: "
-                   << name_scope_inst;
+    CARBON_FATAL("Unexpected instruction kind for name scope: {0}",
+                 name_scope_inst);
   }
 
   // Given an imported entity base, returns an incomplete, local version of it.
@@ -961,8 +961,8 @@ class ImportRefResolver {
     } else {
       // Third phase: perform a consistency check and produce the constant we
       // created in the second phase.
-      CARBON_CHECK(result.const_id == inner_const_id)
-          << "Constant value changed in third phase.";
+      CARBON_CHECK(result.const_id == inner_const_id,
+                   "Constant value changed in third phase.");
       result.const_id = const_id;
     }
 
@@ -1104,7 +1104,7 @@ class ImportRefResolver {
   auto ResolveAsUntyped(SemIR::Inst inst) -> ResolveResult {
     CARBON_CHECK(!HasNewWork());
     auto result = TryEvalInst(context_, SemIR::InstId::Invalid, inst);
-    CARBON_CHECK(result.is_constant()) << inst << " is not constant";
+    CARBON_CHECK(result.is_constant(), "{0} is not constant", inst);
     return {.const_id = result};
   }
 
@@ -1602,8 +1602,8 @@ class ImportRefResolver {
     new_interface.body_block_id = context_.inst_block_stack().Pop();
     new_interface.self_param_id = self_param_id;
 
-    CARBON_CHECK(import_scope.extended_scopes.empty())
-        << "Interfaces don't currently have extended scopes to support.";
+    CARBON_CHECK(import_scope.extended_scopes.empty(),
+                 "Interfaces don't currently have extended scopes to support.");
   }
 
   auto TryResolveTypedInst(SemIR::InterfaceDecl inst,

+ 5 - 5
toolchain/check/inst_block_stack.cpp

@@ -12,8 +12,8 @@ namespace Carbon::Check {
 
 auto InstBlockStack::Push(SemIR::InstBlockId id) -> void {
   CARBON_VLOG("{0} Push {1}\n", name_, id_stack_.size());
-  CARBON_CHECK(id_stack_.size() < (1 << 20))
-      << "Excessive stack size: likely infinite loop";
+  CARBON_CHECK(id_stack_.size() < (1 << 20),
+               "Excessive stack size: likely infinite loop");
   id_stack_.push_back(id);
   insts_stack_.PushArray();
 }
@@ -25,7 +25,7 @@ auto InstBlockStack::Push(SemIR::InstBlockId id,
 }
 
 auto InstBlockStack::PeekOrAdd(int depth) -> SemIR::InstBlockId {
-  CARBON_CHECK(static_cast<int>(id_stack_.size()) > depth) << "no such block";
+  CARBON_CHECK(static_cast<int>(id_stack_.size()) > depth, "no such block");
   int index = id_stack_.size() - depth - 1;
   auto& slot = id_stack_[index];
   if (!slot.is_valid()) {
@@ -35,7 +35,7 @@ auto InstBlockStack::PeekOrAdd(int depth) -> SemIR::InstBlockId {
 }
 
 auto InstBlockStack::Pop() -> SemIR::InstBlockId {
-  CARBON_CHECK(!empty()) << "no current block";
+  CARBON_CHECK(!empty(), "no current block");
   auto id = id_stack_.pop_back_val();
   auto insts = insts_stack_.PeekArray();
 
@@ -55,7 +55,7 @@ auto InstBlockStack::Pop() -> SemIR::InstBlockId {
 }
 
 auto InstBlockStack::PopAndDiscard() -> void {
-  CARBON_CHECK(!empty()) << "no current block";
+  CARBON_CHECK(!empty(), "no current block");
   id_stack_.pop_back();
   insts_stack_.PopArray();
   CARBON_VLOG("{0} PopAndDiscard {1}\n", name_, id_stack_.size());

+ 3 - 3
toolchain/check/inst_block_stack.h

@@ -54,7 +54,7 @@ class InstBlockStack {
 
   // Adds the given instruction ID to the block at the top of the stack.
   auto AddInstId(SemIR::InstId inst_id) -> void {
-    CARBON_CHECK(!empty()) << "no current block";
+    CARBON_CHECK(!empty(), "no current block");
     insts_stack_.AppendToTop(inst_id);
   }
 
@@ -65,7 +65,7 @@ class InstBlockStack {
 
   // Returns a view of the contents of the top instruction block on the stack.
   auto PeekCurrentBlockContents() const -> llvm::ArrayRef<SemIR::InstId> {
-    CARBON_CHECK(!empty()) << "no current block";
+    CARBON_CHECK(!empty(), "no current block");
     return insts_stack_.PeekArray();
   }
 
@@ -75,7 +75,7 @@ class InstBlockStack {
 
   // Runs verification that the processing cleanly finished.
   auto VerifyOnFinish() const -> void {
-    CARBON_CHECK(empty()) << id_stack_.size();
+    CARBON_CHECK(empty(), "{0}", id_stack_.size());
   }
 
   auto empty() const -> bool { return id_stack_.empty(); }

+ 12 - 11
toolchain/check/lexical_lookup.h

@@ -43,13 +43,14 @@ class LexicalLookup {
   // Returns the lexical lookup results for a name.
   auto Get(SemIR::NameId name_id) -> llvm::SmallVector<Result, 2>& {
     auto index = GetLookupIndex(name_id);
-    CARBON_CHECK(index < lookup_.size())
-        << "An identifier was added after the Context was initialized. "
-           "Currently, we expect that new identifiers will never be used with "
-           "lexical lookup (they're added for things like detecting name "
-           "collisions in imports). That might change with metaprogramming: if "
-           "it does, we may need to start resizing `lookup_`, either on each "
-           "identifier addition or in Get` where this CHECK currently fires.";
+    CARBON_CHECK(
+        index < lookup_.size(),
+        "An identifier was added after the Context was initialized. Currently, "
+        "we expect that new identifiers will never be used with lexical lookup "
+        "(they're added for things like detecting name collisions in imports). "
+        "That might change with metaprogramming: if it does, we may need to "
+        "start resizing `lookup_`, either on each identifier addition or in "
+        "Get` where this CHECK currently fires.");
     return lookup_[index];
   }
 
@@ -57,10 +58,10 @@ class LexicalLookup {
   auto Suspend(SemIR::NameId name_id) -> SuspendedResult {
     auto index = GetLookupIndex(name_id);
     auto& results = lookup_[index];
-    CARBON_CHECK(!results.empty())
-        << "Suspending a nonexistent result for " << name_id << ".";
-    CARBON_CHECK(index <= std::numeric_limits<uint32_t>::max())
-        << "Unexpectedly large index " << index << " for name ID";
+    CARBON_CHECK(!results.empty(), "Suspending a nonexistent result for {0}.",
+                 name_id);
+    CARBON_CHECK(index <= std::numeric_limits<uint32_t>::max(),
+                 "Unexpectedly large index {0} for name ID", index);
     return {.index = static_cast<uint32_t>(index),
             .inst_id = results.pop_back_val().inst_id};
   }

+ 5 - 6
toolchain/check/member_access.cpp

@@ -80,8 +80,7 @@ static auto GetClassElementIndex(Context& context, SemIR::InstId element_id)
   if (auto base = element_inst.TryAs<SemIR::BaseDecl>()) {
     return base->index;
   }
-  CARBON_FATAL() << "Unexpected value " << element_inst
-                 << " in class element name";
+  CARBON_FATAL("Unexpected value {0} in class element name", element_inst);
 }
 
 // Returns whether `function_id` is an instance method, that is, whether it has
@@ -321,7 +320,7 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id,
   auto inst = context.insts().Get(result.inst_id);
   auto type_id = SemIR::GetTypeInSpecific(context.sem_ir(), result.specific_id,
                                           inst.type_id());
-  CARBON_CHECK(type_id.is_valid()) << "Missing type for member " << inst;
+  CARBON_CHECK(type_id.is_valid(), "Missing type for member {0}", inst);
 
   // If the named entity has a constant value that depends on its specific,
   // store the specific too.
@@ -372,9 +371,9 @@ static auto PerformInstanceBinding(Context& context, SemIR::LocId loc_id,
       // Find the specified element, which could be either a field or a base
       // class, and build an element access expression.
       auto element_id = context.constant_values().GetConstantInstId(member_id);
-      CARBON_CHECK(element_id.is_valid())
-          << "Non-constant value " << context.insts().Get(member_id)
-          << " of unbound element type";
+      CARBON_CHECK(element_id.is_valid(),
+                   "Non-constant value {0} of unbound element type",
+                   context.insts().Get(member_id));
       auto index = GetClassElementIndex(context, element_id);
       auto access_id = context.AddInst<SemIR::ClassElementAccess>(
           loc_id, {.type_id = unbound_element_type.element_type_id,

+ 6 - 6
toolchain/check/merge.cpp

@@ -348,12 +348,12 @@ static auto CheckRedeclParamSyntax(Context& context,
       !prev_first_param_node_id.is_valid()) {
     return true;
   }
-  CARBON_CHECK(new_last_param_node_id.is_valid())
-      << "new_last_param_node_id.is_valid should match "
-         "new_first_param_node_id.is_valid";
-  CARBON_CHECK(prev_last_param_node_id.is_valid())
-      << "prev_last_param_node_id.is_valid should match "
-         "prev_first_param_node_id.is_valid";
+  CARBON_CHECK(new_last_param_node_id.is_valid(),
+               "new_last_param_node_id.is_valid should match "
+               "new_first_param_node_id.is_valid");
+  CARBON_CHECK(prev_last_param_node_id.is_valid(),
+               "prev_last_param_node_id.is_valid should match "
+               "prev_first_param_node_id.is_valid");
 
   auto new_range = Parse::Tree::PostorderIterator::MakeRange(
       new_first_param_node_id, new_last_param_node_id);

+ 1 - 1
toolchain/check/node_stack.cpp

@@ -14,7 +14,7 @@ auto NodeStack::PrintForStackDump(SemIR::Formatter& formatter, int indent,
     if constexpr (Kind == Id::Kind::None) {
       output << "no value\n";
     } else if constexpr (Kind == Id::Kind::Invalid) {
-      CARBON_FATAL() << "Should not be in node stack";
+      CARBON_FATAL("Should not be in node stack");
     } else if constexpr (Kind == Id::KindFor<SemIR::InstId>()) {
       output << "\n";
       formatter.PrintInst(id.As<Id::KindFor<SemIR::InstId>()>(), indent + 4,

+ 19 - 20
toolchain/check/node_stack.h

@@ -79,11 +79,11 @@ class NodeStack {
   // IR generated by the node.
   auto Push(Parse::NodeId node_id) -> void {
     auto kind = parse_tree_->node_kind(node_id);
-    CARBON_CHECK(NodeKindToIdKind(kind) == Id::Kind::None)
-        << "Parse kind expects an Id: " << kind;
+    CARBON_CHECK(NodeKindToIdKind(kind) == Id::Kind::None,
+                 "Parse kind expects an Id: {0}", kind);
     CARBON_VLOG("Node Push {0}: {1} -> <none>\n", stack_.size(), kind);
-    CARBON_CHECK(stack_.size() < (1 << 20))
-        << "Excessive stack size: likely infinite loop";
+    CARBON_CHECK(stack_.size() < (1 << 20),
+                 "Excessive stack size: likely infinite loop");
     stack_.push_back({.node_id = node_id, .id = Id()});
   }
 
@@ -91,14 +91,13 @@ class NodeStack {
   template <typename IdT>
   auto Push(Parse::NodeId node_id, IdT id) -> void {
     auto kind = parse_tree_->node_kind(node_id);
-    CARBON_CHECK(NodeKindToIdKind(kind) == Id::KindFor<IdT>())
-        << "Parse kind expected a different IdT: " << kind << " -> " << id
-        << "\n";
-    CARBON_CHECK(id.is_valid())
-        << "Push called with invalid id: " << parse_tree_->node_kind(node_id);
+    CARBON_CHECK(NodeKindToIdKind(kind) == Id::KindFor<IdT>(),
+                 "Parse kind expected a different IdT: {0} -> {1}\n", kind, id);
+    CARBON_CHECK(id.is_valid(), "Push called with invalid id: {0}",
+                 parse_tree_->node_kind(node_id));
     CARBON_VLOG("Node Push {0}: {1} -> {2}\n", stack_.size(), kind, id);
-    CARBON_CHECK(stack_.size() < (1 << 20))
-        << "Excessive stack size: likely infinite loop";
+    CARBON_CHECK(stack_.size() < (1 << 20),
+                 "Excessive stack size: likely infinite loop");
     stack_.push_back({.node_id = node_id, .id = Id(id)});
   }
 
@@ -690,27 +689,27 @@ class NodeStack {
   // Require a Parse::NodeKind be mapped to a particular Id::Kind.
   auto RequireIdKind(Parse::NodeKind parse_kind, Id::Kind id_kind) const
       -> void {
-    CARBON_CHECK(NodeKindToIdKind(parse_kind) == id_kind)
-        << "Unexpected Id::Kind mapping for " << parse_kind << ": expected "
-        << static_cast<int>(id_kind) << ", found "
-        << static_cast<int>(NodeKindToIdKind(parse_kind));
+    CARBON_CHECK(NodeKindToIdKind(parse_kind) == id_kind,
+                 "Unexpected Id::Kind mapping for {0}: expected {1}, found {2}",
+                 parse_kind, static_cast<int>(id_kind),
+                 static_cast<int>(NodeKindToIdKind(parse_kind)));
   }
 
   // Require an entry to have the given Parse::NodeKind.
   template <const Parse::NodeKind& RequiredParseKind>
   auto RequireParseKind(Parse::NodeId node_id) const -> void {
     auto actual_kind = parse_tree_->node_kind(node_id);
-    CARBON_CHECK(RequiredParseKind == actual_kind)
-        << "Expected " << RequiredParseKind << ", found " << actual_kind;
+    CARBON_CHECK(RequiredParseKind == actual_kind, "Expected {0}, found {1}",
+                 RequiredParseKind, actual_kind);
   }
 
   // Require an entry to have the given Parse::NodeCategory.
   template <Parse::NodeCategory::RawEnumType RequiredParseCategory>
   auto RequireParseCategory(Parse::NodeId node_id) const -> void {
     auto kind = parse_tree_->node_kind(node_id);
-    CARBON_CHECK(kind.category().HasAnyOf(RequiredParseCategory))
-        << "Expected " << RequiredParseCategory << ", found " << kind
-        << " with category " << kind.category();
+    CARBON_CHECK(kind.category().HasAnyOf(RequiredParseCategory),
+                 "Expected {0}, found {1} with category {2}",
+                 RequiredParseCategory, kind, kind.category());
   }
 
   // The file's parse tree.

+ 4 - 4
toolchain/check/return.cpp

@@ -11,8 +11,8 @@ namespace Carbon::Check {
 
 // Gets the function that lexically encloses the current location.
 static auto GetCurrentFunction(Context& context) -> SemIR::Function& {
-  CARBON_CHECK(!context.return_scope_stack().empty())
-      << "Handling return but not in a function";
+  CARBON_CHECK(!context.return_scope_stack().empty(),
+               "Handling return but not in a function");
   auto function_id = context.insts()
                          .GetAs<SemIR::FunctionDecl>(
                              context.return_scope_stack().back().decl_id)
@@ -23,8 +23,8 @@ static auto GetCurrentFunction(Context& context) -> SemIR::Function& {
 // Gets the currently in scope `returned var`, if any, that would be returned
 // by a `return var;`.
 static auto GetCurrentReturnedVar(Context& context) -> SemIR::InstId {
-  CARBON_CHECK(!context.return_scope_stack().empty())
-      << "Handling return but not in a function";
+  CARBON_CHECK(!context.return_scope_stack().empty(),
+               "Handling return but not in a function");
   return context.return_scope_stack().back().returned_var;
 }
 

+ 32 - 30
toolchain/check/scope_stack.cpp

@@ -10,7 +10,7 @@
 namespace Carbon::Check {
 
 auto ScopeStack::VerifyOnFinish() -> void {
-  CARBON_CHECK(scope_stack_.empty()) << scope_stack_.size();
+  CARBON_CHECK(scope_stack_.empty(), "{0}", scope_stack_.size());
 }
 
 auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
@@ -41,13 +41,13 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
     // For lexical lookups, unqualified lookup doesn't know how to find the
     // associated specific, so if we start adding lexical scopes associated with
     // specifics, we'll need to somehow track them in lookup.
-    CARBON_CHECK(!specific_id.is_valid())
-        << "Lexical scope should not have an associated specific.";
+    CARBON_CHECK(!specific_id.is_valid(),
+                 "Lexical scope should not have an associated specific.");
   }
 
   // TODO: Handle this case more gracefully.
-  CARBON_CHECK(next_scope_index_.index != std::numeric_limits<int32_t>::max())
-      << "Ran out of scopes";
+  CARBON_CHECK(next_scope_index_.index != std::numeric_limits<int32_t>::max(),
+               "Ran out of scopes");
   ++next_scope_index_.index;
 }
 
@@ -56,8 +56,8 @@ auto ScopeStack::Pop() -> void {
 
   scope.names.ForEach([&](SemIR::NameId str_id) {
     auto& lexical_results = lexical_lookup_.Get(str_id);
-    CARBON_CHECK(lexical_results.back().scope_index == scope.index)
-        << "Inconsistent scope index for name " << str_id;
+    CARBON_CHECK(lexical_results.back().scope_index == scope.index,
+                 "Inconsistent scope index for name {0}", str_id);
     lexical_results.pop_back();
   });
 
@@ -74,10 +74,11 @@ auto ScopeStack::Pop() -> void {
 
   CARBON_CHECK(
       scope.next_compile_time_bind_index.index ==
-      static_cast<int32_t>(compile_time_binding_stack_.all_values_size()))
-      << "Wrong number of entries in compile-time binding stack, have "
-      << compile_time_binding_stack_.all_values_size() << ", expected "
-      << scope.next_compile_time_bind_index.index;
+          static_cast<int32_t>(compile_time_binding_stack_.all_values_size()),
+      "Wrong number of entries in compile-time binding stack, have {0}, "
+      "expected {1}",
+      compile_time_binding_stack_.all_values_size(),
+      scope.next_compile_time_bind_index.index);
   compile_time_binding_stack_.PopArray();
 }
 
@@ -85,9 +86,9 @@ auto ScopeStack::PopTo(ScopeIndex index) -> void {
   while (PeekIndex() > index) {
     Pop();
   }
-  CARBON_CHECK(PeekIndex() == index)
-      << "Scope index " << index << " does not enclose the current scope "
-      << PeekIndex();
+  CARBON_CHECK(PeekIndex() == index,
+               "Scope index {0} does not enclose the current scope {1}", index,
+               PeekIndex());
 }
 
 auto ScopeStack::LookupInCurrentScope(SemIR::NameId name_id) -> SemIR::InstId {
@@ -135,8 +136,8 @@ auto ScopeStack::LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id)
     -> SemIR::InstId {
   if (!scope_stack_.back().names.Insert(name_id).is_inserted()) {
     auto existing = lexical_lookup_.Get(name_id).back().inst_id;
-    CARBON_CHECK(existing.is_valid())
-        << "Name in scope but not in lexical lookups";
+    CARBON_CHECK(existing.is_valid(),
+                 "Name in scope but not in lexical lookups");
     return existing;
   }
   ++scope_stack_.back().num_names;
@@ -144,24 +145,25 @@ auto ScopeStack::LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id)
   // TODO: Reject if we previously performed a failed lookup for this name
   // in this scope or a scope nested within it.
   auto& lexical_results = lexical_lookup_.Get(name_id);
-  CARBON_CHECK(lexical_results.empty() ||
-               lexical_results.back().scope_index < PeekIndex())
-      << "Failed to clean up after scope nested within the current scope";
+  CARBON_CHECK(
+      lexical_results.empty() ||
+          lexical_results.back().scope_index < PeekIndex(),
+      "Failed to clean up after scope nested within the current scope");
   lexical_results.push_back({.inst_id = target_id, .scope_index = PeekIndex()});
   return SemIR::InstId::Invalid;
 }
 
 auto ScopeStack::SetReturnedVarOrGetExisting(SemIR::InstId inst_id)
     -> SemIR::InstId {
-  CARBON_CHECK(!return_scope_stack_.empty()) << "`returned var` in no function";
+  CARBON_CHECK(!return_scope_stack_.empty(), "`returned var` in no function");
   auto& returned_var = return_scope_stack_.back().returned_var;
   if (returned_var.is_valid()) {
     return returned_var;
   }
 
   returned_var = inst_id;
-  CARBON_CHECK(!scope_stack_.back().has_returned_var)
-      << "Scope has returned var but none is set";
+  CARBON_CHECK(!scope_stack_.back().has_returned_var,
+               "Scope has returned var but none is set");
   if (inst_id.is_valid()) {
     scope_stack_.back().has_returned_var = true;
   }
@@ -169,7 +171,7 @@ auto ScopeStack::SetReturnedVarOrGetExisting(SemIR::InstId inst_id)
 }
 
 auto ScopeStack::Suspend() -> SuspendedScope {
-  CARBON_CHECK(!scope_stack_.empty()) << "No scope to suspend";
+  CARBON_CHECK(!scope_stack_.empty(), "No scope to suspend");
   SuspendedScope result = {.entry = scope_stack_.pop_back_val(),
                            .suspended_items = {}};
   if (result.entry.scope_id.is_valid()) {
@@ -198,8 +200,8 @@ auto ScopeStack::Suspend() -> SuspendedScope {
   compile_time_binding_stack_.PopArray();
 
   // This would be easy to support if we had a need, but currently we do not.
-  CARBON_CHECK(!result.entry.has_returned_var)
-      << "Should not suspend a scope with a returned var.";
+  CARBON_CHECK(!result.entry.has_returned_var,
+               "Should not suspend a scope with a returned var.");
   return result;
 }
 
@@ -216,11 +218,11 @@ auto ScopeStack::Restore(SuspendedScope scope) -> void {
 
   CARBON_CHECK(
       scope.entry.next_compile_time_bind_index.index ==
-      static_cast<int32_t>(compile_time_binding_stack_.all_values_size()))
-      << "Wrong number of entries in compile-time binding stack "
-         "when restoring, have "
-      << compile_time_binding_stack_.all_values_size() << ", expected "
-      << scope.entry.next_compile_time_bind_index.index;
+          static_cast<int32_t>(compile_time_binding_stack_.all_values_size()),
+      "Wrong number of entries in compile-time binding stack when restoring, "
+      "have {0}, expected {1}",
+      compile_time_binding_stack_.all_values_size(),
+      scope.entry.next_compile_time_bind_index.index);
 
   if (scope.entry.scope_id.is_valid()) {
     non_lexical_scope_stack_.push_back(

+ 6 - 6
toolchain/check/sem_ir_diagnostic_converter.cpp

@@ -18,9 +18,9 @@ auto SemIRDiagnosticConverter::ConvertLoc(SemIRLoc loc,
   auto follow_import_ref = [&](SemIR::ImportIRInstId import_ir_inst_id) {
     auto import_ir_inst = cursor_ir->import_ir_insts().Get(import_ir_inst_id);
     const auto& import_ir = cursor_ir->import_irs().Get(import_ir_inst.ir_id);
-    CARBON_CHECK(import_ir.decl_id.is_valid())
-        << "If we get invalid locations here, we may need to more thoroughly "
-           "track ImportDecls.";
+    CARBON_CHECK(import_ir.decl_id.is_valid(),
+                 "If we get invalid locations here, we may need to more "
+                 "thoroughly track ImportDecls.");
 
     DiagnosticLoc in_import_loc;
     auto import_loc_id = cursor_ir->insts().GetLocId(import_ir.decl_id);
@@ -37,8 +37,8 @@ auto SemIRDiagnosticConverter::ConvertLoc(SemIRLoc loc,
           cursor_ir->import_irs().Get(implicit_import_ir_inst.ir_id);
       auto implicit_loc_id =
           implicit_ir.sem_ir->insts().GetLocId(implicit_import_ir_inst.inst_id);
-      CARBON_CHECK(implicit_loc_id.is_node_id())
-          << "Should only be one layer of implicit imports";
+      CARBON_CHECK(implicit_loc_id.is_node_id(),
+                   "Should only be one layer of implicit imports");
       in_import_loc =
           ConvertLocInFile(implicit_ir.sem_ir, implicit_loc_id.node_id(),
                            loc.token_only, context_fn);
@@ -75,7 +75,7 @@ auto SemIRDiagnosticConverter::ConvertLoc(SemIRLoc loc,
     if (auto diag_loc = handle_loc(loc.loc_id)) {
       return *diag_loc;
     }
-    CARBON_CHECK(cursor_inst_id.is_valid()) << "Should have been set";
+    CARBON_CHECK(cursor_inst_id.is_valid(), "Should have been set");
   }
 
   while (true) {

+ 6 - 6
toolchain/check/subst.cpp

@@ -45,7 +45,7 @@ class Worklist {
     worklist_.push_back({.inst_id = inst_id,
                          .is_expanded = false,
                          .next_index = static_cast<int>(worklist_.size() + 1)});
-    CARBON_CHECK(worklist_.back().next_index > 0) << "Constant too large.";
+    CARBON_CHECK(worklist_.back().next_index > 0, "Constant too large.");
   }
   auto Pop() -> SemIR::InstId { return worklist_.pop_back_val().inst_id; }
 
@@ -240,8 +240,8 @@ auto SubstInst(Context& context, SemIR::InstId inst_id,
     }
   }
 
-  CARBON_CHECK(worklist.size() == 1)
-      << "Unexpected data left behind in work list";
+  CARBON_CHECK(worklist.size() == 1,
+               "Unexpected data left behind in work list");
   return worklist.back().inst_id;
 }
 
@@ -288,8 +288,8 @@ class SubstConstantCallbacks final : public SubstInstCallbacks {
   auto Rebuild(SemIR::InstId /*old_inst_id*/, SemIR::Inst new_inst) const
       -> SemIR::InstId override {
     auto result_id = TryEvalInst(context_, SemIR::InstId::Invalid, new_inst);
-    CARBON_CHECK(result_id.is_constant())
-        << "Substitution into constant produced non-constant";
+    CARBON_CHECK(result_id.is_constant(),
+                 "Substitution into constant produced non-constant");
     return context_.constant_values().GetInstId(result_id);
   }
 
@@ -301,7 +301,7 @@ class SubstConstantCallbacks final : public SubstInstCallbacks {
 
 auto SubstConstant(Context& context, SemIR::ConstantId const_id,
                    Substitutions substitutions) -> SemIR::ConstantId {
-  CARBON_CHECK(const_id.is_constant()) << "Substituting into non-constant";
+  CARBON_CHECK(const_id.is_constant(), "Substituting into non-constant");
 
   if (substitutions.empty()) {
     // Nothing to substitute.

+ 9 - 8
toolchain/diagnostics/diagnostic_emitter.h

@@ -63,8 +63,8 @@ class DiagnosticEmitter {
     auto Note(LocT loc,
               const Internal::DiagnosticBase<Args...>& diagnostic_base,
               Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder& {
-      CARBON_CHECK(diagnostic_base.Level == DiagnosticLevel::Note)
-          << static_cast<int>(diagnostic_base.Level);
+      CARBON_CHECK(diagnostic_base.Level == DiagnosticLevel::Note, "{0}",
+                   static_cast<int>(diagnostic_base.Level));
       AddMessage(loc, diagnostic_base, {emitter_->MakeAny<Args>(args)...});
       return *this;
     }
@@ -137,9 +137,9 @@ class DiagnosticEmitter {
     static auto FormatFn(const DiagnosticMessage& message,
                          std::index_sequence<N...> /*indices*/) -> std::string {
       static_assert(sizeof...(Args) == sizeof...(N), "Invalid template args");
-      CARBON_CHECK(message.format_args.size() == sizeof...(Args))
-          << "Argument count mismatch on " << message.kind << ": "
-          << message.format_args.size() << " != " << sizeof...(Args);
+      CARBON_CHECK(message.format_args.size() == sizeof...(Args),
+                   "Argument count mismatch on {0}: {1} != {2}", message.kind,
+                   message.format_args.size(), sizeof...(Args));
       return llvm::formatv(
           message.format.data(),
           llvm::any_cast<
@@ -189,9 +189,10 @@ class DiagnosticEmitter {
   auto MakeAny(Arg arg) -> llvm::Any {
     llvm::Any converted = converter_->ConvertArg(arg);
     using Storage = Internal::DiagnosticTypeForArg<Arg>::StorageType;
-    CARBON_CHECK(llvm::any_cast<Storage>(&converted))
-        << "Failed to convert argument of type " << typeid(Arg).name()
-        << " to its storage type " << typeid(Storage).name();
+    CARBON_CHECK(
+        llvm::any_cast<Storage>(&converted),
+        "Failed to convert argument of type {0} to its storage type {1}",
+        typeid(Arg).name(), typeid(Storage).name());
     return converted;
   }
 

+ 2 - 2
toolchain/diagnostics/sorting_diagnostic_consumer.h

@@ -22,8 +22,8 @@ class SortingDiagnosticConsumer : public DiagnosticConsumer {
     // likely to refer to data that gets destroyed before the diagnostics
     // consumer is destroyed, because the diagnostics consumer is typically
     // created before the objects that diagnostics refer into are created.
-    CARBON_CHECK(diagnostics_.empty())
-        << "Must flush diagnostics consumer before destroying it";
+    CARBON_CHECK(diagnostics_.empty(),
+                 "Must flush diagnostics consumer before destroying it");
   }
 
   // Buffers the diagnostic.

+ 1 - 1
toolchain/docs/idioms.md

@@ -289,7 +289,7 @@ CARBON_DCHECK([&] {
   return complicated && multiple_parts;
 
 // finish defining the lambda, and then immediately invoke it
-}()) << "Complicated things went wrong";
+}(), "Complicated things went wrong");
 ```
 
 See a description of this technique on

+ 1 - 1
toolchain/driver/clang_runner.cpp

@@ -118,7 +118,7 @@ auto ClangRunner::Run(llvm::ArrayRef<llvm::StringRef> args) -> bool {
   // driver.CC1Main = [](llvm::SmallVectorImpl<const char*>& argv) {};
   std::unique_ptr<clang::driver::Compilation> compilation(
       driver.BuildCompilation(cstr_args));
-  CARBON_CHECK(compilation) << "Should always successfully allocate!";
+  CARBON_CHECK(compilation, "Should always successfully allocate!");
   if (compilation->containsError()) {
     // These should have been diagnosed by the driver.
     return false;

+ 1 - 1
toolchain/driver/clang_runner_test.cpp

@@ -106,7 +106,7 @@ static auto WriteTestFile(llvm::StringRef name_suffix, llvm::Twine contents)
   {
     std::error_code ec;
     llvm::raw_fd_ostream test_file_stream(test_file.string(), ec);
-    CARBON_CHECK(!ec) << "Test file error: " << ec.message();
+    CARBON_CHECK(!ec, "Test file error: {0}", ec.message());
     test_file_stream << contents;
   }
   return test_file;

+ 2 - 2
toolchain/driver/driver.cpp

@@ -946,8 +946,8 @@ auto Driver::Compile(const CompileOptions& options,
   if (options.phase == CompileOptions::Phase::Lower) {
     return make_result();
   }
-  CARBON_CHECK(options.phase == CompileOptions::Phase::CodeGen)
-      << "CodeGen should be the last stage";
+  CARBON_CHECK(options.phase == CompileOptions::Phase::CodeGen,
+               "CodeGen should be the last stage");
 
   // Codegen.
   for (auto& unit : units) {

+ 1 - 1
toolchain/driver/driver_fuzzer.cpp

@@ -20,7 +20,7 @@ static const InstallPaths* install_paths = nullptr;
 
 // NOLINTNEXTLINE(readability-non-const-parameter): External API required types.
 extern "C" auto LLVMFuzzerInitialize(int* argc, char*** argv) -> int {
-  CARBON_CHECK(*argc >= 1) << "Need the `argv[0]` value to initialize!";
+  CARBON_CHECK(*argc >= 1, "Need the `argv[0]` value to initialize!");
   install_paths = new InstallPaths(
       InstallPaths::MakeForBazelRunfiles(FindExecutablePath((*argv)[0])));
   return 0;

+ 10 - 9
toolchain/driver/driver_test.cpp

@@ -65,7 +65,7 @@ class DriverTest : public testing::Test {
     // Save our current working directory.
     std::error_code ec;
     auto original_dir = std::filesystem::current_path(ec);
-    CARBON_CHECK(!ec) << ec.message();
+    CARBON_CHECK(!ec, "{0}", ec.message());
 
     const auto* unit_test = ::testing::UnitTest::GetInstance();
     const auto* test_info = unit_test->current_test_info();
@@ -74,19 +74,20 @@ class DriverTest : public testing::Test {
                       test_info->name())
             .str());
     std::filesystem::create_directory(test_dir, ec);
-    CARBON_CHECK(!ec) << "Could not create test working dir '" << test_dir
-                      << "': " << ec.message();
+    CARBON_CHECK(!ec, "Could not create test working dir '{0}': {1}", test_dir,
+                 ec.message());
     std::filesystem::current_path(test_dir, ec);
-    CARBON_CHECK(!ec) << "Could not change the current working dir to '"
-                      << test_dir << "': " << ec.message();
+    CARBON_CHECK(!ec, "Could not change the current working dir to '{0}': {1}",
+                 test_dir, ec.message());
     return llvm::make_scope_exit([original_dir, test_dir] {
       std::error_code ec;
       std::filesystem::current_path(original_dir, ec);
-      CARBON_CHECK(!ec) << "Could not change the current working dir to '"
-                        << original_dir << "': " << ec.message();
+      CARBON_CHECK(!ec,
+                   "Could not change the current working dir to '{0}': {1}",
+                   original_dir, ec.message());
       std::filesystem::remove_all(test_dir, ec);
-      CARBON_CHECK(!ec) << "Could not remove the test working dir '" << test_dir
-                        << "': " << ec.message();
+      CARBON_CHECK(!ec, "Could not remove the test working dir '{0}': {1}",
+                   test_dir, ec.message());
     });
   }
 

+ 3 - 3
toolchain/install/install_paths.cpp

@@ -54,8 +54,8 @@ auto InstallPaths::MakeForBazelRunfiles(llvm::StringRef exe_path)
   std::string runtimes_error;
   std::unique_ptr<Runfiles> runfiles(
       Runfiles::Create(exe_path.str(), &runtimes_error));
-  CARBON_CHECK(runfiles != nullptr)
-      << "Failed to find runtimes tree: " << runtimes_error;
+  CARBON_CHECK(runfiles != nullptr, "Failed to find runtimes tree: {0}",
+               runtimes_error);
 
   std::string relative_marker_path = (PrefixRoot.str() + MarkerPath).str();
   std::string runtimes_marker_path = runfiles->Rlocation(relative_marker_path);
@@ -68,7 +68,7 @@ auto InstallPaths::MakeForBazelRunfiles(llvm::StringRef exe_path)
                           "../../");
 
   paths.CheckMarkerFile();
-  CARBON_CHECK(!paths.error()) << *paths.error();
+  CARBON_CHECK(!paths.error(), "{0}", *paths.error());
   return paths;
 }
 

+ 5 - 5
toolchain/install/install_paths_test.cpp

@@ -29,7 +29,7 @@ class InstallPathsTest : public ::testing::Test {
   InstallPathsTest() {
     std::string error;
     test_runfiles_.reset(Runfiles::Create(Testing::GetExePath().str(), &error));
-    CARBON_CHECK(test_runfiles_ != nullptr) << error;
+    CARBON_CHECK(test_runfiles_ != nullptr, "{0}", error);
   }
 
   // Test the install paths found with the given `exe_path`. Will check that
@@ -92,8 +92,8 @@ TEST_F(InstallPathsTest, PrefixRootExplicit) {
       "carbon/toolchain/install/prefix_root/lib/carbon/carbon_install.txt");
 
   llvm::StringRef prefix_path = marker_path;
-  CARBON_CHECK(prefix_path.consume_back("lib/carbon/carbon_install.txt"))
-      << "Unexpected suffix of the marker path: " << marker_path;
+  CARBON_CHECK(prefix_path.consume_back("lib/carbon/carbon_install.txt"),
+               "Unexpected suffix of the marker path: {0}", marker_path);
 
   auto paths = InstallPaths::Make(prefix_path);
   ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error();
@@ -109,8 +109,8 @@ TEST_F(InstallPathsTest, TestRunfiles) {
 TEST_F(InstallPathsTest, BinaryRunfiles) {
   std::string test_binary_path =
       test_runfiles_->Rlocation("carbon/toolchain/install/test_binary");
-  CARBON_CHECK(llvm::sys::fs::can_execute(test_binary_path))
-      << test_binary_path;
+  CARBON_CHECK(llvm::sys::fs::can_execute(test_binary_path), "{0}",
+               test_binary_path);
 
   auto paths = InstallPaths::MakeForBazelRunfiles(test_binary_path);
   ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error();

+ 3 - 3
toolchain/install/install_paths_test_helpers.cpp

@@ -15,14 +15,14 @@ auto AddPreludeFilesToVfs(InstallPaths install_paths,
   // Load the prelude into the test VFS.
   auto real_fs = llvm::vfs::getRealFileSystem();
   auto prelude = install_paths.ReadPreludeManifest();
-  CARBON_CHECK(prelude.ok()) << prelude.error();
+  CARBON_CHECK(prelude.ok(), "{0}", prelude.error());
 
   for (const auto& path : *prelude) {
     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
         real_fs->getBufferForFile(path);
-    CARBON_CHECK(file) << "Error getting file: " << file.getError().message();
+    CARBON_CHECK(file, "Error getting file: {0}", file.getError().message());
     bool added = vfs->addFile(path, /*ModificationTime=*/0, std::move(*file));
-    CARBON_CHECK(added) << "Duplicate file: " << path;
+    CARBON_CHECK(added, "Duplicate file: {0}", path);
   }
 }
 

Някои файлове не бяха показани, защото твърде много файлове са промени