Skip to content

Commit 53d7773

Browse files
authored
Merge pull request swiftlang#63142 from tshortli/back-deploy-attr-on-initializers
SILGen/Sema: Add `@_backDeploy` support for constructors
2 parents c6c2310 + 5b942eb commit 53d7773

11 files changed

+303
-38
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3351,6 +3351,14 @@ ERROR(attr_incompatible_with_non_final,none,
33513351
"'%0' cannot be applied to a non-final %1",
33523352
(DeclAttribute, DescriptiveDeclKind))
33533353

3354+
ERROR(attr_incompatible_with_override,none,
3355+
"'%0' cannot be combined with 'override'",
3356+
(DeclAttribute))
3357+
3358+
ERROR(attr_incompatible_with_objc,none,
3359+
"'%0' must not be used on an '@objc' %1",
3360+
(DeclAttribute, DescriptiveDeclKind))
3361+
33543362
ERROR(final_not_on_accessors,none,
33553363
"'final' cannot be applied to accessors, it must be put on the "
33563364
"%select{var|let|subscript}0", (unsigned))

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,13 +1014,15 @@ bool SILDeclRef::isDistributedThunk() const {
10141014
bool SILDeclRef::isBackDeploymentFallback() const {
10151015
if (backDeploymentKind != BackDeploymentKind::Fallback)
10161016
return false;
1017-
return kind == Kind::Func;
1017+
return kind == Kind::Func || kind == Kind::Initializer ||
1018+
kind == Kind::Allocator;
10181019
}
10191020

10201021
bool SILDeclRef::isBackDeploymentThunk() const {
10211022
if (backDeploymentKind != BackDeploymentKind::Thunk)
10221023
return false;
1023-
return kind == Kind::Func;
1024+
return kind == Kind::Func || kind == Kind::Initializer ||
1025+
kind == Kind::Allocator;
10241026
}
10251027

10261028
bool SILDeclRef::isRuntimeAccessibleFunction() const {

lib/SILGen/SILGen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,12 +885,12 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
885885
(decl->isDesignatedInit() ||
886886
decl->isObjC())) {
887887
preEmitFunction(constant, f, decl);
888-
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
888+
PrettyStackTraceSILFunction X("silgen emitClassConstructorAllocator", f);
889889
SILGenFunction(*this, *f, decl).emitClassConstructorAllocator(decl);
890890
postEmitFunction(constant, f);
891891
} else {
892892
preEmitFunction(constant, f, decl);
893-
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
893+
PrettyStackTraceSILFunction X("silgen emitValueConstructor", f);
894894
f->createProfiler(constant);
895895
SILGenFunction(*this, *f, decl).emitValueConstructor(decl);
896896
postEmitFunction(constant, f);

lib/SILGen/SILGenBackDeploy.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ static void extractAllElements(SILValue val, SILLocation loc,
3939
builder.emitDestructureValueOperation(loc, val, result);
4040
}
4141

42+
static Type getResultInterfaceType(AbstractFunctionDecl *AFD) {
43+
if (auto *FD = dyn_cast<FuncDecl>(AFD))
44+
return FD->getResultInterfaceType();
45+
46+
if (auto *CD = dyn_cast<ConstructorDecl>(AFD))
47+
return CD->getResultInterfaceType();
48+
49+
llvm_unreachable("Unhandled AbstractFunctionDecl type");
50+
}
51+
4252
/// Emit the following branch SIL instruction:
4353
/// \verbatim
4454
/// if #available(OSVersion) {
@@ -174,7 +184,7 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
174184
auto loc = thunk.getAsRegularLocation();
175185
loc.markAutoGenerated();
176186
Scope scope(Cleanups, CleanupLocation(loc));
177-
auto FD = cast<FuncDecl>(thunk.getDecl());
187+
auto AFD = cast<AbstractFunctionDecl>(thunk.getDecl());
178188

179189
F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(thunk));
180190

@@ -197,8 +207,8 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
197207
paramsForForwarding.emplace_back(param.forward(*this));
198208
}
199209

200-
prepareEpilog(FD->getResultInterfaceType(), FD->hasThrows(),
201-
CleanupLocation(FD));
210+
prepareEpilog(getResultInterfaceType(AFD), AFD->hasThrows(),
211+
CleanupLocation(AFD));
202212

