mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-08 22:28:01 -05:00
fix(versionable)!: wrong derived bounds in the Versionize macro
Over-restrictive derived bounds were in some cases unsatisfiable, making the `versionize` method uncallable. BREAKING_CHANGE: - The `#[versionize(bound = ...)]` attribute is not needed anymore, so it has been removed.
This commit is contained in:
committed by
Nicolas Sarlin
parent
a631904bd1
commit
b63347336b
@@ -1,11 +1,12 @@
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse_quote, DeriveInput, ImplGenerics, Item, ItemImpl, Lifetime, Path, Type, WhereClause,
|
||||
parse_quote, DeriveInput, Generics, ImplGenerics, Item, ItemImpl, Lifetime, Path, Type,
|
||||
WhereClause,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
add_lifetime_bound, add_trait_bound, add_trait_where_clause, add_where_lifetime_bound,
|
||||
add_lifetime_bound, add_trait_where_clause, add_where_lifetime_bound, extend_where_clause,
|
||||
parse_const_str, DESERIALIZE_TRAIT_NAME, LIFETIME_NAME, SERIALIZE_TRAIT_NAME,
|
||||
};
|
||||
|
||||
@@ -91,6 +92,11 @@ pub(crate) enum AssociatedTypeKind {
|
||||
/// [`DispatchType`]: crate::dispatch_type::DispatchType
|
||||
/// [`VersionType`]: crate::dispatch_type::VersionType
|
||||
pub(crate) trait AssociatedType: Sized {
|
||||
/// Bounds that will be added on the fields of the ref type definition
|
||||
const REF_BOUNDS: &'static [&'static str];
|
||||
/// Bounds that will be added on the fields of the owned type definition
|
||||
const OWNED_BOUNDS: &'static [&'static str];
|
||||
|
||||
/// This will create the alternative of the type that holds a reference to the underlying data
|
||||
fn new_ref(orig_type: &DeriveInput) -> syn::Result<Self>;
|
||||
/// This will create the alternative of the type that owns the underlying data
|
||||
@@ -99,6 +105,30 @@ pub(crate) trait AssociatedType: Sized {
|
||||
/// Generates the type declaration for this type
|
||||
fn generate_type_declaration(&self) -> syn::Result<Item>;
|
||||
|
||||
/// Returns the kind of associated type, a ref or an owned type
|
||||
fn kind(&self) -> &AssociatedTypeKind;
|
||||
|
||||
/// Returns the generics found in the original type definition
|
||||
fn orig_type_generics(&self) -> &Generics;
|
||||
|
||||
/// Returns the generics and bounds that should be added to the type
|
||||
fn type_generics(&self) -> syn::Result<Generics> {
|
||||
let mut generics = self.orig_type_generics().clone();
|
||||
if let AssociatedTypeKind::Ref(opt_lifetime) = &self.kind() {
|
||||
if let Some(lifetime) = opt_lifetime {
|
||||
add_lifetime_bound(&mut generics, lifetime);
|
||||
}
|
||||
add_trait_where_clause(&mut generics, self.inner_types()?, Self::REF_BOUNDS)?;
|
||||
} else {
|
||||
add_trait_where_clause(&mut generics, self.inner_types()?, Self::OWNED_BOUNDS)?;
|
||||
}
|
||||
|
||||
Ok(generics)
|
||||
}
|
||||
|
||||
/// Generics needed for conversions between the original type and associated types
|
||||
fn conversion_generics(&self, direction: ConversionDirection) -> syn::Result<Generics>;
|
||||
|
||||
/// Generates conversion methods between the origin type and the associated type. If the version
|
||||
/// type is a ref, the conversion is `From<&'vers OrigType> for Associated<'vers>` because this
|
||||
/// conversion is used for versioning. If the version type is owned, the conversion is
|
||||
@@ -109,10 +139,6 @@ pub(crate) trait AssociatedType: Sized {
|
||||
/// [`Version`]: crate::dispatch_type::VersionType
|
||||
fn generate_conversion(&self) -> syn::Result<Vec<ItemImpl>>;
|
||||
|
||||
/// The lifetime added for this type, if it is a "ref" type. It also returns None if the type is
|
||||
/// a unit type (no data)
|
||||
//fn lifetime(&self) -> Option<&Lifetime>;
|
||||
|
||||
/// The identifier used to name this type
|
||||
fn ident(&self) -> Ident;
|
||||
|
||||
@@ -144,40 +170,19 @@ pub(crate) struct AssociatingTrait<T> {
|
||||
owned_type: T,
|
||||
orig_type: DeriveInput,
|
||||
trait_path: Path,
|
||||
/// Bounds that should be added to the generics for the impl
|
||||
generics_bounds: Vec<String>,
|
||||
/// Bounds that should be added on the struct attributes
|
||||
attributes_bounds: Vec<String>,
|
||||
}
|
||||
|
||||
impl<T: AssociatedType> AssociatingTrait<T> {
|
||||
pub(crate) fn new(
|
||||
orig_type: &DeriveInput,
|
||||
name: &str,
|
||||
generics_bounds: &[&str],
|
||||
attributes_bounds: &[&str],
|
||||
) -> syn::Result<Self> {
|
||||
pub(crate) fn new(orig_type: &DeriveInput, name: &str) -> syn::Result<Self> {
|
||||
let ref_type = T::new_ref(orig_type)?;
|
||||
let owned_type = T::new_owned(orig_type)?;
|
||||
let trait_path = syn::parse_str(name)?;
|
||||
|
||||
let generics_bounds = generics_bounds
|
||||
.iter()
|
||||
.map(|bound| bound.to_string())
|
||||
.collect();
|
||||
|
||||
let attributes_bounds = attributes_bounds
|
||||
.iter()
|
||||
.map(|bound| bound.to_string())
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
ref_type,
|
||||
owned_type,
|
||||
orig_type: orig_type.clone(),
|
||||
trait_path,
|
||||
generics_bounds,
|
||||
attributes_bounds,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -189,22 +194,24 @@ impl<T: AssociatedType> AssociatingTrait<T> {
|
||||
let ref_ident = self.ref_type.ident();
|
||||
let owned_ident = self.owned_type.ident();
|
||||
|
||||
let mut generics = self.orig_type.generics.clone();
|
||||
|
||||
for bound in &self.generics_bounds {
|
||||
add_trait_bound(&mut generics, bound)?;
|
||||
}
|
||||
|
||||
let trait_param = self.ref_type.as_trait_param().transpose()?;
|
||||
|
||||
let mut ref_type_generics = generics.clone();
|
||||
// AssociatedToOrig conversion always has a stricter bound than the other side so we use it
|
||||
let mut generics = self
|
||||
.owned_type
|
||||
.conversion_generics(ConversionDirection::AssociatedToOrig)?;
|
||||
|
||||
add_trait_where_clause(
|
||||
&mut generics,
|
||||
self.ref_type.inner_types()?,
|
||||
&self.attributes_bounds,
|
||||
)?;
|
||||
// Merge the where clause for the reference type with the one from the owned type
|
||||
let owned_where_clause = generics.make_where_clause();
|
||||
if let Some(ref_where_clause) = self
|
||||
.ref_type
|
||||
.conversion_generics(ConversionDirection::AssociatedToOrig)?
|
||||
.where_clause
|
||||
{
|
||||
extend_where_clause(owned_where_clause, &ref_where_clause);
|
||||
}
|
||||
|
||||
let mut ref_type_generics = self.ref_type.orig_type_generics().clone();
|
||||
// If the original type has some generics, we need to add a lifetime bound on them
|
||||
if let Some(lifetime) = self.ref_type.lifetime() {
|
||||
add_lifetime_bound(&mut ref_type_generics, lifetime);
|
||||
@@ -246,8 +253,13 @@ impl<T: AssociatedType> AssociatingTrait<T> {
|
||||
)
|
||||
]};
|
||||
|
||||
// Creates the type declaration. These types are the output of the versioning process, so
|
||||
// they should be serializable. Serde might try to add automatic bounds on the type generics
|
||||
// even if we don't need them, so we use `#[serde(bound = "")]` to disable this. The bounds
|
||||
// on the generated types should be sufficient.
|
||||
let owned_tokens = quote! {
|
||||
#[derive(#serialize_trait, #deserialize_trait)]
|
||||
#[serde(bound = "")]
|
||||
#ignored_lints
|
||||
#owned_decla
|
||||
|
||||
@@ -260,6 +272,7 @@ impl<T: AssociatedType> AssociatingTrait<T> {
|
||||
|
||||
let ref_tokens = quote! {
|
||||
#[derive(#serialize_trait)]
|
||||
#[serde(bound = "")]
|
||||
#ignored_lints
|
||||
#ref_decla
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ use syn::{
|
||||
|
||||
use crate::associated::{
|
||||
generate_from_trait_impl, generate_try_from_trait_impl, AssociatedType, AssociatedTypeKind,
|
||||
ConversionDirection,
|
||||
};
|
||||
use crate::{
|
||||
add_lifetime_bound, add_trait_bound, add_trait_where_clause, parse_const_str, LIFETIME_NAME,
|
||||
UNVERSIONIZE_ERROR_NAME, VERSIONIZE_TRAIT_NAME, VERSION_TRAIT_NAME,
|
||||
parse_const_str, LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UPGRADE_TRAIT_NAME, VERSION_TRAIT_NAME,
|
||||
};
|
||||
|
||||
/// This is the enum that holds all the versions of a specific type. Each variant of the enum is
|
||||
@@ -47,6 +47,10 @@ fn derive_input_to_enum(input: &DeriveInput) -> syn::Result<ItemEnum> {
|
||||
}
|
||||
|
||||
impl AssociatedType for DispatchType {
|
||||
const REF_BOUNDS: &'static [&'static str] = &[VERSION_TRAIT_NAME];
|
||||
|
||||
const OWNED_BOUNDS: &'static [&'static str] = &[VERSION_TRAIT_NAME];
|
||||
|
||||
fn new_ref(orig_type: &DeriveInput) -> syn::Result<Self> {
|
||||
for lt in orig_type.generics.lifetimes() {
|
||||
// check for collision with other lifetimes in `orig_type`
|
||||
@@ -93,7 +97,7 @@ impl AssociatedType for DispatchType {
|
||||
|
||||
Ok(ItemEnum {
|
||||
ident: self.ident(),
|
||||
generics: self.generics()?,
|
||||
generics: self.type_generics()?,
|
||||
attrs: vec![parse_quote! { #[automatically_derived] }],
|
||||
variants: variants?,
|
||||
..self.orig_type.clone()
|
||||
@@ -101,13 +105,41 @@ impl AssociatedType for DispatchType {
|
||||
.into())
|
||||
}
|
||||
|
||||
fn generate_conversion(&self) -> syn::Result<Vec<ItemImpl>> {
|
||||
let generics = self.generics()?;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
fn kind(&self) -> &AssociatedTypeKind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
fn orig_type_generics(&self) -> &Generics {
|
||||
&self.orig_type.generics
|
||||
}
|
||||
|
||||
fn conversion_generics(&self, direction: ConversionDirection) -> syn::Result<Generics> {
|
||||
let mut generics = self.type_generics()?;
|
||||
let preds = &mut generics.make_where_clause().predicates;
|
||||
|
||||
let upgrade_trait: Path = parse_const_str(UPGRADE_TRAIT_NAME);
|
||||
|
||||
if let ConversionDirection::AssociatedToOrig = direction {
|
||||
if let AssociatedTypeKind::Owned = &self.kind {
|
||||
// Add a bound for each version to be upgradable into the next one
|
||||
for src_idx in 0..(self.versions_count() - 1) {
|
||||
let src_ty = self.version_type_at(src_idx)?;
|
||||
let next_ty = self.version_type_at(src_idx + 1)?;
|
||||
preds.push(parse_quote! { #src_ty: #upgrade_trait<#next_ty> })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(generics)
|
||||
}
|
||||
|
||||
fn generate_conversion(&self) -> syn::Result<Vec<ItemImpl>> {
|
||||
match &self.kind {
|
||||
AssociatedTypeKind::Ref(lifetime) => {
|
||||
// Wraps the highest version into the dispatch enum
|
||||
let generics = self.conversion_generics(ConversionDirection::OrigToAssociated)?;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let src_type = self.latest_version_type()?;
|
||||
let src = parse_quote! { &#lifetime #src_type };
|
||||
let dest_ident = self.ident();
|
||||
@@ -126,6 +158,9 @@ impl AssociatedType for DispatchType {
|
||||
}
|
||||
AssociatedTypeKind::Owned => {
|
||||
// Upgrade to the highest version the convert to the main type
|
||||
let generics = self.conversion_generics(ConversionDirection::AssociatedToOrig)?;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let src_ident = self.ident();
|
||||
let src = parse_quote! { #src_ident #ty_generics };
|
||||
let dest_type = self.latest_version_type()?;
|
||||
@@ -144,6 +179,9 @@ impl AssociatedType for DispatchType {
|
||||
)?;
|
||||
|
||||
// Wraps the highest version into the dispatch enum
|
||||
let generics = self.conversion_generics(ConversionDirection::OrigToAssociated)?;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let src_type = self.latest_version_type()?;
|
||||
let src = parse_quote! { #src_type };
|
||||
let dest_ident = self.ident();
|
||||
@@ -182,13 +220,13 @@ impl AssociatedType for DispatchType {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
|
||||
Some(self.latest_version_type())
|
||||
}
|
||||
|
||||
fn inner_types(&self) -> syn::Result<Vec<&Type>> {
|
||||
self.version_types()
|
||||
}
|
||||
|
||||
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
|
||||
Some(self.latest_version_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl DispatchType {
|
||||
@@ -200,19 +238,6 @@ impl DispatchType {
|
||||
)
|
||||
}
|
||||
|
||||
fn generics(&self) -> syn::Result<Generics> {
|
||||
let mut generics = self.orig_type.generics.clone();
|
||||
if let AssociatedTypeKind::Ref(Some(lifetime)) = &self.kind {
|
||||
add_lifetime_bound(&mut generics, lifetime);
|
||||
}
|
||||
|
||||
add_trait_where_clause(&mut generics, self.inner_types()?, &[VERSION_TRAIT_NAME])?;
|
||||
|
||||
add_trait_bound(&mut generics, VERSIONIZE_TRAIT_NAME)?;
|
||||
|
||||
Ok(generics)
|
||||
}
|
||||
|
||||
/// Returns the number of versions in this dispatch enum
|
||||
fn versions_count(&self) -> usize {
|
||||
self.orig_type.variants.len()
|
||||
|
||||
@@ -16,12 +16,10 @@ use proc_macro2::Span;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::parse::Parse;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, Lifetime,
|
||||
LifetimeParam, Path, TraitBound, Type, TypeParam, TypeParamBound,
|
||||
LifetimeParam, Path, TraitBound, Type, TypeParamBound, WhereClause,
|
||||
};
|
||||
use versionize_attribute::VersionizeAttribute;
|
||||
|
||||
/// Adds the full path of the current crate name to avoid name clashes in generated code.
|
||||
macro_rules! crate_full_path {
|
||||
@@ -39,15 +37,16 @@ pub(crate) const VERSIONIZE_SLICE_TRAIT_NAME: &str = crate_full_path!("Versioniz
|
||||
pub(crate) const VERSIONIZE_VEC_TRAIT_NAME: &str = crate_full_path!("VersionizeVec");
|
||||
pub(crate) const UNVERSIONIZE_TRAIT_NAME: &str = crate_full_path!("Unversionize");
|
||||
pub(crate) const UNVERSIONIZE_VEC_TRAIT_NAME: &str = crate_full_path!("UnversionizeVec");
|
||||
pub(crate) const UPGRADE_TRAIT_NAME: &str = crate_full_path!("Upgrade");
|
||||
pub(crate) const UNVERSIONIZE_ERROR_NAME: &str = crate_full_path!("UnversionizeError");
|
||||
|
||||
pub(crate) const SERIALIZE_TRAIT_NAME: &str = "::serde::Serialize";
|
||||
pub(crate) const DESERIALIZE_TRAIT_NAME: &str = "::serde::Deserialize";
|
||||
pub(crate) const DESERIALIZE_OWNED_TRAIT_NAME: &str = "::serde::de::DeserializeOwned";
|
||||
|
||||
use associated::AssociatingTrait;
|
||||
|
||||
use crate::version_type::VersionType;
|
||||
use crate::versionize_attribute::VersionizeAttribute;
|
||||
|
||||
/// unwrap a `syn::Result` by extracting the Ok value or returning from the outer function with
|
||||
/// a compile error
|
||||
@@ -74,8 +73,6 @@ fn impl_version_trait(input: &DeriveInput) -> proc_macro2::TokenStream {
|
||||
let version_trait = syn_unwrap!(AssociatingTrait::<VersionType>::new(
|
||||
input,
|
||||
VERSION_TRAIT_NAME,
|
||||
&[SERIALIZE_TRAIT_NAME, DESERIALIZE_OWNED_TRAIT_NAME],
|
||||
&[VERSIONIZE_TRAIT_NAME, UNVERSIONIZE_TRAIT_NAME]
|
||||
));
|
||||
|
||||
let version_types = syn_unwrap!(version_trait.generate_types_declarations());
|
||||
@@ -102,16 +99,6 @@ pub fn derive_versions_dispatch(input: TokenStream) -> TokenStream {
|
||||
let dispatch_trait = syn_unwrap!(AssociatingTrait::<DispatchType>::new(
|
||||
&input,
|
||||
DISPATCH_TRAIT_NAME,
|
||||
&[
|
||||
VERSIONIZE_TRAIT_NAME,
|
||||
VERSIONIZE_VEC_TRAIT_NAME,
|
||||
VERSIONIZE_SLICE_TRAIT_NAME,
|
||||
UNVERSIONIZE_TRAIT_NAME,
|
||||
UNVERSIONIZE_VEC_TRAIT_NAME,
|
||||
SERIALIZE_TRAIT_NAME,
|
||||
DESERIALIZE_OWNED_TRAIT_NAME
|
||||
],
|
||||
&[]
|
||||
));
|
||||
|
||||
let dispatch_types = syn_unwrap!(dispatch_trait.generate_types_declarations());
|
||||
@@ -137,9 +124,13 @@ pub fn derive_versions_dispatch(input: TokenStream) -> TokenStream {
|
||||
pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let attributes = syn_unwrap!(VersionizeAttribute::parse_from_attributes_list(
|
||||
&input.attrs
|
||||
));
|
||||
let attributes = syn_unwrap!(
|
||||
VersionizeAttribute::parse_from_attributes_list(&input.attrs).and_then(|attr_opt| attr_opt
|
||||
.ok_or_else(|| syn::Error::new(
|
||||
Span::call_site(),
|
||||
"Missing `versionize` attribute for `Versionize`",
|
||||
)))
|
||||
);
|
||||
|
||||
// If we apply a type conversion before the call to versionize, the type that implements
|
||||
// the `Version` trait is the target type and not Self
|
||||
@@ -197,48 +188,9 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
let versionize_slice_trait: Path = parse_const_str(VERSIONIZE_SLICE_TRAIT_NAME);
|
||||
let unversionize_vec_trait: Path = parse_const_str(UNVERSIONIZE_VEC_TRAIT_NAME);
|
||||
|
||||
let mut versionize_generics = trait_generics.clone();
|
||||
for bound in attributes.versionize_bounds() {
|
||||
syn_unwrap!(add_type_param_bound(&mut versionize_generics, bound));
|
||||
}
|
||||
|
||||
// Add generic bounds specified by the user with the `bound` attribute
|
||||
let mut unversionize_generics = trait_generics.clone();
|
||||
for bound in attributes.unversionize_bounds() {
|
||||
syn_unwrap!(add_type_param_bound(&mut unversionize_generics, bound));
|
||||
}
|
||||
|
||||
// Add Generics for the `VersionizeVec` and `UnversionizeVec` traits
|
||||
let mut versionize_slice_generics = versionize_generics.clone();
|
||||
syn_unwrap!(add_trait_bound(
|
||||
&mut versionize_slice_generics,
|
||||
VERSIONIZE_TRAIT_NAME
|
||||
));
|
||||
|
||||
let mut versionize_vec_generics = versionize_generics.clone();
|
||||
syn_unwrap!(add_trait_bound(
|
||||
&mut versionize_vec_generics,
|
||||
VERSIONIZE_OWNED_TRAIT_NAME
|
||||
));
|
||||
let mut unversionize_vec_generics = unversionize_generics.clone();
|
||||
syn_unwrap!(add_trait_bound(
|
||||
&mut unversionize_vec_generics,
|
||||
UNVERSIONIZE_TRAIT_NAME
|
||||
));
|
||||
|
||||
// split generics so they can be used inside the generated code
|
||||
let (_, _, ref_where_clause) = ref_generics.split_for_impl();
|
||||
let (versionize_impl_generics, _, versionize_where_clause) =
|
||||
versionize_generics.split_for_impl();
|
||||
let (unversionize_impl_generics, _, unversionize_where_clause) =
|
||||
unversionize_generics.split_for_impl();
|
||||
|
||||
let (versionize_slice_impl_generics, _, versionize_slice_where_clause) =
|
||||
versionize_slice_generics.split_for_impl();
|
||||
let (versionize_vec_impl_generics, _, versionize_vec_where_clause) =
|
||||
versionize_vec_generics.split_for_impl();
|
||||
let (unversionize_vec_impl_generics, _, unversionize_vec_where_clause) =
|
||||
unversionize_vec_generics.split_for_impl();
|
||||
let (trait_impl_generics, _, trait_where_clause) = trait_generics.split_for_impl();
|
||||
|
||||
// If we want to apply a conversion before the call to versionize we need to use the "owned"
|
||||
// alternative of the dispatch enum to be able to store the conversion result.
|
||||
@@ -257,8 +209,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
#version_trait_impl
|
||||
|
||||
#[automatically_derived]
|
||||
impl #versionize_impl_generics #versionize_trait for #input_ident #ty_generics
|
||||
#versionize_where_clause
|
||||
impl #trait_impl_generics #versionize_trait for #input_ident #ty_generics
|
||||
#trait_where_clause
|
||||
{
|
||||
type Versioned<#lifetime> =
|
||||
<#dispatch_enum_path #dispatch_generics as
|
||||
@@ -270,8 +222,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #versionize_impl_generics #versionize_owned_trait for #input_ident #ty_generics
|
||||
#versionize_where_clause
|
||||
impl #trait_impl_generics #versionize_owned_trait for #input_ident #ty_generics
|
||||
#trait_where_clause
|
||||
{
|
||||
type VersionedOwned =
|
||||
<#dispatch_enum_path #dispatch_generics as
|
||||
@@ -283,8 +235,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #unversionize_impl_generics #unversionize_trait for #input_ident #ty_generics
|
||||
#unversionize_where_clause
|
||||
impl #trait_impl_generics #unversionize_trait for #input_ident #ty_generics
|
||||
#trait_where_clause
|
||||
{
|
||||
fn unversionize(#unversionize_arg_name: Self::VersionedOwned) -> Result<Self, #unversionize_error> {
|
||||
#unversionize_body
|
||||
@@ -292,8 +244,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #versionize_slice_impl_generics #versionize_slice_trait for #input_ident #ty_generics
|
||||
#versionize_slice_where_clause
|
||||
impl #trait_impl_generics #versionize_slice_trait for #input_ident #ty_generics
|
||||
#trait_where_clause
|
||||
{
|
||||
type VersionedSlice<#lifetime> = Vec<<Self as #versionize_trait>::Versioned<#lifetime>> #ref_where_clause;
|
||||
|
||||
@@ -302,8 +254,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
impl #versionize_vec_impl_generics #versionize_vec_trait for #input_ident #ty_generics
|
||||
#versionize_vec_where_clause
|
||||
impl #trait_impl_generics #versionize_vec_trait for #input_ident #ty_generics
|
||||
#trait_where_clause
|
||||
{
|
||||
|
||||
type VersionedVec = Vec<<Self as #versionize_owned_trait>::VersionedOwned> #owned_where_clause;
|
||||
@@ -314,8 +266,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #unversionize_vec_impl_generics #unversionize_vec_trait for #input_ident #ty_generics
|
||||
#unversionize_vec_where_clause {
|
||||
impl #trait_impl_generics #unversionize_vec_trait for #input_ident #ty_generics
|
||||
#trait_where_clause {
|
||||
fn unversionize_vec(versioned: Self::VersionedVec) -> Result<Vec<Self>, #unversionize_error> {
|
||||
versioned
|
||||
.into_iter()
|
||||
@@ -425,35 +377,6 @@ fn parse_trait_bound(trait_name: &str) -> syn::Result<TraitBound> {
|
||||
Ok(parse_quote!(#trait_path))
|
||||
}
|
||||
|
||||
/// Adds a trait bound for `trait_name` on all the generic types in `generics`
|
||||
fn add_trait_bound(generics: &mut Generics, trait_name: &str) -> syn::Result<()> {
|
||||
let trait_bound: TraitBound = parse_trait_bound(trait_name)?;
|
||||
for param in generics.type_params_mut() {
|
||||
param
|
||||
.bounds
|
||||
.push(TypeParamBound::Trait(trait_bound.clone()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_type_param_bound(generics: &mut Generics, type_param_bound: &TypeParam) -> syn::Result<()> {
|
||||
for param in generics.type_params_mut() {
|
||||
if param.ident == type_param_bound.ident {
|
||||
param.bounds.extend(type_param_bound.bounds.clone());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(syn::Error::new(
|
||||
type_param_bound.span(),
|
||||
format!(
|
||||
"Bound type {} not found in target type generics",
|
||||
type_param_bound.ident
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
/// Adds a "where clause" bound for all the input types with all the input traits
|
||||
fn add_trait_where_clause<'a, S: AsRef<str>, I: IntoIterator<Item = &'a Type>>(
|
||||
generics: &mut Generics,
|
||||
@@ -475,6 +398,18 @@ fn add_trait_where_clause<'a, S: AsRef<str>, I: IntoIterator<Item = &'a Type>>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Extends a where clause with predicates from another one, filtering duplicates
|
||||
fn extend_where_clause(base_clause: &mut WhereClause, extension_clause: &WhereClause) {
|
||||
for extend_predicate in &extension_clause.predicates {
|
||||
if base_clause.predicates.iter().all(|base_predicate| {
|
||||
base_predicate.to_token_stream().to_string()
|
||||
!= extend_predicate.to_token_stream().to_string()
|
||||
}) {
|
||||
base_clause.predicates.push(extend_predicate.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Result [`syn::punctuated::Punctuated`] from an iterator of Results
|
||||
fn punctuated_from_iter_result<T, P: Default, I: IntoIterator<Item = syn::Result<T>>>(
|
||||
iter: I,
|
||||
|
||||
@@ -16,9 +16,9 @@ use crate::associated::{
|
||||
ConversionDirection,
|
||||
};
|
||||
use crate::{
|
||||
add_lifetime_bound, add_trait_where_clause, parse_const_str, parse_trait_bound,
|
||||
punctuated_from_iter_result, LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UNVERSIONIZE_TRAIT_NAME,
|
||||
VERSIONIZE_OWNED_TRAIT_NAME, VERSIONIZE_TRAIT_NAME,
|
||||
add_trait_where_clause, parse_const_str, parse_trait_bound, punctuated_from_iter_result,
|
||||
LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UNVERSIONIZE_TRAIT_NAME, VERSIONIZE_OWNED_TRAIT_NAME,
|
||||
VERSIONIZE_TRAIT_NAME,
|
||||
};
|
||||
|
||||
/// The types generated for a specific version of a given exposed type. These types are identical to
|
||||
@@ -30,6 +30,9 @@ pub(crate) struct VersionType {
|
||||
}
|
||||
|
||||
impl AssociatedType for VersionType {
|
||||
const REF_BOUNDS: &'static [&'static str] = &[VERSIONIZE_TRAIT_NAME];
|
||||
const OWNED_BOUNDS: &'static [&'static str] = &[VERSIONIZE_OWNED_TRAIT_NAME];
|
||||
|
||||
fn new_ref(orig_type: &DeriveInput) -> syn::Result<VersionType> {
|
||||
let lifetime = if is_unit(orig_type) {
|
||||
None
|
||||
@@ -173,40 +176,23 @@ impl AssociatedType for VersionType {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn inner_types(&self) -> syn::Result<Vec<&Type>> {
|
||||
self.orig_type_fields()
|
||||
.iter()
|
||||
.map(|field| Ok(&field.ty))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl VersionType {
|
||||
/// Returns the fields of the original declaration.
|
||||
fn orig_type_fields(&self) -> Punctuated<&Field, Comma> {
|
||||
derive_type_fields(&self.orig_type)
|
||||
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn type_generics(&self) -> syn::Result<Generics> {
|
||||
let mut generics = self.orig_type.generics.clone();
|
||||
if let AssociatedTypeKind::Ref(opt_lifetime) = &self.kind {
|
||||
if let Some(lifetime) = opt_lifetime {
|
||||
add_lifetime_bound(&mut generics, lifetime);
|
||||
}
|
||||
add_trait_where_clause(&mut generics, self.inner_types()?, &[VERSIONIZE_TRAIT_NAME])?;
|
||||
} else {
|
||||
add_trait_where_clause(
|
||||
&mut generics,
|
||||
self.inner_types()?,
|
||||
&[VERSIONIZE_OWNED_TRAIT_NAME],
|
||||
)?;
|
||||
}
|
||||
fn kind(&self) -> &AssociatedTypeKind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
Ok(generics)
|
||||
fn orig_type_generics(&self) -> &Generics {
|
||||
&self.orig_type.generics
|
||||
}
|
||||
|
||||
fn conversion_generics(&self, direction: ConversionDirection) -> syn::Result<Generics> {
|
||||
@@ -224,6 +210,13 @@ impl VersionType {
|
||||
|
||||
Ok(generics)
|
||||
}
|
||||
}
|
||||
|
||||
impl VersionType {
|
||||
/// Returns the fields of the original declaration.
|
||||
fn orig_type_fields(&self) -> Punctuated<&Field, Comma> {
|
||||
derive_type_fields(&self.orig_type)
|
||||
}
|
||||
|
||||
/// Generates the declaration for the Version equivalent of the input struct
|
||||
fn generate_struct(&self, stru: &DataStruct) -> syn::Result<ItemStruct> {
|
||||
|
||||
@@ -2,14 +2,11 @@ use proc_macro2::Span;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{
|
||||
Attribute, Expr, ExprLit, Ident, Lit, Meta, MetaNameValue, Path, Token, TraitBound, Type,
|
||||
TypeParam,
|
||||
};
|
||||
use syn::{Attribute, Expr, Ident, Lit, Meta, Path, Token, TraitBound, Type};
|
||||
|
||||
use crate::{parse_const_str, UNVERSIONIZE_ERROR_NAME, VERSIONIZE_OWNED_TRAIT_NAME};
|
||||
|
||||
/// Name of the attribute used to give arguments to our macros
|
||||
/// Name of the attribute used to give arguments to the `Versionize` macro
|
||||
const VERSIONIZE_ATTR_NAME: &str = "versionize";
|
||||
|
||||
pub(crate) struct VersionizeAttribute {
|
||||
@@ -17,8 +14,6 @@ pub(crate) struct VersionizeAttribute {
|
||||
from: Option<Path>,
|
||||
try_from: Option<Path>,
|
||||
into: Option<Path>,
|
||||
versionize_bounds: Vec<TypeParam>,
|
||||
unversionize_bounds: Vec<TypeParam>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -27,8 +22,6 @@ struct VersionizeAttributeBuilder {
|
||||
from: Option<Path>,
|
||||
try_from: Option<Path>,
|
||||
into: Option<Path>,
|
||||
versionize_bounds: Vec<TypeParam>,
|
||||
unversionize_bounds: Vec<TypeParam>,
|
||||
}
|
||||
|
||||
impl VersionizeAttributeBuilder {
|
||||
@@ -42,8 +35,6 @@ impl VersionizeAttributeBuilder {
|
||||
from: self.from,
|
||||
try_from: self.try_from,
|
||||
into: self.into,
|
||||
versionize_bounds: self.versionize_bounds,
|
||||
unversionize_bounds: self.unversionize_bounds,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -53,18 +44,17 @@ impl VersionizeAttribute {
|
||||
/// `DispatchType` is the name of the type holding the dispatch enum.
|
||||
/// Returns an error if no `versionize` attribute has been found, if multiple attributes are
|
||||
/// present on the same struct or if the attribute is malformed.
|
||||
pub(crate) fn parse_from_attributes_list(attributes: &[Attribute]) -> syn::Result<Self> {
|
||||
pub(crate) fn parse_from_attributes_list(
|
||||
attributes: &[Attribute],
|
||||
) -> syn::Result<Option<Self>> {
|
||||
let version_attributes: Vec<&Attribute> = attributes
|
||||
.iter()
|
||||
.filter(|attr| attr.path().is_ident(VERSIONIZE_ATTR_NAME))
|
||||
.collect();
|
||||
|
||||
match version_attributes.as_slice() {
|
||||
[] => Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
"Missing `versionize` attribute for `Versionize`",
|
||||
)),
|
||||
[attr] => Self::parse_from_attribute(attr),
|
||||
[] => Ok(None),
|
||||
[attr] => Self::parse_from_attribute(attr).map(Some),
|
||||
[_, attr2, ..] => Err(syn::Error::new(
|
||||
attr2.span(),
|
||||
"Multiple `versionize` attributes found",
|
||||
@@ -91,31 +81,6 @@ impl VersionizeAttribute {
|
||||
attribute_builder.dispatch_enum = Some(dispatch_enum.clone());
|
||||
}
|
||||
}
|
||||
Meta::List(list) => {
|
||||
// parse versionize(bound(unversionize = "Type: Bound"))
|
||||
if list.path.is_ident("bound") {
|
||||
let name_value: MetaNameValue = list.parse_args()?;
|
||||
let bound_attr: TypeParam = match &name_value.value {
|
||||
Expr::Lit(ExprLit {
|
||||
attrs: _,
|
||||
lit: Lit::Str(s),
|
||||
}) => syn::parse_str(&s.value())?,
|
||||
_ => {
|
||||
return Err(Self::default_error(meta.span()));
|
||||
}
|
||||
};
|
||||
|
||||
if name_value.path.is_ident("versionize") {
|
||||
attribute_builder.versionize_bounds.push(bound_attr);
|
||||
} else if name_value.path.is_ident("unversionize") {
|
||||
attribute_builder.unversionize_bounds.push(bound_attr);
|
||||
} else {
|
||||
return Err(Self::default_error(meta.span()));
|
||||
}
|
||||
} else {
|
||||
return Err(Self::default_error(meta.span()));
|
||||
}
|
||||
}
|
||||
Meta::NameValue(name_value) => {
|
||||
// parse versionize(from = "TypeFrom")
|
||||
if name_value.path.is_ident("from") {
|
||||
@@ -142,22 +107,11 @@ impl VersionizeAttribute {
|
||||
Some(parse_path_ignore_quotes(&name_value.value)?);
|
||||
}
|
||||
// parse versionize(bound = "Type: Bound")
|
||||
} else if name_value.path.is_ident("bound") {
|
||||
let bound_attr: TypeParam = match &name_value.value {
|
||||
Expr::Lit(ExprLit {
|
||||
attrs: _,
|
||||
lit: Lit::Str(s),
|
||||
}) => syn::parse_str(&s.value())?,
|
||||
_ => {
|
||||
return Err(Self::default_error(meta.span()));
|
||||
}
|
||||
};
|
||||
attribute_builder.versionize_bounds.push(bound_attr.clone());
|
||||
attribute_builder.unversionize_bounds.push(bound_attr);
|
||||
} else {
|
||||
return Err(Self::default_error(meta.span()));
|
||||
}
|
||||
}
|
||||
_ => return Err(Self::default_error(meta.span())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,14 +167,6 @@ impl VersionizeAttribute {
|
||||
quote! { #arg_name.try_into() }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn versionize_bounds(&self) -> &[TypeParam] {
|
||||
&self.versionize_bounds
|
||||
}
|
||||
|
||||
pub(crate) fn unversionize_bounds(&self) -> &[TypeParam] {
|
||||
&self.unversionize_bounds
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_path_ignore_quotes(value: &Expr) -> syn::Result<Path> {
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
/// This trait should be implemented for each version of the original type that is not the current
|
||||
/// one. The upgrade method is called in chains until we get to the last version of the type.
|
||||
pub trait Upgrade<T> {
|
||||
type Error: std::error::Error;
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
fn upgrade(self) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user