Skip to content

Commit d062489

Browse files
committed
SILGen: Don't reference external property descriptors for @backDeployed properties.
The property descriptors of `@backDeployed` properties aren't available on OSes prior to the "back deployed before" OS version, so the descriptors cannot be referenced by clients that may run against older versions of the library that defines the property. See #59214 for prior art. Resolves rdar://106517386
1 parent d725e38 commit d062489

File tree

6 files changed

+54
-29
lines changed

6 files changed

+54
-29
lines changed

lib/SILGen/SILGen.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
351351
/// Emits a thunk from an actor function to a potentially distributed call.
352352
void emitDistributedThunk(SILDeclRef thunk);
353353

354+
/// Returns true if the given declaration must be referenced through a
355+
/// back deployment thunk in a context with the given resilience expansion.
356+
bool requiresBackDeploymentThunk(ValueDecl *decl,
357+
ResilienceExpansion expansion);
358+
354359
/// Emits a thunk that calls either the original function if it is available
355360
/// or otherwise calls a fallback variant of the function that was emitted
356361
/// into the client module.

lib/SILGen/SILGenApply.cpp

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -280,31 +280,6 @@ static void convertOwnershipConventionsGivenParamInfos(
280280
});
281281
}
282282

283-
static bool shouldApplyBackDeploymentThunk(ValueDecl *decl, ASTContext &ctx,
284-
ResilienceExpansion expansion) {
285-
auto backDeployBeforeVersion = decl->getBackDeployedBeforeOSVersion(ctx);
286-
if (!backDeployBeforeVersion)
287-
return false;
288-
289-
// If the context of the application is inlinable then we must always call the
290-
// back deployment thunk since we can't predict the deployment targets of
291-
// other modules.
292-
if (expansion != ResilienceExpansion::Maximal)
293-
return true;
294-
295-
// In resilient function bodies skip calling the back deployment thunk when
296-
// the deployment target is high enough that the ABI implementation of the
297-
// back deployed function is guaranteed to be available.
298-
auto deploymentAvailability = AvailabilityContext::forDeploymentTarget(ctx);
299-
auto declAvailability =
300-
AvailabilityContext(VersionRange::allGTE(*backDeployBeforeVersion));
301-
302-
if (deploymentAvailability.isContainedIn(declAvailability))
303-
return false;
304-
305-
return true;
306-
}
307-
308283
//===----------------------------------------------------------------------===//
309284
// Callee
310285
//===----------------------------------------------------------------------===//
@@ -1155,7 +1130,6 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11551130

