Freeze
is a lang-item marker auto-trait that indicates that a type does not contain any shared mutability (UnsafeCell
) before indirection.
This property is observable on stable (and has been for a long while) even though the trait is unstable; it is only allowed to take a reference to a place in const
context if that place was already behind a reference or has a Freeze
type (feature(const_refs_to_cell)
).
For a #[repr(packed)]
type, the built-in #[derive]
work by copying from the fields, since it's unsafe to reference an underaligned field. However, since the field is copied to the stack, if it were to contain any shared mutability, any writes from the delegated to trait implementation would get silently discarded.
This scenario is impossible so long as a type being Copy
also requires it to be Freeze
. UnsafeCell
is not Copy
, thus implementing Copy
will indirectly require Freeze
. However, I recall discussion saying that Cell<impl Copy>
could potentially be made to impl Copy
, it would just be a bit of a footgun similar to iterators, mutating a copy but expecting to change the original source as well.
I think it could be prudent to just make Freeze
into a supertrait of Copy
(and thus finalize that Cell
must never be Copy
). This turns an informal expectation into a stable guarantee that can then be relied upon.
A Copy + !Freeze
field doesn't make packed derives unsound, since no unsafe
is involved, but it would certainly be surprising; especially so if the derive ever chooses to bypass the copy for fields that are already guaranteed to be sufficiently aligned.