Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
  • GHC GHC
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 5,251
    • Issues 5,251
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
  • Merge requests 583
    • Merge requests 583
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Releases
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Glasgow Haskell CompilerGlasgow Haskell Compiler
  • GHCGHC
  • Issues
  • #14620
Closed
Open
Issue created Dec 26, 2017 by David Feuer@treeowlReporter

Polymorphic functions not easily recognized as join points

This grew out of ticket:14610#comment:146389. If I write

foo :: forall a. (Int -> Bool) -> Int -> a -> a
foo p = go
  where
    go :: Int -> a -> a
    go !n a
      | p n = a
      | otherwise = go (n + 1) a

then I get

foo
  = \ (@ a_aYZ)
      (p_aWO :: Int -> Bool)
      (eta_B2 :: Int)
      (eta1_B1 :: a_aYZ) ->
      case eta_B2 of { GHC.Types.I# ww1_s1bZ ->
      joinrec {
        $wgo_s1c1 [InlPrag=NOUSERINLINE[0], Occ=LoopBreaker]
          :: GHC.Prim.Int# -> a_aYZ -> a_aYZ
        [LclId[JoinId(2)], Arity=2, Str=<L,U><S,1*U>, Unf=OtherCon []]
        $wgo_s1c1 (ww2_X1cu :: GHC.Prim.Int#) (w_s1bW :: a_aYZ)
          = case p_aWO (GHC.Types.I# ww2_X1cu) of {
              False -> jump $wgo_s1c1 (GHC.Prim.+# ww2_X1cu 1#) w_s1bW;
              True -> w_s1bW
            }; } in
      jump $wgo_s1c1 ww1_s1bZ eta1_B1
      }

But if I make go polymorphic,

foo :: (Int -> Bool) -> Int -> a -> a
foo p = go
  where
    go :: Int -> b -> b
    go !n a
      | p n = a
      | otherwise = go (n + 1) a

I get a wrapper and this worker:

T14610.$wfoo
  = \ (@ a_s1cm)
      (w_s1cn :: Int -> Bool)
      (ww_s1cs :: GHC.Prim.Int#)
      (w1_s1cp :: a_s1cm) ->
      letrec {
        $wgo_s1cl [InlPrag=NOUSERINLINE[0], Occ=LoopBreaker]
          :: forall b. GHC.Prim.Int# -> b -> b
        [LclId, Arity=2, Str=<L,U><S,1*U>, Unf=OtherCon []]
        $wgo_s1cl
          = \ (@ b_s1ce) (ww1_s1cj :: GHC.Prim.Int#) (w2_s1cg :: b_s1ce) ->
              case w_s1cn (GHC.Types.I# ww1_s1cj) of {
                False -> $wgo_s1cl @ b_s1ce (GHC.Prim.+# ww1_s1cj 1#) w2_s1cg;
                True -> w2_s1cg
              }; } in
      $wgo_s1cl @ a_s1cm ww_s1cs w1_s1cp

This distinction remains as let vs. let-no-escape in STG. As Joachim Breitner's comments seem to suggest, we could probably recognize this by applying the static argument transformation to the type argument of go. But we don't currently have any machinery for doing that, I don't think. Furthermore, that would fail with polymorphic recursion even if the only type changes are from newtype. That said, the SAT approach would presumably help when the worker has a non-essential type signature for clarity.

Trac metadata
Trac field Value
Version 8.2.2
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC nomeata
Operating system
Architecture
Edited Dec 07, 2020 by Sebastian Graf
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking