From 1ddb1a264aa972f29725a0d76be7f11001d3bba5 Mon Sep 17 00:00:00 2001 From: Jon Meow <46229924+jonmeow@users.noreply.github.com> Date: Tue, 20 Jul 2021 11:52:24 -0700 Subject: [PATCH] Restructure CHECK to provide a stream (#660) --- common/check.h | 54 +++++++++++++++++-- common/check_test.cpp | 23 +++++++- .../interpreter/interpreter.cpp | 2 +- executable_semantics/interpreter/stack.h | 8 +-- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/common/check.h b/common/check.h index 09ef9b9e6893..33bc11b53de0 100644 --- a/common/check.h +++ b/common/check.h @@ -9,10 +9,56 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" -#define CHECK(condition) \ - if (!(condition)) { \ - llvm::sys::PrintStackTrace(llvm::errs()); \ - llvm::report_fatal_error("CHECK failure: " #condition); \ +namespace CheckInternal { + +// Wraps a stream and exiting for CHECK. +class ExitWrapper { + public: + ExitWrapper() { + // Start by printing a stack trace. + llvm::sys::PrintStackTrace(llvm::errs()); + } + ~ExitWrapper() { + // Finish with a newline. + llvm::errs() << "\n"; + exit(-1); } + // Indicates that initial input is in, so this is where a ": " should be added + // before user input. + ExitWrapper& add_separator() { + separator = true; + return *this; + } + + // If the bool cast occurs, it's because the condition is false. This supports + // && short-circuiting the creation of ExitWrapper. + explicit operator bool() const { return true; } + + // Forward output to llvm::errs. + template + ExitWrapper& operator<<(const T& message) { + if (separator) { + llvm::errs() << ": "; + separator = false; + } + llvm::errs() << message; + return *this; + } + + private: + // Whether a separator should be printed if << is used again. + bool separator = false; +}; + +} // namespace CheckInternal + +// Checks the given condition, and if it's false, prints an error and exits. +// For example: +// CHECK(is_valid) << "Data is not valid!"; +#define CHECK(condition) \ + (!(condition)) && \ + (CheckInternal::ExitWrapper() << "CHECK failure: " #condition) \ + .add_separator() + #endif // COMMON_CHECK_H_ diff --git a/common/check_test.cpp b/common/check_test.cpp index eddaa642d1c5..87490a5214ac 100644 --- a/common/check_test.cpp +++ b/common/check_test.cpp @@ -11,7 +11,28 @@ namespace Carbon { TEST(CheckTest, CheckTrue) { CHECK(true); } TEST(CheckTest, CheckFalse) { - ASSERT_DEATH({ CHECK(false); }, "LLVM ERROR: CHECK failure: false"); + ASSERT_DEATH({ CHECK(false); }, "CHECK failure: false"); +} + +TEST(CheckTest, CheckTrueCallbackNotUsed) { + bool called = false; + auto callback = [&]() { + called = true; + return "called"; + }; + CHECK(true) << callback(); + EXPECT_FALSE(called); +} + +TEST(CheckTest, CheckFalseMessage) { + ASSERT_DEATH({ CHECK(false) << "msg"; }, "CHECK failure: false: msg"); +} + +TEST(CheckTest, CheckOutputForms) { + const char msg[] = "msg"; + std::string str = "str"; + int i = 1; + CHECK(true) << msg << str << i << 0; } } // namespace Carbon diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 5c5d265836e6..0682459ce38e 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -807,7 +807,7 @@ void StepStmt() { Frame* frame = state->stack.Top(); Action* act = frame->todo.Top(); const Statement* stmt = act->GetStatementAction().stmt; - CHECK(stmt != nullptr && "null statement!"); + CHECK(stmt != nullptr) << "null statement!"; if (tracing_output) { std::cout << "--- step stmt "; PrintStatement(stmt, 1); diff --git a/executable_semantics/interpreter/stack.h b/executable_semantics/interpreter/stack.h index 9fffa129f266..7ce147abc294 100644 --- a/executable_semantics/interpreter/stack.h +++ b/executable_semantics/interpreter/stack.h @@ -73,7 +73,7 @@ struct Stack { // // - Requires: !this->IsEmpty() auto Pop() -> T { - CHECK(!IsEmpty() && "Can't pop from empty stack."); + CHECK(!IsEmpty()) << "Can't pop from empty stack."; auto r = head->curr; head = head->next; return r; @@ -83,9 +83,9 @@ struct Stack { // // - Requires: n >= 0 && n <= Count() void Pop(int n) { - CHECK(n >= 0 && "Negative pop count disallowed."); + CHECK(n >= 0) << "Negative pop count disallowed."; while (n--) { - CHECK(head != nullptr && "Can only pop as many elements as stack has."); + CHECK(head != nullptr) << "Can only pop as many elements as stack has."; head = head->next; } } @@ -103,7 +103,7 @@ struct Stack { // // - Requires: !this->IsEmpty() auto Top() const -> T { - CHECK(!IsEmpty() && "Empty stack has no Top()."); + CHECK(!IsEmpty()) << "Empty stack has no Top()."; return head->curr; }