Skip to content

Commit 5447443

Browse files
author
Brian King
committed
Add support for sharing the private scope between type declaration and extensions in Swift 4
1 parent 3b89390 commit 5447443

File tree

6 files changed

+384
-29
lines changed

6 files changed

+384
-29
lines changed

include/swift/AST/AccessScope.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class AccessScope {
3030

3131
static AccessScope getPublic() { return AccessScope(nullptr); }
3232

33+
/// Perform the shared private access check. Private scope is shared between
34+
/// the declaration and all of the extensions of that type inside the file.
35+
/// This will return false in Swift 3 mode because private is not shared
36+
/// between types.
37+
static bool checkSharedPrivateAccess(const DeclContext *useDC, const DeclContext *sourceDC);
38+
3339
/// Returns nullptr if access scope is public.
3440
const DeclContext *getDeclContext() const { return Value.getPointer(); }
3541

@@ -48,7 +54,8 @@ class AccessScope {
4854
/// \see DeclContext::isChildContextOf
4955
bool isChildOf(AccessScope AS) const {
5056
if (!isPublic() && !AS.isPublic())
51-
return getDeclContext()->isChildContextOf(AS.getDeclContext());
57+
return (getDeclContext()->isChildContextOf(AS.getDeclContext()) ||
58+
checkSharedPrivateAccess(getDeclContext(), AS.getDeclContext()));
5259
if (isPublic() && AS.isPublic())
5360
return false;
5461
return AS.isPublic();

lib/AST/DeclContext.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,3 +969,60 @@ Accessibility AccessScope::accessibilityForDiagnostics() const {
969969

970970
return Accessibility::Private;
971971
}
972+
973+
/// Return the DeclContext to compare when checking shared private access in
974+
/// Swift 4 mode. The shared scope is the type declaration if the context
975+
/// and the type declaration are in the same file, otherwise it is the types
976+
/// last extension in the source file. If the context does not refer to a
977+
/// declaration or extension, the supplied context is returned.
978+
static const DeclContext *
979+
getSharedPrivateDeclContext(const DeclContext *DC, const SourceFile *useSF) {
980+
auto NTD = DC->getAsNominalTypeOrNominalTypeExtensionContext();
981+
if (!NTD)
982+
return DC;
983+
984+
// use the type declaration as the private scope if it is in the same
985+
// file as useSF. This occurs for both extensions and declarations.
986+
if (NTD->getParentSourceFile() == useSF)
987+
return NTD;
988+
989+
// Otherwise use the last extension declaration in the same file.
990+
const DeclContext *lastExtension = nullptr;
991+
for (ExtensionDecl *ED : NTD->getExtensions())
992+
if (ED->getParentSourceFile() == useSF)
993+
lastExtension = ED;
994+
995+
// If there's no last extension, return the supplied context.
996+
return lastExtension ?: DC;
997+
}
998+
999+
bool AccessScope::checkSharedPrivateAccess(const DeclContext *useDC, const DeclContext *sourceDC) {
1000+
// Shared private scope is not performed in Swift 3 mode.
1001+
if (useDC->getASTContext().isSwiftVersion3())
1002+
return false;
1003+
1004+
// Do not allow access if the sourceDC is in a different file, or if the
1005+
// sourceDC does not represent a type.
1006+
auto sourceNTD = sourceDC->getAsNominalTypeOrNominalTypeExtensionContext();
1007+
auto useSF = useDC->getParentSourceFile();
1008+
if (useSF != sourceDC->getParentSourceFile() || !sourceNTD)
1009+
return false;
1010+
1011+
// Compare the shared private scopes and iterate over the parent types.
1012+
sourceDC = getSharedPrivateDeclContext(sourceDC, useSF);
1013+
while (!useDC->isModuleContext()) {
1014+
useDC = getSharedPrivateDeclContext(useDC, useSF);
1015+
if (useDC == sourceDC)
1016+
return true;
1017+
1018+
// Get the parent type. If the context represents a type, look at the types
1019+
// declaring context instead of the contexts parent. This will crawl up
1020+
// the type hierarchy in nested extensions correctly.
1021+
if (auto NTD = useDC->getAsNominalTypeOrNominalTypeExtensionContext())
1022+
useDC = NTD->getDeclContext();
1023+
else
1024+
useDC = useDC->getParent();
1025+
}
1026+
1027+
return false;
1028+
}

lib/AST/NameLookup.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,9 @@ static bool checkAccessibility(const DeclContext *useDC,
12621262
assert(sourceDC && "ValueDecl being accessed must have a valid DeclContext");
12631263
switch (access) {
12641264
case Accessibility::Private:
1265-
return useDC == sourceDC || useDC->isChildContextOf(sourceDC);
1265+
return (useDC == sourceDC ||
1266+
useDC->isChildContextOf(sourceDC) ||
1267+
AccessScope::checkSharedPrivateAccess(useDC, sourceDC));
12661268
case Accessibility::FilePrivate:
12671269
return useDC->getModuleScopeContext() == sourceDC->getModuleScopeContext();
12681270
case Accessibility::Internal: {
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 3
2+
3+
class Container {
4+
private func foo() {} // expected-note * {{declared here}}
5+
private var bar = 0 // expected-note * {{declared here}}
6+
7+
private struct PrivateInner {} // expected-note * {{declared here}}
8+
9+
func localTest() {
10+
foo()
11+
self.foo()
12+
13+
_ = bar
14+
bar = 5
15+
_ = self.bar
16+
self.bar = 5
17+
18+
privateExtensionMethod() // expected-error {{'privateExtensionMethod' is inaccessible due to 'private' protection level}}
19+
self.privateExtensionMethod() // expected-error {{'privateExtensionMethod' is inaccessible due to 'private' protection level}}
20+
21+
_ = PrivateInner()
22+
_ = Container.PrivateInner()
23+
}
24+
25+
struct Inner {
26+
func test(obj: Container) {
27+
obj.foo()
28+
_ = obj.bar
29+
obj.bar = 5
30+
obj.privateExtensionMethod() // expected-error {{'privateExtensionMethod' is inaccessible due to 'private' protection level}}
31+
32+
_ = PrivateInner()
33+
_ = Container.PrivateInner()
34+
}
35+
36+
var inner: PrivateInner? // expected-error {{property must be declared private because its type uses a private type}}
37+
var innerQualified: Container.PrivateInner? // expected-error {{property must be declared private because its type uses a private type}}
38+
}
39+
40+
var inner: PrivateInner? // expected-error {{property must be declared private because its type uses a private type}}
41+
var innerQualified: Container.PrivateInner? // expected-error {{property must be declared private because its type uses a private type}}
42+
}
43+
44+
func test(obj: Container) {
45+
obj.foo() // expected-error {{'foo' is inaccessible due to 'private' protection level}}
46+
_ = obj.bar // expected-error {{'bar' is inaccessible due to 'private' protection level}}
47+
obj.bar = 5 // expected-error {{'bar' is inaccessible due to 'private' protection level}}
48+
obj.privateExtensionMethod() // expected-error {{'privateExtensionMethod' is inaccessible due to 'private' protection level}}
49+
50+
_ = Container.PrivateInner() // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
51+
}
52+
53+
extension Container {
54+
private func privateExtensionMethod() {} // expected-note * {{declared here}}
55+
56+
func extensionTest() {
57+
foo() // expected-error {{'foo' is inaccessible due to 'private' protection level}}
58+
self.foo() // expected-error {{'foo' is inaccessible due to 'private' protection level}}
59+
60+
_ = bar // expected-error {{'bar' is inaccessible due to 'private' protection level}}
61+
bar = 5 // expected-error {{'bar' is inaccessible due to 'private' protection level}}
62+
_ = self.bar // expected-error {{'bar' is inaccessible due to 'private' protection level}}
63+
self.bar = 5 // expected-error {{'bar' is inaccessible due to 'private' protection level}}
64+
65+
privateExtensionMethod()
66+
self.privateExtensionMethod()
67+
68+
_ = PrivateInner() // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
69+
_ = Container.PrivateInner() // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
70+
}
71+
72+
// FIXME: Why do these errors happen twice?
73+
var extensionInner: PrivateInner? { return nil } // expected-error 2 {{'PrivateInner' is inaccessible due to 'private' protection level}}
74+
var extensionInnerQualified: Container.PrivateInner? { return nil } // expected-error 2 {{'PrivateInner' is inaccessible due to 'private' protection level}}
75+
}
76+
77+
extension Container.Inner {
78+
func extensionTest(obj: Container) {
79+
obj.foo() // expected-error {{'foo' is inaccessible due to 'private' protection level}}
80+
_ = obj.bar // expected-error {{'bar' is inaccessible due to 'private' protection level}}
81+
obj.bar = 5 // expected-error {{'bar' is inaccessible due to 'private' protection level}}
82+
obj.privateExtensionMethod() // expected-error {{'privateExtensionMethod' is inaccessible due to 'private' protection level}}
83+
84+
// FIXME: Unqualified lookup won't look into Container from here.
85+
_ = PrivateInner() // expected-error {{use of unresolved identifier 'PrivateInner'}}
86+
_ = Container.PrivateInner() // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
87+
}
88+
89+
// FIXME: Why do these errors happen twice?
90+
// FIXME: Unqualified lookup won't look into Container from here.
91+
var inner: PrivateInner? { return nil } // expected-error 2 {{use of undeclared type 'PrivateInner'}}
92+
var innerQualified: Container.PrivateInner? { return nil } // expected-error 2 {{'PrivateInner' is inaccessible due to 'private' protection level}}
93+
}
94+
95+
class Sub : Container {
96+
func subTest() {
97+
foo() // expected-error {{'foo' is inaccessible due to 'private' protection level}}
98+
self.foo() // expected-error {{'foo' is inaccessible due to 'private' protection level}}
99+
100+
_ = bar // expected-error {{'bar' is inaccessible due to 'private' protection level}}
101+
bar = 5 // expected-error {{'bar' is inaccessible due to 'private' protection level}}
102+
_ = self.bar // expected-error {{'bar' is inaccessible due to 'private' protection level}}
103+
self.bar = 5 // expected-error {{'bar' is inaccessible due to 'private' protection level}}
104+
105+
privateExtensionMethod() // expected-error {{'privateExtensionMethod' is inaccessible due to 'private' protection level}}
106+
self.privateExtensionMethod() // expected-error {{'privateExtensionMethod' is inaccessible due to 'private' protection level}}
107+
108+
_ = PrivateInner() // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
109+
_ = Container.PrivateInner() // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
110+
}
111+
112+
var subInner: PrivateInner? // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
113+
var subInnerQualified: Container.PrivateInner? // expected-error {{'PrivateInner' is inaccessible due to 'private' protection level}}
114+
}
115+
116+
117+
protocol VeryImportantProto {
118+
associatedtype Assoc
119+
var value: Int { get set } // expected-note {{protocol requires property 'value' with type 'Int'; do you want to add a stub?}}
120+
}
121+
122+
private struct VIPPrivateType : VeryImportantProto {
123+
private typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}}
124+
var value: Int
125+
}
126+
127+
private struct VIPPrivateProp : VeryImportantProto {
128+
typealias Assoc = Int
129+
private var value: Int // expected-error {{property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{3-10=fileprivate}}
130+
}
131+
132+
private struct VIPPrivateSetProp : VeryImportantProto {
133+
typealias Assoc = Int
134+
private(set) var value: Int // expected-error {{setter for property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{3-10=fileprivate}}
135+
}
136+
137+
private class VIPPrivateSetBase {
138+
private var value: Int = 0
139+
}
140+
private class VIPPrivateSetSub : VIPPrivateSetBase, VeryImportantProto { // expected-error {{type 'VIPPrivateSetSub' does not conform to protocol 'VeryImportantProto'}}
141+
typealias Assoc = Int
142+
}
143+
144+
private class VIPPrivateSetPropBase {
145+
private(set) var value: Int = 0 // expected-error {{setter for property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{3-10=fileprivate}}
146+
}
147+
private class VIPPrivateSetPropSub : VIPPrivateSetPropBase, VeryImportantProto {
148+
typealias Assoc = Int
149+
}
150+
151+
extension Container {
152+
private typealias ExtensionConflictingType = Int // expected-note * {{declared here}}
153+
}
154+
extension Container {
155+
private typealias ExtensionConflictingType = Double // expected-note * {{declared here}}
156+
}
157+
extension Container {
158+
func test() {
159+
let a: ExtensionConflictingType? = nil // expected-error {{'ExtensionConflictingType' is inaccessible due to 'private' protection level}}
160+
let b: Container.ExtensionConflictingType? = nil // expected-error {{'ExtensionConflictingType' is inaccessible due to 'private' protection level}}
161+
_ = ExtensionConflictingType() // expected-error {{'ExtensionConflictingType' is inaccessible due to 'private' protection level}}
162+
_ = Container.ExtensionConflictingType() // expected-error {{'ExtensionConflictingType' is inaccessible due to 'private' protection level}}
163+
}
164+
}
165+
166+
// All of these should be errors, but didn't have the correct behavior in Swift
167+
// 3.0GM.
168+
extension Container {
169+
private struct VeryPrivateStruct { // expected-note * {{type declared here}}
170+
private typealias VeryPrivateType = Int // expected-note * {{type declared here}}
171+
var privateVar: VeryPrivateType { fatalError() } // expected-warning {{property should be declared private because its type uses a private type}}
172+
var privateVar2 = VeryPrivateType() // expected-warning {{property should be declared private because its type 'Container.VeryPrivateStruct.VeryPrivateType' (aka 'Int') uses a private type}}
173+
typealias PrivateAlias = VeryPrivateType // expected-warning {{type alias should be declared private because its underlying type uses a private type}}
174+
subscript(_: VeryPrivateType) -> Void { return () } // expected-warning {{subscript should be declared private because its index uses a private type}}
175+
func privateMethod(_: VeryPrivateType) -> Void {} // expected-warning {{method should be declared private because its parameter uses a private type}} {{none}}
176+
enum PrivateRawValue: VeryPrivateType { // expected-warning {{enum should be declared private because its raw type uses a private type}} {{none}}
177+
case A
178+
}
179+
enum PrivatePayload {
180+
case A(VeryPrivateType) // expected-warning {{enum case in an internal enum uses a private type}} {{none}}
181+
}
182+
183+
private class PrivateInnerClass {} // expected-note * {{declared here}}
184+
class PrivateSuper: PrivateInnerClass {} // expected-warning {{class should be declared private because its superclass is private}} {{none}}
185+
}
186+
187+
fileprivate var privateVar: VeryPrivateStruct { fatalError() } // expected-warning {{property should not be declared fileprivate because its type uses a private type}} {{none}}
188+
fileprivate typealias PrivateAlias = VeryPrivateStruct // expected-warning {{type alias should not be declared fileprivate because its underlying type uses a private type}} {{none}}
189+
fileprivate subscript(_: VeryPrivateStruct) -> Void { return () } // expected-warning {{subscript should not be declared fileprivate because its index uses a private type}} {{none}}
190+
fileprivate func privateMethod(_: VeryPrivateStruct) -> Void {} // expected-warning {{method should not be declared fileprivate because its parameter uses a private type}} {{none}}
191+
fileprivate enum PrivateRawValue: VeryPrivateStruct {} // expected-warning {{enum should not be declared fileprivate because its raw type uses a private type}} {{none}}
192+
// expected-error@-1 {{raw type 'Container.VeryPrivateStruct' is not expressible by any literal}}
193+
// expected-error@-2 {{'Container.PrivateRawValue' declares raw type 'Container.VeryPrivateStruct', but does not conform to RawRepresentable and conformance could not be synthesized}}
194+
// expected-error@-3 {{RawRepresentable conformance cannot be synthesized because raw type 'Container.VeryPrivateStruct' is not Equatable}}
195+
fileprivate enum PrivatePayload {
196+
case A(VeryPrivateStruct) // expected-warning {{enum case in an internal enum uses a private type}} {{none}}
197+
}
198+
199+
private class PrivateInnerClass {} // expected-note * {{declared here}}
200+
fileprivate class PrivateSuperClass: PrivateInnerClass {} // expected-warning {{class should not be declared fileprivate because its superclass is private}} {{none}}
201+
fileprivate class PrivateGenericUser<T> where T: PrivateInnerClass {} // expected-warning {{generic class should not be declared fileprivate because its generic requirement uses a private type}} {{none}}
202+
}
203+
204+
fileprivate struct SR2579 {
205+
private struct Inner {
206+
private struct InnerPrivateType {}
207+
var innerProperty = InnerPrivateType() // expected-warning {{property should be declared private because its type 'SR2579.Inner.InnerPrivateType' uses a private type}}
208+
}
209+
// FIXME: We need better errors when one access violation results in more
210+
// downstream.
211+
private var outerProperty = Inner().innerProperty // expected-warning {{property should not be declared in this context because its type 'SR2579.Inner.InnerPrivateType' uses a private type}}
212+
var outerProperty2 = Inner().innerProperty // expected-warning {{property should be declared private because its type 'SR2579.Inner.InnerPrivateType' uses a private type}}
213+
}

0 commit comments

Comments
 (0)