@frozen public
enum Node<Anchor>
{
@frozen public
enum Value
{
case anchor (Anchor)
case inline ((UInt, UInt, UInt32, UInt16, UInt8))
case raw ([UInt8])
}
case value (Value)
case leaf (UInt32, attributes:[Value])
case container (UInt32, attributes:[Value], content:[Self])
}
this really ought to fit in 3 words as long as Anchor doesn’t exceed 23 bytes. but swift lays it out so that it takes four words of storage:
print(MemoryLayout<Node<Int>>.stride)
32
if i specialize it manually, swift can pack it in 3 words of storage:
@frozen public
enum Node2
{
@frozen public
enum Value
{
case anchor (Int)
case inline ((UInt, UInt, UInt32, UInt16, UInt8))
case raw ([UInt8])
}
case value (Value)
case leaf (UInt32, attributes:[Value])
case container (UInt32, attributes:[Value], content:[Self])
}
Swift's generic layout model doesn't allow for overlapping storage involving types that have dynamic layout, so your generic version of Node<Anchor>.Value will always use an extra byte for the enum tag, because it has a case of the generic parameter type Anchor, as will the outer Node<Anchor> since it uses Value as a case, instead of being able to salvage spare bits from padding. You could make case anchor be indirect so that it's always represented as a refcounted pointer inline.
Out of curiosity, I have tried all three cases. Didn't see any difference; they all yield 32 (on Xcode 13.4 (13F17a)).
Summary
//
// EnumMemoryLayout.swift
// SwiftForums
//
// Created by ibex on 27/8/2022.
//
// [https://forums.swift.org/t/why-is-this-enum-s-memorylayout-so-bad/59891]
import Foundation
@main
struct EnumMemoryLayout {
static func main () async {
Original.show ()
Specialized.show ()
OriginalIndirect.show ()
}
}
enum Original {
static func show () {
print ("\(type(of: self))", MemoryLayout <Node <Int>>.stride)
}
enum Node <Anchor> {
@frozen public
enum Value {
case anchor (Anchor)
case inline ((UInt, UInt, UInt32, UInt16, UInt8))
case raw ([UInt8])
}
case value (Value)
case leaf (UInt32, attributes:[Value])
case container (UInt32, attributes:[Value], content:[Self])
}
}
enum Specialized {
static func show () {
print ("\(type(of: self))", MemoryLayout <Node>.stride)
}
enum Node {
@frozen public
enum Value {
case anchor (Int)
case inline ((UInt, UInt, UInt32, UInt16, UInt8))
case raw ([UInt8])
}
case value (Value)
case leaf (UInt32, attributes:[Value])
case container (UInt32, attributes:[Value], content:[Self])
}
}
enum OriginalIndirect {
static func show () {
print ("\(type(of: self))", MemoryLayout <Node <Int>>.stride)
}
enum Node <Anchor> {
@frozen public
enum Value {
indirect case anchor (Anchor)
case inline ((UInt, UInt, UInt32, UInt16, UInt8))
case raw ([UInt8])
}
case value (Value)
case leaf (UInt32, attributes:[Value])
case container (UInt32, attributes:[Value], content:[Self])
}
}