Andreas Klebinger pushed to branch wip/apk/tag-strict-bot at Glasgow Haskell Compiler / GHC

Commits:

24 changed files:

Changes:

  • changelog.d/T27831-bottoming-strict-field
    1
    +section: compiler
    
    2
    +issues: #27831
    
    3
    +mrs: !16719
    
    4
    +synopsis:
    
    5
    +  Fix a segfault that could occur when a bottoming function was stored in a strict field.
    
    6
    +description:
    
    7
    +  This was happening when a function that always diverges (e.g. ``\_ -> error "..."``) was stored in a strict field, and the constructor was pattern matched on
    
    8
    +  in "nearby" code where the structure of the constructor application was still visible to GHC.

  • changelog.d/T27842-bytecode-imported-tags
    1
    +section: bytecode
    
    2
    +issues: #27842
    
    3
    +mrs: !16719
    
    4
    +synopsis:
    
    5
    +  Fix a segfault caused by reusing interface files generated with
    
    6
    +  ``-fbyte-code-and-object-code`` for interpreter sessions using ``-fprefer-byte-code``.

  • compiler/GHC/Stg/EnforceEpt.hs
    ... ... @@ -334,9 +334,15 @@ a) Tags for let-bindings:
    334 334
     b) When referencing ids from other modules the Cmm backend will try to put a
    
    335 335
        proper tag on these references through various means. When doing analysis we
    
    336 336
        usually predict these cases to improve precision of the analysis.
    
    337
    -   But to my knowledge the bytecode generator makes no such attempts so we must
    
    338
    -   not infer imported bindings as tagged.
    
    339
    -   This is handled in GHC.Stg.EnforceEpt.Types.lookupInfo
    
    337
    +   The bytecode generator gives fewer guarantees about what it tags.
    
    338
    +   So we simply assume it fails to tag binders, even in the cases where it does.
    
    339
    +
    
    340
    +   This is handled in GHC.Stg.EnforceEpt.Types.argTagInfo
    
    341
    +
    
    342
    + Eventually we will want to update the Analysis and bco code gen to uphold and
    
    343
    + take advantage of tag inference at least to some degree. But for now it's
    
    344
    + not a perf benefit for interpreted code, and essentially only run to uphold
    
    345
    + the "strict field invariant"/EPT Invariant described in EPT enforcement.
    
    340 346
     
    
    341 347
     
    
    342 348
     -}
    
    ... ... @@ -359,7 +365,7 @@ enforceEpt ppr_opts !for_bytecode logger this_mod stg_binds = do
    359 365
     
    
    360 366
         -- Rewrite STG to uphold the strict field invariant
    
    361 367
         us_t <- mkSplitUniqSupply StgTag
    
    362
    -    let rewritten_binds = {-# SCC "StgEptRewrite" #-} rewriteTopBinds this_mod us_t stg_binds_w_tags :: [TgStgTopBinding]
    
    368
    +    let rewritten_binds = {-# SCC "StgEptRewrite" #-} rewriteTopBinds for_bytecode this_mod us_t stg_binds_w_tags :: [TgStgTopBinding]
    
    363 369
     
    
    364 370
         return (rewritten_binds,export_tag_info)
    
    365 371
     
    
    ... ... @@ -414,12 +420,14 @@ inferTagExpr env (StgApp fun args)
    414 420
              | isDeadEndAppSig (idDmdSig fun) (length args)
    
    415 421
              = TagBottoming
    
    416 422
     
    
    417
    -         | fun_arity == 0 -- Unknown arity => Thunk or unknown call
    
    423
    +         | not (isJoinId fun) -- zero arity join ids behave like functions NOT thunks.
    
    424
    +         , fun_arity == 0 -- Unknown arity => Thunk or unknown call
    
    418 425
              = TagDunno
    
    419 426
     
    
    420 427
              -- Imported function with known return tag
    
    421 428
              | Just (TagFun res_info) <- tagSigInfo (idInfo fun)
    
    422 429
              , fun_arity == length args  -- Saturated
    
    430
    +         , not (te_bytecode env) -- #27842
    
    423 431
              = res_info
    
    424 432
     
    
    425 433
              -- Local function with known return tag
    
    ... ... @@ -593,7 +601,7 @@ initSig (bndr, StgRhsClosure _ _ _ bndrs _ _)
    593 601
     
    
    594 602
     {- Note [Bottom functions are TagBottoming]
    
    595 603
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    596
    -If we have a function with two branches with one
    
    604
    +If we have a function with two branches with one branch
    
    597 605
     being bottom, and the other returning a tagged
    
    598 606
     unboxed tuple what is the result? We give it TagBottoming!
    
    599 607
     To answer why consider this function:
    
    ... ... @@ -603,21 +611,40 @@ foo x = case x of
    603 611
         True -> (# True,True #)
    
    604 612
         False -> undefined
    
    605 613
     
    
    606
    -The true branch is obviously tagged. The other branch isn't.
    
    607
    -We want to treat the *result* of foo as tagged as well so that
    
    608
    -the combination of the branches also is tagged if all non-bottom
    
    609
    -branches are tagged.
    
    610
    -This is safe because the function is still always called/entered as long
    
    611
    -as it's applied to arguments. Since the function will never return we can give
    
    612
    -it safely any tag sig we like.
    
    613
    -So we give it TagBottoming, as it allows the combined tag sig of the case expression
    
    614
    -to be the combination of all non-bottoming branches.
    
    614
    +The true branch obviously returns tagged components. The other branch is bottom.
    
    615
    +We want to treat the components in the *result* returned by foo as tagged.
    
    616
    +Because at a call site like:
    
    617
    +
    
    618
    +    case foo e of
    
    619
    +        (# x, y #) ->
    
    620
    +            case x of
    
    621
    +                ...
    
    622
    +
    
    623
    +Either `foo` will have evaluated undefined or blown up. Or we will bind x and y
    
    624
    +to tagged pointers. A fact we take advantage of to optimize `case x of`.
    
    625
    +
    
    626
    +The rule is then simple. A combination of the alternatives of the same case has
    
    627
    +tagged components if all non-bottom branches have tagged components.
    
    628
    +
    
    629
    +To represent the bottoming alternatives we use TagBottoming. Which says all it's
    
    630
    +components are tagged. Now the same is true if we have:
    
    631
    +
    
    632
    +foo :: Bool -> (# Bool, Bool #)
    
    633
    +foo x = case x of
    
    634
    +    True -> (# True,True #)
    
    635
    +    False -> willCrash x
    
    636
    +
    
    637
    +Where we want to check:
    
    615 638
     
    
    616
    -NB: After the analysis is done we go back to treating bottoming functions as
    
    617
    -untagged to ensure they are evaluated as expected in code like:
    
    639
    +* Is `willCrash x` bottom?
    
    640
    +* If it is not are the returned components tagged?
    
    618 641
     
    
    619
    -  case bottom_id of { ...}
    
    642
    +For the first question we have to be careful about the notion of bottom we use.
    
    643
    +I've got this wrong in the past, because a underapplied bottoming function will
    
    644
    +return a *function* as result. Rather than being bottom.
    
    620 645
     
    
    646
    +If the function *is* bottom then we treat it the same we do undefined above. And
    
    647
    +use TagBottoming as the tagInfo of the expression. Hence the title of the Note.
    
    621 648
     -}
    
    622 649
     
    
    623 650
     -----------------------------
    

  • compiler/GHC/Stg/EnforceEpt/Rewrite.hs
    ... ... @@ -3,6 +3,7 @@
    3 3
     
    
    4 4
     {-# LANGUAGE CPP                        #-}
    
    5 5
     {-# LANGUAGE TypeFamilies               #-}
    
    6
    +{-# LANGUAGE UnboxedTuples              #-}
    
    6 7
     
    
    7 8
     module GHC.Stg.EnforceEpt.Rewrite (rewriteTopBinds, rewriteOpApp)
    
    8 9
     where
    
    ... ... @@ -13,7 +14,7 @@ import GHC.Builtin.PrimOps ( PrimOp(..) )
    13 14
     import GHC.Types.Basic     ( CbvMark (..), isMarkedCbv
    
    14 15
                                , TopLevelFlag(..), isTopLevel )
    
    15 16
     import GHC.Types.Id
    
    16
    -import GHC.Types.Name
    
    17
    +import GHC.Types.Name ( nameIsLocalOrFrom )
    
    17 18
     import GHC.Types.Unique.Supply
    
    18 19
     import GHC.Types.Unique.FM
    
    19 20
     import GHC.Types.RepType
    
    ... ... @@ -22,10 +23,7 @@ import GHC.Unit.Types
    22 23
     
    
    23 24
     import GHC.Core.DataCon
    
    24 25
     import GHC.Core            ( AltCon(..) )
    
    25
    -import GHC.Core.Type
    
    26
    -
    
    27
    -import GHC.StgToCmm.Types
    
    28
    -import GHC.StgToCmm.Closure (importedIdLFInfo)
    
    26
    +import GHC.Core.Type       ( definitelyLiftedType )
    
    29 27
     
    
    30 28
     import GHC.Stg.Utils
    
    31 29
     import GHC.Stg.Syntax as StgSyn
    
    ... ... @@ -34,15 +32,48 @@ import GHC.Data.Maybe
    34 32
     import GHC.Utils.Panic
    
    35 33
     
    
    36 34
     import GHC.Utils.Outputable
    
    37
    -import GHC.Utils.Monad.State.Strict
    
    38 35
     import GHC.Utils.Misc
    
    39 36
     
    
    40 37
     import GHC.Stg.EnforceEpt.Types
    
    41 38
     
    
    42 39
     import Control.Monad
    
    43
    -
    
    44
    -newtype RM a = RM { unRM :: (State (UniqFM Id TagSig, UniqSupply, Module, IdSet) a) }
    
    45
    -    deriving (Functor, Monad, Applicative)
    
    40
    +import GHC.Exts (oneShot)
    
    41
    +
    
    42
    +data RmContext = RmContext { rm_mod :: !Module, rm_interpreter :: !Bool }
    
    43
    +
    
    44
    +data RmState = RmState { rm_us :: !UniqSupply, rm_fvs :: IdSet, rm_sig_env :: !(UniqFM Id TagSig)}
    
    45
    +newtype RM a = RM { unRM :: RmContext -> RmState -> (# RmState, a #) }
    
    46
    +
    
    47
    +instance Functor RM where
    
    48
    +    fmap f (RM x) = RM $ oneShot $ \env -> oneShot $ \state ->
    
    49
    +        case x env state of
    
    50
    +            (# !s1, !x' #) ->
    
    51
    +                let !r = f x'
    
    52
    +                in (# s1, r #)
    
    53
    +
    
    54
    +instance Applicative RM where
    
    55
    +    (RM f) <*> (RM x) = RM $ oneShot $ \env -> oneShot $ \state ->
    
    56
    +        case f env state of
    
    57
    +            (# !s1,!f' #) -> case x env s1 of
    
    58
    +                (# !s2, !x' #) ->
    
    59
    +                    let !r = f' x'
    
    60
    +                    in (# s2, r #)
    
    61
    +    pure !x = RM $ \_ !s -> (# s, x #)
    
    62
    +
    
    63
    +-- See Note [The one-shot state monad trick] in GHC.Utils.Monad
    
    64
    +instance Monad RM where
    
    65
    +    RM x >>= f = RM $ oneShot $ \env -> oneShot $ \state ->
    
    66
    +        case x env state of
    
    67
    +            (# !s1, !x' #) -> case (f x') of RM r -> r env s1
    
    68
    +
    
    69
    +-- | runRM this_mod for_bytecode us
    
    70
    +runRM :: Module -> Bool -> UniqSupply -> RM a -> a
    
    71
    +runRM mod for_bytecode us act  =
    
    72
    +    case (unRM act)
    
    73
    +            (RmContext { rm_mod = mod, rm_interpreter = for_bytecode })
    
    74
    +            (RmState { rm_us = us, rm_fvs = emptyVarSet, rm_sig_env = emptyUFM })
    
    75
    +        of
    
    76
    +    (# _, r #) -> r
    
    46 77
     
    
    47 78
     ------------------------------------------------------------
    
    48 79
     -- Add cases around strict fields where required.
    
    ... ... @@ -107,30 +138,29 @@ when building the free variable list.
    107 138
     --------------------------------
    
    108 139
     
    
    109 140
     instance MonadUnique RM where
    
    110
    -    getUniqueSupplyM = RM $ do
    
    111
    -        (m, us, mod,lcls) <- get
    
    112
    -        let (us1, us2) = splitUniqSupply us
    
    113
    -        (put) (m,us2,mod,lcls)
    
    114
    -        return us1
    
    141
    +    getUniqueSupplyM = RM $ \_ s ->
    
    142
    +        case splitUniqSupply (rm_us s) of
    
    143
    +            (us1, us2) -> (# s { rm_us = us2 }, us1 #)
    
    115 144
     
    
    116 145
     getMap :: RM (UniqFM Id TagSig)
    
    117
    -getMap = RM $ ((\(fst,_,_,_) -> fst) <$> get)
    
    146
    +getMap = RM $ \_ s -> (# s, rm_sig_env s #)
    
    118 147
     
    
    119 148
     setMap :: (UniqFM Id TagSig) -> RM ()
    
    120
    -setMap !m = RM $ do
    
    121
    -    (_,us,mod,lcls) <- get
    
    122
    -    put (m, us,mod,lcls)
    
    123
    -
    
    124
    -getMod :: RM Module
    
    125
    -getMod = RM $ ( (\(_,_,thrd,_) -> thrd) <$> get)
    
    149
    +setMap !m = RM $ \_ s -> (# s { rm_sig_env = m }, () #)
    
    126 150
     
    
    127 151
     getFVs :: RM IdSet
    
    128
    -getFVs = RM $ ((\(_,_,_,lcls) -> lcls) <$> get)
    
    152
    +getFVs = RM $ \_ s -> (# s, rm_fvs s #)
    
    129 153
     
    
    130 154
     setFVs :: IdSet -> RM ()
    
    131
    -setFVs !fvs = RM $ do
    
    132
    -    (tag_map,us,mod,_lcls) <- get
    
    133
    -    put (tag_map, us,mod,fvs)
    
    155
    +setFVs !fvs = RM $ \_ s -> (# s { rm_fvs = fvs }, () #)
    
    156
    +
    
    157
    +getMod :: RM Module
    
    158
    +getMod = RM $ \env s -> (# s, rm_mod env #)
    
    159
    +
    
    160
    +-- | When generating bytecode certain bindings might be untagged. So we have to
    
    161
    +-- have a way to check for that.
    
    162
    +getForBytecode :: RM Bool
    
    163
    +getForBytecode = RM $ \env s -> (# s, rm_interpreter env #)
    
    134 164
     
    
    135 165
     -- Rewrite the RHS(s) while making the id and it's sig available
    
    136 166
     -- to determine if things are tagged/need to be captured as FV.
    
    ... ... @@ -238,51 +268,26 @@ indicates a bug in the tag inference implementation.
    238 268
     For this reason we assert that we are running in interactive mode if a lookup fails.
    
    239 269
     -}
    
    240 270
     isTagged :: Id -> RM Bool
    
    241
    -isTagged v
    
    242
    -    -- See Note [Bottom functions are TagBottoming]
    
    243
    -    | isDeadEndId v = pure False
    
    244
    -    | otherwise = do
    
    271
    +isTagged v = do
    
    272
    +    for_bytecode <- getForBytecode
    
    245 273
         this_mod <- getMod
    
    246
    -    -- See Note [Tag inference for interactive contexts]
    
    247
    -    let lookupDefault v = assertPpr (isInteractiveModule this_mod)
    
    248
    -                                    (text "unknown Id:" <> ppr this_mod <+> ppr v)
    
    249
    -                                    (TagVal TagDunno)
    
    250
    -    case nameIsLocalOrFrom this_mod (idName v) of
    
    251
    -        True
    
    252
    -            | definitelyUnliftedType (idType v)
    
    253
    -              -- NB: v might be the Id of a representation-polymorphic join point,
    
    254
    -              -- so we shouldn't use isUnliftedType here. See T22212.
    
    255
    -            -> return True
    
    256
    -            | otherwise -> do -- Local binding
    
    257
    -                !s <- getMap
    
    258
    -                let !sig = lookupWithDefaultUFM s (lookupDefault v) v
    
    259
    -                return $ case sig of
    
    260
    -                    TagFun _             -> True  -- function closure is tagged
    
    261
    -                    TagVal TagDunno      -> False
    
    262
    -                    TagVal TagEPT        -> True
    
    263
    -                    TagVal TagBottoming  -> True
    
    264
    -                    TagVal (TagTuple _)  -> True  -- Consider unboxed tuples tagged.
    
    265
    -        -- Imported
    
    266
    -        False -> return $!
    
    267
    -                -- Determine whether it is tagged from the LFInfo of the imported id.
    
    268
    -                -- See Note [The LFInfo of Imported Ids]
    
    269
    -                case importedIdLFInfo v of
    
    270
    -                    -- Function, applied not entered.
    
    271
    -                    LFReEntrant {}
    
    272
    -                        -> True
    
    273
    -                    -- Thunks need to be entered.
    
    274
    -                    LFThunk {}
    
    275
    -                        -> False
    
    276
    -                    -- LFCon means we already know the tag, and it's tagged.
    
    277
    -                    LFCon {}
    
    278
    -                        -> True
    
    279
    -                    LFUnknown {}
    
    280
    -                        -> False
    
    281
    -                    LFUnlifted {}  -- Unboxed, tagging irrelevant
    
    282
    -                        -> True
    
    283
    -                    LFLetNoEscape {}
    
    284
    -                    -- Shouldn't be possible. I don't think we can export letNoEscapes
    
    285
    -                        -> True
    
    274
    +    s <- getMap
    
    275
    +    let get_env_sig = lookupUFM s
    
    276
    +    massertPpr
    
    277
    +        -- Assert the env is complete for local lifted binders, which means either
    
    278
    +        -- - The binder is present in the env
    
    279
    +        -- - The binder is not a local lifted binder *or* comes from a ghci context
    
    280
    +        (isJust (get_env_sig v)
    
    281
    +            -- See Note [Tag inference for interactive contexts]
    
    282
    +            || isInteractiveModule this_mod
    
    283
    +            || not (nameIsLocalOrFrom this_mod (idName v))
    
    284
    +            || not (definitelyLiftedType (idType v)))
    
    285
    +               (text "unknown Id:" <> ppr this_mod <+> ppr v)
    
    286
    +    -- TODO: We could refactor the whole pass such that we also set id_tagSig
    
    287
    +    -- for every imported id. But this likely comes with a heavy allocation
    
    288
    +    -- overhead so for now we just ensure to use the same logic across analysis
    
    289
    +    -- and rewriter.
    
    290
    +    pure $! isTaggedInfo $ argTagInfo for_bytecode (get_env_sig v) (StgVarArg v)
    
    286 291
     
    
    287 292
     
    
    288 293
     isArgTagged :: StgArg -> RM Bool
    
    ... ... @@ -299,11 +304,10 @@ mkLocalArgId id = do
    299 304
     ---------------------------
    
    300 305
     
    
    301 306
     
    
    302
    -rewriteTopBinds :: Module -> UniqSupply -> [GenStgTopBinding 'InferTaggedBinders] -> [TgStgTopBinding]
    
    303
    -rewriteTopBinds mod us binds =
    
    304
    -    let doBinds = mapM rewriteTop binds
    
    305
    -
    
    306
    -    in evalState (unRM doBinds) (mempty, us, mod, mempty)
    
    307
    +rewriteTopBinds :: Bool -- ^ Generating bytecode?
    
    308
    +                -> Module -> UniqSupply -> [GenStgTopBinding 'InferTaggedBinders] -> [TgStgTopBinding]
    
    309
    +rewriteTopBinds for_bytecode mod us binds =
    
    310
    +    runRM mod for_bytecode us (mapM rewriteTop binds)
    
    307 311
     
    
    308 312
     rewriteTop :: InferStgTopBinding -> RM TgStgTopBinding
    
    309 313
     rewriteTop (StgTopStringLit v s) = return $! (StgTopStringLit v s)
    

  • compiler/GHC/Stg/EnforceEpt/TagSig.hs
    ... ... @@ -117,7 +117,7 @@ isTaggedSig (TagVal ti) = isTaggedInfo ti
    117 117
     
    
    118 118
     -- | Is the given value-level tag known to be properly tagged?
    
    119 119
     -- NB: unboxed tuples are *not* treated as tagged here; they are handled
    
    120
    --- specially by the rewriter (which considers them already evaluated).
    
    120
    +-- specially by the rewriter. See Note [Constructor TagSigs]
    
    121 121
     isTaggedInfo :: TagInfo -> Bool
    
    122 122
     isTaggedInfo TagEPT       = True
    
    123 123
     isTaggedInfo TagBottoming = True
    

  • compiler/GHC/Stg/EnforceEpt/Types.hs
    ... ... @@ -12,7 +12,7 @@ where
    12 12
     import GHC.Prelude
    
    13 13
     
    
    14 14
     import GHC.Core.DataCon
    
    15
    -import GHC.Core.Type (isUnliftedType)
    
    15
    +import GHC.Core.Type (isUnliftedType, definitelyUnliftedType)
    
    16 16
     import GHC.Types.Id
    
    17 17
     import GHC.Stg.Syntax
    
    18 18
     import GHC.Stg.EnforceEpt.TagSig as TagSig
    
    ... ... @@ -22,6 +22,7 @@ import GHC.Utils.Misc( zipWithEqual )
    22 22
     import GHC.Utils.Panic
    
    23 23
     
    
    24 24
     import GHC.StgToCmm.Types
    
    25
    +import GHC.StgToCmm.Closure (importedIdLFInfo)
    
    25 26
     
    
    26 27
     {- *********************************************************************
    
    27 28
     *                                                                      *
    
    ... ... @@ -97,31 +98,40 @@ lookupReturnInfo env fun = case lookupVarEnv (te_env env) fun of
    97 98
       Just (TagVal _)        -> Nothing
    
    98 99
       Nothing                -> Nothing
    
    99 100
     
    
    100
    --- | Look up a value-level tag for an argument: either from the env (where
    
    101
    --- a function-typed argument flattens to TagEPT — its closure is tagged)
    
    102
    --- or derived from information on the variable itself.
    
    103
    -lookupInfo :: TagEnv p -> StgArg -> TagInfo
    
    104
    -lookupInfo env (StgVarArg var)
    
    101
    +-- | Look up a value-level tag.
    
    102
    +--
    
    103
    +-- If the tag info is completely determined by Id info we use that tag.
    
    104
    +-- If the tag info is dependent on the analysis/call site use the passed TagSig.
    
    105
    +-- If the passed TagSig is Nothing we fall back to safe tag info based on id properties.
    
    106
    +argTagInfo :: Bool -> (Maybe TagSig) -> StgArg -> TagInfo
    
    107
    +{-# INLINE argTagInfo #-} -- Specialize for the call site.
    
    108
    +argTagInfo _for_bytecode _env_sig (StgLitArg{}) = TagEPT
    
    109
    +argTagInfo for_bytecode env_sig (StgVarArg var)
    
    105 110
       -- Nullary data constructors like True, False
    
    106 111
       | Just dc <- isDataConWorkId_maybe var
    
    107 112
       , isNullaryRepDataCon dc
    
    108 113
       , not for_bytecode
    
    109 114
       = TagEPT
    
    110 115
     
    
    111
    -  | isUnliftedType (idType var)
    
    116
    +  -- NB: v might be the Id of a representation-polymorphic join point,
    
    117
    +  -- so we shouldn't use isUnliftedType here. See T22212.
    
    118
    +  | definitelyUnliftedType (idType var)
    
    112 119
       = TagEPT
    
    113 120
     
    
    114 121
       -- Variables in the environment. A function binding flattens to TagEPT
    
    115 122
       -- since a function closure pointer is properly tagged; its return info
    
    116 123
       -- is not relevant when the function is used as a value.
    
    117
    -  | Just sig <- lookupVarEnv (te_env env) var
    
    124
    +  | Just sig <- env_sig
    
    118 125
       = case sig of
    
    119 126
           TagVal info -> info
    
    120 127
           TagFun _    -> TagEPT
    
    121 128
     
    
    122
    -  | Just lf_info <- idLFInfo_maybe var
    
    123
    -  , not for_bytecode
    
    124
    -  =   case lf_info of
    
    129
    +-- Always save defaults.
    
    130
    +  | lf_info <- importedIdLFInfo var -- will make up LFInfo if not present.
    
    131
    +  = if for_bytecode
    
    132
    +    then TagDunno -- See Note [EPT enforcement for interpreted code] in GHC.Stg.EnforceEpt
    
    133
    +    else
    
    134
    +        case lf_info of
    
    125 135
               -- Function, tagged (with arity)
    
    126 136
               LFReEntrant {}
    
    127 137
                   -> TagEPT
    
    ... ... @@ -138,13 +148,12 @@ lookupInfo env (StgVarArg var)
    138 148
               -- Shouldn't be possible. I don't think we can export letNoEscapes
    
    139 149
               LFLetNoEscape {} -> panic "LFLetNoEscape exported"
    
    140 150
     
    
    141
    -  | otherwise
    
    142
    -  = TagDunno
    
    143
    -  where
    
    144
    -    for_bytecode = te_bytecode env
    
    145
    -
    
    146
    -lookupInfo _ (StgLitArg {})
    
    147
    -  = TagEPT
    
    151
    +-- | Get tag info using the environment.
    
    152
    +lookupInfo :: TagEnv p -> StgArg -> TagInfo
    
    153
    +lookupInfo env arg@(StgVarArg var) =
    
    154
    +    let env_sig = (lookupVarEnv (te_env env) var)
    
    155
    +    in  argTagInfo (te_bytecode env) env_sig arg
    
    156
    +lookupInfo _env (StgLitArg{}) = TagEPT
    
    148 157
     
    
    149 158
     isDunnoSig :: TagSig -> Bool
    
    150 159
     isDunnoSig (TagVal TagDunno) = True
    

  • testsuite/tests/bytecode/T27842.hs
    1
    +{-# LANGUAGE UnboxedTuples #-}
    
    2
    +{-# OPTIONS_GHC -fno-worker-wrapper -fno-cpr-anal #-}
    
    3
    +-- Regression test for #27842.
    
    4
    +--
    
    5
    +-- The basic rundown is this:
    
    6
    +-- We produce one interface file, however we store information dependent on the
    
    7
    +-- code generation method in the interface file.
    
    8
    +--
    
    9
    +-- We happen to pick the native code information to store in the interface file.
    
    10
    +--
    
    11
    +-- When we compile a module M this way, and then generate byte code for code
    
    12
    +-- depending on M based on M.hi we will assume certain exported definitions are EPT
    
    13
    +-- tagged, despite the interpreter doing no such thing.
    
    14
    +--
    
    15
    +-- This then led to crashes in the interpreter.
    
    16
    +--
    
    17
    +-- We test three kinds of imported definitions:
    
    18
    +-- * mk uses fun, a function whose TagSig says its result is tagged.
    
    19
    +-- * bar uses just_con, a value whose LFInfo says it is a constructor.
    
    20
    +-- * baz uses gt_con, the same for a nullary constructor.
    
    21
    +module Main where
    
    22
    +
    
    23
    +import T27842A (fun, just_con, gt_con)
    
    24
    +import T27842N (S(..), use_S, M(..), use_M, N(..), use_N)
    
    25
    +
    
    26
    +mk :: Int -> S
    
    27
    +mk x = case fun x of (# xs, _ #) -> S xs
    
    28
    +{-# NOINLINE mk #-}
    
    29
    +
    
    30
    +bar :: M
    
    31
    +bar = M just_con
    
    32
    +{-# NOINLINE bar #-}
    
    33
    +
    
    34
    +baz :: N
    
    35
    +baz = N gt_con
    
    36
    +{-# NOINLINE baz #-}
    
    37
    +
    
    38
    +main :: IO ()
    
    39
    +main = do
    
    40
    +  print (use_S (mk 42))
    
    41
    +  print (use_M bar)
    
    42
    +  print (use_N baz)

  • testsuite/tests/bytecode/T27842.stdout
    1
    +42
    
    2
    +42
    
    3
    +3

  • testsuite/tests/bytecode/T27842A.hs
    1
    +{-# LANGUAGE UnboxedTuples #-}
    
    2
    +{-# OPTIONS_GHC -fno-worker-wrapper -fno-cpr-anal #-}
    
    3
    +module T27842A (fun, just_con, gt_con) where
    
    4
    +
    
    5
    +-- The native EPT run gives fun the TagSig TagFun[TagTuple[TagEPT, TagDunno]]:
    
    6
    +fun :: Int -> (# [Int], Int #)
    
    7
    +fun x = let xs = x : xs in (# xs, x #)
    
    8
    +{-# NOINLINE fun #-}
    
    9
    +
    
    10
    +-- In native code a static constructor, in bytecode an unevaluated closure.
    
    11
    +just_con :: Maybe Int
    
    12
    +just_con = Just 42
    
    13
    +{-# NOINLINE just_con #-}
    
    14
    +
    
    15
    +-- The same for a nullary constructor. Without its pointer tag, the object code
    
    16
    +-- in T27842N would take the LT alternative, so GT shows the difference.
    
    17
    +gt_con :: Ordering
    
    18
    +gt_con = GT
    
    19
    +{-# NOINLINE gt_con #-}

  • testsuite/tests/bytecode/T27842N.hs
    1
    +{-# OPTIONS_GHC -fno-byte-code-and-object-code #-}
    
    2
    +-- We compile this module as native code, depending on A's interface file.
    
    3
    +-- A itself is loaded into GHCi, despite it's interface file being generated based
    
    4
    +-- on native code generation.
    
    5
    +module T27842N (S(..), use_S, M(..), use_M, N(..), use_N) where
    
    6
    +
    
    7
    +data S = S ![Int]
    
    8
    +
    
    9
    +-- We take apart the strict constructor value and branch on the value in its field.
    
    10
    +-- If the field content is not EPT this causes undefined behaviour.
    
    11
    +
    
    12
    +use_S :: S -> Int
    
    13
    +use_S (S xs) = case xs of { (y:_) -> y; [] -> -1 }
    
    14
    +{-# NOINLINE use_S #-}
    
    15
    +
    
    16
    +data M = M !(Maybe Int)
    
    17
    +
    
    18
    +use_M :: M -> Int
    
    19
    +use_M (M m) = case m of { Just x -> x; Nothing -> -1 }
    
    20
    +{-# NOINLINE use_M #-}
    
    21
    +
    
    22
    +data N = N !Ordering
    
    23
    +
    
    24
    +use_N :: N -> Int
    
    25
    +use_N (N o) = case o of { LT -> 1; EQ -> 2; GT -> 3 }
    
    26
    +{-# NOINLINE use_N #-}

  • testsuite/tests/bytecode/all.T
    ... ... @@ -17,3 +17,12 @@ test('T26216', extra_files(["T26216_aux.hs"]), ghci_script, ['T26216.script'])
    17 17
     test('T27001', [extra_files(['T27001.hs']), req_interp],
    
    18 18
          run_command,
    
    19 19
          ['{compiler} -e main -O -fno-unoptimized-core-for-interpreter T27001.hs'])
    
    20
    +
    
    21
    +# Bytecode must not trust tag information from imported interfaces, which
    
    22
    +# comes from native code (#27842): neither the TagSig of an imported function
    
    23
    +# nor the LFInfo of an imported value (with and without constructor fields).
    
    24
    +test('T27842', [extra_files(['T27842A.hs', 'T27842N.hs']), req_interp],
    
    25
    +     run_command,
    
    26
    +     ['{compiler} -e main -O -fno-unoptimized-core-for-interpreter '
    
    27
    +      '-fobject-code -fbyte-code-and-object-code -fwrite-byte-code -fprefer-byte-code '
    
    28
    +      'T27842.hs'])

  • testsuite/tests/simplStg/should_compile/all.T
    ... ... @@ -25,5 +25,17 @@ test('inferTags003', [ only_ways(['optasm']),
    25 25
                          ], compile, ['-ddump-cmm -dno-typeable-binds -O'])
    
    26 26
     test('inferTags004', normal, compile, ['-O -ddump-stg-tags -dno-typeable-binds -dsuppress-uniques'])
    
    27 27
     test('T27005b', normal, compile, ['-O -ddump-stg-tags -dno-typeable-binds -dsuppress-uniques'])
    
    28
    +# Both f and the join point j should have a signature TagTuple[TagEPT, TagEPT]
    
    29
    +test('inferTags005',
    
    30
    +     grep_errmsg(r'(TagTuple\[TagEPT, TagEPT\])', [1]),
    
    31
    +     compile,
    
    32
    +     ['-O -fno-worker-wrapper -fno-cpr-anal -ddump-stg-tags '
    
    33
    +      '-dno-typeable-binds -dsuppress-uniques'])
    
    34
    +# The function needs to have a signature TagTuple[TagEPT, TagEPT]
    
    35
    +test('inferTags006',
    
    36
    +     grep_errmsg(r'(TagTuple\[TagEPT, TagEPT\])', [1]),
    
    37
    +     compile,
    
    38
    +     ['-O -fno-worker-wrapper -fno-cpr-anal -fno-full-laziness -ddump-stg-tags '
    
    39
    +      '-dno-typeable-binds -dsuppress-uniques'])
    
    28 40
     
    
    29 41
     test('T24806', grep_errmsg('^\\(T24806\\.\\$wgo'), compile, ['-O -ddump-stg-tags -dno-typeable-binds -dsuppress-uniques'])

  • testsuite/tests/simplStg/should_compile/inferTags005.hs
    1
    +{-# LANGUAGE UnboxedTuples #-}
    
    2
    +module EnforceEpt005 where
    
    3
    +
    
    4
    +-- If we have
    
    5
    +-- f x y =
    
    6
    +--   join j =
    
    7
    +--       <rhs> -- <TagTuple[TagEPT, TagEPT]]>
    
    8
    +--   in case x of
    
    9
    +--       1 -> j
    
    10
    +--       2 -> j
    
    11
    +--       _ ->
    
    12
    +--         <something> -- <TagTuple[TagEPT, TagEPT]]>
    
    13
    +
    
    14
    +-- Consider the case where rhs and something both represent a tag value of <TagTuple[TagEPT, TagEPT]]>
    
    15
    +-- In this case we want `f` to also give rise to TagFun [TagTuple[TagEPT, TagEPT]]]
    
    16
    +
    
    17
    +-- To achieve this we have to recognize j as a zero-arity function equivalent
    
    18
    +-- which is what this test checks.
    
    19
    +
    
    20
    +-- The payoff is in `g` `r` is then known to be tagged, so `S r` needs no eval.
    
    21
    +-- Only an unboxed tuple result exposes this.
    
    22
    +
    
    23
    +-- We disable worker/wrapper and CPR so `f` matches more closely the tests source.
    
    24
    +
    
    25
    +data T = A Int Int Int Int Int Int | B
    
    26
    +
    
    27
    +f :: Int -> Int -> (# T, T #)
    
    28
    +f x y = case x of
    
    29
    +  0 -> j
    
    30
    +  1 -> j
    
    31
    +  _ -> (# B, B #)
    
    32
    +  where
    
    33
    +    j = (# A (y+1) (y*2) (y-3) (y*y) (y+x) (y*x), B #)
    
    34
    +{-# NOINLINE f #-}
    
    35
    +
    
    36
    +data S = S !T
    
    37
    +
    
    38
    +g :: Int -> Int -> S
    
    39
    +g x y = case f x y of (# r, _ #) -> S r

  • testsuite/tests/simplStg/should_compile/inferTags005.stderr
    1
    +TagTuple[TagEPT, TagEPT]
    
    2
    +TagTuple[TagEPT, TagEPT]

  • testsuite/tests/simplStg/should_compile/inferTags006.hs
    1
    +{-# LANGUAGE UnboxedTuples #-}
    
    2
    +module EnforceEpt006 where
    
    3
    +
    
    4
    +-- If we have
    
    5
    +-- f x y =
    
    6
    +--   join j =
    
    7
    +--       <rhs> -- TagBottoming
    
    8
    +--   in case x of
    
    9
    +--       1 -> j
    
    10
    +--       2 -> j
    
    11
    +--       _ ->
    
    12
    +--         <something> -- <TagTuple[TagEPT, TagEPT]]>
    
    13
    +
    
    14
    +-- Similar to inferTage005 we want to give `f` the signature: TagFun <TagTuple[TagEPT, TagEPT]]>
    
    15
    +-- This requires us to give `j` a `TagFun[TagBottoming]` signature, which is safe because
    
    16
    +-- either its execution will diverge, or the resulting tuple *will* have two
    
    17
    +-- tagged values.
    
    18
    +
    
    19
    +data T = A Int Int Int Int Int Int | B
    
    20
    +
    
    21
    +f :: Int -> Int -> (# T, T #)
    
    22
    +-- This case-of-case is just here to be optimized into a nullary join point.
    
    23
    +f x y = case (case x of { 0 -> False; 1 -> False; _ -> True }) of
    
    24
    +  False -> error ("f: bad input " ++ show x ++ " " ++ show y ++ " " ++ show (x*y))
    
    25
    +  True  -> (# A (y+1) (y*2) (y-3) (y*y) (y+x) (y*x), B #)
    
    26
    +{-# NOINLINE f #-}
    
    27
    +
    
    28
    +data S = S !T
    
    29
    +
    
    30
    +g :: Int -> Int -> S
    
    31
    +g x y = case f x y of (# r, _ #) -> S r

  • testsuite/tests/simplStg/should_compile/inferTags006.stderr
    1
    +TagTuple[TagEPT, TagEPT]

  • testsuite/tests/simplStg/should_run/InferTags007_A.hs
    1
    +-- We suppress arity and LfInfo with the option.
    
    2
    +{-# OPTIONS_GHC -fomit-interface-pragmas #-}
    
    3
    +module InferTags007_A (fun, caf, bot, con) where
    
    4
    +
    
    5
    +fun :: Int -> Int
    
    6
    +fun x = x + 1
    
    7
    +{-# NOINLINE fun #-}
    
    8
    +
    
    9
    +-- A CAF/thunk
    
    10
    +caf :: [Int]
    
    11
    +caf = map (* 2) [1 .. 10]
    
    12
    +{-# NOINLINE caf #-}
    
    13
    +
    
    14
    +bot :: Int -> Int
    
    15
    +bot _ = error "bot called"
    
    16
    +{-# NOINLINE bot #-}
    
    17
    +
    
    18
    +con :: Maybe Int
    
    19
    +con = Just 42
    
    20
    +{-# NOINLINE con #-}

  • testsuite/tests/simplStg/should_run/T27831.hs
    1
    +-- Regression test for #27831: a bottoming function stored in a strict
    
    2
    +-- constructor field.
    
    3
    +--
    
    4
    +-- Different parts of the tag inference machinery didn't agree on weither or not
    
    5
    +-- a bottoming function should be treated as a EPT value, leading to a segfault.
    
    6
    +module Main (main) where
    
    7
    +
    
    8
    +import Control.Exception
    
    9
    +import GHC.Exts.Heap (GenClosure(..), getClosureData)
    
    10
    +import System.Exit
    
    11
    +
    
    12
    +data T = MkT Int !(Int -> Int)
    
    13
    +
    
    14
    +t :: T
    
    15
    +t = MkT 1 (\_ -> error "T27831")
    
    16
    +{-# NOINLINE t #-}
    
    17
    +
    
    18
    +f :: () -> T
    
    19
    +f _ = t
    
    20
    +{-# OPAQUE f #-}
    
    21
    +
    
    22
    +main :: IO ()
    
    23
    +main = do
    
    24
    +    c <- getClosureData t
    
    25
    +    case c of
    
    26
    +        ThunkClosure{} -> putStrLn "FAIL: ThunkClosure" >> exitFailure
    
    27
    +        APClosure{}    -> putStrLn "FAIL: APClosure" >> exitFailure
    
    28
    +        _              -> putStrLn "OK: not a thunk"
    
    29
    +
    
    30
    +    case f () of MkT n _ -> print n
    
    31
    +
    
    32
    +    r <- try (evaluate (case t of MkT _ g -> g 1))
    
    33
    +    case r of
    
    34
    +        Left (ErrorCall msg) -> putStrLn ("OK: " ++ msg)
    
    35
    +        Right v              -> putStrLn ("FAIL: got " ++ show v) >> exitFailure

  • testsuite/tests/simplStg/should_run/T27831.stdout
    1
    +OK: not a thunk
    
    2
    +1
    
    3
    +OK: T27831

  • testsuite/tests/simplStg/should_run/T27857.hs
    1
    +{-# LANGUAGE StrictData #-}
    
    2
    +-- Regression test from #27857: We store a function that loops indefinitely
    
    3
    +-- in a strict field.
    
    4
    +module Main (main) where
    
    5
    +import Control.Monad
    
    6
    +
    
    7
    +data Body
    
    8
    +    = BNone
    
    9
    +    | BStream ((Int -> IO ()) -> IO () -> IO ())
    
    10
    +    | BIface (Int -> IO ())
    
    11
    +    | BBuilder String
    
    12
    +    | BFile Int
    
    13
    +
    
    14
    +data Obj = Obj [String] Body Int
    
    15
    +
    
    16
    +newtype Resp = Resp Obj
    
    17
    +
    
    18
    +stream :: Int -> [String] -> ((Int -> IO ()) -> IO () -> IO ()) -> Resp
    
    19
    +stream st hdr b = Resp $ Obj (show st : hdr) (BStream b) 0
    
    20
    +
    
    21
    +noBody :: Int -> [String] -> Resp
    
    22
    +noBody st hdr = Resp $ Obj (show st : hdr) BNone 0
    
    23
    +
    
    24
    +infinite :: Resp
    
    25
    +infinite = stream 200 ["text/plain"] body
    
    26
    +  where
    
    27
    +    body :: (Int -> IO ()) -> IO () -> IO ()
    
    28
    +    body write flush = do
    
    29
    +        let go n = write n *> flush *> go (succ n)
    
    30
    +        go (0 :: Int)
    
    31
    +
    
    32
    +other :: Resp
    
    33
    +other = noBody 404 []
    
    34
    +
    
    35
    +name :: Body -> String
    
    36
    +name b = case b of
    
    37
    +    BNone -> "None"
    
    38
    +    BStream _ -> "Stream"
    
    39
    +    BIface _ -> "Iface"
    
    40
    +    BBuilder _ -> "Builder"
    
    41
    +    BFile _ -> "File"
    
    42
    +
    
    43
    +main :: IO ()
    
    44
    +main = forM_ [("infinite", infinite), ("other", other)] $ \(n, Resp (Obj _ b _)) ->
    
    45
    +    putStrLn $ n ++ "=" ++ name b

  • testsuite/tests/simplStg/should_run/T27857.stdout
    1
    +infinite=Stream
    
    2
    +other=None

  • testsuite/tests/simplStg/should_run/all.T
    ... ... @@ -3,13 +3,15 @@
    3 3
     #	extra run flags
    
    4 4
     #	expected process return value, if not zero
    
    5 5
     
    
    6
    -# Only compile with optimisation
    
    6
    +# Only run optimised code, native and interpreted:
    
    7
    +# We now generate bytecode from STG so we should test native and bytecode.
    
    7 8
     def f( name, opts ):
    
    8
    -  opts.only_ways = ['optasm']
    
    9
    +  opts.only_ways = ['optasm', 'ghci-opt']
    
    9 10
     
    
    10 11
     setTestOpts(f)
    
    11 12
     
    
    12
    -test('T9291', normal, compile_and_run, [''])
    
    13
    +# Breakpoint ticks would break CSE which this tests.
    
    14
    +test('T9291', [omit_ways(['ghci']), extra_hc_opts('-fno-break-points')], compile_and_run, [''])
    
    13 15
     test('T13536', normal, compile_and_run, [''])
    
    14 16
     
    
    15 17
     test('T13536a',
    
    ... ... @@ -20,6 +22,16 @@ test('T13536a',
    20 22
     
    
    21 23
     test('inferTags001', normal, multimod_compile_and_run, ['inferTags001', 'inferTags001_a'])
    
    22 24
     test('T22042', [extra_files(['T22042a.hs']),only_ways('normal'),unless(have_dynamic(), skip)], makefile_test, ['T22042'])
    
    23
    -test('T23783', normal, multimod_compile_and_run, ['T23783', '-O -v0'])
    
    24
    -test('unpack_enum', normal, compile_and_run, [''])
    
    25
    -test('T27005a', normal, compile_and_run, [''])
    25
    +
    
    26
    +# Depends on tag inference being similarly effective for bytecode which it is not yet.
    
    27
    +test('T23783', omit_ghci, multimod_compile_and_run, ['T23783', '-O -v0'])
    
    28
    +
    
    29
    +# Requires UNPACK which needs -O
    
    30
    +test('unpack_enum', omit_ways(['ghci']), compile_and_run, [''])
    
    31
    +
    
    32
    +# Tests that tag inference optimizes well, which is not the case for bytecode or
    
    33
    +# when we insert profiling ticks.
    
    34
    +test('T27005a', omit_ways(ghci_ways + prof_ways), compile_and_run, [''])
    
    35
    +test('T27831', omit_ways(ghci_ways + prof_ways), compile_and_run, [''])
    
    36
    +test('inferTags007', [extra_files(['InferTags007_A.hs'])], multimod_compile_and_run, ['inferTags007', '-O -v0'])
    
    37
    +test('T27857', normal, compile_and_run, [''])

  • testsuite/tests/simplStg/should_run/inferTags007.hs
    1
    +{-# LANGUAGE MagicHash #-}
    
    2
    +-- This test sanity checks the tag info we generate for imported binders which
    
    3
    +-- have no real info in the .hi file.
    
    4
    +--
    
    5
    +-- This is important to avoid running into cases where we optimistically
    
    6
    +-- generated better information based on things like type than the
    
    7
    +-- compiled code actually provided. For example GHCi doesn't ensure tagging
    
    8
    +-- of constructors.
    
    9
    +module Main (main) where
    
    10
    +
    
    11
    +import Control.Exception
    
    12
    +import Control.Monad
    
    13
    +import GHC.Exts (Addr#)
    
    14
    +import GHC.Exts.Heap (GenClosure(..), getClosureData)
    
    15
    +import GHC.Internal.Control.Exception.Base (patError)
    
    16
    +import System.Exit
    
    17
    +
    
    18
    +import qualified InferTags007_A as A
    
    19
    +
    
    20
    +data S a = S !a
    
    21
    +
    
    22
    +data P = MkP !(Addr# -> Int)
    
    23
    +
    
    24
    +-- Put the imported values into a strict field of a top level Con
    
    25
    +con_top_fun :: S (Int -> Int)
    
    26
    +con_top_fun = S A.fun
    
    27
    +{-# NOINLINE con_top_fun #-}
    
    28
    +
    
    29
    +con_top_caf :: S [Int]
    
    30
    +con_top_caf = S A.caf
    
    31
    +{-# NOINLINE con_top_caf #-}
    
    32
    +
    
    33
    +con_top_bot :: S (Int -> Int)
    
    34
    +con_top_bot = S A.bot
    
    35
    +{-# NOINLINE con_top_bot #-}
    
    36
    +
    
    37
    +con_top_con :: S (Maybe Int)
    
    38
    +con_top_con = S A.con
    
    39
    +{-# NOINLINE con_top_con #-}
    
    40
    +
    
    41
    +con_top_pat :: P
    
    42
    +con_top_pat = MkP patError
    
    43
    +{-# NOINLINE con_top_pat #-}
    
    44
    +
    
    45
    +-- Put the imported values into a strict field of a Con inside a rhs
    
    46
    +expr_fun :: () -> S (Int -> Int)
    
    47
    +expr_fun _ = S A.fun
    
    48
    +{-# OPAQUE expr_fun #-}
    
    49
    +
    
    50
    +expr_caf :: () -> S [Int]
    
    51
    +expr_caf _ = S A.caf
    
    52
    +{-# OPAQUE expr_caf #-}
    
    53
    +
    
    54
    +expr_bot :: () -> S (Int -> Int)
    
    55
    +expr_bot _ = S A.bot
    
    56
    +{-# OPAQUE expr_bot #-}
    
    57
    +
    
    58
    +expr_con :: () -> S (Maybe Int)
    
    59
    +expr_con _ = S A.con
    
    60
    +{-# OPAQUE expr_con #-}
    
    61
    +
    
    62
    +-- Actually use the field.
    
    63
    +use_S_head :: S [Int] -> Int
    
    64
    +use_S_head (S xs) = case xs of { (y:_) -> y; [] -> -1 }
    
    65
    +{-# NOINLINE use_S_head #-}
    
    66
    +
    
    67
    +use_S_fromJust :: S (Maybe Int) -> Int
    
    68
    +use_S_fromJust (S m) = case m of { Just x -> x; Nothing -> -1 }
    
    69
    +{-# NOINLINE use_S_fromJust #-}
    
    70
    +
    
    71
    +use_S_apply :: S (Int -> Int) -> Int
    
    72
    +use_S_apply (S f) = f 1
    
    73
    +{-# NOINLINE use_S_apply #-}
    
    74
    +
    
    75
    +checkNotThunk :: String -> a -> IO ()
    
    76
    +checkNotThunk name x = do
    
    77
    +    c <- getClosureData x
    
    78
    +    case c of
    
    79
    +        ThunkClosure{} -> putStrLn ("FAIL: " ++ name ++ " is a thunk") >> exitFailure
    
    80
    +        APClosure{}    -> putStrLn ("FAIL: " ++ name ++ " is an AP") >> exitFailure
    
    81
    +        _              -> pure ()
    
    82
    +
    
    83
    +-- Pattern matching binds the field itself, so no selector thunk is involved.
    
    84
    +checkField :: String -> S a -> IO ()
    
    85
    +checkField name (S x) = checkNotThunk name x
    
    86
    +{-# NOINLINE checkField #-}
    
    87
    +
    
    88
    +main :: IO ()
    
    89
    +main = do
    
    90
    +    -- Check if the pointer in the strict field is a thunk.
    
    91
    +    checkField "con_top_caf" con_top_caf
    
    92
    +    checkField "con_top_fun" con_top_fun
    
    93
    +    checkField "con_top_bot" con_top_bot
    
    94
    +    checkField "con_top_con" con_top_con
    
    95
    +    case con_top_pat of MkP f -> checkNotThunk "con_top_pat" f
    
    96
    +    checkField "expr_caf" (expr_caf ())
    
    97
    +    checkField "expr_fun" (expr_fun ())
    
    98
    +    checkField "expr_bot" (expr_bot ())
    
    99
    +    checkField "expr_con" (expr_con ())
    
    100
    +
    
    101
    +    -- Actually execute a case on the contents of the field.
    
    102
    +    print (use_S_head con_top_caf, use_S_head (expr_caf ()))
    
    103
    +    print (use_S_fromJust con_top_con, use_S_fromJust (expr_con ()))
    
    104
    +    print (use_S_apply con_top_fun, use_S_apply (expr_fun ()))
    
    105
    +    forM_ [con_top_bot, expr_bot ()] $ \s -> do
    
    106
    +        r <- try (evaluate (use_S_apply s))
    
    107
    +        case r of
    
    108
    +            Left (ErrorCall msg) -> putStrLn ("OK: " ++ msg)
    
    109
    +            Right v              -> putStrLn ("FAIL: got " ++ show v) >> exitFailure

  • testsuite/tests/simplStg/should_run/inferTags007.stdout
    1
    +(2,2)
    
    2
    +(42,42)
    
    3
    +(2,2)
    
    4
    +OK: bot called
    
    5
    +OK: bot called