Skip to content

Commit 0c27193

Browse files
authored
Merge pull request #42014 from tshortli/allow-back-deploy-attr-on-coroutines
SILGen: Handle coroutines with the @_backDeploy attribute
2 parents 9d854d1 + 019a345 commit 0c27193

File tree

7 files changed

+237
-170
lines changed

7 files changed

+237
-170
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6321,9 +6321,5 @@ ERROR(attr_incompatible_with_back_deploy,none,
63216321
"'%0' cannot be applied to a back deployed %1",
63226322
(DeclAttribute, DescriptiveDeclKind))
63236323

6324-
ERROR(back_deploy_not_on_coroutine,none,
6325-
"'%0' is not supported on coroutine %1",
6326-
(DeclAttribute, DescriptiveDeclKind))
6327-
63286324
#define UNDEFINE_DIAGNOSTIC_MACROS
63296325
#include "DefineDiagnosticMacros.h"

lib/SILGen/SILGenBackDeploy.cpp

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,38 @@ static void emitBackDeployForwardApplyAndReturnOrThrow(
9292
auto subs = SGF.F.getForwardingSubstitutionMap();
9393
SmallVector<SILValue, 4> directResults;
9494

95+
// If the function is a coroutine, we need to use 'begin_apply'.
96+
if (silFnType->isCoroutine()) {
97+
assert(!silFnType->hasErrorResult() && "throwing coroutine?");
98+
99+
// Apply the coroutine, yield the result, and finally branch to either the
100+
// terminal return or unwind basic block via intermediate basic blocks. The
101+
// intermediates are needed to avoid forming critical edges.
102+
SILBasicBlock *resumeBB = SGF.createBasicBlock();
103+
SILBasicBlock *unwindBB = SGF.createBasicBlock();
104+
105+
auto *apply = SGF.B.createBeginApply(loc, functionRef, subs, params);
106+
SmallVector<SILValue, 4> rawResults;
107+
for (auto result : apply->getAllResults())
108+
rawResults.push_back(result);
109+
110+
auto token = rawResults.pop_back_val();
111+
SGF.B.createEndApply(loc, token);
112+
SGF.B.createYield(loc, rawResults, resumeBB, unwindBB);
113+
114+
// Emit resume block.
115+
SGF.B.emitBlock(resumeBB);
116+
SGF.B.createBranch(loc, SGF.ReturnDest.getBlock());
117+
118+
// Emit unwind block.
119+
SGF.B.emitBlock(unwindBB);
120+
SGF.B.createBranch(loc, SGF.CoroutineUnwindDest.getBlock());
121+
return;
122+
}
123+
124+
// Use try_apply for functions that throw.
95125
if (silFnType->hasErrorResult()) {
96-
// Apply a throwing function and forward the results and the error to the
126+
// Apply the throwing function and forward the results and the error to the
97127
// return/throw blocks via intermediate basic blocks. The intermediates
98128
// are needed to avoid forming critical edges.
99129
SILBasicBlock *normalBB = SGF.createBasicBlock();
@@ -115,14 +145,15 @@ static void emitBackDeployForwardApplyAndReturnOrThrow(
115145
extractAllElements(result, loc, SGF.B, directResults);
116146

117147
SGF.B.createBranch(loc, SGF.ReturnDest.getBlock(), directResults);
118-
} else {
119-
// Apply a non-throwing function and forward its results straight to the
120-
// return block.
121-
auto *apply = SGF.B.createApply(loc, functionRef, subs, params);
122-
extractAllElements(apply, loc, SGF.B, directResults);
123-
124-
SGF.B.createBranch(loc, SGF.ReturnDest.getBlock(), directResults);
148+
return;
125149
}
150+
151+
// The original function is neither throwing nor a couroutine. Apply it and
152+
// forward its results straight to the return block.
153+
auto *apply = SGF.B.createApply(loc, functionRef, subs, params);
154+
extractAllElements(apply, loc, SGF.B, directResults);
155+
156+
SGF.B.createBranch(loc, SGF.ReturnDest.getBlock(), directResults);
126157
}
127158

128159
void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3572,21 +3572,6 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
35723572
}
35733573
}
35743574

