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