blob: 92afdd42078353ff9dc936416ce1b93ecc0fc63f [file] [log] [blame]
==============
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.