Просмотр исходного кода

Restructure CHECK to provide a stream (#660)

Jon Meow 4 лет назад
Родитель
Сommit
1ddb1a264a

+ 50 - 4
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 <typename T>
+  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_

+ 22 - 1
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

+ 1 - 1
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);

+ 4 - 4
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;
   }