diff options
Diffstat (limited to 'compiler/GHC/IfaceToCore.hs')
-rw-r--r-- | compiler/GHC/IfaceToCore.hs | 50 |
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 |