Skip to content

Commit b5003f4

Browse files
jrose-appletkremenek
authored andcommitted
Private members may not satisfy protocol requirements, ever. (#3842)
* Private members may not satisfy protocol requirements, ever. ...because by construction they can be invoked from outside of the type. Finishing up SE-0025 ('private' and 'fileprivate'). * Update docs and mark SE-0025 ('private' and 'fileprivate') as done! There's still improvements we can make (see 508e825), but the feature is in place and should be working correctly.
1 parent 2dd4754 commit b5003f4

File tree

10 files changed

+121
-62
lines changed

10 files changed

+121
-62
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Note: This is in reverse chronological order, so newer entries are added to the
33
Swift 3.0
44
---------
55

6+
* [SE-0025](https://github.com/apple/swift-evolution/blob/master/proposals/0025-scoped-access-level.md): A declaration marked as `private` can now only be accessed within the lexical scope it is declared in (essentially the enclosing curly braces `{}`). A `private` declaration at the top level of a file can be accessed anywhere in that file, as in Swift 2. The access level formerly known as `private` is now called `fileprivate`.
7+
68
* [SE-0131](https://github.com/apple/swift-evolution/blob/master/proposals/0131-anyhashable.md):
79
The standard library provides a new type `AnyHashable` for use in heterogenous
810
hashed collections. Untyped `NSDictionary` and `NSSet` APIs from Objective-C

docs/AccessControl.rst

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,38 @@ The general guiding principle of Swift access control:
77
**No entity can be defined in terms of another entity that has a lower
88
access level.**
99

10-
There are three levels of access: "private", "internal", and "public".
11-
Private entities can only be accessed from within the source file where they
12-
are defined. Internal entities can be accessed anywhere within the module they
13-
are defined. Public entities can be accessed from anywhere within the module
14-
and from any other context that imports the current module.
10+
There are four levels of access: "private", "fileprivate", "internal", and
11+
"public". Private entities can only be accessed from within the lexical scope
12+
where they are defined. File-private entities can only be accessed from within
13+
the source file where they are defined. Internal entities can be accessed
14+
anywhere within the module they are defined. Public entities can be accessed
15+
from anywhere within the module and from any other context that imports the
16+
current module.
1517

1618
The names ``public`` and ``private`` have precedent in many languages;
17-
``internal`` comes from C#. In the future, ``public`` may be used for both API
18-
and SPI, at which point we may design additional annotations to distinguish the
19-
two.
19+
``internal`` comes from C# and ``fileprivate`` from the Swift community. In the
20+
future, ``public`` may be used for both API and SPI, at which point we may
21+
design additional annotations to distinguish the two.
2022

2123
By default, most entities in a source file have ``internal`` access.
2224
This optimizes for the most common case—a single-target application
2325
project—while not accidentally revealing entities to clients of a framework
2426
module.
2527

28+
.. warning:: This document has not yet been updated for SE-0117, which adds the
29+
"open" level of access.
30+
31+
2632
.. contents:: :local:
2733

2834
Rules
2935
======
3036

3137
Access to a particular entity is considered relative to the current
32-
*access context.* The access context of an entity is the current
33-
file (if ``private``), the current module (if ``internal``), or the current
34-
program (if ``public``). A reference to an entity may only be written within
35-
the entity's access context.
38+
*access scope.* The access scope of an entity is its immediate lexical scope
39+
(if ``private``), the current file (if ``fileprivate``), the current module (if
40+
``internal``), or the current program (if ``public``). A reference to an entity
41+
may only be written within the entity's access scope.
3642

3743
If a particular entity is not accessible, it does not appear in name lookup,
3844
unlike in C++. However, access control does not restrict access to members via
@@ -43,9 +49,13 @@ visibility of symbols in a linked binary.
4349
Globals and Members
4450
-------------------
4551

46-
A global function, constant, or variable may have any access level less than
47-
or equal to the access level of its type. That is, a ``private`` constant can
48-
have ``public`` type, but not the other way around.
52+
All globals and members have a default access level of ``internal``, except
53+
within extensions (as described below).
54+
55+
A declaration may have any access level less than or equal to the access level
56+
of its type. That is, a ``private`` constant can have ``public`` type, but not
57+
the other way around. It is legal for a member to have greater access than its
58+
enclosing type, but this has no effect.
4959

5060
Accessors for variables have the same access level as their associated variable.
5161
The setter may be explicitly annotated with an access level less than or equal
@@ -56,10 +66,6 @@ An initializer, method, subscript, or property may have any access level less
5666
than or equal to the access level of its type (including the implicit 'Self'
5767
type), with a few additional rules:
5868

59-
- If the type's access level is ``private``, the access level of members
60-
defaults to ``private``. If the type's access level is ``internal`` or
61-
``public``, the access level of members defaults to ``internal``.
62-
6369
- If a member is used to satisfy a protocol requirement, its access level must
6470
be at least as high as the protocol conformance's; see :ref:`Protocols` below.
6571

@@ -130,10 +136,13 @@ struct, enum, or class may be extended whenever it is accessible.
130136
A class may be subclassed whenever it is accessible. A class may have any
131137
access level less than or equal to the access level of its superclass.
132138

133-
Members in an extension have the same default access level as members declared
134-
within the extended type. However, an extension may be marked with an explicit
135-
access modifier (e.g. ``private extension``), in which case the default
136-
access level of members within the extension is changed to match.
139+
Members within constrained extensions must have access less than or equal to
140+
the access level of the types used in the constraints.
141+
142+
An extension may be marked with an explicit access modifier (e.g. ``private
143+
extension``), in which case the default access level of members within the
144+
extension is changed to match. No member within such an extension may have
145+
broader access than the new default.
137146

138147
Extensions with explicit access modifiers may not add new protocol
139148
conformances, since Swift does not support private protocol conformances
@@ -167,9 +176,9 @@ or other extensions from outside the module. Therefore, members of a subclass
167176
or extension will not conflict with or inadvertently be considered to override
168177
non-accessible members of the superclass.
169178

170-
Both ``private`` and ``internal`` increase opportunities for devirtualization,
179+
Access levels lower than ``public`` increase opportunities for devirtualization,
171180
though it is still possible to put a subclass of a ``private`` class within the
172-
same file.
181+
same scope.
173182

174183
Most information about a non-``public`` entity still has to be put into a
175184
module file for now, since we don't have resilience implemented. This can be
@@ -186,9 +195,10 @@ selector for members, everything can be inspected at runtime, and even a
186195
private member can cause selector conflicts. In this case, access control is
187196
only useful for discipline purposes.
188197

189-
Members explicitly marked ``private`` are *not* exposed to Objective-C unless
190-
they are also marked ``@objc`` (or ``@IBAction`` or similar), even if declared
191-
within a class implicitly or explicitly marked ``@objc``.
198+
Members explicitly marked ``private`` or ``fileprivate`` are *not* exposed to
199+
Objective-C unless they are also marked ``@objc`` (or ``@IBAction`` or
200+
similar), even if declared within a class implicitly or explicitly marked
201+
``@objc``.
192202

193203
Any ``public`` entities will be included in the generated header. In an
194204
application or unit test target, ``internal`` entities will be exposed as well.
@@ -201,7 +211,7 @@ This proposal omits two forms of access control commonly found in other
201211
languages, a "class-implementation-only" access (often called "private"), and a
202212
"class and any subclasses" access (often called "protected"). We chose not to
203213
include these levels of access control because they do not add useful
204-
functionality beyond ``private``, ``internal``, and ``public``.
214+
functionality beyond ``private``, ``fileprivate``, ``internal``, and ``public``.
205215

206216
"class-only"
207217
If "class-only" includes extensions of the class, it is clear that it
@@ -211,10 +221,9 @@ functionality beyond ``private``, ``internal``, and ``public``.
211221
limit forces code to be declared within the class that might otherwise
212222
naturally be a top-level helper or an extension method on another type.
213223

214-
``private`` serves the proper use case of limiting access to the
224+
``private`` and ``fileprivate`` serve the use case of limiting access to the
215225
implementation details of a class (even from the rest of the module!) while
216-
not requiring that all of those implementation details be written lexically
217-
inside the class.
226+
not tying access to the notion of type.
218227

219228
"protected"
220229
"protected" access provides no guarantees of information hiding, since any
@@ -235,7 +244,7 @@ Potential Future Directions
235244
===========================
236245

237246
- Allowing ``private`` or ``internal`` protocol conformances, which are only
238-
accessible at compile-time from a particular access context.
247+
accessible at compile-time from a particular access scope.
239248

240249
- Limiting particular capabilities, such as marking something ``final(public)``
241250
to restrict subclassing or overriding outside of the current module.

docs/OptimizationTips.rst

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,17 @@ in the following ``C.array1`` and ``D.array1`` will be accessed directly
134134
d.array2[i] = ... // Will access D.array2 through dynamic dispatch.
135135
}
136136

137-
Advice: Use 'private' when declaration does not need to be accessed outside of file
138-
-----------------------------------------------------------------------------------
139-
140-
Applying the ``private`` keyword to a declaration restricts the visibility of
141-
the declaration to the file in which it is declared. This allows the compiler to
142-
be able to ascertain all other potentially overriding declarations. Thus the
143-
absence of any such declarations enables the compiler to infer the ``final``
144-
keyword automatically and remove indirect calls for methods and field accesses
145-
accordingly. For instance in the following, ``e.doSomething()`` and
146-
``f.myPrivateVar``, will be able to be accessed directly assuming ``E``, ``F``
147-
do not have any overriding declarations in the same file:
137+
Advice: Use 'private' and 'fileprivate' when declaration does not need to be accessed outside of file
138+
-----------------------------------------------------------------------------------------------------
139+
140+
Applying the ``private`` or ``fileprivate`` keywords to a declaration restricts
141+
the visibility of the declaration to the file in which it is declared. This
142+
allows the compiler to be able to ascertain all other potentially overriding
143+
declarations. Thus the absence of any such declarations enables the compiler to
144+
infer the ``final`` keyword automatically and remove indirect calls for methods
145+
and field accesses accordingly. For instance in the following,
146+
``e.doSomething()`` and ``f.myPrivateVar``, will be able to be accessed directly
147+
assuming ``E``, ``F`` do not have any overriding declarations in the same file:
148148

149149
::
150150

@@ -153,7 +153,7 @@ do not have any overriding declarations in the same file:
153153
}
154154

155155
class F {
156-
private var myPrivateVar : Int
156+
fileprivate var myPrivateVar : Int
157157
}
158158

159159
func usingE(_ e: E) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,11 +1220,9 @@ checkWitnessAccessibility(Accessibility *requiredAccess,
12201220
bool *isSetter) {
12211221
*isSetter = false;
12221222

1223-
// FIXME: Handle "private(set)" requirements.
12241223
*requiredAccess = std::min(Proto->getFormalAccess(), *requiredAccess);
1225-
1226-
if (*requiredAccess == Accessibility::Private)
1227-
return false;
1224+
if (TC.getLangOpts().EnableSwift3Private)
1225+
*requiredAccess = std::max(*requiredAccess, Accessibility::FilePrivate);
12281226

12291227
Accessibility witnessAccess = witness->getFormalAccess(DC);
12301228

@@ -1242,9 +1240,20 @@ checkWitnessAccessibility(Accessibility *requiredAccess,
12421240
*isSetter = true;
12431241

12441242
auto ASD = cast<AbstractStorageDecl>(witness);
1245-
const DeclContext *accessDC = nullptr;
1246-
if (*requiredAccess == Accessibility::Internal)
1243+
const DeclContext *accessDC;
1244+
switch (*requiredAccess) {
1245+
case Accessibility::Public:
1246+
accessDC = nullptr;
1247+
break;
1248+
case Accessibility::Internal:
12471249
accessDC = DC->getParentModule();
1250+
break;
1251+
case Accessibility::FilePrivate:
1252+
case Accessibility::Private:
1253+
accessDC = DC->getModuleScopeContext();
1254+
break;
1255+
}
1256+
12481257
if (!ASD->isSetterAccessibleFrom(accessDC))
12491258
return true;
12501259
}

stdlib/public/SDK/Foundation/IndexSet.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ extension IndexSet : CustomStringConvertible, CustomDebugStringConvertible, Cust
792792

793793
/// Iterate two index sets on the boundaries of their ranges. This is where all of the interesting stuff happens for exclusive or, intersect, etc.
794794
private struct IndexSetBoundaryIterator : IteratorProtocol {
795-
private typealias Element = IndexSet.Element
795+
typealias Element = IndexSet.Element
796796

797797
private var i1 : IndexSet.RangeView.Iterator
798798
private var i2 : IndexSet.RangeView.Iterator

test/NameBinding/accessibility.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,11 @@ struct ConformerByLocalType : TypeProto {
144144
}
145145

146146
private struct PrivateConformerByLocalType : TypeProto {
147-
private struct TheType {} // okay
147+
struct TheType {} // okay
148+
}
149+
150+
private struct PrivateConformerByLocalTypeBad : TypeProto {
151+
private struct TheType {} // expected-error {{struct 'TheType' must be as accessible as its enclosing type because it matches a requirement in protocol 'TypeProto'}} {{3-10=fileprivate}}
148152
}
149153
#endif
150154

test/SILOptimizer/basic-callee-printer.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ private protocol private_proto_4 {
392392
}
393393

394394
public class private_proto_public_class_private_method : private_proto_4 {
395-
private func theMethod()
395+
fileprivate func theMethod()
396396
}
397397

398398
private func call_through_private_proto_4<T : private_proto_4>(x: T)

test/SILOptimizer/function_order.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ private protocol private_proto_4 {
340340
}
341341

342342
public class private_proto_public_class_private_method : private_proto_4 {
343-
private func theMethod()
343+
fileprivate func theMethod()
344344
}
345345

346346
private func call_through_private_proto_4<T : private_proto_4>(x: T)

test/Sema/accessibility.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public struct PublicStruct: PublicProto, InternalProto, FilePrivateProto, Privat
2222
private func publicReq() {} // expected-error {{method 'publicReq()' must be declared public because it matches a requirement in public protocol 'PublicProto'}} {{3-10=public}}
2323
private func internalReq() {} // expected-error {{method 'internalReq()' must be declared internal because it matches a requirement in internal protocol 'InternalProto'}} {{3-10=internal}}
2424
private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}}
25-
private func privateReq() {}
25+
private func privateReq() {} // expected-error {{method 'privateReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PrivateProto'}} {{3-10=fileprivate}}
2626

2727
public var publicVar = 0
2828
}
@@ -32,7 +32,7 @@ internal struct InternalStruct: PublicProto, InternalProto, FilePrivateProto, Pr
3232
private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{3-10=internal}}
3333
private func internalReq() {} // expected-error {{method 'internalReq()' must be declared internal because it matches a requirement in internal protocol 'InternalProto'}} {{3-10=internal}}
3434
private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}}
35-
private func privateReq() {}
35+
private func privateReq() {} // expected-error {{method 'privateReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PrivateProto'}} {{3-10=fileprivate}}
3636

