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

Specialized impl for a type with const generics in another crate causes a stack overflow in rustc #68104

Closed
stbowers opened this issue Jan 10, 2020 · 2 comments · Fixed by #72600
Labels
A-const-generics Area: const generics (parameters and arguments) C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. F-const_generics `#![feature(const_generics)]` I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@stbowers
Copy link

Currently with const generics you can create a "specialized" implementation, such as:

pub struct MyStruct<const N: usize> {
    _data: [usize; N],
}

impl<const N: usize> MyStruct<N> {
    pub fn new() -> MyStruct<N> {
        return MyStruct {
            _data: unsafe { std::mem::uninitialized() },
        };
    }

    pub fn test_generic(&self) {
        println!("There are {} elements!", N);
    }
}

// Specialized implementation when N == 3
impl MyStruct<3> {
    pub fn test_three(&self) {
        println!("There are 3 elements! Do something cool with this special case!");
    }
}

fn main() {
    let my_struct: MyStruct<3> = MyStruct::new();
    
    my_struct.test_generic();
    my_struct.test_three();
}

Which has the following output:

There are 3 elements!
There are 3 elements! Do something cool with this special case!

(playground link: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c5c862b1590029a6c785ec6feddd6da8)

However, when using such a type in another crate this causes a stack overflow in the compiler. For example, with the following code (full example: https://github.com/stbowers/const-generics-test):

// main.rs
use test_lib::*;

fn main() {
    let my_struct: MyStruct<3> = MyStruct::new();

    my_struct.test_generic();
    my_struct.test_three(); // If this line is commented out it will compile correctly
}
// lib.rs (in another crate)
pub struct MyStruct<const N: usize> {
    data: [usize; N],
}

impl<const N: usize> MyStruct<N> {
    pub fn new() -> MyStruct<N> {
        return MyStruct {
            data: unsafe { std::mem::uninitialized() },
        };
    }

    pub fn test_generic(&self) {
        println!("There are {} elements!", N);
    }
}

impl MyStruct<3> {
    pub fn test_three(&self) {
        println!("There are 3 elements! Do something cool with this special case!");
    }
}

I would expect it to produce the same output as the playground link above, however it produces the following:

$ cargo  nightly run
Compiling test_lib v0.1.0 (/home/sean/Documents/Projects/rust_test/lib/test_lib)
Compiling rust_test v0.1.0 (/home/sean/Documents/Projects/rust_test)

thread 'rustc' has overflowed its stack
fatal runtime error: stack overflow
error: could not compile `rust_test`.

Caused by:
process didn't exit successfully: `rustc --crate-name rust_test --edition=2018 src/main.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=9b6d50326f9f810d -C extra-filename=-9b6d50326f9f810d --out-dir /home/sean/Documents/Projects/rust_test/target/debug/deps -C incremental=/home/sean/Documents/Projects/rust_test/target/debug/incremental -L dependency=/home/sean/Documents/Projects/rust_test/target/debug/deps --extern test_lib=/home/sean/Documents/Projects/rust_test/target/debug/deps/libtest_lib-36dbf7eb695dbec6.rlib` (signal: 6, SIGABRT: process abort signal)

Meta

$ cargo  nightly rustc -- --version --verbose
rustc 1.42.0-nightly (72b2bd55e 2020-01-09)
binary: rustc
commit-hash: 72b2bd55edbb1e63a930c5ddd08b25e4f9044786
commit-date: 2020-01-09
host: x86_64-unknown-linux-gnu
release: 1.42.0-nightly
LLVM version: 9.0
@varkor varkor added A-const-generics Area: const generics (parameters and arguments) C-bug Category: This is a bug. I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 10, 2020
@Centril Centril added F-const_generics `#![feature(const_generics)]` requires-nightly This issue requires a nightly compiler in some way. labels Jan 11, 2020
@Alexendoo Alexendoo added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Apr 22, 2020
@Alexendoo
Copy link
Member

A bisect shows this stopped crashing after #68118, marked as needing a test

@akofke
Copy link

akofke commented Apr 29, 2020

I was still encountering this in my code with rustc 1.44.0-nightly (7f3df5772 2020-04-16) and after trying to minimize it seems that the original example is fixed but the stack overflow still occurs when the const expression in the impl has braces around it. After experimenting I found some more examples of what works and what doesn't:

(see https://github.com/akofke/const-generics-stackoverflow-test to try it)

// lib.rs
#![feature(const_generics)]

pub struct Num<const N: usize>;

impl<const N: usize> Num<N> {
    pub fn foo(&self) {
        println!("any N");
    }

    pub fn new() -> Self {
        Self
    }
}

// Calling this method in separate crate now works 
// (verified that it crashes in earlier nightlies)
impl Num<3> {
    pub fn three(&self) {
        println!("I'm a 3");
    }
}

// Braces around const expression causes crash
impl Num<{4}> {
    pub fn four(&self) {
        println!("I'm a 4");
    }
}

// Another impl this time without the braces is fine
impl Num<4> {
    pub fn also_four(&self) {
        println!("still a 4");
    }
}

const FIVE: usize = 5;

// Using a const even without braces crashes
impl Num<FIVE> {
    pub fn five(&self) {
        println!("I'm a 5");
    }
}

// Using a type alias with braces then an impl on that is fine
pub type Num6 = Num<{6}>;

impl Num6 {
    pub fn six(&self) {
        println!("Type-aliased 6");
    }
}
// bin/okay.rs
use mycrate::Num;
use mycrate::Num6;

pub fn main() {
    let n = Num::<3>;
    n.foo();
    n.three();

    let n = Num::<1>;
    n.foo();

    let n = Num::<4>;
    n.foo();
    n.also_four();

    let n = Num::<5>;
    n.foo();

    let n = Num6::new();
    n.six();
}
// bin/crash.rs
use mycrate::Num;

pub fn main() {
    let n = Num::<4>;
    n.four();
}
// bin/crash2.rs
use mycrate::Num;

pub fn main() {
    let n = Num::<5>;
    n.five();
}

cargo run --bin okay compiles and runs successfully while cargo run --bin crash and crash2 (or cargo check) crash the compiler with the same error as the original:

thread 'rustc' has overflowed its stack
fatal runtime error: stack overflow
error: could not compile `mycrate`.

Caused by:
  process didn't exit successfully: `rustc --crate-name crash2 --edition=2018 src/bin/crash2.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=20e32ce06e01ca6b -C extra-filename=-20e32ce06e01ca6b --out-dir /Users/alex/workspace/const-generics-stackoverflow-test/target/debug/deps -C incremental=/Users/alex/workspace/const-generics-stackoverflow-test/target/debug/incremental -L dependency=/Users/alex/workspace/const-generics-stackoverflow-test/target/debug/deps --extern mycrate=/Users/alex/workspace/const-generics-stackoverflow-test/target/debug/deps/libmycrate-8f49fe4478012ba3.rlib` (signal: 6, SIGABRT: process abort signal)

Meta

rustc --version --verbose:

rustc 1.44.0-nightly (7f3df5772 2020-04-16)
binary: rustc
commit-hash: 7f3df5772439eee1c512ed2eb540beef1124d236
commit-date: 2020-04-16
host: x86_64-apple-darwin
release: 1.44.0-nightly
LLVM version: 9.0

Aaron1011 added a commit to Aaron1011/rust that referenced this issue May 19, 2020
Fixes rust-lang#68104

When printing the DefPath for an unevaluated const, we may end up trying
to print the same const again. To avoid infinite recursion, we use the
'verbose' format when we try to re-entrantly print a const. This ensures
that the pretty-printing process will always terminate.
Manishearth added a commit to Manishearth/rust that referenced this issue Jun 20, 2020
…r=varkor

Properly encode AnonConst into crate metadata

Fixes rust-lang#68104

Previous, we were encoding AnonConst as a regular Const, causing us to
treat them differently after being deserialized in another compilation
session.
RalfJung added a commit to RalfJung/rust that referenced this issue Jun 20, 2020
…r=varkor

Properly encode AnonConst into crate metadata

Fixes rust-lang#68104

Previous, we were encoding AnonConst as a regular Const, causing us to
treat them differently after being deserialized in another compilation
session.
RalfJung added a commit to RalfJung/rust that referenced this issue Jun 20, 2020
…r=varkor

Properly encode AnonConst into crate metadata

Fixes rust-lang#68104

Previous, we were encoding AnonConst as a regular Const, causing us to
treat them differently after being deserialized in another compilation
session.
RalfJung added a commit to RalfJung/rust that referenced this issue Jun 20, 2020
…r=varkor

Properly encode AnonConst into crate metadata

Fixes rust-lang#68104

Previous, we were encoding AnonConst as a regular Const, causing us to
treat them differently after being deserialized in another compilation
session.
@bors bors closed this as completed in ebe5a91 Jun 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-generics Area: const generics (parameters and arguments) C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. F-const_generics `#![feature(const_generics)]` I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
5 participants