Skip to content

Commit 893b93d

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 41d58b7 commit 893b93d

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
@@ -6595,8 +6595,12 @@ NOTE(add_type_wrapper_subscript_stub_note,none,
65956595
ERROR(type_wrapper_failable_init,none,
65966596
"type wrapper initializer %0 cannot be failable", (DeclName))
65976597

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

66026606
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
@@ -3686,6 +3686,7 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
36863686

36873687
enum class UnviabilityReason {
36883688
Failable,
3689+
InvalidWrappedSelfType,
36893690
InvalidPropertyType,
36903691
InvalidStorageType,
36913692
Inaccessible
@@ -3767,6 +3768,7 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37673768

37683769
case UnviabilityReason::InvalidStorageType:
37693770
case UnviabilityReason::InvalidPropertyType:
3771+
case UnviabilityReason::InvalidWrappedSelfType:
37703772
llvm_unreachable("init(storage:) type is not checked");
37713773
}
37723774
}
@@ -3832,15 +3834,20 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
38323834
}
38333835
}
38343836

3835-
// subscript(propertyKeyPath: KeyPath, storedKeypath: {Writable}KeyPath)
3837+
// subscript([wrappedSelf: Wrapped], propertyKeyPath: KeyPath, storedKeypath:
3838+
// {Writable}KeyPath)
38363839
{
3837-
DeclName subscriptName(ctx, DeclBaseName::createSubscript(),
3838-
{ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
3839-
38403840
SmallVector<ValueDecl *, 2> subscripts;
3841-
if (findMembersOrDiagnose(subscriptName, subscripts,
3842-
diag::type_wrapper_requires_subscript))
3843-
return;
3841+
3842+
// Let's try to find all of the required subscripts.
3843+
{
3844+
DeclName subscriptName(ctx, DeclBaseName::createSubscript(),
3845+
{ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
3846+
3847+
if (findMembersOrDiagnose(subscriptName, subscripts,
3848+
diag::type_wrapper_requires_subscript))
3849+
return;
3850+
}
38443851

38453852
llvm::SmallDenseMap<SubscriptDecl *, SmallVector<UnviabilityReason, 2>, 2>
38463853
nonViableSubscripts;
@@ -3856,30 +3863,109 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
38563863
return false;
38573864
};
38583865

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

3862-
auto *propertyKeyPathParam = subscript->getIndices()->get(0);
3927+
auto *indices = subscript->getIndices();
3928+
3929+
// Ignore `wrappedSelf`.
3930+
bool forReferenceType = indices->size() == 3;
3931+
3932+
if (forReferenceType) {
3933+
auto wrappedTypeParamTy =
3934+
genericParams->getParams()[0]->getDeclaredInterfaceType();
38633935

3864-
if (!hasKeyPathType(propertyKeyPathParam)) {
3865-
nonViableSubscripts[subscript].push_back(
3866-
UnviabilityReason::InvalidPropertyType);
3936+
auto wrappedSelf = indices->get(0);
3937+
if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) {
3938+
nonViableSubscripts[subscript].push_back(
3939+
UnviabilityReason::InvalidWrappedSelfType);
3940+
}
38673941
}
38683942

3869-
auto *storageKeyPathParam = subscript->getIndices()->get(1);
3870-
if (hasKeyPathType(storageKeyPathParam)) {
3871-
auto type = storageKeyPathParam->getInterfaceType();
3872-
hasReadOnly |= type->isKeyPath();
3873-
hasWritable |=
3874-
type->isWritableKeyPath() || type->isReferenceWritableKeyPath();
3875-
} else {
3876-
nonViableSubscripts[subscript].push_back(
3877-
UnviabilityReason::InvalidStorageType);
3943+
auto *propertyKeyPathParam =
3944+
indices->get(getPropertyKeyPathParamIndex(subscript));
3945+
{
3946+
if (!hasKeyPathType(propertyKeyPathParam)) {
3947+
nonViableSubscripts[subscript].push_back(
3948+
UnviabilityReason::InvalidPropertyType);
3949+
}
38783950
}
38793951

3880-
if (isLessAccessibleThanType(subscript))
3881-
nonViableSubscripts[subscript].push_back(
3882-
UnviabilityReason::Inaccessible);
3952+
auto *storageKeyPathParam =
3953+
indices->get(getStorageKeyPathParamIndex(subscript));
3954+
{
3955+
if (hasKeyPathType(storageKeyPathParam)) {
3956+
auto type = storageKeyPathParam->getInterfaceType();
3957+
hasReadOnly |= type->isKeyPath();
3958+
hasWritable |=
3959+
type->isWritableKeyPath() || type->isReferenceWritableKeyPath();
3960+
} else {
3961+
nonViableSubscripts[subscript].push_back(
3962+
UnviabilityReason::InvalidStorageType);
3963+
}
3964+
3965+
if (isLessAccessibleThanType(subscript))
3966+
nonViableSubscripts[subscript].push_back(
3967+
UnviabilityReason::Inaccessible);
3968+
}
38833969
}
38843970

38853971
if (!hasReadOnly) {
@@ -3916,47 +4002,55 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
39164002
"KeyPath<<#WrappedType#>, Value>, storageKeyPath "
39174003
"storagePath: "
39184004
"WritableKeyPath<<#Base#>, "
3919-
"Value>) -> Value { get { <#code#> } set { <#code#> } }");
4005+
"Value>) -> Value { get { <#code#> } set { <#code#> } "
4006+
"}");
39204007
});
39214008
attr->setInvalid();
39224009
}
39234010

