Pre-eRFC: Let's fix DSTs


#41
  1. No, the difference is the metadata for sizing is per type not per value, and this metadata can only be determined at runtime unlike a Sized type.

  2. It can be returned by value and assigned like a Sized type, but it can’t do anything that requires the size known at compile time.


#42

Have some thought about DST enum again.

The representation of *mut (dyn Trait) type is currently the 2-tuple (*mut (), &'static Vtable<dyn Trait>), where Vtable<_> is the struct:

#[repr(C)]
struct Vtable<D: TraitObject> {
    destroy: fn(*mut ()),
    size: usize,
    align: usize,
    methods: D::Methods,
}

IIUC @eddyb, what you mean is that for a DST enum *mut Option<dyn Trait>, it will be represented as a 3-tuple (*mut void, &'static Vtable<dyn Trait>, &'static EnumFields<1>), where EnumFields is the struct:

#[repr(C)]
struct EnumFields<const number_of_fields: usize> {
    discriminant: fn(*const ()) -> u64,
    field_offsets: [usize; number_of_fields],
}

As an alternative, I think it is possible to make *mut Option<dyn Trait> still a 2-tuple (*mut (), &'static Vtable<dyn Trait>) by implementing the entire niche-filling algorithm at runtime.

  1. Modify the Vtable<_> struct by encoding the entire rustc::ty::LayoutDetails:

    #[repr(C)]
    struct Vtable<D: TraitObject> {
        destroy: fn(*mut ()),
        size: usize,
        align: usize,
        layout_details: (Variants, FieldPlacement, Abi),  
        //^ new.
        // could store the minimal analyzed information just enough to 
        // compute the discriminant offset+size and field offsets
        methods: D::Methods,
    }
    
  2. The DynSized trait will add a layout_details_of_val method

    trait DynSized {
        fn layout_details_of_val(r: &Self) -> (Variants, FieldPlacement, Abi);
        //^ or maybe restrict to `Self::Meta`
        // I haven't checked the implication of taking `&Self` yet
    }
    
  3. Change mem::discriminant<T> to a runtime implementation of rustc_mir::interpret::EvalContext::read_discriminant_value, based on {layout_details,size,align}_of_val() of each variant of T.

  4. Similarly, the offsets in a variant is to be calculated at runtime using {layout_details,size,align}_of_val() of that variant.

We’d likely disallow customized implementation of layout_details_of_val() in a custom DST.

Cons of this approach:

  • The vtables will become one-word larger even if DST enum is never used.
  • Needs more space to store the layout details as well.
  • Duplicating logic between compile-time and runtime.

#43

IMO this requires too much reflection machinery up-front (even if you don’t need it), whereas my idea “just” monomorphizes a function like mem::discriminant.
It’s also more expensive to do a recursive search through the reflected layout just to call .is_some() or .as_ref().

It’d be nice to have a pointer-sized metadata even in this case, but I don’t know how we could reasonably do it efficiently or without reflecting the layout.