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

Array lengths don't support generic parameters. #43408

Open
roblabla opened this issue Jul 22, 2017 · 28 comments
Open

Array lengths don't support generic parameters. #43408

roblabla opened this issue Jul 22, 2017 · 28 comments
Labels
A-array Area: `[T; N]` A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-const-generics Area: const generics (parameters and arguments) A-lazy-normalization Area: Lazy normalization (tracking issue: #60471) glacier ICE tracked in rust-lang/glacier. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@roblabla
Copy link
Contributor

roblabla commented Jul 22, 2017

EDIT: This can now be made to work on nightly compilers, although it's a bit awkward to use. See this comment.

It would seem that when using size_of in const fn context, it fails to properly compute the size of generic types.

The following function fails

unsafe fn zeroed<T: Sized>() -> T {
    // First create an array that has the same size as T, initialized at 0
    let x = [0u8; std::mem::size_of::<T>()];
    // Then, transmute it to type T, and return it
    std::mem::transmute(x)
}

The error is the following :

error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied
 --> src/main.rs:3:19
  |
3 |     let x = [0u8; std::mem::size_of::<T>()];
  |                   ^^^^^^^^^^^^^^^^^^^^^^ `T` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `T`
  = help: consider adding a `where T: std::marker::Sized` bound
  = note: required by `std::mem::size_of`

This is very confusing because it complains that T doesn't have std::marker::Sized, even though it does have that bound.

Meta

rustc_version : rustc 1.20.0-nightly (ae98ebf 2017-07-20)

@Mark-Simulacrum Mark-Simulacrum added A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. C-bug Category: This is a bug. labels Jul 26, 2017
@Mark-Simulacrum
Copy link
Member

cc @eddyb -- was this intentional?

@Mark-Simulacrum Mark-Simulacrum added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jul 26, 2017
@eddyb
Copy link
Member

eddyb commented Jul 27, 2017

This is a limitation of array length - it can't use any parameters in scope - which also came up during the stabilization of associated consts (cc @withoutboats do we have a common issue to use for this?) and the decision taken was to stabilize associated consts without it.

The exact error here comes from the type parameter being resolved, but the ty::Generics and ty::GenericPredicates (bounds, including the default Sized on type parameters) of the array length are actually both empty. They have to be (for now), otherwise we get cycle errors.

@eddyb
Copy link
Member

eddyb commented Jul 27, 2017

This example triggers a const-evaluation error instead (try on playpen):

fn test<T: ?Sized>() {
    [0u8; std::mem::size_of::<&T>()];
}

cc @GuillaumeGomez @oli-obk Why is the const-eval error printing broken still 😢?

@scottmcm
Copy link
Member

This makes me sad, as it's the one thing I wanted to do with #42859 😢

@ExpHP

This comment has been minimized.

@ExpHP

This comment has been minimized.

@juchiast
Copy link
Contributor

I think we shoud change the error message, this is very confusing now.

@eddyb
Copy link
Member

eddyb commented Apr 17, 2018

@ExpHP The problem is using type parameters (e.g. your T) in array lengths, everything else works.

@juchiast The error message is "emergent" from the same reason we can't "just" allow this to work right now, the the only other solution I can think of is checking if type-parameters are "really in scope" but that would probably break existing code that doesn't need to look at type parameters.

@eddyb eddyb changed the title const fn size_of doesn't work on generic types Array lengths don't support generic type parameters. Apr 17, 2018
@MageSlayer
Copy link

MageSlayer commented May 24, 2018

#![feature(const_fn)]
pub const fn sof<T:?Sized>() -> usize {
    10
}
fn to_byte_array<T>() -> [u8; sof::<T>()] {
     panic!()
}

Trying this way results in compiler crash in nightly.

error: internal compiler error: librustc/ty/subst.rs:456: Type parameter `T/#0` (T/0) out of range when substituting (root type=Some(fn() -> usize {sof::<T>})) substs=[]

thread 'main' panicked at 'Box<Any>', librustc_errors/lib.rs:499:9

Any workarounds known?

@eddyb
Copy link
Member

eddyb commented May 24, 2018

@MageSlayer Nope, it just can't be supported yet, see #43408 (comment) and previous.

@JohnTitor
Copy link
Member

Triage:

The current output of #43408 (comment) with the latest nightly:

error: generic parameters may not be used in const operations
 --> src\main.rs:7:37
  |
7 | fn to_byte_array<T>() -> [u8; sof::<T>()] {
  |                                     ^ cannot perform const operation using `T`
  |
  = note: type parameters may not be used in const expressions
  = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions

error: aborting due to previous error

And with #![feature(const_generics)]:

warning: cannot use constants which depend on generic parameters in types
 --> src\main.rs:8:30
  |
8 | pub fn to_byte_array<T>() -> [u8; sof::<T>()] {
  |                              ^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(const_evaluatable_unchecked)]` on by default
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!        
  = note: for more information, see issue #76200 <https://github.com/rust-lang/rust/issues/76200>

It's no longer ICE but rejected by const_evaluatable_checked.

@roblabla
Copy link
Contributor Author

This now appear to work-ish, but is a bit awkward to use:

#![feature(generic_const_exprs)]

unsafe fn zeroed<T: Sized>() -> T 
where
    [(); std::mem::size_of::<T>()]:
{
    // First create an array that has the same size as T, initialized at 0
    let x = [0u8; std::mem::size_of::<T>()];
    // Then, transmute it to type T, and return it
    std::mem::transmute_copy(&x)
}

Two things of note:

  • The weird where close with an empty bound is necessary, otherwise I get an error: unconstrained generic constant.
  • Transmute doesn't work, because the compiler fails to prove that [u8; size_of::<T>()] has the same size as T, so we have to work around this by using transmute_copy

@ExpHP

This comment has been minimized.

@roblabla
Copy link
Contributor Author

roblabla commented Sep 12, 2021

Though not relevant to the compiler issue, for the sake of those who may come across this it is obligatory to point out that the above implementation of zeroed using std::mem::transmute_copy can invoke UB if std::mem::align_of::<T>() > 1, which is likely not the intent.

@ExpHP I believe this is wrong, and my implementation is safe even if T has a higher alignment than 1. Heres what transmute_copy's documentation says:

This function will unsafely assume the pointer src is valid for size_of::<U> bytes by transmuting &T to &U and then reading the &U (except that this is done in a way that is correct even when &U makes stricter alignment requirements than &T). It will also unsafely create a copy of the contained value instead of moving out of src.

(Emphasis mine)

I believe this means that it is correct even when &U (our output type) has a "stricter alignment" (higher alignment) than &[u8]. If this is wrong, then I think the documentation of transmute_copy should be changed to better define what stricter alignment means.

@agausmann
Copy link
Contributor

agausmann commented Sep 12, 2021

@roblabla is right, transmute_copy uses std::ptr::read_unaligned internally, which will safely read the value of unaligned pointers, and the destination of the copy will be at an aligned location in memory.

The docs for transmute_copy are sort of incorrect though. It never transmutes &T to &U, it obtains an unaligned pointer *const U by casting src as *const T as *const U, which it then reads using read_unaligned. If it ever did create an unaligned reference &U, then it would be UB, but it does not.

@ScSofts

This comment was marked as duplicate.

@ScSofts

This comment was marked as duplicate.

@Enselic
Copy link
Member

Enselic commented Jul 26, 2023

It's no longer ICE

@rustbot label -I-ICE

@rustbot rustbot removed the I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ label Jul 26, 2023
@estebank estebank removed C-bug Category: This is a bug. D-confusing Diagnostics: Confusing error or lint that should be reworked. labels Jul 26, 2023
cmcqueen added a commit to cmcqueen/simplerandom-rs that referenced this issue Jun 21, 2024
Current Rust 1.45.2 doesn't support this. So need to wait until Rust is improved.
rust-lang/rust#43408
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-array Area: `[T; N]` A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-const-generics Area: const generics (parameters and arguments) A-lazy-normalization Area: Lazy normalization (tracking issue: #60471) glacier ICE tracked in rust-lang/glacier. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests