This commit is contained in:
Peter Thiemann 2024-05-29 14:31:13 +02:00
parent 8b0e77cf50
commit 7e1ef7b3c1

View File

@ -387,6 +387,10 @@ let l1' : (*@$\wctype{\rwildcard{X}}{List}{\exptype{List}{\rwildcard{X}}}$@*) =
\section{Global Type Inference Algorithm} \section{Global Type Inference Algorithm}
\label{sec:glob-type-infer} \label{sec:glob-type-infer}
This section gives an overview of the global type inference algorithm
with examples and identifies the challenges that we had to overcome in
the design of the algorithm.
\begin{figure}[h] \begin{figure}[h]
\begin{minipage}{0.49\textwidth} \begin{minipage}{0.49\textwidth}
\begin{lstlisting}[style=tfgj, caption=Valid Java program, label=lst:addExample] \begin{lstlisting}[style=tfgj, caption=Valid Java program, label=lst:addExample]
@ -428,25 +432,30 @@ let l2:(*@$\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{
% \end{description} % \end{description}
%Our algorithm is an extension of the \emph{Global Type Inference for Featherweight Generic Java}\cite{TIforFGJ} algorithm. %Our algorithm is an extension of the \emph{Global Type Inference for Featherweight Generic Java}\cite{TIforFGJ} algorithm.
Listings \ref{lst:addExample}, \ref{lst:addExampleLet}, \ref{lst:addExampleCons}, and Listings~\ref{lst:addExample}, \ref{lst:addExampleLet}, \ref{lst:addExampleCons}, and
\ref{lst:addExampleSolution} showcase our global type inference algorithm step by step. \ref{lst:addExampleSolution} showcase the global type inference algorithm step by step.
In this example we know that the type of the variable \texttt{l} is an existential type and has to undergo a capture conversion In the Java code in Listing~\ref{lst:addExample}, the type of variable
\texttt{l} is an existential type so that it has to undergo a capture conversion
before being passed to a method call. before being passed to a method call.
This is done by converting the program to A-Normal form \ref{lst:addExampleLet}, To this end we convert the program to A-Normal form (Listing~\ref{lst:addExampleLet}),
which introduces a let statement defining a new variable \texttt{v}. which introduces a let statement defining a new variable \texttt{v}.
Afterwards unknown types are replaced by type placeholders ($\tv{v}$ for the type of \texttt{v}) and constraints are generated (see \ref{lst:addExampleCons}). As the type of \texttt{v} is unknown, the algorithm puts a type
These constraints mirror the type rules of our \TamedFJ{} calculus. placeholder $\tv{v}$ for the type of \texttt{v} before generating constraints (see Listing~\ref{lst:addExampleCons}).
These constraints mirror the typing rules of the \TamedFJ{} calculus (section~\ref{sec:tifj}).
% During the constraint generation step the type of the variable \texttt{v} is unknown % During the constraint generation step the type of the variable \texttt{v} is unknown
% and given the type placeholder $\tv{v}$. % and given the type placeholder $\tv{v}$.
The methodcall to \texttt{add} spawns the constraint $\tv{v} \lessdotCC \exptype{List}{\wtv{a}}$. The call to \texttt{add} generates the \emph{capture constraint} $\tv{v} \lessdotCC \exptype{List}{\wtv{a}}$.
Here we introduce a capture constraint ($\lessdotCC$) %a new type of subtype constraint This constraint ($\lessdotCC$) is a kind of subtype constraint, which additionally
expressing that the left side of the constraint is subject to a capture conversion. expresses that the left side of the constraint is subject to a capture conversion.
Now our unification algorithm \unify{} (defined in chapter \ref{sec:unify}) is used to solve these constraints. Next, the unification algorithm \unify{} (section~\ref{sec:unify}) solves the constraints.
In the starting set of constraints no type is assigned to $\tv{v}$ yet. Initially, no type is assigned to $\tv{v}$.
During the course of \unify{} more and more types emerge. % During the course of \unify{} more and more types emerge.
As soon as a concrete type for $\tv{v}$ is given \unify{} can conduct a capture conversion if needed. As soon as a concrete type for $\tv{v}$ is appears during constraint
solving, \unify{} can conduct a capture conversion if needed.
%The constraints where this is possible are marked as capture constraints. %The constraints where this is possible are marked as capture constraints.
In this example $\tv{v}$ will be set to $\wctype{\wildcard{X}{\type{String}}{\bot}}{List}{\rwildcard{X}}$ leaving us with the following constraints: In this example, $\tv{v}$ will be set to
$\wctype{\wildcard{X}{\type{String}}{\bot}}{List}{\rwildcard{X}}$
leaving us with the following constraints:
\begin{displaymath} \begin{displaymath}
\prftree[r]{Capture}{ \prftree[r]{Capture}{
@ -460,17 +469,20 @@ In this example $\tv{v}$ will be set to $\wctype{\wildcard{X}{\type{String}}{\bo
%which converts a constraint of the form $(\wctype{\rwildcard{X}}{C}{\rwildcard{X}} \lessdotCC \type{T})$ to $(\exptype{C}{\rwildcard{X}} \lessdot \type{T})$ %which converts a constraint of the form $(\wctype{\rwildcard{X}}{C}{\rwildcard{X}} \lessdotCC \type{T})$ to $(\exptype{C}{\rwildcard{X}} \lessdot \type{T})$
The constraint $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}} \lessdotCC \exptype{List}{\wtv{a}}$ The constraint $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}} \lessdotCC \exptype{List}{\wtv{a}}$
allows \unify{} to do a capture conversion to $\exptype{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$. allows \unify{} to do a capture conversion to $\exptype{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$.
The captured wildcard $\rwildcard{X}$ gets a fresh name and is stored in the wildcard environment of the \unify{} algorithm. The captured wildcard $\rwildcard{X}$ gets a fresh name \texttt{Y}, which is stored in the wildcard environment of the \unify{} algorithm.
Leaving us with the solution $\exptype{List}{\rwildcard{Y}} \lessdot \exptype{List}{\rwildcard{Y}}$, $\type{String} \lessdot \rwildcard{Y}$ Substituting \texttt{Y} for $\wtv{a}$ yields the constraints almost
The constraint $\type{String} \lessdot \rwildcard{Y}$ is satisfied solved: $\exptype{List}{\rwildcard{Y}} \lessdot
\exptype{List}{\rwildcard{Y}}$, $\type{String} \lessdot
\rwildcard{Y}$.
The first constraint is obviously satisfied and $\type{String} \lessdot \rwildcard{Y}$ is satisfied
because $\rwildcard{Y}$ has $\type{String}$ as lower bound. because $\rwildcard{Y}$ has $\type{String}$ as lower bound.
A correct Featherweight Java program including all type annotations and an explicit capture conversion via let statement is shown in listing \ref{lst:addExampleSolution}. A correct Featherweight Java program including all type annotations and an explicit capture conversion via let statement is shown in Listing \ref{lst:addExampleSolution}.
This program can be deducted from the type solution of our \unify{} algorithm presented in chapter \ref{sec:unify}. This program can be deduced from the solution of the \unify{} algorithm presented in Section~\ref{sec:unify}.
In the body of the let statement the type $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}}$ In the body of the let statement the type $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}}$
becomes $\exptype{List}{\rwildcard{X}}$ and the wildcard $\wildcard{X}{\type{Object}}{\type{String}}$ is free and can be used as becomes $\exptype{List}{\rwildcard{X}}$ and the capture converted wildcard $\wildcard{X}{\type{Object}}{\type{String}}$ can be used as
a type parameter to method call \texttt{<X>add(v, "String")}. a type parameter of the call \texttt{<X>add(v, "String")}.
% The input to our type inference algorithm is a modified version of the calculus in \cite{WildcardsNeedWitnessProtection} (see chapter \ref{sec:tifj}). % The input to our type inference algorithm is a modified version of the calculus in \cite{WildcardsNeedWitnessProtection} (see chapter \ref{sec:tifj}).
% First \fjtype{} (see section \ref{chapter:constraintGeneration}) generates constraints % First \fjtype{} (see section \ref{chapter:constraintGeneration}) generates constraints
@ -496,32 +508,39 @@ a type parameter to method call \texttt{<X>add(v, "String")}.
% do not substitute free type variables % do not substitute free type variables
Global Type inference for Featherweight Java with generics but without wildcards is solved already. Global type inference for Featherweight Generic Java without wildcards
But adding Wildcards to the calculus creates new problems we did not foresee has been considered elsewher \cite{TIforFGJ}.
and which have not been recognized by an existing type unification algorithm for Java with wildcards \ref{plue09_1}. Adding wildcards to the calculus created new problems, which have not
been recognized by existing work on type unification for Java with wildcards~\cite{plue09_1}.
% what is the problem? % what is the problem?
% Java does invisible capture conversion steps % Java does invisible capture conversion steps
Java Wildcard types are represented as existential types and have to be opened before they can be used. Java's wildcard types are represented as existential types that have to be opened before they can be used.
This can either be done implicitly (\cite{aModelForJavaWithWildcards}, \cite{javaTIisBroken}) or explicitly via let statements (\cite{WildcardsNeedWitnessProtection}). Opening can either be done implicitly
For all of those variations it is vital to know the argument types with which a method is called. (\cite{aModelForJavaWithWildcards}, \cite{javaTIisBroken}) or
But our input program does not contain any type annotations. explicitly via a let statement (\cite{WildcardsNeedWitnessProtection}).
We do not know where an existential type will emerge and where a capture conversion is necessary. For all variations it is vital to know the argument types with which a method is called.
This has to be figured out during the type inference algorithm. In the absence of type annotations,
We detected three main challenges related to Java Wildcards and Global Type Inference: we do not know where an existential type will emerge and where a capture conversion is necessary.
Rather, the type inference algorithm has to figure out the placement
of existentials.
We identified three main challenges related to Java wildcards and global type inference.
\begin{enumerate} \begin{enumerate}
\item \label{challenge:1} \item \label{challenge:1}
One challenge is to design the algorithm in a way that it finds a correct solution for the program shown in listing \ref{lst:addExample} One challenge is to design the algorithm in a way that it finds a
and rejects the program in listing \ref{lst:concatError}. correct solution for programs like the one shown in Listing~\ref{lst:addExample}
and rejects programs like the one in Listing~\ref{lst:concatError}.
The first one is a valid Java program, The first one is a valid Java program,
because the type \texttt{List<? super String>} is \textit{capture converted} to a fresh type variable $\rwildcard{X}$ because the type \texttt{List<? super String>} is \textit{capture converted} to a fresh type variable $\rwildcard{X}$
which is used as the generic method parameter for the call to \texttt{add} as shown in listing \ref{lst:addExampleLet}. which is used as the generic method parameter for the call to \texttt{add} as shown in Listing~\ref{lst:addExampleLet}.
Knowing that the type \texttt{String} is a subtype of the free variable $\rwildcard{X}$ Because we know that the type \texttt{String} is a subtype of the free variable $\rwildcard{X}$,
it is safe to pass \texttt{"String"} for the first parameter of the function. it is safe to pass \texttt{"String"} for the first parameter of the function.
The second program shown in listing \ref{lst:concatError} is incorrect. The second program shown in Listing~\ref{lst:concatError} is incorrect.
The method call to \texttt{concat} with two wildcard lists is unsound. The method call to \texttt{concat} with two wildcard lists is unsound.
Each list could be of a different kind and therefore the \texttt{concat} cannot succeed. The element types of the lists may be unrelated, therefore the call to \texttt{concat} cannot succeed.
The problem gets apparent when we try to write the \texttt{concat} method call in our \TamedFJ{} calculus (listing \ref{lst:concatTamedFJ}): The problem gets apparent if we try to write the \texttt{concat}
method call in the \TamedFJ{} calculus
(Listing~\ref{lst:concatTamedFJ}):
\texttt{l1'} and \texttt{l2'} are two different lists inside the body of the let statements, namely \texttt{l1'} and \texttt{l2'} are two different lists inside the body of the let statements, namely
$\exptype{List}{\rwildcard{X}}$ and $\exptype{List}{\rwildcard{Y}}$. $\exptype{List}{\rwildcard{X}}$ and $\exptype{List}{\rwildcard{Y}}$.
For the method call \texttt{concat(x1, x2)} no replacement for the generic \texttt{A} For the method call \texttt{concat(x1, x2)} no replacement for the generic \texttt{A}
@ -533,33 +552,33 @@ $\exptype{List}{\type{A}} <: \exptype{List}{\type{X}},
% Capture Conversion during Unify. % Capture Conversion during Unify.
\item \item
\unify{} morphs a constraint set into a correct type solution \unify{} transforms a constraint set into a correct type solution by
gradually assigning types to type placeholders during that process. gradually assigning types to type placeholders during that process.
Solved constraints are removed and never considered again. Solved constraints are removed and never considered again.
In the following example \unify{} solves the constraint generated by the expression In the following example, \unify{} solves the constraint generated by the expression
\texttt{l.add(l.head())} first, which results in $\ntv{l} \lessdot \exptype{List}{\wtv{a}}$. \texttt{l.add(l.head())} first, which results in $\ntv{l} \lessdot \exptype{List}{\wtv{a}}$.
\begin{verbatim} \begin{verbatim}
anyList() = new List<String>() :? new List<Integer>() anyList() = new List<String>() :? new List<Integer>()
add(anyList(), anyList().head()); add(anyList(), anyList().head());
\end{verbatim} \end{verbatim}
The type for \texttt{l} can be any kind of list, but it has to be a invariant one. The type for \texttt{l} can be an arbitrary list, but it has to be a invariant one.
Assigning a \texttt{List<?>} for \texttt{l} is unsound, because the type list hiding behind Assigning a \texttt{List<?>} for \texttt{l} is unsound, because the type list hiding behind
\texttt{List<?>} could be a different one for the \texttt{add} call than the \texttt{head} method call. \texttt{List<?>} could be a different one for the \texttt{add} call than the \texttt{head} method call.
An additional constraint $\wctype{\rwildcard{X}}{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$ An additional constraint $\wctype{\rwildcard{X}}{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$
is solved by removing the wildcard $\rwildcard{X}$ if possible. is solved by removing the wildcard $\rwildcard{X}$ if possible.
this problem is solved by ANF transformation This problem is solved by ANF transformation
\item \item
% This problem is solved by assuming everything is a wildcard and lateron erasing excessive wildcards % This problem is solved by assuming everything is a wildcard and lateron erasing excessive wildcards
% this is solved by having wildcards bound to a type. But this makes it necessary to remove wildcards lateron otherwise Unify would have to backtrack % this is solved by having wildcards bound to a type. But this makes it necessary to remove wildcards lateron otherwise Unify would have to backtrack
The program in listing \ref{shuffleExample} shows a challenge involving wildcards and subtyping. The program in Listing~\ref{shuffleExample} exhibits a challenge involving wildcards and subtyping.
The method call \texttt{shuffle(l)} is incorrect, because \texttt{l} has the type The method call \texttt{shuffle(l)} is incorrect, because \texttt{l} has type
$\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$ representing a list of unknown lists. $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$ representing a list of unknown lists.
Whereas $\wctype{\rwildcard{X}}{List2D}{\rwildcard{X}}$ is a subtype of However, $\wctype{\rwildcard{X}}{List2D}{\rwildcard{X}}$ is a subtype of
$\wctype{\rwildcard{X}}{List}{\exptype{List}{\rwildcard{X}}}$ representing a list of lists, all of the same type $\rwildcard{X}$, $\wctype{\rwildcard{X}}{List}{\exptype{List}{\rwildcard{X}}}$ which represents a list of lists, all of the same type $\rwildcard{X}$,
and can savely be passed to \texttt{shuffle}. and can be passed safely to \texttt{shuffle}.
This behavior can also be explained by looking at the types and their capture converted versions: This behavior can also be explained by looking at the types and their capture converted versions:
\begin{center} \begin{center}
\begin{tabular}{l | l | l} \begin{tabular}{l | l | l}
@ -619,7 +638,7 @@ shuffle(l2d); // Valid
% \end{center} % \end{center}
% \end{constraintset} % \end{constraintset}
Given such a program our type inference algorithm has to allow the call \texttt{shuffle(l2d)} and Given such a program the type inference algorithm has to allow the call \texttt{shuffle(l2d)} and
decline the call to \texttt{shuffle(l)}. decline the call to \texttt{shuffle(l)}.
% The method call \texttt{shuffle(l)} is invalid however, % The method call \texttt{shuffle(l)} is invalid however,
@ -628,10 +647,10 @@ decline the call to \texttt{shuffle(l)}.
% There is no solution for the subtype constraint: % There is no solution for the subtype constraint:
% $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}} \lessdotCC \exptype{List}{\exptype{List}{\wtv{x}}}$ % $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}} \lessdotCC \exptype{List}{\exptype{List}{\wtv{x}}}$
\item \label{challenge3} \textbf{Free Variables cannot leaver their scope}: \item \label{challenge3} \textbf{Free variables cannot leave their scope}:
Let's assume we have a variable \texttt{ls} with type $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$ Let's assume we have a variable \texttt{ls} with type $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$
%When calling the \texttt{id} function with an element of this list we have to apply capture conversion. %When calling the \texttt{id} function with an element of this list we have to apply capture conversion.
and the following input program: and the following program:
\noindent \noindent
\begin{minipage}{0.62\textwidth} \begin{minipage}{0.62\textwidth}
@ -648,14 +667,15 @@ let x : (*@$\wctype{\rwildcard{X}}{List}{\rwildcard{X}}$@*) = ls.get(0) in id(x)
% the variable z has to % the variable z has to
Take the Java program in listing \ref{lst:mapExample} for example. Take the Java program in listing \ref{lst:mapExample} for example.
It maps every element of a list It uses map to apply a polymorphic method \texttt{id} to every element of a list
$\expr{l} : \exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$ $\expr{l} :
to a polymorphic \texttt{id} method. \exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$.
We want to use our \unify{} algorithm to determine the correct type for the
variable \expr{l2}. How do we get the \unify{} algorithm to determine the correct type for the
variable \expr{l2}?
Although we do not specify constraint generation for language constructs like Although we do not specify constraint generation for language constructs like
lambda expressions used in this example, lambda expressions used in this example,
we can imagine that the constraints have to look something like this: we can imagine that the constraints have to look like in Listing~\ref{lst:mapExampleCons}.
\begin{minipage}{0.45\textwidth} \begin{minipage}{0.45\textwidth}
\begin{lstlisting}[caption=List Map Example,label=lst:mapExample] \begin{lstlisting}[caption=List Map Example,label=lst:mapExample]
@ -682,16 +702,18 @@ stem from the body of the lambda expression
The T-Let rule prevents us from using free variables created by the method call to \expr{id} The T-Let rule prevents us from using free variables created by the method call to \expr{id}
to be used in the return type $\tv{z}$. to be used in the return type $\tv{z}$.
But this has to be signaled to the \unify{} algorithm, which does not know about the origin and context of But this restriction has to be communicated to the \unify{} algorithm,
which does not know about the origin and context of
the constraints. the constraints.
If we naively substitute $\sigma(\tv{z}) = \exptype{List}{\rwildcard{A}}$ If we naively substitute $\sigma(\tv{z}) = \exptype{List}{\rwildcard{A}}$
the return type of the \texttt{map} function would be the type $\exptype{List}{\exptype{List}{\rwildcard{A}}}$. the return type of the \texttt{map} function would be the type
This type solution is unsound. $\exptype{List}{\exptype{List}{\rwildcard{A}}}$, which would be unsound.
The type of \expr{l2} is the same as the one of \expr{l}: The type of \expr{l2} is the same as the one of \expr{l}:
$\exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$ $\exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$
\textbf{Solution:} \textbf{Solution:}
We solve this by distinguishing between wildcard placeholders and normal placeholders. We solve this issue by distinguishing between wildcard placeholders
and normal placeholders and introducing them as needed.
$\ntv{z}$ is a normal placeholder and is not allowed to contain free variables. $\ntv{z}$ is a normal placeholder and is not allowed to contain free variables.
\end{enumerate} \end{enumerate}