Skip to content
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

const generics #116

Closed
wants to merge 22 commits into from
Closed

const generics #116

wants to merge 22 commits into from

Conversation

mriise
Copy link
Contributor

@mriise mriise commented Mar 30, 2021

What this PR does

  • removes generic array
  • implements Default for things that needed it by making an empty array [0u8; SIZE]\
  • closes Use const generics #114

What it doesn't do:

  • modify user api to make use of const generics e.g. Blake3::<SIZE>::digest(&[u8]) (this can't be done without more modification of the proc macro, so instead should probably be another PR)
  • build
  • modify proc macro
  • fix SHA macro_rules!
  • some other things that need to be squashed to even build.

@mriise mriise marked this pull request as draft March 30, 2021 18:16
src/multihash.rs Outdated Show resolved Hide resolved
src/hasher.rs Outdated Show resolved Hide resolved
src/multihash.rs Outdated Show resolved Hide resolved
src/multihash_impl.rs Show resolved Hide resolved
@mriise mriise closed this Mar 31, 2021
@mriise mriise reopened this Mar 31, 2021
@mriise mriise closed this Mar 31, 2021
@mriise mriise reopened this Mar 31, 2021
@mriise
Copy link
Contributor Author

mriise commented Mar 31, 2021

sorry, had this up on my phone in my pocket which caused the closing and opening. Almost posted a chunk of garbled text as a comment too...

@mriise
Copy link
Contributor Author

mriise commented Apr 13, 2021

While stepping through the macro I noticed that to use hash<64> in the macro we need to be able to evaluate if alloc_size > SIZE(of hash), the support syn has for const generic syn::GenericArgument::Const() gives &syn:Expr which from what I can tell cant be evaluated in time to be used for our purposes.

As for alloc_size in the macro, I moved things to syn::LitInt so it can easily be parsed into a usize.

I honestly am not sure where to go from here, It could be something I don't know or it might just be that syn doesn't have support for what we need to do. Honestly this seems to be leaning a bit into const fns that might support what we need, but again i don't know enough about macros to be sure.

@vmx
Copy link
Member

vmx commented Apr 20, 2021

@mriise I had a look at the derive macro part (which is indeed quite complicated, it took me a while to get into again). You can cherry-pick this commit: 19a44ff

It only fixes the derive macro, the build still fails. I indeed just moved things to syn::LintInt. I hope that helps you to make further progress.

@mriise
Copy link
Contributor Author

mriise commented Apr 23, 2021

Thank you so much @vmx, IDK what it was that was confusing me so much.
I managed to fix the sha macros and now have run into problems with digests < max size. Will look into allowing different sized Digests tomorrow morning

@mriise
Copy link
Contributor Author

mriise commented Apr 24, 2021

Fix for above was as simple as using a different generic for the digest, and rust compiler being amazing as it is implies S from the passed in digest automatically so no breaking API for the most part. (does break method signature though)

  fn multihash_from_digest<'a, D, const S: usize>(digest: &'a D) -> Multihash<SIZE>
  where
      D: Digest<S>,
      Self: From<&'a D>;

I removed fn size() and replaced it with an associated const as it just returned SIZE anyways. We could go with changing it to a const fn, but i figured the less unstable features we use the better.

I believe the last mole to whack is serde.
It has a [T; 0] impl for Deserialize & Serialize so we cant just do it for ourselves without a work-around as mentioned in serde-rs/serde#1937, alternatively we could wait for serde-rs/serde#1860 to be merged and released, and even further alternative is to use https://github.com/est31/serde-big-array for its const-generic support in the meantime.

@vmx
Copy link
Member

vmx commented Apr 26, 2021

Thank you so much @vmx, IDK what it was that was confusing me so much.

It is confusing. It took my quite some time to dig into this code again. The final fix looks trivial, but getting there wasn't :)

I believe the last mole to whack is serde.

I can't really tell what's best from reading the threads, which also means I don't have a strong preference. So I suggest trying things out and see what works (best).

@mriise
Copy link
Contributor Author

mriise commented Apr 26, 2021

@vmx simplest option was to use serde-big-array, and maybe when serde merges the PR we can drop it for that.

Everything builds, passes all tests, and benches, so I think it should be good to review. I still have a TODO of renaming S to SIZE, but that's not going to affect code review.

just saw this

After taking a look at the actual code, I change my mind. i think S is better than SIZE.

@mriise mriise marked this pull request as ready for review April 26, 2021 09:46
src/hasher.rs Show resolved Hide resolved
src/hasher_impl.rs Outdated Show resolved Hide resolved
src/multihash.rs Outdated Show resolved Hide resolved
@@ -73,27 +70,37 @@ pub trait MultihashDigest:
/// ```
#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
#[cfg_attr(feature = "serde-codec", serde(bound = "S: Size"))]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think removing this has any Ill effects, but im not 100% sure.

src/multihash.rs Outdated Show resolved Hide resolved
@mriise
Copy link
Contributor Author

mriise commented Apr 27, 2021

did not mean to force push that...

@mriise
Copy link
Contributor Author

mriise commented Apr 27, 2021

build fails because of Warning: can't set `format_code_in_doc_comments = true`, unstable features are only available in nightly channel.

Copy link
Collaborator

@dvc94ch dvc94ch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vmx I think this answers your question

impl StatefulHasher for Sha2_256Truncated20 {
type Size = U20;
type Digest = Sha2Digest<Self::Size>;
impl StatefulHasher<20> for Sha2_256Truncated20 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should still be an associated constant

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's Self::SIZE?

Copy link
Contributor Author

@mriise mriise Jun 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self::SIZE replaced size() since while it could be a const fn it made more sense (in my mind) to make it a public associated constant.

It would make sense to move back to const fn size() in the case of having SIZE be an associated constant without being repetitive.

Copy link
Contributor Author

@mriise mriise Jun 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems using an assosicated const as a paramater is still part of the incomplete const_generics feature in nightly, though with enough feature flags we can make it look something like this

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=cfcf3ec66baa902500cae4dbcefd7dff

Copy link
Member

@vmx vmx Jun 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not being able to have it as associate constant in current stable Rust sucks :-/

I guess we leave it like that then, until it;s possible in stable Rust? What do others think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we in a hurry to merge this? it's a usability regression, and comparing it to the benefits, maybe we should hold off until more of const-generics is stabilized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not in a hurry. Though I fear that it will take a long time until it's usable on stable.

@mriise What do you think about perhaps fixing everything up a single commit (once we are happy with the state of it) and we just keep it open? Then people who want const generic support can easily cherry-pick it themselves and it should also be easier to rebase it if we make other changes (e.g. fixing the blake3 hashing). This way all your work is not lost, still usable and hopefully find it's way in some day.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it is disappointing, I would have to agree. I'll do the final bits of cleanup and request another review.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, is this really a regression? Or could we use this to trim down our digest list a bit (e.g., for all the Blake2s variants).

src/hasher.rs Outdated Show resolved Hide resolved
@mriise
Copy link
Contributor Author

mriise commented Jun 14, 2021

A different way to implement const generics for certian static sized algorithms could be this. This should compile fine and is probably part of the incomplete part. This differs a bit more in complexity but only requires one flag instead of the three in my last suggested option.

@vmx
Copy link
Member

vmx commented Jun 14, 2021

generic_associated_types also seems a lot closer to become a stable feature that full const_generics.

@dvc94ch
Copy link
Collaborator

dvc94ch commented Jun 14, 2021

@mriise thanks for your work on this PR even if it takes a while for it to get merged

@mriise
Copy link
Contributor Author

mriise commented Jun 14, 2021

@vmx Yep! While I will have to put this on the back burner to make room for some other projects, I will open another PR that tries the other approach on nightly at some point.

@dvc94ch thanks!

@vmx
Copy link
Member

vmx commented Jun 14, 2021

@mriise Sure, as we identified it surely isn't time sensitive. I'm also disappointed that it cannot just land. I was warned, but I had hoped we really only need the min_const_generics part. Anyway, I'd like to thank you too, for spending so much time on this and such long review cycles.

@mriise
Copy link
Contributor Author

mriise commented Jun 17, 2021

TL;DR for anyone in the future, this PR made the change of impl StatefulHasher -> impl<const S: usize> StatefulHasher<S> by consequence of Digest<const S: usize> not being able to infer from an associated const (currently we use an associated typenum). As stated above, if you may cherry pick this PR if you need const generic support.

@koushiro
Copy link
Contributor

koushiro commented Sep 17, 2021

@vmx Can you consider merging this PR and releasing a new version? I really need it.

@vmx
Copy link
Member

vmx commented Sep 20, 2021

@koushiro As mentioned in this comment, this PR makes the library harder to use. So the folks currently involved in this PR decided to wait until generic_associated_types land in stable Rust. So it will be a long time until const generics support can land.

@mriise
Copy link
Contributor Author

mriise commented Sep 20, 2021

There may be an alternate approach i've been thinking of... I will play with it a bit and will let you guys know if it will work out.

@mriise
Copy link
Contributor Author

mriise commented Sep 22, 2021

Well, I about tried everything I could think. At this point it boils down to 3 alternatives:

  1. Go back to using Generic Array only implementing const generics for a few items
  2. Unstable features, you can minimally rely on GATs, but it is highly incomplete (I ran across a few ICEs)
  3. An unholy mess of unsafe and pointer magic

The 1st option seems to be possible (I haven't checked but it should work fine), (Option one would rely heavily on const expressions to do const -> typenum) but personally I am a fan of the current implementation simply for how clean it ends up.

I will put together a PR soon with the Generic Array approach.
@vmx @koushiro (@dvc94ch if you want to add anything)
This is still blocked if we want to keep the API the same, though It may be worth merging depending on how urgent @koushiro needs this.

@vmx
Copy link
Member

vmx commented Sep 30, 2021

I'd be in favour of keeping the current API. I don't see a reason to change the API, just for moving from GenericArray to const generics. Though I've little experience on that side of things, so if there would be a good reason, please let me know.

Thanks again @mriise for looking into that.

@mriise
Copy link
Contributor Author

mriise commented Sep 30, 2021

There was one last thing I thought of before setting this aside again that relied heavily on const expressions to convert typenum into const generics, but implementation details are a bit fuzzy and the current implementation isn't ready for it anyway.

So this may be a thing to look at again if full const generics gets stabalized before GATs do (it is anyone's guess at this point i think).

And no problem @vmx, this has been a good challenge for me.

@mriise
Copy link
Contributor Author

mriise commented Oct 6, 2021

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=819a4ceb088d68814c02726708ef0737 ?

still requires nightly features for into/from<[u8; S]> but otherwise OK

@Stebalien
Copy link
Member

@vmx and I discussed this offline and, while annoying, the issues with the interface changes don't outweigh the benefits. We're going to try to move this forward.

Stebalien pushed a commit that referenced this pull request Oct 29, 2021
Rebased and squashed from #116.

Closes #114.
Stebalien pushed a commit that referenced this pull request Oct 29, 2021
Rebased and squashed from #116.

Closes #114.
Stebalien pushed a commit that referenced this pull request Oct 29, 2021
Rebased and squashed from #116. Closes #114.

BREAKING CHANGE: This change replaces uses of typenum with const
generics.

Importantly, the MultihashDigest, Hasher, and StatefulHasher traits are
now generic over the digest size due to compiler limitations. However,
`Hasher` exposes this size as an associated const, so
`MyHasherImpl::SIZE` can often be used to fill in the size parameter
with some help from type inference.

Additionally:

- All usages of typenum must be replaced with usize constants.
- The new MSRV is 1.51.0 due to const generics.
- IdentityDigest is now a tuple struct of usize and the digest instead
  of u8 and a generic array.
@Stebalien
Copy link
Member

Changes merged into the next branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use const generics
5 participants