This commit is contained in:
Nathan Shively-Sanders 2023-06-16 10:18:46 -07:00
Родитель e71a6c81b5
Коммит 8683f92291
1 изменённых файлов: 38 добавлений и 1 удалений

Просмотреть файл

@ -137,7 +137,44 @@ TODO: Finish this
## Control Flow
TODO: Missing completely
Like symbols, control flow involves a walk of the tree, setting some information for certain kinds of nodes and skipping all other nodes.
For control flow, nodes that can narrow or otherwise introduce type information are the relevant ones.
Containers and declarations are the same as for symbol binding, so those concepts are reused.
Declarations introduce type information; containers form the scope where type information is relevant.
Other nodes may narrow, so they also interact with control flow.
The control flow graph is a directed acyclic graph; that means each relevant node points to its antecedents (parents).
Specifically, each node can have a `flowNode`; this flow node has a `kind` and one or more `antecedents`.
As the binder walks the tree, `bindWorker` assigns the current flow node to specific nodes that can introduce type information.
Specific nodes that affect control flow alter the current flow node, such as `bindWhileStatement`.
Here's an example:
```ts
function f(x: string | number) {
if (typeof x === 'string') {
return x
} else {
console.log(x)
}
return x
}
```
Here, the binder creates a `FlowStart` for `x: string | number`.
Then when it walks the `if/else`, it creates two `FlowCondition` nodes, each with an antecedent of the original `FlowStart`.
The two nodes correspond to the `then` body—where the condition `typeof x === "string"` is true—and the `else` body—where it's false.
For `return x` inside the `then` body, it creates an Unreachable flow node.
It also creates a post-if `FlowLabel` that joins the control flow between the two branches of the conditional.
During checking of various references of `x`, control flow analysis will walk up the tree until it finds a control flow node, at which point it runs back through the control flow graph until it reaches a `FlowStart`.
For example, to check the first `x` reference in `typeof x === "string"`, it walks up *past* the `if` — the condition is not contained in the flow of the `then` or the `else` bodies — and reaches FlowStart.
The type here is `string | number`.
But in the first `return x`, the first flow node it reaches is the `FlowCondition` for the `then` branch of the if/else.
That check narrows `string | number` to `string`.
Finally, the last `return x` starts with the post-if flow node, which unions the types that result from the `then` and `else` branches.
But because the `then` branch returns, it doesn't contribute anything to the union; the resulting type is just `number`.
## Emit flags