Ver Fonte

uniquify

Jeremy Siek há 3 anos atrás
pai
commit
74f943929f
1 ficheiros alterados com 79 adições e 37 exclusões
  1. 79 37
      book.tex

+ 79 - 37
book.tex

@@ -14496,14 +14496,15 @@ variable occurrences, we review the standard notion of free variable.
 
 
 \begin{definition}
 \begin{definition}
 A variable is \textbf{free in expression} $e$ if the variable occurs
 A variable is \textbf{free in expression} $e$ if the variable occurs
-inside $e$ but does not have an enclosing binding that is also in
+inside $e$ but does not have an enclosing definition that is also in
 $e$.\index{subject}{free variable}
 $e$.\index{subject}{free variable}
 \end{definition}
 \end{definition}
 
 
-For example, in the expression \code{(+ x (+ y z))} the variables
-\code{x}, \code{y}, and \code{z} are all free.  On the other hand,
+For example, in the expression
+\racket{\code{(+ x (+ y z))}}\python{\code{x + y + z}}
+the variables \code{x}, \code{y}, and \code{z} are all free.  On the other hand,
 only \code{x} and \code{y} are free in the following expression
 only \code{x} and \code{y} are free in the following expression
-because \code{z} is bound by the \code{lambda}.
+because \code{z} is defined by the \code{lambda}.
 {\if\edition\racketEd
 {\if\edition\racketEd
 \begin{lstlisting}
 \begin{lstlisting}
    (lambda: ([z : Integer]) : Integer
    (lambda: ([z : Integer]) : Integer
@@ -14521,14 +14522,13 @@ special treatment. We need to transport, at runtime, the values of
 those variables from the point where the \code{lambda} was created to
 those variables from the point where the \code{lambda} was created to
 the point where the \code{lambda} is applied. An efficient solution to
 the point where the \code{lambda} is applied. An efficient solution to
 the problem, due to \citet{Cardelli:1983aa}, is to bundle the values
 the problem, due to \citet{Cardelli:1983aa}, is to bundle the values
-of the free variables together with the function pointer for the
-lambda's code into a tuple, an arrangement called a \emph{flat
-  closure} (which we shorten to just ``closure'').
-\index{subject}{closure}\index{subject}{flat closure} Fortunately, we
-have all the ingredients to make closures: Chapter~\ref{ch:Lvec} gave
-us tuples and Chapter~\ref{ch:Rfun} gave us function pointers. The
-function pointer resides at index $0$ and the values for the free
-variables fill in the rest of the tuple.
+of the free variables together with a function pointer into a tuple,
+an arrangement called a \emph{flat closure} (which we shorten to just
+``closure'').  \index{subject}{closure}\index{subject}{flat closure}
+Fortunately, we have all the ingredients to make closures:
+Chapter~\ref{ch:Lvec} gave us tuples and Chapter~\ref{ch:Rfun} gave us
+function pointers. The function pointer resides at index $0$ and the
+values for the free variables fill in the rest of the tuple.
 
 
 Let us revisit the example in Figure~\ref{fig:lexical-scoping} to see
 Let us revisit the example in Figure~\ref{fig:lexical-scoping} to see
 how closures work. It's a three-step dance. The program calls function
 how closures work. It's a three-step dance. The program calls function
@@ -14746,15 +14746,8 @@ class InterpLlambda(InterpLfun):
 
 
   def interp_exp(self, e, env):
   def interp_exp(self, e, env):
     match e:
     match e:
-      case FunRefArity(id, arity):
-        return env[id]
       case Lambda(params, body):
       case Lambda(params, body):
         return Function('lambda', params, [Return(body)], env)
         return Function('lambda', params, [Return(body)], env)
-      case Closure(arity, args):
-        return tuple([self.interp_exp(arg, env) for arg in args])
-      case AllocateClosure(length, typ, arity):
-        array = [None] * length
-        return array
       case _:
       case _:
         return super().interp_exp(e, env)
         return super().interp_exp(e, env)
     
     
@@ -15078,7 +15071,47 @@ to address this challenge.
 \section{Uniquify Variables}
 \section{Uniquify Variables}
 \label{sec:uniquify-lambda}
 \label{sec:uniquify-lambda}
 
 
-UNDER CONSTRUCTION
+With the addition of \code{lambda} we have a complication to deal
+with: name shadowing. Consider the following program with a function
+\code{f} that has a parameter \code{x}. Inside \code{f} there are two
+\code{lambda} expressions. The first \code{lambda} has a parameter
+that is also named \code{x}.
+
+\begin{lstlisting}
+def f(x:int, y:int) -> Callable[[int], int]:
+  g : Callable[[int],int] = (lambda x: x + y)
+  h : Callable[[int],int] = (lambda y: x + y)
+  x = input_int()
+  return g
+
+print(f(0, 10)(32))
+\end{lstlisting}
+
+Many of our compiler passes rely on being able to connect variable
+uses with their definitions using just the name of the variable,
+including new passes in this chapter. However, in the above example
+the name of the variable does not uniquely determine its
+definition. To solve this problem we recommend implementing a pass
+named \code{uniquify} that renames every variable in the program to
+make sure they are all unique.
+
+The following shows the result of \code{uniquify} for the above
+example. The \code{x} parameter of \code{f} is renamed to \code{x\_0}
+and the \code{x} parameter of the \code{lambda} is renamed to
+\code{x\_4}.
+
+\begin{lstlisting}
+def f(x_0:int, y_1:int) -> Callable[[int], int] :
+  g_2 : Callable[[int], int] = (lambda x_4: x_4 + y_1)
+  h_3 : Callable[[int], int] = (lambda y_5: x_0 + y_5)
+  x_0 = input_int()
+  return g_2
+
+def main() -> int :
+  print(f(0, 10)(32))
+  return 0
+\end{lstlisting}
+
 
 
 \fi
 \fi
 
 
@@ -15360,13 +15393,19 @@ AST. The interesting cases are the ones for \key{lambda} and function
 application. We transform a \key{lambda} expression into an expression
 application. We transform a \key{lambda} expression into an expression
 that creates a closure, that is, a tuple whose first element is a
 that creates a closure, that is, a tuple whose first element is a
 function pointer and the rest of the elements are the values of the
 function pointer and the rest of the elements are the values of the
-free variables of the \key{lambda}. However, we use the \code{Closure}
-AST node instead of using a tuple so that we can record the arity and
+free variables of the \key{lambda}.
+%
+\racket{However, we use the \code{Closure}
+  AST node instead of using a tuple so that we can record the arity
+  which is needed for \code{procedure-arity} and
 to distinguish closures from tuples in
 to distinguish closures from tuples in
-Section~\ref{sec:optimize-closures}.  In the generated code below,
-\itm{fvs} is the free variables of the lambda, \itm{name} is a unique
-symbol generated to identify the lambda and the \itm{arity} is the
-number of parameters (the length of \itm{ps}).
+Section~\ref{sec:optimize-closures}.}
+%
+In the generated code below, \itm{fvs} is the free variables of the
+lambda and \itm{name} is a unique symbol generated to identify the lambda.
+%
+\racket{The \itm{arity} is the number of parameters (the length of
+  \itm{ps}).}
 %
 %
 {\if\edition\racketEd
 {\if\edition\racketEd
 \begin{lstlisting}
 \begin{lstlisting}
@@ -15380,12 +15419,12 @@ number of parameters (the length of \itm{ps}).
 \begin{lstlisting}
 \begin{lstlisting}
 Lambda(|\itm{ps}|, |\itm{body}|)
 Lambda(|\itm{ps}|, |\itm{body}|)
 |$\Rightarrow$|
 |$\Rightarrow$|
-Closure(|\itm{arity}|, [FunRef(|\itm{name}|), |\itm{fvs}, \ldots|])
+Tuple([FunRef(|\itm{name}|), |\itm{fvs}, \ldots|])
 \end{lstlisting}
 \end{lstlisting}
 \fi}
 \fi}
 %
 %
 In addition to transforming each \key{Lambda} AST node into a
 In addition to transforming each \key{Lambda} AST node into a
-\key{Closure}, we create a top-level function definition for each
+tuple, we create a top-level function definition for each
 \key{Lambda}, as shown below.\\
 \key{Lambda}, as shown below.\\
 \begin{minipage}{0.8\textwidth}
 \begin{minipage}{0.8\textwidth}
 {\if\edition\racketEd
 {\if\edition\racketEd
@@ -15414,7 +15453,7 @@ the next paragraph, to obtain \itm{ps'} and \itm{rt'}.  The type
 \python{\code{Bottom()}}\racket{\code{\_} (the dummy type)} and the rest of
 \python{\code{Bottom()}}\racket{\code{\_} (the dummy type)} and the rest of
 the element types are the types of the free variables in the
 the element types are the types of the free variables in the
 lambda. We use \python{\code{Bottom()}}\racket{\code{\_}} because it
 lambda. We use \python{\code{Bottom()}}\racket{\code{\_}} because it
-is difficult to give a type to the function in the closure's type.%
+is non-trivial to give a type to the function in the closure's type.%
 %
 %
 \footnote{To give an accurate type to a closure, we would need to add
 \footnote{To give an accurate type to a closure, we would need to add
   existential types to the type checker~\citep{Minamide:1996ys}.}
   existential types to the type checker~\citep{Minamide:1996ys}.}
@@ -15456,7 +15495,8 @@ We transform function application into code that retrieves the
 function from the closure and then calls the function, passing in the
 function from the closure and then calls the function, passing in the
 closure as the first argument. We place $e'$ in a temporary variable
 closure as the first argument. We place $e'$ in a temporary variable
 to avoid code duplication.
 to avoid code duplication.
-%
+\begin{center}
+\begin{minipage}{\textwidth}
 {\if\edition\racketEd
 {\if\edition\racketEd
 \begin{lstlisting}
 \begin{lstlisting}
 (Apply |$e$| |$\itm{es}$|)
 (Apply |$e$| |$\itm{es}$|)
@@ -15475,6 +15515,8 @@ Let(|$\itm{tmp}$|, |$e'$|,
        [|$\itm{tmp}$|, |$e'_1, \ldots, e'_n$|]))
        [|$\itm{tmp}$|, |$e'_1, \ldots, e'_n$|]))
 \end{lstlisting}
 \end{lstlisting}
 \fi}
 \fi}
+\end{minipage}
+\end{center}
 
 
 There is also the question of what to do with references to top-level
 There is also the question of what to do with references to top-level
 function definitions. To maintain a uniform translation of function
 function definitions. To maintain a uniform translation of function
@@ -15504,7 +15546,7 @@ $\Rightarrow$
 \fi}
 \fi}
 {\if\edition\pythonEd
 {\if\edition\pythonEd
 \begin{lstlisting}
 \begin{lstlisting}
-Closure(|$n$|, [FunRef(|$f$|)])
+Tuple([FunRef(|$f$|)])
 \end{lstlisting}
 \end{lstlisting}
 \fi}
 \fi}
 \end{minipage}
 \end{minipage}
