You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #31 identified this as an area for continued discussion. Specifically, if you have a struct with single field (struct Foo { x: T }), should we guarantee that the memory layout of Foo is identical to the memory layout of T (note that ABI details around function calls may still draw a distinction, which is why #[repr(transparent)] is needed). What about zero-sized types like PhantomData?
Long ago I proposed that we might want to guarantee (some subset of) newtype unpacking for repr(Rust) structs. @nikomatsakis carried this over into #11 as discussion point but it received no further discussion. I like to think that means it's uncontroversial 😄 I've also never heard of any reason why one might not want that to be true.
To make a specific proposal, let's restrict it to structs [1] that contain a single field having the same memory layout as the type of the sole field. So struct Foo<T>(T); and struct Foo<T> { x: T } would be laid out like T in memory, though possibly still passed differently in function calls.
[1] The same guarantee for (T,) are already covered by the special case of homogeneous tuples being laid out like arrays that is already in this PR.
I believe we should make this guarantee. I would further like to say that it applies to any struct with a single field of non-zero size (as determined b the other layout rules). I'm not sure if that is something we can always do, though. I'm thinking of complications around unsizing, for example.. do they potentially apply?
(In particular, might that force a larger alignment or some such thing?)
I am a big +1 on making newtyping a layout (but not ABI) no-op
Also I believe we should have a rule that layout operates as if types with (size: 0, align: 1) have been removed. (So PhantomData newtypes are also nops)
If we adopt what @gankro said, then this is indeed a subset of #36: After removing (size: 0, align: 1)-types, a "single-field struct" (according to the definition, having a single non-ZST field where I guess we mean ZST with alignment 1) is certainly homogeneous.
So if we say those are like an array, we'd get that single-field structs are like 1-element arrays. Should we guarantee that those are like their element type (except for ABI)? Seems sensible to me.
As far as I know the C standard does not technically guarantee that an array of one T has exactly the same size as T – padding between elements is ruled out but padding at the end isn't. I don't think any C implementation has ever done so, so we shouldn't worry about this hypothetical when we already break compatibility with actually-extant (and much saner) implementation options like CHAR_BIT > 8. So yes, it's a subset of #36. It would still be good to get consensus here though since #36 seems less of a clear-cut win to me.
Reacted by gnzlbg, Niko Matsakis and Eduard-Mihai Burtescu
Is there any particular reason why you think repr(transparent) is significant here? (I would indeed expect your assertion to pass, even if Transparent<T> weren't marked as repr(Transparent)).
Is there any particular reason why you think repr(transparent) is significant here? (I would indeed expect your assertion to pass, even if Transparent<T> weren't marked as repr(Transparent)).
Would the compiler be allowed to change the layout of types containing a #[repr(transparent)] type relative to the type it is wrapping?
As @gankro said, I think that this issue basically proposing the answer is "no" -- and further that #[repr(transparent)] is not necessary (that attribute only affects the function call ABI, not memory layout, afaik).
It seems to me there is a general consensus here that this is a good idea. I like the observation that this is a subset of #36, I had not realized that. But I agree with @rkruppe it's worth calling out on its own regardless.
@gnzlbg There is no central listing of all the knobs, but that seems like a good thing for the language reference. The structs section includes some discussion of struct-specific representation.
Could there be optimization cases where it'd be desirable to over-align a struct based on its usage? Say you have a Foo([u8; 3]) and a profile-based optimization pass recognizes that giving Foo an 8-byte alignment requirement would improve performance.
Activity
nikomatsakis commentedon Oct 11, 2018
I believe we should make this guarantee. I would further like to say that it applies to any struct with a single field of non-zero size (as determined b the other layout rules). I'm not sure if that is something we can always do, though. I'm thinking of complications around unsizing, for example.. do they potentially apply?
(In particular, might that force a larger alignment or some such thing?)
cc @eddyb @gankro
the8472 commentedon Oct 11, 2018
This would automatically fall out of #36, correct?
Gankra commentedon Oct 11, 2018
I am a big +1 on making newtyping a layout (but not ABI) no-op
Also I believe we should have a rule that layout operates as if types with (size: 0, align: 1) have been removed. (So PhantomData newtypes are also nops)
RalfJung commentedon Oct 13, 2018
If we adopt what @gankro said, then this is indeed a subset of #36: After removing (size: 0, align: 1)-types, a "single-field struct" (according to the definition, having a single non-ZST field where I guess we mean ZST with alignment 1) is certainly homogeneous.
So if we say those are like an array, we'd get that single-field structs are like 1-element arrays. Should we guarantee that those are like their element type (except for ABI)? Seems sensible to me.
hanna-kruppe commentedon Oct 13, 2018
As far as I know the C standard does not technically guarantee that an array of one
Thas exactly the same size asT– padding between elements is ruled out but padding at the end isn't. I don't think any C implementation has ever done so, so we shouldn't worry about this hypothetical when we already break compatibility with actually-extant (and much saner) implementation options likeCHAR_BIT > 8. So yes, it's a subset of #36. It would still be good to get consensus here though since #36 seems less of a clear-cut win to me.rodrimati1992 commentedon Oct 13, 2018
Would the compiler be allowed to change the layout of types containing a #[repr(transparent)] type relative to the type it is wrapping?
Would this (hypothetical) function return true for all
T?Edit:
I am aware that this is not safe for all type constructors since some types store a projection of their type parameters.
Eg:
Gankra commentedon Oct 14, 2018
Is there any particular reason why you think
repr(transparent)is significant here? (I would indeed expect your assertion to pass, even ifTransparent<T>weren't marked asrepr(Transparent)).rodrimati1992 commentedon Oct 14, 2018
That was just in case that it made a difference.
nikomatsakis commentedon Oct 18, 2018
There's been some interesting examples of why the "single-field layout guarantee" would be useful in rust-lang/rust#53514.
nikomatsakis commentedon Oct 18, 2018
@rodrimati1992
As @gankro said, I think that this issue basically proposing the answer is "no" -- and further that
#[repr(transparent)]is not necessary (that attribute only affects the function call ABI, not memory layout, afaik).nikomatsakis commentedon Oct 18, 2018
It seems to me there is a general consensus here that this is a good idea. I like the observation that this is a subset of #36, I had not realized that. But I agree with @rkruppe it's worth calling out on its own regardless.
gnzlbg commentedon Oct 19, 2018
offtopic: do we have a definition of
reprsomewhere? the attribute controls memory layout, function call abi, etc. I found this a bit confusing.nikomatsakis commentedon Oct 23, 2018
@gnzlbg There is no central listing of all the knobs, but that seems like a good thing for the language reference. The structs section includes some discussion of struct-specific representation.
kupiakos commentedon Jun 27, 2024
Could there be optimization cases where it'd be desirable to over-align a struct based on its usage? Say you have a
Foo([u8; 3])and a profile-based optimization pass recognizes that givingFooan 8-byte alignment requirement would improve performance.