Skip to content

Commit 6066418

Browse files
authored
Merge pull request #73242 from amritpan/metatype-kp-implementation
2 parents f91ad05 + 8ebc928 commit 6066418

32 files changed

+850
-104
lines changed

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ EXPERIMENTAL_FEATURE(TupleConformances, false)
234234
EXPERIMENTAL_FEATURE(FullTypedThrows, false)
235235
EXPERIMENTAL_FEATURE(SameElementRequirements, false)
236236
EXPERIMENTAL_FEATURE(GlobalActorInferenceCutoff, false)
237+
EXPERIMENTAL_FEATURE(KeyPathWithStaticMembers, false)
237238

238239
// Whether to enable @_used and @_section attributes
239240
EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true)

include/swift/SIL/SILProperty.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ class SILProperty : public llvm::ilist_node<SILProperty>,
7171
return Component;
7272
}
7373

74+
CanType getBaseType() const;
75+
7476
void print(SILPrintContext &Ctx) const;
7577
void dump() const;
7678

include/swift/Sema/CSFix.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,7 +2026,8 @@ class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
20262026

20272027
class AllowInvalidRefInKeyPath final : public ConstraintFix {
20282028
enum RefKind {
2029-
// Allow a reference to a static member as a key path component.
2029+
// Allow a reference to a static member as a key path component if it is
2030+
// declared in a module with built with Swift 6.0 compiler version or older.
20302031
StaticMember,
20312032
// Allow a reference to a declaration with mutating getter as
20322033
// a key path component.
@@ -2042,11 +2043,12 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
20422043
} Kind;
20432044

20442045
ValueDecl *Member;
2046+
Type BaseType;
20452047

2046-
AllowInvalidRefInKeyPath(ConstraintSystem &cs, RefKind kind,
2048+
AllowInvalidRefInKeyPath(ConstraintSystem &cs, Type baseType, RefKind kind,
20472049
ValueDecl *member, ConstraintLocator *locator)
20482050
: ConstraintFix(cs, FixKind::AllowInvalidRefInKeyPath, locator),
2049-
Kind(kind), Member(member) {}
2051+
Kind(kind), Member(member), BaseType(baseType) {}
20502052

20512053
public:
20522054
std::string getName() const override {
@@ -2071,8 +2073,9 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
20712073
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
20722074

20732075
/// Determine whether give reference requires a fix and produce one.
2074-
static AllowInvalidRefInKeyPath *
2075-
forRef(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator);
2076+
static AllowInvalidRefInKeyPath *forRef(ConstraintSystem &cs, Type baseType,
2077+
ValueDecl *member,
2078+
ConstraintLocator *locator);
20762079

20772080
bool isEqual(const ConstraintFix *other) const;
20782081

@@ -2081,8 +2084,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
20812084
}
20822085

20832086
private:
2084-
static AllowInvalidRefInKeyPath *create(ConstraintSystem &cs, RefKind kind,
2085-
ValueDecl *member,
2087+
static AllowInvalidRefInKeyPath *create(ConstraintSystem &cs, Type baseType,
2088+
RefKind kind, ValueDecl *member,
20862089
ConstraintLocator *locator);
20872090
};
20882091

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ UNINTERESTING_FEATURE(GroupActorErrors)
197197
UNINTERESTING_FEATURE(SameElementRequirements)
198198
UNINTERESTING_FEATURE(UnspecifiedMeansMainActorIsolated)
199199
UNINTERESTING_FEATURE(GlobalActorInferenceCutoff)
200+
UNINTERESTING_FEATURE(KeyPathWithStaticMembers)
200201

201202
static bool usesFeatureSendingArgsAndResults(Decl *decl) {
202203
auto isFunctionTypeWithSending = [](Type type) {

lib/IRGen/Linking.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1371,7 +1371,6 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const {
13711371
case Kind::ObjCMetaclass:
13721372
case Kind::SwiftMetaclassStub:
13731373
case Kind::ClassMetadataBaseOffset:
1374-
case Kind::PropertyDescriptor:
13751374
case Kind::NominalTypeDescriptor:
13761375
case Kind::NominalTypeDescriptorRecord:
13771376
case Kind::ModuleDescriptor:
@@ -1390,6 +1389,11 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const {
13901389
case Kind::OpaqueTypeDescriptorAccessorVar:
13911390
case Kind::DistributedAccessor:
13921391
return getDecl()->isWeakImported(module);
1392+
1393+
case Kind::PropertyDescriptor:
1394+
// Static properties may have nil property descriptors if declared in
1395+
// modules compiled with older compilers and should be weak linked.
1396+
return (getDecl()->isWeakImported(module) || getDecl()->isStatic());
13931397

13941398
case Kind::CanonicalSpecializedGenericSwiftMetaclassStub:
13951399
return getType()->getClassOrBoundGenericClass()->isWeakImported(module);

lib/SIL/IR/SIL.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -361,16 +361,21 @@ bool AbstractStorageDecl::exportsPropertyDescriptor() const {
361361
}
362362
}
363363
}
364-
365-
// TODO: Global and static properties ought to eventually be referenceable
366-
// as key paths from () or T.Type too.
367-
if (!getDeclContext()->isTypeContext() || isStatic())
364+
365+
// TODO: Global properties ought to eventually be referenceable
366+
// as key paths from ().
367+
if (!getDeclContext()->isTypeContext())
368368
return false;
369369

370370
// Protocol requirements do not need property descriptors.
371371
if (isa<ProtocolDecl>(getDeclContext()))
372372
return false;
373-
373+
374+
// Static properties in protocol extensions do not need
375+
// descriptors as existential Any.Type will not resolve to a value.
376+
if (isStatic() && getDeclContext()->getSelfProtocolDecl())
377+
return false;
378+
374379
// FIXME: We should support properties and subscripts with '_read' accessors;
375380
// 'get' is not part of the opaque accessor set there.
376381
auto *getter = getOpaqueAccessor(AccessorKind::Get);

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7348,24 +7348,39 @@ void SILFunction::verifySILUndefMap() const {
73487348
}
73497349
}
73507350

7351+
CanType SILProperty::getBaseType() const {
7352+
auto *decl = getDecl();
7353+
auto *dc = decl->getInnermostDeclContext();
7354+
7355+
// TODO: base type for global descriptors
7356+
auto sig = dc->getGenericSignatureOfContext();
7357+
auto baseTy =
7358+
dc->getInnermostTypeContext()->getSelfInterfaceType()->getReducedType(
7359+
sig);
7360+
if (decl->isStatic())
7361+
baseTy = CanMetatypeType::get(baseTy);
7362+
7363+
if (sig) {
7364+
auto env = dc->getGenericEnvironmentOfContext();
7365+
baseTy = env->mapTypeIntoContext(baseTy)->getCanonicalType();
7366+
}
7367+
7368+
return baseTy;
7369+
}
7370+
73517371
/// Verify that a property descriptor follows invariants.
73527372
void SILProperty::verify(const SILModule &M) const {
73537373
if (!verificationEnabled(M))
73547374
return;
73557375

73567376
auto *decl = getDecl();
7357-
auto *dc = decl->getInnermostDeclContext();
7358-
7359-
// TODO: base type for global/static descriptors
7360-
auto sig = dc->getGenericSignatureOfContext();
7361-
auto baseTy = dc->getInnermostTypeContext()->getSelfInterfaceType()
7362-
->getReducedType(sig);
7377+
auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext();
73637378
auto leafTy = decl->getValueInterfaceType()->getReducedType(sig);
73647379
SubstitutionMap subs;
73657380
if (sig) {
7366-
auto env = dc->getGenericEnvironmentOfContext();
7381+
auto env =
7382+
decl->getInnermostDeclContext()->getGenericEnvironmentOfContext();
73677383
subs = env->getForwardingSubstitutionMap();
7368-
baseTy = env->mapTypeIntoContext(baseTy)->getCanonicalType();
73697384
leafTy = env->mapTypeIntoContext(leafTy)->getCanonicalType();
73707385
}
73717386
bool hasIndices = false;
@@ -7386,6 +7401,7 @@ void SILProperty::verify(const SILModule &M) const {
73867401
auto typeExpansionContext =
73877402
TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(
73887403
ResilienceExpansion::Maximal);
7404+
auto baseTy = getBaseType();
73897405
verifyKeyPathComponent(const_cast<SILModule&>(M),
73907406
typeExpansionContext,
73917407
getSerializedKind(),

lib/SILGen/SILGen.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,9 +1873,10 @@ SILGenModule::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl,
18731873
expansion);
18741874
switch (strategy.getKind()) {
18751875
case AccessStrategy::Storage: {
1876-
// Keypaths rely on accessors to handle the special behavior of weak or
1877-
// unowned properties.
1878-
if (decl->getInterfaceType()->is<ReferenceStorageType>())
1876+
// Keypaths rely on accessors to handle the special behavior of weak,
1877+
// unowned, or static properties.
1878+
if (decl->getInterfaceType()->is<ReferenceStorageType>() ||
1879+
decl->isStatic())
18791880
return false;
18801881

18811882
// If the field offset depends on the generic instantiation, we have to
@@ -1973,13 +1974,14 @@ void SILGenModule::tryEmitPropertyDescriptor(AbstractStorageDecl *decl) {
19731974

19741975
Type baseTy;
19751976
if (decl->getDeclContext()->isTypeContext()) {
1976-
// TODO: Static properties should eventually be referenceable as
1977-
// keypaths from T.Type -> Element, viz `baseTy = MetatypeType::get(baseTy)`
1978-
assert(!decl->isStatic());
1979-
1977+
19801978
baseTy = decl->getDeclContext()->getSelfInterfaceType()
19811979
->getReducedType(decl->getInnermostDeclContext()
19821980
->getGenericSignatureOfContext());
1981+
1982+
if (decl->isStatic()) {
1983+
baseTy = MetatypeType::get(baseTy);
1984+
}
19831985
} else {
19841986
// TODO: Global variables should eventually be referenceable as
19851987
// key paths from (), viz. baseTy = TupleType::getEmpty(getASTContext());

lib/SILGen/SILGenExpr.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3194,6 +3194,11 @@ emitKeyPathRValueBase(SILGenFunction &subSGF,
31943194
auto paramSubstValue = subSGF.emitOrigToSubstValue(loc, paramOrigValue,
31953195
AbstractionPattern::getOpaque(),
31963196
baseType);
3197+
3198+
// If base is a metatype, it cannot be opened as an existential or upcasted
3199+
// from a class.
3200+
if (baseType->is<MetatypeType>())
3201+
return paramSubstValue;
31973202

31983203
// Pop open an existential container base.
31993204
if (baseType->isAnyExistentialType()) {
@@ -4031,9 +4036,30 @@ getIdForKeyPathComponentComputedProperty(SILGenModule &SGM,
40314036
AbstractStorageDecl *storage,
40324037
ResilienceExpansion expansion,
40334038
AccessStrategy strategy) {
4039+
auto getAccessorFunction = [&SGM](AbstractStorageDecl *storage,
4040+
bool isForeign) -> SILFunction * {
4041+
// Identify the property using its (unthunked) getter. For a
4042+
// computed property, this should be stable ABI; for a resilient public
4043+
// property, this should also be stable ABI across modules.
4044+
auto representativeDecl = getRepresentativeAccessorForKeyPath(storage);
4045+
// If the property came from an import-as-member function defined in C,
4046+
// use the original C function as the key.
4047+
auto ref =
4048+
SILDeclRef(representativeDecl, SILDeclRef::Kind::Func, isForeign);
4049+
// TODO: If the getter has shared linkage (say it's synthesized for a
4050+
// Clang-imported thing), we'll need some other sort of
4051+
// stable identifier.
4052+
return SGM.getFunction(ref, NotForDefinition);
4053+
};
4054+
40344055
switch (strategy.getKind()) {
40354056
case AccessStrategy::Storage:
4036-
// Identify reabstracted stored properties by the property itself.
4057+
if (auto decl = cast<VarDecl>(storage); decl->isStatic()) {
4058+
// For metatype keypaths, identify property via accessors.
4059+
return getAccessorFunction(storage, /*isForeign=*/false);
4060+
}
4061+
// Otherwise, identify reabstracted stored properties by the property
4062+
// itself.
40374063
return cast<VarDecl>(storage);
40384064
case AccessStrategy::MaterializeToTemporary:
40394065
// Use the read strategy. But try to avoid turning e.g. an
@@ -4046,19 +4072,9 @@ getIdForKeyPathComponentComputedProperty(SILGenModule &SGM,
40464072
}
40474073
LLVM_FALLTHROUGH;
40484074
case AccessStrategy::DirectToAccessor: {
4049-
// Identify the property using its (unthunked) getter. For a
4050-
// computed property, this should be stable ABI; for a resilient public
4051-
// property, this should also be stable ABI across modules.
4052-
auto representativeDecl = getRepresentativeAccessorForKeyPath(storage);
4053-
// If the property came from an import-as-member function defined in C,
4054-
// use the original C function as the key.
4055-
bool isForeign = representativeDecl->isImportAsMember();
4056-
auto getterRef = SILDeclRef(representativeDecl,
4057-
SILDeclRef::Kind::Func, isForeign);
4058-
// TODO: If the getter has shared linkage (say it's synthesized for a
4059-
// Clang-imported thing), we'll need some other sort of
4060-
// stable identifier.
4061-
return SGM.getFunction(getterRef, NotForDefinition);
4075+
return getAccessorFunction(
4076+
storage,
4077+
getRepresentativeAccessorForKeyPath(storage)->isImportAsMember());
40624078
}
40634079
case AccessStrategy::DispatchToAccessor: {
40644080
// Identify the property by its vtable or wtable slot.

lib/SILGen/SILGenType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1247,10 +1247,10 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {
12471247

12481248
void visitVarDecl(VarDecl *vd) {
12491249
// Collect global variables for static properties.
1250-
// FIXME: We can't statically emit a global variable for generic properties.
12511250
if (vd->isStatic() && vd->hasStorage()) {
12521251
emitTypeMemberGlobalVariable(SGM, vd);
12531252
visitAccessors(vd);
1253+
SGM.tryEmitPropertyDescriptor(vd);
12541254
return;
12551255
}
12561256

lib/Sema/CSApply.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5384,9 +5384,6 @@ namespace {
53845384
auto varDecl = cast<VarDecl>(property);
53855385
// Key paths don't work with mutating-get properties.
53865386
assert(!varDecl->isGetterMutating());
5387-
// Key paths don't currently support static members.
5388-
// There is a fix which diagnoses such situation already.
5389-
assert(!varDecl->isStatic());
53905387

53915388
// Compute the concrete reference to the member.
53925389
auto ref = resolveConcreteDeclRef(property, locator);

lib/Sema/CSDiagnostics.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4993,10 +4993,10 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
49934993
}
49944994

49954995
// If this is a reference to a static member by one of the key path
4996-
// components, let's provide a tailored diagnostic and return because
4997-
// that is unsupported so there is no fix-it.
4996+
// components, let's provide a tailored diagnostic with fix-it.
49984997
if (locator->isInKeyPathComponent()) {
4999-
InvalidStaticMemberRefInKeyPath failure(getSolution(), Member, locator);
4998+
InvalidStaticMemberRefInKeyPath failure(getSolution(), BaseType, Member,
4999+
locator);
50005000
return failure.diagnoseAsError();
50015001
}
50025002

@@ -6335,8 +6335,25 @@ SourceLoc InvalidMemberRefInKeyPath::getLoc() const {
63356335
}
63366336

63376337
bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
6338-
emitDiagnostic(diag::expr_keypath_static_member, getMember(),
6339-
isForKeyPathDynamicMemberLookup());
6338+
auto *KPE = getAsExpr<KeyPathExpr>(getRawAnchor());
6339+
auto rootTyRepr = KPE->getExplicitRootType();
6340+
auto isProtocol = getBaseType()->isExistentialType();
6341+
6342+
if (!getConstraintSystem().getASTContext().LangOpts.hasFeature(
6343+
Feature::KeyPathWithStaticMembers)) {
6344+
emitDiagnostic(diag::expr_keypath_static_member, getMember(),
6345+
isForKeyPathDynamicMemberLookup());
6346+
} else {
6347+
if (rootTyRepr && !isProtocol) {
6348+
emitDiagnostic(diag::could_not_use_type_member_on_instance, getBaseType(),
6349+
DeclNameRef(getMember()->getName()))
6350+
.fixItInsert(rootTyRepr->getEndLoc(), ".Type");
6351+
} else {
6352+
emitDiagnostic(diag::could_not_use_type_member_on_instance, getBaseType(),
6353+
DeclNameRef(getMember()->getName()));
6354+
}
6355+
}
6356+
63406357
return true;
63416358
}
63426359

lib/Sema/CSDiagnostics.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,20 +1751,25 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic {
17511751
};
17521752

17531753
/// Diagnose an attempt to reference a static member as a key path component
1754-
/// e.g.
1754+
/// without .Type e.g.
17551755
///
17561756
/// ```swift
17571757
/// struct S {
17581758
/// static var foo: Int = 42
17591759
/// }
17601760
///
1761-
/// _ = \S.Type.foo
1761+
/// _ = \S.foo
17621762
/// ```
17631763
class InvalidStaticMemberRefInKeyPath final : public InvalidMemberRefInKeyPath {
1764+
Type BaseType;
1765+
17641766
public:
1765-
InvalidStaticMemberRefInKeyPath(const Solution &solution, ValueDecl *member,
1766-
ConstraintLocator *locator)
1767-
: InvalidMemberRefInKeyPath(solution, member, locator) {}
1767+
InvalidStaticMemberRefInKeyPath(const Solution &solution, Type baseType,
1768+
ValueDecl *member, ConstraintLocator *locator)
1769+
: InvalidMemberRefInKeyPath(solution, member, locator),
1770+
BaseType(baseType->getRValueType()) {}
1771+
1772+
Type getBaseType() const { return BaseType; }
17681773

17691774
bool diagnoseAsError() override;
17701775
};

0 commit comments

Comments
 (0)