What is "binding" memory?

The "UnsafeRawPointer Migration" guide talks about "binding memory to a type“ as if that was a well known term. I have never heard of it yet though, and googling it returns no relevant results. I do not understand what binding memory is supposed to do.

The migration guide says "Binding uninitialized memory to a type prepares the memory to store values of that type“, but clearly raw memory does not need to be prepared (and cannot be) to hold any arbitrary type and value.

So what is this for, what does it actually do, and to whom is it done (the raw pointer, or the typed pointer which is returned, or the raw memory)?

Manfred

3 Likes

Hi Manfred,

At the top of the migration guide is a link to the memory model explanation:

"A memory location's bound type is an abstract, dynamic property of the memory used to formalize type safety.”

I’m not sure I like the “prepares the memory” language myself. Binding memory communicates to the compiler that the memory locations are safe for typed access. Nothing happens at runtime--until someone writes a type safety sanitizer. It affects the abstract state of the memory location, independent of the pointer variable used to access that memory. Binding memory returns a typed pointer for convenience and clarity, but there’s nothing special about that particular pointer value.

Initialized memory is always bound to some type. A rawpointer can be used to access that memory without knowing its bound type.

-Andy

···

On Nov 1, 2016, at 11:55 AM, Manfred Schubert via swift-users <swift-users@swift.org> wrote:

The "UnsafeRawPointer Migration" guide talks about "binding memory to a type“ as if that was a well known term. I have never heard of it yet though, and googling it returns no relevant results. I do not understand what binding memory is supposed to do.

The migration guide says "Binding uninitialized memory to a type prepares the memory to store values of that type“, but clearly raw memory does not need to be prepared (and cannot be) to hold any arbitrary type and value.

So what is this for, what does it actually do, and to whom is it done (the raw pointer, or the typed pointer which is returned, or the raw memory)?

Manfred

4 Likes

The "UnsafeRawPointer Migration" guide talks about "binding memory to a type“ as if that was a well known term. I have never heard of it yet though, and googling it returns no relevant results. I do not understand what binding memory is supposed to do.

The migration guide says "Binding uninitialized memory to a type prepares the memory to store values of that type“, but clearly raw memory does not need to be prepared (and cannot be) to hold any arbitrary type and value.

So what is this for, what does it actually do, and to whom is it done (the raw pointer, or the typed pointer which is returned, or the raw memory)?

This is more so a semantic distinction rather than some kind of physical operation. The memory is not altered, but all reads and writes to that memory location have to be through the “bound type”. If it’s “bound” to some type T, you must only read and write through values of type T, and not some unrelated type. This is in order to honor Swift's memory model while still exposing access to raw memory (see [1]), and to do so one must be careful about how they treat that memory so as to not violate “strict aliasing” (see [2]). This is similar to the notion of strict aliasing in C (see [3]).

[1] swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub
[2] swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub
[3] The Strict Aliasing Situation is Pretty Bad – Embedded in Academia

···

On Nov 1, 2016, at 11:55 AM, Manfred Schubert via swift-users <swift-users@swift.org> wrote:

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

The way I understand it, it prepares a memory structure that refers to the ‘raw memory’ such that it can be used to access the referenced memory according to the type ‘bound’ to.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: Swiftrien (Rien) · GitHub
Project: http://swiftfire.nl

···

On 01 Nov 2016, at 19:55, Manfred Schubert via swift-users <swift-users@swift.org> wrote:

The "UnsafeRawPointer Migration" guide talks about "binding memory to a type“ as if that was a well known term. I have never heard of it yet though, and googling it returns no relevant results. I do not understand what binding memory is supposed to do.

The migration guide says "Binding uninitialized memory to a type prepares the memory to store values of that type“, but clearly raw memory does not need to be prepared (and cannot be) to hold any arbitrary type and value.

So what is this for, what does it actually do, and to whom is it done (the raw pointer, or the typed pointer which is returned, or the raw memory)?

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

The "UnsafeRawPointer Migration" guide talks about "binding memory
to a type“ as if that was a well known term. I have never heard of
it yet though, and googling it returns no relevant results. I do not

understand what binding memory is supposed to do.

The migration guide says "Binding uninitialized memory to a type
prepares the memory to store values of that type“, but clearly raw
memory does not need to be prepared (and cannot be) to hold any
arbitrary type and value.

So what is this for, what does it actually do, and to whom is it done (the raw pointer, or the

typed pointer which is returned, or the raw memory)?

Manfred

Hi Manfred,

At the top of the migration guide is a link to the memory model explanation:
swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub

"A memory location's bound type is an abstract, dynamic property of the memory used to formalize
type safety.”

I’m not sure I like the “prepares the memory” language myself. Binding
memory communicates to the compiler that the memory locations are safe
for typed access. Nothing happens at runtime--until someone writes a
type safety sanitizer.

Well, that's a slight overstatement IMO. Sanitizers aside, the main
reason for these binding operations is that if you leave them out,
something different *will* happen at runtime... something that will make
your code do the wrong thing.

What I would say is that binding the memory has no immediate runtime
cost... but it's absolutely required if you want your program to behave
(and sometimes behaving correctly is a little slower than misbehaving).

···

on Tue Nov 01 2016, Andrew Trick <swift-users-AT-swift.org> wrote:

On Nov 1, 2016, at 11:55 AM, Manfred Schubert via swift-users <swift-users@swift.org> wrote:

It affects the abstract state of the memory location, independent of
the pointer variable used to access that memory. Binding memory
returns a typed pointer for convenience and clarity, but there’s
nothing special about that particular pointer value.

Initialized memory is always bound to some type. A rawpointer can be
used to access that memory without knowing its bound type.

-Andy
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
-Dave

I’m not sure I like the “prepares the memory” language myself. Binding memory communicates to the compiler that the memory locations are safe for typed access. Nothing happens at runtime--until someone writes a type safety sanitizer.

So nothing happens at runtime, and also nothing appears to happen at compile time. If I try this code:

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

It affects the abstract state of the memory location, independent of the pointer variable used to access that memory. Binding memory returns a typed pointer for convenience and clarity, but there’s nothing special about that particular pointer value.

If it were not returning a typed pointer, what would it actually do?

Manfred

···

Am 01.11.2016 um 21:40 schrieb Andrew Trick <atrick@apple.com>:

This is more so a semantic distinction rather than some kind of physical operation. The memory is not altered, but all reads and writes to that memory location have to be through the “bound type”. If it’s “bound” to some type T, you must only read and write through values of type T, and not some unrelated type.

So is „binding memory to a type“ like declaring the intent to the compiler that this memory is accessed as a certain type?

[1] swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub

Re-reading this, I guess I don’t even understand the need for memory being initialized and deinitialized. I understand that it is useful to have a defined initial value, but why is that a requirement for raw memory? If I receive raw memory from outside of Swift, would this already be „bound“ and „initialized“? Many raw data can be interpreted in multiple ways, so it cannot be bound to any particular type, but it is initialized with meaningful data already.
And what does deinitialize actually do? How could a row of Ints be deinitialized for example? Or is it zeroing the raw bytes of the memory?

Manfred

···

Am 01.11.2016 um 21:43 schrieb Michael Ilseman <milseman@apple.com>:

Good clarification. I really did not mean to imply that binding memory to a type has no effect on runtime behavior. Taken out of context, “nothing happens at runtime” is quite an understatement.

The original poster seemed to have the impression that the operation of binding memory itself might affect program state, independent of any compiler optimization. I want to make it clear that a call to bindMemory(to:capacity:) has no observable runtime side effects at the point of the call. But I need to throw in an exemption for future sanitizers.

-Andy

···

On Nov 2, 2016, at 12:58 PM, Dave Abrahams via swift-users <swift-users@swift.org> wrote:

At the top of the migration guide is a link to the memory model explanation:
swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub

"A memory location's bound type is an abstract, dynamic property of the memory used to formalize
type safety.”

I’m not sure I like the “prepares the memory” language myself. Binding
memory communicates to the compiler that the memory locations are safe
for typed access. Nothing happens at runtime--until someone writes a
type safety sanitizer.

Well, that's a slight overstatement IMO. Sanitizers aside, the main
reason for these binding operations is that if you leave them out,
something different *will* happen at runtime... something that will make
your code do the wrong thing.

What I would say is that binding the memory has no immediate runtime
cost... but it's absolutely required if you want your program to behave
(and sometimes behaving correctly is a little slower than misbehaving).

I’m not sure I like the “prepares the memory” language myself. Binding memory communicates to the compiler that the memory locations are safe for typed access. Nothing happens at runtime--until someone writes a type safety sanitizer.

So nothing happens at runtime, and also nothing appears to happen at compile time. If I try this code:

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

Why do you think it should not be allowed.
AFAICS everything is correct.
Are you referring to the multiple interpretation of the raw memory? That is entirely intentional, indeed one of the main purposes.

Rien.

···

On 02 Nov 2016, at 18:07, Manfred Schubert via swift-users <swift-users@swift.org> wrote:
Am 01.11.2016 um 21:40 schrieb Andrew Trick <atrick@apple.com>:

It affects the abstract state of the memory location, independent of the pointer variable used to access that memory. Binding memory returns a typed pointer for convenience and clarity, but there’s nothing special about that particular pointer value.

If it were not returning a typed pointer, what would it actually do?

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

I’m not sure I like the “prepares the memory” language myself. Binding memory communicates to the compiler that the memory locations are safe for typed access. Nothing happens at runtime--until someone writes a type safety sanitizer.

So nothing happens at runtime, and also nothing appears to happen at compile time.

Nothing observable happens at the call to ptrT = bindMemory(to: T.self, …). It's effect on the program is making the subsequent ptrT.pointee a well-defined operation.

If I try this code:

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

print(widePtr.pointee) is undefined. It may execute just the way you expect. In practice, it may also be reordered with the assignments to narrowPtr. Our optimizer is designed to do that but by chance isn’t in this version of the compiler. In theory anything can happen.

The real danger of undefined behavior of course is that the program will behave as you expect without warning when you develop the code, and future versions of the compiler may change the behavior in unpredictable ways.

This API will make it easier to develop static diagnostics that catch undefined behavior and much easier to write a runtime sanitizer. Neither of those things exist yet. It might be easy to catch obvious cases like this, but the value of catching just the obvious cases is limited.

It affects the abstract state of the memory location, independent of the pointer variable used to access that memory. Binding memory returns a typed pointer for convenience and clarity, but there’s nothing special about that particular pointer value.

If it were not returning a typed pointer, what would it actually do?

It would bind memory. Returning a typed pointer actually has nothing to do with its semantics.

That fact that it’s one of the only ways to get a typed pointer from a raw pointer forces the programmer to bind the memory’s type before performing typed access on the memory.

-Andy

···

On Nov 2, 2016, at 10:07 AM, Manfred Schubert via swift-users <swift-users@swift.org> wrote:
Am 01.11.2016 um 21:40 schrieb Andrew Trick <atrick@apple.com>:

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

This is more so a semantic distinction rather than some kind of physical operation. The memory is not altered, but all reads and writes to that memory location have to be through the “bound type”. If it’s “bound” to some type T, you must only read and write through values of type T, and not some unrelated type.

So is „binding memory to a type“ like declaring the intent to the compiler that this memory is accessed as a certain type?

Yes.

[1] swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub

Re-reading this, I guess I don’t even understand the need for memory being initialized and deinitialized. I understand that it is useful to have a defined initial value, but why is that a requirement for raw memory?

Memory is initialized whenever it holds a value. Memory does not need an “initial” value, so I’m not sure I understand the question.

API’s for reading memory do require that the memory be initialized. After all, they claim to return a valid value.

Deferring initialization is convenient for some contiguous data structures, but generally dealing with uninitialized memory is expected to be rare. About the only things you can do with uninitialized memory are initialize it, deallocate it, or copy it with C memcpy.

If I receive raw memory from outside of Swift, would this already be „bound“ and „initialized“? Many raw data can be interpreted in multiple ways, so it cannot be bound to any particular type, but it is initialized with meaningful data already.

If a C interface returns a pointer to uninitialized memory (e.g. malloc), then Swift code cannot assume that memory is bound to any type.

If a C interface returns a pointer to initialized memory, then it will be returning a typed pointer and indicating the capacity. Swift code can safely assume the memory is bound to that type.

There is a subtle issue here. I’m reluctant to bring it up because it really isn’t the user’s job to understand. But since we’re on the topic… It’s theoretically possible for a C interface to return a pointer to the same memory as different, unrelated, types via different functions. This is valid in C because of language-specific rules regarding aliasing and layout of structs, but would not otherwise be valid in Swift. For this reason, the compiler will be more conservative about aliasing of imported C types. Again, it’s not something the user needs to worry about.

And what does deinitialize actually do? How could a row of Ints be deinitialized for example? Or is it zeroing the raw bytes of the memory?

By definition, deinitializing a value of trivial type, such as Int, does absolutely nothing other than semantically “mark” the memory as deinitialized (it’s a nop in the implementation).

Deinitializing nontrivial values decrements reference counts, runs deinitializers, and what not.

-Andy

···

On Nov 2, 2016, at 10:32 AM, Manfred Schubert via swift-users <swift-users@swift.org> wrote:
Am 01.11.2016 um 21:43 schrieb Michael Ilseman <milseman@apple.com>:

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

1 Like

At the top of the migration guide is a link to the memory model explanation:

swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub

"A memory location's bound type is an abstract, dynamic property of the memory used to formalize
type safety.”

I’m not sure I like the “prepares the memory” language myself. Binding
memory communicates to the compiler that the memory locations are safe
for typed access. Nothing happens at runtime--until someone writes a
type safety sanitizer.

Well, that's a slight overstatement IMO. Sanitizers aside, the main
reason for these binding operations is that if you leave them out,
something different *will* happen at runtime... something that will make
your code do the wrong thing.

What I would say is that binding the memory has no immediate runtime
cost... but it's absolutely required if you want your program to behave
(and sometimes behaving correctly is a little slower than misbehaving).

Good clarification. I really did not mean to imply that binding memory
to a type has no effect on runtime behavior. Taken out of context,
“nothing happens at runtime” is quite an understatement.

The original poster seemed to have the impression that the operation
of binding memory itself might affect program state,

Formally speaking, it does!

independent of any compiler optimization. I want to make it clear that
a call to bindMemory(to:capacity:) has no observable runtime side
effects at the point of the call.

Only because you can't observe what memory is bound to.

But I need to throw in an exemption for future sanitizers.

I don't think you do; sanitizer actions are allowed under undefined
behavior.

···

on Wed Nov 02 2016, Andrew Trick <atrick-AT-apple.com> wrote:

On Nov 2, 2016, at 12:58 PM, Dave Abrahams via swift-users <swift-users@swift.org> wrote:

--
-Dave

If I understand the documentation correctly, this should not be allowed, because it’s not allowed to access memory as different types at the same time. It needs to be „bound“ to the type first, and can only be bound to one type at a time, so all access as another type must be encapsulated within a closure.

Manfred

···

Am 02.11.2016 um 18:37 schrieb Rien <Rien@Balancingrock.nl>:

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

Why do you think it should not be allowed.
AFAICS everything is correct.

1 Like

Allowing type punning is one of the main purposes of raw memory access (via a raw pointer). But in this code, widePtr.pointee is a typed UInt16 load from memory that is bound to UInt8. Loading a UInt16 without rebinding memory first requires a raw load such as:

rawPtr.load(as: UInt16.self)

-Andy

···

On Nov 2, 2016, at 10:37 AM, Rien via swift-users <swift-users@swift.org> wrote:

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

Why do you think it should not be allowed.
AFAICS everything is correct.
Are you referring to the multiple interpretation of the raw memory? That is entirely intentional, indeed one of the main purposes.

Oh boy. I keep failing at this. How does one formally distinguish between semantic state and actual state?
-Andy

···

On Nov 2, 2016, at 3:50 PM, Dave Abrahams <dabrahams@apple.com> wrote:

The original poster seemed to have the impression that the operation
of binding memory itself might affect program state,

Formally speaking, it does!

I think the difference here is that sanitizers affect program characteristics even for correct programs. Introducing a sanitizer can reduce optimization opportunities, changing an algorithm’s complexity. And of course, there’s always a bit of bookkeeping code being executed that wouldn’t be there otherwise.

Jordan

···

On Nov 2, 2016, at 15:50, Dave Abrahams via swift-users <swift-users@swift.org> wrote:

on Wed Nov 02 2016, Andrew Trick <atrick-AT-apple.com <http://atrick-at-apple.com/&gt;&gt; wrote:

On Nov 2, 2016, at 12:58 PM, Dave Abrahams via swift-users <swift-users@swift.org> wrote:

At the top of the migration guide is a link to the memory model explanation:

swift-evolution/0107-unsaferawpointer.md at master · apple/swift-evolution · GitHub

"A memory location's bound type is an abstract, dynamic property of the memory used to formalize
type safety.”

I’m not sure I like the “prepares the memory” language myself. Binding
memory communicates to the compiler that the memory locations are safe
for typed access. Nothing happens at runtime--until someone writes a
type safety sanitizer.

Well, that's a slight overstatement IMO. Sanitizers aside, the main
reason for these binding operations is that if you leave them out,
something different *will* happen at runtime... something that will make
your code do the wrong thing.

What I would say is that binding the memory has no immediate runtime
cost... but it's absolutely required if you want your program to behave
(and sometimes behaving correctly is a little slower than misbehaving).

Good clarification. I really did not mean to imply that binding memory
to a type has no effect on runtime behavior. Taken out of context,
“nothing happens at runtime” is quite an understatement.

The original poster seemed to have the impression that the operation
of binding memory itself might affect program state,

Formally speaking, it does!

independent of any compiler optimization. I want to make it clear that
a call to bindMemory(to:capacity:) has no observable runtime side
effects at the point of the call.

Only because you can't observe what memory is bound to.

But I need to throw in an exemption for future sanitizers.

I don't think you do; sanitizer actions are allowed under undefined
behavior.

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

Why do you think it should not be allowed.
AFAICS everything is correct.

If I understand the documentation correctly, this should not be allowed, because it’s not allowed to access memory as different types at the same time. It needs to be „bound“ to the type first, and can only be bound to one type at a time, so all access as another type must be encapsulated within a closure.

Ah, but that is not the case.

It is important to differentiate between the “gateway” to the memory and the memory area itself.
Different programming languages/compilers have different approaches, but I believe that Swift allocates a struct for every gateway.
widePtr and narrowPtr are two different gateways. They refer to different struct's. But the struct for each of them refers to the same memory area.

Example: widePtr can be located at address 0x1000, narrowPtr can be allocated at address 0x2000 while the memory area both refer to (the raw memory) can be at address 0x3000

Every access to the raw memory via a gateway must follow the rules for that gateway.

Note: Some compilers may only create ephemeral gateways, but as long as the compiler checks that the acces follows the correct rules, that is not a problem.

This is not only very useful, but it also opens the door to better interfaces to some low level Unix APIs.

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: Swiftrien (Rien) · GitHub
Project: http://swiftfire.nl

···

On 03 Nov 2016, at 15:16, Manfred Schubert via swift-users <swift-users@swift.org> wrote:

Am 02.11.2016 um 18:37 schrieb Rien <Rien@Balancingrock.nl>:

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

Why do you think it should not be allowed.
AFAICS everything is correct.

If I understand the documentation correctly, this should not be allowed, because it’s not allowed to access memory as different types at the same time. It needs to be „bound“ to the type first, and can only be bound to one type at a time, so all access as another type must be encapsulated within a closure.

Yes, this would work

  withMemoryRebound(to: UInt16.self, capacity: 1) { print($0.pointee) }

So would this

  rawPtr.bindMemory(to: Int16.self, capacity: 1)
  print(widePtr.pointee)

Or this

  rawPtr.load(as: UInt16.self)

-Andy

···

On Nov 3, 2016, at 7:16 AM, Manfred Schubert via swift-users <swift-users@swift.org> wrote:

Am 02.11.2016 um 18:37 schrieb Rien <Rien@Balancingrock.nl>:

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

That's the point; you don't. :-)

···

on Wed Nov 02 2016, Andrew Trick <atrick-AT-apple.com> wrote:

On Nov 2, 2016, at 3:50 PM, Dave Abrahams <dabrahams@apple.com> wrote:

The original poster seemed to have the impression that the operation
of binding memory itself might affect program state,

Formally speaking, it does!

Oh boy. I keep failing at this. How does one formally distinguish
between semantic state and actual state?

--
-Dave

var rawPtr = UnsafeMutableRawPointer.allocate(bytes: 2, alignedTo: 0)

var widePtr = rawPtr.bindMemory(to: Int16.self, capacity: 1)

widePtr.pointee = 32

var narrowPtr = rawPtr.bindMemory(to: UInt8.self, capacity: 2)

narrowPtr[0] = 16
narrowPtr[1] = 255

print(widePtr.pointee)

This compiles and runs as expected, but it should not be allowed if I understand things correctly. So shouldn’t it be a compile time error or crash at runtime? If not, what do I get over how it was before where I was casting to a typed pointer?

Why do you think it should not be allowed.
AFAICS everything is correct.

If I understand the documentation correctly, this should not be allowed, because it’s not allowed to access memory as different types at the same time. It needs to be „bound“ to the type first, and can only be bound to one type at a time, so all access as another type must be encapsulated within a closure.

Ah, but that is not the case.

It is important to differentiate between the “gateway” to the memory and the memory area itself.
Different programming languages/compilers have different approaches, but I believe that Swift allocates a struct for every gateway.
widePtr and narrowPtr are two different gateways. They refer to different struct's. But the struct for each of them refers to the same memory area.

In the Swift memory model, a pointer value is substitutable with any other pointer of the same value. And for efficiency, pointer values are implemented as addresses. This was an intentional design decision. For example, this is well-defined:

func foo<T: Equatable>(rawPtr: UnsafeRawPointer, ptrT: UnsafePointer<T>) {
  if rawPtr == UnsafeRawPointer(ptrT) {
    assert(rawPtr.assumingMemoryBound(to: T.self).pointee == ptrT.pointee)
  }
}

Note that assumingMemoryBound(to:) is essentially a nice way of doing unsafeBitCast, but intentional and verifiable.

Example: widePtr can be located at address 0x1000, narrowPtr can be allocated at address 0x2000 while the memory area both refer to (the raw memory) can be at address 0x3000

Swift strict aliasing means that unrelated typed accesses to memory cannot overlap in their underlying raw memory.

Our UnsafePointer value representation is not realistically ever going to depend on the Pointee type.

Every access to the raw memory via a gateway must follow the rules for that gateway.

Note: Some compilers may only create ephemeral gateways, but as long as the compiler checks that the acces follows the correct rules, that is not a problem.

This is not only very useful, but it also opens the door to better interfaces to some low level Unix APIs.

I think we would want a Swift interface on top of those APIs that doesn’t rely on type punning.

A Swift pointer value itself doesn’t provide a gateway. It is really bindMemory(to:capacity:) or withMemoryRebound(to:capacity:) that control whether typed access is well-defined at some point in the program.

In native Swift code, interacting with the memory binding APIs will be extremely rare. Type punning is not a “normal” activity outside of use cases like network I/O and binary file formats. In those cases it probably makes more sense to use a raw pointer directly rather than binding memory to a type.

-Andy

···

On Nov 3, 2016, at 7:41 AM, Rien via swift-users <swift-users@swift.org> wrote:

On 03 Nov 2016, at 15:16, Manfred Schubert via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Am 02.11.2016 um 18:37 schrieb Rien <Rien@Balancingrock.nl <mailto:Rien@balancingrock.nl>>:

Manfred
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/&gt;
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/&gt;
Github: Swiftrien (Rien) · GitHub
Project: http://swiftfire.nl <http://swiftfire.nl/&gt;

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users