Files
Gabor Horvath a91844b442 [StrictSafety] Add @unsafe(always), enforced at use sites
'@unsafe' requires uses to be acknowledged with the 'unsafe' keyword only when
strict memory safety checking is enabled. Add '@unsafe(always)', which requires
it in every language mode and diagnoses an error rather than a warning, for
constructs that are very hard to use correctly: ones where nearly every use is
incorrect, so writing one should be a deliberate act.

The motivating case is C++ interop, where an API whose lifetime dependencies
cannot be inferred is imported '@unsafe' today and can then be used with no
acknowledgement at all outside of strict memory safety checking.

This covers the attribute itself and its enforcement at use sites: expressions
and for-in loops. Declaration-level checks (overrides, protocol witnesses,
unsafe storage and superclasses) follow separately.

Notes:
- A plain '@unsafe' declaration whose signature involves an always-unsafe type
  does not launder the stronger requirement.
- Compiler-synthesized code cannot be annotated by hand, so an always-unsafe
  use there falls back to the merely-unsafe behavior instead of erroring.
- Interfaces strip 'unsafe' markers from inlinable bodies, so the diagnostics
  degrade to warnings when type-checking a .swiftinterface.
- '@unsafe(always)' is printed into module interfaces behind a
  '$AlwaysUnsafeAttribute' feature check, falling back to '@unsafe' for
  compilers that predate the spelling.
2026-08-05 10:30:27 +01:00

31 lines
1.1 KiB
Swift

// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name always_unsafe
// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name always_unsafe
// RUN: %FileCheck %s --input-file %t.swiftinterface --match-full-lines
// Older compilers don't understand the argument of '@unsafe(always)', so the
// interface falls back to plain '@unsafe' for them.
// CHECK: #if compiler(>=5.3) && $AlwaysUnsafeAttribute
// CHECK: @unsafe(always) public func alwaysUnsafeFunc()
// CHECK: #else
// CHECK: @unsafe public func alwaysUnsafeFunc()
// CHECK: #endif
@unsafe(always)
public func alwaysUnsafeFunc() { }
// A plain '@unsafe' needs no compatibility guard.
// CHECK-NOT: $AlwaysUnsafeAttribute
// CHECK: @unsafe public func unsafeFunc()
@unsafe
public func unsafeFunc() { }
// The 'unsafe' marker is deliberately stripped from inlinable bodies printed
// into an interface, so the printed body contains a bare call. Verifying the
// interface therefore must not be a hard error.
// CHECK: @inlinable public func inlinableUse() {
// CHECK-NEXT: alwaysUnsafeFunc()
@inlinable
public func inlinableUse() {
unsafe alwaysUnsafeFunc()
}