Skip to content

Commit a8d8cf0

Browse files
committed
[TypeChecker] TypeWrappers: Set access level for $Storage/$storage based on protocol conformances
If a type conforms to a protocol that has a public `$Storage`/`$storage` requirement the synthesis should produce a public `$Storage`/`$storage` implementation as well to match the requirement. Resolves: rdar://103270262
1 parent fa62cf3 commit a8d8cf0

File tree

2 files changed

+204
-13
lines changed

2 files changed

+204
-13
lines changed

lib/Sema/TypeCheckTypeWrapper.cpp

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,33 @@ struct TypeWrapperAttrInfo {
8383
NominalTypeDecl *AttachedTo;
8484
};
8585

86+
static void
87+
getDeclaredProtocolConformances(NominalTypeDecl *decl,
88+
SmallVectorImpl<ProtocolDecl *> &protocols) {
89+
for (unsigned i : indices(decl->getInherited())) {
90+
auto inheritedType = evaluateOrDefault(
91+
decl->getASTContext().evaluator,
92+
InheritedTypeRequest{decl, i, TypeResolutionStage::Interface}, Type());
93+
94+
if (!(inheritedType && inheritedType->isConstraintType()))
95+
continue;
96+
97+
if (auto *protocol =
98+
dyn_cast_or_null<ProtocolDecl>(inheritedType->getAnyNominal())) {
99+
protocols.push_back(protocol);
100+
}
101+
102+
if (auto composition = inheritedType->getAs<ProtocolCompositionType>()) {
103+
for (auto member : composition->getMembers()) {
104+
if (auto *protocol =
105+
dyn_cast_or_null<ProtocolDecl>(member->getAnyNominal())) {
106+
protocols.push_back(protocol);
107+
}
108+
}
109+
}
110+
}
111+
}
112+
86113
static void
87114
getTypeWrappers(NominalTypeDecl *decl,
88115
SmallVectorImpl<TypeWrapperAttrInfo> &typeWrappers) {
@@ -108,18 +135,10 @@ getTypeWrappers(NominalTypeDecl *decl,
108135

109136
// Attributes inferred from (explicit) protocol conformances
110137
// associated with the declaration of the type.
111-
for (unsigned i : indices(decl->getInherited())) {
112-
auto inheritedType = evaluateOrDefault(
113-
ctx.evaluator,
114-
InheritedTypeRequest{decl, i, TypeResolutionStage::Interface}, Type());
115-
116-
if (!(inheritedType && inheritedType->isConstraintType()))
117-
continue;
118-
119-
auto *protocol = inheritedType->getAnyNominal();
120-
if (!protocol)
121-
continue;
138+
SmallVector<ProtocolDecl *, 4> protocols;
139+
getDeclaredProtocolConformances(decl, protocols);
122140

141+
for (auto *protocol : protocols) {
123142
SmallVector<TypeWrapperAttrInfo, 2> inferredAttrs;
124143
getTypeWrappers(protocol, inferredAttrs);
125144

@@ -219,6 +238,54 @@ TypeDecl *NominalTypeDecl::getTypeWrapperStorageDecl() const {
219238
GetTypeWrapperStorage{mutableSelf}, nullptr);
220239
}
221240

241+
static AccessLevel
242+
getAccessLevelForTypeWrapperStorage(NominalTypeDecl *attachedTo) {
243+
auto &ctx = attachedTo->getASTContext();
244+
245+
if (isa<ProtocolDecl>(attachedTo))
246+
return attachedTo->getFormalAccess();
247+
248+
llvm::SmallDenseMap<ProtocolDecl *, bool, 4> visitedProtocols;
249+
std::function<bool(ProtocolDecl *)> hasPublicStorageAssociatedType =
250+
[&](ProtocolDecl *protocol) {
251+
if (visitedProtocols.count(protocol))
252+
return visitedProtocols[protocol];
253+
254+
auto recordResult = [&](ProtocolDecl *P, bool hasWrapper) {
255+
visitedProtocols[P] = hasWrapper;
256+
return hasWrapper;
257+
};
258+
259+
if (auto *storage =
260+
protocol->getAssociatedType(ctx.Id_TypeWrapperStorage)) {
261+
if (storage->getFormalAccess() == AccessLevel::Public)
262+
return recordResult(protocol, true);
263+
}
264+
265+
// Recursively check whether any of the parents have that
266+
// requirement.
267+
for (auto *parent : protocol->getProtocolDependencies()) {
268+
bool hasPublicStorage = hasPublicStorageAssociatedType(parent);
269+
(void)recordResult(parent, hasPublicStorage);
270+
271+
if (hasPublicStorage)
272+
return recordResult(protocol, true);
273+
}
274+
275+
return recordResult(protocol, false);
276+
};
277+
278+
SmallVector<ProtocolDecl *, 4> protocols;
279+
getDeclaredProtocolConformances(attachedTo, protocols);
280+
281+
for (auto *protocol : protocols) {
282+
if (hasPublicStorageAssociatedType(protocol))
283+
return AccessLevel::Public;
284+
}
285+
286+
return AccessLevel::Internal;
287+
}
288+
222289
TypeDecl *GetTypeWrapperStorage::evaluate(Evaluator &evaluator,
223290
NominalTypeDecl *parent) const {
224291
if (!parent->hasTypeWrapper())
@@ -246,7 +313,7 @@ TypeDecl *GetTypeWrapperStorage::evaluate(Evaluator &evaluator,
246313

247314
storage->setImplicit();
248315
storage->setSynthesized();
249-
storage->setAccess(AccessLevel::Internal);
316+
storage->setAccess(getAccessLevelForTypeWrapperStorage(parent));
250317

251318
parent->addMember(storage);
252319

@@ -278,7 +345,8 @@ GetTypeWrapperProperty::evaluate(Evaluator &evaluator,
278345
storage->getDeclaredInterfaceType()});
279346

280347
return injectProperty(parent, ctx.Id_TypeWrapperProperty, propertyTy,
281-
VarDecl::Introducer::Var, AccessLevel::Internal);
348+
VarDecl::Introducer::Var,
349+
getAccessLevelForTypeWrapperStorage(parent));
282350
}
283351

284352
VarDecl *GetTypeWrapperStorageForProperty::evaluate(Evaluator &evaluator,
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-module-interface(%t/TypeWrappers.swiftinterface) %s -module-name TypeWrappers -enable-experimental-feature TypeWrappers
3+
// RUN: %target-swift-typecheck-module-from-interface(%t/TypeWrappers.swiftinterface) -module-name TypeWrappers
4+
// RUN: %FileCheck %s < %t/TypeWrappers.swiftinterface
5+
6+
// CHECK: @typeWrapper public struct Wrapper<W, S> {
7+
// CHECK-NEXT: public init(for: W.Type, storage: S)
8+
// CHECK-NEXT: public subscript<V>(propertyKeyPath _: Swift.KeyPath<W, V>, storageKeyPath path: Swift.KeyPath<S, V>) -> V {
9+
// CHECK-NEXT: get
10+
// CHECK-NEXT: }
11+
// CHECK-NEXT: public subscript<V>(propertyKeyPath _: Swift.KeyPath<W, V>, storageKeyPath path: Swift.WritableKeyPath<S, V>) -> V {
12+
// CHECK-NEXT: get
13+
// CHECK-NEXT: set
14+
// CHECK-NEXT: }
15+
// CHECK-NEXT: }
16+
17+
@typeWrapper
18+
public struct Wrapper<W, S> {
19+
public init(for: W.Type, storage: S) {}
20+
21+
public subscript<V>(propertyKeyPath _: KeyPath<W, V>, storageKeyPath path: KeyPath<S, V>) -> V {
22+
get { fatalError() }
23+
}
24+
25+
public subscript<V>(propertyKeyPath _: KeyPath<W, V>, storageKeyPath path: WritableKeyPath<S, V>) -> V {
26+
get { fatalError() }
27+
set { }
28+
}
29+
}
30+
31+
// CHECK: @TypeWrappers.Wrapper public class Test<T> where T : Swift.StringProtocol {
32+
// CHECK: public init(a: Swift.Int, b: [T])
33+
// CHECK: public init(storageWrapper: TypeWrappers.Wrapper<TypeWrappers.Test<T>, TypeWrappers.Test<T>.$Storage>)
34+
// CHECK: }
35+
36+
@Wrapper
37+
public class Test<T: StringProtocol> {
38+
var a: Int
39+
let b: [T]
40+
}
41+
42+
@Wrapper
43+
public protocol Wrapped {
44+
init()
45+
}
46+
47+
public protocol OuterWrapped : Wrapped {}
48+
49+
// CHECK: public struct WithProtocol : TypeWrappers.Wrapped {
50+
// CHECK: public var a: Swift.Int {
51+
// CHECK: get
52+
// CHECK: set
53+
// CHECK: }
54+
// CHECK: public var b: Swift.String {
55+
// CHECK: get
56+
// CHECK: set
57+
// CHECK: }
58+
// CHECK: public init()
59+
// CHECK: public var $storage: TypeWrappers.Wrapper<TypeWrappers.WithProtocol, TypeWrappers.WithProtocol.$Storage>
60+
// CHECK: public struct $Storage {
61+
// CHECK: }
62+
// CHECK: public init(storageWrapper: TypeWrappers.Wrapper<TypeWrappers.WithProtocol, TypeWrappers.WithProtocol.$Storage>)
63+
// CHECK: }
64+
65+
public struct WithProtocol : Wrapped {
66+
public var a: Int
67+
public var b: String
68+
69+
public init() {
70+
}
71+
}
72+
73+
// CHECK: @TypeWrappers.Wrapper final public class ClassWithProtoocol : TypeWrappers.Wrapped {
74+
// CHECK: required public init()
75+
// CHECK: final public var $storage: TypeWrappers.Wrapper<TypeWrappers.ClassWithProtoocol, TypeWrappers.ClassWithProtoocol.$Storage>
76+
// CHECK: public struct $Storage {
77+
// CHECK: }
78+
// CHECK: public init(storageWrapper: TypeWrappers.Wrapper<TypeWrappers.ClassWithProtoocol, TypeWrappers.ClassWithProtoocol.$Storage>)
79+
// CHECK: }
80+
81+
@Wrapper
82+
public final class ClassWithProtoocol : Wrapped {
83+
var test: String = ""
84+
85+
public required init() {}
86+
}
87+
88+
// CHECK: @TypeWrappers.Wrapper public struct WithOuterWrapper<T> : TypeWrappers.OuterWrapped {
89+
// CHECK: public init()
90+
// CHECK: public init(_ v: T)
91+
// CHECK: public var $storage: TypeWrappers.Wrapper<TypeWrappers.WithOuterWrapper<T>, TypeWrappers.WithOuterWrapper<T>.$Storage>
92+
// CHECK: public struct $Storage {
93+
// CHECK: }
94+
// CHECK: public init(storageWrapper: TypeWrappers.Wrapper<TypeWrappers.WithOuterWrapper<T>, TypeWrappers.WithOuterWrapper<T>.$Storage>)
95+
// CHECK: }
96+
97+
@Wrapper
98+
public struct WithOuterWrapper<T> : OuterWrapped {
99+
var test: T? = nil
100+
101+
public init() {}
102+
103+
public init(_ v: T) {
104+
self.test = v
105+
}
106+
}
107+
108+
protocol UnrelatedProtocol {
109+
}
110+
111+
// CHECK: @TypeWrappers.Wrapper public struct WithComposition {
112+
// CHECK: public init()
113+
// CHECK: public var $storage: TypeWrappers.Wrapper<TypeWrappers.WithComposition, TypeWrappers.WithComposition.$Storage>
114+
// CHECK: public struct $Storage {
115+
// CHECK: }
116+
// CHECK: public init(storageWrapper: TypeWrappers.Wrapper<TypeWrappers.WithComposition, TypeWrappers.WithComposition.$Storage>)
117+
// CHECK: }
118+
// CHECK: extension TypeWrappers.WithComposition : TypeWrappers.OuterWrapped {}
119+
120+
@Wrapper
121+
public struct WithComposition : OuterWrapped & UnrelatedProtocol {
122+
public init() {}
123+
}

0 commit comments

Comments
 (0)