... | ... | @@ -84,15 +84,18 @@ There are three parts to this: |
|
|
|
|
|
The net effect is that at the point when we `loadDecls` the declarations, we have a list of `Name`s and unevaluated `TyThing` thunks, which we write into the global environment. Later, when we actually force the `TyThing` thunk, the suspended typechecking computation goes ahead and looks up the thunk in the environment, which has since been updated with the thunks we need.
|
|
|
|
|
|
**Variation: tying the knot when typechecking mutually recursive interfaces.** Sometimes, recursive declarations can be spread out across several `hi` files (due to an `hs-boot` loop). In this case, laziness plays a similar role; however, instead of consulting per-interface mutable variable, the typechecking process consults the EPS (in the case of `ghc -c`) or the HPT (in the case of `ghc --make`). As before, laziness plays a critical role: we first add thunks representing all of the declarations to the EPS without doing any interface typechecking; then when we force the thunk the names can be found by doing the lookup.
|
|
|
**Variation: tying the knot when typechecking mutually recursive interfaces.** Sometimes, recursive declarations can be spread out across several `hi` files (due to an `hs-boot` loop). In this case, laziness plays a similar role; however, instead of consulting per-interface mutable variable, the typechecking process consults the External Package State (EPS) (in the case of `ghc -c`) or the [Home Package Table (HPT)](commentary/compiler/driver) (in the case of `ghc --make`).
|
|
|
|
|
|
|
|
|
Something extra is needed in the case of `ghc --make`: when we typecheck against an `hs-boot` file, we may build `TyThing`s which refer to incomplete `TyThing`s provided from typechecking the `hs-boot` file. In this case, it's necessary to \*re-typecheck\* all of the interfaces in the module loop, so that they end up pointing to the most up-to-date `TyThing`s.
|
|
|
The **external package state** is a mutable variable which stores a mapping from `Name`s to `TyThing`s for all the externally loaded symbols we know about (in the case of `ghc -c`, it also stores symbols from the home package as well). Whenever you call `loadInterface`, this variable is updated with the new symbols from that interface file. To tie the knot over multiple interface file, laziness plays a critical role: we first add thunks representing all of the declarations to the EPS without doing any interface typechecking for both interfaces; then when we force the thunk the names can be found by consulting the EPS.
|
|
|
|
|
|
|
|
|
The **home package table** is a mapping containing all of the `ModDetails` of modules from the home package which we are currently compiling. It's kept separate from the `EPS` because we often need to remove entities from this environment (e.g., we are in a GHCi session and we update a module.) Because these `ModDetails` are preserved from compilation to compilation (unlike `ghc -c`, where the EPS is reconstructed from scratch every invocation of GHC), we may have some `ModDetail`s in the HPT which are linked against incomplete `TyThing`s provided from typechecking an `hs-boot` file. Once we compile the real `hs` file for the `hs-boot` file, it's necessary to \*re-typecheck\* all of the interfaces in the module loop, so that they end up pointing to the most up-to-date `TyThing`s; this is done by simply throwing out all of the old `ModDetails` from the HPT and then reloading them back in. (`retypecheckLoop` in `GhcMake`).
|
|
|
|
|
|
## Tying the knot when typechecking a module
|
|
|
|
|
|
|
|
|
As we typecheck Haskell source code, we produce `TyCon`s and other type-checking entities for them. If some declarations are mutually recursive, then we need to similarly tie the knot. There are two primary cases when this can occur:
|
|
|
As we typecheck Haskell source code, we produce `TyCon`s and other type-checking entities. If some declarations are mutually recursive, then we need to similarly tie the knot. There are two primary cases when this can occur:
|
|
|
|
|
|
**A mutually recursive set of source declarations.** GHC simply arranges for every declaration in a mutually recursive set of declarations to be typechecked "all at once." For example, `tcTyClDecls` in `TcTyClsDecls` uses `fixM` to refer to the resulting type declarations, so they can be placed in the environment when we typecheck these very type declarations.
|
|
|
|
... | ... | |