{-# LANGUAGE Strict #-}
{-# OPTIONS_GHC -fexpose-all-unfoldings #-}

{-| Functions in this module are for internal compiler use only, and should not
be used elsewhere. -}
module PlutusTx.AsData.Internal where

import PlutusTx.Builtins.Internal as BI

-- See Note [Compiling AsData Matchers and Their Invocations]

-- This may no longer be needed post the van Rossem HF. It needs to be examined and verified.
wrapUnsafeDataAsConstr :: BuiltinData -> BuiltinPair BuiltinInteger (BuiltinList BuiltinData)
wrapUnsafeDataAsConstr :: BuiltinData -> BuiltinPair BuiltinInteger (BuiltinList BuiltinData)
wrapUnsafeDataAsConstr = BuiltinData -> BuiltinPair BuiltinInteger (BuiltinList BuiltinData)
BI.unsafeDataAsConstr
{-# OPAQUE wrapUnsafeDataAsConstr #-}

-- See Note [Dropping redundant unsafeCaseList calls produced by AsData]
droppableUnsafeCaseList :: forall a r. (a -> BuiltinList a -> r) -> BuiltinList a -> r
droppableUnsafeCaseList :: forall a r. (a -> BuiltinList a -> r) -> BuiltinList a -> r
droppableUnsafeCaseList = (a -> BuiltinList a -> r) -> BuiltinList a -> r
forall a r. (a -> BuiltinList a -> r) -> BuiltinList a -> r
BI.unsafeCaseList
{-# OPAQUE droppableUnsafeCaseList #-}

-- See Note [Use list casing in AsData pattern synonyms]
directUnsafeCaseList :: BuiltinList a -> (a, BuiltinList a)
directUnsafeCaseList :: forall a. BuiltinList a -> (a, BuiltinList a)
directUnsafeCaseList = (a -> BuiltinList a -> (a, BuiltinList a))
-> BuiltinList a -> (a, BuiltinList a)
forall a r. (a -> BuiltinList a -> r) -> BuiltinList a -> r
BI.unsafeCaseList (,)
{-# OPAQUE directUnsafeCaseList #-}

{- Note [Compiling AsData Matchers and Their Invocations]

The AsData matchers, such as `AsData.Budget.Types.$mInts`, eagerly unpack all fields,
even for unused fields. This leads to strict dead bindings. Since they are strict and
the inliner doesn't know that they are effect-free, it can't remove them.

It is not uncommon that only one or a small number of fields in `ScriptContext` are used,
so this could cause severe performance penalties.

The matches are generated by GHC for pattern synonyms, not written by hand. As a result,
they are unfortunately not customizable - for example, by attaching certain annotations
to certain variables as signals for the Plinth compiler.

The workaround are the wrapper functions in this module. `wrapUnsafeDataAsConstr`
is marked OPAQUE so that GHC doesn't inline it, yet its unfolding is made available
via `-fexpose-all-unfoldings`. `wrapUnsafeDataAsConstr` is supposed to be used by
AsData only, and not elsewhere, and the Plinth compiler relies on it to identify
which functions are AsData matchers.

An AsData matcher unpacks the fields and passes them to a continuation. What we need is
to mark the variables bound in the continuation as safe to inline. For instance, if the
continuation is `\x y z w -> f x` (indicating a data type with 4 fields, of which only the
first is used), marking `x`, `y`, `z` and `w` as safe to inline allows the inliner to
drop the unused ones.

-}

{- Note [Dropping redundant unsafeCaseList calls produced by AsData]

AsData generates `unsafeCaseList` calls to unpack lists in the Data. It unpacks a list
completely, obtaining all fields, then calls the continuation with the fields. This
can lead to unnecessary `unsafeCaseList` calls, if the continuation does not access
all fields.

In general, we cannot drop the unnecessary `unsafeCaseList` calls, because doing so may
change the semantics: `unsafeCaseList` throws an error if the list is empty, and dropping
it would eliminate the error.

However, we know that the `unsafeCaseList` calls generated from AsData are never applied
to empty lists, so redundant ones can be dropped safely. To do so, we annotate the
`case` expressions with `SafeToDrop`, and use a PIR simplifier pass, DeadCase, to drop the
redundant ones.

-}

{- Note [Use list casing in AsData pattern synonyms]

AsData pattern synonyms cannot directly call `unsafeCaseList` or `droppableUnsafeCaseList`
because they are CPS-style functions and aren't compatible with view patterns. The
solution is to introduce an opaque, direct-style `directUnsafeCaseList`, and rewrite it
to `droppableUnsafeCaseList` in the plugin. Specifically, the plugin turns

case directUnsafeCaseList xs of (hd, tl) -> body

into

droppableUnsafeCaseList (\hd tl -> body) xs

-}