Skip to content

Commit

Permalink
Merge pull request #5 from spastorino/blanket-impl-erased-trait
Browse files Browse the repository at this point in the history
Blanket impl erased trait
  • Loading branch information
tmandry authored Aug 2, 2024
2 parents 3cb27b5 + 2c65586 commit 722ef78
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 164 deletions.
10 changes: 10 additions & 0 deletions dynosaur/tests/pass/basic-with-self.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,15 @@ trait ErasedMyTrait {
'life0: 'dynosaur,
Self: 'dynosaur;
}
impl<DYNOSAUR: MyTrait> ErasedMyTrait for DYNOSAUR {
type Item = <Self as MyTrait>::Item;
fn foo<'life0, 'dynosaur>(&'life0 self)
->
::core::pin::Pin<Box<dyn ::core::future::Future<Output =
Self::Item> + 'dynosaur>> where 'life0: 'dynosaur,
Self: 'dynosaur {
Box::pin(<Self as MyTrait>::foo(self))
}
}

fn main() {}
8 changes: 8 additions & 0 deletions dynosaur/tests/pass/basic.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@ trait ErasedMyTrait {
'life0: 'dynosaur,
Self: 'dynosaur;
}
impl<DYNOSAUR: MyTrait> ErasedMyTrait for DYNOSAUR {
fn foo<'life0, 'dynosaur>(&'life0 self)
->
::core::pin::Pin<Box<dyn ::core::future::Future<Output = i32> +
'dynosaur>> where 'life0: 'dynosaur, Self: 'dynosaur {
Box::pin(<Self as MyTrait>::foo(self))
}
}

fn main() {}
10 changes: 10 additions & 0 deletions dynosaur/tests/pass/handle-generics-on-trait-def.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use dynosaur::dynosaur;

#[dynosaur(DynMyTrait)]
trait MyTrait<T> {
const N: i32;
type Item;
async fn foo(&self, x: &T) -> i32;
}

fn main() {}
38 changes: 38 additions & 0 deletions dynosaur/tests/pass/handle-generics-on-trait-def.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use dynosaur::dynosaur;

trait MyTrait<T> {
const N: i32;
type Item;
async fn foo(&self, x: &T)
-> i32;
}
trait ErasedMyTrait<T> {
const N: i32;
type Item;
fn foo<'life0, 'life1, 'dynosaur>(&'life0 self, x: &'life1 T)
->
::core::pin::Pin<Box<dyn ::core::future::Future<Output = i32> +
'dynosaur>>
where
'life0: 'dynosaur,
'life1: 'dynosaur,
Self: 'dynosaur;
}
impl<T, DYNOSAUR: MyTrait<T>> ErasedMyTrait<T> for DYNOSAUR {
const N: i32 = <Self as MyTrait<T>>::N;
type Item = <Self as MyTrait<T>>::Item;
fn foo<'life0, 'life1, 'dynosaur>(&'life0 self, x: &'life1 T)
->
::core::pin::Pin<Box<dyn ::core::future::Future<Output = i32> +
'dynosaur>> where 'life0: 'dynosaur, 'life1: 'dynosaur,
Self: 'dynosaur {
Box::pin(<Self as MyTrait<T>>::foo(self, x))
}
}

fn main() {}
10 changes: 10 additions & 0 deletions dynosaur/tests/pass/handle-lifetimes.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,15 @@ trait ErasedMyTrait {
'life1: 'dynosaur,
Self: 'dynosaur;
}
impl<DYNOSAUR: MyTrait> ErasedMyTrait for DYNOSAUR {
type Item = <Self as MyTrait>::Item;
fn foo<'life0, 'life1, 'dynosaur, T>(&'life0 self, x: &'life1 T)
->
::core::pin::Pin<Box<dyn ::core::future::Future<Output = i32> +
'dynosaur>> where T: 'dynosaur, 'life0: 'dynosaur,
'life1: 'dynosaur, Self: 'dynosaur {
Box::pin(<Self as MyTrait>::foo(self, x))
}
}

fn main() {}
191 changes: 191 additions & 0 deletions dynosaur_derive/src/expand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use crate::lifetime::{AddLifetimeToImplTrait, CollectLifetimes};
use crate::receiver::{has_self_in_sig, mut_pat};
use proc_macro2::Span;
use quote::{format_ident, quote};
use std::mem;
use syn::punctuated::Punctuated;
use syn::visit_mut::VisitMut;
use syn::{
parse_quote, parse_quote_spanned, FnArg, GenericParam, Generics, Ident, ItemTrait, Lifetime,
LifetimeParam, Pat, ReturnType, Signature, Token, TraitItem, TraitItemFn, Type, WhereClause,
};

