content: {-# 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]
  
  wrapUnsafeDataAsConstr :: BuiltinData -> BuiltinPair BuiltinInteger (BuiltinList BuiltinData)
  wrapUnsafeDataAsConstr = BI.unsafeDataAsConstr
  {-# OPAQUE wrapUnsafeDataAsConstr #-}
  
  wrapTail :: BuiltinList a -> BuiltinList a
  wrapTail = BI.tail
  {-# OPAQUE wrapTail #-}
  
  -- Like `unsafeUncons` but uses `wrapTail` instead of the regular `tail`.
  --
  -- This function can be inlined, because the Plinth compiler does not rely on it being present.
  --
  -- We don't need a `wrapHead`, because there are no strict dead bindings of the form
  -- `let !y = head xs`. The dead bindings are only in the form of `let !ys = tail xs`
  -- and `let !y = unsafeFromBuiltinData (head xs)`.
  wrapUnsafeUncons :: BuiltinList a -> (a, BuiltinList a)
  wrapUnsafeUncons l = (BI.head l, wrapTail l)
  {-# INLINE wrapUnsafeUncons #-}
  
  {- 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.
  
  This eliminates unused field accessors, but there are still the `tailList` dead bindings, which
  are intermediate artifacts in generating the field accessors. This is what `wrapTail` is for.
  `wrapTail` appears in `case` scrutinees, as in `case wrapTail @BuiltinData l of l' -> ...`.
  So the compiler detects such `case` expressions, and mark the binder, `l'`, as
  safe to inline.
  
  -}
  