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

[fixed-hash] Major overhaul of construct_hash! macro #71

Merged
merged 135 commits into from
Oct 30, 2018
Merged

Conversation

Robbepop
Copy link
Contributor

@Robbepop Robbepop commented Oct 20, 2018

Overhaul of the entire sub-crate fixed-hash

Roadmap

Pre-merging

  • Sort out the last few nits with new features

    • Finalize documentation
    • Remove deprecated features
    • Test new features
    • Clean up public API
  • Merge this PR after

  • Bump version to 0.3-beta.0 and publish on crates.io.

Post-merging

Transit all below dependencies to the 0.3.0-beta.X version of fixed-hash. Note that the pwasm-* crates currently depend on parity-hash instead of fixed-hash version 0.2.

  • pwasm-std will provide new uint instances and updated fixed-hash instances without conversion between uint and fixed-hash since I cannot see that it has been provided yet

  • pwasm-ethereum will no longer link to parity-hash and instead use pwasm-std

  • pwasm-abi same as pwasm-ethereum

  • ethbloom simply update fixed-hash

  • ethereum-types update fixed-hash, but already provides uint conversions itself, so no need for providing them by fixed-hash either

  • substrate-primitives update fixed-hash but does not need uint conversion

  • Make any changes that might come up during the work on step 4 and publish 0.3-beta.X as needed

  • Bump version to 0.3, create a tag fixed-hash-0.3 and publish 0.3

Open Questions

  • What to do with impl_hash_uint_conversions macro?
    • The problem: Why should it be defined in fixed-hash and not in uint?
    • Possible solution: Maybe add another crate to combine fixed-hash and uint and allow for this macro.

Summary of changes

  • Major overhaul of the construct_hash macro
    • Possibility to declare types with visibility modifier (not only pub)
    • Possibility to add custom doc comment
    • Syntactically more conforming to default Rust syntax
    • Updated documentation and added examples
  • Major overhaul of the impl_hash_conversion macro
    • No longer requires sizes of input types
    • Added proper error handling for some conversions
    • Updated documentation and added examples
  • The std feature is now a default feature for this crate
  • Removed items that have been deprecated in 0.2.5
  • Removed bad usage of AsRef<Self> impl
  • Removed serialize crate feature

Deref and DerefMut are only meant to be used by smart pointer types.
Hash types as provided by this crate are no smart pointers and thus shouldn't provide Deref or DerefMut impls.
The AsRef<Self> impl is causing problems in generic code because the compiler fails to infer types because of it due to the other more useful AsRef and AsMut impls.
These methods are very similar to the ones provides by std implementations such as String::as_str that are also provided besides String::as_ref impl.
This commit just mirrors this design decision.
An advantage is that there is no type confusion on caller code with these new methods.
The compile errors were direct results of the API changes of the latest commits.
The macro now allows a more standard interface to constructing new types compared to before which is proposed by the Rust API guidelines.
Also added some more examples of new use cases possible by the new interface.
Those include
- private/public type definition
- adding custom doc comment for the type
- having more standard type definition
Using paths that were beginning with `::foo` had a wrong crate as root during the doc tests.
Manually adding `$crate::foo` to all of them fixed the bugs and we can now successfully test those macros in doc comments.

Special thanks to Sergei for pointing $crate out to me!
@parity-cla-bot
Copy link

It looks like @Robbepop signed our Contributor License Agreement. 👍

Many thanks,

Parity Technologies CLA Bot

@Robbepop Robbepop changed the title Api refactorings Major overhaul of construct_hash! macro Oct 20, 2018
@Robbepop
Copy link
Contributor Author

As far as I can tell the build failures reported by TravisCI and Appveyor are not related to fixed-hash and the changes that have been done here.
To verify that yourself you can try to test this crate in isolation locally.

Copy link
Contributor

@tomusdrw tomusdrw left a comment

Choose a reason for hiding this comment

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

Looks pretty cool, would be nice to have a similar overhaul for uint as well, as both types have some commonalities (at least in the way we use them in Parity products).

@Robbepop
Copy link
Contributor Author

Robbepop commented Oct 21, 2018

@tomusdrw Thank you for the feedback!

I have found some other things in the crate that could be improved.
Also the exported macro impl_hash_conversions could have a simpler interface and using path matchers instead of ident in its signature.

This is because all fixed-hash types created via the construct_hash! macro are attributed with the #[repr(C)] which has the effect that they offer a stable interface throughout different compilers and targets for their memory footprint meaning std::mem::size_of<H256>() == 32 is always true if H256 is created as a 32 byte hash type. Also since std::mem::size_of<T>() is a const fn we can replace the size parameters of all the macros with it with the same effect and same optimization level.

