Skip to content

Commit 5bd63fe

Browse files
author
ematejska
authored
Merge pull request #9213 from apple/Merge-9098-4-branch
Merge pull request #9098 from KingOfBrian/feature/SE-0169
2 parents c1d0fbe + 32e7a1d commit 5bd63fe

File tree

6 files changed

+392
-29
lines changed

6 files changed

+392
-29
lines changed

include/swift/AST/AccessScope.h

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

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

33+
/// Check if private access is allowed. This is a lexical scope check in Swift
34+
/// 3 mode. In Swift 4 mode, declarations and extensions of the same type will
35+
/// also allow access.
36+
static bool allowsPrivateAccess(const DeclContext *useDC, const DeclContext *sourceDC);
37+
3338
/// Returns nullptr if access scope is public.
3439
const DeclContext *getDeclContext() const { return Value.getPointer(); }
3540

@@ -48,7 +53,7 @@ class AccessScope {
4853
/// \see DeclContext::isChildContextOf
4954
bool isChildOf(AccessScope AS) const {
5055
if (!isPublic() && !AS.isPublic())
51-
return getDeclContext()->isChildContextOf(AS.getDeclContext());
56+
return allowsPrivateAccess(getDeclContext(), AS.getDeclContext());
5257
if (isPublic() && AS.isPublic())
5358
return false;
5459
return AS.isPublic();

lib/AST/DeclContext.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,8 +956,38 @@ IterableDeclContext::castDeclToIterableDeclContext(const Decl *D) {
956956
llvm_unreachable("Unhandled DeclKind in switch.");
957957
}
958958

959+
/// Return the DeclContext to compare when checking private access in
960+
/// Swift 4 mode. The context returned is the type declaration if the context
961+
/// and the type declaration are in the same file, otherwise it is the types
962+
/// last extension in the source file. If the context does not refer to a
963+
/// declaration or extension, the supplied context is returned.
964+
static const DeclContext *
965+
getPrivateDeclContext(const DeclContext *DC, const SourceFile *useSF) {
966+
auto NTD = DC->getAsNominalTypeOrNominalTypeExtensionContext();
967+
if (!NTD)
968+
return DC;
969+
970+
// use the type declaration as the private scope if it is in the same
971+
// file as useSF. This occurs for both extensions and declarations.
972+
if (NTD->getParentSourceFile() == useSF)
973+
return NTD;
974+
975+
// Otherwise use the last extension declaration in the same file.
976+
const DeclContext *lastExtension = nullptr;
977+
for (ExtensionDecl *ED : NTD->getExtensions())
978+
if (ED->getParentSourceFile() == useSF)
979+
lastExtension = ED;
980+
981+
// If there's no last extension, return the supplied context.
982+
return lastExtension ? lastExtension : DC;
983+
}
984+
959985
AccessScope::AccessScope(const DeclContext *DC, bool isPrivate)
960986
: Value(DC, isPrivate) {
987+
if (isPrivate) {
988+
DC = getPrivateDeclContext(DC, DC->getParentSourceFile());
989+
Value.setPointer(DC);
990+
}
961991
if (!DC || isa<ModuleDecl>(DC))
962992
assert(!isPrivate && "public or internal scope can't be private");
963993
}
@@ -978,3 +1008,41 @@ Accessibility AccessScope::accessibilityForDiagnostics() const {
9781008

9791009
return Accessibility::Private;
9801010
}
1011+
1012+
bool AccessScope::allowsPrivateAccess(const DeclContext *useDC, const DeclContext *sourceDC) {
1013+
// Check the lexical scope.
1014+
if (useDC->isChildContextOf(sourceDC))
1015+
return true;
1016+
1017+
// Only check lexical scope in Swift 3 mode
1018+
if (useDC->getASTContext().isSwiftVersion3())
1019+
return false;
1020+
1021+
// Do not allow access if the sourceDC is in a different file
1022+
auto useSF = useDC->getParentSourceFile();
1023+
if (useSF != sourceDC->getParentSourceFile())
1024+
return false;
1025+
1026+
// Do not allow access if the sourceDC does not represent a type.
1027+
auto sourceNTD = sourceDC->getAsNominalTypeOrNominalTypeExtensionContext();
1028+
if (!sourceNTD)
1029+
return false;
1030+
1031+
// Compare the private scopes and iterate over the parent types.
1032+
sourceDC = getPrivateDeclContext(sourceDC, useSF);
1033+
while (!useDC->isModuleContext()) {
1034+
useDC = getPrivateDeclContext(useDC, useSF);
1035+
if (useDC == sourceDC)
1036+
return true;
1037+
1038+
// Get the parent type. If the context represents a type, look at the types
1039+
// declaring context instead of the contexts parent. This will crawl up
1040+
// the type hierarchy in nested extensions correctly.
1041+
if (auto NTD = useDC->getAsNominalTypeOrNominalTypeExtensionContext())
1042+
useDC = NTD->getDeclContext();
1043+
else
1044+
useDC = useDC->getParent();
1045+
}
1046+
1047+
return false;
1048+
}

lib/AST/NameLookup.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,8 @@ 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+
AccessScope::allowsPrivateAccess(useDC, sourceDC));
12661267
case Accessibility::FilePrivate:
12671268
return useDC->getModuleScopeContext() == sourceDC->getModuleScopeContext();
12681269
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)