Skip to content

Commit dd81846

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 859b28d commit dd81846

File tree

7 files changed

+317
-138
lines changed

7 files changed

+317
-138
lines changed

include/swift/AST/DiagnosticsSema.def

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

6634-
ERROR(type_wrapper_subscript_invalid_parameter,none,
6635-
"type wrapper subscript %0 parameter expects a key path (got: %1)",
6634+
ERROR(type_wrapper_subscript_invalid_parameter_type,none,
6635+
"type wrapper subscript parameter %0 expects type %1 (got: %2)",
6636+
(Identifier, Type, Type))
6637+
6638+
ERROR(type_wrapper_subscript_invalid_keypath_parameter,none,
6639+
"type wrapper subscript parameter %0 expects a key path (got: %1)",
66366640
(Identifier, Type))
66376641

66386642
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
@@ -3734,6 +3734,7 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37343734

37353735
enum class UnviabilityReason {
37363736
Failable,
3737+
InvalidWrappedSelfType,
37373738
InvalidPropertyType,
37383739
InvalidStorageType,
37393740
Inaccessible
@@ -3815,6 +3816,7 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
38153816

38163817
case UnviabilityReason::InvalidStorageType:
38173818
case UnviabilityReason::InvalidPropertyType:
3819+
case UnviabilityReason::InvalidWrappedSelfType:
38183820
llvm_unreachable("init(storage:) type is not checked");
38193821
}
38203822
}
@@ -3880,15 +3882,20 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
38803882
}
38813883
}
38823884

