Skip to content

Commit 7896cd7

Browse files
authored
Merge branch 'main' into es-sil-pkg
2 parents b37877e + 0d3f6a9 commit 7896cd7

File tree

178 files changed

+3420
-1185
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

178 files changed

+3420
-1185
lines changed

CHANGELOG.md

Lines changed: 110 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,52 +61,142 @@
6161

6262
## Swift 5.10
6363

64+
* Swift 5.10 closes all known static data-race safey holes in complete strict
65+
concurrency checking.
66+
67+
When writing code against `-strict-concurrency=complete`, Swift 5.10 will
68+
diagnose all potential for data races at compile time unless an explicit
69+
unsafe opt out, such as `nonisolated(unsafe)` or `@unchecked Sendable`, is
70+
used.
71+
72+
For example, in Swift 5.9, the following code crashes at runtime due to a
73+
`@MainActor`-isolated initializer being evaluated outside the actor, but it
74+
was not diagnosed under `-strict-concurrency=complete`:
75+
76+
```swift
77+
@MainActor
78+
class MyModel {
79+
init() {
80+
MainActor.assertIsolated()
81+
}
82+
83+
static let shared = MyModel()
84+
}
85+
86+
func useShared() async {
87+
let model = MyModel.shared
88+
}
89+
90+
await useShared()
91+
```
92+
93+
The above code admits data races because a `@MainActor`-isolated static
94+
variable, which evaluates a `@MainActor`-isolated initial value upon first
95+
access, is accessed synchronously from a `nonisolated` context. In Swift
96+
5.10, compiling the code with `-strict-concurrency=complete` produces a
97+
warning that the access must be done asynchronously:
98+
99+
```
100+
warning: expression is 'async' but is not marked with 'await'
101+
let model = MyModel.shared
102+
^~~~~~~~~~~~~~
103+
await
104+
```
105+
106+
Swift 5.10 fixed numerous other bugs in `Sendable` and actor isolation
107+
checking to strengthen the guarantees of complete concurrency checking.
108+
109+
Note that the complete concurrency model in Swift 5.10 is conservative.
110+
Several Swift Evolution proposals are in active development to improve the
111+
usability of strict concurrency checking ahead of Swift 6.
112+
64113
* [SE-0412][]:
65114

66-
Under strict concurrency checking, every global or static variable must be either isolated to a global actor or be both immutable and of `Sendable` type.
115+
Global and static variables are prone to data races because they provide memory that can be accessed from any program context. Strict concurrency checking in Swift 5.10 prevents data races on global and static variables by requiring them to be either:
116+
117+
1. isolated to a global actor, or
118+
2. immutable and of `Sendable` type.
119+
120+
For example:
67121

68122
```swift
69123
var mutableGlobal = 1
70124
// warning: var 'mutableGlobal' is not concurrency-safe because it is non-isolated global shared mutable state
71125
// (unless it is top-level code which implicitly isolates to @MainActor)
72126

73-
final class NonsendableType {
74-
init() {}
127+
@MainActor func mutateGlobalFromMain() {
128+
mutableGlobal += 1
129+
}
130+
131+
nonisolated func mutateGlobalFromNonisolated() async {
132+
mutableGlobal += 10
75133
}
76134

77135
struct S {
78-
static let immutableNonsendable = NonsendableType()
79-
// warning: static property 'immutableNonsendable' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor
136+
static let immutableSendable = 10
137+
// okay; 'immutableSendable' is safe to access concurrently because it's immutable and 'Int' is 'Sendable'
80138
}
81139
```
82140

83-
The attribute `nonisolated(unsafe)` can be used to annotate a global variable (or any form of storage) to disable static checking of data isolation, but note that without correct implementation of a synchronization mechanism to achieve data isolation, dynamic run-time analysis from exclusivity enforcement or tools such as Thread Sanitizer could still identify failures.
141+
A new `nonisolated(unsafe)` modifier can be used to annotate a global or static variable to suppress data isolation violations when manual synchronization is provided:
84142

