Skip to content

INLINEABLE function fails to specialize.

Summary

Staged INLINABLE pragmas don't seem to actually allow specialization.

Steps to reproduce

{-# language LambdaCase, Strict, DeriveFunctor, DerivingStrategies #-}
module Altery where
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map

foo :: Either Int Char -> Map (Either Int Char) v -> Maybe (v, (Map (Either Int Char) v))
foo x subst = case Map.alterF (\case {Nothing -> NotFound; Just t -> Found t Nothing}) x subst of
  NotFound -> foo (fmap (toEnum . (+1) . fromEnum) x) subst
  Found p q -> Just (p, q)

data CheckRes a m = NotFound | Found !a ~m
  deriving stock Functor

Compile this with -O2 -ddump-simpl. The interesting parts:

Altery.$wfoo [InlPrag=[2]]
  :: forall {v}.
     Either Int Char
     -> Map (Either Int Char) v -> (# v, Map (Either Int Char) v #)
[GblId[StrictWorker([!, !])],
 Arity=2,
 Str=<SL><SL>,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True, Guidance=IF_ARGS [60 0] 222 10}]
Altery.$wfoo
  = \ (@v_s1kj)
      (x_s1kk :: Either Int Char)
      (subst_s1kl :: Map (Either Int Char) v_s1kj) ->
      case Map.alterF
             @(CheckRes v_s1kj)
             @(Either Int Char)
             @v_s1kj
             (Altery.$fFunctorCheckRes @v_s1kj)
             Altery.foo2
             (Altery.foo1 @v_s1kj)
             x_s1kk
             subst_s1kl
      of {
        NotFound ->
          case x_s1kk of {
            Left x1_a1j5 -> Altery.foo_$s$wfoo1 @v_s1kj x1_a1j5 subst_s1kl;
            Right y_a1j7 ->
              Altery.foo_$s$wfoo
                @v_s1kj
                (case y_a1j7 of { GHC.Types.C# c#_a1jA ->
                 let {
                   i#_s1jT :: GHC.Prim.Int#
                   [LclId]
                   i#_s1jT = GHC.Prim.+# (GHC.Prim.ord# c#_a1jA) 1# } in
                 case GHC.Prim.leWord# (GHC.Prim.int2Word# i#_s1jT) 1114111## of {
                   __DEFAULT -> GHC.Char.$wlvl i#_s1jT;
                   1# -> GHC.Types.C# (GHC.Prim.chr# i#_s1jT)
                 }
                 })
                subst_s1kl
          };
        Found p_aQ7 q_aQ8 -> (# p_aQ7, q_aQ8 #)
      }

foo [InlPrag=[2]]
  :: forall v.
     Either Int Char
     -> Map (Either Int Char) v -> Maybe (v, Map (Either Int Char) v)
[GblId,
 Arity=2,
 Str=<SL><SL>,
 Cpr=2(1),
 Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True,
         Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=False)
         Tmpl= \ (@v_s1kj)
                 (x_s1kk [Occ=Once1] :: Either Int Char)
                 (subst_s1kl [Occ=Once1] :: Map (Either Int Char) v_s1kj) ->
                 case Altery.$wfoo @v_s1kj x_s1kk subst_s1kl of
                 { (# ww_s1ks [Occ=Once1], ww1_s1kt [Occ=Once1] #) ->
                 GHC.Maybe.Just
                   @(v_s1kj, Map (Either Int Char) v_s1kj) (ww_s1ks, ww1_s1kt)
                 }}]
foo
  = \ (@v_s1kj)
      (x_s1kk :: Either Int Char)
      (subst_s1kl :: Map (Either Int Char) v_s1kj) ->
      case Altery.$wfoo @v_s1kj x_s1kk subst_s1kl of
      { (# ww_s1ks, ww1_s1kt #) ->
      GHC.Maybe.Just
        @(v_s1kj, Map (Either Int Char) v_s1kj) (ww_s1ks, ww1_s1kt)
      }

The compiled foo (not just its unfolding) calls $wfoo, which calls unspecialized alterF. That's awful! alterF is defined as INLINABLE [2] because there are rewrite rules attached to it.

Environment

  • GHC version used: 9.4.3

Optional:

  • Operating System:
  • System Architecture:
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information