3575-
// FIXME(backDeploy): support coroutines rdar://90111169
3576-
auto diagnoseCoroutineIfNecessary = [&](AccessorDecl *AD) {
3577-
if (AD->isCoroutine())
3578-
diagnose(Attr->getLocation(), diag::back_deploy_not_on_coroutine,
3579-
Attr, AD->getDescriptiveKind());
3580-
};
3581-
if (auto *ASD = dyn_cast<AbstractStorageDecl>(D)) {
3582-
ASD->visitEmittedAccessors([&](AccessorDecl *AD) {
3583-
diagnoseCoroutineIfNecessary(AD);
3584-
});
3585-
}
3586-
if (auto *AD = dyn_cast<AccessorDecl>(D)) {
3587-
diagnoseCoroutineIfNecessary(AD);
3588-
}
3589-
35903575
auto AtLoc = Attr->AtLoc;
35913576
auto Platform = Attr->Platform;
35923577

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify
2+
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s
3+
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s
4+
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s
5+
6+
// REQUIRES: OS=macosx
7+
8+
@available(macOS 10.50, *)
9+
public struct TopLevelStruct {
10+
// -- Fallback definition for TopLevelStruct.property.read
11+
// CHECK-LABEL: sil non_abi [serialized] [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV8propertyACvrTwB : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
12+
// CHECK: bb0([[BB0_ARG:%.*]] : $TopLevelStruct):
13+
// CHECK: yield [[BB0_ARG]] : $TopLevelStruct, resume [[RESUME_BB:bb[0-9]+]], unwind [[UNWIND_BB:bb[0-9]+]]
14+
//
15+
// CHECK: [[RESUME_BB]]:
16+
// CHECK: [[RESULT:%.*]] = tuple ()
17+
// CHECK: return [[RESULT]] : $()
18+
//
19+
// CHECK: [[UNWIND_BB]]:
20+
// CHECK: unwind
21+
22+
// -- Back deployment thunk for TopLevelStruct.property.read
23+
// CHECK-LABEL: sil non_abi [serialized] [thunk] [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV8propertyACvrTwb : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
24+
// CHECK: bb0([[BB0_ARG:%.*]] : $TopLevelStruct):
25+
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10
26+
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 52
27+
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0
28+
// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
29+
// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
30+
// CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]]
31+
//
32+
// CHECK: [[UNAVAIL_BB]]:
33+
// CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy14TopLevelStructV8propertyACvrTwB : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
34+
// CHECK: ([[YIELD_RES:%.*]], [[YIELD_TOK:%.*]]) = begin_apply [[FALLBACKFN]]([[BB0_ARG]]) : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
35+
// CHECK: end_apply [[YIELD_TOK]]
36+
// CHECK: yield [[YIELD_RES]] : $TopLevelStruct, resume [[UNAVAIL_RESUME_BB:bb[0-9]+]], unwind [[UNAVAIL_UNWIND_BB:bb[0-9]+]]
37+
//
38+
// CHECK: [[UNAVAIL_UNWIND_BB]]:
39+
// CHECK: br [[UNWIND_BB:bb[0-9]+]]
40+
//
41+
// CHECK: [[UNAVAIL_RESUME_BB]]:
42+
// CHECK: br [[RETURN_BB:bb[0-9]+]]
43+
//
44+
// CHECK: [[AVAIL_BB]]:
45+
// CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy14TopLevelStructV8propertyACvr : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
46+
// CHECK: ([[YIELD_RES:%.*]], [[YIELD_TOK:%.*]]) = begin_apply [[ORIGFN]]([[BB0_ARG]]) : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
47+
// CHECK: end_apply [[YIELD_TOK]]
48+
// CHECK: yield [[YIELD_RES]] : $TopLevelStruct, resume [[AVAIL_RESUME_BB:bb[0-9]+]], unwind [[UAVAIL_UNWIND_BB:bb[0-9]+]]
49+
//
50+
// CHECK: [[UAVAIL_UNWIND_BB]]:
51+
// CHECK: br [[UNWIND_BB]]
52+
//
53+
// CHECK: [[AVAIL_RESUME_BB]]:
54+
// CHECK: br [[RETURN_BB]]
55+
//
56+
// CHECK: [[RETURN_BB]]:
57+
// CHECK: [[RESULT:%.*]] = tuple ()
58+
// CHECK: return [[RESULT]] : $()
59+
//
60+
// CHECK: [[UNWIND_BB]]:
61+
// CHECK: unwind
62+
63+
// -- Original definition of TopLevelStruct.property.read
64+
// CHECK-LABEL: sil [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV8propertyACvr : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
65+
@available(macOS 10.51, *)
66+
@_backDeploy(before: macOS 10.52)
67+
public var property: TopLevelStruct {
68+
_read { yield self }
69+
}
70+
}
71+
72+
// CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyAA14TopLevelStructVF : $@convention(thin) (TopLevelStruct) -> ()
73+
// CHECK: bb0([[STRUCT_ARG:%.*]] : $TopLevelStruct):
74+
@available(macOS 10.51, *)
75+
func caller(_ s: TopLevelStruct) {
76+
// -- Verify the thunk is called
77+
// CHECK: {{%.*}} = function_ref @$s11back_deploy14TopLevelStructV8propertyACvrTwb : $@yield_once @convention(method) (TopLevelStruct) -> @yields TopLevelStruct
78+
_ = s.property
79+
}