@@ -15578,11 +15620,11 @@ def lambda_0(fvs_1:tuple[bot,int,tuple[int]],z:int) -> int:
 def f(fvs_2:bot, x:int) -> tuple[Callable[[tuple[],int], int]]
 def f(fvs_2:bot, x:int) -> tuple[Callable[[tuple[],int], int]]
   y = (777,)
   y = (777,)
   y[0] = 4
   y[0] = 4
-  return closure(lambda_0,y,x)
+  return (lambda_0, y, x)
 
 
 def main() -> int:
 def main() -> int:
-  g = (let clos_3 = closure(f) in clos_3[0](clos_3, 5))
-  h = (let clos_4 = closure(f) in clos_4[0](clos_4, 3))
+  g = (let clos_3 = (f,) in clos_3[0](clos_3, 5))
+  h = (let clos_4 = (f,) in clos_4[0](clos_4, 3))
   print((let clos_5 = g in clos_5[0](clos_5, 11))
   print((let clos_5 = g in clos_5[0](clos_5, 11))
         + (let clos_6 = h in clos_6[0](clos_6, 15)))
         + (let clos_6 = h in clos_6[0](clos_6, 15)))
   return 0
   return 0
@@ -15600,6 +15642,7 @@ lexical scoping. Test your compiler on these new programs and all of
 your previously created test programs.
 your previously created test programs.
 \end{exercise}
 \end{exercise}
 
 
+\if\edition\racketEd
 
 
 \section{Expose Allocation}
 \section{Expose Allocation}
 \label{sec:expose-allocation-r5}
 \label{sec:expose-allocation-r5}
@@ -15643,9 +15686,6 @@ operators.
 \end{array}
 \end{array}
 \]
 \]
 \fi}
 \fi}
-{\if\edition\pythonEd
-UNDER CONSTRUCTION
-\fi}
 \end{minipage}
 \end{minipage}
 }
 }
 \caption{The abstract syntax of \LangCLam{}, extending \LangCFun{} (Figure~\ref{fig:c3-syntax}).}
 \caption{The abstract syntax of \LangCLam{}, extending \LangCFun{} (Figure~\ref{fig:c3-syntax}).}
@@ -15669,6 +15709,8 @@ Compile the \code{procedure-arity} operator into a sequence of
 instructions that access the tag from position $0$ of the vector and
 instructions that access the tag from position $0$ of the vector and
 extract the $5$-bits starting at position $58$ from the tag.
 extract the $5$-bits starting at position $58$ from the tag.
 
 
+\fi
+
 \begin{figure}[p]
 \begin{figure}[p]
 \begin{tikzpicture}[baseline=(current  bounding  box.center)]
 \begin{tikzpicture}[baseline=(current  bounding  box.center)]
 \node (Rfun) at (0,2)  {\large \LangLam{}};
 \node (Rfun) at (0,2)  {\large \LangLam{}};