Simon Jakobi pushed to branch wip/sjakobi/T27507 at Glasgow Haskell Compiler / GHC

Commits:

6 changed files:

Changes:

  • compiler/GHC/Stg/EnforceEpt.hs
    ... ... @@ -452,65 +452,12 @@ inferTagExpr _ (StgOpApp op args ty)
    452 452
     inferTagExpr env (StgLet ext bind body)
    
    453 453
       = (info, StgLet ext bind' body')
    
    454 454
       where
    
    455
    -    (env', bind') = inferTagBind env bind
    
    456
    -    (info, body') = inferTagExpr env' body
    
    455
    +    (info, bind', body') = inferTagLet env (localFunsOfBind env False bind) bind body
    
    457 456
     
    
    458 457
     inferTagExpr env (StgLetNoEscape ext bind body)
    
    459
    -  | all (isJust . lookupJoinArgInfo env . fst) root_joins
    
    460
    -  = (plain_info, StgLetNoEscape ext plain_bind plain_body)
    
    461
    -  | otherwise
    
    462
    -  = go initial_arg_infos
    
    458
    +  = (info, StgLetNoEscape ext bind' body')
    
    463 459
       where
    
    464
    -    (plain_body_env, plain_bind) = inferTagBind env bind
    
    465
    -    (plain_info, plain_body) = inferTagExpr plain_body_env body
    
    466
    -
    
    467
    -    joins = root_joins ++ joinsInBind bind ++ joinsInExpr body
    
    468
    -    join_ids = map fst joins
    
    469
    -    initial_arg_infos = [replicate arity TagEPT | (_, arity) <- joins]
    
    470
    -
    
    471
    -    go arg_infos
    
    472
    -      | arg_infos == new_arg_infos
    
    473
    -      = (info, StgLetNoEscape ext bind' body')
    
    474
    -      | otherwise
    
    475
    -      = go new_arg_infos
    
    476
    -      where
    
    477
    -        join_env = extendJoinArgEnv env (zipEqual join_ids arg_infos)
    
    478
    -        (body_env, bind') = inferTagBind join_env bind
    
    479
    -        (info, body') = inferTagExpr body_env body
    
    480
    -        calls = collectJoinCalls (makeTagged join_env) join_ids bind' body'
    
    481
    -        new_arg_infos =
    
    482
    -          [ maybe initial_infos (combineArgs initial_infos) (lookupVarEnv calls join_id)
    
    483
    -          | (join_id, initial_infos) <- zipEqual join_ids initial_arg_infos ]
    
    484
    -
    
    485
    -    combineArgs = zipWithEqual combineAltInfo
    
    486
    -
    
    487
    -    root_joins = joinsOf bind
    
    488
    -
    
    489
    -    joinsOf (StgNonRec bndr rhs) = [joinOf bndr rhs]
    
    490
    -    joinsOf (StgRec pairs) = [joinOf bndr rhs | (bndr, rhs) <- pairs]
    
    491
    -
    
    492
    -    joinOf bndr (StgRhsClosure _ _ _ bndrs _ _)
    
    493
    -      = (getBinderId env bndr, length bndrs)
    
    494
    -    joinOf bndr (StgRhsCon {})
    
    495
    -      = (getBinderId env bndr, idArity (getBinderId env bndr))
    
    496
    -
    
    497
    -    joinsInBind (StgNonRec _ rhs) = joinsInRhs rhs
    
    498
    -    joinsInBind (StgRec pairs) = concatMap (joinsInRhs . snd) pairs
    
    499
    -
    
    500
    -    joinsInRhs (StgRhsClosure _ _ _ _ rhs _) = joinsInExpr rhs
    
    501
    -    joinsInRhs (StgRhsCon {}) = []
    
    502
    -
    
    503
    -    joinsInExpr (StgApp {}) = []
    
    504
    -    joinsInExpr (StgConApp {}) = []
    
    505
    -    joinsInExpr (StgLit {}) = []
    
    506
    -    joinsInExpr (StgTick _ expr) = joinsInExpr expr
    
    507
    -    joinsInExpr (StgOpApp {}) = []
    
    508
    -    joinsInExpr (StgLet _ let_bind expr)
    
    509
    -      = joinsInBind let_bind ++ joinsInExpr expr
    
    510
    -    joinsInExpr (StgLetNoEscape _ let_bind expr)
    
    511
    -      = joinsOf let_bind ++ joinsInBind let_bind ++ joinsInExpr expr
    
    512
    -    joinsInExpr (StgCase scrut _ _ alts)
    
    513
    -      = joinsInExpr scrut ++ concatMap (joinsInExpr . alt_rhs) alts
    
    460
    +    (info, bind', body') = inferTagLet env (localFunsOfBind env True bind) bind body
    
    514 461
     
    
    515 462
     inferTagExpr in_env (StgCase scrut bndr ty alts)
    
    516 463
       -- Unboxed tuples get their info from the expression we scrutinise if any
    
    ... ... @@ -564,28 +511,108 @@ inferTagExpr in_env (StgCase scrut bndr ty alts)
    564 511
         (scrut_info, scrut') = inferTagExpr in_env scrut
    
    565 512
         bndr' = (getBinderId in_env bndr, TagVal TagEPT)
    
    566 513
     
    
    567
    --- See Note [EPT signatures for join point arguments].
    
    568
    -collectJoinCalls
    
    514
    +localFunsOfBind
    
    515
    +  :: TagEnv p -> Bool -> GenStgBinding p -> [(Id, Int)]
    
    516
    +localFunsOfBind env is_join (StgNonRec bndr rhs)
    
    517
    +  = localFunOf env is_join bndr rhs
    
    518
    +localFunsOfBind env is_join (StgRec pairs)
    
    519
    +  = concatMap (uncurry (localFunOf env is_join)) pairs
    
    520
    +
    
    521
    +localFunOf
    
    522
    +  :: TagEnv p -> Bool -> BinderP p -> GenStgRhs p -> [(Id, Int)]
    
    523
    +localFunOf env is_join bndr (StgRhsClosure _ _ _ bndrs _ _)
    
    524
    +  | is_join || notNull bndrs
    
    525
    +  = [(getBinderId env bndr, length bndrs)]
    
    526
    +localFunOf _ _ _ (StgRhsClosure {}) = []
    
    527
    +localFunOf env is_join bndr (StgRhsCon {})
    
    528
    +  | is_join
    
    529
    +  = [(getBinderId env bndr, idArity (getBinderId env bndr))]
    
    530
    +localFunOf _ _ _ (StgRhsCon {}) = []
    
    531
    +
    
    532
    +-- See Note [EPT signatures for local function arguments].
    
    533
    +inferTagLet
    
    534
    +  :: forall p. (OutputableInferPass p, InferExtEq p)
    
    535
    +  => TagEnv p
    
    536
    +  -> [(Id, Int)]
    
    537
    +  -> GenStgBinding p
    
    538
    +  -> GenStgExpr p
    
    539
    +  -> (TagInfo, InferStgBinding, InferStgExpr)
    
    540
    +inferTagLet env root_funs bind body
    
    541
    +  | null root_funs
    
    542
    +  = (plain_info, plain_bind, plain_body)
    
    543
    +  | all (isJust . lookupFunArgInfo env . fst) root_funs
    
    544
    +  = (plain_info, plain_bind, plain_body)
    
    545
    +  | otherwise
    
    546
    +  = go initial_arg_infos
    
    547
    +  where
    
    548
    +    (plain_body_env, plain_bind) = inferTagBind env bind
    
    549
    +    (plain_info, plain_body) = inferTagExpr plain_body_env body
    
    550
    +
    
    551
    +    funs = root_funs ++ funsInBind bind ++ funsInExpr body
    
    552
    +    fun_ids = map fst funs
    
    553
    +    initial_arg_infos = [replicate arity TagEPT | (_, arity) <- funs]
    
    554
    +
    
    555
    +    go arg_infos
    
    556
    +      | arg_infos == new_arg_infos
    
    557
    +      = (info, bind', body')
    
    558
    +      | otherwise
    
    559
    +      = go new_arg_infos
    
    560
    +      where
    
    561
    +        fun_env = extendFunArgEnv env (zipEqual fun_ids arg_infos)
    
    562
    +        (body_env, bind') = inferTagBind fun_env bind
    
    563
    +        (info, body') = inferTagExpr body_env body
    
    564
    +        calls = collectFunCalls (makeTagged fun_env) funs bind' body'
    
    565
    +        new_arg_infos =
    
    566
    +          [ maybe initial_infos (combineArgs initial_infos) (lookupVarEnv calls fun_id)
    
    567
    +          | (fun_id, initial_infos) <- zipEqual fun_ids initial_arg_infos ]
    
    568
    +
    
    569
    +    combineArgs = zipWithEqual combineAltInfo
    
    570
    +
    
    571
    +    funsInBind (StgNonRec _ rhs) = funsInRhs rhs
    
    572
    +    funsInBind (StgRec pairs) = concatMap (funsInRhs . snd) pairs
    
    573
    +
    
    574
    +    funsInRhs (StgRhsClosure _ _ _ _ rhs _) = funsInExpr rhs
    
    575
    +    funsInRhs (StgRhsCon {}) = []
    
    576
    +
    
    577
    +    funsInExpr (StgApp {}) = []
    
    578
    +    funsInExpr (StgConApp {}) = []
    
    579
    +    funsInExpr (StgLit {}) = []
    
    580
    +    funsInExpr (StgTick _ expr) = funsInExpr expr
    
    581
    +    funsInExpr (StgOpApp {}) = []
    
    582
    +    funsInExpr (StgLet _ let_bind expr)
    
    583
    +      = localFunsOfBind env False let_bind ++ funsInBind let_bind ++ funsInExpr expr
    
    584
    +    funsInExpr (StgLetNoEscape _ let_bind expr)
    
    585
    +      = localFunsOfBind env True let_bind ++ funsInBind let_bind ++ funsInExpr expr
    
    586
    +    funsInExpr (StgCase scrut _ _ alts)
    
    587
    +      = funsInExpr scrut ++ concatMap (funsInExpr . alt_rhs) alts
    
    588
    +
    
    589
    +-- See Note [EPT signatures for local function arguments].
    
    590
    +collectFunCalls
    
    569 591
       :: TagEnv 'InferTaggedBinders
    
    570
    -  -> [Id]
    
    592
    +  -> [(Id, Int)]
    
    571 593
       -> InferStgBinding
    
    572 594
       -> InferStgExpr
    
    573 595
       -> IdEnv [TagInfo]
    
    574
    -collectJoinCalls in_env join_ids bind body
    
    596
    +collectFunCalls in_env funs bind body
    
    575 597
       = bind_calls `plusCalls` collectExpr body_env body
    
    576 598
       where
    
    577
    -    join_env = mkVarEnv [(join_id, ()) | join_id <- join_ids]
    
    599
    +    fun_env = mkVarEnv funs
    
    578 600
         (bind_calls, body_env) = collectBind in_env bind
    
    579 601
     
    
    580 602
         collectExpr env (StgApp fun args)
    
    581
    -      | elemVarEnv fun join_env
    
    582
    -      = unitVarEnv fun (map (lookupInfo env) args)
    
    583
    -      | otherwise
    
    584
    -      = emptyVarEnv
    
    585
    -    collectExpr _ (StgConApp {}) = emptyVarEnv
    
    603
    +      = direct_call `plusCalls` collectArgs args
    
    604
    +      where
    
    605
    +        direct_call
    
    606
    +          | Just arity <- lookupVarEnv fun_env fun
    
    607
    +          = unitVarEnv fun
    
    608
    +              (take arity (map (lookupInfo env) args) ++
    
    609
    +               replicate (arity - length args) TagDunno)
    
    610
    +          | otherwise
    
    611
    +          = emptyVarEnv
    
    612
    +    collectExpr _ (StgConApp _ _ args _) = collectArgs args
    
    586 613
         collectExpr _ (StgLit {}) = emptyVarEnv
    
    587 614
         collectExpr env (StgTick _ expr) = collectExpr env expr
    
    588
    -    collectExpr _ (StgOpApp {}) = emptyVarEnv
    
    615
    +    collectExpr _ (StgOpApp _ args _) = collectArgs args
    
    589 616
         collectExpr env (StgLet _ let_bind expr)
    
    590 617
           = let_calls `plusCalls` collectExpr let_env expr
    
    591 618
           where
    
    ... ... @@ -610,39 +637,57 @@ collectJoinCalls in_env join_ids bind body
    610 637
     
    
    611 638
         collectRhs env (StgRhsClosure _ _ _ bndrs rhs _)
    
    612 639
           = collectExpr (extendSigEnv env bndrs) rhs
    
    613
    -    collectRhs _ (StgRhsCon {}) = emptyVarEnv
    
    640
    +    collectRhs _ (StgRhsCon _ _ _ _ args _) = collectArgs args
    
    641
    +
    
    642
    +    collectArgs = plusCallList . map collectArg
    
    643
    +
    
    644
    +    collectArg (StgVarArg var)
    
    645
    +      | Just arity <- lookupVarEnv fun_env var
    
    646
    +      = unitVarEnv var (replicate arity TagDunno)
    
    647
    +    collectArg _ = emptyVarEnv
    
    614 648
     
    
    615 649
         plusCalls = plusVarEnv_C (zipWithEqual combineAltInfo)
    
    616 650
         plusCallList = foldr plusCalls emptyVarEnv
    
    617 651
     
    
    618
    -{- Note [EPT signatures for join point arguments]
    
    619
    -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    620
    -Join points are local, non-escaping functions, and every occurrence is a
    
    621
    -saturated tail call.  Consequently we can infer EPT information for their
    
    622
    -arguments by treating them like SSA block parameters: an argument is EPT when
    
    623
    -the corresponding actual argument is EPT at every jump to the join point.
    
    624
    -
    
    625
    -At each outermost StgLetNoEscape we optimistically start every argument of that
    
    626
    -join group and all nested join groups at TagEPT, infer tags for the binding and
    
    627
    -its body, and collect the argument tags at all calls to those join points.
    
    652
    +{- Note [EPT signatures for local function arguments]
    
    653
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    654
    +For local functions we can inspect every use and infer that a parameter is EPT
    
    655
    +when the corresponding actual argument is EPT at every call.  Join points are
    
    656
    +the particularly simple case: they never escape and every occurrence is a
    
    657
    +saturated tail call, so their parameters behave like SSA block parameters.
    
    658
    +
    
    659
    +Ordinary let-bound functions can escape or be applied to too few arguments.
    
    660
    +We therefore track their parameters independently.  A direct call contributes
    
    661
    +the tag of each supplied argument; an undersaturated call contributes TagDunno
    
    662
    +for the missing suffix.  If the function occurs as a value (for example as an
    
    663
    +argument or constructor field), every parameter becomes TagDunno because a
    
    664
    +later call is outside the analysis.  Arguments beyond the function's arity do
    
    665
    +not describe its parameters and are ignored.  We still inspect every argument
    
    666
    +for escaping occurrences of other tracked functions.
    
    667
    +
    
    668
    +At each outermost local function binding we optimistically start every argument
    
    669
    +of that function group and all nested function and join groups at TagEPT, infer
    
    670
    +tags for the binding and its body, and collect the argument tags at all uses.
    
    628 671
     Combining those call-site tags gives the next, no more optimistic approximation
    
    629
    -for the join arguments.  Iterating reaches the greatest fixed point, including
    
    630
    -for recursive and mutually recursive joins.  Using the greatest fixed point is
    
    631
    -important for recursive calls such as @jump j x@, where @x@ is itself a
    
    632
    -parameter of @j@: such a back edge preserves EPTness rather than providing an
    
    633
    -independent reason to reject it.
    
    634
    -
    
    635
    -Solving all nested join groups simultaneously is important for compile-time
    
    636
    -performance.  Independently solving a nested join group on every iteration of
    
    637
    -each enclosing group causes exponential re-analysis in deeply nested code.
    
    638
    -Nested StgLetNoEscapes therefore merely use the argument information installed
    
    639
    -by the outer solver.
    
    672
    +for the function arguments.  Iterating reaches the greatest fixed point,
    
    673
    +including for recursive and mutually recursive functions.  Using the greatest
    
    674
    +fixed point is important for recursive calls such as @jump j x@, where @x@ is
    
    675
    +itself a parameter of @j@: such a back edge preserves EPTness rather than
    
    676
    +providing an independent reason to reject it.
    
    677
    +
    
    678
    +Solving all nested function and join groups simultaneously is important for
    
    679
    +compile-time performance.  Independently solving a nested group on every
    
    680
    +iteration of each enclosing group causes exponential re-analysis in deeply
    
    681
    +nested code.
    
    682
    +Nested bindings therefore merely use the argument information installed by the
    
    683
    +outer solver.  A dead local function has no call-site evidence against the
    
    684
    +optimistic approximation, which is sound because it cannot be entered.
    
    640 685
     
    
    641 686
     This is tag inference, not strictness inference.  It does not evaluate an
    
    642 687
     argument or change the calling convention.  It merely records that every path
    
    643
    -which enters the join point already supplies an EPT value.  Existing CBV marks
    
    644
    -remain authoritative in inferTagRhs, since the rewriter enforces their EPT
    
    645
    -precondition at call sites.
    
    688
    +which enters the local function already supplies an EPT value.  Existing CBV
    
    689
    +marks remain authoritative in inferTagRhs, since the rewriter enforces their
    
    690
    +EPT precondition at call sites.
    
    646 691
     -}
    
    647 692
     
    
    648 693
     -- Compute binder sigs based on the constructors strict fields.
    
    ... ... @@ -787,7 +832,7 @@ inferTagRhs bnd_id in_env (StgRhsClosure ext cc upd bndrs body typ)
    787 832
           = repeat NotMarkedCbv
    
    788 833
     
    
    789 834
         inferred_arg_infos =
    
    790
    -      fromMaybe (repeat TagDunno) (lookupJoinArgInfo in_env bnd_id)
    
    835
    +      fromMaybe (repeat TagDunno) (lookupFunArgInfo in_env bnd_id)
    
    791 836
     
    
    792 837
         env' = extendSigEnv in_env out_bndrs
    
    793 838
         (info, body') = inferTagExpr env' body
    

  • compiler/GHC/Stg/EnforceEpt/Types.hs
    ... ... @@ -54,9 +54,9 @@ combineAltInfo (TagTuple {}) TagEPT = TagDunno
    54 54
     combineAltInfo (TagTuple is1)  (TagTuple is2)  = TagTuple (zipWithEqual combineAltInfo is1 is2)
    
    55 55
     
    
    56 56
     type TagSigEnv = IdEnv TagSig
    
    57
    -type JoinArgEnv = IdEnv [TagInfo]
    
    57
    +type FunArgEnv = IdEnv [TagInfo]
    
    58 58
     data TagEnv p = TE { te_env :: TagSigEnv
    
    59
    -                   , te_join_args :: JoinArgEnv
    
    59
    +                   , te_fun_args :: FunArgEnv
    
    60 60
                        , te_get :: BinderP p -> Id
    
    61 61
                        , te_bytecode :: !Bool
    
    62 62
                        }
    
    ... ... @@ -73,7 +73,7 @@ getBinderId = te_get
    73 73
     
    
    74 74
     initEnv :: Bool -> TagEnv 'CodeGen
    
    75 75
     initEnv for_bytecode = TE { te_env = emptyVarEnv
    
    76
    -             , te_join_args = emptyVarEnv
    
    76
    +             , te_fun_args = emptyVarEnv
    
    77 77
                  , te_get = \x -> x
    
    78 78
                  , te_bytecode = for_bytecode }
    
    79 79
     
    
    ... ... @@ -81,7 +81,7 @@ initEnv for_bytecode = TE { te_env = emptyVarEnv
    81 81
     -- with no other changes.
    
    82 82
     makeTagged :: TagEnv p -> TagEnv 'InferTaggedBinders
    
    83 83
     makeTagged env = TE { te_env = te_env env
    
    84
    -                    , te_join_args = te_join_args env
    
    84
    +                    , te_fun_args = te_fun_args env
    
    85 85
                         , te_get = fst
    
    86 86
                         , te_bytecode = te_bytecode env }
    
    87 87
     
    
    ... ... @@ -159,9 +159,9 @@ extendSigEnv :: TagEnv p -> [(Id,TagSig)] -> TagEnv p
    159 159
     extendSigEnv env@(TE { te_env = sig_env }) bndrs
    
    160 160
       = env { te_env = extendVarEnvList sig_env bndrs }
    
    161 161
     
    
    162
    -lookupJoinArgInfo :: TagEnv p -> Id -> Maybe [TagInfo]
    
    163
    -lookupJoinArgInfo env join_id = lookupVarEnv (te_join_args env) join_id
    
    162
    +lookupFunArgInfo :: TagEnv p -> Id -> Maybe [TagInfo]
    
    163
    +lookupFunArgInfo env fun_id = lookupVarEnv (te_fun_args env) fun_id
    
    164 164
     
    
    165
    -extendJoinArgEnv :: TagEnv p -> [(Id, [TagInfo])] -> TagEnv p
    
    166
    -extendJoinArgEnv env@(TE { te_join_args = join_env }) joins
    
    167
    -  = env { te_join_args = extendVarEnvList join_env joins }
    165
    +extendFunArgEnv :: TagEnv p -> [(Id, [TagInfo])] -> TagEnv p
    
    166
    +extendFunArgEnv env@(TE { te_fun_args = fun_env }) funs
    
    167
    +  = env { te_fun_args = extendVarEnvList fun_env funs }

  • testsuite/tests/simplStg/should_run/T27507a.hs
    ... ... @@ -12,10 +12,7 @@ data Box a = Box !a
    12 12
     
    
    13 13
     foo :: Bool -> Int -> (Int, Box Int)
    
    14 14
     foo b !x = case x of
    
    15
    -  x' -> let -- Keep fun_exit monomorphic so that it can become a join point.
    
    16
    -            -- Without this signature its polymorphic result type prevents contification.
    
    17
    -            fun_exit :: Int -> Int -> (Int, Box Int)
    
    18
    -            fun_exit x_f i = (i, Box x_f)
    
    15
    +  x' -> let fun_exit x_f i = (i, Box x_f)
    
    19 16
                 {-# NOINLINE fun_exit #-}
    
    20 17
             in if b then fun_exit x' 0 else fun_exit x' 1
    
    21 18
     {-# NOINLINE foo #-}
    

  • testsuite/tests/simplStg/should_run/T27507b.hs
    1
    +{-# LANGUAGE BangPatterns #-}
    
    2
    +
    
    3
    +-- Keep the local functions local and their boxed results visible.
    
    4
    +{-# OPTIONS_GHC -fno-cpr-anal -fno-full-laziness #-}
    
    5
    +
    
    6
    +module Main where
    
    7
    +
    
    8
    +import GHC.Exts (noinline)
    
    9
    +import GHC.Exts.Heap (GenClosure(..), getClosureData)
    
    10
    +import System.Exit (exitFailure)
    
    11
    +
    
    12
    +data Box a = Box !a
    
    13
    +
    
    14
    +recursive :: Int -> Int -> (Int, Box Int)
    
    15
    +recursive !x n =
    
    16
    +  let go y i
    
    17
    +        | i == 0 = (i, Box y)
    
    18
    +        | otherwise = go y (i - 1)
    
    19
    +      {-# NOINLINE go #-}
    
    20
    +  in go x n
    
    21
    +{-# NOINLINE recursive #-}
    
    22
    +
    
    23
    +mixed :: Bool -> Int -> Int -> (Box Int, Box Int)
    
    24
    +mixed b !x z =
    
    25
    +  let fun a c = (Box a, Box c)
    
    26
    +      {-# NOINLINE fun #-}
    
    27
    +  in if b then fun x x else fun x z
    
    28
    +{-# NOINLINE mixed #-}
    
    29
    +
    
    30
    +apply :: (a -> b) -> a -> b
    
    31
    +apply fun x = fun x
    
    32
    +{-# NOINLINE apply #-}
    
    33
    +
    
    34
    +partial :: Int -> Int -> (Box Int, Box Int)
    
    35
    +partial !x z =
    
    36
    +  let fun a c = (Box a, Box c)
    
    37
    +      {-# NOINLINE fun #-}
    
    38
    +      pap = fun x
    
    39
    +  in apply pap z
    
    40
    +{-# NOINLINE partial #-}
    
    41
    +
    
    42
    +escaping :: Int -> (Int, Box Int)
    
    43
    +escaping !x =
    
    44
    +  let fun a = (0, Box a)
    
    45
    +      {-# NOINLINE fun #-}
    
    46
    +  in case noinline Just fun of
    
    47
    +       Just escaped -> escaped x
    
    48
    +       Nothing -> (0, Box x)
    
    49
    +{-# NOINLINE escaping #-}
    
    50
    +
    
    51
    +checkConstr :: String -> a -> IO ()
    
    52
    +checkConstr label value = do
    
    53
    +  closure <- getClosureData value
    
    54
    +  case closure of
    
    55
    +    ConstrClosure{} -> pure ()
    
    56
    +    _ -> putStrLn ("FAIL: " ++ label ++ " was not a constructor") >> exitFailure
    
    57
    +
    
    58
    +checkThunk :: String -> a -> IO ()
    
    59
    +checkThunk label value = do
    
    60
    +  closure <- getClosureData value
    
    61
    +  case closure of
    
    62
    +    ThunkClosure{} -> pure ()
    
    63
    +    _ -> putStrLn ("FAIL: " ++ label ++ " was not a thunk") >> exitFailure
    
    64
    +
    
    65
    +main :: IO ()
    
    66
    +main = do
    
    67
    +  case recursive 42 1 of
    
    68
    +    (_, value) -> checkConstr "recursive local function" value
    
    69
    +
    
    70
    +  case mixed False 42 undefined of
    
    71
    +    (known, unknown) -> do
    
    72
    +      checkConstr "mixed known parameter" known
    
    73
    +      checkThunk "mixed unknown parameter" unknown
    
    74
    +
    
    75
    +  case partial 42 undefined of
    
    76
    +    (known, unknown) -> do
    
    77
    +      checkConstr "partial known prefix" known
    
    78
    +      checkThunk "partial unknown suffix" unknown
    
    79
    +
    
    80
    +  case escaping 42 of
    
    81
    +    (_, value) -> checkThunk "escaping local function" value
    
    82
    +  putStrLn "OK"

  • testsuite/tests/simplStg/should_run/T27507b.stdout
    1
    +OK

  • testsuite/tests/simplStg/should_run/all.T
    ... ... @@ -25,3 +25,4 @@ test('unpack_enum', normal, compile_and_run, [''])
    25 25
     test('T27005a', normal, compile_and_run, [''])
    
    26 26
     test('T27507', normal, compile_and_run, [''])
    
    27 27
     test('T27507a', normal, compile_and_run, [''])
    
    28
    +test('T27507b', normal, compile_and_run, [''])