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

Segfault when returning a boxed struct as a trait object from a match #20097

Closed
shepmaster opened this issue Dec 21, 2014 · 7 comments
Closed
Labels
A-codegen Area: Code generation I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.

Comments

@shepmaster
Copy link
Member

trait Thing {
    fn hello(&self) -> uint;
}

struct Thing1 { name: String }
impl Thing for Thing1 {
    fn hello(&self) -> uint { 1 }
}

struct Thing2 { name: String }
impl Thing for Thing2 {
    fn hello(&self) -> uint { 2 }
}

fn main() {
    let t: Box<Thing> = match true {
        true => box Thing1 { name: "hello".to_string() },
        _    => box Thing2 { name: "hello".to_string() },
    };
    println!("{}", t.hello());
}

Running this code yields various failures (SIGSEGV, "Illegal instruction: 4"):

$ rustc s.rs
# Warnings snipped
$ ./s
Illegal instruction: 4

LLDB has this to say:

(lldb) bt
* thread #1: tid = 0x821935, 0x0000000100003358 s`Box$LT$Thing$GT$::glue_drop.1635::ha5cee14c6e595b4f + 72, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x0000000100003358 s`Box$LT$Thing$GT$::glue_drop.1635::ha5cee14c6e595b4f + 72
    frame #1: 0x00000001000011f8 s`main::hb72fec937d964a29Zaa + 680
    frame #2: 0x000000010000f2d9 s`rust_try_inner + 9
    frame #3: 0x000000010000f2c6 s`rust_try + 6
    frame #4: 0x000000010000e1df s`rt::lang_start::h270935babb5b337fRCz + 831
    frame #5: 0x00000001000013bf s`main + 79
    frame #6: 0x00007fff92d9b5c9 libdyld.dylib`start + 1

and I'm reasonably up-to-date:

$ rustc --version=verbose
rustc 0.13.0-dev (1c2df5cc3 2014-12-20 01:12:19 +0000)
binary: rustc
commit-hash: 1c2df5cc3cfc0c9e80adf9fa6504d55056741c5a
commit-date: 2014-12-20 01:12:19 +0000
host: x86_64-apple-darwin
release: 0.13.0-dev
@alexcrichton
Copy link
Member

cc @nick29581, @luqmana

@shepmaster
Copy link
Member Author

I forgot to add that by explicitly casting, the crash goes away:

let t: Box<Thing> = match true {
    true => box Thing1 { name: "hello".to_string() } as Box<Thing>,
    _    => box Thing2 { name: "hello".to_string() } as Box<Thing>,
};

@jkleint
Copy link

jkleint commented Dec 22, 2014

+1, just ran into this same issue with today's nightly on Linux, with almost the exact same code and workaround.

(I'm a little jealous, my --version "does not take an argument" though... )

$ rustc -v --version
rustc 0.13.0-nightly (cc19e3380 2014-12-20 20:00:36 +0000)
binary: rustc
commit-hash: cc19e3380b4b7c63b6f1f79d1dfc213ea00e16cf
commit-date: 2014-12-20 20:00:36 +0000
host: x86_64-unknown-linux-gnu
release: 0.13.0-nightly

@carllerche
Copy link
Member

I believe that I am hitting this issue as well. Here is my repro:

#![allow(dead_code)]

pub trait Foo : Send {
    fn foo<'a>(me: Managed<'a, Self>);
}

pub trait BoxedFoo : Send {
    fn foo_boxed(self: Box<Self>);
}

pub trait ToFoo<F: Foo> {
    fn to_box(self) -> Box<BoxedFoo>;
}

/*
 *
 * ===== impl Foo for String =====
 *
 */

impl Foo for String {
    fn foo<'a>(me: Managed<'a, Self>) {
        println!("foo: {}", me.take());
    }
}

impl<F: Foo> BoxedFoo for F {
    fn foo_boxed(self: Box<Self>) {
        Foo::foo(Managed::heap(self));
    }
}