11561131
SILDeclRef getDeclRefForStaticDispatchApply(DeclRefExpr *e) {
11571132
auto *afd = cast<AbstractFunctionDecl>(e->getDecl());
1158-
auto &ctx = SGF.getASTContext();
11591133

11601134
// A call to a `distributed` function may need to go through a thunk.
11611135
if (callSite && callSite->shouldApplyDistributedThunk()) {
@@ -1164,8 +1138,9 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11641138
}
11651139

11661140
// A call to `@backDeployed` function may need to go through a thunk.
1167-
if (shouldApplyBackDeploymentThunk(afd, ctx,
1168-
SGF.F.getResilienceExpansion())) {
1141+
1142+
if (SGF.SGM.requiresBackDeploymentThunk(afd,
1143+
SGF.F.getResilienceExpansion())) {
11691144
return SILDeclRef(afd).asBackDeploymentKind(
11701145
SILDeclRef::BackDeploymentKind::Thunk);
11711146
}
@@ -6717,7 +6692,7 @@ SILDeclRef SILGenModule::getAccessorDeclRef(AccessorDecl *accessor,
67176692
ResilienceExpansion expansion) {
67186693
auto declRef = SILDeclRef(accessor, SILDeclRef::Kind::Func);
67196694

6720-
if (shouldApplyBackDeploymentThunk(accessor, getASTContext(), expansion))
6695+
if (requiresBackDeploymentThunk(accessor, expansion))
67216696
return declRef.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Thunk);
67226697

67236698
return declRef.asForeign(requiresForeignEntryPoint(accessor));

lib/SILGen/SILGenBackDeploy.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,36 @@ static void emitBackDeployForwardApplyAndReturnOrThrow(
168168
SGF.B.createBranch(loc, SGF.ReturnDest.getBlock(), directResults);
169169
}
170170

171+
bool SILGenModule::requiresBackDeploymentThunk(ValueDecl *decl,
172+
ResilienceExpansion expansion) {
173+
auto &ctx = getASTContext();
174+
auto backDeployBeforeVersion = decl->getBackDeployedBeforeOSVersion(ctx);
175+
if (!backDeployBeforeVersion)
176+
return false;
177+
178+
switch (expansion) {
179+
case ResilienceExpansion::Minimal:
180+
// In a minimal resilience expansion we must always call the back deployment
181+
// thunk since we can't predict the deployment targets of the modules that
182+
// might inline the call.
183+
return true;
184+
case ResilienceExpansion::Maximal:
185+
// FIXME: We can skip thunking if we're in the same module.
186+
break;
187+
}
188+
189+
// Use of a back deployment thunk is unnecessary if the deployment target is
190+
// high enough that the ABI implementation of the back deployed declaration is
191+
// guaranteed to be available.
192+
auto deploymentAvailability = AvailabilityContext::forDeploymentTarget(ctx);
193+
auto declAvailability =
194+
AvailabilityContext(VersionRange::allGTE(*backDeployBeforeVersion));
195+
if (deploymentAvailability.isContainedIn(declAvailability))
196+
return false;
197+
198+
return true;
199+
}
200+
171201
void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
172202
// Generate code equivalent to:
173203
//

lib/SILGen/SILGenExpr.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3821,6 +3821,12 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
38213821
return false;
38223822
}
38233823

3824+
// Back deployed properties have the same restrictions as
3825+
// always-emit-into-client properties.
3826+
if (requiresBackDeploymentThunk(baseDecl, expansion)) {
3827+
return false;
3828+
}
3829+
38243830
// Properties that only dispatch via ObjC lookup do not have nor
38253831
// need property descriptors, since the selector identifies the
38263832
// storage.

test/SILGen/back_deployed_attr_accessor.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,11 @@ func caller(_ s: TopLevelStruct) {
4545
// -- Verify the thunk is called
4646
// CHECK: {{%.*}} = function_ref @$s11back_deploy14TopLevelStructV8propertyACvgTwb : $@convention(method) (TopLevelStruct) -> TopLevelStruct
4747
_ = s.property
48+
49+
// -- Verify key path
50+
// CHECK: {{%.*}} = keypath $KeyPath<TopLevelStruct, TopLevelStruct>, (root $TopLevelStruct; gettable_property $TopLevelStruct, id @$s11back_deploy14TopLevelStructV8propertyACvg : $@convention(method) (TopLevelStruct) -> TopLevelStruct, getter @$s11back_deploy14TopLevelStructV8propertyACvpACTK : $@convention(thin) (@in_guaranteed TopLevelStruct) -> @out TopLevelStruct)
51+
_ = \TopLevelStruct.property
4852
}
53+
54+
// CHECK-LABEL: sil shared [thunk] [ossa] @$s11back_deploy14TopLevelStructV8propertyACvpACTK : $@convention(thin) (@in_guaranteed TopLevelStruct) -> @out TopLevelStruct
55+
// CHECK: function_ref @$s11back_deploy14TopLevelStructV8propertyACvgTwb

test/attr/attr_backDeployed_evolution.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ do {
149149

150150
let empty = IntArray.empty
151151
precondition(empty.values == [])
152+
precondition(empty[keyPath: \.values] == [])
152153

153154
var array = IntArray([5])
154155

@@ -177,6 +178,7 @@ do {
177178
do {
178179
let defaulted = ReferenceIntArray()
179180
precondition(defaulted.values == [])
181+
precondition(defaulted[keyPath: \.values] == [])
180182

181183
let empty = ReferenceIntArray.empty
182184
precondition(empty.values == [])

0 commit comments

Comments
 (0)