Eta expand primops
While discussing the state of #17760 (closed) (and various related work) with @simonpj today we noticed that currently unsaturated primop applications can stumble into rather questionable territory. For instance, consider the program:
f :: ByteArray# -> State# RealWorld -> (# State# RealWorld, Weak# () #)
f = \x -> mkWeakNoFinalizer# x () g
g = ...
Where the primop mkWeak#
has type,
mkWeakNoFinalizer#
:: forall (r :: RuntimeRep) (a :: TYPE r) b.
a
-> b
-> State# RealWorld
-> (# State# RealWorld, Weak# b #)
Since the mkWeak#
application in f
is unsaturated it will generate a call to the wrapper for mkWeak#
defined in PrimOpWrappers
(which is generated by genprimopcode
):
{-# NOINLINE mkWeakNoFinalizer# #-}
mkWeakNoFinalizer# :: o -> b -> State# (RealWorld) -> (# State# (RealWorld),Weak# b #)
mkWeakNoFinalizer# a1 a2 a3 = (GHC.Prim.mkWeakNoFinalizer#) a1 a2 a3
However, note the type: o
has no explicit kind given and therefore is simply of kind TYPE 'LiftedPtrRep
. This is, of course, necessary since we could not allow a1
to bind its value if it were levity polymorphic. However, the application in f
applies mkWeakNoFinalizer#
to an unlifted type!
In this particular case this all happens to work out fine at runtime (since mkWeakNoFinalizer#
doesn't care about the levity of its first argument, only that it is a pointer), but this is clearly not a great situation.
The problem here is our use of wrappers. This use is a relatively new development. Previously CorePrep
would eta expand primop applications to their expected arity. However, #16846 (closed) prompted us to reconsider this approach due to the difficulty of predicting CAFfyness correctly, resulting in cac8dc9f which rather opted to call wrappers for unsaturated primop applications
Of course, with the resolution of #17648 (closed) (via !1304 (merged)) we no longer need to predict CAFfyness. Consequently, we think it would be better to return to eta expanding unsaturated primop applications.