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 | ArmArchitecture::Armv7) => {
127                             env.0 == "gnueabihf"
128                         }
129                         _ => env.0.is_empty(),
130                     },
131                     OperatingSystem::Netbsd => match self.architecture {
132                         Architecture::Arm(ArmArchitecture::Armv6 | ArmArchitecture::Armv7) => {
133                             env.0 == "eabihf"
134                         }
135                         _ => env.0.is_empty(),
136                     },
137                     OperatingSystem::None_
138                     | OperatingSystem::Cloudabi
139                     | OperatingSystem::Hermit
140                     | OperatingSystem::Ios => match self.environment {
141                         Environment::LinuxKernel => env.0 == "gnu",
142                         _ => env.0.is_empty(),
143                     },
144                     _ => {
145                         if env.0.is_empty() {
146                             matches!(
147                                 self.environment,
148                                 Environment::Unknown
149                                     | Environment::Android
150                                     | Environment::Softfloat
151                                     | Environment::Androideabi
152                                     | Environment::Eabi
153                             )
154                         } else {
155                             match env.0.parse::<Environment>() {
156                                 Ok(e) => {
157                                     // Rustc shortens multiple "gnu*" environments to just "gnu"
158                                     if env.0 == "gnu" {
159                                         match self.environment {
160                                             Environment::Gnu
161                                             | Environment::Gnuabi64
162                                             | Environment::Gnueabi
163                                             | Environment::Gnuspe
164                                             | Environment::Gnux32
165                                             | Environment::GnuIlp32
166                                             | Environment::Gnueabihf => true,
167                                             // Rust 1.49.0 changed all android targets to have the
168                                             // gnu environment
169                                             Environment::Android | Environment::Androideabi
170                                                 if self.operating_system
171                                                     == OperatingSystem::Linux =>
172                                             {
173                                                 true
174                                             }
175                                             Environment::Kernel => {
176                                                 self.operating_system == OperatingSystem::Linux
177                                             }
178                                             _ => false,
179                                         }
180                                     } else if env.0 == "musl" {
181                                         matches!(
182                                             self.environment,
183                                             Environment::Musl
184                                                 | Environment::Musleabi
185                                                 | Environment::Musleabihf
186                                                 | Environment::Muslabi64
187                                         )
188                                     } else if env.0 == "uclibc" {
189                                         matches!(
190                                             self.environment,
191                                             Environment::Uclibc | Environment::Uclibceabi
192                                         )
193                                     } else {
194                                         self.environment == e
195                                     }
196                                 }
197                                 Err(_) => false,
198                             }
199                         }
200                     }
201                 }
202             }
203             Family(fam) => {
204                 use target_lexicon::OperatingSystem::{
205                     AmdHsa, Bitrig, Cloudabi, Cuda, Darwin, Dragonfly, Emscripten, Freebsd,
206                     Fuchsia, Haiku, Hermit, Illumos, Ios, L4re, Linux, MacOSX, Nebulet, Netbsd,
207                     None_, Openbsd, Redox, Solaris, Tvos, Uefi, Unknown, VxWorks, Wasi, Windows,
208                 };
209                 Some(fam)
210                     == match self.operating_system {
211                         Unknown | AmdHsa | Bitrig | Cloudabi | Cuda | Hermit | Nebulet | None_
212                         | Uefi | Wasi => None,
213                         Darwin
214                         | Dragonfly
215                         | Emscripten
216                         | Freebsd
217                         | Fuchsia
218                         | Haiku
219                         | Illumos
220                         | Ios
221                         | L4re
222                         | MacOSX { .. }
223                         | Netbsd
224                         | Openbsd
225                         | Redox
226                         | Solaris
227                         | Tvos
228                         | VxWorks => Some(crate::targets::Family::unix),
229                         Linux => {
230                             // The 'kernel' environment is treated specially as not-unix
231                             if self.environment != Environment::Kernel {
232                                 Some(crate::targets::Family::unix)
233                             } else {
234                                 None
235                             }
236                         }
237                         Windows => Some(crate::targets::Family::windows),
238                         // I really dislike non-exhaustive :(
239                         _ => None,
240                     }
241             }
242             Os(os) => match os.0.parse::<OperatingSystem>() {
243                 Ok(o) => match self.environment {
244                     Environment::HermitKernel => os.0 == "hermit",
245                     _ => self.operating_system == o,
246                 },
247                 Err(_) => {
248                     // Handle special case for darwin/macos, where the triple is
249                     // "darwin", but rustc identifies the OS as "macos"
250                     if os.0 == "macos" && self.operating_system == OperatingSystem::Darwin {
251                         true
252                     } else {
253                         // For android, the os is still linux, but the environment is android
254                         os.0 == "android"
255                             && self.operating_system == OperatingSystem::Linux
256                             && (self.environment == Environment::Android
257                                 || self.environment == Environment::Androideabi)
258                     }
259                 }
260             },
261             Vendor(ven) => match ven.0.parse::<target_lexicon::Vendor>() {
262                 Ok(v) => self.vendor == v,
263                 Err(_) => false,
264             },
265             PointerWidth(pw) => {
266                 // The gnux32 environment is a special case, where it has an
267                 // x86_64 architecture, but a 32-bit pointer width
268                 if !matches!(
269                     self.environment,
270                     Environment::Gnux32 | Environment::GnuIlp32
271                 ) {
272                     pw == match self.pointer_width() {
273                         Ok(pw) => pw.bits(),
274                         Err(_) => return false,
275                     }
276                 } else {
277                     pw == 32
278                 }
279             }
280         }
281     }
282 }
283 
284 impl<'a> TargetPredicate<'a> {
285     /// Returns true of the predicate matches the specified target
286     ///
287     /// ```
288     /// use cfg_expr::{targets::*, expr::TargetPredicate as tp};
289     /// let win = get_builtin_target_by_triple("x86_64-pc-windows-msvc").unwrap();
290     ///
291     /// assert!(
292     ///     tp::Arch(Arch::x86_64).matches(win) &&
293     ///     tp::Endian(Endian::little).matches(win) &&
294     ///     tp::Env(Env::msvc).matches(win) &&
295     ///     tp::Family(Family::windows).matches(win) &&
296     ///     tp::Os(Os::windows).matches(win) &&
297     ///     tp::PointerWidth(64).matches(win) &&
298     ///     tp::Vendor(Vendor::pc).matches(win)
299     /// );
300     /// ```
matches<T>(self, target: &T) -> bool where T: TargetMatcher,301     pub fn matches<T>(self, target: &T) -> bool
302     where
303         T: TargetMatcher,
304     {
305         target.matches(self)
306     }
307 }
308 
309 #[derive(Clone, Debug)]
310 pub(crate) enum Which {
311     Arch,
312     Endian(targ::Endian),
313     Env,
314     Family(targ::Family),
315     Os,
316     PointerWidth(u8),
317     Vendor,
318 }
319 
320 #[derive(Clone, Debug)]
321 pub(crate) struct InnerTarget {
322     which: Which,
323     span: Option<Range<usize>>,
324 }
325 
326 /// A single predicate in a `cfg()` expression
327 #[derive(Debug, PartialEq)]
328 pub enum Predicate<'a> {
329     /// A target predicate, with the `target_` prefix
330     Target(TargetPredicate<'a>),
331     /// Whether rustc's test harness is [enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#test)
332     Test,
333     /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions)
334     /// when compiling without optimizations.
335     DebugAssertions,
336     /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#proc_macro) for
337     /// crates of the proc_macro type.
338     ProcMacro,
339     /// A [`feature = "<name>"`](https://doc.rust-lang.org/nightly/cargo/reference/features.html)
340     Feature(&'a str),
341     /// [target_feature](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature)
342     TargetFeature(&'a str),
343     /// A generic bare predicate key that doesn't match one of the known options, eg `cfg(bare)`
344     Flag(&'a str),
345     /// A generic key = "value" predicate that doesn't match one of the known options, eg `cfg(foo = "bar")`
346     KeyValue { key: &'a str, val: &'a str },
347 }
348 
349 #[derive(Clone, Debug)]
350 pub(crate) enum InnerPredicate {
351     Target(InnerTarget),
352     Test,
353     DebugAssertions,
354     ProcMacro,
355     Feature(Range<usize>),
356     TargetFeature(Range<usize>),
357     Other {
358         identifier: Range<usize>,
359         value: Option<Range<usize>>,
360     },
361 }
362 
363 impl InnerPredicate {
to_pred<'a>(&self, s: &'a str) -> Predicate<'a>364     fn to_pred<'a>(&self, s: &'a str) -> Predicate<'a> {
365         use InnerPredicate as IP;
366         use Predicate::{
367             DebugAssertions, Feature, Flag, KeyValue, ProcMacro, Target, TargetFeature, Test,
368         };
369 
370         match self {
371             IP::Target(it) => match &it.which {
372                 Which::Arch => Target(TargetPredicate::Arch(targ::Arch(
373                     &s[it.span.clone().unwrap()],
374                 ))),
375                 Which::Os => Target(TargetPredicate::Os(targ::Os(&s[it.span.clone().unwrap()]))),
376                 Which::Vendor => Target(TargetPredicate::Vendor(targ::Vendor(
377                     &s[it.span.clone().unwrap()],
378                 ))),
379                 Which::Env => Target(TargetPredicate::Env(targ::Env(
380                     &s[it.span.clone().unwrap()],
381                 ))),
382                 Which::Endian(end) => Target(TargetPredicate::Endian(*end)),
383                 Which::Family(fam) => Target(TargetPredicate::Family(*fam)),
384                 Which::PointerWidth(pw) => Target(TargetPredicate::PointerWidth(*pw)),
385             },
386             IP::Test => Test,
387             IP::DebugAssertions => DebugAssertions,
388             IP::ProcMacro => ProcMacro,
389             IP::Feature(rng) => Feature(&s[rng.clone()]),
390             IP::TargetFeature(rng) => TargetFeature(&s[rng.clone()]),
391             IP::Other { identifier, value } => match value {
392                 Some(vs) => KeyValue {
393                     key: &s[identifier.clone()],
394                     val: &s[vs.clone()],
395                 },
396                 None => Flag(&s[identifier.clone()]),
397             },
398         }
399     }
400 }
401 
402 #[derive(Clone, Debug)]
403 pub(crate) enum ExprNode {
404     Fn(Func),
405     Predicate(InnerPredicate),
406 }
407 
408 /// A parsed `cfg()` expression that can evaluated
409 #[derive(Clone, Debug)]
410 pub struct Expression {
411     pub(crate) expr: SmallVec<[ExprNode; 5]>,
412     // We keep the original string around for providing the arbitrary
413     // strings that can make up an expression
414     pub(crate) original: String,
415 }
416 
417 impl Expression {
418     /// An iterator over each predicate in the expression
predicates(&self) -> impl Iterator<Item = Predicate<'_>>419     pub fn predicates(&self) -> impl Iterator<Item = Predicate<'_>> {
420         self.expr.iter().filter_map(move |item| match item {
421             ExprNode::Predicate(pred) => {
422                 let pred = pred.clone().to_pred(&self.original);
423                 Some(pred)
424             }
425             ExprNode::Fn(_) => None,
426         })
427     }
428 
429     /// Evaluates the expression, using the provided closure to determine the value of
430     /// each predicate, which are then combined into a final result depending on the
431     /// functions not(), all(), or any() in the expression.
432     ///
433     /// `eval_predicate` typically returns `bool`, but may return any type that implements
434     /// the `Logic` trait.
435     ///
436     /// ## Examples
437     ///
438     /// ```
439     /// use cfg_expr::{targets::*, Expression, Predicate};
440     ///
441     /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
442     ///
443     /// let expr = Expression::parse(r#"all(not(windows), target_env = "musl", any(target_arch = "x86", target_arch = "x86_64"))"#).unwrap();
444     ///
445     /// assert!(expr.eval(|pred| {
446     ///     match pred {
447     ///         Predicate::Target(tp) => tp.matches(linux_musl),
448     ///         _ => false,
449     ///     }
450     /// }));
451     /// ```
452     ///
453     /// Returning `Option<bool>`, where `None` indicates the result is unknown:
454     ///
455     /// ```
456     /// use cfg_expr::{targets::*, Expression, Predicate};
457     ///
458     /// let expr = Expression::parse(r#"any(target_feature = "sse2", target_env = "musl")"#).unwrap();
459     ///
460     /// let linux_gnu = get_builtin_target_by_triple("x86_64-unknown-linux-gnu").unwrap();
461     /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
462     ///
463     /// fn eval(expr: &Expression, target: &TargetInfo) -> Option<bool> {
464     ///     expr.eval(|pred| {
465     ///         match pred {
466     ///             Predicate::Target(tp) => Some(tp.matches(target)),
467     ///             Predicate::TargetFeature(_) => None,
468     ///             _ => panic!("unexpected predicate"),
469     ///         }
470     ///     })
471     /// }
472     ///
473     /// // Whether the target feature is present is unknown, so the whole expression evaluates to
474     /// // None (unknown).
475     /// assert_eq!(eval(&expr, linux_gnu), None);
476     ///
477     /// // Whether the target feature is present is irrelevant for musl, since the any() always
478     /// // evaluates to true.
479     /// assert_eq!(eval(&expr, linux_musl), Some(true));
480     /// ```
eval<EP, T>(&self, mut eval_predicate: EP) -> T where EP: FnMut(&Predicate<'_>) -> T, T: Logic + std::fmt::Debug,481     pub fn eval<EP, T>(&self, mut eval_predicate: EP) -> T
482     where
483         EP: FnMut(&Predicate<'_>) -> T,
484         T: Logic + std::fmt::Debug,
485     {
486         let mut result_stack = SmallVec::<[T; 8]>::new();
487 
488         // We store the expression as postfix, so just evaluate each license
489         // requirement in the order it comes, and then combining the previous
490         // results according to each operator as it comes
491         for node in self.expr.iter() {
492             match node {
493                 ExprNode::Predicate(pred) => {
494                     let pred = pred.to_pred(&self.original);
495 
496                     result_stack.push(eval_predicate(&pred));
497                 }
498                 ExprNode::Fn(Func::All(count)) => {
499                     // all() with a comma separated list of configuration predicates.
500                     let mut result = T::top();
501 
502                     for _ in 0..*count {
503                         let r = result_stack.pop().unwrap();
504                         result = result.and(r);
505                     }
506 
507                     result_stack.push(result);
508                 }
509                 ExprNode::Fn(Func::Any(count)) => {
510                     // any() with a comma separated list of configuration predicates.
511                     let mut result = T::bottom();
512 
513                     for _ in 0..*count {
514                         let r = result_stack.pop().unwrap();
515                         result = result.or(r);
516                     }
517 
518                     result_stack.push(result);
519                 }
520                 ExprNode::Fn(Func::Not) => {
521                     // not() with a configuration predicate.
522                     // It is true if its predicate is false
523                     // and false if its predicate is true.
524                     let r = result_stack.pop().unwrap();
525                     result_stack.push(r.not());
526                 }
527             }
528         }
529 
530         result_stack.pop().unwrap()
531     }
532 
533     /// The original string which has been parsed to produce this ['Expression`].
534     ///
535     /// ```
536     /// use cfg_expr::Expression;
537     ///
538     /// assert_eq!(
539     ///     Expression::parse("any()").unwrap().original(),
540     ///     "any()"
541     /// );
542     /// ```
543     #[inline]
original(&self) -> &str544     pub fn original(&self) -> &str {
545         &self.original
546     }
547 }
548 
549 /// [`PartialEq`] will do a **syntactical** comparaison, so will just check if both
550 /// expressions have been parsed from the same string, **not** if they are semantically
551 /// equivalent.
552 ///
553 /// ```
554 /// use cfg_expr::Expression;
555 ///
556 /// assert_eq!(
557 ///     Expression::parse("any()").unwrap(),
558 ///     Expression::parse("any()").unwrap()
559 /// );
560 /// assert_ne!(
561 ///     Expression::parse("any()").unwrap(),
562 ///     Expression::parse("unix").unwrap()
563 /// );
564 /// ```
565 impl PartialEq for Expression {
eq(&self, other: &Self) -> bool566     fn eq(&self, other: &Self) -> bool {
567         self.original.eq(&other.original)
568     }
569 }
570 
571 /// A propositional logic used to evaluate `Expression` instances.
572 ///
573 /// An `Expression` consists of some predicates and the `any`, `all` and `not` operators. An
574 /// implementation of `Logic` defines how the `any`, `all` and `not` operators should be evaluated.
575 pub trait Logic {
576     /// The result of an `all` operation with no operands, akin to Boolean `true`.
top() -> Self577     fn top() -> Self;
578 
579     /// The result of an `any` operation with no operands, akin to Boolean `false`.
bottom() -> Self580     fn bottom() -> Self;
581 
582     /// `AND`, which corresponds to the `all` operator.
and(self, other: Self) -> Self583     fn and(self, other: Self) -> Self;
584 
585     /// `OR`, which corresponds to the `any` operator.
or(self, other: Self) -> Self586     fn or(self, other: Self) -> Self;
587 
588     /// `NOT`, which corresponds to the `not` operator.
not(self) -> Self589     fn not(self) -> Self;
590 }
591 
592 /// A boolean logic.
593 impl Logic for bool {
594     #[inline]
top() -> Self595     fn top() -> Self {
596         true
597     }
598 
599     #[inline]
bottom() -> Self600     fn bottom() -> Self {
601         false
602     }
603 
604     #[inline]
and(self, other: Self) -> Self605     fn and(self, other: Self) -> Self {
606         self && other
607     }
608 
609     #[inline]
or(self, other: Self) -> Self610     fn or(self, other: Self) -> Self {
611         self || other
612     }
613 
614     #[inline]
not(self) -> Self615     fn not(self) -> Self {
616         !self
617     }
618 }
619 
620 /// A three-valued logic -- `None` stands for the value being unknown.
621 ///
622 /// The truth tables for this logic are described on
623 /// [Wikipedia](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
624 impl Logic for Option<bool> {
625     #[inline]
top() -> Self626     fn top() -> Self {
627         Some(true)
628     }
629 
630     #[inline]
bottom() -> Self631     fn bottom() -> Self {
632         Some(false)
633     }
634 
635     #[inline]
and(self, other: Self) -> Self636     fn and(self, other: Self) -> Self {
637         match (self, other) {
638             // If either is false, the expression is false.
639             (Some(false), _) | (_, Some(false)) => Some(false),
640             // If both are true, the expression is true.
641             (Some(true), Some(true)) => Some(true),
642             // One or both are unknown -- the result is unknown.
643             _ => None,
644         }
645     }
646 
647     #[inline]
or(self, other: Self) -> Self648     fn or(self, other: Self) -> Self {
649         match (self, other) {
650             // If either is true, the expression is true.
651             (Some(true), _) | (_, Some(true)) => Some(true),
652             // If both are false, the expression is false.
653             (Some(false), Some(false)) => Some(false),
654             // One or both are unknown -- the result is unknown.
655             _ => None,
656         }
657     }
658 
659     #[inline]
not(self) -> Self660     fn not(self) -> Self {
661         self.map(|v| !v)
662     }
663 }
664