Skip to content

Commit 10c37c6

Browse files
committed
SILGen: Work around for stored property keypath components not supporting generic resilient classes
A keypath component for a stored property can take one of several forms: - The property offset is known to be constant at compile-time. This is used in the simplest cases for classes and structs. - The property offset is not constant, but can be loaded from a global. This is used for classes that require runtime resilient layout, but where the offsets do not depend on the generic context. - The property offset is not constant, and must be loaded from metadata. This is the case where the offset depends on the generic context. Here, we were only set up to load it from a fixed offset in the metadata. This works for generic structs, or generic classes where the superclass chain does not cross a resilience boundary. However, if a resilience boundary is crossed, the offset of the field offset in the metadata must itself be obtained at runtime by adding a constant to a value loaded from a global. This case is not supported by the current keypath ABI due to an oversight. I filed <rdar://problem/59777983> to track extending the ABI to handle this more elegantly in the future. Fixes <rdar://problem/59617119>.
1 parent 49017c8 commit 10c37c6

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

lib/IRGen/GenKeyPath.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ emitKeyPathComponent(IRGenModule &IGM,
794794

795795
switch (getClassFieldAccess(IGM, loweredBaseContextTy, property)) {
796796
case FieldAccess::ConstantDirect: {
797-
// Known constant fixed offset.
797+
// Known compile-time constant field offset.
798798
auto offset = tryEmitConstantClassFragilePhysicalMemberOffset(
799799
IGM, loweredClassTy, property);
800800
assert(offset && "no constant offset for ConstantDirect field?!");
@@ -804,6 +804,9 @@ emitKeyPathComponent(IRGenModule &IGM,
804804
case FieldAccess::NonConstantDirect: {
805805
// A constant offset that's determined at class realization time.
806806
// We have to load the offset from a global ivar.
807+
//
808+
// This means the field offset is constant at runtime, but is not known
809+
// at compile time.
807810
auto header = KeyPathComponentHeader
808811
::forClassComponentWithUnresolvedIndirectOffset(property->isLet());
809812
fields.addInt32(header.getData());
@@ -814,10 +817,15 @@ emitKeyPathComponent(IRGenModule &IGM,
814817
}
815818
case FieldAccess::ConstantIndirect: {
816819
// An offset that depends on the instance's generic parameterization,
817-
// but whose field offset is at a known vtable offset.
820+
// but whose field offset is at a known metadata offset.
818821
auto header = KeyPathComponentHeader
819822
::forClassComponentWithUnresolvedFieldOffset(property->isLet());
820823
fields.addInt32(header.getData());
824+
825+
// FIXME: This doesn't support classes with resilient ancestry, because
826+
// the offset into the metadata is itself not constant.
827+
//
828+
// SILGen emits the descriptor as a computed property in this case.
821829
auto fieldOffset = getClassFieldOffsetOffset(
822830
IGM, loweredClassTy.getClassOrBoundGenericClass(), property);
823831
fields.addInt32(fieldOffset.getValue());

lib/SILGen/SILGen.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,6 +1353,24 @@ SILGenModule::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl,
13531353
// unowned properties.
13541354
if (decl->getInterfaceType()->is<ReferenceStorageType>())
13551355
return false;
1356+
1357+
// If the field offset depends on the generic instantiation, we have to
1358+
// load it from metadata when instantiating the keypath component.
1359+
//
1360+
// However the metadata offset itself will not be fixed if the superclass
1361+
// is resilient. Fall back to treating the property as computed in this
1362+
// case.
1363+
//
1364+
// See the call to getClassFieldOffsetOffset() inside
1365+
// emitKeyPathComponent().
1366+
if (auto *parentClass = dyn_cast<ClassDecl>(decl->getDeclContext())) {
1367+
if (parentClass->isGeneric()) {
1368+
auto ancestry = parentClass->checkAncestry();
1369+
if (ancestry.contains(AncestryFlags::ResilientOther))
1370+
return false;
1371+
}
1372+
}
1373+
13561374
// If the stored value would need to be reabstracted in fully opaque
13571375
// context, then we have to treat the component as computed.
13581376
auto componentObjTy = decl->getValueInterfaceType();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift
3+
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class -I %t %S/../Inputs/resilient_class.swift
4+
5+
// RUN: %target-swift-emit-silgen %s -I %t -enable-library-evolution | %FileCheck %s
6+
7+
import resilient_class
8+
9+
open class MySubclass<T> : ResilientOutsideParent {
10+
public final var storedProperty: T? = nil
11+
}
12+
13+
open class ConcreteSubclass : MySubclass<Int> {
14+
public final var anotherStoredProperty: Int? = nil
15+
}
16+
17+
// CHECK-LABEL: sil shared [thunk] [ossa] @$s26keypaths_resilient_generic10MySubclassC14storedPropertyxSgvplACyxGTK : $@convention(thin) <T> (@in_guaranteed MySubclass<T>) -> @out Optional<T> {
18+
19+
// CHECK-LABEL: sil shared [thunk] [ossa] @$s26keypaths_resilient_generic10MySubclassC14storedPropertyxSgvplACyxGTk : $@convention(thin) <T> (@in_guaranteed Optional<T>, @in_guaranteed MySubclass<T>) -> () {
20+
21+
// CHECK: sil_property #MySubclass.storedProperty<τ_0_0> (
22+
// CHECK-SAME: settable_property $Optional<τ_0_0>,
23+
// CHECK-SAME: id ##MySubclass.storedProperty,
24+
// CHECK-SAME: getter @$s26keypaths_resilient_generic10MySubclassC14storedPropertyxSgvplACyxGTK : $@convention(thin) <τ_0_0> (@in_guaranteed MySubclass<τ_0_0>) -> @out Optional<τ_0_0>,
25+
// CHECK-SAME: setter @$s26keypaths_resilient_generic10MySubclassC14storedPropertyxSgvplACyxGTk : $@convention(thin) <τ_0_0> (@in_guaranteed Optional<τ_0_0>, @in_guaranteed MySubclass<τ_0_0>) -> ()
26+
// CHECK-SAME: )
27+
28+
// CHECK: sil_property #ConcreteSubclass.anotherStoredProperty (
29+
// CHECK-SAME: stored_property #ConcreteSubclass.anotherStoredProperty : $Optional<Int>
30+
// CHECK-SAME: )

0 commit comments

Comments
 (0)