diff options
Diffstat (limited to 'compiler/iface/TcIface.hs')
-rw-r--r-- | compiler/iface/TcIface.hs | 65 |
1 files changed, 45 insertions, 20 deletions
diff --git a/compiler/iface/TcIface.hs b/compiler/iface/TcIface.hs index b41c94823d..1a2d737726 100644 --- a/compiler/iface/TcIface.hs +++ b/compiler/iface/TcIface.hs @@ -1714,13 +1714,13 @@ tcIfaceGlobal name { type_env <- setLclEnv () get_type_env -- yuk ; case lookupNameEnv type_env name of Just thing -> return thing - Nothing -> - pprPanic "tcIfaceGlobal (local): not found" - (ifKnotErr name (if_doc env) type_env) + -- See Note [Knot-tying fallback on boot] + Nothing -> via_external } - ; _ -> do - + ; _ -> via_external }} + where + via_external = do { hsc_env <- getTopEnv ; mb_thing <- liftIO (lookupTypeHscEnv hsc_env name) ; case mb_thing of { @@ -1731,21 +1731,7 @@ tcIfaceGlobal name ; case mb_thing of Failed err -> failIfM err Succeeded thing -> return thing - }}}}} - -ifKnotErr :: Name -> SDoc -> TypeEnv -> SDoc -ifKnotErr name env_doc type_env = vcat - [ text "You are in a maze of twisty little passages, all alike." - , text "While forcing the thunk for TyThing" <+> ppr name - , text "which was lazily initialized by" <+> env_doc <> text "," - , text "I tried to tie the knot, but I couldn't find" <+> ppr name - , text "in the current type environment." - , text "If you are developing GHC, please read Note [Tying the knot]" - , text "and Note [Type-checking inside the knot]." - , text "Consider rebuilding GHC with profiling for a better stack trace." - , hang (text "Contents of current type environment:") - 2 (ppr type_env) - ] + }}} -- Note [Tying the knot] -- ~~~~~~~~~~~~~~~~~~~~~ @@ -1760,11 +1746,50 @@ ifKnotErr name env_doc type_env = vcat -- * Note [Knot-tying typecheckIface] -- * Note [DFun knot-tying] -- * Note [hsc_type_env_var hack] +-- * Note [Knot-tying fallback on boot] -- -- There is also a wiki page on the subject, see: -- -- https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/TyingTheKnot +-- Note [Knot-tying fallback on boot] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-- Suppose that you are typechecking A.hs, which transitively imports, +-- via B.hs, A.hs-boot. When we poke on B.hs and discover that it +-- has a reference to a type T from A, what TyThing should we wire +-- it up with? Clearly, if we have already typechecked T and +-- added it into the type environment, we should go ahead and use that +-- type. But what if we haven't typechecked it yet? +-- +-- For the longest time, GHC adopted the policy that this was +-- *an error condition*; that you MUST NEVER poke on B.hs's reference +-- to a T defined in A.hs until A.hs has gotten around to kind-checking +-- T and adding it to the env. However, actually ensuring this is the +-- case has proven to be a bug farm, because it's really difficult to +-- actually ensure this never happens. The problem was especially poignant +-- with type family consistency checks, which eagerly happen before any +-- typechecking takes place. +-- +-- Today, we take a different strategy: if we ever try to access +-- an entity from A which doesn't exist, we just fall back on the +-- definition of A from the hs-boot file. This is complicated in +-- its own way: it means that you may end up with a mix of A.hs and +-- A.hs-boot TyThings during the course of typechecking. We don't +-- think (and have not observed) any cases where this would cause +-- problems, but the hypothetical situation one might worry about +-- is something along these lines in Core: +-- +-- case x of +-- A -> e1 +-- B -> e2 +-- +-- If, when typechecking this, we find x :: T, and the T we are hooked +-- up with is the abstract one from the hs-boot file, rather than the +-- one defined in this module with constructors A and B. But it's hard +-- to see how this could happen, especially because the reference to +-- the constructor (A and B) means that GHC will always typecheck +-- this expression *after* typechecking T. + tcIfaceTyConByName :: IfExtName -> IfL TyCon tcIfaceTyConByName name = do { thing <- tcIfaceGlobal name |