-
Notifications
You must be signed in to change notification settings - Fork 7
Fix issues #4 and #5 #6
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
Changes from 31 commits
fb569d3
689b6cc
eccd386
9818f80
4ea39fb
8e20eed
5822901
639f677
52cc65d
ec7f5f4
55ee9e8
e4560e3
9d3f271
713517c
bb8969c
5e10596
c322e7e
0ec94e8
68e8107
9ace5a6
568b426
5c00a61
6c41596
46e9472
5569092
f336c99
170df32
7e51a1c
ec25165
99c5141
07900bd
1c787dd
18f88cc
9b53b0d
a158e38
a9076a4
d8c1cd0
71f715c
6330912
61c1af1
ee0e196
ed37d2a
f8e73fc
cb3eec6
b329e32
b82aa9a
02dc84c
81b845c
234d893
7924217
d563403
0609d80
32a1507
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // Test provided in #4 | ||
| // https://github.com/jmg-duarte/sealed-rs/issues/4 | ||
|
|
||
| use sealed::sealed; | ||
|
|
||
| #[sealed] | ||
| pub trait Set<V> {} | ||
|
|
||
| #[sealed] | ||
| impl<T> Set<Option<T>> for T {} | ||
|
|
||
| #[sealed] | ||
| impl<T> Set<Option<T>> for Option<T> {} | ||
|
|
||
| fn main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| use sealed::sealed; | ||
|
|
||
| mod lets { | ||
| pub mod attempt { | ||
| pub mod some { | ||
| pub mod nesting { | ||
| use sealed::sealed; | ||
| #[sealed] | ||
| pub trait LongerSnakeCaseType {} | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub struct A; | ||
|
|
||
| pub struct B(i32); | ||
|
|
||
| #[sealed] | ||
| impl lets::attempt::some::nesting::LongerSnakeCaseType for A {} | ||
|
|
||
| #[sealed] | ||
| impl lets::attempt::some::nesting::LongerSnakeCaseType for B {} | ||
|
|
||
| fn main() { | ||
| return; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| use sealed::sealed; | ||
|
|
||
| #[sealed] | ||
| pub trait T {} | ||
|
|
||
| pub struct A; | ||
| pub struct B(i32); | ||
|
|
||
| #[sealed] | ||
| impl T for A {} | ||
| #[sealed] | ||
| impl T for B {} | ||
|
|
||
| fn main() { | ||
| return; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Example provided in #4 | ||
| // https://github.com/jmg-duarte/sealed-rs/issues/4 | ||
|
|
||
| use proc_macro2::*; | ||
| use sealed::sealed; | ||
| use syn::spanned::Spanned; | ||
|
|
||
| #[sealed] | ||
| pub trait AsSpan { | ||
| fn as_span(&self) -> Span; | ||
| } | ||
| // expands to: | ||
| // pub trait AsSpan: __seal_for_as_span::Sealed { | ||
| // fn as_span(&self) -> Span; | ||
| // } | ||
| // mod __seal_for_as_span { | ||
| // pub trait Sealed {} | ||
| // } | ||
|
|
||
| #[sealed] | ||
| impl AsSpan for Span { | ||
| fn as_span(&self) -> Self { | ||
| *self | ||
| } | ||
| } | ||
| // expands to: | ||
| // impl AsSpan for Span { // foreign type, cannot place #[sealed] | ||
| // fn as_span(&self) -> Self { *self } | ||
| // } | ||
| // impl __seal_for_as_span::Sealed for Span {} | ||
|
|
||
| #[sealed] | ||
| impl<T: Spanned> AsSpan for &T { | ||
| fn as_span(&self) -> Span { | ||
| self.span() | ||
| } | ||
| } | ||
| // expands to: | ||
| // impl<T: Spanned> AsSpan for &T { | ||
| // fn as_span(&self) -> Span { self.span() } | ||
| // } | ||
| // impl<T: Spanned> __seal_for_as_span::Sealed for &T {} | ||
|
|
||
| fn main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,9 +62,11 @@ | |
| //! impl private::Sealed for A {} | ||
| //! ``` | ||
|
|
||
| use heck::SnakeCase; | ||
| use proc_macro::TokenStream; | ||
| use proc_macro2::TokenStream as TokenStream2; | ||
| use quote::quote; | ||
| use syn::{parse_macro_input, parse_quote}; | ||
| use syn::{ext::IdentExt, parse_macro_input, parse_quote}; | ||
|
|
||
| #[proc_macro_attribute] | ||
| pub fn sealed(_args: TokenStream, input: TokenStream) -> TokenStream { | ||
|
|
@@ -75,10 +77,14 @@ pub fn sealed(_args: TokenStream, input: TokenStream) -> TokenStream { | |
| }) | ||
| } | ||
|
|
||
| fn parse_sealed(item: syn::Item) -> syn::Result<proc_macro2::TokenStream> { | ||
| fn seal_name<D: ::std::fmt::Display>(seal: D) -> syn::Ident { | ||
| ::quote::format_ident!("__seal_for_{}", &seal.to_string().to_snake_case()) | ||
| } | ||
|
|
||
| fn parse_sealed(item: syn::Item) -> syn::Result<TokenStream2> { | ||
| match item { | ||
| syn::Item::Struct(item_struct) => parse_sealed_struct(item_struct), | ||
| syn::Item::Trait(item_trait) => parse_sealed_trait(item_trait), | ||
| syn::Item::Impl(item_impl) => parse_sealed_impl(item_impl), | ||
| syn::Item::Trait(item_trait) => Ok(parse_sealed_trait(item_trait)), | ||
| _ => Err(syn::Error::new( | ||
| proc_macro2::Span::call_site(), | ||
| "expected struct or trait", | ||
|
|
@@ -87,21 +93,42 @@ fn parse_sealed(item: syn::Item) -> syn::Result<proc_macro2::TokenStream> { | |
| } | ||
|
|
||
| // Care for https://gist.github.com/Koxiaet/8c05ebd4e0e9347eb05f265dfb7252e1#procedural-macros-support-renaming-the-crate | ||
| fn parse_sealed_struct(strct: syn::ItemStruct) -> syn::Result<proc_macro2::TokenStream> { | ||
| let ident = &strct.ident; | ||
| Ok(quote!( | ||
| #strct | ||
| impl private::Sealed for #ident {} | ||
| )) | ||
| fn parse_sealed_trait(mut item_trait: syn::ItemTrait) -> TokenStream2 { | ||
| let trait_ident = &item_trait.ident.unraw(); | ||
| let trait_generics = &item_trait.generics; | ||
|
jmg-duarte marked this conversation as resolved.
|
||
| let seal = seal_name(trait_ident); | ||
|
jmg-duarte marked this conversation as resolved.
|
||
| item_trait | ||
| .supertraits | ||
| .push(parse_quote!(#seal::Sealed #trait_generics)); | ||
| quote!( | ||
| pub(crate) mod #seal { | ||
| pub trait Sealed #trait_generics {} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, an important thing is to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed! That being said, // Only keep the introduced params (no bounds), since
// the bounds may break in the `#seal` submodule.
let trait_generics = trait_generics.split_for_impl().1;before the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jmg-duarte @danielhenrymantilla if so, we need to consider implicit #[sealed]
pub MyTrait<T: ?Sized>: MyBounds {}
// As described above will desugar as:
pub MyTrait<T: ?Sized>: MyBounds + private::Sealed<T> {}
mod private {
pub Sealed<T> {}
}
// Which breaks the original trait:
impl private::Sealed<dyn Error> for MyType {} // compiler error
// If we remove bounds, we should allways apply `?Sized` to type params,
// to be transparent asap:
pub MyTrait<T: ?Sized>: MyBounds + private::Sealed<T> {}
mod private {
pub Sealed<T: ?Sized> {}
}There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, true! The
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jmg-duarte @danielhenrymantilla I'd prefer to start with a simpler |
||
| } | ||
| #item_trait | ||
| ) | ||
| } | ||
|
|
||
| // Care for https://gist.github.com/Koxiaet/8c05ebd4e0e9347eb05f265dfb7252e1#procedural-macros-support-renaming-the-crate | ||
| fn parse_sealed_trait(mut trt: syn::ItemTrait) -> syn::Result<proc_macro2::TokenStream> { | ||
| trt.supertraits.push(parse_quote!(private::Sealed)); | ||
| Ok(quote!( | ||
| #trt | ||
| mod private { | ||
| pub trait Sealed {} | ||
| } | ||
| )) | ||
| fn parse_sealed_impl(item_impl: syn::ItemImpl) -> syn::Result<TokenStream2> { | ||
| if let Some(impl_trait) = &item_impl.trait_ { | ||
| let mut sealed_path = impl_trait.1.segments.clone(); | ||
| // since `impl for ...` is not allowed, this path will *always* have at least length 1 | ||
| // thus both `first` and `last` are safe to unwrap | ||
| let syn::PathSegment { ident, arguments } = sealed_path.pop().unwrap().into_value(); | ||
| let seal = seal_name(ident); | ||
| sealed_path.push(parse_quote!(#seal)); | ||
| sealed_path.push(parse_quote!(Sealed)); | ||
|
|
||
| let self_type = &item_impl.self_ty; | ||
| let trait_generics = &item_impl.generics.split_for_impl().1; | ||
|
|
||
| Ok(quote! { | ||
| impl #trait_generics #sealed_path #arguments for #self_type {} | ||
|
jmg-duarte marked this conversation as resolved.
Outdated
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am curious about this, what problem were the Basically the example I have in mind is: use ::core::future::Future;
#[sealed]
trait GeneratedBy<F> {}
#[sealed]
impl<F> GeneratedBy<F> for F::Output
where
F : Future,
{}That is, the
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember there was a reason you pointed out. But maybe I just misunderstood and took them out.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was this comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think a distinction must be made between "necessary bounds to ever Each The situation is a bit subtle, granted but by thinking about this in advance we will be dodging many potential bugs / issues 🙂 |
||
| #item_impl | ||
| }) | ||
| } else { | ||
| Err(syn::Error::new_spanned( | ||
| item_impl, | ||
| "missing implentation trait", | ||
| )) | ||
|
jmg-duarte marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,10 @@ | ||
| error[E0277]: the trait bound `C: Sealed` is not satisfied | ||
| --> $DIR/01.rs:16:6 | ||
| --> $DIR/01-general.rs:20:6 | ||
| | | ||
| 11 | #[sealed] | ||
| | --------- required by this bound in `T` | ||
| 12 | trait T {} | ||
| | - required by a bound in this | ||
| ... | ||
| 16 | impl T for C {} | ||
| 20 | impl T for C {} | ||
| | ^ the trait `Sealed` is not implemented for `C` |
Uh oh!
There was an error while loading. Please reload this page.