Skip to content

Commit 00d6964

Browse files
committed
[Property delegates] Model delegate backing storage access as a “target”
Rather than hardcoding the access patterns for the backing property of a property with a delegate, extend the “target implementation” notion used to abstract property access for code synthesis to understand the delegate pattern. This collapses the getter/setter implementation into the “trivial” cases uses the delegate access pattern. As a bonus, we get @_transparent when it makes sense.
1 parent 78e8a36 commit 00d6964

File tree

2 files changed

+49
-42
lines changed

2 files changed

+49
-42
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,19 @@ static void maybeMarkTransparent(AccessorDecl *accessor, ASTContext &ctx) {
314314
accessor->getAccessorKind() == AccessorKind::Set)
315315
return;
316316

317+
// Getters/setters for a property with a delegate are not @_transparent if
318+
// the backing variable has more-restrictive access than the original
319+
// property.
320+
if (auto var = dyn_cast<VarDecl>(accessor->getStorage())) {
321+
if (var->hasPropertyDelegate()) {
322+
if (auto backingVar =
323+
getOrSynthesizePropertyDelegateBackingProperty(var)) {
324+
if (backingVar->getFormalAccess() < var->getFormalAccess())
325+
return;
326+
}
327+
}
328+
}
329+
317330
// Accessors for protocol storage requirements are never @_transparent
318331
// since they do not have bodies.
319332
//
@@ -524,7 +537,9 @@ namespace {
524537
/// an override of it.
525538
Implementation,
526539
/// We're referencing the superclass's implementation of the storage.
527-
Super
540+
Super,
541+
/// We're referencing the backing property for a property with a delegate
542+
Delegate,
528543
};
529544
} // end anonymous namespace
530545

@@ -533,6 +548,17 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
533548
AbstractStorageDecl *storage,
534549
TargetImpl target,
535550
ASTContext &ctx) {
551+
// Local function to "finish" the expression, creating a member reference
552+
// to the "unwrapping" variable if there is one.
553+
VarDecl *unwrapVar = nullptr;
554+
auto finish = [&](Expr *result) -> Expr * {
555+
if (!unwrapVar)
556+
return result;
557+
558+
return new (ctx) MemberRefExpr(
559+
result, SourceLoc(), unwrapVar, DeclNameLoc(), /*Implicit=*/true);
560+
};
561+
536562
AccessSemantics semantics;
537563
SelfAccessorKind selfAccessKind;
538564
switch (target) {
@@ -566,25 +592,35 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
566592
selfAccessKind = SelfAccessorKind::Peer;
567593
}
568594
break;
595+
596+
case TargetImpl::Delegate: {
597+
auto var = cast<VarDecl>(accessor->getStorage());
598+
storage = getOrSynthesizePropertyDelegateBackingProperty(var);
599+
unwrapVar = getAttachedPropertyDelegateInfo(var).unwrapProperty;
600+
semantics = AccessSemantics::DirectToStorage;
601+
selfAccessKind = SelfAccessorKind::Peer;
602+
break;
603+
}
569604
}
570605

571606
VarDecl *selfDecl = accessor->getImplicitSelfDecl();
572607
if (!selfDecl) {
573608
assert(target != TargetImpl::Super);
574-
return new (ctx) DeclRefExpr(storage, DeclNameLoc(), IsImplicit, semantics);
609+
return finish(
610+
new (ctx) DeclRefExpr(storage, DeclNameLoc(), IsImplicit, semantics));
575611
}
576612

577613
Expr *selfDRE =
578614
buildSelfReference(selfDecl, selfAccessKind, ctx);
579615

580616
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
581617
Expr *indices = buildSubscriptIndexReference(ctx, accessor);
582-
return SubscriptExpr::create(ctx, selfDRE, indices, storage,
583-
IsImplicit, semantics);
618+
return finish(SubscriptExpr::create(ctx, selfDRE, indices, storage,
619+
IsImplicit, semantics));
584620
}
585621

586-
return new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage,
587-
DeclNameLoc(), IsImplicit, semantics);
622+
return finish(new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage,
623+
DeclNameLoc(), IsImplicit, semantics));
588624
}
589625