/// Expands the signature of each function on the trait, converting async fn into fn with return
/// type Future and makes lifetimes explicit.
///
/// Converts:
///
/// ```rust
/// trait MyTrait {
/// async fn foo(&self) -> i32;
/// }
/// ```
///
/// into:
///
/// ```rust
/// trait ErasedMyTrait {
/// fn foo<'life0, 'dynosaur>(&'life0 self)
/// ->
/// ::core::pin::Pin<Box<dyn ::core::future::Future<Output = i32> +
/// 'dynosaur>>
/// where
/// 'life0: 'dynosaur,
/// Self: 'dynosaur;
/// }
/// ```
pub fn expand_trait(item_trait: &ItemTrait) -> ItemTrait {
ItemTrait {
items: item_trait
.items
.iter()
.map(|item| {
if let TraitItem::Fn(
trait_item_fn @ TraitItemFn {
sig:
Signature {
asyncness: Some(..),
..
},
..
},
) = item
{
let mut sig = trait_item_fn.sig.clone();

sig.fn_token.span = sig.asyncness.take().unwrap().span;

let (ret_arrow, ret) = match &sig.output {
ReturnType::Default => (Token![->](Span::call_site()), quote!(())),
ReturnType::Type(arrow, ret) => (*arrow, quote!(#ret)),
};

let mut lifetimes = CollectLifetimes::new();
for arg in &mut sig.inputs {
match arg {
FnArg::Receiver(arg) => lifetimes.visit_receiver_mut(arg),
FnArg::Typed(arg) => lifetimes.visit_type_mut(&mut arg.ty),
}
}

for param in &mut sig.generics.params {
match param {
GenericParam::Type(param) => {
let param_name = &param.ident;
let span = match param.colon_token.take() {
Some(colon_token) => colon_token.span,
None => param_name.span(),
};
let bounds = mem::replace(&mut param.bounds, Punctuated::new());
where_clause_or_default(&mut sig.generics.where_clause)
.predicates
.push(parse_quote_spanned!(span=> #param_name: 'dynosaur + #bounds));
}
GenericParam::Lifetime(param) => {
let param_name = &param.lifetime;
let span = match param.colon_token.take() {
Some(colon_token) => colon_token.span,
None => param_name.span(),
};
let bounds = mem::replace(&mut param.bounds, Punctuated::new());
where_clause_or_default(&mut sig.generics.where_clause)
.predicates
.push(parse_quote_spanned!(span=> #param: 'dynosaur + #bounds));
}
GenericParam::Const(_) => {}
}
}

for param in used_lifetimes(&item_trait.generics, &lifetimes.explicit) {
let param = &param.lifetime;
let span = param.span();
where_clause_or_default(&mut sig.generics.where_clause)
.predicates
.push(parse_quote_spanned!(span=> #param: 'dynosaur));
}

if sig.generics.lt_token.is_none() {
sig.generics.lt_token = Some(Token![<](sig.ident.span()));
}
if sig.generics.gt_token.is_none() {
sig.generics.gt_token = Some(Token![>](sig.paren_token.span.join()));
}

for elided in lifetimes.elided {
sig.generics.params.push(parse_quote!(#elided));
where_clause_or_default(&mut sig.generics.where_clause)
.predicates
.push(parse_quote_spanned!(elided.span()=> #elided: 'dynosaur));
}

sig.generics.params.push(parse_quote!('dynosaur));

if has_self_in_sig(&mut sig) {
where_clause_or_default(&mut sig.generics.where_clause)
.predicates
.push(parse_quote! {
Self: 'dynosaur
});
}

for (i, arg) in sig.inputs.iter_mut().enumerate() {
if let FnArg::Typed(arg) = arg {
if match *arg.ty {
Type::Reference(_) => false,
_ => true,
} {
match &*arg.pat {
Pat::Ident(_) => {}
_ => {
let positional = positional_arg(i, &arg.pat);
let m = mut_pat(&mut arg.pat);
arg.pat = parse_quote!(#m #positional);
}
}
}
AddLifetimeToImplTrait.visit_type_mut(&mut arg.ty);
}
}

sig.output = parse_quote! {
#ret_arrow ::core::pin::Pin<Box<
dyn ::core::future::Future<Output = #ret> + 'dynosaur>>
};
TraitItem::Fn(TraitItemFn {
sig,
..trait_item_fn.clone()
})
} else {
item.clone()
}
})
.collect(),
..item_trait.clone()
}
}

fn used_lifetimes<'a>(
generics: &'a Generics,
used: &'a [Lifetime],
) -> impl Iterator<Item = &'a LifetimeParam> {
generics.params.iter().filter_map(move |param| {
if let GenericParam::Lifetime(param) = param {
if used.contains(&param.lifetime) {
return Some(param);
}
}
None
})
}

fn where_clause_or_default(clause: &mut Option<WhereClause>) -> &mut WhereClause {
clause.get_or_insert_with(|| WhereClause {
where_token: Default::default(),
predicates: Punctuated::new(),
})
}

fn positional_arg(i: usize, pat: &Pat) -> Ident {
let span = syn::spanned::Spanned::span(pat).resolved_at(Span::mixed_site());
format_ident!("__arg{}", i, span = span)
}
Loading

0 comments on commit 722ef78

Please sign in to comment.