What is strict memory safety (Swift 6.2), what does the unsafe keyword mark, and what does it not do?

5.11.3 · L5 · Learning

Predict first

With -strict-memory-safety, are the unmarked unsafe uses diagnosed?

import Swift
func f(_ p: UnsafePointer<Int>) -> Int { p.pointee }
func g() -> Int { var x = 1; return withUnsafePointer(to: &x) { f($0) } }
  • Yes: warnings in the StrictMemorySafety category
  • No
  • They are errors
  • Only calls to C functions

Predict first

Without the flag?

import Swift
func f(_ p: UnsafePointer<Int>) -> Int { p.pointee }
func g() -> Int { var x = 1; return withUnsafePointer(to: &x) { f($0) } }
  • No such warning
  • The same warnings
  • An error
  • A crash at runtime

Mental model. It is a hazard sign on every dangerous machine in the factory. The sign does not make the machine safer, but nobody can walk up to it without noticing, and an inspector can count the signs.

Swift is memory safe except where you reach for unsafe constructs: UnsafePointer and friends, unsafeBitCast, unchecked Unmanaged, C pointers. Strict memory safety is an opt-in audit mode: compile with -strict-memory-safety and every use of an unsafe construct must be acknowledged with the unsafe keyword (like try for throwing calls), or the compiler emits a warning with the category StrictMemorySafety. A declaration can be marked @unsafe to say its use is unsafe, and @safe to assert that a type wrapping unsafe parts is safe to use. Without the flag the same file compiles with no such warning, and with the flag it warns at each unmarked use: in the check, UnsafePointer in a signature and the call to withUnsafePointer both warn.

The keyword does not make anything safe: unsafe is a marker for review, like a comment the compiler enforces, and the dangerous operation still has undefined behaviour if you get it wrong. Its value is audit and policy: security-sensitive modules can turn the mode on, treat the warnings as errors, and review every unsafe by grep. A module that should have none can be held to that with the flag and -warnings-as-errors. It also works with Span and RawSpan (safe views over contiguous memory) as the safe replacement for many pointer uses. I verified the warnings with the Swift 6.4 compiler; the interaction with Span-based APIs I did not test here.

The mode asks you to acknowledge each unsafe use.

The audit mode is opt-in.

Common traps

  • Thinking unsafe adds a runtime check. It adds none.
  • Marking whole functions @safe to silence warnings. That is a claim you must justify.

They might follow up with

  • How do I turn it on in SwiftPM? The setting is a Swift setting for the target; I did not run the manifest syntax, so check your toolchain.
  • What replaces pointers? Span and RawSpan, collections’ withUnsafe… closures narrowed by lifetime checks.

Related questions

  • 1.8.5 What are consume, borrowing, and noncopyable types, at a high level?
  • 5.7.2 What are upcoming features and experimental features, and how do they help a migration?
  • 3.2.4 What is unowned(unsafe), and when would you ever use it?
  • 5.2.4 What is exclusivity enforcement, and what does it cost at runtime?

← What does internal import (SE-0409) change, and why does it matter…   How can a library with library evolution still offer a fast generic… →