fiExpr dflags to_drop ann_expr@(_,AnnApp {})
  = mkTicks ticks $ wrapFloats drop_here $ wrapFloats extra_drop $
    mkApps (fiExpr dflags fun_drop ann_fun)
           (zipWith (fiExpr dflags) arg_drops ann_args)
  where
    (ann_fun@(fun_fvs, _), ann_args, ticks)
           = collectAnnArgsTicks tickishFloatable ann_expr
    fun_ty = exprType (deAnnotate ann_fun)
    ((_,extra_fvs), arg_fvs) = mapAccumL mk_arg_fvs (fun_ty, emptyVarSet) ann_args

    -- All this faffing about is so that we can get hold of
    -- the types of the arguments, to pass to noFloatIntoRhs
    mk_arg_fvs :: (Type, FreeVarSet) -> CoreExprWithFVs -> ((Type, FreeVarSet), FreeVarSet)
    mk_arg_fvs (fun_ty, extra_fvs) (_, AnnType ty)
      = ((applyTy fun_ty ty, extra_fvs), emptyVarSet)

    mk_arg_fvs (fun_ty, extra_fvs) (arg_fvs, ann_arg)
      | ASSERT( isFunTy fun_ty ) noFloatIntoRhs ann_arg arg_ty
      = ((res_ty, extra_fvs `unionVarSet` arg_fvs), emptyVarSet)
      | otherwise
      = ((res_ty, extra_fvs), arg_fvs)
      where
       (arg_ty, res_ty) = splitFunTy fun_ty

    drop_here : extra_drop : fun_drop : arg_drops
      = sepBindsByDropPoint dflags False (extra_fvs : fun_fvs : arg_fvs) to_drop

