-
Notifications
You must be signed in to change notification settings - Fork 14
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
Explicitly pass type parameters when delegating the method #19
Comments
Hey, is this issue still available? |
@saresend To my knowledge, no one is working on this yet. So yep, it's available! So you want to work on this? :) You can of course always ask questions in this issue, but you can also contact me via mail (address on my GitHub profile), via Discord (on the Rust server https://discord.gg/2cr8M4D) or via Telegram (username is the same as my GitHub username). I think that often, real time messaging or at least non-public messaging is easier and more effective, if you want to get quick help. |
Yep, I'd be interested in taking this issue! Thanks, I'll be in touch! |
Hi @saresend, I just wanted to check your status. Are you still working on this? But no worries: I don't want to rush you! If you have problems trying to tackle this issue, just let me know! |
Hey! Yeah, I'll likely tackle it this weekend, I'm currently in university and so my availability is quite sporadic :P Expect to hear from me sometime tomorrow! |
Fantastic! :) |
Hey, I realize that I really lack understanding of procedural macros and how they function in Rust. Do you know of any resources to get up to speed? Thanks! |
👋 Hi @saresend! The new procedural macro work doesn't seem to have a lot of documentation just yet, I've set up a gitter channel we can use to help you get started. |
@saresend No problem, that's why we offer this mentoring :) There is actually a section in the book about proc macros. But let me quickly summarize it for you in context of this crate (more explanations never hurt, right?): There are three different kinds of proc macros: custom derives ( #[proc_macro_attribute]
pub fn auto_impl(args: TokenStream, input: TokenStream) -> TokenStream {
...
} So what's a trait Foo<T> {
fn foo();
} This results in the tokens:
The type Let's take the example code above and print the actual #[proc_macro_attribute]
pub fn auto_impl(args: TokenStream, input: TokenStream) -> TokenStream {
for token_tree in input {
println!("{:?}", token_tree);
}
TokenStream::new() // empty result to make it compile
} And add an use auto_impl::auto_impl;
#[auto_impl()]
trait Foo<T> {
fn foo();
}
fn main() {} And finally say
(Side note: yes, we just got our own output while executing So that should give you a rough idea what a token stream is. Lastly, you probably noticed that our function takes two After the compiler called our function, it takes the token stream we returned and replaces the original trait definition with that token stream. So if we always return Usually you want to interpret the token stream as a Rust thing (e.g. a trait). That's what the crate This results in a With that information, we can start generating our output. We don't want to modify the trait definition, but only add tokens. For each of our so called "proxy types", we need to generate one Let's see an example again. The example from above but with use auto_impl::auto_impl;
#[auto_impl(&, Box)]
trait Foo<T> {
fn foo();
}
fn main() {} We can now use the great #![feature(prelude_import)]
#![no_std]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std as std;
use auto_impl::auto_impl;
trait Foo<T> {
fn foo();
}
impl<'a, T, U: 'a + Foo<T>> Foo<T> for &'a U {
fn foo() {
U::foo()
}
}
impl<T, U: Foo<T>> Foo<T> for ::std::boxed::Box<U> {
fn foo() {
U::foo()
}
}
fn main() {} (You can ignore the stuff at the very top, that's not from us.) The generation of these impl blocks is what is the complicated part. All of it starts [here, in Lines 19 to 24 in d7ba83a
One additional thing: you will see the A bit confusing: So I hope that serves as an introduction. For more questions, just ask in the gitter channel :) |
Hey, just wanted to ask a couple of things involving how testing is being built. Specifically, since it depends on using the unstable build-plan to establish the binary target, it seems to be failing to execute the tests. I was wondering if there was a way to rely on something more stable for determining the binary target, specifically I was looking at using |
@saresend I'm not sure if you've seen that but @KodrAus noticed this problem and created rust-lang/cargo#6082. On recent nightlies, our tests fail because of that, yes. But it's already fixed upstream -- now we only have to wait until it lands in nightly. But your idea is very interesting! Ideally, something like a compile-fail tester should be implemented in an external crate and not for every project that wants such a test. Maybe I'll look into using the Some background on the current build system can be found here: #17 Lastly, if you want to test your changes now, you can use an older nightly compiler. For example
|
Oh, cool! Thanks for the heads up! |
Fixed in #35 :) |
Is expanded into:
And results in two compiler errors. So we need to change the method body into an explicit call all of the time. In this case:
V::foo::<T>()
, andV::bar::<T>(*self)
I can mentor anyone interested in tackling this issue :)
Just ping me (via email, this issue, or in any other way)
Instructions: code generation of methods is done in
gen_method_item
ingen.rs
. The important part for this issue are the last two arms of the last match statement in the function (SelfType::Value
andSelfType::Ref | SelfType::Mut
). These generate incorrect code. You can see the generated code in thequote! {}
macro.The most difficult part is probably to generate the list of generic parameters to call the other method. In the example above it's simply
<T>
, but it could be more complicated. Ingen_method_item()
, we have the variablesig
which is asyn::MethodSig
. We are interested insig.decl.generics
which stores the generics of the method we are generating. Sadly, we can't just use that: e.g.fn foo<T: Clone, U>()
would have<T: Clone, U>
as generics and we can't callfoo::<T: Clone, U>()
, but need to callfoo::<T, U>()
. So we might have to remove all bounds. But with some luck, we can usesyn::Generics::split_for_impl
. The second element of the returned tuple should be what we want. But we need to test that!Finally, one or more compile-pass tests should be added which test this exact code.
If anything is unclear, just go ahead and ask!
The text was updated successfully, but these errors were encountered: