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:

ff7068402e/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::<String>() == 24);
    }

EXAMPLE THAT FAILS TO COMPILE:

    extern crate assertions;
    use assertions::const_assert;

    fn main() {
        // fails to compile:
        const_assert!(std::mem::size_of::<String>() == 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::<String>() == 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 <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
David Tolnay 2018-12-06 17:58:09 -08:00 committed by chrome-bot
parent f97991985d
commit 3c0aac44d7
3 changed files with 85 additions and 0 deletions

4
assertions/Cargo.toml Normal file
View file

@ -0,0 +1,4 @@
[package]
name = "assertions"
version = "0.1.0"
authors = ["The Chromium OS Authors"]

47
assertions/src/lib.rs Normal file
View file

@ -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::<String>() == 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::<String>() == 8);
/// }
/// ```
#[macro_export]
macro_rules! const_assert {
($e:expr) => {
let _: $crate::Assert<[(); $e as bool as usize]>;
};
}

View file

@ -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::<String>() == 8);
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `assertions::True`, found struct `assertions::False`
//
pub struct Assert<T: Expr<Value = True>> {
marker: PhantomData<T>,
}