Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.

Legal values for a Box<T> where T is zero-sized #3

Closed
nikomatsakis opened this issue Aug 16, 2016 · 18 comments
Closed

Legal values for a Box<T> where T is zero-sized #3

nikomatsakis opened this issue Aug 16, 2016 · 18 comments

Comments

@nikomatsakis
Copy link
Contributor

On IRC, @mystor asked me whether, given some zero-sized type ZST, it would be legal to represent Box<ZST> using some arbitrary non-null value, or must it be a particular value (e.g., 1)?

For example, if there is a pointer p to some stack variable, can one write unsafe { Box::from_raw(p as *mut ZST) } and have that be a legal box?

@mystor
Copy link

mystor commented Aug 16, 2016

If this is a legal box as well, is it safe to assume that the compiler will never decide to not propagate the given value, and just give 1 whenever asked for what the pointer value is for a boxed zero sized type?

@Amanieu
Copy link
Member

Amanieu commented Aug 16, 2016

I'm of the opinion that Box::from_raw should only ever be called with a pointer allocated by Box::new.

Conceptually, we are allocating a zero-sized block of memory (which may have malloc headers, so the real allocation may be bigger than zero), and we should use the same pointer value when we are freeing it.

The fact that Box::new currently always returns a pointer value of 1 is an implementation detail: it's just an optimization to avoid allocating zero-sized blocks of memory.

@arielb1
Copy link
Contributor

arielb1 commented Aug 17, 2016

My preference is that any non-null value is a valid pointer to a 0-sized type.

Also, for things to make sense, all reborrows would have to prefer that value, e.g.:

struct Foo((), ());

fn addr_of<T>(t: &T) -> usize { t as *const T as usize }
fn main() {
    let foo = unsafe { &*(0x1337 as *const Foo) };
    assert_eq!(addr_of(foo), 0x1337);
    assert_eq!(addr_of(&*foo), 0x1337);
    assert_eq!(addr_of(&foo.0), 0x1337);
    assert_eq!(addr_of(&foo.1), 0x1337);
}

@strega-nil
Copy link

strega-nil commented Aug 17, 2016

@arielb1 I don't think so. &foo is it's own object, not a ZST.

edit: nvm :P

@arielb1
Copy link
Contributor

arielb1 commented Aug 17, 2016

Whatever is legal for Box is more of a library issue. I think programmers want to rely on Box::<ZST>::new() being copyable and being a "const fn".

@strega-nil
Copy link

strega-nil commented Aug 17, 2016

@arielb1 I... don't know if I agree? I think we should be able to say that Box::<Zst>::new() may allocate, just like malloc(0). It might, for example, do some debug info thing.

@arielb1
Copy link
Contributor

arielb1 commented Aug 17, 2016

That would be a breaking change.

@strega-nil
Copy link

strega-nil commented Aug 17, 2016

Is it? Do we ever say that we won't allocate? I guess it is literally a breaking change... but have we made promises in this area?

@arielb1
Copy link
Contributor

arielb1 commented Aug 17, 2016

A breaking change is a change that breaks user code. If we're going around making these kinds of breaking changes we might as well reverse struct drop order and be done with it.

@strega-nil
Copy link

@arielb1 well... I just need to write that RFC :)

@nikomatsakis
Copy link
Contributor Author

So really there are two questions here that should be separated out. One is what is legal in terms of Box<T> (for any T, but especially ZST), and the other is what about pointers to unsized types. I am going to rename this issue heading to be specific to Box<T> and then open a second for the more general question, I guess. Then I'll give my two cents. =)

@nikomatsakis nikomatsakis changed the title Legal values for a pointer to a zero-sized type Legal values for a Box<T> where T is zero-sized Aug 18, 2016
@nikomatsakis
Copy link
Contributor Author

My feeling is that Box should be relatively transparent -- for example, I would tie its value specifically to the default allocator, and document the behavior for ZST. So, if the type is ZST, then we do not invoke free -- otherwise, the pointer can be any pointer which can legally be given to the default allocator to be freed (and which is not aliased).

This would mean that, for example, it may well be legal to take a *T from C which was allocated with malloc and convert it to a Box, as long as you know that the default allocator is C malloc. This seems like a useful bit of interop, if perhaps not the most recommended pattern (because it limits portability).

(Also, of course, if Box becomes customizable with respect to its allocator, then basically all of this logic still stands, but with respect to the allocator for that given box.)

@gnzlbg
Copy link

gnzlbg commented May 28, 2018

Should reading from a pointer that is not properly aligned have an exception for ZSTs or should that be undefined behavior?

Depending on the answer to that question, Box<T> and &T might be able to take any non-zero values when T is a ZST, or they might only take certain values (multiples of the alignment).

FWIW the current implementation returns mem::align_of::<T>(), such that the pointer behind the Box is always properly aligned.

EDIT: @rkruppe mentioned [T; 0] and #[repr(align(N))] struct T() as ZSTs where the difference might matter.


Also related to #10

@RalfJung
Copy link
Member

RalfJung commented May 30, 2018

Should reading from a pointer that is not properly aligned have an exception for ZSTs or should that be undefined behavior?

For references and Box, not just reading is UB when the pointer is unaligned. Even just creating an unaligned reference is UB. I see no reason why ZSTs should get an exception there? We still emit that annotation to LLVM and we still may want to do enum layout optimizations.

@gnzlbg
Copy link

gnzlbg commented May 30, 2018

That makes sense, for me that settles it. Is there a memory model document anywhere where these kind of things are actually written to?

@RalfJung
Copy link
Member

No :( We haven't really started writing down anything yet... or even found a good structure to put stuff in. I hope to be able to work on that some more this summer.

@RalfJung
Copy link
Member

This is now being discussed as part of the validity invariants: rust-lang/unsafe-code-guidelines#145.

@Lucretiel
Copy link

Er- it looks like the linked thread doesn't talk at all about Boxing ZSTs, unless I'm missing something?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants