Skip to content

Commit 8b44880

Browse files
committed
[AST/Sema] TypeWrappers: Use special subscript to manage storage of classes
New subscript allows to pass wrapped self instance down to the type wrapper and is declared as follows: ``` subscript<...>(wrappedSelf <name>: Wrapped, propertyKeyPath: ..., storageKeyPath: ...) ``` The type-checker would use it to synthesize getter/setter accessors for managed storage if wrapped type is a class.
1 parent faabfe3 commit 8b44880

File tree

7 files changed

+291
-121
lines changed

7 files changed

+291
-121
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6594,8 +6594,12 @@ NOTE(add_type_wrapper_subscript_stub_note,none,
65946594
ERROR(type_wrapper_failable_init,none,
65956595
"type wrapper initializer %0 cannot be failable", (DeclName))
65966596

6597-
ERROR(type_wrapper_subscript_invalid_parameter,none,
6598-
"type wrapper subscript %0 parameter expects a key path (got: %1)",
6597+
ERROR(type_wrapper_subscript_invalid_parameter_type,none,
6598+
"type wrapper subscript parameter %0 expects type %1 (got: %2)",
6599+
(Identifier, Type, Type))
6600+
6601+
ERROR(type_wrapper_subscript_invalid_keypath_parameter,none,
6602+
"type wrapper subscript parameter %0 expects a key path (got: %1)",
65996603
(Identifier, Type))
66006604

66016605
ERROR(type_wrapper_type_requirement_not_accessible,none,

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ IDENTIFIER_WITH_NAME(TypeWrapperStorage, "$Storage")
314314
IDENTIFIER_WITH_NAME(TypeWrapperProperty, "$storage")
315315
IDENTIFIER(storageKeyPath)
316316
IDENTIFIER(propertyKeyPath)
317+
IDENTIFIER(wrappedSelf)
317318
IDENTIFIER_WITH_NAME(localStorageVar, "_storage")
318319

319320
// Attribute options

lib/Sema/TypeCheckAttr.cpp

Lines changed: 146 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3675,6 +3675,7 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
36753675

36763676
enum class UnviabilityReason {
36773677
Failable,
3678+
InvalidWrappedSelfType,
36783679
InvalidPropertyType,
36793680
InvalidStorageType,
36803681
Inaccessible
@@ -3756,6 +3757,7 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37563757

37573758
case UnviabilityReason::InvalidStorageType:
37583759
case UnviabilityReason::InvalidPropertyType:
3760+
case UnviabilityReason::InvalidWrappedSelfType:
37593761
llvm_unreachable("init(storage:) type is not checked");
37603762
}
37613763
}
@@ -3821,15 +3823,20 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
38213823
}
38223824
}
38233825

3824-
// subscript(propertyKeyPath: KeyPath, storedKeypath: {Writable}KeyPath)
3826+
// subscript([wrappedSelf: Wrapped], propertyKeyPath: KeyPath, storedKeypath:
3827+
// {Writable}KeyPath)
38253828
{
3826-
DeclName subscriptName(ctx, DeclBaseName::createSubscript(),
3827-
{ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
3828-
38293829
SmallVector<ValueDecl *, 2> subscripts;
3830-
if (findMembersOrDiagnose(subscriptName, subscripts,
3831-
diag::type_wrapper_requires_subscript))
3832-
return;
3830+
3831+
// Let's try to find all of the required subscripts.
3832+
{
3833+
DeclName subscriptName(ctx, DeclBaseName::createSubscript(),
3834+
{ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
3835+
3836+
if (findMembersOrDiagnose(subscriptName, subscripts,
3837+
diag::type_wrapper_requires_subscript))
3838+
return;
3839+
}
38333840

38343841
llvm::SmallDenseMap<SubscriptDecl *, SmallVector<UnviabilityReason, 2>, 2>
38353842
nonViableSubscripts;
@@ -3845,30 +3852,109 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
38453852
return false;
38463853
};
38473854

3855+
auto getPropertyKeyPathParamIndex =
3856+
[](SubscriptDecl *subscript) -> unsigned {
3857+
return subscript->getIndices()->size() == 2 ? 0 : 1;
3858+
};
3859+
3860+
auto getStorageKeyPathParamIndex =
3861+
[&](SubscriptDecl *subscript) -> unsigned {
3862+
return getPropertyKeyPathParamIndex(subscript) + 1;
3863+
};
3864+
3865+
auto diagnoseSubscript = [&](SubscriptDecl *subscript,
3866+
ArrayRef<UnviabilityReason> reasons) {
3867+
for (auto reason : reasons) {
3868+
switch (reason) {
3869+
case UnviabilityReason::InvalidWrappedSelfType: {
3870+
auto wrappedSelfExpectedTy =
3871+
genericParams->getParams()[0]->getDeclaredInterfaceType();
3872+
auto paramTy = subscript->getIndices()->get(0)->getInterfaceType();
3873+
diagnose(subscript,
3874+
diag::type_wrapper_subscript_invalid_parameter_type,
3875+
ctx.Id_wrappedSelf, wrappedSelfExpectedTy, paramTy);
3876+
break;
3877+
}
3878+
3879+
case UnviabilityReason::InvalidPropertyType: {
3880+
auto paramTy = subscript->getIndices()
3881+
->get(getPropertyKeyPathParamIndex(subscript))
3882+
->getInterfaceType();
3883+
diagnose(subscript,
3884+
diag::type_wrapper_subscript_invalid_keypath_parameter,
3885+
ctx.Id_propertyKeyPath, paramTy);
3886+
break;
3887+
}
3888+
3889+
case UnviabilityReason::InvalidStorageType: {
3890+
auto paramTy = subscript->getIndices()
3891+
->get(getStorageKeyPathParamIndex(subscript))
3892+
->getInterfaceType();
3893+
diagnose(subscript,
3894+
diag::type_wrapper_subscript_invalid_keypath_parameter,
3895+
ctx.Id_storageKeyPath, paramTy);
3896+
break;
3897+
}
3898+
3899+
case UnviabilityReason::Inaccessible:
3900+
diagnose(subscript,
3901+
diag::type_wrapper_type_requirement_not_accessible,
3902+
subscript->getFormalAccess(),
3903+
subscript->getDescriptiveKind(), subscript->getName(),
3904+
nominal->getDeclaredType(), nominal->getFormalAccess());
3905+
break;
3906+
3907+
case UnviabilityReason::Failable:
3908+
llvm_unreachable("subscripts cannot be failable");
3909+
}
3910+
}
3911+
};
3912+
38483913
for (auto *decl : subscripts) {
38493914
auto *subscript = cast<SubscriptDecl>(decl);
38503915

3851-
auto *propertyKeyPathParam = subscript->getIndices()->get(0);
3916+
auto *indices = subscript->getIndices();
3917+
3918+
// Ignore `wrappedSelf`.
3919+
bool forReferenceType = indices->size() == 3;
3920+
3921+
if (forReferenceType) {
3922+
auto wrappedTypeParamTy =
3923+
genericParams->getParams()[0]->getDeclaredInterfaceType();
38523924

3853-
if (!hasKeyPathType(propertyKeyPathParam)) {
3854-
nonViableSubscripts[subscript].push_back(
3855-
UnviabilityReason::InvalidPropertyType);
3925+
auto wrappedSelf = indices->get(0);
3926+
if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) {
3927+
nonViableSubscripts[subscript].push_back(
3928+
UnviabilityReason::InvalidWrappedSelfType);
3929+
}
38563930
}
38573931

3858-
auto *storageKeyPathParam = subscript->getIndices()->get(1);
3859-
if (hasKeyPathType(storageKeyPathParam)) {
3860-
auto type = storageKeyPathParam->getInterfaceType();
3861-
hasReadOnly |= type->isKeyPath();
3862-
hasWritable |=
3863-
type->isWritableKeyPath() || type->isReferenceWritableKeyPath();
3864-
} else {
3865-
nonViableSubscripts[subscript].push_back(
3866-
UnviabilityReason::InvalidStorageType);
3932+
auto *propertyKeyPathParam =
3933+
indices->get(getPropertyKeyPathParamIndex(subscript));
3934+
{
3935+
if (!hasKeyPathType(propertyKeyPathParam)) {
3936+
nonViableSubscripts[subscript].push_back(
3937+
UnviabilityReason::InvalidPropertyType);
3938+
}
38673939
}
38683940

3869-
if (isLessAccessibleThanType(subscript))
3870-
nonViableSubscripts[subscript].push_back(
3871-
UnviabilityReason::Inaccessible);
3941+
auto *storageKeyPathParam =
3942+
indices->get(getStorageKeyPathParamIndex(subscript));
3943+
{
3944+
if (hasKeyPathType(storageKeyPathParam)) {
3945+
auto type = storageKeyPathParam->getInterfaceType();
3946+
hasReadOnly |= type->isKeyPath();
3947+
hasWritable |=
3948+
type->isWritableKeyPath() || type->isReferenceWritableKeyPath();
3949+
} else {
3950+
nonViableSubscripts[subscript].push_back(
3951+
UnviabilityReason::InvalidStorageType);
3952+
}
3953+
3954+
if (isLessAccessibleThanType(subscript))
3955+
nonViableSubscripts[subscript].push_back(
3956+
UnviabilityReason::Inaccessible);
3957+
}
38723958
}
38733959

38743960
if (!hasReadOnly) {
@@ -3905,47 +3991,55 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
39053991
"KeyPath<<#WrappedType#>, Value>, storageKeyPath "
39063992
"storagePath: "
39073993
"WritableKeyPath<<#Base#>, "
3908-
"Value>) -> Value { get { <#code#> } set { <#code#> } }");
3994+
"Value>) -> Value { get { <#code#> } set { <#code#> } "
3995+
"}");
39093996
});
39103997
attr->setInvalid();
39113998
}
39123999

39134000
if (subscripts.size() - nonViableSubscripts.size() == 0) {
39144001
for (const auto &entry : nonViableSubscripts) {
3915-
auto *subscript = entry.first;
4002+
diagnoseSubscript(entry.first, entry.second);
4003+
}
39164004

3917-
for (auto reason : entry.second) {
3918-
switch (reason) {
3919-
case UnviabilityReason::InvalidPropertyType: {
3920-
auto paramTy = subscript->getIndices()->get(0)->getInterfaceType();
3921-
diagnose(subscript, diag::type_wrapper_subscript_invalid_parameter,
3922-
ctx.Id_propertyKeyPath, paramTy);
3923-
break;
3924-
}
4005+
attr->setInvalid();
4006+
return;
4007+
}
39254008

3926-
case UnviabilityReason::InvalidStorageType: {
3927-
auto paramTy = subscript->getIndices()->get(1)->getInterfaceType();
3928-
diagnose(subscript, diag::type_wrapper_subscript_invalid_parameter,
3929-
ctx.Id_storageKeyPath, paramTy);
3930-
break;
3931-
}
4009+
// If there were no issues with required subscripts, let's look
4010+
// for subscripts that are applicable only to reference types and
4011+
// diagnose them inline.
4012+
{
4013+
DeclName subscriptName(
4014+
ctx, DeclBaseName::createSubscript(),
4015+
{ctx.Id_wrappedSelf, ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
39324016

3933-
case UnviabilityReason::Inaccessible:
3934-
diagnose(subscript,
3935-
diag::type_wrapper_type_requirement_not_accessible,
3936-
subscript->getFormalAccess(),
3937-
subscript->getDescriptiveKind(), subscript->getName(),
3938-
nominal->getDeclaredType(), nominal->getFormalAccess());
3939-
break;
4017+
for (auto *candidate : nominal->lookupDirect(subscriptName)) {
4018+
auto *subscript = cast<SubscriptDecl>(candidate);
4019+
auto *indices = subscript->getIndices();
39404020

3941-
case UnviabilityReason::Failable:
3942-
llvm_unreachable("subscripts cannot be failable");
3943-
}
4021+
auto wrappedTypeParamTy =
4022+
genericParams->getParams()[0]->getDeclaredInterfaceType();
4023+
4024+
auto wrappedSelf = indices->get(0);
4025+
if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) {
4026+
diagnoseSubscript(subscript,
4027+
UnviabilityReason::InvalidWrappedSelfType);
39444028
}
3945-
}
39464029

3947-
attr->setInvalid();
3948-
return;
4030+
auto *propertyKeyPathParam =
4031+
indices->get(getPropertyKeyPathParamIndex(subscript));
4032+
if (!hasKeyPathType(propertyKeyPathParam))
4033+
diagnoseSubscript(subscript, UnviabilityReason::InvalidPropertyType);
4034+
4035+
auto *storageKeyPathParam =
4036+
indices->get(getStorageKeyPathParamIndex(subscript));
4037+
if (!hasKeyPathType(storageKeyPathParam))
4038+
diagnoseSubscript(subscript, UnviabilityReason::InvalidStorageType);
4039+
4040+
if (isLessAccessibleThanType(subscript))
4041+
diagnoseSubscript(subscript, UnviabilityReason::Inaccessible);
4042+
}
39494043
}
39504044
}
39514045
}

lib/Sema/TypeCheckTypeWrapper.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ static SubscriptExpr *subscriptTypeWrappedProperty(VarDecl *var,
250250
assert(typeWrapperVar);
251251
assert(storageVar);
252252

253+
auto createRefToSelf = [&]() {
254+
return new (ctx) DeclRefExpr({useDC->getImplicitSelfDecl()},
255+
/*Loc=*/DeclNameLoc(), /*Implicit=*/true);
256+
};
257+
253258
// \$Storage.<property-name>
254259
auto *storageKeyPath = KeyPathExpr::createImplicit(
255260
ctx, /*backslashLoc=*/SourceLoc(),
@@ -267,20 +272,35 @@ static SubscriptExpr *subscriptTypeWrappedProperty(VarDecl *var,
267272
/*endLoc=*/SourceLoc());
268273

269274
auto *subscriptBaseExpr = UnresolvedDotExpr::createImplicit(
270-
ctx,
271-
new (ctx) DeclRefExpr({useDC->getImplicitSelfDecl()},
272-
/*Loc=*/DeclNameLoc(), /*Implicit=*/true),
273-
typeWrapperVar->getName());
275+
ctx, createRefToSelf(), typeWrapperVar->getName());
276+
277+
SmallVector<Argument, 4> subscriptArgs;
278+
279+
// If this is a reference type, let's see whether type wrapper supports
280+
// `wrappedSelf:propertyKeyPath:storageKeyPath:` overload.
281+
if (isa<ClassDecl>(parent)) {
282+
DeclName subscriptName(
283+
ctx, DeclBaseName::createSubscript(),
284+
{ctx.Id_wrappedSelf, ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
285+
286+
auto *typeWrapper = parent->getTypeWrapper();
287+
auto candidates = typeWrapper->lookupDirect(subscriptName);
288+
289+
if (!candidates.empty()) {
290+
subscriptArgs.push_back(
291+
Argument(/*loc=*/SourceLoc(), ctx.Id_wrappedSelf, createRefToSelf()));
292+
}
293+
}
294+
295+
subscriptArgs.push_back(
296+
Argument(/*loc=*/SourceLoc(), ctx.Id_propertyKeyPath, propertyKeyPath));
297+
subscriptArgs.push_back(
298+
Argument(/*loc=*/SourceLoc(), ctx.Id_storageKeyPath, storageKeyPath));
274299

275300
// $storage[storageKeyPath: \$Storage.<property-name>]
276-
return SubscriptExpr::create(
277-
ctx, subscriptBaseExpr,
278-
ArgumentList::createImplicit(
279-
ctx, {Argument(/*loc=*/SourceLoc(), ctx.Id_propertyKeyPath,
280-
propertyKeyPath),
281-
Argument(/*loc=*/SourceLoc(), ctx.Id_storageKeyPath,
282-
storageKeyPath)}),
283-
ConcreteDeclRef(), /*implicit=*/true);
301+
return SubscriptExpr::create(ctx, subscriptBaseExpr,
302+
ArgumentList::createImplicit(ctx, subscriptArgs),
303+
ConcreteDeclRef(), /*implicit=*/true);
284304
}
285305

286306
BraceStmt *

test/Interpreter/Inputs/type_wrapper_defs.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ public struct Wrapper<W, S> {
2626
underlying[keyPath: storagePath] = newValue
2727
}
2828
}
29+
30+
public subscript<V>(wrappedSelf w: W,
31+
propertyKeyPath propertyPath: KeyPath<W, V>,
32+
storageKeyPath storagePath: WritableKeyPath<S, V>) -> V {
33+
get {
34+
print("in (reference type) getter storage: \(storagePath), property: \(propertyPath)")
35+
return underlying[keyPath: storagePath]
36+
}
37+
set {
38+
print("in (reference type) setter => \(newValue)")
39+
underlying[keyPath: storagePath] = newValue
40+
}
41+
}
2942
}
3043

3144
@propertyWrapper

0 commit comments

Comments
 (0)