mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
253 lines
11 KiB
Markdown
253 lines
11 KiB
Markdown
# Access Control
|
||
|
||
The general guiding principle of Swift access control:
|
||
|
||
> **No entity can be defined in terms of another entity that has a lower
|
||
> access level.**
|
||
|
||
There are four levels of access: \"private\", \"fileprivate\",
|
||
\"internal\", and \"public\". Private entities can only be accessed from
|
||
within the lexical scope where they are defined. File-private entities
|
||
can only be accessed from within the source file where they are defined.
|
||
Internal entities can be accessed anywhere within the module they are
|
||
defined. Public entities can be accessed from anywhere within the module
|
||
and from any other context that imports the current module.
|
||
|
||
The names `public` and `private` have precedent in many languages;
|
||
`internal` comes from C\# and `fileprivate` from the Swift community. In
|
||
the future, `public` may be used for both API and SPI, at which point we
|
||
may design additional annotations to distinguish the two.
|
||
|
||
By default, most entities in a source file have `internal` access. This
|
||
optimizes for the most common case – a single-target application
|
||
project – while not accidentally revealing entities to clients of a
|
||
framework module.
|
||
|
||
_**Warning**_
|
||
|
||
This document has not yet been updated for
|
||
[SE-0117](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0117-non-public-subclassable-by-default.md),
|
||
which adds the \"open\" level of access.
|
||
|
||
## Rules
|
||
|
||
Access to a particular entity is considered relative to the current
|
||
*access scope.* The access scope of an entity is its immediate lexical
|
||
scope (if `private`), the current file (if `fileprivate`), the current
|
||
module (if `internal`), or the current program (if `public`). A
|
||
reference to an entity may only be written within the entity\'s access
|
||
scope.
|
||
|
||
If a particular entity is not accessible, it does not appear in name
|
||
lookup, unlike in C++. However, access control does not restrict access
|
||
to members via runtime reflection (where applicable), nor does it
|
||
necessarily restrict visibility of symbols in a linked binary.
|
||
|
||
### Globals and Members
|
||
|
||
All globals and members have a default access level of `internal`,
|
||
except within extensions (as described below).
|
||
|
||
A declaration may have any access level less than or equal to the access
|
||
level of its type. That is, a `private` constant can have `public` type,
|
||
but not the other way around. It is legal for a member to have greater
|
||
access than its enclosing type, but this has no effect.
|
||
|
||
Accessors for variables have the same access level as their associated
|
||
variable. The setter may be explicitly annotated with an access level
|
||
less than or equal to the access level of the variable; this is written
|
||
as `private(set)` or `internal(set)` before the `var` introducer.
|
||
|
||
An initializer, method, subscript, or property may have any access level
|
||
less than or equal to the access level of its type (including the
|
||
implicit \'Self\' type), with a few additional rules:
|
||
|
||
- If a member is used to satisfy a protocol requirement, its access
|
||
level must be at least as high as the protocol conformance\'s; see
|
||
["Protocols"](#protocols) below.
|
||
- If an initializer is `required` by a superclass, its access level
|
||
must be at least as high as the access level of the subclass itself.
|
||
- Accessors for subscripts follow the same rules as accessors for
|
||
variables.
|
||
- A member may be overridden whenever it is accessible.
|
||
|
||
The implicit memberwise initializer for a struct has the minimum access
|
||
level of all of the struct\'s stored properties, except that if all
|
||
properties are `public` the initializer is `internal`. The implicit
|
||
no-argument initializer for structs and classes follows the default
|
||
access level for the type.
|
||
|
||
Currently, enum cases always have the same access level as the enclosing
|
||
enum.
|
||
|
||
Deinitializers are only invoked by the runtime and do not nominally have
|
||
access. Internally, the compiler represents them as having the same
|
||
access level as the enclosing type.
|
||
|
||
### Protocols
|
||
|
||
A protocol may have any access level less than or equal to the access
|
||
levels of the protocols it refines. That is, a `private` ExtendedWidget
|
||
protocol can refine a `public` Widget protocol, but not the other way
|
||
around.
|
||
|
||
The access level of a requirement is the access level of the enclosing
|
||
protocol, even when the protocol is `public`. Currently, requirements
|
||
may not be given a lower access level than the enclosing protocol.
|
||
|
||
Swift does not currently support private protocol conformances, so for
|
||
runtime consistency, the access level of the conformance of type T to
|
||
protocol P is equal to the minimum of T\'s access level and P\'s access
|
||
level; that is, the conformance is accessible whenever both T and P are
|
||
accessible. This does not change if the protocol is conformed to in an
|
||
extension. (The access level of a conformance is not currently reflected
|
||
in the source, but is a useful concept for applying restrictions
|
||
consistently.)
|
||
|
||
All members used to satisfy a conformance must have an access level at
|
||
least as high as the conformance\'s. This ensures consistency between
|
||
views of the type; if any member has a *lower* access level than the
|
||
conformance, then the member could be accessed anyway through a generic
|
||
function constrained by the protocol.
|
||
|
||
_**Note**_
|
||
|
||
This rule disallows an `internal` member of a protocol extension to
|
||
satisfy a `public` requirement for a `public` type. Removing this
|
||
limitation is not inherently unsafe, but (a) may be unexpected given the
|
||
lack of explicit reference to the member, and (b) results in references
|
||
to non-public symbols in the current representation.
|
||
|
||
A protocol may be used as a type whenever it is accessible. A nominal
|
||
can conform to a protocol whenever the protocol is accessible.
|
||
|
||
### Structs, Enums, and Classes
|
||
|
||
A struct, enum, or class may be used as a type whenever it is
|
||
accessible. A struct, enum, or class may be extended whenever it is
|
||
accessible.
|
||
|
||
A class may be subclassed whenever it is accessible. A class may have
|
||
any access level less than or equal to the access level of its
|
||
superclass.
|
||
|
||
Members within constrained extensions must have access less than or
|
||
equal to the access level of the types used in the constraints.
|
||
|
||
An extension may be marked with an explicit access modifier (e.g.
|
||
`private extension`), in which case the default access level of members
|
||
within the extension is changed to match. No member within such an
|
||
extension may have broader access than the new default.
|
||
|
||
Extensions with explicit access modifiers may not add new protocol
|
||
conformances, since Swift does not support private protocol conformances
|
||
(see ["Protocols"](#protocols) above).
|
||
|
||
A type may conform to a protocol with lower access than the type itself.
|
||
|
||
### Types
|
||
|
||
A nominal type\'s access level is the same as the access level of the
|
||
nominal declaration itself. A generic type\'s access level is the
|
||
minimum of the access level of the base type and the access levels of
|
||
all generic argument types.
|
||
|
||
A tuple type\'s access level is the minimum of the access levels of its
|
||
elements. A function type\'s access level is the minimum of the access
|
||
levels of its input and return types.
|
||
|
||
A typealias may have any access level up to the access level of the type
|
||
it aliases. That is, a `private` typealias can refer to a `public` type,
|
||
but not the other way around. This includes associated types used to
|
||
satisfy protocol conformances.
|
||
|
||
## Runtime Guarantees
|
||
|
||
Non-`public` members of a class or extension will not be seen by
|
||
subclasses or other extensions from outside the module. Therefore,
|
||
members of a subclass or extension will not conflict with or
|
||
inadvertently be considered to override non-accessible members of the
|
||
superclass.
|
||
|
||
Access levels lower than `public` increase opportunities for
|
||
devirtualization, though it is still possible to put a subclass of a
|
||
`private` class within the same scope.
|
||
|
||
Most information about a non-`public` entity still has to be put into a
|
||
module file for now, since we don\'t have resilience implemented. This
|
||
can be improved later, and is no more revealing than the information
|
||
currently available in the runtime for pure Objective-C classes.
|
||
|
||
### Interaction with Objective-C
|
||
|
||
If an entity is exposed to Objective-C, most of the runtime guarantees
|
||
and optimization opportunities go out the window. We have to use a
|
||
particular selector for members, everything can be inspected at runtime,
|
||
and even a private member can cause selector conflicts. In this case,
|
||
access control is only useful for discipline purposes.
|
||
|
||
Members explicitly marked `private` or `fileprivate` are *not* exposed
|
||
to Objective-C unless they are also marked `@objc` (or `@IBAction` or
|
||
similar), even if declared within a class implicitly or explicitly
|
||
marked `@objc`.
|
||
|
||
Any `public` entities will be included in the generated header. In an
|
||
application or unit test target, `internal` entities will be exposed as
|
||
well.
|
||
|
||
## Non-Goals: \"class-only\" and \"protected\"
|
||
|
||
This proposal omits two forms of access control commonly found in other
|
||
languages, a \"class-implementation-only\" access (often called
|
||
\"private\"), and a \"class and any subclasses\" access (often called
|
||
\"protected\"). We chose not to include these levels of access control
|
||
because they do not add useful functionality beyond `private`,
|
||
`fileprivate`, `internal`, and `public`.
|
||
|
||
### "class-only"
|
||
|
||
If \"class-only\" includes extensions of the class, it is clear that
|
||
it provides no protection at all, since a class may be extended from
|
||
any context where it is accessible. So a hypothetical \"class-only\"
|
||
must already be limited with regards to extensions. Beyond that,
|
||
however, a \"class-only\" limit forces code to be declared within
|
||
the class that might otherwise naturally be a top-level helper or an
|
||
extension method on another type.
|
||
|
||
`private` and `fileprivate` serve the use case of limiting access to
|
||
the implementation details of a class (even from the rest of the
|
||
module!) while not tying access to the notion of type.
|
||
|
||
### "protected"
|
||
|
||
"protected\" access provides no guarantees of information hiding,
|
||
since any subclass can now access the implementation details of its
|
||
superclass\-\--and expose them publicly, if it so chooses. This
|
||
interacts poorly with our future plans for resilient APIs.
|
||
Additionally, it increases the complexity of the access control
|
||
model for both the compiler and for developers, and like
|
||
\"class-only\" it is not immediately clear how it interacts with
|
||
extensions.
|
||
|
||
Though it is not compiler-enforced, members that might be considered
|
||
\"protected\" are effectively publicly accessible, and thus should
|
||
be marked `public` in Swift. They can still be documented as
|
||
intended for overriding rather than for subclassing, but the
|
||
specific details of this are best dealt with on a case-by-case
|
||
basis.
|
||
|
||
## Potential Future Directions
|
||
|
||
- Allowing `private` or `internal` protocol conformances, which are
|
||
only accessible at compile-time from a particular access scope.
|
||
- Limiting particular capabilities, such as marking something
|
||
`final(public)` to restrict subclassing or overriding outside of the
|
||
current module.
|
||
- Allowing the Swift parts of a mixed-source framework to access
|
||
private headers.
|
||
- Revealing `internal` Swift API in a mixed-source framework in a
|
||
second generated header.
|
||
- Levels of `public`, for example `public("SPI")`.
|
||
- Enum cases less accessible than the enum.
|
||
- Protocol requirements less accessible than the protocol.
|