0 | ||| Utilities pertaining to the _lamda-lifted_ intermediate representation of
  1 | ||| Idris 2 programs.
  2 | |||
  3 | ||| This representation of program syntax is one of several used when compiling
  4 | ||| a program. These representations can be used by compiler back-ends to
  5 | ||| compile from versions of Idris programs with reduced complexity---see
  6 | ||| [Which Intermediate Representation (IR) should be consumed by the custom
  7 | ||| back-end?]
  8 | ||| (https://idris2.readthedocs.io/en/latest/backends/backend-cookbook.html?highlight=lifted#which-intermediate-representation-ir-should-be-consumed-by-the-custom-back-end)
  9 | ||| for more information.
 10 | module Compiler.LambdaLift
 11 |
 12 | import Core.CompileExpr
 13 | import Core.Context
 14 |
 15 | import Data.Vect
 16 |
 17 | import Libraries.Data.SnocList.SizeOf
 18 | import Libraries.Data.List.Extra
 19 |
 20 | %default covering
 21 |
 22 | mutual
 23 |
 24 |   ||| Type representing the syntax tree of an Idris 2 program after lambda
 25 |   ||| lifting.
 26 |   |||
 27 |   ||| All constructors take as argument a file context, describing the position
 28 |   ||| of the original code pre-transformation.
 29 |   |||
 30 |   ||| @ vars is the list of names accessible within the current scope of the
 31 |   |||   lambda-lifted code.
 32 |   public export
 33 |   data Lifted : Scoped where
 34 |
 35 |        ||| A local variable in the lambda-lifted syntax tree.
 36 |        |||
 37 |        ||| @ idx is the index that the variable can be found at in the syntax
 38 |        |||   tree's current scope.
 39 |        ||| @ p is evidence that indexing into vars with idx will provide the
 40 |        |||   correct variable.
 41 |        LLocal : {idx : Nat} -> FC -> (0 p : IsVar x idx vars) -> Lifted vars
 42 |
 43 |        ||| A known function applied to exactly the right number of arguments.
 44 |        |||
 45 |        ||| Back-end runtimes should be able to utilise total applications
 46 |        ||| effectively, and so they are given a specific constructor here.
 47 |        |||
 48 |        ||| @ lazy is used to signify that this function application is lazy,
 49 |        |||   and, if so, the reason for lazy application.
 50 |        ||| @ n is the name of the function to be invoked.
 51 |        ||| @ args is the list of arguments for the function call.
 52 |        LAppName : FC -> (lazy : Maybe LazyReason) -> (n : Name) ->
 53 |                   (args : List (Lifted vars)) -> Lifted vars
 54 |
 55 |        ||| A known function applied to fewer arguments than its arity.
 56 |        |||
 57 |        ||| Situations described by this constructor will likely be handled by
 58 |        ||| by runtimes by creating a closure which waits for the remaining
 59 |        ||| arguments.
 60 |        |||
 61 |        ||| @ n is the name of the function to be (eventually) invoked.
 62 |        ||| @ missing is the number of arguments missing from this application.
 63 |        ||| @ args is a list of the arguments known at this point in time.
 64 |        LUnderApp : FC -> (n : Name) -> (missing : Nat) ->
 65 |                    (args : List (Lifted vars)) -> Lifted vars
 66 |
 67 |        ||| A closure applied to one more argument.
 68 |        |||
 69 |        ||| This given argument may be the last one that the closure is waiting
 70 |        ||| on before being able to run; runtimes should check for such a
 71 |        ||| situation, and run the function if it is now fully applied.
 72 |        |||
 73 |        ||| @ lazy is used to signify that this function application is lazy,
 74 |        |||   and, if so, the reason for lazy application.
 75 |        ||| @ closure is the closure being applied.
 76 |        ||| @ arg is the extra argument being given to the closure.
 77 |        LApp : FC -> (lazy : Maybe LazyReason) -> (closure : Lifted vars) ->
 78 |               (arg : Lifted vars) -> Lifted vars
 79 |
 80 |        ||| A let binding: binding a new name to an existing expression.
 81 |        |||
 82 |        ||| Roughly, this constructor represents code of the form:
 83 |        ||| ```idris
 84 |        ||| let
 85 |        |||   x = expr
 86 |        ||| in
 87 |        |||   body
 88 |        ||| ```
 89 |        |||
 90 |        ||| @ x is the new name to bind.
 91 |        ||| @ expr is the expression to bind `x` to.
 92 |        ||| @ body is the expression to evaluate after binding.
 93 |        LLet : FC -> (x : Name) -> (expr : Lifted vars) ->
 94 |               (body : Lifted (x :: vars)) -> Lifted vars
 95 |
 96 |        ||| Use of a constructor to construct a compound data type value.
 97 |        |||
 98 |        ||| @ n is the name of the data type.
 99 |        ||| @ info is information about the constructor.
100 |        ||| @ tag is the tag value for the construction, if the type is an
101 |        |||   algebraic data type.
102 |        ||| @ args is the list of arguments for the constructor.
103 |        LCon : FC -> (n : Name) -> (info : ConInfo) -> (tag : Maybe Int) ->
104 |               (args : List (Lifted vars)) -> Lifted vars
105 |
106 |        ||| An operator applied to operands.
107 |        |||
108 |        ||| @ arity is the arity of the operator.
109 |        ||| @ lazy is used to signify that this operation is lazy, and, if so,
110 |        |||   the reason for lazy application.
111 |        ||| @ op is the operator being used.
112 |        ||| @ args is a vector containing the operands of the operation.
113 |        LOp : {arity : _} ->
114 |              FC -> (lazy : Maybe LazyReason) -> (op : PrimFn arity) ->
115 |              (args : Vect arity (Lifted vars)) -> Lifted vars
116 |
117 |        ||| A second, more involved, form of primitive operation, defined using
118 |        ||| `%extern` pragmas.
119 |        |||
120 |        ||| Backends should cause a compile-time error when encountering an
121 |        ||| unimplemented LExtPrim operation.
122 |        |||
123 |        ||| @ lazy is used to signify that this operation is lazt, and, if so,
124 |        |||   the reason for lazy application.
125 |        ||| @ p is the name of the operator being used.
126 |        ||| @ args is a list of operands for the operation.
127 |        LExtPrim : FC -> (lazy : Maybe LazyReason) -> (p : Name) ->
128 |                   (args : List (Lifted vars)) -> Lifted vars
129 |
130 |        ||| A case split on constructor tags.
131 |        |||
132 |        ||| @ expr is the value to match against.
133 |        ||| @ alts is a list of the different branches in the case statement.
134 |        ||| @ def is an (optional) default branch, taken if no branch in alts is
135 |        |||   taken.
136 |        LConCase : FC -> (expr : Lifted vars) ->
137 |                   (alts : List (LiftedConAlt vars)) ->
138 |                   (def : Maybe (Lifted vars)) -> Lifted vars
139 |
140 |        ||| A case split on a constant expression.
141 |        |||
142 |        ||| @ expr is the expression to match against.
143 |        ||| @ alts is a list of the different branches in the case statement.
144 |        ||| @ def is an (optional) default branch, taken if no branch in alts is
145 |        |||   taken.
146 |        LConstCase : FC -> (expr : Lifted vars) ->
147 |                     (alts : List (LiftedConstAlt vars)) ->
148 |                     (def : Maybe (Lifted vars)) -> Lifted vars
149 |
150 |        ||| A primitive (constant) value.
151 |        LPrimVal : FC -> Constant -> Lifted vars
152 |
153 |        ||| An erased value.
154 |        LErased : FC -> Lifted vars
155 |
156 |        ||| A forceful crash of the program.
157 |        |||
158 |        ||| This kind of crash is generated by the Idris 2 compiler; it is
159 |        ||| separate from crashes explicitly added to code by programmers (for
160 |        ||| example via `idris_crash`).
161 |        |||
162 |        ||| @ msg is a message emitted when crashing that might be useful for
163 |        |||   debugging.
164 |        LCrash : FC -> (msg : String) -> Lifted vars
165 |
166 |   ||| A branch of an "LCon" (constructor tag) case statement.
167 |   |||
168 |   ||| @ vars is the list of names accessible within the current scope of the
169 |   |||   lambda-lifted code.
170 |   public export
171 |   data LiftedConAlt : Scoped where
172 |
173 |        ||| Constructs a branch of an "LCon" (constructor tag) case statement.
174 |        |||
175 |        ||| If this branch is taken, members of the compound data value being
176 |        ||| inspected are bound to new names before evaluation continues.
177 |        |||
178 |        ||| @ n is the name of the constructor that this branch checks for.
179 |        ||| @ info is information about the constructor.
180 |        ||| @ tag is a tag value, present if the type of the value
181 |        |||   inspected is an algebraic data type (this can be matched against
182 |        |||   instead of the constructor's name, if preferable).
183 |        ||| @ args is a list of new names that are bound to the inspected value's
184 |        |||   members before evaluation of this branch's body (this is similar
185 |        |||   to using a let binding for each member of the value).
186 |        ||| @ body is the expression that is evaluated as the consequence of
187 |        |||   this branch matching.
188 |        MkLConAlt : (n : Name) -> (info : ConInfo) -> (tag : Maybe Int) ->
189 |                    (args : List Name) -> (body : Lifted (args ++ vars)) ->
190 |                    LiftedConAlt vars
191 |
192 |   ||| A branch of an "LConst" (constant expression) case statement.
193 |   |||
194 |   ||| @ vars is the list of names accessible within the current scope of the
195 |   |||   lambda-lifted code.
196 |   public export
197 |   data LiftedConstAlt : Scoped where
198 |
199 |        ||| Constructs a branch of an "LConst" (constant expression) case
200 |        ||| statement.
201 |        |||
202 |        ||| @ expr is the constant expression to match against.
203 |        ||| @ body is the expression that is evaluated as the consequence of this
204 |        |||   branch matching.
205 |        MkLConstAlt : (expr : Constant) -> (body : Lifted vars) ->
206 |                      LiftedConstAlt vars
207 |
208 | ||| Top-level definitions in the lambda-lifted intermediate representation of an
209 | ||| Idris 2 program.
210 | public export
211 | data LiftedDef : Type where
212 |
213 |      ||| Constructs a function definition.
214 |      |||
215 |      ||| @ args is the arguments accepted by the function.
216 |      ||| @ scope is the list of names accessible within the current scope of the
217 |      |||   lambda-lifted code.
218 |      ||| @ body is the body of the function.
219 |      -- We take the outer scope and the function arguments separately so that
220 |      -- we don't have to reshuffle de Bruijn indices, which is expensive.
221 |      -- This should be compiled as a function which takes 'args' first,
222 |      -- then 'reverse scope'.
223 |      -- (Sorry for the awkward API - it's to do with how the indices are
224 |      -- arranged for the variables, and it could be expensive to reshuffle them!
225 |      -- See Compiler.ANF for an example of how they get resolved to names)
226 |      MkLFun : (args : Scope) -> (scope : Scope) ->
227 |               (body : Lifted (Scope.addInner args scope)) -> LiftedDef
228 |
229 |      ||| Constructs a definition of a constructor for a compound data type.
230 |      |||
231 |      ||| @ tag is a tag value used by the constructor (if present) to keep track
232 |      |||   of the value's type when using algebraic data types.
233 |      ||| @ arity is the arity of the constructor; the number of arguments it
234 |      |||   takes.
235 |      ||| @ nt is information related to newtype unboxing; backend
236 |      |||   implementations needs not make use of this argument, as newtype
237 |      |||   unboxing is managed by the Idris 2 compiler.
238 |      MkLCon : (tag : Maybe Int) -> (arity : Nat) -> (nt : Maybe Nat) ->
239 |               LiftedDef
240 |
241 |      ||| Constructs a forward declaration of a foreign function.
242 |      |||
243 |      ||| @ ccs is a list of calling conventions; these are annotations to
244 |      |||   foreign functions, used by backends to relate foreign function calls
245 |      |||   correctly.
246 |      ||| @ fargs is a list of the types of the arguments to the function.
247 |      ||| @ ret is the type of the return value of the function.
248 |      MkLForeign : (ccs : List String) ->
249 |                   (fargs : List CFType) ->
250 |                   (ret : CFType) ->
251 |                   LiftedDef
252 |
253 |      ||| Constructs an error condition.
254 |      |||
255 |      ||| The function produced by this construction should accept any number of
256 |      ||| arguments, and should crash at runtime. Such crashes should crash via
257 |      ||| `LCrash` rather than `prim_crash`.
258 |      |||
259 |      ||| @ expl : an explanation of the error.
260 |      MkLError : (expl : Lifted Scope.empty) -> LiftedDef
261 |
262 | showLazy : Maybe LazyReason -> String
263 | showLazy = maybe "" $ (" " ++) . show
264 |
265 | mutual
266 |   export
267 |   covering
268 |   {vs : _} -> Show (Lifted vs) where
269 |     show (LLocal {idx} _ p) = "!" ++ show (nameAt p)
270 |     show (LAppName fc lazy n args)
271 |         = show n ++ showLazy lazy ++ "(" ++ showSep ", " (map show args) ++ ")"
272 |     show (LUnderApp fc n m args)
273 |         = "<" ++ show n ++ " underapp " ++ show m ++ ">(" ++
274 |           showSep ", " (map show args) ++ ")"
275 |     show (LApp fc lazy c arg)
276 |         = show c ++ showLazy lazy ++ " @ (" ++ show arg ++ ")"
277 |     show (LLet fc x val sc)
278 |         = "%let " ++ show x ++ " = " ++ show val ++ " in " ++ show sc
279 |     show (LCon fc n _ t args)
280 |         = "%con " ++ show n ++ "(" ++ showSep ", " (map show args) ++ ")"
281 |     show (LOp fc lazy op args)
282 |         = "%op " ++ show op ++ showLazy lazy ++ "(" ++ showSep ", " (toList (map show args)) ++ ")"
283 |     show (LExtPrim fc lazy p args)
284 |         = "%extprim " ++ show p ++ showLazy lazy ++ "(" ++ showSep ", " (map show args) ++ ")"
285 |     show (LConCase fc sc alts def)
286 |         = "%case " ++ show sc ++ " of { "
287 |              ++ showSep "| " (map show alts) ++ " " ++ show def
288 |     show (LConstCase fc sc alts def)
289 |         = "%case " ++ show sc ++ " of { "
290 |              ++ showSep "| " (map show alts) ++ " " ++ show def
291 |     show (LPrimVal _ x) = show x
292 |     show (LErased _) = "___"
293 |     show (LCrash _ x) = "%CRASH(" ++ show x ++ ")"
294 |
295 |   export
296 |   covering
297 |   {vs : _} -> Show (LiftedConAlt vs) where
298 |     show (MkLConAlt n _ t args sc)
299 |         = "%conalt " ++ show n ++
300 |              "(" ++ showSep ", " (map show args) ++ ") => " ++ show sc
301 |
302 |   export
303 |   covering
304 |   {vs : _} -> Show (LiftedConstAlt vs) where
305 |     show (MkLConstAlt c sc)
306 |         = "%constalt(" ++ show c ++ ") => " ++ show sc
307 |
308 | export
309 | covering
310 | Show LiftedDef where
311 |   show (MkLFun args scope exp)
312 |       = show args ++ show (reverse scope) ++ ": " ++ show exp
313 |   show (MkLCon tag arity pos)
314 |       = "Constructor tag " ++ show tag ++ " arity " ++ show arity ++
315 |         maybe "" (\n => " (newtype by " ++ show n ++ ")") pos
316 |   show (MkLForeign ccs args ret)
317 |       = "Foreign call " ++ show ccs ++ " " ++
318 |         show args ++ " -> " ++ show ret
319 |   show (MkLError exp) = "Error: " ++ show exp
320 |
321 |
322 | data Lifts : Type where
323 |
324 | record LDefs where
325 |   constructor MkLDefs
326 |   basename : Name -- top level name we're lifting from
327 |   defs : List (Name, LiftedDef) -- new definitions we made
328 |   nextName : Int -- name of next definition to lift
329 |
330 | genName : {auto l : Ref Lifts LDefs} ->
331 |           Core Name
332 | genName
333 |     = do ldefs <- get Lifts
334 |          let i = nextName ldefs
335 |          put Lifts ({ nextName := i + 1 } ldefs)
336 |          pure $ mkName (basename ldefs) i
337 |   where
338 |     mkName : Name -> Int -> Name
339 |     mkName (NS ns b) i = NS ns (mkName b i)
340 |     mkName (UN n) i = MN (displayUserName n) i
341 |     mkName (DN _ n) i = mkName n i
342 |     mkName (CaseBlock outer inner) i = MN ("case block in " ++ outer ++ " (" ++ show inner ++ ")") i
343 |     mkName (WithBlock outer inner) i = MN ("with block in " ++ outer ++ " (" ++ show inner ++ ")") i
344 |     mkName n i = MN (show n) i
345 |
346 | unload : FC -> (lazy : Maybe LazyReason) -> Lifted vars -> List (Lifted vars) -> Core (Lifted vars)
347 | unload fc _ f [] = pure f
348 | -- only outermost LApp must be lazy as rest will be closures
349 | unload fc lazy f (a :: as) = unload fc Nothing (LApp fc lazy f a) as
350 |
351 | record Used (vars : Scope) where
352 |   constructor MkUsed
353 |   used : Vect (length vars) Bool
354 |
355 | initUsed : {vars : _} -> Used vars
356 | initUsed {vars} = MkUsed (replicate (length vars) False)
357 |
358 | weakenUsed : {outer : _} -> Used vars -> Used (outer ++ vars)
359 | weakenUsed {outer} (MkUsed xs) =
360 |   MkUsed (rewrite lengthDistributesOverAppend outer vars in
361 |          (replicate (length outer) False ++ xs))
362 |
363 | contractUsed : (Used (x::vars)) -> Used vars
364 | contractUsed (MkUsed xs) = MkUsed (tail xs)
365 |
366 | contractUsedMany : {remove : _} ->
367 |                    (Used (remove ++ vars)) ->
368 |                    Used vars
369 | contractUsedMany {remove=[]} x = x
370 | contractUsedMany {remove=(r::rs)} x = contractUsedMany {remove=rs} (contractUsed x)
371 |
372 | markUsed : {vars : _} ->
373 |            (idx : Nat) ->
374 |            {0 prf : IsVar x idx vars} ->
375 |            Used vars ->
376 |            Used vars
377 | markUsed {vars} {prf} idx (MkUsed us) =
378 |   let newUsed = replaceAt (finIdx prf) True us in
379 |   MkUsed newUsed
380 |     where
381 |     finIdx : {vars : _} -> {idx : _} ->
382 |              (0 prf : IsVar x idx vars) ->
383 |              Fin (length vars)
384 |     finIdx {idx=Z} First = FZ
385 |     finIdx {idx=S x} (Later l) = FS (finIdx l)
386 |
387 | getUnused : Used vars ->
388 |             Vect (length vars) Bool
389 | getUnused (MkUsed uv) = map not uv
390 |
391 | total
392 | dropped : (vars : Scope) ->
393 |           (drop : Vect (length vars) Bool) ->
394 |           Scope
395 | dropped [] _ = []
396 | dropped (x::xs) (False::us) = x::(dropped xs us)
397 | dropped (x::xs) (True::us) = dropped xs us
398 |
399 | usedVars : {vars : _} ->
400 |            {auto l : Ref Lifts LDefs} ->
401 |            Used vars ->
402 |            Lifted vars ->
403 |            Used vars
404 | usedVars used (LLocal {idx} fc prf) =
405 |   markUsed {prf} idx used
406 | usedVars used (LAppName fc lazy n args) =
407 |   foldl (usedVars {vars}) used args
408 | usedVars used (LUnderApp fc n miss args) =
409 |   foldl (usedVars {vars}) used args
410 | usedVars used (LApp fc lazy c arg) =
411 |   usedVars (usedVars used arg) c
412 | usedVars used (LLet fc x val sc) =
413 |   let innerUsed = contractUsed $ usedVars (weakenUsed {outer=Scope.single x} used) sc in
414 |       usedVars innerUsed val
415 | usedVars used (LCon fc n ci tag args) =
416 |   foldl (usedVars {vars}) used args
417 | usedVars used (LOp fc lazy fn args) =
418 |   foldl (usedVars {vars}) used args
419 | usedVars used (LExtPrim fc lazy fn args) =
420 |   foldl (usedVars {vars}) used args
421 | usedVars used (LConCase fc sc alts def) =
422 |     let defUsed = maybe used (usedVars used {vars}) def
423 |         scDefUsed = usedVars defUsed sc in
424 |         foldl usedConAlt scDefUsed alts
425 |   where
426 |     usedConAlt : {default Nothing lazy : Maybe LazyReason} ->
427 |                   Used vars -> LiftedConAlt vars -> Used vars
428 |     usedConAlt used (MkLConAlt n ci tag args sc) =
429 |       contractUsedMany {remove=args} (usedVars (weakenUsed used) sc)
430 |
431 | usedVars used (LConstCase fc sc alts def) =
432 |     let defUsed = maybe used (usedVars used {vars}) def
433 |         scDefUsed = usedVars defUsed sc in
434 |         foldl usedConstAlt scDefUsed alts
435 |   where
436 |     usedConstAlt : {default Nothing lazy : Maybe LazyReason} ->
437 |                     Used vars -> LiftedConstAlt vars -> Used vars
438 |     usedConstAlt used (MkLConstAlt c sc) = usedVars used sc
439 | usedVars used (LPrimVal {}) = used
440 | usedVars used (LErased {})  = used
441 | usedVars used (LCrash {})   = used
442 |
443 | dropIdx : {vars : _} ->
444 |           {idx : _} ->
445 |           (outer : Scope) ->
446 |           (unused : Vect (length vars) Bool) ->
447 |           (0 p : IsVar x idx (outer ++ vars)) ->
448 |           Var (outer ++ (dropped vars unused))
449 | dropIdx [] (False::_) First = first
450 | dropIdx [] (True::_) First = assert_total $
451 |   idris_crash "INTERNAL ERROR: Referenced variable marked as unused"
452 | dropIdx [] (False::rest) (Later p) = Var.later $ dropIdx Scope.empty rest p
453 | dropIdx [] (True::rest) (Later p) = dropIdx Scope.empty rest p
454 | dropIdx (_::xs) unused First = first
455 | dropIdx (_::xs) unused (Later p) = Var.later $ dropIdx xs unused p
456 |
457 | dropUnused : {vars : _} ->
458 |              {auto _ : Ref Lifts LDefs} ->
459 |              {outer : Scope} ->
460 |              (unused : Vect (length vars) Bool) ->
461 |              (l : Lifted (outer ++ vars)) ->
462 |              Lifted (outer ++ (dropped vars unused))
463 | dropUnused _ (LPrimVal fc val) = LPrimVal fc val
464 | dropUnused _ (LErased fc) = LErased fc
465 | dropUnused _ (LCrash fc msg) = LCrash fc msg
466 | dropUnused {outer} unused (LLocal fc p) =
467 |   let (MkVar p') = dropIdx outer unused p in LLocal fc p'
468 | dropUnused unused (LCon fc n ci tag args) =
469 |   let args' = map (dropUnused unused) args in
470 |       LCon fc n ci tag args'
471 | dropUnused {outer} unused (LLet fc n val sc) =
472 |   let val' = dropUnused unused val
473 |       sc' = dropUnused {outer=n::outer} (unused) sc in
474 |       LLet fc n val' sc'
475 | dropUnused unused (LApp fc lazy c arg) =
476 |   let c' = dropUnused unused c
477 |       arg' = dropUnused unused arg in
478 |       LApp fc lazy c' arg'
479 | dropUnused unused (LOp fc lazy fn args) =
480 |   let args' = map (dropUnused unused) args in
481 |       LOp fc lazy fn args'
482 | dropUnused unused (LExtPrim fc lazy n args) =
483 |   let args' = map (dropUnused unused) args in
484 |       LExtPrim fc lazy n args'
485 | dropUnused unused (LAppName fc lazy n args) =
486 |   let args' = map (dropUnused unused) args in
487 |       LAppName fc lazy n args'
488 | dropUnused unused (LUnderApp fc n miss args) =
489 |   let args' = map (dropUnused unused) args in
490 |       LUnderApp fc n miss args'
491 | dropUnused {vars} {outer} unused (LConCase fc sc alts def) =
492 |   let alts' = map dropConCase alts in
493 |       LConCase fc (dropUnused unused sc) alts' (map (dropUnused unused) def)
494 |   where
495 |     dropConCase : LiftedConAlt (outer ++ vars) ->
496 |                   LiftedConAlt (outer ++ (dropped vars unused))
497 |     dropConCase (MkLConAlt n ci t args sc) =
498 |       let sc' = (rewrite sym $ appendAssociative args outer vars in sc)
499 |           droppedSc = dropUnused {vars=vars} {outer=args++outer} unused sc' in
500 |       MkLConAlt n ci t args (rewrite appendAssociative args outer (dropped vars unused) in droppedSc)
501 | dropUnused {vars} {outer} unused (LConstCase fc sc alts def) =
502 |   let alts' = map dropConstCase alts in
503 |       LConstCase fc (dropUnused unused sc) alts' (map (dropUnused unused) def)
504 |   where
505 |     dropConstCase : LiftedConstAlt (outer ++ vars) ->
506 |                     LiftedConstAlt (outer ++ (dropped vars unused))
507 |     dropConstCase (MkLConstAlt c val) = MkLConstAlt c (dropUnused unused val)
508 |
509 | mutual
510 |   makeLam : {vars : _} ->
511 |             {auto l : Ref Lifts LDefs} ->
512 |             {doLazyAnnots : Bool} ->
513 |             {default Nothing lazy : Maybe LazyReason} ->
514 |             FC -> (bound : Scope) ->
515 |             CExp (bound ++ vars) -> Core (Lifted vars)
516 |   makeLam fc bound (CLam _ x sc') = makeLam fc {doLazyAnnots} {lazy} (x :: bound) sc'
517 |   makeLam {vars} fc bound sc
518 |       = do scl <- liftExp {doLazyAnnots} {lazy} sc
519 |            -- Find out which variables aren't used in the new definition, and
520 |            -- do not abstract over them in the new definition.
521 |            let scUsedL = usedVars initUsed scl
522 |                unusedContracted = contractUsedMany {remove=bound} scUsedL
523 |                unused = getUnused unusedContracted
524 |                scl' = dropUnused {outer=bound} unused scl
525 |            n <- genName
526 |            update Lifts { defs $= ((n, MkLFun (dropped vars unused) bound scl') ::) }
527 |            pure $ LUnderApp fc n (length bound) (allVars fc vars unused)
528 |     where
529 |
530 |         allPrfs : (vs : Scope) -> SizeOf seen ->
531 |                   (unused : Vect (length vs) Bool) ->
532 |                   List (Var (seen <>> vs))
533 |         allPrfs [] _ _ = []
534 |         allPrfs (v :: vs) p (False::uvs) = mkVarChiply p :: allPrfs vs (p :< _) uvs
535 |         allPrfs (v :: vs) p (True::uvs) = allPrfs vs (p :< _) uvs
536 |
537 |         -- apply to all the variables. 'First' will be first in the last, which
538 |         -- is good, because the most recently bound name is the first argument to
539 |         -- the resulting function
540 |         allVars : FC -> (vs : Scope) -> (unused : Vect (length vs) Bool) -> List (Lifted vs)
541 |         allVars fc vs unused = map (\ (MkVar p) => LLocal fc p) (allPrfs vs [<] unused)
542 |
543 | -- if doLazyAnnots = True then annotate function application with laziness
544 | -- otherwise use old behaviour (thunk is a function)
545 |   liftExp : {vars : _} ->
546 |             {auto l : Ref Lifts LDefs} ->
547 |             {doLazyAnnots : Bool} ->
548 |             {default Nothing lazy : Maybe LazyReason} ->
549 |             CExp vars -> Core (Lifted vars)
550 |   liftExp (CLocal fc prf) = pure $ LLocal fc prf
551 |   liftExp (CRef fc n) = pure $ LAppName fc lazy n [] -- probably shouldn't happen!
552 |   liftExp (CLam fc x sc) = makeLam {doLazyAnnots} {lazy} fc (Scope.single x) sc
553 |   liftExp (CLet fc x _ val sc) = pure $ LLet fc x !(liftExp {doLazyAnnots} val) !(liftExp {doLazyAnnots} sc)
554 |   liftExp (CApp fc (CRef _ n) args) -- names are applied exactly in compileExp
555 |       = pure $ LAppName fc lazy n !(traverse (liftExp {doLazyAnnots}) args)
556 |   liftExp (CApp fc f args)
557 |       = unload fc lazy !(liftExp {doLazyAnnots} f) !(traverse (liftExp {doLazyAnnots}) args)
558 |   liftExp (CCon fc n ci t args) = pure $ LCon fc n ci t !(traverse (liftExp {doLazyAnnots}) args)
559 |   liftExp (COp fc op args)
560 |       = pure $ LOp fc lazy op !(traverseArgs args)
561 |     where
562 |       traverseArgs : Vect n (CExp vars) -> Core (Vect n (Lifted vars))
563 |       traverseArgs [] = pure []
564 |       traverseArgs (a :: as) = pure $ !(liftExp {doLazyAnnots} a) :: !(traverseArgs as)
565 |   liftExp (CExtPrim fc p args) = pure $ LExtPrim fc lazy p !(traverse (liftExp {doLazyAnnots}) args)
566 |   liftExp (CForce fc lazy tm) = if doLazyAnnots
567 |     then liftExp {doLazyAnnots} {lazy = Nothing} tm
568 |     else liftExp {doLazyAnnots} (CApp fc tm [CErased fc])
569 |   liftExp (CDelay fc lazy tm) = if doLazyAnnots
570 |     then liftExp {doLazyAnnots} {lazy = Just lazy} tm
571 |     else liftExp {doLazyAnnots} (CLam fc (MN "act" 0) (weaken tm))
572 |   liftExp (CConCase fc sc alts def)
573 |       = pure $ LConCase fc !(liftExp {doLazyAnnots} sc) !(traverse (liftConAlt {lazy}) alts)
574 |                            !(traverseOpt (liftExp {doLazyAnnots}) def)
575 |     where
576 |       liftConAlt : {default Nothing lazy : Maybe LazyReason} ->
577 |                    CConAlt vars -> Core (LiftedConAlt vars)
578 |       liftConAlt (MkConAlt n ci t args sc) = pure $ MkLConAlt n ci t args !(liftExp {doLazyAnnots} {lazy} sc)
579 |   liftExp (CConstCase fc sc alts def)
580 |       = pure $ LConstCase fc !(liftExp {doLazyAnnots} sc) !(traverse liftConstAlt alts)
581 |                              !(traverseOpt (liftExp {doLazyAnnots}) def)
582 |     where
583 |       liftConstAlt : {default Nothing lazy : Maybe LazyReason} ->
584 |                      CConstAlt vars -> Core (LiftedConstAlt vars)
585 |       liftConstAlt (MkConstAlt c sc) = pure $ MkLConstAlt c !(liftExp {doLazyAnnots} {lazy} sc)
586 |   liftExp (CPrimVal fc c) = pure $ LPrimVal fc c
587 |   liftExp (CErased fc) = pure $ LErased fc
588 |   liftExp (CCrash fc str) = pure $ LCrash fc str
589 |
590 | export
591 | liftBody : {vars : _} -> {doLazyAnnots : Bool} ->
592 |            Name -> CExp vars -> Core (Lifted vars, List (Name, LiftedDef))
593 | liftBody n tm
594 |     = do l <- newRef Lifts (MkLDefs n [] 0)
595 |          tml <- liftExp {doLazyAnnots} {l} tm
596 |          ldata <- get Lifts
597 |          pure (tml, defs ldata)
598 |
599 | export
600 | lambdaLiftDef : (doLazyAnnots : Bool) -> Name -> CDef -> Core (List (Name, LiftedDef))
601 | lambdaLiftDef doLazyAnnots n (MkFun args exp)
602 |     = do (expl, defs) <- liftBody {doLazyAnnots} n exp
603 |          pure ((n, MkLFun args Scope.empty expl) :: defs)
604 | lambdaLiftDef _ n (MkCon t a nt) = pure [(n, MkLCon t a nt)]
605 | lambdaLiftDef _ n (MkForeign ccs fargs ty) = pure [(n, MkLForeign ccs fargs ty)]
606 | lambdaLiftDef doLazyAnnots n (MkError exp)
607 |     = do (expl, defs) <- liftBody {doLazyAnnots} n exp
608 |          pure ((n, MkLError expl) :: defs)
609 |
610 | -- Return the lambda lifted definitions required for the given name.
611 | -- If the name hasn't been compiled yet (via CompileExpr.compileDef) then
612 | -- this will return an empty list
613 | -- An empty list an error, because on success you will always get at least
614 | -- one definition, the lifted definition for the given name.
615 | export
616 | lambdaLift :  (doLazyAnnots : Bool)
617 |            -> (Name,FC,CDef)
618 |            -> Core (List (Name, LiftedDef))
619 | lambdaLift doLazyAnnots (n,_,def) = lambdaLiftDef doLazyAnnots n def
620 |