\documentclass[acmsmall,nonacm]{acmart}

\bibliographystyle{ACM-Reference-Format}
\citestyle{acmauthoryear}

\RequirePackage{tikz}
\RequirePackage{scalerel}
\RequirePackage{xparse}
\RequirePackage{xifthen}

\usepackage{mathpartir}

\usetikzlibrary{shapes}
\usetikzlibrary{arrows}
\usetikzlibrary{calc}
\usetikzlibrary{arrows.meta}

%% Invariants and Ghost ownership
% PDS: Was 0pt inner, 2pt outer.
% \boxedassert [tikzoptions] contents [name]
\tikzstyle{boxedassert_border} = [sharp corners,line width=0.2pt]
\NewDocumentCommand \boxedassert {O{} m o}{%
	\tikz[baseline=(m.base)]{
		%	  \node[rectangle, draw,inner sep=0.8pt,anchor=base,#1] (m) {${#2}\mathstrut$};
		\node[rectangle,inner sep=0.8pt,outer sep=0.2pt,anchor=base] (m) {${\,#2\,}\mathstrut$};
		\draw[#1,boxedassert_border] ($(m.south west) + (0,0.65pt)$) rectangle ($(m.north east) + (0, 0.7pt)$);
	}\IfNoValueF{#3}{^{\,#3}}%
}
\DeclareMathOperator*{\Sep}{\scalerel*{\ast}{\sum}}
\newcommand*{\ghost}[1]{\boxedassert[densely dashed]{#1}}
\newcommand*{\N}{\mathbb{N}}
\newcommand*{\Z}{\mathbb{Z}}
\newcommand{\wand}{\mathrel{-\!\!\ast}}
\newcommand{\core}[1]{\left| #1 \right|}
\newcommand{\proves}{\vdash}
\newcommand{\makes}{\dashv}
\newcommand{\reach}{\rightsquigarrow}
\newcommand{\constep}{\proves\cdot\makes}
\newcommand{\makesto}{\dashv\!\constep}
\newcommand{\judgment}[2][]{\noindent\\\textbf{#1}\hspace{\stretch{1}}\fbox{$#2$}\nopagebreak}
\newcommand{\judgmentB}[3][]{\noindent\\\textbf{#1}\hspace{\stretch{1}}\fbox{$#2$}\ \ \fbox{$#3$}\nopagebreak}
\newcommand*{\axiom}[2][]{\infer[#1]{}{#2}}

\begin{document}

\title{Metamath C technical appendix}

%% Author with single affiliation.
\author{Mario Carneiro}
\affiliation{
  \institution{Carnegie Mellon University}
}

% \begin{abstract}
% Text of abstract \ldots.
% \end{abstract}

\maketitle

\section{Introduction}

This is an informal development of the theory behind the Metamath C language: the syntax and separation logic, as well as the lowering map to x86. For now, this is just a set of notes for the actual compiler. (Informal is a relative word, of course, and this is quite formally precise from a mathematician's point of view. But it is not mechanized.)

\section{Syntax}

The syntax of MMC programs, after type inference, is given by the following (incomplete) grammar:

\begin{align*}
  \alpha,x,h,k\in \mathrm{Ident} ::={}& \mathrm{identifiers}\\
  s \in \mathrm{Size} ::={}& 8\mid 16\mid 32\mid 64\mid \infty&&\mbox{integer bit size}\\
  t \in \mathrm{TuplePattern} ::={}& \_\mid x\mid \ghost{x}&&\mbox{ignored, variable, ghost variable}\\
    \mid{}&t:\tau \mid \langle \overline{t}\rangle&&\mbox{type ascription, tuple}\\
  R \in \mathrm{Arg} ::={}& x:\tau\mid \ghost{x}:\tau&&\mbox{regular/ghost argument}\\
  \tau\in\mathrm{Type} ::={}& \alpha&&\mbox{type variable reference}\\
    \mid{}& \core\alpha&&\mbox{moved type variable}\\
    \mid{}&\mathbf{1}\mid\top\mid \bot\mid \mathsf{bool}&&\mbox{unit, true, false, booleans}\\
    \mid{}&\N_s\mid \Z_s&&\mbox{unsigned and signed integers of different sizes}\\
    \mid{}&\tau_1\land \tau_2\mid \tau_1\ast \tau_2\mid \tau_1\lor \tau_2&&\mbox{conjunction (regular, separating), disjunction}\\
    \mid{}&\tau_1\to \tau_2\mid \tau_1 \wand \tau_2\mid \neg \tau&&\mbox{implication (regular, separating), negation}\\
    \mid{}&\forall x:\tau_1,\;\tau_2\mid \textstyle\sum x:\tau_1,\;\tau_2&&\mbox{universal, existential quantification}\\
    \mid{}& pe&&\mbox{assert that a boolean value is true}\\
    \mid{}&pe\mapsto pe'&&\mbox{points-to assertion}\\
    \mid{}&\boxed{x:\tau}&&\mbox{typing assertion}\\
    \mid{}&S(\overline{\tau},\overline{pe})&&\mbox{user-defined type}\\
\end{align*}
\begin{align*}
  pe\in \mathrm{PureExpr} ::={}&\mbox{(the first half of Expr below)}&&\mbox{pure expressions}\\
  e \in \mathrm{Expr} ::={}& x&&\mbox{variable reference}\\
    \mid{}&()\mid \mathsf{true}\mid \mathsf{false}\mid n&&\mbox{constants}\\
    \mid{}&e_1 \land e_2\mid e_1 \lor e_2\mid \neg e&&\mbox{logical AND, OR, NOT}\\
    \mid{}&e_1 \mathbin\texttt{\&} e_2\mid e_1 \mathbin\texttt{|} e_2\mid \texttt{!}_s\; e&&\mbox{bitwise AND, OR, NOT}\\
    \mid{}&e_1 + e_2\mid e_1 * e_2\mid -e&&\mbox{addition, multiplication, negation}\\
    \mid{}&e_1 < e_2\mid e_1 \le e_2\mid e_1 = e_2&&\mbox{equalities and inequalities}\\
    \mid{}&\mathsf{if}\;h^? : e_1\;\mathsf{then}\;e_2\;\mathsf{else}\;e_3&&\mbox{conditionals}\\
    \mid{}&\langle\overline{e}\rangle&&\mbox{tuple}\\
    \mid{}&f(\overline{e})&&\mbox{(pure) function call}\\[2mm]
%
    \mid{}&\mathsf{let}\ t := e_1\;\mathsf{in}\; e_2 &&\mbox{assignment to a variable}\\
    \mid{}& \eta \gets pe;\ e\mid\ghost{\eta \gets pe};\ e&&\mbox{move assignment}\\
    \mid{}&F(\overline{e})&&\mbox{procedure call}\\
    \mid{}&\mathsf{unreachable}\;e&&\mbox{unreachable statement}\\
    \mid{}&\mathsf{return}\; \overline{e}&&\mbox{procedure return}\\
    \mid{}&\mathsf{label}\;\overline{k(\overline{R}):=e}\;\mathsf{in}\;e'&&\mbox{local mutual tail recursion}\\
    \mid{}&\mathsf{goto}\;k(\overline{e})&&\mbox{local tail call}\\
    \mid{}&\mathsf{entail}\;\overline{e}\;p&&\mbox{entailment proof}\\
    \mid{}&\mathsf{assert}\;pe&&\mbox{assertion}\\
    \mid{}&\mathsf{typeof}\;pe&&\mbox{take the type of a variable}\\
  p \in \mathrm{PureProof} ::={}&\dots&&\mbox{MM0 proofs}\\
  \eta \in \mathrm{Place} ::={}& x&&\mbox{variable reference}\\
\end{align*}
\begin{align*}
  it \in \mathrm{Item} ::={}&\mathsf{type}\;S(\overline{\alpha}, \overline{R}):=\tau&&\mbox{type declaration}\\
    \mid{}&\mathsf{const}\;t:=e&&\mbox{constant declaration}\\
    \mid{}&\mathsf{global}\;t:=e&&\mbox{global variable declaration}\\
    \mid{}&\mathsf{func}\;f(\overline{R}):\overline{R}:=e&&\mbox{function declaration}\\
    \mid{}&\mathsf{proc}\;f(\overline{R}):\overline{R}:=e&&\mbox{procedure declaration}\\
\end{align*}

Missing elements of the grammar include:
\begin{itemize}
  \item Switch statements, which are desugared to if statements.
  \item Raw MM0 formulas can be lifted to the `Type' type as booleans.
  \item Raw MM0 values can be lifted into $\N_\infty$ and $\Z_\infty$.
  \item There are more operations for working with pointers and arrays. These are discussed in section \ref{sec:pointers}.
  \item There are operations for moving between typed values and hypotheses, which will be discussed later.
  \item There are also \textsf{while} loops and \textsf{for} loops, but we will focus on the general control flow of \textsf{label} and \textsf{goto}.
\end{itemize}

Language items that are considered but not present (yet) in the language include:
\begin{itemize}
  \item Functions and procedures cannot be generic over type and propositional variables. (In fact there are no propositional variables in the language, only the type Prop of propositional expressions.) A generic propositional variable is used internally to model the frame rule but it is not available to user code.
  \item Recursive and mutually recursive function support is currently very limited.
\end{itemize}
Most of the constructs are likely familiar from other languages. We will call some attention to the more unusual features:
\begin{itemize}
  \item Ghost variables $\ghost x$ are used to represent computationally irrelevant data. They can be manipulated just like regular variables, but they must not appear on the data path during code generation. We will use $x^\gamma$ to generalize over ghost and non-ghost variables, where $\gamma=\bot$ means this is a ghost variable and $\gamma=\top$ means it is not. We use $\gamma'\le \gamma$ to mean that $\gamma$ is ``more computationally relevant'' than $\gamma'$, i.e. if $x^\gamma$ is ghost then $x^{\gamma'}$ is too.

  \item The $\texttt{!}_s\; n$ operation performs the mathematical function $2^s-n-1$, taking $2^\infty=0$ so that $\texttt{!}_\infty\; n=-n-1$. $\texttt{!}_s\; n$ is used for bitwise negation of unsigned integers, and $\texttt{!}_\infty\; n$ is used for bitwise negation of signed integers (even those of finite width).
  \item The assignment operator $\mathsf{let}\ t := e_1\;\mathsf{in}\; e_2$ assigns the variables of $t$ to the result of $e_1$, but here it should be understood as a new binding, or shadowing declaration, rather than a reassignment to an existing variable. Even array assignments will be desugared into pure-functional update operations.

  The concrete version of the assignment operator also contains a ``$\mathsf{with}\ x\to y$'' clause, but this only renames variables in the source (which is to say, it changes the mapping of source names to internal names) and so is not relevant for the theoretical presentation here.

  \item The operator $x^\gamma\gets pe;\ e$ is the primitive for mutation of the variables in the context (where, as with ghost variables, we use $\gamma$ to generalize over the ghost and non-ghost versions of the operator). Intuitively, it can be thought as moving $pe$ into $x$, but it has no effect on the type context, and is only used to coordinate data flow. In the grammar the left hand side is generalized to a type of ``places'' (a.k.a lvalues), but for now these can only be variable references. For example,
  \begin{align*}
    &\qquad\mbox{this:}
      &&\!\!\!\!\!\!\mbox{has the same effect as:}
        &&\!\!\!\!\!\!\mbox{which we can $\alpha$-rename to:}\\
    &\mathsf{let}\ x := 1\;\mathsf{in}
      &&\mathsf{let}\ x := 1\;\mathsf{in}
        &&\mathsf{let}\ x := 1\;\mathsf{in}\\
    &\mathsf{let}\ y :=
      &&\mathsf{let}\ \langle x,y\rangle :=
        &&\mathsf{let}\ \langle x',y\rangle :=\\
    &\quad x \gets x+1;
      &&\quad \mathsf{let}\ x := x+1\;\mathsf{in}\;
        &&\quad \mathsf{let}\ x' := x+1\;\mathsf{in}\;\\
    &\quad {-x}\;\mathsf{in}
      &&\quad \langle x,-x\rangle\;\mathsf{in}
        &&\quad \langle x',-x'\rangle\;\mathsf{in}\\
    &e(x,y)
      &&e(x,y)
        &&e(x',y)
  \end{align*}

  \item The expression $\mathsf{label}\;\overline{k(\overline{R}):=e}\;\mathsf{in}\;e'$ is similar in behavior to a recursive let binding such as those found in functional languages, but the $\overline{k}$ are all continuations, which is to say they do not return to the caller when using $\mathsf{goto}\;l(\overline{e})$, which is how we ensure that they can be compiled to plain $\mathsf{label}$ and $\mathsf{goto}$ at the machine code level.

  \item The $\mathsf{typeof}\;pe$ operator ``moves'' a value $x:\tau$ and returns a fact $\boxed{x:\tau}$ that asserts ownership of the resources of $x$. See \ref{sec:moving}.
\end{itemize}

\section{Typing}

\subsection{Overview}

The main typing judgments are:

\begin{itemize}
  \item $\Gamma \proves t:\tau \Rightarrow \overline{R}$\\ types a tuple pattern against a value of type $\tau$, producing additional hypotheses $\overline{R}$ that will enter the context
  \item $\Gamma \proves \tau\;\mathsf{type}$\\ determines that a type $\tau$ is a valid type in the current context
  \item $\Gamma \proves R\;\mathsf{arg}$\\ determines that $R$ is a valid argument extending the current context
  \item $\Gamma;\delta \proves e:\tau \makes\delta'$\\ determines that $e$ is a valid expression of type $\tau$, which modifies the value context from $\delta$ to $\delta'$. In the special case where $\delta'=\delta$, we will write $\Gamma;\delta \proves e:\tau$ instead.
  \item $\Gamma;\delta \proves e\Rightarrow pe:\tau \makes\delta'$\\ is the same as the previous, but additionally says that the returned value can be expressed as the pure expression $pe$ in context $\Gamma$.
  \item $\Gamma\proves \delta$ means that $\delta$ is a valid value context.
  It is defined as: if $(x:=pe:\tau)\in\delta$ then $\Gamma\proves pe:\tau$ and $x\in\mathrm{Dom}(\Gamma)$, and if $(x\to y)\in\delta$ then $x,y\in\mathrm{Dom}(\Gamma)$.
  \item $\Gamma \proves pe:\tau$\\ The typing rule for pure expressions, which does not depend on the value context.
  \item $\Gamma \constep \Gamma'$\\ an auxiliary judgment for applying pending mutations to the context.
  \item $\Gamma\proves it\;\mathsf{ok}$\\ The top level item typing judgment
\end{itemize}

Central to all of these judgments is the context $\Gamma$, which consists of:
\begin{itemize}
  \item The global environment of previously declared items, including in particular a record $\mathsf{self}(\bar R):\bar S$ recording the type of the function being typechecked (if a function/procedure is being checked). This doesn't change during expression typing.
  \item A list of type variables $\overline{\alpha}$. This is only nonempty when type checking a type declaration.
  \item A list of declared jump targets $\overline{k(\delta,\bar{R})}$, including a special jump target $\mathsf{return}(\bar{R})$ where $\bar{R}$ is the declared return type. The $\delta$ in each jump target is the context required for that jump to typecheck; it lies somewhere between the initial context $\delta$ at the point of the $\mathsf{label}$, and the moved-out context $\core{\delta}$.
  \item A list of logical variables $x:\core{\tau}$ with their types. Here $\core{\tau}$ is used to indicate that while the type $\tau$ itself is recorded, it is only accessible in ``moved'' form.
\end{itemize}

The type variables don't depend on anything and cannot be introduced in the middle of an item, so these can be assumed to come first, but jump targets can depend on regular variables. We use the notation $\Gamma,\overline{k(\bar{R})}$ and $\Gamma,\overline{R}$ to denote extension of the context with a list of jump targets or variables, respectively, and $\Gamma,x\gets pe:\tau$ to denote the insertion of $x\gets pe:\tau$ into the list of mutations, replacing $x\gets pe':\tau'$ if it is present.

The secondary context used in the typing rule $\Gamma,\delta \proves e:\tau \makes\delta'$ for expressions is the ``value context'', which contains the actual current value of variables in the context. It has two components:

\begin{itemize}
\item A list of records of the form $x:=pe:\tau$, which represent the ``actual resources'' associated to a variable $x$. Note that $x$ need not be in the context, but $\Gamma\proves pe:\tau$ so all variables in $pe$ must be in the context. For function arguments and other variables with no known value, we use $x:\tau$, a shorthand for $x:=x:\tau$, where $(x:\core\tau)\in\Gamma$.
\item A rename map, which is a list of records of the form $x\to y$ where $x$ and $y$ are variables which are either in the context or in the value context. This keeps track of what a variable's ``current name'' is, after some number of renames. When a block ends, the values associated to renamed variables become the initial values of variable names in the code following the block.

A variable can only be renamed once, and it is always renamed to a fresh variable; this means that the rename map is an injective partial function, i.e., if $x\to y,y'$ then $y=y'$ and if $x,x'\to y$ then $x=x'$.
\end{itemize}

\subsection{Moving types}\label{sec:moving}

The last essential element to understand the typing rules is the ``moved'' modality on types, denoted $\core\tau$. For separating propositions this is also known as the persistence modality, and it represents what is left of a proposition after all the ``ownership'' is removed from it. We use moved types to represent a value that has been accessed. This satisfies the axioms $\core{\core\tau}=\core\tau$ and $A\Leftrightarrow A\ast\core A$. We extend this to arbitrary arguments and contexts $\core R$ and $\core\Gamma$ by applying the modality to all contained types.

A type is called ``$\mathsf{copy}$'' or persistent if $\core\tau=\tau$, and is denoted $\tau\;\mathsf{copy}$.

The moved modality is defined like so:
\begin{align*}
  \mathbf{1},\top,\bot,\mathsf{bool},\N_s, \Z_s,pe&\;\mathsf{copy}\\
  \core{\tau_1\land \tau_2}={}&\core{\tau_1}\land \core{\tau_2}\\
  \core{\tau_1\lor \tau_2}={}&\core{\tau_1}\lor \core{\tau_2}\\
  \core{\tau_1\ast \tau_2}={}&\core{\tau_1}\ast \core{\tau_2}\\
  \core{\textstyle\sum x:\tau_1,\;\tau_2}={}&\textstyle\sum x:\core{\tau_1},\;\core {\tau_2}\\
  \core{S(\overline{\tau},\overline{pe})}={}&\core{S}(\overline{\tau},\overline{pe})\qquad\mbox{(that is, the effect of moving $S$ is precalculated)}\\
  \core{pe\mapsto pe'}={}&\top\\
  \core{\boxed{x:\tau}}={}&\boxed{x:\core{\tau}}\\
  \core{\forall x:\tau,\;\tau}={}& \begin{cases}
    \forall x:\tau,\;\core \tau&\mbox{if $\tau\;\mathsf{copy}$}\\
    \top&o.w.\\
  \end{cases}\\
  \core{\tau\to \tau'}={}& \begin{cases}
    \tau\to \core{\tau'}&\mbox{if $\tau\;\mathsf{copy}$}\\
    \top&o.w.\\
  \end{cases}\\
  \core{\tau\wand \tau'}={}& \begin{cases}
    \tau\wand \core{\tau'}&\mbox{if $\tau\;\mathsf{copy}$}\\
    \top&o.w.\\
  \end{cases}\\
  \core{\neg \tau}={}& \begin{cases}
    \neg \tau&\mbox{if $\tau\;\mathsf{copy}$}\\
    \top&o.w.\\
  \end{cases}\\
\end{align*}
Because moving is monotonic, that is $A\Rightarrow \core A$ but not the other way around, negative uses of a non-persistent proposition cause it to completely collapse to $\top$ when moved.

When we get to pointer types in section \ref{sec:pointers} we will see that $\core{\&^\mathbf{own}\tau}=\core{\&^\mathbf{mut}\tau}=\N_{64}$, so pointers become ``mere integers'' after they are moved away. (Note, however, that they actually retain their original types for type inference purposes; that is, the typechecker remembers that they have type $\core{\&^\mathbf{own}\tau}$ in order to determine the type that would result from dereferencing the pointer, if it were still valid.)

Note that move commutes with substitution for (expression) variables, $\core{\tau}[e/x]=\core{\tau[e/x]}$, but it only partially commutes with substitution for type variables: $\core{\tau[\tau'/\alpha]}\Rightarrow\core{\tau}[\tau'/\alpha]$, because substitution can make a non-copy type copy, so that for example $\core{\alpha[\N/\alpha]}=\core{\N}=\N$ but $\core{\alpha}[\N/\alpha]=\top[\N/\alpha]=\top$.

\subsection{The Typing Rules}

We now give the main typing rules for the logic. This corresponds roughly to the \texttt{typeck} phase of the compiler. Note that ghost variable markings are ignored during this phase; they will come back during the ghost propagation phase.

\judgment[Tuple pattern typing]{\Gamma \proves t:\tau \Rightarrow \overline{R}}
\begin{mathparpagebreakable}
  \axiom[tp-ignore]{\Gamma \proves \_:\tau\Rightarrow \cdot}\and
  \axiom[tp-var]{\Gamma \proves x^\gamma:\tau\Rightarrow x:\tau}\and
  \infer[tp-typed]
    {\Gamma \proves t:\tau\Rightarrow \overline{R}}
    {\Gamma \proves (t:\tau):\tau\Rightarrow \overline{R}}\and
  \infer[tp-sum]
    {\Gamma \proves t:\textstyle \tau\Rightarrow \bar{S}\quad
      \Gamma,\bar{S} \proves t':\textstyle \tau'[t/x]\Rightarrow \bar{S}'}
    {\Gamma \proves \langle t,t'\rangle:\textstyle\sum x:\tau,\tau'\Rightarrow \bar{S},\bar{S}'}\and
  \infer[tp-sep]
    {\forall i,\ \ \Gamma \proves t_i:\tau_i\Rightarrow(\bar{R})_i}
    {\Gamma \proves \langle \overline{t}\rangle:\textstyle\Sep\overline{\tau}\Rightarrow \overline{\bar{R}}}\and
  \infer[tp-and]
    {\forall i,\ \tau_i\;\mathsf{copy}\quad
      \forall i,\ \Gamma \proves t_i:\tau_i\Rightarrow(\bar{R})_i}
    {\Gamma \proves \langle \overline{t}\rangle:\textstyle\bigwedge\overline{\tau}\Rightarrow \overline{\bar{R}}}
\end{mathparpagebreakable}

The only really relevant rules here for expressiveness are the \textsc{tp-var} and \textsc{tpp-var} rules; the rest are convenience rules for being able to destructure a type or proposition into components using the tuple pattern. For notational simplicity we show the \textsc{tp-sum} rule in iterative form, but it actually matches an $n$-ary tuple against an $n$-ary struct type in one go.

In the \textsc{tp-sum} and \textsc{tpp-ex} rules, we use $\overline{R}[t/x]$ to denote the result of substituting $t$ for $x$ in $R$. For this to work, $t$ must be reified as a tuple of variables rather than simply a destructuring pattern, which in particular means that `$\_$' ignore patterns are interpreted as inserting internal variables with no user-specified name rather than being omitted from the context entirely as the \textsc{tp-ignore} rule would suggest.

\judgment[Argument typing]{\Gamma \proves R\;\mathsf{arg}}
\begin{mathparpagebreakable}
  \infer[arg-type]
    {\Gamma \proves \tau\;\mathsf{type}}
    {\Gamma \proves x:\tau\;\mathsf{arg}}
\end{mathparpagebreakable}
This one is simple so we get it out of the way first. We will avoid dealing with variable shadowing rules here; suffice it to say that variables in the context must always be distinct, and we will perform renaming from the surface syntax to ensure this property when necessary.

\judgment[Type validity]{\Gamma \proves \tau\;\mathsf{type}}
\begin{mathparpagebreakable}
  \axiom[ty-unit]{\Gamma \proves \mathbf{1}\;\mathsf{type}}\and
  \axiom[ty-true]{\Gamma \proves \top\;\mathsf{type}}\and
  \axiom[ty-false]{\Gamma \proves \bot\;\mathsf{type}}\and
  \axiom[ty-bool]{\Gamma \proves \mathsf{bool}\;\mathsf{type}}\and
  \axiom[ty-nat]{\Gamma \proves \N_s\;\mathsf{type}}\and
  \axiom[ty-int]{\Gamma \proves \Z_s\;\mathsf{type}}\\
  \infer[ty-var]
    {\alpha\in\Gamma}
    {\Gamma \proves \alpha\;\mathsf{type}}\and
  \infer[ty-core-var]
    {\alpha\in\Gamma}
    {\Gamma \proves \core\alpha\;\mathsf{type}}\and
  \infer[ty-pure]
    {\Gamma \proves pe:\mathsf{bool}}
    {\Gamma \proves pe\;\mathsf{type}}\and
  \infer[ty-not]
    {\Gamma \proves \tau\;\mathsf{type}}
    {\Gamma \proves \neg \tau\;\mathsf{type}}\and
  \infer[ty-and]
    {\Gamma \proves \tau\;\mathsf{type}\and
      \Gamma \proves \tau'\;\mathsf{type}}
    {\Gamma \proves \tau\land \tau'\;\mathsf{type}}\and
  \infer[ty-or]
    {\Gamma \proves \tau\;\mathsf{type}\and
      \Gamma \proves \tau'\;\mathsf{type}}
    {\Gamma \proves \tau\lor \tau'\;\mathsf{type}}\and
  \infer[ty-sep]
    {\Gamma \proves \tau\;\mathsf{type}\and
      \Gamma \proves \tau'\;\mathsf{type}}
    {\Gamma \proves \tau\ast \tau'\;\mathsf{type}}\and
  \infer[ty-wand]
    {\Gamma \proves \tau\;\mathsf{type}\and
      \Gamma \proves \tau'\;\mathsf{type}}
    {\Gamma \proves \tau\wand \tau'\;\mathsf{type}}\and
  \infer[ty-all]
    {\Gamma \proves \tau\;\mathsf{type}\quad
      \Gamma,x:\core\tau \proves \tau\;\mathsf{type}}
    {\Gamma \proves \forall x:\tau,\;\tau\;\mathsf{type}}\and
  \infer[ty-sum]
    {\Gamma \proves \tau\;\mathsf{type}\quad
      \Gamma,x:\core\tau \proves \tau\;\mathsf{type}}
    {\Gamma \proves \textstyle\sum x:\tau,\;\tau\;\mathsf{type}}\and
  \infer[ty-points-to]
    {\Gamma \proves \ell:\mathsf{\N_{64}}\quad
      \Gamma \proves v:\mathsf{\core\tau}}
    {\Gamma \proves \ell\mapsto v\;\mathsf{type}}\and
  \infer[ty-typing]
    {\Gamma \proves x:\core\tau\quad
      \Gamma \proves \tau\;\mathsf{type}}
    {\Gamma \proves \boxed{x:\tau}\;\mathsf{type}}\and
  \infer[ty-user]
    {\mathsf{type}\;S(\overline{\alpha}, \overline{R})\quad
      \forall i,\ \Gamma \proves \tau_i\;\mathsf{type}\quad
      \Gamma \proves \langle \overline{pe}\rangle:\textstyle\sum\overline{R}[\overline{\tau}/\overline{\alpha}]}
    {\Gamma \proves S(\overline{\tau},\overline{pe})\;\mathsf{type}}\and
\end{mathparpagebreakable}

Type validity is also relatively straightforward. Type variables are looked up in the context, and structs can have dependent types, but the only way dependencies can appear is through \textsc{ty-array} (which will appear later), which can have a natural number size bound, and in hypotheses via \textsc{ty-pure}.

There is nothing non-standard in these rules, except perhaps the requirement in the \textsc{typ-forall} and \textsc{typ-exists} rules that the types are moved (needed because the assertion language itself should not be able to take ownership of variables used in the assertions).

The most interesting rule is \textsc{typ-typing}, which describes the typing assertion $\boxed{x:\tau}$. One should think of $x:\tau$ in the context as a separating conjunction of $x:\core\tau$ (which asserts, roughly, that $x$ is a reference to some data in the stack frame that is a valid bit-pattern for type $\tau$), plus the ``fact'' $h:\boxed{x:\tau}$, which represents ownership of all the resources that $x$ may point to. For example, if $x:\&^\mathbf{own}\tau$, then $x$ is itself just a number, but $\boxed{x:\&^\mathbf{own}\tau}$ is equal to $\exists v:\tau,\ x\mapsto v$, saying that $x$ points to some data $v$, and $v:\tau$ may itself own some portion of the heap.

\subsection{Expression typing}

The typing rules for expressions make use of the following operators on contexts:

\begin{itemize}
  \item $\Gamma_{\core x}$ ``moves'' $x$ out of the context, by replacing $x:\tau$ with $x:\core\tau$. This does not invalidate the well formedness of any type, proposition, or pure expression.
\end{itemize}

The rules for pure expression typing are the same as for regular expression typing, although since all the pure expression constructors do not change the context, they are all of the form $\Gamma\proves pe:\tau\makes \Gamma$, which we abbreviate as $\Gamma\proves pe:\tau$.

Note that the \textsc{tye-var-ref} rule ignores the effect of mutations. This is necessary so that new mutations do not cause the context to become ill-typed. Instead, mutations are applied in the translation from surface syntax, so that ``\texttt{x <- 1; x + x}'' is elaborated into ``$x\gets 1;\;1+1$'', while ``$x\gets 1;\ x+x$'' in the core logic means that the $x$ being referred to is the one before the mutation. The surface syntax uses ``\texttt{with x -> y}'' annotations on mutations to allow referencing both the old and new versions of the variable.

\judgment[Expression validity (pure expressions)]{\Gamma \proves pe:\tau}
\begin{mathparpagebreakable}
  \infer[tye-var-ref]
    {(x:\core\tau)\in\Gamma}
    {\Gamma \proves x:\tau}\and
  \axiom[tye-unit]{\Gamma \proves ():\mathbf{1}}\and
  \axiom[tye-true]{\Gamma \proves \mathsf{true}:\mathsf{bool}}\and
  \axiom[tye-false]{\Gamma \proves \mathsf{false}:\mathsf{bool}}\and
  \infer[tye-nat]
    {0\le n\quad s<\infty\to n<2^s}
    {\Gamma \proves n:\N_s}\and
  \infer[tye-int]
    {s<\infty\to -2^{s-1}\le n<2^{s-1}}
    {\Gamma \proves n:\Z_s}\and
  \infer[tye-tuple]
    {\forall i<n,\ \ \Gamma_i \proves e_i:\tau\makes\Gamma_{i+1}}
    {\Gamma_0 \proves \langle\overline{e}\rangle:\textstyle\Sep\tau\makes\Gamma_n}\and
  \infer[tye-not]
    {\Gamma \proves e:\mathsf{bool}}
    {\Gamma \proves \neg e:\mathsf{bool}}\and
  \infer[tye-and, tye-or]
    {\Gamma \proves e_1:\mathsf{bool}\quad
      \Gamma_1 \proves e_2:\mathsf{bool}}
    {\Gamma \proves e_1\land e_2:\mathsf{bool} \quad
      \Gamma \proves e_1\lor e_2:\mathsf{bool}}\and
  \infer[tye-band, tye-bor]
    {\tau\in\{\N_s,\Z_s\}\quad
      \Gamma \proves e_1:\tau\quad
      \Gamma_1 \proves e_2:\tau}
    {\Gamma \proves e_1\mathrel{\texttt{\&}} e_2:\tau \quad
      \Gamma \proves e_1\mathrel{\texttt{|}} e_2:\tau}\and
  \infer[tye-bnot]
    {\tau=\N_s\lor (\tau=\Z_{s'}\land s=\infty)\quad
      \Gamma \proves e:\tau}
    {\Gamma \proves \texttt{!}_s\;e:\tau}\and
  \infer[tye-lt, tye-le, tye-eq]
    {\tau,\tau'\in\{\N_s,\Z_s\}\qquad
      \Gamma \proves e_1:\tau\qquad
      \Gamma \proves e_2:\tau'}
    {\Gamma \proves e_1< e_2:\mathsf{bool}\quad
      \Gamma \proves e_1\le e_2:\mathsf{bool}\quad
      \Gamma \proves e_1= e_2:\mathsf{bool}}\and
  \infer[tye-if]
    {\Gamma \proves c:\mathsf{bool}\quad
      \Gamma \proves e_1:\tau\quad
      \Gamma \proves e_2:\tau}
    {\Gamma \proves (\mathsf{if}\;c\;\mathsf{then}\;e_1\;\mathsf{else}\;e_2):\tau}\and
  \infer[tye-struct]
    {\Gamma \proves e:\tau\quad
      \Gamma \proves \langle \overline{e}\rangle:\textstyle\sum \bar R[e/x]}
    {\Gamma \proves \langle e,\overline{e}\rangle:\textstyle\sum x:\tau,\bar R}\and
  \infer[tye-func-call]
    {\mathsf{func}\;f(\overline{R}):\overline{S}\quad
      \Gamma \proves \langle\overline{e}\rangle:\textstyle\sum\bar R}
    {\Gamma \proves f(\overline{e}):\textstyle\sum\bar S}\and
\end{mathparpagebreakable}

The rules above are the only ones that apply to pure expressions. General expressions have additional typing rules for the other constructions, continued below.

For general expressions, we must worry about the following additional effects:
\begin{itemize}
  \item Variables in the context can be moved by their being referenced (in the \textsc{tye-var-move} rule).
  \item Varables can be changed using no-op rules (the \textsc{tye-cs-left} and \textsc{tye-cs-right} rules). We will return to this in section \ref{sec:noop}.
\end{itemize}

\judgmentB[Expression validity]{\Gamma;\delta \proves e:\tau\makes\delta'}{\Gamma;\delta \proves e\Rightarrow pe:\tau\makes\delta'}
\begin{mathparpagebreakable}
  \infer[tye-cs-left]
    {\Gamma;\delta\constep\delta_1\quad \Gamma;\delta_1\proves e\Rightarrow pe^?:\tau\makes\delta_2}
    {\Gamma;\delta\proves e\Rightarrow pe^?:\tau\makes\delta_2}\and
  \infer[tye-cs-right]
    {\Gamma;\delta\proves e\Rightarrow pe^?:\tau\makes\delta_1\quad \Gamma;\delta_1\constep\delta_2}
    {\Gamma;\delta\proves e\Rightarrow pe^?:\tau\makes\delta_2}\and
  \axiom[tye-var-move]{\Gamma;\delta,x:=pe:\tau \proves x\Rightarrow pe:\tau\makes\delta,x:=pe:\core\tau}\and
  \infer[tye-mut]
    {\!\:{\Gamma;\delta \proves e_1\Rightarrow pe:\tau\makes\delta_1\quad
      \forall z,\ (x\to z)\notin \delta_1\quad
      \Gamma\proves \delta_2\atop
      \Gamma;\delta_1,(x\to y),(y:=pe:\tau)\proves e_2:\tau'\makes\delta_2\quad y\notin \delta_2}}
    {\Gamma;\delta \proves (x^\gamma\gets e_1\ \mathsf{with}\ y\gets x;\ e_2):\tau'\makes\delta_2}\and
  \infer[tye-let-pure]
    {\!\:{\Gamma;\delta \proves e_1\Rightarrow pe:\tau\makes\delta_1\quad
      \Gamma\proves\tau',\delta_2\atop
      \Gamma,x:\core\tau;\delta_1,x:=pe:\tau \proves e_2:\tau'\makes\delta_2}}
    {\Gamma;\delta \proves (\mathsf{let}\ x^\gamma := e_1\;\mathsf{in}\; e_2):\tau'\makes\delta_2}\and
  \infer[tye-unreachable]
    {\Gamma;\delta \proves e:\bot\makes\delta_1\quad \Gamma\proves \delta_2}
    {\Gamma;\delta \proves \mathsf{unreachable}\;e:\tau\makes\delta_2}\and
  \infer[tye-let]
    {\!\:{\Gamma;\delta \proves e_1:\tau\makes \delta_1\quad
      \Gamma \proves t:\tau\Rightarrow \overline{R}\quad
      \Gamma \proves \tau',\delta_2\atop
      \Gamma,\overline{\core{R}};\delta_1,\overline{R} \proves e_2:\tau'\makes\delta_2}}
    {\Gamma;\delta \proves (\mathsf{let}\ t := e_1\;\mathsf{in}\; e_2):\tau'\makes\delta_2}\and
  \infer[tye-proc-call]
    {\mathsf{proc}\;F(\overline{R}):\overline{S}\quad
      \Gamma;\delta \proves \langle\overline{e}\rangle:\textstyle\sum\bar R\makes\delta'}
    {\Gamma;\delta \proves F(\overline{e}):\textstyle\sum\bar S\makes\delta'}\and
  \infer[tye-return]
    {\mathsf{self}(\bar R):\bar S\quad
      \Gamma;\delta \proves \langle\overline{e}\rangle:\textstyle\sum\bar S\makes \delta'}
    {\Gamma;\delta \proves \mathsf{return}\;\overline{e}:\bot\makes\delta'}\and
  \infer[tye-label]
    {\!\:{\forall i,\ \Gamma,\overline{k(\delta;\bar{R})},(\bar{R})_i;\delta_i,(\bar{R})_i \proves e_i:\bot\makes\delta^2_i\atop
      \Gamma,\overline{k(\delta;\bar{R})};\delta^0\proves e':\tau\makes\delta^1}}
    {\Gamma;\delta^0 \proves (\mathsf{label}\;\overline{k(\bar{R}):=e}\;\mathsf{in}\;e'):\tau\makes\delta^1}\and
  \infer[tye-goto]
    {\!\:{k(\delta';\bar{R})\in\Gamma\atop
      \Gamma;\delta \proves \langle\overline{e}\rangle:\textstyle\sum\bar R\makes\delta'}}
    {\Gamma;\delta \proves \mathsf{goto}\;k(\overline{e}):\bot\proves \delta'}\and
  \infer[tye-assert]
    {\Gamma;\delta \proves e\Rightarrow pe:\mathsf{bool} \makes \delta'}
    {\Gamma;\delta \proves \mathsf{assert}\;e:pe\makes\delta'}\and
  \infer[tye-typeof]
    {\Gamma;\delta \proves e\Rightarrow pe:\tau \makes \delta'}
    {\Gamma;\delta \proves \mathsf{typeof}\;e:\boxed{pe:\tau}\makes\delta'}\and
  \infer[tye-entail]
    {\Gamma;\delta \proves \langle\overline{e}\rangle:\textstyle\Sep\overline{A} \makes \delta'\quad
      \proves p:\textstyle\Sep\overline{A}\wand B}
    {\Gamma;\delta \proves \mathsf{entail}\;\overline{e}\;p:B\makes\delta'}\and
\end{mathparpagebreakable}

Proofs are essentially (effectful) expressions with proposition type, so the rules look much the same. Pure proofs are simply imported from the MM0 logical enironment so we do not discuss them here. The main job of Metamath C is to make sure that these pure proofs have simple types, not using the entire context, since the user will be directly interacting with them.

\subsection{No-op steps}\label{sec:noop}

In addition to being able to step as a result of executing some expression, we also need the ability to step without anything happening physically. This is primarily needed in order to clean up the context to eliminate a variable, or to merge control flow to a common context, i.e. after the branches of an \textsf{if} statement, and at a \textsf{return} and \textsf{goto}. It is also used whenever the context has to drop a variable, such as after a $\mathsf{let}$ expression completes.

The rules given below are not deterministic, but they are used whenever we can't otherwise make progress. Using them too much may end up in a state where a variable is missing, causing later typechecking to fail, so the compiler will try to apply these only as necessary.

\judgment[No-op step]{\Gamma;\delta \constep\delta'}
\begin{mathparpagebreakable}
  \axiom[cs-refl]{\Gamma;\delta \constep \delta}\and
  \infer[cs-trans]
    {\Gamma;\delta_1 \constep \delta_2 \quad \Gamma;\delta_2 \constep \delta_3}
    {\Gamma;\delta_1 \constep \delta_3}\and
  \infer[cs-drop]
    {\forall x,(x\to y)\notin \delta}
    {\Gamma;\delta,(y:=pe:\tau)\constep\delta}\and
  \axiom[cs-rename\footnotemark]
    {\Gamma;\delta,(x\to y),(y:=pe:\tau)\constep\delta,(x:=pe:\tau)}\and
  \footnotetext{The \textsc{cs-rename} rule should only be used if it is the only way to make progress, i.e. when $y$ is going out of scope. This is needed because it changes the interpretation of expressions containing $x$.}
  \infer[cs-forget]
    {\Gamma\proves\Gamma[\overline{x\to pe}]}
    {\Gamma;\delta,\overline{x:=pe:\tau}\constep\delta,\overline{x:\tau}}\and
\end{mathparpagebreakable}

This is a nondeterministic judgment, with the ``goal'' being to eliminate a particular variable and/or join with separate control flow which has assigned different values to the variables.
\begin{itemize}
\item The simplest way to drop a variable is with the \textsc{cs-drop} rule, which works as long as this is a variable that was not obtained from a mutation.
\item For variables that are obtained by mutation, we have a $x\to y$ in the context, and we can drop its value while storing the result back in the original variable using the \textsc{cs-rename} rule.
\item In order to join control flow, we also need to ``forget'' the value associated with a variable. For example, if one branch of an if statement sets $x\gets 1$ and the other sets $x\gets 2$, we are allowed to use these settings inside the blocks of the if statement but at the end they must agree about the setting of the variable as well as its properties. For this we use the \textsc{cs-forget} rule, which erases the information that $x:=pe$ for several variables at once. This existentially quantifies over the variables $\overline{x}$ and reintroduces them so that we no longer have access to the value. For this to be sound, we have a side condition that says that the context remains true if we replace $\overline{x}$ with $\overline{pe}$, because the actual assignments to the variables in $\Gamma$ have changed even though we are keeping the same type.
\end{itemize}

To see how this plays out, consider the code
$$x:=0,h:x\ge 0\proves\mathsf{if}\ b\ \{\ x\gets 1\ \},$$
which desugars to ``$\mathsf{if}\ b\ \mathsf{then}\ x^\top\gets 1;\ ()\ \mathsf{else}\ ()$''. After the mutation, we have $x\to x',x':=1$ so we can apply \textsc{cs-rename} to get $x:=1$. But the else branch has $x:=0$ so we can't merge just yet. We can apply \textsc{cs-forget} to forget $x$, because $x:\N,h:x\ge 0\proves 1:\N,1\ge 0$, provided the compiler knows how to synthesize these proofs. (The proof of $1:\N$ is already supplied by $x\gets 1:\N$, but $1\ge 0$ is not immediately available.) If the compiler cannot find this proof, it can be supplied by:
$$x:=0,h:x\ge 0\proves\mathsf{if}\ b\ \{\ x\gets 1;\ h\gets (p:1\ge 0)\ \},$$
where $p$ is a proof of $1\ge 0$. In this case, we are using \textsc{cs-rename} on $x$ and $h$ simultaneously, so the side goal is the same but we get the $1\ge 0$ goal for free from the typing condition on $h:=(p:1\ge 0)$.

\subsection{Top level typing}

The full program consists of a list of top level items, which are typechecked incrementally:

\judgment[AST typing]{\Gamma\proves \overline{it}\makes \Gamma'}
\begin{mathparpagebreakable}
  \axiom[ok-zero]{\Gamma\proves \cdot\makes \Gamma}\and
  \infer[ok-append]
    {\Gamma\proves \overline{it}\makes \Gamma'\quad
      \Gamma'\proves it\makes \Gamma''}
    {\Gamma\proves \overline{it},it'\makes \Gamma''}\and
\end{mathparpagebreakable}
Individual items are typed as follows:
% it \in \mathrm{Item} ::={}&\mathsf{type}\;S(\overline{\alpha}, \overline{R}):=\tau&&\mbox{type declaration}\\
% \mid{}&\mathsf{const}\;t:=e&&\mbox{constant declaration}\\
% \mid{}&\mathsf{global}\;t:=e&&\mbox{global variable declaration}\\
% \mid{}&\mathsf{func}\;f(\overline{R}):\overline{R}:=e&&\mbox{function declaration}\\
% \mid{}&\mathsf{proc}\;f(\overline{R}):\overline{R}:=e&&\mbox{procedure declaration}\\

\judgment[Item typing]{\Gamma \proves it\makes \Gamma'}
\begin{mathparpagebreakable}
  \infer[ok-type]
    {\Gamma,\overline{\alpha} \proves \textstyle\sum\overline{R}\;\mathsf{type}\quad
      \Gamma,\overline{\alpha},\overline{R}\proves\tau\;\mathsf{type}}
    {\Gamma \proves \mathsf{type}\;S(\overline{\alpha}, \overline{R}):=\tau\makes \Gamma,\ \mathsf{type}\;S(\overline{\alpha}, \overline{R}):=\tau}\and
  \infer[ok-const]
    {\Gamma\proves pe:\tau\quad
     \Gamma\proves t:\tau\Rightarrow \bar R}
    {\Gamma \proves \mathsf{const}\;t:=pe\makes \Gamma,\bar R}\and
  \infer[ok-global]
    {\Gamma\proves e:\tau\makes \Gamma'\quad
     \Gamma'\proves t:\tau\Rightarrow \bar R}
    {\Gamma \proves \mathsf{global}\;t:=e\makes \Gamma',\bar R}\and
  \infer[ok-func, ok-proc]
    {\mathbf{kw}\in\{\mathsf{func},\mathsf{proc}\}\quad \Gamma\proves \textstyle\sum\overline{R}\;\mathsf{type}\quad
      \Gamma,\overline{R} \proves\textstyle\sum\overline{S}\;\mathsf{type}\quad
      \Gamma,(\mathsf{self}(\overline{R}):\overline{S}),\overline{R};\ \overline{R} \proves e:\bot\makes\delta}
    {\Gamma \proves \mathbf{kw}\;f(\overline{R}):\overline{S}:=e\makes \Gamma',\ \mathbf{kw}\;f(\overline{R}):\overline{S}}\and
\end{mathparpagebreakable}

\subsection{Uninitialized data}

The approach for handling mutation also cleanly supports uninitialized data. We extend the language as follows:

\begin{mathparpagebreakable}
  \mathrm{Type}::=\dots\mid \tau^?\and
  \mathrm{Expr}::=\dots\mid \mathsf{uninit}\and
  \core{\tau^?}=\core\tau^?\and
  \boxed{x:\tau^?}=\top\\
  \infer[ty-maybe]
    {\Gamma \proves \tau\;\mathsf{type}}
    {\Gamma \proves \tau^?\;\mathsf{type}}\and
  \infer[tye-uninit]
    {\Gamma \proves \tau\;\mathsf{type}}
    {\Gamma;\delta \proves \mathsf{uninit}:\tau^?\makes\delta}\and
\end{mathparpagebreakable}

That's it. Note that $\tau\le \tau^?$ because the typing predicate of $\tau^?$ is $\top$, so we can always satisfy the side condition of \textsc{cs-forget} when performing a strong update of $x:\tau^?$ to $\tau$ when we initialize it.
\subsection{Pointers}\label{sec:pointers}

Thus far the rules have only talked about local variables and mutation of local variables, that we think of as being on the stack frame of the function. To understand the representation of pointers in the type system, it will help to understand the way contexts are modeled as separating propositions. The context is a large separating conjunction of $\boxed{x:\tau}$ assertions for every $(x:\tau)\in\Gamma$ and $A$ for every $h:A$, plus additional ``layout'' information about the relation of non-ghost variables to the stack frame that will be calculated in the layout pass (see section \ref{sec:layout}).

\subsubsection{Singleton pointers}

The simplest pointer type is $\&^\mathbf{sn}\eta$. $x:\&^\mathbf{sn}\eta$ simply means that $x$ is a pointer that points to $\eta$, which is a ``place'', a writable location. $\boxed{x:\&^\mathbf{sn}\eta}=\eta\mathrel{@}x$, where $\eta\mathrel{@}x$ means that $\eta$ is stored in memory at location $x$; see section \ref{sec:semantics}. (This is not the same as $x\mapsto\eta$, because $\eta$ is a place, i.e. a direct reference to a variable in the context, not a value.) This predicate is duplicable, so $\&^\mathbf{sn}\eta$ is \textsf{copy} (and coercible to $\N_{64}$). We add the following:

\begin{mathparpagebreakable}
  \mathrm{Type}::=\dots\mid \&^\mathbf{sn}\eta\and
  \mathrm{Expr}::=\dots\mid {}^\ast e\mid \& e\and
  \&^\mathbf{sn}\eta\;\mathsf{copy}\and
  \boxed{x:\&^\mathbf{sn}\eta}=\eta\mathrel{@}x\\
  \infer[ty-snp]
    {\Gamma \proves \eta\;\mathsf{place}}
    {\Gamma \proves \&^\mathbf{sn}\eta\;\mathsf{type}}\and
  \infer[tye-deref]
    {\Gamma;\delta \proves e:\&^\mathbf{sn}\eta\makes\delta'}
    {\Gamma;\delta \proves {}^* e\Rightarrow \eta\makes\delta'\;\mathsf{place}}\and
  \infer[tye-shr]
    {\Gamma;\delta \proves e\Rightarrow\eta\makes\delta'\;\mathsf{place}}
    {\Gamma;\delta \proves \&e:\&^\mathbf{sn}\eta\makes\delta'}\and
\end{mathparpagebreakable}
To use these generalized lvalues, we need operations to read and write them:

\begin{mathparpagebreakable}
  \infer[tye-read]
    {\!\:{\Gamma;\delta \proves e\Rightarrow\eta\makes\delta_1\;\mathsf{place}\atop
      \Gamma;\delta_1\proves \eta\Rightarrow pe:\tau\makes\delta_2}}
    {\Gamma;\delta \proves e\Rightarrow pe:\tau\makes\delta_2}\and
  \infer[tye-write]
    {\!\:{\Gamma;\delta \proves e\Rightarrow\eta\makes\delta_1\;\mathsf{place}\atop
      \Gamma;\delta_1 \proves (\eta\gets pe;\ e_2):\tau\makes\delta_2}}
    {\Gamma \proves (e\gets pe;\ e_2):\tau\makes\delta_2}\and
\end{mathparpagebreakable}

We needed two new judgments above, $\Gamma \proves \eta\;\mathsf{place}$, which asserts that $\eta$ is a place in the context, and $\Gamma;\delta \proves e\Rightarrow\eta\makes\delta'\;\mathsf{place}$ which asserts that $e$ evaluates as an lvalue to place $\eta$ (which may require transforming the code to add a temporary variable). The simplest example of a place is a variable $x\in\Gamma$, but one can also take a subpart of a struct or a slice of an array. However, note that ${}^*e$ is a place expression but not a place value; it evaluates according to \textsc{tye-deref}.

Note that writing to a place as in \textsc{tye-write} changes the type $\&^\mathbf{sn}\eta$ to $\&^\mathbf{sn}\eta'$ (it rewrites all occurrences of one with the other in the context), if $\eta'$ is the renamed place after the mutation. This is because the pointer has not changed, but the data being pointed to has been updated, so we should now retrieve the new value, not the (ghost) old value.

\subsubsection{Owned pointers}

An owned pointer is fairly simple. We define $\boxed{x:\&^\mathbf{own}\tau}$ as $\exists v:\tau,\ x\mapsto v$, but we can't directly dereference an owned pointer as we must first have access to the variable $v$, so we require that it first be destructured to be used.

\begin{mathparpagebreakable}
  \mathrm{Type}::=\dots\mid \&^\mathbf{own}\tau\and
  \core{\&^\mathbf{own}\tau}=\N_{64}\and
  \boxed{x:\&^\mathbf{own}\tau}=\exists v:\tau,x\mapsto v\\
  \infer[ty-own]
    {\Gamma \proves \tau\;\mathsf{type}}
    {\Gamma \proves \&^\mathbf{own}\tau\;\mathsf{type}}\and
  \infer[tp-own]
    {\Gamma \proves t:\textstyle \tau\Rightarrow \bar{S}\quad
      \Gamma,\bar{S} \proves t':\textstyle \&^\mathbf{sn}t\Rightarrow \bar{S}'}
    {\Gamma \proves \langle t,t'\rangle:\&^\mathbf{own}\tau\Rightarrow \bar{S},\bar{S}'}\and
\end{mathparpagebreakable}

By using destructuring, it is possible to obtain a pointer such as $t:\&^\mathbf{sn}(a,b)$; this type asserts that $a$ and $b$ are contiguous in memory such that a single pointer can access them both. This type can itself be destructured as if it were $\&^\mathbf{sn}a\ast \&^\mathbf{sn}b$.

\subsubsection{Mutable pointers}
Before we can explain mutable pointers, we need the concept of a mutable parameter. We have already seen that the $\gets$ operator can mutate variables inside the value context $\delta$, but currently $\mathsf{return}$ will drop all mutated values and return only the return values in the function signature. In order to allow variables to be mutated through the function, we add the ability to mark a variable in the returns $\overline{S}$ as $\mathsf{out}^x\ y:\tau$, if $x$ is a function parameter (which is itself marked as $\mathsf{mut}\ x:\tau$). This has the meaning that the variable $x$ will be mutated so that $\delta\proves x\to^* y$ when the function reaches the return.

The rule \textsc{tye-return} is unchanged, but we have a new rule for fulfilling an $\mathsf{out}^x\;y$ argument:
\begin{mathparpagebreakable}
  \infer[tye-struct-out]
    {\delta\proves x\to^*y \quad
      \Gamma;\delta \proves y:\tau\makes \delta_1\quad
      \Gamma;\delta_1 \proves \langle \overline{e}\rangle:\textstyle\sum \bar R[pe/y]}
    {\Gamma \proves \langle \overline{e}\rangle:\textstyle\sum (\mathsf{out}^x\,y:\tau),\bar R}\and
\end{mathparpagebreakable}
Here $\delta\proves x\to^*y$ means that $x\to\dots\to y\not\to$ according to the rename map in $\delta$.

Conversely, when calling a function, the $\mathsf{mut}$ parameters get captured in the calling context, and changed to their $\mathsf{out}$ variants. Describing this is technically complicated so we will use a prose description. We define only the construct $\mathsf{let}\ \langle \overline{y},t\rangle:=F(\overline{e})\;\mathsf{in}\; e_2$ where $t$ is a tuple pattern and $\overline{y}$ has the same length as the number of out parameters of $F$; that is, $\mathsf{proc}\ F(\overline{R}):\overline{\mathsf{out}^x\;y:\tau},\overline{S}$.

The arguments of $F$ must be $e:\tau$ if $R=(x:\tau)$, and must be $\eta:\tau\;\mathsf{place}$ if $R=(\mathsf{mut}\ x:\tau)$. If $\eta$ is provided for argument $x$, and $\mathsf{out}^x\ y:\tau'$ is among the out arguments of the function, and $y$ is the corresponding element of the tuple in the $\mathsf{let}\ \langle \overline{y},t\rangle$ pattern match, then we perform an assignment $\eta \gets y$ on return from the function. All these $\eta$ places are disjoint because they were passed simultaneously to $F$, so there is no ambiguity about the order of writes. Finally, the result of the $F(\overline{e})$ invocation is pattern matched against the tuple pattern $t$ and $e_2$ is executed.

The type $\&^\mathbf{mut}\tau$ is not a true type, but is allowed in function signatures to indicate a $\&^\mathbf{sn}\eta$ value where $\eta$ is external to the function. The changes to $\eta$ are a ``side effect'' and so we use the $\mathsf{out}^x\;y$ functionality from the previous section to support it.

In brief, if $x:\&^\mathbf{mut}\tau$ appears in the function arguments, we replace it by $\ghost v:\tau,x:\&^\mathbf{sn}v$ in the function arguments and add $\mathsf{out}_v\;\ghost{v'}:\tau$ at the beginning of the function returns. $\&^\mathbf{mut}\tau$ is not allowed to appear any other place than the top level of a function argument.

\subsubsection{Shared pointers}

Shared pointers are the most complex, because they cannot be modeled by separating conjunctions, at least without techniques such as fractional ownership. This is not a problem until we get to the underlying separation logic. Here we only need to mark work that will be perfomed later on.

We introduce a new type, a heap reservation type called $\mathsf{ref}^a\;\tau$, the elements of which are called heap variables. The expression $x:\mathsf{ref}^a\;\tau$ means that $x:\tau$, but $x$ is not owned by the current context. Heap variables can overlap each other, but not other regular variables in the context.

Heap variables resemble shared references from Rust, and in particular they are annotated with a ``lifetime''. The difference is that the pointer-ness is separated out; a heap variable directly has the type of the pointee, and the pointer is just a $\&^\mathbf{sn}\eta$ where $\eta$ is a heap variable.

A lifetime $a$ is modeled roughly as a (precise, aka subsingleton) separating proposition $P$, with each $x:=pe:\mathsf{ref}^a\;\tau$ being modeled as a place $\eta$ for which $P\Rightarrow (\eta:=pe:\tau)$. That is, we can weaken $P$ to obtain the fact that $\eta:=pe:\tau$. (The relation $P\Rightarrow Q$, which is a regular (not separating) proposition, is defined as $\proves P\to (Q*\top)$.) Because $P$ is a precise proposition, it satisfies $(P\Rightarrow \exists x, Q)\to (\exists x,(P\Rightarrow Q))$, which means we can pattern match on heap variables like regular variables, for example to obtain $\&\tau$ from $\&\&^\mathbf{own}\tau$. But this is only relevant for the semantic model; in the type checker we simply need some rules for how to manipulate these variables.

Syntactically, a lifetime can be either $\mathsf{extern}$, referring to data outside the current context, or $x$, some variable in the context. These denote the scope of the borrow; a variable which is borrowed cannot be mutated. (Possible extensions include lifetimes with scope $\{x,y,z\}$ for creating data that spans multiple variables, and lifetimes with scope $x.\mathsf{field}$ in order to borrow only parts of a variable without locking the whole variable.) The proposition $P$ from the previous paragraph is the implicit frame proposition in the $\mathsf{extern}$ case, and $x:=pe:\tau$ from the value context at the time of the borrow in the case of $x$. (In the case of multiple variables, it is the separating conjunction of these $x:=pe:\tau$ conditions and in the case of a subobject we destructure this proposition and pull out the $\eta:=pe:\tau$ component.)

\begin{mathparpagebreakable}
  a\in\mathrm{Lft}::=\mathsf{extern}\mid x\and
  \mathrm{Type}::=\dots\mid \mathsf{ref}^a\;\tau\and
  \infer[tp-sum-ref]
    {\Gamma \proves t:\textstyle \mathsf{ref}^a\;\tau\Rightarrow \bar{S}\quad
      \Gamma,\bar{S} \proves \langle \overline{t'}\rangle:\textstyle \mathsf{ref}^a(\tau'[t/x])\Rightarrow \bar{S}'}
    {\Gamma \proves \langle t,\overline{t'}\rangle:\mathsf{ref}^a(\textstyle\sum x:\tau,\overline{R})\Rightarrow \bar{S},\bar{S}'}\and
  \infer[ty-ref]
    {\mathrm{Var}(a)\subseteq \Gamma\quad
      \Gamma \proves \tau\;\mathsf{type}}
    {\Gamma \proves \mathsf{ref}^a\; \tau\;\mathsf{type}}\and
  \infer[tye-ref]
    {\Gamma;\delta \proves e\Rightarrow(\eta:=pe:\tau)\makes\delta'\;\mathsf{read}^a}
    {\Gamma;\delta \proves e: \mathsf{ref}^a\;\tau\makes\delta'}\and
\end{mathparpagebreakable}

Here the $\Gamma;\delta \proves e\Rightarrow(\eta:=pe:\tau)\makes\delta'\;\mathsf{read}^a$ judgment is a conjunction of $\Gamma;\delta \proves e\Rightarrow\eta\makes\delta_1\;\mathsf{place}$ followed by $\Gamma\proves\delta_1 \Rightarrow \delta'$, such that $\Gamma;\delta' \proves \eta:=pe:\tau\;\mathsf{read}^a$. That is, first we evaluate the place expression, then we use $\Gamma\proves\delta_1 \Rightarrow \delta'$ to ensure that $\eta$ is locked and readable at type $\tau$, and the final judgment asserts that in the result state we can in fact read $\eta:\tau$ from origin $a$.

\begin{mathparpagebreakable}
  \delta\in\mathrm{VCtx}::=\delta,(\mathsf{ref}^a\;x:=pe:\tau)\\
  \axiom[cs-lock]
    {\Gamma\proves\delta,(x:=pe:\tau) \constep \delta,(\mathsf{ref}^x\;x:=pe:\tau)}\and
  \infer[cs-unlock]
    {\forall y, (\mathsf{ref}^x\;y:=-)\notin \delta}
    {\Gamma\proves\delta,(\mathsf{ref}^x\;x:=pe:\tau) \constep \delta,(x:=pe:\tau)}\and
  \infer[tyr-var]
    {(\mathsf{ref}^a\;x:=pe:\tau)\in\delta}
    {\Gamma;\delta \proves x:=pe:\tau\;\mathsf{read}^a}\and
  \infer[tye-read-ref]
    {\!\:{\Gamma;\delta \proves e\Rightarrow\eta\makes\delta'\;\mathsf{place}\atop
      \Gamma;\delta' \proves \eta:=pe:\tau\;\mathsf{read}^a}}
    {\Gamma;\delta \proves e\Rightarrow pe:\core\tau\makes\delta'}\and
\end{mathparpagebreakable}

Note that we cannot move out a value from a ref variable, which is reflected in the use of $\core\tau$ in \textsc{tye-read-ref}. We also cannot mutate a ref, meaning that while a variable is locked (meaning that it is represented in the value context as a $\mathsf{ref}^x\;x$), mutation is not possible; however it is possible to mutate a variable that is currently locked by first unlocking it using the \textsc{cs-unlock} rule, which requires first deleting all the heap variables that reference $x$ using the \textsc{cs-drop} rule.

In fact, we can't even really read a ref; the value read is only available as a ghost value, unless it is accessed indirectly via a shared reference. Using heap variables, we can desugar shared references similarly to owned pointers:

\begin{mathparpagebreakable}
  \mathrm{Type}::=\dots\mid \&^a\tau\and
  \core{\&^a\tau}=\N_{64}\and
  \boxed{x:\&^a\tau}=\exists v:\mathsf{ref}^a\;\tau,x\mapsto v\\
  \infer[ty-shr]
    {\mathrm{Var}(a)\subseteq \Gamma\quad
      \Gamma \proves \tau\;\mathsf{type}}
    {\Gamma \proves \&^a\tau\;\mathsf{type}}\and
  \infer[tp-shr]
    {\Gamma \proves \mathsf{ref}^a\; t:\textstyle \tau\Rightarrow \bar{S}\quad
      \Gamma,\bar{S} \proves t':\textstyle \&^\mathbf{sn}t\Rightarrow \bar{S}'}
    {\Gamma \proves \langle t,t'\rangle:\&^a\tau\Rightarrow \bar{S},\bar{S}'}\and
\end{mathparpagebreakable}

\subsection{Arrays}\label{sec:arrays}

Arrays here are fixed length, depending on another variable in the context.

\begin{mathparpagebreakable}
  \mathrm{Type}::=\dots\mid \mathsf{array}\;\tau\;pe\and
  \core{\mathsf{array}\;\tau\;n}=\mathsf{array}\;\core\tau\;n\and
  \boxed{x:\mathsf{array}\;\tau\;n}=(x:n\to\core\tau)\ast\textstyle\Sep_{i<n}\boxed{x[i]:\tau}\\
  \infer[ty-array]
    {\Gamma \proves \tau\;\mathsf{type}\quad
      \Gamma \proves n:\N_s}
    {\Gamma \proves \mathsf{array}\;\tau\;n\;\mathsf{type}}\and
\end{mathparpagebreakable}

TODO

\section{Ghost propagation}

Ghost annotations are optional in most cases, because of the ghost propagation pass that automatically makes as many things ghost as possible. The invariant that we uphold is that a ghost variable \emph{must not} have an M-place associated with it, while a regular variable \emph{may} have an M-place. However, it is consistent with this that there are no M-places at all, so we have some inductive conditions on what variables must have M-places, which are roughly analogous to dead-code elimination.

Ghost propagation (dead-store elimination) has to be done in tandem with reachability analysis (dead-code elimination), because \textsf{if} can convert data dependencies into control dependencies, meaning that parts of the code may in fact have the program counter itself being ghost. When this happens, we can't execute anything with side effects or anything whose value is computationally relevant, because the physical machine never reaches these lines.

To express all this, we will use a judgment $\Gamma^\alpha;\delta^\rho\proves e:\tau^\gamma\makes {\delta'}^{\rho'}$ that augments the typing condition with four ghost annotations; in addition we will be modifying the ghost annotations inside $\delta$ and $\delta_1$ to make them more strict (i.e. possibly turning $x^\top$ to $x^\bot$).

\begin{itemize}
  \item $\alpha$, the variable on $\Gamma$, is either $\top$ or $\bot$. If $\alpha=\bot$ then the program counter is ghost, which is to say, we are unable to perform any operation that involves emitting code. This happens when we branch on a ghost variable.
  \item $\rho$, the variables associated to the before and after value contexts, are also $\bot$ or $\top$ and indicate whether the beginning or end of $e$ is reachable.
  \item Because type inference is complete, we can treat the type $\tau$ of $e$ as an input to the judgment. Here $\tau^{\gamma}$ is a type extended with ghost annotations in all subexpressions. The typing rules for such extended types assert that a type is ghost only if all subexpressions are ghost.
\end{itemize}

\subsection{Ghost annotated types and tuple patterns}

The types that show up in the expression judgment are annotated with $\gamma$ ghost annotations at all levels, subject to a local coherence condition that states that a ghost type must only have ghost parts. This allows us to only compute some parts of a type as long as we have all the parts we actually need for downstream processing. While the language itself admits ghost annotations on variables in a tuple pattern and variable binders in a struct, these are only upper bounds on the computational relevance, because we are interested in eliminating parts of a type for optimization purposes even if they were not claimed to be ghost.

\judgment[Ghost-annotated type validity]{\tau^\gamma\;\mathsf{ctype}}
\begin{mathparpagebreakable}
  \axiom[cty-unit, cty-bool]{\mathbf{1}^\gamma,\ \mathsf{bool}^\gamma\;\mathsf{ctype}}\and
  \axiom[cty-nat, cty-int]
    {\N_s^\gamma,\ \Z_s^\gamma\;\mathsf{ctype}}\and
  \infer[cty-var, cty-core-var]
    {\alpha\in\Gamma}
    {\alpha^\gamma,\ \core\alpha^\gamma\;\mathsf{ctype}}\and
  \infer[cty-inter, cty-union, cty-list]
    {\forall i,\ \tau_i^{\gamma_i}\;\mathsf{ctype}\quad
      \forall i,\ \gamma_i\le \gamma'}
    {(\textstyle\bigcap\overline{\tau^\gamma})^{\gamma'},
      \ (\textstyle\bigcup\overline{\tau^\gamma})^{\gamma'},
      \ (\textstyle\Sep\overline{\tau^\gamma})^{\gamma'}\;\mathsf{ctype}}\and
  \axiom[cty-prop]{A^\gamma\;\mathsf{ctype}}\and
  \infer[cty-struct]
    {\gamma_2\le\gamma_1,\gamma\quad
      \tau^{\gamma_2}\;\mathsf{ctype}\quad
      {\tau'}^{\gamma}\;\mathsf{ctype}}
    {(\textstyle\sum x^{\gamma_1}:{\tau^{\gamma_2}},\tau')^{\gamma}\;\mathsf{ctype}}\and
\end{mathparpagebreakable}
\judgment[Ghost-annotated tuple pattern validity]{t:\tau^\gamma\Rightarrow\overline{R^{\gamma'}}}
\begin{mathparpagebreakable}
  \axiom[ctp-ignore]{\_:\tau^\gamma\Rightarrow\cdot}\and
  \infer[ctp-var]
    {\gamma'\le \gamma}
    {x^\gamma:\tau^{\gamma'}\Rightarrow x^{\gamma'}:\tau}\and
  \infer[ctp-typed]
    {t:\tau^\gamma\Rightarrow\overline{R^{\gamma'}}}
    {(t:\tau):\tau^\gamma\Rightarrow\overline{R^{\gamma'}}}\and
  \infer[ctp-sum]
    {\gamma_2\le\gamma_1,\gamma_3\quad
      t:\textstyle \tau^{\gamma_2}\Rightarrow \overline{R^\gamma}\quad
      \langle \overline{t'}\rangle:\textstyle (\tau'[t/x])^{\gamma_3}\Rightarrow \overline{R^\gamma}'}
    {\langle t,\overline{t'}\rangle:(\textstyle\sum x^{\gamma_1}:{\tau^{\gamma_2}},\tau')^{\gamma_3}\Rightarrow \overline{R^\gamma},\overline{R^\gamma}'}\and
\end{mathparpagebreakable}

The intuitive meaning of $\tau^\gamma$ is that $\tau^\top$ is a value that will actually have storage space allocated for it, while $\tau^\bot$ is a value that will not need to be calculated (even if it is stored in a non-ghost variable). These rules assume that the full ghost annotation assignment is known and just give constraints on that assignment, but in practice we will start from an assignment that makes everything ghost, and incrementally shift this upward in a coordinated fashion. At the end we may end up with an impossible constraint such as a computationally relevant unbounded integer value, which will cause an error during legalization.

\subsection{The expression typing judgment}

For the expression judgment $\Gamma^\alpha;\delta^{\rho} \proves e:\tau^\gamma\makes{\delta'}^{\rho'}$, we have $\Gamma,\delta,e,\tau$ as inputs and $\delta'$ as output, with the annotations $\alpha,\rho,\rho',\gamma$ being solved for by a fixed point algorithm. It is safe to assume that $\gamma\le \rho'\le \rho$ and $\gamma\le \alpha$ in this judgment (that is, the end of a statement is only reachable if the beginning is, and the return value is only needed if the end of the statement is reached), unless $\tau$ is a ghost type like $\mathbf{1}$ or $A$ in which case $\gamma\le \rho',\alpha$ need not hold.

We also add an annotation $\sigma\in\{\bot,\top\}$ on functions (including $\mathsf{self}$), which can be seen in rule \textsc{tyc-proc-call}, for example; this is the side effect analysis, see section \ref{sec:sideeffect}.

\judgment[Ghost-annotated expression validity]{\Gamma^\alpha;\delta_1^{\rho_1} \proves e:\tau^\gamma\makes\delta_2^{\rho_2}}
\begin{mathparpagebreakable}
  \infer[tyc-cs-left]
    {\Gamma;\delta\constep\delta_1\quad \Gamma^\alpha;\delta_1^\rho\proves e:\tau^\gamma\makes\delta_2^{\rho'}}
    {\Gamma^\alpha;\delta^\rho\proves e:\tau^\gamma\makes\delta_2^{\rho'}}\and
  \infer[tyc-cs-right]
    {\Gamma^\alpha;\delta^\rho\proves e:\tau^\gamma\makes\delta_1^{\rho'}\quad \Gamma;\delta_1\constep\delta_2}
    {\Gamma^\alpha;\delta^\rho\proves e:\tau^\gamma\makes\delta_2^{\rho'}}\and
  \infer[tyc-var-ref]
    {(x^{\gamma'}:=pe:\tau)\in\delta\quad |\tau| = \tau'\quad \gamma\le\alpha,\rho,\gamma'}
    {\Gamma^\alpha;\delta^{\rho} \proves x:{\tau'}^{\gamma}\makes \delta^{\rho}}\and
  \axiom[tyc-unit]
    {\Gamma^\alpha;\delta^{\rho} \proves ():\mathbf{1}^{\gamma}\makes \delta^{\rho}}\and
  \infer[tyc-true, tyc-false]
    {\gamma\le \alpha,\rho}
    {\Gamma^\alpha;\delta^{\rho} \proves \mathsf{true},\ \mathsf{false}:\mathsf{bool}^{\gamma}\makes \delta^{\rho}}\and
  \infer[tyc-nat, tyc-int]
    {\gamma\le \alpha,\rho}
    {\Gamma^\alpha;\delta^{\rho} \proves n:\N_s^{\gamma},\ \Z_s^{\gamma}\makes \delta^{\rho}}\and
  \infer[tyc-not]
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves e:\mathsf{bool}^\gamma\makes \delta_2^{\rho_2}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves \neg e:\mathsf{bool}^\gamma\makes \delta_2^{\rho_2}}\and
  \infer[tyc-and, tyc-or, \dots]
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves e_1:\mathsf{bool}^\gamma\makes \delta_2^{\rho_2}\quad
      \Gamma^\alpha;\delta_2^{\rho_2} \proves e_2:\mathsf{bool}^\gamma\makes \delta_3^{\rho_3}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves e_1\land e_2,\ e_1\lor e_2:\mathsf{bool}^\gamma\makes \delta_3^{\rho_3}}\and
  \infer[tyc-if]
    {\!\:{\Gamma^\alpha;\delta_1^{\rho_1} \proves c:\mathsf{bool}^{\gamma'}\makes \delta_2^{\rho_2} \atop
      \Gamma^{\alpha\wedge \gamma'};\delta_2^{\rho_2} \proves e_1,\ e_2:\tau^\gamma\makes \delta_3^{\rho_3}}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves (\mathsf{if}\;c\;\mathsf{then}\;e_1\;\mathsf{else}\;e_2):\tau^\gamma\makes \delta_3^{\rho_3}}\and
  \infer[tyc-struct]
    {\!\:{\gamma_2\le\gamma_1, \gamma\quad
      \Gamma^\alpha;\delta_1^{\rho_1} \proves e:\tau^{\gamma_2}\makes \delta_2^{\rho_2}\atop
      \Gamma^\alpha;\delta_2^{\rho_2} \proves \langle \overline{e}\rangle:(\tau'[e/x])^{\gamma}\makes \delta_3^{\rho_3}}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves \langle e,\overline{e}\rangle:(\textstyle\sum x^{\gamma_1}:{\tau^{\gamma_2}},\tau')^{\gamma}\makes \delta_3^{\rho_3}}\and
  \infer[tyc-var-move]
    {\gamma\le \alpha,\rho,\gamma'}
    {\Gamma^\alpha;(\delta,x^{\gamma'}:=pe:\tau)^\rho \proves x:\tau^\gamma\makes(\delta,x^{\gamma'}:=pe:\core\tau)^\rho}\and
  \infer[tyc-mut]
    {\!\:{\gamma_1\le\gamma\quad
      \Gamma^\alpha;\delta_1^{\rho_1} \proves e_1:\tau_1^{\gamma_1}\makes\delta_2^{\rho_2}\atop
      \Gamma^\alpha;\delta_2^{\rho_2},(x\to y),(y^{\gamma_1}:=e_1:\tau_1)\proves e_2:\tau_2^{\gamma_2}\makes\delta_3^{\rho_3}}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves (x^{\gamma}\gets e_1\ \mathsf{with}\ y\gets x;\ e_2):\tau_2^{\gamma_2}\makes\delta_3^{\rho_3}}\and
  \infer[tyc-unreachable]
    {\rho_2\le\rho\quad
      \Gamma^\alpha;\delta^\rho \proves e:\bot^\bot\makes\delta_1^{\rho_1}}
    {\Gamma^\alpha;\delta^\rho \proves \mathsf{unreachable}\;e:\tau^\gamma\makes\delta_2^{\rho_2}}\and
  \infer[tyc-let]
    {\!\:{\Gamma^\alpha;\delta_1^{\rho_1} \proves e_1:\tau_1^{\gamma_1}\makes \delta_2^{\rho_2}\quad
      t:\tau_1^{\gamma_1}\Rightarrow \overline{R^\gamma}\atop
      (\Gamma,\overline{\core{R}})^\alpha;(\delta_2,\overline{R^\gamma})^{\rho_2} \proves e_2:\tau_2^{\gamma_2}\makes\delta_3^{\rho_3}}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves (\mathsf{let}\ t := e_1\;\mathsf{in}\; e_2):\tau_2^{\gamma_2}\makes\delta_3^{\rho_3}}\and
  \infer[tyc-return]
    {\!\:{\rho_2\le\rho\quad\rho_1\le\alpha,\gamma' \quad
      (\textstyle\sum\bar S)^{\gamma'}\;\mathsf{ctype}\atop
      \mathsf{self}^\sigma(\bar R):\bar S\quad
      \Gamma^\alpha;\delta^\rho \proves \langle\overline{e}\rangle:(\textstyle\sum\bar S)^{\gamma'}\makes \delta_1^{\rho_1}}}
    {\Gamma^\alpha;\delta^\rho \proves \mathsf{return}\;\overline{e}:\bot^\gamma\makes \delta_2^{\rho_2}}\and
  \infer[tyc-proc-call]
    {\!\:{\gamma'\le\gamma\quad \sigma\wedge\rho_2\le \alpha,\gamma,\sigma'\quad (\textstyle\sum\bar R)^\gamma,\ (\textstyle\sum\bar S)^{\gamma'}\;\mathsf{ctype}\atop
      \mathsf{self}^{\sigma'}(-):-\quad
      \mathsf{proc}^\sigma\;F(\overline{R}):\overline{S}\quad
      \Gamma^\alpha;\delta_1^{\rho_1} \proves \langle\overline{e}\rangle:(\textstyle\sum\bar R)^\gamma\makes\delta_2^{\rho_2}}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves F(\overline{e}):(\textstyle\sum\bar S)^{\gamma'}\makes\delta_2^{\rho_2}}\and
  \infer[tyc-label]
    {\!\:{\forall i,\ (\Gamma,\overline{k^\alpha(\delta;\bar{R})},(\bar{R})_i)^{\alpha_i};(\delta_i,(\bar{R})_i)^{\rho_i} \proves e_i:\bot^\bot\makes{\delta'_i}^{\rho'_i}\atop
      (\Gamma,\overline{k^\alpha(\delta;\bar{R})})^\alpha;\delta_1^{\rho_1}\proves e':\tau^\gamma\makes\delta_3^{\rho_3}}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves (\mathsf{label}\;\overline{k(\bar{R}):=e}\;\mathsf{in}\;e'):\tau^\gamma\makes\delta_3^{\rho_3}}\and
  \infer[tyc-goto]
    {\!\:{\alpha'\le \alpha\ \ \rho_2\le \rho\ \ \rho_1\le\gamma\quad
      k^{\alpha'}(\delta_1^{\rho_1};\bar{R})\in\Gamma\atop
      \Gamma^\alpha;\delta^{\rho} \proves \langle\overline{e}\rangle:(\textstyle\sum\bar R)^\gamma\makes\delta_1^{\rho_1}}}
    {\Gamma^\alpha;\delta^{\rho} \proves \mathsf{goto}\;k(\overline{e}):\bot^{\gamma'}\proves \delta_2^{\rho_2}}\and
  \infer[tyc-assert]
    {\!\:{\rho_2\le \sigma,\alpha,\gamma\quad
      \mathsf{self}^\sigma(-):-\atop
      \Gamma^\alpha;\delta_1^{\rho_1} \proves e:\mathsf{bool}^\gamma \makes \delta_2^{\rho_2}}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves \mathsf{assert}\;e:e^{\gamma'}\makes\delta_2^{\rho_2}}\and
  \infer[tyc-typeof]
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves e:\tau^\bot \makes \delta_2^{\rho_2}}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves \mathsf{typeof}\;e:\boxed{e:\tau}^{\gamma}\makes\delta_2^{\rho_2}}\and
  \infer[tyc-entail]
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves \langle\overline{e}\rangle:\textstyle(\Sep\overline{A})^\bot \makes \delta_2^{\rho_2}\quad
      \proves p:\textstyle\Sep\overline{A}\wand B}
    {\Gamma^\alpha;\delta_1^{\rho_1} \proves \mathsf{entail}\;\overline{e}\;p:B^\gamma\makes\delta_2^{\rho_2}}\and
\end{mathparpagebreakable}

These rules have the same form as the \textsc{tye-*} rules (slightly simplified to focus on the new part, the $\alpha,\rho,\gamma$ annotations). The key new thing to notice is the inequality side conditions in most of the rules. For example:
\begin{itemize}
\item \textsc{tyc-var-move} requires $\gamma\le \alpha,\rho,\gamma'$ because if the result of the move is actually needed ($\gamma$) then we must execute code ($\alpha$) that is reachable ($\rho$) and the data to move must actually be available ($\gamma'$).
\item \textsc{tyc-if} is the main rule that changes $\alpha$. Inside the branches, $\alpha$ becomes $\alpha\wedge \gamma'$, because if we did not evaluate the condition we can't enter the branches.
\item \textsc{tyc-return} requires $\rho_2\le \rho$, just to ensure the lemma $(\Gamma^\alpha;\delta_1^{\rho_1} \proves e:\tau^\gamma\makes\delta_2^{\rho_2})\to\rho_2\le\rho_1$, but in practice we can always set $\rho_2$ to $\bot$ because it is unreachable. Similar conditions appear in \textsc{tyc-unreachable} and \textsc{tyc-goto}, since these expressions do not terminate normally. The other condition, $\rho_1\le\alpha,\gamma$ says that if we reach the return ($\rho_1$), then we must be able to execute the return statement ($\alpha$), and we need the value to return ($\gamma'$).
\item \textsc{tyc-proc-call} makes use of the $\sigma$ annotations on functions. If a function has $\sigma=\top$, then it may perform a side effect, so we cannot omit it. We require $\gamma'\le \gamma$ because if we need the result ($\gamma'$) then we need the arguments ($\gamma$), and we require $\sigma\wedge\rho_2\le\alpha,\gamma,\sigma'$ because if the function $F$ is side effecting ($\sigma$) and the call is reachable ($\rho_2$), then we must execute the call ($\alpha$), we need the arguments ($\gamma$), and this function is itself side-effecting ($\sigma'$).
\item \textsc{tyc-assert} requires that $\rho_2\le\sigma,\alpha,\gamma$ because if we reach the assert ($\rho_2$), then because failure is a side effect ($\sigma$) we have to execute it ($\alpha$) and we need the condition ($\gamma$).
\item In \textsc{tyc-goto}, we add $\alpha$ and $\rho$ annotations to $k(\delta,\bar{R})$ to coordinate the entry to this block. Here $\alpha'=\bot$ is a bit unusual, because it means that the block we are jumping to does not physically exist. In this case, we don't need to jump to it, so no code is needed ($\alpha'\le \alpha$), we have an arbitrary postcondition $\rho_2$ that may as well be $\bot$, and we need $\rho_1\le\gamma$ because if the goto is reachable then we need the value.
\end{itemize}

We also need an annotated version of the no-op step judgment, in order to weaken variables when they are no longer live. This is exactly the same as \textsc{cs-*}, but with the new rule \textsc{ccs-ghost} that allows us to make a variable ghost. In particular, since the \textsc{tyc-mut} rule does not remove the old value of the variable, it is in general a copy and not actually a mutation, so we will want to use \textsc{ccs-ghost} just after constructing the expression $e_1$ to kill the old value so that we can safely replace it in-place with the new value.

\judgment[Ghost annotated no-op step]{\Gamma;\delta \constep\delta'}
\begin{mathparpagebreakable}
  \axiom[ccs-refl]{\Gamma;\delta \constep \delta}\and
  \infer[ccs-trans]
    {\Gamma;\delta_1 \constep \delta_2 \quad \Gamma;\delta_2 \constep \delta_3}
    {\Gamma;\delta_1 \constep \delta_3}\and
  \infer[ccs-drop]
    {\forall x,(x\to y)\notin \delta}
    {\Gamma;\delta,(y^\gamma:=pe:\tau)\constep\delta}\and
  \axiom[ccs-rename]
    {\Gamma;\delta,(x\to y),(y^\gamma:=pe:\tau)\constep\delta,(x^\gamma:=pe:\tau)}\and
  \infer[ccs-forget]
    {\Gamma\proves\Gamma[\overline{x\to pe}]}
    {\Gamma;\delta,\overline{x^\gamma:=pe:\tau}\constep\delta,\overline{x^\gamma:\tau}}\and
  \infer[ccs-ghost]
    {\gamma'\le \gamma}
    {\Gamma;\delta,(x^\gamma:=pe:\tau)\constep\delta,(x^{\gamma'}:=pe:\tau)}\and
\end{mathparpagebreakable}

\subsection{Side effects}\label{sec:sideeffect}

While the ghost analysis pass is primarily intraprocedural, it contains one interprocedural part, namely the assignment of $\sigma$ annotations to the procedures. When $\sigma=\top$, the procedure may perform a side effect, which is defined as anything which performs IO (i.e. compiler intrinsics and syscalls), plus $\mathsf{assert}\;\mathsf{false}$ which causes early termination (which is also observable).

If a side effectful operation is reachable from a procedure, then we mark the procedure itself as side effectful (note that \textsf{func} functions cannot have side effects). Note in particular that mutation is not considered a side effect, because the compiler has full visibility into what is going on and can track the values appropriately.

\section{Optimization and legalization}\label{sec:optimization}

At this point, we are mostly done with user level errors; if type checking and the ghost analysis pass succeed then we should be able to complete compilation. The only exception to this is types that are too large to exist (which will be caught in this pass) and operations that cannot be compiled, such as unbounded integer operations.

Because the source language makes use of unbounded integer operations even in computationally relevant positions, it is not sufficient to simply require that any variable or expression of type $\N_\infty$ is ghost; for example a reasonable operation might be $x,y:\N_{64}\vdash \mathsf{let}\;z:\N_{64}:=(x+y)\mathrel{\%}2^{64}$, which we expect to be compiled to an ADD instruction, despite the fact that $x+y:\N_{\infty}$ is an intermediate in this computation.

We call this phase legalization because it performs general rewriting in order to replace source level operations with operations which exist on the target architecture. So for example we can replace the subexpression $(x+y)\mathrel{\%}2^{64}$ by $x+_{64}y$, where $x+_{64}y$ is addition modulo $2^{64}$ that we expect to exist on the target machine. Once we have done so, there are no longer any unsized intermediates in the operation, and we can proceed with compilation.

\section{The Layout pass}\label{sec:layout}

The ghost analysis pass has already determined \emph{which} variables are required for the computation to proceed, but to determine \emph{where} to put them, we have another pass, the layout pass.

The layout pass is responsible for assigning concrete memory locations to variables in the code. In particular, multiple variables may overlap the same memory location if they are never \emph{live} at the same time, which is to say, the last use of one variable comes before the definition of the second. The analysis pass that determines these relations is considered part of the ``nondeterministic'' part of the compiler, meaning that it requires no proof. Instead, the analysis pass produces a satisfying layout, and the typing relation will validate that a layout puts variables in disjoint locations if they are live at the same time.

To that end, we introduce another syntactic category not present in the source language, a \emph{machine place}, or M-place for short.

$$\mu::=\mathsf{Reg}\;r\mid \mathsf{Stack}\;s$$

The registers $r$ correspond to the registers on the machine, so there is one for every general-purpose register. (On x86-64 there are 16 general purpose registers, but RSP is the stack pointer, and one register is reserved by the compiler for spilling, so there are 14 registers available for use.)

The stack locations $s$ correspond to an abstraction of the stack frame, optimized for disjointness proofs. A stack frame has a series-parallel layout:
$$\phi ::= \phi_0\ast\phi_1\mid \phi_0\cup \phi_1\mid |\tau|$$
and $s$ is a path into the stack frame:
$$s ::= \mathsf{id}\mid s.0\mid s.1 \mid s.l \mid s.r$$
with the following typing rules:

\judgment[Stack variable typing]{\phi\proves s:\phi'}
\begin{mathparpagebreakable}
  \axiom[stk-id]{\phi\proves \mathsf{id}:\phi}\and
  \infer[stk-fst]
    {\phi\proves s:\phi_1\ast \phi_2}
    {\phi\proves s.0:\phi_1}\and
  \infer[stk-snd]
    {\phi\proves s:\phi_1\ast \phi_2}
    {\phi\proves s.1:\phi_2}\and
  \infer[stk-left]
    {\phi\proves s:\phi_1\cup \phi_2}
    {\phi\proves s.l:\phi_1}\and
  \infer[stk-right]
    {\phi\proves s:\phi_1\cup \phi_2}
    {\phi\proves s.r:\phi_2}
\end{mathparpagebreakable}

Intuitively, $\phi_1\ast\phi_2$ is the stack layout consisting of the layout $\phi_1$ followed by $\phi_2$ in the bytes immediately after, while $\phi_1\cup\phi_2$ consists of $\phi_1$ and $\phi_2$ superimposed on the same bytes (taking up size equal to the larger of the two).

At a given point in execution, each of the unions has one of its members ``active'' and the other ``inactive'', and a variable can only be accessed if it is active in all parent unions.
A ghost variable is never assigned any stack location and hence it can never be accessed. More formally, we say that two stack paths are \emph{incompatible}, written $s_1\perp s_2$, if there exists $s$ such that $s_1$ extends $s.l$ and $s_2$ extends $s.r$, or vice versa. We will maintain the invariant that if two variables in the context are represented by stack paths $s_1$ and $s_2$ then they are compatible.

\section{Semantics}\label{sec:semantics}

Semantics plays a rather more important role in Metamath C compared to other languages because the target architecture for the compiler is literally separation logic. So we need a way to interpret every judgment just described into a separating proposition or theorem.

\subsection{Interpreting the context}

The context $\Gamma$ in the typing rules is ultimately compiled down to a separating proposition over machine states, and we need to interpret it in such a way that a validly typed expression corresponds to a valid theorem in separation logic.

Each variable in the context may or may not be associated with a component of the machine state which is currently storing the value of that variable. A ghost variable will never have machine state attached to it, and a variable may also not have machine state attached to it if it is past its last use, or if it is uninitialized. To express this, we will add a new kind of context, a machine context $\Delta$ which extends $\delta$ with this information at each variable site.

\begin{itemize}
\item For each procedure in the global environment of declared items, we have a (persistent) proposition $\mbox{\textsf{proc-ok}}(\ell:\overline{R}\to\overline{S})$ which asserts that location $\ell$ (an actual machine location) is the entry point to a function $f$ which, if called with arguments $\overline{R}$, will return values $\overline{S}$, according to the calling convention (which can be an additional parameter to \textsf{proc-ok}, but we can suppose that there is one fixed calling convention).

Mutual recursions are more complex, as we may not be able to promise that they are safe to call without additional restrictions. Instead, for such functions we have $\mbox{\textsf{proc-ok}}(\ell:(\ghost{v:\N},h:v<n,\overline{R})\to\overline{S})$ where $v$ is the variant, and $n$ is a parameter, the value of the variant passed into this function. In other words, they must be called with a value of the variant less than the current one. We will not discuss the compilation of recursive functions here.

\item Type declarations correspond to certain unfolding theorems so they have no representation in the context. We can ignore the type variables $\overline{\alpha}$ in $\Gamma$ because we don't support generic functions.

\item The jump targets $\overline{k(\delta,\bar R)}$ in $\Gamma$ become (persistent) propositions $\textsf{jump-ok}(\ell:(\delta,\bar {R})\to \bot)$ asserting that if we jump to location $\ell$ with arguments $\bar {R}$ according to the calling convention of the jump, then this machine state is OK (will eventually reach a final termination with the desired global properties). The $\mathsf{return}(\bar R)$ continuation is also a jump target of this form (where the calling convention uses \texttt{ret} instead of \texttt{jump}).

\item Each variable $x:\core\tau$ becomes a (regular) proposition $\boxed{x:\core\tau}$.
\end{itemize}

The value context $\delta$ is extended to $\Delta$ by extending some of the variable records with $\mathrel{@}\mu$ annotations. They are interpreted like so:
\begin{itemize}
\item We store no additional information regarding the rename map.
\item Each $x^\gamma:=pe:\tau$ may either be left as is or extended to $x^{\top}\mathrel{@}\mu:=pe:\tau$ where $\mu$ is an M-place. The second form is only available for non-ghost variables, and the M-places of distinct variables in the context will always be compatible. The former corresponds to the separating proposition $\boxed{pe:\tau}$, and the latter to $\mu\mapsto pe\ast{}\boxed{pe:\tau}$.
\item For the shared variables extension, we store a list of active locks $x:=pe:\tau$ corresponding to uses of the \textsc{cs-lock} rule. We say $x:=pe:\tau$ is an active lock if $(\mathsf{ref}^x\;x:=pe:\tau)\in\delta$. For each active lock, we also store $\boxed{pe:\tau}$.
\item For each heap variable $\mathsf{ref}^x\;y^\gamma:=pe':\tau'$ such that $x:=pe:\tau$ is an active lock, we store the pure proposition $(\mu\mapsto pe\ast{}\boxed{pe:\tau}\Rightarrow \mu'\mapsto pe'\ast\boxed{pe':\tau'})$ if $x\mathrel{@}\mu$ and $y\mathrel{@}\mu'$, with the $\mu$ conjuncts omitted if one or both of $x$ and $y$ is ghost.
\item For each heap variable $\mathsf{ref}^\mathsf{extern}\;y^\gamma:=pe':\tau'$, we store the pure proposition $(P\Rightarrow \mu'\mapsto pe'\ast\boxed{pe':\tau'})$ where $P$ is the frame proposition (that is, $P$ is an implicit additional precise separating proposition passed in and out of the function).
\end{itemize}

\subsection{Interpreting the judgments}

TODO

\end{document}