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/BUILD.gn b/BUILD.gn
index a2ec171..26e58c0 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -78,6 +78,7 @@
 # Targets for all module unit test groups.
 pw_test_group("pw_module_tests") {
   group_deps = [
+    "$dir_pw_assert:tests",
     "$dir_pw_base64:tests",
     "$dir_pw_checksum:tests",
     "$dir_pw_log:tests",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3e6b40d..2c3c59d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,6 +18,7 @@
 
 include(pw_build/pigweed.cmake)
 
+add_subdirectory(pw_assert)
 add_subdirectory(pw_base64)
 add_subdirectory(pw_checksum)
 add_subdirectory(pw_cpu_exception)
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 076900d..ea7f494f 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -40,6 +40,7 @@
   deps = [
     ":core_docs",
     ":target_docs",
+    "$dir_pw_assert:docs",
     "$dir_pw_bloat:docs",
     "$dir_pw_boot_armv7m:docs",
     "$dir_pw_build:docs",
diff --git a/modules.gni b/modules.gni
index d543d55..9bf257b 100644
--- a/modules.gni
+++ b/modules.gni
@@ -16,6 +16,8 @@
 # allows modules to be moved or swapped out without breaking existing builds.
 # All module variables are prefixed with dir_.
 
+dir_pw_assert = "$dir_pigweed/pw_assert"
+dir_pw_assert_basic = "$dir_pigweed/pw_assert_basic"
 dir_pw_base64 = "$dir_pigweed/pw_base64"
 dir_pw_bloat = "$dir_pigweed/pw_bloat"
 dir_pw_boot_armv7m = "$dir_pigweed/pw_boot_armv7m"
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)
diff --git a/pw_assert_basic/BUILD b/pw_assert_basic/BUILD
new file mode 100644
index 0000000..1292e17
--- /dev/null
+++ b/pw_assert_basic/BUILD
@@ -0,0 +1,26 @@
+# 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_basic",
+    srcs = [
+        "public/pw_assert_basic/assert_basic.h",
+        "public_overrides/pw_assert_backend/assert_backend.h",
+        "assert_basic.cc",
+    ],
+)
diff --git a/pw_assert_basic/BUILD.gn b/pw_assert_basic/BUILD.gn
new file mode 100644
index 0000000..f9f77e0
--- /dev/null
+++ b/pw_assert_basic/BUILD.gn
@@ -0,0 +1,51 @@
+# 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.
+
+config("default_config") {
+  include_dirs = [ "public" ]
+}
+
+config("backend_config") {
+  include_dirs = [ "public_overrides" ]
+}
+
+if (dir_pw_assert_backend == dir_pw_assert_basic) {
+  source_set("pw_assert_basic") {
+    public_configs = [
+      ":backend_config",
+      ":default_config",
+    ]
+    deps = [
+      ":core",
+    ]
+    public = [
+      "public_overrides/pw_assert_backend/assert_backend.h",
+    ]
+    sources = public
+  }
+}
+
+source_set("core") {
+  public_configs = [ ":default_config" ]
+  deps = [
+    "$dir_pw_assert:facade",
+    "$dir_pw_dumb_io",
+    "$dir_pw_preprocessor",
+    "$dir_pw_string",
+  ]
+  public = [
+    "public/pw_assert_basic/assert_basic.h",
+  ]
+  sources = public + [ "assert_basic.cc" ]
+}
diff --git a/pw_assert_basic/assert_basic.cc b/pw_assert_basic/assert_basic.cc
new file mode 100644
index 0000000..b6abdb1
--- /dev/null
+++ b/pw_assert_basic/assert_basic.cc
@@ -0,0 +1,148 @@
+// 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 a very basic direct output log implementation with no buffering.
+
+//#define PW_LOG_MODULE_NAME "ASRT"
+//#include "pw_log/log.h"
+
+#include "pw_assert_basic/assert_basic.h"
+
+#include <cstring>
+
+#include "pw_dumb_io/dumb_io.h"
+#include "pw_preprocessor/util.h"
+#include "pw_string/string_builder.h"
+
+// TODO(pwbug/17): Expose these through the config system.
+#define PW_ASSERT_BASIC_SHOW_BANNER 1
+#define PW_ASSERT_BASIC_USE_COLORS 1
+
+// ANSI color constants to control the terminal. Not Windows compatible.
+// clang-format off
+#if PW_ASSERT_BASIC_USE_COLORS
+#define MAGENTA   "\033[35m"
+#define YELLOW    "\033[33m"
+#define RED       "\033[31m"
+#define GREEN     "\033[32m"
+#define BLUE      "\033[96m"
+#define BLACK     "\033[30m"
+#define YELLOW_BG "\033[43m"
+#define WHITE_BG  "\033[47m"
+#define RED_BG    "\033[41m"
+#define BOLD      "\033[1m"
+#define RESET     "\033[0m"
+#else
+#define MAGENTA   ""
+#define YELLOW    ""
+#define RED       ""
+#define GREEN     ""
+#define BLUE      ""
+#define BLACK     ""
+#define YELLOW_BG ""
+#define WHITE_BG  ""
+#define RED_BG    ""
+#define BOLD      ""
+#define RESET     ""
+#endif  // PW_ASSERT_BASIC_USE_COLORS
+// clang-format on
+
+static const char* kCrashBanner[] = {
+    " ",
+    "   ▄████▄      ██▀███      ▄▄▄           ██████     ██░ ██    ",
+    "  ▒██▀ ▀█     ▓██ ▒ ██▒   ▒████▄       ▒██    ▒    ▓██░ ██▒   ",
+    "  ▒▓█ 💥 ▄    ▓██ ░▄█ ▒   ▒██  ▀█▄     ░ ▓██▄      ▒██▀▀██░   ",
+    "  ▒▓▓▄ ▄██▒   ▒██▀▀█▄     ░██▄▄▄▄██      ▒   ██▒   ░▓█ ░██    ",
+    "  ▒ ▓███▀ ░   ░██▓ ▒██▒    ▓█   ▓██▒   ▒██████▒▒   ░▓█▒░██▓   ",
+    "  ░ ░▒ ▒  ░   ░ ▒▓ ░▒▓░    ▒▒   ▓▒█░   ▒ ▒▓▒ ▒ ░    ▒ ░░▒░▒   ",
+    "    ░  ▒        ░▒ ░ ▒░     ▒   ▒▒ ░   ░ ░▒  ░ ░    ▒ ░▒░ ░   ",
+    "  ░             ░░   ░      ░   ▒      ░  ░  ░      ░  ░░ ░   ",
+    "  ░ ░            ░              ░  ░         ░      ░  ░  ░   ",
+    "  ░",
+    " ",
+};
+
+using pw::dumb_io::WriteLine;
+
+typedef pw::StringBuffer<150> Buffer;
+
+extern "C" void pw_Crash(const char* file_name,
+                         int line_number,
+                         const char* function_name,
+                         const char* message,
+                         ...) {
+  // As a matter of usability, crashes should be visible; make it so.
+#if PW_ASSERT_BASIC_SHOW_BANNER
+  WriteLine(RED);
+  for (const char* line : kCrashBanner) {
+    WriteLine(line);
+  }
+  WriteLine(RESET);
+#endif  // PW_ASSERT_BASIC_SHOW_BANNER
+
+  WriteLine(
+      "  Welp, that didn't go as planned. "
+      "It seems we crashed. Terribly sorry!");
+  WriteLine("");
+  WriteLine(YELLOW "  CRASH MESSAGE" RESET);
+  WriteLine("");
+  {
+    Buffer buffer;
+    buffer << "     ";
+    va_list args;
+    va_start(args, message);
+    buffer.FormatVaList(message, args);
+    va_end(args);
+    pw::dumb_io::WriteLine(buffer.view());
+  }
+
+  WriteLine("");
+  WriteLine(YELLOW "  CRASH FILE & LINE" RESET);
+  WriteLine("");
+  {
+    Buffer buffer;
+    buffer.Format("     %s:%d", file_name, line_number);
+    WriteLine(buffer.view());
+  }
+  WriteLine("");
+  WriteLine(YELLOW "  CRASH FUNCTION" RESET);
+  WriteLine("");
+  {
+    Buffer buffer;
+    buffer.Format("     %s", function_name);
+    WriteLine(buffer.view());
+  }
+  WriteLine("");
+
+  // TODO(pwbug/95): Perhaps surprisingly, this doesn't actually crash the
+  // device. At some point we'll have a reboot BSP function or similar, but for
+  // now this is acceptable since no one is using this basic backend.
+  if (!PW_ASSERT_BASIC_DISABLE_NORETURN) {
+    WriteLine(MAGENTA "  HANG TIME" RESET);
+    WriteLine("");
+    WriteLine(
+        "     ... until a debugger joins. System is waiting in a while(1)");
+    while (1) {
+    }
+    PW_UNREACHABLE;
+  } else {
+    WriteLine(MAGENTA "  NOTE: YOU ARE IN ASSERT BASIC TEST MODE" RESET);
+    WriteLine("");
+    WriteLine("     This build returns from the crash handler for testing.");
+    WriteLine("     If you see this message in production, your build is ");
+    WriteLine("     incorrectly configured. Search for");
+    WriteLine("     PW_ASSERT_BASIC_DISABLE_NORETURN to fix it.");
+    WriteLine("");
+  }
+}
diff --git a/pw_assert_basic/public/pw_assert_basic/assert_basic.h b/pw_assert_basic/public/pw_assert_basic/assert_basic.h
new file mode 100644
index 0000000..fa27cbb
--- /dev/null
+++ b/pw_assert_basic/public/pw_assert_basic/assert_basic.h
@@ -0,0 +1,73 @@
+// 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.
+#pragma once
+
+#include "pw_preprocessor/compiler.h"
+#include "pw_preprocessor/util.h"
+
+// This is needed for testing the basic crash handler.
+// TODO(pwbug/17): Replace when Pigweed config system is added.
+#define PW_ASSERT_BASIC_DISABLE_NORETURN 0
+#if PW_ASSERT_BASIC_DISABLE_NORETURN
+#define PW_ASSERT_NORETURN
+#else
+#define PW_ASSERT_NORETURN PW_NO_RETURN
+#endif
+
+PW_EXTERN_C_START
+
+// Crash, including a message with the listed attributes.
+void pw_Crash(const char* file_name,
+              int line_number,
+              const char* function_name,
+              const char* message,
+              ...) PW_PRINTF_FORMAT(4, 5) PW_ASSERT_NORETURN;
+
+PW_EXTERN_C_END
+
+// Die with a message with many attributes included. This is the crash macro
+// frontend that funnels everything into the C handler above, pw_Crash().
+#define PW_HANDLE_CRASH(...) \
+  pw_Crash(__FILE__, __LINE__, __func__ PW_COMMA_ARGS(__VA_ARGS__))
+
+// Die with a message with many attributes included. This is the crash macro
+// frontend that funnels everything into the C handler above, pw_Crash().
+#define PW_HANDLE_ASSERT_FAILURE(condition_string, message, ...) \
+  pw_Crash(__FILE__,                                             \
+           __LINE__,                                             \
+           __func__,                                             \
+           "Check failed: " condition_string                     \
+           ". " message PW_COMMA_ARGS(__VA_ARGS__))
+
+// Sample assert failure message produced by the below implementation:
+//
+//   Check failed: current_sensor (=610) < new_sensor (=50): More details!
+//
+// Putting the value next to the operand makes the string easier to read.
+
+// clang-format off
+// This is too hairy for clang format to handle and retain readability.
+#define PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(                   \
+    arg_a_str, arg_a_val, comparison_op_str, arg_b_str, arg_b_val, \
+    type_fmt, message, ...)                                        \
+  pw_Crash(__FILE__,                                               \
+           __LINE__,                                               \
+           __func__,                                               \
+           "Check failed: "                                        \
+               arg_a_str " (=" type_fmt ") "                       \
+               comparison_op_str " "                               \
+               arg_b_str " (=" type_fmt ")"                        \
+               ". " message,                                       \
+            arg_a_val, arg_b_val PW_COMMA_ARGS(__VA_ARGS__))
+// clang-format on
diff --git a/pw_assert_basic/public_overrides/pw_assert_backend/assert_backend.h b/pw_assert_basic/public_overrides/pw_assert_backend/assert_backend.h
new file mode 100644
index 0000000..e7e1840
--- /dev/null
+++ b/pw_assert_basic/public_overrides/pw_assert_backend/assert_backend.h
@@ -0,0 +1,20 @@
+// 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 override header merely points to the true backend, in this case the
+// basic one. The reason to redirect is to permit the use of multiple backends
+// (though only pw_assert/assert.h can only point to 1 backend).
+#pragma once
+
+#include "pw_assert_basic/assert_basic.h"
diff --git a/pw_preprocessor/public/pw_preprocessor/compiler.h b/pw_preprocessor/public/pw_preprocessor/compiler.h
index 54d2716..7fa48fc 100644
--- a/pw_preprocessor/public/pw_preprocessor/compiler.h
+++ b/pw_preprocessor/public/pw_preprocessor/compiler.h
@@ -58,3 +58,20 @@
 #else
 #define PW_KEEP_IN_SECTION(name) __attribute__((section(name), used))
 #endif  // __APPLE__
