forked from rems-project/sail
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtypes.tex
258 lines (224 loc) · 11.3 KB
/
types.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
\section{Type System}
\label{sec:types}
(This section is still a work in progress)
\newcommand{\tcheck}[3]{#1 \vdash #2 \Leftarrow #3}
\newcommand{\tinfer}[3]{#1 \vdash #2 \Rightarrow #3}
\newcommand{\msail}[1]{\text{\lstinline[mathescape]+#1+}}
\subsection{Blocks}
\label{subsec:blocks}
\[
\frac{\tcheck{\Gamma}{E_0}{\text{\lstinline+bool+}}
\qquad
\tcheck{\Gamma}{M}{\text{\lstinline+string+}}
\qquad
\tcheck{\mathrm{FlowThen}(\Gamma, E_0)}{\text{\lstinline[mathescape]+\{$E_1$; $\ldots$; $E_n$\}+}}{A}}
{\tcheck{\Gamma}{\text{\lstinline[mathescape]+\{assert($E_0$, $M$); $E_1$; $\ldots$; $E_n$ \}+}}{A}}
\]
\[
\frac{\tcheck{\mathrm{BindAssignment}(\Gamma, L_0, E_0)}{\text{\lstinline[mathescape]+\{$E_1$; $\ldots$; $E_n$\}+}}{A}}
{\tcheck{\Gamma}{\text{\lstinline[mathescape]+\{$L_0$ = $E_0$; $E_1$; $\ldots$; $E_n$ \}+}}{A}}
\]
\[
\frac{\tcheck{\Gamma}{E_0}{\text{\lstinline+unit+}}
\qquad
\tcheck{\Gamma}{\text{\lstinline[mathescape]+\{$E_1$; $\ldots$; $E_n$\}+}}{A}}
{\tcheck{\Gamma}{\text{\lstinline[mathescape]+\{$E_0$; $E_1$; $\ldots$; $E_n$ \}+}}{A}}
\]
\[
\frac{\tcheck{\Gamma}{E}{A}}
{\tcheck{\Gamma}{\text{\lstinline[mathescape]+\{$E$\}+}}{A}}
\]
\subsection{Let bindings}
Note that \lstinline[mathescape]+{let x = y; $E_0$; $\ldots$; $E_n$}+
is equivalent to \lstinline[mathescape]+{let x = y in {$E_0$; $\ldots$; $E_n$}}+,
which is why there are no special cases for let bindings in Subsection~\ref{subsec:blocks}.
\[
\frac{\tcheck{\Gamma}{E_0}{B}
\qquad
\tcheck{\mathrm{BindPattern}(\Gamma, P, B)}{E_1}{A}}
{\tcheck{\Gamma}{\msail{let $\;P\;$ : $\;B\;$ = $\;E_0\;$ in $\;E_1$}}{A}}
\]
\[
\frac{\tinfer{\Gamma}{E_0}{B}
\qquad
\tcheck{\mathrm{BindPattern}(\Gamma, P, B)}{E_1}{A}}
{\tcheck{\Gamma}{\msail{let $\;P\;$ = $\;E_0\;$ in $\;E_1$}}{A}}
\]
\paragraph{Pattern bindings} The $\mathrm{BindPattern}$ and $\mathrm{BindPattern}'$ functions are used to bind patterns into an environment. The first few cases are simple, if we bind an identifier $x$ against a type $T$, where $x$ is either immutable or unbound, then $x : T$ is added to the environment. If we bind a type against a wildcard pattern, then the environment is returned unchanged. An \lstinline+as+ pattern binds its variable with the appropriate type then recursively binds the rest of the pattern. When binding patterns we always bind against the base type, and bring existentials into scope, which is why $\mathrm{BindPattern}$ does this and then calls the $\mathrm{BindPattern}'$ function which implements all the cases.
\begin{align*}
\mathrm{BindPattern}(\Gamma, P, T) &= \mathrm{BindPattern}'(\Gamma \lhd T, P, \mathrm{Base}(T))\\
\mathrm{BindPattern}'(\Gamma, x, T) &= \Gamma \oplus x : T, \tag{$x$ is unbound or immutable}\\
\mathrm{BindPattern}'(\Gamma, \msail{_}, T) &= \Gamma,\\
\mathrm{BindPattern}'(\Gamma, \msail{$P\;$ as $\;x$}, T) &= \mathrm{BindPattern}(\Gamma \oplus x : T, P, T). \tag{$x$ is unbound or immutable}
\end{align*}
If we try to bind a numeric literal $n$ against a type
\lstinline[mathescape]+int($N$)+ then we add a constraint to the
environment that the nexp $N$ is equal to $n$.
\begin{align*}
\mathrm{BindPattern}'(\Gamma, n, \msail{int($N$)}) &= \Gamma \oplus (N = n).
\end{align*}
We also have some rules for typechecking lists, as well as user
defined constructors in unions (omitted here)
\begin{align*}
\mathrm{BindPattern}'(\Gamma, [], \msail{list($A$)}) &= \Gamma,\\
\mathrm{BindPattern}'(\Gamma, \msail{$P_{\mathit{hd}}\;$ :: $\;P_{\mathit{tl}}$}, \msail{list($A$)})
&= \mathrm{BindPattern}(\mathrm{BindPattern}(\Gamma,P_{\mathit{hd}},A),P_{\mathit{tl}},\msail{list($A$)}).
\end{align*}
The pattern binding code follows a similar structure to the
bi-directional nature of the typechecking rules---the
$\mathrm{BindPattern}$ function acts like a checking rule where we
provide the type, and there is also an $\mathrm{InferPattern}$
function which acts like bind pattern but infers the types from the
patterns. There is therefore a final case
$\mathrm{BindPattern}(\Gamma, P, T) = \Gamma'$ where
$(\Gamma', T') = \mathrm{InferPattern}(\Gamma, P)$ and $T \subseteq T'$.
The $\mathrm{InferPattern}$ function is defined by the following cases
\begin{align*}
\mathrm{InferPattern}(\Gamma,x) &= (\Gamma, T_{\mathit{enum}}), \tag{$x$ is an element of enumeration $T_{\mathit{enum}}$}\\
\mathrm{InferPattern}(\Gamma,L) &= (\Gamma, \mathrm{InferLiteral}(L)), \tag{$L$ is a literal}\\
\mathrm{InferPattern}(\Gamma,\msail{$P\;$ : $\;T$}) &= (\mathrm{BindPattern}(\Gamma,P,T), T).
\end{align*}
\paragraph{Type patterns} There is one additional case for $\mathrm{BindPattern}'$ which we haven't discussed. \TODO{type patterns}
\subsection{If statements}
\[
\frac{\tcheck{\Gamma}{E_{\mathit{if}}}{\msail{bool}}
\qquad
\tcheck{\mathrm{FlowThen}(\Gamma, E_{\mathit{if}})}{E_{\mathit{then}}}{A}
\qquad
\tcheck{\mathrm{FlowElse}(\Gamma, E_{\mathit{if}})}{E_{\mathit{else}}}{A}}
{\tcheck{\Gamma}{\msail{if $\;E_{\mathit{if}}\;$ then $\;E_{\mathit{then}}\;$ else $\;E_{\mathit{else}}\;$}}{A}}
\]
\subsection{Return}
When checking the body of a function, the expected return type of the
function is placed into the context $\Gamma$.
\[
\frac{\tcheck{\Gamma}{E}{\mathrm{Return}(\Gamma)}}
{\tcheck{\Gamma}{\msail{return($E$)}}{A}}
\]
\subsection{Functions}
Depending on the context, functions can be either checked or
inferred---although the only difference between the two cases is that
in the checking case we can use the expected return type to resolve
some of the function quantifiers, whereas in the inferring case we
cannot.
\begin{align*}
\frac{
f : \forall Q, C.(B_0,\ldots,B_n) \rightarrow R \in \Gamma
\quad \textsc{InferFun}(\Gamma,Q,C,(B_0,\ldots,B_n),R,(x_0,\ldots,x_n)) = R'
} {
\tinfer{\Gamma}{f(x_0, \ldots, x_n)}{R'}
}
\end{align*}
\begin{align*}
\frac{
f : \forall Q, C.(B_0,\ldots,B_n) \rightarrow R \in \Gamma
\quad \textsc{CheckFun}(\Gamma,Q,C,(B_0,\ldots,B_n),R,(x_0,\ldots,x_n),R')
} {
\tcheck{\Gamma}{f(x_0, \ldots, x_n)}{R'}
}
\end{align*}
The rules for checking or inferring functions are rather more
complicated than the other typing rules and are hard to describe in
purely logical terms, so they are instead presented as an algorithm in
Figure~\ref{fig:funapp}. Roughly the inference algorithm works as
follows:
\begin{enumerate}
\item \textsc{InferFun} takes as input the typing context $\Gamma$, the
list of quantifiers $Q$ (a list of type variable/kind pairs), a
constraint $C$, the function argument types $B_0\ldots B_n$, the
function return type $R$, and finally this list of argument
expressions the function is applied to $x_0\ldots x_n$.
\item We create an empty list of unsolved typing goals
(expression/type pairs) called $\mathit{unsolved}$, a list of
constraints $\mathit{Constraints}$, and a set of existential
variables $\mathit{Existentials}$.
\item We iterate over each argument expression and type $x_m$ and
$B_m$, if $x_m$ contains free type variables in $Q$ we infer the
type of $x_n$ and attempt to unify that inferred type with $B_m$. If
this unification step fails we add $(x_m, B_m)$ to the list of
unsolved goals. This unification step may generate new existential
variables and constraints which are added to $\mathit{Existentials}$
and $\mathit{Constraints}$ as needed. The results of this
unification step are used to resolve the universally-quantified type
variables in $Q$. If $x_m$ does not contain free type variables in
$Q$, then we simply check it against $B_m$.
\item After this loop has finished we expect all the type variables in
$Q$ to have been resolved. If not, we throw a type error.
\item We now try to prove the function's constraint $C$ using the
resolved type variables, and check any remaining function arguments
in $\mathit{unsolved}$.
\item Finally, we add any new existentials and constraints to the
function's return type $R$, simplifying if at all possible (using
\textsc{SimplifyExist}), before returning this type as the inferred
type of the function.
\end{enumerate}
\noindent The \textsc{CheckFun} calls the \textsc{InferFun} function, but it
takes an additional $X$ argument which the the required return type in
the context where the function being checked is called. It
additionally unifies the function's declared return type with the
expected return type, and uses this to resolve any quantifiers in $Q$,
provided that the return type is not existentially quantified. It may
also be required to coerce $R$ into $X$.
\begin{figure}[p]
\begin{algorithmic}[1]
\Function{InferFun}{$\Gamma,Q, C, (B_0,\ldots,B_n), R, (x_0, \ldots, x_n)$}
\State $\mathit{unsolved}\gets []$;
$\mathit{Constraints}\gets []$;
$\mathit{Existentials}\gets \emptyset$
\ForAll{$m \in 0, \ldots, n$}
\If{$B_m$ contains type variables in $Q$}
\State $\Gamma \vdash x_m \Rightarrow E$
\Comment Infer the type of $x_m$ as $E$
\State $\mathit{unifiers}, \mathit{existentials}, \mathit{constraint} \gets$ \Call{CoerceAndUnify}{$\Gamma,E,B$}
\If{\textsc{CoerceAndUnify} failed with \textsc{UnificationError}}
\State $\mathit{unsolved}\gets (x_m,B_m) : \mathit{unsolved}$
\State \textbf{continue}
\Comment Skip to next iteration of loop
\ElsIf{$\mathit{existentials}$ is not empty}
\State Add type variables $\mathit{existentials}$ to $\Gamma$
\State Add constraint $\mathit{constraint}$ to $\Gamma$
\State $\mathit{Constraints}\gets \mathit{constraint} : \mathit{Constraints}$
\State $\mathit{Existentials}\gets \mathit{existentials} \cup \mathit{Existentials}$
\EndIf
\ForAll{$(\mathit{nvar}, \mathit{nexp}) \in \mathit{unifiers}$}
\State $B_0,...,B_n\gets B_0[\mathit{nvar} := \mathit{nexp}],\ldots,B_n[\mathit{nvar} := \mathit{nexp}]$
\State $R\gets R[\mathit{nvar} := \mathit{nexp}]$;
$C\gets C[\mathit{nvar} := \mathit{nexp}]$
\State Remove $\mathit{nvar}$ from $Q$
\EndFor
\ElsIf{$B_m$ does not contain type variables in $Q$}
\State $\tcheck{\Gamma}{x_m}{B_m}$
\Comment Check type of $x_m$ against $B_m$
\EndIf
\EndFor
\If{$Q$ is not empty}
\State \textbf{raise} \textsc{TypeError}
\Comment Unresolved universal quantifers
\EndIf
\State \Call{Prove}{$\Gamma, C$}
\ForAll{$(x_m,B_m) \in \mathit{unsolved}$}
$\tcheck{\Gamma}{x_m}{B_m}$
\EndFor
\State \Return \Call{SimplifyExist}{$\mathtt{exist}\ \mathit{Existentials}, \mathit{Constraints}.\ R$}
\EndFunction\\
\Function{CheckFun}{$\Gamma,Q,C,(B_0,\ldots,B_n),R,(x_0,\ldots,x_n),X$}
\If{$X$ and $R$ are not existentially quantified}
\State $\mathit{unifiers}, \_, \_ \gets$ \Call{Unify}{$\Gamma,R,X$}
\If{\textsc{Unify} failed with \textsc{UnificationError}}
\textbf{skip}
\Else
\ForAll{$(\mathit{nvar}, \mathit{nexp}) \in \mathit{unifiers}$}
\State $B_0,...,B_n\gets B_0[\mathit{nvar} := \mathit{nexp}],\ldots,B_n[\mathit{nvar} := \mathit{nexp}]$
\State $R\gets R[\mathit{nvar} := \mathit{nexp}]$;
$C\gets C[\mathit{nvar} := \mathit{nexp}]$
\State Remove $\mathit{nvar}$ from $Q$
\EndFor
\EndIf
\EndIf
\State $R'\gets$ \Call{InferFun}{$\Gamma,Q,C,(B_0,\ldots,B_n),R,(x_0,\ldots,x_n)$}
\State \Return \Call{Coerce}{$R',X$}
\EndFunction
\end{algorithmic}
\label{fig:funapp}
\caption{Inference and checking algorithms for function calls}
\end{figure}