1 pub mod lexer;
2 mod parser;
3 
4 use smallvec::SmallVec;
5 use std::ops::Range;
6 
7 /// A predicate function, used to combine 1 or more predicates
8 /// into a single value
9 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
10 pub enum Func {
11     /// `not()` with a configuration predicate. It is true if its predicate
12     /// is false and false if its predicate is true.
13     Not,
14     /// `all()` with a comma separated list of configuration predicates. It
15     /// is false if at least one predicate is false. If there are no predicates,
16     /// it is true.
17     ///
18     /// The associated `usize` is the number of predicates inside the `all()`.
19     All(usize),
20     /// `any()` with a comma separated list of configuration predicates. It
21     /// is true if at least one predicate is true. If there are no predicates,
22     /// it is false.
23     ///
24     /// The associated `usize` is the number of predicates inside the `any()`.
25     Any(usize),
26 }
27 
28 use crate::targets as targ;
29 
30 /// All predicates that pertains to a target, except for `target_feature`
31 #[derive(Clone, Copy, PartialEq, Debug)]
32 pub enum TargetPredicate<'a> {
33     /// [target_arch](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch)
34     Arch(targ::Arch<'a>),
35     /// [target_endian](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian)
36     Endian(targ::Endian),
37     /// [target_env](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env)
38     Env(targ::Env<'a>),
39     /// [target_family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family)
40     /// This also applies to the bare [`unix` and `windows`](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows)
41     /// predicates.
42     Family(targ::Family),
43     /// [target_os](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os)
44     Os(targ::Os<'a>),
45     /// [target_pointer_width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width)
46     PointerWidth(u8),
47     /// [target_vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor)
48     Vendor(targ::Vendor<'a>),
49 }
50 
51 pub trait TargetMatcher {
matches(&self, tp: TargetPredicate<'_>) -> bool52     fn matches(&self, tp: TargetPredicate<'_>) -> bool;
53 }
54 
55 impl<'a> TargetMatcher for targ::TargetInfo<'a> {
matches(&self, tp: TargetPredicate<'_>) -> bool56     fn matches(&self, tp: TargetPredicate<'_>) -> bool {
57         use TargetPredicate::{Arch, Endian, Env, Family, Os, PointerWidth, Vendor};
58 
59         match tp {
60             Arch(a) => a == self.arch,
61             Endian(end) => end == self.endian,
62             // The environment is allowed to be an empty string
63             Env(env) => match self.env {
64                 Some(e) => env == e,
65                 None => env.0.is_empty(),
66             },
67             Family(fam) => Some(fam) == self.family,
68             Os(os) => Some(os) == self.os,
69             PointerWidth(w) => w == self.pointer_width,
70             Vendor(ven) => match self.vendor {
71                 Some(v) => ven == v,
72                 None => ven.0 == "unknown",
73             },
74         }
75     }
76 }
77 
78 #[cfg(feature = "targets")]
79 impl TargetMatcher for target_lexicon::Triple {
80     #[allow(clippy::cognitive_complexity)]
81     #[allow(clippy::match_same_arms)]
matches(&self, tp: TargetPredicate<'_>) -> bool82     fn matches(&self, tp: TargetPredicate<'_>) -> bool {
83         use target_lexicon::*;
84         use TargetPredicate::{Arch, Endian, Env, Family, Os, PointerWidth, Vendor};
85 
86         match tp {
87             Arch(arch) => {
88                 if arch.0 == "x86" {
89                     matches!(self.architecture, Architecture::X86_32(_))
90                 } else if arch.0 == "wasm32" {
91                     self.architecture == Architecture::Wasm32
92                         || self.architecture == Architecture::Asmjs
93                 } else if arch.0 == "arm" {
94                     matches!(self.architecture, Architecture::Arm(_))
95                 } else {
96                     match arch.0.parse::<Architecture>() {
97                         Ok(a) => match (self.architecture, a) {
98                             (Architecture::Aarch64(_), Architecture::Aarch64(_))
99                             | (Architecture::Mips32(_), Architecture::Mips32(_))
100                             | (Architecture::Mips64(_), Architecture::Mips64(_))
101                             | (Architecture::Powerpc64le, Architecture::Powerpc64)
102                             | (Architecture::Riscv32(_), Architecture::Riscv32(_))
103                             | (Architecture::Riscv64(_), Architecture::Riscv64(_))
104                             | (Architecture::Sparcv9, Architecture::Sparc64) => true,
105                             (a, b) => a == b,
106                         },
107                         Err(_) => false,
108                     }
109                 }
110             }
111             Endian(end) => match self.architecture.endianness() {
112                 Ok(endian) => matches!(
113                     (end, endian),
114                     (crate::targets::Endian::little, Endianness::Little)
115                         | (crate::targets::Endian::big, Endianness::Big)
116                 ),
117 
118                 Err(_) => false,
119             },
120             Env(env) => {
121                 // The environment is implied by some operating systems
122                 match self.operating_system {
123                     OperatingSystem::Redox => env.0 == "relibc",
124                     OperatingSystem::VxWorks => env.0 == "gnu",
125                     OperatingSystem::Freebsd => match self.architecture {
126                         Architecture::Arm(ArmArchitecture::Armv6)
127                         | Architecture::Arm(ArmArchitecture::Armv7) => env.0 == "gnueabihf",
128                         _ => env.0.is_empty(),
129                     },
130                     OperatingSystem::Netbsd => match self.architecture {
131                         Architecture::Arm(ArmArchitecture::Armv6)
132                         | Architecture::Arm(ArmArchitecture::Armv7) => env.0 == "eabihf",
133                         _ => env.0.is_empty(),
134                     },
135                     OperatingSystem::None_
136                     | OperatingSystem::Cloudabi
137                     | OperatingSystem::Hermit
138                     | OperatingSystem::Ios => match self.environment {
139                         Environment::LinuxKernel => env.0 == "gnu",
140                         _ => env.0.is_empty(),
141                     },
142                     _ => {
143                         if env.0.is_empty() {
144                             matches!(
145                                 self.environment,
146                                 Environment::Unknown
147                                     | Environment::Android
148                                     | Environment::Softfloat
149                                     | Environment::Androideabi
150                                     | Environment::Eabi
151                             )
152                         } else {
153                             match env.0.parse::<Environment>() {
154                                 Ok(e) => {
155                                     // Rustc shortens multiple "gnu*" environments to just "gnu"
156                                     if env.0 == "gnu" {
157                                         match self.environment {
158                                             Environment::Gnu
159                                             | Environment::Gnuabi64
160                                             | Environment::Gnueabi
161                                             | Environment::Gnuspe
162                                             | Environment::Gnux32
163                                             | Environment::GnuIlp32
164                                             | Environment::Gnueabihf => true,
165                                             // Rust 1.49.0 changed all android targets to have the
166                                             // gnu environment
167                                             Environment::Android | Environment::Androideabi
168                                                 if self.operating_system
169                                                     == OperatingSystem::Linux =>
170                                             {
171                                                 true
172                                             }
173                                             Environment::Kernel => {
174                                                 self.operating_system == OperatingSystem::Linux
175                                             }
176                                             _ => false,
177                                         }
178                                     } else if env.0 == "musl" {
179                                         matches!(
180                                             self.environment,
181                                             Environment::Musl
182                                                 | Environment::Musleabi
183                                                 | Environment::Musleabihf
184                                                 | Environment::Muslabi64
185                                         )
186                                     } else if env.0 == "uclibc" {
187                                         matches!(
188                                             self.environment,
189                                             Environment::Uclibc | Environment::Uclibceabi
190                                         )
191                                     } else {
192                                         self.environment == e
193                                     }
194                                 }
195                                 Err(_) => false,
196                             }
197                         }
198                     }
199                 }
200             }
201             Family(fam) => {
202                 use target_lexicon::OperatingSystem::{
203                     AmdHsa, Bitrig, Cloudabi, Cuda, Darwin, Dragonfly, Emscripten, Freebsd,
204                     Fuchsia, Haiku, Hermit, Illumos, Ios, L4re, Linux, MacOSX, Nebulet, Netbsd,
205                     None_, Openbsd, Redox, Solaris, Tvos, Uefi, Unknown, VxWorks, Wasi, Windows,
206                 };
207                 Some(fam)
208                     == match self.operating_system {
209                         Unknown | AmdHsa | Bitrig | Cloudabi | Cuda | Hermit | Nebulet | None_
210                         | Uefi | Wasi => None,
211                         Darwin
212                         | Dragonfly
213                         | Emscripten
214                         | Freebsd
215                         | Fuchsia
216                         | Haiku
217                         | Illumos
218                         | Ios
219                         | L4re
220                         | MacOSX { .. }
221                         | Netbsd
222                         | Openbsd
223                         | Redox
224                         | Solaris
225                         | Tvos
226                         | VxWorks => Some(crate::targets::Family::unix),
227                         Linux => {
228                             // The 'kernel' environment is treated specially as not-unix
229                             if self.environment != Environment::Kernel {
230                                 Some(crate::targets::Family::unix)
231                             } else {
232                                 None
233                             }
234                         }
235                         Windows => Some(crate::targets::Family::windows),
236                         // I really dislike non-exhaustive :(
237                         _ => None,
238                     }
239             }
240             Os(os) => match os.0.parse::<OperatingSystem>() {
241                 Ok(o) => match self.environment {
242                     Environment::HermitKernel => os.0 == "hermit",
243                     _ => self.operating_system == o,
244                 },
245                 Err(_) => {
246                     // Handle special case for darwin/macos, where the triple is
247                     // "darwin", but rustc identifies the OS as "macos"
248                     if os.0 == "macos" && self.operating_system == OperatingSystem::Darwin {
249                         true
250                     } else {
251                         // For android, the os is still linux, but the environment is android
252                         os.0 == "android"
253                             && self.operating_system == OperatingSystem::Linux
254                             && (self.environment == Environment::Android
255                                 || self.environment == Environment::Androideabi)
256                     }
257                 }
258             },
259             Vendor(ven) => match ven.0.parse::<target_lexicon::Vendor>() {
260                 Ok(v) => self.vendor == v,
261                 Err(_) => false,
262             },
263             PointerWidth(pw) => {
264                 // The gnux32 environment is a special case, where it has an
265                 // x86_64 architecture, but a 32-bit pointer width
266                 if !matches!(
267                     self.environment,
268                     Environment::Gnux32 | Environment::GnuIlp32
269                 ) {
270                     pw == match self.pointer_width() {
271                         Ok(pw) => pw.bits(),
272                         Err(_) => return false,
273                     }
274                 } else {
275                     pw == 32
276                 }
277             }
278         }
279     }
280 }
281 
282 impl<'a> TargetPredicate<'a> {
283     /// Returns true of the predicate matches the specified target
284     ///
285     /// ```
286     /// use cfg_expr::{targets::*, expr::TargetPredicate as tp};
287     /// let win = get_builtin_target_by_triple("x86_64-pc-windows-msvc").unwrap();
288     ///
289     /// assert!(
290     ///     tp::Arch(Arch::x86_64).matches(win) &&
291     ///     tp::Endian(Endian::little).matches(win) &&
292     ///     tp::Env(Env::msvc).matches(win) &&
293     ///     tp::Family(Family::windows).matches(win) &&
294     ///     tp::Os(Os::windows).matches(win) &&
295     ///     tp::PointerWidth(64).matches(win) &&
296     ///     tp::Vendor(Vendor::pc).matches(win)
297     /// );
298     /// ```
matches<T>(self, target: &T) -> bool where T: TargetMatcher,299     pub fn matches<T>(self, target: &T) -> bool
300     where
301         T: TargetMatcher,
302     {
303         target.matches(self)
304     }
305 }
306 
307 #[derive(Clone, Debug)]
308 pub(crate) enum Which {
309     Arch,
310     Endian(targ::Endian),
311     Env,
312     Family(targ::Family),
313     Os,
314     PointerWidth(u8),
315     Vendor,
316 }
317 
318 #[derive(Clone, Debug)]
319 pub(crate) struct InnerTarget {
320     which: Which,
321     span: Option<Range<usize>>,
322 }
323 
324 /// A single predicate in a `cfg()` expression
325 #[derive(Debug, PartialEq)]
326 pub enum Predicate<'a> {
327     /// A target predicate, with the `target_` prefix
328     Target(TargetPredicate<'a>),
329     /// Whether rustc's test harness is [enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#test)
330     Test,
331     /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions)
332     /// when compiling without optimizations.
333     DebugAssertions,
334     /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#proc_macro) for
335     /// crates of the proc_macro type.
336     ProcMacro,
337     /// A [`feature = "<name>"`](https://doc.rust-lang.org/nightly/cargo/reference/features.html)
338     Feature(&'a str),
339     /// [target_feature](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature)
340     TargetFeature(&'a str),
341     /// A generic bare predicate key that doesn't match one of the known options, eg `cfg(bare)`
342     Flag(&'a str),
343     /// A generic key = "value" predicate that doesn't match one of the known options, eg `cfg(foo = "bar")`
344     KeyValue { key: &'a str, val: &'a str },
345 }
346 
347 #[derive(Clone, Debug)]
348 pub(crate) enum InnerPredicate {
349     Target(InnerTarget),
350     Test,
351     DebugAssertions,
352     ProcMacro,
353     Feature(Range<usize>),
354     TargetFeature(Range<usize>),
355     Other {
356         identifier: Range<usize>,
357         value: Option<Range<usize>>,
358     },
359 }
360 
361 impl InnerPredicate {
to_pred<'a>(&self, s: &'a str) -> Predicate<'a>362     fn to_pred<'a>(&self, s: &'a str) -> Predicate<'a> {
363         use InnerPredicate as IP;
364         use Predicate::{
365             DebugAssertions, Feature, Flag, KeyValue, ProcMacro, Target, TargetFeature, Test,
366         };
367 
368         match self {
369             IP::Target(it) => match &it.which {
370                 Which::Arch => Target(TargetPredicate::Arch(targ::Arch(
371                     &s[it.span.clone().unwrap()],
372                 ))),
373                 Which::Os => Target(TargetPredicate::Os(targ::Os(&s[it.span.clone().unwrap()]))),
374                 Which::Vendor => Target(TargetPredicate::Vendor(targ::Vendor(
375                     &s[it.span.clone().unwrap()],
376                 ))),
377                 Which::Env => Target(TargetPredicate::Env(targ::Env(
378                     &s[it.span.clone().unwrap()],
379                 ))),
380                 Which::Endian(end) => Target(TargetPredicate::Endian(*end)),
381                 Which::Family(fam) => Target(TargetPredicate::Family(*fam)),
382                 Which::PointerWidth(pw) => Target(TargetPredicate::PointerWidth(*pw)),
383             },
384             IP::Test => Test,
385             IP::DebugAssertions => DebugAssertions,
386             IP::ProcMacro => ProcMacro,
387             IP::Feature(rng) => Feature(&s[rng.clone()]),
388             IP::TargetFeature(rng) => TargetFeature(&s[rng.clone()]),
389             IP::Other { identifier, value } => match value {
390                 Some(vs) => KeyValue {
391                     key: &s[identifier.clone()],
392                     val: &s[vs.clone()],
393                 },
394                 None => Flag(&s[identifier.clone()]),
395             },
396         }
397     }
398 }
399 
400 #[derive(Clone, Debug)]
401 pub(crate) enum ExprNode {
402     Fn(Func),
403     Predicate(InnerPredicate),
404 }
405 
406 /// A parsed `cfg()` expression that can evaluated
407 #[derive(Clone, Debug)]
408 pub struct Expression {
409     pub(crate) expr: SmallVec<[ExprNode; 5]>,
410     // We keep the original string around for providing the arbitrary
411     // strings that can make up an expression
412     pub(crate) original: String,
413 }
414 
415 impl Expression {
416     /// An iterator over each predicate in the expression
predicates(&self) -> impl Iterator<Item = Predicate<'_>>417     pub fn predicates(&self) -> impl Iterator<Item = Predicate<'_>> {
418         self.expr.iter().filter_map(move |item| match item {
419             ExprNode::Predicate(pred) => {
420                 let pred = pred.clone().to_pred(&self.original);
421                 Some(pred)
422             }
423             ExprNode::Fn(_) => None,
424         })
425     }
426 
427     /// Evaluates the expression, using the provided closure to determine the value of
428     /// each predicate, which are then combined into a final result depending on the
429     /// functions not(), all(), or any() in the expression.
430     ///
431     /// `eval_predicate` typically returns `bool`, but may return any type that implements
432     /// the `Logic` trait.
433     ///
434     /// ## Examples
435     ///
436     /// ```
437     /// use cfg_expr::{targets::*, Expression, Predicate};
438     ///
439     /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
440     ///
441     /// let expr = Expression::parse(r#"all(not(windows), target_env = "musl", any(target_arch = "x86", target_arch = "x86_64"))"#).unwrap();
442     ///
443     /// assert!(expr.eval(|pred| {
444     ///     match pred {
445     ///         Predicate::Target(tp) => tp.matches(linux_musl),
446     ///         _ => false,
447     ///     }
448     /// }));
449     /// ```
450     ///
451     /// Returning `Option<bool>`, where `None` indicates the result is unknown:
452     ///
453     /// ```
454     /// use cfg_expr::{targets::*, Expression, Predicate};
455     ///
456     /// let expr = Expression::parse(r#"any(target_feature = "sse2", target_env = "musl")"#).unwrap();
457     ///
458     /// let linux_gnu = get_builtin_target_by_triple("x86_64-unknown-linux-gnu").unwrap();
459     /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
460     ///
461     /// fn eval(expr: &Expression, target: &TargetInfo) -> Option<bool> {
462     ///     expr.eval(|pred| {
463     ///         match pred {
464     ///             Predicate::Target(tp) => Some(tp.matches(target)),
465     ///             Predicate::TargetFeature(_) => None,
466     ///             _ => panic!("unexpected predicate"),
467     ///         }
468     ///     })
469     /// }
470     ///
471     /// // Whether the target feature is present is unknown, so the whole expression evaluates to
472     /// // None (unknown).
473     /// assert_eq!(eval(&expr, linux_gnu), None);
474     ///
475     /// // Whether the target feature is present is irrelevant for musl, since the any() always
476     /// // evaluates to true.
477     /// assert_eq!(eval(&expr, linux_musl), Some(true));
478     /// ```
eval<EP, T>(&self, mut eval_predicate: EP) -> T where EP: FnMut(&Predicate<'_>) -> T, T: Logic + std::fmt::Debug,479     pub fn eval<EP, T>(&self, mut eval_predicate: EP) -> T
480     where
481         EP: FnMut(&Predicate<'_>) -> T,
482         T: Logic + std::fmt::Debug,
483     {
484         let mut result_stack = SmallVec::<[T; 8]>::new();
485 
486         // We store the expression as postfix, so just evaluate each license
487         // requirement in the order it comes, and then combining the previous
488         // results according to each operator as it comes
489         for node in self.expr.iter() {
490             match node {
491                 ExprNode::Predicate(pred) => {
492                     let pred = pred.to_pred(&self.original);
493 
494                     result_stack.push(eval_predicate(&pred));
495                 }
496                 ExprNode::Fn(Func::All(count)) => {
497                     // all() with a comma separated list of configuration predicates.
498                     let mut result = T::top();
499 
500                     for _ in 0..*count {
501                         let r = result_stack.pop().unwrap();
502                         result = result.and(r);
503                     }
504 
505                     result_stack.push(result);
506                 }
507                 ExprNode::Fn(Func::Any(count)) => {
508                     // any() with a comma separated list of configuration predicates.
509                     let mut result = T::bottom();
510 
511                     for _ in 0..*count {
512                         let r = result_stack.pop().unwrap();
513                         result = result.or(r);
514                     }
515 
516                     result_stack.push(result);
517                 }
518                 ExprNode::Fn(Func::Not) => {
519                     // not() with a configuration predicate.
520                     // It is true if its predicate is false
521                     // and false if its predicate is true.
522                     let r = result_stack.pop().unwrap();
523                     result_stack.push(r.not());
524                 }
525             }
526         }
527 
528         result_stack.pop().unwrap()
529     }
530 
531     /// The original string which has been parsed to produce this ['Expression`].
532     ///
533     /// ```
534     /// use cfg_expr::Expression;
535     ///
536     /// assert_eq!(
537     ///     Expression::parse("any()").unwrap().original(),
538     ///     "any()"
539     /// );
540     /// ```
541     #[inline]
original(&self) -> &str542     pub fn original(&self) -> &str {
543         &self.original
544     }
545 }
546 
547 /// [`PartialEq`] will do a **syntactical** comparaison, so will just check if both
548 /// expressions have been parsed from the same string, **not** if they are semantically
549 /// equivalent.
550 ///
551 /// ```
552 /// use cfg_expr::Expression;
553 ///
554 /// assert_eq!(
555 ///     Expression::parse("any()").unwrap(),
556 ///     Expression::parse("any()").unwrap()
557 /// );
558 /// assert_ne!(
559 ///     Expression::parse("any()").unwrap(),
560 ///     Expression::parse("unix").unwrap()
561 /// );
562 /// ```
563 impl PartialEq for Expression {
eq(&self, other: &Self) -> bool564     fn eq(&self, other: &Self) -> bool {
565         self.original.eq(&other.original)
566     }
567 }
568 
569 /// A propositional logic used to evaluate `Expression` instances.
570 ///
571 /// An `Expression` consists of some predicates and the `any`, `all` and `not` operators. An
572 /// implementation of `Logic` defines how the `any`, `all` and `not` operators should be evaluated.
573 pub trait Logic {
574     /// The result of an `all` operation with no operands, akin to Boolean `true`.
top() -> Self575     fn top() -> Self;
576 
577     /// The result of an `any` operation with no operands, akin to Boolean `false`.
bottom() -> Self578     fn bottom() -> Self;
579 
580     /// `AND`, which corresponds to the `all` operator.
and(self, other: Self) -> Self581     fn and(self, other: Self) -> Self;
582 
583     /// `OR`, which corresponds to the `any` operator.
or(self, other: Self) -> Self584     fn or(self, other: Self) -> Self;
585 
586     /// `NOT`, which corresponds to the `not` operator.
not(self) -> Self587     fn not(self) -> Self;
588 }
589 
590 /// A boolean logic.
591 impl Logic for bool {
592     #[inline]
top() -> Self593     fn top() -> Self {
594         true
595     }
596 
597     #[inline]
bottom() -> Self598     fn bottom() -> Self {
599         false
600     }
601 
602     #[inline]
and(self, other: Self) -> Self603     fn and(self, other: Self) -> Self {
604         self && other
605     }
606 
607     #[inline]
or(self, other: Self) -> Self608     fn or(self, other: Self) -> Self {
609         self || other
610     }
611 
612     #[inline]
not(self) -> Self613     fn not(self) -> Self {
614         !self
615     }
616 }
617 
618 /// A three-valued logic -- `None` stands for the value being unknown.
619 ///
620 /// The truth tables for this logic are described on
621 /// [Wikipedia](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
622 impl Logic for Option<bool> {
623     #[inline]
top() -> Self624     fn top() -> Self {
625         Some(true)
626     }
627 
628     #[inline]
bottom() -> Self629     fn bottom() -> Self {
630         Some(false)
631     }
632 
633     #[inline]
and(self, other: Self) -> Self634     fn and(self, other: Self) -> Self {
635         match (self, other) {
636             // If either is false, the expression is false.
637             (Some(false), _) | (_, Some(false)) => Some(false),
638             // If both are true, the expression is true.
639             (Some(true), Some(true)) => Some(true),
640             // One or both are unknown -- the result is unknown.
641             _ => None,
642         }
643     }
644 
645     #[inline]
or(self, other: Self) -> Self646     fn or(self, other: Self) -> Self {
647         match (self, other) {
648             // If either is true, the expression is true.
649             (Some(true), _) | (_, Some(true)) => Some(true),
650             // If both are false, the expression is false.
651             (Some(false), Some(false)) => Some(false),
652             // One or both are unknown -- the result is unknown.
653             _ => None,
654         }
655     }
656 
657     #[inline]
not(self) -> Self658     fn not(self) -> Self {
659         self.map(|v| !v)
660     }
661 }
662