Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] support panic and target_has_atomic predicates #49

Merged
merged 4 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub enum Reason {
InvalidInteger,
/// The root cfg() may only contain a single predicate
MultipleRootPredicates,
/// A `target_has_atomic` predicate didn't correctly parse.
InvalidHasAtomic,
/// An element was not part of the builtin information in rustc
UnknownBuiltin,
}
Expand Down Expand Up @@ -72,9 +74,9 @@ impl fmt::Display for ParseError {
impl fmt::Display for Reason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Reason::{
Empty, InvalidCharacters, InvalidInteger, InvalidNot, MultipleRootPredicates,
UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin, UnopenedParens,
UnopenedQuotes,
Empty, InvalidCharacters, InvalidHasAtomic, InvalidInteger, InvalidNot,
MultipleRootPredicates, UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin,
UnopenedParens, UnopenedQuotes,
};

match self {
Expand All @@ -101,6 +103,7 @@ impl fmt::Display for Reason {
InvalidNot(np) => f.write_fmt(format_args!("not() takes 1 predicate, found {}", np)),
InvalidInteger => f.write_str("invalid integer"),
MultipleRootPredicates => f.write_str("multiple root predicates"),
InvalidHasAtomic => f.write_str("expected integer or \"ptr\""),
UnknownBuiltin => f.write_str("unknown built-in"),
}
}
Expand All @@ -109,9 +112,9 @@ impl fmt::Display for Reason {
impl Error for ParseError {
fn description(&self) -> &str {
use Reason::{
Empty, InvalidCharacters, InvalidInteger, InvalidNot, MultipleRootPredicates,
UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin, UnopenedParens,
UnopenedQuotes,
Empty, InvalidCharacters, InvalidHasAtomic, InvalidInteger, InvalidNot,
MultipleRootPredicates, UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin,
UnopenedParens, UnopenedQuotes,
};

match self.reason {
Expand All @@ -125,7 +128,22 @@ impl Error for ParseError {
InvalidNot(_) => "not() takes 1 predicate",
InvalidInteger => "invalid integer",
MultipleRootPredicates => "multiple root predicates",
InvalidHasAtomic => "expected integer or \"ptr\"",
UnknownBuiltin => "unknown built-in",
}
}
}

/// Error parsing a `target_has_atomic` predicate.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HasAtomicParseError {
pub(crate) input: String,
}

impl fmt::Display for HasAtomicParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "expected integer or \"ptr\", found {}", self.input)
}
}