3883-
// subscript(propertyKeyPath: KeyPath, storedKeypath: {Writable}KeyPath)
3885+
// subscript([wrappedSelf: Wrapped], propertyKeyPath: KeyPath, storedKeypath:
3886+
// {Writable}KeyPath)
38843887
{
3885-
DeclName subscriptName(ctx, DeclBaseName::createSubscript(),
3886-
{ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
3887-
38883888
SmallVector<ValueDecl *, 2> subscripts;
3889-
if (findMembersOrDiagnose(subscriptName, subscripts,
3890-
diag::type_wrapper_requires_subscript))
3891-
return;
3889+
3890+
// Let's try to find all of the required subscripts.
3891+
{
3892+
DeclName subscriptName(ctx, DeclBaseName::createSubscript(),
3893+
{ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
3894+
3895+
if (findMembersOrDiagnose(subscriptName, subscripts,
3896+
diag::type_wrapper_requires_subscript))
3897+
return;
3898+
}
38923899

38933900
llvm::SmallDenseMap<SubscriptDecl *, SmallVector<UnviabilityReason, 2>, 2>
38943901
nonViableSubscripts;
@@ -3904,30 +3911,109 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
39043911
return false;
39053912
};
39063913

3914+
auto getPropertyKeyPathParamIndex =
3915+
[](SubscriptDecl *subscript) -> unsigned {
3916+
return subscript->getIndices()->size() == 2 ? 0 : 1;
3917+
};
3918+
3919+
auto getStorageKeyPathParamIndex =
3920+
[&](SubscriptDecl *subscript) -> unsigned {
3921+
return getPropertyKeyPathParamIndex(subscript) + 1;
3922+
};
3923+
3924+
auto diagnoseSubscript = [&](SubscriptDecl *subscript,
3925+
ArrayRef<UnviabilityReason> reasons) {
3926+
for (auto reason : reasons) {
3927+
switch (reason) {
3928+
case UnviabilityReason::InvalidWrappedSelfType: {
3929+
auto wrappedSelfExpectedTy =
3930+
genericParams->getParams()[0]->getDeclaredInterfaceType();
3931+
auto paramTy = subscript->getIndices()->get(0)->getInterfaceType();
3932+
diagnose(subscript,
3933+
diag::type_wrapper_subscript_invalid_parameter_type,
3934+
ctx.Id_wrappedSelf, wrappedSelfExpectedTy, paramTy);
3935+
break;
3936+
}
3937+
3938+
case UnviabilityReason::InvalidPropertyType: {
3939+
auto paramTy = subscript->getIndices()
3940+
->get(getPropertyKeyPathParamIndex(subscript))
3941+
->getInterfaceType();
3942+
diagnose(subscript,
3943+
diag::type_wrapper_subscript_invalid_keypath_parameter,
3944+
ctx.Id_propertyKeyPath, paramTy);
3945+
break;
3946+
}
3947+
3948+
case UnviabilityReason::InvalidStorageType: {
3949+
auto paramTy = subscript->getIndices()
3950+
->get(getStorageKeyPathParamIndex(subscript))
3951+
->getInterfaceType();
3952+
diagnose(subscript,
3953+
diag::type_wrapper_subscript_invalid_keypath_parameter,
3954+
ctx.Id_storageKeyPath, paramTy);
3955+
break;
3956+
}
3957+
3958+
case UnviabilityReason::Inaccessible:
3959+
diagnose(subscript,
3960+
diag::type_wrapper_type_requirement_not_accessible,
3961+
subscript->getFormalAccess(),
3962+
subscript->getDescriptiveKind(), subscript->getName(),
3963+
nominal->getDeclaredType(), nominal->getFormalAccess());
3964+
break;
3965+
3966+
case UnviabilityReason::Failable:
3967+
llvm_unreachable("subscripts cannot be failable");
3968+
}
3969+
}
3970+
};
3971+
39073972
for (auto *decl : subscripts) {
39083973
auto *subscript = cast<SubscriptDecl>(decl);
39093974

3910-
auto *propertyKeyPathParam = subscript->getIndices()->get(0);
3975+
auto *indices = subscript->getIndices();
3976+
3977+
// Ignore `wrappedSelf`.
3978+
bool forReferenceType = indices->size() == 3;
3979+
3980+
if (forReferenceType) {
3981+
auto wrappedTypeParamTy =
3982+
genericParams->getParams()[0]->getDeclaredInterfaceType();
39113983

3912-
if (!hasKeyPathType(propertyKeyPathParam)) {
3913-
nonViableSubscripts[subscript].push_back(
3914-
UnviabilityReason::InvalidPropertyType);
3984+
auto wrappedSelf = indices->get(0);
3985+
if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) {
3986+
nonViableSubscripts[subscript].push_back(
3987+
UnviabilityReason::InvalidWrappedSelfType);
3988+
}
39153989
}
39163990

3917-
auto *storageKeyPathParam = subscript->getIndices()->get(1);
3918-
if (hasKeyPathType(storageKeyPathParam)) {
3919-
auto type = storageKeyPathParam->getInterfaceType();
3920-
hasReadOnly |= type->isKeyPath();
3921-
hasWritable |=
3922-
type->isWritableKeyPath() || type->isReferenceWritableKeyPath();
3923-
} else {
3924-
nonViableSubscripts[subscript].push_back(
3925-
UnviabilityReason::InvalidStorageType);
3991+
auto *propertyKeyPathParam =
3992+
indices->get(getPropertyKeyPathParamIndex(subscript));
3993+
{
3994+
if (!hasKeyPathType(propertyKeyPathParam)) {
3995+
nonViableSubscripts[subscript].push_back(
3996+
UnviabilityReason::InvalidPropertyType);
3997+
}
39263998
}
39273999

3928-
if (isLessAccessibleThanType(subscript))
3929-
nonViableSubscripts[subscript].push_back(
3930-
UnviabilityReason::Inaccessible);
4000+
auto *storageKeyPathParam =
4001+
indices->get(getStorageKeyPathParamIndex(subscript));
4002+
{
4003+
if (hasKeyPathType(storageKeyPathParam)) {
4004+
auto type = storageKeyPathParam->getInterfaceType();
4005+
hasReadOnly |= type->isKeyPath();
4006+
hasWritable |=
4007+
type->isWritableKeyPath() || type->isReferenceWritableKeyPath();
4008+
} else {
4009+
nonViableSubscripts[subscript].push_back(
4010+
UnviabilityReason::InvalidStorageType);
4011+
}
4012+
4013+
if (isLessAccessibleThanType(subscript))
4014+
nonViableSubscripts[subscript].push_back(
4015+
UnviabilityReason::Inaccessible);
4016+
}
39314017
}
39324018

39334019
if (!hasReadOnly) {
@@ -3964,47 +4050,55 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
39644050
"KeyPath<<#WrappedType#>, Value>, storageKeyPath "
39654051
"storagePath: "
39664052
"WritableKeyPath<<#Base#>, "
3967-
"Value>) -> Value { get { <#code#> } set { <#code#> } }");
4053+
"Value>) -> Value { get { <#code#> } set { <#code#> } "
4054+
"}");
39684055
});
39694056
attr->setInvalid();
39704057
}
39714058

39724059
if (subscripts.size() - nonViableSubscripts.size() == 0) {
39734060
for (const auto &entry : nonViableSubscripts) {
3974-
auto *subscript = entry.first;
4061+
diagnoseSubscript(entry.first, entry.second);
4062+
}
39754063

3976-
for (auto reason : entry.second) {
3977-
switch (reason) {
3978-
case UnviabilityReason::InvalidPropertyType: {
3979-
auto paramTy = subscript->getIndices()->get(0)->getInterfaceType();
3980-
diagnose(subscript, diag::type_wrapper_subscript_invalid_parameter,
3981-
ctx.Id_propertyKeyPath, paramTy);
3982-
break;
3983-
}
4064+
attr->setInvalid();
4065+
return;
4066+
}
39844067

3985-
case UnviabilityReason::InvalidStorageType: {
3986-
auto paramTy = subscript->getIndices()->get(1)->getInterfaceType();
3987-
diagnose(subscript, diag::type_wrapper_subscript_invalid_parameter,
3988-
ctx.Id_storageKeyPath, paramTy);
3989-
break;
3990-
}
4068+
// If there were no issues with required subscripts, let's look
4069+
// for subscripts that are applicable only to reference types and
4070+
// diagnose them inline.
4071+
{
4072+
DeclName subscriptName(
4073+
ctx, DeclBaseName::createSubscript(),
4074+
{ctx.Id_wrappedSelf, ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath});
39914075

3992-
case UnviabilityReason::Inaccessible:
3993-
diagnose(subscript,
3994-
diag::type_wrapper_type_requirement_not_accessible,
3995-
subscript->getFormalAccess(),
3996-
subscript->getDescriptiveKind(), subscript->getName(),
3997-
nominal->getDeclaredType(), nominal->getFormalAccess());
3998-
break;
4076+
for (auto *candidate : nominal->lookupDirect(subscriptName)) {
4077+
auto *subscript = cast<SubscriptDecl>(candidate);
4078+
auto *indices = subscript->getIndices();
39994079

4000-
case UnviabilityReason::Failable:
4001-
llvm_unreachable("subscripts cannot be failable");
4002-
}
4080+
auto wrappedTypeParamTy =
4081+
genericParams->getParams()[0]->getDeclaredInterfaceType();
4082+
4083+
auto wrappedSelf = indices->get(0);
4084+
if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) {
4085+
diagnoseSubscript(subscript,
4086+
UnviabilityReason::InvalidWrappedSelfType);
40034087
}
4004-
}
40054088

4006-
attr->setInvalid();
4007-
return;
4089+
auto *propertyKeyPathParam =
4090+
indices->get(getPropertyKeyPathParamIndex(subscript));
4091+
if (!hasKeyPathType(propertyKeyPathParam))
4092+
diagnoseSubscript(subscript, UnviabilityReason::InvalidPropertyType);
4093+
4094+
auto *storageKeyPathParam =
4095+
indices->get(getStorageKeyPathParamIndex(subscript));
4096+
if (!hasKeyPathType(storageKeyPathParam))
4097+
diagnoseSubscript(subscript, UnviabilityReason::InvalidStorageType);
4098+
4099+
if (isLessAccessibleThanType(subscript))
4100+
diagnoseSubscript(subscript, UnviabilityReason::Inaccessible);
4101+
}
40084102
}
40094103
}
40104104
}

