When MemoryLayout meets Existential Types: one unnecessarily strangles another

Suppose I use a class to handle buffers like this:

public class BufRef<T> where T: P {
    typealias DataType = T
    let buf : UnsafeMutablePointer<DataType>
    let m : Int
    let ldm : Int
    let n : Int
    init(_ m: Int, _ n: Int) { /*allocate buffer*/ }
    deinit { buf.deallocate() }
}

protocol P {}
extension Float : P {}
extension Float16 : P {}

There’s no way to refer to DataType in a MemoryLayout<T>.stride-style struct…

func printstride(buffer: BufRef<some P>) {
    // No no. Compiler chokes.
    let stride = MemoryLayout<type(of:bufref).DataType>.stride
    print(stride)
}

func printstride2(buffer: BufRef<some P>) {
    // Compiler passes alright, but seriously??? Outputs 8 for T=Float16
    let stride = MemoryLayout.stride(ofValue: type(of:bufref).DataType)
    print(stride)
}

Have to write func printstride<T>(buffer: BufRef<T>) where T: P, which is rather sad.

You used some P syntax to avoid giving a name to a type parameter, but in the body of the function you need a fully-named type that uses that type parameter. So give it a name:

func printstride<T: P>(buffer: BufRef<T>) {
    let stride = MemoryLayout<BufRef<T>.DataType>.stride
    print(stride)
}
1 Like

You're asking for the stride of a value, but the value you're passing in is a type. When you use a type as a value in Swift, you get a value whose type is a metatype, which is represented as a pointer at runtime, so it will always report a size of 8 bytes.

To make this clear, here is a reduced example that demonstrates this behavior without involving type(of:) or generics:

typealias A = (Int, Int, Int, Int)

print(MemoryLayout<A>.stride)   // 32
print(MemoryLayout.stride(ofValue: A.self))  // 8
print(MemoryLayout<A.Type>.stride)  // same result as above; also 8
5 Likes