+
+// Indicate to the compiler that the annotated function won't return. Example:
+//
+//   void HandleAssertFailure(ErrorCode error_code) PW_NO_RETURN;
+//
+#define PW_NO_RETURN __attribute__((noreturn))
+
+// Indicate to the compiler that the given section of code will not be reached.
+// Example:
+//
+//   int main() {
+//     InitializeBoard();
+//     vendor_StartScheduler();  // Note: vendor forgot noreturn attribute.
+//     PW_UNREACHABLE;
+//   }
+//
+#define PW_UNREACHABLE __builtin_unreachable()
diff --git a/pw_vars_default.gni b/pw_vars_default.gni
index 58d3037..b7c9ae4 100644
--- a/pw_vars_default.gni
+++ b/pw_vars_default.gni
@@ -94,6 +94,9 @@
 # All of these should default to empty strings. For target-specific defaults,
 # modify these variables in a target configuration file.
 
+# Backend for the pw_assert module.
+dir_pw_assert_backend = ""
+
 # Backend for the pw_boot module.
 dir_pw_boot_backend = ""
 
diff --git a/targets/host/host_common.gni b/targets/host/host_common.gni
index e7df5f4..4a890c3 100644
--- a/targets/host/host_common.gni
+++ b/targets/host/host_common.gni
@@ -21,6 +21,9 @@
 # Use logging-based test output on host.
 pw_unit_test_main = "$dir_pw_unit_test:logging_main"
 
+# Configure backend for assert facade.
+dir_pw_assert_backend = "$dir_pw_assert_basic"
+
 # Configure backend for pw_dumb_io facade.
 dir_pw_dumb_io_backend = "$dir_pw_dumb_io_stdio"