impl<F: Foo> ToFoo<F> for F {
    fn to_box(self) -> Box<BoxedFoo> {
        box self
    }
}

impl<'a, F: Foo> ToFoo<F> for Managed<'a, F> {
    fn to_box(self) -> Box<BoxedFoo> {
        match self.store {
            Store::Heap(boxed) => boxed,
            Store::Stack(opt) => box opt.take().unwrap(),
        }
    }
}

/*
 *
 * ===== Managed =====
 *
 */

pub struct Managed<'a, T:'a> {
    store: Store<'a, T>,
}

impl<'a, T> Managed<'a, T> {
    fn heap(val: Box<T>) -> Managed<'a, T> {
        Managed { store: Store::Heap(val) }
    }

    fn take(self) -> T {
        match self.store {
            Store::Heap(boxed) => *boxed,
            Store::Stack(opt) => opt.take().expect("option is none"),
        }
    }
}

enum Store<'a, T:'a> {
    Heap(Box<T>), // Callback is already heap allocated
    Stack(&'a mut Option<T>), // Callback is stack allocated
}

fn do_foo<T: ToFoo<F>, F: Foo>(f: T) {
    f.to_box().foo_boxed();
}

pub fn main() {
    do_foo(Managed::heap(box "foo".to_string()));
}

Backtrace:

Process 14641 launched: './segv' (x86_64)
Process 14641 stopped
* thread #1: tid = 0x1c03, 0x0000000100002e98 segv`Box$LT$BoxedFoo$u{20}$u{2b}$u{20}$u{27}static$GT$::glue_drop.1583::h37e40b03ca5bab2f + 72, stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    frame #0: 0x0000000100002e98 segv`Box$LT$BoxedFoo$u{20}$u{2b}$u{20}$u{27}static$GT$::glue_drop.1583::h37e40b03ca5bab2f + 72
segv`Box$LT$BoxedFoo$u{20}$u{2b}$u{20}$u{27}static$GT$::glue_drop.1583::h37e40b03ca5bab2f + 72:
-> 0x100002e98:  movq   (%rcx), %rcx
   0x100002e9b:  movq   %rax, %rdi
   0x100002e9e:  callq  *%rcx
   0x100002ea0:  jmp    0x100002e8a               ; Box$LT$BoxedFoo$u{20}$u{2b}$u{20}$u{27}static$GT$::glue_drop.1583::h37e40b03ca5bab2f + 58

Rustc:

$ rustc --version
rustc 0.13.0-nightly (fc2ba1393 2015-01-03 05:35:17 +0000)

@kmcallister kmcallister added A-codegen Area: Code generation I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. labels Jan 16, 2015
@kevinmehall
Copy link
Contributor

Another workaround is to put the body of the match arm in {}. I also noticed that this bug doesn't affect the last arm of the match.

#![feature(box_syntax)]
struct S1(u32);
trait T {}
impl T for S1 {}

#[cfg(broken)]
fn tst(foo: u8) -> Box<T + 'static> {
    match foo {
        0 => box S1(5),
        _ => box S1(6),
    }
}

#[cfg(works1)]
fn tst(foo: u8) -> Box<T + 'static> {
    match foo{
        0 => { box S1(5) },
        _ => { box S1(6) },
    }
}

#[cfg(works2)]
fn tst(foo: u8) -> Box<T + 'static> {
    match foo{
        0 => box S1(5) as Box<T + 'static>,
        _ => box S1(6) as Box<T + 'static>,
    }
}

fn main() {
    tst(0);
}

@kevinmehall
Copy link
Contributor

I think this is a duplicate of #21695, and thus @pnkfelix fixed it in #21692.
In any case, my example above no longer crashes on rustc 1.0.0-nightly (522d09dfe 2015-02-19)

@shepmaster
Copy link
Member Author

@kevinmehall I think that #21695 is a duplicate of this issue. 😈

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.
Projects
None yet
Development

No branches or pull requests

6 participants