Skip to content

Conversation

@Nadrieril
Copy link
Contributor

@Nadrieril Nadrieril commented Nov 14, 2019

After squinting for a while at the wrapper trick for assert_impl_any, it turns out it generalizes quite well. I implemented a generic assert_impl macro with type-level booleans that covers most of the cases where we want to check for a type implementing a set of traits.
Examples:

assert_impl!(Cell<u32>: Send && !Sync);
assert_impl!(u32: (Into<u16>) || (Into<u64>));
assert_impl!(for(T) Rc<T>: Clone);
assert_impl!(for(T: Copy) T: Clone);

The error messages are also quite nice:

error[E0308]: mismatched types
  --> tests/trait_impl.rs:66:1
   |
66 | assert_impl!(for(T) T: Clone);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `static_assertions::True`, found struct `static_assertions::False`
   |
   = note: expected type `static_assertions::True`
              found type `static_assertions::False`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

@Nadrieril
Copy link
Contributor Author

There are a few limitations to the syntax due to macro shenanigans. Maybe we should make a proc_macro instead.

@Nadrieril
Copy link
Contributor Author

This subsumes #27.

@Nadrieril
Copy link
Contributor Author

Since the True and False structs are local to the macro, I believe that this is safe against external blanket traits

@Nadrieril
Copy link
Contributor Author

I just saw that #26 had a similar idea. I could use const booleans too actually, as long as I can recover pretty error messages

@nvzqz nvzqz added this to the v2.0.0 milestone Nov 14, 2019
@nvzqz
Copy link
Owner

nvzqz commented Nov 14, 2019

This is awesome!! One of my plans for 2.0 was to consolidate the assert_impl*! family of macros into a single one that uses ! for "not", | for "any", and & for "all". I haven't had the time to sit down and attempt it, so thank you very much for putting in the work!

After squinting for a while at the wrapper trick for assert_impl_any

I can only imagine you've been squinting for quite some time. I'm very impressed by you being able to do this. I'll read through the implementation when I have more time this weekend to make sense of your tt munching.


In the meantime, could you extract the bits that emit True and False to the assert_impl*! family of macros? I'd like to improve those error messages.

My plan is to adopt this nicer syntax in a 1.x version and deprecate the others to be removed in 2.0.

@Nadrieril
Copy link
Contributor Author

could you extract the bits that emit True and False to the assert_impl*! family of macros?

I'm not sure what you mean :/

I'd like to improve those error messages

I am in the process of using the newly defined type_level_bool::{True, False} types in assert_impl to get better errors :)

@nvzqz
Copy link
Owner

nvzqz commented Nov 14, 2019

So the error message here shows:

expected struct `_::True`, found struct `_::False`

I was saying I'd like that for the old macros.

But it seems that you've redefined the other macros to use this new macro instead. So I imagine you'd get that for free.

The Deref trick used in `assert_impl_any` can be generalized, and we get
a general way of checking whether `$type: $trait` that can be used to
implement most of the `assert_impl_*` and `assert_sub_*` macros.
This subsumes a few more impl macros
@Nadrieril
Copy link
Contributor Author

Rebased on master and now we got the pretty errors

@nvzqz
Copy link
Owner

nvzqz commented Nov 14, 2019

Thanks! I'll definitely try to check this out soon.

Your True and False type stuff got me to create typebool, which I think I'm gonna have this crate use.

@Nadrieril
Copy link
Contributor Author

Nadrieril commented Nov 14, 2019

We probably don't need that much structure on True and False. I'd rather use const booleans if we can instead, and use True and False only for pretty errors. I just haven't quite figured out how because the deref trick can't be used to generate a const value

As a nice side-effect, we don't need the local booleans to secure
against blanket impls.
@Nadrieril
Copy link
Contributor Author

Ugh I hit an ICE rust-lang/rust#62708 when trying to use const bool instead of type-level bools :(

@Nadrieril
Copy link
Contributor Author

Nadrieril commented Nov 14, 2019

Ok, so now it's easy to get a const type-level boolean testing whether a type implements a particular bunch of traits (that's what the hidden does_impl macro does). It's also easy to get a const bool from that. But because of rust-lang/rust#62708, we can't use const_assert on that boolean and remove the intermediate type-level bools :(
If we had another way of implementing to_bool! that might solve this problem.

@nvzqz
Copy link
Owner

nvzqz commented Dec 4, 2019

Sorry I haven't gotten back to you on this. Life got in the way. I'll review this soon, I promise!

@nvzqz nvzqz removed this from the v2.0.0 milestone Dec 29, 2019
@nvzqz nvzqz merged commit 87c22af into nvzqz:master Dec 29, 2019
@nvzqz
Copy link
Owner

nvzqz commented Dec 29, 2019

Hey @Nadrieril thanks again for this PR! ❤️

I finally got around to looking through it and I'm happy with the implementation. Apologies for it taking so long to review. A new version will be released soon with this macro!

@Nadrieril Nadrieril deleted the assert_impl branch December 29, 2019 22:47
nvzqz added a commit to nvzqz/impls that referenced this pull request Dec 30, 2019
They made the initial `does_impl` implementation, which can be found at
nvzqz/static-assertions#28.
nvzqz added a commit to nvzqz/impls that referenced this pull request Dec 30, 2019
Inspired by nvzqz/static-assertions#28 but it
takes a much simpler approach.

This supports the boolean operators: | (OR), & (AND), ^ (XOR), ! (NOT).
It also allows for parentheses (with nesting).

Unlike that in `static_assertions`, it:

- Handles operators in the correct order of precedence. This is because
  it uses the operators defined on `bool` directly, and thus does not
  need to redefine them on `True` and `False`, also reducing complexity.

- Removes the need to parenthesize traits with generic parameters. This
  is done by handling generic parsing directly when the operator can't
  be parsed right after a `path` (& and ^; | seems to work fine).
@nvzqz
Copy link
Owner

nvzqz commented Dec 30, 2019

@Nadrieril I rewrote the macro and fixed all of the issues with it! And I decided to put it into its own library at https://github.com/nvzqz/impls. I credited you as co-author in the README.md, in the LICENSE-MIT file, and in the Cargo.toml file. I posted it on Twitter and people seem to like it. Thanks again!

@Nadrieril
Copy link
Contributor Author

That's great ! You seem to have dropped support for generics though ?

@wycats
Copy link

wycats commented Nov 1, 2021

@nvzqz I was surprised to see the missing support for for(...) generics as well. I see that the code is still in assert_impl, but assert_impl seems not to be exposed.

@wycats
Copy link

wycats commented Nov 1, 2021

Upon further investigation, it looks like the issue is that the commits after November 3, 2019 were never released to crates.io.

@TTWNO
Copy link

TTWNO commented May 9, 2023

@nvzqz Can you please release the latest changes on crates.io? I realize the functionality of this crate is quite stable, but this feature would help me a lot.

Happy to help if needed.

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.

4 participants