test/attr/Inputs/BackDeployHelper.swift

Lines changed: 62 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -31,63 +31,58 @@ public func v2APIsAreStripped() -> Bool {
3131
#endif // STRIP_V2_APIS
3232
}
3333

34+
/// Describes types that can be appended to.
35+
public protocol Appendable {
36+
associatedtype Element
37+
mutating func append(_ x: Element)
38+
}
39+
40+
/// Describes types that can be counted.
41+
public protocol Countable {
42+
var count: Int { get }
43+
}
44+
3445
public enum BadError: Error, Equatable {
3546
/// Indicates badness
3647
case bad
3748
}
3849

39-
/// A totally unnecessary wrapper for `Int` that adds mutability.
40-
public struct MutableInt {
41-
@usableFromInline
42-
internal var _value: Int
43-
44-
public init(_ value: Int) { _value = value }
45-
}
4650

47-
/// A totally unnecessary wrapper for `Int` that provides reference semantics.
48-
public class ReferenceInt {
51+
/// A totally unnecessary array type for `Int` elements.
52+
public struct IntArray {
4953
@usableFromInline
50-
internal var _value: Int
54+
internal var _values: [Int]
5155

52-
public init(_ value: Int) { _value = value }
56+
public init(_ values: [Int]) { _values = values }
5357
}
5458

55-
/// Describes types that can be incremented.
56-
public protocol Incrementable {
57-
associatedtype Operand
59+
extension IntArray: Appendable {
60+
public mutating func append(_ x: Int) {
61+
_values.append(x)
62+
}
63+
}
5864

59-
mutating func incrementByOne() -> String
60-
mutating func increment(by amount: Operand) -> Operand
65+
extension IntArray: Countable {
66+
public var count: Int { _values.count }
6167
}
6268

63-
extension MutableInt: Incrementable {
64-
public mutating func incrementByOne() -> String {
65-
_value += 1
66-
return String(_value)
67-
}
6869

69-
public mutating func increment(by amount: Int) -> Int {
70-
_value += amount
71-
return _value
72-
}
73-
}
70+
/// A totally unnecessary array type for `Int` elements with reference semantics.
71+
public class ReferenceIntArray {
72+
@usableFromInline
73+
internal var _values: [Int]
7474

75-
extension ReferenceInt: Incrementable {
76-
public func incrementByOne() -> String {
77-
_value += 1
78-
return String(_value)
79-
}
75+
public init(_ values: [Int]) { _values = values }
76+
}
8077

81-
public func increment(by amount: Int) -> Int {
82-
_value += amount
83-
return _value
78+
extension ReferenceIntArray: Appendable {
79+
public func append(_ x: Int) {
80+
_values.append(x)
8481
}
8582
}
8683

87-
extension Int {
88-
@usableFromInline internal func byte(at index: Int) -> UInt8 {
89-
UInt8(truncatingIfNeeded: self >> (index * 8))
90-
}
84+
extension ReferenceIntArray: Countable {
85+
public var count: Int { _values.count }
9186
}
9287

9388
// MARK: - Back deployed APIs
@@ -109,87 +104,79 @@ public func pleaseThrow(_ shouldThrow: Bool) throws -> Bool {
109104

110105
@available(BackDeploy 1.0, *)
111106
@_backDeploy(before: BackDeploy 2.0)
112-
public func genericIncrement<T: Incrementable>(
113-
_ x: inout T,
114-
by amount: T.Operand
115-
) -> T.Operand {
116-
return x.increment(by: amount)
107+
public func genericAppend<T: Appendable>(
108+
_ a: inout T,
109+
_ x: T.Element
110+
) {
111+
return a.append(x)
117112
}
118113

119114
@available(BackDeploy 1.0, *)
120115
@_backDeploy(before: BackDeploy 2.0)
121-
public func existentialIncrementByOne(_ x: inout any Incrementable) {
122-
testPrint(handle: #dsohandle, x.incrementByOne())
116+
public func existentialCount(_ c: any Countable) -> Int {
117+
c.count
123118
}
124119

125-
extension MutableInt {
120+
extension IntArray {
126121
@available(BackDeploy 1.0, *)
127122
@_backDeploy(before: BackDeploy 2.0)
128-
public var value: Int { _value }
123+
public var values: [Int] { _values }
129124

130125
@available(BackDeploy 1.0, *)
131126
@_backDeploy(before: BackDeploy 2.0)
132127
public func print() {
133-
// Tests recursive @_backDeploy since `value` is also @_backDeploy
134-
testPrint(handle: #dsohandle, String(value))
128+
// Tests recursive @_backDeploy since `values` is also @_backDeploy
129+
testPrint(handle: #dsohandle, values.description)
135130
}
136131

137132
@available(BackDeploy 1.0, *)
138133
@_backDeploy(before: BackDeploy 2.0)
139-
public static var zero: Self { MutableInt(0) }
140-
141-
@available(BackDeploy 1.0, *)
142-
@_backDeploy(before: BackDeploy 2.0)
143-
public mutating func decrement(by amount: Int) -> Int {
144-
_value -= amount
145-
return _value
146-
}
134+
public static var empty: Self { IntArray([]) }
147135

148136
@available(BackDeploy 1.0, *)
149137
@_backDeploy(before: BackDeploy 2.0)
150-
public func toIncrementable() -> any Incrementable { self }
138+
public func toCountable() -> any Countable { self }
151139

152140
@available(BackDeploy 1.0, *)
153141
@_backDeploy(before: BackDeploy 2.0)
154-
public subscript(byteAt index: Int) -> UInt8 { _value.byte(at: index) }
142+
public subscript(_ i: Int) -> Int {
143+
get { _values[i] }
144+
_modify { yield &_values[i] }
145+
}
155146
}
156147

157-
extension ReferenceInt {
148+
extension ReferenceIntArray {
158149
@available(BackDeploy 1.0, *)
159150
@_backDeploy(before: BackDeploy 2.0)
160-
public final var value: Int { _value }
151+
public final var values: [Int] { _values }
161152

162153
@available(BackDeploy 1.0, *)
163154
@_backDeploy(before: BackDeploy 2.0)
164155
public final func print() {
165-
// Tests recursive use of back deployed APIs, since `value` is also
166-
testPrint(handle: #dsohandle, String(value))
156+
// Tests recursive @_backDeploy since `values` is also @_backDeploy
157+
testPrint(handle: #dsohandle, values.description)
167158
}
168159

169160
@available(BackDeploy 1.0, *)
170161
@_backDeploy(before: BackDeploy 2.0)
171-
public final func copy() -> ReferenceInt {
172-
return ReferenceInt(value)
162+
public final func copy() -> ReferenceIntArray {
163+
return ReferenceIntArray(values)
173164
}
174165

175166
@available(BackDeploy 1.0, *)
176167
@_backDeploy(before: BackDeploy 2.0)
177-
public final class var zero: ReferenceInt { ReferenceInt(0) }
178-
179-
@available(BackDeploy 1.0, *)
180-
@_backDeploy(before: BackDeploy 2.0)
181-
public final func decrement(by amount: Int) -> Int {
182-
_value -= amount
183-
return _value
184-
}
168+
public final class var empty: ReferenceIntArray { ReferenceIntArray([]) }
185169

186170
@available(BackDeploy 1.0, *)
187171
@_backDeploy(before: BackDeploy 2.0)
188-
public final func toIncrementable() -> any Incrementable { self }
172+
public final func toCountable() -> any Countable { self }
189173

190174
@available(BackDeploy 1.0, *)
191175
@_backDeploy(before: BackDeploy 2.0)
192-
public final subscript(byteAt index: Int) -> UInt8 { _value.byte(at: index) }
176+
public final subscript(_ i: Int) -> Int {
177+
get { _values[i] }
178+
_modify { yield &_values[i] }
179+
}
193180
}
194181

195182
#endif // !STRIP_V2_APIS

0 commit comments

Comments
 (0)