[swift-dev] [swift-users] Guarantees of Tuples as Fixed Sized (stack allocated) Arrays

Johannes Weiss johannesweiss at apple.com
Fri Apr 28 08:31:05 CDT 2017


Hi Martin,

> On 28 Apr 2017, at 12:59, Martin R <martinr448 at gmail.com> wrote:
> 
> As far as I know, the only guarantee is made for structures imported from C. From https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160516/001968.html :
> 
>    Swift structs have unspecified layout. If you depend on a specific layout, you should define the struct in C and import it into Swift for now.

Thanks! Yes I know structs have unspecified layout but that doesn't necessarily mean tuples have that too. Especially if you consider that C structs having fixed sized arrays in them get imported as tuples. See for example this:

typedef struct __siginfo {
	int	si_signo;		/* signal number */
	int	si_errno;		/* errno association */
	int	si_code;		/* signal code */
	pid_t	si_pid;			/* sending process */
	uid_t	si_uid;			/* sender's ruid */
	int	si_status;		/* exit value */
	void	*si_addr;		/* faulting instruction */
	union sigval si_value;		/* signal value */
	long	si_band;		/* band event for SIGPOLL */
	unsigned long	__pad[7];	/* Reserved for Future Use */
} siginfo_t;

gets imported as

--- SNIP ---
public struct __siginfo {
    public var si_signo: Int32 /* signal number */
    public var si_errno: Int32 /* errno association */
    public var si_code: Int32 /* signal code */
    public var si_pid: pid_t /* sending process */
    public var si_uid: uid_t /* sender's ruid */
    public var si_status: Int32 /* exit value */
    public var si_addr: UnsafeMutableRawPointer! /* faulting instruction */
    public var si_value: sigval /* signal value */
    public var si_band: Int /* band event for SIGPOLL */
    public var __pad: (UInt, UInt, UInt, UInt, UInt, UInt, UInt) /* Reserved for Future Use */
    public init()
    public init(si_signo: Int32, si_errno: Int32, si_code: Int32, si_pid: pid_t, si_uid: uid_t, si_status: Int32, si_addr: UnsafeMutableRawPointer!, si_value: sigval, si_band: Int, __pad: (UInt, UInt, UInt, UInt, UInt, UInt, UInt))
}
public typealias siginfo_t = __siginfo
--- SNAP ---

So the `unsigned long __pad[7]` becomes `public var __pad: (UInt, UInt, UInt, UInt, UInt, UInt, UInt)`

and given that, this should really be legal, right?

var random_uint_7_tuple: (UInt, UInt, UInt, UInt, UInt, UInt, UInt) = (1, 2, 3, 4, 5, 6, 7)
var si = siginfo_t()
_ = withUnsafeMutablePointer(to: &si.__pad) { (si_pad_ptr) -> Void in
    return withUnsafeMutablePointer(to: &random_uint_7_tuple) { (tuple_ptr) -> Void in
        si_pad_ptr.assign(from: tuple_ptr, count: 1)
    }
}

from this example I believe we can see that the Swift values of type `(UInt, UInt, UInt, UInt, UInt, UInt, UInt)` MUST have the same layout as `unsigned long[7]` imported from C, right? withUnsafeMutablePointer() to those values gives me a UnsafeMutablePointer<(UInt, UInt, UInt, UInt, UInt, UInt, UInt)> in both cases.

That's why I think inducing unspecified tuple layout from unspecified struct layout doesn't work. Structs are nominal types (so I can't recreate the same type that was imported from C in pure Swift. Tuples on the other hand are structural types which means I am able to recreate the same type that is imported from C in Swift (which is what I did in the example above).

Does that make sense?

Cheers,
  Johannes





> 
> and from https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160516/001980.html :
> 
>    That's not necessary. You can leave the struct defined in C and import it into Swift. Swift will respect C's layout.
> 
> Regards, Martin
> 
> 
>> On 28. Apr 2017, at 13:03, Johannes Weiss via swift-users <swift-users at swift.org> wrote:
>> 
>> Hi swift-users,
>> 
>> (sorry for the cross post to swift-dev, but wasn't sure where it belongs)
>> 
>> I tried to find guarantees about the memory layout Swift tuples but couldn't find anything. The reason I ask is because I'd like to use them as fixed sized (stack allocated) arrays. I'm pretty sure they're actually not guaranteed to be stack allocated but highly likely I assume :).
>> 
>> Am I correct in assuming that
>> 
>>    let swift_events: (kevent, kevent) = ...
>> 
>> has the same memory layout as
>> 
>>    struct kevent c_events[2] = ...
>> 
>> ? In other words, is this legal:
>> 
>>    var events = (kevent(), kevent())
>>    withUnsafeMutableBytes(of: &events) { event_ptr in
>>        precondition(MemoryLayout<kevent>.size * 2 == event_ptr.count)
>>        if let ptr = event_ptr.baseAddress?.bindMemory(to: kevent.self, capacity: 2) {
>>            return kevent(someFileDescriptor, ptr, 2, ptr, 2, nil)
>>        }
>>    }
>> 
>> I'm assuming yes but I'd like to make sure.
>> 
>> Many thanks,
>>  Johannes
>> 
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
> 



More information about the swift-dev mailing list