85143
```swift
86-
nonisolated(unsafe) var global: String
144+
// This global is only set in one part of the program
145+
nonisolated(unsafe) var global: String!
87146
```
88147

148+
`nonisolated(unsafe)` can be used on any form of storage, including stored properties and local variables, as a more granular opt out for `Sendable` checking, eliminating the need for `@unchecked Sendable` wrapper types in many use cases:
149+
150+
```swift
151+
import Dispatch
152+
153+
// 'MutableData' is not 'Sendable'
154+
class MutableData { ... }
155+
156+
final class MyModel: Sendable {
157+
private let queue = DispatchQueue(...)
158+
// 'protectedState' is manually isolated by 'queue'
159+
nonisolated(unsafe) private var protectedState: MutableData
160+
}
161+
```
162+
163+
Note that without correct implementation of a synchronization mechanism to achieve data isolation, dynamic run-time analysis from exclusivity enforcement or tools such as the Thread Sanitizer could still identify failures.
164+
89165
* [SE-0411][]:
90166

91-
Default value expressions can now have the same isolation as the enclosing
92-
function or the corresponding stored property:
167+
Swift 5.10 closes a data-race safety hole that previously permitted isolated
168+
default stored property values to be synchronously evaluated from outside the
169+
actor. For example, the following code compiles warning-free under
170+
`-strict-concurrency=complete` in Swift 5.9, but it will crash at runtime at
171+
the call to `MainActor.assertIsolated()`:
93172

94173
```swift
95-
@MainActor
96-
func requiresMainActor() -> Int { ... }
174+
@MainActor func requiresMainActor() -> Int {
175+
MainActor.assertIsolated()
176+
return 0
177+
}
97178

98-
class C {
99-
@MainActor
100-
var x: Int = requiresMainActor()
179+
@MainActor struct S {
180+
var x = requiresMainActor()
181+
var y: Int
182+
}
183+
184+
nonisolated func call() async {
185+
let s = await S(y: 10)
101186
}
102187

103-
@MainActor func defaultArg(value: Int = requiresMainActor()) { ... }
188+
await call()
104189
```
105190

106-
For isolated default values of stored properties, the implicit initialization
107-
only happens in the body of an `init` with the same isolation. This closes
108-
an important data-race safety hole where global-actor-isolated default values
109-
could inadvertently run synchronously from outside the actor.
191+
This happens because `requiresMainActor()` is used as a default argument to
192+
the member-wise initializer of `S`, but default arguments are always
193+
evaluated in the caller. In this case, the caller runs on the generic
194+
executor, so the default argument evaluation crashes.
195+
196+
Under `-strict-concurrency=complete` in Swift 5.10, default argument values
197+
can safely share the same isolation as the enclosing function or stored
198+
property. The above code is still valid, but the isolated default argument is
199+
guaranteed to be evaluated in the callee's isolation domain.
110200

111201
## Swift 5.9.2
112202