{-
Note [Do not destroy the let/app invariant]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Watch out for
   f (x +# y)
We don't want to float bindings into here
   f (case ... of { x -> x +# y })
because that might destroy the let/app invariant, which requires
unlifted function arguments to be ok-for-speculation.

Note [Floating in past a lambda group]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* We must be careful about floating inside a value lambda.
  That risks losing laziness.
  The float-out pass might rescue us, but then again it might not.

* We must be careful about type lambdas too.  At one time we did, and
  there is no risk of duplicating work thereby, but we do need to be
  careful.  In particular, here is a bad case (it happened in the
  cichelli benchmark:
        let v = ...
        in let f = /\t -> \a -> ...
           ==>
        let f = /\t -> let v = ... in \a -> ...
  This is bad as now f is an updatable closure (update PAP)
  and has arity 0.

* Hack alert!  We only float in through one-shot lambdas,
  not (as you might guess) through lone big lambdas.
  Reason: we float *out* past big lambdas (see the test in the Lam
  case of FloatOut.floatExpr) and we don't want to float straight
  back in again.

  It *is* important to float into one-shot lambdas, however;
  see the remarks with noFloatIntoRhs.

So we treat lambda in groups, using the following rule:

 Float in if (a) there is at least one Id,
         and (b) there are no non-one-shot Ids

 Otherwise drop all the bindings outside the group.

This is what the 'go' function in the AnnLam case is doing.

Urk! if all are tyvars, and we don't float in, we may miss an
      opportunity to float inside a nested case branch
-}
fiExpr dflags to_drop ann_expr@(_,AnnApp {})
  = mkTicks ticks $ wrapFloats drop_here $ wrapFloats extra_drop $
    mkApps (fiExpr dflags fun_drop ann_fun)
           (zipWith (fiExpr dflags) arg_drops ann_args)
  where
    (ann_fun@(fun_fvs, _), ann_args, ticks)
           = collectAnnArgsTicks tickishFloatable ann_expr
    fun_ty = exprType (deAnnotate ann_fun)
    ((_,extra_fvs), arg_fvs) = mapAccumL mk_arg_fvs (fun_ty, emptyVarSet) ann_args

    -- All this faffing about is so that we can get hold of
    -- the types of the arguments, to pass to noFloatIntoRhs
    mk_arg_fvs :: (Type, FreeVarSet) -> CoreExprWithFVs -> ((Type, FreeVarSet), FreeVarSet)
    mk_arg_fvs (fun_ty, extra_fvs) (_, AnnType ty)
      = ((applyTy fun_ty ty, extra_fvs), emptyVarSet)

    mk_arg_fvs (fun_ty, extra_fvs) (arg_fvs, ann_arg)
      | ASSERT( isFunTy fun_ty ) noFloatIntoRhs ann_arg arg_ty
      = ((res_ty, extra_fvs `unionVarSet` arg_fvs), emptyVarSet)
      | otherwise
      = ((res_ty, extra_fvs), arg_fvs)
      where
       (arg_ty, res_ty) = splitFunTy fun_ty

    drop_here : extra_drop : fun_drop : arg_drops
      = sepBindsByDropPoint dflags False (extra_fvs : fun_fvs : arg_fvs) to_drop

{-
Note [Do not destroy the let/app invariant]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Watch out for
   f (x +# y)
We don't want to float bindings into here
   f (case ... of { x -> x +# y })
because that might destroy the let/app invariant, which requires
unlifted function arguments to be ok-for-speculation.

Note [Floating in past a lambda group]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* We must be careful about floating inside a value lambda.
  That risks losing laziness.
  The float-out pass might rescue us, but then again it might not.

* We must be careful about type lambdas too.  At one time we did, and
  there is no risk of duplicating work thereby, but we do need to be
  careful.  In particular, here is a bad case (it happened in the
  cichelli benchmark:
        let v = ...
        in let f = /\t -> \a -> ...
           ==>
        let f = /\t -> let v = ... in \a -> ...
  This is bad as now f is an updatable closure (update PAP)
  and has arity 0.

* Hack alert!  We only float in through one-shot lambdas,
  not (as you might guess) through lone big lambdas.
  Reason: we float *out* past big lambdas (see the test in the Lam
  case of FloatOut.floatExpr) and we don't want to float straight
  back in again.

  It *is* important to float into one-shot lambdas, however;
  see the remarks with noFloatIntoRhs.

So we treat lambda in groups, using the following rule:

 Float in if (a) there is at least one Id,
         and (b) there are no non-one-shot Ids

 Otherwise drop all the bindings outside the group.

This is what the 'go' function in the AnnLam case is doing.

Urk! if all are tyvars, and we don't float in, we may miss an
      opportunity to float inside a nested case branch
-}
fiExpr dflags to_drop ann_expr@(_,AnnApp {})
  = mkTicks ticks $ wrapFloats drop_here $ wrapFloats extra_drop $
    mkApps (fiExpr dflags fun_drop ann_fun)
           (zipWith (fiExpr dflags) arg_drops ann_args)
  where
    (ann_fun@(fun_fvs, _), ann_args, ticks)
           = collectAnnArgsTicks tickishFloatable ann_expr
    fun_ty = exprType (deAnnotate ann_fun)
    ((_,extra_fvs), arg_fvs) = mapAccumL mk_arg_fvs (fun_ty, emptyVarSet) ann_args

    -- All this faffing about is so that we can get hold of
    -- the types of the arguments, to pass to noFloatIntoRhs
    mk_arg_fvs :: (Type, FreeVarSet) -> CoreExprWithFVs -> ((Type, FreeVarSet), FreeVarSet)
    mk_arg_fvs (fun_ty, extra_fvs) (_, AnnType ty)
      = ((applyTy fun_ty ty, extra_fvs), emptyVarSet)

    mk_arg_fvs (fun_ty, extra_fvs) (arg_fvs, ann_arg)
      | ASSERT( isFunTy fun_ty ) noFloatIntoRhs ann_arg arg_ty
      = ((res_ty, extra_fvs `unionVarSet` arg_fvs), emptyVarSet)
      | otherwise
      = ((res_ty, extra_fvs), arg_fvs)
      where
       (arg_ty, res_ty) = splitFunTy fun_ty

    drop_here : extra_drop : fun_drop : arg_drops
      = sepBindsByDropPoint dflags False (extra_fvs : fun_fvs : arg_fvs) to_drop

{-
Note [Do not destroy the let/app invariant]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Watch out for
   f (x +# y)
We don't want to float bindings into here
   f (case ... of { x -> x +# y })
because that might destroy the let/app invariant, which requires
unlifted function arguments to be ok-for-speculation.

Note [Floating in past a lambda group]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* We must be careful about floating inside a value lambda.
  That risks losing laziness.
  The float-out pass might rescue us, but then again it might not.

* We must be careful about type lambdas too.  At one time we did, and
  there is no risk of duplicating work thereby, but we do need to be
  careful.  In particular, here is a bad case (it happened in the
  cichelli benchmark:
        let v = ...
        in let f = /\t -> \a -> ...
           ==>
        let f = /\t -> let v = ... in \a -> ...
  This is bad as now f is an updatable closure (update PAP)
  and has arity 0.

* Hack alert!  We only float in through one-shot lambdas,
  not (as you might guess) through lone big lambdas.
  Reason: we float *out* past big lambdas (see the test in the Lam
  case of FloatOut.floatExpr) and we don't want to float straight
  back in again.

  It *is* important to float into one-shot lambdas, however;
  see the remarks with noFloatIntoRhs.

So we treat lambda in groups, using the following rule:

 Float in if (a) there is at least one Id,
         and (b) there are no non-one-shot Ids

 Otherwise drop all the bindings outside the group.

This is what the 'go' function in the AnnLam case is doing.

Urk! if all are tyvars, and we don't float in, we may miss an
      opportunity to float inside a nested case branch
-}