203213
SILBasicBlock *availableBB = createBasicBlock("availableBB");
204214
SILBasicBlock *unavailableBB = createBasicBlock("unavailableBB");
@@ -208,7 +218,7 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
208218
// } else {
209219
// <unavailableBB>
210220
// }
211-
emitBackDeployIfAvailableCondition(*this, FD, loc, availableBB,
221+
emitBackDeployIfAvailableCondition(*this, AFD, loc, availableBB,
212222
unavailableBB);
213223

214224
// <availableBB>:
@@ -217,7 +227,7 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
217227
B.emitBlock(availableBB);
218228
SILDeclRef original =
219229
thunk.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::None);
220-
emitBackDeployForwardApplyAndReturnOrThrow(*this, FD, loc, original,
230+
emitBackDeployForwardApplyAndReturnOrThrow(*this, AFD, loc, original,
221231
paramsForForwarding);
222232
}
223233

@@ -227,9 +237,9 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
227237
B.emitBlock(unavailableBB);
228238
SILDeclRef fallback =
229239
thunk.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Fallback);
230-
emitBackDeployForwardApplyAndReturnOrThrow(*this, FD, loc, fallback,
240+
emitBackDeployForwardApplyAndReturnOrThrow(*this, AFD, loc, fallback,
231241
paramsForForwarding);
232242
}
233243

234-
emitEpilog(FD);
244+
emitEpilog(AFD);
235245
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4525,12 +4525,6 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
45254525
D->getDescriptiveKind());
45264526
}
45274527

4528-
// @objc conflicts with back deployment since it implies dynamic dispatch.
4529-
if (auto *OA = D->getAttrs().getAttribute<ObjCAttr>()) {
4530-
diagnose(OA->getLocation(), diag::attr_incompatible_with_back_deploy, OA,
4531-
D->getDescriptiveKind());
4532-
}
4533-
45344528
// Only functions, methods, computed properties, and subscripts are
45354529
// back-deployable, so D should be ValueDecl.
45364530
auto *VD = cast<ValueDecl>(D);
@@ -4543,13 +4537,43 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
45434537
if (diagnoseAndRemoveAttrIfDeclIsNonPublic(Attr, /*isError=*/true))
45444538
continue;
45454539

4546-
// Back deployment isn't compatible with dynamic dispatch.
4547-
if (VD->isSyntacticallyOverridable()) {
4540+
if (isa<DestructorDecl>(D)) {
4541+
diagnoseAndRemoveAttr(Attr, diag::attr_invalid_on_decl_kind, Attr,
4542+
D->getDescriptiveKind());
4543+
continue;
4544+
}
4545+
4546+
if (VD->isObjC()) {
4547+
diagnoseAndRemoveAttr(Attr, diag::attr_incompatible_with_objc, Attr,
4548+
D->getDescriptiveKind());
4549+
continue;
4550+
}
4551+
4552+
// If the decl isn't effectively final then it could be invoked via dynamic
4553+
// dispatch.
4554+
if (D->isSyntacticallyOverridable()) {
45484555
diagnose(Attr->getLocation(), diag::attr_incompatible_with_non_final,
45494556
Attr, D->getDescriptiveKind());
45504557
continue;
45514558
}
45524559

4560+
// Some methods declared in classes aren't syntactically overridable but
4561+
// still may have vtable entries, implying dynamic dispatch.
4562+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
4563+
if (isa<ClassDecl>(D->getDeclContext()) && AFD->needsNewVTableEntry()) {
4564+
diagnose(Attr->getLocation(), diag::attr_incompatible_with_non_final,
4565+
Attr, D->getDescriptiveKind());
4566+
continue;
4567+
}
4568+
}
4569+
4570+
// If the decl is final but overrides another decl, that also indicates it
4571+
// could be invoked via dynamic dispatch.
4572+
if (VD->getOverriddenDecl()) {
4573+
diagnoseAndRemoveAttr(Attr, diag::attr_incompatible_with_override, Attr);
4574+
continue;
4575+
}
4576+
45534577
if (auto *VarD = dyn_cast<VarDecl>(D)) {
45544578
// There must be a function body to back deploy so for vars we require
45554579
// that they be computed in order to allow back deployment.
@@ -4559,12 +4583,6 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
45594583
}
45604584
}
45614585

4562-
if (isa<DestructorDecl>(D) || isa<ConstructorDecl>(D)) {
4563-
diagnoseAndRemoveAttr(Attr, diag::attr_invalid_on_decl_kind, Attr,
4564-
D->getDescriptiveKind());
4565-
continue;
4566-
}
4567-
45684586
auto AtLoc = Attr->AtLoc;
45694587
auto Platform = Attr->Platform;
45704588

lib/Sema/TypeCheckDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,9 @@ bool
10431043
NeedsNewVTableEntryRequest::evaluate(Evaluator &evaluator,
10441044
AbstractFunctionDecl *decl) const {
10451045
auto *dc = decl->getDeclContext();
1046+
1047+
// FIXME: This is mysterious and seems wrong. However, changing it to return
1048+
// false (as it seems like it should) breaks a couple Serialization tests.
10461049
if (!isa<ClassDecl>(dc))
10471050
return true;
10481051

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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<T> {
10+
@usableFromInline var t: T
11+
12+
// -- Fallback definition for TopLevelStruct.init(_:)
13+
// CHECK-LABEL: sil non_abi [serialized] [ossa] @$s11back_deploy14TopLevelStructVyACyxGxcfCTwB : $@convention(method) <T> (@in T, @thin TopLevelStruct<T>.Type) -> @out TopLevelStruct<T>
14+
// CHECK: bb0([[SELF_OUT:%.*]] : $*TopLevelStruct<T>, [[T_ARG:%.*]] : $*T, [[METATYPE_ARG:%.*]] : $@thin TopLevelStruct<T>.Type):
15+
// CHECK: [[SELF:%.*]] = alloc_box $<τ_0_0> { var TopLevelStruct<τ_0_0> } <T>, var, name "self"
16+
// CHECK: [[MARKED_SELF:%.*]] = mark_uninitialized [rootself] [[SELF]] : $<τ_0_0> { var TopLevelStruct<τ_0_0> } <T>
17+
// CHECK: [[BORROWED_SELF:%.*]] = begin_borrow [lexical] [[MARKED_SELF]] : $<τ_0_0> { var TopLevelStruct<τ_0_0> } <T>
18+
// CHECK: [[BOX:%.*]] = project_box [[BORROWED_SELF]] : $<τ_0_0> { var TopLevelStruct<τ_0_0> } <T>, 0
19+
// CHECK: [[T_STACK:%.*]] = alloc_stack $T
20+
// CHECK: copy_addr [[T_ARG]] to [init] [[T_STACK]] : $*T
21+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] [[BOX]] : $*TopLevelStruct<T>
22+
// CHECK: [[ELEM_ADDR:%.*]] = struct_element_addr [[ACCESS]] : $*TopLevelStruct<T>, #TopLevelStruct.t
23+
// CHECK: copy_addr [take] [[T_STACK]] to [[ELEM_ADDR]] : $*T
24+
// CHECK: end_access [[ACCESS]] : $*TopLevelStruct<T>
25+
// CHECK: dealloc_stack [[T_STACK]] : $*T
26+
// CHECK: copy_addr [[BOX]] to [init] [[SELF_OUT]] : $*TopLevelStruct<T>
27+
// CHECK: destroy_addr [[T_ARG]] : $*T
28+
// CHECK: end_borrow [[BORROWED_SELF]] : $<τ_0_0> { var TopLevelStruct<τ_0_0> } <T>
29+
// CHECK: destroy_value [[MARKED_SELF]] : $<τ_0_0> { var TopLevelStruct<τ_0_0> } <T>
30+
// CHECK: [[RESULT:%.*]] = tuple ()
31+
// CHECK: return [[RESULT]] : $()
32+
33+
// -- Back deployment thunk for TopLevelStruct.init(_:)
34+
// CHECK-LABEL: sil non_abi [serialized] [thunk] [ossa] @$s11back_deploy14TopLevelStructVyACyxGxcfCTwb : $@convention(method) <T> (@in T, @thin TopLevelStruct<T>.Type) -> @out TopLevelStruct<T>
35+
// CHECK: bb0([[SELF_OUT:%.*]] : $*TopLevelStruct<T>, [[T_ARG:%.*]] : $*T, [[METATYPE_ARG:%.*]] : $@thin TopLevelStruct<T>.Type):
36+
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10
37+
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 52
38+
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0
39+
// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
40+
// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
41+
// CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]]
42+
//
43+
// CHECK: [[UNAVAIL_BB]]:
44+
// CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy14TopLevelStructVyACyxGxcfCTwB : $@convention(method) <τ_0_0> (@in τ_0_0, @thin TopLevelStruct<τ_0_0>.Type) -> @out TopLevelStruct<τ_0_0>
45+
// CHECK: {{%.*}} = apply [[FALLBACKFN]]<T>([[SELF_OUT]], [[T_ARG]], [[METATYPE_ARG]]) : $@convention(method) <τ_0_0> (@in τ_0_0, @thin TopLevelStruct<τ_0_0>.Type) -> @out TopLevelStruct<τ_0_0>
46+
// CHECK: br [[RETURN_BB:bb[0-9]+]]
47+
//
48+
// CHECK: [[AVAIL_BB]]:
49+
// CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy14TopLevelStructVyACyxGxcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin TopLevelStruct<τ_0_0>.Type) -> @out TopLevelStruct<τ_0_0>
50+
// CHECK: {{%.*}} = apply [[ORIGFN]]<T>([[SELF_OUT]], [[T_ARG]], [[METATYPE_ARG]]) : $@convention(method) <τ_0_0> (@in τ_0_0, @thin TopLevelStruct<τ_0_0>.Type) -> @out TopLevelStruct<τ_0_0>
51+
// CHECK: br [[RETURN_BB]]
52+
//
53+
// CHECK: [[RETURN_BB]]
54+
// CHECK: [[RESULT:%.*]] = tuple ()
55+
// CHECK: return [[RESULT]] : $()
56+
57+
// -- Original definition of TopLevelStruct.init(_:)
58+
// CHECK-LABEL: sil [available 10.52] [ossa] @$s11back_deploy14TopLevelStructVyACyxGxcfC : $@convention(method) <T> (@in T, @thin TopLevelStruct<T>.Type) -> @out TopLevelStruct<T>
59+
@available(macOS 10.51, *)
60+
@_backDeploy(before: macOS 10.52)
61+
public init(_ t: T) {
62+
self.t = t
63+
}
64+
}
65+
66+
struct S {}
67+
68+
// CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyAA1SVF : $@convention(thin) (S) -> ()
69+
// CHECK: bb0([[STRUCT_ARG:%.*]] : $S):
70+
@available(macOS 10.51, *)
71+
func caller(_ s: S) {
72+
// -- Verify the thunk is called
73+
// CHECK: {{%.*}} = function_ref @$s11back_deploy14TopLevelStructVyACyxGxcfCTwb : $@convention(method) <τ_0_0> (@in τ_0_0, @thin TopLevelStruct<τ_0_0>.Type) -> @out TopLevelStruct<τ_0_0>
74+
_ = TopLevelStruct(s)
75+
}
76+