590626
/// Load the value of VD. If VD is an @override of another value, we call the
@@ -747,7 +783,8 @@ static void synthesizeTrivialGetterBody(AccessorDecl *getter,
747783
TargetImpl target,
748784
ASTContext &ctx) {
749785
auto storage = getter->getStorage();
750-
assert(!isSynthesizedComputedProperty(storage));
786+
assert(!isSynthesizedComputedProperty(storage) ||
787+
target == TargetImpl::Delegate);
751788

752789
SourceLoc loc = storage->getLoc();
753790

@@ -799,20 +836,7 @@ static void synthesizeReadCoroutineGetterBody(AccessorDecl *getter,
799836
/// delegates to the delegate's unwrap property.
800837
static void synthesizePropertyDelegateGetterBody(AccessorDecl *getter,
801838
ASTContext &ctx) {
802-
auto var = cast<VarDecl>(getter->getStorage());
803-
auto backingVar = getOrSynthesizePropertyDelegateBackingProperty(var);
804-
auto unwrapVar = getAttachedPropertyDelegateInfo(var).unwrapProperty;
805-
806-
if (!backingVar || !unwrapVar)
807-
return;
808-
809-
auto backingValue = createPropertyLoadOrCallSuperclassGetter(
810-
getter, backingVar, TargetImpl::Storage, ctx);
811-
auto unwrapRef = new (ctx) MemberRefExpr(
812-
backingValue, SourceLoc(), unwrapVar, DeclNameLoc(), /*Implicit=*/true);
813-
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), unwrapRef);
814-
getter->setBody(
815-
BraceStmt::create(ctx, SourceLoc(), { returnStmt }, SourceLoc()));
839+
synthesizeTrivialGetterBody(getter, TargetImpl::Delegate, ctx);
816840
}
817841

818842
/// Synthesize the body of a setter which just stores to the given storage
@@ -849,25 +873,8 @@ static void synthesizeTrivialSetterBody(AccessorDecl *setter,
849873
/// delegates to the delegate's unwrap property.
850874
static void synthesizePropertyDelegateSetterBody(AccessorDecl *setter,
851875
ASTContext &ctx) {
852-
auto var = cast<VarDecl>(setter->getStorage());
853-
auto backingVar = getOrSynthesizePropertyDelegateBackingProperty(var);
854-
auto unwrapVar = getAttachedPropertyDelegateInfo(var).unwrapProperty;
855-
856-
if (!backingVar || !unwrapVar)
857-
return;
858-
859-
VarDecl *newValueParamDecl = getFirstParamDecl(setter);
860-
auto *newValueDRE =
861-
new (ctx) DeclRefExpr(newValueParamDecl, DeclNameLoc(), IsImplicit);
862-
863-
auto backingValue =
864-
buildStorageReference(setter, backingVar, TargetImpl::Storage, ctx);
865-
auto unwrapRef = new (ctx) MemberRefExpr(
866-
backingValue, SourceLoc(), unwrapVar, DeclNameLoc(), /*Implicit=*/true);
867-
auto assign =
868-
new (ctx) AssignExpr(unwrapRef, SourceLoc(), newValueDRE, IsImplicit);
869-
setter->setBody(
870-
BraceStmt::create(ctx, SourceLoc(), { assign }, SourceLoc()));
876+
synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Delegate,
877+
setter->getStorage(), ctx);
871878
}
872879

873880
static void synthesizeCoroutineAccessorBody(AccessorDecl *accessor,

test/SILGen/property_delegates.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@ struct DelegateWithAccessors {
8080
return 42
8181

8282
// Synthesized setter
83-
// CHECK-LABEL: sil hidden [ossa] @$s18property_delegates21DelegateWithAccessorsV1xSivs : $@convention(method) (Int, @inout DelegateWithAccessors) -> ()
83+
// CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates21DelegateWithAccessorsV1xSivs : $@convention(method) (Int, @inout DelegateWithAccessors) -> ()
8484
// CHECK-NOT: return
8585
// CHECK: struct_element_addr {{%.*}} : $*DelegateWithAccessors, #DelegateWithAccessors.$x
8686
}
8787

8888
var y: Int by WrapperWithInitialValue {
8989
// Synthesized getter
90-
// CHECK-LABEL: sil hidden [ossa] @$s18property_delegates21DelegateWithAccessorsV1ySivg : $@convention(method) (DelegateWithAccessors) -> Int
90+
// CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates21DelegateWithAccessorsV1ySivg : $@convention(method) (DelegateWithAccessors) -> Int
9191
// CHECK-NOT: return
9292
// CHECK: struct_extract %0 : $DelegateWithAccessors, #DelegateWithAccessors.$y
9393

0 commit comments

Comments
 (0)