39244011
if (subscripts.size() - nonViableSubscripts.size() == 0) {
39254012
for (const auto &entry : nonViableSubscripts) {
3926-
auto *subscript = entry.first;
4013+
diagnoseSubscript(entry.first, entry.second);
4014+
}
39274015

3928-
for (auto reason : entry.second) {
3929-
switch (reason) {
3930-
case UnviabilityReason::InvalidPropertyType: {
3931-
auto paramTy = subscript->getIndices()->get(0)->getInterfaceType();
3932-
diagnose(subscript, diag::type_wrapper_subscript_invalid_parameter,
3933-
ctx.Id_propertyKeyPath, paramTy);
3934-
break;
3935-
}
4016+
attr->setInvalid();
4017+
return;
4018+
}
39364019

3937-
case UnviabilityReason::InvalidStorageType: {
3938-
auto paramTy = subscript->getIndices()->get(1)->getInterfaceType();
3939-
diagnose(subscript, diag::type_wrapper_subscript_invalid_parameter,
3940-
ctx.Id_storageKeyPath, paramTy);
3941-
break;
3942-
}
4020+
// If there were no issues with required subscripts, let's look
4021+
// for subscripts that are applicable only to reference types and
4022+
// diagnose them inline.
4023+
{
4024+
DeclName subscriptName(
4025+
ctx, DeclBaseName::createSubscript(),
4026+
{ctx.Id_wrappedSelf, ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
39434027

3944-
case UnviabilityReason::Inaccessible:
3945-
diagnose(subscript,
3946-
diag::type_wrapper_type_requirement_not_accessible,
3947-
subscript->getFormalAccess(),
3948-
subscript->getDescriptiveKind(), subscript->getName(),
3949-
nominal->getDeclaredType(), nominal->getFormalAccess());
3950-
break;
4028+
for (auto *candidate : nominal->lookupDirect(subscriptName)) {
4029+
auto *subscript = cast<SubscriptDecl>(candidate);
4030+
auto *indices = subscript->getIndices();
39514031

3952-
case UnviabilityReason::Failable:
3953-
llvm_unreachable("subscripts cannot be failable");
3954-
}
4032+
auto wrappedTypeParamTy =
4033+
genericParams->getParams()[0]->getDeclaredInterfaceType();
4034+
4035+
auto wrappedSelf = indices->get(0);
4036+
if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) {
4037+
diagnoseSubscript(subscript,
4038+
UnviabilityReason::InvalidWrappedSelfType);
39554039
}
3956-
}
39574040

3958-
attr->setInvalid();
3959-
return;
4041+
auto *propertyKeyPathParam =
4042+
indices->get(getPropertyKeyPathParamIndex(subscript));
4043+
if (!hasKeyPathType(propertyKeyPathParam))
4044+
diagnoseSubscript(subscript, UnviabilityReason::InvalidPropertyType);
4045+
4046+
auto *storageKeyPathParam =
4047+
indices->get(getStorageKeyPathParamIndex(subscript));
4048+
if (!hasKeyPathType(storageKeyPathParam))
4049+
diagnoseSubscript(subscript, UnviabilityReason::InvalidStorageType);
4050+
4051+
if (isLessAccessibleThanType(subscript))
4052+
diagnoseSubscript(subscript, UnviabilityReason::Inaccessible);
4053+
}
39604054
}
39614055
}
39624056
}

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)