Begin nightly-ifying rustc_type_ir
This commit is contained in:
parent
2831701757
commit
4506681e2f
19 changed files with 214 additions and 102 deletions
17
compiler/rustc_index_macros/Cargo.toml
Normal file
17
compiler/rustc_index_macros/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "rustc_index_macros"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
synstructure = "0.13.0"
|
||||
syn = { version = "2.0.9", features = ["full"] }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
|
||||
[features]
|
||||
default = ["nightly"]
|
||||
nightly = []
|
30
compiler/rustc_index_macros/src/lib.rs
Normal file
30
compiler/rustc_index_macros/src/lib.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
#![cfg_attr(feature = "nightly", feature(allow_internal_unstable))]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod newtype;
|
||||
|
||||
/// Creates a struct type `S` that can be used as an index with
|
||||
/// `IndexVec` and so on.
|
||||
///
|
||||
/// There are two ways of interacting with these indices:
|
||||
///
|
||||
/// - The `From` impls are the preferred way. So you can do
|
||||
/// `S::from(v)` with a `usize` or `u32`. And you can convert back
|
||||
/// to an integer with `u32::from(s)`.
|
||||
///
|
||||
/// - Alternatively, you can use the methods `S::new(v)` and `s.index()`
|
||||
/// to create/return a value.
|
||||
///
|
||||
/// Internally, the index uses a u32, so the index must not exceed
|
||||
/// `u32::MAX`. You can also customize things like the `Debug` impl,
|
||||
/// what traits are derived, and so forth via the macro.
|
||||
#[proc_macro]
|
||||
#[cfg_attr(
|
||||
feature = "nightly",
|
||||
allow_internal_unstable(step_trait, rustc_attrs, trusted_step, spec_option_partial_eq)
|
||||
)]
|
||||
pub fn newtype_index(input: TokenStream) -> TokenStream {
|
||||
newtype::newtype(input)
|
||||
}
|
324
compiler/rustc_index_macros/src/newtype.rs
Normal file
324
compiler/rustc_index_macros/src/newtype.rs
Normal file
|
@ -0,0 +1,324 @@
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::parse::*;
|
||||
use syn::*;
|
||||
|
||||
// We parse the input and emit the output in a single step.
|
||||
// This field stores the final macro output
|
||||
struct Newtype(TokenStream);
|
||||
|
||||
impl Parse for Newtype {
|
||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
let mut attrs = input.call(Attribute::parse_outer)?;
|
||||
let vis: Visibility = input.parse()?;
|
||||
input.parse::<Token![struct]>()?;
|
||||
let name: Ident = input.parse()?;
|
||||
|
||||
let body;
|
||||
braced!(body in input);
|
||||
|
||||
// Any additional `#[derive]` macro paths to apply
|
||||
let mut derive_paths: Vec<Path> = Vec::new();
|
||||
let mut debug_format: Option<Lit> = None;
|
||||
let mut max = None;
|
||||
let mut consts = Vec::new();
|
||||
let mut encodable = true;
|
||||
let mut ord = true;
|
||||
let mut gate_rustc_only = quote! {};
|
||||
let mut gate_rustc_only_cfg = quote! { all() };
|
||||
|
||||
attrs.retain(|attr| match attr.path().get_ident() {
|
||||
Some(ident) => match &*ident.to_string() {
|
||||
"gate_rustc_only" => {
|
||||
gate_rustc_only = quote! { #[cfg(feature = "nightly")] };
|
||||
gate_rustc_only_cfg = quote! { feature = "nightly" };
|
||||
false
|
||||
}
|
||||
"custom_encodable" => {
|
||||
encodable = false;
|
||||
false
|
||||
}
|
||||
"no_ord_impl" => {
|
||||
ord = false;
|
||||
false
|
||||
}
|
||||
"max" => {
|
||||
let Meta::NameValue(MetaNameValue { value: Expr::Lit(lit), .. }) = &attr.meta
|
||||
else {
|
||||
panic!("#[max = NUMBER] attribute requires max value");
|
||||
};
|
||||
|
||||
if let Some(old) = max.replace(lit.lit.clone()) {
|
||||
panic!("Specified multiple max: {old:?}");
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
"debug_format" => {
|
||||
let Meta::NameValue(MetaNameValue { value: Expr::Lit(lit), .. }) = &attr.meta
|
||||
else {
|
||||
panic!("#[debug_format = FMT] attribute requires a format");
|
||||
};
|
||||
|
||||
if let Some(old) = debug_format.replace(lit.lit.clone()) {
|
||||
panic!("Specified multiple debug format options: {old:?}");
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
},
|
||||
_ => true,
|
||||
});
|
||||
|
||||
loop {
|
||||
// We've parsed everything that the user provided, so we're done
|
||||
if body.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, we are parsing a user-defined constant
|
||||
let const_attrs = body.call(Attribute::parse_outer)?;
|
||||
body.parse::<Token![const]>()?;
|
||||
let const_name: Ident = body.parse()?;
|
||||
body.parse::<Token![=]>()?;
|
||||
let const_val: Expr = body.parse()?;
|
||||
body.parse::<Token![;]>()?;
|
||||
consts.push(quote! { #(#const_attrs)* #vis const #const_name: #name = #name::from_u32(#const_val); });
|
||||
}
|
||||
|
||||
let debug_format =
|
||||
debug_format.unwrap_or_else(|| Lit::Str(LitStr::new("{}", Span::call_site())));
|
||||
|
||||
// shave off 256 indices at the end to allow space for packing these indices into enums
|
||||
let max = max.unwrap_or_else(|| Lit::Int(LitInt::new("0xFFFF_FF00", Span::call_site())));
|
||||
|
||||
let encodable_impls = if encodable {
|
||||
quote! {
|
||||
#gate_rustc_only
|
||||
impl<D: ::rustc_serialize::Decoder> ::rustc_serialize::Decodable<D> for #name {
|
||||
fn decode(d: &mut D) -> Self {
|
||||
Self::from_u32(d.read_u32())
|
||||
}
|
||||
}
|
||||
#gate_rustc_only
|
||||
impl<E: ::rustc_serialize::Encoder> ::rustc_serialize::Encodable<E> for #name {
|
||||
fn encode(&self, e: &mut E) {
|
||||
e.emit_u32(self.private);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
if ord {
|
||||
derive_paths.push(parse_quote!(Ord));
|
||||
derive_paths.push(parse_quote!(PartialOrd));
|
||||
}
|
||||
|
||||
let step = if ord {
|
||||
quote! {
|
||||
#gate_rustc_only
|
||||
impl ::std::iter::Step for #name {
|
||||
#[inline]
|
||||
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
|
||||
<usize as ::std::iter::Step>::steps_between(
|
||||
&Self::index(*start),
|
||||
&Self::index(*end),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn forward_checked(start: Self, u: usize) -> Option<Self> {
|
||||
Self::index(start).checked_add(u).map(Self::from_usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn backward_checked(start: Self, u: usize) -> Option<Self> {
|
||||
Self::index(start).checked_sub(u).map(Self::from_usize)
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: The implementation of `Step` upholds all invariants.
|
||||
#gate_rustc_only
|
||||
unsafe impl ::std::iter::TrustedStep for #name {}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let debug_impl = quote! {
|
||||
impl ::std::fmt::Debug for #name {
|
||||
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(fmt, #debug_format, self.as_u32())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let spec_partial_eq_impl = if let Lit::Int(max) = &max {
|
||||
if let Ok(max_val) = max.base10_parse::<u32>() {
|
||||
quote! {
|
||||
#gate_rustc_only
|
||||
impl core::option::SpecOptionPartialEq for #name {
|
||||
#[inline]
|
||||
fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
|
||||
if #max_val < u32::MAX {
|
||||
l.map(|i| i.private).unwrap_or(#max_val+1) == r.map(|i| i.private).unwrap_or(#max_val+1)
|
||||
} else {
|
||||
match (l, r) {
|
||||
(Some(l), Some(r)) => r == l,
|
||||
(None, None) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
Ok(Self(quote! {
|
||||
#(#attrs)*
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, #(#derive_paths),*)]
|
||||
#[cfg_attr(#gate_rustc_only_cfg, rustc_layout_scalar_valid_range_end(#max))]
|
||||
#[cfg_attr(#gate_rustc_only_cfg, rustc_pass_by_value)]
|
||||
#vis struct #name {
|
||||
private: u32,
|
||||
}
|
||||
|
||||
#(#consts)*
|
||||
|
||||
impl #name {
|
||||
/// Maximum value the index can take, as a `u32`.
|
||||
#vis const MAX_AS_U32: u32 = #max;
|
||||
|
||||
/// Maximum value the index can take.
|
||||
#vis const MAX: Self = Self::from_u32(#max);
|
||||
|
||||
/// Creates a new index from a given `usize`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `value` exceeds `MAX`.
|
||||
#[inline]
|
||||
#vis const fn from_usize(value: usize) -> Self {
|
||||
assert!(value <= (#max as usize));
|
||||
// SAFETY: We just checked that `value <= max`.
|
||||
unsafe {
|
||||
Self::from_u32_unchecked(value as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new index from a given `u32`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `value` exceeds `MAX`.
|
||||
#[inline]
|
||||
#vis const fn from_u32(value: u32) -> Self {
|
||||
assert!(value <= #max);
|
||||
// SAFETY: We just checked that `value <= max`.
|
||||
unsafe {
|
||||
Self::from_u32_unchecked(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new index from a given `u32`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided value must be less than or equal to the maximum value for the newtype.
|
||||
/// Providing a value outside this range is undefined due to layout restrictions.
|
||||
///
|
||||
/// Prefer using `from_u32`.
|
||||
#[inline]
|
||||
#vis const unsafe fn from_u32_unchecked(value: u32) -> Self {
|
||||
Self { private: value }
|
||||
}
|
||||
|
||||
/// Extracts the value of this index as a `usize`.
|
||||
#[inline]
|
||||
#vis const fn index(self) -> usize {
|
||||
self.as_usize()
|
||||
}
|
||||
|
||||
/// Extracts the value of this index as a `u32`.
|
||||
#[inline]
|
||||
#vis const fn as_u32(self) -> u32 {
|
||||
self.private
|
||||
}
|
||||
|
||||
/// Extracts the value of this index as a `usize`.
|
||||
#[inline]
|
||||
#vis const fn as_usize(self) -> usize {
|
||||
self.as_u32() as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<usize> for #name {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: usize) -> Self {
|
||||
Self::from_usize(self.index() + other)
|
||||
}
|
||||
}
|
||||
|
||||
impl rustc_index::Idx for #name {
|
||||
#[inline]
|
||||
fn new(value: usize) -> Self {
|
||||
Self::from_usize(value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn index(self) -> usize {
|
||||
self.as_usize()
|
||||
}
|
||||
}
|
||||
|
||||
#step
|
||||
|
||||
#spec_partial_eq_impl
|
||||
|
||||
impl From<#name> for u32 {
|
||||
#[inline]
|
||||
fn from(v: #name) -> u32 {
|
||||
v.as_u32()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#name> for usize {
|
||||
#[inline]
|
||||
fn from(v: #name) -> usize {
|
||||
v.as_usize()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for #name {
|
||||
#[inline]
|
||||
fn from(value: usize) -> Self {
|
||||
Self::from_usize(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for #name {
|
||||
#[inline]
|
||||
fn from(value: u32) -> Self {
|
||||
Self::from_u32(value)
|
||||
}
|
||||
}
|
||||
|
||||
#encodable_impls
|
||||
#debug_impl
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn newtype(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as Newtype);
|
||||
input.0.into()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue