SE-0447: Span: Safe Access to Contiguous Storage

Dereferencing the pointer is an "access":
_ = pointer.pointee // this is an access

A safe decoder should only access memory using a type that allows all valid bit patterns.

We don't formally distinguish between loading an aggregate as a whole vs reading a particular field within that aggregate. Although, naturally, the later is more likely break in practice.

We could think about different levels of correctness violations:

  1. API violation, but not UB: verification may fail
  2. Theoretical UB: no well-defined meaning
  3. Practical UB: will likely cause problems
struct Layout {
  var code: UInt8
  var flag: Bool
}
// Given `rawp: UnsafeRawPointer` that points to an unknown bit pattern:
let value = rawp.load(as: Layout.self) // UB, in theory
foo(value.flag) // Practical UB

let codePointer = rawp.bindMemory(to: UInt8.self, capacity: 1)
let code = codePointer.pointee // OK

// API violation because memory is bound to UInt8: verification can fail
let badLayoutPointer1 = rawp.assumingMemoryBound(to: Layout.self) 

let badLayout1 = badLayoutPointer1.pointee // UB, in theory
foo(badLayout1.flag) // Practical UB

// OK to bind memory, regardless of the in-memory bitpattern
let badLayoutPointer2 = rawp.bindMemory(to: Layout.self, capacity: 1)

let badLayout = badLayoutPointer2.pointee // UB, in theory
foo(badLayout.flag) // Practical UB

Similary, zero is an invalid pointer value, so:

let rawp = unsafeBitCast(0, to: UnsafeRawPointer.self) // UB, in theory
return UnsafeRawPointer?(rawp) != nil // Practical UB
3 Likes