You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
\paragraph{Associated types} Perhaps the simplest example of a protocol with an associated type is the \texttt{Iterator} protocol in the standard library. This protocol abstracts over an iterator which produces elements of a type that depends on the conformance:
464
464
\begin{Verbatim}
@@ -473,7 +473,7 @@ \section{Protocols}
473
473
return iter.next()!
474
474
}
475
475
\end{Verbatim}
476
-
The return type of our function is the \emph{identifier type representation} \texttt{I.Element} with two components, ``\texttt{I}'' and ``\texttt{Element}''. Type resolution resolves this type representation to a type by performing a qualified lookup of \texttt{Element} on the base type \texttt{I}. The generic parameter type \texttt{I} is subject to a conformance requirement, and qualified lookup finds the associated type declaration \texttt{Element}.
476
+
The return type of our function is the \emph{declaration reference type representation} \texttt{I.Element} with two components, ``\texttt{I}'' and ``\texttt{Element}''. Type resolution resolves this type representation to a type by performing a qualified lookup of \texttt{Element} on the base type \texttt{I}. The generic parameter type \texttt{I} is subject to a conformance requirement, and qualified lookup finds the associated type declaration \texttt{Element}.
477
477
478
478
\index{dependent member type}
479
479
The resolved type is a \emph{dependent member type} composed from the generic parameter type \texttt{I} and associated type declaration \texttt{Element}. We will denote this dependent member type as \verb|I.[IteratorProtocol]Element| to make explicit the fact that a name lookup has resolved the identifier \texttt{Element} to an associated type.
@@ -487,7 +487,7 @@ \section{Protocols}
487
487
\end{quote}
488
488
489
489
\begin{MoreDetails}
490
-
\item Identifier type representations: Section \ref{identtyperepr}
490
+
\item Declaration reference type representations: Section \ref{declreftyperepr}
A type representation has a tree structure, so when we talk about the type representation \texttt{Array<Int>}, we really mean this:
1395
1395
\begin{quote}
1396
-
``An identifier type representation with a single component, storing the identifier \texttt{Array} together with a single generic argument. The generic argument is another identifier type representation, again with a single component, storing the identifier \texttt{Int}.''
1396
+
``A declaration reference type representation with a single component, storing the identifier \texttt{Array} together with a single generic argument. The generic argument is another declaration reference type representation, again with a single component, storing the identifier \texttt{Int}.''
1397
1397
\end{quote}
1398
1398
Types also have a tree structure, so when we talk about the type \texttt{Array<Int>}, what we really mean is:
1399
1399
\begin{quote}
@@ -1671,7 +1671,7 @@ \section{Abstract Types}
1671
1671
This concept comes from Objective-C, where it is called \texttt{instancetype}. The dynamic Self type in many ways behaves like a generic parameter, but it is not represented as one; the type checker and SILGen implement support for it directly.
1672
1672
\begin{example} Listing~\ref{dynamic self example} demonstrates some of the behaviors of the dynamic Self type. Two invalid cases are shown; \texttt{invalid1()} is rejected because the type checker cannot prove that the return type is always an instance of the dynamic type of \texttt{self}, and \texttt{invalid2()} is rejected because \texttt{Self} appears in contravariant position.
1673
1673
1674
-
Note that \texttt{Self} has a different interpretation inside a non-class type declaration. In a protocol declaration, \texttt{Self} is the implicit generic parameter (Section~\ref{protocols}). In a struct or enum declaration, \texttt{Self} is the declared interface type (Section~\ref{identtyperepr}).
1674
+
Note that \texttt{Self} has a different interpretation inside a non-class type declaration. In a protocol declaration, \texttt{Self} is the implicit generic parameter (Section~\ref{protocols}). In a struct or enum declaration, \texttt{Self} is the declared interface type (Section~\ref{declreftyperepr}).
\paragraph{Type alias types} A type alias type represents a reference to a type alias declaration. It contains an optional parent type, a substitution map, and the substituted underlying type. The canonical type of a type alias type is the substituted underlying type.
1684
1684
1685
-
The type alias type's substitution map is formed in type resolution, from any generic arguments applied to the type alias type declaration itself, together with the generic arguments of the base type (Section~\ref{identtyperepr}). Type resolution applies this substitution map to the underlying type of the type alias declaration to compute the substituted underlying type. The type alias type also preserves this substitution map for printing, and for requirement inference (Section~\ref{requirementinference}).
1685
+
The type alias type's substitution map is formed in type resolution, from any generic arguments applied to the type alias type declaration itself, together with the generic arguments of the base type (Section~\ref{declreftyperepr}). Type resolution applies this substitution map to the underlying type of the type alias declaration to compute the substituted underlying type. The type alias type also preserves this substitution map for printing, and for requirement inference (Section~\ref{requirementinference}).
1686
1686
1687
1687
\index{optional sugared type}
1688
1688
\paragraph{Optional types} The optional type is written as \texttt{T?} for some object type \texttt{T}; its canonical type is \texttt{Optional<T>}.
The first two original types are generic parameters, and substitution directly projects the corresponding replacement type from the substitution map; the second two original types are substituted by recursively replacing generic parameters they contain.
4075
4075
4076
-
References to generic type alias declarations are more complex because in addition to the generic parameters of the base type, the generic type alias will have generic parameters of its own. Section~\ref{identtyperepr} describes how the substitution map is computed in this case.
4076
+
References to generic type alias declarations are more complex because in addition to the generic parameters of the base type, the generic type alias will have generic parameters of its own. Section~\ref{declreftyperepr} describes how the substitution map is computed in this case.
In fact, the type alias \texttt{A} cannot be referenced as a member of this base type at all, because name lookup checks whether the generic requirements of a type declaration are satisfied. Checking generic requirements will be first introduced as part of type resolution (Section~\ref{identtyperepr}), and will come up elsewhere as well.
4260
+
In fact, the type alias \texttt{A} cannot be referenced as a member of this base type at all, because name lookup checks whether the generic requirements of a type declaration are satisfied. Checking generic requirements will be first introduced as part of type resolution (Section~\ref{declreftyperepr}), and will come up elsewhere as well.
\section{Identifier Type Representations}\label{identtyperepr}
5867
+
\section{Declaration Reference Type Representations}\label{declreftyperepr}
5868
5868
5869
5869
\ifWIP
5870
5870
5871
-
\index{identifier type representation}
5872
-
Structural types, such as function types and tuples, have their own type representations parsed from special syntax, and type resolution constructs the corresponding semantic types directly. On the other hand, references to type declarations---nominal types, type aliases, generic parameters and associated types---are resolved via name lookup from a very general kind of type representation called an \emph{identifier type representation}.
5871
+
\index{declaration reference type representation}
5872
+
Structural types, such as function types and tuples, have their own type representations parsed from special syntax, and type resolution constructs the corresponding semantic types directly. On the other hand, references to type declarations---nominal types, type aliases, generic parameters and associated types---are resolved via name lookup from a very general kind of type representation called an \emph{declaration reference type representation}.
5873
5873
5874
-
This kind of type representation consists of one or more \emph{components}, separated by dot in written syntax. Each component stores an identifier, together with an optional list of one or more generic arguments, where each generic argument is again recursively a type representation. The following identifier type representation has three components, two of which have generic arguments:
5874
+
This kind of type representation consists of one or more \emph{components}, separated by dot in written syntax. The first, base component is an arbitrary type representation. Each subsequent component stores an identifier, together with an optional list of one or more generic arguments, where each generic argument is again recursively a type representation. The following declaration reference type representation has three components, two of which have generic arguments:
5875
5875
\begin{quote}
5876
5876
\begin{verbatim}
5877
5877
Foo.Bar<(Int) -> ()>.Baz<Float, String>
5878
5878
\end{verbatim}
5879
5879
\end{quote}
5880
-
\paragraph{Unqualified lookup} The first component is special. An unqualified name lookup is performed to find a type declaration with the given name, starting from the innermost lexical scope, finally reaching the top level, after which all imported modules are searched.
5880
+
\paragraph{Unqualified lookup} The first component is special. If it stores an identifier, an unqualified name lookup is performed to find a type declaration with the given name, starting from the innermost lexical scope, finally reaching the top level, after which all imported modules are searched.
5881
5881
5882
5882
The first component can be a module name, in which case there must be at least two components; modules can only be used as the base of a lookup, and are not first-class values which stand on their own.
5883
5883
@@ -5963,15 +5963,15 @@ \section{Identifier Type Representations}\label{identtyperepr}
5963
5963
}
5964
5964
\end{Verbatim}
5965
5965
\end{listing}
5966
-
\begin{example} The return type of \texttt{f1()} in Listing~\ref{applying generic arguments} is an identifier type representation with two components:
5966
+
\begin{example} The return type of \texttt{f1()} in Listing~\ref{applying generic arguments} is a declaration reference type representation with two components:
5967
5967
\begin{enumerate}
5968
5968
\item The first component is resolved by applying the substitution map $\texttt{T}:=\texttt{Int}$ to the declared interface type of \texttt{Outer}, which outputs \texttt{Outer<Int>}.
5969
5969
\item The second component is resolved by first building a substitution map for the generic signature of \texttt{Inner}, which is \texttt{<T,~U>}. The base type \texttt{Outer<Int>} provides the replacement $\texttt{T}:=\texttt{Int}$, and the component's single generic argument \texttt{String} provides the replacement $\texttt{U}:=\texttt{String}$. The declared interface type of the named type declaration \texttt{Inner} is \texttt{Outer<T>.Inner<U>}. Applying the combined substitution map to this type gives \texttt{Outer<Int>.Inner<String>}.
5970
5970
\end{enumerate}
5971
5971
5972
5972
\end{example}
5973
5973
5974
-
The type resolution process for an identifier type representation might seem unnecessarily convoluted. When resolving a type representation like \texttt{Outer<Int>.Inner<String>}, we build the type of the first component by applying a substitution map to the declared interface type of the type declaration, \texttt{Outer<T>}. In the next step, we turn this type back into a substitution map, extend the substitution map with a replacement type for \texttt{U}, then apply it to the declared interface type of \texttt{Outer<T>.Inner<U>}. It seems like we might be able to get away with performing a chain of name lookups to find the final type declaration, then collect all of the generic arguments and apply them in one shot. Unfortunately, the next example shows why this appealing simplification does not handle the full generality of type resolution.
5974
+
The type resolution process for a declaration reference type representation might seem unnecessarily convoluted. When resolving a type representation like \texttt{Outer<Int>.Inner<String>}, we build the type of the first component by applying a substitution map to the declared interface type of the type declaration, \texttt{Outer<T>}. In the next step, we turn this type back into a substitution map, extend the substitution map with a replacement type for \texttt{U}, then apply it to the declared interface type of \texttt{Outer<T>.Inner<U>}. It seems like we might be able to get away with performing a chain of name lookups to find the final type declaration, then collect all of the generic arguments and apply them in one shot. Unfortunately, the next example shows why this appealing simplification does not handle the full generality of type resolution.
5975
5975
5976
5976
\begin{listing}\captionabove{The named type declaration of a component can depend on generic arguments previously applied}\label{type resolution with dependent base}
5977
5977
\begin{Verbatim}
@@ -6013,7 +6013,7 @@ \section{Identifier Type Representations}\label{identtyperepr}
6013
6013
6014
6014
\end{example}
6015
6015
6016
-
\paragraph{Bound components} A minor optimization worth understanding, because it slightly complicates the implementation. After type resolution of a component succeeds, the bound (or found, perhaps) type declaration is stored inside the component. If the identifier type representation is resolved again, any bound components will skip the name lookup and proceed to compute the final type from the bound declaration. The optimization was more profitable in the past, when type resolution actually had \emph{three} stages, with a third stage resolving interface types to archetypes. The third stage was subsumed by the \texttt{mapTypeIntoContext()} operation on generic environments. Parsing textual SIL also ``manually'' binds components to type declarations which name lookup would otherwise not find, in order to parse some of the more esoteric SIL syntax that we're not going to discuss here.
6016
+
\paragraph{Bound components} A minor optimization worth understanding, because it slightly complicates the implementation. After type resolution of a component succeeds, the bound (or found, perhaps) type declaration is stored inside the component. If the declaration reference type representation is resolved again, any bound components will skip the name lookup and proceed to compute the final type from the bound declaration. The optimization was more profitable in the past, when type resolution actually had \emph{three} stages, with a third stage resolving interface types to archetypes. The third stage was subsumed by the \texttt{mapTypeIntoContext()} operation on generic environments. Parsing textual SIL also ``manually'' binds components to type declarations which name lookup would otherwise not find, in order to parse some of the more esoteric SIL syntax that we're not going to discuss here.
Copy file name to clipboardExpand all lines: docs/proposals/DeclarationTypeChecker.rst
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -148,7 +148,7 @@ How do we get there?
148
148
149
149
The proposed architecture is significantly different from the current type checker architecture, so how do we get there from here? There are a few concrete steps we can take:
150
150
151
-
**Make all AST nodes phase-aware**: Introduce a trait that can ask an arbitrary AST node (``Decl``, ``TypeRepr``, ``Pattern``, etc.) its current phase. AST nodes may compute this information on-the-fly or store it, as appropriate. For example, a ``TypeRepr`` can generally determine its phase based on the existing state of the ``IdentTypeRepr`` nodes it includes.
151
+
**Make all AST nodes phase-aware**: Introduce a trait that can ask an arbitrary AST node (``Decl``, ``TypeRepr``, ``Pattern``, etc.) its current phase. AST nodes may compute this information on-the-fly or store it, as appropriate. For example, a ``TypeRepr`` can generally determine its phase based on the existing state of the ``DeclRefTypeRepr`` nodes it includes.
152
152
153
153
**Make name lookup phase-aware**: Name lookup is currently one of the worst offenders when violating phase ordering. Parameterize name lookup based on the phase at which it's operating. For example, asking for name lookup at the "extension binding" phase might not resolve type aliases, look into superclasses, or look into protocols.
0 commit comments