Expand description
What the checker worked out about each expression.
The type checker settles a type for every expression it walks and, until now, threw each one away as soon as the surrounding form had been checked against it. Everything downstream that needed one had to work it out again — and a pass that walks a tree without the checker’s tables cannot, so it guessed from the shape of the source instead. ADR 0019 states the rule this module exists to make keepable: the lowering reads the checker’s answers rather than recomputing them, so the two cannot disagree.
§Recording is not deciding
Nothing here participates in checking. A fact is written after the checker has settled it and is read by nobody during the walk, so adding one changes no diagnostic. That is the property this table is worth having only if it keeps.
§Keys are dense integers, so a lookup is a load
An expression is named by the file it was parsed from and its
ExprId, which cove_syntax::number::number_unit hands out as
0..n over one file with no gaps. Both halves are therefore an index
into a Vec rather than a hash of anything, which is what lets the
checker afford a push per expression.
§An unknown is an answer
Facts::ty answers None only for an expression the checker never
walked. An expression it walked and could say nothing about answers
Some(Ty::Unknown(..)), because “the checker abstained” and “I
never asked” are different facts and a consumer specialising on one must
not act on the other.
§A name a call resolves is not an expression the checker types
The table is total over a function’s expressions with one exception, and
it is the checker’s shape rather than an omission here. A callee is
walked only when the call goes through a value: f(1) where f is a
binding evaluates f, and so does a call through a field holding a
closure. A callee that instead names a declaration — a function, a
struct being initialized, an enum case, a type’s associated function, or
a method reached through a receiver — is resolved against the checker’s
tables and never given a type, because several of those have none to be
given: Point in Point(x: 0.0) names a type, not a value.
So ty answers None for those, and that answer carries the
distinction rather than losing it: a callee with a recorded type is a
call through a value, and a callee without one is a call to a
declaration — which Facts::target then names.
§A declaration’s boundary is a fact too, and it is a small one
An expression is not the only thing the checker settles. It also resolves
every declaration’s signature — what each parameter is, what the receiver
is, what comes back — and a consumer that has to know where a parameter
lives, or which stack an answer comes back on, is asking about the
signature rather than about any expression inside the body. Re-deriving
it from the source would be the same mistake in a new place: a -> module.Thing written in one module and read in another is a name whose
meaning only the checker holds.
Facts::signature is that table, and it is keyed differently from the
expression tables on purpose — see Facts::signature for why a hash is
the right shape here and the wrong one there.
Structs§
- Facts
- What the checker worked out about each expression.
- Method
Target - The declaration a call resolved to, named the way the package names it.
- Signature
- A declaration’s boundary, as the checker resolved it for that body.