Skip to content

Commit 665bcb9

Browse files
authored
Merge pull request #18100 from jckarter/keypath-external-irgen
IRGen and runtime support for key path resilience.
2 parents 23df594 + 4098aa0 commit 665bcb9

27 files changed

+1459
-1572
lines changed

include/swift/ABI/KeyPath.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ class KeyPathComponentHeader {
190190
bool resolvedID) {
191191
return KeyPathComponentHeader(
192192
(_SwiftKeyPathComponentHeader_ComputedTag
193-
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
193+
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
194194
| (kind != GetOnly
195195
? _SwiftKeyPathComponentHeader_ComputedSettableFlag : 0)
196196
| (kind == SettableMutating
@@ -205,10 +205,14 @@ class KeyPathComponentHeader {
205205
}
206206

207207
constexpr static KeyPathComponentHeader
208-
forExternalComponent() {
209-
return KeyPathComponentHeader(
210-
_SwiftKeyPathComponentHeader_ExternalTag
211-
<< _SwiftKeyPathComponentHeader_DiscriminatorShift);
208+
forExternalComponent(unsigned numSubstitutions) {
209+
return assert(numSubstitutions <
210+
(1u << _SwiftKeyPathComponentHeader_DiscriminatorShift) - 1u
211+
&& "too many substitutions"),
212+
KeyPathComponentHeader(
213+
(_SwiftKeyPathComponentHeader_ExternalTag
214+
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
215+
| numSubstitutions);
212216
}
213217

214218
constexpr uint32_t getData() const { return Data; }

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4438,6 +4438,10 @@ class AbstractStorageDecl : public ValueDecl {
44384438
BehaviorRecord *getMutableBehavior() {
44394439
return BehaviorInfo.getPointer();
44404440
}
4441+
4442+
/// True if the storage exports a property descriptor for key paths in
4443+
/// other modules.
4444+
bool exportsPropertyDescriptor() const;
44414445

44424446
// Implement isa/cast/dyncast/etc.
44434447
static bool classof(const Decl *D) {

lib/IRGen/GenKeyPath.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,27 @@ emitMetadataGeneratorForKeyPath(IRGenModule &IGM,
702702
});
703703
};
704704

705+
static llvm::Function *
706+
emitWitnessTableGeneratorForKeyPath(IRGenModule &IGM,
707+
CanType type,
708+
ProtocolConformanceRef conformance,
709+
GenericEnvironment *genericEnv,
710+
ArrayRef<GenericRequirement> requirements) {
711+
// TODO: Use the standard conformance accessor when there are no arguments
712+
// and the conformance accessor is defined.
713+
return emitGeneratorForKeyPath(IGM, "keypath_get_witness_table", type,
714+
IGM.WitnessTablePtrTy,
715+
genericEnv, requirements,
716+
[&](IRGenFunction &IGF, CanType substType) {
717+
if (type->hasTypeParameter())
718+
conformance = conformance.subst(type,
719+
QueryInterfaceTypeSubstitutions(genericEnv),
720+
LookUpConformanceInSignature(*genericEnv->getGenericSignature()));
721+
auto ret = emitWitnessTableRef(IGF, substType, conformance);
722+
IGF.Builder.CreateRet(ret);
723+
});
724+
}
725+
705726
static void
706727
emitKeyPathComponent(IRGenModule &IGM,
707728
ConstantStructBuilder &fields,
@@ -815,6 +836,46 @@ emitKeyPathComponent(IRGenModule &IGM,
815836
}
816837
case KeyPathPatternComponent::Kind::GettableProperty:
817838
case KeyPathPatternComponent::Kind::SettableProperty: {
839+
// If the component references an external property, encode that in a
840+
// header before the local attempt header, so that we can consult the
841+
// external descriptor at instantiation time.
842+
if (auto externalDecl = component.getExternalDecl()) {
843+
SmallVector<llvm::Constant *, 4> externalSubArgs;
844+
auto componentSig = externalDecl->getInnermostDeclContext()
845+
->getGenericSignatureOfContext();
846+
auto subs = component.getExternalSubstitutions();
847+
if (!subs.empty()) {
848+
enumerateGenericSignatureRequirements(
849+
componentSig->getCanonicalSignature(),
850+
[&](GenericRequirement reqt) {
851+
auto substType = reqt.TypeParameter.subst(subs)
852+
->getCanonicalType();
853+
if (!reqt.Protocol) {
854+
// Type requirement.
855+
externalSubArgs.push_back(
856+
emitMetadataGeneratorForKeyPath(IGM, substType,
857+
genericEnv, requirements));
858+
} else {
859+
// Protocol requirement.
860+
auto conformance = subs.lookupConformance(
861+
reqt.TypeParameter->getCanonicalType(), reqt.Protocol);
862+
externalSubArgs.push_back(
863+
emitWitnessTableGeneratorForKeyPath(IGM, substType,
864+
*conformance,
865+
genericEnv, requirements));
866+
}
867+
});
868+
}
869+
fields.addInt32(
870+
KeyPathComponentHeader::forExternalComponent(externalSubArgs.size())
871+
.getData());
872+
fields.addAlignmentPadding(IGM.getPointerAlignment());
873+
auto descriptor = IGM.getAddrOfPropertyDescriptor(externalDecl);
874+
fields.add(descriptor);
875+
for (auto *arg : externalSubArgs)
876+
fields.add(arg);
877+
}
878+
818879
// Encode the settability.
819880
bool settable = kind == KeyPathPatternComponent::Kind::SettableProperty;
820881
KeyPathComponentHeader::ComputedPropertyKind componentKind;

lib/SIL/SIL.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,49 @@ bool SILModule::isTypeABIAccessible(SILType type) {
208208
// Otherwise, we need to be able to fetch layout-metadata for the type.
209209
return isTypeMetadataForLayoutAccessible(*this, type);
210210
}
211+
212+
bool AbstractStorageDecl::exportsPropertyDescriptor() const {
213+
// The storage needs a descriptor if it sits at a module's ABI boundary,
214+
// meaning it has public linkage.
215+
216+
// TODO: Global and static properties ought to eventually be referenceable
217+
// as key paths from () or T.Type too.
218+
if (!getDeclContext()->isTypeContext() || isStatic())
219+
return false;
220+
221+
// Any property that's potentially resilient should have accessors
222+
// synthesized.
223+
if (!getGetter())
224+
return false;
225+
226+
// If the getter is mutating, we cannot form a keypath to it at all.
227+
if (isGetterMutating())
228+
return false;
229+
230+
// TODO: If previous versions of an ABI-stable binary needed the descriptor,
231+
// then we still do.
232+
233+
auto getter = SILDeclRef(getGetter());
234+
auto getterLinkage = getter.getLinkage(ForDefinition);
235+
236+
switch (getterLinkage) {
237+
case SILLinkage::Public:
238+
case SILLinkage::PublicNonABI:
239+
// We may need a descriptor.
240+
break;
241+
242+
case SILLinkage::Shared:
243+
case SILLinkage::Private:
244+
case SILLinkage::Hidden:
245+
// Don't need a public descriptor.
246+
return false;
247+
248+
case SILLinkage::HiddenExternal:
249+
case SILLinkage::PrivateExternal:
250+
case SILLinkage::PublicExternal:
251+
case SILLinkage::SharedExternal:
252+
llvm_unreachable("should be definition linkage?");
253+
}
254+
255+
return true;
256+
}

lib/SIL/SILDeclRef.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,19 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
314314
neverPublic = true;
315315
}
316316
}
317+
318+
auto effectiveAccess = d->getEffectiveAccess();
319+
320+
// Private setter implementations for an internal storage declaration should
321+
// be internal as well, so that a dynamically-writable
322+
// keypath can be formed from other files.
323+
if (auto accessor = dyn_cast<AccessorDecl>(d)) {
324+
if (accessor->isSetter()
325+
&& accessor->getStorage()->getEffectiveAccess() == AccessLevel::Internal)
326+
effectiveAccess = AccessLevel::Internal;
327+
}
317328

318-
switch (d->getEffectiveAccess()) {
329+
switch (effectiveAccess) {
319330
case AccessLevel::Private:
320331
case AccessLevel::FilePrivate:
321332
return maybeAddExternal(SILLinkage::Private);

lib/SIL/SILVerifier.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,15 @@ void verifyKeyPathComponent(SILModule &M,
232232
&& !component.getSubscriptIndexEquals()
233233
&& !component.getSubscriptIndexHash(),
234234
"property descriptor should not have index information");
235+
236+
require(component.getExternalDecl() == nullptr
237+
&& component.getExternalSubstitutions().empty(),
238+
"property descriptor should not refer to another external decl");
235239
} else {
236240
require(hasIndices == !component.getSubscriptIndices().empty(),
237241
"component for subscript should have indices");
238242
}
239-
243+
240244
ParameterConvention normalArgConvention;
241245
if (M.getOptions().EnableGuaranteedNormalArguments)
242246
normalArgConvention = ParameterConvention::Indirect_In_Guaranteed;

lib/SILGen/SILGen.cpp

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,47 +1228,6 @@ TypeConverter::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl) {
12281228
}
12291229
}
12301230

1231-
static bool doesStorageNeedDescriptor(AbstractStorageDecl *decl) {
1232-
// The storage needs a descriptor if it sits at a module's ABI boundary,
1233-
// meaning it has public linkage.
1234-
1235-
// Any property that's potentially resilient should have accessors
1236-
// synthesized.
1237-
if (!decl->getGetter())
1238-
return false;
1239-
1240-
// If the getter is mutating, we cannot form a keypath to it at all.
1241-
if (decl->isGetterMutating())
1242-
return false;
1243-
1244-
// TODO: If previous versions of an ABI-stable binary needed the descriptor,
1245-
// then we still do.
1246-
1247-
auto getter = SILDeclRef(decl->getGetter());
1248-
auto getterLinkage = getter.getLinkage(ForDefinition);
1249-
1250-
switch (getterLinkage) {
1251-
case SILLinkage::Public:
1252-
case SILLinkage::PublicNonABI:
1253-
// We may need a descriptor.
1254-
break;
1255-
1256-
case SILLinkage::Shared:
1257-
case SILLinkage::Private:
1258-
case SILLinkage::Hidden:
1259-
// Don't need a public descriptor.
1260-
return false;
1261-
1262-
case SILLinkage::HiddenExternal:
1263-
case SILLinkage::PrivateExternal:
1264-
case SILLinkage::PublicExternal:
1265-
case SILLinkage::SharedExternal:
1266-
llvm_unreachable("should be definition linkage?");
1267-
}
1268-
1269-
return true;
1270-
}
1271-
12721231
static bool canStorageUseTrivialDescriptor(SILModule &M,
12731232
AbstractStorageDecl *decl) {
12741233
// A property can use a trivial property descriptor if the key path component
@@ -1322,7 +1281,7 @@ void SILGenModule::tryEmitPropertyDescriptor(AbstractStorageDecl *decl) {
13221281
if (!SILModuleConventions(M).useLoweredAddresses())
13231282
return;
13241283

1325-
if (!doesStorageNeedDescriptor(decl))
1284+
if (!decl->exportsPropertyDescriptor())
13261285
return;
13271286

13281287
Type baseTy;

lib/SILGen/SILGenExpr.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3679,6 +3679,16 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
36793679
}
36803680
}
36813681
}
3682+
3683+
auto isSettableInComponent = [&]() -> bool {
3684+
// For storage we reference by a property descriptor, the descriptor will
3685+
// supply the settability if needed. We only reference it here if the
3686+
// setter is public.
3687+
if (shouldUseExternalKeyPathComponent())
3688+
return storage->isSettable(M.getSwiftModule())
3689+
&& storage->isSetterAccessibleFrom(M.getSwiftModule());
3690+
return storage->isSettable(storage->getDeclContext());
3691+
};
36823692

36833693
if (auto var = dyn_cast<VarDecl>(storage)) {
36843694
CanType componentTy;
@@ -3729,7 +3739,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
37293739
{},
37303740
baseTy, componentTy);
37313741

3732-
if (var->isSettable(var->getDeclContext())) {
3742+
if (isSettableInComponent()) {
37333743
auto setter = getOrCreateKeyPathSetter(*this, loc,
37343744
var, subs,
37353745
needsGenericContext ? genericEnv : nullptr,
@@ -3783,7 +3793,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
37833793
baseTy, componentTy);
37843794

37853795
auto indexPatternsCopy = getASTContext().AllocateCopy(indexPatterns);
3786-
if (decl->isSettable()) {
3796+
if (isSettableInComponent()) {
37873797
auto setter = getOrCreateKeyPathSetter(*this, loc,
37883798
decl, subs,
37893799
needsGenericContext ? genericEnv : nullptr,

lib/TBDGen/TBDGen.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ void TBDGenVisitor::visitAccessorDecl(AccessorDecl *AD) {
156156
}
157157

158158
void TBDGenVisitor::visitAbstractStorageDecl(AbstractStorageDecl *ASD) {
159+
// Add the property descriptor if the decl needs it.
160+
if (SwiftModule->getASTContext().LangOpts.EnableKeyPathResilience
161+
&& ASD->exportsPropertyDescriptor()) {
162+
addSymbol(LinkEntity::forPropertyDescriptor(ASD));
163+
}
164+
159165
// Explicitly look at each accessor here: see visitAccessorDecl.
160166
for (auto accessor : ASD->getAllAccessors()) {
161167
visitAbstractFunctionDecl(accessor);

stdlib/public/SwiftShims/KeyPath.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,18 @@ static const __swift_uint32_t _SwiftKeyPathComponentHeader_DiscriminatorShift
4646
= 24;
4747

4848
static const __swift_uint32_t _SwiftKeyPathComponentHeader_StructTag
49-
= 0;
50-
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedTag
5149
= 1;
52-
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ClassTag
50+
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedTag
5351
= 2;
54-
static const __swift_uint32_t _SwiftKeyPathComponentHeader_OptionalTag
52+
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ClassTag
5553
= 3;
54+
static const __swift_uint32_t _SwiftKeyPathComponentHeader_OptionalTag
55+
= 4;
5656
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ExternalTag
57-
= 0x7F;
57+
= 0;
5858

5959
static const __swift_uint32_t
60-
_SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker = 0xFFFFFFFFU;
60+
_SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker = 0U;
6161

6262
static const __swift_uint32_t _SwiftKeyPathComponentHeader_MaximumOffsetPayload
6363
= 0x00FFFFFCU;
@@ -91,13 +91,21 @@ static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDByVTableOff
9191
= 0x00100000U;
9292
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedHasArgumentsFlag
9393
= 0x00080000U;
94+
// Not ABI, used internally by key path runtime implementation
95+
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedInstantiatedFromExternalWithArgumentsFlag
96+
= 0x00000010U;
9497
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDResolutionMask
9598
= 0x0000000FU;
9699
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDResolved
97100
= 0x00000000U;
98101
static const __swift_uint32_t _SwiftKeyPathComponentHeader_ComputedIDUnresolvedIndirectPointer
99102
= 0x00000002U;
100103

104+
extern void *(swift_keyPathGenericWitnessTable[]);
105+
106+
static inline void *__swift_keyPathGenericWitnessTable_addr(void) {
107+
return swift_keyPathGenericWitnessTable;
108+
}
101109

102110
#ifdef __cplusplus
103111
} // extern "C"

0 commit comments

Comments
 (0)