Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
5c4c3bf4
by Sylvain Henry at 2026-05-02T03:39:28-04:00
-
e242ce4f
by Sylvain Henry at 2026-05-02T03:39:28-04:00
-
3b75cccd
by fendor at 2026-05-02T03:40:14-04:00
-
a179e30b
by Duncan Coutts at 2026-05-05T09:34:15-04:00
-
fdd26b00
by Duncan Coutts at 2026-05-05T09:34:15-04:00
-
1036f2ac
by David Eichmann at 2026-05-05T09:34:15-04:00
-
6414bc49
by Teo Camarasu at 2026-05-05T09:34:16-04:00
-
bce22879
by Teo Camarasu at 2026-05-05T09:34:16-04:00
16 changed files:
- + changelog.d/T27022
- compiler/GHC/Unit/Module/Deps.hs
- hadrian/src/Settings/Packages.hs
- libraries/ghci/GHCi/TH.hs
- rts/IOManager.h
- rts/Linker.c
- rts/LinkerInternals.h
- rts/RtsSymbols.c
- rts/RtsSymbols.h
- rts/linker/Elf.c
- testsuite/tests/MiniQuickCheck.hs
- testsuite/tests/linters/notes.stdout
- testsuite/tests/numeric/should_run/foundation.hs
- + testsuite/tests/th/T27022.hs
- + testsuite/tests/th/T27022.stdout
- testsuite/tests/th/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +synopsis: Fix a divergence in the interaction between ``recover`` and ``putQ`` between the internal and external interpreter
|
|
| 3 | +description: The ``recover`` method in TemplateHaskell now behaves the same
|
|
| 4 | + with the internal and external interpreter.
|
|
| 5 | + In the past, when an error was encountered in a computation in a ``recover`` block,
|
|
| 6 | + the external interpreter would discard any state changes from ``putQ``,
|
|
| 7 | + whereas the internal interpreter would not.
|
|
| 8 | + This was a long-standing error in the implementation of the external interpreter.
|
|
| 9 | + Both now keep state changes from ``putQ`` in ``recover`` blocks.
|
|
| 10 | +mrs: !15994
|
|
| 11 | +issues: #27022 |
| ... | ... | @@ -96,7 +96,7 @@ data Dependencies = Deps |
| 96 | 96 | , dep_boot_mods_ :: Set (UnitId, ModuleNameWithIsBoot)
|
| 97 | 97 | -- ^ All modules which have boot files below this one, and whether we
|
| 98 | 98 | -- should use the boot file or not.
|
| 99 | - -- This information is only used to populate the eps_is_boot field.
|
|
| 99 | + -- This information is only used to populate the 'eps_is_boot' field.
|
|
| 100 | 100 | -- See Note [Structure of dep_boot_mods]
|
| 101 | 101 | |
| 102 | 102 | , dep_orphs_ :: [Module]
|
| ... | ... | @@ -605,7 +605,7 @@ hash of the module. The export hash is computed in `GHC.Iface.Recomp.addFingerpr |
| 605 | 605 | -}
|
| 606 | 606 | |
| 607 | 607 | {-
|
| 608 | -Note [Structure of dep_boot_deps]
|
|
| 608 | +Note [Structure of dep_boot_mods]
|
|
| 609 | 609 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 610 | 610 | |
| 611 | 611 | In `-c` mode we always need to know whether to load the normal or boot version of
|
| ... | ... | @@ -322,6 +322,7 @@ rtsPackageArgs = package rts ? do |
| 322 | 322 | , Profiling `wayUnit` way ? arg "-DPROFILING"
|
| 323 | 323 | , Threaded `wayUnit` way ? arg "-DTHREADED_RTS"
|
| 324 | 324 | , notM targetSupportsSMP ? arg "-optc-DNOSMP"
|
| 325 | + , isWinHost ? arg "-optl-Wl,--disable-runtime-pseudo-reloc"
|
|
| 325 | 326 | |
| 326 | 327 | -- See Note [AutoApply.cmm for vectors] in genapply/Main.hs
|
| 327 | 328 | --
|
| ... | ... | @@ -119,7 +119,7 @@ initQState :: Pipe -> QState |
| 119 | 119 | initQState p = QState M.empty Nothing p
|
| 120 | 120 | |
| 121 | 121 | -- | The monad in which we run TH computations on the server
|
| 122 | -newtype GHCiQ a = GHCiQ { runGHCiQ :: QState -> IO (a, QState) }
|
|
| 122 | +newtype GHCiQ a = GHCiQ { runGHCiQ :: IORef QState -> IO a }
|
|
| 123 | 123 | |
| 124 | 124 | -- | The exception thrown by "fail" in the GHCiQ monad
|
| 125 | 125 | data GHCiQException = GHCiQException QState String
|
| ... | ... | @@ -128,52 +128,54 @@ data GHCiQException = GHCiQException QState String |
| 128 | 128 | instance Exception GHCiQException
|
| 129 | 129 | |
| 130 | 130 | instance Functor GHCiQ where
|
| 131 | - fmap f (GHCiQ s) = GHCiQ $ fmap (\(x,s') -> (f x,s')) . s
|
|
| 131 | + fmap f (GHCiQ m) = GHCiQ $ fmap f . m
|
|
| 132 | 132 | |
| 133 | 133 | instance Applicative GHCiQ where
|
| 134 | 134 | f <*> a = GHCiQ $ \s ->
|
| 135 | - do (f',s') <- runGHCiQ f s
|
|
| 136 | - (a',s'') <- runGHCiQ a s'
|
|
| 137 | - return (f' a', s'')
|
|
| 138 | - pure x = GHCiQ (\s -> return (x,s))
|
|
| 135 | + do f' <- runGHCiQ f s
|
|
| 136 | + a' <- runGHCiQ a s
|
|
| 137 | + return $ f' a'
|
|
| 138 | + pure x = GHCiQ $ \_ -> return x
|
|
| 139 | 139 | |
| 140 | 140 | instance Monad GHCiQ where
|
| 141 | 141 | m >>= f = GHCiQ $ \s ->
|
| 142 | - do (m', s') <- runGHCiQ m s
|
|
| 143 | - (a, s'') <- runGHCiQ (f m') s'
|
|
| 144 | - return (a, s'')
|
|
| 142 | + do m' <- runGHCiQ m s
|
|
| 143 | + a <- runGHCiQ (f m') s
|
|
| 144 | + return a
|
|
| 145 | 145 | |
| 146 | 146 | instance MonadFail GHCiQ where
|
| 147 | - fail err = GHCiQ $ \s -> throwIO (GHCiQException s err)
|
|
| 147 | + fail err = GHCiQ $ \sRef -> readIORef sRef >>= \s -> throwIO (GHCiQException s err)
|
|
| 148 | 148 | |
| 149 | 149 | getState :: GHCiQ QState
|
| 150 | -getState = GHCiQ $ \s -> return (s,s)
|
|
| 150 | +getState = GHCiQ $ \sRef -> readIORef sRef
|
|
| 151 | 151 | |
| 152 | 152 | noLoc :: TH.Loc
|
| 153 | 153 | noLoc = TH.Loc "<no file>" "<no package>" "<no module>" (0,0) (0,0)
|
| 154 | 154 | |
| 155 | 155 | -- | Send a 'THMessage' to GHC and return the result.
|
| 156 | 156 | ghcCmd :: Binary a => THMessage (THResult a) -> GHCiQ a
|
| 157 | -ghcCmd m = GHCiQ $ \s -> do
|
|
| 157 | +ghcCmd m = GHCiQ $ \sRef -> do
|
|
| 158 | + s <- readIORef sRef
|
|
| 158 | 159 | r <- remoteTHCall (qsPipe s) m
|
| 159 | 160 | case r of
|
| 160 | 161 | THException str -> throwIO (GHCiQException s str)
|
| 161 | - THComplete res -> return (res, s)
|
|
| 162 | + THComplete res -> return res
|
|
| 162 | 163 | |
| 163 | 164 | instance MonadIO GHCiQ where
|
| 164 | - liftIO m = GHCiQ $ \s -> fmap (,s) m
|
|
| 165 | + liftIO m = GHCiQ $ \_ -> m
|
|
| 165 | 166 | |
| 166 | 167 | instance TH.Quasi GHCiQ where
|
| 167 | 168 | qNewName str = ghcCmd (NewName str)
|
| 168 | 169 | qReport isError msg = ghcCmd (Report isError msg)
|
| 169 | 170 | |
| 170 | 171 | -- See Note [TH recover with -fexternal-interpreter] in GHC.Tc.Gen.Splice
|
| 171 | - qRecover (GHCiQ h) a = GHCiQ $ \s -> mask $ \unmask -> do
|
|
| 172 | + qRecover (GHCiQ h) a = GHCiQ $ \sRef -> mask $ \unmask -> do
|
|
| 173 | + s <- readIORef sRef
|
|
| 172 | 174 | remoteTHCall (qsPipe s) StartRecover
|
| 173 | - e <- try $ unmask $ runGHCiQ (a <* ghcCmd FailIfErrs) s
|
|
| 175 | + e <- try $ unmask $ runGHCiQ (a <* ghcCmd FailIfErrs) sRef
|
|
| 174 | 176 | remoteTHCall (qsPipe s) (EndRecover (isLeft e))
|
| 175 | 177 | case e of
|
| 176 | - Left GHCiQException{} -> h s
|
|
| 178 | + Left GHCiQException{} -> h sRef
|
|
| 177 | 179 | Right r -> return r
|
| 178 | 180 | qLookupName isType occ = ghcCmd (LookupName isType occ)
|
| 179 | 181 | qReify name = ghcCmd (Reify name)
|
| ... | ... | @@ -200,15 +202,16 @@ instance TH.Quasi GHCiQ where |
| 200 | 202 | qAddTempFile suffix = ghcCmd (AddTempFile suffix)
|
| 201 | 203 | qAddTopDecls decls = ghcCmd (AddTopDecls decls)
|
| 202 | 204 | qAddForeignFilePath lang fp = ghcCmd (AddForeignFilePath lang fp)
|
| 203 | - qAddModFinalizer fin = GHCiQ (\s -> mkRemoteRef fin >>= return . (, s)) >>=
|
|
| 205 | + qAddModFinalizer fin = GHCiQ (\_ -> mkRemoteRef fin) >>=
|
|
| 204 | 206 | ghcCmd . AddModFinalizer
|
| 205 | 207 | qAddCorePlugin str = ghcCmd (AddCorePlugin str)
|
| 206 | - qGetQ = GHCiQ $ \s ->
|
|
| 208 | + qGetQ = do
|
|
| 209 | + s <- getState
|
|
| 207 | 210 | let lookup :: forall a. Typeable a => Map TypeRep Dynamic -> Maybe a
|
| 208 | 211 | lookup m = fromDynamic =<< M.lookup (typeOf (undefined::a)) m
|
| 209 | - in return (lookup (qsMap s), s)
|
|
| 210 | - qPutQ k = GHCiQ $ \s ->
|
|
| 211 | - return ((), s { qsMap = M.insert (typeOf k) (toDyn k) (qsMap s) })
|
|
| 212 | + return $ lookup (qsMap s)
|
|
| 213 | + qPutQ k = GHCiQ $ \sRef ->
|
|
| 214 | + modifyIORef' sRef (\s -> s { qsMap = M.insert (typeOf k) (toDyn k) (qsMap s) })
|
|
| 212 | 215 | qIsExtEnabled x = ghcCmd (IsExtEnabled x)
|
| 213 | 216 | qExtsEnabled = ghcCmd ExtsEnabled
|
| 214 | 217 | qPutDoc l s = ghcCmd (PutDoc l s)
|
| ... | ... | @@ -231,7 +234,8 @@ runModFinalizerRefs pipe rstate qrefs = do |
| 231 | 234 | qs <- mapM localRef qrefs
|
| 232 | 235 | qstateref <- localRef rstate
|
| 233 | 236 | qstate <- readIORef qstateref
|
| 234 | - _ <- runGHCiQ (TH.runQ $ sequence_ qs) qstate { qsPipe = pipe }
|
|
| 237 | + qstate' <- newIORef $ qstate { qsPipe = pipe }
|
|
| 238 | + _ <- runGHCiQ (TH.runQ $ sequence_ qs) qstate'
|
|
| 235 | 239 | return ()
|
| 236 | 240 | |
| 237 | 241 | -- | The implementation of the 'RunTH' message
|
| ... | ... | @@ -267,8 +271,6 @@ runTHQ |
| 267 | 271 | -> IO ByteString
|
| 268 | 272 | runTHQ pipe rstate mb_loc ghciq = do
|
| 269 | 273 | qstateref <- localRef rstate
|
| 270 | - qstate <- readIORef qstateref
|
|
| 271 | - let st = qstate { qsLocation = mb_loc, qsPipe = pipe }
|
|
| 272 | - (r,new_state) <- runGHCiQ (TH.runQ ghciq) st
|
|
| 273 | - writeIORef qstateref new_state
|
|
| 274 | + modifyIORef' qstateref (\qstate -> qstate { qsLocation = mb_loc, qsPipe = pipe })
|
|
| 275 | + r <- runGHCiQ (TH.runQ ghciq) qstateref
|
|
| 274 | 276 | return $! LB.toStrict (runPut (put r)) |
| ... | ... | @@ -21,6 +21,15 @@ |
| 21 | 21 | |
| 22 | 22 | #include "sm/GC.h" // for evac_fn
|
| 23 | 23 | |
| 24 | +#if defined(mingw32_HOST_OS)
|
|
| 25 | +/* Global var (only on Windows) that is exported (hence before BeginPrivate.h)
|
|
| 26 | + * to be shared with the I/O code in the base library to tell us which style
|
|
| 27 | + * of I/O manager we are using: one that uses the Windows native API HANDLEs,
|
|
| 28 | + * or one that uses Posix style fds.
|
|
| 29 | + */
|
|
| 30 | +extern bool rts_IOManagerIsWin32Native;
|
|
| 31 | +#endif
|
|
| 32 | + |
|
| 24 | 33 | #include "BeginPrivate.h"
|
| 25 | 34 | |
| 26 | 35 | /* The ./configure gives us a set of CPP flags, one for each named I/O manager:
|
| ... | ... | @@ -160,14 +169,6 @@ typedef enum { |
| 160 | 169 | /* Global var to tell us which I/O manager impl we are using */
|
| 161 | 170 | extern IOManagerType iomgr_type;
|
| 162 | 171 | |
| 163 | -#if defined(mingw32_HOST_OS)
|
|
| 164 | -/* Global var (only on Windows) that is exported to be shared with the I/O code
|
|
| 165 | - * in the base library to tell us which style of I/O manager we are using: one
|
|
| 166 | - * that uses the Windows native API HANDLEs, or one that uses Posix style fds.
|
|
| 167 | - */
|
|
| 168 | -extern bool rts_IOManagerIsWin32Native;
|
|
| 169 | -#endif
|
|
| 170 | - |
|
| 171 | 172 | |
| 172 | 173 | /* The CapIOManager is the per-capability data structure belonging to the I/O
|
| 173 | 174 | * manager. It is defined in full in IOManagerInternals.h. The opaque forward
|
| ... | ... | @@ -478,16 +478,7 @@ initLinker_ (int retain_cafs) |
| 478 | 478 | symhash = allocStrHashTable();
|
| 479 | 479 | |
| 480 | 480 | /* populate the symbol table with stuff from the RTS */
|
| 481 | - IF_DEBUG(linker, debugBelch("populating linker symbol table with built-in RTS symbols\n"));
|
|
| 482 | - for (const RtsSymbolVal *sym = rtsSyms; sym->lbl != NULL; sym++) {
|
|
| 483 | - IF_DEBUG(linker, debugBelch("initLinker: inserting rts symbol %s, %p\n", sym->lbl, sym->addr));
|
|
| 484 | - if (! ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"),
|
|
| 485 | - symhash, sym->lbl, sym->addr,
|
|
| 486 | - sym->strength, sym->type, 0, NULL)) {
|
|
| 487 | - barf("ghciInsertSymbolTable failed");
|
|
| 488 | - }
|
|
| 489 | - }
|
|
| 490 | - IF_DEBUG(linker, debugBelch("done with built-in RTS symbols\n"));
|
|
| 481 | + initLinkerRtsSyms(symhash);
|
|
| 491 | 482 | |
| 492 | 483 | /* Add extra symbols. rtsExtraSyms() is a weakly defined symbol in the rts,
|
| 493 | 484 | * that can be overrided by linking in an object with a corresponding
|
| ... | ... | @@ -502,4 +502,6 @@ ObjectCode* mkOc( ObjectType type, pathchar *path, char *image, int imageSize, |
| 502 | 502 | void initSegment(Segment *s, void *start, size_t size, SegmentProt prot, int n_sections);
|
| 503 | 503 | void freeSegments(ObjectCode *oc);
|
| 504 | 504 | |
| 505 | +void initLinkerRtsSyms(StrHashTable *symhash);
|
|
| 506 | + |
|
| 505 | 507 | #include "EndPrivate.h" |
| ... | ... | @@ -9,6 +9,8 @@ |
| 9 | 9 | #include "ghcplatform.h"
|
| 10 | 10 | #include "Rts.h"
|
| 11 | 11 | #include "RtsSymbols.h"
|
| 12 | +#include "LinkerInternals.h"
|
|
| 13 | +#include "PathUtils.h"
|
|
| 12 | 14 | |
| 13 | 15 | #include "TopHandler.h"
|
| 14 | 16 | #include "HsFFI.h"
|
| ... | ... | @@ -51,6 +53,20 @@ extern char **environ; |
| 51 | 53 | |
| 52 | 54 | /* -----------------------------------------------------------------------------
|
| 53 | 55 | * Symbols to be inserted into the RTS symbol table.
|
| 56 | + *
|
|
| 57 | + * Note [Naming Scheme for Symbol Macros]
|
|
| 58 | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 59 | + *
|
|
| 60 | + * SymI_*: symbol is internal to the RTS. It resides in an object
|
|
| 61 | + * file/library that is linked into the RTS library (as a static
|
|
| 62 | + * archive or dynamic shared library).
|
|
| 63 | + * SymE_*: symbol is external to the RTS library. It might be linked
|
|
| 64 | + * dynamically.
|
|
| 65 | + *
|
|
| 66 | + * Sym*_HasProto : the symbol prototype is imported in an include file
|
|
| 67 | + * or defined explicitly
|
|
| 68 | + * Sym*_NeedsProto: the symbol is undefined and we add a dummy
|
|
| 69 | + * default proto extern void sym(void);
|
|
| 54 | 70 | */
|
| 55 | 71 | |
| 56 | 72 | #define Maybe_Stable_Names SymI_HasProto(stg_mkWeakzh) \
|
| ... | ... | @@ -162,7 +178,7 @@ extern char **environ; |
| 162 | 178 | SymI_HasProto(stg_asyncWritezh) \
|
| 163 | 179 | SymI_HasProto(stg_asyncDoProczh) \
|
| 164 | 180 | SymI_HasProto(rts_InstallConsoleEvent) \
|
| 165 | - SymI_HasProto(rts_IOManagerIsWin32Native) \
|
|
| 181 | + SymI_HasDataProto(rts_IOManagerIsWin32Native) \
|
|
| 166 | 182 | SymI_HasProto(rts_ConsoleHandlerDone) \
|
| 167 | 183 | SymI_NeedsProto(__mingw_module_is_dll) \
|
| 168 | 184 | RTS_WIN64_ONLY(SymI_NeedsProto(___chkstk_ms)) \
|
| ... | ... | @@ -914,7 +930,7 @@ extern char **environ; |
| 914 | 930 | SymI_HasProto(freeExecPage) \
|
| 915 | 931 | SymI_HasProto(getAllocations) \
|
| 916 | 932 | SymI_HasProto(revertCAFs) \
|
| 917 | - SymI_HasProto(RtsFlags) \
|
|
| 933 | + SymI_HasDataProto(RtsFlags) \
|
|
| 918 | 934 | SymI_NeedsDataProto(rts_breakpoint_io_action) \
|
| 919 | 935 | SymI_NeedsDataProto(rts_stop_next_breakpoint) \
|
| 920 | 936 | SymI_NeedsDataProto(rts_stop_on_exception) \
|
| ... | ... | @@ -925,9 +941,9 @@ extern char **environ; |
| 925 | 941 | SymI_NeedsProto(rts_enableStopAfterReturn) \
|
| 926 | 942 | SymI_NeedsProto(rts_disableStopAfterReturn) \
|
| 927 | 943 | SymI_HasProto(stopTimer) \
|
| 928 | - SymI_HasProto(n_capabilities) \
|
|
| 929 | - SymI_HasProto(max_n_capabilities) \
|
|
| 930 | - SymI_HasProto(enabled_capabilities) \
|
|
| 944 | + SymI_HasDataProto(n_capabilities) \
|
|
| 945 | + SymI_HasDataProto(max_n_capabilities) \
|
|
| 946 | + SymI_HasDataProto(enabled_capabilities) \
|
|
| 931 | 947 | SymI_HasDataProto(stg_traceEventzh) \
|
| 932 | 948 | SymI_HasDataProto(stg_traceMarkerzh) \
|
| 933 | 949 | SymI_HasDataProto(stg_traceBinaryEventzh) \
|
| ... | ... | @@ -1145,12 +1161,27 @@ extern char **environ; |
| 1145 | 1161 | SymI_HasProto(hs_word2float64)
|
| 1146 | 1162 | |
| 1147 | 1163 | |
| 1148 | -/* entirely bogus claims about types of these symbols */
|
|
| 1149 | -#define SymI_NeedsProto(vvv) extern void vvv(void);
|
|
| 1150 | -#define SymI_NeedsDataProto(vvv) extern StgWord vvv[];
|
|
| 1151 | -#define SymE_NeedsProto(vvv) SymI_NeedsProto(vvv);
|
|
| 1152 | -#define SymE_NeedsDataProto(vvv) SymI_NeedsDataProto(vvv);
|
|
| 1153 | -#define SymE_HasProto(vvv) SymI_HasProto(vvv);
|
|
| 1164 | +/* Declare prototypes for the symbols that need it, so we can refer
|
|
| 1165 | + * to them in the rtsSyms table below.
|
|
| 1166 | + *
|
|
| 1167 | + * In particular, for the external ones (SymE_*) we use the dllimport attribute
|
|
| 1168 | + * to indicate that (on Windows) they come from external DLLs. This attribute
|
|
| 1169 | + * is ignored on other platforms.
|
|
| 1170 | + *
|
|
| 1171 | + * The claims about the types of these symbols are entirely bogus.
|
|
| 1172 | + */
|
|
| 1173 | +#if defined(mingw32_HOST_OS) && defined(DYNAMIC)
|
|
| 1174 | +#define DLLIMPORT __attribute__((dllimport))
|
|
| 1175 | +#else
|
|
| 1176 | +#define DLLIMPORT /**/
|
|
| 1177 | +#endif
|
|
| 1178 | + |
|
| 1179 | +#define SymI_NeedsProto(vvv) extern void vvv(void);
|
|
| 1180 | +#define SymI_NeedsDataProto(vvv) extern StgWord vvv[];
|
|
| 1181 | +#define SymE_NeedsProto(vvv) extern DLLIMPORT void vvv(void);
|
|
| 1182 | +#define SymE_NeedsDataProto(vvv) extern DLLIMPORT StgWord vvv[];
|
|
| 1183 | + |
|
| 1184 | +#define SymE_HasProto(vvv) /**/
|
|
| 1154 | 1185 | #define SymI_HasProto(vvv) /**/
|
| 1155 | 1186 | #define SymI_HasDataProto(vvv) /**/
|
| 1156 | 1187 | #define SymI_HasProto_redirect(vvv,xxx,strength,ty) /**/
|
| ... | ... | @@ -1179,6 +1210,8 @@ RTS_SYMBOLS_PRIM |
| 1179 | 1210 | #undef SymE_NeedsProto
|
| 1180 | 1211 | #undef SymE_NeedsDataProto
|
| 1181 | 1212 | |
| 1213 | +/* See Note [Naming Scheme for Symbol Macros] */
|
|
| 1214 | + |
|
| 1182 | 1215 | #define SymI_HasProto(vvv) { MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
|
| 1183 | 1216 | (void*)(&(vvv)), STRENGTH_NORMAL, SYM_TYPE_CODE },
|
| 1184 | 1217 | #define SymI_HasDataProto(vvv) { MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
|
| ... | ... | @@ -1199,7 +1232,16 @@ RTS_SYMBOLS_PRIM |
| 1199 | 1232 | { MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
|
| 1200 | 1233 | (void*)(&(xxx)), strength, ty },
|
| 1201 | 1234 | |
| 1202 | -RtsSymbolVal rtsSyms[] = {
|
|
| 1235 | + |
|
| 1236 | + |
|
| 1237 | +/* Initialize (if not already initialized) and return an array of symbols with stuff from the RTS. */
|
|
| 1238 | +void initLinkerRtsSyms (StrHashTable *symhash) {
|
|
| 1239 | + /* The address of data symbols with the dllimport attribute are not
|
|
| 1240 | + * compile-time constants and so cannot be used in constant initialisers.
|
|
| 1241 | + * For this reason, rtsSyms is a local variable within this function
|
|
| 1242 | + * rather than a global constant (as it was historically).
|
|
| 1243 | + */
|
|
| 1244 | + const RtsSymbolVal rtsSyms[] = {
|
|
| 1203 | 1245 | RTS_SYMBOLS
|
| 1204 | 1246 | RTS_RET_SYMBOLS
|
| 1205 | 1247 | RTS_POSIX_ONLY_SYMBOLS
|
| ... | ... | @@ -1214,7 +1256,20 @@ RtsSymbolVal rtsSyms[] = { |
| 1214 | 1256 | RTS_SYMBOLS_PRIM
|
| 1215 | 1257 | SymI_HasDataProto(nonmoving_write_barrier_enabled)
|
| 1216 | 1258 | { 0, 0, STRENGTH_NORMAL, SYM_TYPE_CODE } /* sentinel */
|
| 1217 | -};
|
|
| 1259 | + };
|
|
| 1260 | + |
|
| 1261 | + IF_DEBUG(linker, debugBelch("populating linker symbol table with built-in RTS symbols\n"));
|
|
| 1262 | + for (const RtsSymbolVal *sym = rtsSyms; sym->lbl != NULL; sym++) {
|
|
| 1263 | + IF_DEBUG(linker, debugBelch("initLinker: inserting rts symbol %s, %p\n", sym->lbl, sym->addr));
|
|
| 1264 | + if (! ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"),
|
|
| 1265 | + symhash, sym->lbl, sym->addr,
|
|
| 1266 | + sym->strength, sym->type, 0, NULL)) {
|
|
| 1267 | + barf("ghciInsertSymbolTable failed");
|
|
| 1268 | + }
|
|
| 1269 | + }
|
|
| 1270 | + IF_DEBUG(linker, debugBelch("done with built-in RTS symbols\n"));
|
|
| 1271 | +}
|
|
| 1272 | + |
|
| 1218 | 1273 | |
| 1219 | 1274 | |
| 1220 | 1275 | // Note [Extra RTS symbols]
|
| ... | ... | @@ -46,8 +46,6 @@ typedef struct _RtsSymbolVal { |
| 46 | 46 | SymType type;
|
| 47 | 47 | } RtsSymbolVal;
|
| 48 | 48 | |
| 49 | -extern RtsSymbolVal rtsSyms[];
|
|
| 50 | - |
|
| 51 | 49 | extern RtsSymbolVal* __attribute__((weak)) rtsExtraSyms(void);
|
| 52 | 50 | |
| 53 | 51 | /* See Note [_iob_func symbol]. */
|
| ... | ... | @@ -76,18 +76,6 @@ |
| 76 | 76 | *
|
| 77 | 77 | * See bug #781
|
| 78 | 78 | * See thread http://www.haskell.org/pipermail/cvs-ghc/2007-September/038458.html
|
| 79 | - *
|
|
| 80 | - * Naming Scheme for Symbol Macros
|
|
| 81 | - *
|
|
| 82 | - * SymI_*: symbol is internal to the RTS. It resides in an object
|
|
| 83 | - * file/library that is statically.
|
|
| 84 | - * SymE_*: symbol is external to the RTS library. It might be linked
|
|
| 85 | - * dynamically.
|
|
| 86 | - *
|
|
| 87 | - * Sym*_HasProto : the symbol prototype is imported in an include file
|
|
| 88 | - * or defined explicitly
|
|
| 89 | - * Sym*_NeedsProto: the symbol is undefined and we add a dummy
|
|
| 90 | - * default proto extern void sym(void);
|
|
| 91 | 79 | */
|
| 92 | 80 | #define X86_64_ELF_NONPIC_HACK (!RtsFlags.MiscFlags.linkerAlwaysPic)
|
| 93 | 81 |
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | {-# LANGUAGE DerivingStrategies #-}
|
| 3 | 3 | {-# LANGUAGE GeneralisedNewtypeDeriving #-}
|
| 4 | 4 | {-# LANGUAGE RecordWildCards #-}
|
| 5 | +{-# LANGUAGE TypeApplications #-}
|
|
| 5 | 6 | {-# LANGUAGE TypeFamilies #-}
|
| 6 | 7 | |
| 7 | 8 | -- | A minimal QuickCheck-like property testing framework for use in the GHC
|
| ... | ... | @@ -52,6 +53,8 @@ module MiniQuickCheck |
| 52 | 53 | ) where
|
| 53 | 54 | |
| 54 | 55 | -- base
|
| 56 | +import Control.Exception
|
|
| 57 | + ( SomeException, displayException, evaluate, try )
|
|
| 55 | 58 | import Control.Monad.IO.Class
|
| 56 | 59 | ( liftIO )
|
| 57 | 60 | import Data.Bits
|
| ... | ... | @@ -181,16 +184,39 @@ nest :: String -> ReaderT RunS IO a -> ReaderT RunS IO a |
| 181 | 184 | nest c = local (\s -> s { depth = depth s + 1, context = c : context s })
|
| 182 | 185 | |
| 183 | 186 | runPropertyCheck :: PropertyCheck -> ReaderT RunS IO Result
|
| 184 | -runPropertyCheck (PropertyBinaryOp ok desc s1 s2) =
|
|
| 185 | - if ok
|
|
| 186 | - then return Success
|
|
| 187 | - else do
|
|
| 188 | - ctx <- context <$> ask
|
|
| 189 | - let msg = "Failure: " ++ s1 ++ " " ++ desc ++ " " ++ s2
|
|
| 190 | - putMsg msg
|
|
| 191 | - return (Failure [msg : ctx])
|
|
| 192 | -runPropertyCheck (PropertyAnd a b) =
|
|
| 193 | - (<>) <$> runPropertyCheck a <*> runPropertyCheck b
|
|
| 187 | +runPropertyCheck pcThunk = do
|
|
| 188 | + -- See Note [Catching exceptions in property evaluation].
|
|
| 189 | + pcRes <- liftIO $ try @SomeException (evaluate pcThunk)
|
|
| 190 | + case pcRes of
|
|
| 191 | + Left e -> reportFailure ("Failure: exception: " ++ displayException e)
|
|
| 192 | + Right (PropertyAnd a b) ->
|
|
| 193 | + (<>) <$> runPropertyCheck a <*> runPropertyCheck b
|
|
| 194 | + Right (PropertyBinaryOp ok desc s1 s2) -> do
|
|
| 195 | + okRes <- liftIO $ try @SomeException (evaluate ok)
|
|
| 196 | + case okRes of
|
|
| 197 | + Right True -> return Success
|
|
| 198 | + Right False -> reportFailure ("Failure: " ++ s1 ++ " " ++ desc ++ " " ++ s2)
|
|
| 199 | + Left e -> reportFailure ("Failure: exception: " ++ displayException e)
|
|
| 200 | + |
|
| 201 | +reportFailure :: String -> ReaderT RunS IO Result
|
|
| 202 | +reportFailure msg = do
|
|
| 203 | + ctx <- context <$> ask
|
|
| 204 | + putMsg msg
|
|
| 205 | + return (Failure [msg : ctx])
|
|
| 206 | + |
|
| 207 | +-- Note [Catching exceptions in property evaluation]
|
|
| 208 | +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 209 | +-- A property like `\a b -> let !r = a `div` 0 in r === b` builds a
|
|
| 210 | +-- `PropertyCheck` thunk whose forcing raises an exception -- in this case
|
|
| 211 | +-- already at the `PropertyBinaryOp` constructor, before its `ok` field is
|
|
| 212 | +-- ever inspected. Other properties may force `ok = (s1 == s2)` instead and
|
|
| 213 | +-- raise from there.
|
|
| 214 | +--
|
|
| 215 | +-- To handle both, we `evaluate` first the `PropertyCheck` thunk and then
|
|
| 216 | +-- the `ok` field, each inside `try`, and report any exception through the
|
|
| 217 | +-- normal `reportFailure` path. The surrounding loop then still prints
|
|
| 218 | +-- "With arguments ... (Seed: ...)" and the test driver continues with
|
|
| 219 | +-- subsequent properties instead of aborting.
|
|
| 194 | 220 | |
| 195 | 221 | runProperty :: Iterations -> Property -> ReaderT RunS IO Result
|
| 196 | 222 | runProperty (Iterations iters) (Prop p) = do
|
| ... | ... | @@ -27,7 +27,6 @@ ref compiler/GHC/Tc/Solver/Rewrite.hs:1020:7: Note [Stability of rewritin |
| 27 | 27 | ref compiler/GHC/Tc/TyCl.hs:1662:6: Note [Unification variables need fresh Names]
|
| 28 | 28 | ref compiler/GHC/Tc/Types/Constraint.hs:209:9: Note [NonCanonical Semantics]
|
| 29 | 29 | ref compiler/GHC/Types/Demand.hs:304:25: Note [Preserving Boxity of results is rarely a win]
|
| 30 | -ref compiler/GHC/Unit/Module/Deps.hs:97:13: Note [Structure of dep_boot_mods]
|
|
| 31 | 30 | ref compiler/GHC/Utils/Monad.hs:415:34: Note [multiShotIO]
|
| 32 | 31 | ref compiler/Language/Haskell/Syntax/Binds.hs:206:31: Note [fun_id in Match]
|
| 33 | 32 | ref configure.ac:205:10: Note [Linking ghc-bin against threaded stage0 RTS]
|
| ... | ... | @@ -77,13 +77,42 @@ testMultiplicative _ = Group "Multiplicative" |
| 77 | 77 | , Property "a * b == Integer(a) * Integer(b)" $ \(a :: a) (b :: a) -> a * b === fromInteger (toInteger a * toInteger b)
|
| 78 | 78 | ]
|
| 79 | 79 | |
| 80 | -testDividible :: forall a . (Show a, Eq a, Integral a, Num a, Arbitrary a, Typeable a)
|
|
| 80 | +-- | Divisibility test for Bounded Integral types (Int, Int{8,16,32,64},
|
|
| 81 | +-- Word, Word{8,16,32,64}).
|
|
| 82 | +testDivisible :: forall a . (Show a, Eq a, Bounded a, Integral a, Num a, Arbitrary a, Typeable a)
|
|
| 81 | 83 | => Proxy a -> Test
|
| 82 | -testDividible _ = Group "Divisible"
|
|
| 84 | +testDivisible _ = Group "Divisible"
|
|
| 85 | + [ Property "(x `div` y) * y + (x `mod` y) == x" $ \(a :: a) (NonZero b) ->
|
|
| 86 | + -- See Note [Skipping minBound `div` (-1)].
|
|
| 87 | + if (minBound :: a) < 0 && a == minBound && b == (-1)
|
|
| 88 | + then True === True
|
|
| 89 | + else a === (a `div` b) * b + (a `mod` b)
|
|
| 90 | + ]
|
|
| 91 | + |
|
| 92 | +-- | Divisibility test for unbounded Integral types (Integer). No overflow
|
|
| 93 | +-- can occur here, so the property holds without exception for all NonZero b.
|
|
| 94 | +testDivisibleUnbounded :: forall a . (Show a, Eq a, Integral a, Num a, Arbitrary a, Typeable a)
|
|
| 95 | + => Proxy a -> Test
|
|
| 96 | +testDivisibleUnbounded _ = Group "Divisible"
|
|
| 83 | 97 | [ Property "(x `div` y) * y + (x `mod` y) == x" $ \(a :: a) (NonZero b) ->
|
| 84 | 98 | a === (a `div` b) * b + (a `mod` b)
|
| 85 | 99 | ]
|
| 86 | 100 | |
| 101 | +-- Note [Skipping minBound `div` (-1)]
|
|
| 102 | +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 103 | +-- For a fixed-width *signed* Integral type, `minBound `div` (-1)` raises
|
|
| 104 | +-- ArithException(Overflow) because `-minBound` is not representable in the
|
|
| 105 | +-- type (e.g., for Int8, `-(-128)` would be 128, out of range). The div/mod
|
|
| 106 | +-- identity property cannot hold there, so we skip exactly that one pair.
|
|
| 107 | +--
|
|
| 108 | +-- We detect "signed Bounded" with `(minBound :: a) < 0`: True for Int{N},
|
|
| 109 | +-- False for Word{N}. This way unsigned Bounded types lose no coverage,
|
|
| 110 | +-- and only the genuine overflow sample is skipped for signed types.
|
|
| 111 | +--
|
|
| 112 | +-- For the unbounded `Integer`, no overflow can occur and we use a separate
|
|
| 113 | +-- 'testDivisibleUnbounded' (without the Bounded constraint or the skip).
|
|
| 114 | +-- See #27222.
|
|
| 115 | + |
|
| 87 | 116 | testOperatorPrecedence :: forall a . (Show a, Eq a, Prelude.Num a, Integral a, Num a, Arbitrary a, Typeable a)
|
| 88 | 117 | => Proxy a -> Test
|
| 89 | 118 | testOperatorPrecedence _ = Group "Precedence"
|
| ... | ... | @@ -101,14 +130,26 @@ testOperatorPrecedence _ = Group "Precedence" |
| 101 | 130 | ]
|
| 102 | 131 | |
| 103 | 132 | |
| 104 | -testNumber :: (Show a, Eq a, Prelude.Num a, Integral a, Num a, Arbitrary a, Typeable a)
|
|
| 133 | +testNumber :: (Show a, Eq a, Prelude.Num a, Bounded a, Integral a, Num a, Arbitrary a, Typeable a)
|
|
| 105 | 134 | => String -> Proxy a -> Test
|
| 106 | 135 | testNumber name proxy = Group name
|
| 107 | 136 | [ testIntegral proxy
|
| 108 | 137 | , testEqOrd proxy
|
| 109 | 138 | , testAdditive proxy
|
| 110 | 139 | , testMultiplicative proxy
|
| 111 | - , testDividible proxy
|
|
| 140 | + , testDivisible proxy
|
|
| 141 | + , testOperatorPrecedence proxy
|
|
| 142 | + ]
|
|
| 143 | + |
|
| 144 | +-- | Variant of 'testNumber' for unbounded Integral types (e.g., Integer).
|
|
| 145 | +testNumberUnbounded :: (Show a, Eq a, Prelude.Num a, Integral a, Num a, Arbitrary a, Typeable a)
|
|
| 146 | + => String -> Proxy a -> Test
|
|
| 147 | +testNumberUnbounded name proxy = Group name
|
|
| 148 | + [ testIntegral proxy
|
|
| 149 | + , testEqOrd proxy
|
|
| 150 | + , testAdditive proxy
|
|
| 151 | + , testMultiplicative proxy
|
|
| 152 | + , testDivisibleUnbounded proxy
|
|
| 112 | 153 | , testOperatorPrecedence proxy
|
| 113 | 154 | ]
|
| 114 | 155 | |
| ... | ... | @@ -119,7 +160,7 @@ testNumberRefs = Group "ALL" |
| 119 | 160 | , testNumber "Int16" (Proxy :: Proxy Int16)
|
| 120 | 161 | , testNumber "Int32" (Proxy :: Proxy Int32)
|
| 121 | 162 | , testNumber "Int64" (Proxy :: Proxy Int64)
|
| 122 | - , testNumber "Integer" (Proxy :: Proxy Integer)
|
|
| 163 | + , testNumberUnbounded "Integer" (Proxy :: Proxy Integer)
|
|
| 123 | 164 | , testNumber "Word" (Proxy :: Proxy Word)
|
| 124 | 165 | , testNumber "Word8" (Proxy :: Proxy Word8)
|
| 125 | 166 | , testNumber "Word16" (Proxy :: Proxy Word16)
|
| ... | ... | @@ -399,7 +440,7 @@ testPrimops = Group "primop" |
| 399 | 440 | , testPrimop "-#" (Primop.-#) (Wrapper.-#)
|
| 400 | 441 | , testPrimop "*#" (Primop.*#) (Wrapper.*#)
|
| 401 | 442 | , testPrimop "timesInt2#" Primop.timesInt2# Wrapper.timesInt2#
|
| 402 | - , testPrimop "mulIntMayOflo#" Primop.mulIntMayOflo# Wrapper.mulIntMayOflo#
|
|
| 443 | + , testPrimopMayOflo "mulIntMayOflo#" Primop.mulIntMayOflo# Wrapper.mulIntMayOflo#
|
|
| 403 | 444 | , testPrimopDivLike "quotInt#" Primop.quotInt# Wrapper.quotInt#
|
| 404 | 445 | , testPrimopDivLike "remInt#" Primop.remInt# Wrapper.remInt#
|
| 405 | 446 | , testPrimopDivLike "quotRemInt#" Primop.quotRemInt# Wrapper.quotRemInt#
|
| ... | ... | @@ -497,6 +538,31 @@ instance TestPrimop (Int# -> Int# -> Int#) where |
| 497 | 538 | testPrimopDivLike s l r = Property s $ twoNonZero $ \ (uInt#-> x0) (uInt#-> x1) -> wInt# (l x0 x1) === wInt# (r x0 x1)
|
| 498 | 539 | testPrimopShift s l r = Property s $ \ (uInt#-> x0) (BoundedShiftAmount @Int shift) -> wInt# (l x0 (uInt# shift)) === wInt# (r x0 (uInt# shift))
|
| 499 | 540 | |
| 541 | +-- | Compare two 'mulIntMayOflo#'-like primops only on whether their result
|
|
| 542 | +-- is zero. See Note [Comparing mulIntMayOflo# results].
|
|
| 543 | +testPrimopMayOflo :: String
|
|
| 544 | + -> (Int# -> Int# -> Int#)
|
|
| 545 | + -> (Int# -> Int# -> Int#)
|
|
| 546 | + -> Test
|
|
| 547 | +testPrimopMayOflo s l r =
|
|
| 548 | + Property s $ \ (uInt# -> x0) (uInt# -> x1) ->
|
|
| 549 | + (wInt# (l x0 x1) == 0) === (wInt# (r x0 x1) == 0)
|
|
| 550 | + |
|
| 551 | +-- Note [Comparing mulIntMayOflo# results]
|
|
| 552 | +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 553 | +-- The 'mulIntMayOflo#' primop is only specified to return 0 if the signed
|
|
| 554 | +-- multiplication does not overflow, and a non-zero value if it /may/
|
|
| 555 | +-- overflow (see Note [MO_S_MulMayOflo significant width] in
|
|
| 556 | +-- GHC.Cmm.MachOp). The exact non-zero value is unspecified and legitimately
|
|
| 557 | +-- differs between backends and between inlined vs. non-inlined call sites
|
|
| 558 | +-- (e.g., the LLVM backend's `isSMulOK` returns `sext_signbit(low) - high`,
|
|
| 559 | +-- which is some arbitrary non-zero word on overflow).
|
|
| 560 | +--
|
|
| 561 | +-- Comparing the raw Int# results bit-for-bit is therefore too strict and
|
|
| 562 | +-- causes spurious test failures whenever the random arguments happen to
|
|
| 563 | +-- overflow. We compare zero/non-zero instead, which matches the spec.
|
|
| 564 | +-- See #27222.
|
|
| 565 | + |
|
| 500 | 566 | instance TestPrimop (Int# -> Int# -> (# Int#,Int# #)) where
|
| 501 | 567 | testPrimop s l r = Property s $ \ (uInt#-> x0) (uInt#-> x1) -> WTUP2(wInt#,wInt#, (l x0 x1)) === WTUP2(wInt#,wInt#, (r x0 x1))
|
| 502 | 568 | testPrimopDivLike s l r = Property s $ twoNonZero $ \ (uInt#-> x0) (uInt#-> x1) -> WTUP2(wInt#,wInt#, (l x0 x1)) === WTUP2(wInt#,wInt#, (r x0 x1))
|
| 1 | +{-# LANGUAGE TemplateHaskell #-}
|
|
| 2 | +-- | This tests the behaviour of TH's recover method.
|
|
| 3 | +-- It should behave the same in the internal and external interperter.
|
|
| 4 | +-- In the past, they have diverged, and the external interpreter would roll back the state of putQ/getQ whereas the internal interpreter would not.
|
|
| 5 | +module Main where
|
|
| 6 | + |
|
| 7 | +import Language.Haskell.TH.Syntax
|
|
| 8 | +main = print $(putQ "0" >> recover (pure ()) (putQ "42" >> fail "oops") >> getQ @String >>= lift ) |
| 1 | +Just "42" |
| ... | ... | @@ -650,3 +650,4 @@ test('GadtConSigs_th_dump1', normal, compile, ['-v0 -ddump-splices -dsuppress-un |
| 650 | 650 | test('T26099', normal, compile_fail, [''])
|
| 651 | 651 | test('T8306_th', only_ways(['ghci']), ghci_script, ['T8306_th.script'])
|
| 652 | 652 | test('T26862_th', only_ways(['ghci']), ghci_script, ['T26862_th.script'])
|
| 653 | +test('T27022', normal, compile_and_run, ['']) |