Oleg Grenrus pushed to branch wip/keepAlive-th-names-found-in-annotations at Glasgow Haskell Compiler / GHC
Commits:
-
105c4237
by Oleg Grenrus at 2025-10-09T03:23:28+03:00
4 changed files:
- compiler/GHC/Rename/Splice.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Types/Meta.hs
- compiler/GHC/Utils/Outputable.hs
Changes:
... | ... | @@ -1072,6 +1072,32 @@ a bit hacky. |
1072 | 1072 | For these chaps (which have Internal Names) we don't want to put
|
1073 | 1073 | them in the keep-alive set.
|
1074 | 1074 | |
1075 | +Note [Keeping things alive referenced by TH.Name from annotations]
|
|
1076 | + |
|
1077 | +Consider
|
|
1078 | + |
|
1079 | + {-# ANN module 'f #-}
|
|
1080 | + |
|
1081 | +We stage-check annotations as splices, because we are going to run them,
|
|
1082 | +and thus similar stage restrictions apply.
|
|
1083 | + |
|
1084 | +That however leads for 'f not being persisted. Consider briefly
|
|
1085 | + |
|
1086 | + expr = $( [| f |] )
|
|
1087 | + |
|
1088 | +Here, `f` isn't kept alive by the same mechanism as in
|
|
1089 | +Note [Keeping things alive for Template Haskell], as there isn't need for that.
|
|
1090 | +Once the splice is evaluated and spliced, compiler will see
|
|
1091 | + |
|
1092 | + expr = f
|
|
1093 | + |
|
1094 | +in other words, there will be explicit use of `f`.
|
|
1095 | + |
|
1096 | +Returning back to the ANN example: once the annotation is run,
|
|
1097 | +we previously had an opaque Serialised blob. There might be a TH Name inside,
|
|
1098 | +but GHC won't know about it. To work around that we not only serialise
|
|
1099 | +the annotation value, but also traverse it to extract all TH Names.
|
|
1100 | + |
|
1075 | 1101 | Note [Quoting names]
|
1076 | 1102 | ~~~~~~~~~~~~~~~~~~~~
|
1077 | 1103 | A quoted name 'n is a bit like a quoted expression [| n |], except that we
|
... | ... | @@ -166,14 +166,16 @@ import qualified Data.ByteString.Lazy as LB |
166 | 166 | import Data.Dynamic ( fromDynamic, toDyn )
|
167 | 167 | import qualified Data.IntMap as IntMap
|
168 | 168 | import qualified Data.Map as Map
|
169 | -import Data.Typeable ( typeOf, Typeable, TypeRep, typeRep )
|
|
170 | -import Data.Data (Data)
|
|
169 | +import Data.Typeable ( typeOf, Typeable, TypeRep, typeRep, cast )
|
|
170 | +import Data.Data (Data, gmapQ)
|
|
171 | 171 | import Data.Proxy ( Proxy (..) )
|
172 | 172 | import Data.IORef
|
173 | 173 | import GHC.Parser.HaddockLex (lexHsDoc)
|
174 | 174 | import GHC.Parser (parseIdentifier)
|
175 | 175 | import GHC.Rename.Doc (rnHsDoc)
|
176 | 176 | |
177 | +import Debug.Trace
|
|
178 | + |
|
177 | 179 | {-
|
178 | 180 | Note [Template Haskell state diagram]
|
179 | 181 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
... | ... | @@ -668,14 +670,12 @@ Example: |
668 | 670 | -}
|
669 | 671 | |
670 | 672 | -- None of these functions add constraints to the LIE
|
671 | - |
|
672 | -tcTypedBracket :: HsExpr GhcRn -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
|
|
673 | -tcUntypedBracket :: HsExpr GhcRn -> HsQuote GhcRn -> [PendingRnSplice] -> ExpRhoType
|
|
674 | - -> TcM (HsExpr GhcTc)
|
|
675 | -tcTypedSplice :: HsTypedSpliceResult -> HsTypedSplice GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
|
|
676 | - |
|
677 | -getUntypedSpliceBody :: HsUntypedSpliceResult (HsExpr GhcRn) -> TcM (HsExpr GhcRn)
|
|
678 | -runAnnotation :: CoreAnnTarget -> LHsExpr GhcRn -> TcM Annotation
|
|
673 | +--
|
|
674 | +-- * tcTypedBracket
|
|
675 | +-- * tcUntypedBracket
|
|
676 | +-- * tcTypedSplice
|
|
677 | +-- * getUntypedSpliceBody
|
|
678 | +-- * runAnnotation
|
|
679 | 679 | |
680 | 680 | {-
|
681 | 681 | ************************************************************************
|
... | ... | @@ -686,6 +686,7 @@ runAnnotation :: CoreAnnTarget -> LHsExpr GhcRn -> TcM Annotation |
686 | 686 | -}
|
687 | 687 | |
688 | 688 | -- See Note [How brackets and nested splices are handled]
|
689 | +tcTypedBracket :: HsExpr GhcRn -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
|
|
689 | 690 | tcTypedBracket rn_expr expr res_ty
|
690 | 691 | = addErrCtxt (TypedTHBracketCtxt expr) $
|
691 | 692 | do { cur_lvl <- getThLevel
|
... | ... | @@ -728,6 +729,7 @@ tcTypedBracket rn_expr expr res_ty |
728 | 729 | meta_ty res_ty }
|
729 | 730 | |
730 | 731 | -- See Note [Typechecking Overloaded Quotes]
|
732 | +tcUntypedBracket :: HsExpr GhcRn -> HsQuote GhcRn -> [PendingRnSplice] -> ExpRhoType -> TcM (HsExpr GhcTc)
|
|
731 | 733 | tcUntypedBracket rn_expr brack ps res_ty
|
732 | 734 | = do { traceTc "tc_bracket untyped" (ppr brack $$ ppr ps)
|
733 | 735 | |
... | ... | @@ -929,6 +931,7 @@ tcCodeTy m_ty exp_ty |
929 | 931 | -- getUntypedSpliceBody: the renamer has expanded the splice.
|
930 | 932 | -- Just run the finalizers that it produced, and return
|
931 | 933 | -- the renamed expression
|
934 | +getUntypedSpliceBody :: HsUntypedSpliceResult (HsExpr GhcRn) -> TcM (HsExpr GhcRn)
|
|
932 | 935 | getUntypedSpliceBody (HsUntypedSpliceTop { utsplice_result_finalizers = mod_finalizers
|
933 | 936 | , utsplice_result = rn_expr })
|
934 | 937 | = do { addModFinalizersWithLclEnv mod_finalizers
|
... | ... | @@ -936,6 +939,7 @@ getUntypedSpliceBody (HsUntypedSpliceTop { utsplice_result_finalizers = mod_fina |
936 | 939 | getUntypedSpliceBody (HsUntypedSpliceNested {})
|
937 | 940 | = panic "tcTopUntypedSplice: invalid nested splice"
|
938 | 941 | |
942 | +tcTypedSplice :: HsTypedSpliceResult -> HsTypedSplice GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
|
|
939 | 943 | tcTypedSplice HsTypedSpliceTop ctxt@(HsTypedSpliceExpr _ expr) res_ty
|
940 | 944 | = addErrCtxt (TypedSpliceCtxt Nothing ctxt) $
|
941 | 945 | setSrcSpan (getLocA expr) $
|
... | ... | @@ -1082,6 +1086,7 @@ stubNestedSplice = warnPprTrace True "stubNestedSplice" empty $ |
1082 | 1086 | ************************************************************************
|
1083 | 1087 | -}
|
1084 | 1088 | |
1089 | +runAnnotation :: CoreAnnTarget -> LHsExpr GhcRn -> TcM Annotation
|
|
1085 | 1090 | runAnnotation target expr = do
|
1086 | 1091 | -- Find the classes we want instances for in order to call toAnnotationWrapper
|
1087 | 1092 | loc <- getSrcSpanM
|
... | ... | @@ -1106,17 +1111,32 @@ runAnnotation target expr = do |
1106 | 1111 | specialised_to_annotation_wrapper_expr expr'))
|
1107 | 1112 | })
|
1108 | 1113 | |
1114 | + traceM $ "runAnnotation: " ++ showPprUnsafe zonked_wrapped_expr'
|
|
1115 | + |
|
1109 | 1116 | -- Run the appropriately wrapped expression to get the value of
|
1110 | 1117 | -- the annotation and its dictionaries. The return value is of
|
1111 | 1118 | -- type AnnotationWrapper by construction, so this conversion is
|
1112 | 1119 | -- safe
|
1113 | - serialized <- runMetaAW zonked_wrapped_expr'
|
|
1120 | + (names, serialized) <- runMetaAW zonked_wrapped_expr'
|
|
1121 | + -- See Note [Keeping things alive referenced by TH.Name from annotations]
|
|
1122 | + traceM $ "runAnnotation names: " ++ showPprUnsafe names
|
|
1123 | + |
|
1124 | + forM_ names $ \th_name -> do
|
|
1125 | + mb_name <- lookupThName_maybe th_name
|
|
1126 | + traceM $ "runAnnotation found: " ++ showPprUnsafe mb_name
|
|
1127 | + forM_ mb_name keepAlive -- TODO: do we need to do checks, like isExternalName here?
|
|
1128 | + |
|
1114 | 1129 | return Annotation {
|
1115 | 1130 | ann_target = target,
|
1116 | 1131 | ann_value = serialized
|
1117 | 1132 | }
|
1118 | 1133 | |
1119 | -convertAnnotationWrapper :: ForeignHValue -> TcM Serialized
|
|
1134 | +extractTHNames :: Data a => a -> [TH.Name]
|
|
1135 | +extractTHNames x = case cast x of
|
|
1136 | + Just n -> [n]
|
|
1137 | + Nothing -> concat (gmapQ extractTHNames x)
|
|
1138 | + |
|
1139 | +convertAnnotationWrapper :: ForeignHValue -> TcM ([TH.Name], Serialized)
|
|
1120 | 1140 | convertAnnotationWrapper fhv = do
|
1121 | 1141 | interp <- tcGetInterp
|
1122 | 1142 | case interpInstance interp of
|
... | ... | @@ -1133,8 +1153,7 @@ convertAnnotationWrapper fhv = do |
1133 | 1153 | -- annotation are exposed at this point. This is also why we are
|
1134 | 1154 | -- doing all this stuff inside the context of runMeta: it has the
|
1135 | 1155 | -- facilities to deal with user error in a meta-level expression
|
1136 | - rnf serialized `seq` serialized
|
|
1137 | - |
|
1156 | + (extractTHNames value, rnf serialized `seq` serialized)
|
|
1138 | 1157 | #endif
|
1139 | 1158 | |
1140 | 1159 | {-
|
... | ... | @@ -1216,7 +1235,7 @@ defaultRunMeta (MetaAW r) |
1216 | 1235 | |
1217 | 1236 | ----------------
|
1218 | 1237 | runMetaAW :: LHsExpr GhcTc -- Of type AnnotationWrapper
|
1219 | - -> TcM Serialized
|
|
1238 | + -> TcM ([TH.Name], Serialized)
|
|
1220 | 1239 | runMetaAW = runMeta metaRequestAW
|
1221 | 1240 | |
1222 | 1241 | runMetaE :: LHsExpr GhcTc -- Of type (Q Exp)
|
... | ... | @@ -14,6 +14,7 @@ where |
14 | 14 | import GHC.Prelude
|
15 | 15 | |
16 | 16 | import GHC.Serialized ( Serialized )
|
17 | +import qualified GHC.Boot.TH.Syntax as TH
|
|
17 | 18 | |
18 | 19 | import GHC.Hs
|
19 | 20 | import GHC.Utils.Outputable
|
... | ... | @@ -26,7 +27,7 @@ data MetaRequest |
26 | 27 | | MetaP (LPat GhcPs -> MetaResult)
|
27 | 28 | | MetaT (LHsType GhcPs -> MetaResult)
|
28 | 29 | | MetaD ([LHsDecl GhcPs] -> MetaResult)
|
29 | - | MetaAW (Serialized -> MetaResult)
|
|
30 | + | MetaAW (([TH.Name], Serialized) -> MetaResult)
|
|
30 | 31 | |
31 | 32 | -- | data constructors not exported to ensure correct result type
|
32 | 33 | data MetaResult
|
... | ... | @@ -34,7 +35,7 @@ data MetaResult |
34 | 35 | | MetaResP (LPat GhcPs)
|
35 | 36 | | MetaResT (LHsType GhcPs)
|
36 | 37 | | MetaResD [LHsDecl GhcPs]
|
37 | - | MetaResAW Serialized
|
|
38 | + | MetaResAW ([TH.Name], Serialized)
|
|
38 | 39 | |
39 | 40 | instance Outputable MetaResult where
|
40 | 41 | ppr (MetaResE e) = text "MetaResE" <> braces (ppr e)
|
... | ... | @@ -63,7 +64,7 @@ unMetaResD :: MetaResult -> [LHsDecl GhcPs] |
63 | 64 | unMetaResD (MetaResD d) = d
|
64 | 65 | unMetaResD mr = pprPanic "unMetaResD" (ppr mr)
|
65 | 66 | |
66 | -unMetaResAW :: MetaResult -> Serialized
|
|
67 | +unMetaResAW :: MetaResult -> ([TH.Name], Serialized)
|
|
67 | 68 | unMetaResAW (MetaResAW aw) = aw
|
68 | 69 | unMetaResAW mr = pprPanic "unMetaResAW" (ppr mr)
|
69 | 70 | |
... | ... | @@ -81,6 +82,6 @@ metaRequestT h = fmap unMetaResT . h (MetaT MetaResT) |
81 | 82 | metaRequestD :: Functor f => MetaHook f -> LHsExpr GhcTc -> f [LHsDecl GhcPs]
|
82 | 83 | metaRequestD h = fmap unMetaResD . h (MetaD MetaResD)
|
83 | 84 | |
84 | -metaRequestAW :: Functor f => MetaHook f -> LHsExpr GhcTc -> f Serialized
|
|
85 | +metaRequestAW :: Functor f => MetaHook f -> LHsExpr GhcTc -> f ([TH.Name], Serialized)
|
|
85 | 86 | metaRequestAW h = fmap unMetaResAW . h (MetaAW MetaResAW)
|
86 | 87 |
... | ... | @@ -167,6 +167,8 @@ import GHC.Show ( showMultiLineString ) |
167 | 167 | import GHC.Utils.Exception
|
168 | 168 | import GHC.Exts (oneShot)
|
169 | 169 | |
170 | +import qualified GHC.Boot.TH.Syntax as TH
|
|
171 | + |
|
170 | 172 | {-
|
171 | 173 | ************************************************************************
|
172 | 174 | * *
|
... | ... | @@ -1094,13 +1096,15 @@ instance Outputable a => Outputable (SCC a) where |
1094 | 1096 | instance Outputable Serialized where
|
1095 | 1097 | ppr (Serialized the_type bytes) = int (length bytes) <+> text "of type" <+> text (show the_type)
|
1096 | 1098 | |
1099 | +instance Outputable TH.Name where
|
|
1100 | + ppr = text . TH.showName
|
|
1101 | + |
|
1097 | 1102 | instance Outputable Extension where
|
1098 | 1103 | ppr = text . show
|
1099 | 1104 | |
1100 | 1105 | instance Outputable ModuleName where
|
1101 | 1106 | ppr = pprModuleName
|
1102 | 1107 | |
1103 | - |
|
1104 | 1108 | pprModuleName :: IsLine doc => ModuleName -> doc
|
1105 | 1109 | pprModuleName (ModuleName nm) =
|
1106 | 1110 | docWithStyle (ztext (zEncodeFS nm)) (\_ -> ftext nm)
|
... | ... | @@ -1250,6 +1254,7 @@ instance (OutputableP env a) => OutputableP env (Set a) where |
1250 | 1254 | instance OutputableP env Void where
|
1251 | 1255 | pdoc _ = \ case
|
1252 | 1256 | |
1257 | + |
|
1253 | 1258 | {-
|
1254 | 1259 | ************************************************************************
|
1255 | 1260 | * *
|