# Technical Documentation: facebook/hhvm

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/facebook/hhvm)

### Overview (README.md)

# HHVM

[HHVM page](https://hhvm.com) |
[HHVM documentation](https://docs.hhvm.com/hhvm/) |
[Hacklang page](http://hacklang.org) |
[General group](https://www.facebook.com/groups/hhvm.general/) |
[Dev group](https://www.facebook.com/groups/hhvm.dev/) |
[Twitter](https://twitter.com/HipHopVM)

HHVM is an open-source virtual machine designed for executing programs written in [Hack](http://hacklang.org). HHVM uses a just-in-time (JIT) compilation approach to achieve superior performance while maintaining amazing development flexibility.

HHVM should be used together with a webserver like the built in, easy to deploy [Proxygen](https://docs.hhvm.com/hhvm/basic-usage/proxygen), or a [FastCGI](https://docs.hhvm.com/hhvm/advanced-usage/fastCGI)-based webserver on top of nginx or Apache.

## Installing

If you're new, try our [getting started guide](https://docs.hhvm.com/hhvm/getting-started/getting-started).

You can install a [prebuilt package](https://docs.hhvm.com/hhvm/installation/introduction#prebuilt-packages) or [compile from source](https://docs.hhvm.com/hhvm/installation/building-from-source).

## Running

You can run standalone programs just by passing them to hhvm: `hhvm example.hack`.

If you want to host a website:
* Install your favorite webserver. [Proxygen](https://docs.hhvm.com/hhvm/basic-usage/proxygen) is built into HHVM, fast and easy to deploy.
* Install our [package](https://docs.hhvm.com/hhvm/installation/introduction#prebuilt-packages)
* Start your webserver
* Run `sudo /etc/init.d/hhvm start`
* Visit your site at `http://.../main.hack`

Our [getting started guide](https://docs.hhvm.com/hhvm/getting-started/getting-started) provides a slightly more detailed introduction as well as links to more information.

## Contributing

We'd love to have your help in making HHVM better. If you're interested, please read our [guide to contributing](CONTRIBUTING.md).

## License

HHVM is licensed under the PHP and Zend licenses except as otherwise noted.

The [Hack typechecker](hphp/hack) is licensed under the MIT [License](hphp/hack/LICENSE) except as otherwise noted.

The [Hack Standard Library](hphp/hsl) is licensed under the MIT [License](hphp/hsl/LICENSE) except as otherwise noted.

## Reporting Crashes

See [Reporting Crashes](https://github.com/facebook/hhvm/wiki/Reporting-Crashes) for helpful tips on how to report crashes in an actionable manner.

## Security

For information on reporting security vulnerabilities in HHVM, see [SECURITY.md](SECURITY.md).

## FAQ

Our [user FAQ](https://docs.hhvm.com/hhvm/FAQ/faq) has answers to many common questions about HHVM, from [general questions](https://docs.hhvm.com/hhvm/FAQ/faq#general) to questions geared towards those that want to [use](https://docs.hhvm.com/hhvm/FAQ/faq#users).

There is also a FAQ for [contributors](https://github.com/facebook/hhvm/wiki/FAQ#contributors) to HHVM.

---

### Hphp Doc Hackers Guide README (`hphp/doc/hackers-guide/README.md`)

# Hacker's Guide to HHVM

This directory contains documentation on the internal architecture of HHVM,
targeted at C++ developers looking to hack on HHVM itself. If you're a
Hack developer looking for documentation on using HHVM, that can be found
[here](https://docs.hhvm.com/).

HHVM is a [virtual
machine](https://en.wikipedia.org/wiki/Virtual_machine#Process_virtual_machines)
that executes [Hack](http://hacklang.org/) programs using a [bytecode
interpreter](https://en.wikipedia.org/wiki/Interpreter_(computing)#Bytecode_interpreters)
and a [JIT compiler](https://en.wikipedia.org/wiki/Just-in-time_compilation)
(the latter is vastly more complex, and will get much more airtime here).
[PHP](https://php.net) is also currently supported for historical reasons.

You should already be comfortable reading and writing C++ (specifically, HHVM is
written in [C++14](https://en.wikipedia.org/wiki/C%2B%2B14)), as well as
navigating around large codebases using
[grep](https://en.wikipedia.org/wiki/Grep),
[ctags](https://en.wikipedia.org/wiki/Ctags), or any similar tool of your
choice. Prior experience with compilers and runtimes will help but is not
strictly necessary.

## Code References

Since this guide is intended to help familiarize readers with the HHVM codebase,
it naturally contains a number of links to the code. Some of these links are to
the current version of a file, and others are to specific lines in specific
versions of files. If you find links of either kind that are out-of-date with
current `master` or are otherwise misleading, please let us know.

## Building HHVM

Instructions for building HHVM and running our primary test suite can be found
[here](https://docs.hhvm.com/hhvm/installation/building-from-source). The
initial build may take an hour or longer, depending on how fast your machine is.
Subsequent builds should be faster, as long as you're not touching core header
files.

## Architecture Overview

HHVM, like most compilers, is best thought of as a pipeline with many different
stages. Source code goes in one end, and after a number of different
transformations, executable machine code comes out the other end. Some stages
are optional, controlled by runtime options. Each stage is described in detail
in the articles listed below, but here's a quick-and-dirty overview:

Source code enters the compiler frontend and is converted to a token stream by
the [lexer](https://en.wikipedia.org/wiki/Lexical_analysis). The
[parser](https://en.wikipedia.org/wiki/Parsing#Computer_languages) reads this
token stream and converts that to an [abstract syntax
tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree), or AST. This AST is
then converted to a stream of [bytecode
instructions](https://en.wikipedia.org/wiki/Bytecode) by the bytecode emitter.
Everything up to this point is written in OCaml; the rest of HHVM is written in
C++ and assembly.

After the bytecode and associated metadata are created, our bytecode optimizer,
HHBBC, is optionally run. Finally, the bytecode, optimized or not, is stored
into a `.hhbc` file we call a "repo".

If the frontend was invoked directly by HHVM, the bytecode also lives in-memory
in the `hhvm` process, and execution can begin right away. If the frontend was
invoked as an ahead-of-time build step, the bytecode will be loaded from the
repo by `hhvm` when it eventually starts. If the JIT is disabled, the bytecode
interpreter steps through the code one instruction at a time, decoding and
executing each one. Otherwise, the JIT is tasked with compiling the bytecode
into machine code.

The first step in the JIT is region selection, which decides how many bytecode
instructions to compile at once, based on a number of complicated heuristics.
The chosen bytecode region is then lowered to HHIR, our primary [intermediate
representation](https://en.wikipedia.org/wiki/Intermediate_representation). A
series of optimizations are run on the HHIR program, then it is lowered into
vasm, our low-level IR. More optimizations are run on the vasm program, followed
by register allocation, and finally, code generation. Once various metadata is
recorded and the code is relocated to the appropriate place, it is ready to be
executed.

## Getting Started

If you're not sure where to start, skimming these articles is a good first step:

* [Directory structure](directory-structure.md)
* [FAQ](faq.md)
* [Glossary](glossary.md)

## HHVM Internals

The articles in this section go into more detail about their respective
components:

* [HHBC spec](../bytecode.specification)
* Frontend
  * Parser
  * Emitter
  * HHBBC
  * ...
* VM Runtime
  * [Core data structures](data-structures.md)
    * [Hack-visible](data-structures.md#hack-visible-values)
      * [Datatype, Value, and TypedValue](data-structures.md#datatype-value-and-typedvalue)
      * [ArrayData](data-structures.md#arraydata)
      * [StringData](data-structures.md#stringdata)
      * [ObjectData](data-structures.md#objectdata)
      * [Smart pointer wrappers](data-structures.md#smart-pointer-wrappers)
    * [Runtime-internal](data-structures.md#runtime-internal-data-structures)
      * [Unit](data-structures.md#unit)
      * [PreClass/Class](data-structures.md#preclass-and-class)
      * [Func](data-structures.md#func)
  * [Memory management](memory-management.md)
  * Execution Context
  * [Bytecode interpreter](bytecode-interpreter.md)
  * Unwinder
  * Treadmill
  * Debugger
  * ...
* JIT Compiler
  * [Core concepts](jit-core.md)
  * [Optimization passes](jit-optimizations.md)
  * ...

---

### Hphp Doc Hands On Index (`hphp/doc/hands-on/index.md`)

Welcome to HHVM! This section of our documentation is a hands-on guide for
people who want to learn about compiler and runtime internals. The goal is to
work through making, testing, and measuring a sample optimization to our
compiler. You'll make the most out of this guide if you work through the
exercises as you read it.

[Lesson 0: Building and running HHVM](lesson0.md)

[Lesson 1: Understanding Hack runtime types](lesson1.md)

[Lesson 2: Understanding Hack bytecode](lesson2.md)

[Lesson 3: Refcounting and value types](lesson3.md)

[Lesson 4: Making an IR optimization](lesson4.md)

---

### Hphp Hack Doc HIPs Shape Poly Splat Impl Overview (`hphp/hack/doc/HIPs/shape_poly_splat/impl/overview.md`)

# Splat: merge algebra, subtyping, and inference

A design document for the `impl/` OCaml engine covering the three key additions:

1. the **merge algebra** (how spread collapses an ordered list of rows into one row),
2. the **subtyping rules** (with a long section on the type-parameter cases), and
3. **inference** (how spread variables and unification variables are solved).

File and line references point at the OCaml under `impl/lib` and the tests under
`impl/test/test_splat.ml`. The core is three files: `repr.ml` (the representation and
the algebra), `splat.ml` (subtyping and inference), and `env.ml` (variable environments
and solving). `prop.ml` is the residual-constraint tree.

Throughout, "row" is a separate syntactic kind that is never exposed to a source
program. A row becomes a type only through `shape`, and a type re-enters row position
only through `spread`. This mirrors Hack: a `shape(...)` type is a row lifted into a
type, and spreading a shape (`shape(...$s, 'k' => v)`) is the destructor.

---

## 1. Representation

All types live in `repr.ml`. There are four levels: base types, rows, field
descriptors, and requiredness.

### 1.1 Base types

`repr.ml:27-34`:

```
base =
  | Top                      -- ⊤
  | Bottom                   -- ⊥
  | Prim of Prim.t           -- Bool | Nat (stand-ins for scalar types)
  | Union of base * base     -- first-class union types
  | Shape of row             -- a row lifted into a type (Hack's shape(...))
  | Flex of Ty_var.t         -- an inference variable (unification variable)
  | Rigid of Ty_param.t      -- a bounded generic parameter (Hack's `T as ...`)
```

Hack mapping: `Prim` stands for the scalar leaves, `Union` is Hack's union type,
`Shape` is `shape(...)`, `Rigid` is a generic type parameter with bounds, and `Flex`
is an inference type variable (`Tvar`). `Ty_param.t` is just a name string
(`ty_param.ml:7`); `Ty_var.t` is an integer id (`ty_var.ml:3`).

### 1.2 Rows

`repr.ml:37-57`:

```
row =
  | Row_simple of row_simple          -- a concrete record shape
  | Row_splat of row_splat            -- an ordered list of things to merge

row_simple = { known : field_desc Label.Map.t ; unknown : base }

row_splat = { elems : row_splat_elem list }

row_splat_elem = Spread of base
```

A **simple row** is a finite map from known labels to field descriptors, plus an
`unknown` base standing for every label not in the map. The `unknown` is always the
type of an *optional* absent label. This is the important subtlety: a simple row does
not store a full field descriptor for the tail, only a base, because a required unknown
field would demand that every absent label be present, which is uninhabited. So the tail
projects to `Field_desc.opt unknown` (`repr.ml:626-630`). This is exactly Hack's
open-shape tail (`shape(..., ...)`): `unknown = Top` is a fully open shape, `unknown =
Bottom` is a closed shape.

A **splat** is an ordered list of spread elements. Each element is `Spread base`. If the
base is `Shape (Row_simple s)` the element is an inline row; if it is `Rigid a` it is a
spread of a generic parameter; if it is `Bottom` it is the bottom-row contribution
(`repr.ml:55-57`). Order matters: merge is rightmost-wins (Section 2).

### 1.3 Field descriptors and requiredness

`repr.ml:64-72`:

```
field_desc = { req : field_req ; base : base }
field_req  = Req | Opt
```

A field descriptor is a **product of two lattices**:

- **requiredness** is a presence lower bound. `Req` means the field is definitely
  present; `Opt` means it may be absent. The order is `Req < Opt` with `Req` as bottom
  and `Opt` as top (`repr.ml:75-88`, `field_req.ml` counterpart in
  `Field_req`, `repr.ml:438-463`).
- **base** is a type upper bound: the largest type the field's value can have.

Requiredness subtyping: a subfield must be at least as present as the superfield, so
`sub.req <= super.req`, i.e. `Req <= Opt` (`Field_req.lteq`, `repr.ml:449-454`). A
required field can be supplied where an optional one is expected, not the reverse. In
Hack terms, a required shape field is a subtype of the same field marked optional.

The pairing of presence and type as two independent coordinates is the key design
choice. TypeScript conflates "optional" with "`| undefined`", which is a known bug
source; keeping presence separate from type makes merge a clean product operation
(Section 2.3) and makes the corner reasoning for parameters clean (Section 4).

### 1.4 Distinguished rows

- `Row.Simple.top` (`repr.ml:596`): all fields optional at `Top`. The largest row.
- `Row.Simple.empty` (`repr.ml:601`): no known fields, `unknown = Bottom`. This is the
  **merge identity**, not bottom. It is not uninhabited, because `Opt Bottom` is
  satisfied by an absent field.
- `Row.bot` (`repr.ml:845`): the bottom row, represented as `Row_splat [Spread Bottom]`.
  It shape-wraps to `Base.Bottom`.

Note the asymmetry: a simple row is never bottom (`is_bot_row`, `repr.ml:229-232`);
bottom-ness lives only in `Base.Bottom` and in a splat that carries a `Spread Bottom`.

---

## 2. The merge algebra

Merge is the semantic content of spread: it collapses a splat (an ordered list of rows)
into one simple row, rightmost-wins and non-commutative. It is a monoid with
`Row.Simple.empty` as identity.

### 2.1 Requiredness meet and join

`repr.ml:75-88`. Two-point lattice, `Req` bottom, `Opt` top:

- `meet_field_req`: `Req` if either is `Req`, else `Opt`.
- `join_field_req`: `Opt` if either is `Opt`, else `Req`.

These are exercised exhaustively (only four inputs) in `field_req_tests`
(`test_splat.ml:592-624`).

### 2.2 Union, meet, join on base types

**Union** (`union_base`, `repr.ml:105-111`) is the join for base types. It is kept as a
first-class `Union` constructor and left-associated so that canonicalization is
idempotent. Crucially Splat does **not** distribute a union into a shape:
`Shape r1 | Shape r2` is never rewritten to `Shape (r1 join r2)`. That rewrite is only
denotation-preserving when the two rows differ in at most one field; with two or more
differing fields it invents cross terms and becomes unsound. The long comment at
`repr.ml:90-104` gives the counterexample
`{x:Bool,y:Bool} | {x:Nat,y:Nat}` versus `{x:Bool|Nat, y:Bool|Nat}`. Keeping unions
first-class is why join never needs to distribute.

**Meet** (`meet_base`, `repr.ml:113-126`): there are no intersection types, so meet
distributes structurally: `Bottom` absorbs, `Top` is identity, two shapes meet
field-wise (`meet_row`), a union meets by the distributive law
`(a|b) & t = (a&t)|(b&t)`, equal prims meet to themselves, unequal prims and a
prim-vs-shape meet to `Bottom`. Meeting with a `Flex` or `Rigid` leaf is unrepresentable
(no intersection type is available to express it) and raises; the caller must solve or
instantiate the variable first.

**Meet and join of rows** (`meet_row_simple`, `join_row_simple`, `repr.ml:142-182`):
pointwise over labels using `Label.Map.merge`, with an absent label filled in from the
row's `unknown` at `Opt`. Both are only defined on simple rows; a splat that still
carries a variable or parameter has no single-row meet or join and raises. Callers
canonicalize (which resolves solved splats to simple rows) first.

`Base.meet` and `Base.join` (`repr.ml:406-416`) canonicalize inputs and output, because
meeting two shapes can produce an uninhabited shape (a field meets to `Req Bottom`) that
must collapse to `Bottom` to keep `is_bot` correct.

These laws are checked as lattice properties against a brute oracle in `algebra_props`
(`test_splat.ml:655-808`): commutativity, associativity, idempotence, absorption,
identities, and "meet is the greatest lower bound" / "join is the least upper bound".

### 2.3 Merge on field descriptors (rightmost-wins)

This is the heart of spread. `merge_field_desc` (`repr.ml:184-191`):

```
merge ~left ~right =
  match right.req with
  | Req -> right                                    -- required right overrides fully
  | Opt -> { req = meet left.req Opt ; base = union left.base right.base }
```

Read it as: the right operand is the later spread. If the right field is **required**,
it completely overrides the left (it is definitely present with its own type). If the
right field is **optional**, the merged field's presence follows the left field
(`meet left.req Opt = left.req`) and its type is the **union** of both, because at
runtime the value is either the left one (right absent) or the right one (right present).

This is structurally identical to TypeScript's object-spread rule and to Hack's shape
spread: a required later key wins, an optional later key unions with the earlier type and
keeps the earlier presence. Because presence is a separate coordinate here, there is no
need for TypeScript's `\ undefined` workaround.

`Row.Simple.empty` (`Opt Bottom` everywhere) is the two-sided identity: merging it in
changes nothing (`meet left.req Opt = left.req`, `union base Bottom = base`). Verified as
`Field_desc.merge identity (opt bottom)` and `Simple.merge identity (empty row)`
(`test_splat.ml:735-807`).

### 2.4 Merge on simple rows

`merge_row_simple` (`repr.ml:193-209`): merge the `known` maps label-wise with
`merge_field_desc`, filling an absent label from that side's `unknown` at `Opt`, and set
the merged `unknown` to the union of the two `unknown` bases (both are optional, so the
union of bases is right).

### 2.5 Projection through a splat

Merging an entire splat is done lazily, one label at a time, by `Splat.proj`
(`repr.ml:792-816`). To project label `l`:

1. Start from the **rightmost** element's field at `l` (`Splat_elem.proj`,
   `repr.ml:682-690`: a rigid parameter reads its corner assignment from the passed
   `fld_assignment` map; `Bottom` yields `Req Bottom`; an inline simple row projects
   normally).
2. Walk leftward, folding with `merge_field_desc ~left ~right`.
3. **Short-circuit on `Req`** (`proj_help`, `repr.ml:792-804`): once the accumulated
   field is `Req`, everything further left is masked and cannot contribute, so stop.

The `None` label case projects the tail (`unknown`), always optional.

### 2.6 Canonicalization

`canon_base` / `canon_row` / `canon_row_splat` (`repr.ml:284-347`) put a type into a
normal form so that predicates like `is_bot` and structural equality are meaningful. The
important moves:

- A `Shape` whose row is bottom collapses to `Bottom` (`repr.ml:288-290`).
- A splat with a single concrete spread collapses to that simple row
  (`repr.ml:299-305`, `row_splat` at `repr.ml:260-267`).
- A `Spread (Shape (Row_splat ...))` is flattened: the inner splat's elements are
  spliced into the parent (`collapse_spread`, `repr.ml:312-316`).
- Adjacent concrete spreads are eagerly merged (`canon_row_splat_help`,
  `repr.ml:336-347`, using `merge_splat_elem_opt`, `repr.ml:250-258`). Two spreads merge
  only when both are concrete inline rows; an opaque `Rigid`/`Flex` spread or a
  `Spread Bottom` blocks the merge and stays a separate element.

Canonicalization is idempotent and denotation-preserving, checked in
`test_splat.ml:756-765` and (for the new spread paths) `canon_spread_tests`
(`test_splat.ml:1210-1233`). After canon, "is this row bottom" is decidable by inspecting
for a `Spread Bottom` (`is_bot_row`, `repr.ml:229-232`).

`tell_subtype` / `tell_subrow` (`splat.ml:955-970`) canonicalize their inputs once at the
top; the recursion below assumes it only ever sees canonical (flat) splats.

---

## 3. Subtyping: base types

`subtype_base ~sub ~super env` (`splat.ml:169-223`) returns an updated env and a residual
`Prop.t` (a proposition over the remaining flex variables; Section 5). When there are no
flex variables the residual is either `valid` or `invalid`.

**Rule order matters.** The comment at `splat.ml:169-174` fixes it. The cases, in order:

1. `Bottom <: _` is valid; `_ <: Top` is valid (`splat.ml:176-179`).
2. Any `Flex` on either side records a residual atom and stops (`splat.ml:181`). A flex
   sub against a union is recorded as a single upper bound, **not** split into a
   disjunction (splitting would be incomplete). Solving happens later, in dispatch.
3. A **sub-union** is conjunctive and exact: `(a|b) <: t` iff `a <: t` and `b <: t`
   (`splat.ml:183-186`).
4. A **super-union** distributes disjunctively: `t <: (a|b)` becomes `t <: a` or `t <: b`
   (`splat.ml:187-190`). This is checked after the sub-union and flex cases so that the
   disjunction never hides a variable that should have been recorded.
5. **Rigid parameters (F-sub)**: reflexivity, then reduce through the bound
   (Section 4.1).
6. Eager ground rejections: `_ <: Bottom` for non-bottom, `Top <: _` for non-top, prim
   mismatches, prim-vs-shape (`splat.ml:204-221`).
7. `Shape sub <: Shape super` reduces to `subrow` (`splat.ml:223`).

The ground fragment of this relation is checked to be sound and complete against an
independent brute oracle (`gsub`) over 25000 random pairs
(`ground differential: check = brute`, `test_splat.ml:449-455`), plus reflexivity,
bottom-least, top-greatest, transitivity, and the lattice laws.

---

## 4. Subtyping: the type-parameter cases

This section is the focus. It covers two mechanisms: the **F-sub rule** for a parameter
at base position, and the **corner decision procedure** for parameters spread into a row.

### 4.1 F-sub: a rigid parameter at base position

`splat.ml:191-223`. A `Rigid a` is a generic with an interval `[lower, upper]` of base
types recorded in `env.ty_param` (`Env.Ty_param_env`, `env.ml:4-80`). The rule:

- `Rigid a <: Rigid b` with `a = b`: reflexivity, valid (`splat.ml:197`).
- `Rigid a <: T`: reduce through `a`'s **upper** bound: `a <: upper(a) <: T`, so check
  `upper(a) <: T` (`splat.ml:198-200`).
- `T <: Rigid b`: reduce through `b`'s **lower** bound: `T <: lower(b) <: b`, so check
  `T <: lower(b)` (`splat.ml:201-203`).
- `Rigid a <: Rigid b` distinct: chains both, reducing to `upper(a) <: lower(b)`.

This is standard bounded-quantification F-sub. Reflexivity is tried first so a parameter
whose bounds are loose is still a subtype of itself. Tests: `fsub_tests`
(`test_splat.ml:824-838`), including the distinct-parameter chain.

Hack already implements exactly this for generic parameters with `as`/`super` bounds. The
only new thing is that a parameter can also appear at **spread** position, which is the
next subsection.

### 4.2 Why parameters spread into a row need corners

Consider a subrow query where a parameter is spread into one or both sides:

```
|{x: Req Bool}, ...T|  <:  |...T|     with T in [ {x: Req Nat} , {x: Opt Top} ]
```

For this to hold it must hold **for every instantiation** of `T` inside its bound
interval. `T`'s field at `x` is a field descriptor somewhere in the box between the
lower bound `Req Nat` and the upper bound `Opt Top`. Because the field descriptor is a
product of two lattices (requiredness and type), that box has up to **four corners**:

```
1) Req Nat   (lower bound)
2) Opt Nat   (top requiredness, bottom type)
3) Req Top   (bottom requiredness, top type)
4) Opt Top   (upper bound)
```

The worked example in `repr.ml:513-568` shows that checking only the two bounds (corners
1 and 4) reaches the wrong answer: both bounds pass, but corner 2 (`Opt Nat`) makes the
proposition false. So in general all four corners of the box must be checked.

The justification for corners being sufficient (rather than checking the whole
continuum) is monotonicity: `<:` is monotone in each field-descriptor coordinate, so the
worst case for `L <: R` sits at an extreme of each parameter's box. That is why a
parameter appearing on **one side only** collapses to a single corner (Section 4.5), and
a parameter on **both sides** needs the full box (up to four).

`Field_desc.corners ~lower ~upper` (`repr.ml:569-577`):

```
corners ~lower ~upper =
  match lower.req, upper.req with
  | Req, Opt -> [ lower ; upper ; {lower with Opt} ; {upper with Req} ]   -- 4
  | Req, Req | Opt, Opt -> [ lower ; upper ]                              -- 2
  | Opt, Req -> []                                                        -- ill-formed
```

The four-corner case only arises when the lower bound is `Req` and the upper is `Opt`
(a genuinely two-dimensional box). Tests: `corners_tests` (`test_splat.ml:627-652`) and
the property that every corner lies in the `[meet, join]` box
(`test_splat.ml:744-754`).

### 4.3 The per-label decision procedure

`subrow_splat` (`splat.ml:248-260`) reduces a splat-vs-splat (or splat-vs-simple) subrow
to a conjunction of per-label subfield checks, one per label in `subrow_label_set`
(Section 4.7). Each label is handled by `subrow_splat_at` (`splat.ml:319-396`), which is
the interesting procedure. At one label:

1. **Restrict to live parameters** (`splat.ml:322-324`). Only parameters that could
   still contribute to this label matter. `Row.live_spread_at` (`repr.ml:773-790`) walks
   a splat from the right and collects rigid parameters that are not masked by a `Req`
   field to their right. A `Req` inline field, or a `Spread Bottom`, masks everything to
   its left (rightmost-wins). Parameters to the left of such a required field cannot
   affect the merged field at this label, so they are dropped from the enumeration. This
   is condition (2) in the design comment at `splat.ml:271-277`.

2. **Topologically sort** the live parameters (`splat.ml:326-332`). Parameters can appear
   in each other's bounds (a parameter `Q` whose bound spreads `P`). The bound projection
   of one parameter reads the corner assignment of the parameters in its bound, so those
   must be assigned first. `Env.Ty_param_env.closure` (`env.ml:46-56`) pulls in every
   parameter reachable through bounds (so a parameter that lives *only* inside another's
   bound is included), and `Env.Ty_param_env.topo` (`env.ml:58-79`) orders them so
   dependencies come first.

3. **Compute the dependency set** `depended_on` (`splat.ml:335-339`): parameters that
   appear in some other parameter's bound. A parameter not in this set is "free": its
   assignment does not feed any other parameter's projection, so its corners can often be
   pruned (Section 4.5).

4. **Enumerate corner assignments** in topological order (`loop_params` /
   `loop_assignments`, `splat.ml:365-395`). For each parameter, `corners_for`
   (Section 4.5) returns the corner field descriptors to try; the loop takes the product
   over all live parameters, extending the `fld_assignment : field_desc Ty_param.Map.t`
   incrementally. Because the order is topological, when a parameter's corners are
   computed the parameters in its bound already have assignments in the map.

5. **At a complete assignment**, project both rows at the label with that assignment
   (`Row.proj`, which runs the merge of Section 2.5 with the parameters resolved to their
   corner fields) and emit the subfield check (`splat.ml:377-382`, `subfield` at
   `splat.ml:863-874`).

The whole thing is a conjunction: the subrow holds iff the subfield check passes at every
label and every corner assignment. This is co-assignment exponential in the number of
live, dependent parameters, but in practice the count is small and the shortcuts below
prune it hard.

### 4.4 Field bounds of a parameter at a label

`Row_help.field_bounds` (`splat.ml:67-77`): to get a parameter's `[lower, upper]` box at
a label, read the parameter's bound shapes from `env.ty_param`, take the row of each
bound (`row_of_base`, `splat.ml:23-29`: a shape's row, or the bottom row for `Bottom`),
and project each at the label under the current partial assignment (so a parameter whose
bound itself spreads another parameter is resolved through that parameter's already-fixed
corner). The result is the field-descriptor interval whose corners drive the enumeration.

### 4.5 The corner shortcuts (`corners_for`)

`corners_for` (`splat.ml:342-361`) applies the four optimizations described in the design
comment at `splat.ml:280-317`. Let a parameter be `free` (not in `depended_on`),
`in_sub` (live in the sub-row at this label), `in_super` (live in the super-row):

- **(i) sub side only** (`free && in_sub && not in_super`): use `[upper]` only. A
  parameter appearing only in the sub-row contributes only to the left of `<:`; the
  hardest case is the one that maximizes the sub-row, which is the parameter's upper
  bound.
- **(i) super side only** (`free && not in_sub && in_super`): use `[lower]` only.
  Symmetric: the hardest case minimizes the super-row, the lower bound.
- **both sides** (`free && in_sub && in_super`): look at masking (Section 4.6):
  - **(ii) masked in sub** (`masking_sub = Masked`): the parameter cannot contribute to
    the sub-row (something to its right always overrides it there), so it acts as a
    super-only parameter: use `[lower]`.
  - **(iii) masked in super** (`masking_super = Masked`): symmetric, super side is fixed,
    acts as sub-only: use `[upper]`.
  - **(iv) unmasked in super with optional lower** (`masking_super = Unmasked` and
    `lower.req = Opt`): use `[lower]`. When the parameter can never be masked on the super
    side and its lower requiredness is already `Opt`, the field's *presence* is settled
    (the merge stays `Opt`), so only the *type* matters, and the type that is hardest for
    `<:` is the one that grows the super side least, the lower bound. The proposition has
    the shape `(U | t) <: (V | t)` where `t` is this field's type, and a free `t` is
    worst-cased at its lower bound.
  - otherwise: the full `corners ~lower ~upper` (up to four).
- **not free**: the full `corners`. A depended-on parameter cannot be pruned, because its
  choice changes another parameter's projected bounds.

These shortcuts are what keep the enumeration cheap: a parameter that occurs on only one
side, or is masked, collapses from four corners to one.

### 4.6 Masking

`Row_help.masking_of` / `masking_of_splat` (`splat.ml:99-165`). At a label and a partial
assignment, a parameter within a row is:

- **Masked**: some parameter to its right is `Req` in its **upper** bound at this label,
  so that rightward parameter always supplies the field and overrides everything to the
  left. The target cannot contribute.
- **Unmasked**: every parameter to its right is `Opt` in its **lower** bound, so nothing
  to the right can override it.
- **Unknown**: neither holds (some rightward parameter has `Req` lower but `Opt` upper, so
  we cannot tell), or the target does not occur in this row. Unknown is the pessimistic
  answer and blocks the single-corner shortcuts, falling back to full corners.

The key subtlety (`splat.ml:133-144`): whether a rightward parameter `r` masks the target
is decided by `r`'s **own** bounds at this label, not the target's. If `r`'s upper is
`Req` it masks; if `r`'s lower is `Req` but upper `Opt` the answer is Unknown; if `r` is
`Opt` at both bounds it does not mask. This is regression-tested by
`masking: rightward param's bounds decide` (`test_splat.ml:970-976`) and its mirror
(`test_splat.ml:980-988`).

### 4.7 Discovering labels that live only in bounds

`Row.label_set` of a splat of bare parameter spreads is empty (a `Spread (Rigid a)` has
no inline labels). But a label can live only inside a parameter's bound, and it is still
a real per-field obligation. `Row_help.bound_label_set` (`splat.ml:38-51`) collects the
labels of every bound of every parameter mentioned, taking the transitive closure so a
label buried in a nested parameter's bound is found too. `subrow_label_set`
(`splat.ml:55-60`) unions the inline labels of both rows with these bound labels, and the
per-label loop visits all of them plus the `None` (unknown-tail) pseudo-label
(`subrow_labels`, `splat.ml:62-64`). Missing this would silently skip a field and be
unsound; tested by `bound-only label is discovered` (`test_splat.ml:989-993`).

### 4.8 Nested parameters

A parameter `Q` whose bound spreads `P` (so `P` is reachable only through `Q`'s bound) is
"the hard case". The transitive `closure` (Section 4.3 step 2) pulls `P` into the
enumeration, `topo` orders `P` before `Q`, and `field_bounds` projects `Q`'s bound with
`P` already assigned to a corner. Without the closure the engine would raise. This is
covered by `nested_tests` and the `nested-param differential` property over 20000 cases
(`test_splat.ml:1120-1158`), whose oracle universally quantifies `P` with `Q` determined
by `P`.

### 4.9 The differential oracle for parameters

`rigid-param differential: check = forall-instantiation` (`test_splat.ml:1023-1035`,
generator at `test_splat.ml:840-932`) is the strongest correctness check on the corner
machinery. It places parameters `P`, `Q` at spread position with random shape-bound
boxes, then compares the engine's decision against a **true denotational oracle** that
enumerates every instantiation of `P` and `Q` within their boxes and checks the concrete
subrow at each. This exercises corner enumeration, the masking shortcut, bound-label
discovery, and single-side drops, over 20000 random cases. The engine agrees exactly:
the corner procedure decides the semantic (universally quantified) subrow.

---

## 5. Subtyping: simple rows

`subrow_simple` (`splat.ml:833-856`) is the ground case: both sides are concrete simple
rows. It checks the subfield relation at every known label of either side plus the `None`
tail. `subfield` (`splat.ml:863-874`): a subfield holds iff `sub.req <= super.req`
(presence) and `sub.base <: super.base` (type), the two coordinates checked
independently. Projection at an absent label uses the row's `unknown` at `Opt`
(`Simple.proj`, `repr.ml:626-630`).

This is the same shape-subtyping Hack already does for `shape(...)` types: required beats
optional, field types are checked covariantly, and the open tail governs unknown fields.

---

## 6. Inference

Inference has two independent axes: **flex variables** (unification variables at base
position) and **spread variables** (a flex variable spread into row position). Both are
driven through a residual proposition.

### 6.1 The residual proposition

`prop.ml`. `subtype_base` and friends do not decide immediately when a flex variable is
involved; they emit a `Prop.t`:

```
atom = Subtype_base { sub ; super } | Subfield { label ; sub ; super }
t    = Atom of atom | Conj of t list | Disj of { failures ; props }
```

`valid = Conj []`; `invalid f = Disj { failures = [f]; props = [] }`. Smart constructors
`conj` / `disj` (`prop.ml:41-81`) keep the tree normalized: `conj` drops valids and folds
nested conjunctions, `disj` collects alternatives. `is_invalid` is "a `Disj` with no
surviving alternatives" (`prop.ml:29-33`); `is_valid` is "the empty `Conj`". A query is
**accepted** when its final proposition is not invalid (`test_splat.ml:16-18`).

### 6.2 Flex variables and dispatch

After building the residual, `tell_prop` (`splat.ml:883-952`) walks it, records each
variable constraint in the env, and for every genuinely new bound generates the implied
transitive constraint and dispatches that in turn. The recursion terminates because
implications are generated **only for newly recorded bounds**.

- `tell_atom_base` (`splat.ml:921-929`): a `Flex <: Flex` records an upper on the left
  variable and a lower on the right; a one-sided flex records one bound; a ground atom is
  re-checked by `subtype_base`.
- `record_ty_upper` (`splat.ml:931-940`): add `super` as an upper bound of `var_sub`; if
  new, for each existing lower bound `l` of the variable emit `l <: super` (transitivity
  through the variable) and dispatch.
- `record_ty_lower` (`splat.ml:942-952`): the mirror.

Bounds are stored per variable in `Env.Ty_var_env` (`env.ml:82-110`) as lists of uppers
and lowers. This is the standard "constraint graph, propagate on new edge" approach and
matches how Hack's `Typing_subtype` records `Tvar` bounds and closes them transitively.
The transitive-closure behaviour is checked in `flex transitive closure`
(`test_splat.ml:427-431`) and the full soundness-and-completeness closure over 25000
cases in `flex differential: check = exists-witness` (`test_splat.ml:483-493`).

### 6.3 Solving a flex variable

`Env.solve_ty_var` (`env.ml:143-152`): a variable's current solution is read **only** from
bounds already discharged into the env, never from undischarged residual atoms. With
lower bounds present, the solution is the join of the lowers (the least value satisfying
them); with only uppers, the meet of the uppers; with neither, `Bottom`. Bounds are
resolved recursively before combining (`resolve_base`, `env.ml:207-214`), so meet and
join only see ground types. Termination relies on the bound graph being acyclic. The
recovered solution is checked to be a real witness in `solution is a witness`
(`test_splat.ml:497-507`).

### 6.4 Spread variables: overview

A spread variable is a flex variable in spread position (`Spread (Flex v)`), standing for
an unknown row to be merged in. The dispatch in `subrow` (`splat.ml:226-241`) branches on
which sides carry spread variables:

- neither side: ground splat, go to `subrow_splat` (Section 4).
- sub side only: `subrow_infer_sub` (Section 6.5).
- super side only: `subrow_infer_super` (Section 6.6).
- both sides: `subrow_infer_sub_super` (Section 6.7).

For inference we want at most one spread variable per side; `topo_spread_vars`
(`repr.ml:737-748`) picks an order and `rightmost_spread_var` (`repr.ml:759-768`) is the
one to retain when eliminating (Section 6.8).

### 6.5 A spread variable in the sub-row

`subrow_infer_sub` / `subrow_infer_sub_part` / `subrow_infer_sub_part_at`
(`splat.ml:402-582`). For a variable `?p` in the sub-row, partition the sub-row at `?p`
into `pre` (left of `?p`) and `post` (right of `?p`). Resolve every *other* variable to
its current solution (`solve_all_except`, `splat.ml:11-15`), leaving `?p` symbolic. Then,
per label, build the field descriptor that `?p` must have and accumulate a residual
constraint `?p <: shape({those fields})` (`splat.ml:459-463`).

At a single label (`subrow_infer_sub_part_at`, `splat.ml:475-582`) the logic mirrors the
merge:

- If `post` **requires** the field at every corner assignment, `?p` is overwritten
  everywhere (masked to its right), so its upper bound is `Top` and we just check
  `post <: super` per assignment.
- Otherwise `?p` contributes. Its requiredness is forced to `Req` when the field is `Req`
  in super and `pre` cannot supply it, or when `pre`'s type does not fit super so `?p`
  must be `Req` to mask the offending `pre` field. This is the `must_req` disjunction at
  `splat.ml:522-546`. The merged sub type at this label is the union of the fresh variable
  for `?p`, `post`, and (unless `?p` is `Req`) `pre`, checked against super.

The unknown-tail label (`None`) never forces `Req`, because a required unknown is
unrepresentable (`splat.ml:526-531`).

The sub-side masking fix at `splat.ml:536-545` (force `?p` to `Req` to mask a
type-violating neighbour) is what makes the single-row-variable relation exactly complete;
without it there was a residual over-reject. The round trip is checked in
`row differential (1 var): check_row = exists-witness` over 20000 cases
(`test_splat.ml:508-520`) and `row solving: recovered solution is a witness`
(`test_splat.ml:527-538`).

### 6.6 A spread variable in the super-row

`subrow_infer_super` and helpers (`splat.ml:585-765`) are the dual. For `?p` in the
super-row, per label build the field `?p` must have as a **lower** bound and accumulate
`shape({those fields}) <: ?p`. The requiredness of `?p`'s field mirrors the sub-row: `Opt`
if some live sub field is `Opt` (the merge must then stay `Opt`), else `Req`. An
unsatisfiable case is a sub field that is `Opt` while super `pre` is `Req` (the merge is
forced `Req`, and `Opt` is not `<= Req`), reported invalid at `splat.ml:731-743`.

### 6.7 Spread variables on both sides

`subrow_infer_sub_super` (`splat.ml:805-829`):

- **Exactly one variable on each side**: couple them through a fresh intermediary row
  `mid` (`subrow_infer_couple`, `splat.ml:779-803`). Build a fresh field per label in
  `mid`, check `sub <: mid` (which bounds `?p_sub` from above) and `mid <: super` (which
  bounds `?p_super` from below); `mid`'s fresh variables link the two so the sub side's
  contribution flows through to the super side. Sound by transitivity
  `sub <: mid <: super`. `mid`'s known fields are fixed `Req` (measured to over-reject
  much less than `Opt`), and its unknown is a fresh opt-only base. This keeps the
  cross-side coupling that the naive elimination would drop.
- **More than one variable on a side**: fall back to the **decoupled** solver
  (`splat.ml:816-829`). Resolve the other side's variables to their current solutions
  (an unsolved one becomes the empty row) to make it concrete, then solve this side
  against it. Sound, but the cross-side coupling is dropped, so this over-rejects. The
  two-variable case is checked sound-only in
  `row differential (2 vars): SOUND (accept implies witness)` (`test_splat.ml:539-549`),
  and the `DIAG` harness (`test_splat.ml:1309-1377`) quantifies the over-reject rate.

### 6.8 Resolving a spread variable to a row

`Env.resolve_spread_var` (`env.ml:170-196`): a spread variable resolves to its current
solution **as a row**. The bounds are combined at the **row** level: `Row.join` of the
lower shapes (covariant) or `Row.meet` of the upper shapes (contravariant), not via
`Base.join` / `Base.meet`. This matters because base join does not distribute into shapes,
so combining two lower shapes with `Base.join` would produce an ill-formed
`Spread (Union (Shape, Shape))`; the row join is the correct single-row bound. An unbounded
spread variable resolves to the **empty row** (the merge identity, contributing nothing),
not bottom. Tested by `spread var with 2 distinct lowers stays a single row`
(`test_splat.ml:996-1019`).

`resolve_row` / `resolve_base` (`env.ml:156-214`) substitute every flex variable by its
current solution recursively, with an optional `except` to leave the one variable being
solved untouched.

---

## 7. Known incompleteness (deliberate)

These are the facts the engine drops to stay sound and decidable. Each is documented in
the source with a counterexample.

1. **No shape/union distribution** (`repr.ml:90-104`). `Shape r1 | Shape r2` is never
   folded to `Shape (r1 join r2)`. Sound only when the rows differ in at most one field;
   otherwise it invents cross terms. Consequence: `{x:Bool} | {x:Nat}` is not recognized
   as a subtype of `{x: Bool|Nat}`.

2. **Both-sides spread coupling** (`splat.ml:769-778`). With one spread variable on each
   side the intermediary `mid` fixes its known fields to `Req`, which over-rejects some
   satisfiable queries (the "keep-both requiredness" gap). With more than one variable on
   a side the coupling is dropped entirely (Section 6.7). Both are sound.

3. **Meet with a variable or parameter raises** (`repr.ml:122-125`). There are no
   intersection types, so an unrepresentable meet is a hard error; the caller must solve
   or instantiate first.

None of these affect soundness. The ground and single-spread-variable fragments are
complete (Sections 4.9, 6.5); the multi-variable both-sides fragment is sound only.

---

## 8. Test strategy

The tests are the specification of correctness and are worth porting as differentials:

- **Ground differential** (`test_splat.ml:449-455`): engine vs brute per-field oracle
  `gsub`, 25000 cases. Sound and complete on ground types.
- **Flex differential** (`test_splat.ml:483-493`): a variable query is accepted iff some
  chain assignment satisfies it, 25000 cases.
- **Solution is a witness** (`test_splat.ml:497-507`): the solved value really satisfies
  the query.
- **Row differential, 1 var** (`test_splat.ml:508-520`): the single-spread-variable
  relation is a full iff.
- **Rigid-param differential** (`test_splat.ml:1023-1035`): the corner procedure vs a
  universally quantified denotational oracle over parameter instantiations, 20000 cases.
  This is the strongest check on the type-parameter machinery.
- **Nested-param differential** (`test_splat.ml:1146-1158`): the transitive closure case.
- **Row differential, 2 vars** (`test_splat.ml:539-549`): sound only; the `DIAG` harness
  quantifies the over-reject.
- **Algebra laws** (`test_splat.ml:655-808`): merge, meet, join, canon, corners against
  the lattice laws and the brute order oracle.

The pattern to reuse: an independent brute oracle that enumerates the finite witness
universe (ground types, or all instantiations of a bounded parameter) and a
`check = oracle` differential. That is what ties the corner procedure to the semantic
subrow relation.

---

### Hphp Doc Coding Conventions (`hphp/doc/coding-conventions.md`)

HHVM Coding Conventions
=======================

This document is meant to serve as a guide to writing C++ in the HHVM codebase,
covering when and how to use various language features as well as how code
should be formatted. Our goal is to ensure a consistently high-quality codebase
that is easy to read and contribute to, especially for newcomers.

The HHVM codebase contains a wide variety of code from many different authors.
It's been through a few different major stages in its life, including stints in
multiple different repositories. As a result, large (primarily older) parts of
the codebase do not fit this guide. When in doubt about how to write or format
something, always prefer the advice here over existing conventions in the
code. If you're already touching some older code as part of your work, please
do clean it up as you go along. But please do not spend hours applying the
formatting guidelines here to code you aren't otherwise modifying. While we'd
love for the entire codebase to follow this guide, we'd rather get there
gradually than lose lots of git history and developer time to purely cosmetic
changes. That said, if cosmetic changes that you're making as part of a larger
diff keep growing in scope, it may be worth pulling them out into a separate
diff.

There's no well-defined cutoff here - just try to minimize effort for your
reviewers. A good rule of thumb is that if your cosmetic changes require adding
significant new sections to the diff (such as a function rename that touches
all callsites), it should probably be pulled out into its own diff.


## Headers ##

Every .cpp file in the HHVM repository should have a corresponding .h file with
the same name, and which declares its public interfaces. We tend to value API
documentation more heavily than inline implementation comments, so *all*
declarations in headers (classes, enums, functions, constants, etc.)  should be
documented. See Comments and Documentation for more details.

Build times are a frequent source of pain in many large C++ projects. Try not
to make large header files that mostly serve to include groups of other large
header files. This can discourage "include what you use," discussed in the
"What to include section".

### Include guards ###

To prevent multiple inclusion, all headers should have the following directive
after their license header comment:

```cpp
/*
 * ...see the 'File copyright' section for details on what goes here...
 */

#pragma once

// File contents
```

### What to include ###

The golden rule for what to include is "include what you use" (IWYU). In brief,
this means you should not rely on any headers you include to transitively
include other headers which have definitions you require. You should also
prefer to forward declare structs and classes when the definition is not needed
(so, "don't include what you don't use"), which helps reduce HHVM's nontrivial
build time.

To make it easier to achieve IWYU, we have the following guidelines for
includes:

- Always include the corresponding .h for a .cpp first, before even system
  headers.
- Separate includes into groups: C++ standard library headers, external projects
  (such as Boost and Intel TBB), and finally headers within HHVM. Each group
  should be separated by a newline, for readability. (Whether to separate HHVM
  includes by subsystem (e.g., `jit`) is left up to the author.)
- Keep headers alphabetized within each group. This makes it easier to ensure
  that all necessary includes are made, and no extraneous ones are left behind.
- Use double quotes for Folly and HHVM headers and angle brackets for all
  others.

As an example, here is what the include section might look like for a file
named `bytecode.cpp`:

```cpp
#include "hphp/runtime/vm/bytecode.h"

#include <cstdio>
#include <string>

#include <boost/program_options/options_description.hpp>

#include "hphp/runtime/vm/class.h"
#include "hphp/runtime/vm/func.h"
#include "hphp/runtime/vm/hhbc.h"
#include "hphp/util/string.h"
```

### Inline functions ###

Defining functions inline is encouraged for very short functions.

When defining inline member functions on structs or classes which have tight,
compact interfaces (e.g., a smart pointer class, or any wrapper class), prefer
to define the functions in the class definition, for concision.

However, for classes with more complex, malleable APIs where inline helpers
proliferate (e.g., Func, Class, IRInstruction, etc.), restrict the class
definition to member function prototypes *only*. This makes the API much
cleaner. For these classes, define all inline functions in a corresponding
`-inl.h` file.

```cpp
// At the bottom of func.h.

#include "hphp/runtime/vm/func-inl.h"
```

```cpp
// After the copyright in func-inl.h.

namespace HPHP {

// Definitions go here.

}
```

For API's large enough to warrant -inl.h files, move *all* definitions into the
-inl.h, even one-line accessors. This serves both to keep the API cleaner and
to avoid splitting implementations among three files (the header, the inline,
and the source).

Some files, with or without a corresponding -inl.h file, may need a -defs.h
file. This file also contains definitions of inline functions, but it is *not*
included by the main header. It is intended to be used when only a few callers
need access to the definitions, or when the definitions can't be in the main
header because it would create circular dependencies. It should be included
directly by the callers that do need access to the definitions it contains.


## Structs and Classes ##

Classes are used extensively throughout the HHVM codebase, with a number of
coding conventions. See also Naming for conventions around class naming.

### Using struct vs. class ###

In C++, `struct` and `class` have nearly identical meanings; the only
difference lies in the default accessibility (`struct` defaults to public, and
`class`, to private).

We do not assign further meaning to these keywords, so we use `struct`
everywhere.  Efforts to compile under MSVC also require that we use the same
keyword between a struct/class definition and its forward declarations due to
MSVC's failure to adhere to the C++ spec, and sticking to `struct` everywhere
makes this easier.

### Access control ###

Try to avoid the `protected` keyword. It tends to give a false sense of
security about encapsulation: since anyone can inherit from your class, anyone
can access the `protected` member with a little extra effort.

### Implicit and explicit constructors ###

By default, always use `explicit` for single-argument, non-initializer list
constructors.

```cpp
struct MyStruct {
  // We don't want to implicitly convert ints to MyStructs
  explicit MyStruct(int foo);

  // Two-argument constructor; no need for explicit
  MyStruct(const std::string& name, int age);
};
```

### Public data members vs. getters/setters ###

Prefer declaring public member variables to using getters and setters. Getters
and setters that don't manage object state in a nontrivial way serve to bloat
the API and introduce unnecessary boilerplate.

Getters are, of course, encouraged for private members. Avoid prefixing getters
with `get`:

```cpp
struct Func {
  const SVInfoVec& staticVars() const;
  void setStaticVars(const SVInfoVec&);

  ArFunction arFuncPtr() const;

  static constexpr ptrdiff_t sharedBaseOff();
};
```

### Declaration order ###

Adhere to the following order for declarations in a struct or class definition:

1. Friend classes.
2. Nested classes, enums, typedefs. (If possible, just declare the nested
   class and define it following the enclosing class definition.)
3. Constructors, destructor.
4. Member functions, including static functions, documented and grouped
   coherently.
5. Constants and static data members.
6. *All* instance data members, regardless of accessibility.

Private member functions can be interspersed with public functions, or
relegated to a single section before the data members. However, all instance
properties *must* occur contiguously at the end of the class definition.


## Other C++ Language Features ##

Very few language features are unconditionally banned. However, if you want to
use one of the more controversial constructs such as `goto` or `operator,()`,
you'd better have a convincing argument as to why it's better than the
alternatives. C++ is a very large and complex language and we don't want to
artificially limit what developers can do, but that puts a lot of
responsibility on your shoulders.

Avoiding restrictions on useful language features (e.g., exceptions, templates,
C++11 lambdas) is a major motivating factor for maintaining our own style guide
rather than adopting an existing one.

### Namespaces ###

All HHVM code should be scoped in `namespace HPHP { /* everything */ }`. Large
submodules such as `HPHP::jit` and `HPHP::rds` may be contained in their own
namespace within `HPHP`. We often use anonymous namespaces instead of the
`static` keyword to keep symbols internal to their translation unit. This is
mostly left up to the author; just keep in mind that classes and structs,
unlike functions and variables, *must* be in an anonymous namespace in order to
be properly hidden.

Avoid `using namespace` whenever possible, especially in headers. It is
acceptable in `.cpp` files in very limited scopes (function-level or deeper) if
it will significantly aid in readability of the code that follows. `using
namespace std;` at the top of a `.cpp` is explicitly disallowed.

### Enums ###

Prefer `enum class` whenever possible. Old-style enums are generally only
acceptable if you expect that your type will be frequently used in an integer
context, such as array indexing.


## Naming ##

HHVM code adheres to the some broad naming conventions.

When the convention is left open, in general, prefer the local conventions
used in the file you are working on---e.g., in a struct whose data members all
have `m_namesLikeThis`, prefer `m_anotherNameLikeThis` to `m_this_style`, even
though the latter is found in other parts of the codebase.

### Variables ###

Use `lowerCamelCase` or `lower_case_with_underscores` for all local variables,
adhering to whichever is the discernable local convention if possible.  Static
variables (whether declared in an anonymous namespace or with the `static`
keyword) should additionally be prefixed by `s` (e.g., `s_funcVec`).  Globals,
likewise, should be prefixed by `g_` (e.g., `g_context`).

### Constants ###

All constants should be prefixed with `k` and use `CamelCase`, e.g.,
`kInvalidHandle`. Prefer `constexpr` to `const` whenever possible.

### Class data members ###

As with variables, use `lowerCamelCase` or `lower_case_with_underscores` for
all data members. Additionally, private instance members should be prefixed
with `m_` (e.g., `m_cls`, `m_baseCls`, `m_base_cls`), and all static members
should be prefixed with `s_` (e.g., `s_instance`).  Prefer to leave public
members unprefixed.

### Functions ###

We generally prefer `lowerCamelCase` for header-exposed functions, including
member functions, although we use `lower_case_with_underscores` as well (e.g.,
`hphp_session_init`), more commonly in file-local scopes.  As usual, follow the
local naming conventions of the file you are working in.

If you are modeling a class after an existing pattern, such as an STL
container, prefer to follow the appropriate conventions (e.g.,
`my_list::push_back` is preferred over `my_list::pushBack`).

### Classes ###

Classes use `UpperCamelCase`, except when modeling existing patterns like STL
containers or smart pointers.

### Namespaces ###

New namespaces should use `lowercase`---and single-word namespaces are greatly
prefered for common usage.  For longer namespaces (e.g., `vasm_detail`), use
`lower_case_with_underscores`.

### Other conventions ###

Prefer correctly capitalizing acronyms in new code (e.g., prefer `IRTranslator`
to `HhbcTranslator`). In this vein, prefer `ID` (e.g., `TransID`) to `Id`
(e.g., `FuncId`) in new code.


## Formatting ##

While consistent code formatting doesn't directly affect correctness, it makes
it easier to read and maintain. For this reason, we've come up with a set of
guidelines about how code should be formatted. There's a good chance that some
of these will conflict with your own personal preferred style, but we believe
that having a consistently easy to read codebase is more important than letting
each developer write code that he or she thinks is uncompromisingly beautiful.

Anything not specified here is left up to the judgment of the developer.
However, this document is not set in stone, so if a particular formatting issue
keeps coming up in code review it probably deserves a few lines in here.

### General rules ###

- All indentation is to be done using spaces.
- Each indentation level is 2 spaces wide.
- Lines may be no longer than 80 characters, unless absolutely required for
  some syntactic reason.
- Lines should not have any trailing whitespace. This includes blank lines at
  non-zero indentation levels; the only character on those lines should be a
  newline.

### Types and variables ###

- When declaring a variable or typedef, the `*` and `&` characters for pointer
  and reference types should be adjacent to the type, not the name (e.g.,
  `const Func*& func`).
- Limit variable declarations to one per line.

### Function signatures ###

The following function signatures are formatted properly:

```cpp
// If arguments would fit on 1 line:
inline void Func::appendParam(bool ref, const Func::ParamInfo& info) {
}

// If the arguments need to wrap, we have two accepted styles, both of which
// are OK even if the wrapping wasn't necessary:
SSATmp* HhbcTranslator::ldClsPropAddr(Block* catchBlock,
                                      SSATmp* ssaCls,
                                      SSATmp* ssaName,
                                      bool raise) {
  doSomeStuff();
}

// This style is helpful if any of the function, argument, or type names
// involved are particularly long.
SSATmp* HhbcTranslator::ldClsPropAddr(
  Block* catchBlock,
  SSATmp* ssaCls,
  SSATmp* ssaName,
  bool raise
) {
  doSomeStuff();
}
```

Always keep the type on the same line as the function name, unless it would
leave insufficient room for arguments. Do likewise with other modifying
keywords (`inline`, `static`, any attributes).

Wrapped arguments should always be aligned with the argument on the previous
line. The opening curly brace should be on the same line as the last argument,
with the exception of class constructors (see the Constructor initializer list
section). When writing function declarations in headers, include argument names
unless they add no value:

```cpp
struct Person {
  // The single string argument here is obviously the name.
  void setName(const std::string&);

  // Two string arguments, so it's not obvious what each one is without names.
  void setFavorites(const std::string& color, const std::string& animal);
};
```

### Statements ###

Conditional and loop statements should be formatted like so:

```cpp
if (vmpc() == nullptr) {
  fprintf(stderr, "whoops!\n");
  std::abort();
}
```

Note that there is a single space after the `if` keyword, no spaces between
`condition` and the surrounding parentheses, and a single space between the `)`
and the `{`. As with all blocks, the body should be one indentation level
deeper than the `if`. If the *entire* statement (condition and body) fits on
one line, you may leave it on one line, omitting the curly braces. In all
other cases, the braces are required. For example, the following are OK:

```cpp
if (obj->_count == 0) deleteObject(obj);

for (auto block : blocks) block->setParent(nullptr);
```

But these are not acceptable:

```cpp
if (veryLongVariableName.hasVeryLongFieldName() &&
    (rand() % 5) == 0) launchRocket();

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
  goto fail;
```

Avoid assignments in conditional expressions, unless the variable is declared
within the condition, e.g.,

```cpp
if (auto const unit = getMyUnit(from, these, args)) {
  // Do stuff with unit.
}
```

Prefer C++11 foreach syntax to explicit iterators:

```cpp
for (auto const& thing : thingVec) {
  // Do stuff with thing.
}
```

### Expressions ###

- All binary operators should have one space on each side, except for `.`,
  `->`, `.*`, and `->*` which should have zero.
- Do not include redundant parentheses unless you think the expression would be
  confusing to read otherwise. A good rule of thumb is that if you and/or your
  reviewers have to look at a chart of operator precedence to decide if the
  expression parses as expected, you probably need some extra parentheses. GCC
  or clang may suggest extra parens in certain situations; we compile with
  `-Werror` so you must always follow those guidelines.
- If an expression does not fit on one line, attempt to wrap it after an
  operator (rather than an identifier or keyword) and indent subsequent lines
  with the beginning of the current parenthesis/brace nesting level. For
  example, here are some long expressions, formatted appropriately:
```cpp
if (Cfg::Jit::RegionSelector != "" &&
    (Cfg::HHIR::RefcountOpts ||
     RuntimeOption::EvalHHITExtraOptPass) &&
    Func::numLoadedFuncs() < 600) {
  // ...
}

longFunctionName(argumentTheFirst,
                 argumentTheSecond,
                 argumentTheThird,
                 argumentTheFourth);
```

- Function calls should be formatted primarily using the previous rule. If one
  or more of the arguments to the function is very wide, it may be necessary to
  shift all the arguments down one line and align them one level deeper than
  the current scope. This is always acceptable, but is especially common when
  passing lambdas:
```cpp
m_irb->ifThen(
  [&](Block* taken) {
    gen(CheckType, Type::Int, taken, src);
  },
  [&] {
    doSomeStuff();
    lotsOfNonTrivialCode();
    // etc...
  }
);
```

### Constructor initializer lists ###

If an initializer list can be kept on a single line, it is fine to do so:

```cpp
MyClass::MyClass(uint64_t idx) : m_idx(idx) {}

MyClass::MyClass(const Func* func) : m_idx(-1) {
  // Do stuff.
}
```

Otherwise, it is always correct to format lists thusly:

```cpp
MyClass::MyClass(const Class* cls, const Func* func, const Class* ctx)
  : m_cls(cls)
  , m_func(func)
  , m_ctx(ctx)
  , m_isMyConditionMet(false)
{}

MyClass::MyClass(const Class* cls, const Func* func)
  : m_cls(cls)
  , m_func(func)
  , m_ctx(nullptr)
  , m_isMyConditionMet(false)
{
  // Do stuff.
}
```

### Namespaces ###

We don't nest namespaces very deeply, so prefer to keep the scoping to a single
line:

```cpp
namespace HPHP::jit::x64 {
///////////////////////////////////////////////////////////////////////////////

/*
 * Some nice documentation.
 */
struct SomeNiceThing {
  // some nice properties
};

///////////////////////////////////////////////////////////////////////////////
}
```

Do not increase the indentation level when entering namespace scope. Instead,
consider adding a line of forward slashes as a separator, to more clearly
delineate the namespace (this is especially useful for anonymous namespaces in
source files). This form of delineation is encouraged, but we have no strict
convention for its formatting (you'll see 70- or 79- or 80-character
separators, with or without an extra newline between it and the braces, etc.).


## Comments ##

All public and private APIs in headers should be documented in detail. Names
and notions which are not obvious (e.g., "persistent" or "simple") should be
explained. Preconditions and postconditions should be noted.

Inline code comments are encouraged for complex logic, but their density is
left up to the author. Rather than summarizing/paraphrasing what your code is
doing, focus on explaining what overarching goal the code is achieving and/or
why that goal is necessary or desirable.

### Comment style ###

Here are some comment styles we use or avoid:

```cpp
// This style of comment is the most common for relatively short inline
// comments. It's fine if it's multi-line.
//
// It's also fine if it has line breaks. The extra newline aids readability in
// this case.

/*
 * This style of comment is the right one to use for struct/function
 * documentation. Prefer one star on the opening line, as opposed to the
 * doxygen standard of two.
 *
 * This is also sometimes used for inline code comments, although the // style
 * makes it easier to comment out blocks of code.
 */

struct ClassLikeThing {
  std::vector<const Func*> methods; // This is fine for short annotations.

  /* This is also ok, though try not to mix and match too much in a file. */
  std::vector<const ClassLikeThing*> parents;
};

/* Don't write multiline comments where some lines are missing their prefix.
   This is pretty weird. */
```

Try to use complete sentences in all but the shortest of comments. All comments
should be flowed to 79 characters in width.

### Separators ###

Delineate sections of code with a line of forward slashes. There is no strict
convention, but prefer lines of slashes to other delineators (e.g., `/*****/`,
five newlines, ASCII-art cartoon characters).

### File copyright ###

All files must begin with a copyright/license notice. For files created by
Facebook employees, the following should be used:

```cpp
/*
   +----------------------------------------------------------------------+
   | HipHop for PHP                                                       |
   +----------------------------------------------------------------------+
   | Copyright (c) 2010-201x Facebook, Inc. (http://www.facebook.com)     |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
*/

// File contents start here.
```

We do not require copyright assignment for external contributions, so
non-Facebook contributors should include their own header. The exact contents
are up to you, as long as the license is compatible with the PHP license.

---

### Hphp Doc Ini (`hphp/doc/ini.md`)

# Using ini

ini usage in HHVM is fairly similar to that of php-src, albeit currently with
some limitations and enhancements. For example, HHVM currently doesn't support
per-dir ini settings (support coming soon), but it does support vector-based
settings and wildcards for copying and symlinking values from other settings.

## Common usage

Here is the typical format of an ini file (values not necessarily realistic):

```
hhvm.hot_func_count = 11
hhvm.stats.slot_duration = 11
hhvm.env_variables["MYBOOL"] = true
hhvm.env_variables["MYINT"] = 5
hhvm.env_variables["MYSTR"] = "Custom String"
hhvm.env_variables["ANOTHERSTR"] = "Another String"
hhvm.server_variables[] = "Key will be next available int"
hhvm.server_variables[] = "Value will be next available string"
hhvm.error_handling.notice_frequency = 1
hhvm.error_handling.warning_frequency = 1
hhvm.enable_obj_destruct_call = true
hhvm.enable_xhp = true
```

## Copying and Symlinking Settings

**NOTE**: This feature only currently work with core system settings. They
don't yet work with extensions, `ini_set()`, etc.

You can also provide wildcards to settings signaling that you want to use the
value of another setting for its value.

* `@`: Copy the value directly into this setting.
* `:`: Symlink the value from the other setting to this setting. If the other
setting changes, then this setting will change with it, and vice-versa.

To use this feature, use the form

```
hhvm.setting[any-sub-key | @ | :][...] = value | "hhvm.substitution-setting"
```

e.g.,

```
hhvm.a = 3
hhvm.b[@] = "hhvm.a"
hhvm.c[d][@] = "hhvm.a"
```

Here is a more complete example:

```
hhvm.hot_func_count = 11
hhvm.stats.slot_duration[@] = "hhvm.hot_func_count"
hhvm.server.allowed_exec_cmds[0] = "ls"
hhvm.server.allowed_exec_cmds[1][@]= "hhvm.env_variables[MYSTR]"
hhvm.server.allowed_exec_cmds[2][@]= "hhvm.env_variables[ANOTHERSTR]"
hhvm.env_variables["MYBOOL"] = true
hhvm.env_variables["MYINT"][:] = "hhvm.hot_func_count"
hhvm.env_variables["MYSTR"] = "Custom String"
hhvm.env_variables["ANOTHERSTR"] = "Another String"
hhvm.server_variables[0] = "Key will be next available int"
hhvm.server_variables[1][@] = "hhvm.server.allowed_exec_cmds[0]"
hhvm.error_handling.notice_frequency = 1
hhvm.error_handling.warning_frequency[:] = "hhvm.error_handling.notice_frequency"
hhvm.enable_obj_destruct_call = true
hhvm.enable_xhp[@]= "hhvm.enable_obj_destruct_call"
```

**NOTE**: If you using this feature with vector or map based settings where you
can specify `[]` to indicate an in-order increment of a setting, you must
specify explicit indices for them because they will be used to determine which values to be used when copying or symlinking.

---

### Hphp Doc Hackers Guide Bytecode Interpreter (`hphp/doc/hackers-guide/bytecode-interpreter.md`)

# The HHVM Bytecode Interpreter

A typical interpreter is implemented using a dispatch loop that looks something
like this:

```cpp
while (true) {
  switch(*pc) {
    case ADD:
      /* add implementation */
      pc++;
      break;

    case SUB:
      /* sub implementation */
      pc++;
      break;

    /*
     * handlers for other bytecodes
     */
  }
}
```

That is, you have a loop which steps through the bytecode program, examining
each one. The `switch` statement executes the current bytecode instruction,
advances the program counter, then repeats, until the program terminates.

If you try to find a code structure like this in HHVM, you won’t find it.
Instead, the interpreter is broken up into a number of smaller pieces, some of
which are defined using a series of nested macros. This is to reduce duplication
and to keep it more manageable, although it can make the code seem intimidating
to newcomers.

HHVM's interpreter makes repeated use of the [X
Macro](https://en.wikipedia.org/wiki/X_Macro) pattern to generate code and data
for each bytecode instruction. The list macro is `OPCODES`, defined in
[runtime/vm/hhbc.h](https://github.com/facebook/hhvm/blob/f484e7c597763bff68ad9e0e355aff763b71ec1e/hphp/runtime/vm/hhbc.h#L383).
It contains the name, signature, and attributes for every bytecode instruction.
The "X" macro is `O()`, which you can see is repeatedly invoked by `OPCODES`.

## Bytecode implementations

HHVM's bytecode interpreter lives in
[runtime/vm/bytecode.cpp](../../runtime/vm/bytecode.cpp). For every bytecode
instruction `Foo`, there is a function `iopFoo()`. This function contains the
interpreter implementation of `Foo`, and takes arguments representing all of the
immediate arguments to the instruction. For example, since the `Add` instruction
takes no immediates, `void iopAdd()` takes no arguments. `Int`, on the other
hand, takes a 64-bit integer immediate, so its signature is `void iopInt(int64_t
imm)`.

These functions are not called directly by the dispatch loop. Instead, the
dispatch loop calls `iopWrapFoo()`, giving it a pointer to the appropriate
`iopFoo()` function. These wrapper functions are automatically generated from
each bytecode's signature in `hhbc.h`. Each one decodes the appropriate
immediates, then passes them to the corresponding `iopFoo()` function. You
should not have to modify the machinery that generates these functions unless
you add a new bytecode immediate type.

## InterpOne

In addition to the hand-written `iop*()` functions and the macro-generated
`iopWrap*()` functions, the `OPCODES` macro is used
[here](https://github.com/facebook/hhvm/blob/f484e7c597763bff68ad9e0e355aff763b71ec1e/hphp/runtime/vm/bytecode.cpp#L7475-L7515)
to create a set of functions named `interpOne*()`. These functions are used by
the JIT when a certain instruction would be too complicated to compile to native
machine code: it syncs all live state to memory, calls the appropriate
`interpOne*()` function, then resumes running the native code after the
problematic bytecode.

## Interpreter dispatch loop

The dispatch loop is in `dispatchImpl()`, defined in
[runtime/vm/bytecode.cpp](../../runtime/vm/bytecode.cpp). It’s declared as:

```cpp
  template <bool breakOnCtlFlow> TCA dispatchImpl()
```

Two different versions are instantiated, one for each possible value of
`breakOnCtlFlow`. When `breakOnCtlFlow` is `true`, the function will return to
the caller after a control flow (i.e. branch) instruction is encountered. If
`breakOnCtlFlow` is `false`, the interpreter will continue executing
instructions until the current VM entry finishes.

There are two versions of the interpreter loop. The Windows version (indicated
with `_MSC_VER`) implements an ordinary switch-based interpreter loop, while the
Linux version implements a threaded interpreter. In a threaded interpreter, the
handler for each bytecode jumps directly to the handler for the next bytecode
rather than going to a single central switch statement. This eliminates a jump
to a different cache line and improves branch prediction by allowing the
processor’s branch predictor to find associations between the bytecodes. These
different mechanisms are hidden by the `DISPATCH_ACTUAL` macro.

There are three separate parts to each bytecode handler. One part for dealing
with Hack debugging, one part for tracking code coverage, and a third part which
implements the actual handler. These are defined in the `OPCODE_DEBUG_BODY`,
`OPCODE_COVER_BODY`, and `OPCODE_MAIN_BODY` macros, respectively. In the Windows
version, [defined
here](https://github.com/facebook/hhvm/blob/f484e7c597763bff68ad9e0e355aff763b71ec1e/hphp/runtime/vm/bytecode.cpp#L7621-L7628),
the three macros are put in a `case` for each opcode, like the example at the
beginning of this document.

In the Linux version, [defined
here](https://github.com/facebook/hhvm/blob/f484e7c597763bff68ad9e0e355aff763b71ec1e/hphp/runtime/vm/bytecode.cpp#L7631-L7636),
the threaded interpreter uses a dispatch table with [computed
goto](https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html). Each
instruction gets three labels: one for each `OPCODE_*_BODY` macro. These labels
are collected into three different dispatch tables, [defined
here](https://github.com/facebook/hhvm/blob/f484e7c597763bff68ad9e0e355aff763b71ec1e/hphp/runtime/vm/bytecode.cpp#L7532-L7549).
`optabDirect` points to `OPCODE_MAIN_BODY`, `optabCover` points to
`OPCODE_COVER_BODY`, and `optabDbg` points to `OPTAB_DEBUG_BODY`. The correct
dispatch table is selected at runtime depending on the current code coverage
configuration, and whether or not a debugger is attached.

## Performance

In general, we strongly prefer simplicity over performance in the interpreter.
Any performance-sensitive uses of HHVM rely on the JIT compiler, so we've found
it beneficial to keep the interpreter as straightforward as possible, kind of
like a reference implementation to compare the JIT against. This is an
intentional tradeoff that has resulted in an interpreter that is fairly slow.

One aspect of interpreter performance that we have focused on is inlining
decisions. We've found that most compilers choose to not inline various parts of
the interpreter in ways that measurably hurt performance (even when the
functions are marked `inline`). To counter this, we use the `OPTBLD_INLINE`
macro, which forces the compiler to inline functions in optimized builds. It does
nothing in debug builds, so we can still debug the interpreter.

---

### Hphp Doc Hackers Guide Data Structures (`hphp/doc/hackers-guide/data-structures.md`)

# HHVM Runtime Data Structures

This page contains a brief overview of the most important runtime data
structures in HHVM. It is not meant to be an exhaustive reference for all
details about these data structures, since those details change too often to
reliably document so far away from the code. Rather, reading the descriptions
here should leave you with a good high-level understanding of what each one is
used for and where to find the code implementing it for further investigation.

## Hack-visible values

### `DataType`, `Value`, and `TypedValue`

`DataType`, defined in [datatype.h](../../runtime/base/datatype.h), is an enum
representing the type of a Hack value: `KindOfNull` means `null`,
`KindOfBoolean` means `bool`, `KindOfInt64` means `int`, etc. Some user-visible
types are split into multiple `DataType`s to keep track of whether or not a
value is reference counted: `KindOfPersistentFoo` means "`KindOfFoo` that we
know is not reference counted." The reverse, however, is not true:
`KindOfPersistentFoo` is a subtype of `KindOfFoo`, so it is valid to use
`KindOfFoo` with a non-reference-counted Foo.

`TypedValue`, defined in [typed-value.h](../../runtime/base/typed-value.h)
represents a single Hack value, and appears in many different places in HHVM. It
contains a `DataType` and a `Value`, which is a union with one member for every
possible type. Primitive types (`null`, `bool`, `int`, and `float`) are stored
inline in the `TypedValue`, while all other types heap-allocate a data object
and store a pointer to it in the `TypedValue.`

### `ArrayData`

[`ArrayData`](../../runtime/base/array-data.h) is used to represent all
array-like types in Hack: `array`, `dict`, `vec`, and `keyset`, though you'll
never see any raw `ArrayData` objects created anywhere. Instead, a specific kind
of array is created and tagged using one of the [several current array `HeaderKind` values]
(https://github.com/facebook/hhvm/blob/HHVM-3.27/hphp/runtime/base/header-kind.h#L46-L49).
We use a custom vtable to dispatch to the appropriate implementation for all
`ArrayData` member functions; the current implementation classes are
[`PackedArray`](../../runtime/base/packed-array.h),
[`VanillaDict`](../../runtime/base/vanilla-dict.h), and
[`SetArray`](../../runtime/base/set-array.h). Note that many of these types
don't directly inherit from `ArrayData`, so they're only subtypes of `ArrayData`
by convention.

Arrays in Hack have value semantics, implemented using copy-on-write. As a
result, most member functions that perform mutations take a `bool copy`
parameter to indicate whether or not the array should be copied before
performing the mutation. It is up to the caller to call `cowCheck()` before any
mutations to determine if a copy is necessary. Additionally, any mutation could
cause reallocation of a new `ArrayData`, either to grow or escalate to a
different array kind. To support this, all mutation functions return a new
`ArrayData*`; the `ArrayData*` that was mutated should be considered dead and
you should use the new one in its place.

### `StringData`

[`StringData`](../../runtime/base/string-data.h) represents a Hack `string`
value. Like arrays, strings have value semantics using copy-on-write, so callers
are responsible for calling `cowCheck()` before mutating, although the copy must
be done manually with `StringData::Make()` rather than a `bool copy` parameter
to the mutator function.

Most `StringData`s store their data in space allocated immediately after the
`StringData`. This layout is required in `USE_PACKEDPTR`, so `StringData::m_data`
is [conditionally
defined](https://github.com/facebook/hhvm/blob/e05d2041a598ff655f594c4fec7e5f1708d9466b/hphp/runtime/base/string-data.h#L539-L542).
For normal builds, `m_data` will usually point right after the `StringData`, but
it may point elsewhere for strings from APC.

### `ObjectData`

[`ObjectData`](../../runtime/base/object-data.h) represents a Hack `object`. It
contains a `Class*` describing its type (described below) and a series of
attributes. Declared properties are stored in an array of `TypedValue`s that is
allocated after the `ObjectData`.

### `Smart pointer wrappers`

All of the types described so far have smart pointer wrappers that are generally
used for high-level non-performance-critical C++ code that needs to work with
one of them. The wrapper type can be found by dropping the `Data` suffix, so
`StringData`'s wrapper is [`String`](../../runtime/base/type-string.h). Note
that like most smart pointer types, these wrappers can all represent a `null`
value, but the pointer in a `TypedValue` representing an array, string, etc.
must *never* be `nullptr`. A `null` value is represented using `KindOfNull`.

## Runtime-internal data structures

### `Unit`

A [`Unit`](../../runtime/vm/unit.h) represents all of the information contained
in one Hack file: classes, functions, constants, top-level code, etc. All
references to entities that could be defined in another file are only referenced
by name, even if they are defined in the current file. This includes, but it not
limited to, function names in call expressions, parent class names, and used
traits. This is to support one of HHBC's core principles: it is always possible
to emit bytecode (and construct a `Unit`) for a single file in complete
isolation. If that file references entities that are undefined at runtime, the
appropriate error will be raised.

### `PreClass` and `Class`

Every `Unit` has a list of classes that are defined in the file it came from.
Each of these classes is stored as a [`PreClass`](../../runtime/vm/preclass.h),
which represents the class definition as it is visible in the source code.
Parent classes, used traits, and any other references to entities other than the
class itself are stored as string names.

When a class is defined at runtime, these references are resolved to concrete
entities, producing a [`Class`](../../runtime/vm/class.h). The resolved parent
class is stored as a `Class*` rather than the string name from the `PreClass`.

This two-level representation of classes is necessary because the mapping
between name and class can change with every request. All classes are redefined
from scratch in each request<sup>[1](#f1)</sup>, and different definitions of a
class can be selected by including different files, or by putting the
definitions on different control flow paths. Properly-typed Hack code will
never have multiple definitions of the same class, but HHVM still supports it.

### `Func`

All Hack functions, including methods on classes, are represented using a
[`Func`](../../runtime/vm/func.h). Each `Func` contains a pointer to the `Unit`
it was defined in, the `Class` it is a member of (if any), its location in the
`Unit`'s bytecode, information about its parameters, and various other metadata.

Every `Func` has a 32-bit `FuncId`, which is assigned in ascending order as each
`Func` is created. There is a global table that allows mapping from this ID back
to a `Func*`, and `FuncId`s are often used in places where we want to store a
reference to a `Func` without a full 64-bit pointer<sup>[2](#f2)</sup>.
`FuncId`s are also guaranteed to be unique for the lifetime of the process,
unlike `Func*`.

--------

<b id="f1">1:</b> This is what HHVM presents to the user, but we have
mechanisms to avoid the work of actually redefining everything in every request
when possible.

<b id="f2">2:</b> HHVM also has a `USE_PACKEDPTR` build mode that allocates
certain data structures, including `Func`s, in the lower 32GiB of address space,
allowing us to store 32-bit pointers using `HPHP::PackedPtr<T>`. However,
`PackedPtr<T>` is 64 bits in non-`USE_PACKEDPTR` builds, and some uses of
`FuncId` rely on it being 32 bits in all build modes.

---

### Hphp Doc Hackers Guide Directory Structure (`hphp/doc/hackers-guide/directory-structure.md`)

## Directory Structure

There are over 1000 C++ files in HHVM's codebase, split into a number of
different directories. This is a rough overview of what lives where (all paths are under `hphp/`):

`compiler/`: The old parser and bytecode emitter. This is deprecated and is currently being removed; the new replacement is in `hack/src/hhbc`.

`doc/`: Documentation, of varying age and quality.

`hack/src/`: The Hack parser, typechecker, bytecode emitter, and various other Hack-related tools.

`hhbbc/`: HipHop Bytecode-to-Bytecode Compiler, our ahead-of-time static analysis program.

`hhvm/`: `main()` for the `hhvm` binary

`neo/`: Code to read `.hdf` files, used for configuration.

`parser/`: The parser grammar and interface.

`pch/`: Pre-compiled headers for MSVC (not currently supported).

`ppc64-asm/`: Code to read and write [ppc64](https://en.wikipedia.org/wiki/Ppc64) machine code.

`runtime/`: HHVM's runtime.\
`runtime/base/`: Runtime internals related to Hack types and values (strings, objects, etc...).\
`runtime/debugger/`: Support for debugging Hack programs at runtime.\
`runtime/ext/`: Extensions.\
`runtime/server/`: Built-in servers, both for web requests and other HHVM-specific types of requests.\
`runtime/test/`: C++ unit tests for various runtime components.\
`runtime/vm/`: Most of the internals of the VM. The distinction between `runtime/base/` and `runtime/vm` is a bit fuzzy.\
`runtime/vm/jit/`: HHVM's JIT compiler.\
`runtime/vm/verifier/`: The bytecode verifier.

`system/`: systemlib, a collection of Hack code that is embedded into `hhvm` and always available.

`test/`: Integration tests for the whole compiler pipeline, written in PHP and Hack.

`tools/`: Utility programs useful in the development and/or use of HHVM.\
`tools/benchmarks/`: Microbenchmarks. If you run these, take them with a pound of salt; we care much more about macrobenchmarks based on real-life workloads.\
`tools/gdb/`: Python scripts to assist in debugging `hhvm` in `gdb`.\
`tools/hfsort`: HFSort, the algorithm used to lay out functions in both `hhvm` and JIT-compiled code.\
`tools/tc-print/`: TCPrint, a tool to analyze the output of the JIT compiler.

`util/`: Functions and data structures used by HHVM that could be useful to other programs, and have no dependencies on other parts of HHVM.

`vixl/`: Code to read and write [AArch64](https://en.wikipedia.org/wiki/ARM_architecture#AArch64) machine code.

`zend/`: Code imported from the standard [PHP interpreter](https://github.com/php/php-src).

---

### Hphp Doc Hackers Guide Faq (`hphp/doc/hackers-guide/faq.md`)

# FAQ

This document contains answers to some of the most common questions we get from
people as they learn HHVM's codebase. Some of the answers may be pointers to
other pieces of documentation, if that information has a natural home somewhere
else.

## How is HHVM configured?

HHVM has a large number of configuration options that can be set with
command-line arguments of the form `-vName=Value`. Unfortunately, documentation
on them is very sparse, but some can be found in
[doc/options.compiled](../options.compiled) and
[doc/options.compiler](../options.compiler). The authoritative source for core
runtime options is the `RuntimeOption` class, in
[runtime/base/runtime-option.h](../../runtime/base/runtime-option.h) and
[runtime/base/runtime-option.cpp](../../runtime/base/runtime-option.cpp).

Multiple options may be collected in a `.hdf` file, [described here](../hdf),
and passed to HHVM with `-c path/to/config.hdf`. Options given with `-v` will
override any found in the `.hdf`. HHVM also has support for PHP's `.ini` files,
documented [here](../ini.md).

## How do I see the bytecode for my Hack/PHP script?

Use the `Eval.DumpBytecode` runtime option:

```sh
$ hhvm -vEval.DumpBytecode=1 hello.php
```

This will give you output that looks something like
[this](https://gist.github.com/swtaarrs/991c37af6e474733c47911731521a8ec).
[bytecode.specification](../bytecode.specification) contains descriptions of the
behavior of every bytecode instruction. Note that running with
`-vEval.DumpBytecode=1` will still execute the script after printing its
bytecode. To prevent this, add the `--lint` option, which will exit after
parsing and printing the bytecode.

You can also use `Eval.DumpHhas`:

```sh
$ hhvm -vEval.DumpHhas=1 hello.php
```

This gives output similar to
[this](https://gist.github.com/swtaarrs/4b2fffacd74c31d4e65298888922805d).
Running with `-vEval.DumpHhas=1` will *not* execute the script after printing
its bytecode.

Why are there two ways to dump bytecode with slightly different semantics? It's
partly for historical reasons that may be cleaned up at some point, but there
are a few meaningful differences between the two:
- `DumpBytecode` shows bytecode offsets, while `DumpHhas` doesn't. This is because HHAS is designed to be human-writable, so it uses labels rather than raw offsets for control flow. However, the offsets are useful if you're working in the interpreter or JIT, as they use bytecode offsets to locate code.
- `DumpBytecode` writes to the current `TRACE` file (see the next section for details), while `DumpHhas` writes to stdout.
- Metadata is printed explicitly, separately from function bodies with `DumpBytecode`, while `DumpHhas` either has the same metadata represented inline in the bytecode, or left out when it can be inferred from the bytecode itself.

In general, if you're working with HackC (the parser and bytecode emitter), you
should use `DumpHhas`, because that's how HHVM and HackC communicate. If you're
working with HHVM's interpreter or JIT, you should use `DumpBytecode`, since its
more verbose format can be helpful during debugging.

## How do I enable debug logging from different parts of HHVM?

The primary method of printing debug information is our tracing system, defined
in [util/trace.h](../../util/trace.h). Most of the functionality is disabled in
release builds for performance reasons, so you should be using a debug build if
you want to enable tracing.

### Basic usage

The `TRACE_MODULES` macro in `trace.h` defines a series of *trace modules*, each
with a short name. To enable one or more modules, set the `TRACE` environment
variable to a comma-separated list of `module:level` pairs. All levels default
to 0, and higher levels are typically more verbose (this is only by convention,
and is not enforced anywhere). For example, to set the `hhir` module to level 1
and the `printir` module to level 2, run:

```sh
$ TRACE=hhir:1,printir:2 hhvm my_script.php
```

If `TRACE` is set, even to an empty value, tracing is written to
`/tmp/hphp.log`; this can be overridden by setting `HPHP_TRACE_FILE` to the file
of your choice. If `TRACE` is not set, all tracing goes to stderr (this applies
to features like `Eval.DumpBytecode`, described above). To send tracing to
stderr even when `TRACE` is set, use `HPHP_TRACE_FILE=/dev/stderr`.

To find out which trace module applies to a specific file, look for a use of the
`TRACE_SET_MOD` macro, usually near the top of the file. To add your own tracing
lines, use either `TRACE(level, "printf-style format", args...);` or
`FTRACE(level, "folly::format-style format", args...);`. The `level` argument is
the lowest level at which the trace will be active.

### `printir`

If you're working in the JIT, `printir` is probably the most important module to
be familiar with. It controls the printing of HHIR units as they're constructed
and optimized, and `TRACE=printir:1` is the most convenient way to see the
output of the JIT. It's also one of the only modules to have well-defined
meanings for each trace level, defined
[here](https://github.com/facebook/hhvm/blob/38ee69496f66e87528a128e22c38e2ee12da5470/hphp/runtime/vm/jit/print.h#L76-L101).
It is enabled in all build modes, so you don't have to create a debug build to
use it.

## My change is crashing HHVM. What do I do?

HHVM can be debugged with the standard debugging tools for your compiler and
platform. For most of us, that means using
[GDB](https://www.gnu.org/software/gdb/):

```sh
$ gdb --args ./hhvm path/to/script.php
```

There are a variety of support scripts in [tools/gdb](../../tools/gdb) to help
with inspecting many of HHVM's data structures; see the README in that directory
for more information on usage.

# Common Pitfalls

This section contains answers to questions that most people don't think to ask,
usually because they involve some non-obvious or surprising behavior in HHVM.

## `jit::Type`

If you spend any time in the JIT, you'll probably deal with the
[`jit::Type`](../../runtime/vm/jit/type.h) struct. [HHIR](../ir.specification)
is statically typed, so every value is tagged with a `Type` and every
instruction has a set of acceptable `Type`s for its inputs. If you're used to
types in languages like Java or C++, `Type` is much more complicated than
something like `int` or `Foo*`. We recommend reading about it
[here](jit-core.md#type-system) before writing any nontrivial HHIR code. You
don't have to memorize every single `Type`, but you should make sure you
understand the [usage guidelines](jit-core.md#usage-guidelines).

---

### Hphp Doc Hackers Guide Glossary (`hphp/doc/hackers-guide/glossary.md`)

# Glossary

This document contains a list of terms, abbreviations, and acronyms that you may
come across either in this guide or in the HHVM source code. Don't worry if none
of the definitions mean anything to you yet; it may make more sense to revisit
this page as needed once you've read some more of the guide.

## HHVM/Hack/PHP-specific

The terms in this section are specific to HHVM, Hack, or PHP. Concepts that
apply more generally are listed in the [next
section](glossary.md#general).

### APC

The [Alternative PHP Cache](http://php.net/manual/en/intro.apc.php) was
originally added to PHP as a cache for compiled bytecode, to reduce the overhead
of loading a program's source code with every request. It has since been
replaced, but HHVM still supports it as a process-global hash table, used to
share data between active requests and/or cache data to be reused by later
requests.

### BOLT

[BOLT](https://github.com/facebookincubator/BOLT) is a binary optimizer,
developed by Facebook to optimize the performance of large applications.

### FPI/FPI Region

Function parameter info and corresponding regions in the bytecode. Described in
detail in [bytecode.specification](../bytecode.specification).

### HackC

The Hack Compiler, HHVM's new frontend (as of mid-2018). A parser and bytecode
emitter written completely in OCaml.

### HHAS

HipHop assembly: a human-readable and -writable representation of HHBC, HHVM's
bytecode.

### HHBC

HipHop bytecode, HHVM's custom bytecode. Described in
[bytecode.specification](../bytecode.specification).

### HHBBC

HopHop bytecode-to-bytecode compiler. Our static analysis engine that performs a
number of offline optimizations on bytecode.

### HHIR

HipHop intermediate representation, the primary IR in HHVM's JIT. Described in
[ir.specification](../ir.specification).

### HPHPc

[HipHop for PHP](https://en.wikipedia.org/wiki/HipHop_for_PHP), a transpiler
from PHP to C++ and HHVM's predecessor. A large part of HHVM's runtime is
inherited from HPHPc.

### HNI

[HHVM native interface](https://github.com/facebook/hhvm/wiki/Extension-API).
HHVM's mechanism for calling native code from PHP (name inspired by [JNI](https://en.wikipedia.org/wiki/Java_Native_Interface)).

### RDS/Target Cache

The request data segment, defined in
[runtime/base/rds.h](../../runtime/base/rds.h) is a form of [thread-local
storage](https://en.wikipedia.org/wiki/Thread-local_storage), used to hold
information that can vary between PHP requests, like the definitions of classes
and functions. The target cache is a collection of request-local caches used
by the JIT that live in RDS.

### Repo/Repo authoritative

HHVM stores bytecode in a cache we call the [repo](../repo), short for
"repository". Repo authoritative is an execution mode in which HHVM assumes that
no code exists outside of what's already in the currently-loaded repo, allowing
much more aggressive optimizations.

### TC/Translation cache

The block of memory where HHVM stores JIT-compiled code.

### TCA

Translation cache address: a pointer to code or data in the translation cache.

### tc-print

Translation cache printer: a tool for inspecting the code generated by HHVM,
optionally annotated with performance events.

### Tracelet

A region of bytecode used as the basic unit of compilation in some modes of
HHVM's JIT. No larger than a basic block.

### vasm

Virtual assembly, our low-level intermediate representation. Positioned between
HHIR and machine code.

### XLS

Extended linear scan, the algorithm used by our [register
allocator](../../runtime/vm/jit/vasm-xls.cpp).

## General

If you're new to compilers, performance-sensitive applications, or just curious,
you may want to skim through the following terms:

- [ARM](https://en.wikipedia.org/wiki/ARM_architecture): A family of CPU architectures. In the context of HHVM: [AArch64](https://en.wikipedia.org/wiki/ARM_architecture#AArch64), the 64-bit version of ARM.
- [AST/abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)
- [Bytecode](https://en.wikipedia.org/wiki/Bytecode)
- [Basic block](https://en.wikipedia.org/wiki/Basic_block)
- **CFG**: [control flow graph](https://en.wikipedia.org/wiki/Control_flow_graph) or [context-free grammar](https://en.wikipedia.org/wiki/Context-free_grammar) (nearly always the former here).
- [Compilation unit/translation unit](https://en.wikipedia.org/wiki/Translation_unit_(programming))
- [COW/copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write)
- [CPU cache](https://en.wikipedia.org/wiki/CPU_cache). Also d-cache, data cache, i-cache, instruction cache.
- [IR/intermediate representation](https://en.wikipedia.org/wiki/Intermediate_representation).
- [FP/frame pointer](https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers).
- **ICF/identical code folding**: a compiler/linker optimization that detects and de-duplicates identical blocks of code.
- [TLB/iTLB/dTLB](https://en.wikipedia.org/wiki/Translation_lookaside_buffer)
- [Bytecode interpreter](https://en.wikipedia.org/wiki/Interpreter_(computing)#Bytecode_interpreters)
- [JIT/just-in-time compilation](https://en.wikipedia.org/wiki/Just-in-time_compilation)
- **Leaf function**: a function that calls no other functions; a leaf node in the control flow graph.
- [NUMA/non-uniform memory access](https://en.wikipedia.org/wiki/Non-uniform_memory_access)
- [PGO/profile-guided optimization](https://en.wikipedia.org/wiki/Profile-guided_optimization)
- [RSS/resident set size](https://en.wikipedia.org/wiki/Resident_set_size)
- [SP/stack pointer](https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers)
- [x86](https://en.wikipedia.org/wiki/X86-64): A family of CPU architectures. In the context of HHVM: [x86-64](https://en.wikipedia.org/wiki/X86-64), the 64-bit version of x64.

---

### Hphp Doc Hackers Guide Jit Core (`hphp/doc/hackers-guide/jit-core.md`)

# HHVM JIT

HHVM's just-in-time compiler module is responsible for translating sequences of
HipHop Bytecode ([HHBC](../bytecode.specification)) into equivalent sequences
of machine code. The code implementing the JIT lives in the namespace
`HPHP::jit`, in [hphp/runtime/vm/jit](../../runtime/vm/jit). Most file and
class names referenced in this document will be relative to that namespace and
path.

The basic assumption guiding HHVM's JIT is that while PHP is a dynamically
typed language, the types flowing through PHP programs aren't very dynamic in
practice. We observe the types present at runtime and generate machine code
specialized to operate on these types, inserting runtime typechecks where
appropriate to verify assumptions made at compile time.

## Region Selection

The first step in translating HHBC into machine code is deciding exactly which
code to translate, in a process called region selection. The size and shape of
regions depend on many factors, ranging from a single HHBC instruction up to an
entire function with complex control flow. There are a number of different ways
to select a region, all producing a `RegionDesc` struct in the end (defined in
[region-selection.h](../../runtime/vm/jit/region-selection.h)). `RegionDesc`
contains a list of `RegionDesc::Block` structs, and each `Block` represents a
basic block of bytecodes by holding a starting offset and length in
instructions. The list of `Block`s is kept sorted in reverse post order. Blocks
also contain optional metadata about the code they contain and the state of the
VM before, during, and after execution of that code. This metadata includes
type predictions, statically known call destinations, and certain
postconditions.

### Tracelet Region Selection

The first-gear region selector is the tracelet region selector. The name
"tracelet" is somewhat vestigal, and refers to a region of HHBC typically no
larger than a single basic block. This code lives in
[region-tracelet.cpp](../../runtime/vm/jit/region-tracelet.cpp), and its goal is
to select a region given only the current live VM state - most importantly the
current program counter and the types of all locals and eval stack slots. The
`translator` module (see the "HHIR" section) is used to simulate execution of
the bytecode using types instead of values, continuing until a control flow
instruction is encountered or an instruction attempts to consume a value of an
unknown type. These rules are not absolute; there are a number of exceptions. If
an unconditional forward `Jmp` is encountered, the tracelet will continue at the
`Jmp`'s destination. In addition, certain bytecodes (such as `PopC` and
`IsTypeC`) are able to operate on generic values and don't cause a tracelet to
terminate due to imprecise types.

### PGO

HHVM's JIT has support for profile-guided optimizations, or PGO. The code
supporting PGO is spread throughout many parts of the JIT. This section is a
high-level overview of the major pieces.

#### Profiling Regions

The tracelet selector has a special mode for selecting "profiling regions".
There are a few differences in profiling mode, indicated by code that inspects
`env.profiling` in
[region-tracelet.cpp](../../runtime/vm/jit/region-tracelet.cpp):

1. Only the very first instruction in a region is allowed to consume a
   referenced value.
2. Certain bytecodes that normally wouldn't break a tracelet do. See the
   `instrBreaksProfileBB()` function for the list.
3. All control flow instructions always end the region, including unconditional
   `Jmp`s.

The purpose of these restrictions is to create much simpler regions that are
guaranteed to be a single `Block`, with no mid-region side exits due to
referenced values. These smaller regions also contain execution counters to
track the relative hotness of each basic block of bytecode.

#### Hotcfg Region Selection

After translating and executing profiling regions for a while, optimized
retranslation is triggered (the exact details of how and where this happens are
currently changing somewhat rapidly, so this sentence is intentionally vague
for now). Once retranslation is triggered, a number of "hot CFGs" are selected
for each function, by the code in
[region-hot-cfg.cpp](../../runtime/vm/jit/region-hot-cfg.cpp). A hot
[CFG](https://en.wikipedia.org/wiki/Control_flow_graph) is a `RegionDesc`
containing a non-strict subset of the profiling regions from a function,
stitched together into one large region. It may be a single straight-line path
through the hottest part of a function, or it may be the entire body of the
function, containing loops and other control flow. The exact shape of each hot
CFG depends on which parts of the function were executed during the profiling
phase.

## Region Translation

Regardless of the source of the `RegionDesc`, the translation process from this
point forward is the same. The `RegionDesc` containing blocks of HHBC is lowered
into HHIR, which is then lowered to vasm, and finally the vasm is lowered to
x86-64, arm64, or ppc64 machine code, depending on the architecture of the host
CPU. Each level of this pipeline has a number of associated optimization passes.
This section describes each lowering process and some of the optimizations.

## HHIR

Once a bytecode region has been selected, it is lowered into [HipHop
Intermediate Representation](../ir.specification), commonly referred to as
HHIR.  HHIR is an
[SSA-form](https://en.wikipedia.org/wiki/Static_single_assignment_form),
strongly-typed intermediate representation positioned between HHBC and machine
code. A few different classes and modules are involved in this process:

* `irgen`: Declared in [irgen.h](../../runtime/vm/jit/irgen.h), this module is
  used to convert the bytecode instructions from a RegionDesc into a sequence
  of HHIR instructions.  One `emitFoo()` function is defined for every HHBC
  instruction. The implementations for these functions are grouped into
  `irgen-*.cpp` files (e.g.
  [irgen-basic.cpp](../../runtime/vm/jit/irgen-basic.cpp),
  [irgen-arith.cpp](../../runtime/vm/jit/irgen-arith.cpp)).
* `IRGS`: Defined in [irgen-state.h](../../runtime/vm/jit/irgen-state.h), this
  class contains all the state tracked during the irgen process. The two most
  important pieces are `IRBuilder` and `IRUnit`:
* `IRBuilder`: Defined in [ir-builder.h](../../runtime/vm/jit/ir-builder.h),
  this class tracks state during symbolic execution and performs some very
  basic optimizations based on this state.
* `IRUnit`: Defined in [ir-unit.h](../../runtime/vm/jit/ir-unit.h), this class
  is responsible for creating and storing the runtime data structures that
  represent HHIR instructions, values, and blocks.
* `simplify`: Declared in [simplify.h](../../runtime/vm/jit/simplify.h), this
  module is responsible for performing state-free optimizations such as
  constant folding and propagation or anything else that only requires
  inspecting an instruction and its sources.

### Type System

All values in HHIR have a type, represented by the `Type` class in
[type.h](../../runtime/vm/jit/type.h). A `Type` may represent a primitive type
or any arbitrary union of primitive types. Primitive types exist for
Hack-visible types such as `Int`, `Obj`, and `Bool`, and runtime-internal types
such as `FramePtr`, `Func`, and `Cls`. Primitive types also exist for PHP
references and pointers to PHP/Hack values: for each primitive PHP/Hack type
`T`, `BoxedT`, `PtrToT`, and `PtrToBoxedT` also exist. A number of types
commonly thought of as primitives are actually unions: `Str` is defined as
`{PersistentStr+CountedStr}` and `Arr` is defined as
`{PersistentArr+CountedArr}`. Predefined `constexpr Type` objects are provided
for primitive types and many common union types: simply prepend `T` to the name
of the type (so `TInt` represents the `Int` type, `TCell` represents the `Cell`
type, etc...).

In addition to arbitrary unions of primitive types, `Type` can also represent
constant values and "specialized" types. A constant `Type` may represent the
integer 5 (created with `Type::cns(5)`) or the string "Hello, world!"
(`Type::cns(makeStaticString("Hello, World!"))`), while a specialized type can
represent an object of a specific class (`Type::ExactObj(cls)`) or an array of a
specific kind (`Type::Array(ArrayData::kSomeKind)`).

Since types represent sets of values, we define relations on types in terms of
the sets of values they represent. Two types `S` and `T` are equal (`S == T`)
iff they represent equal sets of values. `S` is a subtype of `T` (`S <= T`) if
the set of values represented by `S` is a subset of the set of values
represented by `T`. `S` and `T` are not related if their intersection is the
empty set (also called `Bottom`).

As previously mentioned, types in HHIR represent a mix of Hack-visible types and
internal types. The following table describes types representing Hack values.
Note that the types used here are more specific than what can be discriminated
by user code (e.g., `StaticStr` and `CountedStr` both appear as type "string" at
the Hack level).

  Type           | HHVM representation
  ---------------|-------------------
  Uninit         | `KindOfUninit`
  InitNull       | `KindOfNull`
  Null           | `{Uninit+InitNull}`
  Bool           | `false=0`, `true=1` (8 bits at runtime)
  Int            | `int64_t` (64-bit two's complement binary integer)
  Dbl            | `double` (IEEE 754 64-bit binary floating point)
  StaticStr      | `StringData*` where `isStatic() == true`
  SharedStr      | `StringData*` where `isShared() == true`
  PersistentStr  | `StringData*` `{StaticStr+SharedStr}`
  CountedStr     | `StringData*` where `isRefCounted() == true`
  Str            | `StringData*` `{PersistentStr+CountedStr}`
  \*Arr          | `ArrayData*` (same variants as `Str`)
  \*Vec          | `ArrayData*` where `kind() == Vec`
  \*Dict         | `ArrayData*` where `kind() == Dict`
  \*Keyset       | `ArrayData*` where `kind() == Keyset`
  UncountedInit  | `TypedValue`: `{Null+Bool+Int+Dbl+PersistentStr+PersistentArr}`
  Uncounted      | `TypedValue`: `{UncountedInit+Uninit}`
  Obj            | `ObjectData*`
  Obj<=Class     | `ObjectData*` of the specified Class or one of its subclasses
  Obj=Class      | `ObjectData*` of the specified Class (not a subtype)
  Cls            | `Class*`
  Func           | `Func*`
  Counted        | `{CountedStr+CountedArr+Obj+BoxedCell}`
  Cell           | `{Null+Bool+Int+Dbl+Str+Arr+Obj}`

The VM also manipulates values of various internal types, which are never
visible at the PHP level.

  Type           | HHVM representation
  ---------------|--------------------
  PtrToT         | Exists for all T in `Cell`. Represents a `TypedValue*`
  Bottom         | No value, `{}`. Subtype of every other type
  Top            | Supertype of every other type
  VarEnv         | `VarEnv*`
  NamedEntity    | `NamedEntity*`
  Cctx           | A `Class*` with the lowest bit set (as stored in `ActRec::m_cls`)
  Ctx            | `{Obj+Cctx}`
  RetAddr        | Return address
  StkPtr         | Pointer into VM execution stack
  FramePtr       | Pointer to a frame on the VM execution stack
  TCA            | Machine code address
  Nullptr        | C++ `nullptr`

### Usage guidelines

We've observed some common misuses of `Type` from people new to the codebase.
They are described here, along with how to avoid them.

#### Comparison operators

Since a `Type` represents a set of values, the standard comparison operators on
`Type` perform the corresponding set operations:

- `==`, `!=`: Equality/inequality
- `<`, `>`: Strict subset/strict superset
- `<=`, `>=`: Non-strict subset/non-strict superset

One important consequence of this is that a [strict weak
ordering](https://en.cppreference.com/w/cpp/named_req/Compare) does not exist
for `Type` objects, which means `Type` cannot be used with algorithms like
`std::sort()`. Put another way, many pairs of `Type`s cannot be ordered: both
`TInt < TStr` and `TInt >= TStr` are `false`, for example. As long as you think
in terms of set comparisons and not numerical ordering, it should be fairly
intuitive.

To check if a value is of a certain type, you almost always want to use `<=`.
So, instead of `val->type() == TInt`, use `val->type() <= TInt`, or more
compactly, `val->isA(TInt)`. Using exact equality in this situation would give
unexpected results if `val` had a constant type, like `Int<5>`, or if we ever
added other subtypes of `Int` (with value range information, for example).

A related problem is determining when a value is *not* of a certain type. Here,
the difference between "`val` is not known to be an `Int`" and "`val` is known
to not be an `Int`" is crucial. The former is expressed with `!(val->type() <=
TInt)`, while the latter is `!val->type().maybe(TInt)`. Types like `{Str+Int}`
illustrate the difference between these two predicates: `((TStr | TInt) <= Int)
== false` and `(TStr | TInt).maybe(TInt) == true`.

#### Inner types

HHIR programs are in SSA form, so a value can't change once it has been defined.
This implies that the value's type also can't change, which requires extra
consideration when working with types that have inner types, like `PtrToFoo`.
The immutability of types also applies to these inner types, so when reading
from or writing to a `PtrToInt`, it is safe to assume the the pointee is always
an `Int`.

This may sound obvious, but there are some situations in which you may find
yourself wanting to construct a value with an inner type that can't be relied
on. Flow-sensitive type information should not be used to feed inner types,
especially runtime type guards. Much of HHBBC's type inference is
flow-insensitive, and that can safely feed inner types of pointers.

There is one small tweak to the rule "a value's type cannot change once
defined": if the pointee is destroyed, the pointer can't be safely dereferenced,
and the type doesn't have to be valid anymore. One example of this is an object
property that HHBBC says is always a `Dbl`. It's safe to create a `PtrToDbl`
pointer to the property, because the information from HHBBC is flow-insensitive.
But if the object is freed and a new object is allocated at the same memory
address, it's possible that this pointer will now point to something completely
different. This is fine, because dereferencing the pointer would be analogous a
use-after-free bug in C++, resulting in undefined behavior.

### Values, Instructions, and Blocks

An HHIR program is made up of `Block`s, each containing one or more
`IRInstruction`s, each of which produces and consumes zero or more `SSATmp`
values. These structures are defined in
[block.h](../../runtime/vm/jit/block.h),
[ir-instruction.h](../../runtime/vm/jit/ir-instruction.h), and
[ssa-tmp.h](../../runtime/vm/jit/ssa-tmp.h), respectively.

An `SSATmp` has a type, an SSA id, and a pointer to the `IRInstruction` that
defined it. Every `SSATmp` is defined by exactly one `IRInstruction`, though one
`IRInstruction` may define more than one `SSATmp`. Every instruction has an
`Opcode`, indicating the operation it represents, and a `BCMarker`, which
contains information about the HHBC instruction it is part of. Depending on the
opcode of the instruction, it may also have one or more `SSATmp*` sources, one
or more `SSATmp*` dests, one or more `Edge*`s to other `Block`s, a `Type`
parameter (known as a `typeParam`), and an `IRExtraData` struct to hold
compile-time constants. `IRInstructions` are typically created with the
`irgen::gen()` function, which takes an `Opcode`, `BCMarker`, and a variadic
number of arguments representing the other properties of the instruction.

A `Block` represents a basic block in a control flow graph. A pointer to one
`Block` is stored in `IRUnit` as the entry point to the program; all other
`Block`s must be reached by traversing the CFG. Certain instructions are "block
end" instructions, meaning they must be the last instruction in their `Block`
and they contain one or more `Edge`s to other `Block`s. `Jmp` is the simplest
block end instruction; it represents an unconditional jump to a destination
block. `CheckType` is an example of an instruction with two `Edge`s: "taken" and
"next". It compares the runtime type of its source value to its `typeParam`,
jumping to "taken" block if the type doesn't match, and jumping to the "next"
block if it does.

While block end instructions may only exist at the end of a `Block`, there are
two instructions that may only exist at the beginning of a `Block`: `DefLabel`
and `BeginCatch`. `DefLabel` serves as a phi-node, joining values at control
flow merge points. `BeginCatch` marks the beginning of a "catch block", which
will be covered in more detail later.

### Control Flow

HHIR can represent arbitrary control flow graphs. In place of traditional
phi-nodes, HHIR uses `Jmp` instructions that take sources and pass them to
`DefLabel` instructions. Consider the following program that performs some
numerical computation:

```
B1:
  t1:Int = ...
  JmpZero t1:Int -> B3
 -> B2

B2:
  t2:Int = AddInt t1:Int 5
  Jmp t2:Int -> B4

B3:
  t3:Dbl = ConvIntToDbl t1:Int
  t4:Dbl = MulDbl t3:Dbl 3.14
  Jmp t4:Dbl -> B4

B4:
  t5:{Int|Dbl} = DefLabel
  ...
```

After control flow splits at the end of B1, B2 and B3 each do their own
computation and then pass the result to the `DefLabel` at the join point,
B4. This is equivalent to the following phi-node: `t5:{Int|Dbl} = phi(B2 ->
t2:Int, B3 -> t4:Dbl)`

## Optimizations

Two types of basic optimizations are performed on each instruction by
`IRBuilder` as it is generated and added to its `Block`: pre-optimization and
simplification. Pre-optimization performs simple optimizations based on tracked
state in `IRBuilder`, such as replacing a `LdLoc` instruction with a use of an
already known value for that local variable. Simplification performs any
optimizations that only require inspecting an instruction and its sources. This
is primarily constant folding but also includes things like eliminating
`CheckType` instructions that are known at compile-time to succeed. Both
pre-optimization and simplification may return an already-defined value to use
instead of the new instruction, or they may generate one or more different
instructions to use in place of the original.

Once the initial translation pass is complete, a series of more involved
optimizations are run on the entire CFG. These are described in detail in
[jit-optimizations.md](jit-optimizations.md).

## Machine Code Generation

### Register Allocation

### Code Generation

---

### Hphp Doc Hackers Guide Jit Optimizations (`hphp/doc/hackers-guide/jit-optimizations.md`)

# HHVM JIT Optimization Passes

## Guard Relaxation

By default, every live program location read in a translation creates a type
guard. There are currently 10 different primitive types that can be guarded on,
so a translation with just 4 guards can have up to 10<sup>4</sup> unique
combinations of input types, leading to a combinatorial explosion in the number
of retranslations required to support all runtime type combinations. This is
bad news for both JIT code size and runtime performance - these retranslations
chain linearly to each other, so the time it takes to make it past the guards
is O(nTranslations) in the worst case. The runtime option
`Eval.JitMaxTranslations` limits the number of translations allowed per
`SrcKey`, and once this limit is hit any further retranslation requests will
result in a call out to the interpreter. This is almost always less desirable
than generating slightly suboptimal machine code in one or more of the
translations, allowing it to accept a variety of input types. The process we
use to determine which type guards can be loosened is called guard relaxation.

There are two parts to guard relaxation: value constraining and the relaxation
itself. Value constraining happens during the initial IR generation pass, and
is managed by the [IRBuilder](../../runtime/vm/jit/ir-builder.h) class. The
important methods in this process are `IRBuilder::constrainValue()`,
`IRBuilder::constrainLocal()`, and `IRBuilder::constrainStack()`. Whenever
the behavior of a sequence of HHIR depends on the types of one or more values,
the types of those values must be constrained using the constrain* methods
previously mentioned. Each takes the value to be constrained and how it should
be constrained. The possible constraints are defined in the `DataTypeCategory`
enum in [datatype.h](../../runtime/base/datatype.h) and are defined in order of
ascending specificity:

* `DataTypeGeneric` indicates that the type of the value does not matter and is
  equivalent to not constraining the value at all. This is most often used for
  values that are simply moved from place to place.
* `DataTypeCountness` indicates that the only use of the value is to incref or
  decref it. If the value's type is not refcounted, the type guard may be
  relaxed to `Uncounted`.
* `DataTypeCountnessInit` is similar to `DataTypeCountness`, with the exception
  that guards for the type `Uninit` will not be relaxed. This is most commonly
  used for bytecodes like `CGetL`, where `Uninit` values cause a notice to be
  raised while all other uncounted types are treated equally.
* `DataTypeSpecific` is the default constraint, and indicates that the guard
  should not be relaxed past a specific `DataType`.
* `DataTypeSpecialized` indicates that in addition to requiring the value's
  `DataType`, there is an additional type tag in the value that must be
  checked. Currently this includes object classes and array kinds, used by
  [MInstrTranslator](../../runtime/vm/jit/minstr-translator.cpp) to emit more
  efficient machine code for some container objects.

The guard relaxation process often needs to track additional information beyond
a single `DataTypeCategory`. The `TypeConstraint` struct, defined in
[type.h](../../runtime/vm/jit/type.h) is used to hold this information. A
value's type constraint is typically specified by passing a `TypeConstraint`
(`DataTypeCategory` implicitly converts to `TypeConstraint`) to value accessor
methods like `HhbcTranslator::popC()` and `HhbcTranslator::ldLoc()`. The former
is used extensively and so its `TypeConstraint` parameter is optional,
defaulting to `DataTypeSpecific`.

`TypeConstraint`, when used with `DataTypeSpecialized`, also requires
additional information about what property of the type is desired. This
information can be specified using the `setWantArrayKind()` and
`setDesiredClass()` methods.

Note that **any** decisions made in the JIT based on a value's type must be
reflected in that value's type constraint. This includes situations where the
absence of code depends on a value's type, such as eliding refcounting
operations on non-refcounted values. Typically, this just means using
`DataTypeSpecific` and giving no further thought to guard relaxation. If,
however, the operation you are translating can tolerate less specific types,
use an appropriate type constraint and ensure that any HHIR opcodes emitted can
tolerate having the types of their inputs loosened.

When `IRBuilder` is instructed to constrain the type of a value, it walks up
the chain of instructions leading to the value's definition, looking for the
instruction that determined the value's type. Sometimes this instruction is an
opcode with a known output type, such as `ConcatStrStr` which always produces a
`Str`. In these cases nothing is constrained, since the value's type does not
depend on a guard. When the source of a value's type is found to be a guard
instruction such as `GuardLoc`, `GuardStk`, or `CheckType`, the current
constraint for that guard is tightened according to the new constraint. All
guards start as `DataTypeGeneric` and may only have their constraints
tightened. The constraints for all guards are stored in a `GuardConstraints`
object owned by `IRBuilder`.

Certain optimizations performed by `Simplifier` are disabled during the initial
IR generation pass. The best example of this pattern is the `IncRef`
opcode. When given an input value of an uncounted type, the IncRef can be
eliminated. However, if the input type of the instruction may be loosened by
guard relaxation, it must not be eliminated. The reoptimize pass, described
below, eliminates instructions that are still eligible for simplification after
guard relaxation runs.

Once initial IR generation is complete, guard relaxation is the first
optimization pass to run. This is to simplify the other passes: any
modifications of the IR performed before guard relaxation would have to be
careful to keep the `GuardConstraints` map updated, and the loosened types
produced by guard relaxation may affect the behavior of other passes. The
relaxation pass is implemented in `relaxGuards()` in
[guard-relaxation.cpp](../../runtime/vm/jit/guard-relaxation.cpp). It is a
fairly simple pass: for all guard instructions present in the trace, their
constraint is looked up in the `GuardConstraints`. If the type of the guard is
more specific than is required by its constraint, the type is loosened. Some
guards will be loosened all the way to `Gen`; these guards will be eliminated
in the reoptimize pass. After loosening guard types as needed, one more pass
over the trace is performed to recompute any types derived from the modified
guards.

Most code in the JIT doesn't have to care about guard relaxation. If you're
implementing an operation that is type agnostic (or can be made type agnostic
without bloating the code much), it may be a good candidate for something more
general than `DataTypeSpecific`. Be aware that guard relaxation's effect on the
generated code is almost always negative, so it should only be used in
situations where the benefits of having fewer translations for the current
`SrcKey` outweigh the increased complexity of the generated code.

## Dead code elimination

---

### Hphp Doc Hackers Guide Memory Management (`hphp/doc/hackers-guide/memory-management.md`)

## HHVM Memory Layout and Management

This page contains a bird's eye view of HHVM memory layout and management. WIP

## Stack

### C++ stack

The normal function call stack

### VM stack

Contains a mix of TypedValues and Activation Records. `Vmsp` points to this
Both grow down (high address -> low address) and shrink upwards in parallel as the request runs

The OS thread stack is manipulated by compiled C++ as it runs.  The VM thread stack is manipulated by the interpreter or compiled JIT code as it runs.  When JIT code or the interpreter call into C++, they use the OS stack.

If you're running the interpreter (no jit) then the vm stack grows/shrinks with PHP call but you stay in a single C++ activation frame the whole time.

## HHVM Request Heap

All refcounted objects go to heap, all inherit from HeapObject which keeps the refcount. If the refcount goes to 0, the object is freed immediately by pushing it on a freelist (see MemoryManager)

Periodically a mark-sweep garbage collector marks transitive objects starting from roots (RDS, C++ stack, and the VM stack), and frees objects not reachable from a root, even if they have a nonzero refcount. If there were no refcounting bugs, this would only identify objects in or reachable from reference cycles. But with refcounting bugs (missing decrefs), it also cleans up objects whose refcount is incorrect.

The JIT uses profiling to identify decref sites that never reach 0 or 1, and can optimize away the decref. This leads to floating garbage that only the mark-sweep GC can reclaim. It was a small win at some point in the past.

RDS (see below) and the 2 stacks are the root set of the heap.

### Two special types of heap objects

#### Uncounted

Live longer than a request, managed by other mechanisms, note that even uncounted objects have a sentinel refcount but it's used by garbage collector whose value marks the object as uncounted. The refcount primitives use the sentinel to avoid mutating the count of objects shared between requests. The GC uses memory ranges to identify request heap objects without inspecting their refcount

#### Static

Never freed once created. In lowptr builds these are allocated in the low 4GB so we can use 32-bit pointers - see LowPtr<T>.

Together, both of the above go by KindOfPersistent data type.

## RDS

Special, fixed sized area for per request data with request lifetime, addressable consistently by any request.
RDS::link stores the index of each item in the process memory, while a request is executing
vmtl (or R12 in jit'd code) points to the request's RDS segment.
RDS consists of a header, a normal segment, a local segment (persistent but non shared) and a persistent segment (shared persistent)
Read more: hphp/runtime/base/rds.h


## APC

Key value store that sits outside the process memory. Keys are string, values can be {string,object,array}. Objects are stored in a serialized representation while array etc in their native form. Values in APC can be deleted or expire. Treadmill is used to reclaim memory.

APC is implemented as a tbb::ConcurrentHashMap, containing pointers to uncounted objects that live it's all in the hhvm process heap (but not individual request heaps).


## Memory Management

### Reference counting

HHVM's user heap is managed primarily with reference counting. All types that
represent a user-visible value inherit from
[`HeapObject`](../../runtime/base/header-kind.h), which contains the value's
reference count, a `HeaderKind` that describes its type, some mark bits for use
by the garbage collector, and some padding bits available for type-specific
uses. `HeapObject`s are created with a reference count of 1, and this count is
incremented or decremented as references to it are created or destroyed.
When a `HeapObject`'s reference count goes to 0, it is destroyed.


### Garbage collection

HHVM also has a [mark-sweep garbage
collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection) that runs
as a backup for reference counting. `HeapObject`s will be freed by the garbage
collector if they are not reachable from the request's roots, for example when
the only references to them are part of a reference cycle. The collector
primarily lives in [heap-collect.cpp](../../runtime/base/heap-collect.cpp) and
[heap-scan.h](../../runtime/base/heap-scan.h), with [scanning
code](https://github.com/facebook/hhvm/search?q=TYPE_SCAN_CUSTOM&unscoped_q=TYPE_SCAN_CUSTOM)
spread throughout various other files, situated with the types being scanned.

For information on annotations, see [type-scan.h](../../util/type-scan.h).

---

### Hphp Doc Hands On Lesson0 (`hphp/doc/hands-on/lesson0.md`)

# Lesson 0: Building and running HHVM

## Lesson goals

* Understand what HHVM is and what it can be used for
* Set up a devserver and build HHVM
* Run HHVM on a small standalone Hack file, and examine its output

---

## Step 0: Starting a build

Kick off the following steps before proceeding through the guide. They take
time, so we want to get them started now.

> As performance engineers, we're always looking for ways to optimize our lives =)

1. [Consult the main documentation for system requirements.](https://docs.hhvm.com/hhvm/installation/building-from-source)
2. [Check out the HHVM repository.](https://docs.hhvm.com/hhvm/installation/building-from-source#downloading-the-hhvm-source-code)
3. [Kick off a build of HHVM.](https://docs.hhvm.com/hhvm/installation/building-from-source#building-hhvm)

---

## What is HHVM?

HHVM is a compiler and runtime for the dynamically-typed language Hack.

Hack is a variant of PHP. Like PHP, Hack is a good language for writing the
backend of a web application. Large websites are written in Hack, including
[facebook.com](https://www.facebook.com/).

## What are our goals for HHVM?

We'd like to improve HHVM in two main ways:

1. **Better performance:** By optimizing the machine code that HHVM generates,
   we can realize significant capacity wins for people who are deploying code
   and websites built in Hack.
2. **Language improvements:** We work with the Hack language team to improve
   the language itself. These improvements include changes that make Hack
   simpler and safer (e.g. [Hack Array Migration](https://hhvm.com/blog/10649/improving-arrays-in-hack))
   as well as new language features (e.g. [readonly](https://hhvm.com/blog/2022/02/22/announcing-readonly.html)).

> Performance wins in HHVM can save our users lots of money. That's why there
> are so many $$$ in Hack code!

## How does HHVM work?

HHVM has two strategies for executing Hack code:

* [An interpreter:](https://en.wikipedia.org/wiki/Interpreter_(computing))
  basically, a while loop that executes Hack code step-by-step.
* [A just-in-time (JIT) compiler:](https://en.wikipedia.org/wiki/JIT_compilation)
  a compiler that can compile a whole sequence of Hack code into optimized
  machine code, at runtime. Most webservers running HHVM use the JIT to compile
  code while serving web requests.

> Let's say that one more time, because it's a bit strange and magical! **HHVM
> is a JIT compiler, which means that it can compile Hack code while executing
> that code.** We'll discuss this idea at length later.

HHVM's interpreter and JIT are interoperable; to execute a given web request,
we might run some JIT-ed code for some Hack functions and use the interpreter
for others. JIT-ed code is typically 10-50x faster than interpreted code. Our
aim is to maximize overall performance, measured as webserver throughput. To
do so, we preferentially JIT the "hottest" (most expensive) Hack functions.
In typical usage, we may only JIT a small fraction (say 1%) of a web codebase,
but that may be enough coverage for us to spend ~99% of our time in JIT-ed code
and only ~1% of time in the interpreter.

---

## Step 1: Running HHVM

Since you've completed step 0 already ;) you should have an HHVM binary sitting
in your build directory. Let's poke it. Here's what mine says if I ask for its
version. Yours will have a different compiler ID, repo schema, etc. The
compiler ID should match the git hash at which you compiled HHVM:

```
$ hg log -r . -T{node}
e0e849921332a9ccacf5d06a6ad05b87bc0ae0ba
$ ./build/hhvm --version
HipHop VM 4.141.0-dev (dbg) (lowptr)
Compiler: default-0-ge0e849921332a9ccacf5d06a6ad05b87bc0ae0ba
Repo schema: 3eabd3c8d14119f88cbf4ade871a893128d72200
```

Let's compile and run a simple PHP file. Create a directory called ~/php -
that's where all of our code examples will live! - and then put the following
code into ~/php/hello-world.php:

```
<?hh

<<__NEVER_INLINE>>
function add($a, $b) {
    return $a + $b;
}

<<__EntryPoint>>
function main() {
  var_dump(add(7, 10));
  var_dump(add(7.0, 10.0));
}
```

We're going to use a script called "hhvm_wrapper" to run HHVM on this file.
This script is unnecessary for simply running a file, but it makes it much
easier to run HHVM in various compilation modes that mimic its production
behavior, and to see debugging output. As a result, we will **always use
hhvm_wrapper** in our examples. Here's a simple invocation:


```
$ hphp/tools/hhvm_wrapper.php ~/php/hello-world.php
int(17)
float(17)
```

Okay, that's good. HHVM can do simple arithmetic correctly...phew!

What's more interesting about this example is that we called add once with two
integers, and once with two floating-point values. Integers and floats have
completely different representations at the hardware level, and we need to use
different machine code instructions (and even different arithmetic circuitry)
to do math on these two types. And yet, HHVM somehow compiles machine code for
"add" that works for both cases. How does that work?

Well, what we do is...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...


Nope! The answer to that question is way beyond the scope of this lesson.

---

## Step 2: Down the rabbit hole

What we can offer you, for now, is a glimpse into what HHVM is doing behind the
scenes. Let's run hhvm_wrapper in two more modes. First off, let's look at
HHVM's bytecode representation. We'll add the flag`--hdf Eval.DumpHhas=1` to
enable bytecode output. This flag disable code executions, so we'll only see
the bytecode, no results:

```
$ hphp/tools/hhvm_wrapper.php --hdf Eval.DumpHhas=1 ~/php/hello-world.php
```

There's no guarantee that you'll get exactly what I got, but if you focus on
the bytecode for the "add" function, you might see something like the following
output:

```
.function{} ["__NEVER_INLINE"("""v:0:{}""")] (4,6) <"" N > add($a, $b) {
  .srcloc 5:15,5:16;
  CGetL $b
  CGetL2 $a
  Add
  .srcloc 5:3,5:17;
  RetC
}
```

We're doing something with locals $a and $b - in a funny order! - and then
we're executing the "Add" bytecode. That makes sense, right? The most important
takeaway from this small test case is that Hack bytecode is untyped. When we
compile Hack source code to bytecode, we don't know the types of Hack values,
so we don't emit type-specific code. The "Add" bytecode takes two inputs and
does "whatever + does in Hack" for those inputs.

> Hack is a dynamic language, and we can write functions - like this "add"
> function, or like "Vec\map" - that operate on many different types of inputs.
> As a result, **Hack bytecode (usually) does not contain type information.**

Now, let's take a look at the next steps of compilation: an intermediate
representation (IR) and the resulting machine code. Let's add a few flags to
our original command:

* `TRACE=printir:1` will cause us to print both IR and machine code.
* `-r 2` will make HHVM run the code twice, then recompile the code in a more optimized way.
* `| less -RN` is just a Unix pager for this output. There's gonna be a lot of output!

```
$ TRACE=printir:1 hphp/tools/hhvm_wrapper.php -r 2 ~/php/hello-world.php | less -RN
```

Don't be intimidated by the volume of output here! Focus on the colors. Aren't
they pretty?

Okay, let's examine a tiny piece of this code. Search for "TransOptimize" in
this output. (In less, you can use "/" to search. That means you should type
"/TransOptimize", then press Enter. Then, you can type "n" to jump to the next
instance of that string, and "N" to jump back to the previous one.)

We're searching for "TransOptimize" because we're only looking for optimized
code generation; unoptimized output looks very, very different. The first
optimized function you'll find is probably "main". You can tell where you are
because the function is labeled (in purple, for me) near that header - I see,
"Function main" (after the digraph output). Keep jumping to the next instance
of "TransOptimize" until you find one for "add". There should be two compiled
sections for this function - one for integers, and one for floats!

> HHVM's JIT automatically specializes functions for the most common input
> types to that function.

Can you tell which compilations of "add" correspond to integers, and which to
floats? Hints:

* Look at the "CheckLoc" - that is, "check a local's type" - operations that appear early in each compilation of "add".
* Remember that "Dbl", or "double", is a 64-bit float.

---

## Lesson summary

* HHVM is a just-in-time or JIT compiler for the dynamically-typed programming language, Hack.
* A JIT compiler can compile code at runtime, and can choose which functions to compile and which to interpret.
* You can use hhvm_wrapper to execute a given Hack file, or to look at intermediate steps of compiler output.

---

## Exercises

Let's modify our "hello-world.php" script to investigate Hack bytecode and HHVM's IR.

1. If we change "add" to "return $a + 2 * $b", how does its bytecode change?
   Which arithmetic operation is done first?
2. With this modified "add" function, how do we implement the "times 2" bit for
   integer $b? Look at the printir!
3. Suppose, instead, we modify main to include these two additional lines. How
   many TransOptimize compilations do we now generate for the "add" function?
   Does this number make sense? Why?

```
var_dump(add(7, 10.0));
var_dump(add(7.0, 10));
```

Now, let's go back to the original version of "hello-world.php", and look at
the printir trace for "main", instead.

1. How many function calls do we make in the TransOptimize compilation for "main"?
2. If we remove the `<<__NEVER_INLINE>>` hint on "add", how does the
   TransOptimize compilation for "main" change? How many function calls do we
   make there now?

---

### Hphp Doc Hands On Lesson1 (`hphp/doc/hands-on/lesson1.md`)

# Lesson 1: Understanding Hack runtime types

## Lesson goals

* Learn about the most common user-visible types in Hack.
* See how HHVM uses a "TypedValue" to represent any Hack value.
* Modify the interpreter to treat "+" as the operator for string concatenation!

---

## Step 0: Pull, update, and re-compile HHVM

If you've been following along, you should have [set up your environment](lesson0.md)
in the prior lesson. If not, do so now.

Since it takes quite a while for HHVM to compile, you should generally kick
off a build whenever you plan to edit its code, before making the edits. We're
going to modify the interpreter in this lesson, so start a build now.

---

## Step 1: User-visible Hack types

Hack is a dynamically-typed language. Most Hack values will be one of the
following types:

* null
* bool
* int
* float
* string
* vec
* dict
* keyset
* object

Let's write and execute a small Hack program that uses these types. Save the
following as ~/php/runtime-types.php:

```
<?hh

class C { function __construct() {} }

function show($x) {
  $repr = json_encode($x);
  if (!$repr) $repr = '<unserializable>';
  print('$x is a '.gettype($x).': '.$repr."\n");
}

<<__EntryPoint>>
function main() {
  show(null);
  show(true);
  show(17);
  show(0.5);
  show('hello');
  show(vec[true, 17]);
  show(dict['a' => true, 'b' => 17]);
  show(keyset[17, 'hello']);
  show(new C());
}
```

As always, we'll run this code with HHVM-wrapper:


```
hphp/tools/hhvm_wrapper.php ~/php/runtime-types.php
```

Now, let's try to see if there are any other types. Modify the file to call
"show" on the following additional values:

```
  show(shape('a' => true, 'b' => 17)); // A shape
  show($x ==> $x * $x);                // A closure
  show(show<>);                        // A function pointer
```

Which of these values have new runtime types?

At this point, you may be confused. Hack - that is, the "hh" type checker -
treats shapes and dicts as different types, but as the above examples show,
they have the same type at runtime. To explain what's going on, we need to look
at how this runtime type system differs from the Hack type system.

---

## Runtime types vs. the Hack type system

So far, we've been talking about the type of a value at runtime. That's related
to, but NOT the same as, the type of the value in the Hack type system! Here's
why a runtime type and a type system's type annotation are different:

* A type annotation constrains what a value **could be**, but a runtime type
  tells us what the value **actually is**.
* A type annotation can include union types, like Hack's "arraykey" (int | string)
  "num" (int | float), or even "mixed" (union of everything!) but at runtime,
  any given value must have some specific runtime type.

Let's consider an example. If a parameter $x is typed as an arraykey, then at
runtime, any given value for $x is EITHER an int OR a string. It doesn't make
any sense for it to be both! At the level of the type system, though, we're
just putting constraints on $x, so "arraykey" does make sense as a type
annotation.

To put it yet another way: the "mixed" type annotation means "no constraint".
**No value is "mixed" at runtime!**

Finally, there's an important fact that's specific to the Hack type system: it
is unsound. That means that there's no guarantee that a given Hack type
constraint for a value will actually match the value's type at runtime. We can
use the Hack type system to identify likely bugs, but we cannot assume that all
of its type annotations are correct. Later on, we'll see that HHVM's JIT
includes an alternate, sound type system, and that this type system is
essential to generating efficient machine code!

> A "sound" type system provides some kind of guarantee about runtime behavior.
Typically, the guarantee is that if the whole program type-checks, then when it
is executed, the runtime type of every value will match its type annotation.
One reason that the Hack type system is unsound is that it includes HH_FIXME
and UNSAFE_CAST as "escape hatches" that allow you to ignore type errors. There
are other reasons, too.

For now, we're going to completely ignore Hack's type system. Type annotations
are optional; HHVM can execute Hack code without any type annotations. But as
we've learned, HHVM still tracks the runtime type of the values it operates on!

---

## Step 1: Representing Hack values

At runtime, HHVM needs a way to represent a Hack value that can be any of
Hack's dozen-or-so runtime types.

We use the classic "C-style" solution to this problem: a tagged union.

Let's consider this idea in C-like pseudocode:

```
enum DataType : uint8_t {
  KindOfNull = 0,
  KindOfBool = 1,
  KindOfInt64 = 2,
  ...
  KindOfObject = 8
};

struct Value {
  union {
    // Null doesn't need a value
    bool m_bool;
    int64_t m_int64;
    ...
    ObjectData* m_obj;
  };
};

struct TypedValue {
  Value m_data;
  DataType m_type;
};
```

Basically, this code says that every Hack value is represented as a TypedValue
struct. This struct has two fields: a "tag" byte called DataType, which tells
us what kind of value the TypedValue contains, and the Value, which contains
*overlapping* storage for each of the different kinds of values. (The "union"
keyword means: "the following fields occupy the same location in memory".)

The first thing we should note about this kind of code is: **this kind of C++
data structure is not safe.**

If we read or write to m_data using the wrong interpretation of its union
field, we might accidentally read and use an int64_t value as an ObjectData*
pointer. In doing so, we can arbitrarily corrupt the heap memory of our
runtime!

> Folks like to say that bugs like this one could result in [nasal
> demons](http://catb.org/jargon/html/N/nasal-demons.html), but in practice,
> the most common outcome is that we produce incorrect results for some Hack
> functions, then segfault soon afterwards =)

On the HHVM team, we put up with unsafe practices like the above because it's
the easiest way to express the low-level behavior that we want the machine to
execute. JIT compilation is inherently unsafe, since any bug in the compiled
output is going to produce incorrect behavior that's worse than pretty much any
bug in regular C++ code.

Now, let's look at our actual implementation of this idea. It appears in two files:

* [runtime/base/datatype.h](../../runtime/base/datatype.h)
* [runtime/base/typed-value.h](../../runtime/base/typed-value.h)

Take a look through these files. Do you see the "DataType" enum? What about
"struct TypedValue", in the latter file?

---

## Step 2: Operating on Hack values

Now, we're going to look at the simplest possible way to operate on these Hack
values.

As we saw in the previous lesson, HHVM can execute an "Add" bytecode to do
"whatever Hack + does" for two arbitrary Hack values! Like all operations in
HHVM, this "Add" bytecode is implemented in both the interpreter and the JIT.

In the interpreter, the implementation of "Add" is as simple as it gets:

1. We pop two inputs from the stack. (Our bytecode assumes that we're using a "stack machine".)
2. We check the input types. Today, "Add" only works on "numeric" - int or float - inputs. We throw on other types.
3. We switch over each of the valid input types, and implement the addition logic for each one.
4. We push the resulting TypedValue output back onto the stack.

Now, Hack already has a string concatenation operator: ".". But perhaps you may
have wondered: why not use "+" for string concatenation in Hack, like other
languages like JavaScript and Python support? Let's make it happen!

The logic for the interpreter is in the following file:

* [runtime/vm/bytecode.cpp](../../runtime/vm/bytecode.cpp)

Search in this file for a function called "iopAdd". This function implements
the logic above. Read it, and see how it works. You should find that it uses a
generic helper to implement the "pop 2 inputs from the stack and push 1 output"
logic. This generic helper takes the binary operation to perform on the two
inputs as a parameter, and for "iopAdd", that operation is "tvAdd", which is in
this file:

* [runtime/base/tv-arith.cpp](../..//runtime/base/tv-arith.cpp)

Once you've read enough of these functions to have a basic understanding,
modify them so that tvAdd also concatenates two string inputs! Use the
definition of the TypedValue, above, and the API for strings in this file to
help you out:

* [runtime/base/string-data.h](../../runtime/base/string-data.h)

After making these changes, recompile HHVM. Remember that your implementation
ONLY modifies the behavior of the interpreter - NOT the JIT!  That means that
HHVM will have two different behaviors based on whether we are interpreting or
compiling a piece of Hack code. That's not good, but for now, we can still test
this interpretation by running HHVM with the JIT disabled.  Put the following
test case into ~/php/concat.php:

```
<?hh

function add($x, $y) {
  return $x + $y;
}

<<__EntryPoint>>
function main() {
  var_dump(add(17, 34));
  var_dump(add(17.0, 34.0));
  var_dump(add("17", "34"));
}
```

Then run:

```
hphp/tools/hhvm_wrapper.php -i ~/php/concat.php
```

If you've done this step, you should see the results 51, 51.0, and "1734"
printed to stdout!

---

## Lesson summary

- Hack is a dynamically-typed language. At runtime, a Hack value has some
  specific runtime type.
- A "type system" constrains what types a value may have. A type system can be
  "sound", meaning that its claims about types are guaranteed to hold for
  values at runtime.
- The Hack type-checker's type system is unsound. HHVM uses a separate, sound
  type system for JIT compilation.
- HHVM represents Hack values as a tagged union: a 1-byte DataType enum, which
  can be used to interpret an 8-byte Value. The Value may be a pointer (e.g.
  strings, objects), an int or bool value, or unused (e.g. for null).
- In the interpreter, we implement Hack bytecode by doing casework on the value
  of this DataType.

---

## Exercises

1. What "secret" DataTypes exist in HHVM but are invisible to Hack? What do you think "KindOfUninit" is?
2. Extend your modified tvAdd so that, if at least one of the two inputs is a string, it uses the logic for Hack "string casts" (the syntax: `(string)$x`) to cast the other input to a string, then concatenates the results.
   1. String casts have an associated bytecode, "CastString". That means that you can search bytecode.cpp for "iopCastString" to see how string cast is implemented, and share that logic.
   2. Test your work by modifying the file to use "add" to concatenate a string and an int, or a string and a float. Does it work? If so, congratulations: **you have just implemented dime-store JavaScript.**
3. Enable the JIT again. How do the results of the "concat.php" file change?
   1. You may be surprised to see that even with the JIT enabled, we still use "+" for string concat - or do we?
   2. Enable the JIT and also enable the "TRACE=printir:1" debug output from the previous lesson. Look at how many times we compile "add", and for which types. Does the string version of "add" explain your results?
4. In C++, we can ask for the "sizeof" a given struct, in bytes. Take a look at this example, where we use static_assert (checked at compile time) to check that different types have the sizes we expect: https://godbolt.org/z/bP4xTPMPq
   1. Why is a DataType 1 byte?
   2. Why is a Value 8 bytes? Does it matter that a bool can fit in 1 byte?
   3. How large is a TypedValue? Add static_assert(sizeof(TypedValue) == ...); to confirm.
   4. Why is a TypedValue that large? (Hint: read [http://www.catb.org/esr/structure-packing/](http://www.catb.org/esr/structure-packing/))

---

### Hphp Doc Hands On Lesson2 (`hphp/doc/hands-on/lesson2.md`)

# Lesson 2: Understanding Hack bytecode

## Lesson goals:

* Learn how we evaluate Hack expressions using a stack machine.
* See how we implement operators, function calls, and array and object constructors in bytecode.
* Consider how alternative bytecode sequences could result in performance improvements.
* Write Hack code that compiles to specific bytecodes by [reading our documentation](../bytecode.specification).

---

## Step 0: Pull, update, and re-compile HHVM

As in the [previous lessons](lesson0.md), let's start by re-compiling HHVM as
we review some concepts.

---

## Bytecode for a stack machine

Hack bytecode is a linear sequence of instructions that implements the logic of
a Hack program. The main challenge of compiling Hack source code to bytecode is
to flatten an abstract syntax tree (AST) into this form. Compiling for a stack
machine is one natural way to do this flattening.

Let's work through an example. Here's the Hack expression that we're going to
compile to bytecode:

```
$a * f($b, 2, 'x') + $c
```

We're going to assume that we have a working parser for this language, so we
can parse this expression into a tree format. Furthermore, we're going to
assume that this parser handles order of operations for us, so that the root
node of this tree is the "add" operation, even though that operation comes
second. (Note that the root node of the tree is the **last** expression
evaluated!) Here's an AST for this expression:

```
      Add
     /   \
   Mul    $c
  /   \
$a      Call: f
       /   |   \
     $b    2    $x
```

Our goal is to produce Hack bytecode that will push the result of an expression
on the "stack". Here, the stack is an abstract construct of HHVM's virtual
machine. It does not necessarily correspond to a computer's native stack, and
depending on how we compile a function to machine code, we may even optimize
away all access to this stack! However, we can define HHVM's behavior in terms
of this abstract stack.

Let's call the function that produces bytecode for an AST node
"CompileToBytecode". We can implement this function recursively. In pseudocode,
that might look like:

```
def CompileToBytecode(ast_node, bytecode):
  if not ast_node.children:
    bytecode.push(CompileLeaf(ast_node.leaf))
    return

  for child of ast_node.children:
    CompileToBytecode(child)
  bytecode.push(CompileOp(ast_node.op))
```

> It's funny: whenever I say I'm going to write some pseudocode, what comes out
> is valid Python.

## Evaluating leaf nodes

Let's examine this pseudocode. First off, note that there's a distinction
between "leaf" and "internal" nodes – the blue and green nodes in our diagram
above, respectively. Leaf nodes are primitive expressions that don't take any
stack inputs. $a is a leaf node, because pushing $a on the stack is
accomplished with the CGetL bytecode:

```
CGetL <Local ID>: push the local with ID <Local ID> onto the stack
```

CGetL does take an input—the ID of the local to push—but this input is a
constant, so it's **part of the bytecode**, not a stack input! In the compilers
world, we often refer to the constant inputs of a low-level instruction as
"immediates". CGetL is a bytecode that takes one immediate - a local ID - and
no stack inputs, and pushes that local onto the stack.

The constant expressions 2 and "x" can similarly be evaluated by bytecodes with
no stack inputs. Again, these bytecodes have immediates - the constant integer
and the constant string to push, respectively. We have:

```
Int <constant int>: push this integer onto the stack
```

and:

```
String <constant string>: push this string onto the stack
```

These three bytecodes - CGetL, Int, and String - are sufficient to cover all
the leaf nodes in our example. Now, let's move onto the internal nodes. The
second half of our CompileToBytecode deals with these nodes.

## Evaluating internal nodes

We've already seen an example of an internal nodes in the previous lesson: the
"Add" bytecode. This bytecode pops its two inputs from the stack, does
"whatever + does in Hack" for those two inputs, and pushes the result into the
stack. To evaluate `(expr_1) + (expr_2)` it suffices to recursively evaluate
the two expressions (putting each result on the stack), then execute an "Add"
bytecode.

"Add" and "Mul" are simple examples of internal nodes, in that they consume a
fixed number of stack inputs and take no immediates. Other nodes introduce a
few wrinkles to this basic pattern:

1. Some nodes take immediates. For example, "IsTypeC" (used to evaluate `(expr)
   is TYPE`) takes one input—the expression—from the stack, but takes the type
   as the immediate.
2. Some nodes take a variable number of arguments from the stack. For example,
   "NewVec <constant int>" (used to evaluate `vec[(expr_1), (expr_2), ...
   (expr_N)]`) takes the number of inputs `N` as an immediate, pops `N` values
   from the stack, and pushes the vec constructed from those values.
3. Some nodes take additional, hidden stack inputs in addition to the syntactic
   inputs from the original Hack source.

We'll examine a function call node—the "Call" node in our example above—which
includes of the complexity above. There are several bytecodes that deal with
function calls in Hack. We support calls to free functions, instance methods,
and class methods, with static and dynamic versions of each, so we need at
least six function call bytecodes. There are several immediates used by all of
these cases, so we package them all up into "FCallArgs":

```
struct FCallArgs {
  uint32_t numInputs;
  uint32_t numOutputs;
  // Other fields for async calls, inout args, coeffects, etc.
};
```

The two most important bits in this struct are the "numInputs" and "numOutputs"
fields, which tell us how many arguments the call bytecode will pop from the
stack, and how many return values it will push. (We model functions with inout
arguments as pushing those results as additional return values, but for any
"normal" function call without such arguments, numOutputs will equal 1.)

The remaining immediates are specific to each type of function call. They
identify which function is being called. Hopefully, the fact that we have
multiple bytecodes for calls makes sense, because we must take different inputs
to identify the "receiver" of free functions, instance methods, and class
methods:

* A free function is identified by its string name.
* An instance method is identified by an object to call the method on and the name of the function to call.
* A class method is identified by a class pointer to call the method on and the name of the function to call.

The bytecode for a static call to a free function, FCallFuncD, takes two
immediates: FCallArgs, and a constant string function name. It has the
following signature:

```
FCallFuncD <FCallArgs> <constant string>:
  Call the function with the given string name, popping FCallArgs.numInputs arguments
  from the stack and pushing FCallArgs.numOutputs return values to it.
```

The final wrinkle for these function calls is that they require—for now!—two
stack inputs before the syntactic arguments to the function. These inputs only
serve to pad the stack in memory with enough space for a [machine-level
function call frame](https://en.wikipedia.org/wiki/Call_stack#Structure) (also
called an "activation record" or "ActRec").

> The fact that a call takes two hidden stack inputs is contingent on the
> current size, in bytes, of [HHVM's in-memory representation of an ActRec](../../runtime/vm/act-rec.h).
> In particular, stack slots occupy 16 bytes in memory—each one is a
> TypedValue!—and an ActRec occupies 32 bytes. It's rare for Hack bytecode to
> be sensitive to low-level implementation details, but in this case,
> unfortunately, it is.

To handle these hidden inputs, we have to modify our pseudocode above to
account for them in its "children" loop. In particular, for any call bytecode,
the hidden inputs are pushed first, before the syntactic arguments.

## Putting it together

Let's run our pseudocode on the AST above. Here's the execution trace, showing the recursive calls, in order:

1. Compile(Add):
   1. Compile(Mul):
      1. Compile(Leaf $a) => **CGetL $a**
      2. Compile(Call):
         1. Push ActRec padding => **2x NullUninit**
         2. Compile(Leaf $b) => **CGetL $b**
         3. Compile(Leaf 2) => **Int 2**
         4. Compile(Leaf 'x') => **String "x"**
         5. Push the op => **FCallFuncD {3, 1} "f"**
      3. Push the op => **Mul**
   2. Compile(Leaf $c) => **CGetL $c**
   3. Push the op => **Add**

Remember that the algorithm produces a linear bytecode sequence via this
in-order tree traversal. Here's the result. Try stepping through it, and verify
that it computes the result of `$a * f($b, 2, 'x') + $c` and leaves it on the
stack!

```
CGetL $a
  NullUninit
  NullUninit
  CGetL $b
  Int 2
  String "x"
  FCallFuncD {3, 1} "f"
  Mul
  CGetL $c
  Add
```

---

## Step 1: Examining basic expressions

In this section, we're going to look at the actual bytecode that HHVM generates
for different cases. There are a few hundred distinct bytecode operations to
keep track of. Luckily, these operations are all documented in a file:

* [hphp/doc/bytecode.specification](../bytecode.specification)

Let's start by double-checking our prediction for the example above. Do so by
creating Hack script that evaluates the given expression and using the
"Eval.DumpHhas=1" debug flag. Put the following code into `~/php/bytecode.php`:

```
<?hh

function test($a, $b, $c) {
  return $a * f($b, 2, 'x') + $c;
}
```
Then run this code with:

```
hphp/tools/hhvm_wrapper.php --hdf Eval.DumpHhas=1 ~/php/bytecode.php | grep -v srcloc
```

(The last part of this command filters out "srcloc" annotations in the
generated bytecode, which attribute bytecode back a file and line in the Hack
source.) If we run this command on our test file, we'll get a compilation of
"test" which is basically the bytecode needed to compute our example
expression. The only additional bytecode is a RetC, which turns an expression
into a return statement: it pops one element off the stack and returns that
value to the caller. Here's a (slightly edited form) of what I get:

```
.function{} (3,5) <"" N > test($a, $b, $c) {
  NullUninit
  NullUninit
  CGetL $b;"b"
  Int 2
  String "x"
  FCallFuncD ... 3 1 ... "f"
  CGetL2 $a;"a"
  Mul
  CGetL $c;"c"
  Add
  RetC
}
```

This bytecode is quite close to our prediction above. There are a few more
components to the FCallArgs struct than what we showed, but that's expected;
Hack supports a variety of special ways to call functions. A bigger difference
is that $a is pushed onto the stack *after* the function call result, and with
a different op—CGetL2, instead of CGetL.

> Wait, what the heck? Shouldn't HHVM evaluate these expressions in order? A
> compiler may re-order expression evaluations as long as doing so has no
> observable effect. For example, we generally can't re-order function calls,
> because function calls could have side effects like writing to the heap,
> throwing errors, or doing IO. Pushing a local onto the stack is side-effect
> free, so in this case, this rewrite is safe.

We should check that this bytecode sequence has the same behavior as the one we
predicted. To do so, we need to understand the semantics of CGetL2. There are a
few ways to find out what a bytecode operation does. You can read:

1. Its docs, by searching for CGetL2 in [bytecode.specification](../bytecode.specification).
2. Its implementation in the interpreter, by searching for "iop$NAME" (i.e. iopCGetL2) in [bytecode.cpp](../../runtime/vm/bytecode.cpp).
3. Its implementation in the JIT, by searching for "emit$NAME" (i.e. emitCGetL2) in the [JIT directory](../../vm/jit).

Here are the docs for CGetL2. (We skip the part about throwing errors, which will not happen in our example.)

```
Get local as cell. If the local variable given by %1 is defined, this
instruction gets the value of the local variable, pushes it onto the stack as
a cell, and then pushes $1 onto the stack.
```

In this explanation, `%1` refers to the first immediate—that is, the local
ID—and `$1` refers to the top element of the stack. Basically, the docs are
saying that CGetL2 pops one element off the stack, pushes the local, then
pushes the popped element on top of it. The net result is that doing CGetL2
after evaluating an expression is equivalent to doing CGetL before evaluating
it.

---

## Step 2: Examining more complex expressions

Let's try out a few more expressions with our setup from above. Try this version:

```
<?hh
class C {}

function test_vec() {
  return vec[17, new C()];
}

function test_dict($x) {
  return dict['a' => 17, 'b' => $x, 'c' => 'd'];
}

function test_concat($x) {
  return 'prefix'.(string)$x.'suffix';
}
```

Here's what I get for these examples. Make sure these outputs make sense. Other
than the complex logic needed to implement the "new C()" syntax, these outputs
are an exact match for what our pseudocode would predict.

```
.function test_vec() {
  Int 17
  NewObjD "C"
  Dup
  NullUninit
  FCallCtor ... {0, 1} ...
  PopC
  LockObj
  NewVec 2
  RetC
}

.function test_dict() {
  Int 17
  CGetL $x
  String "d"
  NewStructDict <"a" "b" "c">
  RetC
}

.function test_concat($x) {
  String "prefix"
  CGetL $x
  CastString
  Concat
  String "suffix"
  Concat
  RetC
}
```

These examples show us a couple of new bytecodes used to implement these basic
elements of Hack syntax:

1. `NewVec 2` pops that two elements from the stack, appends them to a new vec,
   and pushes that vec.
2. "new C()" expands to several bytecodes - from `NewObjD "C"` to `LockObj` -
   that split up the operations of allocating the object and calling the
   constructor. That makes sense, because calling the constructor may require
   evaluating further expressions (the constructor arguments).
3. `NewStructDict <"a" "b" "c">` is similar to NewVec, in that it's a bytecode
   with a variable number of inputs. It takes its values from the stack, but
   the keys are a vector of string immediates.
4. Concatenation happens two inputs as a time - at least, if done with `Concat`
   bytecodes.

---

## Lesson summary

* Hack bytecodes operate on a stack machine. Most bytecodes pop inputs from the
  stack and push an output to it.
* We can compile an expression to bytecode recursively, by compiling its
  subexpressions, then appending a bytecode that applies the top-level
  expression transformation to those inputs.
* Bytecodes take "immediates" - constant arguments that are part of the
  instruction stream - as well as stack inputs.
* Because Hack is a high-level language, it includes bytecodes for constructing
  and operating on complex data structures like objects, strings, vecs, dicts,
  and keysets. [You can see a full list of bytecodes here.](../bytecode.specification)

---

## Exercises

1. Search bytecode.specification for other bytecodes related to string concatenation.
   1. Is there an alternative bytecode sequence that we could use to implement `test_concat`?
   2. Which bytecode sequence would you expect to be faster? Why? (Reading the interpreter may help in this case.)

2. Rewrite `test_dict` from our last example as follows:

```
function test_dict($x, $y, $z) {
  return dict['a' => 17, 'b' => $x, 'c' => 'd', $y => $z];
}
```

   1. How do we implement this modified dict constructor in bytecode?
   2. Are there performance differences between this approach and NewStructDict?
   3. Can you provide an alternative bytecode sequence for this expression that could improve its performance?

Investigate how HHVM handles "member operations"—basically, read/write access
into array elements or object properties. Take a look at the bytecode for these
functions:

```
function prop($x) {
  $x->y = 17;
}

function elem($x) {
  $x['y'] = 17;
}

function nested_one_level($x) {
  $x['y']->z = 17;
}

function nested_two_levels($x) {
  $x['y']->z[] = 17;
}
```

1. How are we handling these sequences of operations in a compositional way?
2. Read the relevant bytecode docs in bytecode.specification, and write Hack
   functions that use the bytecodes BaseH, BaseSC, SetOpM, and UnsetM. Confirm
   with the Eval.DumpHhas output.
3. What changes if we replace these mutating ops with "read-only" member-ops
   like `return $x['y']->z[17];`
4. What state do we need to keep in between steps of a nested member-op write
   sequence? Take a look at [struct MInstrState in this file](../../hhbbc/interp-state.h)
   to confirm your guess.

---

### Hphp Doc Hands On Lesson3 (`hphp/doc/hands-on/lesson3.md`)

## Lesson goals:

* Explore a unique feature of the PHP and Hack languages: value-type strings and array-likes.
* Learn about how HHVM uses reference counting to efficiently implement value types and to reclaim memory.
* See how certain Hack bytecodes result in refcounting operations.
* See how HHVM uses an ahead-of-time compilation step to reduce unnecessary refcounting.
* Make and test a small attempt at a performance optimization.

---

## Step 0: Mysterious missing mutations

Save the following Hack code in `~/php/refcounting.php`:

```
<?hh
class C { function __construct(public int $x) {} }
function mutate_int(int $x) {
  $x = 34;
}
function mutate_object(C $c) {
  $c->x = 34;
}
function mutate_dict(dict<string, int> $d) {
  $d['x'] = 34;
}
<<__EntryPoint>>
function main() {
  $x = 17;
  mutate_int($x);
  var_dump($x);
  $c = new C(17);
  mutate_object($c);
  var_dump($c);
  $d = dict['x' => 17];
  mutate_dict($d);
  var_dump($d);
}
```

What would you expect to see if you ran this code? Make a guess before reading on.

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

Okay, let's run the file. I assume you already have HHVM compiled at this point! Run:

```
hphp/tools/hhvm_wrapper.php ~/php/refcounting.php
```

Here's what you should see as output:

```
int(17)
object(C) (1) {
  ["x"]=>
  int(34)
}
dict(1) {
  ["x"]=>
  int(17)
}
```

What is going on here? The caller `main()` sees the modification made to `$c`
in `mutate_object()`, but it doesn't see the modification made to `$d` in
`mutate_dict()`. Clearly, HHVM is broken. It's a compiler bug! Shut it down!

...no. This result is correct. In PHP and Hack, objects are "passed by
reference" or are "reference types", but strings, vecs, dicts, and keysets are
"passed by value" or are "value types". Primitives like ints, floats, booleans,
and the value "null" are also value types.

> Vecs, dicts, and keysets share many similarities. They're heap-allocated
containers of varying size with value-type semantics. We use the term
"array-likes" throughout our codebase to refer to this trio of types.

When we pass an object as a function argument, we're passing a reference to a
unique value. Mutations to that object are visible to anyone who has a
reference to it. On the other hand, **when we pass a value type as a function
argument, the caller's copy and the callee's copy of that value are logically
distinct**. Mutations made to a value type inside a function are not visible
outside it.

Now, it's not surprising that primitives like ints are value types. Indeed, any
other implementation would be quite inefficient!  [As we saw in Lesson 1](lesson1.md),
ints, floats, and booleans are stored directly in the TypedValue—they aren't
hidden in heap-memory behind a pointer. When we push `$x` onto the stack to
call `mutate_int()`, the value on the stack is independent of the value in the
local.

It's also not surprising that objects are reference types. Again, from Lesson
1, we saw that objects are heap-allocated. All that we store in a TypedValue is
a pointer to this heap memory; the object's class and its properties are stored
in the heap. When we push the object `$c` onto the stack, we're copying the
pointer, not the object! As a result, when we write through this pointer to
modify the object in `mutate_object()`, the effect is visible in main! (If
`mutate_object()` first replaced `$c` with a new object - i.e. `$c = new C()`
- then modifications to this value would not affect the original one.)

The surprising bit here is that in PHP and Hack, strings and array-likes are
**heap-allocated value types**. Even though these values are represented as a
pointer to the heap, when we pass them to a function, the two pointers act like
distinct values. Take a moment to think about how you might implement this
behavior. One straightforward approach is to copy these values when we pass
them as function arguments, but that would be a performance disaster. For
example, it would turn Shapes::idx from a simple O(1) hash table lookup into an
O(n) dict copy! We need a better way.

---

## Enter refcounting

HHVM implements efficient heap-allocated value types using reference counting.
**In fact, all heap-allocated values are refcounted, regardless of whether
they're reference types or value types.** Refcounting serves two purposes:

1. It's a way to eagerly free heap-allocated values. We also use a
   [mark-sweep garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection#Na%C3%AFve_mark-and-sweep)
   to handle cycles.
2. It enables [copy-on-write (COW)](https://en.wikipedia.org/wiki/Copy-on-write)
   for strings and array-likes, allowing us to pass them as arguments in O(1) time.

The first purpose applies to objects, strings, and array-likes. Like Java and
Python, and unlike C++, Hack automatically manages memory for its users. It
frees heap-allocated values that are no longer used. Refcounting is one
approach to automated memory management: if a heap-allocated value has no
references, we can free its memory. The second purpose only applies to
heap-allocated value types, i.e. strings and array-likes.

> Refcounting is not a complete solution to memory management!
> Read ["Moon instructs a student"](http://www.catb.org/jargon/html/koans.html) to see why.

Let's look at how we implement refcounting. In our code, every heap-allocated
value starts with an 8-byte header, the C++ struct HeapObject. This struct is
defined in [the file header-kind.h](../../runtime/base/header-kind.h).
(Don't ask why. The reason has been lost to the mists of time.) Thus, we can
say that even though strings and array-likes are not Hack objects, all
heap-allocated values are HeapObjects.

> Be careful with the terminology! A HeapObject is any heap-allocated value. It
can be a reference type—e.g. an object—or a value type—e.g. a string. We will
always use "object" to refer to Hack objects, and "HeapObject" to refer to the
union of Hack objects, strings, and array-likes.

Every HeapObject tracks a **reference count**: a count of the number of
references to that value that are currently live. These references might be
locals or stack elements. If a HeapObject appears in a vec, the vec holds a
refcount on the HeapObject. If a HeapObject is stored to an object's property,
then the object holds a refcount to the HeapObject. A HeapObject's refcount
stores the number of distinct places that have a pointer to that HeapObject.

> That's not actually true! There are some cases where HHVM skips decrementing
refcounts, making the refcounts an overestimate. The real constraint is that
**a HeapObject's refcount is an upper bound on the number of distinct places
that have a pointer to that HeapObject**. But let's ignore this subtlety.

Every time we copy a HeapObject pointer—for example, if we push a copy of a
HeapObject local onto the stack—we increment the HeapObject's refcount
("inc-ref"). Every time we overwrite or let go of a HeapObject pointer—for
example, if we overwrite a HeapObject local, or if we pop one off the stack—we
decrement its reference count ("dec-ref").

Using refcounting for memory management is simple. Every time we dec-ref a
HeapObject, we check if its refcount is 0. If it is, then we  know that that
object will never be used again, and we can free its associated memory.

Using refcounting to implement efficient heap-allocated value types is a bit
more involved. Whenever we modify a value-type HeapObject, such as a dict, we
use a strategy called copy-on-write:

* If the dict has refcount == 1, we modify it in place. No one besides us has a
  pointer to the dict, so we're still maintaining value-type semantics, despite
  doing O(1) in-place edits.
* If the dict has refcount != 1, we copy it (to get a copy with refcount == 1)
  and the modify the copy. We dec-ref the old value since we've replaced our
  pointer to it with a pointer to the copy.

---

## Refcounting and bytecode

The stack machine semantics of a Hack bytecode imply refcounting bookkeeping
that we have to do to implement it.

We can figure out how to do this refcounting by asking ourselves a question: if
the inputs to a given bytecode are refcounted HeapObjects, how many pointers to
those objects exist after the operation is complete?

Let's start with the simplest case, a CGetL bytecode. From
[bytecode.specification](../bytecode.specification):

```
CGetL <local variable id>    []  ->  [C]

  Get local as cell. If the local variable given by %1 is defined, this instruction
  gets its value and pushes it onto the stack. If the local variable is not defined,
  this instruction raises a warning and pushes null onto the stack.
```

This bytecode takes a local and pushes it onto the stack. If the local is a
HeapObject, then there is now one additional copy of that heap pointer. **In
addition to pushing a value on the stack, CGetL must inc-ref it.**

PushL is a related bytecode that "pushes" a value onto a stack instead of
"getting" it. The difference is that PushL both gets the value of the local on
the stack and unsets the local itself (i.e. sets the local to Uninit, which is
Hack's flavor of "undefined").

```
PushL <local variable id>    []  ->  [C]

  Teleport local value to eval stack. The local given by %1 must be defined.
  This instruction pushes the local's value on the stack, then unsets it,
  equivalent to the behavior of a CGetL / UnsetL pair.
```

If the local that we pushed with PushL is a HeapObject, then we've gained
another copy of that heap pointer on the stack, but we've unset the pointer in
the local. Overall, its refcount is unchanged. **Because PushL "moves" a heap
pointer, it does no refcounting.**

Let's look at one more case, an operation that uses a stack value to set a local:

```
PopL <local variable id>    [C]  ->  []

  Teleport value from the stack into a local. This instruction stores the top
  value of the stack $1 to the local %1, then pops this value off of the stack.
```

Let's say the value on top of the stack was a HeapObject. As with PushL, we're
gaining one reference to it (the new value of the local), but we're also losing
one reference to it (the one we pop off the stack). We do not need to inc-ref
or dec-ref the stack value. However, there is refcounting associated with PopL:
if the old value of the local was a HeapObject, we must dec-ref it! **PopL does
not need to inc-ref or dec-ref the stack value that it's storing to a local,
but it must dec-ref the local's old value.**

Now that you've seen these examples, predict the refcounting needed for these bytecodes:

1. **UnsetL:** "unsets" a local by setting its value to Uninit.
2. **NewVec:** pops n values off the stack and creates a vec containing those elements.
3. **RetC:** pops and returns the top value of the stack, cleaning up the function's frame.
4. **Concat:** pops two values off the stack; if they're not both strings, it throws, else, it concatenates them and pushes the result.

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

If UnsetL is operating on a refcounted local, it must dec-ref the old value.
**UnsetL dec-refs the local's old value.**

NewVec can "move" any HeapObjects it's taking as stack inputs. A stack slot
used to refer to them; now, a vec element refers to them; the overall
refcounting effect is neutral. However, NewVec allocates a new, refcounted vec
and pushes a pointer to it onto the stack. This new vec should have a refcount
equal to 1. **Constructors like NewVec create a new HeapObject with a refcount
of 1.**

RetC signifies a function return. It "frees" the memory associated with the
function's frame. (Frames are usually stack-allocated, so freeing the memory is
just decrementing the stack offset.) Functions may hold references to
HeapObjects in their locals, so before we "free" the frame, we must dec-ref
these values. **RetC dec-refs the function's locals.**

Let's assume Concat is operating on two refcounted strings. Concat should
allocate a new string, so it's acting kind of like a constructor; the new
string will have a refcount of 1. Concat also pops two strings off the stack,
so it must dec-ref those. **In general, stack-only bytecodes like Concat
dec-ref their inputs and either allocate a new output with refcount 1, or
inc-ref an existing value to push it as an output.**

---

## Step 1. Visualizing refcounting

When we run HHVM in interpreted mode, we can turn on debug tracing to see refcounting working as above. Put the following code into `~/php/refcounting.php`:

```
<?hh
class LinkedList { function __construct(public $next = null) {} }
<<__EntryPoint>>
function main() {
  $x = new LinkedList();
  $y = new LinkedList($x);
  $z = $y;
  $y = 17;
  var_dump($z);
  $z = 34;
}
```

Then, run this code in interpreter mode with refcounting debug enabled:

```
TRACE=bcinterp:9 hphp/tools/hhvm_wrapper.php -i ~/php/refcounting.php
```

The resulting output shows the types and refcounts of all locals and stack values at each bytecode. For example, just prior to the final RetC that ends the function main(), we have:

```
dispatch:                    __
dispatch:                    || === Stack at /home/kshaunak/php/refcounting.php:13 func main ===
dispatch:                    || {func:main,callOff:0,this:0x0}<C:0x7f06b903e320:c(1):Object(LinkedList) C:0x11 C:0x22> C:Null
dispatch:                    \/
dispatch: 71: RetC
```

This output says that in function `main()`, local 0 (which is `$x`) contains a
LinkedList with refcount 1 at heap address 0x7f06b903e320. The other two
locals, `$y` and `$z`, are the integers 0x11 and 0x22, respectively, because we
assigned to them earlier. There's one value on the stack, a null, which is the
value that the RetC will return.

Look through this output and see how LinkedList refcounts change with CGetL /
SetL bytecodes. Confirm for yourself that the refcounting semantics match our
predictions above. What is the maximum refcount we see for any object in this
function? Which object has that refcount, which bytecode does it occur at, and
where are all of the references to it?

---

## Step 2: Static strings and array-likes

Strings and array-likes are value types. A bit of Hack code that has a
reference to one of these values can guarantee that no other Hack code can edit
that value. HHVM implements heap-allocated value types by storing a refcount in
their heap data, and blocking mutation for value types with a refcount that is
not equal to 1.

This constraint allows HHVM to make a critical optimization: placing strings
and array-likes in shared memory. Most Hack objects are thread-local. HHVM can
run multiple requests concurrently, and a given object is only referenced on a
single thread: the thread that allocated it. But this approach would be a
disaster for commonly used values like the empty vec or empty dict! Instead,
we'd like to be able to share these values between requests. Multiple requests
would all have concurrent access to a single empty vec and dict.

Here's an approach to sharing these values that does NOT work:

* Store a single, empty vec in a process global with an initial refcount of 1.
* When we retrieve this vec on a request, we inc-ref it, so it starts with a
  refcount of at least 2. We inc-ref and dec-ref it as normal when executing
  bytecode for each request.
* Because the minimum refcount of this vec, as seen from any Hack code, is 2,
  no one will ever modify it.

This approach seems to work. Whenever Hack code is manipulating this global
vec, it will have a refcount of at least 2 (one held by the global itself, and
one held by the value in the request), so it can't be modified. The bug here is
subtle: **modifying any value in shared memory introduces a race condition,
unless done with atomic memory operations**. The bug here is that we can't
actually inc-ref or dec-ref any values in shared memory!

Here's an example of a race condition that can occur when inc-ref-ing the
shared vec:

1. Thread A loads its initial refcount X.
2. Thread B loads its initial refcount X.
3. Thread A writes back the incremented refcount X + 1.
4. Thread B writes back the incremented refcount X + 1.

Even though threads A and B have created two new references to the shared vec,
the refcount has only increased by 1. At some point, when these threads dec-ref
the pointers they hold, they can dec-ref the value to 0 and "free" the array,
causing HHVM to crash!

Instead, here is how HHVM implements shared-memory strings and array-likes:

* The refcount of a string or array-like can be either some positive or
  negative value.
* If the refcount is positive, the HeapObject is thread-local; if it is
  negative, it is in shared memory.
* When doing any inc-ref or dec-ref op on a string or array-like, we first
  check the refcount's sign. If it's negative, we do nothing!

Since concurrent reads on shared memory are safe, this approach doesn't
introduce any race conditions. We can share a single empty vec and empty dict,
and many other constant HeapObjects, between all of our requests! Let's look at
a quick example. HHVM places any "constant value" like a constant string or vec
into shared memory. Put the following code into `~/php/static.php`:

```
<?hh
<<__EntryPoint>>
function main() {
  $x = vec[17, 34];
  var_dump($x);
  $y = 'my constant string';
  var_dump($y);
}
```

Then run it with:

```
TRACE=bcinterp:9 hphp/tools/hhvm_wrapper.php -i ~/php/static.php
```

When we manipulate `$x` and `$y` as locals and as stack values, you should see
a refcount of "c(static)" for them: a sentinel negative refcount value. HHVM's
tvIncRefGen and tvDecRefGen, the generic refcounting helpers, avoid mutating
static strings and array-likes by checking that the refcount is positive before
decrementing it. The refcount of a static string or array-like will never
change!

> You might ask, why don't we use atomic increments or decrement operations
  like "lock-add" to inc-ref and dec-ref all values? Doing that would save a lot
  of instructions: a compare-and-branch on every refcounting op. Unfortunately,
  it would also tank performance.
  [On x86-64, in order to write to any memory, a core must take exclusive access to that memory address](https://en.wikipedia.org/wiki/MESI_protocol)
  (and to the whole cache line containing that address). That means that
  inc-ref-ing a shared value is essentially single-threaded.

As compiler engineers, we should be wary of relying on delicate invariants like
"the refcount of a static value is never mutated". Luckily, we have an ace up
our sleeve to help us enforce such guarantees: the debug-mode build! The
refcounts that are 1 less and 1 greater than the "StaticValue" refcount are
considered to be invalid refcounts. In debug mode, if we ever see a heap object
with a negative refcount that's close to, but not equal to, "StaticValue", we
crash with a nice error message.

> Assertions are a critical tool for testing our code. We check these
  assertions in debug builds, then compile them out in release builds where
  performance is a top priority. In HHVM code, the "assertx" helper checks an
  assertion and prints a stack trace and other diagnostics on failure.
  **Structure your code so that clear invariants hold, then use assertions to
  check them wherever possible!**

---

## Step 3: Refcounting optimizations

As we've seen before, a compiler like HHVM is free to diverge from the
"trivial" implementation of a given bytecode, as long as the logic that HHVM
executes matches the stack machine in any observable behavior. In the previous
lesson, HackC and HHVM used this freedom to re-order some bytecodes, but the
resulting logic was not meaningfully different than the trivial implementation.
Now, we'll look at how we can tweak compilation to gain performance.

**Refcounting is expensive, because it requires accessing heap memory.** Memory
access is one of the most expensive operations on modern CPUs - far more
expensive than doing arithmetic on values in hardware registers. As a result,
saving inc-refs or dec-ref ops is usually a nice win. Furthermore, an inc-ref
and dec-ref on the same value cancel out! The "PushL" bytecode uses this fact
for optimization. Verify for yourself that these bytecode sequences have the
same semantics:

1. CGetL $local; UnsetL $local
2. PushL $local

If we execute the first sequence the "trivial" way, then we'll inc-ref the
local for CGetL (because its value is now in the local AND on the stack), then
immediately dec-ref it (in order to overwrite it with Uninit). Because PushL
does the two ops together, we can cancel these two operations - both when we're
interpreting Push, and when we're JIT-compiling it. HackC does not directly
emit PushL bytecodes, but HHVM comes with an ahead-of-time bytecode optimizer
called HHBBC that does these rewrites. (HHBBC even adds UnsetL ops for locals
that are "dead" - unread by later code in a function - in order to create more
optimizable sequences.)

> Remember that HHVM converts, or "lowers" bytecode to machine code in stages.
It compiles bytecode to intermediate representation (HHIR), then IR to virtual
assembly (vasm), then vasm to machine code. Most optimizing compilers implement
similar optimizations at multiple stages. For example, HHBBC optimizes
bytecode; it replaces bytecode sequences with equivalent sequences with better
refcounting properties. But if we lower bytecode to IR that does an inc-ref
followed by a dec-ref of the same value, we have IR level optimizations (see:
refcount-opts.cpp) that can cancel those ops as well!

Now, let's try to make our own refcounting optimization! Because HHVM has a
garbage collector (GC), we can skip dec-refs without changing the result of any
Hack operations. **Taken to an extreme, we could modify HHVM's DecRef code
generation to return immediately, so dec-refs are a no-op at the machine code
level!** At this point, we'd leak memory to the GC all over the place, but
refcounting would be a lot faster!

> Does this change affect any observable behavior? It turns out that the notion
of "observable" is a bit fuzzy! If we leak this much memory, there's a good
chance the teams monitoring HHVM on live webservers will see a large
increase in memory used. Worse still, the OS's OOM killer (which kills
processes that use too much memory) might kill HHVM servers. Leaking too much
memory is an observable, but leaking a small amount may be okay.

We're going to try a less extreme version of this optimization. The Idx
bytecode takes an array, a key, and a default value as stack inputs. It does
the array access and pushes the result onto the stack, pushing the default
value if the key is not present in the array. Idx must inc-ref the stack output
and dec-ref the stack inputs.

In our codebase, the code that lowers a bytecode $NAME to IR is in a function
called emit$NAME. So, we can find the code to compile Idx by searching for Idx
in the hphp directory. It turns out that it's in
[irgen-builtin.cpp](../../runtime/vm/jit/irgen-builtin.cpp).

1. Modify emitArrayIdx to avoid dec-ref-ing any of the inputs. (It's sufficient to modify the vec and dict cases.)
2. Write a small test file using idx() to confirm that your modification is
   working. You can look at the TRACE=printir:1 output to see that it has fewer
   dec-refs than before (compare the output between your modified HHVM and the
   output from release HHVM). As a shortcut, pass `-b /usr/local/hphpi/bin/hhvm`
   to hhvm_wrapper.php to use release HHVM without recompiling!
3. Kick off local unit tests for your code: `hphp/test/run quick slow`

When you have these steps working, you'll have saved a small amount of
instructions, loads and stores at the cost of a significant memory penalty.
Is this tradeoff an overall CPU win? Only data can tell - but my guess is,
it's a regression! In the next lesson, we'll try to find a performance win.

---

## Lesson summary

* Hack has multiple heap-allocated types: objects, strings, and array-likes
  (vecs, dicts, and keysets).
* Of these types, objects are reference types, while strings and array-likes
  are value types. A mutation to an object is visible to everyone with a
  reference to it. On the other hand, two references to a string behave like
  distinct values.
* HHVM uses refcounting for two purposes: to implement copy-on-write value
  types, and to eagerly free heap values.
* HHVM uses a negative refcount to indicate that strings and array-likes are in
  shared memory. This trick allows us to de-duplicate "static", or constant,
  strings and array-likes across requests.
* Because Hack bytecodes can create new references to heap values, HHVM must do
  inc-refs and dec-refs to implement these bytecodes. For example, we inc-ref the
  stack outputs of a bytecode and dec-ref the stack inputs.

---

## Exercises

1. Remember that HeapObjects include reference types like objects as well as
   value types like strings and array-likes. Why doesn't HHVM support "static"
   (shared-memory) objects?
2. Take a look at HHVM's tvDecRefGen, which does a generic dec-ref of a
   TypedValue. Note that this helper can be used on any TypedValue, including
   non-heap-allocated values like integers.
   1. How many cases does tvDecRefGen have to handle? Confirm by reading the comments in [decref-profile.h](../../runtime/vm/jit/decref-profile.h).
   2. Is it safe to use tvDecRefGen to dec-ref an object?
   3. Is it optimal to use tvDecRefGen to dec-ref an object? What could we do instead?
3. Modify your code for the Idx bytecode to skip the inc-ref of the new value.
   1. Why is the new code unsafe? Why is it okay to skip a dec-ref, but not an inc-ref?
   2. Commit this modified version run the tests with `hphp/test/run quick slow`
   3. If those tests are passing, you can also add other flags to the command:
       a. To test "repo mode", pass "-r" to hphp/test/run. This flag turns on HHBBC.
       b. To test optimized JIT compilation, add "--retranslate-all 2".
       c. Typical HHVM CI will run a battery of tests including these flags.

Here is an hphp/test/run command that includes all of the flags above:

```
hphp/test/run -m jit -r --retranslate-all 2 hphp/test/quick/dict/basic-getters.php
```

Once you can reproduce a failure, fix them by re-adding the inc-refs you
removed and confirm that the test now passes.

---

### Hphp Doc Hands On Lesson4 (`hphp/doc/hands-on/lesson4.md`)

# Lesson 4: Making an IR optimization

## Lesson goals:

* Use profiling tools to find an optimization opportunity.
* Learn how to read printir tracing, the IR documentation, and JIT types.
* Improve IR generation to speed up casting collections (e.g. Vector) to arrays (e.g. vec).
* Move this code to "simplify", one of the IR-to-IR optimization routines, to make it more general.

---

## Step 0: Suboptimal code generation

In this step, we're going to hunt for a potential performance win.

Our main tool in this hunt is profiling. One way to do that is to run
[Linux perf ](https://perf.wiki.kernel.org/index.php/Main_Page)
on a machine with an HHVM webserver under load. HHVM even outputs a
"perf-PID.map" file that can be used to symbolize stack traces in JIT-ed Hack
code. That said, it's easiest to read perf's numbers for C++ helpers, and many
performance opportunities in HHVM come from optimizing C++ functions that are
used to implement the Hack runtime.

In a sample perf trace I took, I saw that ~0.2% CPU is spent doing
object-to-array-like casts (e.g. convObjToVec, convObjToDict, convObjToKeyset).

What makes these helpers a good potential target for optimization? First off,
there is some opportunity there: if we could eliminate most of the cost of
these helpers, it could be a 0.2% gCPU win. Second, the opportunity is
relatively easy to realize. It's difficult to implement general-purpose code to
cast an object to an array-like. That's why HHVM implements these casts by
calling a C++ helper. But after having read enough of web code written in Hack,
I can make an educated guess that most object-to-array casts are casts on a
collections objects. These objects are secretly backed by arrays, which makes
it possible for us to generate faster code to do the cast in this case.

When we have a potential perf idea, it's important to write a small test case
and check that the opportunity shows up. Let's put the following code into
~/php/convert.php:

```
<?hh
<<__EntryPoint>>
function main() {
  $x = Vector { 17, 34 };
  var_dump(vec($x));
}
```

Now, when we run this code with tracing enabled, we must do so at the highest
optimization level! If we don't, we might go down a rabbit hole trying to make
an optimization that is irrelevant for our production use case. To see how HHVM
compiles the code above, we have to run it with both HHBBC (the
bytecode-to-bytecode optimizer) and RetranslateAll (the two-phase JIT) enabled:

```
TRACE=printir:1 hphp/tools/hhvm_wrapper.php -c -r 2 ~/php/convert.php | less -RN
```

Then we have to jump to the TransOptimize translation for main. Here's the
relevant part:

```
 B4: [profCount=2] (preds B0)
    --- bc main(id 1078142128)@5, fp 0, fixupFP 0, spOff 1,  [profTrans=0]
    ColFromArray Vector
    (12) t5:Obj=HH\Vector = NewColFromArray<HH\Vector> vec(0x8018d4e0)=Vanilla
        Main:
        0x3280000e: mov $0x8018d4e0, %edi
        0x32800013: callq  0xa712ff0 <HPHP::collections::allocFromArrayVector(HPHP::ArrayData*)>
        0x32800018: mov %rax, %rbx
    --- bc main(id 1078142128)@24, fp 0, fixupFP 0, spOff 3,  [profTrans=0]
    CastVec
    (22) t6:Vec = ConvObjToVec t5:Obj=HH\Vector -> B5<Catch>
        Main:
        0x3280001b: mov %rbx, %rdi
        0x3280001e: callq  0xa260c00 <HPHP::jit::convObjToVecHelper(HPHP::ObjectData*)>
  -> B6
 B6: [profCount=2] (preds B4)
    (23) StStk<IRSPOff -4> t1:StkPtr, t6:Vec
        Main:
        0x32800023: movb  $0x17, -0x38(%rbp)
        0x32800027: movq  %rax, -0x40(%rbp)
```

The important thing here is that, even though we know that t5 is an HH\Vector
in instruction (22) ConvObjToVec, we still generate machine code for it that
makes a call to a C++ function convObjToVecHelper. We can do better!

---

## Understanding IR tracing

We last looked at the TRACE=printir:1 output in the first lesson. Since then,
we've learned enough about HHVM to tease its output apart in more detail. This
tracing is useful because it simultaneously displays three levels of code
representation:

1. The input Hack bytecode, [documented in bytecode.specification](../bytecode.specification)
2. HHVM intermediate representation, [documented in ir.specification](../ir.specification)
3. The output machine code, [documented in an x86 manual](https://www.felixcloutier.com/x86/)

Here's the Hack bytecode annotation for the first IR op above. First, we see
the function and bytecode offset where this bytecode came from, a sort of
annotation from the bytecode back to source code. Then, we see the bytecode's
name and immediates:

```
    --- bc main(id 1078142128)@5, fp 0, fixupFP 0, spOff 1,  [profTrans=0]
    ColFromArray Vector
```

Here's the IR op. Like the bytecode, the IR op takes immediates - the
parameters inside the angle brackets. Unlike the bytecode, the IR takes inputs
and outputs inline. HHVM's IR operates on
[static, single-assignment (SSA) temporary values](https://en.wikipedia.org/wiki/Static_single_assignment_form).
For this op, the input SSATmp is a constant, so we hide its ID; the output
SSATmp is not constant, so we show that it's "t5", of type "Obj=HH\Vector". At
the IR level, we're no longer implicitly operating on a stack machine - all
stack operations are explicit!

```
    (12) t5:Obj=HH\Vector = NewColFromArray<HH\Vector> vec(0x8018d4e0)=Vanilla
```

Here's the machine code that we generate for this IR op. Machine code can be
placed in multiple regions of memory, depending on whether we think blocks at
the machine code level are likely to be executed. If they are, we put the code
in "Main"; otherwise, we put it in "Cold" or "Frozen". The machine code for
this op is simple because the heavy lifting is done in C++:

```
        Main:
        0x3280000e: mov $0x8018d4e0, %edi
        0x32800013: callq  0xa712ff0 <HPHP::collections::allocFromArrayVector(HPHP::ArrayData*)>
        0x32800018: mov %rax, %rbx
```

After the ColFromArray bytecode comes the CastVec bytecode. We compile this
bytecode to two IR ops. ConvObjToVec is doing the main logic of the Cast op,
and, again, is implemented mostly in C++. StStk ("store stack") pushes the
final result of the cast onto the stack. Unlike the other IR ops here, StStk is
compiled directly to machine code. It's simple enough that we don't need to
shell out to C++ for it.

## Where is the rest of the code?

If we look at the bytecode for these operations, we'll see that the IR trace
above skipped several bytecodes. You can see for yourself by running this
command, which is like our command above, except that:

* We're adding in "grep -v srcloc" to filter out source location attribution.
* We drop `TRACE=printir:1` and add `--hdf Eval.DumpHhas=1` to get a bytecode rather than an IR dump.
* We drop `-r 2` (which expands to `--retranslate-all 2`) because RetranslateAll results in more optimized IR generation - it doesn't affect the bytecode!

```
hphp/tools/hhvm_wrapper.php -c --hdf Eval.DumpHhas=1 ~/php/convert.php | grep -v srcloc
```

Here's what I get if I run that command:

```
.function{} [unique persistent "__EntryPoint"("""v:0:{}""")] (4,7) <"" N  > main() {
  .declvars _0;
  Vec @A_0
  ColFromArray Vector
  AssertRATL _0 Uninit
  AssertRATStk 0 Obj=HH\Vector
  PopL _0
  NullUninit
  NullUninit
  AssertRATL _0 Obj=HH\Vector
  PushL _0
  CastVec
  FCallFuncD <SkipRepack SkipCoeffectsCheck> 1 1 "" "" - "" "var_dump"
  AssertRATStk 0 InitNull
  PopC
  Null
  AssertRATL _0 Uninit
  RetC
}
```

In the bytecode, in between creating the Vector and casting it to a vec, we
store it to local 0 (via PopL), then unset the local and push it back onto the
stack (via PushL). Check bytecode.specification to confirm your understanding!
There are also some type assertion bytecodes (AssertRATL, "assert
repo-authoritative type for a local", and AssertRATStk, for the stack), but it
makes sense that these bytecodes don't compile to machine code. They just add
in type information at compile time.

It makes sense that the bytecode stores the Vector to a local - after all, that's what the source code does! The question is, why don't the IR ops match the bytecode above exactly? If the IR were to implement the exact same operations that the bytecode above claims to do, then we'd have several IR ops for each bytecode in this sequence:

1. **ColFromArray** would do the NewColFromArray, then use StStk to push that value onto the stack.
2. **PopL** would use LdStk ("load stack") to load that value into an SSATmp, then use StLoc ("store local") to store it to a local. In general, PopL also needs to load and dec-ref the old value of the local first, but in this case, the **AssertRATL** above tells us that the local is already Uninit, which is not a refcounted type.
3. **PushL** would use LdLoc ("load local") to load the value, then use StStk push it onto the stack. It would use StLoc to store an Uninit value to the local to "unset" it.
4. **CastVec** would use LdStk to read that stack value, do the cast, and then use StStk to push the result.

> Compiler engineers love abbreviations! "Loads" and "stores" are just reads
> and writes to memory. Because interacting with memory is ubiquitous in Hack
> code, we have tons of IR ops that use "Ld" or "St" in their names. We also
> use shorthand for many other ops, like CreateAFWH, DecRefNZ, JmpZ, and Shr.
> These names seem opaque at first, but they make reading code easier once you
> are versed in the domain.
> [If you're ever confused about what an IR op does, consult ir.specification.](../ir.specification)

These are sensible and correct translations of the bytecode above into IR.
They're also wasteful. The ColFromArray, PopL, PushL, and CastVec are pushing
and popping a single Vector value. If the net effect of all of these ops is to
do a cast on the Vector's SSATmp, it's better if we just do that and skip the
intermediate stack and local stores and loads. HHVM produces this more
optimized code in two steps:

1. It does a literal translation of each bytecode to IR, exactly as we did above.
2. Then, it analyzes the resulting IR ops and sees which ones can be optimized away.

There are actually two places we do the analysis needed to make these
optimizations. First, the irgen ("IR generation") code for each bytecode uses a
[forward-only analysis called FrameState](../../runtime/vm/jit/frame-state.h),
which eliminates operations loads that can be trivially removed (e.g. a LdStk
that comes right after a StStk). FrameState can eliminate redundant loads in
straight-line code, but we need to apply a more complex fixed-point algorithm
to handle branches and loops.
[load-elim](../../runtime/vm/jit/load-elim.cpp) and [store-elim](../../runtime/vm/jit/store-elim.cpp)
do these optimizations in general, after irgen is complete.

## Reading the IR spec

[ir.specification](../ir.specification) is actually a kind of executable
documentation. We use [this script](../generate-ir-opcodes.sh)
to process the lines beginning with `|` in this file to produce C++ code
defining a table of IR ops, the single source of truth for the IR. Because the
documentation is the implementation, it's always up to date! Let's look at a
few examples:

```
| ConvObjToVec, D(Vec), S(Obj), PRc|CRc

| StStk<offset>, ND, S(StkPtr) S(Cell), NF

| CheckType<T>, DRefineS(0), S(Cell), B|P
```

The first line says that ConvObToVec is an IR op taking one SSATmp input that
must be a subtype of TObj, and returning a subtype of TVec. The last bit of
that line defines the op's "flags" - in this case, it consumes a refcount on
the input and produces one on the output. That is: this IR op takes care of the
inc-ref and dec-ref required by the CastVec bytecode.
[For a complete list of flags, look arlier in the file.](../ir.specification?lines=119-159)

The second line says that StStk takes two inputs - a stack pointer and an
arbitrary Hack value (Cell == "mixed") - and doesn't return anything (ND == "no
destination"). That should make sense, as StStk simply writes to memory. StStk
doesn't have any additional flags, but it does have an immediate: the stack
offset, which is a constant offset off of the stack pointer. (Recall that an
immediate is an argument to an instruction that is "immediately" available at
compile time, as opposed to inputs, which are only available at runtime.) The
immediates in ir.specification are just comments; we define the immediates for
each op in code [at the end of the file extra-data.h](../../runtime/vm/jit/extra-data.h).
If you look for StStk there, you'll see that it takes an immediate of type
IRSPRelOffsetData.

The third line says that CheckType takes one input, an arbitrary Hack value,
and returns an output. The return type of this op isn't fixed; instead, it must
be computed at compile time based on the input, and based on the immediate type
T that a given CheckType is checking for. For example, we might compile a
CheckType<Bool> op, which takes a Cell input, checks that it's a bool, and
returns a refined SSATmp of type TBool. If the runtime type check fails, then
instead of returning the result, we'd branch (the "B" flag) to a "taken" block.

## The JIT type system

A key difference between Hack bytecode and HHVM's IR is that each IR op places
type constraints on its inputs, and produces typed outputs. The "CastVec"
bytecode will take any type of input from the stack and "do what vec cast does"
for that input. That could mean leaving the input unchanged (if it's a vec),
doing a cast (if it's a dict, keyset, or object), or throwing an error (all
other types). By contrast, the ConvObjToVec IR op will only implement vec
cast behavior for object inputs. It's up to our irgen code to only generate
IR ops whose type constraints are satisfied.

> In debug builds, after completing irgen for some block of code, we have
> assertions to check these type constraints. The only invariants that hold in
> HHVM are the ones that we actively check in debug mode!

We've already seen some examples of JIT types in the sources and destinations
of each op in ir.specification. These examples are instances of the C++ class
jit::Type, defined in [hphp/runtime/vm/jit/type.h](../../runtime/vm/jit/type.h).
A JIT type constraints what a Hack value could be at runtime. Unlike the Hack
type system, the JIT type system is sound, modulo bugs in HHVM: it provides
actual guarantees about runtime values, and without these guarantees, we
wouldn't be able to produce any nontrivial machine code.

Each instance of a JIT type represents a subset of values that match that type.
For example:

* TCell matches "any Hack value" - that is, an int, an array-like, an object, etc.
* TInt matches "any Hack integer" - some int, but we don't know which.
* TInt=5 matches "the integer 5"

That's right - JIT types can be constants! Like most compilers, HHVM does
[constant folding](https://en.wikipedia.org/wiki/Constant_folding): if an IR
instruction has constant inputs, and if it's side effect free, HHVM may compute
its output at compile time. We check if an IR instruction can be const-folded
by checking if all of that instruction's source SSATmps have types of constant
values.

> Based on the previous lesson, on refcounting, you may have a guess as to why
> we could have constant subtypes of TInt, TStr, TVec, etc., but not of TObj.
> There are even some types, like TInitNull, which always represent a constant!

The set of all JIT types forms [a mathematical structure called a lattice](https://en.wikipedia.org/wiki/Lattice_(order)).
That means that JIT types support two key operations: union and intersection.
The union `|` of two JIT types A and B is the type of values that could be an A
or a B. The intersection `&` of A and B is the type of values that must be both
an A and a B. Here are a few examples:

```
TCell | TInt = TCell   // A TCell could be any Hack value, so this union could be, too.
TCell & TInt = TInt    // A TCell and an int must be an int.
TStr & TInt  = TBottom // No value is an int and a string. TBottom is "the empty set".

TInt | TStr           = (Int|Str)          // jit::Type can represent this union...
(Int|Str) | TInitNull = (Int|Str|InitNull) // ...and this one.

// jit::Type can represent any union of Hack DataTypes (i.e. "top-level" Hack types)!

TInt=3 | TInt=5 = TInt    // We can't represent two constants, so we expand this union.
TInt=3 & TInt=5 = TBottom // No value is ever both the int 3 and the int 5.
```

We use the lattice operations to bound the type of values at compile time:

* **Unions represent merged control flow.** If we compile an if statement, and
  we assign local $x a value of type X in the "then" block and a value of type
  Y in the "else" block, then afterwards, $x could be either one. It has type X | Y.
* **Intersections represent type checks.** If some SSATmp has the type X, and
  we do a CheckType<Y> on it, then if the check passes, both type constraints
  apply. The CheckType result has type X & Y.

There's a lot more to learn about this type system, but it's best done by
reading code. Luckily, jit::Type is one of the most heavily-tested parts of
HHVM. In addition to exercising this code via the end-to-end tests in the
`hphp/test` directory, we have
[a battery of unit tests which double as examples of usage and expected behavior](../../runtime/test/type.cpp).

---

## Step 1: Optimizing CastVec IR generation

Phew! That was a lot of information. Take five and step away from the computer.
Go for a walk! Get a cup of coffee!

...but before you do, kick off a build of HHVM =)

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

It's time to apply what we've learned to optimize the example from the start of
this lesson - a simple vec cast on a Vector. At a high level, here's what we
have to do:

1. We want to improve irgen for CastVec. By convention, we know that the
   interpreter's logic for this bytecode is in iopCastVec and that the JIT's is
   in emitCastVec. **We must edit emitCastVec.**
2. Our optimization can only kick in if we have the right type info for the
   input. **We must check if the stack's top value is a subtype of
   TObj<=HH\Vector**, the specialized jit::Type representing objects of class
   Vector.
3. We need to use better - more specialized! - IR ops that will be more
   performant for this cast case. **We must find and compose existing IR ops,
   e.g. one that fetches the vec that backs a Vector.**

Let's go through these items together. For item 1, we can simply search in the
`hphp/runtime` directory for "emitCastVec". [Here's the code that I found.](../../runtime/vm/jit/irgen-basic.cpp)
At the rev you're working at, the code may be a bit different, but the
high-level structure is probably similar:

```
void emitCastVec(IRGS& env) {
  auto const src = popC(env);
  auto const raise = [&](const char* type) {
    auto const message =
      makeStaticString(folly::sformat("{} to vec conversion", type));
    gen(env, ThrowInvalidOperation, cns(env, message));
    return cns(env, TBottom);
  };
  push(
    env,
    [&] {
      if (src->isA(TVec))     return src;
      if (src->isA(TArrLike)) return gen(env, ConvArrLikeToVec, src);
      if (src->isA(TClsMeth)) return raise("ClsMeth");
      if (src->isA(TObj))     return gen(env, ConvObjToVec, src);
      if (src->isA(TNull))    return raise("Null");
      if (src->isA(TBool))    return raise("Bool");
      if (src->isA(TInt))     return raise("Int");
      if (src->isA(TDbl))     return raise("Double");
      if (src->isA(TStr))     return raise("String");
      if (src->isA(TFunc))    return raise("Func");
      if (src->isA(TRes))     return raise("Resource");
      PUNT(CastVecUnknown);
    }()
  );
}
```

What's going on here? The C++ lambda inside the "push" call returns an SSATmp
that's the result of doing a cast. TVec, TArrLike, TObj, etc. are all JIT
types; most of them represent values of some DataType, but TArrLike is a
special union type that just means - you guessed it! - TVec | TDict | TKeyset.
In the TVec, TArrLike, and TObj cases, we produce IR to actually do the cast.
In all of the other cases, here, the cast is guaranteed to throw, so we emit a
"terminal" (the flag "T" in ir.specification) op `ThrowInvalidOperation`. This
op is guaranteed to halt execution, so we can return a dummy SSATmp of type
TBottom, since we know that value is unreachable.

> When compiling a bytecode to IR, it's critical to keep track of refcounting.
> This bytecode pops one value - the "src" SSATmp - from the stack, and pushes
> one value - the return value of the lambda - onto it. Based on the standard
> refcounting semantics, we must dec-ref the stack input and inc-ref the
> output.But we don't see any DecRef or IncRef IR ops here! That's because the
> ConvArrLikeToVec and ConvObjToVec IR ops handle the refcounting for us. We
> can confirm that by reading their ir.specification entries. The CRc|PRc flags
> on these ops mean that they consume a refcount on their input and produce one
> on their output. If we replace these ops with ones that don't handle
> refcounting, we'll have to generate refcounting IR ops ourselves.

For item 2, we must check the conditions under which our optimization applies.
Since we're trying to optimize vec casts on Vector objects, we only have to
consider the TObj case, but we can't apply our optimization to all objects. We
must use the JIT type system to check for a more specific type here.

Let's make this change by editing the TObj line alone. We can insert another
C++ lambda to give us some syntactic space to make this logic more complex.
Then, we can use the jit::Type's specialized constructor, jit::Type::SubObj, to
produce a type to compare against here:

```
      if (src->isA(TObj))     return [&]{
        auto const TVector = Type::SubObj(c_Vector::classof());
        if (src->isA(TVector)) {
          // TODO: We can optimize this case!
        }
        return gen(env, ConvObjToVec, src);
      }();
```

> We create and immediately invoke a closure here as a way to pack complex
> control flow in an expression position. It's the same reason that we create
> and invoke the outer closure, the one that produces the output that we push
> onto the stack. You could consider this pattern a "cute" "trick", or you
> could refactor this code to use named helper methods instead. The choice is
> yours!

Make these edits and check that HHVM still compiles. (You may need to include
"hphp/runtime/ext/collections/ext_collections-vector.h" at the top of the file,
too.) On to item 3! We need to find an IR op to extract the vec that backs a
vector. There are a few ways to look for this op:

* We could do a (case-insensitive) search for "collection" or "vector" in the file.
* We could look for ops that take an object as input, by searching for S(Obj).
* We could look for ops that return a vec as output, by searching for D(Vec).

The first approach quickly leads us to:

```
| LdColVec, D(Vec), S(Obj), NF

  Load the vec array backing a collection instance in S0. S0 must be a Vector
  or ImmVector, and that specific object type must be known at compile time.
```

That's exactly what we need! However, we now have to generate the appropriate
refcounting IR ops. Unlike ConvObjToVec, LdColVec has no special flags - it
simply does a load. In order to generate any IR ops here, we need to use the
magical templated "gen" helper. This helper takes a variadic inputs and builds
and IR instruction from those inputs. In order, its inputs are:

1. The IRGS& environment struct "env".
2. The IR op for the new instruction, specified by its name. (It's a C++ enum.)
3. (Optional) The "taken" branch, a jit::Block to jump to. Only used for control-flow-y ops (with flags "B" or "T").
4. (Optional) The jit::Type for the op. Only used if the op takes a type immediate, like CheckType does.
5. (Optional) Any other immediates associated with that op. (See extra-data.h to see if each op takes one.)
6. The list of source SSATmps for that op, in order.

To get this optimization working, we must gen an LdColVec, an IncRef of the
result, and a DecRef of the input. LdColVec and IncRef don't need any of the
three optional arguments above. DecRef has an associated immediate, but no
other optional arguments. As a result, this code should work for us:

```
      if (src->isA(TObj))     return [&]{
        auto const TVector = Type::SubObj(c_Vector::classof());
        if (src->isA(TVector)) {
          auto const result = gen(env, LdColVec, src);
          gen(env, IncRef, result);
          gen(env, DecRef, DecRefData(), src);
          return result;
        }
        return gen(env, ConvObjToVec, src);
      }();
```

> It's important that we do the IncRef here before the DecRef. Make sure you understand why!

When you've got this code compiling, rerun the TRACE=printir:1 command with the
test file and your new HHVM. You should see that the C++ call in ConvObjToVec
has been replaced with an offset load. Our code generation for this case is
much improved!

---

## Step 2: Moving the optimization to simplify

When writing a compiler optimization, you should always try to make it as
general as possible. If your optimization only applies in certain special
cases, then users of your language may see large performance differences due to
small edits to their code, which is a frustrating experience! Further,
performance measurement can be noisy, and the more cases your optimization
covers, the greater the chance of measuring an unambiguous improvement on real
benchmarks.

> Compiler engineers are greedy. They want to optimize as many cases as
> possible. Compiler engineers are also lazy. They may give up at the point at
> which it becomes hard to extend an optimization further. As a result of these
> warring impulses, compilers exhibit stepwise behavior. Ubiquitous, simple
> cases are heavily optimized. A variety of common cases are handled somewhat
> well. The general case may be slow as molasses.

There are a variety of ways to extend this optimization. We'll look at simpler
extensions in the exercises, but the most important extension here is to take
advantage of late type information.

[HHVM includes a variety of optimization passes.](../../runtime/vm/jit/opt.cpp)
These passes run after irgen is complete. They take an IR unit that implements
some Hack source code, and modify it in place to produce better IR that
implements the same code. These passes can produce tighter type bounds on
values than we originally had at irgen time; for example, if we inline a
function, we may get a better type for its return value, which recursively
results in tighter types for SSATmps computed based on that value.

If we can do an IR optimization without introducing new control flow, we can
take advantage of these late types by [moving the optimization to simplify.cpp](../../runtime/vm/jit/simplify.cpp).
The simplifier is run on every IR op after every optimization pass. If there's
any opportunity to make the optimization, the simplifier will find it. The
simplifier's contract is, uh, simple...it processes a single IR instruction,
and if it's possible to replace it with zero or more IR instructions, it does
so.

> When can we simplify an operation down to zero IR instructions? When we can
> const-fold it! Take a look at simplifyXorInt for an example. One of the cases
> we optimize here is xor-ing an SSATmp with itself. If we do that, we can
> return cns(env, 0). Since this result is a constant SSATmp, we don't need to
> execute any IR instructions to compute it. The simplifier will remove the
> XorInt op and replace its result with the constant.

To move the optimization to the simplifier, we need to:

1. **Revert the change in the irgen code.** The simplify version is strictly
   more general. If we leave the irgen change around, then when we examine the
   printir, it'll be hard to tell which of our changes kicked in.
2. **Add a simplifyConvObjToVec function to simplify.cpp.** We won't be able to
   const-fold here, but based on the input's type, we can still replace this IR
   instruction with a more optimized sequence of instructions.
3. **Register the IR op in the "X" macro below.** Search for "XorInt" in the
   file. It should appear in a few places, but one line in particular will just
   say "X(XorInt)". This line is a macro invocation that dispatches to the new
   simplify function.

Here's what I've got for the simplify code (as a diff: D34433352):

```
SSATmp* simplifyConvObjToVec(State& env, const IRInstruction* inst) {
  auto const src = inst->src(0);
  auto const TVector = Type::SubObj(c_Vector::classof());
  if (src->isA(TVector)) {
    auto const result = gen(env, LdColVec, src);
    gen(env, IncRef, result);
    gen(env, DecRef, DecRefData(), src);
    return result;
  }
  return nullptr;
}
```

It's basically the same as the irgen version, but by doing it in the right
place, we optimize more cases! Make this change, get it to compile, and verify
that you can see the optimization on the test case. Then kick off diff testing
and benchmarking!

---

## Lesson summary

* HHVM's IR is typed under the sound JIT type system. This type system
  constrains which IR operations we can use on a given value, but we can also
  use these types as optimization conditions.
* We use profiling tools to find "hot" (high-cost) functions that may be good
  targets for optimization.
* Once we've found a potential optimization target, we create a small test case
  and verify that the opportunity still exists at the highest optimization
  level.
* When we make an optimization, we should apply it to as many cases as
  possible. One way to make an optimization more general is to move it to the
  simplifier, so it runs whenever we gain type information.

---

## Exercises

Your first exercise is to think about how else we could extend this optimization. Do so before reading on!

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

For all of these extensions, be sure to create a small test case that shows the current, suboptimal code generation first. Then make the optimization and confirm that the test case is improved!

1. Modify the optimization to apply to vec-cast on ImmVector as well as vec-cast on Vector.
2. Add optimizations for doing a dict cast on a Map, ImmMap, Set, and ImmSet.
3. Add optimizations for doing a vec cast on a Map, ImmMap, Set, or ImmSet, and for doing a dict cast on a Vector or ImmVector. (Look for other relevant IR ops, like the ones that convert between different array-likes. Watch out for refcounting!)

---

### Hphp Hack Doc HIPs Contexts And Coeffects (`hphp/hack/doc/HIPs/contexts_and_coeffects.md`)

### Feature Name: Contexts and CoEffects

### Start Date: July 14, 2020

### Status: Candidate

# Summary:

A generalized system for the description and enforcement of permissions and restrictions of a context.

This HIP presents feature that overlays a coeffect system into the Hack type system through lightweight context annotations that logically map into a set of capabilities. These capabilities establish both the calling conventions (i.e., which functions/method may call which other functions/methods) as well as the operations permitted within the present context.

“Contexts are like onions.”

# Feature motivation:

Several important in-progress language features require us to alter behavior dependent on the executing context. Examples of this include:

* **Context Implicit Purpose Policies (CIPP)**: Inside of a CIPP context, there are stricter rules about how data can be handled in order to ensure their purposes are maintained.
* **Purity/Reactivity:** In a pure/reactive context, the programmer cannot access global data nor mutate external references in order to achieve enforced determinism.

In order to support execution contexts with different permissions/restrictions, we propose adding **Contextual Effects (Coeffects)** to the Hack type system. Coeffects are expressive enough to support generalized execution scopes while also lending themselves nicely to syntactic sugar that will make them easy to use and require minimal changes to typechecker internals.

# Definitions

In the following document we will use a few terms either not previously defined within the context of Hack or else potentially nonspecific. For the sake of clarity, we will first have a list of definitions for reference throughout the rest of the document.

Capability: a permission or description of a permission. For example, one might consider the ability to do `io` or access globals as capabilities.

Context: a higher level representation of a set of capabilities. A function may be comprised of one or more contexts which represent the set union of the underlying capabilities.

Example context: for the purpose for the rest of the definitions, a context created solely for the purpose of this document rather than for intended release.

The Pure Context: A simple way of saying the context representing the empty list of capabilities `{}`.

`rand`: An example context representing the capabilities required to do random number generation. Refers to the set of capabilities `{Rand}`.

`io`: An example context representing the capabilities required to do IO. Refers to the set of capabilities `{IO}`.

`throws<T>`: A parameterized example context representing the capabilities required to throw exception type `T` or any children of `T`. Refers to the set of capabilities `{Throws<T>}`. One may consider that the type parameter of this context is covariant.

`defaults`: The context representing the set of capabilities present in a function prior to the introduction of this feature. Refers to the set of capabilities `{Throws<mixed>, IO, Rand}`.

Higher order functions: A function accepting, as one of its arguments, a value containing a function-type such as a function pointer or closure.

# User Experience - Syntax

Note that all of the below syntax is still very much up for bikeshedding and may not be finalized.

The specification for the syntax of a function including co-effects is as follows, with the list of co-effects itself being optional.

```
function function_name<list_of_generics>(
  parameter_type_hint variable_name,
  ...
)[list_of_contexts] : return_type_hint where_clauses {
  function_body
}
```

## Declaring Contexts and Capabilities

This is done natively within the typechecker and the runtime. More information on this is provided under those sections.

## Basic Declarations and Closures

A function or method may optionally choose to list one or more contexts:

```
function no_contexts(): void {...}
function one_context()[C]: void {...}
function many_context()[C1, C2, ..., Cn]: void {...}
```

Additionally, the context list may appear in a function type:

```
function has_fn_args(
  (function (): void) $no_list,
  (function ()[io, rand]: void) $list,
  (function ()[]: void) $empty_list,
): void {...}
```

As with standard functions, closures may optionally choose to list one or more contexts. Note that the outer function may or may not have its own context list. Lambdas wishing to specify a list of contexts must include a (possibly empty) parenthesized argument list.

```
function some_function(): void {
  $no_list = () ==> {...};
  $single = ()[C] ==> {...};
  $multiple = ()[C1, C2, ..., Cn] ==> {...};
  $with_types = ()[C]: void ==> {...};
  // legacy functions work too
  $legacy = function()[C]: void {};

  // does not parse
  $x[C] ==> {}
}
```

## Higher Order Functions With Dependent Contexts

One may define a higher order function whose context depends on the dynamic context of one or more passed in function arguments.

```
function has_dependent_fn_arg(
  (function()[_]: void) $f,
)[rand, ctx $f]: void {... $f(); ...}

function has_dependent_fn_args(
  (function()[_]: void) $f,
  (function()[_]: void) $f2,
)[rand, ctx $f, ctx $f2]: void {... $f(); ... $f2(); ...}
```

One may reference the dependent context of a function argument in later arguments as well as in the return type.

```
function has_double_dependent_fn_arg(
  (function()[_]: void) $f1,
  (function()[ctx $f1]: void) $f2,
)[rand, ctx $f1]: void {$f1(); $f2(); }

function has_dependent_return(
  (function()[_]: void) $f,
)[rand, ctx $f]: (function()[ctx $f]: void) {
  $f();
  return $f;
}
```

Attempting to use the dependent context of an argument before it is defined will result in an error about using an undefined variable.

Note that the special `_` placeholder context may only be used on function arguments.

```
// The following are all disallowed
type A = (function()[_]: void);
newtype A = (function()[_]: void);

Class Example {
  public (function()[_]: void) $f;
  public static (function()[_]: void) $f2;
}
function example(): (function()[_]: void) {}
```

## Constants

In addition to standard and type constants, classes may define context constants:

```
class WithConstant {
  const ctx C = [io];
  ...
}
```

Context constants may be abstract, and possibly have a default. If they are abstract, they may additionally contain one or both of the as and super constraints.

```
interface WithAbstractConstants<T> {
  abstract const ctx C1; // bare abstract
  abstract const ctx C2 = [io]; // abstract with default
  abstract const ctx C3 as [io, rand]; // abstract with bound
  abstract const ctx C4 super [io, rand] = [io]; // abstract with bound and default

  // Disallowed: Concrete with bound.
  const ctx C5 super [io, rand] = [io];
}
```

Context constants are accessed off of function arguments in a similar manner to function-type arguments. The same restrictions about use-before-define apply.

```
function type_const(SomeClassWithConstant $t)[$t::C]: void { $t->f(); }
```

Context constants are accessed off `this` or a specific type directly within the contexts list:

```
public function access_directly()[this::C1, MySpecificType::C2]: T {...}
```

Context constants may not be referenced off of a dependent/nested type. Said another way, context constants may only have the form `$arg::C`, not `$arg::T::C`, etc. It is possible we will relax this restriction in a future version.

```
interface IHasCtx {
  abstract ctx C;
}

interface IHasConst {
  abstract const type TC as IHasCtx;
}

// Disallowed: nested type access
function type_const(IHasConst $t)[$t::TC::C]: void {}

abstract class MyClass implements IHasConst {
  // Disallowed: also applies to dependent types of this
  public  function type_const()[this::TC::C]: void{}
}
```

For the sake of simplicity, `this` must be used in lieu of `$this` within the context list.

## Additional Dependent Contexts Information

Dependent contexts may be accessed off of nullable parameters. If the dynamic value of the parameter is `null`, then the contexts list will be empty.

```
function type_const(
  ?SomeClassWithConstant $t,
  ?(function()[_]: void) $f,
)[$t::C, ctx $f]: void {
  $t?->foo();
  if ($f is nonnull) {
    $f();
  }
}
```

Parameters used for accessing a dependent context may not be reassigned within the function body.

```
function type_const(SomeClassWithConstant $t)[$t::C]: void {
  // disallowed
  $t = get_some_other_value();
}

function has_dependent_fn_arg((function()[_]: void) $f)[ctx $f]: void {
  // disallowed
  $f = get_some_other_value();
}
```

Dependent contexts may not be referenced within the body of a function. This restriction may be relaxed in a future version.

```
function f(
  (function()[_]: void $f,
  SomeClassWithConstant $t,
)[rand, ctx $f, $t::C]: void {
  (()[ctx $f] ==> 1)();    // Disallowed
  (()[$t::C] ==> 1)();    // Disallowed
  (()[rand] ==> 1)(); // Allowed, not a dependent context
  (()[] ==> 1)();     // Allowed
  (() ==> 1)();       // Allowed. Note that this is logically equivalent to [rand, ctx $f, $t::C]
}
```

# User Experience - Semantics

## Basic Function Declarations

As this feature is fully opt-in, the lack of a context list results in implicity having the `defaults` context.

I.E. the following to definitions are functionally identical:

```
function default_context1(): void {...}
function default_context2()[defaults]: void {...}
```

## How Contextful Functions Interact

Contexts represent a set of capabilities. A list of contexts represent the set union of their capabilities. In order to invoke a function, one must have access to all capabilities required by the callee. However, the caller may have more capabilities than is required by the callee, in which case simply not all capabilities are "passed" to the callee.

This is perhaps more easily seen via example:

```
function pure_fun()[]: void { /* has {} capability set */}
function rand_int()[rand]: void {/* has {Rand} capability set */}

function rand_fun()[rand]: void {
  pure_fun(); // fine: {} ⊆ {Rand}
  rand_int(); // fine: {Rand} ⊆ {Rand}
}

function unannotated_fun(): void {
  rand_fun(); // fine: {Rand} ⊆ {IO, Rand, Throws<mixed>} aka the default set
}

function pure_fun2()[]: void {
  rand_fun(); // error: {Rand} ⊈ {}
}
```

Above, `rand_fun` is logically safe, as its ability to do nondeterministic computation shouldn’t prevent it from invoking functions without that ability. However, note that `pure_fun2` is unsafe, as it does not have that capability, and therefore must refrain from invoking those that require it.

Now consider the following:

```
function pp_coinflip()[io, rand]: void {
  pretty(conflip());
}

function coinflip()[rand]: bool {
  return rand_int() == 0;
}

function pretty(bool $b)[io]: void {
  print($b ? "heads" : "tails");
}

function pure_fun()[]: void {
  pp_coinflip(); // whoops
}

```

The invocation of `pp_coinflip` from `pure_fun` is obviously unsafe, as invoking `pure_fun` could, in fact, actually result in impure actions. Therefore, functions with the pure context only invoke other functions with the pure context. Note, however, that `pp_coinflip` is fine to invoke `coinflip` and `pretty`.

## Subtyping & Hierarchies

Semantically, capabilities work as if they were required parameters
to functions, and are thus contravariant.  This means that, for example,
a closure that requires a `[rand]` or `[]` (pure) context may be passed
where the expected type is a function that requires `[rand, io]`.
(The converse is disallowed because that would mean giving an
additional capability for randomness out of thin air.)

The errors then fall out by normal subtyping rules by internally
treating permissions as (implicit) arguments of a function/method.

```
class Parent {
  public function maybeRand()[rand]: void {...} // {Rand}
  public function maybePure(): void {...} // {Throws<mixed>, IO, Rand}
}

class Mid extends Parent {
  public function maybeRand()[rand]: void {...} // {Rand} -> fine {Rand} ⊆ {Rand}
  public function maybePure()[io]: void {...} // {IO} -> fine {IO} ⊆ {Throws<mixed>, IO, Rand}
}

class Child extends Mid {
  public function maybeRand()[]: void {...} // {} -> fine {} ⊆ {Rand}
  public function maybePure()[]: void {...} // {} -> fine {} ⊆ {IO}
}
```

In the above, the contexts on the methods in `Parent` and `Child` are required for `Mid` to typecheck successfully. Note also that `maybePure` in `Parent` need not be the pure context, and that `maybeRand` in `Child` need not be `rand`.

### Capability subtyping

In reality, there may also exist a subtyping relationship between
capabilities; suppose that a new capability `FileInput` is defined.
Since reading from a file does *not* preclude one from reading
a special file such as `/dev/random` on a UNIX-like system,
the semantic model should conservatively assume that a function
with capability `FileInput` must also have the `Rand` capability.
Therefore, `FileInput` must be a subtype (subcapability) of `Rand`.

This has an important consequence that falls out by design:
whenever some capability `B` that is subtype of capability `A`
is available (in scope), any function (or operation) that
requires `A` can be called (or performed, respectively).

## Interaction with closures

By default, closures require the same capabilities as the context in which they are created. Explicitly annotating the closure can be used to opt-out of this implicit behaviour. This is most useful when requiring the capabilities of the outer scope result in unnecessary restrictions, such as if the closure is returned rather than being invoked within the enclosing scope.

```
function foo()[io]: void { // scope has {IO}
  $callable1 = () ==> {...}; // requires {IO} - By far the most common usage
  $callable2 = ()[] ==> {...}; // does not require {IO}, requires {}
  $uncallable1 = ()[rand] ==> {...}; // does not require {IO}, requires {Rand}
  $uncallable2 = ()[defaults] ==> {...}; // does not require {IO}, requires the default set
}
```

Note that in the previous example, `$uncallable1` cannot be called as `foo` cannot provide the required `Rand` capability. `$callable2` is invocable because it requires strictly fewer capabilities than `foo` can provide.

## Higher-order Functions With Dependent Contexts

Higher-order functions are typically used for generalization purposes, with common examples including standard `map` and `filter` functions. For these functions, a common pattern is to generalize over the inputs and/or outputs of their function-typed arguments. It is imperative that the addition of contexts does not remove this generalizability while maintaining simplicity of their definitions.

Consider the following higher-order function declaration and calling functions. In order to maintain generality, safety, and backwards compatibility, the end result needs to be that `good_caller` and `nocontext_caller` should typecheck while `bad_caller` should not. We solve this problem via the use of dependent contexts, defined above.

```
function callee(
  (function()[_]: void) $f,
)[rand, ctx $f]: void {... $f(); ...}

function good_caller()[io, rand]: void {
  // pass pure closure
  callee(()[] ==> {...}); // callee is {Rand}

  // pass {IO} closure
  callee(()[io] ==> echo "output"); // callee is {Rand, IO}
  // pass {IO, Rand} closure
  callee(() ==> echo "output"); // callee is {Rand, IO}
  callee(() ==> {...}); // callee is {Rand, IO}
}

function bad_caller()[]: void {
  // pass {} closure but tries to do IO
  callee(()[] ==> echo "output"); // // C is {} -> callee is {Rand}
  // pass {} closure
  callee(() ==> {...}); // C is {} -> callee is {Rand}
}

function nocontext_caller(): void {
  // this closure requires the default context
  callee(() ==> {...}); // callee is {Rand, Throws<mixed>, IO}
}
```

Note that, logically, this suggests and requires that all other statements within `callee` require only the `Rand` capability, as the actual `C` passed cannot be depended upon to be any specific capability (and can in fact be the empty set of capabilities).

A potentially more compelling example is the `Vec\map` function in the Hack Standard Library.

```
function map<Tv1, Tv2>(
  Traversable<Tv1> $traversable,
  (function(Tv1)[_]: Tv2) $value_func,
)[ctx $value_func]: vec<Tv2> { ... }
```

## Partially Contextful Hierarchies aka Context Constants

### Background

It is not uncommon to want your API to accept an object implementing an interface and then invoke a method appearing on that interface. That is fine in the common case, wherein the context of that function within the hierarchy is invariant. However, it is possible for a hierarchy to exist for which it is difficult or impossible to guarantee that all implementations of a method have the same context. There are a good number of these situations within the Facebook codebase, but the easiest example is the `Traversable` hierarchy.

The following is an oversimplification of the hierarchy and methodology for how `Traversable`s work in Hack. Do not consider this an actual reference to their inner workings. Consider a `Traversable` interface that defines a `next` function which gives you the next item and an `isDone` function that tells you if there are more elements.

```
interface Traversable<T> {
  public function next()[???]: T; // what do we put here?
  public function isDone()[]: bool; // always pure
}
```

The most common children of `Traversable` are the builtin `Containers`, with children like `vec` and `Map`. However, non-builtin objects are allowed to extend `Traversable` as well, creating arbitrarily traversable objects.

```
interfact Container<T> implements Traversable<T> {
  public function next()[]: T; // {}
}

final class CreateTenNumbers implements Traversable<int> {
  private int $nums = 0;
  private bool $done = false;
  public function isDone()[]: bool { return $this->done; } // {}
  public function next()[rand]: int { // {Rand}
    invariant(!$this->done, 'off the end');
    if ($this->nums++ === 10) { $this->done = true; }
    return rand_int();
  }
}
```

Now consider the following function:

```
function sum(Traversable<int> $nums)[]: int { // Has {}!!!
  $sum = 0;
  while(!$nums->isDone()) {
    // if $nums is CreateTenNumbers, this is unsafe!
    $sum += $nums->next(); // hmmmmm
  }
  return $sum;
}
```

This code should not typecheck! The `sum` function has no capabilities, but what are the capability requirements of the call to `next`?

### Solution

The solution to this problem is the capability constants described above; the idea simply being that the interface has an abstract capability list, usable on methods of the interface, and concretized by children. In our case, we would use such a capability constant to describe the `next` function:

```
interface Traversable<T> {
  abstract const ctx C;
  public function next()[this::C]: T;
  public function isDone()[]: T;
}

interface Container<T> implements Traversable<T> {
  const ctx C = [];
}

final class CreateTenNumbers implements Traversable<int> {
  ...
  const ctx C = [rand];
  public function next()[rand]: int { ... }
  ...
}

function sum(Traversable<int> $nums)[$nums::C]: int { ... }
```

In fact, the `Vec\map` function above would likely actually look something like this:

```
function map<Tv1, Tv2>(
  Traversable<Tv1> $traversable,
  (function(Tv1)[_]: Tv2) $value_func,
)[$traversable::C, ctx value_func]: vec<Tv2> { ... }
```

As with normal type constants, one cannot override a concrete capability constant.

### Context Constant Defaults

As with normal type constants, we don’t want to force all children to specify the constant. Thus we expose abstract context constants with defaults.

```
abstract const ctx C = [defaults];
```

Here, the first non-abstract class that doesn’t define a concrete context for `C` gets `[defaults]` synthesized for it.

## Local operations

Examples of coeffect-enforced operations include printing,
static property or superglobal access, as well as throwing exceptions.
For output such as from invocation of the `echo` built-in, the `IO`
capability must be locally available in the function/method body.

```
function io_good()[io]: void {
  echo "good";  // ok: {IO} ⊆ {IO}
}
function io_bad()[]: void {
  echo "bad";  // error: {} ⊈ {IO}
}
```

## Parameterized Contexts / Capabilities

Thus far, we’ve covered examples of relatively simple contexts, effectively representing a binary option. However, contexts must also be usable for situations requiring more than that binary choice. Consider, now in more detail, the `throw` context defined above. Rather than describing that a function can throw, this would describe `which` classes of exceptions a function can throw. In that scenario, the context would require a parameter representing the exception class: `throws<-T as Exception>`.

One might note, however, that it would be reasonable for a function to throw multiple kinds of exceptions, often without a unified hierarchy. While it is possible to use an additional interface to unify those exceptions, that would quickly result in a combinatorial explosion. Instead, the result would look like this:

```
function throws_foo_exception()[throws<FooException>]: void { // {Throws<FooException>}
  throw new FooException();
}

function throws_bar_exception()[throws<BarException>]: void { // {Throws<BarException>}
  throw new BarException();
}

function throws_foo_or_bar_exception(bool $cond)[
  throws<FooException>, throws<BarException> // {Throws<FooException>, Throws<BarException>}
]: void {
  if ($cond) {
    throws_foo_exception();
  } else {
    throws_bar_exception();
  }
}
```

The above would indicate that `throws_foo_or_bar_exception` may throw any of the listed exception classes.

This also applies to to the introduction of additional instances of a parameterized context due to dependent contexts:

```
function throws(
  (function()[_]: void) $f,
)[throws<FooException>, ctx $f]: void {...}
```

In the above, if `ctx $f` is `throws<BarException>` then `throws($f)` would be `{Throws<FooException>, Throws<BarException>}`.

# IDE experience:

The IDE experience for this feature will be similar to that of standard types. Autocomplete, syntax highlighting, etc will all be available.

The error messages will be customizable for each context such that the errors make it clear

1. What’s missing in order to typecheck function calls successfully
2. What illegal operation is taking place within the body of a contextful function.

For example, attempting to `echo` within a function having the pure context would simply indicate that doing IO is not permissable due to that context.

# Implementation details:

## Typechecker

The proposed system is modeled as implicit parameters in the
typechecker. These parameters do not exist at runtime, and the
locals that carry them are not denotable by users (`$#` prefix).
The parameters are automatically looked up and checked during type
inference of call expressions via the `$#capability` local.
Coeffect-enforced local operations (such as I/O and throwing)
look up and check if the `$#local_capability` has the
appropriate type.  Multiple capabilities are encoded using
an intersection type, i.e., a capability set `{C1, C2, C3}`
would be represented as `(C1 & C2 & C3)`, assuming an
oversimplification (for now) that there exists a one-to-one mapping
between contexts and same-named types.  In either case,
the *available* capability needs to be a subtype of the *required*
one in order for type-checking to succeed (note that a set is a
subtype of any of its supersets).  This way, we reuse existing
typing infrastructure, and get subtyping for free.

```
namespace HH\Capabilities {
  interface A {}
  interface B extends A {}
  interface A2 {}
}

function f(/* A $#capability */)[A]: void {
  f(/* $#capability */); // ok
  g(/* $#capability */); // bad, A </: B
}   //                  available^     ^required

function g(/* B $#capability */)[B]: void {
  g(/* $#capability */); // ok
  f(/* $#capability */); // ok (B <: A)
}   //                 available^    ^required

function h(/* (A & A2) $#capability */)[A, A2]: void {
  g(/* $#capability */); // bad (A & A2) </: B)
  f(/* $#capability */); // ok  (A & A2) <:  A)
}   //                  available^^^^^^      ^required
```

So, a function with a `mixed` coeffect has *none* of the capabilities we define, and can be called by any function. By comparison, a function with a nothing coeffect has every capability, but can only be called by other functions whose coeffect is `nothing`. In practice, we won’t use `nothing`, but rather an intersection type that covers a *default* set of capabilities, namely `defaults`.

### Mapping of contexts to capabilities

The intended place to define new contexts and capabilities is an
`.hhi` file under `hphp/hack/src/hhi/coeffect`.  In the same directory,
there is also a GraphViz visualization that concisely describes the
relationship between contexts and capabilities system in a way
that may be more understandable to an average Hack developer.

The kind of coeffects coincides with the kind of types.  The syntactic
piece `[C1, ..., Cn]` is interpreted during naming as follows. Each
annotation `Ck` must either be:

* a *fully* namespace-qualified type using the preferred CamelCase naming,
  e.g., `\HH\Capabilities\Rand`;

* or an *unqualified* type representing a *context* using a snake case name,
  e.g., `rand`, which is an alias to one or more of the above
  (multiple types are intersected on the right-hand side)

The former would be sufficient and constitutes a sound (co)effect system;
however, we encourage uniformly using the latter instead because
it provides a general mechanism that facilitates top-down migrations
(more details on that below).

In case a context requires and provides multiple capabilities, there
are two choices:

* declare a (sealed interface) supertype of the desired capabilities; or
* declare an alias and use intersection types (preferred).

Declaring an alias to an intersection of capabilities (or other
aliases) is strongly preferred as it allow the requirements to be
constructible in multiple ways; e.g., if a context `composite_context`
maps to capabilities (and contexts) `Cap1` (`context1`) and `Cap2
`(`context2`), then the following code would still work fine as expected,

```
function callee()[composite_context]: void {}
function caller()[context1, context2]: void {
  callee(); // ok (composite_context = context1 & context2)
}
```

unlike the former hierarchy-based approach
(`CompositeCap extends CompositeCap1, CompositeCap2`)
that would fail with the following message:

```
This call is not allowed because its coeffects are incompatible with the context (Typing[4390])
  ... context of this function body provides the capability set
    {Capability1, Capability2}
  ... But the function being called requires the capability
    CompositeCapability
```

### Enforcing local operations

To enforce local operations (such as throwing exceptions),
the typechecker keeps track of a fake local variable
`$#local_capability`. At the beginning of `f`'s body, it has type:

`\HH\Contexts\context1 & ... & \HH\Contexts\contextN`

where `context1` through `contextN` are the listed on `f`:

```
function f(/*params*/)[context1, ..., contextN]: ReturnType
```

Since each context `contextI` maps to some underlying set of
capabilities, the type of `$#local_capability` simplifies to:

`Cap1 & ... & CapK`

This kind of coeffect(s) is tracked orthogonally to the one used to
enforce calling conventions and they are always sound; no context
can *unsafely* conjure a capability to perform a local operation.

### Enforcing calling conventions

To establish calling conventions in the typechecker,
user-denoted contexts are mapped to two sets of capabilities:

- *C*: intersection of types define by `\HH\Contexts\contextI`
  (exactly as described in the previous subsection)
- *Cunsafe*: intersection of types defined by
  `\HH\Contexts\Unsafe\contextI`
  (analogous to the above modulo the namespace resolution)

Then the `$#capability` local is typed as `(C & Cunsafe)`;
intuitively, this means that the set of available capabilities
for performing a function/method call is the set union of
the safe and unsafe counterparts of each context, where `mixed`
corresponds to an empty set (i.e., it is no-op).  Notably,
this means that usage of a context is *always* sound if the
underlying type `\HH\Contexts\Unsafe\context = mixed`.
On the contrary, `\HH\Contexts\Unsafe\context = (C1 & C2)`,
for example, would mean that the usage of the context is unsound
as it *unsafely* conjures capabilities `C1` and `C2`,
thereby potentially allowing calls into contexts that require
capabilities `C1` and `C2` that would otherwise not be available.

The errors then fall out by normal subtyping rules by internally
treating `$#capability` as an implicit function/method argument
that has type: `C & Cunsafe`.

### A comprehensive example

```
// defined in an .hhi
namespace \HH\Contexts {
  type io = IO;
  namespace Unsafe { type safe_context = mixed; } // == {}

  type unsafe_context = (Rand & Throws<Exception>);
  namespace Unsafe { type unsafe_context = IO; }
}

// user code (WWW)
function do_io()[io]: void {
  // $#local_capability has type: IO
  // $#capability has type: (mixed & IO) = IO
  echo(/* $#local_capability */) "good";  // ok (IO <: IO)
}
function cannot_make_local_io_but_can_call_into_io_ctx()[
  unsafe_context
]: void {
  // $#local_capability has type: Rand & Throws<Exception>
  // $#capability has type: IO & (Rand & Throws<Exception>)
  if (coinflip(/* $#capability */)) {      // <: Rand (ok)
    echo(/* $#local_capability */) "bad";  // </: IO
  } else {
    do_io(/* capability); // ok
  } //       ^ (Rand & Throws<Exception>) & IO <: IO
}
```

### Capturing of capabilities/contexts

When a context list is omitted from a lambda, the type-checker
does not need redefine the two coeffects mentioned above
(for calling and for performing local operations); instead
it exploits capturing of `$#capability` and `$#local_capability`
from the *enclosing scope* (since they are local variables).
This enables memory and CPU savings during type-checking.

Observe that there is no semantic difference between inheriting
capabilities on lambda vs capturing (some of) them from the
enclosing function/method, e.g.:
```
function pp_coinflip()[io, rand]: void {
  $coinflip = () ==> { // capture `rand` */
    // VS.    ()[io, rand] ==>
    return rand_int() == 0;
  };
  ...
}
```

Such capturing is disallowed when context list is present
by merely overwriting the locals with the types resolved
during the context resolution into capabilities.  Partial
capturing (i.e., capturing some capabilities but explicitly
requiring others through the context list) is anyway
discouraged for reasons explained in the [vision document](https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/fbcode/hphp/hack/facebook/vision_docs/tracking_effects_and_coeffects.md):

```
function with_rand_and_io()[rand, io]: void {
  // doesn't make much sense (has benefits of neither)
  $bad = ()[rand] ==> { /* capture `io` */ ... };
}
```

## HHVM

### Runtime Semantics of Co-effects

This section will discuss the user observable behavior of co-effects in the runtime. Note that this section does not assume the code type checks via the Hack typechecker as Hack is not a fully static and sound language as well as the typechecker can be bypassed via HH_FIXME and other means. That being said, this section does assume that the code is syntactically correct. For syntactically incorrect programs, the parser will produce an error that will invalidate the entire file in which the syntactical incorrectness is present.

Co-effects will be enforced natively as part of the calling convention; however, the enforcement will be opt-in by the author of the co-effect. This means that each co-effect will always be enforced or never enforced. We will not allow Hack developers to opt out of enforcement; however, we will selectively provide local and shallow enforcement for migratory purposes as well as a global mode where we raise warnings instead of exception. Using the local, shallow or warning level enforcement will not allow you to use features that are enabled by the enforcement but it will be a stepping stone for full enforcement (See migration section for more information).

We will separate co-effects into two buckets from the runtime’s perspective: 1) Erased co-effects, 2) Enforced co-effects.

The erased co-effects represents co-effects where the enforcement is not needed for correctness in runtime nor gives any benefit to the runtime. Hence these co-effects do not need to be enforced in the runtime. These will be dropped from runtime after their syntactical correctness is ensured

The enforced co-effects represents co-effects where the enforcement is required in order to establish correctness and power potential features such as reactive cache and implicit contexts respectively. From here onwards, we are going to assume the co-effects discussed will all be enforced co-effects.

Runtime will have a native knowledge of each co-effect. We will not allow co-effects to be defined in user-land code. All possible list of co-effects will be defined in the runtime via the RuntimeOptions. In addition to their definition, the enforcement level(which includes whether we raise a warning, throw an exception or nothing upon enforcement failures) will also be configurable via the RuntimeOptions to enable safe migration. An important aspect to note here is that certain co-effects that need deep support from runtime such as `io` and `pure` will need to be implemented in the runtime (i.e. runtime is aware of them without the RuntimeOptions definition) and RuntimeOption definition of it only denotes the enforcement level of these co-effects.

These restrictions are in place due to performance and correctness requirements of the implementation. Correctness requirement is presence and absence of some of the enforced co-effects enable usage of various features discussed above and in order to grant access to these features, runtime needs to natively know these co-effects. Performance requirement is that the runtime will need to implement some sort of validation and the number of co-effects present will heavily influence the cost of this check. Specific runtime strategies for this check is discussed in implementation details section.

The runtime will enforce the co-effects as a part of the calling convention. The exact order of these checks is still subject to change as most of these checks do not depend on each other for correctness but for those that do, the runtime will create the best topological sorting of the checks. The list of checks that happen as a part of calling convention are the following in their current order:
1) Function name resolution
2) Visibility checks
3) Inout arity and parity checks
4) Forbidden dynamic-call checks
5) Reified generic arity/parity checks
6) Function parameter arity checks
7) Co-effect enforcement
8) Parameter type enforcement

This means that co-effect enforcement errors will be triggered prior to parameter type errors and other similar errors that trigger the error handler. Co-effect enforcement error will trigger a `BadMethodCallException` that can be caught in the userland to recover.

If preferable, one may think of the runtime tracking of coeffects as an implicit parameter containing a bitset of active capabilities that is passed to and from all function invocations. At the call boundary, a special check is made to ensure that the parameter as requested is sufficiently provided by the caller. Following that check, however, the passed in implicit parameter is not simply forwarded, but logically replaced by one exactly matching the requirements of the called function. This is similar to "dropping" the unrequired coeffects from the passed in implicit parameter, such that they may not be used inside the callee even if provided by the caller.

### Why enforce in HHVM at all?

One may ask why it is necessary to enforce these guarantees at runtime. The language as a whole is generally unsound, and we're working to progress that forward. Why not just add this to the list of pieces that will get iteratively fixed with the rest of the language? Further, what terrible thing actually happens if the rules aren't enforced dynamically?

The answer to the first question is relatively simple. The planned system for a sound dynamic type requires that types implementing it make some guarantees about their properties, methods, etc, such that writing into one via a dynamic type won't result in unsoundness. If all functions have unenforced capabilities, then they would all necessarily preempt a type from implementing dynamic.

There are multiple planned contexts for whom the goal is to make strong guarantees about chain of trust. Without dynamic enforcement, chain of trust is broken and those guarantees aren't successful. This would be tantamount to a constant recurring SEV.

### Rules received by HHVM and how they get translated from the syntax

As a part of bytecode emission, HHVM will receive a list of rules for how to compute the enforced capability set for each function declaration (including closure declarations). The final capability set for an invocation of that function is the set union of the results of computing the rules.

The rules are as follows:

1. **STATIC\<set of zero or more capabilities>**: The set of statically known capabilities.
2. **FUN_ARG\<arg num>**: Argument <arg num> is a function-type argument. Read the list of capabilities for the passed in function. Read the empty set if the value is null. Fatal if the argument is not null, a function pointer, a closure, or a PHP-style reference to a function.
3. **CC_ARG\<arg num, context constant name>**: Argument <arg num> is an object that defines a concrete context constant named <context constant name>. Read the contents of that context constant. Read empty set if the value is null. Fatal if the context constant doesn’t exist, is abstract, or if the argument is not null or an object. Arrays are special-cased to have any abstract context constants defined in their class hierarchy.
4. **CC_THIS\<context constant name>**: `this` defines a concrete context constant named <context constant name>. Read the contents of that context constant. Fatal if the context constant doesn’t exist or is abstract.

Given a list of coeffects [*concrete<sub>1</sub>*, ... *concrete<sub>n</sub>*, *this::C<sub>1</sub>*, ... *this::C<sub>n</sub>*, *ctx $arg<sub>a</sub>*, ... *ctx $arg<sub>z</sub>*, *$arg<sub>a</sub>::C<sub>1</sub>*, ... *$arg<sub>z</sub>::C<sub>n</sub>*]

* *concrete<sub>k</sub>* -> STATIC<{concrete<sub>k</sub>}>
* *ctx $arg<sub>k</sub>* -> if *$arg<sub>k</sub>* is the name of argument *i* -> FUN_ARG<*i*>. If no arguments are named *$arg<sub>k</sub>*, raise an error.
* *$arg<sub>k</sub>::C<sub>l</sub>* -> if *$arg<sub>k</sub>* is the name of argument *i* -> CC_ARG<*i*, *C<sub>l</sub>*>. If no arguments are named *$arg<sub>k</sub>*, raise an error.
* *this::C<sub>k</sub>* -> CC_THIS<*C<sub>k</sub>*>

Following generation, the STATIC rules are set-unioned into a single rule, and rules are otherwise deduplicated syntactically.

Some examples are as follows along with the emitted list of rules for that declaration.

```
function f(mixed $x)[io, rand] {}
Rules: [STATIC<{io, rand}>]

function f((function()[_]: void) $x)[io, ctx $x] {}
Rules: [STATIC<{io}>, FUN_ARG<0>]

function f(
  (function()[_]: void) $x1,
  (function()[_]: void) $x2,
)[io, ctx $x1, ctx $x2] {}
Rules: [STATIC<{io}>, FUN_ARG<0>, FUN_ARG<1>]

function f(Something $x)[io, $x::C] {}
Rules: [STATIC<{io}>, CC_ARG<0, C>]

function f(Something $x1, Something $x2)[$x1::C, $x2::C] {}
Rules: [STATIC<{}>, CC_ARG<0, C>, CC_ARG<1, C>]

public function f()[this::C, rand] {}
Rules: [STATIC<{rand}>, CC_THIS<C>]

public function f(Something $x1)[$x1::C, this::C] {}
Rules: [STATIC<{rand}>, CC_ARG<0, C>, CC_THIS<C>]

public function f(
  Something $x1,
  (function()[_]: void) $x2,
)[$x1::C, ctx $x2, this::C, IO] {}
Rules: [STATIC<{io}>, CC_ARG<0, C>, FUN_ARG<1>, CC_THIS<C>]
```

### What HHVM does with the given rules

HHVM receives a list of rules from the bytecode emitter and processes these rules to create a set of capabilities that will be used to enforce the capabilities of the function call. Note that HHVM will have a list of capabilities but not a mapping between capabilities and the generics that Hack sees which means that the runtime will not not have access to the original forms of the capabilities, generics and context types. This means that the capabilities cannot be reified, used in is/as expressions or accessed via reflection.

HHVM will convert each rule into a set of capabilities and union the sets generated by each rule to finalize the set of capabilities for the function. When HHVM is unable to convert a rule into a set of capabilities, a `BadCapabilityException` will be raised.
Next, we will discuss how each rule gets converted to a set of capabilities and we will follow it with how HHVM compares the rules for a function call.

For all the following rules, runtime will enforce that <arg num> field is within the boundaries of the function and that <context constant name> exists and is not abstract. One more aspect to note here is that only certain type of defaulted arguments may be used for capability rules: null and compile time known static Hack Arrays. This is a limitation of the runtime due to performance reasons as runtime needs to enforce capabilities prior to executing the default value initializers.

1. **STATIC\<set of zero or more capabilities>**

This rule is the most straight forward of the rules. Runtime will iterate over the capabilities and add them to the result set. If any of the capabilities listed on this list is not natively known to the runtime, runtime will raise an exception.

2. **FUN_ARG\<arg num>**

Runtime will access the closure object, or the function pointer after loading it, associated with the function in argument slot <arg num> and extract its set of capabilities and return it. Runtime will store the capabilities on the closure object.

3. **CC_ARG\<arg num, context constant name>**

Runtime will fetch the argument slot <arg num> and access the late static bound class of this object. On the runtime class object, runtime will return the set of capabilities named <context constant name>.

4. **CC_THIS\<context constant name>**

Runtime will access the runtime class of the current function and return the set of capabilities named <context constant name>. If the current function is not a non-static method, `BadCapabilityException will` be raised.

Once set of capabilities are extracted from each rule, the runtime will union the capabilities and generate a final list of capabilities for the function. Note that this list of capabilities contains unique capabilities that are each known to the runtime. At this point, no capability is polymorphic or based off of some other context. We will call this set of capabilities the **ambient set of capabilities** of the function. As a result of this, polymorphic co-effects cannot be be referenced inside the body of the function, as they have already been resolved at that point.
Due to runtime limitations, specifically, runtime not being able to distinguish between each polymorphic co-effect since runtime operates over the entire set of co-effects, polymorphic co-effects cannot be be referenced inside the body of the function.

Now we will discuss how the runtime enforces a function call based on these set of capabilities. Note that from here on the runtime is operating over the ambient set of capabilities, we will no more need to discuss polymorphism or any other co-effect capabilities.

When a function `f` calls a function `g`, the runtime will fetch the ambient set of capabilities for each function. In order to enforce the correctness of capabilities, the caller environment must be able to satisfy all the capabilities of the callee. More concretely, in order for the function call from `f` to `g` to happen, the ambient set of capabilities of `f` must be a superset of that of `g`. If this is true, the function call will happen with the enforcement guarantee. Otherwise, the runtime will raise an exception, raise a warning or silently ignore based on the enforcement level specified by the compiler configuration.

Due to runtime limitations, specifically, runtime not being able to distinguish between each polymorphic co-effect since runtime operates over the entire set of co-effects, polymorphic co-effects cannot be be referenced inside the body of the function.

### End-to-end Example

In this section, we will walk through a real life example end-to-end to illustrate each component. Note that the following example has two function calls to `map_with_logging`, one good and one bad. We will discuss how and why the good one passes the enforcement and similarly how and why the bad one fails.

```
function map_with_logging<Tv1, Tv2,>(
  Traversable<Tv1> $traversable,
  (function(Tv1)[_]: Tv2) $value_func,
)[io, $traversable::C, ctx $value_func]: vec<Tv2> { ... }

function caller_good(vec<int> $v)[rand, io]: void {
  map_with_logging($v, ($i)[rand] ==> generate_rand_int());
}

function caller_bad(vec<int> $v)[rand]: void {
  map_with_logging($v, ($i)[rand] ==> generate_rand_int());
}
```

First, the bytecode generator will generate the following rules for each function.

```
Rules for map_with_logging: [Static<{io}>, CC_ARG<0, C>, FUN_ARG<1>]
Rules for caller_good:      [Static<{rand, io}>]
Rules for caller_bad:       [Static<{rand}>]
```

Secondly, the runtime will extract the ambient set of capabilities of each function.

```
caller_good: [Static<{rand, io}>] => {rand, io}
caller_bad:  [Static<{rand}>] => {rand}
```

Now, let’s inspect map_with_logging for each call.

```
map_with_logging called by caller_good: [Static<{io}>, CC_ARG<0, C>, FUN_ARG<1>]
Static<{io}> => {io}
CC_ARG<0, C> => {}
FUN_ARG<1>   => {rand}

Ambient: {io} | {} | {rand} = {rand, io}
```

```
map_with_logging called by caller_bad: [Static<{io}>, CC_ARG<0, C>, FUN_ARG<1>]
Static<{io}> => {io}
CC_ARG<0, C> => {}
FUN_ARG<1>   => {rand}

Ambient: {io} | {} | {rand} = {rand, io}
```

Notice in both examples the ambient set of capabilities of `map_with_logging` resulted in `{rand, io}`. This is due to both functions passing the lambda argument with same capabilities.
Lastly, we will execute the enforcement section.

```
Ambient set of capabilities of caller_good: {rand, io}
Ambient set of capabilities of map_with_logging called by caller_good: {rand, io}

{rand, io} is a superset of {rand, io}. Function call succeeds.

Ambient set of capabilities of caller_good: {rand}
Ambient set of capabilities of map_with_logging called by caller_bad: {rand, io}

{rand} is NOT a superset of {rand, io}. Function call fails.
```

### Eager versus Lazy Enforcement of Conditional Co-effects

The above implementation discusses the eager enforcement of conditional co-effects. Another way to enforce co-effects would be to lazily enforce them. This type of enforcement changes the user observe-able effects of the enforcement since with eager enforcement note that we enforce the lambda on the above example when the lambda is passed as an argument, whereas; with lazy enforcement the enforcement would be done when the lambda is used. This also means that with lazy enforcement more programs would be valid as if the lambda is not used, the enforcement would never be done. Lazy enforcement would allow us to enforce more programs that would otherwise be disallowed with eager enforcement such as passing a vector of co-effectful lambdas. With eager enforcement, since the runtime currently does not enforce inner type of containers, we would disallow such programs from being syntactically correct; however, with lazy enforcement since we’d be enforcing them when the lambda is used, we could allow them.

It is worth re-iterating that lazy enforcement would potentially allow other types of illegal programs to execute:

```
function callee((function(): void)[_] $f, dynamic $d)[ctx $f] {
  // callee inherits only the co-effects from $f (io in this case)
  // $d is a dynamic function pointer to a function that requires the rand co-effect
  // Because of lazy enforcement we simply forward all co-effects from
  // caller() to callee() and work them out later. In this case, we erroneously
  // pick up the "rand" co-effect which would allow the invocation of
  // $d to succeed incorrectly.

  $d();
}

function caller()[io, rand] {
  callee(() [io] ==> print(1), random_int<>);
}
```

We have decided the employ the eager enforcement since we believe that it will be more performant for the runtime and less complex since after each function call, we know that everything is enforced and we do not need to keep track of what needs to be enforced.

Since eager enforcement is stricter than lazy, it will be possible to switch to lazy enforcement if needed in the future and we could employ other strategies to get the benefits of lazy enforcement as well by introducing some sort of runtime tagging of containers.

### Trade-offs and Restrictions

Historically, adding enforcement to the runtime (parameter and return type enforcement, upper bound generic enforcement, property type enforcement, etc) has often come at a cost as the associated checks are not always free. Ideally the information we gain from this enforcement improves the efficiency of generated code by allowing the JIT to specialize for the enforced properties. This benefit will in many cases offset the cost of the enforcement by allowing the JIT to specialize in places where without enforcement the search space would be too large. In this case, however, the runtime cannot take advantage of the co-effect enforcement to improve code emission. Later, we will discussed some strategies to mitigate possible runtime cost that of this enforcement. Before that we will discuss trade-offs and requirements in order to enforce co-effects with minimal runtime cost.

1. Emit significantly more code for these functions: Allowing any arbitrary function to have these polymorphic co-effects would lead to code bloat and instruction cache problems. Our assumption is that polymorphic co-effects will only be common for library functions, and since they are extremely hot and mostly inlined, this would be a good tradeoff.
2. Have the runtime aware of each possible co-effect: This means that each co-effect must be defined in the runtime.
3. Have a hard limit on how many co-effects there can be: A bounded set of co-effects may be efficiently enforced in O(1) time, while an unbounded set of co-effects will require O(n) checks on each call. The chosen bound will be influenced by call ABI and implementation specific constraints within the runtime.
4. Build tracking of more types than we currently do: Currently HHVM does not do any sort of tracking of types for lambdas and type constants. In order to efficiently enforce the above requirements we would need to build such tracking in HHBBC as well as the JIT so that the aforementioned specialized translations can be done. This is, in general, a good direction for the runtime; however, it is also massive amounts of work.
5. Future co-effects which may influence runtime behavior and have additional correctness requirements: An example to this would be, without going into too much detail, banning memoization of functions that use polymorphic co-effects and co-effect type constants. For co-effects such as cipp, we will be depending on the enforcement to assure correctness for implicit contexts. Since for memoized functions we need to know at JIT time whether to memoize the context or not, and we cannot specialize on this for correctness reasons. Any function that must be memoized along with the context must be annotated with non-polymorphic coeffect list, such as [cipp] and have the __Memoize attribute. Runtime will not need to determine dynamically whether to capture the context during memoization.
6. We will disallow partially forwarding polymorphic co-effects: This is discussed with an example above.

# Motivating usecases

## Purity

Pure code is not able to do very much, but most standard library functions fit neatly into the model. The most important aspect of an example for this is that both pure and impure code can safely and cleanly utilize the pure framework code.

For hack, as above, the pure context is simply one with the empty context list.

```
function map<Tv1, Tv2>(
  Traversable<Tv1> $traversable,
  (function(Tv1)[_]: Tv2) $value_func,
)[$traversable::C, ctx value_func]: vec<Tv2> { ... }

function double_it(vec<int> $v)[]: vec<int> { // {}
  return map($v, $i ==> $i*2); // C -> {}, Ce -> {}, overall -> {}
}

// impure code -> not an error
function double_and_print_it(vec<int> $v)[io]: vec<int> {
  return map($v, $i ==> {
    $doubled = $i*2;
    echo "$doubled\n";
    return $doubled;
  });
}

// ERROR (both with and without [io] on lambda)
function whoops(vec<int> $v)[]: vec<int> {
  return map($v, ($i)[io] ==> {
    $doubled = $i*2;
    echo "$doubled\n";
    return $doubled;
  });
}
```

## CIPP

For CIPP, the most important capability is having access to the runtime Purpose Policy. This is a capability that needs to be added to the context by setting the implicit. Most likely, this will be done using a magic builtin function:

```
function with_implicit<TIn, TOut>(
  TIn $implicit,
  (function()[implicit<TIn>]: TOut) $f
): TOut {
  ..
}
```

Here, the `implicit<T>` capability gives code the ability to access a value of type `T` at runtime. The `cipp<T>` capability is an alias signaling that code should have access to an implicit `T` and also must be analyzed for data leaks. The API for entering into a CIPP context might look like this:

```
function eval<Tcipp, T>(
  Tcipp $purpose_policy,
  (function()[cipp<Tcipp>]: T) $f,
): Wrapped<Tcipp, T> {
  ...
}
```

Developers would then do something approximating this:

```
$wrapped_result = Cipp\eval(Policy::MY_POLICY, () ==> {
  // Do My Policy things
  ...
});
```

# Design rationale and alternatives:

The [vision document on tracking (co)effects](https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/fbcode/hphp/hack/facebook/vision_docs/tracking_effects_and_coeffects.md)
motivates our decision to use a *capability*-based **coeffect** system
as opposed to alternatives documented there.

## Why this needs to be a language feature

The major question to consider in this section is whether the feature is truly necessary or whether it is possible to emulate this within the language presently. It is generally possible to emulate the cross-function interaction via exposing the hierarchies of permissions in user-space, and then mimicking the behavior with explicit parameters. However, the user experience of that would be absolutely horrendous, especially consider the planned usage of this in such things as the Hack Standard Library. Further, implementing the dependent contexts behavior in user-space, if even possible, would be very complicated and likely extremely confusing.

However, even if one were determined to implement the cross-function behavior of contexts in user-space, there is no way to replicate the special typing rules and effects on function bodies that this proposal provides. Consider the pure context - logically, this requires banning the usage of the `echo` statement within the body of that function. This is only implementable via a lint rule, which will never be 100% effective, resulting in any given context being transitively untrustable.

## Drawbacks

The current design involves checking an extra argument at every function invocation in the codebase. Because the types in question are big intersections, this has the potential to considerably slow down overall typechecking.

Initially, the default context will have 2-3 capabilities (permission for external mutable references, global state, and to call reactive/pure code). Reactive code would have 1 capability and pure would have 0. Multiple capabilities are currently represented via an intersection type in the typechecker, so we need only a single subtyping check for each function/method call, albeit with a large constant factor as subtyping type intersections (and unions) could be expensive.

Options to mitigate this (within the typechecker, not shown to users):

1. Special-case the most common cases to avoid full intersection type subtyping/simplification, e.g. if both caller and callee is unannotated, both are fully pure/CIPP, etc. This is probably simplest optimization to try and introduces a fast path and makes the slow path a tiny bit slower.
2. Ad-hoc replace intersections of common combinations of capabilities with a single interface type, so that the check is cheap. This wouldn't be too conservative (incomplete but sound) if we can prove that 2 or more capabilities in the intersection of interest cannot be introduced independently (i.e., they always appear in tandem). See subsection "Encoding multiple capabilities" in the Vision doc

# Prior art:

See this [vision document](https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/fbcode/hphp/hack/facebook/vision_docs/tracking_effects_and_coeffects.md), section Prior art, for existing systems, which also deal with the challenge of minimizing the burden on users via a “lightweight” syntax.

## Effects in Haskell

Haskell is one of the few programming languages that already has an effect system. Although effects in Haskell are monadic rather than capability-based, we can still learn about from how they are used. Haskell functions are pure by default and Haskell has many of the same effects that we have discussed in this document including failure (Either), implicit arguments (Reader), mutation (State), nondeterminism (Random), and IO. Most monads are single purpose, but the IO monad can do nearly everything. IO in Haskell is similar to the default set of capabilities in Hack.

Although Haskell has monad transformers which allows users to combine effects, the common usage pattern is to use a single Monad if you need a single effect, or IO if you need many effects. It is fair to assume that the same will be true in Hack; the best practice should be to use default rather than specifying a long list of individual effects.

# Unresolved questions:

## Tracking Dynamic calls

A major issue for this proposal, as well as hack generally, is the existence of dynamic invocations. This is worse for contexts, as a dynamic invocation down the stack breaks the transitive guarantees up the stack. A pure function might actually result in IO due to some dynamic call far down the stack. This is not a problem for contexts whose calling conventions HHVM will enforce, but that will almost certainly not be universally the case.

Somewhat ironically, the solution for this is an additional context: `can_dyn_call`. `can_dyn_call` gives the capability to invoke functions on receivers that are not known to hack to be static function types. Common examples include `HH\dynamic_fun` and invoking off of `mixed` via a `HH_FIXME`.

```
function call_dynamically_bad(string $funname, mixed $fun): void {
  // HH_FIXME[4009]
  $fun('foobar'); // this would be unfixmeable
  HH\dynamic_fun($funname)('foobar');
}

function call_dynamically_safe(string $funname, mixed $fun)[can_dyn_call]: void {
  // HH_FIXME[4009] - still needs to be fixmed
  $fun('foobar'); // but no additional unfixmeable error
  HH\dynamic_fun($funname)('foobar');
}
```

This would extend to other types of dynamic invocations as well, such as invoking off a dynamic type, TAny, etc.

Note that this context gives a capability to functions, therefor requiring it to be present all the way up the stack from the entry point to the call that invokes the dynamic function.

Unfortunately, even the above is not sufficient due to the following case:

```
function callit(
  (function()[_]: void) $f,
)[ctx $f]: void { $f(); }

function breakit(mixed $fun): void {
  /* HH_FIXME[4110] */
  callit($fun); // whoops
  /* HH_FIXME[4110] */
  callit(HH\dynamic_fun($fun)); // still whoops
}
```

Therefore, we would also require an additional annotation: whether a function type *may* contain a value that would trigger a dynamic invocation error if used directly.

In the above case, something like the following would be required:

`function callit(<<__MaybeDynFun>> (function(): void) $f): void { $f(); }`

Combing the above two features allows us to generally trust the transitive promises of unenforced contexts.

There is technically one further issue, but we do not believe it is a blocking one:

```
function callit((function(): void) $f): void { $f(); }

function breakit(mixed $var_containing_callit, mixed $fun)[can_dyn_call]: void {
  /* HH_FIXME[4009] HH_FIXME[4110] */
  $var_containing_callit($fun); // whoops
}
```

Note that in the above case, this is only possible because `breakit` is marked as `can_dyn_call`, so even though `callit` isn’t marked as such, we’re not accidentally introducing a capability not provided by the call stack. This is similar to the question of coercibility for like types.

However, due to the above, we would need to ban passing mismatching function-type args unless the caller’s arg is marked as `<<__MaybeDynFun>>`.

This work would necessarily be a prerequisite for adding sound capabilities whose calling conventions aren’t guaranteed by the runtime.

Further questions: Does the ability to do `$x->foo = $my_fun` where `$x` is unknown break this because `$foo` could be not marked as `__MaybeDynFun?` Do we need to hard ban this? What does this mean for generics, reified generics, and higher kinded types?

Even with all of the above, it might not generally be possible to guarantee safety in all cases, meaning that we can’t soundly roll out unenforced contexts.

# Future possibilities:

One can imagine any number of applications for this feature. This is quite a powerful and flexible feature that can be used to represent a number of things, depending on our wishes. Some ideas are given below.

## Use Capabilities to Model Async/Await

Rather than the current version:

```
async function gen_do_things(): Awaitable<void> {
  await gen_other_things();
}
```

We could model the permission to use await as a capability:

```
function gen_do_things()[async]: Awaitable<void> {
  await gen_other_things();
}
```

Besides uniform treatment (resulting in a simpler overall system), this would allow for abstracting over synchronicity (i.e., `async` vs normal). E.g., the following snippet would type-check:

```
function do_later((function()[_]: void) $work)[ctx $work]: void { ... }

function with_async_io()[async, io]: void {
  do_later(() ==> print("quick"));  // captures `io` capability
  do_later(() ==> await slow_fun());  // captures `async` cap.
  do_later(()[async] ==> await slow_fun()) // explicitly has `async` cap
}
```

One could further use dependent contexts and have fine-grained callbacks:

```
function wrap_callback(
  (function()[_]: void) $f
):(function()[ctx $f, io]: void) {
    return () ==> {
      $ret = $f();
      print("done");
      return $ret;
    };
}
function demo_callback(): void {
  wrap_callback(() ==> 42);
  wrap_callback(()[async] ==> await slow_fun());
}
```

## Modules

Modules are a proposed Hack feature that would enforce the boundaries between Prod, Intern, and Test code. The idea is that the typechecker would prevent developers from calling from Prod into Intern or Test because that code is not available on production machines and would otherwise fail at runtime. This feature can be easily implemented using capabilities. Prod code simply would not have the permission required to call into intern or test code.

```
// file_in_prod.php
module Prod; // implicitly gives [prod] context

function calls_into_intern()/*[prod]*/: void {
  callee_in_intern(); // ERROR
}
function callee_in_prod()/*[prod]*/: void { ... }

// file_in_intern.php
module Intern; // implicitly gives [intern] context

function calls_into_prod()/*[intern]*/: void {
  callee_in_prod(); // OK
}
function callee_in_intern()/*[intern]*/: void { ... }
```

## Ability to log to Stdout/Stderr

An unfortunately common mistake is one is which a developer adds a `slog`
statement within their code for the purpose of debugging but
then forgets to remove it before committing. We could make the capability
to log to stderr/stdout represented via the context system, such that most
code utilizing it would have a hack error, rendering it uncommittable.
A debugging or development environment could conjure this capability
to facilitate quick development cycle (see the previous subsection).

## Levels of Dynamic Coercibility

In the forthcoming like-types project, there is the concept of coercion for dynamic values. In the base version, there will be some ability for a dynamic value to be passed where another is expect. Much of this is still up in the air, but one exactly is passing a dynamic value into an enforced typehint.

There has been some discussion of having a way to turn off that implicit coercion. We could present this as a capability represented via capabilities, regardless of the desired default.

## Flow-sensitive capabilities

For `throws` context to be fully practical, we would need to refine the
local and calling capabilities depending on the language constructs.
Specifically, a `try`-block would introduce the `Throws` capability
regardless of whether the function provides it, and the keyword
`throw` would require that capability or error otherwise, e.g.:

```
function try_once<T> {
  (function()[throws<Foo>]: T) $f
)[rand]: ?T { $t = null;
  try { // adds Throws<mixed> as a local & calling capability
    if (coinflip()) {
      throw "ok";    // {Rand, Throws<mixed>} ⊆ {Throws<mixed>}
    } else {
      $t = $f(); // ok: {Rand, Throws<mixed>} ⊆ {Throws<mixed>}
    }
  } catch(...) { // Throws<Foo> is no longer available here
    throw "bad"; // error: {Rand} ⊈ {Throws<mixed>}
  }
  return $t;
}
```

It is questionable if the run-time would every support throwing, since
capability violations result in exceptions (chicken and egg problem).
However, the typechecker could track the capabilities as follows:

```
function try_once<T> {
  (function()[throws<Foo>]: T) $f
)[rand]: ?T {
  $t = null;
  //           $#local_capability = Rand
  try {     // $#local_capability = Rand & Throws<mixed>
    if (...) {
      throw(/* $#local_capability */) "ok";
            // $#local_capability = Rand & Throws<mixed>
            //                          <: Throws<Foo>
    }
    // $#capability = $#capability & $#local_capability
    //             == Rand & Throws<mixed>
    $t = $f(/* $#capability */); // ok: Rand & Throws<mixed>
                                 //         <: Throws<mixed>
                                 //         <: Throws<Foo>
  } catch (...) { // $#local_capability = Rand
      throw(/* $#local_capability */) "bad"; // error
            // $#local_capability = Rand </: Throws<Foo>
  }
  return $t;
}
```

---

### Hphp Hack Doc HIPs Converging Constants (`hphp/hack/doc/HIPs/converging_constants.md`)

Status: This is not a full HIP following the usual template/process, but shared as a HIP for external visibility.

# Converging constants in Hack and HHVM

I’d like to ease a few HHVM restrictions on type constant overriding, and along the way to consolidate the rules around constant inheritance in general.

## Motivation

The attribute-based approach to conditional purity  (and conditional reactivity) worked like this.

```Hack
abstract class A {
  <<__Pure, __OnlyRxIfImpl(I::class)>>
  abstract public function f(): void;
}

interface I {
  <<__Pure>>
  public function f(): void;
}

class C extends A implements I {
  <<__Pure>>
  public function f(): void {}
}

class D extends A {
  // impure
  public function f(): void {}
}
```

The `__OnlyRxIfImpl` attribute says that a method invocation on a value of type A is pure only if that value is also an instance of interface I. Under coeffects, we express this conditional purity with an abstract context constant.

```Hack
abstract class A {
  abstract const ctx C = [defaults];
  abstract public function f()[this::C]: void;
}

interface I {
  const ctx C = [];
}

class C extends A implements I {
  public function f()[]: void {}
}

class D extends A {
  public function f(): void {}
}
```

HHVM doesn’t allow non-abstract constants in an interface to conflict with non-abstract constants in the parent class, and since abstract type constants with defaults are simple non-abstract constants at runtime, we can’t use the current HHVM constant semantics to express the definition of class C.

## Background

*Note: I’ve compacted class definitions as I think the examples are easier to read this way.*

Constants in HHVM can be overridden in subclasses, which is a behavior inherited from PHP.

```Hack
class A           { const int X = 3;              }
class B extends A { const int X = 4; /* allowed*/ }
```

Hack allows this behavior to match the runtime. However, if an *interface* declares a constant with the same name, then we have an error when the subclass is loaded.

```Hack
class A     { const int X = 3; }
interface I { const int X = 4; }

class B extends A implements I {} // runtime error
```

If X were instead an abstract constant, then class B can be defined.

```Hack
abstract class A { abstract const int X; }
interface I      { const int X = 4;      }

class B extends A implements I {} // ok
```

Of course, if the interface weren’t present, class B would have to define the constant itself to satisfy the requirements of A.

## **Type Constants**

The type constants feature of Hack, which provides a form of path-dependent types via where constraints, was built on top of class constants. A type constant can be thought of as a class constant whose value is a type structure. As such, the overriding rules for type constants in the runtime are the same — the following code runs without error.

```Hack
class A           { const type T = int;    }
class B extends A { const type T = string; } // runtime ok, Hack error
```

For type safety, Hack bans this overriding behavior. We’ve reached our first major point of divergence between Hack and HHVM.

There is an escape hatch in Hack, however, in the form of partially abstract type constants. These type constants have an `as` constraint and their values can be overridden in a subclass. At runtime, the `as` constraint is simply erased and the constant is interpreted as a regular, non-abstract type constant. In other words, the following example is identical to the previous example from the runtime’s perspective.

```Hack
class A           { const type T as arraykey = arraykey; }
   // runtime sees `const type T = arraykey;`

class B extends A { const type T = int; } // no Hack error, runtime remains ok
```

This is convenient to developers, who use the feature to define a value for a type constant in a superclass and only override it in a few subclasses, but it adds complexity because it requires Hack to make judgements about the type `this::T` based on the exactness of types.

I added the ability to define default values for abstract type constants.

```Hack
abstract class A  { abstract const type T = arraykey; }
   // runtime sees `const type T = arraykey;`

class B extends A { const type T = int; }
```

At runtime, this uses the same machinery as partially abstract type constants, which allows for drop in replacements without risking behavior changes. In the typechecker, abstract type constants are generally much better behaved than partially abstract type constants. They cannot appear in concrete classes, ban direct references via `self::` and `A::`, and cannot be read with `type_structure`. Without partially abstract type constants, `this::T` can be unambiguously resolved in concrete classes.

Unfortunately, the combination of all the implementation details discussed above thereby prevents the following convenient use case.

```Hack
abstract class A { abstract const type T = arraykey; }
interface I      { const type T = int; }

class B extends A implements I {} // runtime error
```

This is the mirror of the context constant example discussed in the motivation section.

Finally, [traits now support constants](https://hhvm.com/blog/2021/02/16/hhvm-4.97.html). Their previous ban of constants could be trivially circumvented by having the trait implement an interface. However, if a constant conflict comes from an interface via a trait, the conflict is silently dropped. With this in mind, the prior interface conflict example can be side-stepped using a trait.

```Hack
class A     { const int X = 3; }
interface I { const int X = 4; }
trait T implements I {}

class B extends A { use T; } // no runtime error!
```

## Proposal

* We use the abstract keyword instead of the presence of a value to denote abstractness for type constants in HHVM. We have committed changes to expose this in HackC and HHVM.
* We require that any classlike (class, interface, trait) has *at most* a single, canonical concrete constant for a given name. If there is a conflict in two defaults for an abstract type constant, we require the developer to redeclare the desired one. Finally, if there are two conflicting defaults but also a single concrete type constant in the hierarchy, the concrete one wins without error.
    * Trivial, existing behavior
      ```Hack
      abstract class A  { abstract const type T = int; }

      class C extends A {} /* const type T = int; */
      class D extends A { const type T = string; }

      class X extends C {
        const type T = string; // error, cannot override concrete C::T
      }
      ```
    * Inherited concrete winning over a inherited abstract
      ```Hack
      abstract class A   { abstract const type T = int; }
      interface I        { const type T = string; }

      class C extends A implements I {} // ok, C::T = I::T
      class D extends A implements I {
        const type T = int; // error, conflicts with I::T
      }
      ```
    * Two conflicting defaults
      ```Hack
      abstract class A   { abstract const type T = int; }
      interface IAbs     { abstract const type T = string; }

      class C extends A implements IAbs {
        // error, must declare a concrete T
      }
      abstract class D extends A implements IAbs {
        // error, must redeclare T and choose a default
        // (can do abstract or concrete)
      }
      ```
    * Two conflicting defaults, but concrete wins
      ```Hack
      abstract class A   { abstract const type T = int; }
      interface IAbs     { abstract const type T = string; }
      interface IConc    { const type T = float; }

      class C extends A implements IAbs, IConc {
         // ok, C::T = IConc
      }
      ```
    * **Design question**: Two conflicting defaults **with same default**
      ```Hack
      abstract class A   { abstract const type T = int; }
      interface IAbs     { abstract const type T = int; }

      class C extends A implements IAbs {
         // should this error?
      }
      ```
    * Abstract override concrete (from class)
      ```Hack
      abstract class A { const type T = int; }

      abstract class B extends A {
        // error, abstract cannot override concrete
        abstract const type T = string;
      };

      interface I { // same error
        require extends A;
        abstract const type T = bool;
      }

      trait T { // same error
        require extends A;
        abstract const type T = float;
      }
      ```
    * Abstract override concrete (from interface)
        * Note: the runtime doesn’t check `require extends` or `require implements`, so those local errors would be in Hack only. The runtime would error later when the interface is implemented or the trait is used.

      ```Hack
      interface I { const type T = int; }

      abstract class A implements I {
        // error, abstract cannot override concrete
        abstract const type T = string;
      };

      interface I1 extends I { // same error
        abstract const type T = bool;
      }

      trait T1 implements I { // same error
        abstract const type T = float;
      }

      trait T2 { // same error
        require implements I;
        abstract const type T = float;
      }
      ```
    * Abstract override concrete (from trait)
      ```Hack
      trait T { const type T = int; }

      abstract class A {
        // error, abstract cannot override concrete
        use T;
        abstract const type T = string;
      };

      trait U { // same error
        use T;
        abstract const type T = float;
      }
      ```
    * Two conflicting concretes
      ```Hack
      interface I { const type T = int; }
      interface J { const type T = string; }

      interface K extends I {
        const type T = string; // error, conflict with I::T;
      }
      interface L extends I, J {} // error, J::T conflicts with I::T
      ```
    * Inherited concrete winning over inherited abstract
      ```Hack
      interface I { abstract const type T = int; }
      trait Tr    { const type T = string; }

      class C implements I { use Tr; } // C::T = string
      ```

      ```Hack
      interface I { const type T = int; }
      trait Tr    { abstract const type T = string; }

      class C implements I { use Tr; } // C::T = int
      ```
      Note that while we are requiring users to redeclare in cases of conflicting defaults, the bounds in the redeclared type constant must still satisfy the all of the bounds in all of the parents. All of the examples above are `as mixed super nothing`. Example:
      ```Hack
      abstract class A   { abstract const type T as arraykey = arraykey; }
      interface IAbs     { abstract const type T as int; }
      interface IConc    { const type T = float; }

      class C extends A implements IConc {
        // error, IConc::T = float, but T is `as arraykey`
      }

      abstract class D extends A implements IAbs {
        // error, IAbs::T brings in constraint `as int`
        // must redeclare T with a default that satisfies this constraint
      }
      ```
    * The first draft of this proposal did not require users to resolve default conflicts, instead taking the last default in the linearization. Feedback was to require explicit resolution and drop the inherited conflicting defaults.

* We make type_structure start failing on abstract type constants with defaults, to push them to behave closer to abstract type constants without defaults. We also do the same for direct references via the class. Of course, as with all runtime changes, we start by logging and sampling before moving to hard enforcement.
  ```Hack
  abstract class A  {
      abstract const type T;
      abstract const type U = int;
  }

  type_structure("A", "T"); // this fatals
  type_structure("A", "U"); // this currently does not

  function f(
    A::T $a, // this fatals
    A::U $b, // this currently does not
  ): void {}
  ```
* Optional: We ban overriding of class constants in Hack to match the type constant behavior. A benefit of this is that `static::X` and `self::X` will have identical meanings in concrete classes. We then ban overriding of concrete class constants and concrete type constants in HHVM.

  ```Hack
  class A           { const int X = 3;           }
  class B extends A { const int X = 4; /* ban */ }
  ```
* Possibly for coherence, we should add default values to abstract class constants as well.

  ```Hack
  abstract class A  { abstract const int X = 3; }
  class B extends A {}
  class C extends A { const int X = 4; /* override */ }
  ```
    * If we implement this and the previous bullet, the semantics of class constants will become identical to type constants across HHVM and Hack.

---

### Hphp Hack Doc HIPs Data Classes (`hphp/hack/doc/HIPs/data_classes.md`)

Title: Data Classes
Status: Draft

## The Problem

The Hack runtime does not know which fields are defined in an arbitrary
shape, and does not enforce them. It also cannot reorder shape fields, and
cannot store them contiguously.

This forces the runtime to use data representations that are slower
and use more memory.

By defining a new data type with runtime enforcement, we can provide a
high performance data structure where HHVM knows the fields.

## Design Summary

Data classes are high-performance immutable classes, enforced at
runtime, with syntactic sugar for creation and updates.

### Basic Usage

Data classes are defined using a syntax similar to other forms of
classes.

```
data class User {
  string $name;
  bool $admin;
  ?string $email = null;
}
```

Data classes live in the type namespace, so they must have a name
distinct from other classes.

Data classes are instantiated with `[]`. This enables HackC to produce
different bytecodes for normal class instantiation and data class
instantiation.

```
$p = Person[name = "wilfred", admin = false];
```

Properties on data classes are accessed with `->`, consistent with
normal classes.

```
// Access
$n = $p->name;
```

Data classes are immutable. To create a new data class with slightly
modified fields, use `...` splat syntax.

```
$p2 = Person[...$p, name = "anonymous"];
```

### Inheritance

Data classes can inherit from other data classes with single
inheritance. Child classes may only add new fields.

```
abstract data class Named {
  string $name;
}

data class User extends Named {
  bool $admin;
}
```

Only abstract data classes may have children. Abstract data classes
may not be instantiated.

### Runtime Type Information

Data classes have distinct type tags. This enables HHVM to enforce
the names and types of properties. These runtime tags may also be
inspected with `is` and `as`, consistent with normal classes.

```
$p is Person; // true
```

This enables runtime type enforcement, unlike shapes.

```
// Type checker error and runtime error.
$p = Person[...$p, name = 123];
```

Function boundaries can also enforce types at runtime.

```
data class Server {
  string $name;
}
data class Person {
  string $name;
}

// Type checker error and runtime error.
takes_person(Server[server = 'db-001']);
```

This is nominal subtyping. Note that shapes have structural subtyping,
so `takes_person_shape(shape('name' => 'db-001'))` produces no errors.

## Release and Migration

HHVM will have a feature flag to prevent people using data
classes.

This enables us to ship frontend support (parser, codegen, type
checker) for data classes without accidentally allowing users to use this.

This does not replace any existing Hack features, so we do not
anticipate large scale codemods. Hotspots in performance sensitive
code will be worth migrating. Users may also prefer to migrate shape
code that doesn't heavily rely on structural subtyping.

## Implementation Details

Since data classes have a distinct runtime representation, HHVM will
need updating so `->` bytecodes work with this new datatype.

Data class splat updates may require a new bytecode entirely.

## Alternatives Considered

### Structs

Adding a simple 'bag of data' struct was also a [design we
explored](https://fb.intern.workplace.com/groups/468328013809203/permalink/541895673119103/).

```
record Person {
  string $name;
  bool $admin;
  ?string $email = null;
}
```

Since we want a nominally typed data model for performance, it would
not be feasible to codemod shapes code to use structs. We
would end up with a larger programming language with more features
that users must learn.

This also makes it difficult for us to explore adding methods in future.

### Alternative update syntax

```
$p2 = clone $p with name = "anonymous", email = "example@example.com";
```

Whilst `clone` is legal PHP/Hack syntax, it's not well known. The name
is also unfortunate, as it's a shallow copy. It's more verbose too.

```
$p2 = $p with name = "anonymous", email = "example@example.com";
```

This syntax would enable good code completion, but has little Hack
precedent. It also means data classes have more distinct syntax from
normal classes, making it hard for us to unify syntax in future.

### Alternative instantiation syntax

We plan to emit a different bytecode for data class
instantiation. This requires syntax that unambiguously represents a
record instantiation.

This bytecode exploits that data classes cannot be in a partially
constructed state, because they don't have constructors. HHVM can
treat it specially.

```
$p = Person(name = "anonymous");
$p2 = Person();
```

This is ambiguous with function calls when all the properties have
default values.

```
$p = new Person(name = "anonymous");
$p2 = new Person();
```

This is ambiguous with normal classes when all the properties have
default values.

### Reusing class declaration syntax

```
final data class User {
  public string $name;
  public bool $admin;
  public ?string $email = null;
}
```

This is significantly more verbose for limited benefit. We want this
to be a compelling alternative to shapes, which have a lightweight
syntax.

### Open Data Classes

```
// '...' allowing additional fields, like shapes.
data class OpenUser {
  string $name;
  bool $admin;
  ...
}
```

Similar to open shapes, this would additional fields to be added
dynamically.

This would require additional runtime work, and wouldn't enjoy the
performance benefits. We believe that an explicit `extra` property is
sufficient.

```
data class OpenUser {
  string $name;
  bool $admin;
  shape(..) $extra = shape();
}
```

## Open Questions

### Equality

We'd probably want structural equality for `===`.

```
Person[name = "x"] === Person[name = "x"]; // true
```


This is inconsistent with other forms of classes though.

It's also unclear what semantics `==` should have for data classes.

### Serialization

It's not clear how data classes should serialize and deserialize.

### Shape Interoperability

It's not clear what APIs we will offer for converting between data
classes and shapes. This is potentially a lossy transform, due to the
nominal typing of data classes and the observable ordering of shapes.

### Generics

We have avoided considering generics so far, to keep the typing rules
simple. We're hoping to ship v1 without generics.

This is definitely a feature we want to add after v1 is feature
complete and shipped.

### Nested Records

Shapes are copy-on-write, allowing updates of nested items.

```
$s['x']['y'] = 42;
```

There is no equivalent syntax for data classes, making this use case
more verbose.

### `$this` semantics

If we do add methods in the future, it is not clear how we handle
`$this`. Would methods be able to change `$this`? If not, what can
methods return?

### Field Ordering

HHVM has had performance wins by reordering fields in classes. We'd
like to retain that flexibility with data classes. If we really need
observable ordering for data classes, it should probably be opt-in.

## Prior Art

C# has [value
types](https://docs.microsoft.com/en-us/dotnet/standard/base-types/common-type-system?redirectedfrom=MSDN#structures)
which are introduced with the `struct` keyword. They do not support inheritance.

Scala has [case
classes](https://docs.scala-lang.org/tour/case-classes.html). They are
immutable classes with public properties, and also provide a `.copy`
method for creating modified copies. They use structural equality.

Kotlin has [data
classes](https://kotlinlang.org/docs/reference/data-classes.html). They
support inheritance and interfaces, and automatically provide equality
and copy methods.

---

### Hphp Hack Doc HIPs Duplicate Enum Values (`hphp/hack/doc/HIPs/duplicate_enum_values.md`)

# Feature Name: Reject duplicate enum member values
## Start Date: 2026-06-19
## Status: Draft

# Summary:

Make it a typecheck error for two members of the same enum to declare the same
value, including when one of the members comes from a `use`-included enum.

```
enum Color: int { Red = 1; Green = 1; Blue = 3; } // Green has the same value as Red
```

Today such enums typecheck cleanly ("No errors") and only fail at runtime:
`HH\BuiltinEnum::getNames()` builds a value-to-name map and throws an
`InvariantViolationException` when two members share a backing value, crashing
whatever calls it.

The check is gated behind the `check_duplicate_enum_values` `.hhconfig` option.
A core goal is to leave **no silent gaps**: every enum is either *fully checked*
or *explicitly opted out* via an attribute. An enum is opted out when it
intentionally aliases values, or when some of its values are *unverifiable* (a
constant reference, arithmetic, or another form the check cannot compare);
otherwise an unverifiable value is itself an error (see *User experience*).

# Feature motivation:

Duplicate enum values are a recurring source of production SEVs.
`HH\BuiltinEnum::getNames()` throws an `InvariantViolationException` when two
members share a backing value, and that exception crashes whatever calls it.
Recent incidents:

- [S565800](https://www.internalfb.com/intern/sevmanager/view/s/565800) — SEV2,
  137K+ affected users (Encrypted Backups, Messenger group-add, Workchat and IGD
  broken via a Trusted Caller enum).
- [S652374](https://www.internalfb.com/intern/sevmanager/view/s/652374),
  [S623413](https://www.internalfb.com/intern/sevmanager/view/s/623413),
  [S659583](https://www.internalfb.com/intern/sevmanager/view/s/659583) — SEV3s.

The blast radius is concentrated in logging/event listeners. Instagram's Falco
event listeners are the most frequent victims: hundreds of callsites under
`www/flib/logging/Falco/Listeners/` call `getNames()` to map client-reported int
values to strings, many via the unguarded direct-access pattern
`getNames()[$value]`. When the invariant fires, the whole listener crashes and
logging data for that event is silently dropped — sometimes for hours before
detection.

The root cause is almost always a **land-time collision**: two diffs land
concurrently, each adding an enum member with the same value. No merge conflict
is raised because the additions touch different lines, so neither author sees
the clash.

Existing tooling does not reliably prevent this: the current lint rule is
best-effort and has structural gaps (see *Prior art*), so duplicates still reach
production and fail late at runtime, where they are hard to attribute. The
codebase already carries accidental duplicates today (e.g. `CodecType`, whose
members are all `0`), which the check surfaces.

# User experience:

With the option on, a duplicate is a typecheck error. So that error-driven
codemods can rely on a consistent shape, the **primary position is the enum
declaration**, with each colliding member reported as a **secondary position**:

```
enum Color: int { Red = 1; Green = 1; Blue = 3; }
//   ^ Enum `Color` declares the value `1` for more than one member
//                 --- Red          ----- Green   (secondary positions)
```

(Anchoring the error at the enum also handles a clash introduced purely by
`use`-inclusion, where no local member is involved and pointing inside an unowned
file would be misleading.)

Three situations require an explicit opt-out attribute; otherwise they are
errors:

1. **Intentional aliasing** — the enum deliberately gives two members the same
   value.
2. **Unverifiable values** — a member's value is something the check cannot
   compare (a constant reference, arithmetic, or a non-UTF-8 string literal; see
   *Implementation details*). Skipping these silently would leave holes: a
   computed value can collide with a checkable one in an enum that `use`s it and
   go undetected, e.g. `A = 1 << 1` in one enum and `B = 2` in an enum that
   includes it.
3. **Downward closure** — an enum that `use`-includes an opted-out enum inherits
   its unchecked/duplicate values, so it must itself carry the attribute. This
   keeps the exemption visible at every enum that relies on it rather than
   silently inherited (a `use` of an annotated enum should not quietly grant a
   false sense of safety).

```
<<__AllowDuplicateValues>>             // name under discussion -- see below
enum Legacy: int { A = 0; B = 0; }     // permitted

enum AlsoLegacy: int {
  use Legacy;                          // inherits a duplicate value, so
}                                      // AlsoLegacy must also be annotated, else error
```

# The opt-out attribute:

Because the attribute now permits more than literal duplicates — it also marks an
enum whose values are *unverifiable* — `<<__AllowDuplicateValues>>` is arguably
too narrow a name. A single attribute covering both cases is sufficient; the name
is open for the review (candidates: `<<__AllowDuplicateValues>>`,
`<<__AllowComputeAndDuplicateValues>>`, `<<__UncheckedEnumValues>>`). The
attribute is **downwards-closed**: a `use` of an annotated enum requires the
using enum to be annotated as well.

# IDE experience:

Surfaces as a standard typecheck diagnostic. No special IDE handling is required.

# Implementation details:

- *Recording.* The direct decl parser records each enum member's value on the
  class constant as a discriminated value — int (as `i64`), string, `nameof`, or
  `::class` — gated by a parser option, `include_enum_member_values`. A `tast`
  check then iterates an enum's folded constants and flags any value that repeats
  an earlier member's, plus (under the "no silent gaps" rule) any unverifiable
  member of a non-exempt enum. Because folded decls already merge `use`-included
  members, direct duplicates, local-vs-used clashes, and clashes between two
  included enums are all caught in one pass.
- *Canonicalization.* Values are normalized so equivalent literals compare equal
  (e.g. `0x10` == `16`, `'a'` == `"a"`). Only int/string literals, `nameof`, and
  `::class` are recorded; anything else (constant references, arithmetic) is
  unverifiable.
- *`nameof` vs `::class`.* These are recorded as **distinct** kinds and never
  collide with each other: at runtime `nameof C` is a string but `C::class` is a
  class pointer, so they are not equal. (Class-pointer work by @vmladenov /
  @dizzy may make `::class` ineligible as an enum value altogether, in which case
  this case disappears.)
- *Non-UTF-8 string literals.* Treated as unverifiable: a non-UTF-8 byte string
  has no lossless comparable canonical form, so recording it risks two distinct
  byte strings collapsing to the same key and being *falsely* flagged as
  duplicates.
- *Opt-out.* The attribute makes the check skip the enum and the parser drop its
  recorded values; it is downwards-closed (see above).
- *HHVM changes.* None required.
- *Corner cases.* Enum classes are out of scope (plain enums only).
- *Codemodding.* Existing duplicate-value and unverifiable-value enums must be
  fixed or annotated before the check is turned on repo-wide.

# Migration scale:

The "no silent gaps" rule means two populations need migrating before rollout:
enums with duplicate values, and enums with unverifiable values. To size this
(asked by reviewers), we will gather from a local run over WWW:

- the number of would-be duplicate-value errors, and the share that are in
  generated code;
- the number of enums with unverifiable/computed values (the additional
  migration "no silent gaps" implies);
- coverage: the fraction of enum members that are checkable (int/string/`nameof`/
  `::class`) vs unverifiable.

*(Numbers pending; to be filled in from a local `hh` run over WWW.)*

# Design rationale and alternatives:

- *Typecheck-time vs runtime.* The bug is severe and currently only fails at
  runtime; catching it statically is the whole point.
- *No silent gaps (error on unverifiable values rather than skip).* Silently
  skipping unverifiable values leaves holes — a computed value can collide with a
  checkable one in an enum that `use`s it, undetected. Requiring those enums to
  carry the opt-out makes every enum either checked or explicitly exempt. This is
  cheap to implement (the decl already records nothing for unverifiable members,
  so the check just flags such a member of a non-exempt enum) at the cost of a
  larger migration. It also answers "why should the attribute affect computed
  values at all": because computed/unverifiable values are now errors by default,
  not silently allowed — the attribute is exactly what permits them.
- *Canonical-value comparison vs full constant evaluation.* Comparing recorded
  canonical literals keeps the check simple and predictable. Resolving arbitrary
  constant expressions (e.g. `X = Foo::BAR` or arithmetic) was considered and
  dropped. Values are recorded by the direct decl parser -- a fast, syntactic,
  per-file pass that intentionally neither resolves references nor evaluates
  expressions. Full evaluation would mean building a constant evaluator inside
  that parser and resolving references across decls (looking up the referenced
  constant's own decl, often in another file). That cross-decl resolution also
  adds fanout: an enum member's recorded value would then depend on the
  referenced decl, so editing that decl would have to re-record the enum and
  re-run the check -- enlarging the decl's dependency footprint and the
  incremental-recheck cost. The "no silent gaps" rule lets us be safe without
  paying for evaluation: unverifiable values are surfaced (via the required
  attribute) rather than resolved.
- *Error, not warning.* A warning would be easy to ignore for a bug this likely
  to break at runtime.
- *Opt-out attribute vs always-on.* A few enums legitimately alias values or use
  unverifiable values; the attribute is a precise, visible escape hatch rather
  than disabling the check globally.

# Prior art:

- **Aurora lint rule `DUPLICATE_ENUM_VALUES`** (`DuplicateEnumValuesLintRule` in
  `www/flib/intern/aurora/lint_rules/ast_linters/`) already flags duplicate enum
  values, but as best-effort lint it has structural gaps that let collisions
  through: it does not run on generated files or on files too long / that fail to
  parse, and catching collisions introduced across files via enum `use`-inclusion
  has been a persistent struggle. The typecheck-level check closes these gaps —
  it reads the folded decl (including `use`-merged members) for every enum,
  generated or not, and reports before the duplicate can land.
- **Runtime-resilience proposal:**
  [RFC: Make Enum::getNames() resilient to duplicate values](https://fb.workplace.com/groups/hackforhiphop/permalink/31492867100335171/)
  proposes making `getNames()` tolerate duplicates (a fallback string) instead of
  throwing. That is complementary rather than competing: resilience limits the
  runtime blast radius, while this proposal prevents duplicates from landing at
  all — the two can coexist.
- **Prior discussion:**
  [Duplicate enum value - Hack Warning?](https://fb.workplace.com/groups/hackforhiphop/permalink/29663604556594777/).

# Drawbacks:

- Existing duplicate-value *and* unverifiable-value enums must be fixed or
  annotated before rollout (size TBD — see *Migration scale*).
- *Decl memory footprint.* Recording a value per enum member enlarges decls.
  Measured full-init peak memory increase is ~+1.3% (a point estimate, not
  statistically significant at 99%); ints are stored as an 8-byte `i64` rather
  than a string, and a member without a recorded value costs only an empty
  option slot.

# Unresolved questions:

- Rollout plan and the timeline for turning the check on by default.
- The final attribute name (see *The opt-out attribute*).

# Future possibilities:

- Resolve simple constant references to widen coverage, turning some
  currently-unverifiable values into checkable ones and shrinking the migration.

---

### Hphp Hack Doc HIPs Enum Class Labels (`hphp/hack/doc/HIPs/enum-class-labels.md`)

# HIP: Enum Class Labels

### Status: Candidate

**Depends on:**  Enum Classes project.

**Implementation status:** Done. Depends on a couple of lags

* ~~Enum classes flag, now enabled by default. (HHVM: `-v Hack.Lang.EnableEnumClasses=1`)~~ This is now removed
* ~~Unstable Features `enum_class_label` (HHVM: `-v Hack.Lang.AllowUnstableFeatures=1`)~~ No longer an unstable feature
* ~~common `disallow-hash-comments` flag~~ this flag has been removed from the parser.

## Motivation

Enums and Enum classes are sets of values. Enum constants and enum class values can be accessed via bindings, but the bindings themselves do not have denotable expressions.  Consider for instance:

```
enum E : int {
   A = 42;
   B = 42;
}

function f(E $x): void {
  switch ($x) {
    case E::A: echo "from A I get "; break;
    case E::B: echo "from B I get "; break;
  }
  echo "$x\n";
}

function test(): void {
  // Both echoes "from A I get 42"
  f(E::A);
  f(E::B);
}
```

In the code above, `E::A` and  `E::B` are values of type `E` and the programmer used the `switch` statement to recover which binding was used to access the enum constant bound to `$x`.  However, since the runtime is only aware of enum constants and not bindings, in both invocations of `f` the runtime will always match the constant `42` (bound in both cases to `$x`) against the constant `42` (obtained via `E::A` in the first case statement): in both invocations the first case statement is selected and `"from A I get 42"` is displayed twice.

While using Enum classes to replace codegen with generic programming, we faced some situations where we needed these bindings to be denotable and first class.  Neither enums, nor enum classes, provide this at the moment.  We also realised that using enum classes in www increases verbosity compared to the old codegened APIs: a call to an “old-fashioned" codegened `getName()` method might now look like `get(MyLongClassNameParameters::Name)`.


## Proposal

This proposal introduces the concept of **labels** for **enum class** constants.  Fully qualified labels are new expressions of the form `Foo#Bar`, where `Foo` is the name of an enum class and `Bar` is the one of `Foo`’s constant. Labels provide first-class bindings for enum classes and can be used in two new builtin functions to obtain the name and value of the corresponding enum class constant. Labels also have a short syntax, of the form `#Bar`, that limits verbosity whenever type inference can be leveraged to reconstruct internally the full `Foo#Bar` label.

**Expressions & types.** A label from the enum class `Foo` binding a constant to the type `T` has type `HH\EnumClass\Label<Foo, T>`.  The new type `HH\EnumClass\Label` is opaque.

```
enum class E : I {
  C Name = ...;
}

E#Name : HH\EnumClass\Label<E, C>

$x = E#Name; // ok
f(E#Name);   // ok
```

In practice, Hack often has enough information to infer the correct enum class name, so we can make it implicit: the `f(E#Name)` call can then be shortened as `f(#Name)`. When type information are not sufficient, Hack reports an error and asks for a fully qualified annotation. We currently only allow the short form in some function calls, but we plan to allow more locations in the future.

```
$x = #Name; // error, not enough context to infer the enum
f(#Name);   // ok, because Hack knows f's first arg type. It behaves like f(E#Name)
```


**New builtin functions.** This proposal extends the definition of enum classes to provide two new static methods that can consume labels:

* `nameOf` which takes a label and returns a string representation of it
* `valueOf` which takes a label and returns the value associated with this label

Here is a short example of their use:

```
function show_label<T>(HH\EnumClass\Label<E, T> $label) : void {
  echo E::nameOf($label);
}

function get<T>(HH\EnumClass\Label<E, T> $label) : T {
  return E::valueOf($label);
}
```

In this example:

* `show_label(E#Name)` will display a string representation of `E#Name`. For now, it would display the string `Name`.
* `get(E#Name)` returns the value `E::Name`.


As long as Hack can infer the enum class name, fully qualified and short expressions can be used transparently:
Since `get(#Name)` behaves like `get(E#Name)`, it will return the same value `E::Name`.

### Sugar coating requested by Beta testers

As requested by some beta testers, we also provide an alternative way write the special case where the **first** function argument is a label, allowing to write `f(#X, ...)` as `f#X(...).` This will enable the developers to use a syntax closer to the old-fashioned  `getName()`, by writing `get#Name()`.

Note that this is only syntactic sugar and does not provide additional possibilities like taking a function pointer or a lambda: `get#Name<>` is forbidden, as it would be a function pointer on a partially applied function.

### IDE Integration

* Automatic completion provides constant names when writing a valid prefix like `getParam#` (implemented by Vassil)
* Documentation hover shows as hover types with doc for labels and function calls sugar like `get#Name()` (implemented by Tom)

## Implementation

### Forewords

It is important to note that until recently, the `#` symbol was a marker for a one line comment.  This has been removed, and the  `#` symbol is now reused by this proposal.  This is not a limitation as Hack still supports one line comments using the `//` syntax.

### Runtime

This proposal introduces two new expressions in the AST, the fully qualified enum class labels (`E#Name`) and the short form (`#Name`).   The current sample runtime implementation works as follow:

* both fully qualified labels and  short labels are compiled to the `Name` label identifier, discarding the `E` part;
*  the base class of enum classes, `BuiltinEnumClass`, is extended with two new final static methods, `nameOf` and `valueOf`.  Given a label identifier, and the class name they are invoked on, these can perform name and value resolution via a special HHVM intrinsics.

### Type checking

To type fully qualified enum class labels, we introduce a new type `HH\EnumClass\Label<-Tenum, Tdata>` which reflects the enum class where the label is defined, and its associated type. The variance of the first type parameter is the same as the one for the `HH\MemberOf` type, introduced by enum classes.  Please refer to the enum class HIP for a detailed discussion of  variance.

The use of fully qualified enum class labels does not require any type checker change.  The shorter version instead needs minor changes to type checking function calls.  We detail how Hack’s type inference is modified to reconstruct the enum class name.

Let us do one step backward and consider Hack's handling of function calls.  At any point in a program, Hack has access to the signature of all previously defined functions.  Suppose that Hack is inferring the type of the expression `f(expr)`:

* it already knows the type of the top level function/method `f` from the context : `A -> B`
* it recursively infers the type of `expr` : `C`
* it checks that `C` is a valid subtype of `A` : `C <: A`
* if that’s the case, the whole expression has type `B`

In general the type of `f` is not needed to infer the type of `expr`.   However, when short syntax is encountered, the type information about `f` arguments is used to infer the type of a label. As an example, let us consider again the `get` function:

```
function get<T>(HH\EnumClass\Label<E, T> $x) : T
```

When inferring the type of `get(#Name)` Hack can perform the following steps:

* from the context, it knows that `get` has type `HH\EnumClass\Label<E, T> -> T`
* it recursively infers the type of `#Name`, *knowing that a constant from `E` is expected*
* there is indeed a constant named `Name` in the enum class `E` so the call is valid
* the full expression will thus behave as if the developer wrote `get(E#Name)`

We currently support this mechanic only for function call when the top level type is a `Label`. In the next section, we’ll discuss ways to support the short syntax in more locations, but this HIP only guarantees the support for function calls.

## Future extensions

### Extension to enumeration types

Standard enumeration types could benefit from the same usage of fully qualified and short labels.  In this proposal, we only focus on enum classes, but the similar machinery might be ported to enums, and extend the expressivity of labels to them. Since there is currently no demand for it, we restrict this feature to enum classes for now.

The main differences  we expect are:

* A dedicated label type, `HH\Enum\Label<Tenum, Tdata>` with different constraints on the type parameter (`Tenum` will probably be invariant since enum inclusion does not entail subtyping, and `Tdata` constraint by `arraykey`)
* The `nameOf` and `valueOf` methods would have to be defined in the `BuiltinEnum` class, and the signature of `valueOf` would need to be adapted.

### Allowing short syntax at more locations

Internally Hack keeps an optional **expected type** information when type checking expressions. We plan to use this information to allow the short labels more broadly, e.g. in return statements.

### Performance sensitive code

The current proposal only addresses the verbosity issue for labels.  For enum class constant themselves, the full name must still be passed to functions that expect constants, not labels, as in the example below:

```
function get<T>(HH\MemberOf<E, T> $member) : T {
  return $member;
}

get(E::Name); // ok
get(#Name);   // not allowed
```

A solution to this issue is to call `valueOf` by hand and rewrite this `get` method like this:

```
function get<T>(HH\EnumClass\Label<E, T> $label) : T {
  return E::valueOf($label);
}

get(E::Name); // not allowed
get(#Name);   // ok
```

In performance-sensitive code, this extra function call might be too expensive.  **_If such code exists_** and the shorter enum class invocation syntax needs to be supported, we might provide an attribute that enables the short notation for constants too, eg.:

```
function get_via_label<T>(<<__ViaLabel>>HH\MemberOf<E, T> $member) : T {
  return $member;
}

get(E::Name); // not allowed
get(#Name);   // ok
```

The logic added to deal with labels has been added at function definition sites:  until more type information is available at compile time, or information from HHBBC can be exploited, we cannot support `<<__ViaLabel>>` by instrumenting call sites. Paul Bissonnette has ideas for that, but it won’t be discussed in this proposal.

We are emitting a special byte code sequence when `__ViaLabel` is used to transform the label (just a constant name) into the right class constant access, in the function **prologue**. This implies that inside `get_via_label`, even if called with the label `#Name`, `$member` is the *constant* value `E::Name`, not the label. There is no way to fetch back the label.


We currently have a running prototype to test this feature, but I don’t think we should ship this unless there are effectively some performance issues in the framework that will use labels.

---

### Hphp Hack Doc HIPs Equality And Identity (`hphp/hack/doc/HIPs/equality_and_identity.md`)

Title: Equality, Identity, and Comparisons

Start Date: June 24 2020

Status: Draft

## Summary

Unify `==` and `===`, introduce sane alternatives for classes that need to use
behaviour similar to the old versions.

## Feature motivation

Hack has two main comparison operators `==` and `===`. They're individually
comprised of behaviours both good and bad, and combine to a muddied, confusing,
and overall broken experience. `==` does coercing value equality, including on
objects, and compares arrays (sometimes unorderedly). `===` does pointer equality
checks on objects (including collections), and breaks some invariants for
reactive and pure code.

We'd like to simplify and correct the mistakes of the previous design

### The current state

1. `===`
   1. Objects: pointer equality (note that closures are instances of different objects)
   2. arrays: compares all elements (keys and values) using `===`, requiring identical internal ordering.
   3. values: value equality
   4. Hack Collections: pointer equality (they are objects)
2. `==`
   1. Objects: compares all properties using `==`
     1. note that closures still don’t compare equal because they’re instances of different objects
   2. arrays: compares all elements (keys and values) using `===` for keys and `==` for values, ignoring order for `dicts`/`darrays`/`keysets`
   3. values: value equality following complex PHP coercion rules.
   4. Hack Collections: compares all elements using `==`,  ignoring order for `Map`/`Set`
   5. If comparing different types will attempt to coerce one side to the type of the other before comparing
3. `<=`, `>=`: Use `==` under the hood. No `===` variant.
4. Sorts and switch statements implicitly use the == behaviour under the hood

## User experience

1. To test object pointer equality, use builtin `is_same_obj($a, $b)` that does a pointer check
   1. Potentially would require a marker interface such as `IIdentityTestable` (name to be bikeshed).
   2. In that case, whether `is_same_obj` is free standing or works as `$a→isSame($b)` to be bikeshed
      1. In the method case, this is non-overridable
2. To get object structural equality require explicit opt-in via an interface such as `IEquatable`
   1. To be bikeshed: does this allow using `==` or does it do something like `$a→eq($b)`
      1. In the method case, this is non-overridable
   2. Would require all properties to be either values or also `IEquatable`
   3. Note that this would probably do an identity test first as an optimization
      1. This also would require reflexivity for the optimization to be sound.
   4. If we can’t compare Collections and Arrays, how do props of those types work? Do we allow it implicitly in this case?
3. For the previous two, if we go the object method route of comparison, absolutely cannot allow overriding implementations.
4. `==` works as follows
   1. never coerces the arguments
   2. Objects: see 1 and 2.
      1. If `IEquatable` means can use `==`, do we return false or throw when not present?
      2. What happens when objects have different types?
   3. Bikeshed more: Cannot compare arrays/collections. Use HSL or Collections methods
   4. Values: work the same
   5. Closures are not `IEquatable`, and pointer equality on two of them is always false (or throws?).
      1. This will cause issues with reflexivity.
   6. bikeshed: `resource` and `NaN`? Other edge cases?
      1. Note that NaN is already weird. Currently `NaN !== Nan` but `vec[NAN] === vec[NAN]`
5. `===` doesn’t exist
6. `<=`,`>=`, etc inherit the changes to ==
   1. Are `IEquatable` objects comparable this way? I expect not.
   2. Do we allow them on Containers?
7. Having an object-arg to a memoized `pure`/`rx` function requires them to be `IEquatable`
8. Sorts and switch statements use the new sane == under the hood
9. Will likely need a builtin coercing_eq_yikity_yikes() that does the old == behaviour for migration purposes

## IDE experience

N/A

## Implementation details

Mostly still open questions above or below. Did I miss any?

What the typing rules of == under this proposal? Is it still a function that takes two values of type mixed?

## Design rationale and alternatives

TODO once we get more consensus about the open questions above

## Drawbacks

TODO

It's a ton of work. Probably not HAM level, but definitely a major project from the HF perspective

## Prior art

TODO

Note rust's ord, eq, partial ord, and partial eq. Rust (which took these ideas from Haskell) only allows you to compare values that implement these traits (typeclasses in Haskell).

## Unresolved Questions (in addition to the currently inline ones above)

For opt-in structural equality, what if I have a non-opted-in parent class? By opting in do I also opt it's fields in to being inspected? What about traits?

Should switches always use pointer equality on objects? This seems very relevant to enum classes.

How do function pointers work? And are you guaranteed to get the same pointer for two different calls

Does structural equality on well-formed IEquatable classes always work?

```
class ListNode implements IEquatable {
  public function __construct(
    public int $val,
    public ?ListNode $prev = null,
    public ?ListNode $next = null,
  ) {}
}

function compare_nodes(): void {
  // Build a linked list [1, 2].
  $n = new ListNode(1);
  $n2 = new ListNode(2, $n);
  $n->next = $n2;

  // What value does $b have?
  $b = $n == $n2;
}
```
The obvious recursive function for structural equality would loop infinitely on $n == $n2.


## Future possibilities

I think this is mostly N/A? Unless we want to reuse === for something?

---

### Hphp Hack Doc HIPs Gradual Modularity (`hphp/hack/doc/HIPs/gradual_modularity.md`)

# Gradual Modularity in Hack

Status: draft, not actively being worked on. If this changes, this pre-HIP proposal should be updated to match
the current HIP template.

Last updated: 2019-10-09

Shared as a HIP for external visibility

## The Problem

In production builds of Facebook WWW, certain directories are dropped, such as internal tools and test directories.
This means that any code attempting to access symbols in those directories will fatal at runtime with an autoloader fatal. Hack is not currently able to enforce this.

As a related problem, due to the global nature of our repo, framework developers struggle to hide the implementation details of their frameworks. Often, we rely on naming conventions like `_DO_NOT_USE`. But, again, there is no static analysis to enforce this, and these symbols end up getting used anyway.

Then, there are similar problems that arise when attempting to define 'black box' APIs - we may want hard isolation while maintaining access to core infrastructure: for example, there may be a blessed library to interact with a
data store, and direct access should be banned except by the library.

Lastly, there's the classic problem of open-sourcing Hack frameworks with a source of truth as part of a proprietary monorepo: for example, all of Facebook WWW can use the HSL, but the HSL can not depend on any proprietary code.

These problems are manifestations of a more general problem: the lack of modularity in the Hack language—everything is globally accessible. Upon closer inspection, we can see that there are multiple granularities to this problem: two examples are the "environment" level and the "library" level.

* Environments represent build-time partitioning of the repository. The 'intern' (internal tools and libraries)/production boundary is a classic example: code in intern may access code in prod, but not vice-versa. This is most akin to the workspaces feature of modern package managers (see Prior Art).
* Libraries are logically related groups of code. These are like modules/packages that we see in most modern languages. Libraries may selectively hide their internals from their dependencies.

Note that these granularities are not mutually exclusive—for example, HSL is both a library and an environment.

In the same way that we slowly introduced types into an untyped Hack, we can introduce modularity gradually, from coarsest to finest granularity. Accordingly, **this document explores a solution to the problem of "environments,"** while leaving the possibility open to solving the "libraries" problem in the future.

## Solution Requirements

* **Discreet.** We desire a limited number of environments in Facebook's WWW codebase, to retain the advantaged of monorepo development.  Environment definitions should be hidden from developers, and should be hard for developers to subvert. Essentially, this means that any per-file syntax is undesirable.
* **Hierarchical.** Environments should be able to depend on others, where "environment A depends on environment B" is defined as: code from A may reference code from B, but code from B may *not* reference code from A. This allows, for example, Intern to access Prod code but not vice-versa. Dependencies are not transitive: i.e. if A depends on B and B depends on C, A does not implicitly depend on C.
* **Permeable.** While environment dependency is generally one-way, we must have a type-safe way of piercing that boundary (for example, *some* Intern symbols *can* be accessed from Prod). This implies that the feature should be first-class-ish enough for the typechecker/compiler/runtime to interact with it. This requirement is intended to be a WWW-only compromise, so we can tolerate it being clunky to use as a deterrent.
* **Single Source of Truth.** Environment specifications should be able to be consumed by the typechecker (to statically enforce the boundaries), the runtime (to dynamically enforce the boundaries), and the build system (to selectively include code in the build). This way, the typechecker has an accurate view of reality.

## Abstract Proposal: Environments

*This section describes the environments feature in an abstract sense, using pseudocode, to bring the semantics of the feature to the forefront. Later sections propose a concrete syntax for the feature.*

An "environment" is an isolated subdivision of a monorepo, where "isolated" means that code defined outside of a particular environment may not access code within that environment (barring certain exceptions). The boundaries are enforced both by the typechecker and the runtime.

The build system will select environments to include in the build, but all environments do not have to correspond to a hard runtime boundary. This allows environments to be used for massive sections of a repository (e.g. intern/prod), but also for libraries (e.g. HSL).

### Defining Environments and Builds

An environment is specified inside a special configuration file in a directory. A special filename isn't strictly necessary, but it makes environments easy to find within a codebase containing extremely large numbers of files, and makes it harder for users to accidentally define environments.

```
// Example: in ~/www/flib/environment.x:

environment {
  name = Prod
}
```

All code in source files in said directory and recursive subdirectories belongs to this environment. A source file belongs to exactly one environment: the nearest one defined. For example, if `flib/` defines an environment "Prod" and `flib/intern/` defines an environment "Intern", then all code under `flib/intern/` belongs only to Intern. Source files which do not have a nearest environment definition belong to a “default environment”.

Environments can depend on multiple other environments. For migration purposes, all environments implicitly depend on the “default environment,” with the assumption that all code will eventually move to a defined environment. To define an environment that depends on another, simply define the environment normally and include its dependencies:

```
// Example: in ~/www/flib/intern/environment.x:

environment {
  name = Intern
  dependencies = [Prod]
}
```

As with the first example, all code under `flib/intern/` belongs only to Intern. However, now that Intern depends on Prod, code belonging to Intern may access code in Prod, but not vice-versa! The dependency relationship is not transitive; for example, if there were an environment "Scripts" that depended on Intern, it would only be able to access code in Prod, if it also depended on Prod.

That `flib/intern/` is a subdirectory of `flib/` is irrelevant—dependent environments don't have to be defined in subdirectories of the environments they depend on. Conversely, Intern doesn't have to depend on Prod just because `flib/intern/` is a subdirectory of `flib/`. In fact, the relationship can be inverted—for example, Prod could depend on an "HSL" environment defined in `flib/core/hack/lib/`!

All environments implicitly depend on the aforementioned “default environment”. This allows for environments to be gradually migrated into a monorepo, without having to do it all at once. It also handles the case of HHVM builtins, which are currently provided by HHIs. Eventually, they may all be inside of a “builtin” environment, but initially they will simply live in the default environment and be accessible everywhere.

Given that `__tests__` are scattered around the codebase currently, any proposed feature would be restricted to running regexes on filenames, which would hamstring its ability to integrate into the language. **Ideally, this feature assumes that tests in WWW are moved from `__tests__` directories to one top-level `tests/` directory.** But, we may be able to reach a compromise if one environment.x file is able to define multiple environments via regular expressions that only apply to files in that directory.

```
// Example: in ~/www/flib/environment.x:

environments {
  ProdTests {
    regex = "#/__tests__/#"
    dependencies = [Prod]
  }
  Prod {
    regex = "#.*#"
  }
}
```

A “build” is a collection of environments that indicate the code that’s available in a particular build of the repository. It is specified inside a special configuration file in the root directory of the repository. For example, Facebook’s builds might look like this. Note that our builds evolved to be hierarchical, allowing us to only specify one environment per build, but it's not necessary.

```
 // Example: in ~/www/builds.x:

build {
  name = Prod
  environments = [Prod]
}

build {
  name = Intern
  environments = [Intern]
}

build {
  name = Scripts
  environments = [Scripts]
}
```

### Permeability

There are two cases of issues in which existing boundaries in WWW are violated. The first issue is simpler than the other: certain classes are defined in Intern, but really should be in Prod. The solution to this problem is to make environments typechecker-only initially, and use the static analysis to move definitions to where they need to be before turning on runtime enforcement.

The second issue is trickier. Sometimes, definitions can't be moved, explicit checks on the current environment at runtime, which explicitly break the abstraction. To support this permeability, we need to be able to introspect on which environments are available in the current build.

```
// Example: in some prod file:

if (<current build includes Intern>) {
  // Access intern code here...
}
```

Or, if we’re refining with some other mechanism (for example, being is a script context implies that the Scripts environment is available), then users can assert an environment is available with `invariant` to provide a useful error message.

```
// Example: in some prod file:

if (Environment::isScript()) {
  invariant(
    <current build includes Scripts>,
    'Being in a script implies that Scripts is available',
  );
  // Access scripts code here...
}
```

### Enforcement & User Experience

Environment accessibility is enforced whenever an identifier is referenced. Enforcement is based entirely on the source files of the “caller” and “callee”—the permeability construct doesn’t affect functions called within a permeability block.

```
// Example: in some prod file:

class ProdClass extends InternClass // Error
  implements InternInterface { // Error
  use InternTrait; // Error
}

function f(mixed $x): void {
  intern_function(); // Error
  intern_function<>; // Error
  InternClass::SOME_CONST; // Error
  h<InternClass>(); // Error
  new InternClass(); // Error
  $x is InternClass; // Error
  if (<current build includes Intern>) {
    intern_function(); // OK
    intern_function<>; OK
    InternClass::SOME_CONST; // OK
    h<InternClass>(); // OK
    new InternClass(); // OK
    $x is InternClass; // OK
    g(); // Note that the usages in g() are still errors
  }
}

function g(): void {
  intern_function(); // Error
  echo InternClass::SOME_CONST; // Error
  // etc...
}

function h<reify T>(): void {}
```

**When typechecking a file**, the typechecker maintains a list of available environments. Initially, the list has exactly one element. Whenever a symbol is referenced, the typechecker compares the environment of the current source file against the environment of the referenced symbol. If they’re incompatible, an error is raised. The permeability construct will add the checked environment to the otherwise singleton list of current environments, and the typechecker will iterate over the current environments within the permeating block.

When a boundary violation is found, the typechecker's error message will list the two environments, including an explanation as to why each source file is in its respective environment. The explanation can be computed from the two ways environments can be defined (directory plus optional string pattern). However, the error message will *not* point to the environment file, as we do not want to advertise this feature to WWW users (but they can easily find it by looking in the directory specified).

For example, consider the environments defined above (Prod, ProdTests, Intern). If code from Intern attempted to access code from ProdTests, the error message would say:

```
Cannot reference a symbol in another environment
  This use-site is in the Intern environment because it is in directory flib/intern/
  The symbol X is in the ProdTests environment because it is in directory flib/ and matches the pattern __tests__
  Intern does not depend on ProdTests
```

**In repo-authoritative mode,** the current build should be known when the repository is compiled (e.g. we know if we are going to deploy to prod, intern, etc). This means that environments that aren’t part of the build will simply be dropped from the repository, any environment checks on identifiers can be elided, and permeability conditions can be statically checked and compiled out (akin to `ifdef`). However, if a function is annotated with the `__DynamicallyCallable` attribute, then the checks must remain, because we don’t know which environments it may be called from.

**In sandbox mode,** the available environments must be defined per-request, so that they behave identically to how such a request would behave in production. For example, in development, a user should not be able to access scripts in a web request. The native autoloader can be environment-aware, and refuse to load symbols if the environments are incompatible. Similarly to the typechecker, the permeability construct will “refine” the current environment to the one checked within the scope of the checked block.

Some ideas on how to determine the available environments:

* Could introspect on WebController and related frameworks to create a build object
* Could use native autoloader to determine the right build object
* __EntryPoint could have a way of specifying which environments are available

## Application in WWW

### Philosophy

Facebook has benefited tremendously by betting on the monorepo model. This feature is intended to mitigate some shortcomings of the monorepo without cutting into the benefits. We want to help users understand when they’re writing code that will behave differently in production, but we do not want to encourage carving up the WWW repo into silos. We want to avoid a world in which developers do not feel empowered to contribute to parts of the codebase because it “belongs” to someone else. We also don’t want to enable an intractable dependency graph to develop in the WWW repo.

Therefore, for the initial rollout of this feature, **we will only allow environments that correspond to a form of hard isolation (e.g. a build boundary, or an OSS library).** Framework maintainers who wish to hide implementation details will have to wait—environments are not intended to be a packages feature.

### Rollback Plan

What if we decide that environments aren't the right solution for WWW? Perhaps they'd proliferate too quickly or make the WWW developer experience too cumbersome. Recall this property of environment definitions discussed above:


> Source files which do not have a nearest environment definition belong to a “default environment”.


It follows that to roll back to a pre-environment state in WWW, it would be sufficient to delete all environment files from WWW. Then, all declarations would be in the default environment, and would be accessible to each other, both in the typechecker and the runtime (if we get that far before rolling back).

The environment checking code could then be removed from the typechecker, followed by the code that records environments into saved states.

### Sample Configuration

This is one possible initial configuration which adheres to the principle defined above:

* **HSL: **The Hack Standard Library, which should depend on nothing except builtins provided by HHVM.
    * Depends on: <NONE>
* **HSL-Experimental: **Components that are in development for eventual addition into the HSL, but not ready to be used in WWW.
    * Depends on: HSL
* **HSL-FB:** Components that may be promoted to HSL eventually, but are useful for some FB use-cases.
    * Depends on: HSL
* **TestInfra: **Components of our test frameworks, such as UnitTest, ExpectObj, etc.
    * Depends on: HSL, HSL-FB
* **Build** Build steps that we may want to isolate from WWW at large.
    * Depends on: HSL, HSL-FB
* **Prod:** WWW at large, anything under `flib/` but not `flib/intern/`.
    * Depends on: HSL, HSL-FB
    * Test environment: **Prod-Tests**
        * Depends on: HSL, HSL-FB, Prod, TestInfra
* **Intern:** Internal code, anything under `flib/intern/`.
    * Depends on: HSL, HSL-FB, Prod
    * Test environment: **Intern-Tests**
        * Depends on: HSL, HSL-FB, Intern, Prod, TestInfra
* **Scripts:** Scripts, anything under `scripts/`.
    * Depends on: HSL, HSL-FB, Intern, Prod

Then, our three builds would be:

* Prod: includes Prod and dependencies
* Intern: includes Intern and dependencies
* Scripts: includes Scripts and dependencies

## Other Considerations

### Incremental Typechecking

There are a few fundamental points that determine how environments will affect Hack's incremental typechecking model.

1. Environments do not create implicit namespaces. Each name in the monorepo is still globally unique, and is defined in exactly one file.
2. We already maintain a best-effort dependency graph between every file in the codebase, so that if a particular file changes, we re-typecheck it and its dependents.
3. All source files belong to exactly one environment (either in a user-defined environment or the "default" environment).

Then, _most working copy changes related to environments may be reduced down to one or more files moving from environment A to B_, in which case we re-typecheck those files and their dependents. This means that the only machinery we must add to the server is watching for environment changes, and mapping those changes to a list of files that moved environments. Consider the following situations:

* **If a file moves between environments,** that file must be re-typechecked, along with its dependent files.
* **If the name of an environment changes, **it's analogous to every file in the environment moving to the new one. Therefore, all files in that environment must be re-typechecked, along with their dependent files.
* **If an environment file is added,** it's analogous to all files in that directory moving to the newly-added environment. Therefore, all files under the directory containing the new environment file must be re-typechecked, along with their dependent files.
* **If an environment file is removed,** it's analogous to all files in that directory moving to the next-closest environment. Therefore everything under the directory containing the deleted environment file must be re-typechecked, along with their dependent files.
* **If an environment file is moved,** it's analogous to zero or more files being added and/or removed from the environment. Therefore everything under the new and old directories containing the environment file must be re-typechecked, along with their dependent files.
* **If the environment's dependencies change,** then all files in that environment must be re-typechecked, along with their dependent files.

Another concern is how the addition of environments affect saved states. TODO:

* add an environments table along with the dependencies between them
* add a reference to environment for each file in the naming table.
* Need to do performance testing for O(repo) updates to the naming table when environments change.

### Open Source

If each of our OSS libraries will be an environment internally, there is potential to integrate environment specification files with package managers. In order to do so specifications would have to be extended with at least the following information:

* Dependency version: allowing for multiple semver constraints
* Dependency source: e.g. npm, GitHub, this repo (in the case of local environments)
* Separation of production vs. development dependencies

For a specific package manager, the current frontrunner is Esy, Reason's package manager. One large factor in choosing Esy over Yarn is that it's a binary (versus Yarn requiring Node on the machine, or us having to package Node with HHVM). We'd need to implement a plugin to read HDF and make sure environment definitions are flexible enough to be used by package managers, and the developers of Esy have been open to collaborating on the design process.

A pitfall to consider is that Esy and Yarn install packages in a global cache on the system (instead of in the project root). This means that the typechecker will need to be taught about this cache in some way to still work in OSS.

## Concrete Syntax Proposal: HDF

*For a concrete proposal for environments, we’ll choose HDF. It’s consistent with HHVM’s current configuration format, and it supports dictionaries and lists. Other options considered were YAML, TOML, and JSON, but each were either inappropriate or suboptimal.*

To define environments, define named nodes inside an environments field in a file named `__environments.hdf` in a directory. The special filename isn't strictly necessary, but it makes environments easy to find within a 2.5M file codebase, and makes it harder for users to accidentally define environments. To define an environment that depends on another, simply define the environment normally add a `dependencies` field:

```
// Example: in ~/www/flib/__environments.hdf:

environments {
  ProdTests {
    regex = "#/__tests__/#"
    dependencies {
      Prod
      TestInfra
    }
  }
  Prod {
    regex = "#.*#"
  }
}

// Example: in ~/www/flib/intern/__environments.hdf:

environments {
  InternTests {
    regex = "#/__tests__/#"
    dependencies {
      Intern
      TestInfra
    }
  }
  Intern {
    regex = "#.*#"
    dependencies {
      Prod
    }
  }

```

A "build" represents a concrete subdivision of a monorepo which is composed of environments. To define builds, define named nodes inside a `builds` field inside a file named `__builds.hdf` in the root directory of the repository.

```
// Example: in ~/www/__builds.hdf:

builds {
  Prod {
    environments {
      Prod
    }
  }
  Intern {
    environments {
      Intern
    }
  }
  Scripts {
    environments {
      Scripts
    }
  }
}
```

Note that our builds evolved to be hierarchical, allowing us to only specify one environment per build, but it's not necessary. A build may contain multiple completely separate environments.

To implement runtime permeability, we use a new builtin function `HH\environment_available`, which takes a string representing an environment name as defined above. If the environment is included in the current build, it returns true, or false otherwise. The typechecker understands this function and will include the listed environment for the duration of the block. It follows that the argument passed to the function must be a string literal.

```
// Example: in some prod file:

if (HH\environment_available('Intern')) {
  // Access intern code here...
}
```

Or, we’re refining with some other mechanism (for example, being in a script implies that the Scripts environment is available), then we can assert an environment is available with `invariant` to provide a better error message.

```
// Example: in some prod file:

if (Environment::isScript()) {
  invariant(
    HH\environment_available('Scripts'),
    'Being in a script implies that Scripts is available',
  );
  // Access scripts code here...
}
```

## Prior Art

### [Cargo Workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) (Rust)

A *Cargo workspace* is a set of packages that share the same `Cargo.lock` and output directory. Packages within a workspace may depend on each other, and depend on the same set of external dependencies.

This is similar to this proposal in which the repo is the workspace, and each “package” is an environment. At Facebook, there is no lock file, but externally, a repo would have just one `composer.lock`, and each environment in the repo would depend on the same external dependencies.

One key difference is that there isn’t a notion of transitive dependencies (there was at some point, [but it was a bug](https://github.com/rust-lang/cargo/issues/1037)); dependencies must be declared explicitly:


> The top-level *Cargo.lock* now contains information about the dependency of `add-one` on `rand`. However, even though `rand` is used somewhere in the workspace, we can’t use it in other crates in the workspace unless we add `rand` to their *Cargo.toml* files as well.

### [Yarn Workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) (JS)

The nomenclature is different here, but the concepts are similar to Cargo workspaces. A *Yarn workspace* is what Cargo would call a package (or we’d call an environment), but otherwise works similarly for interdependencies and external dependencies:


> Requiring `workspace-a` from a file located in `workspace-b` will now use the exact code currently located inside your project rather than what is published on npm, and the `cross-env` package has been correctly deduped and put at the root of your project to be used by both `workspace-a` and `workspace-b`.


Some key differences between Yarn workspaces and this proposal include:

* *“Workspaces must be descendants of the workspace root in terms of folder hierarchy. You cannot and must not reference a workspace that is located outside of this filesystem hierarchy.”*
    * In our proposal, the environments could be anywhere, as long as both the typechecker and the autoloader know about them.
* *“Nested workspaces are not supported at this time.”*
    * Our proposal does support nested environments, with the restriction that a source file may only be in exactly one environment.
* *“Be careful when publishing packages in a workspace. If you are preparing your next release and you decided to use a new dependency but forgot to declare it in the `package.json` file, your tests might still pass locally if another package already downloaded that dependency into the workspace root.”*
    * This is more than a transitive dependency problem. This means if any other workspace in the project has downloaded an external dependency, the current workspace can import it (even if it doesn’t depend on one of those other workspaces). This is a limitation of the package resolution algorithm. Our proposal requires dependencies to be either explicitly declared transitive or imported in each environment, and this will be enforced both by the typechecker and runtime.

### [Assemblies](https://docs.microsoft.com/en-us/dotnet/standard/assembly/index) (C#)

> You can think of an assembly as a collection of types and resources that form a logical unit of functionality and are built to work together. In .NET Core and .NET Framework, an assembly can be built from one or more source code files. In .NET Framework, assemblies can contain one or more modules.


Assemblies are analogous to environments in our proposal, and modules would be analogous to packages, which we may design in the future.

One key difference between assemblies and our proposal is the notion of [friend assemblies](https://docs.microsoft.com/en-us/dotnet/standard/assembly/friend-assemblies): a *friend assembly* is an assembly that can access another assembly's [internal](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/internal) (in C# or [Friend](https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/modifiers/friend) in Visual Basic) types and members. For V1 of this proposal, we have omitted environment-level visibility and punted until we choose to design a packages feature.

### [MODULES](https://blog.golang.org/using-go-modules) (Go)

Probably something to look into as well, because:

* it’s Google scale
* fairly new (mid-2019)
* there were mixed opinions around vendoring & dependencies before

* * *

# Design Meeting Notes

## Discussion with Dwayne (9/10)

* Permeability needs to be fleshed out more
    * There appear to be two kinds of permeability
        * 1: Explicitly introspecting on the current build to see which environments are available
        * 2: Certain files simply being included in the prod build, seems arbitrary. The proposal kind of hand-waves these away by saying we’ll use the typechecker to move them to the right environment. But why haven’t we done that already? Is there a deeper reason?
    * Is it intended to be a permanent feature?
        * If so, it should be designed like a real language feature, not a special function with a string. It should support autocomplete, jump-to-definition, etc.
        * If not, we need to deeply understand the current use-cases and discuss a 100% migration story. Possibly post in Intern/Prod Boundary Working Group to gain more specific use cases to discuss
    * Needs more code examples
        * How to break up switch cases that match on symbols within and outside of the current environment?
* How does this fit into our dependency tracking model?
    * What if environment files change? Must we re-typecheck that environment and all of its dependencies?
    * What if a file moves between environments?
    * How will this feature affect saved state (e.g. will it increase the size on disk)?
* Since we intend for this feature to be used for OSS libraries, how does this feature interact with package managers?

## Brainstorm 3 (8/19)

* Scala packages appear to be pretty close to Hack namespaces already
    * note the “agnostic to file layout”: https://docs.scala-lang.org/tour/packages-and-imports.html
    * If code is not declared in a package, it’s in the empty package (e.g. root namespace in Hack)
    * But does Scala have the problem of people injecting their code into packages?
        * Idea: Environments define which namespaces(packages) can be defined within them. If code inside another environment declares that namespace(package), raise a typechecker error
* Entering a permeability block does NOT change the current environment, it just gives access to another environment
    * This distinction is important when we have non-transitive dependencies
* TODO: Go back and revisit assumptions that may have changed due to existence of non-transitive dependencies
    * Also explicitly list use-cases for non-transitive dependencies
* Tests
    * Problem 1: We never want code in intern/prod etc. to depend on code in tests (environments problem)
    * Problem 2: We want test code to have access to namespace internals (namespaces problem

## Brainstorm 2 (8/05)

* We should find a way to model the needs of libraries with environments as well
    * Fundamentally, namespaces can be declared everywhere, so they do not fit well into notion of “packages”
        * Could namespaces be converted to a “packages” feature?
        * Scala packages appear to be pretty close to Hack namespaces already; note the “agnostic to file layout”: https://docs.scala-lang.org/tour/packages-and-imports.html
            * If code is not declared in a package, it’s in the empty package (e.g. root namespace in Hack)
    * Transitivity should be configurable (transitive by default). Or, ability to seal environments
    * Could have an internal (or export) keyword to control access to declarations within an environment
* Tests: might need some notion of “Friend” environment
    * Could replace <<__TestsBypassVisibility>> attribute
* Decision: not ready for design review
    * Want to have an explicit plan on how to support libraries
        * What about method-level granularity?
    * Should this replace namespaces?

## Brainstorm 1 (7/29)

* Research other languages
    * Scala has a relatively fine-grained visibility system
    * Research Rust crates
    * JS (package managers like Yarn)
* ✅ How to enforce environments at runtime in sandbox mode?
    * Could you construct builds at runtime?
    * Sandbox enforcement should be identical to production environment
    * Will need to integrate with autoloader and facts
        * Symbols would need to track the environment that they’re in
    * That may be an argument against is expressions, because introspecting on the current build would change this runtime enforcement!
* ✅ Are Hack classes/interfaces the right DSL to model environments? Are they markedly better than just regular config files (e.g. package.json, etc.)?
    * If we decide Hack syntax is not appropriate, what’s the alternative? HDF? TOML?
    * Maybe this is a question for design review.
    * Resolution: split proposal into abstract and concrete, will discuss syntax at design review.
* ✅ Do we REALLY need to centralize tests? Is there a middle ground between a first class feature and just regexes on file paths?
* ✅ The idea of hiding framework internals seems more like a language-level module problem (i.e. written in syntax, potentially via improved namespace support), while the problem at hand appears more to be a package management problem. Reflect that in the problem statement.

---

### Hphp Hack Doc HIPs Implicit Pessimisation (`hphp/hack/doc/HIPs/implicit_pessimisation.md`)

# HIP: Implicit pessimisation

## Motivation

The existence of HH_FIXME and use of unsound dynamic in a large Hack codebase
precludes us from statically determining which values are used dynamically, and
which classes and functions may safely opt out. We would like to start from a
position that every value in the codebase supports safe dynamic operations. This
makes it safe for a value to fall into the blast radius of an HH_FIXME, because
it may simply be replaced with an upcast to an appropriate like type instead
using `HH\FIXME\UNSAFE_CAST`, and flow unimpeded into functions that support
dynamic calls.

# Proposal

We propose a uniform pessimisation strategy where every class and function in
the codebase is implicitly marked `<<__SupportDynamicType>>`. To meet the
conditions for supporting dynamic, we implicitly interpret as follows:

All function and method returns of unenforced types become like types.
```
type D<T> = T;
class C {
  public function good(): int {
    return 4;
  }
  public function bad(): D<int> {
    /* HH_FIXME[4110] */
    return "hello";
  }
}

function test(): void {
  $c = new C();
  $a = $c->good(); // $a: int
  $b = $c->bad(); // $b: ~int
}
```
Enums get partial enforcement at their base types to prevent override errors.
```
enum Transparent: int as int { A = 1; }
enum Opaque: int { B = 2; }
class C {
  public function f(): int {
    return 4;
  }
  public function g(): arraykey {
    return 4;
  }
}
class D extends C {
  public function f(): Transparent /* ~Transparent & int */ {
    return Transparent::A;
  }
  public function g(): Opaque /* ~Opaque & arraykey */ {
    return Opaque::B;
  }
}
```
Add supportdyn bounds on abstract type constants
```
abstract class A {
  abstract const type T /* as supportdyn<mixed> */;
  public function f(): this::T { ... };
}
```

Interpret `HH\FIXME\UNSAFE_CAST<T1, T2>(e)` as `e upcast ~T1`.
```
  public function bad(): this::T /* like type */ {
    return HH\FIXME\UNSAFE_CAST<string, int>("hello"); // returning ~int
  }
```

Interpret `mixed`, `nonnull`, function types, and open shape types as wrapped
in `supportdyn`.
```
function d(dynamic $d): void {}
function m(mixed $m): void {
  // $m: supportdyn<mixed>
  d($m); // implicit upcast to dynamic
}
```

Weaken returns and inouts in function type hints
```
function f((function(): string) $f): void {
  // $f: supportdyn<(function(): ~string)>
}

function g((function(inout string): void) $g): void {
  // $f: supportdyn<(function(inout ~string): ~void)>
}
```
> The `~void` is unfortunate, but is addressed by the `return await f();`
proposal.

Interpret interface and abstract method returns as like types, as they have no
function body to trigger enforcement.
```
interface I {
  public function f(): int;
}
abstract class A {
  public function g(): int;
}
class C extends A implements I {
  const type T = int;
  public function f(): this::T { ... } // ~this::T overrides ~int
  public function g(): this::T { ... }
}
```
TODO: @sowens for the complex extends / implements case.

## Hierarchy Poisoning

Interpreting unenforced types as like types causes problems in hierarchies.
```
class A {
  public function f(): int { ... }
}
class B {
  const type T = int;
  public function f(): this::T { ... } /* override error */
}
```
We resolve this by manually weakening the type of the parent class ahead of time
to prepare the codebase for implicit pessimisation
```
class A {
  public function f(): ~int { ... }
}
```
We introduce a handful of marker types to aid this preparation
| Marker name            | Current interpretation | Sound dynamic interpretation |
| ---------------------- | ---------------------- | ---------------------------- |
| `POISON_MARKER<T>`     | `T`                    | `~T`                         |
| `TANY_MARKER<T>`       | `_`                    | `T`                          |
| `SUPPORTDYN_MARKER<T>` | `T`                    | `supportdyn<T>`              |

# Future

## <<__NoAutoDynamic>>

Implicit pessimisation locks all classes and functions into supporting dynamic.
In order to migrate away from dynamism, we propose an opt-out mechanism `<<__NoAutoDynamic>>` that can be used for functions that are not dynamically
callable, and classes in the future once they are similarly verified. The
attribute turns off implicit pessimisation for the attached definition. The
definitions
```
class C {}
<<__NoAutoDynamic, __SupportDynamicType>>
class D extends C {}
```
under implicit pessimisation are equivalent to
```
<<__SupportDynamicType>>
class C {}
<<__SupportDynamicType>>
class D extends C {}
```
without implicit pessimisation.

## Elimination of implicit pessimisation

The core goal of this project is to make explicit the use of dynamism in the
codebase by eliminating misfeatures such as HH_FIXME. Once a sufficient number
of classes are marked up with `<<__NoAutoDynamic>>`, we would like to "flip the
switch" and turn off implicit pessimisation. This will require explicit
insertion of `<<__SupportDynamicType>>` and like type hints. Then, any new
classes and functions will be outside dynamic by default, and users can reap the
benefits of static typing.

# Decision points
- (copy paste) Which parts of sound dynamic will we ship?
  - `~T` vs. `HH\FIXME\POISON_MARKER<T>`
  - `supportdyn<T>` vs. `HH\FIXME\SUPPORTDYN_MARKER<T>`
  - `e upcast T` vs. `HH\FIXME\UNSAFE_CAST<_, T>` (like types only)
  - naming of `<<__SupportDynamicType>>`
- Hierarchy overrides in the context of implicit pessimisation

---

### Hphp Hack Doc HIPs Int Intish Shape Keys (`hphp/hack/doc/HIPs/int_intish_shape_keys.md`)

# Feature Name: int/intish shape keys
## Start Date: 2020-06-29
## Status: Draft

# Summary:

Shape keys are currently required to be:
- string literals that do not start with an int
- class constants

This proposal is to permit any `arraykey` literal, in addition to class
constants. More specifically:
- permit int literal shape keys
- permit any string literal shape key, even if they would be converted to ints
  by PHP

# Feature motivation:

- The ban on int-like shape keys is a PHPIsm in disguise: this exists because
  of array-key-cast behavior which no longer exists in hack.
  - The ban on actual int shape keys was to avoid any confusion with int-like
    keys, however there are other reasons (e.g. avoiding confusion with tuples)
- The bans are not actually consistently enforced, due to permitting type constants
- HSL Regex is already special and creates shapes with integer keys. It would
  be useful to be able to type regexp patterns and matches fully as they're
  passed around
- This would remove some special-casing from the typechecker

# User experience: 

Generally as already-present for `Regex\Match`; additionally, this will now be
possible:

```
// Takes any regex, returns the entire matched string
function helper<T as Regex\Pattern(shape(0 => string, ...))>(
  T $pattern,
): string {
  return Regex\Match($pattern);
}
```

Currently, it can only be typed as `Regex\Match`, which is declared as
`shape(...)` and it is impossible to refine it or redeclare it to say that
`0` (entire string) or any specific numbered captures are present.

# IDE experience:

Already present for Regex\Match; no change expected.

# Implementation details:

* How the feature will interact with other Hack features
  - will make HSL Regex more consistent; other interactions as already applying
* How the feature will be implemented
  - removal of restrictions/special-casing
* A description of corner cases
  - existing problems with class constants
* Any changes to HHVM
  - none
* If applicable, strategies for codemodding
  - n/a

# Design rationale and alternatives:

Largely covered by 'motivation' above. No alternatives have been considered
for removal of ban on 'int-like' `string` keys. These are a PHPism.

## Alternatives for the Regexp problem

The rest of this section addresses alternatives for actual `int` shape keys.

### Using int-like string keys positional captures

For example, `$shape['0']`. The main problem is potential future issues: while
PCRE currently bans named capture groups that start with a number, the syntax
for referencing them differs; it appears that it would be possible to remove
this restriction without breaking compatibility, *unless* we introduce this
syntax.

This would also have a minor drawback from usability/familiarity benefit, as it
would be different to the representations in all other languages.

### Using objects with getters

e.g. `->getNameCapture(string $name)`, `->getPositionalCapture(int $idx)`

This is the approach taken by most other languages/libraries that support named
captures.

This would remove the need for any changes to shapes and tuples, however to
maintain the same static safety that we currently have (i.e. we know which
named and positional captures are valid), these objects will in turn need
to be special-cased - for example, perhaps `re"/(foo(?<bar>baz))/"` is inferred
to be a `RegexpPattern<tuple(string, string), shape('bar' => string)>` - however,
if tuples are used as part of the generic, changes will be needed to support
subtyping.

### Tuples

These natively support sequential integer keys, however they have several
drawbacks here:

- it's valid to mix named and positional captures (and common, especially for 0);
  in practice, would likely return  both a tuple and shape, especially from some APIs
- no sub-typing; in particular, most requests for specifying a type are for "I want
  to take any regexp with at least these captures" - for example:
  - whole string, and a specific named capture: `shape(0 => string, 'foo' => string, ...)`
    - Perhaps this would be `((string, ...), shape('foo' => string, ...)`
  - at least one position capture: `shape(1 => string, ...)`
    - perhaps `((string, string, ...), shape(...))`

Combined with the fact that all elements are the same type, this problem feels
like it would be better solved by bounded-size vecs - i.e. a `vec<string>` with
at least `n` elements - however, in the regexp case, the user normally cares
about presence of a specific n, not `0..=n`, which is a problem already addressed
by shapes.

# Drawbacks: 

- may be misused when tuples are a better fit
- can not be converted to be class-based in the future
  - unlikely due to existing COW semantics
  - likely already impractical due to class constant hole and lack of runtime enforcement
- can not be converted to 'data classes' (a.k.a. 'records') in the future
  - likely already impractical due to class constant hole and lack of runtime enforcement

# Unresolved questions: 

# Future possibilities:

- remove class constant support

---

### Hphp Hack Doc HIPs Memoization Options (`hphp/hack/doc/HIPs/memoization_options.md`)

# Memoization options

This document describes proposed changes to the already defined Implicit Context feature in pursuit of the following goals:

* Expedite wide-scale usage of Implicit Context (IC)
* Ensure IC is always properly accounted for in memoization
* Reduce risk of breaking runtime assertions as part of initial migration or normal development
* Reduce risk of breaking existing logic depending on behavior of `<<__Memoize>>` today

> This feature is also referred to as the "Dynamically Enforced Implicit Context" feature.

## Today: IC requires Contexts and Capabilities

The Implicit Context feature provides a value that is implicitly propagated to callees recursively. For example:

```
// Native, in HH namespace
abstract class ImplicitContext {
  abstract const type T as nonnull;
  public static function set<Tout>(
    this::T $context,
    (function ()[zoned]: Tout) $f
  )[zoned]: Tout { ... }
  public static function get()[zoned]: ?this::T;
}

// Userland
final class MyPolicyImplicitContext extends HH\ImplicitContext {
 ...
}
```

```
MyPolicyImplicitContext::set(
  $my_policy,
  ()[zoned] ==> do_stuff(),
);

...

function some_recursive_callee_of_do_stuff()[zoned]: void {
  $_ = MyPolicyImplicitContext::get(); // returns $my_policy
}
```

In order to prevent poisoning memoization caches (fetching the cached results computed under one IC value when executing under a different IC value), we designed the IC feature to be coupled with the coeffects feature which applies static recursive restrictions. We required that:

* You can only set an IC for the execution of functions requiring at most `[zoned]` but we ignore this context in this document)
* Memoized functions that are callable from a `[zoned]` function must either:
    * shard the memoization cache based on the IC value (iff the context of the function is `[zoned]`)
    * guarantee that the IC is not used by having a context without the `ImplicitPolicy` capability which is required to access the IC (e.g. `[leak_safe]`)

While tying the IC to contexts and capabilities gives us static guarantees about code, adding these more restrictive contexts to code requires a lot of effort, and some developers want the benefits of the IC without the requirement of using contexts.

## Proposal

We propose allowing setting an IC when executing functions requiring `[defaults]`. The new signature for this method on the `ImplicitContext::` class will be:

```
  public static function runWith<Tout>(
    this::T $context,
    (function ()[_]: Tout) $f
  )[ctx $f, zoned]: Tout { ... }
```

>In the above, we have also renamed `ImplicitContext::set` to `ImplicitContet::runWith` in order to avoid an incorrect assumption that the IC is set to the given value *after* the completion of this method call. We use the new “runWith” name for the remainder of this document.


We will avoid poisoning memoization caches by dynamically requiring that memoized functions executing under an IC fall into one of two safe categories:

* The function is marked as having its memoization cache key incorporate the IC. This is achieved by using the `__Memoize(#KeyedByIC)` attribute (formerly known as `__PolicyShardedMemoize`) instead of regular `__Memoize`. We will allow this attribute to be used with any function with the `ImplicitPolicy` capability including `[defaults]` functions.
* The functions is marked as never using the IC. This can be achieved either by:
    * Using the new attribute `__Memoize(#MakeICInaccessible)` instead of regular `__Memoize`. This attribute will act like `__Memoize` except: attempting to fetch the IC or calling an uncategorized memoized function from a function with this attribute will throw. This behavior applies to immediate and recursive callees until an IC value is set again (if ever).
    * Using regular `__Memoize` and requiring a context that does not have the `ImplicitPolicy` capability. Because fetching the IC requires `[zoned]`, we know that recursive callees cannot access the IC. (Given the current set of contexts, this is the set of contexts as capable as or less capable than `[leak_safe, globals]`.)
* If a function is marked with `__Memoize` without an categorization argument or coeffect, it will be treated as `__Memoize(#MakeICInaccessible)`.

>In the above, we are adding an optional enum class label argument to the `__Memoize` attribute. See the “Syntax” section.

Note: In this document, we describe semantics in terms of `__Memoize` and `__Memoize()`. The same statements apply to `__MemoizeLSB` and new `__MemoizeLSB()`.

## Description of states

Under this proposal, the IC can be in one of three states:

```
null : The IC has not been set
value(T) : The IC has been set to some value
inaccessible : Fetching the IC or calling uncategorized memoized functions will result in an exception
```

## Description of state transitions

The following actions will have the following behavior:

### Calling a function with regular `__Memoize` that has the `ImplicitPolicy` capability (e.g. `[defaults]`)

This is treated the same as `__Memoize(#MakeICInaccessible)`

```
* : Run, do not key cache with IC, transition to inaccessible state with this function as blame
```

### Calling a function with `__Memoize(#KeyedByIC)`

This does not affect the state of the IC.

```
null: Run and use null as the IC key
value: Run and use the IC value as the IC key
inaccessible: Run and use the inaccessible state (singleton value) as the IC key
```

### Calling a function with `__Memoize(#MakeICInaccessible)`

```
* : Run, do not key cache with IC, transition to inaccessible state with this function as blame
```

### Calling a function with `__Memoize` that does not have the `ImplicitPolicy` capability

This does not affect the state of the IC.

```
* : Run, do not key with IC
```

### Calling `ImplicitContext::get`

This does not affect the state of the IC.

```
null: return null
value: return value
inaccessible: throw
```

### Calling `ImplicitContext::isInaccessible`

This does not affect the state of the IC.

```
null: return false
value: return false
inaccessible: true
```

### Calling `ImplicitContext::runWith`

```
* : set the IC to a value, transition to the value state with this function call as blame
```

### Calling `HH\Coeffects\backdoor` or aliases

These backdoors allow calling code requiring `[defaults]` from various, less-capable contexts.

```
* : move to null state (clears any IC value)
```

### Calling `HH\Coeffects\fb\backdoor_to_globals_leak_safe__DO_NOT_USE`

This backdoor allows calling code requiring at most `[leak_safe, globals]` from any context.

```
* : no op, no state change
```

Note that by enforcing that the code executed via this backdoor can only require at most `[globals, leak_safe]`, we prevent problematic calls to unsafe memoized functions and calls to fetch the IC.

## Attributes’ interactions with coeffects

This section describes rules for what functions with what coeffects can use these new attributes.


* `__Memoize(#KeyedByIC)` will only be permitted on functions that are known to have the `ImplicitPolicy` capability at compile time.
    * This means that the functions’ context list must be implicitly `[defaults]` or contain one of the following contexts explicitly: `defaults`, `zoned`, `zoned_shallow`, `zoned_local`.
    * Reason: A developer should be able to expect that a function without the `ImplicitPolicy` capability is not affected by the Implicit Context. However, this would not be the case if we allowed `#KeyedByIC` — e.g. imagine a memoized, leak-safe function that returns a random number.
* `__Memoize(#MakeICInaccessible)` will only be allowed on functions with any of the following contexts: `defaults`, `leak_safe_shallow`, `leak_safe_local`
    * Reason: For functions without the `ImplicitPolicy` capability, the IC is already inaccessible and the function can use regular `__Memoize`. For functions with `zoned`, using this attribute seems contradictory.
    * This will be similarly enforced at compile time.
* (As before) A memoized function with the `zoned` context must use `#KeyedByIC`.
* (As before) A function with dependent contexts may not be memoized.

## Expected migration path

A developer that wants to enable adopt DEIC in a codebase:

1. At the start of request, begin executing the code using `ImplicitContext::runWith`
2. Add calls to `ImplicitContext::isInaccessible` where you expect to fetch the implicit context via `ImplicitContext::get` and log all violations.
3. Address these logs and remediate them as fit (ie. Change `__Memoize` functions to be `#KeyedByIC`).
4. Once violations are sufficiently addressed, start using `ImplicitContext::get` to influence runtime.

## Path to contexts and capabilities

Allowing use of a dynamically-enforced Implicit Context is compatible with a statically-enforced Implicit Context using Contexts and Capabilities. In fact, the work to specify IC-handling for memoized functions is a subset of the work required to make functions callable from `[zoned]` contexts.

In abstract, you can think of functions using `__Memoize(#KeyedByIC)` as being functions where the intention is to eventually require `[zoned]` (if it does not already) and think of functions using `__Memoize(#MakeICInaccessible)` as being functions where the intention is to eventually require `[leak_safe]` or some more restrictive context.

## Syntax

The current proposal introduces an optional, enum class label argument to the existing `__Memoize` (and `__MemoizeLSB`) attributes e.g. `__Memoize(#KeyedByIC)`. This would require adding the ability to use enum class labels in attribute argument positions.

```
enum class MemoizeOption: string {
  string KeyedByIC = 'KeyedByIC';
  string MakeICInaccessible = 'MakeICInaccessible';
}
```

Currently, the `__Memoize` attribute is hard-coded into the typechecker and there is no declaration for it. However, you could declare it like so:

```
final class __Memoize implements HH\FunctionAttribute, HH\MethodAttribute {
  public function __construct(
    private ?HH\EnumClass\Label<MemoizeOption, string> $kind,
  ) {}
}
```

Alternatives we considered:

* Using special constants e.g. `__Memoize(KeyedByIC)`. However, constants are not permitted as attribute arguments as attributes are evaluated at compile time and using evaluation constants would require “decls in compilation.” We also compute attributes in order to build indices for Facts which may never have access to cross-file declarations.
* Using special strings e.g. `__Memoize("KeyedByIC")` — this has the downside of a string argument appears to take arbitrary strings, but can only take specific, special strings. This may also require additional changes to support autocompleting these strings in IDEs.
* Creating a new attribute to replace each variant e.g. `__MemoizeKeyedByIC` and `__MemoizeMakeICInaccessible`
    * If we choose this option, we may consider more fluid names like `__MemoizeWithIC` and `__MemoizeAndMakeICInaccessible`
    * This has the downside of adding each attribute for memoize and lsb variants, increasing the code complexity in parser/runtime
* Creating auxiliary attributes to use alongside `__Memoize` e.g. `<<__Memoize, __MakeICInaccessible>>`
    * This option has the downside of introducing attributes that can only be used with other attributes which would be especially undesirable for `__Memoize(KeyedByIC)`
    * We could conceivably allow `__MakeICInaccessible` to be used standalone. However, at the moment, we do not see a legitimate use for it without an accompanying `__Memoize`.

## Naming

"KeyedByIC" uses "Keyed" which references the fact that the IC is incorporated into the key used in the memoization cache. We could also possibly use imperative "Key" vs past participle "Keyed." Possible alternatives to the verb "key:"

* "Shard" - original verbiage from "PolicyShardedMemoize" but noted possible incorrectly inferred association with database sharding
* "Split"
* "Partition"

"MakeICInaccessible" describes how the IC cannot be accessed. A previously proposed name used the verbiage "clear IC," however this created a distinction between a state where the IC was "cleared" vs one where the IC was never set. Primary concerns with this name are aesthetic: "inaccessible" is long and may be awkward to say and spell. A name like <imperative verb> + "IC" may be preferable, but verbs like "prohibit" or "ban" may incorrectly imply immediate exceptional behavior of the function with the attribute instead of restrictions on the function's dependencies.

“IC” as an abbreviation for “Implicit Context” was chosen for brevity and lack of more attractive options. We concluded that brevity was valuable given expected prevalent usage of these attributes and that the disadvantage of possible ambiguity of “IC” would be offset by the fact that the full symbols names would be sufficiently unique. Other alternatives that were considered:

* “IArg” as in “Implicit Argument” — This primary issue with this option is that “implicit arguments” is a feature in other languages with a different meaning. Using this term would both set incorrect expectations for readers with experience in those languages as well as be problematic should we want Hack to adopt a similar feature.
* “ICtx” (using “ctx” as an alternative abbreviation for “context”) — The primary objection to this was that while we use “ctx” as an abbreviation for “context” in other features of the language, in those cases, “ctx” refers to a coeffects “context,” which, while there are some tie-ins, the “contexts” in each case are distinct concepts.
* “ImplicitContext” — This was considered too long, especially in `__Memoize(#SoftMakeImplicitContextInaccessible)`.
* “Ctx” or “Context” (without “implicit”) — This was considered insufficiently disambiguated as “context” is already an overloaded name. A developer may reasonably, but incorrectly, assume that “KeyedByContext” would mean “keyed by the coeffects context.”
* Other names: “Implicit Value” or “IVal,” “ScopedValue,” “ContextValue” or “CVal” or “CtxVal”

## Multiple Implicit Contexts

The current implementation of Implicit Contexts allows for multiple IC “flavors.” You create a flavor by defining a class implementing the native `HH\ImplicitContext` class. This presents the question for how the above features should work when there is more than one IC flavor.

We have identified a few options:

1. **Only allow a single flavor and require any further division of the value to be done in userland.** This has the benefit of simplifying the semantics. This also inherently requires coupling among all flavors, but unless you parameterize `KeyedByIC` and `MakeICInaccessible` features by flavor, the flavors are already tightly coupled since they will need to agree on when these attributes ought to be used. Coupling is also introduced by sharing related coeffects and the backdoor functions.
2. **Track an IC state per flavor and allow individual setting.** Each flavor’s state could be set individual by `runWith` calls, but `MakeICInaccesible` would set all flavors’ state to `inaccessible`. This would likely require more checks at runtime resulting in worsened performance.
3. **Only track flavors in the value state.** Setting one flavor from the `inaccessible` state would resulting in implicitly setting all other flavors’ value to the initial null state.

Our recommendation is that we take Option 3 which is closest to the current implementation of Implicit Contexts but require in userland that there is at most a single child of `HH\ImplicitContext`. This effectively means choosing Option 1 from the perspective of the user. This compromise allows us to make fewer changes to the runtime in the near term. We can choose to simplify the runtime to not support multiple flavors at a later date.

---

### Hphp Hack Doc HIPs Named Parameters (`hphp/hack/doc/HIPs/named_parameters.md`)

Authors: Max Heiber, Sinan Cepel
Status: Accepted and Implemented
Internal context: [https://fburl.com/hack\_named\_parameters\_proposal](https://fburl.com/hack_named_parameters_proposal)

# Named Parameters: HIP

## Goals and Constraints {#goals-and-constraints}

### Goals

Listed in descending priority order (we plan to deliver on both):

* G1: DevX for **both humans and AI**  
  * Why:  
    * Lack of named parameters makes WWW code hard to work with: users must manually deal with large parameter lists or with the awkwardness of simulating named parameters with shapes.  
    * Our ergonomics has fallen behind that of other languages  
    * Part of a broader effort to make WWW easier for **both humans and AIs** to work with (both reading code and writing code)  
    * See also [Example of improved ergonomics for py2hack](https://fburl.com/gdoc/hsscchpg)  
* G2: increase safety  
  * Why:  
    * Prevent SEVs and related issues (including runtime errors and unexpected behavior). Specific SEVs are in https://fburl.com/hack_named_parameters_proposal
    * Support AI-generated code efforts through enabling more trust in tooling

### Hard Constraints

* C1: Easy to adopt. Implies:  
  * Ergonomics  
  * Codemoddability  
  * Converting a function and its call sites from simulating-named-params-using-shapes to real named parameters should be perf-positive  
* C2: Feasible.  
  * We need to be able to deliver value in 2025

### Defeasible Constraints:

* C3: Avoid prematurely restricting the design space  
  * We ship early but make it as straightforward as we can to add or change things later.

### Non-goals

* Improve performance significantly  
  * Why: Improving on simulating-named-params-via-shape-passing is sufficient to meet our goals and the design space is already heavily constrained.  
* Feature-completeness:  
  * Why: we can provide value and gain learnings earlier by shipping something that meets the goals but leaves open room for more features (see **Potential improvements outside initial MVP scope** below).

## Solution Sketch {#solution-sketch}

- When a parameter is named, the call site must provide the corresponding argument name.  
- Named parameters are distinct from positional parameters (parameters are not "named by default")  
- Parameter names for named parameters are part of the function type.

Examples:

```php
// definition
function foo(int $a, int $b = 0; int ...$args, named int $c, named int $d = 0): void {}

function main(): void {
    foo(1, c=2);
    foo(1, 3, d=2, c=2);
    foo(1, 3, 4, c=2, d=2);

    // hh error: too few arguments
    // HHVM RuntimeException
    foo(); 

    // hh error: too few arguments
    // HHVM RuntimeException
    foo(1);

    // hh error: unexpected named argument
    // HHVM RuntimeException
    foo(1, missing=2);
}
```

Hierarchies:

```php
class A {
    public function m(named int $a): void {}
}

class B extends A {
    // hh error: `m` must have a named parameter `$a` since it overrides `m` in class `A` which has named parameter `$a`
    // Runtime error: `m`'s override declaration is inconsistent with base.
    <<__Override>>
    public function m(named int $b): void {}

}

function main(): void {
  let $b: A = new B();

  // HHVM RuntimeException
  $b->m(a=1);}
```

Anonymous functions and types:

Note: `optional` is [an existing keyword in function types](https://fburl.com/workplace/8el6ohb9)

```php
function take((function(int, named int $b, named optional int $c)) $f): void {}


function main(): void {
    $f = (int $a, named int $b, named int $c = 0) ==> $a + b + c;
    $f(0, b=1);
    $f(0, b=1, c=2);
    take($f);
}
```

## User-facing Documentation {#user-facing-documentation}

You can declare parameters of a function as `named`, in which case callsites must specify the parameter by name instead of positionally.

```php
function add_to_table(named int $row, named int $col): void {
    // ....
}

.....

function main(): void {
    add_to_table(row=1, col=2);
}
```

In the example above, named parameters help callers avoid mixing up `$row` and `$col`:

    `add_to_table(2, 1); // hh error: missing named parameters $row and $col`

Named parameters can make your code safer and easier to read when a function has parameters that would otherwise be easily confused:  
\- multiple parameters of overlapping types (e.g. boolean flags, ?int and null)  
\- especially when such parameters have default arguments

Here’s an example with easily-confused parameters:

```php
 protected async function genMessageDelta(
    Context $context,
    ID $id,
    Map<string, string> $data,
    ?arraykey $offline_id = null,
    ?vec<string> $sender_tags = null,
    ?vec<string> $meta_tags = null
)
.......

await genMessageDelta($ctx, $id, null, null, vec['tag1']);
```

By making the easily-confused parameters named, calls to the function get both safer and easier to read:

```php
 protected async function genMessageDelta(
    Context $context,
    ID $id,
    Map<string, string> $data,
    named ?arraykey $offline_id = null,
    named ?vec<string> $sender_tags = null,
    named ?vec<string> $meta_tags = null
)
.......

await genMessageDelta($ctx, $id, meta_tags=vec['tag1']);
```

Named parameters behave just like positional ones except for how you refer to them at callsites: named parameters can have default arguments, and are matched according to their names at call sites. 

Note that args for named parameters can be interspersed with positional args :

```php
function send(
  string $message, 
  named string $host = 'www.thefacebook.com',
  int $retries = 3 // default argument
) { 
...
}


async function foo() {
  send('poke', host='www.facebook.com');
  // can permute order
  send(host='www.facebook.com', 'poke');
}
```

#### Historical remark

You may see older code that simulates named parameters using shapes:

```php
 protected async function genMessageDeltaShapeVersion(
    Context $context,
    ID $id,
    Map<string, string> $data,
    shape(
     ?'offline_id' => arraykey,
     ?'sender_tags' => vec<string>,
     ?'meta_tags' => vec<string>,
    ) $opts
) {
  $offline_id = $opts['offline_id'];
  $sender_tags = $opts['sender_tags'];
  $meta_tags = $opts['meta_tags'];
  // ...
}
.......

await genMessageDeltaShapeVersion($ctx, $id, shape('meta_tags' => vec['tag1']));
```

Prefer using named parameters over simulating-named-parameters-via-shapes: Names parameters are:  
\- more ergonomic  
\- more performant  
\- safer: HHVM checks types for named parameters but not for individual keys in a shape in parameter position

## Solution in Detail {#solution-in-detail}

### Syntax

\> Syntactic restrictions are enforced by both HHVM and Hack, which share a parser  
\> For syntax at a glance, see [examples](?tab=t.0#bookmark=id.hxe2kyrt1ja5) above

In declarations:

- Named parameters are indicated with the `named`  keyword.  
- Excerpt from how we will update the parser, the new part is indicated. (note that our current BNF-style comments in the parser are out of sync with the impl, so impl is more informative here):  
  - [parameter\_declaration before]($REPO//hphp/hack/src/parser/core/declaration_parser.rs): `attrs_opt  visibility_opt  inout_opt  readonly_opt ellipsis_opt <ellided>`  
  - parameter\_declaration after   : `attrs_opt  visibility_opt  inout_opt named_modifier_opt  readonly_opt ellipsis_opt <ellided, no change>`  
  - `named` can be used in constructors and is allowed with promoted properties: `class A { function __constructor(public named int $x)`  
- Named parameters can have default arguments: function foo(named int $x \= 1): void {}  
  - Same restrictions (and freedoms) on default arguments for named parameters as for positional parameters  
- Named parameters will initially not support inout [for pragmatic reasons to do with the runtime](https://fburl.com/gdoc/lki55ag2). Such support may be added in future.

In call sites:

- Basic syntax: `foo(x=3)` supplies argument `3` for named param `$x`  
  - Why: [Named Parameters HIP supplement: why = for separating param names from arg values in function calls](https://fburl.com/gdoc/jmme24ua)  
- Positional arguments can be interspersed with named arguments: `bar(y=5, 1, 2, 3, x=4)`

Named parameters in type syntax:

* Named parameters are indicated with the `named` keyword, which comes before `optional` and before the type of a parameter. `function take((function(int, named int $b, named optional int $c)) $f): void {}`  
* Named parameters must have names.

### Runtime Semantics and Checks

A complete description of the runtime behavior is in the Supplement: Runtime Design section below.

#### Basic representation

- The runtime will extend Func objects to store the names of named parameters, and store them internally in an implementation-defined order, distinguished from positional parameters.  
- The calling convention will be extended such that:  
  - Named parameters are passed from the caller to the callee respecting the implementation-defined order of names, including an additional token containing the names of the parameters to deal with the case of dynamic and unknown calls.  
  - The callee will perform the various parameter name and type hint checks, detailed below, and populate default values as appropriate. A runtime exception will be thrown if a required name is missing or an argument name not matching a parameter is provided.  
- Type hints and behavior named parameters will be consistent with positional parameters except for the additional name checks.

#### Order of argument evaluation

Arguments will be evaluated left to right (based on call site). Specifically, for a callsite `foo(b=expr_b(), a=expr_a())` , `expr_b()` would be evaluated first even if the implementation-defined ordering would pass `a` before `b` to the callee.

Why:

- Likely best DevX (G1), users have an intuition of evaluation being left-to-right. Additionally, every other language we’ve checked evaluates args-for-named-params in left-to-right order (except OCaml, where order is not specified). See column D13 in the Supplement: Named Parameters Prior Art and Design Space section below.  
- HackC doesn’t have access to decls, so ordering based on the parameter order in the declaration is not really on the table by [C2](?tab=t.0#bookmark=id.cifcypqlf7c7) (feasibility constraint)

### Callee-side runtime checks

#### Type checks

On the caller side, type checks for named parameters will be performed analogously to positional params. Type hints will be checked in the order of (1) positional parameters, (2) named parameters and (3) all reified types.

Type hint checks for positional and named parameters will be consistent in the checks performed, but for named parameters, the checks will *not* necessarily be in the user-declared order. Type hint error messages will be issued in an order consistent regardless of which order they were declared in.

#### Callsite arity & name checks

HHVM behavior for positional parameters is unchanged:

- HHVM will produce a `RuntimeException` when a required positional parameter is not provided.  
- HHVM will not produce an exception for extra parameters passed positionally. This is arguably a mistake, but we're not changing it here.

HHVM behavior for named parameters:

- `RuntimeException` for not providing a named argument for a non-optional named parameter  
  - Why:  
    - by G2 (safety goal). If we don’t do this check, it’ll be behavior changing for developers to add new named parameters.  
    - by [C3](?tab=t.0#bookmark=id.fcysferp7pxg) (avoid prematurely limiting the design space). If we don’t do this check, it will be harder for us to add shape-splatting for named parameters in future.  
- `RuntimeException` for providing a named argument that doesn't correspond to any named parameter.  
  - Why:  
    - by G2 (safety goal) we want it to be safe for API designers to add new named parameters in the future without breaking existing code  
    - by [C3](?tab=t.0#bookmark=id.fcysferp7pxg) (avoid prematurely limiting design space) we want to allow for potentially adding named variadics in future (on analogy with positional variadics, may resemble Python \*\*kwargs).

#### Hierarchy checks

We propose introducing override compatibility checks in the runtime. If a class attempts to override a method with a new method that doesn't provide all named parameters or overrides a named parameter with-default into one without, HHVM will throw a RuntimeError.

### Named Parameters in Type Structures

Reified types for functions containing named parameters will include the name of the parameters in the type structure. Named parameters for function types in type structures will be laid out after all positional parameters in an implementation-defined order, regardless of the declaration order, and the name will be retained in the type structure.

### \_\_Memoize, \_\_MemoizeLSB

Functions with named parameters will be memoizable. Memoization keys will depend on the set of parameter names, but will not be affected by the declaration ordering. Changing the order of named parameters in the declaration won’t change the memoization key.

### hh

* Named parameters are included in types.

See also [example above](?tab=t.0#bookmark=id.n58g1kbhzq4l).

*Note for Hack team: named parameters should be explainable in terms of a [purely-theoretical-but-not-actual rewrite to code that passes shapes](https://fburl.com/gdoc/69roa5fa), for migratability and language feature composability.* See also https://github.com/mheiber/lapes for a formalization.

Function subtyping rules follow from the rewriting semantics, an example is:

```java
   (function(arraykey, named arraykey $b, named optional arraykey $c))
<: (function(int    ,  named int      $b, named optional int      $c))
```

In addition to checking types:

- hh errors when a named argument is missing (when there is no named argument at the call site corresponding to a named parameter in the callee)  
- hh errors on extra named arguments (named arguments that don’t have a corresponding named parameter)  
- hh errors on incorrect overrides  
  - Note: names of named parameters are part of function types

In addition, `hh` will reflect the [order of evaluation of arguments](?tab=t.0#bookmark=id.bqam4o1hu2hi) for:

- type refinement (such as refining from ?int to int)  
- type invalidation (due to side effects)

## Prior Art and More Design Justification {#prior-art-and-more-design-justification}

See the Supplement: Named Parameters Prior Art and Design Space section below.

## Potential future work {#potential-future-work}

See the Supplement: Potential Future Work section below.

## Supplement: Runtime Design

Main HIP: See the Named Parameters: HIP section above.

#### Runtime constraints

We plan on named parameter semantics to be explainable via elaboration-into-shapes. i.e. 

```
// Declaration
foo(named int $x, named int $y = 2) { ... }

// Can be thought of as
foo(shape(‘x’ => int, ‘y’ => int) $s) { $x = $s[‘x’]; $y = $s[‘y’]; … }

// Use
foo(x:1, y:2);
// Elaborated to
foo(shape('x' => 1, 'y' => 2));
// Reordering is OK
foo(y:2, x:1);
```

This elaboration into shape has some relevant consequences for us:

* In general, we won’t know the position of a named parameter but we need to be able to match it to the right param  
* Positional parameters are treated distinctly from named parameters, i.e. named params are defined by their name rather than their position, and should not interact with positional parameters. As an example, it should be possible to interleave named parameters anywhere in the declaration without affecting how positional parameters or variadic parameters are laid out.  
* We can’t rely on a consistent order for things like defaults in the general calling convention.

We have some additional checks we want to have for future-proofing:

* HHVM should throw a RuntimeException if an unexpected parameter is passed in.  
* HHVM should throw a RuntimeException for a missing named parameter without a default.  
* HHVM should throw a RuntimeException when overriding a base method with incompatible named parameters (missing a named parameter that exists in the base method, or lacking a default value for a named parameter that has a default in the base).

#### Representation changes

Functions have an existing shared ParamInfo object that’s already 88 bytes in non-lowptr: [$REPO//hphp/runtime/vm/func.h]($REPO//hphp/runtime/vm/func.h)

I propose adding a new “Named” flag to parameters (still fits in the uint8\_t) and adding an extra `LowStringPtrOrId` field for the parameter name.

#### Calling convention with named parameters

See [https://fburl.com/excalidraw/f2l4khnn](https://fburl.com/excalidraw/f2l4khnn) for details.

#### Inout Named Parameters

We propose banning inout named parameters for the MVP. The problem they pose from a runtime perspective becomes clear when considering how regular inout parameters are handled:

For existing inout params, the caller specifies a count of inout parameters when calling, and creates extra cells for the right number of inout values being returned. The callee will `RetM` with the appropriate number of extra return values.

For named inout params, we have two choices: 

1. We could ignore the names, and rely on the consistent runtime ordering observed elsewhere (i.e. if you have `inout $a` and `inout $b`, have a convention of always ordering the cell for `$a`’s value before `$b`’s cell), and throw a RuntimeException if the number of cells returned don’t match the cells pushed by the caller. This would be more performant, but have a safety downside: If a dynamic or unknown call has a separate set of names for the inout named parameters (with matching count), we’d be silently propagating incorrect values.  
2. We could validate the returned names matching the inout names on the callee side. One potential design here would involve the caller placing a C-style array with the inout names before the inout cells (in addition to the set of all parameters), and the callee validating that structure in the prologue, and use the design of (a) for the rest of the checks. This would be a significant tax on calls with inout params, but that may be acceptable depending on the frequency of calls.

#### Memoize

Memoization will be handled transparently.

#### Initializers

We will initially not support initializers. There’s a couple options on how to support it moving forward:

1. We can try directly inlining the bytecode for initializers/call the 86cinit functions in prologues.  
2. We can have a special bytecode section that keeps the bytecode needed to initialize each constant, have the prologue jump to those.

#### Splatting

We will have a separate unique stub for splatting.

## Supplement: Potential Future Work

The following could be added after we release the features described in the Named Parameters: HIP section above.

These are a combination of:

* We have some nice-to-haves that can be added after we’ve addressed our main goals. These include likely-desirable features, but we’d like to focus on getting an extensible basic design first and relying on user feedback to prioritize features.  
* Things in the design space we don’t want to prematurely close off but are not considering any time soon

This work includes:

* Tooling:  
  * lint+codemod requiring parameters to be named in cases that would otherwise be footgunny. We can prevent future SEVs (like those listed in Goals above) using a lint that nudges toward “use a named parameter here” at otherwise-footgunny function definitions..  
  * quickfix for making a parameter named  
* [Punning](https://dev.realworldocaml.org/variables-and-functions.html#labeled-arguments) may make the DevX (goal G1) *even better* , but isn’t strictly necessary for better devX and safety, so we defer it.  
* Enable [shape splatting](https://fburl.com/gdoc/tfn2wohw), similar to how we now allow splatting tuples into positional arguments.   
* Named generic parameters: dict\<Key:string,Value:int\>  
  * Not much interaction with the current proposal, best considered in separate proposals imo, if it turns out there are use cases.  
* See also the Supplement: Named Parameters Prior Art and Design Space section below: we indicate more future extensions and their advantages and disadvantages.

## Supplement: Named Parameters Prior Art and Design Space

### Column Key

- **D1**: When a parameter is named, giving the name at call sites is required or optional?
- **D2**: A child class can override a method and provide different names for its arguments
- **D2.1**: Type-checker checks by (note: subtle due to overridden methods)
- **D2.2**: Runtime checks (note: subtle due to overridden methods)
- **D3**: Can reorder named arguments at call site
- **D4**: Can define internal parameter name distinct from external parameter name
- **D5**: All parameters are "named" by default
- **D6**: Method ptr type includes param names for named params AND named params allowed in lambdas
- **D7**: Punning
- **D8**: Splatting
- **D9**: Parameter name available via facts API
- **D10**: Syntax
- **D11**: Restrictions on order of params
- **D12**: Named parameter version of variadics (python kwargs)
- **D12b**: Inout parameters
- **D13**: Evaluation order for arguments corresponding to named parameters

| Language | D1 | D2 | D2.1 | D2.2 | D3 | D4 | D5 | D6 | D7 | D8 | D9 | D10 | D11 | D12 | D12b | D13 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| disadvantages |  | - ergonomics (complicated, potentially unintuitive)<br>- may lead to unsafety (unless we manage with restrictions) |  |  | (1) leads to inconsistency between call sites. (2) May make it less obvious to AIs and people how to use the function (3) reduces correspondence between the call site and the definition (which, fwiw, I find convenient and intuitive) | - noisy, verbose, and (imo) hard to read and remember<br>- LLMs seem to have trouble figuring out how to correctly call functions that have separate internal and external parameter names | - requires codemodding the world, especially for overrides that change parameter names vs the parent<br>- moves the language away from one way to do things |  |  | Multiple overlapping splats and potential splat/explicit naming overlaps are a footgun in other languages that contradicts G1 | n/a |  |  |  |  |  |
| advantages |  | - adoptability for existing code<br>- refactorability<br>- more specialized names may make more sense in subclasses |  | By C3 (avoid prematurely restricting design space): adding checks before a feature is adopted is way easier than adding after. We can always loosen restrictions later if we need to.<br><br>By C1 (DevX): HHVM failing fast rather than doing something weird imo helps users pinpoint problems faster. | - Andrew Kennedy has pointed out that it would be nice to explain the semantics in terms of shapes (even though we won't implement that way). Advantages I see is that such a "desugaring semantics" pushes toward consistency, make it more likely that we are set up well for splatting to work sensibly, and will make it more likely that we can accomodate existing use cases for shape-passing (providing a better alternative).<br>- Given that we include names for named parameters in function types, allowing reordering makes it more things compatible. It could be annoying and wasteful to have to write adaptor functions to convert from a function `(named int x, named int y)` to (named int y, named int x)` or to duplicate interfaces. | Note: It's always possible to simulate this capability using local variables.<br>- can help devs avoid lints for unused parameters in function definitions. The workaround on the current proposal is `function foo(named int $x): void { $_ = $x; }`. <br>- can make it easier to support method overriding s.t. names have to match: we could make it so only the external name needs to match.<br>- Gives a way to access things in an outer scope without changing the outer scope. Example with internal-vs-external-names P1881417913 and a workaround: P1881416688 | Enables callers to opt in to more safety for parameters not explicitly marked as named |  | - reduces boilerplate, sometimes making Hack code easier to write and read | Makes taming larger signatures easier, helps G2 | Makes it easier (possible?) for codemods to generate correct code |  |  |  | byG1 (DevX for AI and for humans):<br>in particular it seems to be easier for AI to generate code when features compose and stuff just works |  |
| reversible/relaxable decision | Name is required | No | No (would be a big change) | No (would be a big change) | Yes | No | No | Yes | No | No | Yes, helps with G2 |  | Yes | Yes | ? |  |
| our recommendation. | Yes, by G2. | No for MVP, by G1, C2, maybe G2 | static type. Which will match the runtime type, due to the decision in previous column (no overriding and changing name).<br><br>hh will reject:<br>- not providing a named argument for a non-optional named parameter<br>- providing a named argument that doesn't correspond to any named parameter<br>- overriding a method incorrectly (remember that the names of named parameters are included in function types) | by method declared in runtime class (following the same rules of dynamic dispatch in Hack. Note that this is distinct from languages like Scala where *static class* determines which names are used for named parameters).<br><br>HHVM will reject:<br>- not providing a named argument for a non-optional named parameter<br>- providing a named argument that doesn't correspond to any named parameter<br>- overriding a method incorrectly (remember that the names of named parameters are included in function types) | Yes | No for MVP, by G1. In experiments with LLMs, they were unable to infer how to call a function correctly when it had different internal-vs-external params (and, fwiw, I often forgot the syntax for separating internal-vs-external names in OCaml and Objective-C) | No for MVP by C1 and C2.<br>Unlikely for follow-on work, since it would be confusing to have both named-or-positional args and named args. Python has both kinds (actually, at least three kinds) but in a way that is unsafe | Yes, needed for typed mocking. We want to support typed mocking by G2 (safety) even though this makes implementation harder (tension with C1).<br>See Named Parameters: Goals and Constraints | No for MVP, not needed for G1 or G2 and in slight tension with C1 (C1 implies codemoddability, but punning introduces a minor+mitigatable refactoring hazard) | No for MVP, by feasibility constraint C2 | No for MVP, by feasibility constraint C2 saving for follow-on | provisional syntax:<br><br>```<br>function foo(named int $x): void {}  <br>...<br>foo(x:$x) // PHP syntax<br>```<br><br>Syntax chosen because LLMs predicted the call syntax correctly given the decl syntax and an LLM recommended this `named` keyword solution. | intuition: positional before named<br><br>restriction:<br>positional+required, followed by positional+optional, positional+variadic, followed by named<br><br>Note: we need to relax the current rule that variadic has to come last<br><br>Allowed:<br><br>function foo(int $a, int $b = 1, named int $c, named int $d = 1)<br><br>Disallowed<br>function foo(int $a, int $b = 1, named int $c = 1, named int $d)<br>function foo(int $b = 1, named int $c = 1, named int $d, int $a)<br>.....<br><br>Allowed but counterintuitive:<br><br>function foo(named int $x = 0, named int $y = 1) {}<br>function bar() { foo(y:2); }<br><br>Why:<br><br>By C3 we do not want to block future features. This seems to me to leave things maximally open, including enabling reordering arguments (D3), maybe allowing named-args-everywhere, fast-calling-convention under the hood, etc. | No for MVP by feasibility constraint C2.<br><br>Two potential follow-ons:<br>- enable splatting shapes<br>   - Note: for consistency and to avoid bad incentives, we would probably then also allow reordering of named arguments, since shape types do not specify order<br>   - Note: unsure of performance implications here<br>- enable splatting named tuples<br>   - Note: would require named tuples to exist in Hack<br><br>Use cases may include Ents needs. Currently they use splatting along with enum classes and variadics to avoid code bloat. | Will support if there's an efficient way to do so in the runtime |  |
| Scala <br><br>Scala spec<br>Scala example 1 | Optional | Yes | Parameter name in the class given by the static type example.<br><br>In Scala, since overriding methods can have default values, arguments at the same position can match with distinct parameters (by position) when overriding (example). Seems like a safety hazard. We could address the hazard by not allowing such ambiguous-looking cases, but this does require propagating a lot of information. A way of partially addressing is to disallow permutation. | None (90% sure) | Yes, with restrictions (D11) | No | Yes | ? | ? |  |  |  |  |  |  | left-to-right by call site: https://scastie.scala-lang.org/sM75rKoBQ3eEno3wLQY32Q |
| Kotlin <br><br>Kotlin Spec<br>Kotlin example 1 | Optional | Yes | Parameter name in the class given by the static type: example<br><br>In Kotlin, an overriding method cannot have default arguments (example). This avoids ambiguities but is counter to how Hack works and could hinder adoptability. | None (90% sure) | Yes, with restrictions (D11) | No | Yes | ? | ? |  |  |  |  |  |  | left-to-right by call site: https://pl.kotl.in/4bCPiojcz |
| Swift<br><br>Swift reference<br>Swift example | Required | n/a, see next column | n/a, since parameter name is part of the method name. Also: Can get a compiler error by using ‘override’ keyword in a function definition with the same signature except for parameter name. Also Swift is Yes for D4 (internal-vs-external names) | n/a, since parameter name is part of the method name | No<br><br>example | Yes | Yes | ? | ? |  | n/a |  |  |  |  | left-to-right by call site |
| C#<br>C# spec<br>C# example | Optional | Yes | Name in the overridden method (even if the static type is from the parent class) | ? | Yes, with restrictions (D11) | No | Yes? | ? | ? |  | n/a |  | TODO |  |  | left-to-right by call site |
| Python (mypy), but note that mypy probably violates the spec<br><br>Canonical references for typing:<br>- Typing spec<br>- Typing conformance suite | Optional unless defined as “keyword-only” | Yes | By static type (unsound!) | By runtime type | Yes, with restrictions (D11) | No | Yes, unless defined as positional-only | Yes, optionally (80% sure) | No |  | n/a |  | TODO | Yes<br><br>def foo(a: int, b: int): ...<br><br>kwargs = {'a': 1, 'b': 2}<br>foo(**kwargs) |  | left-to-right by call site |
| Python (pyre) |  |  |  |  |  |  |  |  |  |  | n/a |  |  | same as cell above |  | same as cell above |
| Python (pyright) |  |  |  |  |  |  |  |  |  |  | n/a |  |  | same as cell above |  | same as cell above |
| Ruby<br><br>Ruby docs<br>Ruby example | Required | Yes | n/a | By runtime type | Yes, with restrictions (D11) | No | Yes | n/a | No |  | n/a |  | TODO |  |  |  |
| Ocaml<br><br>OCaml manual<br>OCaml example<br>OCaml syntax cheat sheet | Required (when setting the warning to be an error, which in practice I think most professional codebases do) | No | n/a: overridden method must have same names for named parameters | n/a, not checked at runtime (95% sure) | Yes, with restrictions (D11) | Yes<br><br>Example:<br><br>let add_1 ~x:y = y + 100<br><br> - external name is `x`<br> - internal name is `y` | No | Yes | Yes |  | n/a |  | TODO |  |  | unspecified |

---

### Hphp Hack Doc HIPs Nameof (`hphp/hack/doc/HIPs/nameof.md`)

# HIP: `nameof`

## Summary

A new way to get class names as strings: `nameof C`.

> NOTE: C is a stand-in for an `Aast.class_id`, excluding `Aast.CIexpr`.

## Feature motivation

This feature is meant to replace the use of `C::class` expressions in
string positions that trigger an implicit cast, so the implicit behavior can be
eliminated. Typing mimics `C::class` to maintain types during migration,
while compilation mimics `(string)C::class` to model existing implicit string coercion.

The feature also supports the migration of places where package boundaries
are violated, so that `C::class` can be changed to return a validated class
pointer. We will then ban and eliminate implicit and explicit `(string)`
conversion in the runtime.

## Specification

The expressions valid on the left hand side of `C::class` are
1. Classes
2. Interfaces
3. Traits
4. Enums / enum classes
5. `static`, `self`, and `parent`
6. Type aliases
7. Reified generics in scope

`nameof` supports 1-6 as targets. The confusion around 7 was significant enough
to where we've decided to exclude it. Other top-level identifiers
are similarly unsupported by nameof at the moment, such as `nameof my_function`,
`nameof MY_CONST` and `nameof my.module`, and Hack will error as though they
are missing (or illegal in the case of `my.module`) class names.

Let's proceed by example. Notation is `id: type = bytecode`.

### Class
```
class B extends A {
  public function f(): void {
    $b = nameof B; // $b: classname<B> = String "B"
    $s = nameof self; // $s: classname<B> = String "B"
    $p = nameof parent; // $b: classname<A> = String "A"
    $stat = nameof static; // $stat: classname<this> = LateBoundCls; ClassName
  }
}
```

### Parent undefined
```
class B {
  public function f(): void {
    $b = nameof parent; // $b: classname<nothing> (Typing[4076]) = ParentCls; ClassName
  }
}
```

### Interface
```
interface I extends I2 {
  const string S = nameof I; // S = """l:1:\"I\";""";
  const string T = nameof self; // T = """l:1:\"I\";""";

  // matches parent::class and static::class behavior
  const string U = nameof parent; // Parsing[1002] invalid constexpr
  const string V = nameof static; // Parsing[1002] invalid constexpr
}
```

### Interface require extends (same as previous)
```
interface I {
  require extends I2;

  const string S = nameof I; // S = """l:1:\"I\";""";
  const string T = nameof self; // T = """l:1:\"I\";""";
  const string U = nameof parent; // Parsing[1002] invalid constexpr
  const string V = nameof static; // Parsing[1002] invalid constexpr
}
```

### Trait
```
class A {}
trait T {
  require extends A;

  public function f(): void {
    // Types are sketchy but match T::class and self::class for migration
    $t = nameof T; // $t: classname<T> = String "T"
    $s = nameof self; // $s: classname<T> = SelfCls; ClassName

    $p = nameof parent; // $p: classname<A> = ParentCls; ClassName
    $stat = nameof static; // $stat: classname<this> = LateBoundCls; ClassName
  }
}
```

### Trait no parent
```
trait T {
  public function f(): void {
    $p = nameof parent; // $p: classname<nothing> (Typing[4074]) = ParentCls; ClassName
  }
}
```

### Type alias
```
type T = int;
newtype U = string; // assume in different file

function f(): void {
  $t = nameof T; // $t: typename<int> = String "T";
  $u = nameof U; // $u: typename<U> = String "U";
}
```

## User Experience

`nameof` is a new keyword in VSCode's grammar, highlighted in blue with default
settings. Class names are autocompleted as they would be for `new`, without the
restriction on concrete classes.

## Unsupported

### Reified generics
```
class C {}
function f<reify T as C>(): void {
  $t = nameof T; // $x: classname<T> (Parsing[1002]) = <bytecode_arg>
}
class R<reify Tr as C> {
  public function m<reify Tm as C>(): void {
    $t = nameof Tr; // $x: classname<Tr> (Parsing[1002]) = <bytecode_prop>
    $t = nameof Tm; // $x: classname<Tm> (Parsing[1002]) = <bytecode_arg>
  }
}

/**
 * This specifies the current behavior of T::class when T is a reified generic,
 * which is matched by the nameof implementation. The main difference is that
 * function-bound reified generics are parameters and class-bound ones are
 * properties. Since they're type structures, we just query the 'classname'
 * field and throw if it doesn't exist e.g. type structure for `int`. The
 * typechecker raises an error if the generic is not bounded by a class type.
 *
 * <bytecode_arg>
 *
 * BaseL $0ReifiedGenerics Warn Any
 * QueryM 0 CGet EI:0 Any
 * BaseC 0 Warn
 * QueryM 1 CGet ET:"classname" Any
 *
 * <bytecode_prop>
 *
 * CheckThis
 * BaseH
 * Dim Warn PT:"86reified_prop" Any
 * QueryM 0 CGet EI:0 Any
 * BaseC 0 Warn
 * QueryM 1 CGet ET:"classname" Any
 */
```

---

### Hphp Hack Doc HIPs Needs Concrete (`hphp/hack/doc/HIPs/needs_concrete.md`)

Author: Max Heiber
Status: accepted and implemented

# HIP: `__NeedsConcrete` attribute

## Real-World Example

> This has been modified for open-source audiences. See the [original document](https://fburl.com/gdoc/40aqaxob) for the original code

There are tons of methods you can call in WWW today that will immediately blow up due to weaknesses in `abstract` checking. For example, a call to `AssocBackedEntQuery::query()` straightforwardly fails with "cannot instantiate abstract class". Code (abridged):

```hack
abstract class AssocQuery
  extends Fetcher
  implements QueryHasTime, QueryBackedByAssoc {

  protected function cloneQuery(): this {
        return self::query();
   }
}

trait Fetcher {
  final public static function query(): this {
    return new static();
  }
```

With this proposal, we prevent calling `AssocQuery::query` and `cloneQuery` directly via the `__NeedsConcrete` method attribute: the methods are then safe with no other changes to WWW.

## **Motivation**

Errors from attempting to access abstract methods, constructors, and constants account for one of the largest known categories of runtime errors preventable with better type-checking:

**SEV prevention:** **[redacted](https://fburl.com/hack_needs_concrete)**

**Dev Efficiency:** There are **[redacted](https://fburl.com/hack_needs_concrete)** static methods in WWW that will fail if called ([D71133334](https://fburl.com/diff/t0lzpuii)). Users work around this today through fixing runtime errors and maintaining complicated invariants. **We can shift these errors left.**

\* Both an over and under-estimate: this proposal doesn't make classname or class pointers any safer, that is saved for potential follow-on work un-blocked with this HIP. Under-counting comes from gaps in our ability to attribute.

## **Solution**

## Solution: High-level explanation

**Definitions**:

* **`abstract`-safety**: `hh` is abstract-safe if it prevents attempts at runtime to call a constructor that doesn't exist, call an unimplemented abstract method, or access an unimplemented abstract constant.
* A **concrete class** is a class that is either non-abstract or is final+not-`__ConsistentConstruct`. The intuition behind this definition is "in a concrete class there are no unimplemented members".

**Invariant**: In a `__NeedsConcrete` method, we know that `static` points to a concrete class.

The checks introduced in this HIP all follow from **leveraging** or **preserving**  the invariant to get **more `abstract`- safety**. I suspect with this proposal we'll be abstract-safe modulo dynamism, modulo classname\<T\>/class\<T\>, and existing type holes.

Here's one of the ways we **leverage** the invariant:

- In a `__NeedsConcrete` method, `static::abstract_method()` is allowed

Here's one of the ways we **preserve** the invariant:

- ### If method `Abs::m` is marked `__NeedsConcrete`, and `Abs` is not a concrete class, then `Abs::m()` is not allowed

### Why final, non-\_\_ConsistentConstruct classes are considered concrete

Because there's nothing wrong with code like the following, which is really common in WWW (abridged from [real code](https://fburl.com/code/y1lfyhnq)). There are **[redacted](https://fburl.com/hack_needs_concrete)** cases in WWW ([D71548214](https://fburl.com/diff/wnrgpofg)) where treating final+not-`__ConsistentConstruct` classes as concrete prevents spurious hh errors:

```

abstract class GraphQLClientBaseArtifactData
  implements IGraphQLClientArtifactData {

  private function __construct()[] {}

  public static function addToOutput(
    GraphQLQueryClientContext $context,
    inout GraphQLHackClientSandboxCacheOutput $output,
  )[write_props]: void {
    static::addToOutputImpl($context, inout $output);
  }
}

abstract final class GraphQLClientBloksQueryInputArtifactData
  extends GraphQLClientBaseArtifactData
  implements IGraphQLClientArtifactData {}
```

*See also why \_\_ConsistentConstruct matters, below*

### Solution By Example

> These examples are all adapted from the test suites for [D71745959](https://fburl.com/diff/6fh9z98q) and [D69055023](https://fburl.com/diff/gjw7t67e)
> See the next section for an algorithm sketch and link to full algo

We error when there's an access of an abstract member via `static` and `static` isn't known to point to a concrete class:

```java
<?hh

abstract class A {
  public static function m1(): void {
    // hh error: unsafe because `static` might not refer to a concrete class.
    static::m2();
  }
  public static abstract function m2(): void;
}

```

The easiest fix in such cases is to add `<<__NeedsConcrete>>`:

```java
<?hh

abstract class A {
  public static function m1(): void {
    // hh error: unsafe because `static` might not refer to a concrete class.
    self::m2();
  }

  <<__NeedsConcrete>>
  public static function m2(): void {
    static::m3(); // ok: the attribute ensures `static` refers to a concrete class
  }

  public static abstract function m3(): void;
}

```

The following shows how we check calls to \_\_NeedsConcrete methods where the receiver is a class ID. Nice and simple, the receiver must be concrete:

```java
<?hh

abstract class A {
  <<__NeedsConcrete>>
  public static function m1(): void {
    static::m2(); // ok: the attribute ensures `static` refers to a concrete class
  }

  public static abstract function m2(): void;
}

<<__EntryPoint>>
function main(): void {
  A::m1(); // hh error and runtime error
}

```

Note that the runtime behavior is for `parent` to forward a value for `static`. The following example shows both what happens at runtime and my first stab at an error message:

*I admit that the error message is hard to understand, but it's merely making explicit what was previously hidden complexity*

```java
<?hh

abstract class C1 {
  <<__NeedsConcrete>>
  public static function m2(): void {
    static::m3();
  }
  public static abstract function m3(): void;

}

abstract class C2 extends C1 {
  public static function m1(): void {
    // Cannot call `C1::m2` (a `<<__NeedsConcrete>>` method) via `parent`. It requires `static` to refer to a concrete class, but `parent` sets `static` to a class that may not be concrete. (Typing[4490])
    parent::m2();
  }
}

class C3 extends C2 {
  public static function m3(): void {
    echo "m3\n";
  }
}

<<__EntryPoint>>
function main(): void {
  C3::m1(); // prints "m3"
  // C2::m1(); // fatal: "cannot call abstract method C1::m3()"
}

```

No errors here:

```java
<?hh

abstract class A {
  <<__NeedsConcrete>>
  public static function m1(): void {
    static::m2(); // ok
  }
  <<__NeedsConcrete>>
  public static function m2(): void {}
}

```

`abstract final` classes that are non-\_\_ConsistentConstruct are common in WWW. We treat such cases as concrete, so there are no errors here:

```java
<?hh

abstract class C1 {
  abstract const int X;
  <<__NeedsConcrete>>
  public static function nc(): void {}

  public static abstract function abs(): void;

}

// C2 is concrete because it is final+non-__ConsistentConstruct
abstract final class C2 extends C1 {
  const int X = 3;
  public static function m(): void {
    // all the following are ok because C2 is concrete
    static::nc();
    static::abs();
    static::abs<>;
    static::X;
  }

  public static function abs(): void {}
}

```

Here's why \_\_ConsistentConstruct matters. C2::m() below would fail at runtime, since it would try to instantiate an abstract class:

```java
<?hh

<<__ConsistentConstruct>>
abstract class C1 {
  abstract const int X;
  <<__NeedsConcrete>>
  public static function nc(): void {
    new static();
  }

  public static abstract function abs(): void;

}

// C2 is *not* concrete even though it is `abstract final`.
// C2 has an unimplemented member (the constructor)
abstract final class C2 extends C1 {
  const int X = 3;
  public static function m(): void {
    static::nc(); // runtime error - because it calls `new` and the constructor for C2 doesn't exist
  }

  public static function abs(): void {}
}

```

As always, if a method is \_\_NeedsConcrete then `static` refers to a concrete class:

```java
<?hh


<<__ConsistentConstruct>>
abstract class A {
  abstract const int X;
  public static abstract function abs(): void;

  <<__NeedsConcrete>>
  public static function ok(): void {
    static::X; // ok
    static::abs<>; // ok
    new static(); // ok
  }
}

```

Hierarchy checks, similar to the rule that an abstract method cannot override a non-abstract method:

```java
<?hh

abstract class A {
  public static function nc(): void {}
  public abstract static function abs(): void;
}

abstract class B extends A {
// hh error: Cannot declare this method as __NeedsConcrete, since it overrides a non-__NeedsConcrete method (Typing[4487])
  <<__NeedsConcrete>>
  public static function nc(): void {
    static::abs(); // HHVM error
  }
  public static abstract function abs(): void;
}

function example(classname<A> $klass): void {
  $klass::nc();
}

<<__EntryPoint>>
function main(): void {
  example(B::class);
}

```

## Implementation

Here's a sketch of how we check `static::foo() or MyClass::foo():`

```

let containing_class_is_final_and_concrete cls =
      final class && (non_abstract cls || non_consistent_construct cls)

let static_points_to_concrete_class env =
      containing_method_has_needs_concrete_attribute
      || not env.in_static_method
      || containing_class_is_final_and_concrete env.containing_class

let check_static_call receiver env = match receiver with
 | CIstatic when not (static_points_to_concrete_class env) ->
          if callee_has_needs_concrete_attribute || callee_is_abstract then
           error ()
  | CI class_name when callee_has_needs_concrete_attribute && (not class_is_concrete class_name) -> error ()
  | _ -> ....
```

The [implementation (D71745959)](https://fburl.com/diff/6fh9z98q) checks accesses of constructors, constants, and methods via super, self, parent, and class ID, but the rules are obvious once it's clear what the invariants are.

##  Migration Plan

* Codemod to add the `__NeedsConcrete` attribute to **[redacted](https://fburl.com/hack_needs_concrete)** methods in WWW:
  * Fixpoint until nothing changes:
    * If a static method `m` of a non-concrete class that is not \_\_NeedsConcrete accesses an abstract or \_\_NeedsConcrete method via static/self/parent, add the \_\_NeedsConcrete attribute to `m`
    * If `NeedsConcrete` method `C2::m` overrides a non-`\_\_NeedsConcrete` method `C1::m`, add the `__NeedsConcrete` attribute to `C1::m`
  * Note: the logic will be very similar to that implemented in the analysis: [D71133334](https://fburl.com/diff/t0lzpuii)
* fixme or fix the small number of new errors in WWW

## Trade-offs and Stats about WWW

Benefits:

* Safer code
* Easier to for users to write+maintain code
  * My understanding is that today's WWW is the result of **great effort** to make code work well as much as possible
    * complex invariants around hierarchies and concreteness
    * fixing issues discovered in production
    * fixing issues discovered in testing
  * We reduce future WWW development and maintenance cost through shifting left
* Un-block further Safe Abstract work for more safety

Cost:

* `__NeedsConcrete` needs to be learned by users
  * My take: it's better to have Hack track concreteness for you than to manage complex invariants around `self` and `static` without tool assistance
* Implementation complexity: not much, fairly orthogonal [D71745959](https://fburl.com/diff/6fh9z98q)
* Changes to existing code:
  * Add \_\_NeedsConcrete attribute in **[redacted](https://fburl.com/hack_needs_concrete)** places ([source](https://fburl.com/diff/t0lzpuii)). Note that this is easily reversible (just delete the attribute!)
  * introduce **[redacted](https://fburl.com/hack_needs_concrete)** new errors at call sites in WWW ([source](https://fburl.com/diff/wnrgpofg)). Even if we're off by an order of magnitude that's still not many fixmes/refactors.

## Appendix

### Potential Follow-on Work: class pointers and class names

`class<T>` and `classname<T>` are still not abstract-safe, but may be addressed in a future proposal.

We break the problem into pieces

* This proposal *only* adds `<<__NeedsConcrete>>`
* No new checking for `class<T>` or `classname<T>` (yet)

Why:

* Easier migration than moving WWW to full Safe Abstract checking at once
* Incremental value and learnings
* Ordering: `__NeedsConcrete` would be needed for `class<T>`/`classname<T>` checking anyway

### Potential Follow-on Work: no longer need to pass value for `static` through `self` and `parent`

Python has inheritance for static methods, similar to Hack, but more clearly separates which classes get to access the current runtime method.:

```py
class C:
    @classmethod
    def m1(cls):
        # `cls` points to the current class
        # like `static` in Hack static methods or `this` in Flow static methods
        cls.m2()
        ...

    @staticmethod
    def m2():
        # No access to "current runtime class",
        # is more like a Java static method
        ...
```

> Jan Oravec and Vassil Mladenov have iirc mentioned wanting to get Hack closer to Python's explicitness and clarity w.r.t. late static bindings. And Mistral Contrastin has pointed out that explicitly passing the class at least provides a helpful intuition for how PHP/Hack Late Static bindings work.

### Why not fine-grained Method Requirements

A source of incompleteness is:

- Method C::`lsb` is \_\_NeedsConcrete
- Child1 inherits `lsb` and does not override it
- All the abstract things accessed in `lsb` are concrete in `Child1`
- So we have a method (`Child1::lsb`) which is `__NeedsConcrete` but doesn't need to be:

```
<hh
abstract class C {
    public abstract static function m(): void;
    // C::lsb is the common ancestor of weird hierarchy members Child1::lsb and Child2::lsb
    // The --needs-concrete analysis will show that C::lsb should be <<__NeedsConcrete>>
    // because ::lsb() will fail if the LHS is an abstract class that does not have an implementation of `m`
    public static function lsb(): void {
        static::m();
    }
}

abstract class Child1 extends C {
    public static function m(): void {}
    // (unwritten) member Child1::lsb is a weird_hierarchy_member
    // because it is <<__NeedsConcrete>> but it doesn't actually need a concrete receiver
}

abstract class Child2 extends C {
    public static function m(): void {}
    // (unwritten) member Child2::lsb is a weird_hierarchy_member
    // because it is <<__NeedsConcrete>> but it doesn't actually need a concrete receiver
}


function example(): void {
    // incompleteness. If our checking were more precise, we would know that
    // Child1::lsb() is safe at runtime. However, Safe_abstract_check will
    // produce an error here (assuming C::lsb is marked as <<__NeedsConcrete>>)
    Child1::lsb();
}
```

We could address the incompleteness by using a more fine-grained notion than just concrete/non-concrete.

However, the incompleteness does not seem so bad in practice. The analysis in [D71548214](https://fburl.com/diff/wnrgpofg) found **[redacted](https://fburl.com/hack_needs_concrete)** such cases of this in WWW. Even if we're off by an order of magnitude, that's still not many.

### Prior Art from Other Languages

# **Prior Art from Other Languages for type-checking Late Static Bindings**

Supplement to [Proposal: Shift Left `static::` and `classname` errors](https://fburl.com/gdoc/2yjhdsle)

## Python/Pyright:

* Has late static binding (static methods that are inherited where `cls` refers to the runtime rather than lexical class)
* Has a similar soundness hole to Hack's ([GitHub issue I opened](https://github.com/microsoft/pyright/issues/8550))

## Python/Pyre:

* Has late static binding (static methods that are inherited where `cls` refers to the runtime rather than lexical class)
* Doesn't prevent even direct calls to abstract static methods ([example](https://fburl.com/workplace/cvv83f56)), making it easy to get runtime errors.

## TypeScript:

* Has late static binding: `this` in a static method refers to the runtime class, and static methods are inherited.
* Abstract and interface methods cannot be static, so similar problems to Hack's do not arise.

## Flow

* No abstract classes or static interface methods, so similar problems to Hack's do not arise.

## Java, Scala, Kotlin, and C\#:

* Abstract and interface methods cannot be static, so similar problems to Hack do not arise.
* Notes:
  * I tested with C\# 7, newer versions may be different
  * Java afaict does not have LSB: it doesn't seem possible to get a handle to the runtime class as distinct from the lexical class. Not sure about the other languages (but moot point since there's no abstract static)
  * Scala and Kotlin technically do not have static methods, and instead have "companion objects". Afaict, companion objects have no abstract members.
  * Banning abstract static methods seems to be the respectable thing to do, but imo the ship has sailed for WWW. tbgs found **[redacted](https://fburl.com/hack_needs_concrete)** uses of 'abstract.+static' and 'static.+abstract' and that doesn't even cover occurrences in interfaces.


### Prior Art for Hack

The now-partially-outdated-but-perhaps-informative proposal [Proposal: Safe Abstract (formerly known as Shift Left Abstract Static)](https://fburl.com/gdoc/oqpo8udj). That proposal also addresses `classname<T>` and `class<T>` and has [formalization in Alloy and Lean](https://github.com/mheiber/lsbsteak/)

See also the Prior Art section of [Proposal: Safe Abstract (formerly known as Shift Left Abstract Static)](https://fburl.com/gdoc/pjlp4q5c).

---

### Hphp Hack Doc HIPs Ownership And Linearity (`hphp/hack/doc/HIPs/ownership_and_linearity.md`)

Title: Ownership and Linearity Tracking

Start Date: June 26 2020

Status: Postponed

## Summary

A system for tracking and enforcement of ownership for Objects, specifically
via linearity.

## Feature motivation

There are multiple existing or future features for which ownership tracking is
required for soundness. The major usecase of this is the Purity project, which
will have its own HIP in the coming weeks. However, there are other features,
such as const classes and disposables, for which language-level ownership
tracking is necessary for soundness. We expect there to be additional future
features that can make use of this system.

Additional benefits include:
1. HHVM can produce optimizations based on the guarantees described below when utilizing these features.
2. Provability that an API requesting a unique value is actually given one

## User experience

### The Four States of Ownership

There are four states of ownership:
1. Unowned (The default)
2. Owned
3. Borrowed
4. MaybeOwned (temp name, looking for alternatives).

The main differences between each state are the operations allowed on an object
of that state. Ownership is a property of the linearity of an object. Owned and
Borrowed objects are enforceably linear, Unowned objects are not, though they may
be naturally, and MaybeOwned may or may not be forcibly linear.

Described in terms of the operations allowed on them, the states are as follows:
1. Unowned - The default state in hack. May already have aliases
(i.e. be nonlinear). May be freely aliased.
2. Borrowed - Cannot be aliased and is a non-owner of the object. The owner
of the object is a (possibly indirect) caller, so no other code with a reference
to this object can run until this function returns (or further lends the object
to a more deeply nested callee). Because of this restriction, we consider the
object to still be linear in this state. If the caller did not opt in to stricter
tracking aliases may already exist, but the borrowing code will not make new ones.
See the next section for more details
3. Owned - Does not have aliases and cannot be aliased. May be lent to a
(synchronous or immediately awaited async) callee, which preserves the invariant
that at most one runnable frame has a reference. Can transfer ownership and can
release ownership, moving the object into the unowned state.
4. MaybeOwned - May be an object in any of the previous 3 states. This state is
distinct from Borrowed because some features built on ownership tracking (purity
and const classes) need to allow mutations only on linear (i.e. Owned or
Borrowed) values. Functions that only need to read from an object will be more
widely reusable if they receive objects in this state rather than Borrowed.
See the next section for more details

As a Table, where LHS is current state, and Column is whether they have a specific
capability:

|            | Has alias | May alias | May move/disown |
|:----------:|:---------:|:---------:|:---------------:|
| Unowned    |   maybe   |    yes    |       no        |
| Owned      |    no     |    no     |       yes       |
| Borrowed   |   maybe*  |    no     |       no        |
| MaybeOwned |   maybe   |    no     |       no        |

\* This is a choice for the caller. If they opt in to stricter tracking, then
Borrowed cannot have an alias. See next section.

Described in terms of what parameters they may be passed to given that
the parameter is defined to be a specific state:

1. Unowned - Can be passed to Unowned or MaybeOwned. Whether it can pass to
borrowed depends on whether the caller opts in to stricter tracking. See the
next section for more details.
2. Owned - Can be passed to Borrowed or MaybeOwned, or may transfer ownership to Owned.
3. Borrowed - Can be passed to Borrowed or MaybeOwned.
4. MaybeOwned - Can be passed to MaybeOwned.

As a Table, where LHS is current state and Column is whether they can be passed
to a param of that state (or used as the $this in a method invocation,
when otherwise permitted):

|            | Unowned | Owned | Borrowed | MaybeOwned |
|:----------:|:-------:|:-----:|:--------:|:---------:|
| Unowned    |    yes  |   no  |  maybe*  |    yes    |
| Owned      |    no   | moved |   yes    |    yes    |
| Borrowed   |    no   |   no  |   yes    |    yes    |
| MaybeOwned |    no   |   no  |   no     |    yes    |

\* This is a choice for the caller. If they opt in to stricter tracking, then
Borrowed cannot have an alias. See next section.

#### Unowned and Borrowed

The choice of whether or not unowned may flow into borrowed is a subtle one -
one that developers will never have to consider under this proposal. The
usecase for not being able to pass unowned into borrowed is enabling of mutations
on linear objects (when they'd be banned on nonlinear ones).

Consider Rust. They have locally mutable values, which are analogous to our
Owned values, and `mut &`s and `&`s, respectively analogous to our Borrowed and
MaybeOwned. The key is that rust does not have an unowned state - all values are
owned by a single reference. When a system requires linearity to enable
mutability, passing an unowned value into the equivalent to a `mut &` is obviously
unsafe, as that would result in mutations happening to nonlinear values.

Under this proposal, systems that require linearity for the purpose of tracked
mutations (such as Const Classes and Purity), would implicitly disallow passing
unowned values to parameters etc marked as borrowed. Developers would get an
error specifically about this telling them why and they will merely need to
conform to these restrictions, often via use of the MaybeOwned state.

One may note that this is in fact the main reason for the existence of the
otherwise quite similar Borrowed and MaybeOwned state. For systems that don't
require this stricter ruleset, those states are functionally identical (although
still not interchangeable). For the most part, developers should not need to know
or care about this.

#### Concurrent uses of tracked objects

Borrowed, Owned, and MaybeOwned values may not be used multiple times
in the same statement or concurrent block as this creates implicit aliases,
breaking linearity. Further, objects of those states may not be passed to async
functions if they are not immediately awaited.

```
function takes_borrowed(borrowed Foo $b): void {...}
async function gen_takes_borrowed(borrowed Foo $b): Awaitable<void> {...}
function takes_two_borrowed(borrowed Foo $b, borrowed Foo $c): void {...}
function example(owned Foo $x): void {
  takes_borrowed($x); // fine
  takes_two_borrowed($x, $x); // banned
  // this might be fine because takes_borrowed($x) is finished before bar is called, but banned in V1 to be safe
  takes_two_borrowed(takes_borrowed($x), $x);
  takes_two_borrowed($x, takes_borrowed($x)); // as previous
  // as previous. This might be fine because foo($x) is run synchronously, but banned in V1 to be safe
  takes_two_borrowed(takes_borrowed($x), takes_borrowed($x));
  takes_two_borrowed(await gen_takes_borrowed($x), await gen_takes_borrowed($x)); // banned
  gen_takes_borrowed($x); // banned - must await immediately
}
```

For the above, any use of a local counts as a borrow and the same logic applies
for concurrent blocks as these individual statements. Note that invocations like
`foo($x)` cannot be returning `$x` internally as that would require aliasing a
borrowed value.

### Declaring Objects as Tracked

You will note below that there is no way to explicitly state that a value
is unowned. There is no usecase we could determine for which this was a useful
designation.

Note that only one of the following may be used per method object, parameter,
and return value, but multiple may occur in the same declaration header.

#### Parameters

Parameters may be declared to be any of Unowned, Owned, Borrowed, and MaybeOwned

```
public function foo(IFoo $foo): Foo { return new Foo(); }
public function foo(owned IFoo $foo): Foo { return new Foo(); }
public function foo(borrowed IFoo $foo): Foo { return new Foo(); }
public function foo(maybeowned IFoo $foo): Foo { return new Foo(); }
```

#### Return Types

Return values may be declared as either unowned or Owned

```
public function foo(IFoo $foo): Foo { return new Foo(); }
public function foo(IFoo $foo): owned Foo { return new Foo(); }
```

Returning a Borrowed/MaybeOwned value is forbidden, as doing so would create an
alias. Returning an owned value implicitly transfers ownership to the caller.

#### Method Objects

The object on which a method is called may be declared as Unowned, Borrowed, or MaybeOwned

```
public function foo(IFoo $foo): Foo { return new Foo(); }
public borrowed function foo(IFoo $foo): Foo { return new Foo(); }
public maybeowned function foo(IFoo $foo): Foo { return new Foo(); }
```

While it is logically sound for $this to be owned, with ownership transferred to
the method call, this is not permitted due to runtime considerations. There are
major issues with `$this` being available via things like stack traces rather
than just within the method itself. Further, unsetting `$this` is not something
the runtime can support at present.

### Moving Between States

#### Creating Owned Values

When an object is created it must explicitly opt in to the owned state by using
the `own` keyword, e.g. `$x = own new Foo();`. Any object that is not explicitly
created as such is unowned.

#### Transferring Owned Values

For those who have used C++ and Rust, transferring ownership will be somewhat familiar.

Consider the following functions, one that requires to be given an Owned value
and the other that creates one as above:

```
function takes_owned(owned Foo $foo): void {...}
function creates_owned(): void {
  $f = own new Foo();
  // would like to give ownership of $f to `takes_owned`
}
```

Ownership transference is done via the `move` keyword

```
function creates_owned(): void {
  $f = own new Foo();
  takes_owned(move $f);
}
```

In order to maintain linear ownership, after `move`ing a value, the variable
initially containing that value is unset (i.e. KindOfUninit). In the previous
example, attempting to use $f after invoking `takes_owned` would fail.
Additionally, as you would expect, attempting to `move` the same value twice in
the same statement/concurrent block is illegal.

Note that the `move` keyword isn't allowed on the result of an `own`
expression. Rather, it is always implicit when necessary e.g. in `takes_owned(own new Foo());`.

The other location of ownership transfer is at the return site of a function
marked as returning an owned value, as in the following

```
function returns_owned(): owned Foo {
  $f = own new Foo();
  return $f;
}
```

Note that the `move` keyword is not required here, as it is trivially determinable
from the signature of the enclosing function that an ownership transfer is required.

The `own` keyword may be used to receive an owned value from a function marked
as returning an owned value. Additionally, if the expression being returned is
one on which `own` can be used, `own` is also not required, and, in order to
remain consistent, disallowed:


```
function returns_owned(): owned Foo {
  return new Foo();
}
function returns_owned2(): owned Foo {
  return returns_owned();
}

function returns_foo(): owned Foo { return new Foo(); }
function creates_owned_foo(): void {
  $f = own returns_foo();
}
```

Note that it is possible to return an owned value in an async function. In order
to receive that owned value, the caller must immediately `await` and `own` the
result:

```
async function gen_returns_foo(IFoo $foo): owned Awaitable<Foo> { return new Foo(); }
async function gen_creates_owned_foo(): Awaitable<void> {
   $f = own await gen_returns_foo();
}
```

`Awaitable` is the only situation in which the ownership declaration does not apply
directly to the item following it. In this way, it is similar to `__Soft`.

Conditionally `move`ing values is currently disallowed. If, in a future version,
we change the `move`d variable to be null instead of unset, it is possible we
can allow this.

As a reminder, any value that _may_ be `own`ed but isn't is implicitly disowned.

#### Disowning Values

It is sometimes desired to disown a value midway through a function. This is done
via the `disown` keyword.

```
function foo(owned Foo $foo): void {
  // cannot alias $foo here
  $unowned_foo = disown $foo;
  // $unowned_foo is unowned. $foo is unset
}
```

Note that similarly to `move`ing a value, `disown`ing one unsets the
`disown`ed variable. In the same vein, a variable may not be conditionally disowned.

#### Linearly Lending Values

Owned values may be lent to other functions promising to maintain linearity, and
borrowed values may be lent to further functions. This is handled without
requiring additional keywords.

```
class Foo {
  public borrowed function borrow(): void {}
}

function foo(owned Foo $foo): void {
  borrow($foo);
  $foo->borrow();
}

function borrow(borrowed Foo $foo): void {
  borrow_again($foo);
  $foo->borrow();
}

function borrow_again(borrowed Foo $foo): void {}
```

### Additional information

#### Interactions with `null`

`null` is a valid value in any state, as it is not actually an object but rather
the lack of one.

#### Hierarchical requirements

Logically, the subtyping rules for the states can be thought of as follows,
where (|) is subtyping and ~~> is coercion:

```
     MaybeOwned

    /        \

Borrowed <~~ UnOwned

   |

 Owned
```

For simplicity, however, the initial implementation will require that
hierarchies be invariant in their usages of ownership states. It seems likely
that we can enable owned return positions to be subtypes of unmarked return
positions, but the rules for, and usefulness of, subtyping relationships for
parameters becomes less clear.

This includes the usage of the `__Unownable` and `__ReturnsBorrowedThis`
attributes, to be described below.

#### Properties of objects cannot be owned.

At present, properties (including container contents) are always unowned.

It is possible that this may be implementable in a future version.

#### Preservation of Single Ownership Type Per Variable Name

In order to enable performant tracking by HHVM, while retaining a reasonably
simple implementation within the emitter, variables may be used to reference
at most one ownership state. However, a single variable may be used to
reference multiple objects of the same ownership state.

```
class Foo {}
function my_function(
  owned Foo $owned,
  borrowed Foo $borrowed,
  maybeowned Foo $maybe,
  Foo $unowned,
): void {
  $owned = own new Foo(); // fine, still owned
  $disowned = disown $owned; // fine, new disowned variable
  $owned = own new Foo(); // fine, owned again
  $owned = $disowned; // error
  $borrowed = own new Foo(); // error
  $borrowed = $disowned; // error
  $maybe = own new Foo(); // error
  $maybe = $unowned // error;
  $unowned = own new Foo(); // error
  $owned = own new Foo(); // fine, still owned
  $disowned = own new Foo(); // error
  $disowned = new Foo(); // fine, creating implicitly unowned value
  $unowned = new Foo(); // fine, creating implicitly unowned value
  $new_unowned = new Foo(); // fine, creating implicitly unowned value
}
```

#### Capturing values in closures
Locals that contain Owned, Borrowed or MaybeOwned values cannot be captured as
that creates an implicit alias, breaking linearity

```
class Foo {
  public int $foo = 0;
  public borrowed function capturesMutable(): void {
    $x = () ==> { $this; }; // error
  }
  public maybeowned function capturesMaybeMutable(): void {
    $x = () ==> { $this; }; // error
  }
}
function captures_owned(
  owned Foo $obj1
  borrowed Foo $obj2,
  maybeowned Foo $obj3,
): void {
  $x = () ==> {
    // these are all errors
    $obj1;
    $obj2;
    $obj3;
  };
}
```

#### Ownership in Constructors

Constructors pose something of an unique problem for ownership tracking. In
order for tracking to work, one must be allowed to `own` the result of a
constructor call. However, if linearity is broken *within* the constructor, then
that no longer holds true.

The solution landed upon is for constructors to be opt-out for linearity instead
of opt-in. In the standard case, `$this` within a constructor is in the `borrowed`
state. Following the termination of the constructor, the rules apply as stated
above. The opt-out mechanism is via a new `__Unownable` attribute, which
indicates that the result of that constructor may not be `own`ed. Additionally,
hack errors triggered in a non-opted-out constructor will initially suggest
opting out as a last resort after other standard suggestions.

This conclusion resulted from analysis of patterns within the FB codebase.

Of the very large number of constructors, <3% of them would need to be opted out
if no further work was done. However, of that <3%, >95% of them are due to
invoking a helper function on $this which is not accepting of a borrowed value.
Upon inspection of a random sampling of those helper functions, a large
majority of them are trivial to allow accepting borrowed values with no other
changes (except for potentially marking a further function). Further, >4% of the
problematic constructors arise from capturing $this in a closure, which may
become legal in a future update (see Future possibilities section). Thus, only 1%
of the problematic cases are ones which require more serious refactoring. The
simplest solution for those are to convert them to a builder pattern in which the
builder function invokes the constructor and then does any problematic behaviour
following it.

We may choose in the future to remove this method of opting out.

#### Interactions with Memoize

Memoized functions may not return values as owned, as doing so would necessarily
allow creating multiple aliases to the same object. For objects implementing
`IMemoizeParam`, the `getInstanceKey` method must be valid to invoke on the
passed object for whichever state the parameter is marked. (e.g. if the param
is borrowed, `getInstanceKey` must use a Borrowed or MaybeOwned `this`);

Prior to rollout, we will determine whether this is actually necessary or if we
can globally enforce a MaybeOwned `this` for getInstanceKey methods

#### Interactions with inout

An `inout` parameter can neither be Borrowed nor MaybeOwned as these are
tantamount to returning such values, which is banned.

For simplicity, an `inout` parameter may not be `owned`. It is possible that a
future version of this feature will allow this if usecases arise.

#### Interactions with the Mutable Builder Pattern

The mutable builder pattern is one in which a class contains setters that modify
a property and then return $this. This can then be used as a chain to "build up"
the object. This becomes a problem when ownership tracking comes into play, as
if $this is tracked, then the return statement creates an alias.

The problem then becomes how to allow these objects to be built up in the case
where the object is tracked while not diminishing the experience for unowned
objects for general developers.

The solution to this problem for this proposal takes the form of a temporary
Hack. A function can be marked with the magic `__ReturnsBorrowedThis` attribute.
In the presence of this attribute, the return value can only be used by the
caller if the object the method is called on is unowned. Otherwise, the method
may be called, but the return value must be thrown away.

```
class Foo {
  private ?int $val;
  private ?int $val2;
  <<__ReturnsBorrowedThis>>
  public borrowed function set1(int $val): this {
     $this->val = $val;
     return $this;
  }
  <<__ReturnsBorrowedThis>>
  public borrowed function set2(int $val): this {
     $this->val2 = $val;
     return $this;
  }
}

function useit(): void {
  // this is the standard chaining version
  $unowned = new Foo();
  $unowned
     ->set1(42)
     ->set2(1234);

  // here, since return value cannot be used, must use multiple statements
  $owned = own new Foo();
  $owned->set1(42);
  $owned->set2(1234);
}
```

This is not ideal, but it is required for safety of tracking. We hope that in
the future additional features can be added to the language obviating the need
for this hack. See the discussion on void chaining linked below.

Note that methods with this attribute must act on a borrowed `this` and return
exactly `$this`.

## IDE experience

The new keywords will be supported in a first class manner with respect to syntax highlighting.

There will be a large number of new error types both in the typechecker and parser.

Due to the nature of this system, most developers won't see any errors due to
the inherent flexibility of the default state. When annotated code calls unannotated
code, we can straightforwardly suggest how to annotate the unannotated code to
facilitate progress. When annotated code calls annotated code, the error messages
will be similar to standard type errors where they will explain why the different
states are incompatible and it will be up to the user to fix them.


## Implementation details

### Interaction with other features

The initial version of the feature will have no interactions with other hack
features other than those explicitly expressed above.

Following the rollout of this feature, we expect to utilize it for sound
implementations of Purity, Const Classes, and Disposables. Note that the below
sections are examples rather than confirmed decisions. It is possible that
details about the interactions will change.

#### Disposables

Disposables currently have their own implemented version of ownership tracking
wherein disposables are "owned" by their using statement, and may be lent or
given to called functions. This is completely subsumed by this more complete
proposal, however they will also have additional restrictions (such as
prohibiting disowning them).

As an example, here is what currently exists

```
/* Detailed source-code truncated for AI context efficiency. */
```

and how it would look using ownership management (note that all error
messages are examples):

```
class MyDisposable implements IDisposable {
  // won't require `borrowed` on __dispose method because it's always the case.
  public function __dispose(): void {}
}

function returns_disposable(): owned MyDisposable {
  using $foo = new MyDisposable();
  // here $foo is borrowed

  // error: may not return borrowed values
  // objects in a using block are always borrowed
  // return $foo;

  return new MyDisposable();
}

function accepts_disposable(borrowed MyDisposable $md): void {
  // error: may not `use` borrowed values (or must `use` owned values)
  // using ($md);

  // error: may not return borrowed values
  // return $md;
}

function takes_mixed(mixed $m): void {}

// Disposable Parameters must always be Borrowed.
// function illegal_param(MyDisposable $m): void {}

function example(): void {
  // Disposable objects may not be disowned
  // returns_disposable returns an owned value
  // this value is implicitly disowned here
  // $x = returns_disposable();

  // Disposable objects may not be explicitly owned
  // they must be used by a `using` block
  // $x = own returns_disposable();

  using ($x = returns_disposable()) {
    // here $x is borrowed

    // cannot pass borrowed object to function expecting unowned
    // takes_mixed($x);

  } // here $x is destroyed following lending itself to __dispose

  // Variable $x is undefined, or not always defined Hack(2050)
  // echo $x;
}
```

However, when using ownership management it becomes possible to implement things
like potentially transferring ownership of disposables into called functions
that must then `use` them. There are a handful of more powerful features
required by the HSL IO library in order to make its implementation disposable.
Having them powered by ownership allows for these potential additions.

Given that they are never unowned, it is illegal to have a typehint of a
disposable type without an ownership annotation, typically `borrowed` or `owned`.

Conceptually, the object is "owned" by the using statement and is lent to the
block scope.

#### Purity

We're not going to go into too much detail on purity as it is still an
experimental feature, and we don't want to have discussions on its merits or
decisions at this location.

This is another example of the discussion above regarding requiring linearity
for the purpose of mutability.

In the experimental version, there are 4 states of mutability:
1. Immutable (the default) - an immutable nonlinear value
2. Owned Mutable -  an owned linear value that is mutable
3. Borrowed Mutable - a borrowed linear value that is mutable
4. Maybe Mutable - a value that may or may not be linear, so is immutable but also
must be treated as if it was linear.

The states of this proposal can replace the previous states 1-1:
Immutable -> unowned
Owned Mutable -> owned
Borrowed Mutable -> borrowed
Maybe Mutable -> MaybeOwned

If Purity is rewritten to use the ownership annotations, it must only add
the 2 rules, everything else directly falls out of this proposal.
1. Only Owned or Borrowed objects may be mutated.
2. Value types may always be mutated.

One might note that it's possible to pass an unowned value into the system
from impure code calling pure code, but that is no different than the current
state of the feature.

To be completely clear, this will completely replace the experimental mutability
annotations added by the reactivity project.

#### Const Classes

The design for const classes prior to this proposal worked fairly simply. They
can be modified within the constructor as well as from within private methods
called by the constructor, however once the constructor finishes, the classes
"lock" and become immutable.

However, this has a pretty unfortunate typehole:

```
const class MyConstClass {
  private int $x = 0;
  public function __construct() {
    $this->can_mutate();
  }

  private function can_mutate(): void {
    $this->x = 42;
  }

  private function my_unrelated_method(): void {
    // do stuff
  }

  public function oops(): void {
    $this->my_unrelated_method();
    $this->can_mutate(); // uhh....
  }
}

function example(): void {
  $cc = new MyConstClass();
  $cc->oops(); // explode
}
```

This problem is solvable via a combination of ownership management and making
const classes more powerful. The solution is simple: opt const classes into
the stricter ownership tracking and making them immutable when unowned.

```
const class MyConstClass {
  private int $x = 0;
  public int $even_more = 42;
  public function __construct() {
    $this->can_mutate();
  }

  private borrowed function can_mutate(): void {
    $this->x = 42;
  }

  private function my_unrelated_method(): void {
    // do stuff
  }

  public function oops(): void {
    $this->my_unrelated_method();
    // error: cannot call function with borrowed $this
    // $this here is unowned
    // $this->can_mutate();
  }
}

function example(): void {
  $ncc = own new MyConstClass();
  $ncc->even_more++;
  $cc = disown $cc;
  $cc->oops(); // safe!
}
```

In this system, all instances or typehints of a const class will be opted-in to
the stricter tracking. Further, const classes can now make use of the
extended constructor pattern!

### HHVM Support

This feature will have full HHVM support and enforcement. Objects may only be
used as described above or else they will throw an exception. For cross-function
boundaries the enforcement will work similarly to type enforcement.

### Codemodding

The only major codemod required for this feature is the interaction with
constructors described above to insert the `__Unownable` attribute where
necessary.

Other than that, no codemodding for this will be done except to convert
the current Disposables and temporary Mutability tracking feature to this
complete one. In that case, we will first do a static transformation from the
old attributes to the new system to confirm everything still typechecks.

## Revert Plan

If, following rollout of this feature, we decide to remove it, reversion
will simply be a codemodded removal of the associated keywords. However,
if used within Purity, Const classes, and other major features, it becomes
increasingly difficult to roll back without exposing major soundness holes
in the language.

Note, however, that the initial plan is to have the system implicitly back both
disposables and the experimental Reactivity/Purity syntax under the hood,
so we should hopefully know prior to full rollout if revert is necessary.


## Design rationale and alternatives

This design was created under a few basic principles:
1. Opt-in feeling: It is important for a large codebase that this need not be the default
2. Usability: The states are not conceptually difficult and hack-errors can
be used to direct newcomers towards correct patterns, as situations of the
form "You are doing A, which is wrong, but B would be OK" are common.
3. HHVM Enforceability and efficiency: In order to be used for optimizations,
HHVM must be confident of the guarantees. Therefore, it must be enforceable.
Therefore, it must be efficient.

This is a completely new segment of the language such that attempting to do this
via existing features is a nonstarter.

Not doing this prevents multiple projects, either via soundness issues or simply
lack of required featureset. See above for examples.

### Alternative designs

There are multiple minor tweaks considered to the above syntax.

Instead of `public borrowed function foo(): void {...}`, we considered
`public function foo(): void where this is borrowed {...}`.
We decided against this as, while it makes it clear we're referring to `this`,
it requires modification of function-level where clauses and is extremely verbose.
It is worthwhile to note that `static` appears in the same location and is a modifier
that describes the state of `$this`

We considered the keyword `owned` instead of `own` for marking a value as
owned, but don't believe the extra two letters add anything.

We considered referring to the unowned state as disowned, but this is
confusing for things that are never owned.

As an alternative to `__ReturnsBorrowedThis`, we would like to resurrect the
[void chaining](https://fb.workplace.com/groups/1353587738066495/permalink/1690769081015024/) discussion.
This allows us to use the fluid builder pattern on non-unowned code, but also
allows for the potential to completely remove the pattern of functions that
return $this for the purpose of chaining.

An alternative to the `disown` keyword is to use `release`. Bikeshedding welcome.

The __ReturnsBorrowedThis attribute is technically unnecessary as the emitter
can infer when it would be required and handle the situation appropriately. We
think it's better to be explicit here.

When owning the result of an async function, should the `own` be before or
after the `await`? We landed on before because you're `own`ing the wrapped value,
not the Awaitable. Maybe there are arguments for the alternative?

## Drawbacks

It is complicated and requires significant runtime and typechecker support.

## Prior art

At present, there is only one major language implementing an ownership system.
There are a handful of languages implementing Linearity systems, however that
does not map particularly well to an imperative language like our own.

Rust's ownership tracking is more robust and allows for nested ownership
tracking (such as owned properties) and explicit lifetimes. This is made
possible as they have an AoT compiler that can do enforcement and analysis. We
have different constraints. Further, we do not require quite the same complexity.
This can more simply be described as `borrowed T -> &mut T` and `own T -> T`.
However, rust doesn't typically allow for unowned values. The closes it comes
is `&T` immutable references. These are references to an owned value that is
merely being utilized multiple times at once (and so must be immutable).
In general, our requirements are flipped. They want everything owned. We want
most things unowned with specific things being owned.

Additionally, there a handful of other languages actively exploring this space:

[Swift's ownership manifesto](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md)
is designed based on different constraints and purposes. Ours is mostly to unblock other features,
while also enabling some performance gains. Their purposes are flipped.

[Haskell](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0111-linear-types.rst#id22)
with [tech talk](https://www.youtube.com/watch?v=o0z-qlb5xbI&t=1s):
Rather than discussing linearity of values, they describe linearity of functions,
such that for a fn a -> b, if b is consumed exactly once, then a is consumed
exactly once. They implement polymorphism by taking in closures and making the
overall linearity of the function be polymorphic upon the linearity of the
passed function. Further, they do not have the concept of borrowing linear
values, which is extremely important to our system.

[Clean](https://clean.cs.ru.nl/download/html_report/CleanRep.2.2_11.htm):
Clean has a similar system to Haskell except that it's type-based rather than
function based. It has some similar properties to our system, but considers
non-unique <: unique because of its functional nature, such that you can
pass uniq values into a function accepting non-uniq, but what you get out is
nonuniq. In general, these all apply to consumed inputs, thus they don't have
the borrowed state. They do have a generic on types to specify propagation
of uniqueness, but again, this requires the move in -> move out scenario.

[Idris](http://docs.idris-lang.org/en/latest/reference/uniqueness-types.html):
They use dependent typing for this and have discovered the need for a borrowed
state but have not yet handled the polymorphic MaybeOwned state. They have a
representation for Owned V Unowned, but specifically note that it doesn't allow
for representation of borrowed. It's more similar to Haskell's polymorphism due
to its lack of a borrowed state.

Also, from another paper or note:
> Uniqueness types, in Idris, are being replaced by linear types

#### Papers on the subject:

[Islands: aliasing protection in object-oriented languages](https://dl.acm.org/doi/10.1145/117954.117975)
(by John Hogg) (paper available upon request):

The overall goal here is to create "islands" of objects in which they are, as a
whole, only referenced via a single "bridge" such that all modifications to
the island (or even access to it) must go through the bridge. Thus, they may
handle things like side effects, which are nonissues for const objects and
disposables, and general alias management and concept encapsulation.

Three major concepts: `read` `unique` and `free` where `read` is similar to const,
and the other two are about whether aliases are allowed.
> A variable z many be unrestricted, read, unique, free, read and unique, or read and free.

Interestingly, here 'unique' means that there is only one reference to an object
via the heap (such as instance properties), and the rest are stack-based, and
`free` means there are no heap-based references to the object (even if the
object itself is on the heap).

A unique object is most often used via lending it out on the stack, similar to
our `borrowed` parameters. They don't need two keywords because `unique` appears
on things like instance properties to imply its the only heap-based reference
and on parameters to mean that it's a reference to a `unique` object. However,
in many ways, their `free` is similar to our `owned`. You can create a `free`
reference by destructively reading a `unique` heap-based reference. Further,
`free` references can only be accessed via destructive reads (creating another
`free` refence or assigned into an `unique` one).

I honestly, don't understand this restriction and description.
> If a method receiver is unique, then every parameter and the result must be
> read or unique or free.
> Unique is a transitive property. If an object’s acquaintances are aliased,
> then the observable state of the object may be unexpectedly changed, even if
> there is no aliasing of the object itself. Therefore, an object being accessed
> as unique must not import or export any unprotected references. An unprotected
> parameter could be retained by the object, however.

This doesn't apply to `free` receivers, because it doesn't matter.

This is an interesting approach for allowing tracking of members. However, this
doesn't solve our problem with the `unique` object being referenced from within
an `unrestricted` object, which they don't talk about as far as I can tell.

As a side note, they don't cover practical problems of code deduplication and/or
polymorphic states, which are the main problems we generally struggle with.
They do generally seem to have an opt-in system, however, in that that outside
an "island" everything can just be unrestricted, and only when interacting with
an island must things be annotated.

[Alias burying: Unique variables without destructive reads](http://www.cs.uwm.edu/faculty/boyland/papers/unique-preprint.ps) (by Boyland)

> Alias burying: when a unique field is read, any existing aliases are
> required to be dead. Together with restrictions on aliasing across procedure
> calls, alias burying can be checked by a static analysis in a modular fashion.

Their main goal is to be able to track mutations for safe concurrent reads, which,
while not identical to our goals, is more or less translatable.

They concur on our decision to mark misbehaved constructors
> A constructor can keep a reference to the object it is creating in a globally
> accessible location; not all constructors create unshared objects. Thus a
> constructor that does create an unshared object should be declared as such.
> ...
> Constructors in Java are not responsible for creating an object, just
> initializing it. Therefore constructors that “return” unique objects are
> annotated as having borrowed receiver

Their system of ownership and borrowing seems *very* similar to our own
> An alias of a variable for our purposes is a variable that refers to the same
> object as the first variable. This definition is nonstandard, but is convenient
> for languages such as Java that do not have explicit dereferencing
> A static alias is a field variable; a dynamic alias is a receiver, parameter,
> local variable, or return value alias
> A dynamic alias of an otherwise unique variable is also known as a “non-consumable,”
> “uncaptured,” or “limited” variable.
> Such a variable may be used for computation and copied into other local
> variables or parameters (which become dynamic aliases) but must never be stored in a
> field. In this paper, we refer to variables whose values may never be stored
> (or returned) as borrowed variables.

They recommend using `borrowed` to refer to a value that may either by a dynamic alias
to an owned object or an unowned object.

However, they further recognize that the existence of a union state of this
description results in issues with tracking mutations.
> Weakening a uniqueness variant to admit dynamic aliases makes it easier to
> program with unique variables than with destructive reads alone, but this
> weakening comes with a cost. One main purpose in avoiding aliasing was to make
> it easier to isolate mutations; dynamic aliases make it harder to track down
> all mutations.

They allow for unique static aliases (our borrowed) similarly to Hogg, above.

In general, their states are Unique, Unrestricted, and Borrowed. As far as I can
tell this works in a system that cares only about aliasing and not restricting mutations to
linear values because you don't need dynamic aliases that are required to be references
to unique objects. They allow modifications to Borrowed objects. This seems to
generally safe only because they mark mutating methods as `synchronized`, which
alleviates any interleaving concerns automagically. I'm not quite clear on how
that works, however, given that reads can happen in nonsynchronized methods.

> When a unique field of an object is read, all aliases of the field are made undefined.
> If we can determine statically that no alias is ‘buried alive’, no dynamic
> checks will be necessary.

Their goal is to statically prove that this invariant holds true such that
unique properties _must_ actually be unique dynamically. We prove that via a
combination of static and dynamic checks (because we can't trust types, therefor
disallowing us to trust that function invocations comply).

Their hierarchy of states is Unique/Owned <: Owned/Shared <: Borrowed/Shared <: undefined

Unique/Owned: only allows for explicitly unique values and they must be moved in
destructively.

Owned/Shared: allows for either unique or shared values and generally can treat
them as shared. After passing a unique value to this, one can no longer presume
uniqueness

Borrowed/Shared: May be unique or shared, but my not generate new aliases except
to pass to other borrowed notated states.

> A shared (that is, not unique) parameter may be declared borrowed

This sentence in isolation is somewhat inscrutable. Based on context and later
descriptions, I believe this is noting that a shared value may pass into a parameter
declared as accepting borrowed. This seems *potentially* fine considering it's
specifically stricter, but it doesn't allow making any assumptions about borrowed parameters

> a unique value may be passed to a procedure expecting a shared parameter, but
> a borrowed value cannot be

This explicitly allows for passing unique values into params expecting shared.
This seems like a tenuous assertion if you want to statically prove unique
objects aren't leaking refs.

They further note that
> The analysis also uses an annotation on procedures that indicates which variables it may read

but then never mention this again. I believe this is specifically related to
fields on passed in objects.

> The interface annotations may be read as obligations and dual privileges:
>
> **unique parameters** The caller of a procedure must ensure that parameters declared unique are indeed
> unaliased. A procedure may assume unique parameters are unaliased.
>
> **borrowed parameters** A procedure must ensure that a borrowed parameter is not further aliased when
> it returns. A caller may assume that a borrowed actual parameter will not be further aliased when
> the procedure returns.
>
> **unique return values** A procedure must ensure that a return value declared unique is indeed
> unaliased. A caller may assume that a unique return value is unaliased.
>
> **unique on proc entry** The caller of a procedure must ensure that any unique field read or written by
> the procedure (or by a procedure it calls, recursively) is unaliased at the point of call. A procedure
> may assume that the first time it reads or writes a unique field, the field has no (live) aliases.
>
> **unique on proc exit** A procedure must ensure that any unique field it reads or writes during its
> execution is unaliased upon procedure exit, and that no fields have been made undefined. A
> caller may assume that a procedure does not create any aliases of unique fields or make fields
> undefined
>
> If all procedures fulfill these responsibilities, then the only potentially live variables that may be made
> undefined due to alias burying in a procedure are variables set by this procedure. Thus assuming we
> can check a procedure, we can check a program

From what I can tell, the first three have to do with annotations on params/return values
and the last 2 are just general invariants all methods must adhere to. How they
assert that this magically allows for static assertion of correctness is beyond me.
They certainly don't explain it.

In general, their restrictions on unique and borrowed are identical to ours.
Having their shared state be a supertype of unique and a subtype of borrowed is
somewhat baffling.

If we consider our lack of unique member variables, the last two restrictions
become moot, but the ability to pass unique references to effectively unrestricted
params accepting shared values is confusing.

> Of course, accessing unique fields of possibly shared objects must still be properly
> synchronized in order to prevent race conditions. When the lock is released,
> the unique field must be uncompromised and have no live aliases. If a language
> has asynchronous calls (Java does not), then we must add the rule that
> borrowed values cannot be passed in such calls

Not accepting borrowed inside async calls is an interesting concept, but seriously
limits the usability in our system.

All in all, this system strongly resembles our own, but takes a hard left turn
at a crucial fork and ends up in a place that doesn't quite solve our problems

[Linear Haskell: Practical Linearity in a Higher-Order Polymorphic Language](https://arxiv.org/pdf/1710.09756.pdf)

A relevant quote:
> Seen as a system of constraints, uniqueness typing is a non-aliasing analysis
> while linear typing provides a cardinality analysis....The former aims at
> in-place updates and related optimisations, the latter at inlining and fusion.

In general, their system does not neatly correlate to our needs for the above
reasons.

[Promises: Limited specifications for analysis and manipulation](https://courses.cs.washington.edu/courses/cse590p/00wi/promises.pdf)
(By Chan, Boyland, Scherlis)

Their system of uniqueness management generally seems to mirror our own.
They consider both the general concept of unique references and the issue of
ill-behaved constructors before noting that being able to pass unique references
to parameters that promise not to store lasting aliases do not cause additional
problems. They refer to these as `limited`.

They, too, consider `limited` to be a promise rather than a requirement.
This is contrasted with `unique` parameters that *require* the object passed in
to be unique (and consumed upon passing). This is similar to the concepts by
Boyland in his above paper (which actually came later than this one).

> Since the caller’s copy of the reference cannot be accessed during the dynamic
> context of the call, one can pass a unique reference as a limited unique
> reference instead (as long as the reference isn’t passed as more than that one parameter).
> Limited unique parameters are unaliased within a certain dynamic context and
> have the properties of both unique and limited parameters

Their `unique` is obviously analogous to our `owned`. limited` is generally
analogous to our MaybeOwned with `limited unique` being their borrowed.

They additionally consider the possibility of storing unique values within other objects:

> `Unshared fields` are really no more than a form of unique variable, except
> allocated in the store as part of an object. Similarly static unshared fields
> hold unique references associated with the class object.

> A problem with unshared fields is that they invalidate the assumption that a
> variable, and thus the reference it contains, is only accessible from a single
> frame. For instance, suppose that within a class with an unshared field, we
> call a method with the reference from this field as a parameter. Since there
> is no guarantee that the object with the unshared field is itself unique, we
> cannot assume the field will be inaccessible during the call.

> A simple solution to this is to use a promise that guarantees that the code
> being called will not access that field. Thus, we only allow an unshared field
> to be an actual parameter if it is passed as limited and the method to which
> it is passed does not read or modify the region for that field. As a
> consequence, if we have existing code that does need to access that region, we
> may have to nullify the unshared field during the call.

All in all, their description of this system is given in relatively short order,
but is quite analogous to our own.

[Deny Capabilities for Safe, Fast Actors](https://www.ponylang.io/media/papers/fast-cheap.pdf)

The overall goal of this system is to allow statically checkable safe shared memory.
Their system is interesting. It is effectively a matrix of whether
local/global/both aliases are allow to read and/or write to a reference.

They make the interesting choice to note receiver state between the fn keyword and the fn name.

This seems unenforceable statically (box being disallows global writes):
> If an actor has a box reference to an object, no alias can be used by other
> actors to write to that object. This means that other actors may be able to
> read the object, and aliases in the same actor may be able to write to it
> (although not both: if the actor can write to the object, other actors cannot
> read from it)

Like us, they separate between methods that can be run asynchronously and ones that don't.
It's not safe to give a `box` to a method that can run asynchronously, because the
caller can retain a locally mutable instance, so the async method could read while
the caller does some writes. We ban this via strict limits on borrowed objects
in concurrently executed statements.

They note that passing an owned value to another async method is safe because it
is destroyed in the caller.

They suggest that `tag` (all aliases allowed) cannot be read/written, but may have
further async methods invoked upon them. Those async methods may see `this` as
`ref` (no global aliases, all local aliases). They consider `tag` objects to be
`actor`s (the objects on which async methods are invoked). I don't understand
why this distinction makes mutating their inner state safe.

Apparently you can store tag reference to an otherwise iso (global deny, local allow) reference
> a newly created alias of an iso reference must be neither readable nor writeable (i.e. a tag).

At this point, I decided that the system described was significantly more complicated
than our own with questionable applications to our problemspace. I skimmed the rest
of the paper and determined that I was correct.

[Ownership types for flexible alias protection](http://janvitek.org/pubs/ecoop98.pdf) (by Clarke, Vitek, Potter, Noble)
(this won "the most influential paper of the last decade" at OOPSLA'16)

The overall goal of this paper is to statically ensure abstraction implementation
encapsulation such that given an abstraction A exposed via an API on object B
implemented on classes C...N, the internals of the implementation of the
abstraction A via C...N cannot be referenced outside of B and the API must
be utilized to realize the abstraction.

They note that value types don't require tracking because they're immutable (or
in our cases, sometimes COW).

They suggest the concept of splitting the workings of a container into two pieces:
mutable state that is visible only internally, and immutable state that is
fine to expose (and even potentially share between multiple containers).

It is further possible that for nested containers, the exposed immutable state
may actually be the inner mutable state for an inclosing container. In this way,
it is noted that the immutabile set of an object is only immutable to that object,
but not necessarily to the outside.

They consider the concept of "roles", which map to the different set types. This
is used for the purpose of more abstractly separating (and keeping separate) of sets.

An important quote that also applies to our own system:
> Note that although they are similar, aliasing mode checking and type checking
> are completely orthogonal. An expression’s aliasing mode correctness implies
> nothing about its type correctness, and vice versa.

> The aliasing mode system comprises the following modes: arg, rep, free, var,
> and val.

These modes are/work as follows:

arg: The state visible externally.
> only provide access to the immutable interface of the objects to which they
> refer. There are no restrictions upon the transfer or use of arg expressions
> around a program

rep: The state invisible externally.
> Can change and be changed, can be stored and retrieved from internal containers
> but can never be exported from the object to which they belong

free: Objects with a reference count of exactly 1 (such as recently created objects).

var: "provides a loophole for auxiliary objects which provide weaker aliasing guarantees"
Works like the default semantics of non-alias checking languages except for the
assignment compatibility constraints.
> a mutable object which may be aliased. Expressions with mode var may be changed
> freely, may change asynchronously, and can be passed into or returned from
> messages sent to objects

val: Value types.
The following is because their value types are truly immutable rather than COW:
> has the same semantics as the arg mode, however, we have introduced a separate
> val mode so that explicit arg roles are not required for value types

Arg and Rep modes can be further tagged with Roles to ensure that two object
of the same mode but designed for different purposes cannot intermingle.

None of these modes are assignment compatible except for `free` which can be
used for any of the others.

They note that depending on the overall API of an object, it may or may not be
aliased in different ways.
> For example, if an object uses only modes arg, free, and val, it will be a
> “clean” immutable object, that is, it will implement a referentially transparent
> value type. If all of an object’s method’s parameters and return values (except
> the implicit self parameter) are restricted to arg, free, or val, the object will be
> an alias-protected container with flexible alias protection, and if in addition it
> has no variables of arg mode, the object will provide full alias encapsulation.

All in all, this is an interesting system, but one that is designed around
different and incompatible restraints from our own. This can potentially be
mimicked via something like considering pure contexts rather than pure functions,
but that doesn't allow for the solutions to other language requirements.

[Gradual Ownership Types](https://ilyasergey.net/papers/gradual-esop12.pdf) (By Ilya Sergey and Dave Clarke)

The overall goal of this paper is to explain a system by which ownership annotations
can incrementally be added to a system, similarly to how an untyped program can have
types added to it incrementally.

They focus primarily on the situations involving nested ownership, which results
in being less applicable to our usecase. Further, their goals are mostly related
to proving soundness of the overall system. They spend the vast majority of the
paper laying out the precise proofs and mathematical properties of the system,
which has less obvious applications to us. It might be useful to have someone
with a better theoretical background take a look at this.

[An object-oriented effects system](https://www.researchgate.net/publication/221496521_An_Object-Oriented_Effects_System) (By Greenhouse and Boyland)

The overall goal of this paper is to describe a system by which two method calls
can ensure that neither mutates state available to the other, allowing things
like better interleaving. This is an obvious corollary with our goals in doing
ownership tracking to ensure sound purity enforcement.

They define "regions", which can be thought of as similar to namespaces except that
they are defined within classes and their contents can be one or more properties.
When defining a property, the region they are within is specified. Lack of specification
implies the `Instance` region, which lives inside the global `All` region.
They also include the concept of static regions containing static members (and
doesn't live inside the Instance region). Following this, methods specify which
regions (if any) they act (read and/or write) on. The typechecker guarantees
both that these are true statements locally and that they are transitively true.
Overall, this is effectively another layer of private/public/protected specifiers
that typically exist for more fine control over encapsulation layers.

They additionally allow for members to be marked as `unshared`, stating that
referring to the object requires going through that member, as well as `unique`
on parameters and return values, allowing for ensuring that those objects
have a ref-count of 1. `Unshared` is entirely used for the purpose of promising
that internals are not shared. They note that `Unique` and newly created objects
don't require effects tracking due to their linearity.

All in all, this system is powerful and fascinating, but doesn't handle issues
arising from dealing with the relative capabilities of non-members (such as
parameters) in a way that is ergonomic.

I can certainly see such a system being generally beneficial for the purpose of
encapsulation of different concepts described within members of a class, but for
the purpose of generalized ownership/uniqueness (and the benefits it yields), this
doesn't satisfy our constraints.

[Capabilities for uniqueness and borrowing](https://link.springer.com/content/pdf/10.1007%2F978-3-642-14107-2.pdf) (By Haller and Odersky)

[Kilim: Isolation-typed actors for java](https://www.malhar.net/sriram/kilim/kilim_ecoop08.pdf) (By Srinivasan and Mycroft)

[Uniqueness and reference immutability for safe parallelism](https://www.cs.drexel.edu/~csgordon/papers/oopsla12.pdf) (By Gordon, Parkinson, Parsons, Bromfield, and Duffy)

[Copying, sharing, and aliasing](https://www.researchgate.net/publication/2821935_Copying_Sharing_and_Aliasing) (By Grogono and Chalin)

## Unresolved Questions

Do we want to elide `own` in return statements as described above or we rather
it be explicit? e.g. in a function that is marked as returning owned, do we have
`return own new Foo()` or `return new Foo()`? Note this also applies if instead
of `new Foo()` we were invoking another function that returning an owned value.

For the initial rollout, should we only allow usage of this feature in the
presence of cases that require the feature (i.e. const classes, disposables, and Pure/Rx)?
This would ensure a tighter rollout that wouldn't affect quite as many
developers.

Would we want something like class-level where clauses to mark that *all* methods
on a class required, for example, borrowed `$this`?

Is `own $f = new Foo();` a viable alternative for creating a newly owned value?
The major problems are for things like this:
`function foo(own Foo $f): void { }`
`foo(own new Foo());`
We could potentially elide `own` on constructed objects being passed directly
to things requiring an owned/borrowed/etc object. However, this doesn't work
quite well for functions that return owned values.

Should we require specifically that params etc with ownership notations have
types (in the runtime)?

Should we require (in hack) that the types of params etc with ownership notations
be a class or interface? (this would be changed if we want to track value types)
Should we ban using ownership notations with mixed, dynamic, unbounded generics, etc?

### The general question of naming

Suggestion: rename "Borrowed" to "Uniquely Borrowed", or "Unaliased"

Suggestion: rename "UnKnowed" to "Shared" as it's similar to C++ use cases for
shared_ptr.

Suggestion: rename "MaybeOwned"  to "Restricted".
```
function foo(restricted Foo $f): void {}
public restricted function foo(): void {}
```
From the user perspective, it is nonobvious what restricted means here.
Further, we can imagine that restricted is a bit too vague considering
potential restrictions other than ownership.

Suggestion: rename "UnOwned" to '_' or '_&'.
MaybeOwned feels similar in concept to a polymorphic type/generic, and it works
out to being roughly the same as a "top" type.
The problem for this is similar to the above reasoning for restricted. Without
context, it's unclear what this refers to.

Suggestion: rename "Unowned" to "NonLinear". Looking at the hierarchy, it's
clearly not linear and "MaybeOwned" behaves like a wildcard type.
Then we have the following taxonomy:
Linear: Borrowed, Owned
Nonlinear: Unowned->NonLinear
Either (exclusive): MaybeOwned

Should we generally re-describe this from `ownership` to `uniqueness`?
Something like
Old -> New, Keyword
Owned -> uniq, `uniq`
Borrowed -> uniq ref,  `uniq&` / `&uniq`
MaybeOwned -> maybe uniq ref, `uniq?&` / `?uniq&` / `?&uniq` etc
unowned -> nonuniq ref, still no keyword
with the state-changing operations being `move`, `uniq`, and `share`.
The new version of MaybeOwned feels a bit soupy. Maybe just `?uniq` or `uniq?`?

Suggestion: unowned -> Shared, Owned -> Uniq, MaybeOwned -> MaybeUniq, Borrowed -> Borrowed

Suggestion: MaybeOwned -> top/mixedref?

## Future possibilities

Mostly discussed in the interaction section above.

We may want to allow lambdas to close over owned/borrowed values as long as they
are specially marked and themselves treated as borrowed values.

```
function foobar(vec<int> $v): vec<int> {
  $f = own new Foo();
  return Vec\filter($v, borrowed ($i) ==> $i < $f->num);
}
```

Potentially relax some restrictions on using borrowed/owned values multiple
times in the same statement if/when we can prove soundness.

Could we have some way of marking an arbitrary class/hierarchy as required to
always be linear? Useful for things like disposables but also generators,
where trying to iterate them more than once goes badly. Maybe promote to keyword later?

Should we have an annotation to generally opt-in whole functions to the
stricter tracking for those who want to utilize the stricter system? This would
probably utilize the coeffects system to enforce transitivity

Hack Native has specifically requested we don't close the door on the possibility
of utilizing this system to globally track Arrays and strings in order to avoid
the need for refcounting them. The major blocker for this is that for that to
work, those types would need to *never* be unowned, which would result in a huge
usability degradation.

### Deeper Tracking

We could potentially allow properties of objects to be tracked as well. This
area has not been too deeply explored due to our lack of obvious need for it.
However, were we to add this, here are some considerations:

Some options:
1. We'd have to do something special when you disown the containing object such
as recursively disowning any properties.
2. Ban disowning such objects (linear forever)
3. When disowning objects, if they have any props marked as containing owned,
assert they're null or throw. That would work in the runtime but it would be
difficult for the typechecker to enforce (since it would have to keep track of
if every owned prop is null or not).

Note that we'd have to extend the "passing multiple refs" rule to include
referencing a prop and the outer object at the same time.

## FAQ

There are a handful of questions that don't fit in the above HIP, but were
themes among feedback received.

**Q** Should we consider making the unowned -> borrowed conversion opt-in via an
<<__AllowUnowned>> attribute on the function decl header? This makes the
system strict _unless_ explicitly opted out.

**A** This makes the system more complicated and is probably the wrong default.

**Q** Do we need a __Soft equivalent?

**A** Since unowned can flow into most things, a __Soft doesn't buy much.

**Q** HHVM can produce optimizations based on the guarantees described below when
utilizing these features. Can I break these guarantees with HH_FIXME, typeholes,
or abusing erased generics? If so, what happens?

**A** No. While we're likely to require types while using this feature, the
assertions made here are fully enforced by the runtime. The ownership concept
is fully reified regardless of the static types themselves.

**Q** what guarantees are we trying to provide via ownership system, and
specifically the "borrowed" state? More specifically, how does this apply when
mixing normal objects and disposables?

**A** A combination of strong and weak lifetime guarantees. The important piece is
that these objects don't get mixed. Const classes and disposables are
perma-opted-in to stricter tracking, so they get "strong" lifetime guarantees.
Further, anything in the `owned` state has a strong guarantee. The only weak
guarantee comes into play with the borrowed state, since that can actually be
nonlinear, but the guarantee there is from that point on it will stay linear.

**Q** can `own` be implied by `new` or is the `own` keyword just there for clarity?

**A** no. Not all created objects are tracked and owned. Many objects are never
logically owned.

**Q** why `$f = own new Foo();` instead of `own $f = new Foo();`?

**A** The problem comes into play for things like this:
```
function foo(own Foo $f): void { }
foo(own new Foo());
```
For runtime enforcement, we need to correctly forward `owned`ness across
function boundaries. This may be possible in the case of an explicit `new`,
but becomes even more complicated in the presence of functions returning owned

**Q** Can you disown a value received from a function invocation? Would there be any
advantage to doing something like that rather than let it implicitly become unowned?

**A** You cannot, and at present there isn't an advantage.

**Q** Will most code need to use these annotations?

**A** We generally expect the majority of code not to need to deal with ownership at all.

**Q** Why would a developer want to use this feature? What patterns should they
look for in their code that will make them want to track ownership?

**A** We don't expect most developers to want to generally opt in their codebase.
We give most of the "worry free" parallelism rust gives by using an
asynchronous model rather a true parallel one. There will likely be rare cases
where users want to maintain linearity for the lifetime of an object, in which
case this can be used to give them type-and-runtime level guarantees. The most
likely scenario for use will be very hot code that uses these annotations to
tell the runtime that it may safely elide reference counts and do other
optimizations.

**Q** Would the design be different if we had types in compilation? What would be
different? Would it require fewer annotations to the code?

**A** Types in compilation enable a handful of more powerful features such as more
deep tracking. However, as with typing, we would still need the declarations to
contain the developer intent. It is possible that some of the function-local
restrictions such as single-flavour-per-local could be relaxed. It is also
possible that more inference about the function-local state of objects could
be inferred rather than requiring explicit owning and disowning. See below
about lambdas.

**Q** Same question as previous but with just declaration level information

**A** Declarations in compilation unfortunately do not yield much useful information
for this feature. We could be smarter about handling named top-level functions,
but that doesn't buy us much.

**Q** How should I think about this ownership system. It isn't a type right? So
it wouldn't make sense to write say `type OwnedC = owned C`? How about
`vec<owned C>`?

**A** ownership is conceptually a parallel concept to typing in most cases. Ownership
is an attribute of an object the same way the type is, but ownership state isn't
a type. It's possible we could implement aliases, but because we rely strongly
on compile-time guarantees for efficiency, that has not been investigated for
the first iteration of this feature. `vec<owned C>` doesn't make sense because
we only have shallow ownership tracking. If we some day allow for deep tracking,
we will likely revisit this.

**Q** How will this interact with lambdas? Will ownership annotations need to be
placed on parameters? Can they be inferred?

**A** Ownership annotations will be required. From the perspective of this feature,
they are equivalent to any other function declaration. Types in compilation may
allow us to avoid this requirement.

**Q** How does this relate to projects like Co-Effects?

**A** This is mostly orthogonal to Co-effects. There will likely be some co-effects
that add/remove capabilities based on ownership state (such as mutability in
pure code), but otherwise one does not depend on the other. Co-effects are about
function-level contexts and ownership tracking is object/instance/variable specific.

---

### Hphp Hack Doc HIPs Pure Functions (`hphp/hack/doc/HIPs/pure_functions.md`)

# Purity

### Feature Name: Pure Functions

### Start Date: August 28, 2020

### Status: Candidate

# Summary:

Hack functions that are deterministic, idempotent, and do not result in side effects.

Feature motivation:

Fixing codegen is a major initiative within Facebook. There are two currently unsolved problems:

1. Some major uses of codegen require dependency tracking in order to determine when to regenerate code. If the dependencies aren’t pure, then running codegen may have different results without reflecting changes in the file system, meaning the dependencies aren’t statically trackable.
2. A common pattern of codegen is to compute a large map and then store it to disk to avoid constantly recomputing it. Using creative automatic dependency tracking, HHVM can maintain a server-local cache of these maps that only gets recomputed when an underlying dependency changes (which is when codegen would need to run anyway). In this way, we can avoid writing these maps to disk as codegen.

# User Experience:

Pure functions will utilize the coeffects framework. As such, the way to mark a function as pure is simply this:

`<<__Pure>> function my_fn(): RetType {...}`

There are a few relatively simple restrictions on what is allowed within pure functions.

## Pure Transitivity

Pure functions may only invoke other pure functions, though may be invoked by impure functions. This all falls out naturally from the coeffects system.

```
function my_impure_fn(): void {/* terribly impure things */}
<<__Pure>> function my_pure_fn() void {
  my_impure_function(); // whoops
}
function my_other_impure_fn(): void {
  my_pure_fn(); // fine
}
```

## No Accessing Global State

Writing to global state results in side effects. Reading from global state results in the loss of idempotency. As such, accessing global state is simply wholesale banned in pure functions. This includes such things as superglobals and static members.

```
class Foo {
  public static int $foo;
  private static int $bar;

  <<__Pure>> private static function getBar(): int {
    // Static property cannot be used in a pure context. (Typing[4228])
    return self::$bar;
  }
}

<<__Pure>> function rx_func(): void {
  // Static property cannot be used in a pure context. (Typing[4228])
  Foo::$foo;
  // Superglobal $_GET cannot be used in pure function. (NastCheck[3041])
  $_GET;
}
```

## No IO

IO is effectively an example of accessing global state. The only way to do IO in hack is to utilize functions exposed by HHVM or via an intrinsics such as `echo`. The former will simply not be exposed as pure and the latter are banned operations within pure functions.

```
<<__Pure>> function pure(): void {
  // `echo` or `print` are not allowed in pure functions. Hack(4226)
  echo 'foo';
  // `echo` or `print` are not allowed in pure functions. Hack(4226)
  print 'par';
  // Pure functions can only call other pure functions. Hack(4200)
  fgets(STDIN);
}
```

## No Modifying Local Objects

By default, hack objects can have multiple references to them, including those potentially stored outside of the pure context or simply visible to another frame of execution. Due to this, modifications of objects is illegal and all objects are considered immutable within pure functions. This restriction is relaxed only for the `$this` object within constructors.

Note that this means that functions on Hack Collections modifying the collection are necessarily impure.

```
class Foo {
  private int $bar;

  // both explicit and implicit assignments here are fine
  <<__Pure>> public function __construct(int $bar, public string $baz) {
    $this->bar = $bar;
  }

  <<__Pure>> public function setBar(): void {
    // This object's property is being mutated(used as an lvalue)
    // You cannot set object properties in pure functions (Typing[4202])
    $this->bar = 42;
    // This object's property is being mutated(used as an lvalue)
    // You cannot set object properties in pure functions (Typing[4202])
    $this->bar += 17;
  }
}

<<__Pure>> function some_pure_fn(
  Foo $foo,
  Vector<string> $vector,
  Map<string, int> $map,
  Set<int> $set,
): void {
  // This object's property is being mutated(used as an lvalue)
  // You cannot set object properties in pure functions (Typing[4202])
  $foo->baz = 'monkeys';
  // Cannot append to a Hack Collection object in a pure context. (Typing[4201])
  $vector[] = 'apples';
  // Cannot assign to element of Hack Collection object via [] in a pure context. (Typing[4201])
  $vector[0] = 'bananas';
  // Pure functions can only call other pure functions. Hack(4200)
  $vector->set(0, 'bananas');
  // Cannot append to a Hack Collection object in a pure context (Typing[4201])
  $set[] = 11;
  // Cannot assign to element of Hack Collection object via [] in a pure context. (Typing[4201])
  $map['pajamas'] = 42;
}
```

Values types, however, may be "modified", as these modifications are necessarily not visible outside the current frame of execution.

```
<<__Pure>> function some_pure_fn(
  vec<string> $vec,
  dict<string, int> $dict,
  keyset<int> $keyset,
): void {
  $vec[] = 'apples';
  $vec[0] = 'bananas';
  $keyset[] = 11;
  $dict['pajamas'] = 42;
}
```

An exception made to the above rule is memoization using the `__Memoize` attribute.

## Enforced Determinism

Non-determinism is prohibited as it invalidates computational consistency. By design, these operations must utilize HHVM builtins at some point in their execution, and those builtins are not able to be pure due to their innate side-effects. Typical examples are functions such as rand and time. While it would be possible to implement a pseudo-random number generator completely without using HHVM builtins, this function would either need to be deterministic or store internal state to produce changing results. The former would result in code that is acceptable within a pure function, but not particularly useful, and the latter is illegal for reasons other than issues with determinism.

Due to the inherent non-determinism caused by Hack's asynchronous execution model, all asynchronous functions must be immediately `await`ed. `Awaitables` are only legal as the result of a function call and must be immediately unwrapped into their resultant type. Concurrent blocks are permissible.

In order to avoid issues arising from suspension of execution, it is banned for the common cases asynchronous execution and generators. HHVM will enforce that we never suspend execution of a pure function due to usage of `await`. `yield` and friends are banned syntactically within pure functions. However, an exception is made specifically for the error handler that is invoked due to notices triggered by HHVM. For the sake of execution of the overall program, we are considering the error handler to be pure in that it will not affect state visible to the context. Once the error handler is moved into HHVM, this will become even more true. It is possible that we will delay the error handler being invoked until after leaving the pure context.

# IDE experience:

The IDE experience is as above in the user experience. Users will get descriptive error messages when attempting to do illegal operations within pure functions.

# Implementation details:

See the proposal for coeffects.

This is fully enforced within HHVM. The illegal operations within a function are banned at the opcode level and will fail to emit. The transitivity requirement is enforced by calling conventions, including polymorphically-pure functions as described in the coeffects proposal.

# Design rationale and alternatives:

The major additional feature considered on top of this involves some way to add safe mutations that are tracked such that they do not result in side effects or nondeterminism. This will be discussed in more detail in a followup document.

# Drawbacks:

The lack of mutable state combined with the other restrictions of this system make it more difficult to write idiomatic hack code, and make it impossible to represent certain structures requiring generally mutable state.

# Prior art:

`constexpr` in C++.

`const fn`s in rust.

`comptime` in Zig

`noSideEffect` in Nim

Purity in more than a handful of functional languages.

# Unresolved questions:

Do we want some way to do print-debugging within pure code in sandbox mode? What about the interactive debugger? Does stepping through a pure function and modifying values cause problems?

# Future possibilities:

We can potentially add multiple different kinds of optimizations based on the knowledge that the code isn’t side-effectful as well as idempotent and deterministic.

An additional major benefit to this work is the potential to use Pure functions in constant initializer positions. This is currently a major source of unsoundness. Additionally, there are very practical applications of this to the upcoming Enum Classes feature.

## Coeffects

Once the coeffects feature is shipped, pure contexts will be integrated within it. As such the syntax will change from `<<__pure>> function(): void {...}` to `function()[pure]: void {...}` or similar, depending on the finalized version of the coeffects proposal.

---

### Hphp Hack Doc HIPs Sealed Methods (`hphp/hack/doc/HIPs/sealed_methods.md`)

## [HIP]  `__Sealed` methods

**First proposal**: 22/4/2024

**Updated**: 16/10/2025

**Author**: Francesco Zappa Nardelli

**Contributors**: Adi Kumar, Catherine Gasnier, Mistral Contrastin, Jake Cordero, Scott Owens, Shashank Kambhampati.

**TL;DR**  Following feedback from programmers it seems that there is interest in having a method-level `__Sealed` attribute that controls which subclasses can override the method.  This document proposes a design, a prototype implementation is available for experimentation.

**Description**:  we propose to add a method-level `__Sealed` attribute.  Semantics is defined below, according to the placement of the attribute.

1. **Sealed method defined in a concrete class.**

If the attribute is added to a method defined in a **concrete class**, the attribute controls which subclasses can override the method.  This part of the design is straightforward and is exemplified by the following examples.  The simplest use-case is below, which would be **accepted** since `foo` is sealed with class `D`, and overridden in class `D`.

```php
class C {
  <<__Sealed(D::class)>>
  public function foo(): void {  echo "I am foo in C\n"; }
}

class D extends C {
  <<__Override>>
  public function foo(): void { echo "I am foo in D\n"; }
}
```

On the other hand, continuing the example,  class E would be **rejected** as it is not allowed to override `foo` in `C`.

```php
...

class E extends C {
 <<__Override>>
  public function foo(): void {  echo "I am foo in E\n"; }
}
```

Class-level `__Sealed` attributes only impose restrictions on direct extends, so a class that extends `D` can override `foo` again.  We mimic the same semantics with method-level `__Sealed` attributes, so building on our running example, the following is accepted:

```php
...

class F extends D {
  <<__Override>>
  public function foo(): void { echo "I am foo in F which extends D\n"; }
}
```

Similarly, it is possible to introduce new `__Sealed` restrictions in subclasses, eg. the following is accepted:

```php
class C {
  <<__Sealed(D::class)>>
  public function foo(): void {  echo "I am foo in C\n"; }
}

class D extends C {
  <<__Override, __Sealed(E::class)>>
  public function foo(): void { echo "I am foo in D\n"; }
}

class E extends D {
  <<__Override>>
  public function foo(): void { echo "I am foo in E\n"; }
}
```

It is arguable whether the following should be accepted or rejected, since there is no analogous scenario in class-level `__Sealed` attributes:

```php
class C {
  <<__Sealed(D::class)>>
  public function foo(): void {  echo "I am foo in C\n"; }
}

class D extends C {}

class E extends D {
  <<__Override>>
  public function foo(): void { echo "I am foo in E\n"; }
}
```

We propose to reject this code so that the `__Sealed(D::class)` attribute in C can be interpreted as _"if foo in C is overridden, then it is overridden in D"_.

2. **Sealed method defined in an abstract class.**

If the attribute is added to an abstract method defined in an **abstract class**, then we have two options (and we should support both):

* the `__Sealed` attribute can specify which **classes** can implement the abstract method, as in the class `D` below:

```php
abstract class C {
  <<__Sealed(D::class)>>
  abstract public function foo(): void;
}

// The __Sealed attribute can specify the class
// that implements the abstract method
class D extends C {
  <<__Override>>
  public function foo(): void { echo "I am foo in D\n"; }
}
```

The abstract class `C` might also defer the overriding implementation of `foo` to a trait, as below.

```php
// method_abstractclass_02.php

abstract class C {
  <<__Sealed(D::class)>>
  abstract public function foo(): void;
}

trait T {
  <<__Override>>
  public function foo(): void { echo "I am foo in T\n"; }
}

class D extends C {
  use T;
}
```

* the `__Sealed` attribute can specify which **traits** can implement the abstract method.  Arbitrary classes can then use the traits, as in the valid code below:

```php
// method_abstractclass_03.php

abstract class C {
  <<__Sealed(T::class)>>
  abstract public function foo(): void;
}

// The __Sealed attribute can specify the trait
// that implements the abstract method.
trait T {
  public function foo(): void { echo "I am foo in T\n"; }
}

// Arbitrary subclasses of C can then use the trait.
class E extends C {
  use T;
}

class F extends C {
  use T;
}
```

If a method is sealed with a trait, then it is not possible to redefine the trait method in the class that uses the trait (otherwise the `__Sealed(T::class)` annotation would be useless).  The following is thus rejected:

```php
// method_abstractclass_04.php

abstract class C {
  <<__Sealed(T::class)>>
  abstract public function foo(): void;
}

trait T {
  <<__Override>>
  public function foo(): void { echo "I am foo in T\n"; }
}

class D extends C {
  use T;
  // This should be rejected
  <<__Override>>
  public function foo(): void { echo "I am foo in D\n"; }
}

class E extends C {
  // This should be rejected
  <<__Override>>
  public function foo(): void { echo "I am foo in E2\n"; }
}

```

If a method is sealed with a trait, then it is not possible to redefine the trait method in the class that uses the trait (otherwise the `__Sealed(T::class)` annotation would be useless).  The following is thus rejected:

```php
// method_abstractclass_05.php

abstract class C {
  <<__Sealed(T1::class)>>
  abstract public function foo(): void;
}

trait T1 {
  <<__Override>>
  public function foo(): void { echo "I am foo in T1\n"; }
}

trait T2 {
  <<__Override>>
  public function foo(): void { echo "I am foo in T2\n"; }
}

class D1 extends C {
  use T1;  // this is ok
}

class D2 extends C {
  use T2;  // this is rejected
}
```

In both cases the rules for transitive visibility of `__Sealed` attributes for methods defined in abstract classes and traits are  similar to those for methods defined in classes (eg. no transitive interpretation).

3. **Sealed method defined in a trait**

If the attribute is added to methods defined in a **trait**, then again, the `__Sealed` attribute can specify which classes or which traits can override the method.  The following is thus accepted:

```php
// method_trait_01.php

trait T1 {
  <<__Sealed(C::class, T2::class)>>
  public function foo(): void { echo "I am foo in T1\n"; }
}

// class C can override foo in T1
class C {
  use T1;

  <<__Override>>
  public function foo(): void { echo "I am foo in C\n"; }
}

// trait T2 can override foo in T1, T2 can then be used by arbitrary classes
trait T2 {
  use T1;

  <<__Override>>
  public function foo(): void { echo "I am foo in T2\n"; }
}

class D {
  use T2;
}
```

while the following overrides are rejected:

```php
// method_trait_02.php

trait T1 {
  <<__Sealed(C::class, T2::class)>>
  public function foo(): void { echo "I am foo in T1\n"; }
}

// class D cannot override foo from T1
class D {
  use T1;

  <<__Override>>
  public function foo(): void { echo "I am foo in D\n"; }
}

// trait T3 cannot override foo from T1
trait T3 {
  use T1;

  <<__Override>>
  public function foo(): void { echo "I am foo in T3\n"; }
}
```

The following is rejected too (the rule of thumb is _"if T1 is inlined in E1 then E2 cannot override foo"_):

```php
// method_trait_03.php

trait T1 {
  <<__Sealed(C::class, T2::class)>>
  public function foo(): void { echo "I am foo in T1\n"; }
}

class E1 {
  use T1;
}

class E2 extends E1 {
  <<__Override>>
  // this is rejected
  public function foo(): void { echo "I am foo in E2\n"; }
}

```

**Syntactic restrictions:**

- Sealed cannot be used on constructors and private methods
- Sealed cannot be used on methods defined in interfaces
- Methods cannot be sealed to names that are not visible (eg. `internal` to a different module)

**Property:**

If a method is sealed with an empty list of classes, then it cannot be overridden (eg. it is final).

**Implementation**:

**Hack**: we have a prototype implementation that can be used for experimentation:
* allow `__Sealed` on methods,
* store in shallow decls
* while doing override checks, enforce the sealed semantics whenever the overridden method has the relevant attribute

**HHVM**: HHVM enforces the class-level Sealed attributes at class/trait load time.  We argue that HHVM does not need to enforce method-level `__Sealed` attributes.  If needed, we can mimic the enforcement for method-level Sealed attributes.  No extra run-time checks / overhead are needed, and no changes to Func objects are required.

---

### Hphp Hack Doc HIPs Shape And Tuple Destructuring (`hphp/hack/doc/HIPs/shape_and_tuple_destructuring.md`)

**Status**: Accepted, parsing and type-checking are implemented
**Audience**: Hack Improvement Proposal committee
**Author**: Max Heiber

# HIP: Shape and Tuple Destructuring

## Summary

```c#
shape('x' => $x, ?'y' => $y, 'z' => _) = $point;

// punning on lhs
shape($x, $y) = $point;

// ignore remaining fields
shape($x, ...) = $point;

// tuple destructuring: for compositionality
tuple($a, $b) = $tup;
tuple($a, optional $b) = $tup;

// ignore remaining fields
tuple($a, ...) = $tup;

// also allow punning on RHS for consistency and ergonomics
$point = shape($x, $y);

```

## Motivation

### Goal

- **Ergonomics**:
  - Helps avoid useless boilerplate
  - Users want the feature.
    - Some example requests: [1](https://fburl.com/workplace/v7tfdqaj) [2](https://fburl.com/workplace/11tsa7t5) [3](https://fburl.com/workplace/zsargvja) [4](https://fburl.com/workplace/2uv3sw3f) [5](https://fburl.com/workplace/hbhjst33) [6](https://fburl.com/workplace/8myxpd6d)
  - Don't have worse ergonomics than PHP (!!) which has [destructuring](https://www.php.net/manual/en/language.types.array.php#language.types.array.syntax.destructuring), including for shape-like arrays.

Adapted from real-world code (see [original](https://fburl.com/code/w3pbqgjg) and 1800 more in https://fburl.com/diff/dl2g4nen):

```
 $ctx = $config['request_context'];
 $service_name = $config['service_name'];
 $endpoint_override = $config['endpoint_override'];
 $endpoint_fallback = $config['endpoint_fallback'];
 $retry_policy = $config['retry_policy'];
 $retry_strategy = $retry_policy['strategy'];
 $log_level = $config['log_level'];
 $deploy_target = $config['deploy_target'];
 $version = $config['api_version'];
 $display_name = $config['display_name'];
 $health_check_types = $config['health_check_types'];
 $last_updated = $config['last_updated'];
```

After:

```
 shape(
   'request_context' => $ctx,
   $service_name,
   $endpoint_override,
   $endpoint_fallback,
   'retry_policy' => $retry_policy,
   $log_level,
   $deploy_target,
   'api_version' => $version,
   $display_name,
   $health_check_types,
   $last_updated,
 ) = $config;
```



We intend to meet the goal, with the following constraints:

- AI-friendliness (and friendly for humans too):
  - Avoid introducing things unlikely to be used frequently (harder to learn, harder to teach)
- safety:
  - make it harder for there to be unhandled shape fields, especially as code evolves
  - Punning eliminates trivial mappings of keys to variables, making it safer to skim both AI or human code in reviews
  - We are willing to trade off *perfect* safety in favor of ergonomics. (slogan: safety at the expense of ergonomics is often safety at the expense of safety). This is why we allow not mentioning optional keys in closed patterns against closed shapes. For example, `shape($a, $b) = $sh;` is allowed when `$sh` has type `shape(‘a’ => int, ‘b’ => int, ?’c’ => int)`.
- language evolvability:
  - Don't make it harder to add pattern matching later
  - Avoid complex feature interactions
  - Don't prematurely fill in the feature space. For example, we can take learnings from shape destructuring and apply them to future proposals for vec+tuple destructuring or for punning.

### Relevant Data

1. While `list` is extremely flexible in theory, devs **rarely use this flexibility**.

- Destructuring is primarily done on ***structured*** **data**:
  - The vast majority of `list` destructurings are done on tuples.
- Destructuring typically involves simple lvalues (only containing `list` and variables):
  - Nearly all lvalues in `list` are simple (var or list) in prod, non-generated code.
  - The bulk of complex lvalues come from a single pattern in tests where devs destructure into properties that are `__LateInit`.
- Function calls on the left-hand-side are essentially unused.

source: [analysis](https://fburl.com/hack_shape_destructuring_hip)

See also [Future Possibilities](#future-possibilities). Starting simple doesn’t mean we can’t complicate the feature with supporting KeyedContainer and destructuring into static properties etc. later if we need to, but I argue that the data supports the hypothesis that devs don’t need the messy version with weird semantics that is hard to optimize and reason about.

2. The feature is expected to have wide applicability

Accessing all required fields of a shape seems to be common in practice, so the feature will have wide applicability. According to a vibe analysis, a majority of shape accesses are in functions that access all fields. source: [analysis](https://fburl.com/hack_shape_destructuring_hip)–validation and examples in progress, take this point with a grain of salt as the analysis is complex.

## Solution

Our design is meant to gibe with the relevant data and the constraints above.
See examples in the [Solution](#solution) section below.

- **Destructuring is just pattern-matching for irrefutable patterns**:
  - No observable evaluation order:
    - by safety and AI-friendliness constraint: semantics should be predictable. And yet, PHP-style `list` evaluation order is right-to-left by list elements but left-to-right for array indices.
    - by language evolution constraint: observable evaluation order would make it harder to optimize and reason about pattern-matching, and we want destructuring to essentially be a special case of pattern matching
- **Only destructure structured data** for now:
  - gibes with how existing features are used in practice (see "relevant data" above)
  - language design principles, in particular compositionality
  - use cases for destructuring vecs are rare. If we do want to add vec destructuring, we will likely also need rest-on-the-LHS `vec[1, 2, ...$rest] = $v;`, best considered in a future proposal.
- **Predictable syntax and semantics**:
  - I suspect `KeyedContainer('a' ?? f() => f()[])`, while very general and flexible, would have discoverability and learnability issues for AIs and humans. This kind of syntax is unlikely to be represented in the training data due to differences from other languages. I suspect such a construction would be hard to learn in-context, given that the power features of `list` are so rarely used in practice.

### Syntax

```
  destructure_lvalue ::=
      | lvar
      | wildcard   // `_`, for ignoring fields/entries
      | destructure_shape
      | destructure_tuple

  destructure_shape ::=
      | shape(destructure_shape_entry, .., destructure_shape_entry)
      | shape(destructure_shape_entry, .., destructure_shape_entry, ...)
      | shape(...)

  destructure_shape_entry ::=
      | destructure_shape_field
      | destructure_optional_shape_field

  destructure_shape_field ::=
      | string_literal => destructure_lvalue
      | lvar

  destructure_optional_shape_field ::=
      | ?string_literal => destructure_lvalue
      | ?lvar

  destructure_tuple ::=
      | tuple(destructure_tuple_entry, .., destructure_tuple_entry)
      | tuple(destructure_tuple_entry, .., destructure_tuple_entry, ...)
      | tuple(...)

  destructure_tuple_entry ::=
      | destructure_lvalue
      | destructure_optional_tuple_field

  destructure_optional_tuple_field ::=
      | optional destructure_lvalue
```

Statement contexts where `destructure_lvalue` may appear:

```
destructure_lvalue = expr;
foreach (expr as destructure_lvalue) stmt
foreach (expr as destructure_lvalue => destructure_lvalue) stmt
let destructure_lvalue : type_hint = expr;
```

Some key choices:

- We allow punning. `shape($x)` is equivalent to `shape(‘x’ => $x)`
- We have both closed patterns (no …) and open patterns (contain …)
- We have both required matching `’key’ => value` and optional field matching `?’key’ => value`

Note: shape/tuple destructuring is

- not allowed in: function/method parameters nor catch bindings
- shape/tuple aren't allowed in `list` nor vice-versa in order to not expose evaluation order or allow arbitrary side effects, etc. We may find that there are no/few use cases of `list` remaining. Codemodding away most `list` and linting against its use may be in scope. Fully eradicating `list` is too much scope, imo.

### Naming errors

The following rules are for us to make evaluation order unobservable, for safety and simplicity:

- No variable may be assigned multiple times in a destructuring assignment.
- `list` is not allowed in shape/tuple destructuring patterns.
- shapes and tuples are not allowed in `list`.

Here's why:

- `list` evaluation order is observable, since side effects are allowed and exceptions can happen
- `list` evaluation order is arguably not predictable for users, since it's (the only?) part of Hack that is evaluated right-left (!).
- If `list` is allowed inside shapes, then shape evaluation order would leak, and we would have to choose between inheriting the bizarre behavior of `list` or introducing inconsistency in the language.

The naming errors will not be bypassable and will guarantee good behavior in prod. These restrictions will not be implemented in HHVM due to insufficient motivation for such enforcement, in my opinion. This means that devs will be able to observe assignment order in non-prod environments, if they run code that doesn't type-check. `tuple($a, $a) = tuple(1, 2); echo $a` reveals the (implementation-defined) evaluation order.

### Runtime “as if” semantics

We will compile to userland-style code in HackC, leveraging existing mechanisms in HackC and HHVM.

This semantics given here is “**as-if**”: our actual implementation may differ but should produce the same bindings of variables to values.

We sketch informally, using specific examples. I did take a stab at a greeked version of the semantics in the [Appendix: Correctness of Typing](#appendix-correctness-of-typing) section but find these examples easier to read.

**Closed shape pattern:**

```
// source
shape('x' => $x, 'y' => $y) = $point;

// desugared
$t = $point;
$x = $t['x'];
$y = $t['y'];
```

**Open shape pattern:**

```
// source
shape('x' => $x, ...) = $point;

// desugared
$t = $point;
$x = $t['x'];
```

The `...` produces no code. Only mentioned fields are accessed.

**Optional fields in patterns:**

```
// source:  T = shape('x' => int, ?'y' => int)
shape('x' => $x, ?'y' => $y) = $point;

// desugared
$t = $point;
$x = $t['x'];
$y = Shapes::idx($t, 'y');       // null if 'y' absent
```

Optional fields use `Shapes::idx`, which returns `null` for absent keys.

**Nested optional fields in patterns:**

```
shape('name' => $name,
      ?'address' => shape('city' => $city, ?'zip' => $zip)) = $person;

// desugared
$t1 = $person;
$name = $t1['name'];                           // EXACT: direct access
$t2 = Shapes::idx($t1, 'address', dict[]);     // dict[] sentinel if absent
$city = Shapes::idx($t2, 'city');              // NULLABLE: required, but ancestor optional
$zip = Shapes::idx($t2, 'zip');                // NULLABLE: optional
```

The `dict[]` sentinel is the key trick: when `'address'` is absent, `$t2` becomes `dict[]`, and all subsequent `Shapes::idx` calls return `null`. No if/else branching needed.

**Wildcard `_`:**

```
// source
shape('x' => $x, 'y' => _) = $point;

// desugared
$t = $point;
$x = $t['x'];
$t['y']; // accessed and ignored
```

Wildcard is not a variable and cannot be used in expression contexts.

Wildcard is not `$_`. `$_` is a variable–the type checker treats it differently from other variables in some cases, but not consistently, for example, users can pass around `$_`, as Kevin Viratyosin pointed out in [a paste](https://fburl.com/phabricator/hcf2pg5s).

Also note that we generate code for fields that are ignored with wildcard, as suggested by Kevin Viratyosin. A few reasons for this:
- This way the runtime behavior of destructuring is more likely to match that of pattern matching. Hack doesn’t have pattern matching yet, but if it does in the future, we are likely to check that wildcarded fields exist. Consider `let $x: mixed = EXPR; match $x with shape(‘x’ => _) ==> echo ‘has x’ | shape(‘y’ => _) ==> echo has ‘y`. In such cases, we are using the presence of a field to determine which branch to take.
- Accessing the field is a proof that the field exists. This may enable us to reduce like types `(~)` if we want to use the information.

**Basic tuple:**

```
// source
tuple($a, $b) = $pair;

// desugared
$t = $pair;
$a = $t[0];
$b = $t[1];
```

**Tuple with optional entry:**

```
// source
tuple($a, optional $b) = $pair;

// desugared
$t = $pair;
$a = $t[0];
$b = HH\idx($t, 1);                           // null if index 1 absent
```

Note: optional fields are rare in tuple types, so this `optional` syntax on LHS is expected to be rarely used. We provide it anyway to allow ease of refactoring or migration between shapes and tuples, and to make the language more predictable than if we had an asymmetry between shape and tuple destructuring. `optional` keyword is rare cross-linguistically but is consistently used in Hack for tuple types and tuple-like things (such as optional named parameters).

**Tuple with ellipsis (prefix match):**

```
// source
tuple($a, $b, ...) = $triple;

// desugared
$t = $triple;
$a = $t[0];
$b = $t[1];
// trailing elements ignored, no code generated for ...
```

### Runtime performance notes

This document specifies an “as if” semantics, but future work is to find a good implementation. We will try to avoid code bloat and leverage existing optimizations.

I’m considering taking the sketch above almost literally:
- The design is such that HackC will compile to bytecode without conditional branching: all branching will be downstream (HHBBC, interpreter, JIT).
- One tweak: instead of compiling to Shapes::idx, we’d likely compile to the Idx opcode, to skip the overhead of a function call. HackC already compiles away calls to `HH\Idx`.

These may be relevant code pointers that may help w.r.t. perf considerations (disclaimer: not an expert):

- We use `dict[]` a bunch for defaults, but I think it is cheap: [`staticEmptyDictArray()`](https://github.com/facebook/hhvm/blob/d911b10bc976d0a931/hphp/runtime/base/array-data-inl.h#L35)
- hackc [`emit_idx`](https://github.com/facebook/hhvm/blob/d911b10bc976d0a931/hphp/hack/src/hackc/emitter/emit_expression.rs#L1865) compiles to the `Idx` instruction.
- HHBBC [`Idx` handler](https://github.com/facebook/hhvm/blob/d911b10bc976d0a931/hphp/hhbbc/interp.cpp#L5825)
- JIT: [`emitBespokeShapesIdx`](https://github.com/facebook/hhvm/blob/d911b10bc976d0a931/hphp/runtime/vm/jit/irgen-bespoke.cpp#L1030) has handling for known layouts

### Typing

#### Type errors: overview

Restrict shape destructuring to known fields:

```c#
let $sh : shape(‘a’ => int, …) = ….
shape(‘a’, ‘b’, ...) = $sh; // Error, ‘b’ is not a known field of shape(‘a’ => int, …)
```

In closed shape destructuring patterns (no … in the pattern) all required fields must be listed:

```c#
let $sh : shape(‘a’ => int, ?'b' => int, 'c' => int) = ….
shape(‘a’, ?‘b’) = $sh; // Error, ‘c’ is missing in shape destructuring pattern. 'c' is required in shape('a' => int, ?'b' => int, 'c' => int, ...)
```

Ellipsis is required for destructuring an open shape:

```c#
let $sh : shape(‘a’ => int, …) = ….
shape(‘a’, ...) = $sh; // OK
shape(‘a’) = $sh; // Error, missing ellipsis: ... is required at the end of the field list when destructuring an open shape
```

Closed tuple destructuring patterns should respect arity

```c#
let $tup : tuple(int, int) = ….
tuple($x) = $sh; // Error: expected tuple pattern of arity 2 and got tuple pattern of arity 1
```

Ellipsis is required for destructuring open tuples:

```c#
let $tup : tuple(int, ...) = ….
tuple($x, ...) = $tup; // OK
```

#### Type warnings

For monotonicity of type checking, attempting to destructure an optional field as required will be a warning instead of an error. Example:

```c#
$sh = shape(‘x’ => 1, ‘y’ => 1);
shape(‘x’, ?‘y’) = $sh; // warn
```

#### Typing judgments

We use the following notation:

```
G                    typing environment, maps variables to types
G |-[m] p : T -| G'  pattern p at type T in mode m produces bindings G'
m                    mode: exact or nullable, used for dealing with patterns for optional keys
{ k : S | r }        shape type with required field k where k inhabits S
{ ?k : S | r }       shape type with optional field k where k inhabits S
T[i]                 type of i-th element in tuple type T (0-indexed)
?T                   nullable type, equivalent  to T | null, ??T = ?T
required(T)          set of required field names in shape type T
closed(T)            T is a closed shape (no ... in the type)
arity(T)             number of elements in tuple type T
keys(P)              set of field names in pattern P
G_0 + .. + G_n       disjoint union of typing environments (well-defined
                     because naming rules ensure no variable is bound twice)
..                   meta-level ellipsis uses two dots `..`, distinct from
                     the surface-syntax open marker ...
```

The judgment `G |-[m] p : T -| G'` reads: "in environment `G`, pattern `p` checked against type `T` in mode `m` produces extended environment `G'`."

The mode `m` is either **exact** or **nullable**:

- **exact** \-- the target value is known to have type `T`. Used when all ancestor fields are required.
- **nullable** \-- the target value might be `dict[]` or `null` instead of a value of type `T`, because some ancestor field was optional. Variables receive nullable types.

**DESTRUCTURE** \-- top-level entry point. We always start in exact mode:

```
  G |- e : T
  G |-[exact] p : T -| G'
----------------------------------------- DESTRUCTURE
G |- (p = e) -| G'
```

**VAR** \-- variable pattern (leaf). Mode determines nullability:

```
--------------------- VAR-EXACT
G |-[exact] $x : T -| G, $x : T


--------------------- VAR-NULLABLE
G |-[nullable] $x : T -| G, $x : ?T
```

In exact mode, the variable receives the exact type. In nullable mode, the variable receives the nullable type, because the value might be `null`.

**VAR-DISCARD** \-- discard variable `$_`. Ignores the value:

```
--------------------- VAR-DISCARD
G |-[_] $_ : T -| G, $_ : null
```

\> recall that we set $\_ to null to avoid having evaluation order be observable.

When the variable is `$_`, the mode is irrelevant. The type is `null` because the runtime always assigns `null` without accessing the value. `$_` may appear multiple times in a pattern (it is the only variable exempt from the uniqueness requirement).

**FIELD-REQ** \-- required shape field. Passes mode through unchanged:

```
  T = { k : S | r }
  G |-[m] p : S -| G'
--------------------- FIELD-REQ
G |-[m] ('k' => p) : T -| G'
```

The inner pattern is typed at the field's type `S`. The mode `m` is inherited from the enclosing context \-- this is how nullable mode propagates through required fields under an optional ancestor.

**FIELD-OPT** \-- optional shape field. Switches to nullable mode:

```
  T = { ?k : S | r }
  G |-[nullable] p : S -| G'
--------------------- FIELD-OPT
G |-[_] (?'k' => p) : T -| G'
```

The inner pattern is typed at `S` (the field's declared type, not `?S`) in **nullable** mode. The input mode is irrelevant (written as `_`).

The nullability is NOT applied to the type `S` here \-- it is pushed down to the leaves via VAR-NULLABLE. This is essential: if we typed the inner pattern at `?S`, then SHAPE-EXACT (below) could not fire, because `?S` is a nullable type, not a shape type. By keeping the type as `S` and using nullable mode instead, compound patterns (shapes, tuples) can recurse normally while leaves correctly receive nullable types.

**SHAPE-EXACT** \-- closed shape pattern with all required fields present:

```
  closed(T)
  required(T) \subseteq keys(P)
  G |-[m] f_i : T -| G_i    for each field f_i in P
----------------------------------------- SHAPE-EXACT
G |-[m] shape(f_0, .., f_n) : T -| G_0 + .. + G_n
```

**SHAPE-OPEN** \-- open shape pattern (with `...`), ignoring unmentioned fields:

```
  G |-[m] f_j : T -| G_j    for each field f_j in P
----------------------------------------- SHAPE-OPEN
G |-[m] shape(f_0, .., f_n, ...) : T -| G_0 + .. + G_n
```

**TUPLE-EXACT** \-- tuple pattern matching all positions:

```
  arity(T) = n
  G |-[m] p_i : T[i] -| G_i    for each i in 0..n-1
----------------------------------------- TUPLE-EXACT
G |-[m] tuple(p_0, .., p_{n-1}) : T -| G_0 + .. + G_{n-1}
```

**TUPLE-OPEN** \-- tuple pattern matching a prefix (with `...`):

```
  k <= arity(T)
  G |-[m] p_i : T[i] -| G_i    for each i in 0..k-1
----------------------------------------- TUPLE-OPEN
G |-[m] tuple(p_0, .., p_{k-1}, ...) : T -| G_0 + .. + G_{k-1}
```

**TUPLE-ENTRY-OPT** \-- optional tuple entry (`optional` keyword). Switches to nullable mode, analogous to FIELD-OPT for shapes:

```
  G |-[nullable] p : T[i] -| G'
--------------------- TUPLE-ENTRY-OPT
G |-[_] (optional p at position i) : T -| G'
```

#### Typing examples

##### Example 1: Simple (all required, closed)

```
let $point: shape('x' => int, 'y' => int) = EXPR;
shape('x' => $x, 'y' => $y) = $point;
```

Derivation (read bottom-to-top; premises above the line, rule name to the right):

```
D_1:
                            -------------------------------- VAR-EXACT
  T = { 'x' : int | r }    G |-[exact] $x : int -| {$x:int}
  ----------------------------------------------------------- FIELD-REQ
  G |-[exact] ('x' => $x) : T -| {$x : int}

D_2:
                            -------------------------------- VAR-EXACT
  T = { 'y' : int | r }    G |-[exact] $y : int -| {$y:int}
  ----------------------------------------------------------- FIELD-REQ
  G |-[exact] ('y' => $y) : T -| {$y : int}

  closed(T)    required(T) \subseteq keys(P)    D_1    D_2
  ---------------------------------------------------------- SHAPE-EXACT
  G |-[exact] shape('x'=>$x, 'y'=>$y) : T -| {$x:int, $y:int}

  G |- $point : T    (above)
  ---------------------------------------------------------- DESTRUCTURE
  G |- (shape('x'=>$x, 'y'=>$y) = $point) -| {$x:int, $y:int}
```

Both variables receive exact (non-nullable) types because all fields are required and the pattern is closed.

##### Example 2: Open pattern on a closed shape

```
let $point: shape('x' => int, 'y' => int) = EXPR;
shape('x' => $x, ...) = $point;
```

Derivation:

```
D_1:
                            -------------------------------- VAR-EXACT
  T = { 'x' : int | r }    G |-[exact] $x : int -| {$x:int}
  ----------------------------------------------------------- FIELD-REQ
  G |-[exact] ('x' => $x) : T -| {$x : int}

  D_1
  ----------------------------------------------------------- SHAPE-OPEN
  G |-[exact] shape('x'=>$x, ...) : T -| {$x : int}

  G |- $point : T    (above)
  ----------------------------------------------------------- DESTRUCTURE
  G |- (shape('x'=>$x, ...) = $point) -| {$x : int}
```

##### Example 3: Nested optional with open inner shape

```
let $person: shape(
  'name' => string,
  ?'address' => shape('city' => string, 'state' => string, ?'zip' => string),
) = EXPR;
shape('name' => $name,
      ?'address' => shape('city' => $city, ...)) = $person;
```

Derivation (abbreviations: \[e\] \= exact, \[n\] \= nullable):

```
/* Detailed source-code truncated for AI context efficiency. */
```

#### Correctness of type checking

* For any given variable used in a tuple or shape pattern in a destructuring assignment, our runtime semantics say how to evaluate and our typing judgments say how to type. The value of each variable assigned in a destructuring pattern should inhabit the set corresponding to the type the judgments give the type variable.
* Evaluation order should not be observable. This is also covered in the formalization linked above, but hopefully is also an obvious property given the design.

[See formalization](https://fburl.com/workplace/jsgp3bjp), with the caveat that in the formalization we pretend `null` is the sentinel value for optional fields rather than `dict[]`, which shouldn’t make a difference (`idx(null, ‘some_key’) === idx(dict[], ‘some_key’) === null`).

### Punning in shape expressions

We introduce punning in shape construction:

```
$point = shape($x, $y);
```

Motivation 1: Users will likely expect punning to work on the right hand side, given the symmetries between construction and destruction.

Construction with punning:

```
$point = shape($x, $y);
```

Destruction with punning:

```
$shape('x', 'y') = $point;
```

Motivation 2: users are likely to love it

Motivation 3: Punning in shape expressions is familiar from other languages, including Flow.

The runtime and typing rules follow from:

```
shape($x, $y);
```

desugars to:

```
shape('x' => $x, 'y' => $y);
```

Punned and non-punned fields can be freely mixed: `shape($x, 'y' => $y)` also desugars to `shape('x' => $x', 'y' => $y)`.

Punning is not allowed for `$_` – doing so could be misleading, since we assign `$_` to `null`. There are only [a few](https://fburl.com/hack_shape_destructuring_hip) uses of a ‘\_’ shape key in practice, so the restriction won’t arise often.

Punning similar to the feature proposed here is also found in JS/TS/Flow, OCaml, Haskell (with an [extension](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/record_puns.html)):

## Drawbacks

We are adding something to the language, which humans and agents will have to learn how to read, write, and modify. This does have a cost.

That said, the features are similar enough to those of languages like Flow and TypeScript that it doesn’t seem to be hard for LLMs like Claude to understand it, even though Claude was a little doubtful that the code was valid. Transcripts (grain of salt, small sample size): [1](https://claude.ai/share/0122e9fc-c97d-4ac8-82d5-e27059bed35e), [2](https://claude.ai/share/984881d6-fecb-4460-b852-3be9591508ca), [3](https://claude.ai/share/7fb7edf6-400d-4475-a128-ba154b73add4), [4](https://claude.ai/chat/d761c1c4-6fa0-4a7c-92b5-0cb41967434c), [5](https://claude.ai/share/f864e7ac-f49d-4a74-95e3-2ca82d52c48e), [6](https://claude.ai/share/ced5b6ca-210d-413d-a614-a9fbc57d3252), 82-93% of 50 subagents in a transcript.

## Future Possibilities

### Future work: expressive defaults

> Note: maybe even this could be optimized sometimes by HHVM: `shape(.....) = merge($defaults, $sh)`

The current proposal does not distinguish between missing fields and fields whose value is `null`. The distinction does not seem to be generally relevant for destruction, and the information can be recovered via `Shapes::keyExists`.

That said, sometimes `null` is not the best default and devs want to default to things like `vec[]`. However, type-directed defaulting would require type-directed bytecode.

A previous proposal considered `shape('x' ?? 2 => $x)`, but there were many open questions about syntax and semantics.

[As Kendall suggested](https://fburl.com/workplace/2w0s2x1b), by supporting shape merge on the right-hand-side, we can avoid the contentious syntax and unknown semantics of defaults in destructuring.

A future HIP (in progress, contact Michael Thomas) may address shape merge and enable more expressive defaults via an expression on the RHS.

### Potential future work: `as` in patterns

It’s common in pattern matching to allow binding of nested patterns. `shape(‘x’ => (shape(‘y’ => $y, …) as $inner))` , which would bind `$inner` to the result of the inner shape pattern.

We defer “as patterns” to potential future work rather than planning it now, because:
- `as` patterns typically don’t even save devs much keyboarding. The “long version” in this example is only 5 more characters and (arguably) easier to read:
`shape(‘x’ => $inner);`
`shape(‘y’ => $y, …) = $inner;`
- Given nested destructuring with `list` is uncommon today, we expect nesting to be uncommon for shape and tuple destructuring. A special language feature for rare cases of uses of something already rare may impair learnability of `as` in destructuring.
- Nothing in the current proposal precludes adding `as` patterns later. Or adding later something of equivalent expressiveness– Andrew Kennedy pointed out that “and patterns” are at least as expressive.

### Potential future work: weird semantics

We *could* allow things that lead to observable evaluation order, having arbitrary side effects, etc. The most compelling such feature, imo, is to allow property accesses:

`shape(‘x’ => $this->x, ‘y’ => $this->y) = $point;`

Some reasons not to allow property access in destructuring patterns are:
- Destructuring into properties is currently allowed via `list` (which works on tuple, vec, containers) but the feature is **essentially unused** in prod code (see Relevant Data above).
- Allowing complex lvalues would make it harder to maintain the language and to enable optimized pattern matching (or make pattern matching and destructuring very different), etc.
- Aren’t in any other language except PHP, afaict, and are not theoretically well-founded.

I think it’s safer to do the simpler thing first and then do the weird thing that only PHP does only if we must.

### Potential future work: variadics

The current proposal does not support the following operations:

```
shape('x' => 'y', ...$rest) = $open_shape;
tuple('x', 'y', ...$rest) = $tuple
```

We could consider one or both of these in a future proposal, though I worry a bit about perf of shape variadics.

### Potential future work: pattern matching

The hope is that if/when we introduce patterns for shapes and tuples, the syntax and semantics will be learnable and predictable for agents+users because destructuring just *is* irrefutable pattern matching. Re the implementation: we haven't closed off opportunities like easily-optimizable and theoretically well-understood pattern matching.

### Potential future work: deprecate `list`

I suspect that, long-term, large codebases would be in a better place with shape/tuple and no `list`. After launch, I would like to do medium-priority work to gradually codemod `list` to `tuple` in non-generated code where doing so can’t change runtime behavior. Then, ~six months later, evaluate feasibility and priority of more aggressive `list` deprecation or removal. This gap gives us time to incorporate user feedback and our experience from the codemodding.

### Potential future work: rationalize `$_` and `_` (dollar underscore and wildcard)

Status quo: `$_` really wants to be a “discard/throwaway” variable and is often used that way, but the type checker only sometimes special-cases it, and often treats `$_` as if it were any other variable ([example](https://fburl.com/phabricator/hcf2pg5s)).

State after we implement this HIP: users will need to know to use `_` to discard things in some places and `$_` in other places. That sucks.

Desired state: use `_` consistently for wildcard. Allow it most places a variable is used. Codemod away `$_`.

## Prior Art

- [Previous proposal by Scott Owens](https://fburl.com/diff/njw23trz).
- [Destructuring in PHP](https://www.php.net/manual/en/language.types.array.php#language.types.array.syntax.destructuring)
- Punning in JS/TS/Flow, Ocaml, and Haskell ([extension](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/record_puns.html))
- Destructuring in JS and pattern-matching in `let` in Rust and OCaml
- Python pattern matching as [an example of how to violate our language design principles](https://www.hillelwayne.com/post/python-abc/)
- For enabling pattern matching on arbitrary userland types with very flexible semantics (not necessarily a good idea):
  - Kotlin’s [destructuring declarations](https://kotlinlang.org/docs/destructuring-declarations.html)
  - [Python class patterns](https://peps.python.org/pep-0634/#class-patterns)
  - Scala [extractor objects](https://docs.scala-lang.org/tour/extractor-objects.html)


## Appendix: Correctness of Typing

Disclaimers:
- Canonical source runtime semantics of shape and tuple destructuring is given above in the HIP proper. This document is designed to give some flavor of what it means for typing for destructuring to be “correct”.
- An oversight: the following does *not* say how destructuring interacts with union types nor dynamic, though the behavior is strongly suggested by this HIP.

### Runtime

We described the operational semantics by example in the [Runtime "as if" semantics](#runtime-as-if-semantics) section above. I’ll take a stab at something more symbolic here, though it’s challenging to read and write.

Note that this is an “as if” semantics, and may not represent the actual implementation–the important thing is that the real implementation should produce the same bindings of variables to values as this as-if semantics.

This is a supplement to the main HIP above.

### Notation

The runtime desugaring is defined by three mutually recursive functions.

```
| Function | Signature | Purpose |
|---|---|---|
| `desugar` | `(||pat||, expr) -> S` | Top-level entry point. Binds `expr` to a fresh temporary and matches in EXACT mode. |
| `match` | `(||pat||, t, mode) -> S` | Matches pattern against variable `t` in the given mode. |
| `entry` | `(||field||, t, mode) -> S` | Processes a single shape field against variable `t`. |
```

Arguments:

- **`||pat||`** \-- a destructuring pattern (see Syntax section), written in denotation bars `|| ||` to mark it as surface-level syntax being interpreted by the metatheory.
- **`||field||`** \-- a shape entry: `||'k' => pat||` or `||?'k' => pat||`.
- **`expr`** \-- an arbitrary Hack expression.
- **`t`** \-- a Hack variable (compiler-generated temporary or source variable).
- **`mode`** \-- EXACT or NULLABLE:
  - **EXACT** \-- required fields use direct access `t['k']`.
  - **NULLABLE** \-- required fields use `Shapes::idx(t, 'k')`, because `t` might be `dict[]` or `null`.
- **`S`** \-- an ordered sequence of Hack statements. `\cup` denotes concatenation.
- **"fresh temp"** \-- a compiler-generated variable (e.g., `$__destructure_0`).
- **`_`** \-- wildcard: any value; the parameter is unused.
- **`~>`** \-- "desugars to": used in derivation annotations to show how a metatheory function call produces generated Hack code.

**`Shapes::idx` semantics** (for reference):

- `Shapes::idx(t, 'k')` \-- returns `t['k']` if key `'k'` is present, else `null`.
- `Shapes::idx(t, 'k', default)` \-- returns `t['k']` if key `'k'` is present, else `default`.
- When `t` is `null`: returns the default (`null` for 2-arg, the explicit default for 3-arg).

**`HH\idx` semantics** (for tuples):

- `HH\idx(t, i)` \-- returns `t[i]` if index `i` is present, else `null`.

### Rules

**Top-level:**

```
desugar(||pat||, e) =
  let t = fresh temp
  { t = e; } \cup match(||pat||, t, EXACT)
```

**M-Var** \-- variable pattern (leaf):

```
match(||$x||, t, _) = { $x = t; }
```

**M-Discard** \-- discard variable `$_`. Skips the value:

```
match(||$_||, t, _) = { $_ = null; }
```

The runtime does not read `t`; it unconditionally assigns `null`.

**M-Shape** \-- shape pattern:

```
match(||shape(E_1, .., E_n)||, t, mode) =
  entry(||E_1||, t, mode) \cup .. \cup entry(||E_n||, t, mode)
```

**E-Req-Var** \-- required field, variable (`$x != $_`):

```
entry(||'k' => $x||, t, EXACT)    = { $x = t['k']; }
entry(||'k' => $x||, t, NULLABLE) = { $x = Shapes::idx(t, 'k'); }
```

When the variable is `$_`, the field access is skipped:

```
entry(||'k' => $_||, t, _) = { $_ = null; }
```

**E-Req-Nested** \-- required field, nested pattern:

```
entry(||'k' => pat||, t, EXACT) =
  let t' = fresh temp
  { t' = t['k']; } \cup match(||pat||, t', EXACT)

entry(||'k' => pat||, t, NULLABLE) =
  let t' = fresh temp
  { t' = Shapes::idx(t, 'k', dict[]); } \cup match(||pat||, t', NULLABLE)
```

**E-Opt-Var** \-- optional field, variable (`$x != $_`):

```
entry(||?'k' => $x||, t, _) = { $x = Shapes::idx(t, 'k'); }
```

When the variable is `$_`, the field access is skipped:

```
entry(||?'k' => $_||, t, _) = { $_ = null; }
```

**E-Opt-Nested** \-- optional field, nested pattern:

```
entry(||?'k' => pat||, t, _) =
  let t' = fresh temp
  { t' = Shapes::idx(t, 'k', dict[]); } \cup match(||pat||, t', NULLABLE)
```

`Shapes::idx(t, 'k', dict[])` returns: the inner shape when `'k'` is present and non-null; `null` when `'k'` is present but null (possible with nullable field types); `dict[]` when `'k'` is absent. In the latter two cases, subsequent `Shapes::idx` calls on `null` or `dict[]` return their defaults, so all descendant variables become `null`.

**M-Tuple** \-- tuple pattern:

```
match(||tuple(p_0, .., p_{k-1})||, t, mode) =
  { $x_0 = t[0]; } \cup match(||p_0||, $x_0, mode)
  \cup .. \cup
  { $x_{k-1} = t[k-1]; } \cup match(||p_{k-1}||, $x_{k-1}, mode)
```

Each element is accessed by index. When `p_i` is a variable, `match` reduces to M-Var and the intermediate temp is unnecessary (an implementation may optimize this away).

**T-Entry-Opt** \-- optional tuple entry (`optional` keyword):

```
match(||optional p at position i||, t, mode) =
  let t' = fresh temp
  { t' = HH\idx(t, i); } \cup match(||p||, t', NULLABLE)
```

`HH\idx(t, i)` returns `t[i]` if index `i` is present, else `null`. This is analogous to `Shapes::idx` for shapes.

**Ellipsis (`...`) is a runtime no-op.** The `...` marker in both shape and tuple patterns affects only which entries appear in the pattern. The runtime rules iterate over the entries present in the pattern and ignore everything else. There is no generated code for `...` itself.

### Runtime example

For more examples, see the [Runtime "as if" semantics](#runtime-as-if-semantics) section above.
The result of desugaring (right-hand-side of \~\>) is a sequence of assignment statements

```
desugar(||shape('name' => $name,
               ?'address' => shape('city' => $city, ...))||,
        $person)

  = { $t1 = $person; }
    \cup entry(||'name' => $name||, $t1, EXACT)
         ~> { $name = $t1['name']; }
    \cup entry(||?'address' => shape('city' => $city, ...)||, $t1, EXACT)
         = { $t2 = Shapes::idx($t1, 'address', dict[]); }   -- mode switches to NULLABLE
           \cup entry(||'city' => $city||, $t2, NULLABLE)
                ~> { $city = Shapes::idx($t2, 'city'); }
```

**Desugared Hack:**

```
$t1 = $person;
$name = $t1['name'];                              // direct access (EXACT)
$t2 = Shapes::idx($t1, 'address', dict[]);        // idx + dict[] fallback
$city = Shapes::idx($t2, 'city');                  // idx (NULLABLE -- required, but ancestor optional)
```

It follows from the desugaring that

- when `'address'` is present (such as `shape('city' => 'London', 'state' => 'NY', 'zip' => 'SW1')`), pattern `?'address' => shape('city' => $city, ...)` contributes to the environment the binding `$city = 'London'`.
- When `'address'` is absent, `$t2 = dict[]` and `Shapes::idx(dict[], 'city')` returns `null`, so the contribution to the environment is `$city = null`.

## Correspondence between typing and runtime

Each [typing judgment given above](#typing-judgments) has a direct counterpart in the runtime semantics, so an induction proof shows our correctness property: that the value assigned to a variable by the runtime in destructuring assignments matches the type assigned by the type checker.

**Semantic Soundness.**\=def If `G |- (p = e) -| G'` is derivable and `e` evaluates to a value `v \in [[T]]` where `G |- e : T`, then executing the desugared code of `p = e` on value `v` binds each variable `$x \in dom(G') \ dom(G)` to a value `v_x \in [[G'($x)]]`.

We have an [executable proof on github](https://github.com/mheiber/lapes). Note that the Lean development uses `$_` instead of wildcard (`_`): a minor difference that actually makes correctness harder to show.

### Worked correspondence: Example 1 (flat)

```
// T = shape('x' => int, ?'y' => int)
shape('x' => $x, ?'y' => $y) = $point;
```

| Step | Typing | Runtime |
| :---- | :---- | :---- |
| Top-level | DESTRUCTURE: exact | `desugar`: EXACT |
| Field `'x'` | FIELD-REQ \[exact\] \-\> VAR-EXACT: `$x : int` | E-Req-Var (EXACT): `$x = $t['x']` |
| Field `?'y'` | FIELD-OPT \-\> VAR-NULLABLE: `$y : ?int` | E-Opt-Var: `$y = Shapes::idx($t, 'y')` |

Typing assigns `$x : int` \-- runtime produces `$t['x']` which has type `int`. Typing assigns `$y : ?int` \-- runtime produces `Shapes::idx($t, 'y')` which has type `?int`.

### Worked correspondence: Example 2 (nested optional)

```
// let $person: shape('name' => string, ?'address' => shape('city' => string, ?'zip' => string)) = EXPR;
shape('name' => $name,
      ?'address' => shape('city' => $city, ?'zip' => $zip)) = $person;
```

| Step | Typing | Runtime |
| :---- | :---- | :---- |
| Top-level | DESTRUCTURE: exact | `desugar`: EXACT |
| `'name'` | FIELD-REQ \[exact\] \-\> VAR-EXACT | E-Req-Var (EXACT) |
|  | `$name : string` | `$name = $t1['name']` |
| `?'address'` | FIELD-OPT: mode \-\> nullable | E-Opt-Nested: mode \-\> NULLABLE |
| (access) | (inner typed at `S`, not `?S`) | `$t2 = Shapes::idx($t1, 'address', dict[])` |
| `'city'` (under nullable) | FIELD-REQ \[nullable\] \-\> VAR-NULLABLE | E-Req-Var (NULLABLE) |
|  | `$city : ?string` | `$city = Shapes::idx($t2, 'city')` |
| `?'zip'` (under nullable) | FIELD-OPT \-\> VAR-NULLABLE | E-Opt-Var |
|  | `$zip : ?string` | `$zip = Shapes::idx($t2, 'zip')` |

The mode transition happens at the same point in both systems: `?'address'`. Above it, `$name` gets exact type `string` and direct access `$t1['name']`. Below it, `$city` and `$zip` get nullable types and `Shapes::idx` access.

Typing assigns `$city : ?string` because FIELD-REQ passes `string` in nullable mode and VAR-NULLABLE wraps it to `?string`.

Runtime produces `Shapes::idx($t2, 'city')`, which returns `string` when `$t2` is the actual address shape, and `null` when `$t2` is `dict[]`.

The value is always in `[[?string]]`.

---

### Hphp Hack Doc HIPs Sound Dynamic (`hphp/hack/doc/HIPs/sound_dynamic.md`)

# HIP: Sound `dynamic` type

## Motivation

The `dynamic` type was created to type legacy PHP code where types are optional:

```
// before
function f($a) { return 3; }
// after
function f(dynamic $a): dynamic { return 3; }
```
The dynamic type admits any operation (subscripting, member access, etc.) and
does not have any direct subtypes or supertypes beyond `nothing` and `mixed`.
Its semantics are defined by a non-transitive relation called *coercion*. Any
type can coerce to dynamic at enforcement points, and dynamic is allowed to
coerce to types that are enforced at runtime:
```
function f(dynamic $d): int { return $d; /* ok */ }
```

Unfortunately, coercion to dynamic is unsound for arbitrary values. Consider a
simple generic Box class:

```
class Box<T> {
  public function __construct(private T $t) {}
  public function set(T $t): void {
    $this->t = $t;
  }
  public function get(): T {
    return $this->t;
  }
}

function evil(dynamic $d1, dynamic $d2): void {
  $x = $d1->get(); // $x: dynamic
  $d1->set($d2);
}

function expect_int(int $i): void {}

function test(): void {
  $b = new Box(4);
  $an_int = $b->get(); // $an_int: int = 3
  evil($b, "hello");
  $not_an_int = $b->get(); // $not_an_int: int = "hello" (!!!)

  expect_int($not_an_int); // TypeHintViolationException
}
```

We allow the `Box<int>` to coerce to `dynamic`, but this is unsafe because the
function writes a `string` into the boxed value without our knowledge. This
means that the `$not_an_int` variable has the wrong type, and we get an
exception in a program that has no Hack errors.

With a sound dynamic type, we can fully eliminate error suppression in Hack,
have trustworthy types, and perform global analysis to constrain the level of
dynamism in the codebase. As it has global effect, which makes per-file
experimental gating impractical, the sound dynamic feature is controlled by
`.hhconfig` flag `enable_sound_dynamic_type`.

## Proposal

We propose the following changes to the `dynamic` type to make it so that any
value typed `dynamic` safely supports dynamic operations.

First, we eliminate coercion entirely, requiring operations with `dynamic` to
use subtyping instead. Next, we define a covariant type constructor `~`
(pronounced "like") such that `~T` represents the union of `dynamic` and `T`. To
describe types that support dynamic, we define
```
newtype supportdyn<+T> as T = (T & dynamic);
```
and introduce a new subtyping relation called *dynamic-aware subtyping* (written
`<D:`), which adds the following simple rule:
```
-----------------------------
supportdyn<mixed> <D: dynamic
```
We also introduce the `upcast` expression to allow us to take values into
`dynamic`. Its typing rule is
```
e : U    U <D: T
----------------
 e upcast T : T
```
Finally, we define normal subtyping (written `<:`) rules for `supportdyn<T>`.

### Like types

These are the simplest part of the proposal. We use `~T` to signal that a value
may have type `T`, but it may also be a dynamic value. Due to standard union
rules, a `~T` only supports operations that are valid for `T` (`dynamic` admits
any operation). For example, it is a type error to call a method on `~string`,
even if the call would succeed at runtime.

Many PHP builtins counterintuitively return `false` in exceptional cases, and a
value otherwise, e.g. `file_get_contents`. If we were to type the function
return as `(string | bool)`, every use site must now statically check this
union, even if the overwhelming majority of callsites to not observe `false`.
A less invasive approach is to give the function a type `~string`. Users are now
aware that they may not be holding a `string`, but they have a choice in whether
to check. The rest of the proposal describes how like types are used to remove
lying types like the one observed in the introduction.

Like types are transparent to the runtime, enforced as their inner type. A
function that expects a `~string` will throw if you pass it a `float`. We have
also considered a variant where like types are not enforced -- none of the
semantics of `dynamic` require enforcement. However, we expect to mainly be
weakening ill-typed code rather than adding types to untyped code, so this
approach preserves existing enforcement behavior.

### Upcast

Hack has an unsafe cast feature that can cast a value to any type. Its purpose
is to aid in the elimination of error-suppression comments in Hack:
```
enum E: int { A = 1; }
function f(int $i): E {
  /* HH_FIXME[4110] trust me */
  return $i;
}

==>

function f(int $i): E {
  return HH\FIXME\UNSAFE_CAST<int, E>($i);
}
```

With sound dynamic, we can replace `UNSAFE_CAST<T1, T2>(e)` with `e upcast ~T2`.
Dynamic-aware subtyping is defined naturally for unions, so this is equivalent
to asking if `int <D: dynamic` (more on that in the following section). To
complete the example, we must weaken `f`'s return type.

```
function f(int $i): ~E {
  return $i upcast ~E;
}
```
A consequence of this is we constrain any value that had flowed into an unsafe
cast to support dynamic. Further, it is not legal to unsafe cast arbitrary
`mixed` values, because `mixed` is not a dynamic-aware subtype of `dynamic`.
Instead, the value's type may be at most `supportdyn<mixed>`.

```
function f(mixed $m1, supportdyn<mixed> $m2): void {
    $m1 upcast ~int; // error
    $m2 upcast ~int; // ok
}
```
Also, since we eliminated coercion, technically all non-dynamic values must be
upcast to dynamic in order to flow into dynamic positions. However, for
ergonomics, we allow implicit upcasting when the target type is exactly
`dynamic`
```
function f(): dynamic {
    return 4; // ok
}

function g(): ~string {
    return 4; // not ok
    return 4 upcast dynamic; // ok
    return 4 upcast ~string; // also ok
}
```
The same applies to parameters and properties. Upcast expressions are erased at
compile time.

### <<__SupportDynamicType>>

The type `supportdyn<T>` means: the set of all values that are belong to `T` and
support dynamic operations. Alternatively, this can be read as the
*intersection* of the sets of values represented by `T` and `dynamic`.
Naturally, this means that
```
----------------------------
dynamic <: supportdyn<mixed>
```

> Note: `supportdyn<mixed>` and `dynamic` represent **the exact same set of
values!**

Unlike dynamic values, however, a value with type `supportdyn<mixed>` does *not*
support arbitrary operations, just as the `mixed` type does not.

What does it mean for a value to support dynamic? Above, we showed an example of
calling a method with an arbitrary `dynamic` value. We may also use the returned
value of any operation that succeeds as dynamic:
```
function f(dynamic $d): void {
    $d1 = $d->someMethod();
    $d2 = $d->someProp;
    $d3 = $d[0];
    // etc
}
```

The main idea here is to prevent users from getting a `dynamic` reference to
anything that does not protect its invariants. Primitives like numbers,
`string`, `null`, and booleans all support dynamic trivially. Their values are
immutable and their runtime properties cannot be changed by dynamic operations.
Tuples and closed shapes support dynamic if their components do. *Open* shapes
do not support dynamic, as we cannot be confident the unspecified fields contain
values that support dynamic. To upcast an open shape to dynamic, it must be
wrapped as `supportdyn<shape(...)>`, which requires that the remaining fields
support dynamic. `mixed` and `nonnull` similarly do not support dynamic.

### Functions

Consider an arbitrary function `f`:

```
// current code
function f(int $i): string { <body> }

function evil(dynamic $d1, dynamic $d2): dynamic {
    $x = $d1($d2); // $x: dynamic
    return $x;
}

function test(dynamic $d): void {
    $y = evil(f<>, $d); // $y: dynamic
}
```
Forget passing a dynamic value to `f`, we can get a handle to the whole function
as dynamic and do as we please! Therefore, to ensure `f` can be safely used in a
dynamic context, we must require that it is still type-safe when all of its
parameters are dynamic, and that its return value may be used dynamically. We
introduce a new attribute `<<__SupportDynamicType>>`:
```
<<__SupportDynamicType>>
function ff(T1 $t1): T2 { <body> }
```
that works by checking an overloaded definition of `ff` with a dynamic
signature:
```
function ff(T1 $t1): T2 {
    <body> // typechecks
}
function ff(dynamic $t1): dynamic {
    <body> // must also typecheck
}
```
The type of `ff` is `supportdyn<(function (int): string)>`, or alternatively
`(function (int): string) & (function(dynamic): dynamic)`. That is, if you call
`ff` with an `int`, you will get back a `string`. If you call `ff` with
`dynamic`, you get back `dynamic`. Finally, if you call `ff` with a `~int`, by
the application of the intersection, you will get back a `~string`. Of course,
if the body of `ff` has an error when we check it with a dynamic signature, then
you get an error that `ff` cannot be marked `<<__SupportDynamicType>>`.

More precisely, a function type `function(T1, ..., Tn): Tr <: supportdyn<mixed>`
if `dynamic <D: T1 & ... & dynamic <D: Tn` and `Tr <D: dynamic`.

### Classes

Recall the previous `Box` example, with the constructor ommitted for clarity:
```
class Box<T> {
  private T $t;
  public function set(T $t): void {
    $this->t = $t;
  }
  public function get(): T {
    return $this->t;
  }
}
```
If a `Box` is to support being used as a dynamic value, then

- all of its methods (incl. inherited) must support dynamic
- any public properties must support dynamic writing and reading
- any class that extends `Box` must also support dynamic

Consider the `set` method:
```
  public function set(T $t): void {
    $this->t = $t;
  }
  public function set(dynamic $t): dynamic {
    $this->t = $t; // type error!
  }
```
In its dynamic version, we are writing a `dynamic` value to the property `t`, so
we will get a type error unless we weaken its type
```
  private ~T $t;
```
and consequently weaken the `get` method
```
  public function get(): ~T {
```

Now let's consider the `get` method:
```
  public function get(): ~T {
    return $this->t;
  }
  public function get(): dynamic {
    return $this->t; // type error!
  }
```
The result of the `get` method must be a valid value that can be typed
`dynamic`, so `get`'s return type `~T` must be be a dynamic-aware subtype of
`dynamic`. We can achieve this by constraining the type parameter
```
class Box<T as supportdyn<mixed>> {
```
and we are finally done.
```
<<__SupportDynamicType>>
class Box<T as supportdyn<mixed>> {
  private ~T $t;
  public function set(T $t): void {
    $this->t = $t;
  }
  public function get(): ~T {
    return $this->t;
  }
}
```
For any valid `Box<T>`, we now have `Box<T> <: supportdyn<Box<T>>` and therefore
`Box<T> <D: dynamic`.

Now let's bring back the original example:
```
function evil(dynamic $d1, dynamic $d2): void { ... }
<<__SupportDynamicType>> function expect_int(int $i): void {}

function test(): void {
  $b = new Box(4);
  $an_int = $b->get(); // $an_int: ~int = 3
  evil(
    $b /* upcast dynamic */,
    "hello" /* upcast dynamic */
  );
  $not_an_int = $b->get(); // $not_an_int: ~int = "hello" (ok)

  expect_int($not_an_int); // TypeHintViolationException
}
```
We are no longer lying to the user that `get` only returns `int`s. The call to
`expect_int` will still throw, but this is the price of dynamism. However, if
the user sees a non-dynamic type `int`, they can now be confident that its
runtime value is actually an integer.

### Pessimisation

The process of weakening and constraining types in a codebase to enable `<<__SupportDynamicType>>` and admit existing use of legacy `dynamic` is called *pessimisation*. Users are free to pessimise their code however they like. For
example, we could have instead pessimised the `Box` example as
```
<<__SupportDynamicType>>
class Box<T> {
  private dynamic $t;
  public function set(dynamic $t): void {
    $this->t = $t;
  }
  public function get(): dynamic {
    return $this->t;
  }
}
```

We also allow enforced types to behave as like types when writing so that we do
not need to weaken them:
```
<<__SupportDynamicType>>
class IntBox {
  private int $t;
  public function set(int $t): void {
    $this->t = $t;
    // we're acting like `t` has type `~int` for the dynamic pass's assignment
  }
  public function get(): int {
    return $this->t;
  }
}
```

## Conditionally-dynamic classes

The restriction of `<T as supportdyn<mixed>>` in our `Box` is particularly
onerous for library classes. For example, it precludes Hack containers from
instantiation with classes that do not support dynamic.
```
<<__Sealed(KeyedContainer::class), __SupportDynamicType>>
interface Container<+Tv as supportdyn<mixed>> extends Traversable<Tv> {
```
The Shack formalization of dynamic allows for arbitrary constraints on classes
to support dynamic provided that the core requirements for reading and writing
hold under those constraints. We propose an instance of this by allowing
```
<<__SupportDynamicType>>
class C<T> {}
```
where `C<T> <D: dynamic` if `T <D: dynamic`.  With this constraint, we can now
write the `Box` example without constraining `T`.
```
class Box<T> {
  private ~T $t;
  public function set(T $t): void {
    $this->t = $t;
  }
  public function get(): ~T {
    return $this->t;
  }
}
```
The method `get` passes the `<<__SupportDynamicType>>` check because we can now
assume `T` was instantiated with a subtype of `dynamic`, so the return
requirement `~T <D: dynamic` holds.

## Inheritance

To aid in the transition of untyped hierarchies that leverage error suppression,
we allow methods that return any `T` to override methods that return dynamic,
provided that `T <D: dynamic`. This is similar to the automatic upcasting to
dynamic shown above.

```
<<__SupportDynamicType>>
class A1 {
  public function f(): ~string { ... }
}
<<__SupportDynamicType>>
class A2 extends A1 {
  public function f(): dynamic { ... }
}
<<__SupportDynamicType>>
class A3 extends A2 {
  public function f(): int { ... }
}
```

even though `int </: dynamic` under regular subtyping. This allowance is not
transitive, and so the following is an error

```
<<__SupportDynamicType>>
class B1 {
  public function f(): ~string { ... }
}
<<__SupportDynamicType>>
class B2 extends B1 {
  public function f(): int { ... } // error
}
```

Note that it would be perfectly sound to allow
```
<<__SupportDynamicType>>
class C1 {
  public function f(): ~string { ... }
}
<<__SupportDynamicType>>
class C2 extends C1 {
  <<__Override>>
  public function f(): ~int { ... }
}
```
This is again because `~string` and `~int` represent the exact same set of
values. In fact, `~T1` is semantically equivalent to `~T2` for all types where
`T1 <D: dynamic` and `T2 <D: dynamic`. However, this is not very ergonomic, so
we propose an alternative
```
<<__SupportDynamicType>>
class D1 {
  public function f(): ~string { ... }
}
<<__SupportDynamicType>>
class D2 extends D1 {
  <<__Override, __DynamicOverride>>
  public function f(): ~int { ... }
}
```

The strictest option is to require regular `<:` subtyping for overrides, in
which case the example above must be weakened to
```
<<__SupportDynamicType>>
class E1 {
  public function f(): ~arraykey { ... }
}
<<__SupportDynamicType>>
class E2 extends E1 {
  public function f(): ~int { ... }
}
<<__SupportDynamicType>>
class E3 extends E2 {
  public function f(): int { ... }
}
```
# Future

## Runtime dynamism

Classes and methods can still be referenced via strings in the runtime, which
creates a blind spot for `<<__SupportDynamicType>>`. In general, it is not safe
to say that a class does not support dynamic if there exist runtime dynamic
references to it. We would like to extend the concept of
`<<__DynamicallyCallable>>` to cover these cases, and tie it to
`<<__SupportDynamicType>>`. This would make it so that all dynamically
referenced functions/classes support dynamic, and those that do not would fatal
on dynamic reference.

## Mocking

Function mocking for tests heavily depends on dynamism. We would like to avoid a
situation where a function is only marked `<<__SupportDynamicType>>` so that it
can be used dynamically in a test, and otherwise has fully static usage. With
this in mind, we are considering some restricted primitives to get `dynamic`
handles on non-dynamic targets for testing.

# Decision points
- General syntax
- Which parts of sound dynamic will we ship?
  - `~T` vs. `HH\FIXME\POISON_MARKER<T>`
  - `supportdyn<T>` vs. `HH\FIXME\SUPPORTDYN_MARKER<T>`
  - `e upcast T` vs. `HH\FIXME\UNSAFE_CAST<_, T>` (like types only)
  - naming of `<<__SupportDynamicType>>`
- Inheritance behavior
  - Proposed behavior
    - Lints?
  - Required `<<__DynamicOverride>>`
  - Only regular subtyping

---

### Hphp Hack Doc HIPs Static In Expression Trees (`hphp/hack/doc/HIPs/static_in_expression_trees.md`)

# HIP: `static` in Expression Trees

### Motivation

The goal of this proposal is to make it easier for developers to write clear and concise code in expression trees.

Currently, expression trees don't support `static::` for class constants and static methods. To work around this, developers reference full class names which is especially verbose for XHP:

```
MyDsl`:component:with-a-very-verbose-name::CARD_WIDTH`
MyDsl`:component:with-a-very-verbose-name::getOverlay()`
```

### Proposed Design

This proposes that we update expression trees so that developers can write code like:

```
MyDsl`static::CARD_WIDTH`
MyDsl`static::getOverlay()`
```

If we stop raising a parser error for `static::`, the code above will be desugared to:

```
$v->visitConstant(
  new ExprPos(...),
  nameof static,
  "CARD_WIDTH",
  \static::CARD_WIDTH,
);

$sm0 = static::getOverlay<>;
$v->visitCall(
  new ExprPos(...),
  $v->visitStaticMethod(
    new ExprPos(...),
    $sm0,
  ),
);
```

### Design Considerations

#### Consistent with Hack Semantics

`static::` behaves the same way in expression trees as it does in regular Hack code. This reduces cognitive overhead for developers already familiar with Hack, and prevents subtle bugs that could arise from mismatched expectations.

#### Traits and Non-Final Classes

Hack does not support using `self::` in function references within traits or non-final classes. Because of this restriction, this proposal excludes support for `self::` and instead prioritizes `static::`, which is supported in these scenarios.

#### Cache Correctness

To support caching class constants and static methods, the class context is provided at runtime and should be included in the cache key for correctness:
- In `visitConstant`, the class context is provided as `nameof static`.
- In `visitStaticMethod`, the class context can be resolved at runtime by calling `fb_callable2name` on the function reference.

### Known Limitations

- **Cannot access private members**: Similar to Hack, private members cannot be accessed with `static::` since a child class may also have an identically named private member.

---

### Hphp Hack Doc HIPs Type Refinements (`hphp/hack/doc/HIPs/type-refinements.md`)

Start Date: April 19, 2022

Stage: DRAFT

## Summary

This feature, referred to as _type refinements_, adds the ability to
specify structural constraints on a type. The type subject to the
constraints is said to be _refined_. In this HIP, type refinements
can only constrain type and context constants of a class/interface.
The constraints on such constants can be either _exact_ or _loose_.
An exact constraint fully specifies the constant while a loose
constraint is a combination of lower and/or upper bounds that may
match multiple concrete constants. In the future, type refinements
could be further extended to apply other constraints to other types.

We now give an example use of the feature. Assuming the following
interface definition
```
interface Box { abstract const type T; const ctx C super [defaults]; }
```

type refinements would allow writing types such as:

* `Box with { type T = int; ctx C super [globals]; }`
* `Box with { type T as arraykey }`


## Feature motivation

Type refinements is a feature that is present in the Scala programming
language where it was proved to be useful and safe. It would enhance
Hack in 3 key areas:

* _expressiveness_ -- __complementing where-constraints__; some
  useful constraints cannot be expressed with where constraints,
  but can be expressed with type refinements; e.g., constraints
  on type constants of class-level generic parameters, or constraints
  on an existentially quantified return type.
* _soundness_ -- __more principled basis for type accesses__;
  offering a limited form of dependent types that can help
  replace unsafe where constraints with type accesses and
  needed to soundly model polymorphic contexts, for example.
* _convenience_ -- __reduces boilerplate Hack code__; no intermediate
  interfaces/classes need to be introduced to merely refine the constraints
  on type/context constants.


### Increased expressiveness

Considering the following definitions

```
interface ReadonlyBox {
  abstract const type T;
  public function get(): this::T;
}
interface TBox extends ReadonlyBox {
  public function set(this::T $val): void;
}
```

suppose we want to write a delegator class that exposes the box's
content type via a generic parameter and also provides a getter
function returning a ReadonlyBox; using type refinements, we can
write

```
class SafeBoxDelegator<Tref> {
  public __construct(private Box with { type T = Tref } $w) {}
  public function set(Tref $val): void { $this->b->set($val); }
  public function get(): ReadonlyBox with { type T = Tref } { … }
}
```

The above example is currently inexpressible in Hack for two
reasons. First, the refinement in the constructor would require
class-level where clauses, which are not available today and would
in this case include one occurrence of poorly-supported type
accesses on generic variables. Second, a common attempt to use
where-clauses to type the getter function fails:

```
class SafeBoxDelegator<Tref> {
  ...
  public function get<TIB as ReadonlyBox>(): TIB
          where TIB::T = Tref { … }
```

This function prototype, while usable, is not practically
implementable. To implement it, the programmer has to write code
that returns an object that is in _all_ subtypes of ReadonlyBox
that satisfy the where constraint. This is not the intent of the
programmer, who likely meant to return _one_ subtype of ReadonlyBox
satisfying the constraint. In terms of type theory, `TIB` is
universally quantified when the intent was to existentially
quantify it.

### Replacement for unsound projections

One might attempt to work around the existing limitations of where
clauses by changing the interface of `SafeBoxDelegator` to accept
the Box as a class generic, i.e.:

```
class SafeBoxDelegator<TBox as Box> {
  public __construct(private TBox $w) {}
  public function set(TBox::T $val): void { … }
            // error: ^^^^^^^
  public get(): ReadonlyBox with { type T = TBox::T } { … }
  …                             // error:   ^^^^^^^
}
```

However, the definition above is rejected by Hack because type
accesses on generics are not permitted as type hints. More
generally, type accesses on generics are poorly supported by the
language; for example, the typechecker does not prevent instantiating
generics with class types that have abstract type constants, leading
to unsoundness.
With refinements, it is possible to constrain a class-level generic
parameter to define a type constant of interest. For example

```
class SafeBoxDelegator<TinBox, TBox as Box with { type T = TinBox }> {
  public __construct(private TBox $w) {}
  public function set(TInBox $val): void { $this->b->set($val); }
  public function get(): ReadonlyBox with { type T = TInBox } { … }
}
```

Currently, Hack does allow one way to express a limited form of dependent
typing (i.e., a function where the type of one parameter depends on the
type of another one)

```
function setBox<T1, TBox as Box>(Tbox $b, T1 $v): void
    where T1 = TBox::T
```

However, the boilerplate required in the accepted prototype is a common
source of confusion for users and, as explained above, is only very weakly
shielding them from unsound code.  Using type refinements we write

```
function setBox<T1>(Box with { type T = T1; } $b, T1 $v): void
```

And the function `setBox` can no longer receive as first argument a
value of a type for which we do not precisely know what the associated
type constant is.

As a final benefit, type refinements would serve as a more solid
intermediate representation for dependent context constants in the
typechecker. E.g.

```
interface BoxWithCtx extends Box { const ctx C; }
function useBox(BoxWithCtx $b)[$b::C]: void { … }
```

would be internally represented via refinement of context constant
`C` (which is then further desugared into a type)

```
function useBox<TC>(
    BoxWithCtx with { ctx C = [TC] } $b
)[Ctx]: void
```

this new internal representation would replace the current desugaring
into where-clauses, generics, and projections of questionable soundness.

As a final remark about soundness, we will point out that unlike projections
off types, the type refinements feature has been well studied in the context
of Scala's core [DOT calculus](http://lampwww.epfl.ch/~amin/dot/fool.pdf)
[3,4].

### Less definition boilerplate

In the special case where a type refinement involves only concrete type
and context constants, it is possible to replace the refinement with
an additional subtyping constraint involving an interface that represents
the `with { … }` refinement component. Such interfaces may look like

```
interface LoggedBox extends BoxWithCtx { abstract ctx C super [globals]; }
interface NumericBox extends Box { abstract const type T as num; }
interface LoggedNumericBox extends LoggedBox, NumericBox {}
interface LoggedIntBox extends LoggedBox { const type T = int; }
interface VecNumericBoxPure extends Box {
   abstract const type T as vec<num>;
   const ctx C = [];
}
```

However, this nominal encoding of refinements does not scale: it leads to
an exponential number of artificial interfaces with combinations of
constraints, significantly cluttering code and namespaces. In a sense,
we allow the use of some form of anonymous interfaces.

## Feature definition

We now expose in more details the specifics of type refinements. This
section aims to provide enough foundational information to successfully
complete an implementation of the feature.

### Syntax

A type refinement would be written as `Classish<…> with { … }` where
`{ … }` refines one or more type and context constants.  An example
is a function signature that returns a `Box` whose type constant `T`
is fixed to `int` (the new syntax is overlined):

```
                           _____________________
function getIntBox(): (Box with { type T = int })
```

Each refinement inside `{ … }` should be a valid type/context
constraint, it must contain either an exact `=` constraint or
an arbitrary combination of one or more `as`/`super` bounds.

#### EBNF notation

Add rules:

```
TypeRefinement   ::= TypeSpecifier `with` `{` Refinement [`;`] `}` ;
Refinement       ::= RefineType | RefineCtx | Refinement `;` Refinement  ;

RefineType       ::= `type` QualifiedName ( `=` TypeSpecifier | RefineTypeBounds ) ;
RefineTypeBounds ::= ( `as` | `super` ) TypeSpecifier RefineTypeBounds
                   | ( `as` | `super` ) TypeSpecifier ;

RefineCtx        ::= `ctx` QualifiedName ( `=` CtxList | RefineCtxBounds ) ;
RefineTypeBounds ::= ( `as` | `super` ) CtxList RefineTypeBounds
                   | ( `as` | `super` ) CtxList ;
```

Extend rule:

```
TypeSpecifier ::= (* new *) TypeRefinement | (* old syntax *) … ;
```

Note: Our draft implementation does not allow multiple sequenced
refinements and instead suggests to the user to merge them into
a single refinement.

### Well-formedness

In a refinement `Classish<…> with { … }`, the left-hand side classish
must be a fully applied interface, class, or type alias that
recursively expands to an interface or a class. This constraint means
that we currently do not support refining opaque types aliases (newtype),
the this type, or generics.

There is no constraint on the bounds of a refinement. In particular,
we may well have an unsatisfiable loose refinement. We will see in the
semantics section that the result of such a refinement is simply an
empty type equivalent to the bottom type nothing.

There is no requirement that the type/context constants in the
refinement are defined in the classish they refine. However, in this
case, a linter (TAST check) could warn the user and suggest constants
appearing in the classish with similar names.

### Typing rules

We only give typing rules for type constant refinements, context
constant refinements are handled very similarly.

First, we modify the type access expansion algorithm to have the
following new clauses:

```
taccess((C<…> with { type T = t'; … }), 'T') = t'
taccess((C<…> with { … }),              'T') = taccess(C<…>, 'T')
... (* old clauses *)
```

These additional rules guarantee that any existing typing logic
will successfully take exact refinements into account. Since type
accesses on abstract type constants are poorly handled in general,
we suggest to not implement any specific rules about loose
refinements in the type access expansion logic.

We define a new type member lookup unit. The goal is to eventually
have this unit take care of all the type access expansion logic.
Throughout the transition, we take care to keep this type member
lookup principled and stick to well-understood accesses.

```
lookupty(/*class*/ C, 'T') =
  if 'T' is Abstract(lowerBnd,upperBnd) in C:
    BOUNDED(lowerBnd,upperBnd)
  else if 'T' is Concrete(ty) in C:
    BOUNDED(ty,ty)
  else
    ERROR

lookupty((/*class*/ C with { R }), 'T') =
  if R['T'] is (super lowerBnd as upperBnd):
    merge(lookupty(C, 'T'), BOUNDED(lowerBnd,upperBnd))
  else if R['T'] is (= ty):
    merge(lookupty(C, 'T'), BOUNDED(ty,ty))
  else
    lookupty(C, 'T')

// Merge two lookup results
merge(ERROR,other)                       = other
merge(other,ERROR)                       = other
merge(BOUNDED(lo1,up1),BOUNDED(lo2,up2)) = BOUNDED((lo1|lo2),(up1&up2))
```

We also modify the subtyping algorithm so that it applies the
following rules. The rules are listed by order of precendence.

```
          t ⊢ R          t <: C<…>
    (<:1) ------------------------
            t <: C<…> with { R }


                C<…> <: t
    (<:2) ------------------------
           C<…> with { … } <: t
```

Thinking about inference, we note that it is important to apply the
rule (<:1) before applying any rule that goes looking up into the
bounds or extends/implements of the type on the left-hand side of a
subtype query. This ensures that we have the best information
possible when looking up type constants.

The first typing rule uses a new judgement that we define below.

```
            lookupty(C,'T') = BOUNDED(lo,up)
            lo' <: lo              up <: up'
            C ⊢ { R }
    (⊢l) --------------------------------------
           C ⊢ { type T super lo' as up'; R }


            lookupty(C,'T') = BOUNDED(lo,up)
            ty <: lo              up <: ty
            C ⊢ { R }
    (⊢e) --------------------------------------
                 C ⊢ { type T = ty; R }


    (⊢[]) -----------
            C ⊢ { }
```

### Semantics

In the spirit of the [Shack](https://github.com/facebookresearch/shack)
project that interprets Hack types as sets of values, we give a semantics
for type refinements by defining the interpretation of refinements

```
[[ C with { type T cstr } ]]Σ =
    [[ C ]]Σ ∩ { l ∈ loc |
                 l ↦ (tag, phi);
                 [[ class_def[tag].T ]]_ ⊢ [[ cstr ]]Σ }
```

That is, the constrained class type is all the objects in the class type
of C that, additionally, have a type member that satisfies the constraint
in the type. It is noteworthy that the semantics interpret the type
refinements as an intersection type.

The interpretation function [[ . ]]Σ is parameterized by Σ an environment
that contains the interpretation for ambient generic parameters. When
interpreting a type constant obtained from the class definition this
environment is irrelevant because type constants are currently required
to be independent from all generic parameters. The constraint in the
refinement, on the other hand, is interpreted using the ambient generic
parameters. One unknown is that the current typechecker implementation
allows using the 'this' type in type constant definition, and 'this' has
not been formalized in Shack yet.

Another shortcoming of these semantics is the lack of account for Hack's
parallel object model where classname values are used as singleton objects
for final abstract classes. This lack of precision in the semantics sheds
doubts about the interaction of type refinements with classname values
and their associated capabilities (call to static methods).

## IDE experience:

### Auto-suggested type refinements

It would be useful if IDE could detect if a user attempts to use a
certain type/context constants with implicit assumptions in mind that
a quick-fix appears “Refine type member that belongs to …”, e.g.:

```
function reads_box_T_as_int(Box $b): int { return $b->get(); }
```

would ideally suggest “Refine type member in the type associated with
parameter $b”, and accepting the quick-fix would refactor the above as:

```
function reads_box_T_as_int(Box with { type T as int; } $b): int {
  return $b->get();
}
```

### Good assistance if with keyword is forgotten

Users familiar with languages such as Scala that implement this feature
with a similar syntax may accidentally write the following:

```
abstract function getIntBox(): Box { type T = int; }
```

which may misparse as a non-abstract function with a body containing a
type definition:

```
abstract function getIntBox(): Box {
  type T = int; // hardly distinguishable from a body
}
```

If not the parser itself, the IDE should ideally suggest: “Did you
mean Box with { … }?”


## Implementation details:

### Parser

After careful refactoring, most of the core parsing logic used by type
and context constants is _reusable_. The only modified execution path is
when a type specifier (hint) is parsed; the parser must check if `with`
is the next token. The performance impact is therefore negligible, albeit
it requires a lookahead of size 1 only when parsing XHP attributes as
they can comprise multiple tokens (e.g., `attribute Type with-token2`).

### ASTs

TODO

### Typing

TODO

### HHVM

This feature is type-checker only because it does not provide a means of
declaring anything new. Refinements merely give additional “hints”
about the types associated with them (i.e., types before the `with`
keyword), and are used by type inference only -- nothing can fail at
run-time.

Therefore, HackC can simply ignore the entire refinement after a
type. This is true even when a type refinement appear in an aliases or
a type constant, e.g.:

```
type IntBox = Box with { type T = int }
```

will be bytecode-compiled identical to:

```
type IntBox = Box
```

which is sound for run-time because type aliases are not enforced,
and neither are type constants.


## Design rationale and alternatives:

### Alternative syntax

One might argue why we need the `with` keyword before the braces unlike
languages such as Scala where the syntax avoids the extra keyword. While
this would be more compact, it would require tricks or very complicated
lookahead and/or special-casing when parsing return types to discriminate
between refinement and the function body (see section _IDE experience_
for an example).

There have also been wishes that the syntax was less verbose potential
alternatives, e.g.:

```
Box[ T=int, C as[globals]super[zoned] ]
```

However, this would add significant parsing overhead as the parser would
need to backtrack:

* parse a series of bounds with _context list_ if possible, otherwise
* parse a series of bounds with _type_.

If we want to prioritize reducing verbosity, there is hope that it
can be done if we _unify representations_ of types and contexts --
at least for the purposes of type refinements, which is easier because
this feature and syntax is invisible to HHVM as it doesn’t affect the
runtime. Since context lists are already internally represented as types
in the typechecker, we could express the above as follows:

```
Box[ T=int, C as CtxGlobal super CtxZoned ]
```


### Smarter where-clauses

TODO

### DOT-like object model

Generalizing Hack’s object model by basing it on the Dependend Object
Types (DOT) calculus [3,4] is being actively considered as well, but it is
a much larger undertaking. DOT-style path projections are _instance-based_
(`$obj.TypeOrCtx`) and would serve as a sound and more powerful substitute
for type projections (`Type#TypeOrCtx`). Nonetheless, even languages that
support instance-based projections (e.g., Scala) do offer refinements
of types, attesting to the value added by this feature alone.


## Drawbacks

This feature does not strip away or change existing functionality,
so this section is largely inapplicable.

Nonetheless, it is a valid question to ask how Hack users are going to
be educated and what assistance will be provided to help them choose
between generics vs type constants with refinements.  For example,
if the definition of `Box` above was written using generics, we would
*not* need refinements but *would* end up hitting other limitations
imposed by generics.  Until we better unify the notions of generics and
type constants, we need to make the design intuitive in order to avoid
confusing Hack users with seemingly interchangeable sets of features.


## Prior art

### Type refinements in Scala

Scala 2 refers to the syntactic piece
`TypeSpecifier with { … }` as _compound type_ (see
[Sec 3.2.9](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#compound-types)
of the official specification), whereas the `{ … }` is also called
_refinement_. The `with` keyword is used for intersecting two types,
which is superseded by the `&` operator that denotes _type intersection_
in Scala 3. Interestingly, Scala allows any sort of structural typing

* adding a new _value_ definition such as a method or field;
* adding a new _type_ definition that can contain equal or lower/upper bounds.

Notably, Scala allows introduction of _new_ types/values in refinements
and thus offers a mechanism for _structural type_ refinements. E.g., we
could declare an interface that accepts any subtype of Box that defines
a new method:

```
def accepts_any_object_settable_to_int(
  box: AnyRef with { def set(v: Int): Unit }
)
```

Nonetheless, cyclic or self-referential accesses are prohibited in
the refinement:

```
def all_args_are_invalid(
  box0: Box with { type T2 <: this.T }, // ERROR
  box1: Box with { def set(v: this.T): Unit }, // ERROR
                           // ----- self-ref disallowed
  box2: Box with { def set(v: box2.T): Unit }, // ERROR
)
```

even when they come from an outer object:

```
trait Outer { self => // refer to `Outer.this` as `self`
  type B = Box with { type T >: self.type }  // ERROR
}
```


### References

[1] Scala 2.13 Specification, [Sec 3.2.9 (Compound Types)](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#compound-types)

[2] [EBNF notation](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form)

[3] paper: [Dependent Object Types](http://lampwww.epfl.ch/~amin/dot/fool.pdf)

[4] video: [Dependent Object Types](https://www.youtube.com/watch?v=b7AokpvwzgI)

[5] HIP: [context and coeffects](https://www.internalfb.com/code/fbsource/[a56b675db21fb8c075f735721a3147fe4e660822]/fbcode/hphp/hack/doc/HIPs/contexts_and_coeffects.md)

[6] [Shack](https://github.com/facebookresearch/shack), formal semantics for Hack types

## Unresolved questions:

TODO


## Future possibilities

TODO

---

### Hphp Hack Doc HIPs Typed Open Shapes (`hphp/hack/doc/HIPs/typed_open_shapes.md`)

**Status**: Draft, prototype implementation behind `typed_open_shapes` typechecker option
**Audience**: Hack Improvement Proposal committee
**Author**: Andrew Kennedy

# HIP: Typed Open Shapes

## Summary

Today, an *open* shape — written `shape('a' => int, ...)` — says nothing about
the type of the fields not listed by name. The compiler treats them as
`mixed`. This proposal generalises the `...` syntax to allow an explicit type:

```hack
// Existing: untyped open shape — unknown fields have type mixed
type ConfigU = shape('a' => int, ...);

// New: typed open shape — every unknown field must be a string
type ConfigT = shape('a' => int, string...);

function f(ConfigT $c): void {
  $x = $c['a'];          // : int
  $y = Shapes::idx($c, 'anything');   // : ?string (because every unknown field is a string), with null default
  $z = Shapes::idx($c, 'anything', 'default'); // : string because there is a string default
}
```

Plain `...` continues to mean "unknown fields are `mixed`" and is therefore
equivalent to writing `mixed...` explicitly.

## Motivation

### Goal

Hack already lets users say "this shape has a known field of type `X`". It
also lets users say "this shape may have other fields, of any type". What it
cannot currently express is the very common middle ground: "this shape has a
known field of type `X`, and may have other fields, all of which have type
`Y`". That is what this proposal adds.

Concretely, this comes up whenever a shape models a "metadata + payload"
record:

```hack
// A logging event: one well-known "level" field plus arbitrary structured
// data, where the data is always JSON-serialisable.
type LogEvent = shape('level' => LogLevel, mixed...);

// A request envelope: a well-known method, plus header/value pairs that
// must all be strings.
type Request = shape('method' => string, string...);

// A sparse number map: one canonical "total" plus per-bucket counts.
type Histogram = shape('total' => int, int...);
```

Without typed open shapes, the only way to express any of the above is to
weaken the unknown-field type to `mixed` and then narrow at every use site
with `is`/`as` or `Shapes::idx`-and-cast. That is verbose, error-prone, and
hides the developer's intent.

## User experience

Authoring is a simple extension of existing syntax: `...` becomes
`T...` to bind a type to unknown fields. This syntax is already used for similar
purpose in variadic function types, and open tuple types. Everything else — field syntax,
optional-field syntax, `Shapes::` helpers — stays as it is.

```hack
// Function & method hints
function takes(shape('id' => int, string...) $req): void {}

// Type aliases
type TypedOpen = shape('a' => int, ?'b' => bool, vec<int>...);

// Class type constants
class C {
  const type TX = shape('a' => int, ?'b' => string, MyEnum...);
}

// Nested in generics
function batch(vec<shape('a' => int, string...)> $v): void {}
```

We call the type of the open part of the shape a *variadic* type,
in common with variadic parameters and the variadic component of open tuple types.
### Equivalence: `...` and `mixed...`

These two forms are intentionally indistinguishable at every observable
boundary:

```hack
// All three describe the same type
type A = shape('x' => int, ...);
type B = shape('x' => int, mixed...);
type C = shape('x' => int, \HH\mixed...);
```

This invariant lets us add the syntax without breaking any existing program
that uses `...`. It is enforced both at the typechecker level (subtyping
treats them identically) and at the HackC level (the bytecode emitter
collapses `mixed...` and `...` to the same type-structure shape — no
`variadic_type` field is written).

Note also that `shape('x' => int, nothing...)` is equivalent to the *closed* shape type `shape('x' => int)`.

### IDE experience

Hover/completion shows the typed-open form verbatim — `shape('a' => int,
string...)` — when the source uses it, and the legacy `shape('a' => int,
...)` form otherwise.

### Errors

When the typechecker is run with the option `typed_open_shapes=false`, naming raises:

```
Naming[2132] Typed open shapes (e.g. shape('x' => int, T...)) are not enabled.
Use plain ... for unknown fields.
```

### Subtyping

Subtyping on shapes follows the principle: fields in the subtype should be at least as defined as fields in the supertype, and the type should be at least as specific.
In particular:

* If a field `'a' => t` or `?'a' => t` appears in the subtype, but not in the supertype, then the supertype should have a variadic type `u...` where `t <: u`. (The variadic type may be `mixed`, or left unspecified and default to `mixed`.)
* If the subtype is open with a variadic type `t`, then the supertype must be open, with variadic type `u` such that `t <: u`.

### Indexing

As with optional fields, and existing open shapes, fields should be accessed using `Shapes::idx($x, 'a')` (returning `null` if not present), or `Shapes::idx($x, 'a', $def)`, returning `$def` if not present.

### Type structure representation

For a `TypeStructure` that represents a shape type with variadic component, the optional `variadic_type` field
is set to a `TypeStructure` representation of the variadic type. This field is *not* set if the variadic type is `mixed` i.e. it's a normal open shape type.
The boolean `allows_unknown_fields` field is set to `true`.

## Rollout

Initially, the feature is enabled in the typechecker only if the `.hhconfig` flag is set `typed_open_shapes=true`. Once the changes land in HHVM (for HackC and type structures), the feature can be safely enabled.

---

### Hphp Hack Doc HIPs Variadic Named Parameters (`hphp/hack/doc/HIPs/variadic_named_parameters.md`)

**Status**: Draft, prototype implementation behind `variadic_named_parameters` and `named_variadic_type` typechecker options
**Audience**: Hack Improvement Proposal committee
**Author**: Andrew Kennedy

# HIP: Variadic Named Parameters

## Summary

The proposal consists of two features.

1. Support for a *named variadic* in a function type, e.g. `(function(int, named bool $flag, named string...):void)`
represents functions that can be applied to a single positional parameter of type `int`,
a named parameter `flag` of type `bool`, and any number of additional named parameters of type `string`.
For example, `foo(3, flag=false, other="A")` is a legal call to such a function,
and `foo(3, flag=false, other=3)` and `foo(3, flag=3, other="A")` are illegal calls.

2. Support for functions, methods and lambda definitions that accept any number of named parameters of a given type, absent the ability to make use of those parameters in the body.
For example, `(int $arg, named bool $flag, named string...) ==> $flag ? $arg : 0` is a function whose type is the one mentioned in (1) above.

## Motivation

Ultimately, we want to support fully generic variadics for named parameters, as with positional parameters, including the packaging of named parameters as `dict` (cf packaging of positional parameters in a `vec`), expression-level splatting of
a shape or dictionary into named parameters (cf `...$x` splatting for tuples into positional parameters), and type-level
splatting of shape types into named parameters in function types.

But that's a lot of design and implementation work. For the specific case of *typed mocking*, it's possible to cover most uses with features (1) and (2) described in the summary. For example, consider the `mockReturn` function, that currently does not support mocking of functions with named parameters.
```hack
public function mockReturn<Tr>(Tr $v): this
  where
    Tfun super (readonly function(mixed...)[]: Tr) {
      return $this->mockImplementation((mixed ...$_args)[]: Tr ==> $v);
  }
```
Using the two features proposed here, we can support named parameters, as follows:
```hack
public function mockReturn<Tr>(Tr $v): this
  where
    Tfun super (readonly function(mixed..., named mixed...)[]: Tr) {
      return $this->mockImplementation((mixed ...$_args, named mixed...)[]: Tr ==> $v);
  }
```
## Syntax

Function types are extended with the syntax `named t...` where `t` is the type of the named parameters. Some examples are listed below:
```hack
// Named variadic only
type T1 = (function(named int...):void);
// Positional variadic and named variadic
type T2 = (function(int, string..., named bool...):void);
// Explicit named parameter and variadic
type T3 = (function(named int $n, named string...):void);
```
The named variadic can appear anywhere in the parameter list, but there can be at most one named variadic.

Function definitions (top-level, method, and lambda) are extended with the same syntax, i.e. without a name for binding
the named variadic parameters. Some examples are listed below:
```hack
// Has type T1 from above
function foo1(named int...):void { }
// Has type T2 from above
function foo2(int $x, string... $args, named bool...):void { }
// Has type T3 from above
function foo3(named int $n, named string...):void { }
```

## Typing

Calls to functions whose types have variadic named parameters are type-checked as you'd expect: explicit and required named parameters must be bound to corresponding named arguments of the correct type, explicit but optional named parameters *may* be bound to an argument, and then any remaining named arguments must have the type specified by the named variadic. For example, using the examples from the previous section:
```hack
foo1(a=3); // legal
foo1(a=3, b=4); // legal
foo1(a=false); // illegal
foo2(3, x=false); // legal
foo2(3, "A", x=5); // illegal
foo3(n=2, a="A"); // legal
foo3(n="A", a="A"); // illegal
foo3(n=2, a=3); // illegal
```
Subtyping on function types is as follows. Given two function types F1 and F2, for F1 to be a subtype of F2, it is necessary that for every name `n`,

1. if F1 *requires* `n` of type `t1` then F2 must also require `n` of type `t2` with `t2<:t1`, and

2. if F2 *accepts* `n` of type `t2` then F1  must also accept `n` of type `t1` with `t2<:t1`.

Here,
* F *requires* `n` of type `t` if it declares `named t $n`.
* F *accepts* `n` of type `t` if it declares `optional named t $n` or `named t $n` or has no `named` parameter `$n` but does declare `named t...`.

Another way of viewing subtyping is by elaboration to shapes: translate named parameters to a shape type, with required named parameters translated to required shape fields, optional named parameters translated to optional shape fields, and variadic named parameter translated to a typed open shape, e.g. `(function(named int $x, optional named string $y, named arraykey...):void)` becomes `(function(shape('x' => int, ?'y' => string, arraykey...)):void)`.

## Restrictions

Functions or methods with named variadic parameters cannot be marked `<<__Memoize>>`: this is a naming error. They also cannot be marked `readonly`: this is a parse error. This matches the behaviour for ordinary variadics.

## Runtime

Prior to this change, if a function is called with a named argument whose name does not match any named parameter in the definition, an exception is raised. The new functionality is as follows: this exception is *not* raised if the function
declares a named variadic. The function itself does not have access to the parameter; it is simply dropped. There is no runtime enforcement of the type specified on the named variadic.

## Type structures

At present, there is no type structure support for named parameters in function types.

## Rollout

Initially, feature (1) is enabled in the typechecker only if the `.hhconfig` flag is set `named_variadic_type=true`.
Feature (2) is enabled only if the `.hhconfig` flag is set `variadic_named_parameters=true`.
Once the changes land in HHVM, the feature can be safely enabled in the type-checker.

---

### Hphp Hack Doc HSL Design Io (`hphp/hack/doc/HSL_design/io.md`)

# HSL IO Key Design Decisions

# Status:

Fully implemented in Hack code (https://github.com/hhvm/hsl-experimental and Facebook WWW); widely used externally by most CLI applications in Hack, including HHAST’s LSP support. Originally derived from HHAST LSP server’s async IO implementation.

Used in Facebook www in limited places due to ‘experimental’ status.

# Summary:

HSL IO aims:

* to be a 100% replacement for PHP IO builtins functions and and related resources (files, sockets, stdio, etc); it is meant to unblock their removal (among other things), so a 90% solution is not acceptable.
* to replace or reduce the need for additional HHVM extensions when async clients for network services are needed
* be a true ‘async-first’ design
* use the type system for as much safety as possible. For example, files opened read-only do not have ‘write’ methods; this is comparable to Java exposing separate InputBuffer and OutputBuffers.

It is composed of several new namespaces:

* `HH\Lib\OS`: this is a thin layer exposing the traditional C File-Descriptor-based APIs; minimal changes are made to the C APIs.
    * Direct usage is *strongly* discouraged: no attempt is made to make APIs hard-to-misuse. For example, if `OS\write("foo")` only writes 1 character, this is considered success, not a failure, and users must check for it - just like in C.
* `HH\Lib\{File, Unix, TCP}`: functions, classes, and interfaces that are specific to a particular kind of IO ‘handle’.
* `HH\Lib\{IO, Network}`: functions, classes, and interfaces that are shared or reusable between multiple kinds of IO handles

## Why a design document:

* While this is not a language change, it will be part of a fundamental change to how Hack programs work: it should eventually replace `php://input`, `php://output`, `print()`, etc. See ‘Future Work’ for details.
* there are some contentious design issues; **the primary contentious issue is that the user-facing APIs are not ‘disposable-first’.**

This document is **not** intended to be a full API design review of the library; however, for completeness, the full APIs can be reviewed in the documentation at https://docs.hhvm.com/hsl-experimental/reference/

# Feature motivation:

The primary motivations are:

* async support: this is already needed by projects such as HHAST, and is desired externally to allow replacing the PHP redis extension with an async library.
* portable, reliable error detection and handling. The PHP builtins return false and log a warning, while `posix_get_last_error()` , `posix_errno()`, and `socket_last_error()` are unreliable, especially when async or CLI server mode is being used; they also depend on mutable global state.
* it is a necessary step in the removal of PHP `resource` types; file/stream/socket resources have observable destructor-like behavior

Additional motivations are:

* type system: with current PHP APIs, files, mysql connections, streams, curl handles all share the same `resource` type
* type system: `T | false` return types are prevalent and not supported by the Hack type system.
* consistency: sockets are sometimes stream resources, sometimes socket resources, depending on how they are created. Each has different limitations

## Prioritization factors:

Making HSL IO built-in will unblock work on:

* removing PHP IO primitives
* redesigning the entrypoint API - for example, removing `STDIN`, `STDOUT`, `php://input`, `php://output`
    * this in turn currently blocks removal of PHP globals
    * this is a soft-blocker on adding a friendly, type-safe alternative to the current xbox parallelism APIs

# User experience:

```
/* Detailed source-code truncated for AI context efficiency. */
```


`$file` is a `File\CloseableWriteHandle`; in turn, this is an `IO\CloseableSeekableWriteHandle` and:

* an `IO\Handle`: this is an empty base interface
* an `IO\CloseableHandle`: an `IO\Handle` with `close()`; currently, all concrete `IO\Handle`s are closeable, but others have been suggested in the past; e.g. a `IO\server_error()` handle returning process STDERR; an individual request should not be able to close HHVM server stderr.
* a  `IO\WritableHandle` and an `IO\SeekableHandle`
* an `IO\FDHandle`: this is the integration point with the `OS\` namespace: this means that:
    * `$file->getFileDescriptor()` is available and returns an `OS\FileDescriptor`
    * The majority of operations (e.g. writing) are implemented using `OS\` functions, e.g. `writeAllAsync()` is implemented with `OS\write()`  and `OS\poll_async()`

`$conn` is also a  `Closeable`, `Writable`, and `FileDescriptor` Handle, but it is not a `Seekable` handle.

These interfaces are best thought of as intersections: a FooBarBazHandle is a FooBarHandle, FooBazHandle, BarBazHandle, FooHandle, BarHandle, and BazHandle. Concretely, a CloseableReadWriteHandle is a CloseableHandle, a ReadHandle, a WriteHandle, a ReadWriteHandle, a CloseableReadHandle, and CloseableWriteHandle. Function authors should aim to describe what functionality they need when restricting input types, and take less specific interface possible - for example, if a function only needs to call `writeAllAsync()`, it should take an `IO\WriteHandle`, not a `File\WriteHandle`

`IO\Handle`s are not disposables; they must manually be closed, or `closeWhenDisposed()` should be called to get a disposable that will close on exit. This disposable is not itself an `IO\Handle`.

# Implementation details:

The majority of `IO\Handle`s are `IO\FDHandles`, built on top of an `OS\FileDescriptor`. This is a native object which is a thin wrapper around the C int file descriptor concept, which ensures that:

* file descriptors are not left open at the end of the request. Refcounting is not observable - if not explicitly closed, file descriptors are closed at the end of the request.
* they are transferred between the client and server correctly in CLI server mode
* if a closed FD number is re-used, operations on the original handle will fail, instead of working on the new FD with the same number. This is essential for correctness in a multi-request single-process environment.

For example, a `writeAllowPartialSuccessAsync()` call ends up being a call to `OS\write($this->fd)`.

`OS\write()` is a very thin Hack wrapper around the native builtin `HH\Lib\_Private\_OS\write()`; the separation of responsibilities is that:

* native functions are responsible for cross-request correctness/isolation
* everything else is the responsibility of the Hack code

For example, `_OS\write()` may throw an `_OS\ErrnoException()`, and `OS\write()`  may catch this and instead throw an `OS\FileNotFoundException`; as user-facing exception hierarchy is a very subjective and opinionated area, it is left for the Hack code.

Async support for IO`\FDHandle` is built on `O_NONBLOCK`, and libevent/libevent2’s FD support.

The current exception hierarchy is based on Python 3’s work, which appears well received. Concretely, there is:

* `OS\ErrnoException`: this is both a base class, and instantiable when there is not a more specific exception
* many subclasses for failures that frequently caught, e.g. `OS\AlreadyExistsException`, `OS\IsADirectoryException`, `OS\IsNotADirectoryException`

While `catch (OS\ErrnoException $e) { switch ($e->getErrno()) { /* ... */ }}` is possible, the hierarchy aims to make this an antipattern in the common case.

### Required Changes

Implementing this HIP would require moving the relevant code from Facebook’s www repository and the github hsl-experimental repository to HHVM builtins, and adding appropriate HHIs.

# Design rationale and alternatives:

## API is not Disposable-based

There were previously strong opinions that all IO handles should be Disposable, and closed when disposed.

### Context: current usage of *“PHP IO”* features is not representative of IO as a whole

The vast majority of PHP IO usage in Facebook WWW should be using Disposable-based APIs, specifically APIs focussed on temporary files. However, the majority of IO does not go through PHP IO, or things we usually think of as “the IO library” - it uses dedicated extensions, such as:

* Thrift
* McRouter
* MySQL
* Various FB-proprietary extensions

We aim for HSL IO to be usable for implementing clients instead of extensions for other services that are not currently supported (for example, gRPC, Redis) but solve similar use cases; as such, we should be asking ourselves: “would this design choice prevent us from reimplementing McRouter in Hack using HSL IO?”.

I do not believe that disposable-only Thrift/McRouter/MySQL/<censored> APIs would be practical for Facebook for the same reasons that I believe that HSL IO can not be disposable-only, detailed below.

### Non-composability makes Disposables unsuitable for “100%” solutions

Alternatively: Disposable is a ban on encapsulation.

* this makes them impossible to use as a hidden implementation detail of a higher-level API
* as such, they can never be an essential part of an API that we expect others to build on

If HSL IO was Disposable-based, and a 100% replacement:

*  a pure Hack async Redis client could not contain a `TCP\Socket` as an implementation detail. Instead, either:
    * it would need to be passed around to every function, and could not be hidden in a ‘Connection’ object or similar
    * every operation on Redis would need to open a new TCP connection. This is unacceptable from a performance perspective.
* TLS/SSL can not be implemented as a wrapper around TCP or Unix IO handles, or other transports. Users would need to explicitly wait for events on the socket handle, pass them (and the socket) on to the TLS handler, and see if any post-TLS state change was observable.
* `interface Logger { public function logAsync(string $message): Awaitable<void> }` could be implemented in three ways, none of which are practical:
    * Open and close the file descriptor inside that method every time `logAsync()` is called; this is unacceptable performance-wise even for local files, and definitely for networked logging services.
    * Take any necessary IO handles as `<<__AcceptDisposable>> IO\WriteHandle` parameters. This is bad for usability, prevents implementation hiding, and requires a codemod to add an extra arg to every callsite if we ever want to log to two places (e.g. when migrating logging frameworks).
    * Discard the log message, without displaying or saving

### ‘Virality’

I mean this in a similar sense to ‘viral licenses’: if one thing is Disposable, everything that touches it must also be Disposable, recursively.

Using the previous `Logger` example: if we make the IO handle a parameter, any component that could possibly want to log - or use another component that may want to log - would need to take an `<<__AcceptDisposable>> $logTo` parameter.

If the common ‘controller’ pattern is used:

* due to the composition restrictions, this must be an explicit parameter to every single method
* if those composition restrictions were weakened to allow disposables to contain other disposables, there seem to be two approaches:
    * add a disposable ‘callcontext’ parameter to every method instead of needing the IO handle to be its’ own parameter
    * make every controller disposable, and likely the majority of the call stack in both directions; store the ‘callcontext’ as a property
* both are substantial changes to code structure, and will lead to a large amount of boilerplate


For some use cases (e.g. live stream uploads), it *must* be possible to access raw IO streams for POST data; this means that the web controller stack would have similar needs to the hypothetical logger case.

### An alternative: special cases

Instead of making it so that STDERR or similar is passed through the application, we could keep it accessible via a free function. The next immediate problem is that the first function that logs to STDERR will acquire it as a disposable, and automatically close it when that function exits.

We could address this by special-casing the STDIO handles to not actually close on exit, or by making them global constants similar to the existing STDIN/STDOUT/STDERR constants (which would require allowing constants that are objects, and that implement IDisposable).

The major shortcoming here is that replacing “log to STDERR” with “log to file”, “log to syslog”, or “log to my favorite SAAS logging provider” becomes a massive challenge requiring refactoring of every logging callsite. It also is itself a breach of encapsulation, as a `IO\WriteHandle` parameter without `<<__AcceptDisposable>>` will effectively be the same thing as an `STDIOWriteHandle`.

This could be avoided by implementing a special API to ‘strip’ d

### An alternative: ‘leases’

Instead of the disposable actually controlling open/close, it could be an indirect reference to the *real* IO handle (similar to a shared_ptr). This would address:

* the ‘every method call needs a reconnect/reopen’ problem
* the ‘pass stderr/a log device to every function’ problem
* the ‘if I don’t get passed stderr, but get it from a global accessor, a disposable for stderr in my function, and it’s disposed, have I just closed it for every other function?’ problem

This is not actually a solution for Disposable-based IO handles: it is equivalent to “not Disposable-based” as it requires there to be an underlying non-disposable IO handle backing this, and requires it to be possible to acquire one from a disposable, removing the desired safety.

## No locking/queuing

This is undefined behavior:

```Hack
concurrent {
  await $f->writeAllAsync('FooBar');
  await $f->writeAllAsync('HerpDerp');
}
```

As `OS\write()` can partially succeed, even with a single thread, this can result in `FooHerpBarDerp` or many other sequences.

In an early experimental version of HSL IO, write operations were queued, meaning that “FooBarHerpDerp” and “HerpDerpFooBar” would be the only possible results from the above code. This queueing was removed when callers were added that were essentially:

```Hack
concurrent {
  await async {
    await $f->seekAsync(123);
    $data = await $f->writeAllAsync("foo");
  };
  await $f->writeAllAsync("barbaz\n");
}
```

This was similarly undefined behavior; additionally, there is no guarantee that the “foo” write is the next write after the seek.

This queuing was removed as the only way to make multiple writers actually safe is to add application-level locking/queuing that spans multiple IO operations (such as `Async\Semaphore`, or linked-list-of-awaitable-style queues). Removing this built-in queuing also removed the need for methods like `seek()` and `close()` to be async.

## Exception Hierarchy

HSL IO/OS’s exception hierarchy is heavily influenced by Python 3; this is a substantial change from Python 2, described in https://www.python.org/dev/peps/pep-3151/. These tend to indicate the cause of failure, rather than what went wrong. For example, opening a file may fail with a FileNotFoundError, not a FileOpenError.

The primary goal for HSL IO was for the exception type to be sufficient to determine the appropriate action; in particular, we did not want this to become a common pattern:

```
try {
  ...
} catch (IO\Exception $e) {
  switch ($e->getErrno()) {
    case ENOENT:
      ...
    ...
  }
}
```

Instead, we wanted most code to look like this:

```
try {
   ...
} catch (OS\FileNotFoundException $e) {
  ...
}
```

This difference is very similar to the change that has been made between Python 2 and 3, and the Python 3 hierarchy meets HSL IO’s design goals, and is well received by the Python community. While the first form is still possible (and necessary for some rare cases), it is not the usual pattern. Java and Ruby take a similar approach.

There is a more in depth discussion and cross-language comparison at https://github.com/hhvm/hsl-experimental/issues/37

## “ReadWrite” handles vs separate read and write handles

An early version of HSL IO did not have read-write handles; instead, you could have both a read and write handle for the same file. This would have reduced the number of interfaces significantly, and resolved some of the issues that the locking/queuing were also meant to address, assuming the two handles operated independently.

This feature was removed and replaced with ReadWrite handles as there is not adequate portable support in libc:

* opening the resource twice, in different modes, is not possible for some kinds of FDs
* if split with the C `dup()` call, they are not truly independent, and behavior various by platform. For example, a `seek()` on the ‘read’ FD may also seek the ‘write’ FD.

# Prior art:

## Disposable API

Rust, Python, and the Qt C++ library all provide disposable-like behavior, however it is either optional, or additional language features (pointers, borrowing) or weaker restrictions (e.g. composability) address the problems.

`File`s and `Socket`s in Rust have Disposable-like behavior/restrictions, however the “borrowing” language feature provides a clean way to avoid the issues raised previously.

In Python, `with` is strongly encouraged, but is optional. This provides safety for simple cases, but allows the flexibility needed to address others (e.g. composition is permitted).

QIODevice, QFile, and QTCPSocket classes in the Qt library for C++ are non-copyable like disposables, close on destruct, however pointers and composition are permitted, avoiding the problems above.

## Implementation based on File Descriptors

Rust and Python take a similar approach to HSL IO: common operations are provided via high-level classes - however the raw file descriptor is available for niche operations. HSL IO differs in that an object wrapper is used (`OS\FileDescriptor`) instead of directly exposing the int.

The wrapper object is needed for correctness in CLI-server mode, and in a multi-request environment.

## BufferedReader as a separate class

Buffered reading was split out to a separate class as mixing buffered and unbuffered reads leads to undefined and unintuitive behavior; for example, `readByteAsync()` may have read and cached 1024 bytes but only returned the first 1. Unbuffered reads need to remain possible for packet-based network protocols.

Rust (`std::io::BufReader`), Java (`java.io.BufferedReader`), and Python (`io.BufferedReader`) all take the same solution to this problem.

Python also offers implicit buffering when opening a file, however this makes all operations buffered - it does not mix them.

## Read and Write in the type system

In Rust, `Read` and `Write` are distinct interfaces - however, `File::open()` always returns a `File`, which implements both, regardless of the open mode. This allows functions to specify that a parameter only needs to be readable, but it remains possible to pass a write-only file to such a function.

Java IO is similar to HSL IO for handling of read (via `InputStream`), write (via `OutputStream`), and read write (`ByteChannel`) for files - however these are created via separate functions/methods, and are not part of a `File` or `Socket` object. For example, a `Socket` does not extend `OutputStream`, but provides `getInputStream` and `getOutputStream` methods. This design encourages APIs to take `InputStream` or `OutputStream` parameters instead of `Socket`s, providing similar benefits to HSL IO’s `IO\ReadHandle` and `IO\WriteHandle` separation.

# Unresolved questions:

- `var_dump()`, `print_r()`, etc: setting `O_NONBLOCK` on stderr breaks a lot of debugging utilities, especially with large outputs. Should they set `O_NONBLOCK off temporarily, or should they be async, or hidden `HH\Asio\join`?

- What do we ‘encourage’ in www, e.g. via namespace aliasing?
  * `IO\`: yes, especially the interfaces
  * `OS\`: yes: while the functions in this namespace should very rarely be used directly, the exception classes should, and this is the correct place for them
  * `File\`: maybe; we may want to only encourage wrappers instead, e.g. restricting what paths can be opened.
  * `TCP\`, `Unix\`, `Network\`: yes: these will be rarely used, but are so niche that it is unlikely that general-purpose wrappers will be written.

# Future possibilities:

## Entrypoint API

In order to remove the PHP STDIN, STDOUT, STDERR constants, $_POST, $_GET and similar, we need to define a replacement. This should be built on HSL IO, and will effectively tie all Hack programs to some aspects of HSL IO’s design - in particular, if HSL IO handles were disposable, this would apply severe constraints to the design of Hack programs.

Suggestions have included:

```
<<__CLIEntryPoint>>
async function main(
  vec<string> $argv,
  vec<string> $envp,
  IO\ReadHandle $stdin,
  IO\WriteHandle $stdout,
  IO\WriteHandle $stderr,
): Awaitable<void> {}

<<__CLIEntryPoint>>
function main(CLIRequestContext $ctx): void {
  $argv = $argv->getArgv();
  $stdin = $ctx->getStdin();
}

<<__WebEntryPoint>>
function main(WebRequestContext $ctx): void {}

<<__EntryPoint>>
function main(): Awaitable<void> {
  $argv = OS\argv();
  $envp = OS\envp();
  $stdin = OS\stdin(); // an IO\CloseableReadFDHandle or throw
  $input = OS\request_input(); // CLI STDIN or HTTP POST data (php://input)
}
```

While the specific API does not matter at this point, this both constrains - and is constrained by - HSL IO’s design, especially if Disposables were used. If Disposables were used, the ‘virality’ issues above will be severe for every Hack program that does any form of IO, including sending HTML to the browser.

## Remove PHPisms

IO `resource`s inherited from PHP are:

* one of the remaining cases of observable refcounting/destructor-like behavior
* poorly typed: it should be a type error to pass a file resource to curl_exec when a curl handle is expected

Once HSL IO is built-in, we can start to remove these.

* removing the PHP builtin STDIN, STDOUT, STDERR constants will be possible when a new entrypoint API is agreed on; that will potentially allow complete removal of the ‘PlainFile’ resource type too
* removing further resource types will be possible as HSL IO is extended, in particular with TLS and subprocess support.

## Denotable intersection types

There are various operations that are only appropriate for some handles, e.g.:

* read()
* write()
* seek()
* getpeername()

Each set of related operations is represented by an interface, e.g.:

* `IO\ReadHandle`
* `IO\WriteHandle`
* `IO\SeekableHandle`
* `Network\Socket`

As mentioned in ‘user experience’, each combination needs its’ own interface in order to be usable as a parameter type; for example, if a function needs to read, write, and seek, it takes an `IO\SeekableReadWriteHandle`.

Early versions of HSL IO contained manual definitions of the ‘reasonable’ combinations, but this was a frequent source of bugs due to oversights. Now the interfaces are generated, which means that if any new interface is added, the number of generated interfaces roughly doubles. Currently, there are 26.

There have been requests/suggestions for additional interfaces, e.g.:

* TruncatableHandle
* RewindableHandle as distinct to Seekable
* DisposableHandle

If these (or any other 3) were added, there would be 245 generated interfaces; if 5 were added, we’d get to 1010. This is not scalable.

We will either need to be extremely selective about what new interfaces are added, or we will need a new way to represent these types. Denotable intersection types are the clearest fit, as these empty interfaces are effectively defining intersections:


```
- interface ReadWriteHandle extends ReadHandle, WriteHandle {}
+ type ReadWriteHandle = ReadHandle & WriteHandle;
```

The main advantage is that the intersections do not need to be predefined: currently, if a function needs to `read()` and `seek()`, it should take an `IO\SeekableReadHandle`, which must have been predefined in the HSL, and implemented by the concrete handle being passed.

If denotable intersection types were supported:

* if `IO\SeekableReadHandle` was defined as an intersection, it would not need to be explicitly present in the type hierarchy of the concrete implementations
* `IO\SeekableReadHandle` does not need to be predefined: a function could take a `IO\SeekableHandle & IO\ReadHandle` parameter instead.

This would:

* greatly simplify the type hierarchy (below)
* allow decisions about interfaces to be made purely on the usability/correctness merits

### The type hierarchy of a read-write file handle without denotable intersection types



### The type hierarchy of a read-write file handle using denotable intersection types



## Disposable IO API

While a non-disposable IO API is essential as a building block for higher-level libraries (e.g. redis clients), a Disposable API is highly desirable for operations we commonly think of as IO, like opening files and directly interacting with a TCP socket. This can be built on top of a non-Disposable API, but a non-Disposable API can not be built on top of a Disposable API.

Some ways this could be implemented:

### Wrappers

A `File\DisposableReadWriteHandle` could wrap a `File\ReadWriteHandle`; functions should aim to specify their parameters as `<<__AcceptDisposable>>` wherever possible.

This requires another interface (and the corresponding doubling of generated interfaces), and hand-written wrappers for each instantiable handle class.

### Add an “optionally disposable” language feature

The user experience would be similar to Python’s `with`:

```Hack
class File implements ..., IOptionallyDisposable {
  // ...

  public function __dispose() {
    $this->close();
  }
}

// No `using`, __dispose is simply not called:
$f = new File('/tmp/foo');
// ... so must manually close:

$f->close();

using $f = new File('/tmp/bar');
// automatically closed  - but you can call `$f->close()` earlier if you want

```

This should be a separate HIP, but I believe it would be the best approach:

* no need for 2x the classes/interfaces: good for maintainability and discoverability
* good for discoverability: only one API

There is one major issue with this concept though:

```Hack
function takes_rh(IO\ReadHandle $_): void {}
function takes_drh(<<__AcceptDisposable>> IO\ReadHandle): void {}

takes_rh(new File('/tmp/foo')); // No error

using $f = new File('/tmp/foo');
takes_drh($f); // No error
takes_rh($f); // error?
```

Error: it is being used as a disposable, so should act like one
No error:  IOptionallyDisposable would primarily be useful for convenience; it should not be considered an ‘ownership’ model. As such, it is likely that `__dispose()` calls another public method (like `close()`), so if something keeps hold of the handle after __dispose was called, it may be in a bad state, but this state would be reachable even if it wasn’t disposable.

---

### Hphp Hack Doc Async Await As An Expression Spec (`hphp/hack/doc/async/await-as-an-expression-spec.md`)

For a summary, see the [await-as-an-expression](https://docs.hhvm.com/hack/asynchronous-operations/await-as-an-expression) docs.

The guiding principle of “unconditionally consumed with the statement” is to prevent computation from being thrown away. Since we could run the awaits before the statement, if they turn out to not be consumed, it will result in waste. We intentionally ignore the possibility of Exceptions being thrown for the definition of “conditional”.

For the position to be considered “unconditionally consumed” we require all parents of the await expression until the closest statement pass this check.

Valid positions:

* ConditionalExpression: Only first position is allowed
    * `(await $yes) ? (await $no) : (await $no)`
* FunctionCallExpression: Any position is allowed as long as receive isn't conditionally executed. Only the receiver is allowed if the receiver is conditional.
    * `(await $yes)->foo(await $yes, await $yes)`
    * `(await $yes)?->foo(await $no, await $no)`
* SafeMemberSelectionExpression: Only the object is allowed.
    * `(await $yes)?->(await $no)`
* Allow any valid expression position of all of the following expressions:
    * CastExpression: `(<token>)(await $yes)`
    * MemberSelectionExpression: `(await $yes)->(await $yes)`
    * ScopeResolutionExpression: `(await $yes)::(await $yes)`
    * IsExpression: `(await $yes) is Int`
    * AsExpression: `(await $yes) as Int`
    * NullableAsExpression: `(await $yes) ?as Int`
    * EmptyExpression: `empty(await $yes)`
    * IssetExpression: `isset(await $yes)`
    * ParenthesizedExpression: `(await $yes)`
    * BracedExpression: `{await $yes}`
    * EmbeddedBracedExpression: `“{await $yes}"`
    * CollectionLiteralExpression: `Map { await $yes => await $yes }` or `Vector { await $yes }`
    * ObjectCreationExpression, ConstructorCall: `new (await $yes)(await $yes)`
    * ShapeExpression, FieldInitializer: `shape('key'`` => await $yes)`
    * TupleExpression: `tuple(await $yes, await $yes)`
    * ArrayCreationExpression: `array(await $yes => await $yes)`
    * ArrayIntrinsicExpression: `[await $yes => await $yes]`
    * DarrayIntrinsicExpression: `darray[await $yes => await $yes]`
    * VarrayIntrinsicExpression: `varray[await $yes]`
    * DictionaryIntrinsicExpression: `dict[await $yes => await $yes]`
    * KeysetIntrinsicExpression: `keyset[await $yes]`
    * VectorIntrinsicExpression: `vec[await $yes]`
    * ElementInitializer: `await $yes => await $yes`
    * SubscriptExpression: `(await $yes)[await $yes]`
    * EmbeddedSubscriptExpression: `{(await $yes)[await $yes]}`
    * YieldExpression: `yield (await $yes)`
    * SyntaxList, ListItem: `await $yes, await $yes`
* PrefixUnaryExpression | PostfixUnaryExpression | DecoratedExpression
    * `!`: `!(await $yes)`
    * `~`: `~(await $yes)`
    * `+`: `+(await $yes)`
    * `-`: `-(await $yes)`
    * `@`: `@(await $yes)`
    * `clone`: `clone (await $yes)`
    * `print`: `print (await $yes)`
* BinaryExpression
    * Binary operators that only allow await in the left position:
        * `AND`: `(await $yes) AND (await $no)`
        * `OR`: `(await $yes) OR (await $no)`
        * `||`: `(await $yes) || (await $no)`
        * `&&`: `(await $yes) && (await $no)`
        * `?:`: `(await $yes) ?: (await $no)`
        * `??`: `(await $yes) ?? (await $no)`
    * Binary operators that do assignment:
        * `=`: `(await $no) = (await $yes)`
        * `|=`: `(await $no) |= (await $yes)`
        * `+=`: `(await $no) += (await $yes)`
        * `*=`: `(await $no) *= (await $yes)`
        * `**=`: `(await $no) **= (await $yes)`
        * `/=`: `(await $no) /= (await $yes)`
        * `.=`: `(await $no) .= (await $yes)`
        * `-=`: `(await $no) -= (await $yes)`
        * `%=`: `(await $no) %= (await $yes)`
        * `^=`: `(await $no) ^= (await $yes)`
        * `&=`: `(await $no) &= (await $yes)`
        * `<<=`: `(await $no) <<= (await $yes)`
        * `>>=`: `(await $no) >>= (await $yes)`
    * Null Coalescing Assignment is both, so it doesn't allow either:
        * `??=`: `(await $no) ??= (await $no)`
    * Binary operators that allow await in both positions:
        * `+`: `(await $yes) + (await $yes)`
        * `-`: `(await $yes) - (await $yes)`
        * `*`: `(await $yes) * (await $yes)`
        * `/`: `(await $yes) / (await $yes)`
        * `**`: `(await $yes) ** (await $yes)`
        * `===`: `(await $yes) === (await $yes)`
        * `<`: `(await $yes) < (await $yes)`
        * `>`: `(await $yes) > (await $yes)`
        * `==`: `(await $yes) == (await $yes)`
        * `%`: `(await $yes) % (await $yes)`
        * `.`: `(await $yes) . (await $yes)`
        * `!=`: `(await $yes) != (await $yes)`
        * `<>`: `(await $yes) <> (await $yes)`
        * `!==`: `(await $yes) !== (await $yes)`
        * `<=`: `(await $yes) <= (await $yes)`
        * `<=>`: `(await $yes) <=> (await $yes)`
        * `>=`: `(await $yes) >= (await $yes)`
        * `&`: `(await $yes) & (await $yes)`
        * `|`: `(await $yes) | (await $yes)`
        * `<<`: `(await $yes) << (await $yes)`
        * `>>`: `(await $yes) >> (await $yes)`
        * `^`: `(await $yes) ^ (await $yes)`
* Pipe BinaryExpression (`|>`): Since we disallow dependent awaits in a single statement (or concurrent block), we need to disallow awaits in pipe operators that de-sugar to nested awaits. The simple rule to disallow this: we treat the `$$` as an `await` if the left side contains an `await`.
    * Disallowed: `(await $x) |> (await $$)` de-sugars into `(await (await $x))`
    * Allowed: `$x |> (await $$)` de-sugars into `(await $x)`
    * Allowed: `(await $x) |> f($$)` de-sugars into `f(await $x)`
    * Allowed: `(await $x) |> (f($$) + await $y)` de-sugars into `f(await $x) + await $y`
    * Disallowed: `(await $x) |> f($$) |> await g($$)` de-sugars into `await g(f(await $x))`
    * Disallowed: `(await $x) |> $y ?? $$` de-sugars into `$y ?? (``await $x)`
* Statement position:
    * ExpressionStatement: `await $yes;`
    * ReturnStatement: `return await $yes;`
    * UnsetStatement: `unset($a[await $yes]);`
    * EchoStatement: `echo (await $yes);`
    * PrintStatement: `print (await $yes);`
    * IfStatement: `if (await $yes) { await $yes_but_new_statement; }`
    * ThrowStatement: `throw (await $yes);`
    * SwitchStatement: `switch (await $yes) { ... }`
    * ForeachStatement: `foreach ((await $yes) [await] as ... ) { ... }`
    * ForStatement: `for (await $yes; await $no; await $no) { ... }`
* Disallowed in all other unmentioned positions.

---

### Hphp Hack Doc Vision Docs Tracking Effects And Coeffects (`hphp/hack/doc/vision_docs/tracking_effects_and_coeffects.md`)

Title: **Tracking effects and coeffects** via capabilities

Start date: July 7, 2020

Status: DRAFT

# Summary

The purpose of this proposal is twofold:

* unify and systematically model reasoning about *effects,* including
  reactivity (all flavors), purity, determinism, global state; and
* build a sound foundation for contextual properties, formally known
  as *coeffects,* such as (implicit) viewer contexts or environments

The proposed *capability-based* approach has the unique benefit that
it can model both effects and coeffects. The main idea is to enhance
the typechecker with a limited form of *implicit parameters* (a simple
coeffect type system) and desugar into and pass capabilities that act
as permissions for effects or resource/context access. It has very
little impact on the Hack syntax and almost no impact to the users.

Although capabilities have been employed in many security systems
since the last century, it wasn’t until 2015-16 that this idea was
welcomed in the programming languages (PL) community to solve a larger
problem of (co)effects. Around that time, two independent PL research
labs have not only formalized their interestingly similar
capability-based type systems; they also implemented it as a *small*
language extension/plug-in of Scala and shown that the approach scales
to large & complex code base of 100K+ lines (Scala Standard
Library). See section “Prior art” for details.

To be clear, this proposal is not about exposing the feature of
implicit parameters to the Hack users (yet); it is about building a
*typechecker primitive* that enables other complex features (both
existing and upcoming) to be modeled elegantly and soundly in the
typechecker, thus enabling Hack to be more expressive. For
example, the (conditional) reactivity/purity would not each
require special context-propagation logic and subtyping and
conformance (caller-callee) rules — everything would simply just “fall
out” from the typing rules with only a minor addition — a capability
type is read from an additional context carried along with regular
typing context. A similar desired behavior can be achieved for contexts
such as normal/experimental by mapping them into implicitly passed
capabilities that statically prevent unsafe calls from non-experimental
code into experimental code (for stability and future compatibility).

# Motivation

As outlined in the Summary, this would provide a reusable mechanism to
neatly and soundly express existing features:

* reactivity & recently introduced purity,
* *conditional* context propagation / treatment of effects in general

and in addition it would enable to easily start tracking a new kind of
effect, such as IO, non-determinism, etc.  For that, a new capability
*domain* is to be introduced, with the appropriate subtyping
relationship. E.g., to track input/output that can be further refined
into disk-based and network-based I/O, the subtyping of capabilities
would be:

```
CanDisk <: CanStdIO // capability for accessing local disk
CanNet <: CanStdIO  // capability for access network/Internet
CanIO <: (CanNet | CanDisk)  // ~bottom type for effect domain IO
```

Now the basic idea is to require that an appropriate capability (or a
subtype) is available in scope, either via being:

* required by a so-called *stoic* function/method; or
* captured from a *contextful* (potentially effectful/impure) function.

The prominent way to propagate these capabilities is to treat them as
*implicit* parameters, as section Prior Art explains.

***Note***:
The syntax `@{Capability1, ...}` below is just a **desugaring** and to
illustrate how typechecker would see a function signature; it is
intentionally chosen to resemble the standard “Γ **@ C**” notation for
coeffects (as opposed to “T ! E” of effects). Comment
`/*{Capability}*/` denotes capabilities that are filled in by the
typechecker, which is akin to passing implicit arguments as it has no
implications on the user code.

```
// 2 stoic functions that require different capabilities
function log2disk(string $message)@{CanDisk}: void { ... }
function publish_paste(string $text)@{CanNet}: void { ... }

function log_and_publish_as_paste(string $msg)@{CanIO}
  log2disk($message)/*{CanIO}*/;      // ok: CanIO <: CanDisk
  publish_paste($message)/*{CanIO}*/; // ok: CanIO <: CanNet
}
```

## Effect polymorphism

Effect polymorphism is the property that enables writing a single
definition for a function/method whose effect can vary depending on
the arguments passed. Contexts and associated capabilities can be seen
as (co)effects. E.g.:

```
function handle_messages(
   vec<string> $msgs,
   // denoted by: => in [FLiu’16-17]
   //             @local in [OOPSLA'16]
   <<__Contextful>> (function(string): void) $log
): void { ... }

function pure_context(): void {
  handle_messages(vec["a", "b"],
    // no capability flows in
    (string $msg) ==> {});
}
function disk_context()@{CanDisk}: void {
  handle_messages(vec["a", "b"],
    // lambda captures the capability from outer scope
    (string $msg) ==> log2disk($msg)/*{CanDisk}*/ );
}
```

The new attribute `<<__Contextful>>` will be described shortly; its
idea is to allow passing *both*:

* functions that *not* capture/require context (e.g., pure ones), and
* contextful functions that capture some context,

which enables effect polymorphism. This *by design* solves the
*conditional* context propagation, which is just a
specific instance of effect polymorphism (with reactivity being
considered as effect). For convenience, we could also support
desugaring of `log2disk` into the explicitly written lambda in the
last function above.

***Note***: `<<__Contextful>>` is can be internally desugared from the
existing syntax for most use cases such as reactivity, as it will be
explained later.

Compared to desugaring into generics, distinguishing between the two
kinds of functions at the type level provides benefits similar to the ones
explained in [Rytz'14]§2.2 and [ECOOP'12]. First, it enables a
clean mapping from a lightweight user syntax (considered in a separate HIP)
and type-checking errors does not involve mismatches of inferred types.
Second and more important, provides a means to accept a function that
makes arbitrary effects in *any* domain without impact on the user syntax.
For example, adding a new effect domain (e.g., what exception is thrown,
whether it is deterministic, what type of IO, etc.) would require:
- inferring the new capability in existing code for every call site,
  essentially regressing type-checking whenever a new effect domain is added;
- new syntax to be placed whenever we want to account for the new domain.

The classic example is Java, where in order to consider a new
domain of effects such as Exceptions, you need to add a new version of
function parameterized by additional generic `E` as follows:
```
<E> void handle_messages(
  Vector<String> msgs,
  FunctionThatThrows<String, Void, E> log
) { ... }
// NOTE: N+1 definitions needed for N domains of effects
void handle_messages(
   Vector<String> msgs,
   Function<String, Void> log
) { ... }
```
Therefore, the generics-based mapping does not scale well; this is further
explained later on and in materials listed under section Prior art.

Fortunately, only a handful of functions, namely higher-order functions,
will surface this distinction, so the impact of the semantics and syntax
is very limited compared to the clear benefits it provides for the users,
as well as the soundness and performance of the typechecker.

### Current ad-hoc *conditional* effects

Without the proposed way of supporting effect polymorphism, the above
snippet would need to be expressed like this:

```
<<__CanIO, __AtMostIOAsArgs>>
function handle_messages(
   vec<string> $msgs,
   <<__CanDisk, __AtMostIOAsFunc>> (function(string): void) $log
): void { ... }
```

Besides annotation burden, this clearly does not scale in terms of
implementation effort because each use case of capability *Cap*
necessitates special handling of a brand new set of attributes such
as: *AtMost{Cap}AsArgs, AtMost{Cap}AsFunc, Only{Cap}IfImpl*, etc. If
encoded with the old (current) system, the same challenge applies for
every new effect domain or contextual information propagation.

## Subtyping

Current use cases that would be superseded by capabilities, notably
reactivity/purity, specially type-check function calls and methods, by
checking if a function (or method) can soundly call another (or
override, respectively). This challenge is often phrased as “calling
conventions”.

With the newly proposed system, one could also instead rely solely on
the subtyping of capabilities, considering implicit parameters are in
*covariant* position. The desired behavior would simply “fall out”
from normal typing rules, provided that capabilities are treated as
the last/first argument in the typechecker:

```
function handle_messages(
   vec<string> $msgs,
   (function(string)@{CanIO}: void) $log
): void { ... }

function pure_context(): void {
  handle_messages(vec["a", "b"],
    // absence of capability is same as top type
    // note: {} is equivalent to {CanNothing}
    (string $msg)/*@{}*/ ==> {});
}
function disk_context()@{CanDisk}: void {
  handle_messages(vec["a", "b"],
    // ok: function(string){CanDisk}:void <: function(string){CanIO}
    //     because CanIO <: CanDisk
    log2disk);
}
```

However, such *monomorphic* handling is *not* fine-grained — it
doesn’t tell much about what precise effects the `handle_messages`
function can make besides “any IO effect”, which is equivalent bottom
type for this particular capability lattice of IO effects. Introducing
generics for capabilities would not only complicate inference without
solving the problem, but it would also introduce a large cognitive
overhead & burden for the Hack users, as section Prior art (and
associated literature) confirm:

```
function handle_messages<C super CanIO>(vec<string> $msgs,
   (function(string)@{C}: void) $log
): void { ... }
```

# User experience

## Syntax

This proposal by itself doesn’t require any changes to the parser. The
`<<__Contextful>>` attribute would just be specially interpreted by
the typechecker. The rest are just typechecker changes that impact
the developers of Hack.

To decouple this proposal from others, this proposal uses a
self-contained *desugared* syntax as explained above. To repeat, the
`@{Capability}` part is *not* the user syntax and neither is the
`__Contextful` attribute — it is just the way how typechecker sees the
signatures of functions and methods.

All syntax used in this proposal is for explanatory purposes and a
future HIPs on contexts and reactivity/purity will explain the *new*
syntax and how it maps to the implicit capabilities in this proposal.

## Semantics

The Hack users will need to be aware that capturing a capability from
a lambda makes it more restrictive. In the [FLiu’16-17] such
*contextful* functions are referred to as
non-stoic/free/impure. Conversely, stoic functions cannot capture a
capability but *can* require some. In [OOPSLA’16], contextful
functions are treated as 2nd-class, similar to procedures in Algol and
Pascal but still less restrictive.

This proposal calls for distinguishing stoic/monomorphic functions vs
contextful/polymorphic functions at the *type* *level*. E.g.:

```
function higherOrder(
  (function(): int) $monoStoicAndPure,
  (function()@{CanThrow}: int) $monoStoic,
  <<__Contextful>> (function(): int) $poly,
  <<__Contextful>> (function()@{CanNonDet}: int) $poly2,
): void
```

While the syntax can be massaged so that this distinction is opaque at
a syntactic level, users do need to be aware of the semantics. The
first 2 argument functions are stoic, meaning that they cannot close
over (capture) a capability. The last 2 argument functions are not;
they are effect-polymorphic in a sense that they represent a
computation created with *arbitrary context*, thus capturing any
number of capabilities.  Here is an example of passing each kind of
argument function:

```
function throwingIOstuff()@{CanThrow,CanIO}: void {
  higherOrder(
    /*$monoStoicAndPure=*/() ==> 42,

    /*$monoStoic=*/ ()@{CanThrow} ==>
      throw/*{CanThrow}*/ new Exception(),

    /*$poly=*/() ==>
      fopen("/non/existing/file")/*{CanThrow}*/
        ->readInt()/*{CanIO}*/,

    /*$poly2=*/()@{CanNonDet} ==>
      randomUpTo(2)/*{CanNonDet}*/ +
      Console\readInt()/*{CanIO}*/,
  );
)
```

In fact, implicitly passed capabilities are a restricted form of *coeffects.*

### Implicit arguments as coeffects

To understand the need for imposing restrictions on
capability-capturing lambdas, it helps to illustrate how implicit
parameters behave. They can be bound at either *declaration* site and
*call* site, e.g.:

```
function nondeterministic_context()@{NonDet}: void {
  // capability captured by the lambda that has no context
  $decl_site_bound = () ==> randomUpTo(1)/*{NonDet}*/
}
function deterministic_context()@{}: void {
  // capability required at each call site
  $call_site_bound = ()@{NonDet} ==> randomUpTo(2)/*{NonDet}*/
  ...
}
```

The former may be fine for certain (future) use cases, perhaps
environments, but requires careful consideration. If we allowed this
for effect capabilities, this could lose track of effects if the
capability escapes:

```
function wrap_random_as_deterministic(
)@{NonDet}: (function(): int) {
  return () ==> randomUpTo(1)/*{NonDet}*/
}

void nondeterministic_context(...)@{NonDet}: void {
  $some_class->property = wrap_random_as_deterministic()
}
void deterministic_context(...): void {
  ($some_class->property)() // leak
}
```

but the soundness ultimately depends on whether:

* capabilities can ever be introduced “out of thin air” akin to
  *unsafe* blocks in Rust or *FIXMEs* in Hack; and
* lifetime of a capability can be extended beyond declaring *scope.*

### Implicit capabilities as effects

Therefore, capability-based effect systems implemented in a host
language where capabilities are first-class, i.e., regularly typed
variable, need to reintroduce some restrictions. Two such systems are
[FLiu’16-17] and [OOPSLA’16] as the section Prior arts explains.

The former is a simpler but more restrictive option, quoting key bits
from Section 3:

> we need to change the definition of the function pure to **exclude
> free function types from the pure environment**. This restriction is
> important, because there’s no way to know what side effects there
> might be inside free functions. **If stoic functions have access to
> free functions, we’ll loose the ability to track the effects of
> stoic functions in the type system.**

Porting the former system to Hack would mean treating all functions as
non-stoic/impure initially, allowing them to capture capabilities.

The latter system is implemented in Scala, with capability being
represented as implicit parameters, therefore it reimposes a
*2nd-class* value discipline (the challenge is also known as the
[funarg problem](https://en.wikipedia.org/wiki/Funarg_problem)) on
such capabilities as well as capability-capturing functions. The
[[OOPSLA’16]](https://dl.acm.org/doi/pdf/10.1145/2983990.2984009)
paper describes the rationale on pages 1-2:

> Second-class values in the sense of ALGOL have the benefit of
> following a strict stack discipline (“downward funargs”), i.e., they
> cannot escape their defining scope. This makes them **cheaper to
> implement** ...  Since **first-class object**s may escape their
> defining scope, they **cannot be used to represent static
> capabilities** or access tokens – a task that second-class values
> are ideally suited to because they have bounded lifetimes and they
> have to “show up in person”.

Porting that to Hack would mean:

* treating capability-capturing lambdas (e.g., `$decl_site_bound`) as
  2nd-class, preventing them from being:
    * stored in a property; or
    * returned (to be reconsidered, see end of previous subsection).

(Some of these restrictions are reconsidered under Unresolved questions.)

In either porting direction, we would need to:

* gradually change some functions to track effects via capabilities
  desugared from the old attribute syntax or the new context syntax;
* work around restrictions reimposed for soundness by
  over-approximating effects, e.g., implicitly desugaring some
  attributes or functions to have `CanEverything` capability.

As the following snippet demonstrates, this latter approach is more
expressive in a sense that we can still pass around such contextful
(impure) function.

```
function nondeterministic_context()@{NonDet nondet}: void {
  // note: $get_int is no longer 1st-class
  $get_int = () ==> randomIntUpTo(5){nondet};

  // it can still be passed around safely nonetheless
  eagerly_call($get_int);
}

function eagerly_call(<<__Contextful>> (function(): int) $f) {
  $f(); ...
}
```

However, the treatment of capabilities as non-1st-class
(`<<_Contextful>>`) correctly disallows untracked effects.

```
new Store($get_int) // ERROR: expected stoic but got contextful
...
class Store {
  function __construct(public (function():int) $callback) { ... }

  // note: omitting the attribute wouldn't allow passing a stoic function
  function store(<<__Contextful>> (function(): int) $f) {
    this->$callback = $f; // ERROR: expected stoic got contextful fun.
  }
}
```

Note that we do not need to expose the 2nd-class terminology to the
users, we can just explain how contextful functions are different from
stoic functions, which is needed for adopting either of the two
approaches!

# Implementation details

For most use cases, we wouldn’t require support in the runtime. As an
example, reactivity and purity of functions is already tracked with a
special bit in HHVM, so they don’t need to rely on having runtime
values for implicit capabilities/contexts.  Consequently, capabilities
can be tracked purely *statically*, and the run-time does not need to
know about the existence of implicit parameters — unless we decide to
unify it with a pending HHVM proposal on implicit contexts.

The feature can be implemented in the typechecker in several mostly
*non-overlapping* phases:

1. implement distinction between regular (stoic) lambdas and
    effect-polymorphic/contextful ones (the distinction is also needed
    for alternative #1)
2. implement desugaring of contextful functions (those that can
   capture capabilities) passed to *higher-order* functions as
   2nd-class or impure/non-stoic:
    1. either via an existing attribute syntax
    2. or a new *contexts* syntax, to be proposed in a separate HIP;
3. modify the typechecker environment to also carry capabilities
   (multiple, or a single one using an intersection type)
4. modify the typing rule for function application to check if
   the required capability is available in the (implicit) context;

Once phase 2 is finished, we should add these annotations to WWW,
which would allow for smoke-testing
phases 3.-4. (as well as 1.).  As a part of phase 2, we may need to
implement a subset of functionality of 2nd-class values, but this is a
relatively easy task:

* built in 2 days during a Hackathon
  (even with user syntax that is not needed here);
* also implemented and tested in another language, Scala in *only*
 [**400 lines**](https://github.com/losvald/scala/blob/3070ee4931f0429a80a517a0c167028ed3e5865d/src/compiler/scala/tools/nsc/typechecker/EscLocal.scala#L65)
 in typechecker &
 [10 lines](https://github.com/losvald/scala/blob/9495ebca55f712ae3459fe5eb86ffab2477fde8a/src/library/scala/Esc.scala)
 in standard library (see section Prior art for details)

For prototyping & unit-testing, we actually also modified the
parser to support parsing Concrete Syntax Tree directly into
capabilities, but that should obviously be gated so that WWW cannot
use it / be aware of this exploit.

### Encoding multiple capabilities

Multiple capabilities that express permissions to make effect in
multiple domains (e.g., determinism, exceptions, reactivity, etc.),
are encoded via an intersection type. E.g., `NonDet & Throwing`
represents having *both* the capability for non-deterministic
execution as well as the one for throwing exceptions,
respectively. Since `NonDet & Throwing <: NonDet`, it satisfies the
requirement of / privilege for non-deterministic
execution. Intuitively, intersection gives the caller more privilege
(not less) and soundly approximates multiple capabilities.

Unannotated functions in Hack should implicitly have
some default set of capabilities, at the very least
the capability to call into non-deterministic code
(including other unannotated functions or methods)
and permit untracked mutation (such as global state),
neither of which is allowed in reactive/pure code.

Considering this simplified scenario, we encode this set of
capabilities and give it a name using an intersection type.

```
type CanDefaults = (CanNonDet & CanMutateUntracked);
```

The benefit of using intersection type as opposed to
interface inheritance is apparent considering
a hypothetical call site that passes each of
the comprising capabilities *separately*, e.g.:

```
// implicitly @{CanNonDet,CanMutateUntracked}=CanDefaults
function randomBool(): bool { ... }

class CachedNonDetFactory {
  private static ?seed = null;

  function withNonDetBool<T>()@{CanMutateUntracked}(
    (function (
      <<__Contextful>> function()@{CanNonDet}: bool
    ): T) $continuation
  ) {
    return $continuation(()@{CanNonDet} ==> {
      // captures the outer capability: required for mutating $seed
      self::$seed ??=/*{CanMutateUntracked}*/ currentUnixTime();

      // passes the explicit as well as the captured capability
      randomBool()/*{(CanNonDet & CanMutateUntracked)}*/
    });
  }
}
```

Conversely, if we relied on *nominal subtyping* and defined

```
interface CanDefaults extends CanNonDet, CanMutateUntracked {}
```

then the call to `randomBool` would result in a typing error because
`CanNonDet & CanMutateUntracked` would *not* be a subtype of
`CanDefaults` anymore.

### Default capabilities as an unsound migration mechanism

The capability passing as described so far is essentially the
same as implicit parameter passing in several other languages.

```
function randomUpTo(int $n)@{CanNonDet}: int
```
would be modeled in Scala as follows:
```
def randomUpTo(n: Int)(implicit $_: CanNonDet): Int
```

An interesting question arises: what if we make some capabilities
optional; i.e., provide a default for the corresponding implicit
parameter?

This is surely unsound for effect tracking as the capability is no
longer required at the call site; in fact, it is a means of obtaining
the corresponding privilege "out of thin air".

In a language with *default* implicit parameters such as Scala,
this would be written as:

```
def randomBoolUnsafe()(implicit $_: CanNonDet = new CanNonDet {}): Int
```

In Hack, we express this using by enhancing our prototypical syntax as
follows:

```
// requires: {} (no capability, i.e., can always be called)
// provides: {CanNonDet}
function randomBoolUnsafe()@{ +CanNonDet}: void { ... }
```

where the part following `+` in the braced list (`{}`) after `@`
denotes the *unsafe* (i.e., provided but not required) capability.

## Feature interactions

This feature would supersede the special-cased (ad-hoc) logic for
type-checking reactive and pure functions.

### Dependent types

The expressibility would be greater if Hack adopted path-dependent
types, see Unresolved questions below.

### Reification & HHVM implicit contexts

Ideally, this should influence the design of HHVM’s proposed but put
“on hold” feature of implicit contexts because it would be nice if we
could one day unify these two via reification:

* non-reified capabilities → only static enforcement in the typechecker;
* reified capabilities → also run-time inspection/retrieval of the
  corresponding context.

See the section Future possibilities for more details.

# Design rationale and alternatives

Implicit parameters offer expressiveness that can be hardly surpassed
by any other feature. Therefore, it’s better to compare alternatives
for problems solvable via implicit parameters, most notably effects.

### Alternative #0: special tags + ad-hoc typechecking logic

This is mostly similar the how the current typechecker reasons about
reactivity.  There is an extra step in typing functions and their call
sites, which examines the tags and validates the rules. However, this
means that subtyping, for example, needs to be special-cased for each
of these reactivity-like tags. Second, many attributes behave
specially and have to be carefully handled at several stages in
parsing & type-checking. Third, this is complicated by fact that we
support what is known as *conditional* reactivity, i.e., a function is
reactive only if its argument function is reactive or a subclasses are
known to be reactive. This clearly doesn’t scale in terms of
implementation and complexity (due to special cases) as soon as one
introduces new form(s) of “context” — essentially new (co)effects:

* purity — cannot call `Rx\IS_ENABLED`, but otherwise stricter than `Rx`;
* IO (e.g., from data source / or to sinks);
* non-determinism and parallelism;
* async, as well as sync (immediately awaited async call);
* contexts that restrict the flow of (sensitive) data;
* exceptions?

There is a general consensus to move away from
this verbose and overly restrictive syntax.  For the alternatives
below, the new syntax for reactivity and purity — or *contexts*
in general — would be *desugared* into more *reusable and
well-known primitives*. This would solve the major challenge of the
current approach — each “domain” of (conditional) context
needs a special type-checking logic that is not
well-integrated with the rest of the type system, as well as
propagation through different stages (bug-prone and inflexible).

### Alternative #1: type-and-effects systems

This is the oldest and most common way of encoding effects. Some early
paper(s) point out different challenges in sound and not overly
restrictive approximation of runtime behavior (especially using
constraint-based solvers?).  The main disadvantage of this approach is
that it is *quite invasive* — nearly every typing rule would need to
be modified — and effects carried in the typechecker. The latter may
lead to significant performance overhead, and the former is also less
than ideal in terms of implementation cost & maintenance effort, as
well as overall complexity, of the typechecker code.

The current proposal requires far *fewer* modification to typing rules;
*only* function call needs to read from the (implicit) capability context,
and *only* function declarations add to it. Conversely, type-and-effect
systems require a type in *every* position to be associated with an effect
(as it can potentially be a function that makes an effect).
The trade-off is somewhat similar to that of virtual dispatch via:
- vtable (context per class) -> additional capability context
  is looked up per each function call;
- [fat pointer](https://en.wikipedia.org/wiki/Dynamic_dispatch#Fat_pointer)
  (every pointer is *doubled* in size) -> encoding of a type
  is bloated by being *paired* with an effect.

Since virtual dispatch in languages with a virtual machine and JIT
has negligible performance overhead (unlike statically compiled ones),
the performance impact on the typechecking is expected to be minimal.

### Alternative #2: monad-based encoding

In [POPL’18], Martin Odersky et al have found that monadic encoding of
“freer” monad suffered
[5x performance hit](https://dl.acm.org/doi/pdf/10.1145/3158130#page=22)
compared to encoding via implicit parameters. This was done in Scala,
where implicit arguments carry slight run-time overhead, meaning that
implementing a subset of implicit parameters functionality that
doesn’t require run-time mechanism in Hack would lead to even bigger
performance wins compared to wrapping types in monads.

### Alternative #3: algebraic effects

Based on my high-level understanding of the relatively few papers that
talk about it:

* `+` provide a modular way for customizing effect handling
* `-` not rigorously verified via large case studies for usability & performance;
* `-` requires special treatment in [IR](https://dl.acm.org/doi/pdf/10.1145/3341643)/VM to efficiently do [CPS translation](https://dl.acm.org/doi/pdf/10.1145/1291151.1291179)
    (not so practical without invading into HHVM internals)

Even if we invested a lot into building the support for *efficient*
translation to Continuation Passing Style (CPS), which does not look
trivial at all (see Andrew’s [ICFP’07] paper), we would need to
perform more “testing in the wild” as these approaches do not have
comparable user & performance studies, unlike capability-based
(co)effect systems.  Finally, the most well-known approach, published
in [ICFP’13](https://dl.acm.org/doi/pdf/10.1145/2500365.2500581), is
*not* entirely static and require dependent types; and another
practical candidate
([Effekt @ Scala’17](https://dl.acm.org/doi/pdf/10.1145/3136000.3136007))
requires multi-prompt delimited continuations, which would in turn be
inefficient as delimited continuations correspond to monads (see above
why monads are inefficient).

Fortunately, representing effects using a type lattice is both
flexible and modular. Please refer to above examples and read details
can be found in [ECOOP’12] and [OOPSLA’16] papers in section Prior
Art.

## Impact to Hack users & WWW

This proposal does *not* expose implicit arguments as 1st-class
language feature; so Hack users:

* *cannot* pass arbitrary values/contexts implicitly;
* do *not* need to know that contexts, such as (shallowly/locally)
  reactive or deterministic, would be from a type-checking perspective
  propagated just like implicit arguments.

Nonetheless, it does help to create a mental model behind each use case,
by generally describing that:

* contexts internally map to capabilities/permissions to do stuff;
* they are propagated in a similar fashion as implicit arguments in
  Scala and Haskell.

The latter would actually help Hack developers with some experience in
either programming language.

Impact to the average WWW developer is negligible, but power users
may want to know:

1. the rationale for effect-polymorphic functions, explicable to Hack
   developers using “conditional” effect examples;
2. that the syntax for reactive/pure contexts internally
   desugars to implicit parameters;
3. *optional*: new “capability type” if we need to support advanced
   use variable-context class hierarchies (see section Unresolved
   questions).

(1.) is a small price to pay that would allow us to eliminate
annotation boilerplate present. However, this is something that even
alternative approaches require (see section Prior art). Regardless of
the implementation direction (i.e., whether capability capturing is
restricted or requires 2nd-class treatment), we will need to translate
user-facing annotations on higher-order functions into either
effect-polymorphic/2nd-class or monomorphic functions. As of June
2020, it seems that only about a thousand function signatures involve
higher-order functions that would require this new type of
annotation(s). Verified via bunnylol: `tbgs (function(`

*Only* the Hack developers will care about:

* capability lattice that enforces *calling convention* for each types
  of contexts such as reactive, non-deterministic, etc., (each type
  corresponds to a use case of implicit arguments internally);

# Drawbacks

I think we should support a foundation for capability-tracking
(co)effects, but the question is how many of the subfeatures should be
present:

* for prototyping, exposing the syntax for ad-hoc in Concrete Syntax
  Tree but gating that parser feature;
* exposing capabilities as types in user code (see section Unresolved
  questions);
* 2nd-class treatment of capability-capturing functions vs disallow
  capturing capabilities from stoic functions (the former is similar
  to how functions in Pascal/Algol behave, see [OOPSLA’16]).

# Prior art

Implicit parameters are a concrete instance of a
[coeffect](http://tomasp.net/academic/papers/coeffects/coeffects-icalp.pdf#page=3)
system, and coeffects are more general than effects (mathematically,
they correspond to indexed comonads as opposed to monads, see
[ICALP’13, JENTCS’08]).

They have been widely accepted as an essential feature of Scala; in
fact a study show that >90% repositories on GitHub use them (see
[Conclusion in [POPL’18]](https://dl.acm.org/doi/pdf/10.1145/3158130#page=26)).
Haskell also supports them
([link](https://www.haskell.org/hugs/pages/users_guide/implicit-parameters.html)),
albeit in a somewhat more restricted form.

In [ScalaDays’15], M. Odersky, the designer of Scala (and a top-notch
PL researcher), pioneered the idea of using implicit *capabilities*
for modeling *permissions*. Implicit parameters are already a feature
in Scala since v2, and overhead of using implicits has been evaluated
on a large corpus of real-world code in [POPL’18], shown to perform
**5x faster than monads**.

Leo's [OOPSLA’16] work has won the Distinguished Artifact Award; it
designs, machine-proves and implements a *capability-based (co)effect*
system using second-class values (they give more expressiveness
compared to the similar approach of Fengyun Liu). It also evaluates
the annotation burden — relevant for our code-mod — showing that
**<2% of code changed** in Scala Standard Library has to be changed to
propagate effects (checked exceptions, parallelism).  The second
author of [OOPSLA’16], G. Essertel has machine-proved in Coq:

* [Simply-Typed Lambda Calculus (STLC) with 2nd-class values (1/2)](https://github.com/TiarkRompf/scala-escape/tree/master/coq#coq-proofs-for-stlc-12);
* [System D **with subtyping** (~Scala\{DOT}) and 2nd-class values](https://github.com/TiarkRompf/scala-escape/tree/master/coq#coq-proofs-for-dsub-12).

(System D with subtyping has been proven sound by Tiark Rompf and Nada Amin in
[*another* OOPSLA’16 paper](https://dl.acm.org/doi/pdf/10.1145/3022671.2984008).)

Fengyun Liu has built a similar but somewhat simpler capability-based
effect system in his PhD dissertation [FLiu’16-17]. The two
simplifications compared to [OOPSLA’16] are (the analogy is parenthesized):

* *effect-tracking* (non-stoic) lambdas *cannot capture* capabilities
  (as opposed to being treated 2nd-class);
* capability parameters are *explicit* (less practical for users & code mod);

He also machine-proved in Coq his approach generalized to STLC,
System F and System D on [GitHub](https://github.com/liufengyun/stoic#stoic).

The common practical challenge of effect systems is known as *effect
polymorphism*, initially described in [POPL’88]. Papers
[ECOOP’12, RytzOdersky’12] show how to encode effects as lattice in a
modular way, and more importantly show how to *annotate higher-order*
functions in a lightweight way that supports effect polymorphism. The
importance of providing effect polymorphism without heavyweight
annotations has been reiterated by a recent work on *gradual*
polymorphic effects in [OOPSLA’15]:

> effect polymorphism, which is crucial for handling common
> higher-order abstractions with enough precision

who implemented
[Effscript](https://pleiad.cl/research/software/effscript) as a Scala
plug-in after extending the theory of [ECOOP’12] (also implemented as
a Scala plug-in).

Annotating signatures of higher-order functions is “**common
denominator**” to all of the approaches above that support effect
polymorphism, namely: [OOPSLA’16], [FLiu’16-17], [ECOOP’12], [RytzOdersky’12].
This is also consistent with Hack’s more verbose
solution for conditional reactivity, which is just a verbose version
of effect polymorphism for the reactivity effect (or in coeffect sense,
a capability for accessing reactive backend).

One of the most practical promising alternative to this proposal,
which is along the lines of [OOPSLA’16] and [FLiu’16-17], is the
type-and-effect system presented in [RytzOdersky’12] and [Rytz’14],
implemented in [EffScript’15], with the idea original idea originating
from [ECOOP’12]. However, the ergonomic treatment of “relative
declaration” requires *dependent* function types as [[Rytz’14]
§3.2](https://lrytz.github.io/download/thesis-rytz.pdf#page=54) points
out, and its implementation is quite complexed judging from
[EffScript’15].  Fortunately, *both* the current proposal and their
approach require the **distinction between** *effect-polymorphic* vs
*monomorphic* functions, which confirms that this should be a
milestone on its own.

[ImplicitsRevisited'19] explains design mistakes of Scala 2 implicits
and reveals how they are being fixed in Dotty and soon in Scala 3.
The current proposals avoids a lot of these by not exposing the
feature as a first-class language construct and instead ports only a
*tiny* subset of the Scala's behavior specifically suited for
passing contextual information and enforcing calling conventions
(but *not* for type classes).

## References:

Most related work was implemented as extensions of Scala, but there’s
also Haskell and others (E-lang, EffScript).

* ScalaSpec: [Scala Language Specification](https://www.scala-lang.org/files/archive/spec/2.11/) (M. Odersky et al.)
* **ScalaDays’15**: [**Scala** - where it came from, where it is going](https://www.slideshare.net/Odersky/scala-days-san-francisco-45917092) (M. Odersky), pages 44-46
* **POPL’18**: [Simplicitly](https://dl.acm.org/doi/pdf/10.1145/3158130) (M. Odersky et al.)
* **OOPSLA’16**: [...Affordable 2nd-Class Values for Fun and **(Co-)Effect**](https://dl.acm.org/doi/pdf/10.1145/2983990.2984009) (Leo Osvald et al.)
    * https://www.dropbox.com/s/iag4gho34ewzk57/OOPSLA16-coeffects_code-changes%2Bresults_README.pdf?dl=1
    * https://github.com/losvald/scala/compare/2.11.x...losvald:esc#files_bucket
    * https://github.com/TiarkRompf/scala-escape/tree/master/coq#coq-proofs-for-dsub-12
* **FLiu’16-17**: [A Study of Capability-Based Effect Systems](https://infoscience.epfl.ch/record/219173?ln=en) (Fengyun Liu), PhD dissertation
    * https://github.com/liufengyun/stoic#stoic
    * https://github.com/liufengyun/stoic/tree/master/2017
* **Haskell’16**: [Effect capabilities for Haskell](https://www.sciencedirect.com/science/article/pii/S0167642315004062#br0170) (Figueroa, Tabareau, Tanter)
* Elang‘98: [The E language](http://erights.org/elang/index.html) (M.S. Miller)
* EffScript’15: [EffScript](https://pleiad.cl/research/software/effscript) (authors of [OOPSLA’15] below)
* ImplicitsRevisited'19: [Lambda World 2019 - Implicits Revisited](https://www.youtube.com/watch?v=uPd9kJq-Z8o) (M. Odersky)
* **SID-1**: [Scala Named and Default arguments](https://docs.scala-lang.org/sips/named-and-default-arguments.html) (L. Rytz), subsection Implicit parameters
* **ECOOP’12**: [Lightweight Polymorphic Effects](http://www.lirmm.fr/~ducour/Doc-objets/ECOOP2012/ECOOP/ecoop/258.pdf) (Rytz, Odersky, Haller)
* RytzOdersky’12: [Relative Effect Declarations for Lightweight Effect-Polymorphism](http://infoscience.epfl.ch/record/175546/files/rel-eff_1.pdf) (L. Rytz & M. Odersky)
* Rytz’14: [A **Practical** Effect System for Scala](https://lrytz.github.io/download/thesis-rytz.pdf) (L. Rytz), PhD dissertation
* OOPSLA’15: [Customizable **Gradual** Polymorphic Effects for Scala](https://dl.acm.org/doi/pdf/10.1145/2858965.2814315) (Toro & Tanter)
* **ICALP’13**: [Coeffects: Unified static analysis of context-dependence](http://tomasp.net/academic/papers/coeffects/coeffects-icalp.pdf) (T. Petricek, D. Orchard, A. Mycroft)
* ICFP’14: [Coeffects: A calculus of context-dependent computation](http://tomasp.net/academic/papers/structural/coeffects-icfp.pdf) (T. Petricek, D. Orchard, A. Mycroft)
* OrchardTalk’14: [Coeffects: contextual effects / the dual of effects](https://www.cs.kent.ac.uk/people/staff/dao7/talks/coeffects-dundee2014.pdf) (D. Orchard, T. Petricek, A. Mycroft)
* OrchardPhD’14: [Programming contextual computations](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-854.pdf) (Dominic Orchard), PhD dissertation
* ICFP’07: [Compiling with Continuations, Continued](https://dl.acm.org/doi/pdf/10.1145/1291151.1291179) (Andrew Kennedy)
* ICFP’19: [Compiling with Continuations, or without? Whatever.](https://dl.acm.org/doi/pdf/10.1145/3341643) (Y. Cong, Leo Osvald, G. Essertel, T. Rompf)
* Scala’17: [Effekt: Extensible Algebraic Effects in Scala](https://dl.acm.org/doi/pdf/10.1145/3136000.3136007) (Brachthäuser & Schuster)
* ICFP’13: [Programming and Reasoning with Algebraic Effects and Dependent Types](https://dl.acm.org/doi/pdf/10.1145/2500365.2500581) (E. Brady)
* DOT’16: [Type Soundness for Dependent Object Types](https://www.cs.purdue.edu/homes/rompf/papers/rompf-oopsla16.pdf) (Tiark Rompf, Nada Amin)
* POPL’88: [Polymorphic Effect Systems](https://dl.acm.org/doi/pdf/10.1145/73560.73564) (Lucassen & Gifford)
* Talpin‘94: [Type and Effect Discipline](https://www.sciencedirect.com/science/article/pii/S0890540184710467) (Talpin & Jouvelot)
* JENTCS’08: [**Comonad**ic notions of computation](https://www.sciencedirect.com/science/article/pii/S1571066108003435) (Uustalu & Vene)

# Unresolved questions

## Encoding capabilities as (dependent) types

This is something that would be needed to cleanly model
partially reactive class hierarchies (see Future possibilities).

```
class SometimesContextful {
  type CanDo
  function do()@{CanDo}: void
}

class NeverRequiresContext extends SometimesContextful {
  type CanDo
  function do()@{}: void { ... }
}

class AlwaysContextful extends SometimesContextful {
  <<__Override>> type CanDo = CanStdIO
  function do()@{CanDo}: void {
    print("stdout")/*{CanStdio}*/;  // ok: CanDo <: CanStdio
  }
}
```

However, dispatching on such partially contextful class hierarchies
requires dependent types:

```
// generic T would be exposed to users, unfortunately
function maybe_contextful_do<T as SometimesContextful>(
  T $maybe_contextful
)@{
  C = $maybe_contextful::CanDo  // path-dependent (OK)
  // C = T::CanDo  // error, no way to prove that type C is matching
}: void {
  maybe_contextful->do()
}
```

It seems that this could be modeled via Pocket universes, which offer
enum-dependent types, albeit that would require the capability
definitions to be expressed via corresponding enum fields for each
class in the hierarchies, which may not be practical at all.

**Q: Considering such uses cases are fairly common in WWW, should we
generalize dependent types and/or pocket universes?**

## Interaction with lazy collections

This is somewhat related to the above, and has been extensively
studied in [OOPSLA’16] (see Prior Art).  The gist of it is shown by
the following snippet:

```
class Iterable<T> {
  type MaybeContextful;  // used as meta-attribute
  function map<T, U>(
    <<MaybeContextful>>
    (function(T): U) $f): Iterable<U>
}

class Vector<T> extends Iterable<T> {
  type MaybeContextful = __Contextful;
  <<__Override>>
  function map<T, U>(
    <<MaybeContextful>>  // = __Contextful
    (function(T): U) $f): Iterable<U>
}

class Iterator<T> extends Iterable<T> {
  type MaybeContextful = Nothing;
  <<__Override>>
  function map<T, U>(
    // = no attribute (accepts pure functions only!)
    (function(T): U) $f): Iterable<U>
}
```

Note that lazy collections such as subtypes of `Iterator` don’t call
argument function `$f` immediately, they could delay effects,
therefore we don’t want to permit capturing capabilities in their
argument functions passed to `map`. E.g.:

```
function would_hide_effect(
  Iterator<int> $iter
)@{CanNonDet}: Iterator<int> {
  return $iter->map(
    // error: expected contextless (stoic) function
    //     but the passed lambda captures a capability
    (int $x) ==> $x + randomUpTo(1)/*{CanNonDet}*/
  );
}
```

**Q: Is this a better way, or we should stick with alternatives by exposing capabilities as generics (see previous subsection)?**

## 2nd-class/restricted treatment of closures

One can argue that we may not actually need to treat
capability-capturing closures as 2nd-class. The supporting argument
could go along these lines:

* a context (requiring a capability) that once exists cannot disappear; e.g.:
    * the (sub)request has access to reactive backend or it doesn’t
    * execution context is either deterministic or non-deterministic
* a function *cannot* introduce new permissions, it can just opt-out
  of certain permissions by not requiring certain capabilities;
* there is no scope-like facility to introduces a capability type;
* capabilities are not exposed as user types;

The last 2 points were the primary motivation for a conservative
treatment of capability-capturing closures as 2nd-class in [OOPSLA’16]
or impure in [FLiu’16-17] , and also makes the system more generic (if
the first 2 points do not hold).

**Q: Is semi 1st-class treatment of capabilities sound and sufficiently general for our *all* our use cases?**

# Future possibilities

Implicit parameters are one of the simplest forms of coeffects; i.e.,
a “flat” type-and-coeffect system as introduced in the
[ICALP’13 paper](http://tomasp.net/academic/papers/coeffects/coeffects-icalp.pdf#page=3). The
prefix co- stands for **co**ntextual properties and it’s also
essentially a dual of effects (see
[slides](https://www.cs.kent.ac.uk/people/staff/dao7/talks/coeffects-dundee2014.pdf#page=8)).

That means that other language features could be expressed using
implicit arguments under the hood, notably:

* reactive and pure **contexts**
* generic **effects** (by limiting the escaping of capabilities
  through declaration site, see above)
* **modules & features** (contextual by definition):
  (non-)experimental and opt-in features

## Remodel reactivity flavors

There is a one-to-many mapping between reactivity (including purity)
attributes and implicit parameters, e.g.:

* `<<__Pure>>` → `@{CanThrow}` or `@{mixed}` if exceptions untracked
* `<<__Rx>>` → `@{CanRx}`
* `<<__RxShallow>>` → `@{CanRxShallow, ...}`

The *calling conventions* are enforced by design if these capabilities
are organized into a type lattice as follows:

```
CanRxShallow <: CanRx <: CanThrow
```

Intuitively, the more specific the capability type, the higher the
privilege; also some examples:

```
function rx_context()@{CanRx}: void {}
function rx_calling_into_others()@{CanRx}: void {
  // ok: CanRxLocal <: CanRxShallow
  rx_context()/*{CanRx}*/

  // error: CanRxShallow not a subtype of CanRx
  rx_shallow_context()/*{CanRx}*/

  pure_context(); // ok: capability either not passed (mixed) or
  pure_context()/*{CanRxLocal}*/ // passed as a subtype of CanThrow
}
```

Treating pure functions as not having implicit arguments is attractive
from a performance standpoint (typechecker doesn’t need to do extra
work for pure functions), but it is too conservative because pure
functions in the reactive sense do allow certain other kind of
non-conflicting effects such as throwing an exception.


## HHVM’s implicit context as reified capability

Ideally, this should somewhat influence the design of HHVM’s proposed
but postponed because it would be nice if we could one day unify these
two via reification. Some contexts are reified by default such as
reactive ones (tracked via a few bits at run-time), but we may want to
start tracking certain kind of effects statically in the typechecker
and avoid breaking changes to the run-time/VM. The way to do it mark
some contexts/capabilities as non-reified (*internally* in the
typechecker, since there is a finite number of them). E.g.:

```
function with_implicit_context()@{
  ContextOrCapabilityForUseCase1,
  reify ContextOrCapabilityForUseCase2,
}: void {
  // context $ctx2 is available at run-time
}
```

The main benefit of such unification include:

* reified capabilities are *always* part of memoization key (since
  they are available at run-time);
* non-reified capabilities are *never* part of the memoization key;

which hopefully neatly solves the dilemma about impact on performance
and/or correctness of memoization, unblocking the path to finalize the
semantics of implicit contexts. E.g.:

```
<<__Memoize>>
function contextless(): void

<<__Memoize>>  // NOT part of memo. key as it isn't reified
function throwing()@{CanThrowException}: void
               // exceptions aren't tracked at run-time

<<__Memoize>>  // IS part of memo. key as it is reified
function reactive()@{reify CanRxIsEnabled}: void
  if (RX\IS_ENABLED/*{CanRxIsEnabled}*/) { ... }
  else { ... }
}
```

In the last function, the `RX\IS_ENABLED` would require implicit
capability when type-checking. Moreover, that capability should be
reified as the function can run both within a disabled and enabled
reactivity backend, therefore its result depends is dependent on the
context.

## Modules & features

Further, we could enforce some encapsulated code such as experimental
cannot be called from a normal *module*, by desugaring these
modules into capabilities:

```
// file1
module Normal;  // Normal is a weaker capability than Experimental

function calls_into_experimental()/*@{Normal}*/: void {
  // ERROR: the call requires capability: Experimental
  //                  but got capability: Normal
  // and Experimental is not a supertype of Normal
  // (i.e., the capability cannot be upcast)
  callee_in_experimental()/*{Normal}*/
}

// file2
module Experimental;

function callee_in_experimental()/*@{Experimental}*/
```
with the following subtyping relationship for this domain of (co)effects:
```
Experimental <: Normal
```

Rules for which module can access which other module can be encoded by
carefully organizing more capabilities into a subtyping, which allows
for encapsulation similar to modules/packages in Scala/Java, respectively.

Interestingly, fine-grained use of experimental features can also be
enforced by requiring appropriate capabilities in a similar fashion,
which is comparable to Rust's tracked usage of (unstable) features.
For example, experimental features of union & intersection types
would be modeled as requiring a file-local capability
`CanUnionIntersection`, desugared from the file-level attribute:

```
<<file:__EnableUnstableFeatures('union_intersection_type_hints')>>
```

where the desugaring would simply inject the capability into the
typing environment used when type-checking anything in that file
(but not other files).


## Haskell-like type constraints

From [[OrchardPhD’14] §6.5 (page
135+)](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-854.pdf#page=135):

> **Type constraints can be seen as a coeffect** describing the
> contextual-requirements of an expression. ... This section goes
> towards unifying coeffects and type constraints, which is used in
> the next chapter to encode a coeffect system based on sets using
> Haskell’s type class constraints.  ...  The coeffect approach
> describes the implicit parameter requirements of a function as
> latent coeffects, rather than constraints on the type in the
> approach of Lewis et al.

---

--- METRICS ---
- Files Extracted: 44
- Estimated Token Budget: ~181132 tokens
- Canonical Reference: https://codewiki.google/github.com/facebook/hhvm