@@ -9998,4 +10088,4 @@ using the `.dynamicType` member to retrieve the type of an expression should mig
999810088
[#57225]: <https://github.com/apple/swift/issues/57225>
999910089
[#56139]: <https://github.com/apple/swift/issues/56139>
1000010090
[#70065]: <https://github.com/apple/swift/pull/70065>
10001-
[swift-syntax]: https://github.com/apple/swift-syntax
10091+
[swift-syntax]: https://github.com/apple/swift-syntax

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,11 +784,17 @@ if (CMAKE_Swift_COMPILER)
784784
else()
785785
message(STATUS "Swift Compiler (None).")
786786
endif()
787+
788+
set(THREADS_PREFER_PTHREAD_FLAG YES)
789+
include(FindThreads)
790+
787791
if(SWIFT_PATH_TO_CMARK_BUILD)
788792
execute_process(COMMAND ${SWIFT_PATH_TO_CMARK_BUILD}/src/cmark --version
789793
OUTPUT_VARIABLE _CMARK_VERSION
790794
OUTPUT_STRIP_TRAILING_WHITESPACE)
791795
message(STATUS "CMark Version: ${_CMARK_VERSION}")
796+
elseif(SWIFT_INCLUDE_TOOLS)
797+
find_package(cmark-gfm CONFIG REQUIRED)
792798
endif()
793799
message(STATUS "")
794800

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
//===--- AddressUtils.swift - Utilities for handling SIL addresses -------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// Classify address uses. This can be used by def-use walkers to
16+
/// ensure complete handling of all legal SIL patterns.
17+
///
18+
/// TODO: Integrate this with SIL verification to ensure completeness.
19+
///
20+
/// TODO: Convert AddressDefUseWalker to conform to AddressUtils after
21+
/// checking that the additional instructions are handled correctly by
22+
/// escape analysis.
23+
///
24+
/// TODO: Verify that pointerEscape is only called for live ranges in which
25+
/// `findPointerEscape()` returns true.
26+
protocol AddressUseVisitor {
27+
var context: Context { get }
28+
29+
/// An address projection produces a single address result and does not
30+
/// escape its address operand in any other way.
31+
mutating func projectedAddressUse(of operand: Operand, into value: Value)
32+
-> WalkResult
33+
34+
/// An access scope: begin_access, begin_apply, load_borrow.
35+
mutating func scopedAddressUse(of operand: Operand) -> WalkResult
36+
37+
/// A address leaf use cannot propagate the address bits beyond the
38+
/// instruction.
39+
///
40+
/// An apply or builtin propagates an address into the callee, but
41+
/// it is considered a leaf use as long as the argument does not escape.
42+
mutating func leafAddressUse(of operand: Operand) -> WalkResult
43+
44+
/// A loaded address use propagates the value at the address.
45+
mutating func loadedAddressUse(of operand: Operand, into value: Value)
46+
-> WalkResult
47+
48+
/// A loaded address use propagates the value at the address to the
49+
/// destination address operand.
50+
mutating func loadedAddressUse(of operand: Operand, into address: Operand)
51+
-> WalkResult
52+
53+
/// A non-address owned `value` whose ownership depends on the in-memory
54+
/// value at `address`, such as `mark_dependence %value on %address`.
55+
mutating func dependentAddressUse(of operand: Operand, into value: Value)
56+
-> WalkResult
57+
58+
/// A pointer escape may propagate the address beyond the current instruction.
59+
mutating func escapingAddressUse(of operand: Operand) -> WalkResult
60+
61+
/// A unknown address use. This should never be called in valid SIL.
62+
mutating func unknownAddressUse(of operand: Operand) -> WalkResult
63+
}
64+
65+
extension AddressUseVisitor {
66+
/// Classify an address-type operand, dispatching to one of the
67+
/// protocol methods above.
68+
mutating func classifyAddress(operand: Operand) -> WalkResult {
69+
switch operand.instruction {
70+
case is BeginAccessInst, is BeginApplyInst, is LoadBorrowInst,
71+
is StoreBorrowInst:
72+
return scopedAddressUse(of: operand)
73+
74+
case let markDep as MarkDependenceInst:
75+
if markDep.valueOperand == operand {
76+
return projectedAddressUse(of: operand, into: markDep)
77+
}
78+
assert(markDep.baseOperand == operand)
79+
// If another address depends on the current address,
80+
// handle it like a projection.
81+
if markDep.type.isAddress {
82+
return projectedAddressUse(of: operand, into: markDep)
83+
}
84+
if LifetimeDependence(markDependence: markDep, context) != nil {
85+
// This is unreachable from InteriorUseVisitor because the
86+
// base address of a `mark_dependence [nonescaping]` must be a
87+
// `begin_access`, and interior liveness does not check uses of
88+
// the accessed address.
89+
return dependentAddressUse(of: operand, into: markDep)
90+
}
91+
// A potentially escaping value depends on this address.
92+
return escapingAddressUse(of: operand)
93+
94+
case let pai as PartialApplyInst where pai.isOnStack:
95+
return dependentAddressUse(of: operand, into: pai)
96+
97+
case let pai as PartialApplyInst where !pai.isOnStack:
98+
return escapingAddressUse(of: operand)
99+
100+
case is AddressToPointerInst:
101+
return escapingAddressUse(of: operand)
102+
103+
case is StructElementAddrInst, is TupleElementAddrInst,
104+
is IndexAddrInst, is TailAddrInst, is TuplePackElementAddrInst,
105+
is InitEnumDataAddrInst, is UncheckedTakeEnumDataAddrInst,
106+
is InitExistentialAddrInst, is OpenExistentialAddrInst,
107+
is ProjectBlockStorageInst, is UncheckedAddrCastInst,
108+
is UnconditionalCheckedCastAddrInst,
109+
is MarkUninitializedInst, is DropDeinitInst,
110+
is CopyableToMoveOnlyWrapperAddrInst,
111+
is MoveOnlyWrapperToCopyableAddrInst,
112+
is MarkUnresolvedNonCopyableValueInst:
113+
let svi = operand.instruction as! SingleValueInstruction
114+
return projectedAddressUse(of: operand, into: svi)
115+
116+
case is ReturnInst, is ThrowInst, is YieldInst, is TryApplyInst,
117+
is SwitchEnumAddrInst, is CheckedCastAddrBranchInst,
118+
is SelectEnumAddrInst, is InjectEnumAddrInst,
119+
is StoreInst, is StoreUnownedInst, is StoreWeakInst,
120+
is AssignInst, is AssignByWrapperInst, is AssignOrInitInst,
121+
is TupleAddrConstructorInst, is InitBlockStorageHeaderInst,
122+
is RetainValueAddrInst, is ReleaseValueAddrInst,
123+
is DestroyAddrInst, is DeallocStackInst,
124+
is DeinitExistentialAddrInst,
125+
is EndApplyInst, is IsUniqueInst, is MarkFunctionEscapeInst,
126+
is PackElementSetInst:
127+
return leafAddressUse(of: operand)
128+
129+
case is LoadInst, is LoadUnownedInst, is LoadWeakInst,
130+
is ValueMetatypeInst, is ExistentialMetatypeInst,
131+
is PackElementGetInst:
132+
let svi = operand.instruction as! SingleValueInstruction
133+
return loadedAddressUse(of: operand, into: svi)
134+
135+
case let sdai as SourceDestAddrInstruction
136+
where sdai.sourceOperand == operand:
137+
return loadedAddressUse(of: operand, into: sdai.destinationOperand)
138+
139+
case let sdai as SourceDestAddrInstruction
140+
where sdai.destinationOperand == operand:
141+
return leafAddressUse(of: operand)
142+
143+
case let builtin as BuiltinInst:
144+
switch builtin.id {
145+
case .Copy where builtin.operands[1] == operand: // source
146+
return loadedAddressUse(of: operand, into: builtin.operands[0])
147+
148+
case .Copy where builtin.operands[0] == operand: // dest
149+
return leafAddressUse(of: operand)
150+
151+
// Builtins that cannot load a nontrivial value.
152+
case .TSanInoutAccess, .ResumeThrowingContinuationReturning,
153+
.ResumeNonThrowingContinuationReturning, .GenericAdd,
154+
.GenericFAdd, .GenericAnd, .GenericAShr, .GenericLShr, .GenericOr,
155+
.GenericFDiv, .GenericMul, .GenericFMul, .GenericSDiv,
156+
.GenericExactSDiv, .GenericShl, .GenericSRem, .GenericSub,
157+
.GenericFSub, .GenericUDiv, .GenericExactUDiv, .GenericURem,
158+
.GenericFRem, .GenericXor, .TaskRunInline, .ZeroInitializer,
159+
.GetEnumTag, .InjectEnumTag:
160+
return leafAddressUse(of: operand)
161+
default:
162+
// TODO: SIL verification should check that this exhaustively
163+
// recognizes all builtin address uses.
164+
return .abortWalk
165+
}
166+
167+
case is BranchInst, is CondBranchInst:
168+
fatalError("address phi is not allowed")
169+
170+
default:
171+
if operand.instruction.isIncidentalUse {
172+
return leafAddressUse(of: operand)
173+
}
174+
// Unkown instruction.
175+
return unknownAddressUse(of: operand)
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)