Skip to content

Commit 3040221

Browse files
committed
SILGen/Sema: Add @_backDeploy support for constructors.
Resolves rdar://94436652
1 parent 0e039da commit 3040221

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
@@ -3311,6 +3311,14 @@ ERROR(attr_incompatible_with_non_final,none,
33113311
"'%0' cannot be applied to a non-final %1",
33123312
(DeclAttribute, DescriptiveDeclKind))
33133313

3314+
ERROR(attr_incompatible_with_override,none,
3315+
"'%0' cannot be combined with 'override'",
3316+
(DeclAttribute))
3317+
3318+
ERROR(attr_incompatible_with_objc,none,
3319+
"'%0' must not be used on an '@objc' %1",
3320+
(DeclAttribute, DescriptiveDeclKind))
3321+
33143322
ERROR(final_not_on_accessors,none,
33153323
"'final' cannot be applied to accessors, it must be put on the "
33163324
"%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
@@ -996,13 +996,15 @@ bool SILDeclRef::isDistributedThunk() const {
996996
bool SILDeclRef::isBackDeploymentFallback() const {
997997
if (backDeploymentKind != BackDeploymentKind::Fallback)
998998
return false;
999-
return kind == Kind::Func;
999+
return kind == Kind::Func || kind == Kind::Initializer ||
1000+
kind == Kind::Allocator;
10001001
}
10011002

10021003
bool SILDeclRef::isBackDeploymentThunk() const {
10031004
if (backDeploymentKind != BackDeploymentKind::Thunk)
10041005
return false;
1005-
return kind == Kind::Func;
1006+
return kind == Kind::Func || kind == Kind::Initializer ||
1007+
kind == Kind::Allocator;
10061008
}
10071009

10081010
/// Use the Clang importer to mangle a Clang declaration.

lib/SILGen/SILGen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -884,12 +884,12 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
884884
(decl->isDesignatedInit() ||
885885
decl->isObjC())) {
886886
preEmitFunction(constant, f, decl);
887-
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
887+
PrettyStackTraceSILFunction X("silgen emitClassConstructorAllocator", f);
888888
SILGenFunction(*this, *f, decl).emitClassConstructorAllocator(decl);
889889
postEmitFunction(constant, f);
890890
} else {
891891
preEmitFunction(constant, f, decl);
892-
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
892+
PrettyStackTraceSILFunction X("silgen emitValueConstructor", f);
893893
f->createProfiler(constant);
894894
SILGenFunction(*this, *f, decl).emitValueConstructor(decl);
895895
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
@@ -4425,12 +4425,6 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
44254425
D->getDescriptiveKind());
44264426
}
44274427

4428-
// @objc conflicts with back deployment since it implies dynamic dispatch.
4429-
if (auto *OA = D->getAttrs().getAttribute<ObjCAttr>()) {
4430-
diagnose(OA->getLocation(), diag::attr_incompatible_with_back_deploy, OA,
4431-
D->getDescriptiveKind());
4432-
}
4433-
44344428
// Only functions, methods, computed properties, and subscripts are
44354429
// back-deployable, so D should be ValueDecl.
44364430
auto *VD = cast<ValueDecl>(D);
@@ -4443,13 +4437,43 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
44434437
if (diagnoseAndRemoveAttrIfDeclIsNonPublic(Attr, /*isError=*/true))
44444438
continue;
44454439

4446-
// Back deployment isn't compatible with dynamic dispatch.
4447-
if (VD->isSyntacticallyOverridable()) {
4440+
if (isa<DestructorDecl>(D)) {
4441+
diagnoseAndRemoveAttr(Attr, diag::attr_invalid_on_decl_kind, Attr,
4442+
D->getDescriptiveKind());
4443+
continue;
4444+
}
4445+
4446+
if (VD->isObjC()) {
4447+
diagnoseAndRemoveAttr(Attr, diag::attr_incompatible_with_objc, Attr,
4448+
D->getDescriptiveKind());
4449+
continue;
4450+
}
4451+
4452+
// If the decl isn't effectively final then it could be invoked via dynamic
4453+
// dispatch.
4454+
if (D->isSyntacticallyOverridable()) {
44484455
diagnose(Attr->getLocation(), diag::attr_incompatible_with_non_final,
44494456
Attr, D->getDescriptiveKind());
44504457
continue;
44514458
}
44524459

4460+
// Some methods declared in classes aren't syntactically overridable but
4461+
// still may have vtable entries, implying dynamic dispatch.
4462+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
4463+
if (isa<ClassDecl>(D->getDeclContext()) && AFD->needsNewVTableEntry()) {
4464+
diagnose(Attr->getLocation(), diag::attr_incompatible_with_non_final,
4465+
Attr, D->getDescriptiveKind());
4466+
continue;
4467+
}
4468+
}
4469+
4470+
// If the decl is final but overrides another decl, that also indicates it
4471+
// could be invoked via dynamic dispatch.
4472+
if (VD->getOverriddenDecl()) {
4473+
diagnoseAndRemoveAttr(Attr, diag::attr_incompatible_with_override, Attr);
4474+
continue;
4475+
}
4476+
44534477
if (auto *VarD = dyn_cast<VarDecl>(D)) {
44544478
// There must be a function body to back deploy so for vars we require
44554479
// that they be computed in order to allow back deployment.
@@ -4459,12 +4483,6 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
44594483
}
44604484
}
44614485

4462-
if (isa<DestructorDecl>(D) || isa<ConstructorDecl>(D)) {
4463-
diagnoseAndRemoveAttr(Attr, diag::attr_invalid_on_decl_kind, Attr,
4464-
D->getDescriptiveKind());
4465-
continue;
4466-
}
4467-
44684486
auto AtLoc = Attr->AtLoc;
44694487
auto Platform = Attr->Platform;
44704488

lib/Sema/TypeCheckDecl.cpp

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

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)