Rust: Array lengths don't support generic parameters.

Created on 22 Jul 2017  路  19Comments  路  Source: rust-lang/rust

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 (ae98ebfcb 2017-07-20)

A-const-fn A-const-generics A-lazy-normalization C-bug D-confusing I-ICE T-compiler glacier

Most helpful comment

This makes me sad, as it's the one thing I wanted to do with https://github.com/rust-lang/rust/pull/42859 :cry:

All 19 comments

cc @eddyb -- was this intentional?

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.

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 馃槩?

This makes me sad, as it's the one thing I wanted to do with https://github.com/rust-lang/rust/pull/42859 :cry:

Weird thing: You can... um... kind of work around it. (but perhaps you shouldn't)

pub trait ConstSizeOf: Sized {
    const SIZE: usize = ::std::mem::size_of::<Self>();
}

impl<T> ConstSizeOf for T { }

You get a, uh... warning... that sounds extremely serious and that seems to suggest that this will have a very high probability of blowing up in your face.

   Compiling playground v0.0.1 (file:///playground)
warning: constant evaluation error: the type `Self` has an unknown layout
 --> src/main.rs:2:25
  |
2 |     const SIZE: usize = ::std::mem::size_of::<Self>();
  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: #[warn(const_err)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 0.52 secs
     Running `target/debug/playground`

But, um...

it, uh...seems to work.

fn main() {
    // using println instead of assert_eq! to make sure it isn't just optimized
    // away as undefined behavior
    println!("{:?}", <i8>::SIZE); // prints 1
    println!("{:?}", <i16>::SIZE); // prints 2
    println!("{:?}", <i32>::SIZE); // prints 4
    println!("{:?}", <i64>::SIZE); // prints 8
    println!("{:?}", <Vec<()>>::SIZE); // prints 24
    println!("{:?}", <Vec<i8>>::SIZE); // prints 24

    fn test_vec<T>() {
        println!("{:?}", Vec::<T>::SIZE);
    }
    fn test_tuple<T>() {
        println!("{:?}", <(T, T)>::SIZE);
    }
    test_vec::<()>(); // prints 24
    test_vec::<i8>(); // prints 24
    test_tuple::<()>(); // prints 0
    test_tuple::<i8>(); // prints 2
}

Oops, never mind.

// error: T: Sized is not satisfied
fn to_byte_array<T>() -> [u8; T::SIZE] {
    panic!()
}

I could have sworn I've done something before with associated consts in array types, though...

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

@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.

#![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?

@MageSlayer Nope, it just can't be supported yet, see https://github.com/rust-lang/rust/issues/43408#issuecomment-381945540 and previous.

Here is my workaround in Serde for [u8; mem::size_of::<T>()]. It supports rustc 1.20+. Note that the type is at least as big as T rather than exactly as big as T, so instead of transmute you would need ptr::write / ptr::read to interact with the bytes.

https://github.com/serde-rs/serde/blob/d07b62bba481af6603a736f027b5ebbb74a4a63a/serde/src/backport.rs

Although it is surprising that this is not a standard language feature, I just discovered the crate generic_array which appears to achieve the desired effect.

fn test<const N: usize>() {
    let array = [0; N];
}

Fails with error: array lengths can't depend on generic parameters.

@gorilskij Hmm, that's a different bug, despite of the phrasing matching this issue.

@varkor @oli-obk I think we should "just" make Rvalue::Repeat hold a ty::Const for the length, I don't foresee any issues arising from that.
(More realistically, it should probably be a "broadcast assignment" statement, but either way, it should hold a ty::Const or rely on the one in the ty::Array type)

@eddyb: I've opened https://github.com/rust-lang/rust/issues/68567 to make sure we don't lose track of that. I'll tackle it soon if no-one else wants to take it.

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 cry?

I don't know if this is a known bug, but this snippet results in an ICE in both stable (rustc 1.40.0 (73528e339 2019-12-16)) and nightly (rustc 1.42.0-nightly (8a79d08fa 2020-01-27)) versions, link to playpen.

Current output for some of these cases:

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

error: constant expression depends on a generic parameter
 --> src/lib.rs:3:19
  |
3 |     let x = [0u8; std::mem::size_of::<T>()];
  |                   ^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: this may fail depending on what value the parameter takes

error: constant expression depends on a generic parameter
 --> src/lib.rs:5:5
  |
5 |     std::mem::transmute(x)
  |     ^^^^^^^^^^^^^^^^^^^
  |
  = note: this may fail depending on what value the parameter takes

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

error: constant expression depends on a generic parameter
 --> src/main.rs:2:11
  |
2 |     [0u8; std::mem::size_of::<&T>()];
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: this may fail depending on what value the parameter takes

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=3447eb55e57acb584ed10a73655d7179 now works

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=ffb92ec39b7481bfd1b3f435257ed3ce still ICEs in the same way

Here is an ICE I stumbled upon recently which seems relevant. Reporting as per @estebank's suggestion.

use std::marker::PhantomData;

struct Buffer<T: ?Sized> {
    buf: [u8; Self::LEN],
    phantom: PhantomData<T>,
}

impl<T: ?Sized> Buffer<T> {
    const LEN: usize = 64;
}

fn main() {
    println!("Hello, world!");
}


Backtrace

error: internal compiler error: src/librustc/ty/subst.rs:565: type parameter `T/#0` (T/0) out of range when substituting (root type=Some(T)) substs=[]

thread 'rustc' panicked at 'Box<Any>', <::std::macros::panic macros>:2:4
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:78
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:59
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1063
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1426
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:204
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:224
  10: rustc_driver::report_ice
  11: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:474
  12: std::panicking::begin_panic
  13: rustc_errors::HandlerInner::span_bug
  14: rustc_errors::Handler::span_bug
  15: rustc::util::bug::opt_span_bug_fmt::{{closure}}
  16: rustc::ty::context::tls::with_opt::{{closure}}
  17: rustc::ty::context::tls::with_opt
  18: rustc::util::bug::opt_span_bug_fmt
  19: rustc::util::bug::span_bug_fmt
  20: <rustc::ty::subst::SubstFolder as rustc::ty::fold::TypeFolder>::fold_ty
  21: rustc::ty::fold::TypeFoldable::fold_with
  22: <rustc::ty::subst::SubstFolder as rustc::ty::fold::TypeFolder>::fold_const
  23: rustc::ty::normalize_erasing_regions::<impl rustc::ty::context::TyCtxt>::subst_and_normalize_erasing_regions
  24: rustc_mir::interpret::operand::<impl rustc_mir::interpret::eval_context::InterpCx<M>>::eval_operand
  25: rustc_mir::interpret::step::<impl rustc_mir::interpret::eval_context::InterpCx<M>>::eval_rvalue_into_place
  26: rustc_mir::interpret::step::<impl rustc_mir::interpret::eval_context::InterpCx<M>>::run
  27: rustc_mir::const_eval::eval_queries::const_eval_raw_provider
  28: rustc::ty::query::__query_compute::const_eval_raw
  29: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors for rustc::ty::query::queries::const_eval_raw>::compute
  30: rustc::dep_graph::graph::DepGraph::with_task_impl
  31: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::get_query
  32: rustc_mir::const_eval::eval_queries::const_eval_validated_provider
  33: rustc::ty::query::__query_compute::const_eval_validated
  34: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors for rustc::ty::query::queries::const_eval_validated>::compute
  35: rustc::dep_graph::graph::DepGraph::with_task_impl
  36: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::get_query
  37: rustc_mir::const_eval::eval_queries::const_eval_validated_provider
  38: rustc::ty::query::__query_compute::const_eval_validated
  39: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors for rustc::ty::query::queries::const_eval_validated>::compute
  40: rustc::dep_graph::graph::DepGraph::with_task_impl
  41: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::get_query
  42: rustc::mir::interpret::queries::<impl rustc::ty::context::TyCtxt>::const_eval_resolve
  43: rustc::ty::sty::Const::eval::{{closure}}
  44: rustc::ty::sty::Const::eval
  45: rustc::ty::structural_impls::<impl rustc::ty::fold::TypeFoldable for &rustc::ty::TyS>::super_fold_with
  46: <rustc_infer::traits::project::AssocTypeNormalizer as rustc::ty::fold::TypeFolder>::fold_ty
  47: rustc_infer::traits::project::normalize
  48: rustc_infer::infer::InferCtxt::partially_normalize_associated_types_in
  49: <core::iter::adapters::Map<I,F> as core::iter::traits::iterator::Iterator>::fold
  50: rustc::ty::context::GlobalCtxt::enter_local
  51: rustc_typeck::check::wfcheck::check_item_well_formed
  52: rustc::ty::query::__query_compute::check_item_well_formed
  53: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors for rustc::ty::query::queries::check_item_well_formed>::compute
  54: rustc::dep_graph::graph::DepGraph::with_task_impl
  55: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::get_query
  56: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::ensure_query
  57: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:86
  58: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
  59: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:86
  60: rustc_hir::hir::Crate::par_visit_all_item_likes
  61: rustc_session::session::Session::track_errors
  62: rustc_typeck::check_crate
  63: rustc_interface::passes::analysis
  64: rustc::ty::query::__query_compute::analysis
  65: rustc::dep_graph::graph::DepGraph::with_task_impl
  66: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::get_query
  67: rustc::ty::context::tls::enter_global
  68: rustc_interface::interface::run_compiler_in_existing_thread_pool
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.43.1 (8d69840ab 2020-05-04) running on x86_64-unknown-linux-gnu

note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type bin

note: some of the compiler flags provided by cargo are hidden

query stack during panic:
#0 [const_eval_raw] const-evaluating `Buffer::buf::{{constant}}#0`
#1 [const_eval_validated] const-evaluating + checking `Buffer::buf::{{constant}}#0`
#2 [const_eval_validated] const-evaluating + checking `Buffer::buf::{{constant}}#0`
#3 [check_item_well_formed] processing `Buffer`
#4 [analysis] running analysis passes on this crate
end of query stack
error: aborting due to previous error

Thanks for the great work!
Is there any chance this will be available soon?

Was this page helpful?
0 / 5 - 0 ratings