Skip to content

Commit b16ee94

Browse files
authored
Merge pull request #24512 from DougGregor/property-delegates-serialization
2 parents 5ced89b + eb3f288 commit b16ee94

File tree

9 files changed

+167
-17
lines changed

9 files changed

+167
-17
lines changed

include/swift/AST/Evaluator.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,24 @@ class Evaluator {
285285
(*this)(requests)...);
286286
}
287287

288+
/// Cache a precomputed value for the given request, so that it will not
289+
/// be computed.
290+
template<typename Request,
291+
typename std::enable_if<Request::hasExternalCache>::type* = nullptr>
292+
void cacheOutput(const Request &request,
293+
typename Request::OutputType &&output) {
294+
request.cacheResult(std::move(output));
295+
}
296+
297+
/// Cache a precomputed value for the given request, so that it will not
298+
/// be computed.
299+
template<typename Request,
300+
typename std::enable_if<!Request::hasExternalCache>::type* = nullptr>
301+
void cacheOutput(const Request &request,
302+
typename Request::OutputType &&output) {
303+
cache.insert({getCanonicalRequest(request), std::move(output)});
304+
}
305+
288306
/// Clear the cache stored within this evaluator.
289307
///
290308
/// Note that this does not clear the caches of requests that use external

include/swift/Serialization/ModuleFormat.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 488; // assign_by_delegate
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 489; // backing variables
5656

5757
using DeclIDField = BCFixed<31>;
5858

@@ -1047,7 +1047,8 @@ namespace decls_block {
10471047
AccessLevelField, // access level
10481048
AccessLevelField, // setter access, if applicable
10491049
DeclIDField, // opaque return type decl
1050-
BCArray<TypeIDField> // accessors and dependencies
1050+
BCFixed<2>, // # of property delegate backing properties
1051+
BCArray<TypeIDField> // accessors, backing properties, and dependencies
10511052
>;
10521053

10531054
using ParamLayout = BCRecordLayout<

lib/Sema/CodeSynthesis.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,11 +1771,14 @@ static void maybeAddAccessorsForPropertyDelegate(VarDecl *var,
17711771
auto valueVar = var->getAttachedPropertyDelegateTypeInfo().valueVar;
17721772
assert(valueVar && "Cannot fail when the backing var is valid");
17731773

1774+
auto parentSF = var->getDeclContext()->getParentSourceFile();
17741775
bool delegateSetterIsUsable =
17751776
var->getSetter() ||
1776-
(!var->isLet() &&
1777-
valueVar->isSettable(nullptr) &&
1778-
valueVar->isSetterAccessibleFrom(var->getInnermostDeclContext()));
1777+
(parentSF &&
1778+
parentSF->Kind != SourceFileKind::Interface &&
1779+
!var->isLet() &&
1780+
valueVar->isSettable(nullptr) &&
1781+
valueVar->isSetterAccessibleFrom(var->getInnermostDeclContext()));
17791782

17801783
if (!var->getGetter()) {
17811784
addGetterToStorage(var, ctx);

lib/Sema/TypeCheckPropertyDelegate.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,11 @@ AttachedPropertyDelegateRequest::evaluate(Evaluator &evaluator,
263263
if (!nominal || !nominal->getAttrs().hasAttribute<PropertyDelegateAttr>())
264264
continue;
265265

266+
// If the declaration came from a module file, we've already done all of
267+
// the semantic checking required.
268+
if (!dc->getParentSourceFile())
269+
return mutableAttr;
270+
266271
// Check various restrictions on which properties can have delegates
267272
// attached to them.
268273

lib/Serialization/Deserialization.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
#include "swift/AST/Pattern.h"
2323
#include "swift/AST/ParameterList.h"
2424
#include "swift/AST/PrettyStackTrace.h"
25+
#include "swift/AST/PropertyDelegates.h"
2526
#include "swift/AST/ProtocolConformance.h"
27+
#include "swift/AST/TypeCheckRequests.h"
2628
#include "swift/ClangImporter/ClangImporter.h"
2729
#include "swift/ClangImporter/ClangModule.h"
2830
#include "swift/Serialization/BCReadingExtras.h"
@@ -2664,13 +2666,13 @@ class swift::DeclDeserializer {
26642666
DeclContextID contextID;
26652667
bool isImplicit, isObjC, isStatic, hasNonPatternBindingInit;
26662668
bool isGetterMutating, isSetterMutating;
2667-
unsigned rawSpecifier, numAccessors;
2669+
unsigned rawSpecifier, numAccessors, numBackingProperties;
26682670
uint8_t readImpl, writeImpl, readWriteImpl, opaqueReadOwnership;
26692671
uint8_t rawAccessLevel, rawSetterAccessLevel;
26702672
TypeID interfaceTypeID;
26712673
ModuleFile::AccessorRecord accessors;
26722674
DeclID overriddenID, opaqueReturnTypeID;
2673-
ArrayRef<uint64_t> accessorAndDependencyIDs;
2675+
ArrayRef<uint64_t> arrayFieldIDs;
26742676

26752677
decls_block::VarLayout::readRecord(scratch, nameID, contextID,
26762678
isImplicit, isObjC, isStatic, rawSpecifier,
@@ -2683,7 +2685,8 @@ class swift::DeclDeserializer {
26832685
overriddenID,
26842686
rawAccessLevel, rawSetterAccessLevel,
26852687
opaqueReturnTypeID,
2686-
accessorAndDependencyIDs);
2688+
numBackingProperties,
2689+
arrayFieldIDs);
26872690

26882691
Identifier name = MF.getIdentifier(nameID);
26892692

@@ -2693,13 +2696,17 @@ class swift::DeclDeserializer {
26932696
return llvm::make_error<OverrideError>(name);
26942697
}
26952698

2696-
// Exctract the accessor IDs.
2697-
for (DeclID accessorID : accessorAndDependencyIDs.slice(0, numAccessors)) {
2699+
// Extract the accessor IDs.
2700+
for (DeclID accessorID : arrayFieldIDs.slice(0, numAccessors)) {
26982701
accessors.IDs.push_back(accessorID);
26992702
}
2700-
accessorAndDependencyIDs = accessorAndDependencyIDs.slice(numAccessors);
2703+
arrayFieldIDs = arrayFieldIDs.slice(numAccessors);
2704+
2705+
// Extract the backing property IDs.
2706+
auto backingPropertyIDs = arrayFieldIDs.slice(0, numBackingProperties);
2707+
arrayFieldIDs = arrayFieldIDs.slice(numBackingProperties);
27012708

2702-
for (TypeID dependencyID : accessorAndDependencyIDs) {
2709+
for (TypeID dependencyID : arrayFieldIDs) {
27032710
auto dependency = MF.getTypeChecked(dependencyID);
27042711
if (!dependency) {
27052712
// Stored properties in classes still impact class object layout because
@@ -2787,7 +2794,28 @@ class swift::DeclDeserializer {
27872794
var->setOpaqueResultTypeDecl(
27882795
cast<OpaqueTypeDecl>(MF.getDecl(opaqueReturnTypeID)));
27892796
}
2790-
2797+
2798+
// If there are any backing properties, record the
2799+
if (numBackingProperties > 0) {
2800+
VarDecl *backingVar = cast<VarDecl>(MF.getDecl(backingPropertyIDs[0]));
2801+
VarDecl *storageDelegateVar = nullptr;
2802+
if (numBackingProperties > 1) {
2803+
storageDelegateVar = cast<VarDecl>(MF.getDecl(backingPropertyIDs[1]));
2804+
}
2805+
2806+
PropertyDelegateBackingPropertyInfo info(
2807+
backingVar, storageDelegateVar, nullptr, nullptr, nullptr);
2808+
ctx.evaluator.cacheOutput(
2809+
PropertyDelegateBackingPropertyInfoRequest{var}, std::move(info));
2810+
ctx.evaluator.cacheOutput(
2811+
PropertyDelegateBackingPropertyTypeRequest{var},
2812+
backingVar->getInterfaceType());
2813+
backingVar->setOriginalDelegatedProperty(var);
2814+
2815+
if (storageDelegateVar)
2816+
storageDelegateVar->setOriginalDelegatedProperty(var);
2817+
}
2818+
27912819
return var;
27922820
}
27932821

lib/Serialization/Serialization.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/ParameterList.h"
2525
#include "swift/AST/Pattern.h"
2626
#include "swift/AST/PrettyStackTrace.h"
27+
#include "swift/AST/PropertyDelegates.h"
2728
#include "swift/AST/ProtocolConformance.h"
2829
#include "swift/AST/RawComment.h"
2930
#include "swift/AST/TypeCheckRequests.h"
@@ -3291,12 +3292,23 @@ void Serializer::writeDecl(const Decl *D) {
32913292
rawSetterAccessLevel =
32923293
getRawStableAccessLevel(var->getSetterFormalAccess());
32933294

3295+
unsigned numBackingProperties = 0;
32943296
Type ty = var->getInterfaceType();
3295-
SmallVector<TypeID, 2> accessorsAndDependencies;
3297+
SmallVector<TypeID, 2> arrayFields;
32963298
for (auto accessor : accessors.Decls)
3297-
accessorsAndDependencies.push_back(addDeclRef(accessor));
3299+
arrayFields.push_back(addDeclRef(accessor));
3300+
if (auto backingInfo = var->getPropertyDelegateBackingPropertyInfo()) {
3301+
if (backingInfo.backingVar) {
3302+
++numBackingProperties;
3303+
arrayFields.push_back(addDeclRef(backingInfo.backingVar));
3304+
}
3305+
if (backingInfo.storageDelegateVar) {
3306+
++numBackingProperties;
3307+
arrayFields.push_back(addDeclRef(backingInfo.storageDelegateVar));
3308+
}
3309+
}
32983310
for (Type dependency : collectDependenciesFromType(ty->getCanonicalType()))
3299-
accessorsAndDependencies.push_back(addTypeRef(dependency));
3311+
arrayFields.push_back(addTypeRef(dependency));
33003312

33013313
unsigned abbrCode = DeclTypeAbbrCodes[VarLayout::Code];
33023314
VarLayout::emitRecord(Out, ScratchRecord, abbrCode,
@@ -3318,7 +3330,8 @@ void Serializer::writeDecl(const Decl *D) {
33183330
addDeclRef(var->getOverriddenDecl()),
33193331
rawAccessLevel, rawSetterAccessLevel,
33203332
addDeclRef(var->getOpaqueResultTypeDecl()),
3321-
accessorsAndDependencies);
3333+
numBackingProperties,
3334+
arrayFields);
33223335
break;
33233336
}
33243337

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -typecheck -swift-version 5 -module-name TestResilient -emit-parseable-module-interface-path %t/TestResilient.swiftinterface -enable-library-evolution %s
4+
// RUN: %FileCheck %s < %t/TestResilient.swiftinterface --check-prefix CHECK --check-prefix RESILIENT
5+
6+
// RUN: %target-swift-frontend -build-module-from-parseable-interface -swift-version 5 %t/TestResilient.swiftinterface -o %t/TestResilient.swiftmodule
7+
// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules -swift-version 5 -emit-parseable-module-interface-path - %t/TestResilient.swiftmodule -module-name TestResilient | %FileCheck %s --check-prefix CHECK --check-prefix RESILIENT
8+
9+
@_propertyDelegate
10+
public struct Wrapper<T> {
11+
public var value: T
12+
}
13+
14+
@_propertyDelegate
15+
public struct WrapperWithInitialValue<T> {
16+
public var value: T
17+
18+
public init(initialValue: T) {
19+
self.value = initialValue
20+
}
21+
22+
public init(alternate value: T) {
23+
self.value = value
24+
}
25+
}
26+
27+
// CHECK: public struct HasDelegates {
28+
public struct HasDelegates {
29+
// CHECK: @TestResilient.Wrapper public var x: {{(Swift.)?}}Int {
30+
// CHECK-NEXT: get
31+
// CHECK-NEXT: set
32+
// CHECK-NEXT: }
33+
@Wrapper public var x: Int
34+
35+
// CHECK: @TestResilient.WrapperWithInitialValue public var y: Swift.Int {
36+
// CHECK-NEXT: get
37+
// CHECK-NEXT: }
38+
@WrapperWithInitialValue public private(set) var y = 17
39+
40+
// CHECK: @TestResilient.WrapperWithInitialValue public var z: Swift.Bool {
41+
// CHECK-NEXT: get
42+
// CHECK-NEXT: set
43+
// CHECK-NEXT: }
44+
@WrapperWithInitialValue(alternate: false) public var z
45+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@_propertyDelegate
2+
public struct SomeDelegate<T> {
3+
public var value: T
4+
5+
public init(initialValue: T) {
6+
self.value = initialValue
7+
}
8+
}
9+
10+
public struct HasDelegates {
11+
@SomeDelegate public var x: Int = 17
12+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/def_property_delegates.swift
3+
// RUN: %target-swift-frontend -typecheck -I%t -verify %s -verify-ignore-unknown
4+
5+
// Same test, but with -enable-testing so we can see the backing properties
6+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/def_property_delegates.swift -enable-testing
7+
// RUN: %target-swift-frontend -DTESTING -typecheck -I%t %s
8+
9+
#if TESTING
10+
@testable import def_property_delegates
11+
#else
12+
import def_property_delegates
13+
#endif
14+
15+
func useDelegates(hd: HasDelegates) {
16+
// Access the original properties
17+
let _: Int = hd.x
18+
19+
let _: SomeDelegate<Int> = hd.$x // expected-error{{'$x' is inaccessible due to 'internal' protection level}}
20+
21+
var mutableHD = hd
22+
mutableHD.x = 17
23+
24+
mutableHD.$x = SomeDelegate(initialValue: 42) // expected-error{{'$x' is inaccessible due to 'internal' protection level}}
25+
}

0 commit comments

Comments
 (0)