Skip to content

Commit dd89a2e

Browse files
committed
rust: improve static_assert!
Now `static_assert!` allows for several forms: - Boolean assertion: `expr`. - Set membership assertion: `(expr) is in {a0, ..., aN}`. - Interval membership assertion: `(expr) is in [min, max]`. - Fits-in-type assertion: `(expr) fits in type`. These are all `static_assert!`s instead of being split into a separate macro (e.g. `fits_in!`) because we want to ensure they are only within/as a `static_assert!` (e.g. for the `i128` trick). The documentation is also improved for the boolean assertion case. Signed-off-by: Miguel Ojeda <[email protected]>
1 parent fc2b177 commit dd89a2e

File tree

1 file changed

+166
-3
lines changed

1 file changed

+166
-3
lines changed

rust/kernel/static_assert.rs

+166-3
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,198 @@
44
55
/// Static assert (i.e. compile-time assert).
66
///
7-
/// Similar to C11 [`_Static_assert`] and C++11 [`static_assert`].
7+
/// There are several forms of this macro:
8+
///
9+
/// - Boolean assertion: `expr`.
10+
/// - Set membership assertion: `(expr) is in {a0, ..., aN}`.
11+
/// - Interval membership assertion: `(expr) is in [min, max]`.
12+
/// - Fits-in-type assertion: `(expr) fits in type`.
13+
///
14+
/// The expressions in all the forms are evaluated in [const context].
15+
///
16+
/// [const context]: https://doc.rust-lang.org/reference/const_eval.html
817
///
18+
/// # Boolean assertion: `expr`
19+
///
20+
/// Statically asserts the given expression.
21+
///
22+
/// Similar to C11 [`_Static_assert`] and C++11 [`static_assert`].
923
/// The feature may be added to Rust in the future: see [RFC 2790].
1024
///
1125
/// [`_Static_assert`]: https://en.cppreference.com/w/c/language/_Static_assert
1226
/// [`static_assert`]: https://en.cppreference.com/w/cpp/language/static_assert
1327
/// [RFC 2790]: https://github.com/rust-lang/rfcs/issues/2790
1428
///
15-
/// # Examples
29+
/// ## Examples
1630
///
1731
/// ```
32+
/// // Trivial assert.
1833
/// static_assert!(42 > 24);
34+
///
35+
/// // Assert on sizes, similar to C's `sizeof(T)`.
1936
/// static_assert!(core::mem::size_of::<u8>() == 1);
2037
///
38+
/// // Assert on binary string.
2139
/// const X: &[u8] = b"bar";
22-
/// static_assert!(X[1] == 'a' as u8);
40+
/// static_assert!(X[1] == b'a');
41+
///
42+
/// // Check we uphold some constraint from the C side by testing the bindings.
43+
/// static_assert!(RUST_BUFFER_SIZE >= bindings::LOG_LINE_MAX);
2344
///
45+
/// // Calling `const fn`s is possible.
2446
/// const fn f(x: i32) -> i32 {
2547
/// x + 2
2648
/// }
2749
/// static_assert!(f(40) == 42);
2850
/// ```
51+
///
52+
/// # Set membership assertion: `(expr) is in {a0, ..., aN}`
53+
///
54+
/// Statically asserts that the given expression (typically a `const` integer) is in a set.
55+
/// The negated form (`is not in`) is also available.
56+
///
57+
/// ## Examples
58+
///
59+
/// ```
60+
/// // Trivial usage.
61+
/// static_assert!((-2) is not in {-1, 0, 2});
62+
/// static_assert!((-1) is in {-1, 0, 2});
63+
/// static_assert!(( 0) is in {-1, 0, 2});
64+
/// static_assert!(( 1) is not in {-1, 0, 2});
65+
/// static_assert!(( 2) is in {-1, 0, 2});
66+
/// static_assert!(( 3) is not in {-1, 0, 2});
67+
///
68+
/// // Typical usage.
69+
/// static_assert!((SOME_CONSTANT_DEPENDING_ON_ARCH) is in {FOO, BAR, BAZ});
70+
/// static_assert!((core::mem::size_of::<usize>()) is in {4, 8});
71+
/// ```
72+
///
73+
/// # Interval membership assertion: `(expr) is in [min, max]`
74+
///
75+
/// Statically asserts that the given expression (typically a `const` integer) is in a closed
76+
/// interval (i.e. inclusive range). The negated form (`is not in`) is also available.
77+
///
78+
/// ## Examples
79+
///
80+
/// ```
81+
/// // Trivial usage.
82+
/// static_assert!((-2) is not in [-1, 2]);
83+
/// static_assert!((-1) is in [-1, 2]);
84+
/// static_assert!(( 0) is in [-1, 2]);
85+
/// static_assert!(( 1) is in [-1, 2]);
86+
/// static_assert!(( 2) is in [-1, 2]);
87+
/// static_assert!(( 3) is not in [-1, 2]);
88+
///
89+
/// // Typical usage.
90+
/// static_assert!((FOO) is in [MIN_FOO, MAX_FOO]);
91+
/// ```
92+
///
93+
/// # Fits-in-type assertion: `(expr) fits in type`
94+
///
95+
/// Statically asserts that the given expression (typically a `const` integer) fits in the given
96+
/// type (which must provide `T::MIN` and `T::MAX`). The negated form (`does not fit in`) is also
97+
/// available.
98+
///
99+
/// Casting a "kernel integer" (i.e. up to [`i64`]/[`u64`]) to [`i128`] within the expression is
100+
/// allowed to easily manipulate integers: no 128-bit code will be generated since it will be
101+
/// evaluated in a const context.
102+
///
103+
/// ## Examples
104+
///
105+
/// ```
106+
/// // Trivial usage.
107+
/// static_assert!(( -1) does not fit in u8);
108+
/// static_assert!(( 0) fits in u8);
109+
/// static_assert!((255) fits in u8);
110+
/// static_assert!((256) does not fit in u8);
111+
///
112+
/// // Two's complement.
113+
/// static_assert!((-128) fits in i8);
114+
/// static_assert!(( 127) fits in i8);
115+
/// static_assert!(( 128) does not fit in i8);
116+
///
117+
/// // Using `i128` for easy manipulation of integers.
118+
/// const MAX_ERRNO: u32 = 4095;
119+
/// static_assert!((-(MAX_ERRNO as i128)) fits in i16);
120+
/// ```
29121
#[macro_export]
30122
macro_rules! static_assert {
123+
// Boolean assertion: `expr`.
31124
($condition:expr) => {
32125
// Based on the latest one in `rustc`'s one before it was [removed].
33126
//
34127
// [removed]: https://github.com/rust-lang/rust/commit/c2dad1c6b9f9636198d7c561b47a2974f5103f6d
35128
#[allow(dead_code)]
36129
const _: () = [()][!($condition) as usize];
37130
};
131+
132+
// Set membership assertion: `(expr) is in {a0, ..., aN}`.
133+
(($expression:expr) is in {$($a:expr),+}) => {
134+
static_assert!( $(($expression) == ($a))||* );
135+
};
136+
(($expression:expr) is not in {$($a:expr),+}) => {
137+
static_assert!(!($(($expression) == ($a))||*));
138+
};
139+
140+
// Interval membership assertion: `(expr) is in [min, max]`.
141+
(($expression:expr) is in [$min:expr, $max:expr]) => {
142+
static_assert!( ($expression) >= ($min) && ($expression) <= ($max) );
143+
};
144+
(($expression:expr) is not in [$min:expr, $max:expr]) => {
145+
static_assert!(!(($expression) >= ($min) && ($expression) <= ($max)));
146+
};
147+
148+
// Fits-in-type assertion: `(expr) fits in type`.
149+
(($expression:expr) fits in $t:ty) => {
150+
static_assert!(($expression) is in [<$t>::MIN as i128, <$t>::MAX as i128]);
151+
};
152+
(($expression:expr) does not fit in $t:ty) => {
153+
static_assert!(($expression) is not in [<$t>::MIN as i128, <$t>::MAX as i128]);
154+
};
155+
}
156+
157+
// Tests.
158+
//
159+
// These should later on go into a proper test.
160+
161+
static_assert!(42 > 24);
162+
static_assert!(core::mem::size_of::<u8>() == 1);
163+
164+
const X: &[u8] = b"bar";
165+
static_assert!(X[1] == b'a');
166+
167+
const fn f(x: i32) -> i32 {
168+
x + 2
38169
}
170+
static_assert!(f(40) == 42);
171+
172+
static_assert!((-2) is not in {-1, 0, 2});
173+
static_assert!((-1) is in {-1, 0, 2});
174+
static_assert!(( 0) is in {-1, 0, 2});
175+
static_assert!(( 1) is not in {-1, 0, 2});
176+
static_assert!(( 2) is in {-1, 0, 2});
177+
static_assert!(( 3) is not in {-1, 0, 2});
178+
179+
static_assert!((core::mem::size_of::<usize>()) is in {4, 8});
180+
181+
static_assert!((-2) is not in [-1, 2]);
182+
static_assert!((-1) is in [-1, 2]);
183+
static_assert!(( 0) is in [-1, 2]);
184+
static_assert!(( 1) is in [-1, 2]);
185+
static_assert!(( 2) is in [-1, 2]);
186+
static_assert!(( 3) is not in [-1, 2]);
187+
188+
static_assert!((-129) does not fit in i8);
189+
static_assert!((-128) fits in i8);
190+
static_assert!(( 127) fits in i8);
191+
static_assert!(( 128) does not fit in i8);
192+
193+
static_assert!(( -1) does not fit in u8);
194+
static_assert!(( 0) fits in u8);
195+
static_assert!((255) fits in u8);
196+
static_assert!((256) does not fit in u8);
197+
198+
const MAX_ERRNO: u32 = 4095;
199+
static_assert!((-(MAX_ERRNO as i128)) fits in i16);
200+
static_assert!((-(MAX_ERRNO as i128)) does not fit in i8);
201+
static_assert!((-(MAX_ERRNO as i128)) does not fit in u16);

0 commit comments

Comments
 (0)