Skip to content

Commit 3faf0e8

Browse files
authored
Merge pull request #25884 from DougGregor/property-wrappers-enclosing-self
[Property wrappers] Prototype support for enclosing self-based access
2 parents 21365cc + bc2e605 commit 3faf0e8

File tree

7 files changed

+352
-15
lines changed

7 files changed

+352
-15
lines changed

include/swift/AST/DiagnosticsSema.def

+3
Original file line numberDiff line numberDiff line change
@@ -4450,6 +4450,9 @@ ERROR(property_wrapper_type_requirement_not_accessible,none,
44504450
"more restrictive access than its enclosing property wrapper type %3 "
44514451
"(which is %select{private|fileprivate|internal|public|open}4)",
44524452
(AccessLevel, DescriptiveDeclKind, DeclName, Type, AccessLevel))
4453+
ERROR(property_wrapper_ambiguous_enclosing_self_subscript, none,
4454+
"property wrapper type %0 has multiple enclosing-self subscripts %1",
4455+
(Type, DeclName))
44534456

44544457
ERROR(property_wrapper_attribute_not_on_property, none,
44554458
"property wrapper attribute %0 can only be applied to a property",

include/swift/AST/KnownIdentifiers.def

+3
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ IDENTIFIER(delegateValue)
5656
IDENTIFIER(dynamicallyCall)
5757
IDENTIFIER(dynamicMember)
5858
IDENTIFIER(Element)
59+
IDENTIFIER_(enclosingInstance)
5960
IDENTIFIER(Encodable)
6061
IDENTIFIER(encode)
6162
IDENTIFIER(encodeIfPresent)
@@ -95,6 +96,7 @@ IDENTIFIER_(ObjectiveCType)
9596
IDENTIFIER(Optional)
9697
IDENTIFIER_(OptionalNilComparisonType)
9798
IDENTIFIER(parameter)
99+
IDENTIFIER(projected)
98100
IDENTIFIER(projectedValue)
99101
IDENTIFIER(Protocol)
100102
IDENTIFIER(rawValue)
@@ -122,6 +124,7 @@ IDENTIFIER(WinSDK)
122124
IDENTIFIER(with)
123125
IDENTIFIER(withArguments)
124126
IDENTIFIER(withKeywordArguments)
127+
IDENTIFIER(wrapped)
125128
IDENTIFIER(wrappedValue)
126129
IDENTIFIER(wrapperValue)
127130

include/swift/AST/PropertyWrappers.h

+11
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ struct PropertyWrapperTypeInfo {
5252
/// will be created that redirects to this property.
5353
VarDecl *projectedValueVar = nullptr;
5454

55+
/// The static subscript through which the access of instance properties
56+
/// of classes can be directed (instead of wrappedValue), providing the
57+
/// ability to reason about the enclosing "self".
58+
SubscriptDecl *enclosingInstanceWrappedSubscript = nullptr;
59+
60+
/// The static subscript through which the access of instance properties
61+
/// of classes can be directed (instead of projectedValue), providing the
62+
/// ability to reason about the enclosing "self".
63+
SubscriptDecl *enclosingInstanceProjectedSubscript = nullptr;
64+
65+
///
5566
/// Whether this is a valid property wrapper.
5667
bool isValid() const {
5768
return valueVar != nullptr;

lib/Sema/CodeSynthesis.cpp

+142-15
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ static void maybeMarkTransparent(AccessorDecl *accessor, ASTContext &ctx) {
369369
return;
370370

371371
break;
372+
} else if (var->getOriginalWrappedProperty(
373+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) {
374+
break;
372375
}
373376
}
374377

@@ -613,6 +616,57 @@ namespace {
613616
};
614617
} // end anonymous namespace
615618

619+
namespace {
620+
/// Describes the information needed to perform property wrapper access via
621+
/// the enclosing self.
622+
struct EnclosingSelfPropertyWrapperAccess {
623+
/// The (genreric) subscript that will be used to perform the access.
624+
SubscriptDecl *subscript;
625+
626+
/// The property being accessed.
627+
VarDecl *accessedProperty;
628+
};
629+
}
630+
631+
/// Determine whether the given property should be accessed via the enclosing-self access pattern.
632+
static Optional<EnclosingSelfPropertyWrapperAccess>
633+
getEnclosingSelfPropertyWrapperAccess(VarDecl *property, bool forProjected) {
634+
// The enclosing-self pattern only applies to instance properties of
635+
// classes.
636+
if (!property->isInstanceMember())
637+
return None;
638+
auto classDecl = property->getDeclContext()->getSelfClassDecl();
639+
if (!classDecl)
640+
return None;
641+
642+
// The pattern currently only works with the outermost property wrapper.
643+
Type outermostWrapperType = property->getAttachedPropertyWrapperType(0);
644+
if (!outermostWrapperType)
645+
return None;
646+
NominalTypeDecl *wrapperTypeDecl = outermostWrapperType->getAnyNominal();
647+
if (!wrapperTypeDecl)
648+
return None;
649+
650+
// Look for a generic subscript that fits the general form we need.
651+
auto wrapperInfo = wrapperTypeDecl->getPropertyWrapperTypeInfo();
652+
auto subscript =
653+
forProjected ? wrapperInfo.enclosingInstanceProjectedSubscript
654+
: wrapperInfo.enclosingInstanceWrappedSubscript;
655+
if (!subscript)
656+
return None;
657+
658+
EnclosingSelfPropertyWrapperAccess result;
659+
result.subscript = subscript;
660+
661+
if (forProjected) {
662+
result.accessedProperty =
663+
property->getPropertyWrapperBackingPropertyInfo().storageWrapperVar;
664+
} else {
665+
result.accessedProperty = property;
666+
}
667+
return result;
668+
}
669+
616670
/// Build an l-value for the storage of a declaration.
617671
static Expr *buildStorageReference(AccessorDecl *accessor,
618672
AbstractStorageDecl *storage,
@@ -621,6 +675,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
621675
ASTContext &ctx) {
622676
// Local function to "finish" the expression, creating a member reference
623677
// to the given sequence of underlying variables.
678+
Optional<EnclosingSelfPropertyWrapperAccess> enclosingSelfAccess;
624679
llvm::TinyPtrVector<VarDecl *> underlyingVars;
625680
auto finish = [&](Expr *result) -> Expr * {
626681
for (auto underlyingVar : underlyingVars) {
@@ -699,9 +754,20 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
699754
case TargetImpl::Wrapper: {
700755
auto var = cast<VarDecl>(accessor->getStorage());
701756
storage = var->getPropertyWrapperBackingProperty();
702-
for (unsigned i : indices(var->getAttachedPropertyWrappers())) {
703-
underlyingVars.push_back(
704-
var->getAttachedPropertyWrapperTypeInfo(i).valueVar);
757+
758+
// If the outermost property wrapper uses the enclosing self pattern,
759+
// record that.
760+
unsigned lastWrapperIdx = var->getAttachedPropertyWrappers().size();
761+
unsigned firstWrapperIdx = 0;
762+
enclosingSelfAccess =
763+
getEnclosingSelfPropertyWrapperAccess(var, /*forProjected=*/false);
764+
if (enclosingSelfAccess)
765+
firstWrapperIdx = 1;
766+
767+
// Perform accesses to the wrappedValues along the composition chain.
768+
for (unsigned i : range(firstWrapperIdx, lastWrapperIdx)) {
769+
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i);
770+
underlyingVars.push_back(wrapperInfo.valueVar);
705771
}
706772
semantics = AccessSemantics::DirectToStorage;
707773
selfAccessKind = SelfAccessorKind::Peer;
@@ -710,9 +776,14 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
710776

711777
case TargetImpl::WrapperStorage: {
712778
auto var =
713-
cast<VarDecl>(accessor->getStorage())->getOriginalWrappedProperty();
779+
cast<VarDecl>(accessor->getStorage())->getOriginalWrappedProperty();
714780
storage = var->getPropertyWrapperBackingProperty();
715-
underlyingVars.push_back( var->getAttachedPropertyWrapperTypeInfo(0).projectedValueVar);
781+
enclosingSelfAccess =
782+
getEnclosingSelfPropertyWrapperAccess(var, /*forProjected=*/true);
783+
if (!enclosingSelfAccess) {
784+
underlyingVars.push_back(
785+
var->getAttachedPropertyWrapperTypeInfo(0).projectedValueVar);
786+
}
716787
semantics = AccessSemantics::DirectToStorage;
717788
selfAccessKind = SelfAccessorKind::Peer;
718789
break;
@@ -757,26 +828,82 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
757828
selfDRE = new (ctx) DerivedToBaseExpr(selfDRE, selfTypeForAccess);
758829
}
759830

760-
LookupExpr *lookupExpr;
831+
Expr *lookupExpr;
761832
ConcreteDeclRef memberRef(storage, subs);
833+
auto type = storage->getValueInterfaceType()
834+
.subst(subs, SubstFlags::UseErrorType);
835+
if (isMemberLValue)
836+
type = LValueType::get(type);
837+
838+
// When we are performing access via a property wrapper's static subscript
839+
// that accepts the enclosing self along with key paths, form that subscript
840+
// operation now.
841+
if (enclosingSelfAccess) {
842+
Type storageType = storage->getValueInterfaceType()
843+
.subst(subs, SubstFlags::UseErrorType);
844+
// Metatype instance for the wrapper type itself.
845+
TypeExpr *wrapperMetatype = TypeExpr::createImplicit(storageType, ctx);
846+
847+
// Key path referring to the property being accessed.
848+
Expr *propertyKeyPath = new (ctx) KeyPathDotExpr(SourceLoc());
849+
propertyKeyPath = new (ctx) UnresolvedDotExpr(
850+
propertyKeyPath, SourceLoc(),
851+
enclosingSelfAccess->accessedProperty->getFullName(), DeclNameLoc(),
852+
/*Implicit=*/true);
853+
propertyKeyPath = new (ctx) KeyPathExpr(
854+
SourceLoc(), nullptr, propertyKeyPath);
855+
856+
// Key path referring to the backing storage property.
857+
Expr *storageKeyPath = new (ctx) KeyPathDotExpr(SourceLoc());
858+
storageKeyPath = new (ctx) UnresolvedDotExpr(
859+
storageKeyPath, SourceLoc(), storage->getFullName(), DeclNameLoc(),
860+
/*Implicit=*/true);
861+
storageKeyPath = new (ctx) KeyPathExpr(
862+
SourceLoc(), nullptr, storageKeyPath);
863+
Expr *args[3] = {
864+
selfDRE,
865+
propertyKeyPath,
866+
storageKeyPath
867+
};
762868

763-
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
869+
SubscriptDecl *subscriptDecl = enclosingSelfAccess->subscript;
870+
auto &tc = static_cast<TypeChecker&>(*ctx.getLazyResolver());
871+
lookupExpr = SubscriptExpr::create(
872+
ctx, wrapperMetatype, SourceLoc(), args,
873+
subscriptDecl->getFullName().getArgumentNames(), { }, SourceLoc(),
874+
nullptr, subscriptDecl, /*Implicit=*/true);
875+
tc.typeCheckExpression(lookupExpr, accessor);
876+
877+
// Make sure we produce an lvalue only when desired.
878+
if (isMemberLValue != lookupExpr->getType()->is<LValueType>()) {
879+
if (isMemberLValue) {
880+
// Strip off an extraneous load.
881+
if (auto load = dyn_cast<LoadExpr>(lookupExpr))
882+
lookupExpr = load->getSubExpr();
883+
} else {
884+
lookupExpr = new (ctx) LoadExpr(
885+
lookupExpr, lookupExpr->getType()->getRValueType());
886+
}
887+
}
888+
} else if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
764889
Expr *indices = buildSubscriptIndexReference(ctx, accessor);
765890
lookupExpr = SubscriptExpr::create(ctx, selfDRE, indices, memberRef,
766891
IsImplicit, semantics);
892+
893+
if (selfAccessKind == SelfAccessorKind::Super)
894+
cast<LookupExpr>(lookupExpr)->setIsSuper(true);
895+
896+
lookupExpr->setType(type);
897+
767898
} else {
768899
lookupExpr = new (ctx) MemberRefExpr(selfDRE, SourceLoc(), memberRef,
769900
DeclNameLoc(), IsImplicit, semantics);
770-
}
771901

772-
if (selfAccessKind == SelfAccessorKind::Super)
773-
lookupExpr->setIsSuper(true);
902+
if (selfAccessKind == SelfAccessorKind::Super)
903+
cast<LookupExpr>(lookupExpr)->setIsSuper(true);
774904

775-
auto type = storage->getValueInterfaceType()
776-
.subst(subs, SubstFlags::UseErrorType);
777-
if (isMemberLValue)
778-
type = LValueType::get(type);
779-
lookupExpr->setType(type);
905+
lookupExpr->setType(type);
906+
}
780907

781908
return finish(lookupExpr);
782909
}

lib/Sema/TypeCheckPropertyWrapper.cpp

+65
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,67 @@ static ConstructorDecl *findDefaultInit(ASTContext &ctx,
229229
return init;
230230
}
231231

232+
/// Determine whether we have a suitable static subscript to which we
233+
/// can pass along the enclosing self + key-paths.
234+
static SubscriptDecl *findEnclosingSelfSubscript(ASTContext &ctx,
235+
NominalTypeDecl *nominal,
236+
Identifier propertyName) {
237+
Identifier argNames[] = {
238+
ctx.Id_enclosingInstance,
239+
propertyName,
240+
ctx.Id_storage
241+
};
242+
DeclName subscriptName(ctx, DeclBaseName::createSubscript(), argNames);
243+
244+
SmallVector<SubscriptDecl *, 2> subscripts;
245+
for (auto member : nominal->lookupDirect(subscriptName)) {
246+
auto subscript = dyn_cast<SubscriptDecl>(member);
247+
if (!subscript)
248+
continue;
249+
250+
if (subscript->isInstanceMember())
251+
continue;
252+
253+
if (subscript->getDeclContext() != nominal)
254+
continue;
255+
256+
subscripts.push_back(subscript);
257+
}
258+
259+
switch (subscripts.size()) {
260+
case 0:
261+
return nullptr;
262+
263+
case 1:
264+
break;
265+
266+
default:
267+
// Diagnose ambiguous init() initializers.
268+
nominal->diagnose(diag::property_wrapper_ambiguous_enclosing_self_subscript,
269+
nominal->getDeclaredType(), subscriptName);
270+
for (auto subscript : subscripts) {
271+
subscript->diagnose(diag::kind_declname_declared_here,
272+
subscript->getDescriptiveKind(),
273+
subscript->getFullName());
274+
}
275+
return nullptr;
276+
277+
}
278+
279+
auto subscript = subscripts.front();
280+
// the subscript must be as accessible as the nominal type.
281+
if (subscript->getFormalAccess() < nominal->getFormalAccess()) {
282+
subscript->diagnose(diag::property_wrapper_type_requirement_not_accessible,
283+
subscript->getFormalAccess(),
284+
subscript->getDescriptiveKind(),
285+
subscript->getFullName(), nominal->getDeclaredType(),
286+
nominal->getFormalAccess());
287+
return nullptr;
288+
}
289+
290+
return subscript;
291+
}
292+
232293
llvm::Expected<PropertyWrapperTypeInfo>
233294
PropertyWrapperTypeInfoRequest::evaluate(
234295
Evaluator &eval, NominalTypeDecl *nominal) const {
@@ -275,6 +336,10 @@ PropertyWrapperTypeInfoRequest::evaluate(
275336
result.projectedValueVar =
276337
findValueProperty(ctx, nominal, ctx.Id_projectedValue,
277338
/*allowMissing=*/true);
339+
result.enclosingInstanceWrappedSubscript =
340+
findEnclosingSelfSubscript(ctx, nominal, ctx.Id_wrapped);
341+
result.enclosingInstanceProjectedSubscript =
342+
findEnclosingSelfSubscript(ctx, nominal, ctx.Id_projected);
278343

279344
// If there was no projectedValue property, but there is a delegateValue
280345
// or wrapperValue, property, use that and warn.

0 commit comments

Comments
 (0)