summaryrefslogtreecommitdiff
path: root/compiler/GHC/IfaceToCore.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/IfaceToCore.hs')
-rw-r--r--compiler/GHC/IfaceToCore.hs50
1 files changed, 47 insertions, 3 deletions
diff --git a/compiler/GHC/IfaceToCore.hs b/compiler/GHC/IfaceToCore.hs
index f4bb4057c9..a69cc34a73 100644
--- a/compiler/GHC/IfaceToCore.hs
+++ b/compiler/GHC/IfaceToCore.hs
@@ -1715,7 +1715,7 @@ tcIdInfo ignore_prags toplvl name ty info = do
need_prag :: IfaceInfoItem -> Bool
-- Always read in compulsory unfoldings
-- See Note [Always expose compulsory unfoldings] in GHC.Iface.Tidy
- need_prag (HsUnfold _ (IfCoreUnfold src _ _)) = isCompulsorySource src
+ need_prag (HsUnfold _ (IfCoreUnfold src _ _ _)) = isCompulsorySource src
need_prag _ = False
tcPrag :: IdInfo -> IfaceInfoItem -> IfL IdInfo
@@ -1776,13 +1776,14 @@ tcLFInfo lfi = case lfi of
tcUnfolding :: TopLevelFlag -> Name -> Type -> IdInfo -> IfaceUnfolding -> IfL Unfolding
-- See Note [Lazily checking Unfoldings]
-tcUnfolding toplvl name _ info (IfCoreUnfold src if_guidance if_expr)
+tcUnfolding toplvl name _ info (IfCoreUnfold src cache if_guidance if_expr)
= do { uf_opts <- unfoldingOpts <$> getDynFlags
; expr <- tcUnfoldingRhs (isCompulsorySource src) toplvl name if_expr
; let guidance = case if_guidance of
IfWhen arity unsat_ok boring_ok -> UnfWhen arity unsat_ok boring_ok
IfNoGuidance -> calcUnfoldingGuidance uf_opts is_top_bottoming expr
- ; return $ mkCoreUnfolding src True expr guidance }
+ -- See Note [Tying the 'CoreUnfolding' knot]
+ ; return $ mkCoreUnfolding src True expr (Just cache) guidance }
where
-- Strictness should occur before unfolding!
is_top_bottoming = isTopLevel toplvl && isDeadEndSig (dmdSigInfo info)
@@ -1795,6 +1796,49 @@ tcUnfolding _toplvl name dfun_ty _ (IfDFunUnfold bs ops)
doc = text "Class ops for dfun" <+> ppr name
(_, _, cls, _) = tcSplitDFunTy dfun_ty
+{- Note [Tying the 'CoreUnfolding' knot]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The unfolding of recursive definitions can contain references to the
+Id being defined. Consider the following example:
+
+ foo :: ()
+ foo = foo
+
+The unfolding template of 'foo' is, of course, 'foo'; so the interface
+file for this module contains:
+
+ foo :: (); Unfolding = foo
+
+When rehydrating the interface file we are going to make an Id for
+'foo' (in GHC.IfaceToCore), with an 'Unfolding'. We used to make this
+'Unfolding' by calling 'mkFinalUnfolding', but that needs to populate,
+among other fields, the 'uf_is_value' field, by computing
+'exprIsValue' of the template (in this case, 'foo').
+
+'exprIsValue e' looks at the unfoldings of variables in 'e' to see if
+they are evaluated; so it consults the `uf_is_value` field of
+variables in `e`. Now we can see the problem: to set the `uf_is_value`
+field of `foo`'s unfolding, we look at its unfolding (in this case
+just `foo` itself!). Loop. This is the root cause of ticket #22272.
+
+The simple solution we chose is to serialise the various auxiliary
+fields of `CoreUnfolding` so that we don't need to recreate them when
+rehydrating. Specifically, the following fields are moved to the
+'UnfoldingCache', which is persisted in the interface file:
+
+* 'uf_is_conlike'
+* 'uf_is_value'
+* 'uf_is_work_free'
+* 'uf_expandable'
+
+These four bits make the interface files only one byte larger per
+unfolding; on the other hand, this does save calls to 'exprIsValue',
+'exprIsExpandable' etc for every imported Id.
+
+We could choose to do this only for loop breakers. But that's a bit
+more complicated and it seems good all round.
+-}
+
{- Note [Lazily checking Unfoldings]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For unfoldings, we try to do the job lazily, so that we never typecheck