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