3737
public var publicVar = 0
3838
}
@@ -42,17 +42,17 @@ fileprivate struct FilePrivateStruct: PublicProto, InternalProto, FilePrivatePro
4242
private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{3-10=fileprivate}}
4343
private func internalReq() {} // expected-error {{method 'internalReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'InternalProto'}} {{3-10=fileprivate}}
4444
private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}}
45-
private func privateReq() {}
45+
private func privateReq() {} // expected-error {{method 'privateReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PrivateProto'}} {{3-10=fileprivate}}
4646

4747
public var publicVar = 0
4848
}
4949

5050
// expected-note@+1 * {{type declared here}}
5151
private struct PrivateStruct: PublicProto, InternalProto, FilePrivateProto, PrivateProto {
52-
private func publicReq() {}
53-
private func internalReq() {}
54-
private func filePrivateReq() {}
55-
private func privateReq() {}
52+
private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{3-10=fileprivate}}
53+
private func internalReq() {} // expected-error {{method 'internalReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'InternalProto'}} {{3-10=fileprivate}}
54+
private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}}
55+
private func privateReq() {} // expected-error {{method 'privateReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PrivateProto'}} {{3-10=fileprivate}}
5656

5757
public var publicVar = 0
5858
}

test/Sema/accessibility_private.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,38 @@ class Sub : Container {
110110
var subInner: PrivateInner? // FIXME expected-error {{use of undeclared type 'PrivateInner'}}
111111
var subInnerQualified: Container.PrivateInner? // FIXME expected-error {{'PrivateInner' is not a member type of 'Container'}}
112112
}
113+
114+
115+
protocol VeryImportantProto {
116+
associatedtype Assoc
117+
var value: Int { get set } // expected-note {{protocol requires property 'value' with type 'Int'; do you want to add a stub?}}
118+
}
119+
120+
private struct VIPPrivateType : VeryImportantProto {
121+
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'}}
122+
var value: Int
123+
}
124+
125+
private struct VIPPrivateProp : VeryImportantProto {
126+
typealias Assoc = Int
127+
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}}
128+
}
129+
130+
private struct VIPPrivateSetProp : VeryImportantProto {
131+
typealias Assoc = Int
132+
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}}
133+
}
134+
135+
private class VIPPrivateSetBase {
136+
private var value: Int = 0
137+
}
138+
private class VIPPrivateSetSub : VIPPrivateSetBase, VeryImportantProto { // expected-error {{type 'VIPPrivateSetSub' does not conform to protocol 'VeryImportantProto'}}
139+
typealias Assoc = Int
140+
}
141+
142+
private class VIPPrivateSetPropBase {
143+
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}}
144+
}
145+
private class VIPPrivateSetPropSub : VIPPrivateSetPropBase, VeryImportantProto {
146+
typealias Assoc = Int
147+
}

0 commit comments

Comments
 (0)