Read more: https://doc.rust-lang.org/std/mem/fn.size_of.html#size-of-reprc-items

I didn't fix it since this PR is already pretty big but in the end it should be fixed as well before we release another major version of the fixed-hash crate.

Macro matcher of macro_rules! construct_hash.
The macro no longer requires inputs of the type's sizes since they can be easily infered without overhead thus making this API much more easy to use.
Also greatly improved the documentation and added a usage example for the new API.
The only single usage of it got replaced inline manually.
@Robbepop
Copy link
Contributor Author

TravisCI and Appveyor are showing errors because of this issues which seems to be unrelated to the work in this PR.

@Robbepop Robbepop changed the title Major overhaul of construct_hash! macro [fixed-hash] Major overhaul of construct_hash! macro Oct 21, 2018
Copy link
Contributor

@dvdplm dvdplm left a comment

Choose a reason for hiding this comment

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

Much better docs&examples, and I like the type visibility changes (although: nightly only is a no-no for this to be merged). Tests are failing and I'm not sure exactly why.
The feature set listed in Cargo.toml looks odd (not this PRs fault, but worth fixing if we're introducing breaking changes) and plain wrong (e.g. we don't have any serde code).
Past experience tells me new issues tend to pop up we start using the parity-common crates in parity-ethereum or wherever they're used; it'd be fantastic if you could start a PR in the projects that use fixed-hash to make sure the changes work well there. This crate in particular should work as a replacement for parity-hash so it'd be good to check what needs to be done for that to happen as well.

