mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
254 lines
11 KiB
ReStructuredText
254 lines
11 KiB
ReStructuredText
==============
|
|
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 three levels of access: "private", "internal", and "public".
|
|
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#. 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.
|
|
|
|
.. contents:: :local:
|
|
|
|
Rules
|
|
======
|
|
|
|
Access to a particular entity is considered relative to the current
|
|
*access context.* The access context of an entity is the current
|
|
file (if ``private``), 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 context.
|
|
|
|
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
|
|
-------------------
|
|
|
|
A global function, constant, or variable 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.
|
|
|
|
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 the type's access level is ``private``, the access level of members
|
|
defaults to ``private``. If the type's access level is ``internal`` or
|
|
``public``, the access level of members defaults to ``internal``.
|
|
|
|
- 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 :ref:`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:
|
|
|
|
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 in an extension have the same default access level as members declared
|
|
within the extended type. However, 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.
|
|
|
|
Extensions with explicit access modifiers may not add new protocol
|
|
conformances, since Swift does not support private protocol conformances
|
|
(see :ref:`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.
|
|
|
|
Both ``private`` and ``internal`` increase opportunities for devirtualization,
|
|
though it is still possible to put a subclass of a ``private`` class within the
|
|
same file.
|
|
|
|
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`` 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``, ``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`` serves the proper use case of limiting access to the
|
|
implementation details of a class (even from the rest of the module!) while
|
|
not requiring that all of those implementation details be written lexically
|
|
inside the class.
|
|
|
|
"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 context.
|
|
|
|
- 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.
|