Magnus pushed to branch wip/mangoiv/ghc-9.12-bp at Glasgow Haskell Compiler / GHC

Commits:

30 changed files:

Changes:

  • changelog.d/T27124.md
    1
    +section: compiler
    
    2
    +issues: #25926 #27124
    
    3
    +mrs: !15895
    
    4
    +synopsis:
    
    5
    +  Fix "failed to detect OverLit" panic in the pattern-match checker.
    
    6
    +description:
    
    7
    +  Fixed an issue in which overloaded literals (e.g. numeric literals, overloaded
    
    8
    +  strings with -XOverloadedStrings, overloaded lists, etc) could cause a GHC
    
    9
    +  crash when using -fdefer-type-errors, with an error message of the form
    
    10
    +  "failed to detect OverLit".

  • changelog.d/fix-finalizers-27072
    1
    +section: codegen
    
    2
    +synopsis: Fix module finalizers on multiple platforms
    
    3
    +description: {
    
    4
    +  GHC-generated module finalizers (e.g. ``hs_spt_remove`` for the Static
    
    5
    +  Pointer Table) now run correctly on ELF platforms, darwin, wasm32 and
    
    6
    +  Windows. Also fixes running finalizers when unloading objects with the
    
    7
    +  RTS linker.
    
    8
    +}
    
    9
    +issues: #27072
    
    10
    +mrs: !15762

  • compiler/GHC/CoreToStg/Prep.hs
    ... ... @@ -1125,6 +1125,9 @@ cpeApp top_env expr
    1125 1125
            || f `hasKey` nospecIdKey        -- Replace (nospec a) with a
    
    1126 1126
                 -- See Note [nospecId magic] in GHC.Types.Id.Make
    
    1127 1127
     
    
    1128
    +        -- NB: keep this in sync with GHC.HsToCore.Pmc.Solver.Types.coreExprAsPmLit,
    
    1129
    +        -- as that also needs to see through these magic Ids.
    
    1130
    +
    
    1128 1131
             -- Consider the code:
    
    1129 1132
             --
    
    1130 1133
             --      lazy (f x) y
    

  • compiler/GHC/Driver/CodeOutput.hs
    ... ... @@ -124,6 +124,7 @@ codeOutput logger tmpfs llvm_config dflags unit_state this_mod filenm location g
    124 124
                       { a <- linted_cmm_stream
    
    125 125
                       ; let stubs = genForeignStubs a
    
    126 126
                       ; emitInitializerDecls this_mod stubs
    
    127
    +                  ; emitFinalizerDecls this_mod stubs
    
    127 128
                       ; return (stubs, a) }
    
    128 129
     
    
    129 130
             ; let dus1 = newTagDUniqSupply 'n' dus0
    
    ... ... @@ -138,19 +139,23 @@ codeOutput logger tmpfs llvm_config dflags unit_state this_mod filenm location g
    138 139
             }
    
    139 140
     
    
    140 141
     -- | See Note [Initializers and finalizers in Cmm] in GHC.Cmm.InitFini for details.
    
    141
    -emitInitializerDecls :: Module -> ForeignStubs -> CgStream RawCmmGroup ()
    
    142
    -emitInitializerDecls this_mod (ForeignStubs _ cstub)
    
    143
    -  | initializers <- getInitializers cstub
    
    144
    -  , not $ null initializers =
    
    145
    -      let init_array = CmmData sect statics
    
    146
    -          lbl = mkInitializerArrayLabel this_mod
    
    147
    -          sect = Section InitArray lbl
    
    142
    +emitInitializerDecls, emitFinalizerDecls :: Module -> ForeignStubs -> CgStream RawCmmGroup ()
    
    143
    +emitInitializerDecls = emitInitFiniArrayDecls InitArray mkInitializerArrayLabel getInitializers
    
    144
    +emitFinalizerDecls   = emitInitFiniArrayDecls FiniArray mkFinalizerArrayLabel   getFinalizers
    
    145
    +
    
    146
    +emitInitFiniArrayDecls :: SectionType -> (Module -> CLabel) -> (CStub -> [CLabel])
    
    147
    +                       -> Module -> ForeignStubs -> CgStream RawCmmGroup ()
    
    148
    +emitInitFiniArrayDecls sect_type mk_lbl get_labels this_mod (ForeignStubs _ cstub)
    
    149
    +  | labels <- get_labels cstub
    
    150
    +  , not $ null labels =
    
    151
    +      let lbl     = mk_lbl this_mod
    
    152
    +          sect    = Section sect_type lbl
    
    148 153
               statics = CmmStaticsRaw lbl
    
    149 154
                 [ CmmStaticLit $ CmmLabel fn_name
    
    150
    -            | fn_name <- initializers
    
    155
    +            | fn_name <- labels
    
    151 156
                 ]
    
    152
    -    in Stream.yield [init_array]
    
    153
    -emitInitializerDecls _ _ = return ()
    
    157
    +    in Stream.yield [CmmData sect statics]
    
    158
    +emitInitFiniArrayDecls _ _ _ _ _ = return ()
    
    154 159
     
    
    155 160
     doOutput :: String -> (Handle -> IO a) -> IO a
    
    156 161
     doOutput filenm io_action = bracket (openFile filenm WriteMode) hClose io_action
    

  • compiler/GHC/HsToCore/Pmc/Solver/Types.hs
    ... ... @@ -626,6 +626,15 @@ coreExprAsPmLit :: CoreExpr -> Maybe PmLit
    626 626
     coreExprAsPmLit (Tick _t e) = coreExprAsPmLit e
    
    627 627
     coreExprAsPmLit (Lit l) = literalToPmLit (literalType l) l
    
    628 628
     coreExprAsPmLit e = case collectArgs e of
    
    629
    +
    
    630
    +  -- Look through nospec, noinline and lazy, which are only eliminated by Core Prep.
    
    631
    +  -- See Note [coreExprAsPmLit and nospec]
    
    632
    +  (Var x, Type _ : inner : rest_args)
    
    633
    +    | x `hasKey` nospecIdKey
    
    634
    +   || x `hasKey` noinlineIdKey
    
    635
    +   || x `hasKey` lazyIdKey
    
    636
    +    -> coreExprAsPmLit (mkApps inner rest_args)
    
    637
    +
    
    629 638
       (Var x, [Lit l])
    
    630 639
         | Just dc <- isDataConWorkId_maybe x
    
    631 640
         , dc `elem` [intDataCon, wordDataCon, charDataCon, floatDataCon, doubleDataCon]
    
    ... ... @@ -768,6 +777,34 @@ with large exponents case. This will return a `PmLitOverRat` literal.
    768 777
     Which is then passed to overloadPmLit which simply returns it as-is since
    
    769 778
     it's already overloaded.
    
    770 779
     
    
    780
    +Note [coreExprAsPmLit and nospec]
    
    781
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    782
    +For coverage checking, we need to analyse overloaded literal patterns to figure
    
    783
    +out which literals they correspond to; this is what 'coreExprAsPmLit' does.
    
    784
    +For example, the literal pattern "fromString" (with -XOverloadedStrings)
    
    785
    +will turn into an equality check against the **expression**
    
    786
    +
    
    787
    +  fromString @T $dFromString "hello"#
    
    788
    +
    
    789
    +and 'coreExprAsPmLit' recovers the string by taking apart this application.
    
    790
    +
    
    791
    +However, when $dFromString is non-canonical (e.g. when an INCOHERENT
    
    792
    +instance was discarded during resolution of the typeclass constraint, or when
    
    793
    +the dictionary comes from 'withDict'), the desugarer wraps 'fromString' in
    
    794
    +'nospec' (as per Note [nospecId magic] in GHC.Types.Id.Make and
    
    795
    +Note [Desugaring non-canonical evidence] in GHC.HsToCore.Expr):
    
    796
    +
    
    797
    +  nospec @(IsString a => String -> Maybe a) fromString @T $dFromString "hello"#
    
    798
    +
    
    799
    +(For a full example, see test case T27124a.)
    
    800
    +
    
    801
    +The 'nospec' mechanism only exists for the specialiser; it should be transparent
    
    802
    +to everything else. 'coreExprAsPmLit' must thus look through the 'nospec'
    
    803
    +application in order obtain the string "hello". If it doesn't, we can't do
    
    804
    +pattern match checking (in fact GHC.HsToCore.Pmc.Desugar.desugarPat is liable
    
    805
    +to crash!).
    
    806
    +
    
    807
    +The same reasoning applies to `noinline` and `lazy`.
    
    771 808
     -}
    
    772 809
     
    
    773 810
     instance Outputable PmLitValue where
    

  • compiler/GHC/Linker/Static.hs
    ... ... @@ -241,7 +241,20 @@ linkBinary' staticLink logger tmpfs dflags unit_env o_files dep_units = do
    241 241
                               then ["-Wl,--gc-sections"]
    
    242 242
                               else [])
    
    243 243
     
    
    244
    -                      ++ o_files
    
    244
    +                     -- On Windows, module .o files may be archives (see
    
    245
    +                     -- Note [Object merging] in GHC.Driver.Pipeline.Execute).
    
    246
    +                     -- Use --whole-archive to ensure all archive members are
    
    247
    +                     -- included, especially those containing .ctors/.dtors
    
    248
    +                     -- initializer/finalizer sections. See Note [Initializers and
    
    249
    +                     -- finalizers in Cmm] in GHC.Cmm.InitFini.
    
    250
    +                     ++ (if platformOS platform == OSMinGW32
    
    251
    +                         then ["-Wl,--whole-archive"]
    
    252
    +                         else [])
    
    253
    +                     ++ o_files
    
    254
    +                     ++ (if platformOS platform == OSMinGW32
    
    255
    +                         then ["-Wl,--no-whole-archive"]
    
    256
    +                         else [])
    
    257
    +
    
    245 258
                           ++ lib_path_opts)
    
    246 259
                           ++ extra_ld_inputs
    
    247 260
                           ++ map GHC.SysTools.Option (
    

  • compiler/GHC/Tc/Errors.hs
    ... ... @@ -1217,11 +1217,11 @@ addDeferredBinding ctxt err (EI { ei_evdest = Just dest, ei_pred = item_ty
    1217 1217
     
    
    1218 1218
            ; case dest of
    
    1219 1219
                EvVarDest evar
    
    1220
    -             -> addTcEvBind ev_binds_var $ mkWantedEvBind evar EvNonCanonical err_tm
    
    1220
    +             -> addTcEvBind ev_binds_var $ mkWantedEvBind evar EvCanonical err_tm
    
    1221 1221
                HoleDest hole
    
    1222 1222
                  -> do { -- See Note [Deferred errors for coercion holes]
    
    1223 1223
                          let co_var = coHoleCoVar hole
    
    1224
    -                   ; addTcEvBind ev_binds_var $ mkWantedEvBind co_var EvNonCanonical err_tm
    
    1224
    +                   ; addTcEvBind ev_binds_var $ mkWantedEvBind co_var EvCanonical err_tm
    
    1225 1225
                        ; fillCoercionHole hole (mkCoVarCo co_var) } }
    
    1226 1226
     addDeferredBinding _ _ _ = return ()    -- Do not set any evidence for Given
    
    1227 1227
     
    

  • compiler/GHC/Types/ForeignStubs.hs
    ... ... @@ -60,11 +60,85 @@ initializerCStub platform clbl declarations body =
    60 60
     -- | @finalizerCStub fn_nm decls body@ is a 'CStub' containing C finalizer
    
    61 61
     -- function (e.g. an entry of the @.fini_array@ section) named
    
    62 62
     -- @fn_nm@ with the given body and the given set of declarations.
    
    63
    +--
    
    64
    +-- See Note [Finalizers via __cxa_atexit]
    
    63 65
     finalizerCStub :: Platform -> CLabel -> SDoc -> SDoc -> CStub
    
    64
    -finalizerCStub platform clbl declarations body =
    
    65
    -    functionCStub platform clbl declarations body
    
    66
    +finalizerCStub platform clbl declarations body
    
    67
    +  | ArchWasm32 <- platformArch platform
    
    68
    +  = -- See Note [Finalizers via __cxa_atexit]
    
    69
    +    cxaAtexitFinalizerCStub platform clbl declarations body
    
    70
    +finalizerCStub platform clbl declarations body
    
    71
    +  | OSDarwin <- platformOS platform
    
    72
    +  = -- See Note [Finalizers via __cxa_atexit]
    
    73
    +    cxaAtexitFinalizerCStub platform clbl declarations body
    
    74
    +finalizerCStub platform clbl declarations body
    
    75
    +  = functionCStub platform clbl declarations body
    
    66 76
         `mappend` CStub empty [] [clbl]
    
    67 77
     
    
    78
    +-- | Generate a @__cxa_atexit@-based finalizer.
    
    79
    +-- See Note [Finalizers via __cxa_atexit]
    
    80
    +cxaAtexitFinalizerCStub :: Platform -> CLabel -> SDoc -> SDoc -> CStub
    
    81
    +cxaAtexitFinalizerCStub platform clbl declarations body =
    
    82
    +    let clbl_pretty = pprCLabel platform clbl
    
    83
    +        fini_name    = hcat [clbl_pretty, text "$fini"]
    
    84
    +        wrapper_name = hcat [clbl_pretty, text "$fini_atexit"]
    
    85
    +        c_code = vcat
    
    86
    +          [ declarations
    
    87
    +          , text "int __cxa_atexit(void (*)(void *), void *, void *);"
    
    88
    +          , hcat [text "static void ", fini_name, text "(void)"]
    
    89
    +          , braces body
    
    90
    +          , hcat [text "static void ", wrapper_name, text "(void *arg __attribute__((unused)))"]
    
    91
    +          , braces (hcat [fini_name, text "();"])
    
    92
    +          , hsep [text "void", clbl_pretty, text "(void)"]
    
    93
    +          , braces (hcat [text "__cxa_atexit(", wrapper_name, text ", 0, 0);"])
    
    94
    +          ]
    
    95
    +    in CStub c_code [clbl] []
    
    96
    +
    
    97
    +{-
    
    98
    +Note [Finalizers via __cxa_atexit]
    
    99
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    100
    +On some platforms, placing a function pointer in the .fini_array /
    
    101
    +__mod_term_func section is not sufficient to have it called on exit.
    
    102
    +On these platforms we instead lower finalizers as initializers that register
    
    103
    +the actual finalizer function via __cxa_atexit.
    
    104
    +
    
    105
    +Affected platforms:
    
    106
    +
    
    107
    +  Wasm32: does not support .fini_array sections.
    
    108
    +
    
    109
    +  Darwin: modern macOS dyld no longer processes __DATA,__mod_term_func entries.
    
    110
    +  Clang now lowers __attribute__((destructor)) as an initializer that calls
    
    111
    +  __cxa_atexit, placing the initializer in __DATA,__mod_init_func (which the
    
    112
    +  linker converts to __TEXT,__init_offsets). GHC must follow the same pattern.
    
    113
    +
    
    114
    +For a finalizer with label `clbl` and body `body`, on these platforms we
    
    115
    +generate:
    
    116
    +
    
    117
    +    static void clbl$fini(void) {
    
    118
    +        <body>
    
    119
    +    }
    
    120
    +    static void clbl$fini_atexit(void *arg) {
    
    121
    +        clbl$fini();
    
    122
    +    }
    
    123
    +    void clbl(void) {
    
    124
    +        __cxa_atexit(clbl$fini_atexit, 0, 0);
    
    125
    +    }
    
    126
    +
    
    127
    +The function `clbl` is placed in the initializers list (getInitializers)
    
    128
    +instead of the finalizers list (getFinalizers). During code output,
    
    129
    +emitInitializerDecls places it in .init_array / __mod_init_func, so the
    
    130
    +registration runs at startup.
    
    131
    +
    
    132
    +The actual finalizer body is in the static helper `clbl$fini`. A separate
    
    133
    +wrapper `clbl$fini_atexit` with the void(*)(void*) signature expected by
    
    134
    +__cxa_atexit is needed because some platforms (e.g. wasm32) enforce exact
    
    135
    +function signature matching at call sites — a simple cast would trap at
    
    136
    +runtime.
    
    137
    +
    
    138
    +This matches what clang does when lowering __attribute__((destructor)) on
    
    139
    +these platforms.
    
    140
    +-}
    
    141
    +
    
    68 142
     newtype CHeader = CHeader { getCHeader :: SDoc }
    
    69 143
     
    
    70 144
     instance Monoid CHeader where
    

  • rts/Linker.c
    ... ... @@ -1107,6 +1107,27 @@ freePreloadObjectFile (ObjectCode *oc)
    1107 1107
         oc->fileSize = 0;
    
    1108 1108
     }
    
    1109 1109
     
    
    1110
    +/* Note [Object unloading and finalizers]
    
    1111
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    1112
    + * An ObjectCode may contain .fini_array/.dtors sections with finalizers that
    
    1113
    + * should run when the object is unloaded. However, we must only run these
    
    1114
    + * finalizers if the corresponding initializers (.init_array/.ctors) have
    
    1115
    + * actually been executed.
    
    1116
    + *
    
    1117
    + * Archive members start in OBJECT_LOADED state and only progress to
    
    1118
    + * OBJECT_NEEDED -> OBJECT_RESOLVED -> OBJECT_READY when a symbol from
    
    1119
    + * them is actually required. An archive member that was never needed never
    
    1120
    + * has its relocations applied, so its .fini_array section data still
    
    1121
    + * contains zeros (unresolved relocation targets). Running those finalizers
    
    1122
    + * would dereference NULL function pointers.
    
    1123
    + *
    
    1124
    + * When unloadObj sets an object's status to OBJECT_UNLOADED, it does so
    
    1125
    + * regardless of the previous state, so we cannot rely on the status alone
    
    1126
    + * to decide whether finalizers should run. Instead, we track whether
    
    1127
    + * initializers were executed via the initializersRan flag, which is set in
    
    1128
    + * ocRunInit after successfully running the initializers.
    
    1129
    + */
    
    1130
    +
    
    1110 1131
     /*
    
    1111 1132
      * freeObjectCode() releases all the pieces of an ObjectCode.  It is called by
    
    1112 1133
      * the GC when a previously unloaded ObjectCode has been determined to be
    
    ... ... @@ -1116,11 +1137,9 @@ void freeObjectCode (ObjectCode *oc)
    1116 1137
     {
    
    1117 1138
         IF_DEBUG(linker, ocDebugBelch(oc, "freeObjectCode: start\n"));
    
    1118 1139
     
    
    1119
    -    // Run finalizers
    
    1120
    -    if (oc->type == STATIC_OBJECT &&
    
    1121
    -            (oc->status == OBJECT_READY || oc->status == OBJECT_UNLOADED)) {
    
    1122
    -        // Only run finalizers if the initializers have also been run, which
    
    1123
    -        // happens when we resolve the object.
    
    1140
    +    // Run finalizers only if initializers have been run.
    
    1141
    +    // See Note [Object unloading and finalizers].
    
    1142
    +    if (oc->type == STATIC_OBJECT && oc->initializersRan) {
    
    1124 1143
     #if defined(OBJFORMAT_ELF)
    
    1125 1144
             ocRunFini_ELF(oc);
    
    1126 1145
     #elif defined(OBJFORMAT_PEi386)
    
    ... ... @@ -1285,6 +1304,7 @@ mkOc( ObjectType type, pathchar *path, char *image, int imageSize,
    1285 1304
        oc->imageMapped       = mapped;
    
    1286 1305
     
    
    1287 1306
        oc->misalignment      = misalignment;
    
    1307
    +   oc->initializersRan   = false;
    
    1288 1308
        oc->cxa_finalize      = NULL;
    
    1289 1309
        oc->extraInfos        = NULL;
    
    1290 1310
     
    
    ... ... @@ -1681,6 +1701,7 @@ int ocRunInit(ObjectCode *oc)
    1681 1701
         foreignExportsFinishedLoadingObject();
    
    1682 1702
     
    
    1683 1703
         if (!r) { return r; }
    
    1704
    +    oc->initializersRan = true;
    
    1684 1705
         oc->status = OBJECT_READY;
    
    1685 1706
     
    
    1686 1707
         return 1;
    

  • rts/LinkerInternals.h
    ... ... @@ -268,6 +268,12 @@ struct _ObjectCode {
    268 268
            after allocation, so that we can use realloc */
    
    269 269
         int        misalignment;
    
    270 270
     
    
    271
    +    /* Set to true after initializers (.init_array, .ctors, etc.) have been
    
    272
    +     * executed. Used by freeObjectCode to decide whether finalizers should
    
    273
    +     * run: only objects whose initializers ran should have their finalizers
    
    274
    +     * executed. See Note [Object unloading and finalizers]. */
    
    275
    +    bool initializersRan;
    
    276
    +
    
    271 277
         /* The address of __cxa_finalize; set when at least one finalizer was
    
    272 278
          * register and therefore we must call __cxa_finalize before unloading.
    
    273 279
          * See Note [Resolving __dso_handle]. */
    

  • testsuite/tests/codeGen/should_run/T27072d.hs
    1
    +{-# LANGUAGE StaticPointers #-}
    
    2
    +module T27072d where
    
    3
    +
    
    4
    +import GHC.StaticPtr
    
    5
    +
    
    6
    +f :: StaticPtr Int
    
    7
    +f = static 1
    
    8
    +
    
    9
    +g :: StaticPtr Int
    
    10
    +g = static 2

  • testsuite/tests/codeGen/should_run/T27072d.stdout
    1
    +SPT entries after init: 2
    
    2
    +SPT entries after finalizer: 0

  • testsuite/tests/codeGen/should_run/T27072d_c.c
    1
    +// Test that GHC-generated module initializers and finalizer registrations
    
    2
    +// work correctly on Darwin.
    
    3
    +//
    
    4
    +// On Darwin, GHC lowers finalizers as __cxa_atexit registrations from an
    
    5
    +// initializer placed in __DATA,__mod_init_func (see Note [Finalizers via
    
    6
    +// __cxa_atexit] in GHC.Types.ForeignStubs).
    
    7
    +//
    
    8
    +// This test verifies the mechanism by checking that:
    
    9
    +//  1. The SPT initializer runs at load time (entries are inserted).
    
    10
    +//  2. The SPT finalizer (registered via __cxa_atexit from __mod_init_func)
    
    11
    +//     fires during exit() and removes the entries.
    
    12
    +//
    
    13
    +// We verify (2) by registering our own __cxa_atexit checker from a
    
    14
    +// constructor in a dylib that is loaded before the main executable's
    
    15
    +// initializers run. Since __cxa_atexit handlers fire in LIFO order,
    
    16
    +// a handler registered earlier runs later — so our checker runs after the
    
    17
    +// GHC-generated finalizer, and can observe that SPT entries were removed.
    
    18
    +//
    
    19
    +// The Apple linker does not support --wrap, so this is the Darwin
    
    20
    +// equivalent of T27072w's approach.
    
    21
    +
    
    22
    +#include "Rts.h"
    
    23
    +#include <stdio.h>
    
    24
    +
    
    25
    +extern int hs_spt_key_count(void);
    
    26
    +
    
    27
    +int main(int argc, char *argv[]) {
    
    28
    +    RtsConfig conf = defaultRtsConfig;
    
    29
    +    conf.rts_opts_enabled = RtsOptsAll;
    
    30
    +    hs_init_ghc(&argc, &argv, conf);
    
    31
    +
    
    32
    +    printf("SPT entries after init: %d\n", hs_spt_key_count());
    
    33
    +    fflush(stdout);
    
    34
    +
    
    35
    +    // Do NOT call hs_exit(). Return normally so __cxa_atexit handlers fire,
    
    36
    +    // which includes the GHC-generated finalizer registered during init.
    
    37
    +    return 0;
    
    38
    +}

  • testsuite/tests/codeGen/should_run/T27072d_check.c
    1
    +// Checker dylib for T27072d.
    
    2
    +//
    
    3
    +// Compiled as a dylib and linked against the test executable. Because dylib
    
    4
    +// initializers run before the main executable's __mod_init_func entries,
    
    5
    +// our __cxa_atexit registration happens first. Since __cxa_atexit handlers
    
    6
    +// fire in LIFO order, our checker runs *after* the GHC-generated finalizer,
    
    7
    +// allowing us to observe that SPT entries were removed.
    
    8
    +
    
    9
    +#include <stdio.h>
    
    10
    +
    
    11
    +// Provided by the RTS.
    
    12
    +extern int hs_spt_key_count(void);
    
    13
    +
    
    14
    +static void check_spt_finalizer(void *arg __attribute__((unused))) {
    
    15
    +    int count = hs_spt_key_count();
    
    16
    +    printf("SPT entries after finalizer: %d\n", count);
    
    17
    +    fflush(stdout);
    
    18
    +}
    
    19
    +
    
    20
    +// Register the checker. This constructor runs during dylib initialization,
    
    21
    +// which happens before the main executable's initializers.
    
    22
    +__attribute__((constructor))
    
    23
    +static void register_spt_checker(void) {
    
    24
    +    // Use __cxa_atexit so we participate in the same LIFO chain as the
    
    25
    +    // GHC-generated finalizer.
    
    26
    +    extern int __cxa_atexit(void (*)(void *), void *, void *);
    
    27
    +    extern void *__dso_handle;
    
    28
    +    __cxa_atexit(check_spt_finalizer, (void *)0, &__dso_handle);
    
    29
    +}

  • testsuite/tests/codeGen/should_run/T27072w.hs
    1
    +{-# LANGUAGE StaticPointers #-}
    
    2
    +module T27072w where
    
    3
    +
    
    4
    +import GHC.StaticPtr
    
    5
    +
    
    6
    +f :: StaticPtr Int
    
    7
    +f = static 1
    
    8
    +
    
    9
    +g :: StaticPtr Int
    
    10
    +g = static 2

  • testsuite/tests/codeGen/should_run/T27072w.stdout
    1
    +SPT entries after init: 2
    
    2
    +finalizer: hs_spt_remove called
    
    3
    +finalizer: hs_spt_remove called

  • testsuite/tests/codeGen/should_run/T27072w_c.c
    1
    +// Test that GHC-generated finalizers actually run on wasm32
    
    2
    +//
    
    3
    +// We use --wrap=hs_spt_remove to intercept calls from the GHC-generated
    
    4
    +// finalizer and verify they happen during exit().
    
    5
    +
    
    6
    +#include "Rts.h"
    
    7
    +#include <stdio.h>
    
    8
    +
    
    9
    +extern int hs_spt_key_count(void);
    
    10
    +
    
    11
    +// --wrap=hs_spt_remove: the linker redirects all calls to hs_spt_remove
    
    12
    +// through our wrapper, and provides __real_hs_spt_remove for the original.
    
    13
    +extern void __real_hs_spt_remove(StgWord64 key[2]);
    
    14
    +
    
    15
    +void __wrap_hs_spt_remove(StgWord64 key[2]) {
    
    16
    +    printf("finalizer: hs_spt_remove called\n");
    
    17
    +    fflush(stdout);
    
    18
    +    __real_hs_spt_remove(key);
    
    19
    +}
    
    20
    +
    
    21
    +int main(int argc, char *argv[]) {
    
    22
    +    RtsConfig conf = defaultRtsConfig;
    
    23
    +    conf.rts_opts_enabled = RtsOptsAll;
    
    24
    +    hs_init_ghc(&argc, &argv, conf);
    
    25
    +
    
    26
    +    printf("SPT entries after init: %d\n", hs_spt_key_count());
    
    27
    +    fflush(stdout);
    
    28
    +
    
    29
    +    // Do NOT call hs_exit(). Return normally so exit() fires the
    
    30
    +    // __cxa_atexit registered handlers.
    
    31
    +    return 0;
    
    32
    +}

  • testsuite/tests/codeGen/should_run/all.T
    ... ... @@ -256,3 +256,22 @@ test('T24893', normal, compile_and_run, ['-O'])
    256 256
     test('CCallConv', [req_c], compile_and_run, ['CCallConv_c.c'])
    
    257 257
     test('T26061', normal, compile_and_run, [''])
    
    258 258
     test('T26537', js_broken(26558), compile_and_run, ['-O2 -fregs-graph'])
    
    259
    +
    
    260
    +# Check that GHC-generated finalizers run on Darwin. The Apple linker doesn't
    
    261
    +# support --wrap, so we can't intercept hs_spt_remove directly.  Instead we
    
    262
    +# compile a small checker dylib (T27072d_check.c) whose constructor registers
    
    263
    +# a __cxa_atexit handler *before* the executable's __mod_init_func entries run.
    
    264
    +# LIFO ordering ensures the checker fires after the GHC-generated finalizer,
    
    265
    +# so it can observe that SPT entries were removed.
    
    266
    +# Requires dynamic way so the RTS is a dylib (avoids archive conflicts).
    
    267
    +test('T27072d', [req_c, only_ways(['dyn']), when(not opsys('darwin'), skip),
    
    268
    +     pre_cmd('{compiler} -shared -no-hs-main'
    
    269
    +             ' -optl -undefined -optl dynamic_lookup'
    
    270
    +             ' -o T27072d_check.dylib T27072d_check.c')],
    
    271
    +     compile_and_run,
    
    272
    +     ['T27072d_c.c -no-hs-main'
    
    273
    +      ' -optl -Wl,-needed_library,T27072d_check.dylib -optl -rpath -optl .'])
    
    274
    +# check that finalizers are being run, using --wrap to intercept hs_spt_remove.
    
    275
    +# Skipped on Darwin (Apple linker doesn't support --wrap).
    
    276
    +test('T27072w', [req_c, js_skip, when(opsys('darwin'), skip)],
    
    277
    +     compile_and_run, ['T27072w_c.c -no-hs-main -optl-Wl,--wrap=hs_spt_remove'])

  • testsuite/tests/overloadedstrings/should_fail/T25926.hs
    1
    +module T25926 where
    
    2
    +
    
    3
    +f () 0 = ()
    
    4
    +f 'a' _ = ()

  • testsuite/tests/overloadedstrings/should_fail/T25926.stderr
    1
    +T25926.hs:4:3: warning: [GHC-83865] [-Wdeferred-type-errors (in -Wdefault)]
    
    2
    +    • Couldn't match expected type ‘()’ with actual type ‘Char’
    
    3
    +    • In the pattern: 'a'
    
    4
    +      In an equation for ‘f’: f 'a' _ = ()
    
    5
    +

  • testsuite/tests/overloadedstrings/should_fail/T27124.hs
    1
    +{-# LANGUAGE OverloadedStrings #-}
    
    2
    +
    
    3
    +module T27124 where
    
    4
    +
    
    5
    +foo :: [String] -> Bool
    
    6
    +foo "HI" = True
    
    7
    +foo _ = False
    
    8
    +
    
    9
    +main = pure ()

  • testsuite/tests/overloadedstrings/should_fail/T27124.stderr
    1
    +T27124.hs:6:5: warning: [GHC-18872] [-Wdeferred-type-errors (in -Wdefault)]
    
    2
    +    • Couldn't match type ‘[Char]’ with ‘Char’
    
    3
    +        arising from the literal ‘"HI"’
    
    4
    +    • In the pattern: "HI"
    
    5
    +      In an equation for ‘foo’: foo "HI" = True
    
    6
    +

  • testsuite/tests/overloadedstrings/should_fail/all.T
    1
    +test('T25926', normal, compile, ['-fdefer-type-errors'])
    
    2
    +test('T27124', normal, compile, ['-fdefer-type-errors'])

  • testsuite/tests/overloadedstrings/should_run/T27124a.hs
    1
    +{-# LANGUAGE FlexibleInstances #-}
    
    2
    +{-# LANGUAGE OverloadedStrings #-}
    
    3
    +
    
    4
    +module T27124a where
    
    5
    +
    
    6
    +import Data.String (IsString(..))
    
    7
    +
    
    8
    +newtype Wrap a = Wrap a deriving (Eq, Show)
    
    9
    +
    
    10
    +instance IsString a => IsString (Wrap a) where
    
    11
    +  fromString = Wrap . fromString
    
    12
    +
    
    13
    +instance {-# INCOHERENT #-} IsString (Wrap Bool) where
    
    14
    +  fromString _ = Wrap False
    
    15
    +
    
    16
    +f :: (Eq a, IsString a) => Wrap a -> Bool
    
    17
    +f "hello" = True
    
    18
    +f _       = False
    
    19
    +
    
    20
    +main :: IO ()
    
    21
    +main = do
    
    22
    +  print (f (Wrap ("hello" :: String)))
    
    23
    +  print (f (Wrap ("world" :: String)))

  • testsuite/tests/overloadedstrings/should_run/all.T
    1 1
     test('overloadedstringsrun01', normal, compile_and_run, [''])
    
    2
    +test('T27124a', normal, compile, ['-fno-specialise-incoherents'])

  • testsuite/tests/rts/linker/T27072/Lib.c
    1
    +// Minimal module with an initializer and finalizer.
    
    2
    +// The compiler places the function pointers in .init_array/.fini_array
    
    3
    +// (ELF) or __mod_init_func/__mod_term_func (Mach-O).
    
    4
    +//
    
    5
    +// The counter lives in the main binary so it survives after this
    
    6
    +// object is unloaded.
    
    7
    +
    
    8
    +extern int init_counter;
    
    9
    +
    
    10
    +__attribute__((constructor))
    
    11
    +static void lib_init(void) {
    
    12
    +    init_counter++;
    
    13
    +}
    
    14
    +
    
    15
    +__attribute__((destructor))
    
    16
    +static void lib_fini(void) {
    
    17
    +    init_counter--;
    
    18
    +}

  • testsuite/tests/rts/linker/T27072/Makefile
    1
    +.PHONY: clean_build_and_run build_and_run clean build
    
    2
    +
    
    3
    +clean_build_and_run:
    
    4
    +	$(MAKE) clean
    
    5
    +	$(MAKE) build_and_run
    
    6
    +
    
    7
    +build_and_run: build
    
    8
    +	./main
    
    9
    +
    
    10
    +clean:
    
    11
    +	$(RM) Lib.o main.o main
    
    12
    +
    
    13
    +build: Lib.o main
    
    14
    +
    
    15
    +Lib.o: Lib.c
    
    16
    +	$(CC) -c -fPIC Lib.c -o Lib.o
    
    17
    +
    
    18
    +main: main.c
    
    19
    +	"$(TEST_HC)" $(filter-out -rtsopts, $(TEST_HC_OPTS)) \
    
    20
    +		-no-hs-main -optc-Werror \
    
    21
    +		main.c -o main

  • testsuite/tests/rts/linker/T27072/T27072.stdout
    1
    +counter before load: 0
    
    2
    +counter after load: 1
    
    3
    +counter after unload: 0

  • testsuite/tests/rts/linker/T27072/all.T
    1
    +test('T27072',
    
    2
    +     [req_rts_linker,
    
    3
    +      js_skip,
    
    4
    +      extra_files(['Lib.c', 'main.c'])],
    
    5
    +     makefile_test,
    
    6
    +     ['clean_build_and_run'])

  • testsuite/tests/rts/linker/T27072/main.c
    1
    +// Test that the RTS linker executes .init_array entries on load and
    
    2
    +// .fini_array entries on unload.  The loaded module increments a
    
    3
    +// counter in its initializer and decrements it in its finalizer.
    
    4
    +
    
    5
    +#include "Rts.h"
    
    6
    +#include <stdio.h>
    
    7
    +
    
    8
    +#if defined(mingw32_HOST_OS)
    
    9
    +#define PATH_STR(str) L##str
    
    10
    +#else
    
    11
    +#define PATH_STR(str) str
    
    12
    +#endif
    
    13
    +
    
    14
    +int init_counter = 0;
    
    15
    +
    
    16
    +int main(int argc, char *argv[]) {
    
    17
    +    RtsConfig conf = defaultRtsConfig;
    
    18
    +    conf.rts_opts_enabled = RtsOptsAll;
    
    19
    +    hs_init_ghc(&argc, &argv, conf);
    
    20
    +
    
    21
    +    initLinker_(0);
    
    22
    +    insertSymbol(PATH_STR("main"), "init_counter", &init_counter);
    
    23
    +
    
    24
    +    printf("counter before load: %d\n", init_counter);
    
    25
    +    fflush(stdout);
    
    26
    +
    
    27
    +    int ok;
    
    28
    +    ok = loadObj(PATH_STR("Lib.o"));
    
    29
    +    if (!ok) {
    
    30
    +        errorBelch("loadObj(Lib.o) failed");
    
    31
    +        return 1;
    
    32
    +    }
    
    33
    +    ok = resolveObjs();
    
    34
    +    if (!ok) {
    
    35
    +        errorBelch("resolveObjs() failed");
    
    36
    +        return 1;
    
    37
    +    }
    
    38
    +
    
    39
    +    printf("counter after load: %d\n", init_counter);
    
    40
    +    fflush(stdout);
    
    41
    +
    
    42
    +    ok = unloadObj(PATH_STR("Lib.o"));
    
    43
    +    if (!ok) {
    
    44
    +        errorBelch("unloadObj(Lib.o) failed");
    
    45
    +        return 1;
    
    46
    +    }
    
    47
    +
    
    48
    +    // GC triggers actual unloading and finalizer execution.
    
    49
    +    performMajorGC();
    
    50
    +    performMajorGC();
    
    51
    +
    
    52
    +    printf("counter after unload: %d\n", init_counter);
    
    53
    +    fflush(stdout);
    
    54
    +
    
    55
    +    hs_exit();
    
    56
    +    return 0;
    
    57
    +}