Open
Description
Code
fn main() {
const EMPTY: Vec<u32> = vec![];
let mut grid: [Vec<u32>; 2500] = [EMPTY; 2500];
let grid_clone = grid.clone();
}
Current output
thread 'main' has overflowed its stack
error: process didn't exit successfully: `target\debug\t.exe` (exit code: 0xc00000fd, STATUS_STACK_OVERFLOW)
Desired output
thread 'main' has overflowed its stack
stack overflow caused by clone() on line 5
error: process didn't exit successfully: `target\debug\t.exe` (exit code: 0xc00000fd, STATUS_STACK_OVERFLOW)
Rationale and extra context
No response
Other cases
No response
Anything else?
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
[-]Large Array of Vecs cloned causes stack overflow but doesn't provide any details[/-][+]Medium Array of Vecs cloned causes stack overflow but doesn't provide any details[/+]saethlin commentedon May 18, 2023
What is your
rustc --version --verbose
?The code as-written doesn't stack overflow on Linux, bumping the buffer size up to
30000
will always stack overflow (on stable), but somewhere around29000
, the program sometimes overflows and sometimes doesn't.saethlin commentedon May 18, 2023
The backtrace to the crash:
The crash is here (I bumped the array size to 50000):
I think a large fraction of the problem here is that
try_from_fn
contains enough stack space for 5 of these arrays:I suspect the rest of the memory overhead comes from the fact that this
clone()
call returns the cloned array through many layers of abstraction that don't get optimized away. Every stack frame along the way probably needs space for the array.I don't know how to solve this well. But I feel like it should be possible to write a
Clone
implementation for arrays that doesn't need so many locals that are the size of the array.scottmcm commentedon May 18, 2023
I did make this better back in 52df055#diff-b83fad8dc170d433a4e4aaa27de41f508c1ec8e854326fe815d779e70338080a, but it still needs things like #111551 before it'll be properly better -- see specifically https://github.com/rust-lang/rust/pull/111551/files#diff-b83fad8dc170d433a4e4aaa27de41f508c1ec8e854326fe815d779e70338080a showing that letting LLVM get rid of another array-sized temporary.
See also the general issues #91521 and #79914
saethlin commentedon May 18, 2023
So IMO the fact that this crashes at all is already covered better elsewhere, and this is really just a diagnostic request.
@rustbot label -T-libs
scottmcm commentedon May 18, 2023
One problem here is that it needs to return it. At the ABI level, for an array this long that'll be a pointer, which is great, but there's no way in the Rust code to get that pointer, so there's no way for the Rust code to write to it directly.
Thus getting this to not use extra stack space fundamentally depends on either
[MaybeUninit<T>; N]
and then doingreturn transmute(that_local)
compile into code that's writing to the return place directly, or&mut MaybeUninit<T>
?) and then return the value already in there -- custom MIR could do it, but we probably don't want that incore
.saethlin commentedon May 19, 2023
Sure.
Clone
needs to return the array once, but the stack exhaustion cited here occurs because we return it many times. One extra temporary might be in the range of expectation, but currently we have about 10.teken commentedon May 19, 2023
I'm on windows
scottmcm commentedon May 19, 2023
#107634 landed for 1.69, so definitely check if it's better after an update -- in 1.68.2 it had an extra step of going through
Result<[T; N], IntoIter<T, N>>
that in practice didn't optimize out.teken commentedon May 19, 2023
When I updated graded to
rustc 1.69.0 (84c898d65 2023-04-16)
it overflows at around 22000 items