mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
1237 lines
89 KiB
TeX
1237 lines
89 KiB
TeX
\documentclass[../generics]{subfiles}
|
|
|
|
\begin{document}
|
|
|
|
\chapter{Declarations}\label{chap:decls}
|
|
|
|
\lettrine{D}{eclarations} are the \IndexDefinition{declaration}building blocks of Swift programs. In \ChapRef{chap:compilation model}, we saw that the user's entire program is a \index{module declaration}\emph{module declaration} at the root of a hierarchy, with \index{file unit}\emph{file units} as immediate children. Now, we will see that a file unit holds a list of \IndexDefinition{top-level declaration}\emph{top-level declarations}, which correspond to the main divisions in a source file, and these declarations can have further declarations nested within them. We will investigate the different kinds of declarations into a taxonomy, as we did with types in \ChapRef{chap:types}. Then, we focus on the syntactic representations for declaring generic parameters and stating requirements, which are common to all generic declarations. Finally, we end the chapter with a discussion of functions, closures, and captured values.
|
|
|
|
We begin with two major divisions in the declaration taxonomy:
|
|
\begin{enumerate}
|
|
\item A \IndexDefinition{value declaration}\emph{value declaration} is a one that can be referenced by name from an \index{expression}expression; this includes variables, functions, and such. Every value declaration has an \IndexDefinition{interface type!value declaration}\emph{interface type}, which is the type we assign to an expression naming this declaration.
|
|
\item A \IndexDefinition{type declaration}\emph{type declaration} is one that can be referenced by name from within a \index{type representation}type representation. This includes structs, type aliases, and so on. A type declaration declares a type, called the \IndexDefinition{declared interface type}\emph{declared interface type} of the type declaration.
|
|
\end{enumerate}
|
|
Not all declarations are value declarations. An \index{extension declaration}extension declaration adds members to an existing nominal type declaration, as we'll see in \ChapRef{chap:extensions}, but an extension does not itself have a name. A \IndexDefinition{top-level code declaration}\emph{top-level code declaration} holds the statements and expressions written at the top level of a source file, and again, it does not have a name, semantically.
|
|
|
|
\paragraph{Declaration contexts.} Every declaration is contained in a \IndexDefinition{declaration context}\emph{declaration context}, and a declaration context is anything that \emph{contains} declarations. Consider this program:
|
|
\begin{Verbatim}
|
|
func squares(_ nums: [Int]) -> [Int] {
|
|
return nums.map { x in x * x }
|
|
}
|
|
\end{Verbatim}
|
|
The \index{parameter declaration}parameter declaration ``\texttt{x}'' is a child of the closure expression ``\verb|{ x in x * x }|'', and not a direct child of the enclosing function declaration. So a \index{closure expression}closure expression is a declaration context, but not a declaration. On the other hand, a parameter declaration is a declaration, but not a declaration context. Finally, the \texttt{squares()} function itself is both a declaration, and a declaration context.
|
|
|
|
\paragraph{Type declarations.} Since the Swift grammar allows type representations to appear inside expressions, every type declaration is \emph{also} a value declaration. The interface type of a type declaration is the metatype formed from its declared interface type. This is a mouthful, but the basic idea should be familiar to every Swift programmer. Consider a global variable with a type annotation and an initial value:
|
|
\begin{Verbatim}
|
|
struct Horse {}
|
|
let myHorse: Horse = Horse()
|
|
\end{Verbatim}
|
|
The struct declaration \index{horse}\texttt{Horse} is referenced twice here, first in the type annotation that follows ``\texttt{:}'', and then again in the \index{expression}\index{initial value expression}initial value expression that follows ``\texttt{=}''. Inside the type annotation, \texttt{Horse} means the \emph{declared interface type} of \texttt{Horse}, which is simply the nominal type~\texttt{Horse}; we're declaring that the \texttt{myHorse} variable stores a value whose type has the stated name. The second reference to \texttt{Horse}, from the initial value expression, refers to \emph{the type itself} as a value, so this uses its \emph{interface type}, which is the \index{metatype type}metatype \texttt{Horse.Type}. (Recall the diagram from \SecRef{sec:more types}.) Furthermore, this metatype value is the callee of a call expression, which is a shorthand for calling a constructor member named \texttt{init}. We can write this out as follows to be even more explicit:
|
|
\begin{Verbatim}
|
|
let myHorseType: Horse.Type = Horse.self
|
|
let myHorse: Horse = myHorseType.init()
|
|
\end{Verbatim}
|
|
|
|
\paragraph{Nominal type declarations.}
|
|
\IndexDefinition{nominal type declaration}Declared by the \texttt{struct}, \IndexDefinition{enum declaration}\texttt{enum}, and \IndexDefinition{class declaration}\texttt{class} keywords; \IndexSwift{5.5}Swift~5.5 also added \texttt{actor}, which to us, is just a class~\cite{se0306}. Nominal type declarations are declaration contexts, and the declarations they contain are called their \IndexDefinition{member declaration}\emph{member declarations}. A function member declaration is commonly called a \IndexDefinition{method declaration}\emph{method}, a member variable is a \IndexDefinition{property declaration}\emph{property}, and a \IndexDefinition{member type declaration}\emph{member type declaration} is exactly that.
|
|
|
|
Structs and classes can contain a special kind of property declaration called a \IndexDefinition{stored property declaration}\emph{stored property declaration}. Struct values directly store their stored properties, while a class value is a reference to a heap allocated \index{boxing}box that contains its stored properties. A value of enum type stores exactly one element among several; instead of stored properties, enum declarations contain \IndexDefinition{enum element declaration}\emph{enum element declarations}, introduced with the \texttt{case} keyword.
|
|
|
|
The members of a nominal type declaration are visible to name lookup (\SecRef{name lookup}), both in the nominal type declaration's scope (unqualified lookup) and outside (qualified lookup). \ListingRef{unqualified lookup listing} shows three features we will discuss in detail later:
|
|
\begin{itemize}
|
|
\item A class can inherit from a \index{superclass type}superclass type, and members of the superclass are also visible from the subclass (\SecRef{classinheritance}).
|
|
\item Nominal type declarations can conform to protocols (\ChapRef{chap:conformances}).
|
|
\item Extensions add members to existing nominal type declarations (\ChapRef{chap:extensions}).
|
|
\end{itemize}
|
|
|
|
\begin{listing}\captionabove{Some behaviors of name lookup}\label{unqualified lookup listing}
|
|
\begin{Verbatim}
|
|
class Form { static func callee1() {} }
|
|
protocol Shape { static func callee2() }
|
|
extension Shape { static func callee3() {} }
|
|
|
|
struct Square: Shape {
|
|
class Circle: Form {
|
|
static func caller() {
|
|
... // unqualified lookup from here
|
|
}
|
|
}
|
|
}
|
|
\end{Verbatim}
|
|
\end{listing}
|
|
|
|
The body of \texttt{caller()} can reference \texttt{callee1()}, \texttt{callee2()} or \texttt{callee3()} with a single identifier. To resolve the identifier, unqualified lookup must search through these declaration contexts, starting in the top-left corner:
|
|
\begin{center}
|
|
\begin{tikzpicture}[x=4.5cm,y=1.5cm]
|
|
|
|
\node (caller) [class,anchor=mid] at (0,2) {\texttt{\vphantom{p}func caller()}};
|
|
|
|
\node (Circle) [class,anchor=mid] at (1,2) {\texttt{\vphantom{p}class Circle}};
|
|
\node (Square) [class,anchor=mid] at (2,2) {\texttt{struct Square}};
|
|
|
|
\node (Form) [class,anchor=mid] at (1,1) {\texttt{\vphantom{p}class Form}};
|
|
\node (Shape) [class,anchor=mid] at (2,1) {\texttt{protocol Shape}};
|
|
|
|
\node (extShape) [class,anchor=mid] at (2,0) {\texttt{extension Shape}};
|
|
|
|
\path [arrow] (caller) edge [above] node {\footnotesize{parent}} (Circle);
|
|
\path [arrow] (Circle) edge [above] node {\footnotesize{parent}} (Square);
|
|
\path [arrow] (Square) edge [right] node {\footnotesize{conforms to}} (Shape);
|
|
\path [arrow] (Circle) edge [right] node {\footnotesize{superclass}} (Form);
|
|
\draw [arrow] (Shape) edge [right] node {\footnotesize{extension}} (extShape);
|
|
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
We will say more about name lookup in \ChapRef{chap:type resolution} and \SecRef{direct lookup}.
|
|
|
|
A nominal type declaration declares a new type with its own name and identity (hence ``nominal''). The declared interface type of a nominal type declaration is called a \index{nominal type}nominal type, which we talked about in \SecRef{fundamental types}:
|
|
\begin{Verbatim}
|
|
struct Universe { // declared interface type: Universe
|
|
struct Galaxy {} // declared interface type: Universe.Galaxy
|
|
|
|
func solarSystem() {
|
|
struct Planet() // declared interface type: Planet
|
|
}
|
|
}
|
|
\end{Verbatim}
|
|
The declared interface type of the \texttt{Galaxy} struct is \texttt{Universe.Galaxy}, while the declared interface type of \texttt{Planet} is just \texttt{Planet}, with no parent type. This reflects the semantic difference; \texttt{Galaxy} is visible to qualified lookup as a member of \texttt{Universe}, while \texttt{Planet} is only visible to unqualified lookup within the scope of \texttt{solarSystem()}; we call it a \IndexDefinition{local type declaration}\emph{local type declaration}. \SecRef{nested nominal types} gives more detail about nominal type nesting.
|
|
|
|
\paragraph{Type alias declarations.} These are introduced by the \IndexDefinition{type alias declaration}\texttt{typealias} keyword. The \IndexDefinition{underlying type!of type alias declaration}underlying type is written on the right-hand side of ``\texttt{=}'':
|
|
\begin{Verbatim}
|
|
typealias Hands = Int // one hand is four inches
|
|
func measure(horse: Horse) -> Hands {...}
|
|
let metatype = Hands.self
|
|
\end{Verbatim}
|
|
The declared interface type of a type alias declaration is a \index{type alias type}type alias type. The canonical type of this type alias type is just the underlying type. Therefore, if we print the return type of \texttt{measure()} in a diagnostic message, we will print it as ``\texttt{Hands}'', but otherwise it behaves as if it were an \texttt{Int}.
|
|
|
|
As with all type declarations, the interface type of a type alias declaration is the metatype of its declared interface type. In the above, the expression ``\texttt{Hands.self}'' has the metatype type \texttt{Hands.Type}. This is a sugared type, canonically equal to \texttt{Int.Type}.
|
|
|
|
While type aliases are declaration contexts, the only declarations a type alias can contain are generic parameter declarations, in the event the type alias is generic.
|
|
|
|
\paragraph{Other type declarations.}
|
|
We've now seen the first two kinds of type declarations. In the next two sections, we expand on this by looking at the declarations of generic parameters, protocols, and associated types. At this point, our foray into Swift generics can begin in earnest. Here are all the type declaration kinds and their declared interface types, with a representative specimen of each:
|
|
\begin{center}
|
|
\begin{tabular}{ll}
|
|
\toprule
|
|
\textbf{Type declaration}&\textbf{Declared interface type}\\
|
|
\midrule
|
|
Nominal type declaration:&Nominal type:\\
|
|
\verb|struct Horse {...}|&\texttt{Horse}\\
|
|
\midrule
|
|
Type alias declaration:&Type alias type:\\
|
|
\verb|typealias Hands = Int|&\texttt{Hands} (canonically \texttt{Int})\\
|
|
\midrule
|
|
Generic parameter declaration:&Generic parameter type:\\
|
|
\verb|<T: Sequence>|&\tT\ (canonically \rT)\\
|
|
\midrule
|
|
Protocol declaration:&Protocol type:\\
|
|
\verb|protocol Sequence {...}|&\tSequence\\
|
|
\midrule
|
|
Associated type declaration:&Dependent member type:\\
|
|
\verb|associatedtype Element|&\verb|Self.Element|\\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
\section{Generic Parameters}\label{generic params}
|
|
|
|
Various kinds of declarations can have a \IndexDefinition{generic parameter list}generic parameter list. We call them \IndexDefinition{generic declaration}\emph{generic declarations}. We start with those where the generic parameter list is written in source: \index{struct declaration}structs, \index{enum declaration}enums, \index{class declaration}classes, \IndexDefinition{generic type alias}type aliases, \index{function declaration}functions, \index{constructor declaration}constructors, and \index{subscript declaration}subscripts. In all cases, the \IndexDefinition{parsed generic parameter list}generic parameter list is declared by following the generic declaration's name with the \texttt{<...>} syntax:
|
|
\begin{Verbatim}
|
|
struct Outer<T> {...}
|
|
\end{Verbatim}
|
|
Each comma-separated element in this list is a \IndexDefinition{generic parameter declaration}\emph{generic parameter declaration}; this is a type declaration that declares a generic parameter type. Generic parameter declarations are scoped to the entire \index{source range}source range of their parent declaration, the one with the generic parameter list. A declaration with a generic parameter list can be nested inside of another generic declaration; each inner generic declaration is effectively parameterized its own generic parameters, together with all generic parameters of its outer declarations. Thus, unqualified lookup will ``see'' all outer generic parameter declarations.
|
|
|
|
Any declaration kind that can have a generic parameter list is also a \index{declaration context}declaration context in our taxonomy, because it contains other declarations; namely, its generic parameter declarations. We say that a declaration context is a \IndexDefinition{generic context}\emph{generic context} if at least one parent context has a generic parameter list.
|
|
|
|
The name of a generic parameter declaration plays no role after \index{unqualified lookup}unqualified lookup. Instead, to each generic parameter declaration, we assign a pair of integers (or more accurately, natural numbers; they're non-negative), the \IndexDefinition{depth}\emph{depth} and the \IndexDefinition{index}\emph{index}:
|
|
\begin{itemize}
|
|
\item The depth selects a generic parameter list; the generic parameters declared by the outermost generic parameter list are at depth zero, and we increment the depth by one for each nested generic parameter list.
|
|
\item The index selects a generic parameter within a generic parameter list; we number sibling generic parameter declarations consecutively starting from zero.
|
|
\end{itemize}
|
|
|
|
Let's write some nested generic declarations inside the \texttt{Outer} struct above. In the following, \texttt{two()} is generic over \tT~and~\tU, while \texttt{four()} is generic over~\tT, \texttt{V}, \texttt{W}~and~\texttt{X}:
|
|
\begin{Verbatim}
|
|
struct Outer<T> {
|
|
func two<U>(u: U) -> T {...}
|
|
|
|
struct Both<V, W> {
|
|
func four<X>() -> X {...}
|
|
}
|
|
}
|
|
\end{Verbatim}
|
|
When type resolution resolves the type representation ``\tT'' in the return type of \texttt{two()}, it outputs a generic parameter type that prints as ``\tT'', if it appears in a diagnostic for example. This is a \index{sugared type}sugared type. Every generic parameter type also has a canonical form which only records the depth and index; we denote a canonical generic parameter type by ``\ttgp{d}{i}'', where \texttt{d} is the depth and \texttt{i} is the index. Two generic parameter types are canonically equal if they have the same depth and index. This is sound, because the depth and index unambiguously identify a generic parameter within its lexical scope.
|
|
|
|
Let's enumerate all generic parameters visible within \texttt{two()},
|
|
\begin{center}
|
|
\begin{tabular}{rll}
|
|
\toprule
|
|
\textbf{Name:}&\tT&\tU\\
|
|
\midrule
|
|
\textbf{Depth:}&0&1\\
|
|
\textbf{Index:}&0&0\\
|
|
\textbf{Type:}&\ttgp{0}{0}&\ttgp{1}{0}\\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
and \texttt{four()},
|
|
\begin{center}
|
|
\begin{tabular}{rllll}
|
|
\toprule
|
|
\textbf{Name:}&\tT&\texttt{V}&\texttt{W}&\texttt{X}\\
|
|
\midrule
|
|
\textbf{Depth:}&0&1&1&2\\
|
|
\textbf{Index:}&0&0&1&0\\
|
|
\textbf{Type:}&\ttgp{0}{0}&\ttgp{1}{0}&\ttgp{1}{1}&\ttgp{2}{0}\\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
The generic parameter~\tU\ of \texttt{two()} has the same \index{declared interface type!generic parameter declaration}declared interface type, \ttgp{1}{0}, as the generic parameter~\tV\ of \texttt{four()}. This is not a problem because the \index{source range}source ranges of their parent declarations, \texttt{two()} and \texttt{Both}, do not intersect.
|
|
|
|
The numbering by depth can be seen in the \index{declared interface type!nested nominal type}declared interface type of a nested generic nominal type declaration. For example, the declared interface type of \texttt{Outer.Both} is the generic nominal type \texttt{Outer<\ttgp{0}{0}>.Both<\ttgp{1}{0}, \ttgp{1}{1}>}.
|
|
|
|
\paragraph{Implicit generic parameters.} Sometimes the generic parameter list is not written in source. Every protocol declaration has a generic parameter list with a single generic parameter named \IndexSelf\tSelf\ (\SecRef{protocols}), and every extension declaration has a generic parameter list cloned from that of the extended type (\ChapRef{chap:extensions}). These implicit generic parameters can be referenced by name within their scope, just like the generic parameter declarations in a parsed generic parameter list (\SecRef{identtyperepr}).
|
|
|
|
Function, constructor and subscript declarations can also declare \IndexDefinition{opaque parameter}\emph{opaque parameters} with the \texttt{some} keyword, possibly in combination with a generic parameter list:
|
|
\begin{Verbatim}
|
|
func pickElement<E>(_ elts: some Sequence<E>) -> E {...}
|
|
\end{Verbatim}
|
|
An opaque parameter simultaneously declares a parameter value, a generic parameter type that is the type of the value, and a requirement this type must satisfy. Here, we can refer to ``\texttt{elts}'' from an expression inside the function body, but we cannot name the \emph{type} of ``\texttt{elts}'' in a type representation. From \index{expression}expression context however, the type of an opaque parameter can be obtained via the \texttt{type(of:)} special form, which produces a metatype value. This allows for invoking static methods on these types.
|
|
|
|
The \IndexDefinition{generic parameter list request}\Request{generic parameter list request} appends the opaque parameters to the parsed generic parameter list, so they follow the parsed generic parameters in index order. In \texttt{pickElement()}, the generic parameter \texttt{E} has canonical type~\rT, while the opaque parameter associated with ``\texttt{elts}'' has canonical type~\rU. Opaque parameter declarations also state a constraint type, which imposes a requirement on this unnamed generic parameter. We will discuss this in the next section. Note that when \texttt{some} appears in the return type of a function, it declares an \emph{opaque result type}, which is a related but different feature (\ChapRef{chap:opaque result types}).
|
|
|
|
In \ChapRef{chap:generic signatures}, we will discuss the generic signature, a data structure which collects all generic parameters that parameterize a declaration, independent of surface syntax.
|
|
|
|
\section{Requirements}\label{sec:requirements}
|
|
|
|
The requirements of a generic declaration constrain the generic argument types that can be provided by the caller. This endows the generic declaration's type parameters with new capabilities, so they abstract over the concrete types that satisfy those requirements. We use the following encoding for requirements in the theory and implementation.
|
|
|
|
\begin{definition}\label{requirement def}
|
|
A \IndexDefinition{requirement}\emph{requirement} is a triple consisting of a \emph{requirement kind}, a \IndexDefinition{subject type!of requirement}subject type~\tT\ (usually a \index{type parameter}type parameter), and one final piece of information that depends on the requirement kind:
|
|
\begin{itemize}
|
|
\item A \IndexDefinition{conformance requirement}\textbf{conformance requirement} $\TP$ states that the replacement type for~\tT\ must conform to~\texttt{P}, which must be a protocol, protocol composition, or parameterized protocol type.
|
|
\item A \IndexDefinition{superclass requirement}\textbf{superclass requirement} $\TC$ states that the replacement type for~\tT\ must be a subclass of some \index{class type}class type~\tC.
|
|
\item A \IndexDefinition{layout requirement}\textbf{layout requirement} $\TAnyObject$ states that the replacement type for~\tT\ must be represented as a single \index{reference count}reference-counted pointer at run time.
|
|
\item A \IndexDefinition{same-type requirement}\textbf{same-type requirement} $\TU$ states that the replacement types for \tT~and~\tU\ must be \index{canonical type equality}canonically equal.
|
|
\end{itemize}
|
|
\end{definition}
|
|
|
|
When looking at concrete instances of requirements in a self-contained snippet of code, there is no ambiguity in using the same notation for the first three kinds, because the type referenced by the right-hand side determines the requirement kind. When talking about requirements in the abstract, we will explicitly state that~\texttt{P} is some protocol, or~\tC\ is some class, before talking about $\TP$ or $\TC$.
|
|
|
|
\paragraph{Constraint types.}
|
|
Before we introduce the trailing \texttt{where} clause syntax for stating requirements in a fully general way, let's look at the shorthand of stating a \IndexDefinition{constraint type}\emph{constraint type} in the \IndexDefinition{inheritance clause!generic parameter declaration}inheritance clause of a \index{generic parameter declaration}generic parameter declaration:
|
|
\begin{Verbatim}
|
|
func allEqual<E: Equatable>(_ elements: [E]) {...}
|
|
\end{Verbatim}
|
|
The generic parameter declaration~\texttt{E} declares the generic parameter type~\rT, and it also states the constraint type \texttt{Equatable}. This is a protocol declared in the standard library, so the stated requirement is the conformance requirement $\ConfReq{\rT}{Equatable}$. More generally, the constraint type is one of the following:
|
|
\begin{enumerate}
|
|
\item A \index{protocol type!constraint type}protocol type, like \texttt{Equatable}.
|
|
\item A \index{parameterized protocol type!constraint type}parameterized protocol type, like \texttt{Sequence<String>}.
|
|
\item A \index{protocol composition type!constraint type}protocol composition type, like \texttt{Sequence \& MyClass}.
|
|
\item A \index{class type!constraint type}class type, like \texttt{NSObject}.
|
|
\item The \Index{AnyObject@\texttt{AnyObject}}\texttt{AnyObject} \index{layout constraint}\emph{layout constraint}, which restricts the possible concrete types to those represented as a single \index{reference count}reference-counted pointer.
|
|
\end{enumerate}
|
|
In the first three cases, the stated requirement becomes a conformance requirement. Otherwise, it is a superclass or layout requirement. In all cases, the subject type of the requirement is the \index{declared interface type!generic parameter declaration}declared interface type of the generic parameter.
|
|
|
|
\begin{example}
|
|
Notice how the generic parameter \texttt{B} of \texttt{open()} states the constraint type \texttt{Box<C>}, and this refers to the second generic parameter, \tC:
|
|
\begin{Verbatim}
|
|
func open<B: Box<C>, C>(box: B) -> C {
|
|
return box.contents!
|
|
}
|
|
|
|
class Box<Contents> {
|
|
var contents: Contents? = nil
|
|
}
|
|
\end{Verbatim}
|
|
This illustrates a property of the \index{scope tree}scope tree: generic parameters are visible in the entire \index{source range}source range of a generic declaration, including in the \index{generic parameter list}generic parameter list itself.
|
|
The declaration of \texttt{open()} thus states the superclass requirement $\ConfReq{\rT}{Box<\rU>}$. Here is a possible usage of \texttt{open()}, which we leave unexplained:
|
|
\begin{Verbatim}
|
|
struct Vegetable {}
|
|
class FarmBox: Box<Vegetable> {}
|
|
let vegetable: Vegetable = open(box: FarmBox())
|
|
\end{Verbatim}
|
|
\end{example}
|
|
|
|
\paragraph{Opaque parameters.}
|
|
An opaque parameter declaration is written with a \index{constraint type!opaque parameter}constraint type after the \texttt{some} keyword (\SecRef{generic params}). This specifies a conformance, superclass or layout requirement on the \index{opaque parameter}generic parameter type introduced by the opaque parameter declaration. For example, the following two declarations are equivalent:
|
|
\begin{Verbatim}
|
|
func pickElement<E>(_ elts: some Sequence<E>) -> E {...}
|
|
func pickElement<E, S: Sequence<E>>(_ elts: S) -> E {...}
|
|
\end{Verbatim}
|
|
|
|
We will see later that constraint types also appear in various other positions, and in all cases, they state a requirement with some distinguished subject type:
|
|
\begin{enumerate}
|
|
\item In the inheritance clause of a protocol or associated type (\SecRef{protocols}).
|
|
\item Following the \texttt{some} keyword in return position, where it declares an opaque result type (\ChapRef{chap:opaque result types}).
|
|
\item Following the \texttt{any} keyword for introducing an existential type (\ChapRef{chap:existential types}), with the exception that the constraint type cannot be a class by itself (for example, we allow ``\verb|any MyClass & Equatable|'', but ``\verb|any MyClass|'' is just ``\texttt{MyClass}'').
|
|
\end{enumerate}
|
|
|
|
\paragraph{Trailing where clauses.} Requirements can also be stated in a \IndexDefinition{where clause@\texttt{where} clause}\index{trailing where clause@trailing \texttt{where} clause|see{\texttt{where} clause}}\texttt{where} clause attached to the generic declaration. This allows for more generality than can be expressed in the inheritance clause of a generic parameter alone.
|
|
|
|
A \texttt{where} clause entry defines a requirement whose subject type is written explicitly, so that \index{dependent member type!in requirements}dependent member types can be subject to requirements; here, we state two requirements, $\rTSequence$ and $\ConfReq{\rT.Element}{Comparable}$:
|
|
\begin{Verbatim}
|
|
func isSorted<S>(_: S) where S: Sequence, S.Element: Comparable {...}
|
|
\end{Verbatim}
|
|
|
|
A \texttt{where} clause can also state a same-type requirement. In the next example, we state two conformance requirements using the inheritance clause syntax, another conformance requirement, and the same-type requirement $\SameReq{\rT.Element}{\rU.Element}$:
|
|
\begin{Verbatim}
|
|
func merge<S1: Sequence, S2: Sequence>(_: S1, _: S2) -> [S1.Element]
|
|
where S1.Element: Comparable, S1.Element == S2.Element {...}
|
|
\end{Verbatim}
|
|
|
|
Note that there is no way to refer to an opaque parameter type within the function's \Index{where clause@\texttt{where} clause!opaque parameter}\texttt{where} clause, but every declaration using opaque parameters can always be rewritten into an equivalent one using named generic parameters, so no generality is lost.
|
|
|
|
We saw in \ChapRef{chap:types} that when the parser reads a type annotation in the source, it constructs a \index{type representation}type representation, a lower-level syntactic object which must be \index{type resolution}resolved to obtain a \index{type}type. Similarly, requirements have a syntactic form, called a \IndexDefinition{requirement representation}\emph{requirement representation}. The parser constructs requirement representations while reading a \texttt{where} clause. The relationship between the syntactic and semantic entities is shown in this diagram:
|
|
\begin{center}
|
|
\begin{tikzpicture}[node distance=1cm]
|
|
\node (ReqRepr) [data] {Requirement representation};
|
|
\node (TypeRepr) [data, below=of ReqRepr] {Type representation};
|
|
\node (Req) [data, right=2cm of ReqRepr] {Requirement};
|
|
\node (Type) [data, below=of Req] {Type};
|
|
|
|
\draw [arrow] (ReqRepr) -- (TypeRepr);
|
|
\draw [arrow] (Req) -- (Type);
|
|
|
|
\path [arrow] (ReqRepr) edge [left] node {\footnotesize{contains}} (TypeRepr);
|
|
\path [arrow] (Req) edge [right] node {\footnotesize{contains}} (Type);
|
|
|
|
\path [arrow] (ReqRepr) edge [above] node {\footnotesize{resolves to}} (Req);
|
|
\path [arrow] (TypeRepr) edge [below] node {\footnotesize{resolves to}} (Type);
|
|
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
There are only two kinds of requirement representations, because the ``\texttt{:}'' form cannot distinguish conformance, superclass and layout requirements until we resolve the type representation on the right-hand side:
|
|
\begin{enumerate}
|
|
\item A \IndexDefinition{constraint requirement representation}\textbf{constraint requirement representation} ``\texttt{T:\ C}'', where \tT\ and \tC\ are type representations.
|
|
\item A \IndexDefinition{same-type requirement representation}\textbf{same-type requirement representation} ``\texttt{T == U}'', where \tT\ and \tU\ are type representations.
|
|
\end{enumerate}
|
|
|
|
Recall that a conformance requirement has a protocol type, protocol composition type, or parameterized protocol type on the right-hand side (\SecRef{fundamental types}). In the case of a protocol composition, we decompose the requirement into simpler requirements, one for each member of the composition. For example, if \texttt{MyClass} is a class, the requirement $\ConfReq{\rT}{Sequence~\&~MyClass}$ splits up as $\rTSequence$ and $\ConfReq{\rT}{MyClass}$, the latter being a superclass requirement. The empty protocol composition, written \Index{Any@\texttt{Any}}\texttt{Any}, is a trivial case; stating a conformance requirement to \texttt{Any} does nothing in a \texttt{where} clause, but it is allowed. Parameterized protocol types also decompose, as we'll see in~\SecRef{protocols}.
|
|
|
|
In the next section, we will introduce a formal system for derived requirements, where we will assume that only conformance requirements to protocol types remain, and that the subject type of a requirement is always a type parameter, and not an arbitrary type. \SecRef{requirement desugaring} will show how we eliminate these unnecessary forms of generality.
|
|
|
|
\paragraph{Contextually-generic declarations.} A generic declaration nested inside of another generic declaration can state a \texttt{where} clause, without introducing new generic parameters of its own. This is called a \IndexDefinition{contextually-generic declaration}\emph{contextually-generic declaration}:
|
|
\begin{Verbatim}
|
|
enum LinkedList<Element> {...}
|
|
|
|
extension LinkedList {
|
|
func sum() -> Element where Element: AdditiveArithmetic {...}
|
|
}
|
|
\end{Verbatim}
|
|
There is no semantic distinction between attaching a \texttt{where} clause to a member of a type, or moving the member to a \index{constrained extension}constrained extension (\SecRef{constrained extensions}), so the above is equivalent to the following:
|
|
\begin{Verbatim}
|
|
extension LinkedList where Element: AdditiveArithmetic {
|
|
func sum() -> Element {...}
|
|
}
|
|
\end{Verbatim}
|
|
\index{mangling}
|
|
However, for historical reasons, these two declarations have distinct \index{mangling!contextually-generic declaration}mangled symbol names, so the above is not an \index{ABI}ABI-compatible transformation.
|
|
|
|
\medskip
|
|
|
|
In \ChapRef{chap:generic signatures}, we will see that the generic signature of a declaration records all of its requirements, regardless of they were stated in source.
|
|
|
|
\paragraph{History.} The syntax described in this section has evolved over time:
|
|
\begin{itemize}
|
|
\item The \texttt{where} clause used to be written within the ``\texttt{<}'' and ``\texttt{>}'', but was moved to the current \Index{where clause@\texttt{where} clause!history}``trailing'' position in \IndexSwift{3.0}Swift 3 \cite{se0081}.
|
|
\item Generic type aliases were introduced in \IndexSwift{3.0}Swift 3 \cite{se0048}.
|
|
\item \index{protocol composition type!history}Protocol compositions involving class types were introduced in \IndexSwift{4.0}Swift 4 \cite{se0156}.
|
|
\item Generic \index{subscript declaration!history}subscripts were introduced in \IndexSwift{4.0}Swift 4 \cite{se0148}.
|
|
\item Implementation limitations prevented the \texttt{where} clause from stating requirements that constrain outer generic parameters until Swift 3, and \index{contextually-generic declaration!history}contextually-generic declarations were not allowed until \IndexSwift{5.3}Swift 5.3 \cite{se0261}.
|
|
\item \index{opaque parameter!history}Opaque parameter declarations were introduced in \IndexSwift{5.7}Swift 5.7 \cite{se0341}.
|
|
\end{itemize}
|
|
|
|
\section{Protocols}\label{protocols}
|
|
|
|
The \texttt{protocol} keyword introduces a \IndexDefinition{protocol declaration}\emph{protocol declaration}, which is a special kind of nominal type declaration. The members of a protocol, with the exception of type aliases, are requirements that must be witnessed by corresponding members in a \index{conforming type}conforming type. The protocol's properties, subscripts, and methods are called \IndexDefinition{value requirement}\emph{value requirements}. They don't have bodies, but are otherwise like member declarations of a concrete nominal type. The declared interface type of a protocol declaration is a \index{protocol type}protocol type.
|
|
|
|
Every protocol has an implicit generic parameter list with a single generic parameter named \IndexDefinition{protocol Self type@protocol \tSelf\ type}\tSelf, which abstracts over the conforming type. The declared interface type of \tSelf\ is always~\rT; protocols cannot be nested in other generic contexts (\SecRef{nested nominal types}), nor can they declare any other generic parameters.
|
|
|
|
The \texttt{associatedtype} keyword introduces an \IndexDefinition{associated type declaration}\emph{associated type declaration}, which can only appear inside of a protocol. The declared interface type is a \index{dependent member type!associated type declaration}dependent member type (\SecRef{fundamental types}). Specifically, the \index{declared interface type!associated type declaration}declared interface type of an associated type~\texttt{A} in a protocol~\texttt{P} is the \index{bound dependent member type!associated type declaration}bound dependent member type denoted \texttt{Self.[P]A}, formed from the base type of~\tSelf\ together with~\texttt{A}. A nominal type conforming to a protocol with associated types must declare a type witness for each associated type (\SecRef{type witnesses}).
|
|
|
|
Protocols can also impose \IndexDefinition{associated requirement}\emph{associated requirements} on \tSelf\ and its dependent member types. A conforming type and its type witnesses must together satisfy these associated requirements. There are a number of ways to state associated requirements in the language, so we will review them now.
|
|
|
|
\paragraph{Protocol inheritance clauses.}
|
|
A protocol can have an \index{inheritance clause!protocol declaration}inheritance clause with a list of one or more comma-separated \index{constraint type!protocol inheritance clause}constraint types. Each inheritance clause entry states an associated requirement with a subject type of \tSelf. These are additional requirements the conforming type itself must satisfy in order to conform.
|
|
|
|
An associated conformance requirement with a subject type of \tSelf\ establishes a \index{protocol inheritance|see{inherited protocol}}\IndexDefinition{inherited protocol}\emph{protocol inheritance} relationship. The protocol stating the requirement is the \emph{derived protocol}, and the protocol on the right-hand side is the \emph{base protocol}. The derived protocol is said to \emph{inherit} from (or sometimes, \emph{refine}) the base protocol. A \index{qualified lookup!protocol inheritance}qualified lookup into a protocol (or concrete nominal types) will traverse all of its base protocols (or all base protocols of each conformed protocol of the concrete nominal type).
|
|
|
|
For example, the standard library's \texttt{Collection} protocol inherits from \texttt{Sequence} by stating the associated requirement $\ConfReq{Self}{Sequence}$:
|
|
\begin{Verbatim}
|
|
protocol Collection: Sequence {...}
|
|
\end{Verbatim}
|
|
|
|
Protocols can restrict their conforming types to those with a \index{reference count}reference-counted pointer representation by stating an \texttt{AnyObject} layout constraint in the inheritance clause:
|
|
\begin{Verbatim}
|
|
protocol BoxProtocol: AnyObject {...}
|
|
\end{Verbatim}
|
|
|
|
Protocols can also limit their conforming types to subclasses of some superclass:
|
|
\begin{Verbatim}
|
|
class Plant {}
|
|
class Animal {}
|
|
protocol Duck: Animal {}
|
|
class MockDuck: Plant, Duck {} // error: not a subclass of Animal
|
|
\end{Verbatim}
|
|
A protocol is \IndexDefinition{class-constrained protocol}\emph{class-constrained} if the $\ConfReq{Self}{AnyObject}$ \index{associated layout requirement}associated layout requirement is either explicitly stated, or a consequence of some other associated requirement. We'll say more about the semantics of protocol inheritance clauses and name lookup in \SecRef{requirement sig}, \SecRef{identtyperepr}, and \ChapRef{chap:building generic signatures}.
|
|
|
|
\paragraph{Primary associated types.}
|
|
A protocol can declare a list of \IndexDefinition{primary associated type}\emph{primary associated types} with a syntax resembling that of a generic parameter list:
|
|
\begin{Verbatim}
|
|
protocol IteratorProtocol<Element> {
|
|
associatedtype Element
|
|
mutating func next() -> Element?
|
|
}
|
|
\end{Verbatim}
|
|
While generic parameter lists introduce new generic parameter declarations, the entries in the primary associated type list reference \emph{existing} associated type declarations, either in the protocol itself, or some base protocol.
|
|
|
|
A \index{parameterized protocol type}\emph{parameterized protocol type} can be formed from a reference to a protocol with primary associated types, by taking a list of generic argument types, one for each primary associated type. On the right-hand side of a conformance requirement, a parameterized protocol type decomposes into a conformance requirement to the protocol, followed by a series of same-type requirements. The following are equivalent:
|
|
\begin{Verbatim}
|
|
func sumOfSquares<I>(_: I) -> Int
|
|
where I: IteratorProtocol<Int> {...}
|
|
func sumOfSquares<I>(_: I) -> Int
|
|
where I: IteratorProtocol, I.Element == Int {...}
|
|
\end{Verbatim}
|
|
More details appear in \SecRef{requirement desugaring}. Parameterized protocol types and primary associated types were added to the language in \IndexSwift{5.7}Swift~5.7~\cite{se0346}.
|
|
|
|
\paragraph{Associated requirements.}
|
|
An associated type declaration can have an \IndexDefinition{inheritance clause!associated type declaration}inheritance clause, consisting of one or more comma-separated constraint types. Each entry defines a requirement on the declared interface type of the associated type declaration, so we get $\ConfReq{Self.Data}{Codable}$ and $\ConfReq{Self.Data}{Hashable}$ below:
|
|
\begin{Verbatim}
|
|
associatedtype Data: Codable, Hashable
|
|
\end{Verbatim}
|
|
|
|
An associated type declaration can also have a trailing \Index{where clause@\texttt{where} clause!associated type declaration}\texttt{where} clause, which allows associated requirements to be stated in full generality. The standard library \texttt{Sequence} protocol showcases both primary associated types, and associated requirements:
|
|
\begin{Verbatim}
|
|
protocol Sequence<Element> {
|
|
associatedtype Iterator: IteratorProtocol
|
|
associatedtype Element where Element == Iterator.Element
|
|
|
|
func makeIterator() -> Iterator
|
|
}
|
|
\end{Verbatim}
|
|
The associated conformance requirement on \texttt{Self.Iterator} could have been stated using a \texttt{where} clause instead:
|
|
\begin{Verbatim}
|
|
associatedtype Iterator where Iterator: IteratorProtocol
|
|
\end{Verbatim}
|
|
A \Index{where clause@\texttt{where} clause!protocol declaration}\texttt{where} clause can also be attached to the protocol itself; there is no semantic difference between that and attaching it to an associated type declaration:
|
|
\begin{Verbatim}
|
|
protocol Sequence where Iterator: IteratorProtocol,
|
|
Element == Iterator.Element {...}
|
|
\end{Verbatim}
|
|
Finally, we can explicitly qualify the member types with \tSelf:
|
|
\begin{Verbatim}
|
|
protocol Sequence where Self.Iterator: IteratorProtocol,
|
|
Self.Element == Self.Iterator.Element {...}
|
|
\end{Verbatim}
|
|
In all cases, we state the same two associated requirements. Our notation is to subscript the associated requirement with the name of the protocol stating this requirement:
|
|
\begin{gather*}
|
|
\ConfReq{Self.Iterator}{IteratorProtocol}_\texttt{Sequence}\\
|
|
\SameReq{Self.Element}{Self.Iterator.Element}_\texttt{Sequence}
|
|
\end{gather*}
|
|
|
|
Let's summarize all the ways of stating associated requirements in a protocol~\texttt{P}:
|
|
\begin{itemize}
|
|
\item The protocol can state an inheritance clause. Each entry defines a conformance, superclass, or layout requirement with a subject type of \tSelf.
|
|
\item An associated type declaration \texttt{A} can state an inheritance clause. Each entry defines a conformance, superclass or layout requirement with a subject type of \texttt{Self.[P]A}.
|
|
\item Arbitrary associated requirements can be stated in \Index{where clause@\texttt{where} clause!protocol declaration}trailing \texttt{where} clauses, attached to the protocol or any of its associated types, in any combination.
|
|
\end{itemize}
|
|
|
|
A protocol's associated requirements are collected in its requirement signature, which we will see is dual to a generic signature in some sense (\SecRef{requirement sig}). How concrete types satisfy the requirement signature will be discussed in \ChapRef{chap:conformances}.
|
|
|
|
\paragraph{Self requirements.}
|
|
The \Index{where clause@\texttt{where} clause!protocol declaration}\texttt{where} clause of a protocol method or subscript requirement cannot constrain \tSelf\ or its associated types. For example, the following protocol is rejected, because there would be no way to implement the \texttt{minElement()} requirement in a concrete conforming type whose \texttt{Element} type is \emph{not} \texttt{Comparable}:
|
|
\begin{Verbatim}
|
|
protocol SetProtocol {
|
|
associatedtype Element // we want `where Element: Comparable' here
|
|
func minElement() -> Element where Element: Comparable // error
|
|
}
|
|
\end{Verbatim}
|
|
|
|
\paragraph{History.}
|
|
Older releases of Swift permitted protocols and associated types to state constraint types in their inheritance clauses, but more general associated requirements did not exist. Associated requirements were introduced when the trailing \texttt{where} clause syntax was generalized to associated types and protocols in \IndexSwift{4.0}Swift~4~\cite{se0142}.
|
|
|
|
\section{Functions}\label{function decls}
|
|
|
|
A \IndexDefinition{function declaration}function declaration can appear at the top level of a source file, as a member of a nominal type or extension (which we recall is called a method declaration), or as a \IndexDefinition{local function declaration}\emph{local function} nested inside of another function. In this section, we will describe the computation of the interface type of a function declaration, and then conclude with a discussion how closure expressions and local functions capture values.
|
|
|
|
The \IndexDefinition{interface type request}\Request{interface type request} computes the interface type of a function declaration. This is a \index{function type}function type or \index{generic function type}generic function type, constructed from the interface types of the function's parameter declarations, together with its return type, and generic signature, if any. If no return type is given, it becomes the empty tuple type~\texttt{()}:
|
|
\begin{Verbatim}
|
|
func f(x: Int, y: String) -> Bool {...}
|
|
// Interface type: (Int, String) -> Bool
|
|
|
|
func g() {...}
|
|
// Interface type: () -> ()
|
|
\end{Verbatim}
|
|
|
|
\paragraph{Method declarations.}
|
|
In addition to the formal parameters declared in its parameter list, a \index{method declaration}method declaration also has an implicit \IndexDefinition{self parameter@\texttt{self} parameter}\texttt{self} parameter, to receive the value on the left-hand side of the ``\texttt{.}'' in the \index{member reference expression}member reference expression. The interface type of a method declaration is a function type which receives the \texttt{self} parameter, and returns another function which then takes the method's formal parameters. The ``\texttt{->}'' syntax for a function type associates to the right, so \verb|A -> B -> C| means \verb|A -> (B -> C)|:
|
|
\begin{Verbatim}
|
|
struct Universe {
|
|
func wormhole(x: Int, y: String) -> Bool {...}
|
|
// Interface type: (Universe) -> (Int, String) -> Bool
|
|
|
|
static func bigBang() {}
|
|
// Interface type: (Universe.Type) -> () -> ()
|
|
|
|
mutating func teleport() {}
|
|
// Interface type: (inout Universe) -> () -> ()
|
|
}
|
|
\end{Verbatim}
|
|
We derive the \index{interface type!self parameter}interface type and \index{ownership specifier!self parameter}ownership specifier for the \texttt{self} parameter as follows:
|
|
\begin{itemize}
|
|
\item We start with the \IndexDefinition{self interface type}\emph{self interface type} of the method's parent declaration context. In a struct, enum, or class, this is the same as the \index{declared interface type!self interface type}declared interface type. In a protocol, this is the protocol \IndexSelf\tSelf\ type (\SecRef{protocols}). In an extension, the self interface type is that of the extended type.
|
|
|
|
\item If the method is declared inside a class, and if it returns the \Index{dynamic Self type@dynamic \tSelf\ type}dynamic \tSelf\ type, we wrap the \texttt{self} type in the dynamic \tSelf\ type (\SecRef{sec:special types}).
|
|
|
|
\item If the method is \IndexDefinition{static method declaration}\texttt{static}, we further wrap the \texttt{self} type in a \index{metatype type}metatype.
|
|
|
|
\item If the method is \texttt{mutating}, we pass the \texttt{self} parameter \texttt{inout}.
|
|
|
|
\end{itemize}
|
|
|
|
\begin{figure}[b!]\captionabove{The method call \texttt{universe.wormhole(x:~1, y:~"hi")}}\label{method call expr}
|
|
\begin{center}
|
|
\begin{tikzpicture}[%
|
|
grow via three points={one child at (0.5,-0.7) and
|
|
two children at (0.5,-0.7) and (0.5,-1.4)},
|
|
edge from parent path={[->] (\tikzparentnode.south) |- (\tikzchildnode.west)}]
|
|
\node [class] {\vphantom{p}call: \texttt{universe.wormhole(x:~1, y:~"hi")}}
|
|
child { node [class] {\vphantom{p}self call: \texttt{universe.wormhole}}
|
|
child { node [class] {\vphantom{p}callee: \texttt{Universe.wormhole}}}
|
|
child { node [class] {\vphantom{p}declaration reference: \texttt{universe}}}}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child { node [class] {\vphantom{p}argument list: \texttt{(x:~1, y:~"hi")}}
|
|
child { node [class] {\vphantom{p}literal: \texttt{1}}}
|
|
child { node [class] {\vphantom{p}literal: \texttt{"hi"}}}}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
\end{figure}
|
|
|
|
Let's compare the interface type of a method declaration with the structure of a method call expression in the abstract syntax tree, shown in \FigRef{method call expr}:
|
|
\begin{itemize}
|
|
\item The outer expression's callee, \texttt{universe.wormhole}, is itself a call expression, so we must evaluate this inner call expression first.
|
|
|
|
The inner call expression applies the argument \texttt{universe} to the \texttt{self} parameter of \texttt{Universe.wormhole()}. This represents the method lookup. The return type of the inner call expression is \verb|(Int, String) -> Bool|.
|
|
\item The outer call expression applies the argument list \verb|(x: 1, y: "hi")| to the result of the inner call expression. This represents the method call itself, with a return type of \texttt{Bool}.
|
|
\end{itemize}
|
|
The extra function call disappears in \index{SILGen}SILGen, where we lower a method to a SIL function that receives all formal parameters and the \texttt{self} parameter at once.
|
|
|
|
A partially-applied method reference, such as \texttt{universe.wormhole}, can also be used as a value of type \verb|(Int, String) -> Bool|. This binds the \texttt{self} parameter but does not call the method. We handle this by wrapping the method reference in a closure that invokes the method; this closure has the same type as the method reference. In the \index{lambda calculus}lambda calculus formalism, this is what's known as \index{eta expansion}\emph{$\eta$-expansion}:
|
|
\begin{Verbatim}
|
|
{ x, y in universe.wormhole(x: x, y: y) }
|
|
\end{Verbatim}
|
|
The \index{unapplied function reference}unapplied form, \texttt{Universe.wormhole}, desugars into a closure returning a closure:
|
|
\begin{Verbatim}
|
|
{ mySelf in { x y in mySelf.wormhole(x: x, y: y) } }
|
|
\end{Verbatim}
|
|
This desugaring simplifies SILGen, because we only need to implement a lowering for fully-applied method calls that pass all formal parameters and \texttt{self} at once.
|
|
|
|
\begin{wrapfigure}[25]{l}{17.2em}
|
|
\begin{minipage}{17em}
|
|
\begin{Verbatim}
|
|
struct Example {
|
|
func instanceMethod() {}
|
|
static func staticMethod() {}
|
|
|
|
struct Lookup {
|
|
func innerMethod() {}
|
|
|
|
func test() {
|
|
instanceMethod() // bad
|
|
staticMethod() // ok
|
|
innerMethod() // ok
|
|
}
|
|
}
|
|
|
|
func anotherMethod(x: Int) {
|
|
struct Local {
|
|
func test() {
|
|
print(x) // bad
|
|
}
|
|
}
|
|
}
|
|
}
|
|
\end{Verbatim}
|
|
\end{minipage}
|
|
\end{wrapfigure}
|
|
|
|
All function declarations must be followed by a body in the source language, except for protocol requirements. A function body can contain \index{statement}statements, \index{expression}expressions, and other declarations. (Unlike types and declarations, we will not exhaustively survey all statements and expressions in this book.) The example on the left shows some \index{call expression}call expressions.
|
|
|
|
In a method body, an unqualified reference to a member of the innermost nominal type declaration is interpreted as having an implicit ``\texttt{self.}'' qualification. Thus, instance methods can refer to other instance methods this way, and static methods can refer to other static methods.
|
|
|
|
An unqualified reference to a member of an outer nominal type can only be made if the member is static, because there is no ``outer \Index{self parameter@\texttt{self} parameter!nested nominal type}\texttt{self} parameter'' to invoke the method with; a \emph{value} of the nested type does not contain a \emph{value} of its parent type.
|
|
|
|
For the same reason, methods inside \index{local type declaration}local types cannot refer to local variables declared outside of the local type. (Contrast this with \index{Java}Java inner classes for example, which can be declared as \texttt{static} or instance members of their outer class; a non-\texttt{static} inner class \index{captured value!Java inner class}captures a ``\texttt{this}'' reference from the outer class. Inner classes nested in methods can also capture local variables in Java.)
|
|
|
|
\paragraph{Constructor declarations.}
|
|
\IndexDefinition{constructor declaration}Constructor declarations are introduced with the \texttt{init} keyword. The parent context of a constructor must be a nominal type or extension.
|
|
|
|
From the outside, the interface type of a constructor looks like a static method that returns a new instance of the type, but inside the constructor, \Index{self parameter@\texttt{self} parameter!constructor declaration}\texttt{self} is the instance being initialized, so the interface type of \texttt{self} is the nominal type, and not its metatype. In a struct or enum, \texttt{self} is also \texttt{inout}. Constructors can delegate to other constructors in various ways. To model the delegation with a call expression, the \IndexDefinition{initializer interface type}\emph{initializer interface type} describes the type of an in-place initialization at a location provided by the caller:
|
|
\begin{Verbatim}
|
|
struct Universe {
|
|
init(age: Int) {...}
|
|
// Interface type: (Universe.Type) -> (Int) -> Universe
|
|
// Initializer interface type: (inout Universe) -> (Int) -> Universe
|
|
}
|
|
\end{Verbatim}
|
|
|
|
\paragraph{Destructor declarations.}
|
|
\IndexDefinition{destructor declaration}Introduced with the \texttt{deinit} keyword, which is only valid inside a class. A destructor cannot have a generic parameter list or \texttt{where} clause. Its interface type is that of a method with no formal parameters and a return type of \texttt{()}.
|
|
|
|
\paragraph{Local contexts.}
|
|
A \IndexDefinition{local context}\emph{local context} is any declaration context that is not a module, source file, type declaration or extension. Swift allows variable, function, and type declarations to appear in local context. The following are local contexts:
|
|
\begin{itemize}
|
|
\item \index{top-level code declaration}Top-level code declarations.
|
|
\item Function declarations.
|
|
\item \index{closure expression}Closure expressions.
|
|
\item If a variable is not itself in local context (for example, it's a member of a nominal type declaration), then its \index{initial value expression}initial value expression defines a new local context.
|
|
\item \index{subscript declaration}Subscript and \index{enum element declaration}enum element declarations are local contexts, because they can contain parameter declarations (and also a generic parameter list, in the case of a subscript declaration).
|
|
\end{itemize}
|
|
|
|
Local functions and closures can \IndexDefinition{captured value}\emph{capture} references to other local declarations from outer scopes. We use the standard technique of \IndexDefinition{closure conversion}\emph{closure conversion} to lower functions with captured values into ones without. We can understand this process as introducing an additional parameter for each captured value, followed by a walk of the function body to replace references to those captured values with references to the corresponding parameters. In Swift, this is part of \index{SILGen}SILGen's lowering process, and not a separate transformation on the abstract syntax tree.
|
|
|
|
The \IndexDefinition{capture info request}\Request{capture info request} computes the list of values captured by the given function and all of its nested local functions and closure expressions.
|
|
|
|
\begin{wrapfigure}[10]{l}{10.6em}
|
|
\begin{minipage}{10.5em}
|
|
\begin{Verbatim}
|
|
func f() {
|
|
let x = 0, y = 0
|
|
|
|
func g() {
|
|
var z = 0
|
|
print(x)
|
|
|
|
func h() {
|
|
print(y, z)
|
|
}
|
|
}
|
|
}
|
|
\end{Verbatim}
|
|
\end{minipage}
|
|
\end{wrapfigure}
|
|
|
|
Consider the three nested functions shown on the left. We proceed to compute their captures from the inside out.
|
|
|
|
The innermost function~\texttt{h()} captures \texttt{y}~and~\texttt{z}. The middle function~\texttt{g()} captures~\texttt{x}. It also captures~\texttt{y}, because~\texttt{h()} captures~\texttt{y}, but it does not capture~\texttt{z}, because~\texttt{z} is declared by~\texttt{g()} itself. Finally,~\texttt{f()} is declared at the top level, so it doesn't have any captures.
|
|
|
|
We can summarize this as follows (see \AppendixRef{math summary} for a summary of set notation):
|
|
% FIXME
|
|
\begin{quote}
|
|
\qquad\qquad
|
|
\begin{tabular}{lll}
|
|
\toprule
|
|
\textbf{Function}&\textbf{Captures}\\
|
|
\midrule
|
|
\texttt{f()}&$\varnothing$\\
|
|
\texttt{g()}&$\{\texttt{x},\,\texttt{y}\}$\\
|
|
\texttt{h()}&$\{\texttt{y},\,\texttt{z}\}$\\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{quote}
|
|
|
|
\bigskip
|
|
|
|
\begin{algorithm}[Compute closure captures]\label{closure captures algorithm}
|
|
As input, takes the type-checked body of a \index{closure function}closure expression or \index{local function declaration}local function~$F$. Outputs the \index{set!captures}set of captures of~$F$.
|
|
\begin{enumerate}
|
|
\item Initialize the return value with an \index{empty set}empty set, $C\leftarrow\varnothing$.
|
|
\item Recursively walk the type-checked body of $F$ and handle each element:
|
|
\item (Declaration references) If $F$ contains an expression that references some local variable or local function~$d$ by name, let $\texttt{PARENT}(d)$ denote the declaration context containing~$d$. This is either $F$ itself, or an outer local context, because we found~$d$ by unqualified lookup from~$F$.
|
|
|
|
If $\texttt{PARENT}(d)\neq F$, set $C\leftarrow C\cup\{d\}$.
|
|
\item (Nested closures) If $F$ contains a nested closure expression or local function $F^\prime\!$, then any captures of $F^\prime$ not declared by~$F$ are also captures of~$F$.
|
|
|
|
Recursively compute the captures of $F^\prime\!$. For each $d$ captured by $F^\prime$ such that $\texttt{PARENT}(d)\neq F$, set $C\leftarrow C\cup\{d\}$.
|
|
|
|
\item (Local types) If $F$ contains a local type, do not walk into the children of the local type. Local types do not capture values; we enforce this in the next step.
|
|
|
|
\item (Diagnose) After the recursive walk, consider each element $d\in C$. If the path of parent declaration contexts from $F$ to $d$ contains a nominal type declaration, we have an unsupported capture inside a local type. Diagnose an error.
|
|
|
|
\item Return $C$.
|
|
\end{enumerate}
|
|
\end{algorithm}
|
|
|
|
\begin{wrapfigure}[9]{r}{16.5em}
|
|
\begin{minipage}{16.5em}
|
|
\begin{Verbatim}
|
|
func f() {
|
|
let x = 0, y = 0, z = 0
|
|
|
|
func g() { print(x); h() }
|
|
func h() { print(y); g() }
|
|
func i() { print(z); h() }
|
|
}
|
|
\end{Verbatim}
|
|
\end{minipage}
|
|
\end{wrapfigure}
|
|
|
|
Local functions can also reference each other recursively. Consider the functions shown on the right and notice how \texttt{f()} and \texttt{g()} are mutually recursive. At runtime, we cannot represent this by forming two closure contexts \index{reference counting}where each one retains the other, because then neither context will ever be released.
|
|
|
|
We use a second algorithm to obtain the list of \IndexDefinition{lowered captures}\emph{lowered captures}, by replacing any captured local functions with their corresponding capture lists, repeating this until fixed point. The final list contains variable declarations only. With our example, the captures and lowered captures of each function are as follows:
|
|
|
|
\begin{center}
|
|
\begin{tabular}{lll}
|
|
\toprule
|
|
\textbf{Function}&\textbf{Captures}&\textbf{Lowered}\\
|
|
\midrule
|
|
\texttt{f()}&$\varnothing$&$\varnothing$\\
|
|
\texttt{g()}&$\{\texttt{x},\,\texttt{h()}\}$&$\{\texttt{x},\,\texttt{y}\}$\\
|
|
\texttt{h()}&$\{\texttt{y},\,\texttt{g()}\}$&$\{\texttt{x},\,\texttt{y}\}$\\
|
|
\texttt{i()}&$\{\texttt{z},\,\texttt{h()}\}$&$\{\texttt{x},\,\texttt{y},\,\texttt{z}\}$\\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
(As a special case, if a set of local functions reference each other but capture no other state from the outer declaration context, their lowered captures will be empty, so no runtime context allocation is necessary.)
|
|
|
|
\begin{algorithm}[Compute lowered closure captures]\label{lowered closure captures algorithm}
|
|
As input takes the type-checked body of a \index{closure function}closure expression or \index{local function declaration}local function~$F$. Outputs the \index{set!captures}set of variable declarations \index{transitive closure}transitively captured by~$F$.
|
|
\begin{enumerate}
|
|
\item Initialize the set $C\leftarrow\varnothing$; this will be the return value. Initialize an empty worklist. Initialize an empty visited set. Add $F$ to the worklist.
|
|
\item If the worklist is empty, return $C$. Otherwise, remove the next function $F$ from the worklist.
|
|
\item If $F$ is in the visited set, go back to Step~2. Otherwise, add $F$ to the visited set.
|
|
\item Compute the captures of~$F$ using \AlgRef{closure captures algorithm} and consider each capture~$d$. If~$d$ is a local variable declaration, set $C\leftarrow C\cup\{d\}$. If~$d$ is a local function declaration, add~$d$ to the worklist.
|
|
\item Go back to Step~2.
|
|
\end{enumerate}
|
|
\end{algorithm}
|
|
|
|
This completely explains captures of \texttt{let} variables, but mutable \texttt{var} variables and \texttt{inout} parameters merit further explanation.
|
|
|
|
A \index{non-escaping function type}\emph{non-escaping} closure can capture a \texttt{var} or \texttt{inout} by simply capturing the memory address of the storage location. This is safe, because a non-escaping closure cannot outlive the dynamic extent of the storage location.
|
|
|
|
An \index{escaping function type}\texttt{@escaping} closure can also capture a \texttt{var}, which requires promoting the \texttt{var} to a \index{boxing}heap-allocated box with a \index{reference counting}reference count, with all variable accesses indirecting through the box. The below example can be found in every \index{Lisp}Lisp textbook. Each invocation of \texttt{counter()} allocates a new counter value on the heap, and returns three closures that reference the box; the box itself is completely hidden by the abstraction:
|
|
\begin{Verbatim}
|
|
func counter() -> (read: () -> Int, inc: () -> (), dec: () -> ()) {
|
|
var count = 0 // promoted to a box
|
|
return ({ count }, { count += 1 }, { count -= 1 })
|
|
}
|
|
\end{Verbatim}
|
|
|
|
Before \IndexSwift{3.0}Swift~3, \texttt{@escaping} closures were permitted to capture \texttt{inout} parameters as well. To make this safe, the contents of the \texttt{inout} parameter were first copied into a heap-allocated box, which was captured by the closure. The contents of this box were then copied back before the function returned to its caller. This was essentially equivalent to doing the following transform, where we introduce \verb|_n|:
|
|
\begin{Verbatim}
|
|
func changeValue(_ n: inout Int) {
|
|
var _n = n // copy the value
|
|
|
|
let escapingFn = {
|
|
_n += 1 // capture the box
|
|
}
|
|
|
|
n = _n // write it back
|
|
}
|
|
\end{Verbatim}
|
|
In this scheme, if the closure outlived the dynamic extent of the \texttt{inout} parameter, any subsequent writes from within the closure were silently dropped. This was a source of user confusion, so Swift~3 banned \texttt{inout} captures from escaping closures instead~\cite{se0035}.
|
|
|
|
In SILGen, captured values introduce new parameters at the end of the function's parameter list, and a closure value is formed from a function with captures by \index{partial application}\emph{partially applying} the captured values. This produces a new function value with the required type, so the captured values are ``sliced off.'' In IRGen, we lower a partial application by allocating space for storing the captures (either on the stack for a \index{non-escaping function type}non-escaping function type, otherwise it's on the heap for \texttt{@escaping}), and then we emit a thunk, which takes a pointer to the \IndexDefinition{closure context}context as an argument, unpacks the captured values from the context, and passes them as individual arguments to the original function. This thunk together with the context forms a \index{thick function}\emph{thick function} value which represents the closure.
|
|
|
|
If nothing is captured (or if all captured values are zero bytes in size), we can pass a null pointer as the context, without performing a heap allocation. If there is exactly one captured value and this value can be represented as a \index{reference count}reference-counted pointer, we can also elide the allocation by passing the captured value as the context pointer instead. For example, if a closure's single capture is an instance of a \index{class type}class type, nothing is allocated. If the single capture is the \index{heap-allocated box}heap-allocated box that wraps a \texttt{var}, we must still allocate the box for the \texttt{var}, but we avoid a second context allocation.
|
|
|
|
\section{Storage}\label{other decls}
|
|
|
|
\IndexDefinition{storage declaration}
|
|
\index{l-value type}
|
|
Storage declarations represent locations that can be read and written.
|
|
|
|
\paragraph{Parameter declarations.} Functions, enum elements, and subscripts can have parameter lists; each parameter is represented by a \IndexDefinition{parameter declaration}parameter declaration. Parameter declarations are a kind of variable declaration.
|
|
|
|
\paragraph{Variable declarations.} \IndexDefinition{variable declaration}Variables that are not parameters are introduced with \texttt{var} and \texttt{let}. A variable is either \emph{stored} or \emph{computed}; the behavior of a computed variable is determined by its \index{accessor declaration}\emph{accessor declarations}. The \IndexDefinition{value interface type}\emph{value interface type} of a variable is the type of its value. The interface type of a variable is obtained by taking the type of its value, and possibly wrapping it in a \index{reference storage type}reference storage type if the variable is declared as \index{weak reference type}\texttt{weak} or \index{unowned reference type}\texttt{unowned}.
|
|
|
|
\IndexDefinition{pattern binding declaration}
|
|
\IndexDefinition{pattern binding entry}
|
|
\IndexDefinition{pattern}
|
|
\IndexDefinition{initial value expression}
|
|
Variable declarations are always created alongside a \emph{pattern binding declaration} which represents the various ways in which variables can be bound to values in Swift. A pattern binding declaration consists of one or more \emph{pattern binding entries}. Each pattern binding entry has a \emph{pattern} and an optional \emph{initial value expression}. A pattern declares zero or more variables.
|
|
|
|
Here is a pattern binding declaration with a single entry that does not declare any variables:
|
|
\begin{Verbatim}
|
|
let _ = ignored()
|
|
\end{Verbatim}
|
|
Here is a pattern binding declaration with a single entry, where the pattern declares a single variable:
|
|
\begin{Verbatim}
|
|
let x = 123
|
|
\end{Verbatim}
|
|
We can write a more complex pattern, for example binding the first element of a tuple while discarding the second element:
|
|
\begin{Verbatim}
|
|
let (x, _) = (123, "hello")
|
|
\end{Verbatim}
|
|
Here is a pattern binding declaration with a single entry, whose pattern declares two variables \texttt{x} and \texttt{y}:
|
|
\begin{Verbatim}
|
|
let (x, y) = (123, "hello")
|
|
\end{Verbatim}
|
|
Here is a pattern binding declaration with two entries, declaring \texttt{x} and \texttt{y} respectively:
|
|
\begin{Verbatim}
|
|
let x = 123, y = "hello"
|
|
\end{Verbatim}
|
|
And finally, here we have two pattern binding declarations, where each pattern binding declaration has a single entry declaring a single variable:
|
|
\begin{Verbatim}
|
|
let x = 123
|
|
let y = "hello"
|
|
\end{Verbatim}
|
|
When a pattern binding declaration appears outside of a local context, each entry must declare at least one variable, so we reject both of the following:
|
|
\begin{Verbatim}
|
|
let _ = 123
|
|
|
|
struct S {
|
|
let _ = "hello"
|
|
}
|
|
\end{Verbatim}
|
|
|
|
\index{typed pattern}
|
|
\index{tuple pattern}
|
|
A funny quirk of the pattern grammar is that typed patterns and tuple patterns do not compose in the way one might think. If ``\texttt{let x:~Int}'' is a typed pattern declaring a variable \texttt{x} type with annotation \texttt{Int}, and ``\texttt{let (x, y)}'' is a tuple pattern declaring two variables \texttt{x} and \texttt{y}, we might expect ``\texttt{let~(x:~Int,~y:~String)}'' to declare two variables \texttt{x} and \texttt{y} with type annotations \texttt{Int} and \texttt{String} respectively; what actually happens is we get a tuple pattern declaring two variables named \texttt{Int} and \texttt{String} that binds a two-element tuple with \emph{labels} \texttt{x} and \texttt{y}:
|
|
\begin{Verbatim}
|
|
let (x: Int, y: String) = (x: 123, y: "hello")
|
|
print(Int) // huh? prints 123
|
|
print(String) // weird! prints "hello"
|
|
\end{Verbatim}
|
|
|
|
\paragraph{Subscript declarations.} \IndexDefinition{subscript declaration}Subscripts are introduced with the \texttt{subscript} keyword. They can only appear as members of nominal types and extensions. The interface type of a subscript is a function type taking the index parameters and returning the storage type. The value interface type of a subscript is just the storage type. For historical reasons, the interface type of a subscript does not include the \tSelf\ clause, the way that method declarations do. Subscripts can either be instance or static members; static subscripts were introduced in \IndexSwift{5.1}Swift~5.1 \cite{se0254}.
|
|
|
|
\paragraph{Accessor declarations.}
|
|
Each storage declaration has a \IndexDefinition{accessor declaration}set of accessor declarations, which are a special kind of function declaration. The accessor declarations are siblings of the storage declaration in the declaration context hierarchy. The interface type of an accessor depends the accessor kind. For example, getters return the value, and setters take the new value as a parameter. Variable accessors do not take any other parameters; subscript accessors also take the subscript's index parameters. We will not need any more details about accessor and storage declarations in this book.
|
|
|
|
\section{Source Code Reference}\label{src:declarations}
|
|
|
|
Key source files:
|
|
\begin{itemize}
|
|
\item \SourceFile{include/swift/AST/Decl.h}
|
|
\item \SourceFile{include/swift/AST/DeclContext.h}
|
|
\item \SourceFile{lib/AST/Decl.cpp}
|
|
\item \SourceFile{lib/AST/DeclContext.cpp}
|
|
\end{itemize}
|
|
Other source files:
|
|
\begin{itemize}
|
|
\item \SourceFile{include/swift/AST/DeclNodes.def}
|
|
\item \SourceFile{include/swift/AST/ASTVisitor.h}
|
|
\item \SourceFile{include/swift/AST/ASTWalker.h}
|
|
\end{itemize}
|
|
|
|
\IndexSource{declaration}
|
|
\apiref{Decl}{class}
|
|
Base class of declarations. \FigRef{declhierarchy} shows various subclasses, which correspond to the different kinds of declarations described previously in this chapter.
|
|
\begin{figure}\captionabove{The \texttt{Decl} class hierarchy}\label{declhierarchy}
|
|
\begin{center}
|
|
\begin{tikzpicture}[%
|
|
grow via three points={one child at (0.5,-0.7) and
|
|
two children at (0.5,-0.7) and (0.5,-1.4)},
|
|
edge from parent path={[->] (\tikzparentnode.south) |- (\tikzchildnode.west)}]
|
|
\node [class] {\texttt{\vphantom{p}Decl}}
|
|
child { node [class] {\texttt{\vphantom{p}ValueDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}TypeDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}NominalTypeDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}StructDecl}}}
|
|
child { node [class] {\texttt{\vphantom{p}EnumDecl}}}
|
|
child { node [class] {\texttt{\vphantom{p}ClassDecl}}}
|
|
child { node [class] {\texttt{\vphantom{p}ProtocolDecl}}}
|
|
}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child { node [class] {\texttt{\vphantom{p}TypeAliasDecl}}}
|
|
child { node [class] {\texttt{\vphantom{p}AbstractTypeParamDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}GenericTypeParamDecl}}}
|
|
child { node [class] {\texttt{\vphantom{p}AssociatedTypeDecl}}}
|
|
}
|
|
}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child { node [class] {\texttt{\vphantom{p}AbstractFunctionDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}FuncDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}AccessorDecl}}}
|
|
}
|
|
child [missing] {}
|
|
child { node [class] {\texttt{\vphantom{p}ConstructorDecl}}}
|
|
child { node [class] {\texttt{\vphantom{p}DestructorDecl}}}
|
|
}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child { node [class] {\texttt{\vphantom{p}AbstractStorageDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}VarDecl}}
|
|
child { node [class] {\texttt{\vphantom{p}ParamDecl}}}
|
|
}
|
|
child [missing] {}
|
|
child { node [class] {\texttt{\vphantom{p}SubscriptDecl}}}
|
|
}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child [missing] {}
|
|
child { node [class] {\texttt{\vphantom{p}ExtensionDecl}}};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
\end{figure}
|
|
|
|
\IndexSource{synthesized declaration}
|
|
Instances are always allocated in the permanent arena of the \texttt{ASTContext}, either when the declaration is parsed, or synthesized. The top-level \verb|isa<>|, \verb|cast<>| and \verb|dyn_cast<>| template functions support dynamic casting from \texttt{Decl *} to any of its subclasses.
|
|
\begin{itemize}
|
|
\item \texttt{getDeclContext()} returns the parent \texttt{DeclContext} of this declaration.
|
|
\item \texttt{getInnermostDeclContext()} if this declaration is also a declaration context, returns the declaration as a \texttt{DeclContext}, otherwise returns the parent \texttt{DeclContext}.
|
|
\item \texttt{getASTContext()} returns the singleton AST context from a declaration.
|
|
\end{itemize}
|
|
|
|
\index{visitor pattern}
|
|
\IndexSource{declaration kind}
|
|
\index{exhaustive switch}
|
|
\index{statement}
|
|
\index{expression}
|
|
\index{type representation}
|
|
\paragraph{Visitors.}
|
|
To exhaustively handle each kind of declaration, the simplest way is to switch over the kind, which is an instance of the \texttt{DeclKind} enum, like this:
|
|
\begin{Verbatim}
|
|
Decl *decl = ...;
|
|
switch (decl->getKind()) {
|
|
case DeclKind::Struct: {
|
|
auto *structDecl = decl->castTo<StructDecl>();
|
|
...
|
|
}
|
|
case DeclKind::Enum:
|
|
...
|
|
case DeclKind::Class:
|
|
...
|
|
}
|
|
\end{Verbatim}
|
|
However, just as with types, is can be more convenient to use the visitor pattern, by subclassing \texttt{ASTVisitor} and overriding various \texttt{visit\emph{Kind}Decl()} methods. The visitor's \texttt{visit()} method performs the switch and dynamic cast dance above, and calls each method:
|
|
\begin{Verbatim}
|
|
class MyVisitor: public ASTVisitor<MyVisitor> {
|
|
public:
|
|
void visitStructDecl(StructType *decl) {
|
|
...
|
|
}
|
|
};
|
|
|
|
MyVisitor visitor;
|
|
|
|
Decl *decl = ...;
|
|
visitor.visit(decl);
|
|
\end{Verbatim}
|
|
The \texttt{ASTVisitor} also defines various methods corresponding to abstract base classes in the \texttt{Decl} hierarchy, so for example an override of \texttt{visitNominalTypeDecl()} will handle all nominal type declarations at once. The \texttt{ASTVisitor} is more general than just visiting declarations; it also supports visiting statements, expressions, and type representations.
|
|
|
|
A more elaborate form is implemented by the \texttt{ASTWalker}. While the visitor visits a single declaration, the walker traverses nested declarations, statements and expressions in a pre-order walk.
|
|
|
|
\IndexSource{value declaration}
|
|
\apiref{ValueDecl}{class}
|
|
Base class of named declarations.
|
|
|
|
\begin{itemize}
|
|
\item \texttt{getDeclName()} returns the declaration's name.
|
|
\item \texttt{getInterfaceType()} returns the declaration's \IndexSource{interface type}interface type.
|
|
\end{itemize}
|
|
|
|
\subsection*{Type Declarations}
|
|
|
|
\apiref{TypeDecl}{class}
|
|
Base class of \IndexSource{type declaration}type declarations.
|
|
\begin{itemize}
|
|
\item \texttt{getDeclaredInterfaceType()} returns the \IndexSource{declared interface type}type of an instance of this declaration.
|
|
\end{itemize}
|
|
|
|
\IndexSource{nominal type declaration}
|
|
\IndexSource{struct declaration}
|
|
\IndexSource{enum declaration}
|
|
\IndexSource{class declaration}
|
|
\IndexSource{nominal type declaration}
|
|
\IndexSource{self interface type}
|
|
\apiref{NominalTypeDecl}{class}
|
|
Base class of nominal type declarations. Also a \texttt{DeclContext}.
|
|
\begin{itemize}
|
|
\item \texttt{getSelfInterfaceType()} returns this nominal type declaration's self interface type (\SecRef{function decls}). This is the same as the declared interface type, except if this is a protocol declaration. A protocol's declared interface type is a nominal type, but its self interface type is the generic parameter \tSelf.
|
|
\item \texttt{getDeclaredType()} returns the type of an instance of this declaration, without generic arguments. If the declaration is generic, this is an \IndexSource{unbound generic type}unbound generic type. If this declaration is not generic, this is the same as the declared interface type. This is occasionally used in diagnostics instead of the declared interface type, when the generic parameter types are irrelevant.
|
|
\end{itemize}
|
|
|
|
\IndexSource{type alias declaration}
|
|
\IndexSource{underlying type!of type alias declaration}
|
|
\apiref{TypeAliasDecl}{class}
|
|
A type alias declaration. Also a \texttt{DeclContext}.
|
|
\begin{itemize}
|
|
\item \texttt{getDeclaredInterfaceType()} returns the underlying type of the type alias declaration, wrapped in type alias type sugar.
|
|
\item \texttt{getUnderlyingType()} returns the underlying type of the type alias declaration, without wrapping it in type alias type sugar.
|
|
\end{itemize}
|
|
|
|
\subsection*{Declaration Contexts}
|
|
|
|
\IndexSource{declaration context}
|
|
\apiref{DeclContext}{class}
|
|
Base class for declaration contexts. See also \SecRef{src:generic signatures}.
|
|
|
|
The top-level \verb|isa<>|, \verb|cast<>| and \verb|dyn_cast<>| template functions also support dynamic casting from a \texttt{DeclContext *} to any of its subclasses.
|
|
|
|
\pagebreak
|
|
|
|
\IndexSource{closure expression}
|
|
\IndexSource{source file}
|
|
\IndexSource{file unit}
|
|
There are a handful of subclasses which are not also subclasses of \texttt{Decl *}:
|
|
\begin{itemize}
|
|
\item \texttt{ClosureExpr}.
|
|
\item \texttt{FileUnit} and its various subclasses, such as \texttt{SourceFile}.
|
|
\item A few other less interesting ones found in the source.
|
|
\end{itemize}
|
|
|
|
Methods for understanding the nesting of declaration contexts:
|
|
\begin{itemize}
|
|
\item \texttt{getAsDecl()} if declaration context is also a declaration, returns the declaration, otherwise returns \texttt{nullptr}.
|
|
\item \texttt{getParent()} returns the parent declaration context.
|
|
\item \texttt{isModuleScopeContext()} returns true if this is a \texttt{ModuleDecl} or \texttt{FileUnit}.
|
|
\item \texttt{isTypeContext()} returns true if this is a nominal type declaration or an extension.
|
|
\item \texttt{isLocalContext()} returns true if this is not a module scope context or type context.
|
|
\item \texttt{getParentModule()} returns the module declaration at the root of the hierarchy.
|
|
\item \texttt{getModuleScopeContext()} returns the innermost parent which is a \texttt{ModuleDecl} or \texttt{FileUnit}.
|
|
\item \texttt{getParentSourceFile()} returns the innermost parent which is a source file, or \texttt{nullptr} if this declaration context was not parsed from source.
|
|
\item \texttt{getInnermostDeclarationDeclContext()} returns the innermost parent which is also a declaration, or \texttt{nullptr}.
|
|
\item \texttt{getInnermostDeclarationTypeContext()} returns the innermost parent which is also a nominal type or extension, or \texttt{nullptr}.
|
|
\end{itemize}
|
|
Operations on type contexts:
|
|
\begin{itemize}
|
|
\item \texttt{getSelfNominalDecl()} returns the nominal type declaration if this is a type context, or \texttt{nullptr}.
|
|
\item \texttt{getSelfStructDecl()} as above but result is a \texttt{StructDecl *} or \texttt{nullptr}.
|
|
\item \texttt{getSelfEnumDecl()} as above but result is a \texttt{EnumDecl *} or \texttt{nullptr}.
|
|
\item \texttt{getSelfClassDecl()} as above but result is a \texttt{ClassDecl *} or \texttt{nullptr}.
|
|
\item \texttt{getSelfProtocolDecl()} as above but result is a \texttt{ProtocolDecl *} or \texttt{nullptr}.
|
|
\item \texttt{getDeclaredInterfaceType()} delegates to the method on \texttt{NominalTypeDecl} or \texttt{ExtensionDecl} as appropriate.
|
|
\item \texttt{getSelfInterfaceType()} is similar.
|
|
\end{itemize}
|
|
|
|
Generic parameters and requirements:
|
|
\begin{itemize}
|
|
\item \texttt{isGenericContext()} answers true if either this generic context or one of its parents has a generic parameter list.
|
|
\item \texttt{isInnermostContextGeneric()} answers if this declaration context itself has a generic parameter list. Compare with \texttt{isGenericContext()}.
|
|
\end{itemize}
|
|
|
|
\subsection*{Generic Contexts}
|
|
|
|
Key source files:
|
|
\begin{itemize}
|
|
\item \SourceFile{include/swift/AST/GenericParamList.h}
|
|
\item \SourceFile{include/swift/AST/Requirement.h}
|
|
\item \SourceFile{lib/AST/GenericParamList.cpp}
|
|
\item \SourceFile{lib/AST/NameLookup.cpp}
|
|
\item \SourceFile{lib/AST/Requirement.cpp}
|
|
\end{itemize}
|
|
|
|
\IndexSource{generic context}
|
|
\IndexSource{generic declaration}
|
|
\IndexSource{parsed generic parameter list}
|
|
\apiref{GenericContext}{class}
|
|
Subclass of \texttt{DeclContext}. Base class for declaration kinds which can have a generic parameter list. See also \SecRef{src:generic signatures}.
|
|
\begin{itemize}
|
|
\item \texttt{getParsedGenericParams()} returns the declaration's parsed generic parameter list, or \texttt{nullptr}.
|
|
\item \texttt{getGenericParams()} returns the declaration's full generic parameter list, which includes any implicit generic parameters. Evaluates a \texttt{GenericParamListRequest}.
|
|
\item \texttt{hasGenericParamList()} answers if this declaration has a generic parameter list. This is equivalent to calling \texttt{isInnermostContextGeneric()} on \texttt{DeclContext}. Compare with \texttt{DeclContext::isGenericContext()}.
|
|
\item \texttt{getGenericContextDepth()} returns the \IndexSource{depth}depth of the declaration's generic parameter list, or \texttt{(unsigned)-1} if neither this declaration nor any outer declaration is generic.
|
|
\item \texttt{getTrailingWhereClause()} returns the declaration's trailing \texttt{where} clause, or \texttt{nullptr}.
|
|
\end{itemize}
|
|
|
|
Trailing \texttt{where} clauses are not preserved in serialized generic contexts. Most code should look at \texttt{GenericContext::getGenericSignature()} instead (\SecRef{src:generic signatures}), except when actually building the generic signature.
|
|
|
|
|
|
\IndexSource{generic parameter list}
|
|
\apiref{GenericParamList}{class}
|
|
A generic parameter list.
|
|
\begin{itemize}
|
|
\item \texttt{getParams()} returns an array of generic parameter declarations.
|
|
\item \texttt{getOuterParameters()} returns the outer generic parameter list, linking multiple generic parameter lists for the same generic context. Only used for extensions of nested generic types.
|
|
\end{itemize}
|
|
|
|
\IndexSource{protocol Self type@protocol \tSelf\ type}
|
|
\apiref{GenericParamListRequest}{class}
|
|
This \IndexSource{generic parameter list request}request creates the full generic parameter list for a declaration. Kicked off from \texttt{GenericContext::getGenericParams()}.
|
|
\begin{itemize}
|
|
\item For protocols, this creates the implicit \tSelf\ parameter.
|
|
\item For functions and subscripts, calls \texttt{createOpaqueParameterGenericParams()} to walk the formal parameter list and look for \texttt{OpaqueTypeRepr}s.
|
|
\item For extensions, calls \texttt{createExtensionGenericParams()} which clones the generic parameter lists of the extended nominal itself and all of its outer generic contexts, and links them together via \texttt{GenericParamList::getOuterParameters()}.
|
|
\end{itemize}
|
|
|
|
\IndexSource{generic parameter declaration}
|
|
\apiref{GenericTypeParamDecl}{class}
|
|
A generic parameter declaration.
|
|
\begin{itemize}
|
|
\item \IndexSource{depth}\texttt{getDepth()} returns the depth of the generic parameter declaration.
|
|
\item \IndexSource{index}\texttt{getIndex()} returns the index of the generic parameter declaration.
|
|
\item \texttt{getName()} returns the name of the generic parameter declaration.
|
|
\item \texttt{getDeclaredInterfaceType()} returns the \IndexSource{sugared type}sugared generic parameter type for this declaration, which prints as the generic parameter's name.
|
|
\item \texttt{isOpaque()} answers if this generic parameter is associated with an \IndexSource{opaque parameter}opaque parameter.
|
|
\item \texttt{getOpaqueTypeRepr()} returns the associated \texttt{OpaqueReturnTypeRepr} if this is an opaque parameter, otherwise \texttt{nullptr}.
|
|
\item \texttt{getInherited()} returns the generic parameter declaration's \IndexSource{inheritance clause!generic parameter declaration}inheritance clause.
|
|
\end{itemize}
|
|
|
|
Inheritance clauses are not preserved in serialized generic parameter declarations. Requirements stated on generic parameter declarations are part of the corresponding generic context's generic signature, so except when actually building the generic signature, most code uses \texttt{GenericContext::getGenericSignature()} instead (\SecRef{src:generic signatures}).
|
|
|
|
\apiref{GenericTypeParamType}{class}
|
|
A \IndexSource{generic parameter type}generic parameter type.
|
|
\begin{itemize}
|
|
\item \texttt{getDepth()} returns the depth of the generic parameter declaration.
|
|
\item \texttt{getIndex()} returns the index of the generic parameter declaration.
|
|
\item \texttt{getName()} returns the name of the generic parameter declaration if this is the sugared form, otherwise returns a string of the form ``\ttgp{d}{i}''.
|
|
\end{itemize}
|
|
|
|
\IndexSource{where clause@\texttt{where} clause}
|
|
\apiref{TrailingWhereClause}{class}
|
|
The syntactic representation of a trailing \texttt{where} clause.
|
|
\begin{itemize}
|
|
\item \texttt{getRequirements()} returns an array of \texttt{RequirementRepr}.
|
|
\end{itemize}
|
|
|
|
\IndexSource{requirement representation}
|
|
\apiref{RequirementRepr}{class}
|
|
The syntactic representation of a requirement in a trailing \texttt{where} clause.
|
|
\begin{itemize}
|
|
\item \texttt{getKind()} returns a \texttt{RequirementReprKind}.
|
|
\item \texttt{getFirstTypeRepr()} returns the first \texttt{TypeRepr} of a same-type requirement.
|
|
\item \texttt{getSecondTypeRepr()} returns the second \texttt{TypeRepr} of a same-type requirement.
|
|
\item \texttt{getSubjectTypeRepr()} returns the first \texttt{TypeRepr} of a constraint or layout requirement.
|
|
\item \texttt{getConstraintTypeRepr()} returns the second \texttt{TypeRepr} of a constraint requirement.
|
|
\item \texttt{getLayoutConstraint()} returns the layout constraint of a layout requirement.
|
|
\end{itemize}
|
|
|
|
\apiref{RequirementReprKind}{enum class}
|
|
Return type of \texttt{RequirementRepr::getKind()}.
|
|
\begin{itemize}
|
|
\item \texttt{RequirementRepr::TypeConstraint}
|
|
\item \texttt{RequirementRepr::SameType}
|
|
\item \texttt{RequirementRepr::LayoutConstraint}
|
|
\end{itemize}
|
|
|
|
\apiref{WhereClauseOwner}{class}
|
|
Represents a reference to some set of requirement representations which can be resolved to requirements, for example a trailing \texttt{where} clause. This is used by various requests, such as the \texttt{RequirementRequest} below, and the \texttt{InferredGenericSignatureRequest} in \SecRef{src:building generic signatures}.
|
|
\begin{itemize}
|
|
\item \texttt{getRequirements()} returns an array of \texttt{RequirementRepr}.
|
|
\item \texttt{visitRequirements()} resolves each requirement representation and invokes a callback with the \texttt{RequirementRepr} and resolved \texttt{Requirement}.
|
|
\end{itemize}
|
|
|
|
\apiref{RequirementRequest}{class}
|
|
Request which can be evaluated to resolve a single requirement representation in a \texttt{WhereClauseOwner}. Used by \texttt{WhereClauseOwner::visitRequirements()}.
|
|
|
|
\IndexSource{protocol declaration}
|
|
\IndexSource{primary associated type}
|
|
\apiref{ProtocolDecl}{class}
|
|
A protocol declaration.
|
|
\begin{itemize}
|
|
\item \texttt{getTrailingWhereClause()} returns this protocol \IndexSource{where clause@\texttt{where} clause!protocol declaration}\texttt{where} clause, or \texttt{nullptr}.
|
|
\item \texttt{getAssociatedTypes()} returns an array of all associated type declarations in this protocol.
|
|
\item \texttt{getPrimaryAssociatedTypes()} returns an array of all primary associated type declarations in this protocol.
|
|
\item \texttt{getInherited()} returns this protocol's \IndexSource{inheritance clause!protocol declaration}inheritance clause.
|
|
\end{itemize}
|
|
|
|
Trailing \texttt{where} clauses and inheritance clauses are not preserved in serialized protocol declarations. Except when actually building the requirement signature, most code uses \texttt{ProtocolDecl::getRequirementSignature()} instead (\SecRef{src:generic signatures}).
|
|
|
|
The last three utility methods operate on the requirement signature, so are safe to use on deserialized protocols:
|
|
\begin{itemize}
|
|
\item \texttt{getInheritedProtocols()} returns an array of all protocols directly \IndexSource{inherited protocol}inherited by this protocol, computed from the inheritance clause.
|
|
\item \texttt{inheritsFrom()} determines if this protocol inherits from the given protocol, possibly transitively.
|
|
\item \texttt{getSuperclassDecl()} returns the protocol's superclass declaration.
|
|
\end{itemize}
|
|
|
|
\apiref{AssociatedTypeDecl}{class}
|
|
An \IndexSource{associated type declaration}associated type declaration.
|
|
\begin{itemize}
|
|
\item \texttt{getTrailingWhereClause()} returns this associated type's trailing \IndexSource{where clause@\texttt{where} clause!associated type declaration}\texttt{where} clause, or \texttt{nullptr}.
|
|
\item \texttt{getInherited()} returns this associated type's \IndexSource{inheritance clause!associated type declaration}inheritance clause.
|
|
\end{itemize}
|
|
|
|
Trailing \texttt{where} clauses and inheritance clauses are not preserved in serialized associated type declarations. Requirements on associated types are part of a protocol's requirement signature, so except when actually building the requirement signature, most code uses \texttt{ProtocolDecl::getRequirementSignature()} instead (\SecRef{src:generic signatures}).
|
|
|
|
\subsection*{Function Declarations}
|
|
|
|
\apiref{AbstractFunctionDecl}{class}
|
|
Base class of \IndexSource{function declaration}function-like declarations. Also a \texttt{DeclContext}.
|
|
\begin{itemize}
|
|
\item \texttt{getImplicitSelfDecl()} returns the implicit \IndexSource{self parameter@\texttt{self} parameter}\texttt{self} parameter if this is a method, \texttt{nullptr} otherwise.
|
|
\item \texttt{getParameters()} returns the function's parameter list.
|
|
\item \texttt{getMethodInterfaceType()} returns the type of a method without the \tSelf\ clause.
|
|
\item \texttt{getResultInterfaceType()} returns the return type of this function or method.
|
|
\end{itemize}
|
|
|
|
\apiref{ParameterList}{class}
|
|
The parameter list of \texttt{AbstractFunctionDecl}, \texttt{EnumElementDecl}, or \texttt{SubscriptDecl}.
|
|
\begin{itemize}
|
|
\item \texttt{size()} returns the number of parameters.
|
|
\item \texttt{get()} returns the \texttt{ParamDecl} at the given index.
|
|
\end{itemize}
|
|
|
|
\IndexSource{constructor declaration}
|
|
\apiref{ConstructorDecl}{class}
|
|
A constructor declaration.
|
|
\begin{itemize}
|
|
\item \texttt{getInitializerInterfaceType()} returns the initializer interface type, used when type checking \texttt{super.init()} delegation.
|
|
\end{itemize}
|
|
|
|
\subsection*{Closure Conversion}
|
|
|
|
Key source files:
|
|
\begin{itemize}
|
|
\item \SourceFile{include/swift/AST/CaptureInfo.h}
|
|
\item \SourceFile{include/swift/SIL/TypeLowering.h}
|
|
\item \SourceFile{lib/AST/CaptureInfo.cpp}
|
|
\item \SourceFile{lib/Sema/TypeCheckCaptures.cpp}
|
|
\item \SourceFile{lib/SIL/IR/TypeLowering.cpp}
|
|
\end{itemize}
|
|
|
|
\IndexSource{captured value}
|
|
\IndexSource{closure conversion}
|
|
\apiref{CaptureInfo}{class}
|
|
An immutable list of captured values.
|
|
|
|
\apiref{CaptureInfoRequest::evaluate}{method}
|
|
Computes the \texttt{CaptureInfo}. This is \AlgRef{closure captures algorithm}.
|
|
|
|
\apiref{TypeConverter::getLoweredLocalCaptures()}{method}
|
|
Computes the lowered \texttt{CaptureInfo}. This is \AlgRef{lowered closure captures algorithm}. See \SecRef{sec:type lowering} and \SecRef{src:substitution maps} for a discussion of SIL type lowering and the \texttt{TypeConverter}.
|
|
|
|
\subsection*{Storage Declarations}
|
|
|
|
\IndexSource{storage declaration}
|
|
\apiref{AbstractStorageDecl}{class}
|
|
Base class for storage declarations.
|
|
\begin{itemize}
|
|
\item \texttt{getValueInterfaceType()} returns the type of the stored value, without \texttt{weak} or \texttt{unowned} storage qualifiers.
|
|
\end{itemize}
|
|
|
|
\IndexSource{variable declaration}
|
|
\IndexSource{property declaration}
|
|
\IndexSource{local variable declaration}
|
|
\apiref{VarDecl}{class}
|
|
Subclass of \texttt{AbstractStorageDecl}.
|
|
|
|
\IndexSource{subscript declaration}
|
|
\apiref{SubscriptDecl}{class}
|
|
Subclass of \texttt{AbstractStorageDecl} and \texttt{DeclContext}.
|
|
|
|
\IndexSource{accessor declaration}
|
|
\apiref{AccessorDecl}{class}
|
|
Subclass of \texttt{AbstractFunctionDecl}.
|
|
|
|
\end{document}
|