From 3c0aac44d7d7150bbfb2981b92a4ed98a64a1a49 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 6 Dec 2018 17:58:09 -0800 Subject: [PATCH] assertions: Add compile-time assertion macro A static assertion is particularly appropriate when unsafe code relies on two types to have the same size, or on some type to have a particular size. This is a pattern I observed in USB code, for example: https://chromium.googlesource.com/chromiumos/platform/crosvm/+/ff7068402e6cff527a8558adf597b1596f075166/devices/src/usb/xhci/xhci_abi_schema.rs#522 EXAMPLE: extern crate assertions; use assertions::const_assert; fn main() { const_assert!(std::mem::size_of::() == 24); } EXAMPLE THAT FAILS TO COMPILE: extern crate assertions; use assertions::const_assert; fn main() { // fails to compile: const_assert!(std::mem::size_of::() == 8); } FAILURE LOOKS LIKE: error[E0271]: type mismatch resolving `<[(); 0] as assertions::Expr>::Value == assertions::True` --> src/main.rs:5:5 | 5 | const_assert!(std::mem::size_of::() == 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `assertions::True`, found struct `assertions::False` TEST=`cargo test` the new crate Change-Id: I6abe36d5a6a4bd4acb1a02e3aa7c1ece5357f007 Reviewed-on: https://chromium-review.googlesource.com/1366819 Commit-Ready: ChromeOS CL Exonerator Bot Tested-by: David Tolnay Reviewed-by: Dylan Reid --- assertions/Cargo.toml | 4 ++++ assertions/src/lib.rs | 47 +++++++++++++++++++++++++++++++++++++ assertions/src/mechanism.rs | 34 +++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 assertions/Cargo.toml create mode 100644 assertions/src/lib.rs create mode 100644 assertions/src/mechanism.rs diff --git a/assertions/Cargo.toml b/assertions/Cargo.toml new file mode 100644 index 0000000000..36f9beb5e6 --- /dev/null +++ b/assertions/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "assertions" +version = "0.1.0" +authors = ["The Chromium OS Authors"] diff --git a/assertions/src/lib.rs b/assertions/src/lib.rs new file mode 100644 index 0000000000..c53c0c4e49 --- /dev/null +++ b/assertions/src/lib.rs @@ -0,0 +1,47 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! Macros that assert properties of code at compile time. +//! +//! A static assertion is particularly appropriate when unsafe code relies on +//! two types to have the same size, or on some type to have a particular size. + +#[doc(hidden)] +pub mod mechanism; + +// Re-export so that these types appear with a more concise name in error +// messages. +#[doc(hidden)] +pub use mechanism::*; + +/// Macro that fails to compile if a given const expression is not true. +/// +/// # Example +/// +/// ```rust +/// extern crate assertions; +/// use assertions::const_assert; +/// +/// fn main() { +/// const_assert!(std::mem::size_of::() == 24); +/// } +/// ``` +/// +/// # Example that fails to compile +/// +/// ```rust,compile_fail +/// extern crate assertions; +/// use assertions::const_assert; +/// +/// fn main() { +/// // fails to compile: +/// const_assert!(std::mem::size_of::() == 8); +/// } +/// ``` +#[macro_export] +macro_rules! const_assert { + ($e:expr) => { + let _: $crate::Assert<[(); $e as bool as usize]>; + }; +} diff --git a/assertions/src/mechanism.rs b/assertions/src/mechanism.rs new file mode 100644 index 0000000000..e14b91a866 --- /dev/null +++ b/assertions/src/mechanism.rs @@ -0,0 +1,34 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::marker::PhantomData; + +pub struct True; +pub struct False; + +pub trait Expr { + type Value; +} + +impl Expr for [(); 0] { + type Value = False; +} + +impl Expr for [(); 1] { + type Value = True; +} + +// If the macro instantiates this with `T = [(); 1]` then it compiles successfully. +// +// On the other hand if `T = [(); 0]` the user receives an error like the following: +// +// error[E0271]: type mismatch resolving `<[(); 0] as assertions::Expr>::Value == assertions::True` +// --> src/main.rs:5:5 +// | +// 5 | const_assert!(std::mem::size_of::() == 8); +// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `assertions::True`, found struct `assertions::False` +// +pub struct Assert> { + marker: PhantomData, +}