Skip to content

DST/custom coercions #18598

@nrc

Description

@nrc
Member

Tracking issue for RFC rust-lang/rfcs#982.

Current state:

  • Implementation work (largely done)
    Stabilization -- the CoerceUnsized trait is unstable and we wish to revisit its design before stabilizing, so for now only stdlib types can be "unsized"

Things to consider prior to stabilization

Part of #18469

Activity

steveklabnik

steveklabnik commented on Jan 27, 2015

@steveklabnik
Member

I'm confused as to what this is about. isn't #18469 the tracking issue?

nrc

nrc commented on Jan 27, 2015

@nrc
MemberAuthor

Yes, this is a specific part of 18469. Seems useful to have it by itself since it stands alone (goes for the other issues too).

added
B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.
T-langRelevant to the language team
on Nov 29, 2016
eternaleye

eternaleye commented on Jan 14, 2017

@eternaleye
Contributor

Is there anything that could be done to push this towards stabilization? I have some APIs in mind that would benefit significantly from the ability to bound on the Unsize trait.

alexcrichton

alexcrichton commented on Feb 6, 2017

@alexcrichton
Member

I was idly wondering about this the other date with a type such as:

struct Foo {
    flag: bool,
    data: str,
}

Today there's not a huge amount of support for that but I noticed one oddity. So in theory the only way to instantiate such a type is to start with a concrete type, right? Let's say:

struct FooConcrete {
    flag: bool,
    data: [u8; 10],
}

I would personally expect, then that if we had a function like:

fn foo(drop_me: Box<Foo>) {
    drop(drop_me);
}

That function would simultaneously run the destructor for the underlying type and also free the memory. Currently, however, it only frees the memory and doesn't actually run any destructor. More interestingly this namely to me at least means that the Box<Foo> type needs two pieces of external data, both a vtable for the destructor but also a usize for the length of the dynamically sized data.

I'm not sure if this is just an unsupported use case perhaps? Or maybe support for this is planned elsewhere? Or maybe this isn't part of this issue at all?

In any case just wanted to bring it up!

arielb1

arielb1 commented on Feb 6, 2017

@arielb1
Contributor

What do you mean by the destructor not being called? I am quite sure it is:

use std::mem;

struct Foo {
    flag: bool,
    data: str,
}

struct FooConcrete {
    flag: bool,
    data: [u8; 10],
}

impl Drop for Foo {
    fn drop(&mut self) {
        println!("splat! {:?} {}", self.flag, &self.data)
    }
}


fn main() {
    let _foo: Box<Foo> = unsafe { mem::transmute(
        (Box::new(FooConcrete {
            flag: false,
            data: [0; 10]
        }), 10)
    )};
}

EDIT:

Code above is buggy, better version:

use std::mem;

#[repr(C)]
struct Foo {
    flag: bool,
    data: str,
}

#[repr(C)]
struct FooConcrete {
    flag: bool,
    data: [u8; 10],
}

impl Drop for Foo {
    fn drop(&mut self) {
        println!("splat! {:?} {}", self.flag, &self.data)
    }
}


fn main() {
    let _foo: Box<Foo> = unsafe { mem::transmute(
        (Box::new(FooConcrete {
            flag: false,
            data: [0; 10]
        }), 10usize)
    )};
}
alexcrichton

alexcrichton commented on Feb 6, 2017

@alexcrichton
Member

Oh I mean when you implement the destructor for FooConcrete instead of Foo, that one isn't run. (your example gives a segfault locally for me as well...)

I'm specifically thinking of something like this:

pub struct Foo {
    flag: bool,
    data: str,
}

#[no_mangle]
pub extern fn drop(_b: Box<Foo>) {
}

where the IR is:

; ModuleID = 'foo.cgu-0.rs'
source_filename = "foo.cgu-0.rs"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%Foo = type { i8, i8 }
%"unwind::libunwind::_Unwind_Exception" = type { i64, void (i32, %"unwind::libunwind::_Unwind_Exception"*)*, [6 x i64] }
%"unwind::libunwind::_Unwind_Context" = type {}

; Function Attrs: nounwind uwtable
define void @drop(%Foo* noalias nonnull, i64) unnamed_addr #0 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* @rust_eh
_personality {
entry-block:
  %2 = add i64 %1, 1
  %3 = icmp eq i64 %2, 0
  br i1 %3, label %_ZN4drop17hc3530aa457fb35acE.exit, label %cond.i

cond.i:                                           ; preds = %entry-block
  %4 = getelementptr inbounds %Foo, %Foo* %0, i64 0, i32 0
  tail call void @__rust_deallocate(i8* %4, i64 %2, i64 1) #1
  br label %_ZN4drop17hc3530aa457fb35acE.exit

_ZN4drop17hc3530aa457fb35acE.exit:                ; preds = %entry-block, %cond.i
  ret void
}

; Function Attrs: nounwind
declare void @__rust_deallocate(i8*, i64, i64) unnamed_addr #1

; Function Attrs: nounwind
declare i32 @rust_eh_personality(i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*) unnamed_addr #1

Notably there's some concrete type behind Box<Foo>, so presumably some dynamic call to a destructor needs to be made, but in this case it isn't :(

Stebalien

Stebalien commented on Feb 6, 2017

@Stebalien
Contributor

@alexcrichton transmuting Box::new((..., 10usize)) instead of Box::new((..., 10)) fixes the segfault (bug?).

Back on topic, Foo isn't a trait so I don't see why Box<Foo> should have a vtable; Box<Foo> is a concrete type (just an unsized one). After transmuting, Box<Foo> should (and does) forget it ever was a Box<FooConcrete>.

alexcrichton

alexcrichton commented on Feb 6, 2017

@alexcrichton
Member

@Stebalien ah yes that does indeed fix it!

For me at least I'd expect Box<Foo> here to still run the destructor of the underlying type. It's not a trait object, yeah, but it's still erasing the underlying type. Otherwise this'd end up leaking resources, and would practically not be too useful I'd imagine?

(that being said I'm still not sure of the practicality of the Foo type anyway)

51 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-DSTsArea: Dynamically-sized types (DSTs)A-coercionsArea: implicit and explicit `expr as Type` coercionsB-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCS-tracking-needs-summaryStatus: It's hard to tell what's been done and what hasn't! Someone should do some investigation.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @steveklabnik@alexcrichton@alexreg@eternaleye@joshtriplett

        Issue actions

          DST/custom coercions · Issue #18598 · rust-lang/rust