Change:
I this to get DST landed. For types I use a fat pointer. If the vector is empy I use null for the pointer, so when this deallocates via drop glue jemalloc segfaults. It should be fairly easy to do a zero sized allocation or something and use that value and then we can remove the null check. That also allows us to use the Option to non-null pointer optimisation. For now I just want to get this branch landed though. cc
FWIW, as the sentinel value for this sort of situation (to ensure that still works, since that uses the "non-null" pointer as the discriminant).
It seems that landed the non-null pointer optimization for slices so this shouldn't be required anymore. Can it be closed?
This ended up landing as part of the initial implementation, but I don't think it is actually accomplishing anything.

Discussion:
// FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias` //                and `nonnull` <del> use core::ptr::RawPtr; </del> #[cfg(not(test))] use core::raw; #[cfg(stage0, not(test))] use util;

/// the value returned by `usable_size` for the requested size. #[inline] pub unsafe fn deallocate(ptr: *mut u8, size: uint, align: uint) { <del> // FIXME(14395) This is only required for DST ~[T], it should be removed once // we fix that representation to not use null pointers. if ptr.is_null() { return; } </del> imp::deallocate(ptr, size, align) }

let vec_ty = ty::mk_uniq(tcx, ty::mk_vec(tcx, unit_ty, None)); let scratch = rvalue_scratch_datum(bcx, vec_ty, "__unsize_unique"); <del> if len == 0 { Store(bcx, C_null(type_of::type_of(bcx.ccx(), unit_ty).ptr_to()), get_dataptr(bcx, scratch.val)); } else { // Box<[(), ..n]> will not allocate, but ~[()] expects an // allocation of n bytes, so we must allocate here (yuck). let llty = type_of::type_of(bcx.ccx(), unit_ty); if llsize_of_alloc(bcx.ccx(), llty) == 0 { let ptr_unit_ty = type_of::type_of(bcx.ccx(), unit_ty).ptr_to(); let align = C_uint(bcx.ccx(), 8); let alloc_result = malloc_raw_dyn(bcx, ptr_unit_ty, vec_ty, ll_len, align); bcx = alloc_result.bcx; let base = get_dataptr(bcx, scratch.val); Store(bcx, alloc_result.val, base); } else { let base = get_dataptr(bcx, scratch.val); let base = PointerCast(bcx, base, type_of::type_of(bcx.ccx(), datum_ty).ptr_to()); bcx = lval.store_to(bcx, base); } } </del> <ins> let base = get_dataptr(bcx, scratch.val); let base = PointerCast(bcx, base, type_of::type_of(bcx.ccx(), datum_ty).ptr_to()); bcx = lval.store_to(bcx, base); </ins> Store(bcx, ll_len, get_len(bcx, scratch.val)); DatumBlock::new(bcx, scratch.to_expr_datum())

}; if should_deallocate { <del> let not_null = IsNotNull(bcx, dataptr); with_cond(bcx, not_null, |bcx| { let llty = type_of::type_of(ccx, unit_ty); let llsize = machine::llsize_of(ccx, llty); let llalign = C_uint(ccx, machine::llalign_of_min(ccx, llty) as uint); let size = Mul(bcx, llsize, get_len(bcx, vptr)); glue::trans_exchange_free_dyn(bcx, dataptr, size, llalign) }) </del> <ins> let llty = type_of::type_of(ccx, unit_ty); let unit_size = llsize_of_alloc(ccx, llty); if unit_size != 0 { let len = get_len(bcx, vptr); let not_empty = ICmp(bcx, llvm::IntNE, len, C_uint(ccx, 0)); with_cond(bcx, not_empty, |bcx| { let llalign = C_uint(ccx, machine::llalign_of_min(ccx, llty) as uint); let size = Mul(bcx, C_uint(ccx, unit_size as uint), len); glue::trans_exchange_free_dyn(bcx, dataptr, size, llalign) }) } else { bcx } </ins> } else { bcx }

pub fn main() { assert!(Some(box() ()).is_some()); <ins> let xs: Box<[()]> = box []; assert!(Some(xs).is_some()); </ins> struct Foo; assert!(Some(box Foo).is_some()); <ins> let ys: Box<[Foo]> = box []; assert!(Some(ys).is_some()); </ins> }