lib/Sema/TypeCheckTypeWrapper.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ static SubscriptExpr *subscriptTypeWrappedProperty(VarDecl *var,
257257
assert(typeWrapperVar);
258258
assert(storageVar);
259259

260+
auto createRefToSelf = [&]() {
261+
return new (ctx) DeclRefExpr({useDC->getImplicitSelfDecl()},
262+
/*Loc=*/DeclNameLoc(), /*Implicit=*/true);
263+
};
264+
260265
// \$Storage.<property-name>
261266
auto *storageKeyPath = KeyPathExpr::createImplicit(
262267
ctx, /*backslashLoc=*/SourceLoc(),
@@ -274,20 +279,35 @@ static SubscriptExpr *subscriptTypeWrappedProperty(VarDecl *var,
274279
/*endLoc=*/SourceLoc());
275280

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

282307
// $storage[storageKeyPath: \$Storage.<property-name>]
283-
return SubscriptExpr::create(
284-
ctx, subscriptBaseExpr,
285-
ArgumentList::createImplicit(
286-
ctx, {Argument(/*loc=*/SourceLoc(), ctx.Id_propertyKeyPath,
287-
propertyKeyPath),
288-
Argument(/*loc=*/SourceLoc(), ctx.Id_storageKeyPath,
289-
storageKeyPath)}),
290-
ConcreteDeclRef(), /*implicit=*/true);
308+
return SubscriptExpr::create(ctx, subscriptBaseExpr,
309+
ArgumentList::createImplicit(ctx, subscriptArgs),
310+
ConcreteDeclRef(), /*implicit=*/true);
291311
}
292312

293313
BraceStmt *

test/Interpreter/Inputs/type_wrapper_defs.swift

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,28 @@ 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: KeyPath<S, V>) -> V {
33+
get {
34+
print("in (reference type) let getter storage: \(storagePath), property: \(propertyPath)")
35+
return underlying[keyPath: storagePath]
36+
}
37+
}
38+
39+
public subscript<V>(wrappedSelf w: W,
40+
propertyKeyPath propertyPath: KeyPath<W, V>,
41+
storageKeyPath storagePath: WritableKeyPath<S, V>) -> V {
42+
get {
43+
print("in (reference type) getter storage: \(storagePath), property: \(propertyPath)")
44+
return underlying[keyPath: storagePath]
45+
}
46+
set {
47+
print("in (reference type) setter => \(newValue)")
48+
underlying[keyPath: storagePath] = newValue
49+
}
50+
}
2951
}
3052

3153
@propertyWrapper
@@ -284,7 +306,7 @@ public class UserDefinedInitWithConditionalTest<T> {
284306
@Wrapper
285307
public class ClassWithConvenienceInit<T> {
286308
public var a: T?
287-
public var b: String = ""
309+
public var b: String = "<default-placeholder>"
288310

289311
public init(aWithoutB: T?) {
290312
self.a = aWithoutB
@@ -302,13 +324,13 @@ public class ClassWithConvenienceInit<T> {
302324
}
303325

304326
public convenience init() {
305-
self.init(a: nil)
327+
self.init(val: nil)
306328
print(self.a)
307329
print(self.b)
308330
}
309331

310-
public convenience init(a: T?) {
311-
self.init(a: a, b: "<placeholder>")
332+
public convenience init(val: T?) {
333+
self.init(a: val, b: "<placeholder>")
312334
print(self.a)
313335
print(self.b)
314336

0 commit comments

Comments
 (0)