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.
rustc_version : rustc 1.20.0-nightly (ae98ebfcb 2017-07-20)
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 const
s 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.
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:
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
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=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?
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: