use proc_macro2::TokenStream;
use quote::quote;
use syn::{
Attribute, Data, DeriveInput, Error, GenericArgument, Generics, Ident, Index, Member, Path,
PathArguments, ReturnType, Token, Type, TypeTuple, Visibility, WhereClause, WherePredicate,
parenthesized,
parse::{Parse, ParseStream},
parse_quote,
punctuated::Punctuated,
spanned::Spanned,
token::Paren,
};
mod keyword {
syn::custom_keyword!(constructor);
syn::custom_keyword!(constructor_mut);
syn::custom_keyword!(derive);
syn::custom_keyword!(derive_owned);
syn::custom_keyword!(error);
syn::custom_keyword!(getter);
syn::custom_keyword!(invariant);
syn::custom_keyword!(owned);
syn::custom_keyword!(transparent);
syn::custom_keyword!(unsafe_constructor);
syn::custom_keyword!(unsafe_constructor_mut);
syn::custom_keyword!(AsRef);
syn::custom_keyword!(CloneBoxed);
syn::custom_keyword!(Debug);
syn::custom_keyword!(Deref);
syn::custom_keyword!(Into);
syn::custom_keyword!(IntoBoxed);
syn::custom_keyword!(TryFrom);
}
pub(crate) fn expand(input: DeriveInput) -> syn::Result<TokenStream> {
if !input.attrs.iter().any(attr_is_repr_transparent) {
return Err(Error::new(
input.span(),
"`DstNewtype` requires the subject type to be `#[repr(transparent)]`",
));
}
let ident = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let member = NewtypeMember::try_from(&input.data)?;
let NewtypeMember {
member: newtype_member,
ty: inner_ty,
} = member.clone();
let mut dizzy_args = Vec::new();
for attr in input.attrs {
if attr.path().is_ident("dizzy") {
let args =
attr.parse_args_with(Punctuated::<Arg, Token![,]>::parse_separated_nonempty)?;
dizzy_args.extend(args)
}
}
let opts = DstNewtypeOpts::from_args(dizzy_args.into_iter())?;
let invariant = opts
.invariant
.ok_or_else(|| Error::new(ident.span(), "missing dizzy `invariant` attribute"))?;
let make_constructor = |fn_descriptor: &FnDescriptor, is_mut, is_unsafe| {
ConstructorDescriptor {
ident,
generics: &input.generics,
inner_ty: &inner_ty,
fn_descriptor,
error_ty: opts.error_type.as_ref(),
invariant: &invariant,
is_mut,
is_unsafe,
}
.render()
};
let impl_constructor = opts
.constructor
.as_ref()
.map(|desc| make_constructor(desc, false, false))
.unwrap_or_default();
let impl_constructor_mut = opts
.constructor_mut
.as_ref()
.map(|desc| make_constructor(desc, true, false))
.unwrap_or_default();
let impl_unsafe_constructor = opts
.unsafe_constructor
.as_ref()
.map(|desc| make_constructor(desc, false, true))
.unwrap_or_default();
let impl_unsafe_constructor_mut = opts
.unsafe_constructor_mut
.as_ref()
.map(|desc| make_constructor(desc, true, true))
.unwrap_or_default();
let impl_getter = if let Some(FnDescriptor {
attrs,
visibility,
constness,
ident: name,
}) = opts.getter
{
quote! {
impl #impl_generics #ident #ty_generics #where_clause {
#(#attrs)*
#[inline(always)]
#visibility #constness fn #name (&self) -> &#inner_ty {
&self.#newtype_member
}
}
}
} else {
quote! {}
};
let trait_impls = opts.traits.into_iter().map(|trait_name| {
trait_name.render_with(
ident.clone(),
&input.generics,
&invariant,
&member,
opts.error_type.as_ref(),
)
});
let owned_type_descriptor = opts.owned.as_ref().map(|descriptor| OwnedTypeDescriptor {
newtype_ident: ident,
generics: &input.generics,
member: &member,
descriptor,
});
let owned_type = owned_type_descriptor
.as_ref()
.map(OwnedTypeDescriptor::render);
let owned_trait_impls = if let Some(descriptor) = owned_type_descriptor.as_ref() {
let impls = opts
.owned_traits
.into_iter()
.map(|trait_name| trait_name.render_owned_with(descriptor));
quote! { #(#impls)* }
} else if !opts.owned_traits.is_empty() {
Error::new(ident.span(), "`derive_owned` requires `owned`").to_compile_error()
} else {
quote! {}
};
Ok(quote! {
#impl_constructor
#impl_constructor_mut
#impl_unsafe_constructor
#impl_unsafe_constructor_mut
#impl_getter
#(#trait_impls)*
#owned_type
#owned_trait_impls
})
}
struct ConstructorDescriptor<'a> {
ident: &'a Ident,
generics: &'a Generics,
inner_ty: &'a Type,
fn_descriptor: &'a FnDescriptor,
error_ty: Option<&'a Type>,
invariant: &'a Path,
is_mut: bool,
is_unsafe: bool,
}
impl ConstructorDescriptor<'_> {
fn render(&self) -> TokenStream {
let lib = lib_path();
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let ident = self.ident;
let inner_ty = self.inner_ty;
let invariant = self.invariant;
let error_ty = self.error_ty;
let FnDescriptor {
attrs,
visibility,
constness,
ident: name,
} = self.fn_descriptor;
let mutability = if self.is_mut {
quote! { mut }
} else {
quote! {}
};
let transmute = transmute_ref(inner_ty, "e! { Self }, "e! { input }, self.is_mut);
let body = if self.is_unsafe {
let debug_check = match error_ty {
Some(_) => quote! { debug_assert!(#invariant(input).is_ok()); },
None => quote! { debug_assert!(#invariant(input)); },
};
quote! {
#debug_check
#transmute
}
} else {
match error_ty {
Some(_) => quote! {
match #invariant(input) {
#lib::Err(error) => #lib::Err(error),
#lib::Ok(()) => #lib::Ok({ #transmute }),
}
},
None => quote! {
match #invariant(input) {
false => #lib::None,
true => #lib::Some({ #transmute }),
}
},
}
};
let unsafe_token = if self.is_unsafe {
quote! { unsafe }
} else {
quote! {}
};
let return_ty = if self.is_unsafe {
quote! { &#mutability Self }
} else {
match error_ty {
Some(error_ty) => quote! { #lib::Result<&#mutability Self, #error_ty> },
None => quote! { #lib::Option<&#mutability Self> },
}
};
quote! {
impl #impl_generics #ident #ty_generics #where_clause {
#(#attrs)*
#visibility #constness #unsafe_token fn #name (input: &#mutability #inner_ty) -> #return_ty {
#body
}
}
}
}
}
struct OwnedTypeDescriptor<'a> {
newtype_ident: &'a Ident,
generics: &'a Generics,
member: &'a NewtypeMember,
descriptor: &'a NewtypeDescriptor,
}
impl OwnedTypeDescriptor<'_> {
fn render(&self) -> TokenStream {
let lib = lib_path();
let generics = self.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let ident = self.newtype_ident;
let NewtypeMember {
member: newtype_member,
ty: inner_ty,
} = self.member;
let NewtypeDescriptor {
attrs,
visibility,
ident: name,
paren_token: _,
inner: owned_inner_ty,
} = self.descriptor;
let owned_newtype_member = owned_newtype_member();
let phantom_ty_param = make_phantom_type(self.generics);
let where_clause_with = |ty, predicate| {
concat_predicate_if_necessary(ty, generics, where_clause.cloned(), predicate)
};
let where_clause_clone = where_clause_with(
owned_inner_ty,
parse_quote! { #owned_inner_ty: #lib::clone::Clone },
);
let where_clause_from = where_clause_with(
owned_inner_ty,
parse_quote! { #owned_inner_ty: for<'__dizzy> #lib::convert::From<&'__dizzy #inner_ty> },
);
let where_clause_deref = where_clause_with(
owned_inner_ty,
parse_quote! { #owned_inner_ty: #lib::ops::Deref<Target = #inner_ty> },
);
let where_clause_deref_mut = concat_predicate_if_necessary(
owned_inner_ty,
generics,
where_clause_deref.clone(),
parse_quote! { #owned_inner_ty: #lib::ops::DerefMut },
);
let where_clause_as_ref = where_clause_with(
owned_inner_ty,
parse_quote! { #owned_inner_ty: #lib::convert::AsRef<#inner_ty> },
);
let where_clause_as_mut = where_clause_with(
owned_inner_ty,
parse_quote! { #owned_inner_ty: #lib::convert::AsMut<#inner_ty> },
);
let where_clause_borrow = where_clause_with(
owned_inner_ty,
parse_quote! { #owned_inner_ty: #lib::borrow::Borrow<#inner_ty> },
);
let where_clause_to_owned = where_clause_with(
owned_inner_ty,
parse_quote! { #inner_ty: #lib::ToOwned<Owned = #owned_inner_ty> },
);
let where_clause_cow = where_clause_with(
inner_ty,
parse_quote! { #inner_ty: #lib::ToOwned<Owned = #owned_inner_ty> },
);
let mut cow_generics = self.generics.clone();
cow_generics.params.insert(0, parse_quote! { '__dizzy });
let (cow_impl_generics, _, _) = cow_generics.split_for_impl();
let newtype_ty = quote! { #ident #ty_generics };
let transmute_shared = transmute_ref(&inner_ty, &newtype_ty, "e! { inner_ref }, false);
let transmute_mut = transmute_ref(&inner_ty, &newtype_ty, "e! { inner_mut }, true);
let transmute_borrowed = transmute_ref(&inner_ty, &newtype_ty, "e! { borrowed }, false);
quote! {
#(#attrs)*
#visibility struct #name #generics {
__data: #lib::marker::PhantomData<#phantom_ty_param>,
#owned_newtype_member: #owned_inner_ty,
}
impl #impl_generics #lib::clone::Clone for #name #ty_generics #where_clause_clone {
#[inline(always)]
fn clone(&self) -> Self {
let cloned = <#owned_inner_ty as #lib::clone::Clone>::clone(&self.#owned_newtype_member);
Self { __data: #lib::marker::PhantomData, #owned_newtype_member: cloned }
}
}
impl #impl_generics #lib::convert::From<& #ident #ty_generics> for #name #ty_generics #where_clause_from {
#[inline(always)]
fn from(value: & #ident #ty_generics) -> Self {
let inner = <#owned_inner_ty as #lib::convert::From<& #inner_ty>>::from(&value.#newtype_member);
Self { __data: #lib::marker::PhantomData, #owned_newtype_member: inner }
}
}
impl #impl_generics #lib::convert::From<&mut #ident #ty_generics> for #name #ty_generics #where_clause_from {
#[inline(always)]
fn from(value: &mut #ident #ty_generics) -> Self {
let inner = <#owned_inner_ty as #lib::convert::From<& #inner_ty>>::from(&value.#newtype_member);
Self { __data: #lib::marker::PhantomData, #owned_newtype_member: inner }
}
}
impl #impl_generics #lib::ops::Deref for #name #ty_generics #where_clause_deref {
type Target = #ident #ty_generics;
#[inline(always)]
fn deref(&self) -> &Self::Target {
let inner_ref: &#inner_ty = <#owned_inner_ty as #lib::ops::Deref>::deref(&self.#owned_newtype_member);
#transmute_shared
}
}
impl #impl_generics #lib::ops::DerefMut for #name #ty_generics #where_clause_deref_mut {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
let inner_mut: &mut #inner_ty = <#owned_inner_ty as #lib::ops::DerefMut>::deref_mut(&mut self.#owned_newtype_member);
#transmute_mut
}
}
impl #impl_generics #lib::convert::AsRef<#ident #ty_generics> for #name #ty_generics #where_clause_as_ref {
#[inline(always)]
fn as_ref(&self) -> &#ident #ty_generics {
let inner_ref = <#owned_inner_ty as #lib::convert::AsRef<#inner_ty>>::as_ref(&self.#owned_newtype_member);
#transmute_shared
}
}
impl #impl_generics #lib::convert::AsMut<#ident #ty_generics> for #name #ty_generics #where_clause_as_mut {
#[inline(always)]
fn as_mut(&mut self) -> &mut #ident #ty_generics {
let inner_mut = <#owned_inner_ty as #lib::convert::AsMut<#inner_ty>>::as_mut(&mut self.#owned_newtype_member);
#transmute_mut
}
}
impl #impl_generics #lib::borrow::Borrow<#ident #ty_generics> for #name #ty_generics #where_clause_borrow {
#[inline(always)]
fn borrow(&self) -> &#ident #ty_generics {
let borrowed = <#owned_inner_ty as #lib::borrow::Borrow<#inner_ty>>::borrow(&self.#owned_newtype_member);
#transmute_borrowed
}
}
impl #impl_generics #lib::ToOwned for #ident #ty_generics #where_clause_to_owned {
type Owned = #name #ty_generics;
#[inline(always)]
fn to_owned(&self) -> Self::Owned {
let owned = <#inner_ty as #lib::ToOwned>::to_owned(&self.#newtype_member);
#name { __data: #lib::marker::PhantomData, #owned_newtype_member: owned }
}
}
impl #cow_impl_generics #lib::convert::From<&'__dizzy #name #ty_generics> for #lib::Cow<'__dizzy, #ident #ty_generics> #where_clause_cow {
#[inline(always)]
fn from(value: &'__dizzy #name #ty_generics) -> Self {
#lib::Cow::Owned(<#ident #ty_generics as #lib::ToOwned>::to_owned(value))
}
}
}
}
}
#[derive(Clone)]
struct NewtypeMember {
member: Member,
ty: Type,
}
impl<'a> TryFrom<&'a Data> for NewtypeMember {
type Error = Error;
fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
match value {
Data::Enum(data_enum) => {
let span = data_enum.enum_token.span;
Err(Error::new(span, "expected a struct but got an enum"))
}
Data::Union(data_union) => {
let span = data_union.union_token.span;
Err(Error::new(span, "expected a struct but got a union"))
}
Data::Struct(data_struct) => {
let span = data_struct.struct_token.span;
let fields = &data_struct.fields;
match fields.len() {
0 => Err(Error::new(span, "expected at least one struct field")),
_ => {
let field = fields.iter().last().unwrap();
let ty = field.ty.clone();
let member = field.ident.clone().map(Member::Named).unwrap_or_else(|| {
Member::Unnamed(Index {
index: (fields.len() as u32) - 1,
span: ty.span(),
})
});
Ok(NewtypeMember { member, ty })
}
}
}
}
}
}
#[derive(Default)]
struct DstNewtypeOpts {
invariant: Option<Path>,
error_type: Option<Type>,
constructor: Option<FnDescriptor>,
constructor_mut: Option<FnDescriptor>,
unsafe_constructor: Option<FnDescriptor>,
unsafe_constructor_mut: Option<FnDescriptor>,
getter: Option<FnDescriptor>,
traits: Vec<TraitName>,
owned: Option<NewtypeDescriptor>,
owned_traits: Vec<TraitName>,
}
impl DstNewtypeOpts {
fn from_args(args: impl Iterator<Item = Arg>) -> syn::Result<Self> {
let mut errors = Vec::new();
let mut opts = DstNewtypeOpts::default();
macro_rules! try_set {
($key:ident = $value:expr, $name:literal, $span:expr) => {
match opts.$key {
Some(_) => errors.push(Error::new(
$span,
format!("duplicate dizzy attribute `{}`", $name),
)),
None => {
opts.$key = Some($value);
}
}
};
}
for arg in args {
match arg {
Arg::Constructor {
constructor_token: token,
eq_token: _,
descriptor,
} => try_set!(constructor = descriptor, "constructor", token.span),
Arg::ConstructorMut {
constructor_mut_token: token,
eq_token: _,
descriptor,
} => try_set!(constructor_mut = descriptor, "constructor_mut", token.span),
Arg::Derive { trait_names, .. } => opts.traits.extend(trait_names),
Arg::DeriveOwned { trait_names, .. } => opts.owned_traits.extend(trait_names),
Arg::Error {
error_token: token,
eq_token: _,
ty,
} => try_set!(error_type = ty, "error", token.span),
Arg::Getter {
getter_token: token,
eq_token: _,
descriptor,
} => try_set!(getter = descriptor, "getter", token.span),
Arg::Invariant {
invariant_token: token,
eq_token: _,
path,
} => try_set!(invariant = path, "invariant", token.span),
Arg::Owned {
owned_token: token,
eq_token: _,
descriptor,
} => try_set!(owned = descriptor, "owned", token.span),
Arg::UnsafeConstructor {
unsafe_constructor_token: token,
eq_token: _,
descriptor,
} => {
try_set!(
unsafe_constructor = descriptor,
"unsafe_constructor",
token.span
)
}
Arg::UnsafeConstructorMut {
unsafe_constructor_mut_token: token,
eq_token: _,
descriptor,
} => try_set!(
unsafe_constructor_mut = descriptor,
"unsafe_constructor_mut",
token.span
),
}
}
let mut errors = errors.into_iter();
match errors.next() {
None => Ok(opts),
Some(head) => {
let mut combined = head;
for error in errors {
combined.combine(error);
}
Err(combined)
}
}
}
}
#[allow(dead_code)]
enum Arg {
Constructor {
constructor_token: keyword::constructor,
eq_token: Token![=],
descriptor: FnDescriptor,
},
ConstructorMut {
constructor_mut_token: keyword::constructor_mut,
eq_token: Token![=],
descriptor: FnDescriptor,
},
Derive {
derive_token: keyword::derive,
paren_token: Paren,
trait_names: Punctuated<TraitName, Token![,]>,
},
DeriveOwned {
derive_owned_token: keyword::derive_owned,
paren_token: Paren,
trait_names: Punctuated<TraitName, Token![,]>,
},
Error {
error_token: keyword::error,
eq_token: Token![=],
ty: Type,
},
Getter {
getter_token: keyword::getter,
eq_token: Token![=],
descriptor: FnDescriptor,
},
Invariant {
invariant_token: keyword::invariant,
eq_token: Token![=],
path: Path,
},
Owned {
owned_token: keyword::owned,
eq_token: Token![=],
descriptor: NewtypeDescriptor,
},
UnsafeConstructor {
unsafe_constructor_token: keyword::unsafe_constructor,
eq_token: Token![=],
descriptor: FnDescriptor,
},
UnsafeConstructorMut {
unsafe_constructor_mut_token: keyword::unsafe_constructor_mut,
eq_token: Token![=],
descriptor: FnDescriptor,
},
}
impl Parse for Arg {
fn parse(input: ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(keyword::constructor) {
Ok(Self::Constructor {
constructor_token: input.parse()?,
eq_token: input.parse()?,
descriptor: input.parse()?,
})
} else if lookahead.peek(keyword::constructor_mut) {
Ok(Self::ConstructorMut {
constructor_mut_token: input.parse()?,
eq_token: input.parse()?,
descriptor: input.parse()?,
})
} else if lookahead.peek(keyword::derive) {
let content;
Ok(Self::Derive {
derive_token: input.parse()?,
paren_token: parenthesized!(content in input),
trait_names: content.parse_terminated(TraitName::parse, Token![,])?,
})
} else if lookahead.peek(keyword::derive_owned) {
let content;
Ok(Self::DeriveOwned {
derive_owned_token: input.parse()?,
paren_token: parenthesized!(content in input),
trait_names: content.parse_terminated(TraitName::parse, Token![,])?,
})
} else if lookahead.peek(keyword::error) {
Ok(Self::Error {
error_token: input.parse()?,
eq_token: input.parse()?,
ty: input.parse()?,
})
} else if lookahead.peek(keyword::getter) {
Ok(Self::Getter {
getter_token: input.parse()?,
eq_token: input.parse()?,
descriptor: input.parse()?,
})
} else if lookahead.peek(keyword::invariant) {
Ok(Self::Invariant {
invariant_token: input.parse()?,
eq_token: input.parse()?,
path: input.parse()?,
})
} else if lookahead.peek(keyword::owned) {
Ok(Self::Owned {
owned_token: input.parse()?,
eq_token: input.parse()?,
descriptor: input.parse()?,
})
} else if lookahead.peek(keyword::unsafe_constructor) {
Ok(Self::UnsafeConstructor {
unsafe_constructor_token: input.parse()?,
eq_token: input.parse()?,
descriptor: input.parse()?,
})
} else if lookahead.peek(keyword::unsafe_constructor_mut) {
Ok(Self::UnsafeConstructorMut {
unsafe_constructor_mut_token: input.parse()?,
eq_token: input.parse()?,
descriptor: input.parse()?,
})
} else {
Err(lookahead.error())
}
}
}
#[derive(Clone)]
#[allow(dead_code)]
struct NewtypeDescriptor {
attrs: Vec<Attribute>,
visibility: Visibility,
ident: Ident,
paren_token: Paren,
inner: Type,
}
impl Parse for NewtypeDescriptor {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(Self {
attrs: input.call(Attribute::parse_outer)?,
visibility: input.parse()?,
ident: input.parse()?,
paren_token: parenthesized!(content in input),
inner: content.parse()?,
})
}
}
#[derive(Clone)]
struct FnDescriptor {
attrs: Vec<Attribute>,
visibility: Visibility,
constness: Option<Token![const]>,
ident: Ident,
}
impl Parse for FnDescriptor {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
attrs: input.call(Attribute::parse_outer)?,
visibility: input.parse()?,
constness: input.parse()?,
ident: input.parse()?,
})
}
}
#[allow(dead_code)]
enum TraitName {
AsRef(keyword::AsRef),
CloneBoxed(keyword::CloneBoxed),
Debug(keyword::Debug),
Deref(keyword::Deref),
Into(keyword::Into),
IntoBoxed(keyword::IntoBoxed),
TryFrom(keyword::TryFrom),
}
impl Parse for TraitName {
fn parse(input: ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(keyword::AsRef) {
Ok(Self::AsRef(input.parse()?))
} else if lookahead.peek(keyword::CloneBoxed) {
Ok(Self::CloneBoxed(input.parse()?))
} else if lookahead.peek(keyword::Debug) {
Ok(Self::Debug(input.parse()?))
} else if lookahead.peek(keyword::Deref) {
Ok(Self::Deref(input.parse()?))
} else if lookahead.peek(keyword::Into) {
Ok(Self::Into(input.parse()?))
} else if lookahead.peek(keyword::IntoBoxed) {
Ok(Self::IntoBoxed(input.parse()?))
} else if lookahead.peek(keyword::TryFrom) {
Ok(Self::TryFrom(input.parse()?))
} else {
Err(lookahead.error())
}
}
}
impl TraitName {
fn render_with(
&self,
def_name: Ident,
generics: &Generics,
invariant: &Path,
member: &NewtypeMember,
error_ty: Option<&Type>,
) -> TokenStream {
let lib = lib_path();
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let NewtypeMember {
member: newtype_member,
ty: inner_ty,
} = member;
match self {
TraitName::AsRef(_) => {
quote! {
impl #impl_generics #lib::convert::AsRef<#inner_ty> for #def_name #ty_generics #where_clause {
#[inline(always)]
fn as_ref(&self) -> &#inner_ty {
&self.#newtype_member
}
}
}
}
TraitName::CloneBoxed(_) => {
let where_clause = concat_predicate_if_necessary(
inner_ty,
generics,
where_clause.cloned(),
parse_quote! { #lib::Box<#inner_ty>: for<'z> #lib::convert::From<&'z #inner_ty> },
);
quote! {
impl #impl_generics #lib::clone::Clone for #lib::Box<#def_name #ty_generics> #where_clause {
#[inline(always)]
fn clone(&self) -> Self {
let cloned = <#lib::Box<#inner_ty> as #lib::convert::From<&#inner_ty>>::from(&self.#newtype_member);
unsafe {
let ptr = #lib::Box::into_raw(cloned);
#lib::Box::from_raw(ptr as *mut #def_name #ty_generics)
}
}
}
}
}
TraitName::Debug(_) => {
let where_clause = concat_predicate_if_necessary(
inner_ty,
generics,
where_clause.cloned(),
parse_quote! { #inner_ty: #lib::fmt::Debug },
);
quote! {
impl #impl_generics #lib::fmt::Debug for #def_name #ty_generics #where_clause {
#[inline(always)]
fn fmt(&self, f: &mut #lib::fmt::Formatter<'_>) -> #lib::Result<(), #lib::fmt::Error> {
<#inner_ty as #lib::fmt::Debug>::fmt(&self.#newtype_member, f)
}
}
}
}
TraitName::Deref(_) => quote! {
impl #impl_generics #lib::ops::Deref for #def_name #ty_generics #where_clause {
type Target = #inner_ty;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.#newtype_member
}
}
},
TraitName::Into(_) => quote! {
impl #impl_generics #lib::convert::From<&#def_name #ty_generics> for &#inner_ty #where_clause {
#[inline(always)]
fn from(value: &#def_name #ty_generics) -> Self {
&value.#newtype_member
}
}
},
TraitName::IntoBoxed(_) => {
let where_clause = concat_predicate_if_necessary(
inner_ty,
generics,
where_clause.cloned(),
parse_quote! { #lib::Box<#inner_ty>: for<'z> #lib::convert::From<&'z #inner_ty> },
);
quote! {
impl #impl_generics #lib::convert::From<&#def_name #ty_generics> for #lib::Box<#def_name #ty_generics> #where_clause {
#[inline(always)]
fn from(value: &#def_name #ty_generics) -> Self {
let cloned = <#lib::Box::<#inner_ty> as #lib::convert::From<&#inner_ty>>::from(&value.#newtype_member);
unsafe {
let ptr = #lib::Box::into_raw(cloned);
#lib::Box::from_raw(ptr as *mut #def_name #ty_generics)
}
}
}
impl #impl_generics #lib::convert::From<&mut #def_name #ty_generics> for #lib::Box<#def_name #ty_generics> #where_clause {
#[inline(always)]
fn from(value: &mut #def_name #ty_generics) -> Self {
let shared_ref: &#def_name #ty_generics = value;
Self::from(shared_ref)
}
}
}
}
TraitName::TryFrom(_) => {
let conversion_error_ty = match error_ty {
Some(ty) => ty,
None => &parse_quote! { () },
};
let transmute_mut = transmute_ref(
inner_ty,
"e! { #def_name #ty_generics },
"e! { value },
true,
);
let transmute_ref = transmute_ref(
inner_ty,
"e! { #def_name #ty_generics },
"e! { value },
false,
);
let try_from_ref_body = match error_ty {
Some(_) => quote! {
match #invariant(value) {
#lib::Ok(()) => #lib::Ok({ #transmute_ref }),
#lib::Err(error) => #lib::Err(error),
}
},
None => quote! {
match #invariant(value) {
true => #lib::Ok({ #transmute_ref }),
false => #lib::Err(()),
}
},
};
let try_from_mut_body = match error_ty {
Some(_) => quote! {
match #invariant(value) {
#lib::Ok(()) => #lib::Ok({ #transmute_mut }),
#lib::Err(error) => #lib::Err(error),
}
},
None => quote! {
match #invariant(value) {
true => #lib::Ok({ #transmute_mut }),
false => #lib::Err(()),
}
},
};
quote! {
impl #impl_generics #lib::convert::TryFrom<&#inner_ty> for &#def_name #ty_generics #where_clause {
type Error = #conversion_error_ty;
#[inline(always)]
fn try_from(value: &#inner_ty) -> #lib::Result<Self, Self::Error> {
#try_from_ref_body
}
}
impl #impl_generics #lib::convert::TryFrom<&mut #inner_ty> for &mut #def_name #ty_generics #where_clause {
type Error = #conversion_error_ty;
#[inline(always)]
fn try_from(value: &mut #inner_ty) -> #lib::Result<Self, Self::Error> {
#try_from_mut_body
}
}
}
}
}
}
fn render_owned_with(&self, descriptor: &OwnedTypeDescriptor<'_>) -> TokenStream {
let lib = lib_path();
let (impl_generics, ty_generics, where_clause) = descriptor.generics.split_for_impl();
let newtype_ident = &descriptor.newtype_ident;
let _newtype_member = &descriptor.member.member;
let inner_ty = &descriptor.member.ty;
let owned_name = &descriptor.descriptor.ident;
let owned_inner_ty = &descriptor.descriptor.inner;
let owned_newtype_member = owned_newtype_member();
match self {
TraitName::Debug(_) => {
let where_clause = concat_predicate_if_necessary(
inner_ty,
descriptor.generics,
where_clause.cloned(),
parse_quote! { #newtype_ident #ty_generics: #lib::fmt::Debug },
);
let where_clause = concat_predicate_if_necessary(
owned_inner_ty,
descriptor.generics,
where_clause,
parse_quote! { #owned_inner_ty: #lib::ops::Deref<Target = #inner_ty> },
);
quote! {
impl #impl_generics #lib::fmt::Debug for #owned_name #ty_generics #where_clause {
#[inline(always)]
fn fmt(&self, f: &mut #lib::fmt::Formatter<'_>) -> #lib::Result<(), #lib::fmt::Error> {
let inner_deref = <#owned_name #ty_generics as #lib::ops::Deref>::deref(self);
<#newtype_ident #ty_generics as #lib::fmt::Debug>::fmt(&inner_deref, f)
}
}
}
}
TraitName::IntoBoxed(_) => {
let where_clause = concat_predicate_if_necessary(
owned_inner_ty,
descriptor.generics,
where_clause.cloned(),
parse_quote! { #owned_inner_ty: #lib::convert::Into<#lib::Box<#inner_ty>> },
);
quote! {
impl #impl_generics #lib::convert::From<#owned_name #ty_generics> for #lib::Box<#newtype_ident #ty_generics> #where_clause {
#[inline(always)]
fn from(value: #owned_name #ty_generics) -> Self {
let owned_inner = value.#owned_newtype_member;
let boxed_raw = <#owned_inner_ty as #lib::convert::Into<#lib::Box<#inner_ty>>>::into(owned_inner);
unsafe {
let ptr = #lib::Box::into_raw(boxed_raw);
#lib::Box::from_raw(ptr as *mut #newtype_ident #ty_generics)
}
}
}
}
}
TraitName::AsRef(token) => Error::new(
token.span,
"`AsRef` is an invalid argument to `derive_owned`",
)
.into_compile_error(),
TraitName::CloneBoxed(token) => Error::new(
token.span,
"`CloneBoxed` is an invalid argument to `derive_owned`",
)
.into_compile_error(),
TraitName::Deref(token) => Error::new(
token.span,
"`Deref` is an invalid argument to `derive_owned`",
)
.into_compile_error(),
TraitName::Into(token) => Error::new(
token.span,
"`Into` is an invalid argument to `derive_owned`",
)
.into_compile_error(),
TraitName::TryFrom(token) => Error::new(
token.span,
"`TryFrom` is an invalid argument to `derive_owned`",
)
.into_compile_error(),
}
}
}
fn transmute_ref(
from: &impl quote::ToTokens,
to: &impl quote::ToTokens,
expr: &impl quote::ToTokens,
is_mut: bool,
) -> TokenStream {
let lib = lib_path();
let mutability = if is_mut {
quote! { mut }
} else {
quote! {}
};
quote! {
unsafe { #lib::mem::transmute::<&#mutability #from, &#mutability #to>(#expr) }
}
}
fn type_may_be_generic(ty: &Type, generics: &Generics) -> bool {
match ty {
Type::Path(ty) => {
if let Some(qself) = &ty.qself
&& type_may_be_generic(&qself.ty, generics)
{
return true;
}
match ty.path.get_ident() {
Some(ident) => generics.type_params().any(|param| ¶m.ident == ident),
None => ty
.path
.segments
.iter()
.any(|segment| match &segment.arguments {
PathArguments::None => false,
PathArguments::AngleBracketed(args) => {
args.args.iter().any(|arg| match arg {
GenericArgument::Type(ty) => type_may_be_generic(ty, generics),
_ => false,
})
}
PathArguments::Parenthesized(args) => {
let ret_ty_is_generic = match &args.output {
ReturnType::Default => false,
ReturnType::Type(_, ty) => type_may_be_generic(ty, generics),
};
ret_ty_is_generic
|| args
.inputs
.iter()
.any(|ty| type_may_be_generic(ty, generics))
}
}),
}
}
Type::Array(ty) => type_may_be_generic(&ty.elem, generics),
Type::BareFn(ty) => {
let ret_ty_is_generic = match &ty.output {
ReturnType::Default => false,
ReturnType::Type(_, ty) => type_may_be_generic(ty, generics),
};
ret_ty_is_generic
|| ty
.inputs
.iter()
.any(|arg| type_may_be_generic(&arg.ty, generics))
}
Type::Group(ty) => type_may_be_generic(&ty.elem, generics),
Type::Never(_) => false,
Type::Paren(ty) => type_may_be_generic(&ty.elem, generics),
Type::Ptr(ty) => type_may_be_generic(&ty.elem, generics),
Type::Reference(ty) => type_may_be_generic(&ty.elem, generics),
Type::Slice(ty) => type_may_be_generic(&ty.elem, generics),
Type::Tuple(ty) => ty.elems.iter().any(|ty| type_may_be_generic(ty, generics)),
Type::TraitObject(_)
| Type::ImplTrait(_)
| Type::Infer(_)
| Type::Macro(_)
| Type::Verbatim(_) => true,
_ => true,
}
}
fn make_phantom_type(generics: &Generics) -> Type {
let mut tuple: TypeTuple = parse_quote! { () };
for type_param in generics.type_params() {
let ident = &type_param.ident;
tuple.elems.push(Type::Path(parse_quote! { #ident }));
}
for const_param in generics.const_params() {
let ident = &const_param.ident;
tuple.elems.push(parse_quote! { [(); #ident] });
}
for lifetime_param in generics.lifetimes() {
let lifetime = &lifetime_param.lifetime;
tuple.elems.push(parse_quote! { &#lifetime () });
}
Type::Tuple(tuple)
}
fn concat_predicate_if_necessary(
ty: &Type,
generics: &Generics,
clause: Option<WhereClause>,
predicate: WherePredicate,
) -> Option<WhereClause> {
match type_may_be_generic(ty, generics) {
false => clause,
true => Some(concat_predicate(clause, predicate)),
}
}
fn concat_predicate(clause: Option<WhereClause>, predicate: WherePredicate) -> WhereClause {
match clause {
Some(mut clause) => {
clause.predicates.push(predicate);
clause
}
None => WhereClause {
where_token: Default::default(),
predicates: Punctuated::from_iter(std::iter::once(predicate)),
},
}
}
fn owned_newtype_member() -> Ident {
parse_quote! { __dizzy_owned_inner }
}
fn lib_path() -> Path {
parse_quote! { ::dizzy::lib }
}
fn attr_is_repr_transparent(input: &Attribute) -> bool {
input.path().is_ident("repr") && input.parse_args_with(keyword::transparent::parse).is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
fn generics_with_t() -> Generics {
parse_quote! { <T> }
}
fn check(ty: Type, generics: &Generics, expected: bool) {
assert_eq!(
type_may_be_generic(&ty, generics),
expected,
"type_may_be_generic({:?}, ...) should be {}",
quote::quote!(#ty).to_string(),
expected,
);
}
#[test]
fn concrete_primitives_are_not_generic() {
let g = generics_with_t();
check(parse_quote! { str }, &g, false);
check(parse_quote! { u32 }, &g, false);
check(parse_quote! { bool }, &g, false);
}
#[test]
fn bare_type_param_is_generic() {
let g = generics_with_t();
check(parse_quote! { T }, &g, true);
}
#[test]
fn unrelated_type_param_is_not_generic() {
let g = generics_with_t();
check(parse_quote! { U }, &g, false);
}
#[test]
fn concrete_multi_segment_path() {
let g = generics_with_t();
check(parse_quote! { std::string::String }, &g, false);
}
#[test]
fn generic_in_angle_brackets() {
let g = generics_with_t();
check(parse_quote! { Vec<T> }, &g, true);
check(parse_quote! { Vec<u8> }, &g, false);
check(
parse_quote! { std::collections::HashMap<String, T> },
&g,
true,
);
}
#[test]
fn nested_generic() {
let g = generics_with_t();
check(parse_quote! { Option<Vec<T>> }, &g, true);
check(parse_quote! { Option<Vec<u8>> }, &g, false);
}
#[test]
fn reference_to_generic() {
let g = generics_with_t();
check(parse_quote! { &T }, &g, true);
check(parse_quote! { &str }, &g, false);
check(parse_quote! { &mut T }, &g, true);
}
#[test]
fn slice_of_generic() {
let g = generics_with_t();
check(parse_quote! { [T] }, &g, true);
check(parse_quote! { [u8] }, &g, false);
}
#[test]
fn array_of_generic() {
let g = generics_with_t();
check(parse_quote! { [T; 4] }, &g, true);
check(parse_quote! { [u8; 4] }, &g, false);
}
#[test]
fn pointer_to_generic() {
let g = generics_with_t();
check(parse_quote! { *const T }, &g, true);
check(parse_quote! { *mut T }, &g, true);
check(parse_quote! { *const u8 }, &g, false);
}
#[test]
fn tuple_with_generic() {
let g = generics_with_t();
check(parse_quote! { (T, u8) }, &g, true);
check(parse_quote! { (u8, u16) }, &g, false);
check(parse_quote! { () }, &g, false);
}
#[test]
fn bare_fn_with_generic() {
let g = generics_with_t();
check(parse_quote! { fn(T) -> u8 }, &g, true);
check(parse_quote! { fn(u8) -> T }, &g, true);
check(parse_quote! { fn(u8) -> u16 }, &g, false);
}
#[test]
fn never_type() {
let g = generics_with_t();
check(parse_quote! { ! }, &g, false);
}
#[test]
fn qualified_self_with_generic() {
let g = generics_with_t();
check(parse_quote! { <T as Iterator>::Item }, &g, true);
check(parse_quote! { <u8 as Iterator>::Item }, &g, false);
}
#[test]
fn multiple_type_params() {
let g: Generics = parse_quote! { <A, B> };
check(parse_quote! { A }, &g, true);
check(parse_quote! { B }, &g, true);
check(parse_quote! { C }, &g, false);
check(parse_quote! { Vec<A> }, &g, true);
check(parse_quote! { (A, B) }, &g, true);
}
#[test]
fn generic_in_non_final_segment() {
let g = generics_with_t();
check(parse_quote! { Foo::<T>::Bar }, &g, true);
}
#[test]
fn empty_generics() {
let g: Generics = parse_quote! {};
check(parse_quote! { str }, &g, false);
check(parse_quote! { T }, &g, false);
check(parse_quote! { Vec<u8> }, &g, false);
}
}