impl Error for HasAtomicParseError {}
32 changes: 30 additions & 2 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ pub enum TargetPredicate {
/// This also applies to the bare [`unix` and `windows`](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows)
/// predicates.
Family(targ::Family),
/// [target_has_atomic](https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic).
HasAtomic(targ::HasAtomic),
/// [target_os](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os)
Os(targ::Os),
/// [panic](https://doc.rust-lang.org/reference/conditional-compilation.html#panic)
Panic(targ::Panic),
/// [target_pointer_width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width)
PointerWidth(u8),
/// [target_vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor)
Expand All @@ -54,7 +58,9 @@ pub trait TargetMatcher {

impl TargetMatcher for targ::TargetInfo {
fn matches(&self, tp: &TargetPredicate) -> bool {
use TargetPredicate::{Arch, Endian, Env, Family, Os, PointerWidth, Vendor};
use TargetPredicate::{
Arch, Endian, Env, Family, HasAtomic, Os, Panic, PointerWidth, Vendor,
};

match tp {
Arch(a) => a == &self.arch,
Expand All @@ -65,12 +71,14 @@ impl TargetMatcher for targ::TargetInfo {
None => env.0.is_empty(),
},
Family(fam) => self.families.contains(fam),
HasAtomic(has_atomic) => self.has_atomics.contains(*has_atomic),
Os(os) => Some(os) == self.os.as_ref(),
PointerWidth(w) => *w == self.pointer_width,
Vendor(ven) => match &self.vendor {
Some(v) => ven == v,
None => ven == &targ::Vendor::unknown,
},
Panic(panic) => &self.panic == panic,
}
}
}
Expand All @@ -81,7 +89,9 @@ impl TargetMatcher for target_lexicon::Triple {
#[allow(clippy::match_same_arms)]
fn matches(&self, tp: &TargetPredicate) -> bool {
use target_lexicon::*;
use TargetPredicate::{Arch, Endian, Env, Family, Os, PointerWidth, Vendor};
use TargetPredicate::{
Arch, Endian, Env, Family, HasAtomic, Os, Panic, PointerWidth, Vendor,
};

match tp {
Arch(arch) => {
Expand Down Expand Up @@ -263,6 +273,11 @@ impl TargetMatcher for target_lexicon::Triple {
_ => false,
}
}
HasAtomic(_) => {
// atomic support depends on both the architecture and the OS. Assume false for
// this.
false
}
Os(os) => match os.0.parse::<OperatingSystem>() {
Ok(o) => match self.environment {
Environment::HermitKernel => os == &targ::Os::hermit,
Expand All @@ -282,6 +297,10 @@ impl TargetMatcher for target_lexicon::Triple {
}
}
},
Panic(_) => {
// panic support depends on the OS. Assume false for this.
false
}
Vendor(ven) => match ven.0.parse::<target_lexicon::Vendor>() {
Ok(v) => self.vendor == v,
Err(_) => false,
Expand All @@ -308,6 +327,9 @@ impl TargetMatcher for target_lexicon::Triple {
impl TargetPredicate {
/// Returns true of the predicate matches the specified target
///
/// Note that when matching against a [`target_lexicon::Triple`], the
/// `has_target_atomic` and `panic` predicates will _always_ return `false`.
///
/// ```
/// use cfg_expr::{targets::*, expr::TargetPredicate as tp};
/// let win = get_builtin_target_by_triple("x86_64-pc-windows-msvc").unwrap();
Expand Down Expand Up @@ -337,6 +359,8 @@ pub(crate) enum Which {
Env,
Family,
Os,
HasAtomic(targ::HasAtomic),
Panic,
PointerWidth(u8),
Vendor,
}
Expand Down Expand Up @@ -409,6 +433,10 @@ impl InnerPredicate {
s[it.span.clone().unwrap()].to_owned(),
))),
Which::Endian(end) => Target(TargetPredicate::Endian(*end)),
Which::HasAtomic(has_atomic) => Target(TargetPredicate::HasAtomic(*has_atomic)),
Which::Panic => Target(TargetPredicate::Panic(targ::Panic::new(
s[it.span.clone().unwrap()].to_owned(),
))),
Which::PointerWidth(pw) => Target(TargetPredicate::PointerWidth(*pw)),
},
IP::Test => Test,
Expand Down
22 changes: 22 additions & 0 deletions src/expr/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ impl Expression {
}
}
}
"panic" => match val {
Some((_, vspan)) => InnerPredicate::Target(InnerTarget {
which: Which::Panic,
span: Some(vspan),
}),
None => {
return Err(ParseError {
original: original.to_owned(),
span,
reason: Reason::Unexpected(&["= \"<panic_strategy>\""]),
});
}
},
target_key if key.starts_with("target_") => {
let (val, vspan) = match val {
None => {
Expand Down Expand Up @@ -154,6 +167,14 @@ impl Expression {
})?),
span: None,
},
"has_atomic" => InnerTarget {
which: Which::HasAtomic(val.parse().map_err(|_err| ParseError {
original: original.to_owned(),
span: vspan,
reason: Reason::InvalidHasAtomic,
})?),
span: None,
},
"pointer_width" => InnerTarget {
which: Which::PointerWidth(val.parse().map_err(|_err| ParseError {
original: original.to_owned(),
Expand All @@ -174,6 +195,7 @@ impl Expression {
"target_family",
"target_env",
"target_endian",
"target_has_atomic",
"target_pointer_width",
"target_vendor",
]),
Expand Down
111 changes: 109 additions & 2 deletions src/targets.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::error::Reason;
use crate::error::{HasAtomicParseError, Reason};
use std::{borrow::Cow, ops::Deref};

mod builtins;
Expand Down Expand Up @@ -35,6 +35,10 @@ pub struct Family(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Env(pub Cow<'static, str>);

/// The panic strategy used on this target by default.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Panic(pub Cow<'static, str>);

macro_rules! field_impls {
($kind:ident) => {
impl $kind {
Expand Down Expand Up @@ -80,6 +84,44 @@ field_impls!(Vendor);
field_impls!(Os);
field_impls!(Family);
field_impls!(Env);
field_impls!(Panic);

/// Integer size and pointers for which there's support for atomic functions.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum HasAtomic {
/// The platform supports atomics for the given integer size in bits (e.g. `AtomicU8` if
/// `HasAtomic::IntegerSize(8)`).
IntegerSize(u16),

/// The platform supports atomics for pointers (`AtomicPtr`).
Pointer,
}

impl std::str::FromStr for HasAtomic {
type Err = HasAtomicParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(size) = s.parse::<u16>() {
Ok(Self::IntegerSize(size))
} else if s == "ptr" {
Ok(HasAtomic::Pointer)
} else {
Err(HasAtomicParseError {
input: s.to_owned(),
})
}
}
}

impl std::fmt::Display for HasAtomic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::IntegerSize(size) => write!(f, "{}", size),
Self::Pointer => write!(f, "ptr"),
}
}
}

/// A set of families for a target.
///
Expand Down Expand Up @@ -145,6 +187,66 @@ impl std::fmt::Display for Families {
}
}

/// A set of [`HasAtomic`] instances a target.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct HasAtomics(Cow<'static, [HasAtomic]>);

impl HasAtomics {
/// Constructs a new instance.
///
/// If you have a `&'static [HasAtomic]`, prefer [`Self::new_const`].
#[inline]
pub fn new(val: impl IntoIterator<Item = HasAtomic>) -> Self {
let mut has_atomics: Vec<_> = val.into_iter().collect();
has_atomics.sort_unstable();
Self(Cow::Owned(has_atomics))
}

/// Constructs a new instance of this struct from a static slice of [`HasAtomic`].
///
/// `val` must be in sorted order: this constructor cannot check for that due to
/// limitations in current versions of Rust.
#[inline]
pub const fn new_const(val: &'static [HasAtomic]) -> Self {
// TODO: Check that val is sorted.
Self(Cow::Borrowed(val))
}

/// Returns true if this list of families contains a given family.
#[inline]
pub fn contains(&self, val: HasAtomic) -> bool {
self.0.contains(&val)
}
}

impl Deref for HasAtomics {
type Target = [HasAtomic];
fn deref(&self) -> &Self::Target {
&*self.0
}
}

impl AsRef<[HasAtomic]> for HasAtomics {
#[inline]
fn as_ref(&self) -> &[HasAtomic] {
&*self.0
}
}

impl std::fmt::Display for HasAtomics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
let len = self.0.len();
for (idx, has_atomic) in self.0.iter().enumerate() {
write!(f, "{}", has_atomic)?;
if idx + 1 < len {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
}

macro_rules! target_enum {
(
$(#[$outer:meta])*
Expand Down Expand Up @@ -238,6 +340,11 @@ pub struct TargetInfo {
/// [target_endian](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian)
/// predicate.
pub endian: Endian,
/// The target's support for atomics. Used by the has_target_atomics predicate.
pub has_atomics: HasAtomics,
/// The panic strategy used on this target by default. Used by the
/// [panic](https://doc.rust-lang.org/beta/reference/conditional-compilation.html#panic) predicate.
pub panic: Panic,
}

/// Attempts to find the `TargetInfo` for the specified target triple
Expand All @@ -257,7 +364,7 @@ pub fn get_builtin_target_by_triple(triple: &str) -> Option<&'static TargetInfo>
/// versions.
///
/// ```
/// assert_eq!("1.59.0", cfg_expr::targets::rustc_version());
/// assert_eq!("1.60.0", cfg_expr::targets::rustc_version());
/// ```
pub fn rustc_version() -> &'static str {
builtins::RUSTC_VERSION
Expand Down
Loading