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

Limitations #1

Closed
8 tasks done
nvzqz opened this issue Aug 13, 2017 · 11 comments
Closed
8 tasks done

Limitations #1

nvzqz opened this issue Aug 13, 2017 · 11 comments

Comments

@nvzqz
Copy link
Owner

nvzqz commented Aug 13, 2017

Currently the following macros are only available from within the context of a function:

  • assert_eq_size (commit f3da7e4)
    • Relies on forget, transmute, and uninitialized as provided by core::mem. transmute and uninitialized are marked as unsafe, which makes it unlikely for them to become const.
    • Could provide an assertion built on const_assert if mem::size_of were a const fn. To resolve the limitation, const_assert would need to be resolved first.
  • const_assert (commit b7ca44e)
  • const_assert_eq (commit b7ca44e)
    • Dependent on const_assert, so resolving that macro would resolve this one.

Commits b7ca44e and f3da7e4 have somewhat resolved this.

The following macros now require labels (identifiers) unique to their namespace to be used in a non-function context:

  • assert_eq_size
  • assert_obj_safe
  • assert_impl
  • const_assert
  • const_assert_eq

This, along with having more assertions available (#2), would allow for a 1.0.0 release.

nvzqz added a commit that referenced this issue Aug 13, 2017
If used outside of a function, a unique label is required.

This is a partial solution to issue #1.
nvzqz added a commit that referenced this issue Aug 13, 2017
This is a partial solution to issue #1.
@nvzqz nvzqz added this to the Stable (v1.0.0) milestone Aug 13, 2017
@nvzqz
Copy link
Owner Author

nvzqz commented Nov 1, 2018

See rust-lang/rust#54912 for the fix to this issue. With this, we can declare macros like const_assert! as such (run in playground):

#![feature(underscore_const_names)]

macro_rules! const_assert {
    ($($xs:expr),+ $(,)*) => {
        const _: [(); 0 - !($($xs)&&+) as usize] = [];
    };
}

@asomers
Copy link

asomers commented Nov 8, 2018

Another limitation is that the size of a generic type cannot be determined. For example, the following does not work:

#[test]
fn test_generic_types() {
    fn foo<T, U>(_t: T, _u: U) {
        assert_eq_size!(T, U);
    }
}
error[E0512]: transmute called with types of different sizes
   --> tests/eq_size.rs:113:9
    |
113 |         assert_eq_size!(T, U);
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: source type: T (this type's size can vary)
    = note: target type: U (this type's size can vary)
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

I don't know if it's possible to remove this limitation, but it would be extremely useful to do so.

@nvzqz
Copy link
Owner Author

nvzqz commented Nov 11, 2018

@asomers Unfortunately, if we try taking the const_assert! approach to fix this, other errors show up. 😕

Say you have the following (test in playground):

use std::mem::size_of;

fn test<T, U>() {
    // Essentially const_assert!()'s internals
    const ASSERT: [
        ();
        0 - !(size_of::<T>() == size_of::<U>()) as usize
    ] = [];
}

This generates the following errors:

error[E0401]: can't use type parameters from outer function
 --> src/main.rs:7:25
  |
3 | fn test<T, U>() {
  |    ---- - type variable from outer function
  |    |
  |    try adding a local type parameter in this method instead
...
7 |         0 - !(size_of::<T>() == size_of::<U>()) as usize
  |                         ^ use of type variable from outer function

error[E0401]: can't use type parameters from outer function
 --> src/main.rs:7:43
  |
3 | fn test<T, U>() {
  |    ----    - type variable from outer function
  |    |
  |    try adding a local type parameter in this method instead
...
7 |         0 - !(size_of::<T>() == size_of::<U>()) as usize
  |                                           ^ use of type variable from outer function

I'm not sure if anyone's working to fix this issue. I agree that it would be quite nice to be able to have these assurances in monomorphized functions.

@asomers
Copy link

asomers commented Nov 11, 2018

Interesting. The reference to an "outer function" is what you would expect if [(); _] were a macro, but it isn't. I'm of the opinion that this is a compiler bug. It's an incorrect error message, if nothing else.

What you're doing looks basically like creating a variable-sized array, which of course isn't supported in Rust. I don't have any better ideas about how to do it, though. I suppose you could wrap the generic function in a macro, but that's ugly.

@nvzqz
Copy link
Owner Author

nvzqz commented Nov 12, 2018

Not just in an array context; doing this won't work either:

const X: usize = size_of::<T>();

@regiontog
Copy link

I found that I can create unique identifiers for macros on stable using procedual macros. Maybe this could help remove some of your limitations?

Take a look at this crate for the implementation.

@nvzqz
Copy link
Owner Author

nvzqz commented Jan 19, 2019

Is a separate crate still necessary for procedural macros or can they now be written within the same crate in which they're used?

Also, I consider bumping the minimum rustc version from 1.24.0 to 1.30.0 a breaking change, which means static_assertions going from 0.3.x to 0.4. I'm fine with that, but I'd like to see if I come up with some features to add or introduce other breaking changes before I go down that path.

Eventually I want to go down the const _ route since that feels cleaner to me. This means bumping rustc to whatever future version underscore_const_names gets stabilized in, which again is a breaking change. So I think at that point, I'd be comfortable with making this crate 1.0. However, that would make the only difference between 0.4 and 1.0 just be a rustc version bump, which is underwhelming. I can probably come up with ideas for other things to include in 1.0 but that's not a guarantee, and I don't want to create very basic features that don't add much value.

@regiontog
Copy link

regiontog commented Jan 19, 2019

AFAIK you still need a seperate crate, at least for crates with both #[macro_export] and #[proc_macro]. You could have a multi-crate setup I think e.g. like serde.

After testing multi-crates it seems like it works, however users of the #[macro_export] library have to explicitly depend on both crates. If not then you get a use of undeclared type or module "gensym" from the macro expansion.

@Centril
Copy link

Centril commented May 30, 2019

I'm of the opinion that this is a compiler bug.

It's not. A const, type, ... item may not refer to the parameters of the outer scope unless the immediate scope is an impl. This is by design. Rust does not support lexically scoped type variables.

@carbotaniuman
Copy link

carbotaniuman commented Aug 25, 2019

Why is it still not possible to use this outside of a function even though underscore_const_names has stabilized?

@nvzqz nvzqz modified the milestone: Stable (v1.0.0) Oct 2, 2019
@nvzqz nvzqz closed this as completed in e718a13 Oct 2, 2019
@nvzqz
Copy link
Owner Author

nvzqz commented Oct 2, 2019

1.0 is now available! So yes @carbotaniuman now you can use most things outside of the context of a function.

@nvzqz nvzqz unpinned this issue Oct 6, 2019
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

No branches or pull requests

5 participants