($large_ty:ident, $small_ty:ident) => {
impl From<$small_ty> for $large_ty {
fn from(value: $small_ty) -> $large_ty {
use std::mem;
Copy link
Contributor

Choose a reason for hiding this comment

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

Something fishy is going on with this macro; on this branch I get warnings about missing std. If the conversions need to work in both std/no_std we should use core::mem (and sort out the implications in lib.rs). I also get a bunch of type mismatch test failures when enabling this feature.
In the PR description you mentioned "testing in isolation". Not sure what you meant by that exactly but I copied the files out of parity-common and fixed up Cargo.toml and ran the tests: same failures. Can you elaborate further on this aspect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The use std::mem; is probably wrong here and should be replaced with use $crate::core::mem are you have suggested I guess. Besides that I will look into the problems you have had.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately I can't find where I have written something about "testing in isolation" and can't remember either. :(
I hope the GitHub downtime is over soon and things are syncing again correctly.

Copy link
Contributor

Choose a reason for hiding this comment

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

To verify that yourself you can try to test this crate in isolation locally.

Copy link
Contributor Author

@Robbepop Robbepop Oct 22, 2018

Choose a reason for hiding this comment

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

@pepyakin and me were trying to find a solution together.

The solution is to include the "std" feature in the default features.
This means that you have to build the crate with the cargo flag --no-default-features for a proper no_std environment. Fortunately this fixes the problems with doc tests above.

Also note that this is how it is done in other no_std crates in the pwasm-* area.

This crate now generates a useful ApiDummy hash type for the purpose of API inspection at docs.rs.
Also the Cargo.toml now refers to the documentation that is going to be generated at docs.rs once uploaded to crates.io.
Deprecated since this crate no longer supports interoperability with uint in version 0.3.
This support has to be provided from the outside, like pwasm-std.
.travis.yml Outdated
@@ -9,7 +9,7 @@ script:
- cargo check --all --tests
- cargo build --all
- cargo test --all --exclude uint --exclude fixed-hash
- cd fixed-hash/ && cargo test --features=std,heapsizeof,uint_conversions && cd ..
- cd fixed-hash/ && cargo test --features=quickcheck-support,heapsize-support && cd ..
Copy link
Contributor

Choose a reason for hiding this comment

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

This does not run all tests I think (24 vs 37). Should perhaps be cargo test --all-features?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea!

serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
rand = { version = "0.5", optional = true, default-features = false }
rustc-hex = { version = "2.0", optional = true, default-features = false }
Copy link
Contributor

Choose a reason for hiding this comment

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

I kind of wish we could get rid of this dependency; I don't think it buys us much ergonomics. One day perhaps...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well we could disable its feature guard by default.

Copy link
Contributor

Choose a reason for hiding this comment

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

From what I've seen in other places rustc-hex is all over the place so doing that would be quite some (boring) work. Probably not something we have time for right now... :)

rand = { version = "0.5", optional = true, default-features = false }
rustc-hex = { version = "2.0", optional = true, default-features = false }
quickcheck = { version = "0.7", optional = true }
byteorder = { version = "1.2.7", optional = true, default-features = false }
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use version = "1.2" here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we can

serialize = ["serde", "serde_derive"]
uint_conversions = ["uint"]
default = ["std", "libc", "rand-support", "rustc-hex-support", "byteorder-support"]
std = ["rustc-hex/std", "rand/std", "byteorder/std"]
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can be just:

default = ["std"]
std = ["rustc-hex/std", "rand/std", "byteorder/std", "libc"]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So you say that all of those features should be disabled by default? I can do that but in my opinion it is always a good thing to have the common uses active by default. This is for example byteorder-support and I would even say rustc-hex-support.

Copy link
Contributor

Choose a reason for hiding this comment

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

No, that's not what I meant. I think that by having rand/std you tell cargo to "use the rand feature and use it with the std feature of rand enabled", i.e. default = ["std"] implies that rustc-hex, rand and byteorder will all be enabled (and will all be configured to use their respective std feature). Do double-check me on that though, I'm not 100% sure!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is that this requires not having rand, byteorder and rustc-hex being activated but their features rand-support, byteorder-support and rustc-hex-support should be enabled since the entire code is guarded by them and not by their respective features which is more accurate I think. So this would cause problems I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Btw.: I think the normal use case is to either use std feature or libc but not both at the same time. So implying the one by the other seems to be wrong to me. But I might be confused about that!

}
Ok(())
}
}

impl Copy for $from {}
impl $crate::core::marker::Copy for $name {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be crazy to make this optional? I.e. to let consumers decide if they want their hash type to be Copy or rather use move semantics? cc @debris

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we could even do this with the simple addition of attributes via #[derive(Copy)] on the user side. This would be pretty cool. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the Copy reference we should keep the behaviour of implementing Copy by default for all hash types: https://doc.rust-lang.org/std/marker/trait.Copy.html#when-should-my-type-be-copy

impl Clone for $from {
fn clone(&self) -> $from {
let mut ret = $from::new();
#[cfg_attr(feature = "dev", allow(expl_impl_clone_on_copy))]
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no dev feature I think? Can we remove this code perhaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is from an old revision and to be honest I thought that the dev feature is something magical. But ofc we can remove it if it doesn't exist at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

@debris do you know the origin of features="dev" per chance?

ret
/// Utilities using the `rand` crate.
#[cfg(feature = "rand-support")]
impl $name {
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too there's an expansion of the API and while the code looks great I'm a bit hesitant to add new public methods like this without knowing more about which projects are going to use them. Can you elaborate?

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 can understand your critics about expanding the API. I just did the same as I have done already in my crate apint. There the rand-based API is a good addition and allows for easy testing with the *_using APIs and allows for user friendly usage by the APIs with the default RNG. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Keep in mind that the rand API existed earlier but wasn't testable since it was missing the *_using methods. So basically the new API allows for greater control and better testability.


/// Implements conversion to and from a hash type and the equally sized unsigned int.
/// CAUTION: Bad things will happen if the two types are not of the same size!
#[cfg(feature="uint_conversions")]
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the plan for these? Move them to uint? Own crate? Just remove them? They are currently in use in ethereum-types so we need some kind of upgrade path for consumers.

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 had the idea to move them to pwasm-std for the pwasm-* crates and also provide uint types by pwasm-std so it fits again for it. And for ethereum-types I would do the same I guess. Maybe just another crate to unify both? It actually is not feasible to make uint depending on fixed-hash since it is built up from u64 words instead of bytes and would require another major overhaul. However, uint is far more complex than fixed-hash.

So to summarize: The best way is to have this functionality in the crate that actually needs this interoperability or in another small utility crate featuring both types.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, a tricky one! My personal opinion is that it's probably less disruptive/more pragmatic to just keep these conversions in fixed-hash for the time being and refactor later – maybe we can make the consuming code use the conversions less or not at all and we can just remove this? If you feel strongly this is not a good path ahead, I can be swayed though (though we can't merge before we have a solution to this as it'd make upgrading impossible).

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 am not strongly against having the conversions in fixed-hash as a conversion path to a better future. However, I do not see anyone of us implementing this better future anytime soon if we stay with this approach. :D

feature = "libc",
not(target_os = "unknown")
))]
#[cfg(feature = "heapsize-support")]
Copy link
Contributor

Choose a reason for hiding this comment

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

The target_os need to be there I'm afraid. WASM can't use the heapsize crate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

okay will revert it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Seems like heapsize needs libc and target_os = "unknown" settings.
@dvdplm
Copy link
Contributor

dvdplm commented Oct 30, 2018

LGTM. Great work and perseverence here @Robbepop! Many thanks.

@Robbepop Robbepop merged commit e101b64 into master Oct 30, 2018
@ordian ordian deleted the api-refactorings branch May 11, 2019 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants