pw_assert: Initial assert module

This starts the assert and assert_basic modules, which are the beginning
of our assert foundation. Much more is needed; in particular the "tests"
don't really do much other than check for compilation.

Screenshot
==========

   ▄████▄      ██▀███      ▄▄▄           ██████     ██░ ██
  ▒██▀ ▀█     ▓██ ▒ ██▒   ▒████▄       ▒██    ▒    ▓██░ ██▒
  ▒▓█ 💥 ▄    ▓██ ░▄█ ▒   ▒██  ▀█▄     ░ ▓██▄      ▒██▀▀██░
  ▒▓▓▄ ▄██▒   ▒██▀▀█▄     ░██▄▄▄▄██      ▒   ██▒   ░▓█ ░██
  ▒ ▓███▀ ░   ░██▓ ▒██▒    ▓█   ▓██▒   ▒██████▒▒   ░▓█▒░██▓
  ░ ░▒ ▒  ░   ░ ▒▓ ░▒▓░    ▒▒   ▓▒█░   ▒ ▒▓▒ ▒ ░    ▒ ░░▒░▒
    ░  ▒        ░▒ ░ ▒░     ▒   ▒▒ ░   ░ ░▒  ░ ░    ▒ ░▒░ ░
  ░             ░░   ░      ░   ▒      ░  ░  ░      ░  ░░ ░
  ░ ░            ░              ░  ░         ░      ░  ░  ░
  ░

  Welp, that didn't go as planned. It seems we crashed. Terribly sorry!

  CRASH MESSAGE

     Check failed: x (=50) > y (=51300): You SHOULD see this message

  CRASH FILE & LINE

     ../../pw_assert/assert_test.cc:46

  CRASH FUNCTION

     PigweedTestBody

==========

Change-Id: I9dbb460b3b33040d8009749f44b9fc174e2c8138
diff --git a/pw_assert/BUILD b/pw_assert/BUILD
new file mode 100644
index 0000000..c6af310
--- /dev/null
+++ b/pw_assert/BUILD
@@ -0,0 +1,33 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+filegroup(
+    name = "pw_assert",
+    srcs = [
+        "public/pw_assert/assert.h",
+    ],
+)
+
+# TODO: This isn't a real Bazel build yet.
+filegroup(
+    name = "test",
+    srcs = [
+        "assert_test.cc",
+        "assert_test.c",
+    ],
+)
diff --git a/pw_assert/BUILD.gn b/pw_assert/BUILD.gn
new file mode 100644
index 0000000..2e60199
--- /dev/null
+++ b/pw_assert/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("$dir_pw_build/facade.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+
+config("default_config") {
+  include_dirs = [ "public" ]
+}
+
+pw_facade("pw_assert") {
+  backend = dir_pw_assert_backend
+  public_configs = [ ":default_config" ]
+  public = [
+    "public/pw_assert/assert.h",
+  ]
+  public_deps = [
+    dir_pw_preprocessor,
+  ]
+}
+
+pw_test_group("tests") {
+  tests = []
+  if (dir_pw_assert_backend != "") {
+    tests += [ ":assert_test" ]
+  }
+}
+
+if (dir_pw_assert_backend != "") {
+  pw_test("assert_test") {
+    deps = [
+      ":pw_assert",
+      dir_pw_assert_backend,
+    ]
+
+    sources = [
+      "assert_test.c",
+      "assert_test.cc",
+    ]
+  }
+}
+
+pw_doc_group("docs") {
+  sources = [
+    "docs.rst",
+  ]
+}
diff --git a/pw_assert/CMakeLists.txt b/pw_assert/CMakeLists.txt
new file mode 100644
index 0000000..508393f
--- /dev/null
+++ b/pw_assert/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+pw_add_facade(pw_assert
+  PUBLIC_DEPS
+    pw_preprocessor
+)
diff --git a/pw_assert/assert_test.c b/pw_assert/assert_test.c
new file mode 100644
index 0000000..a309cf0
--- /dev/null
+++ b/pw_assert/assert_test.c
@@ -0,0 +1,137 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// This is "test" verifies that the assert backend:
+//
+// - Compiles as plain C
+//
+// Unfortunately, this doesn't really test the crashing functionality since
+// that is so backend dependent.
+//
+// NOTE: To run these tests for pw_assert_basic, you must modify two things:
+//
+//   (1) Set DISABLE_ASSERT_TEST_EXECUTION 1 in assert_test.cc (this file)
+//   (1) Set DISABLE_ASSERT_TEST_EXECUTION 1 in assert_test.c
+//   (2) Set PW_ASSERT_BASIC_DISABLE_NORETURN 1 in assert_basic.h
+//
+// This is obviously not a long term solution.
+
+#include "pw_assert/assert.h"
+
+#ifdef __cplusplus
+#error "This file must be compiled as plain C to verify C compilation works."
+#endif  // __cplusplus
+
+// This is a global constant to feed into the formatter for tests.
+// Intended to pair with FAIL_IF_DISPLAYED_ARGS or FAIL_IF_HIDDEN_ARGS.
+static const int z = 10;
+
+// At some point in the future when there is a proper test system in place for
+// crashing, the below strings can help indicate pass/fail for a check.
+
+#define FAIL_IF_DISPLAYED "FAIL IF DISPLAYED"
+#define FAIL_IF_DISPLAYED_ARGS "FAIL IF DISPLAYED: %d"
+
+#define FAIL_IF_HIDDEN "FAIL IF HIDDEN"
+#define FAIL_IF_HIDDEN_ARGS "FAIL IF HIDDEN: %d"
+
+// This switch exists to support compiling and/or running the tests.
+#define DISABLE_ASSERT_TEST_EXECUTION 1
+#if DISABLE_ASSERT_TEST_EXECUTION
+#define MAYBE_SKIP_TEST return
+#else
+#define MAYBE_SKIP_TEST ;
+#endif
+
+static int Add3(int a, int b, int c) { return a + b + c; }
+
+void AssertTestsInC() {
+  {  // TEST(Crash, WithAndWithoutMessageArguments)
+    MAYBE_SKIP_TEST;
+    PW_CRASH(FAIL_IF_HIDDEN);
+    PW_CRASH(FAIL_IF_HIDDEN_ARGS, z);
+  }
+
+  {  // TEST(Check, NoMessage)
+    MAYBE_SKIP_TEST;
+    PW_CHECK(1);
+    PW_CHECK(0);
+  }
+
+  {  // TEST(Check, WithMessageAndArgs)
+    MAYBE_SKIP_TEST;
+    PW_CHECK(1, FAIL_IF_DISPLAYED);
+    PW_CHECK(1, FAIL_IF_DISPLAYED_ARGS, z);
+
+    PW_CHECK(0, FAIL_IF_HIDDEN);
+    PW_CHECK(0, FAIL_IF_HIDDEN_ARGS, z);
+  }
+
+  {  // TEST(Check, IntComparison)
+    MAYBE_SKIP_TEST;
+    int x_int = 50;
+    int y_int = 66;
+
+    PW_CHECK_INT_LE(x_int, y_int);
+    PW_CHECK_INT_LE(x_int, y_int, "INT: " FAIL_IF_DISPLAYED);
+    PW_CHECK_INT_LE(x_int, y_int, "INT: " FAIL_IF_DISPLAYED_ARGS, z);
+
+    PW_CHECK_INT_GE(x_int, y_int);
+    PW_CHECK_INT_GE(x_int, y_int, "INT: " FAIL_IF_HIDDEN);
+    PW_CHECK_INT_GE(x_int, y_int, "INT: " FAIL_IF_HIDDEN_ARGS, z);
+  }
+
+  {  // TEST(Check, UintComparison)
+    MAYBE_SKIP_TEST;
+    unsigned int x_uint = 50;
+    unsigned int y_uint = 66;
+
+    PW_CHECK_UINT_LE(x_uint, y_uint);
+    PW_CHECK_UINT_LE(x_uint, y_uint, "UINT: " FAIL_IF_DISPLAYED);
+    PW_CHECK_UINT_LE(x_uint, y_uint, "UINT: " FAIL_IF_DISPLAYED_ARGS, z);
+
+    PW_CHECK_UINT_GE(x_uint, y_uint);
+    PW_CHECK_UINT_GE(x_uint, y_uint, "UINT: " FAIL_IF_HIDDEN);
+    PW_CHECK_UINT_GE(x_uint, y_uint, "UINT: " FAIL_IF_HIDDEN_ARGS, z);
+  }
+
+  {  // TEST(Check, FloatComparison)
+    MAYBE_SKIP_TEST;
+    float x_float = 50.5;
+    float y_float = 66.5;
+
+    PW_CHECK_FLOAT_LE(x_float, y_float);
+    PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED);
+    PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED_ARGS, z);
+
+    PW_CHECK_FLOAT_GE(x_float, y_float);
+    PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN);
+    PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN_ARGS, z);
+  }
+
+  {  // TEST(Check, ComparisonArgumentsWithCommas)
+    MAYBE_SKIP_TEST;
+    int x_int = 50;
+    int y_int = 66;
+
+    PW_CHECK_INT_LE(Add3(1, 2, 3), y_int);
+    PW_CHECK_INT_LE(x_int, Add3(1, 2, 3));
+
+    PW_CHECK_INT_LE(Add3(1, 2, 3), y_int, FAIL_IF_DISPLAYED);
+    PW_CHECK_INT_LE(x_int, Add3(1, 2, 3), FAIL_IF_DISPLAYED_ARGS, z);
+
+    PW_CHECK_INT_LE(Add3(1, 2, 3), Add3(1, 2, 3), "INT: " FAIL_IF_DISPLAYED);
+    PW_CHECK_INT_LE(x_int, y_int, "INT: " FAIL_IF_DISPLAYED_ARGS, z);
+  }
+}
diff --git a/pw_assert/assert_test.cc b/pw_assert/assert_test.cc
new file mode 100644
index 0000000..89cd39b
--- /dev/null
+++ b/pw_assert/assert_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// This is mostly a compile test to verify that the log backend is able to
+// compile the constructs promised by the logging facade; and that when run,
+// there is no crash.
+//
+// TODO(pwbug/88): Add verification of the actually logged statements.
+
+// clang-format off
+#include "pw_assert/assert.h"
+// clang-format on
+
+#include "gtest/gtest.h"
+
+// This is a global constant to feed into the formatter for tests.
+// Intended to pair with FAIL_IF_DISPLAYED_ARGS or FAIL_IF_HIDDEN_ARGS.
+static const int z = 10;
+
+// At some point in the future when there is a proper test system in place for
+// crashing, the below strings can help indicate pass/fail for a check.
+
+#define FAIL_IF_DISPLAYED "FAIL IF DISPLAYED"
+#define FAIL_IF_DISPLAYED_ARGS "FAIL IF DISPLAYED: %d"
+
+#define FAIL_IF_HIDDEN "FAIL IF HIDDEN"
+#define FAIL_IF_HIDDEN_ARGS "FAIL IF HIDDEN: %d"
+
+// This switch exists to support compiling and/or running the tests.
+#define DISABLE_ASSERT_TEST_EXECUTION 1
+#if DISABLE_ASSERT_TEST_EXECUTION
+#define MAYBE_SKIP_TEST return
+#else
+#define MAYBE_SKIP_TEST ;
+#endif
+
+namespace {
+
+TEST(Crash, WithAndWithoutMessageArguments) {
+  MAYBE_SKIP_TEST;
+  PW_CRASH(FAIL_IF_HIDDEN);
+  PW_CRASH(FAIL_IF_HIDDEN_ARGS, z);
+}
+
+TEST(Check, NoMessage) {
+  MAYBE_SKIP_TEST;
+  PW_CHECK(true);
+  PW_CHECK(false);
+}
+
+TEST(Check, WithMessageAndArgs) {
+  MAYBE_SKIP_TEST;
+  PW_CHECK(true, FAIL_IF_DISPLAYED);
+  PW_CHECK(true, FAIL_IF_DISPLAYED_ARGS, z);
+
+  PW_CHECK(false, FAIL_IF_HIDDEN);
+  PW_CHECK(false, FAIL_IF_HIDDEN_ARGS, z);
+}
+
+TEST(Check, IntComparison) {
+  MAYBE_SKIP_TEST;
+  int x_int = 50;
+  int y_int = 66;
+
+  PW_CHECK_INT_LE(x_int, y_int);
+  PW_CHECK_INT_LE(x_int, y_int, "INT: " FAIL_IF_DISPLAYED);
+  PW_CHECK_INT_LE(x_int, y_int, "INT: " FAIL_IF_DISPLAYED_ARGS, z);
+
+  PW_CHECK_INT_GE(x_int, y_int);
+  PW_CHECK_INT_GE(x_int, y_int, "INT: " FAIL_IF_HIDDEN);
+  PW_CHECK_INT_GE(x_int, y_int, "INT: " FAIL_IF_HIDDEN_ARGS, z);
+}
+
+TEST(Check, UintComparison) {
+  MAYBE_SKIP_TEST;
+  unsigned int x_uint = 50;
+  unsigned int y_uint = 66;
+
+  PW_CHECK_UINT_LE(x_uint, y_uint);
+  PW_CHECK_UINT_LE(x_uint, y_uint, "UINT: " FAIL_IF_DISPLAYED);
+  PW_CHECK_UINT_LE(x_uint, y_uint, "UINT: " FAIL_IF_DISPLAYED_ARGS, z);
+
+  PW_CHECK_UINT_GE(x_uint, y_uint);
+  PW_CHECK_UINT_GE(x_uint, y_uint, "UINT: " FAIL_IF_HIDDEN);
+  PW_CHECK_UINT_GE(x_uint, y_uint, "UINT: " FAIL_IF_HIDDEN_ARGS, z);
+}
+
+TEST(Check, FloatComparison) {
+  MAYBE_SKIP_TEST;
+  float x_float = 50.5;
+  float y_float = 66.5;
+
+  PW_CHECK_FLOAT_LE(x_float, y_float);
+  PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED);
+  PW_CHECK_FLOAT_LE(x_float, y_float, "FLOAT: " FAIL_IF_DISPLAYED_ARGS, z);
+
+  PW_CHECK_FLOAT_GE(x_float, y_float);
+  PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN);
+  PW_CHECK_FLOAT_GE(x_float, y_float, "FLOAT: " FAIL_IF_HIDDEN_ARGS, z);
+}
+
+static int Add3(int a, int b, int c) { return a + b + c; }
+
+TEST(Check, ComparisonArgumentsWithCommas) {
+  MAYBE_SKIP_TEST;
+  int x_int = 50;
+  int y_int = 66;
+
+  PW_CHECK_INT_LE(Add3(1, 2, 3), y_int);
+  PW_CHECK_INT_LE(x_int, Add3(1, 2, 3));
+
+  PW_CHECK_INT_LE(Add3(1, 2, 3), y_int, FAIL_IF_DISPLAYED);
+  PW_CHECK_INT_LE(x_int, Add3(1, 2, 3), FAIL_IF_DISPLAYED_ARGS, z);
+
+  PW_CHECK_INT_LE(Add3(1, 2, 3), Add3(1, 2, 3), "INT: " FAIL_IF_DISPLAYED);
+  PW_CHECK_INT_LE(x_int, y_int, "INT: " FAIL_IF_DISPLAYED_ARGS, z);
+}
+
+// These are defined in assert_test.c, to test C compatibility.
+extern "C" {
+void AssertTestsInC();
+}  // extern "C"
+
+TEST(Check, AssertTestsInC) {
+  MAYBE_SKIP_TEST;
+  AssertTestsInC();
+}
+
+static int global_state_for_multi_evaluate_test;
+static int IncrementsGlobal() {
+  global_state_for_multi_evaluate_test++;
+  return 0;
+}
+
+// This test verifies that the binary CHECK_*(x,y) macros only
+// evaluate their arguments once.
+TEST(Check, BinaryOpOnlyEvaluatesOnce) {
+  MAYBE_SKIP_TEST;
+
+  global_state_for_multi_evaluate_test = 0;
+  PW_CHECK_INT_EQ(0, IncrementsGlobal());
+  EXPECT_EQ(global_state_for_multi_evaluate_test, 1);
+
+  global_state_for_multi_evaluate_test = 0;
+  PW_CHECK_INT_EQ(IncrementsGlobal(), IncrementsGlobal());
+  EXPECT_EQ(global_state_for_multi_evaluate_test, 2);
+
+  // Fails; should only evaluate IncrementGlobal() once.
+  global_state_for_multi_evaluate_test = 0;
+  PW_CHECK_INT_EQ(1, IncrementsGlobal());
+  EXPECT_EQ(global_state_for_multi_evaluate_test, 1);
+
+  global_state_for_multi_evaluate_test = 0;
+  PW_CHECK_INT_EQ(IncrementsGlobal(), 1 + IncrementsGlobal());
+  EXPECT_EQ(global_state_for_multi_evaluate_test, 2);
+}
+
+}  // namespace
diff --git a/pw_assert/docs.rst b/pw_assert/docs.rst
new file mode 100644
index 0000000..44ed397
--- /dev/null
+++ b/pw_assert/docs.rst
@@ -0,0 +1,27 @@
+.. _chapter-pw-assert:
+
+.. default-domain:: cpp
+
+.. highlight:: cpp
+
+---------
+pw_assert
+---------
+Pigweed's assert module provides facilities for applications to check
+preconditions.  The module is split into two components:
+
+1. The facade (this module) which is only a macro interface layer
+2. The backend, provided elsewhere, that implements the low level checks
+
+Basic usage example
+-------------------
+
+.. code-block:: cpp
+
+  #include "pw_assert/assert.h"
+
+  int main() {
+    bool sensor_running = StartSensor(&msg);
+    PW_CHECK(sensor_running, "Sensor failed to start; code: %s", msg);
+  }
+
diff --git a/pw_assert/public/pw_assert/assert.h b/pw_assert/public/pw_assert/assert.h
new file mode 100644
index 0000000..f1a4d07
--- /dev/null
+++ b/pw_assert/public/pw_assert/assert.h
@@ -0,0 +1,249 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+// =============================================================================
+//
+// This file describes Pigweed's public user-facing assert API.
+//
+// THIS API IS NOT STABLE OR COMPLETE! NEITHER FACADE NOR BACKEND API!
+//
+#pragma once
+
+#include "pw_preprocessor/macro_arg_count.h"
+
+// The pw_assert public API:
+//
+//   Trigger a crash with a message. Replaces LOG_FATAL() in other systems.
+//   PW_CRASH(msg, ...)
+//
+//   Asserts the condition, crashes on failure. Equivalent to assert.
+//   PW_CHECK(condition) or
+//   PW_CHECK(condition, msg, ...)
+//
+//   In many cases an assert is a binary comparison. In those cases, using the
+//   special binary assert macros below for <, <=, >, >=, == enables reporting
+//   the values of the operands in addition to the string of the condition.
+//
+//   In all cases, the message argument is optional:
+//   PW_CHECK_INT_LE(x, y) or
+//   PW_CHECK_INT_LE(x, y, "Was booting %s subsystem", subsystem_name)
+//
+//   Binary comparison asserts for 'int' type ("%d" in format strings):
+//   PW_CHECK_INT_LE  (a, b, msg, ...)  Asserts a <= b
+//   PW_CHECK_INT_LT  (a, b, msg, ...)  Asserts a <  b
+//   PW_CHECK_INT_GE  (a, b, msg, ...)  Asserts a >= b
+//   PW_CHECK_INT_GT  (a, b, msg, ...)  Asserts a >  b
+//   PW_CHECK_INT_EQ  (a, b, msg, ...)  Asserts a == b
+//
+//   Binary comparison asserts for 'unsigned int' type ("%u" in format strings):
+//   PW_CHECK_UINT_LE (a, b, msg, ...)  Asserts a <= b
+//   PW_CHECK_UINT_LT (a, b, msg, ...)  Asserts a <  b
+//   PW_CHECK_UINT_GE (a, b, msg, ...)  Asserts a >= b
+//   PW_CHECK_UINT_GT (a, b, msg, ...)  Asserts a >  b
+//   PW_CHECK_UINT_EQ (a, b, msg, ...)  Asserts a == b
+//
+//   Binary comparison asserts for 'float' type ("%f" in format strings):
+//   PW_CHECK_FLOAT_LE(a, b, msg, ...)  Asserts a <= b
+//   PW_CHECK_FLOAT_LT(a, b, msg, ...)  Asserts a <  b
+//   PW_CHECK_FLOAT_GE(a, b, msg, ...)  Asserts a >= b
+//   PW_CHECK_FLOAT_GT(a, b, msg, ...)  Asserts a >  b
+//   PW_CHECK_FLOAT_EQ(a, b, msg, ...)  Asserts a == b
+//
+//   Note: PW_CRASH is the equivalent of LOG_FATAL in other systems, where a
+//   device crash is triggered with a message. In Pigweed, logging and
+//   crashing/asserting are separated. There is a LOG_CRITICAL level in the
+//   logging module, but it does not have side effects; for LOG_FATAL, instead
+//   use this macro (PW_CRASH).
+//
+// The pw_assert_backend must provide these macros:
+//
+//   PW_HANDLE_CRASH(msg, ...)
+//   PW_HANDLE_ASSERT_FAILURE(condition, msg, ...)
+//   PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(a, op, b, type_fmt, msg, ...)
+//
+//   The low level functionality of triggering a crash, rebooting a device,
+//   collecting information, or hanging out in a while(1) loop, must be
+//   provided by the underlying assert backend as part of the crash or assert
+//   failure handling.
+//
+//   Note that for the assert failures, the handler should assume the assert
+//   has already failed (the facade checks the condition before delegating).
+//
+#include "pw_assert_backend/assert_backend.h"
+
+// PW_CRASH - Crash the system, with a message.
+#define PW_CRASH(message, ...) \
+  PW_HANDLE_CRASH(message PW_COMMA_ARGS(__VA_ARGS__))
+
+// PW_CHECK - If condition evaluates to false, crash. Message optional.
+#define PW_CHECK(condition, ...)                              \
+  do {                                                        \
+    if (!(condition)) {                                       \
+      _PW_CHECK_SELECT_MACRO(                                 \
+          #condition, PW_HAS_ARGS(__VA_ARGS__), __VA_ARGS__); \
+    }                                                         \
+  } while (0)
+
+// PW_CHECK_<type>_<comparison> - if
+// Checks for int: LE, LT, GE, GT, EQ.
+#define PW_CHECK_INT_LE(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, int, "%d", __VA_ARGS__)
+#define PW_CHECK_INT_LT(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, <, argb, int, "%d", __VA_ARGS__)
+#define PW_CHECK_INT_GE(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, int, "%d", __VA_ARGS__)
+#define PW_CHECK_INT_GT(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, >, argb, int, "%d", __VA_ARGS__)
+#define PW_CHECK_INT_EQ(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, int, "%d", __VA_ARGS__)
+
+// Checks for unsigned int: LE, LT, GE, GT, EQ.
+#define PW_CHECK_UINT_LE(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, unsigned int, "%u", __VA_ARGS__)
+#define PW_CHECK_UINT_LT(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, <, argb, unsigned int, "%u", __VA_ARGS__)
+#define PW_CHECK_UINT_GE(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, unsigned int, "%u", __VA_ARGS__)
+#define PW_CHECK_UINT_GT(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, >, argb, unsigned int, "%u", __VA_ARGS__)
+#define PW_CHECK_UINT_EQ(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, unsigned int, "%u", __VA_ARGS__)
+
+// Checks for float: LE, LT, GE, GT, EQ.
+#define PW_CHECK_FLOAT_LE(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_LT(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, <, argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_GE(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_GT(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, >, argb, float, "%f", __VA_ARGS__)
+#define PW_CHECK_FLOAT_EQ(arga, argb, ...) \
+  _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, float, "%f", __VA_ARGS__)
+
+// =========================================================================
+// Implementation for PW_CHECK
+
+// TODO: Explain why we must expand another time.
+#define _PW_CHECK_SELECT_MACRO(condition, has_args, ...) \
+  _PW_CHECK_SELECT_MACRO_EXPANDED(condition, has_args, __VA_ARGS__)
+
+// Delegate to the macro
+#define _PW_CHECK_SELECT_MACRO_EXPANDED(condition, has_args, ...) \
+  _PW_CHECK_HAS_MSG_##has_args(condition, __VA_ARGS__)
+
+// PW_CHECK version 1: No message or args
+#define _PW_CHECK_HAS_MSG_0(condition, ignored_arg) \
+  PW_HANDLE_ASSERT_FAILURE(condition, "")
+
+// PW_CHECK version 2: With message (and maybe args)
+#define _PW_CHECK_HAS_MSG_1(condition, ...) \
+  PW_HANDLE_ASSERT_FAILURE(condition, __VA_ARGS__)
+
+// =========================================================================
+// Implementation for PW_CHECK_<type>_<comparison>
+
+// TODO: Explain why we must expand another time.
+#define _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(argument_a_str,       \
+                                                 argument_a_val,       \
+                                                 comparison_op_str,    \
+                                                 argument_b_str,       \
+                                                 argument_b_val,       \
+                                                 type_fmt,             \
+                                                 has_args,             \
+                                                 ...)                  \
+  _PW_CHECK_SELECT_BINARY_COMPARISON_MACRO_EXPANDED(argument_a_str,    \
+                                                    argument_a_val,    \
+                                                    comparison_op_str, \
+                                                    argument_b_str,    \
+                                                    argument_b_val,    \
+                                                    type_fmt,          \
+                                                    has_args,          \
+                                                    __VA_ARGS__)
+
+// Delegate to the macro
+#define _PW_CHECK_SELECT_BINARY_COMPARISON_MACRO_EXPANDED(argument_a_str,    \
+                                                          argument_a_val,    \
+                                                          comparison_op_str, \
+                                                          argument_b_str,    \
+                                                          argument_b_val,    \
+                                                          type_fmt,          \
+                                                          has_args,          \
+                                                          ...)               \
+  _PW_CHECK_BINARY_COMPARISON_HAS_MSG_##has_args(argument_a_str,             \
+                                                 argument_a_val,             \
+                                                 comparison_op_str,          \
+                                                 argument_b_str,             \
+                                                 argument_b_val,             \
+                                                 type_fmt,                   \
+                                                 __VA_ARGS__)
+
+// PW_CHECK_BINARY_COMPARISON version 1: No message or args
+#define _PW_CHECK_BINARY_COMPARISON_HAS_MSG_0(argument_a_str,    \
+                                              argument_a_val,    \
+                                              comparison_op_str, \
+                                              argument_b_str,    \
+                                              argument_b_val,    \
+                                              type_fmt,          \
+                                              ignored_arg)       \
+  PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(argument_a_str,        \
+                                          argument_a_val,        \
+                                          comparison_op_str,     \
+                                          argument_b_str,        \
+                                          argument_b_val,        \
+                                          type_fmt,              \
+                                          "");
+
+// PW_CHECK_BINARY_COMPARISON version 2: With message (and maybe args)
+//
+// TODO(pwbug/117): Passing __VA_ARGS__ below should instead use
+// PW_COMMA_ARGS(), but for some reason this isn't working. For now, leave it.
+#define _PW_CHECK_BINARY_COMPARISON_HAS_MSG_1(argument_a_str,    \
+                                              argument_a_val,    \
+                                              comparison_op_str, \
+                                              argument_b_str,    \
+                                              argument_b_val,    \
+                                              type_fmt,          \
+                                              ...)               \
+  PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(argument_a_str,        \
+                                          argument_a_val,        \
+                                          comparison_op_str,     \
+                                          argument_b_str,        \
+                                          argument_b_val,        \
+                                          type_fmt,              \
+                                          __VA_ARGS__);
+
+// For the binary assertions, this private macro is re-used for all the
+// variants. Due to limitations of C formatting, it is necessary to have
+// separate macros for the types.
+//
+// The macro avoids evaluating the arguments multiple times at the cost of some
+// macro complexity.
+//
+// TODO: Concat names with __LINE__; requires an extra layer of macros.
+#define _PW_CHECK_BINARY_CMP_IMPL(                                       \
+    argument_a, comparison_op, argument_b, type_decl, type_fmt, ...)     \
+  do {                                                                   \
+    type_decl evaluated_argument_a = (type_decl)(argument_a);            \
+    type_decl evaluated_argument_b = (type_decl)(argument_b);            \
+    if (!(evaluated_argument_a comparison_op evaluated_argument_b)) {    \
+      _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(#argument_a,              \
+                                               evaluated_argument_a,     \
+                                               #comparison_op,           \
+                                               #argument_b,              \
+                                               evaluated_argument_b,     \
+                                               type_fmt,                 \
+                                               PW_HAS_ARGS(__VA_ARGS__), \
+                                               __VA_ARGS__);             \
+    }                                                                    \
+  } while (0)