test/attr/Inputs/BackDeployHelper.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public protocol Countable {
4646
public enum BadError: Error, Equatable {
4747
/// Indicates badness
4848
case bad
49+
50+
/// Even worse
51+
case reallyBad
4952
}
5053

5154

@@ -123,6 +126,10 @@ extension IntArray {
123126
@_backDeploy(before: BackDeploy 2.0)
124127
public var values: [Int] { _values }
125128

129+
@available(BackDeploy 1.0, *)
130+
@_backDeploy(before: BackDeploy 2.0)
131+
public init() { self.init([]) }
132+
126133
@available(BackDeploy 1.0, *)
127134
@_backDeploy(before: BackDeploy 2.0)
128135
public func print() {
@@ -161,6 +168,10 @@ extension IntArray {
161168
}
162169

163170
extension ReferenceIntArray {
171+
@available(BackDeploy 1.0, *)
172+
@_backDeploy(before: BackDeploy 2.0)
173+
public convenience init() { self.init([]) }
174+
164175
@available(BackDeploy 1.0, *)
165176
@_backDeploy(before: BackDeploy 2.0)
166177
public final var values: [Int] { _values }
@@ -216,4 +227,16 @@ extension Array {
216227
}
217228
}
218229

230+
extension BadError {
231+
@available(BackDeploy 1.0, *)
232+
@_backDeploy(before: BackDeploy 2.0)
233+
public init?(fromEmoji emoji: Character) {
234+
switch emoji {
235+
case "❗️": self = .bad
236+
case "‼️": self = .reallyBad
237+
default: return nil
238+
}
239+
}
240+
}
241+
219242
#endif // !STRIP_V2_APIS

0 commit comments

Comments
 (0)