Skip to content

CoreTidy drops specialisations of exported functions

This is T7785, from #7785 (closed):

{-# LANGUAGE TypeFamilies, ConstraintKinds #-}

module Foo( shared, foo, bar) where

import Data.Kind

type family Domain (f :: Type -> Type) a :: Constraint
type instance Domain [] a = ()

class MyFunctor f where
  myfmap :: (Domain f a, Domain f b) => (a -> b) -> f a -> f b
instance MyFunctor [] where
  myfmap = map

shared :: (MyFunctor f, Domain f Int) => f Int -> f Int
shared = let
  f = myfmap negate
  in
  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.
  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.
  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.
  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.  f.
  f . f . f . f . f . f . f . f . f . f . f . f . f

foo, bar :: [Int] -> [Int]
foo xs = shared $ 0:xs
bar xs = 0:shared xs

The gist is that we have an exported, specialisable function shared :: (MyFunctor f, Domain f Int) => f a -> f b. There are two call sites foo, bar within the module that force a specialisation for [], $sshared. We get a RULE attached to shared that rewrites shared @[] $dMyFunctor = $sshared. Ultimately, we want to see that rule (with -ddump-rules) exported and thus the specialisation $sshared kept alive. Indeed, the expected test output is

==================== Tidy Core rules ====================
"SPEC shared @[]"
    forall ($dMyFunctor :: MyFunctor []) (irred :: Domain [] Int).
      shared @[] $dMyFunctor irred
      = bar_$sshared

But when I look at -ddump-simpl output, this RULE is announced as an IMP rule!

------ Local rules for imported ids --------
"SPEC shared @[]"
    forall ($dMyFunctor :: MyFunctor []) (irred :: Domain [] Int).
      shared @[] $dMyFunctor irred
      = Foo.bar_$sshared

Which seems wrong. As it happens, the specialisation $sshared is only kept alive through the call site in bar (which I haven't shown), not through the RULE associated with the exported function shared.

Now !7788 (closed) comes up with a slightly different specialisation that causes $sshared to inline into that call site. Result: the RULE and $sshared are dropped completely and our specialisation work has been lost.

Question: Is there a reason why CoreTidy doesn't keep alive $sshared if its only occurrence is in the RHS of a RULE attached to the exported function shared?

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