We generally prefer to avoid polluting clients' namespaces with top-level typenames for auxiliary types. We have struct Dictionary.Index, not struct DictionaryIndex; we have struct String.Iterator, not struct StringIterator.
On the other hand, Int8, UInt8 and Bool ought to all share the same atomic representation (i.e., Int8.AtomicRepresentation == UInt8.AtomicRepresentation should hold true), and introducing a top-level AtomicStorage8 would let us have a nice dedicated name for it, instead having to define it under one of these types.
An interesting analog is Duration: it is a top-level name for an associated type that is shared across multiple Clock types. Superficially, this seems like it would make a good argument for defining top-level storage types.
One important difference is that unlike Duration, the atomic storage types will not be truly standalone things: each storage will be tightly coupled to one particular stdlib type. In the current pitch the storage types provide no public members, but I think this is overly restrictive: to allow clients to create custom AtomicValue conformances, we'll want to provide public ways to convert values to/from atomic storage values:
@_alignment(1)
@frozen public struct AtomicStorage8: AtomicStorage {
public init(_ value: UInt8) { ... }
public consuming func dispose() -> UInt8 { ... }
...
}
(The API details aren't interesting. Perhaps we'll prefer to have public static func encode/decode methods to align closer to AtomicValue.)
Note how we'll need to select a specific representative type to convert from/to -- it's highly unlikely we'd want these to be generic member functions (and it's unclear what constraints they would use anyway).
Given that we'll have such tight coupling between, say, the 8-bit atomic representation and UInt8, I think it makes sense to nest the storage under the UInt8 type rather than defining it a standalone top-level thing.
Int8 and Bool would semantically reuse UInt8's atomic representation rather than defining their own, so it makes sense that their AtomicRepresentations would be typealiases pointing to UInt8.AtomicRepresentation.
Is there a particular reason such a hypothetical #if canImport(struct Synchronization.AtomicStorage128) syntax couldn't also support testing for nested types, as in #if canImport(struct DoubleWord.AtomicRepresentation)?