-
Notifications
You must be signed in to change notification settings - Fork 855
Expand file tree
/
Copy pathEraseClosures.fs
More file actions
821 lines (689 loc) · 32.6 KB
/
EraseClosures.fs
File metadata and controls
821 lines (689 loc) · 32.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
module internal FSharp.Compiler.AbstractIL.ILX.EraseClosures
open Internal.Utilities.Library
open FSharp.Compiler.AbstractIL.ILX.Types
open FSharp.Compiler.AbstractIL.Morphs
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.IlxGenSupport
open FSharp.Compiler.Syntax.PrettyNaming
// --------------------------------------------------------------------
// Erase closures and function types
// by compiling down to code pointers, classes etc.
// --------------------------------------------------------------------
let rec stripUpTo n test dest x =
if n = 0 then
([], x)
else if test x then
let l, r = dest x
let ls, res = stripUpTo (n - 1) test dest r
(l :: ls), res
else
([], x)
// --------------------------------------------------------------------
// Flags. These need to match the various classes etc. in the
// ILX standard library, and the parts
// of the makefile that select the right standard library for a given
// combination of flags.
//
// Beyond this, the translation inserts classes or value classes for
// the closure environment.
// --------------------------------------------------------------------
let destTyLambda =
function
| Lambdas_forall(l, r) -> (l, r)
| _ -> failwith "no"
let isTyLambda =
function
| Lambdas_forall _ -> true
| _ -> false
let isTyApp =
function
| Apps_tyapp _ -> true
| _ -> false
let stripTyLambdasUpTo n lambdas =
stripUpTo n isTyLambda destTyLambda lambdas
// --------------------------------------------------------------------
// Three tables related to indirect calling
// -------------------------------------------------------------------- *)
// Supported indirect calling conventions:
// 1
// 1_1
// 1_1_1
// 1_1_1_1
// 1_1_1_1_1
// plus type applications - up to 7 in one step
// Nb. later code currently takes advantage of the fact that term
// and type applications are never mixed in a single step.
let stripSupportedIndirectCall apps =
match apps with
| Apps_app(x, Apps_app(y, Apps_app(z, Apps_app(w, Apps_app(v, rest))))) -> [], [ x; y; z; w; v ], rest
| Apps_app(x, Apps_app(y, Apps_app(z, Apps_app(w, rest)))) -> [], [ x; y; z; w ], rest
| Apps_app(x, Apps_app(y, Apps_app(z, rest))) -> [], [ x; y; z ], rest
| Apps_app(x, Apps_app(y, rest)) -> [], [ x; y ], rest
| Apps_app(x, rest) -> [], [ x ], rest
| Apps_tyapp _ ->
let maxTyApps = 1
let tys, rest = stripUpTo maxTyApps isTyApp destTyFuncApp apps
tys, [], rest
| rest -> [], [], rest
// Supported conventions for baking closures:
// 0
// 1
// 1_1
// 1_1_1
// 1_1_1_1
// 1_1_1_1_1
// plus type applications - up to 7 in one step
// Nb. later code currently takes advantage of the fact that term
// and type applications are never mixed in a single step.
let stripSupportedAbstraction lambdas =
match lambdas with
| Lambdas_lambda(x, Lambdas_lambda(y, Lambdas_lambda(z, Lambdas_lambda(w, Lambdas_lambda(v, rest))))) -> [], [ x; y; z; w; v ], rest
| Lambdas_lambda(x, Lambdas_lambda(y, Lambdas_lambda(z, Lambdas_lambda(w, rest)))) -> [], [ x; y; z; w ], rest
| Lambdas_lambda(x, Lambdas_lambda(y, Lambdas_lambda(z, rest))) -> [], [ x; y; z ], rest
| Lambdas_lambda(x, Lambdas_lambda(y, rest)) -> [], [ x; y ], rest
| Lambdas_lambda(x, rest) -> [], [ x ], rest
| Lambdas_forall _ ->
let maxTyApps = 1
let tys, rest = stripTyLambdasUpTo maxTyApps lambdas
tys, [], rest
| rest -> [], [], rest
// --------------------------------------------------------------------
// Prelude for function types. Only use System.Func for now, prepare
// for more refined types later.
// --------------------------------------------------------------------
[<Literal>]
let fsharpCoreNamespace = "Microsoft.FSharp.Core"
let mkFuncTypeRef fsharpCoreAssemblyScopeRef n =
if n = 1 then
mkILTyRef (fsharpCoreAssemblyScopeRef, fsharpCoreNamespace + ".FSharpFunc`2")
else
mkILNestedTyRef (fsharpCoreAssemblyScopeRef, [ fsharpCoreNamespace + ".OptimizedClosures" ], "FSharpFunc`" + string (n + 1))
type cenv =
{
ilg: ILGlobals
tref_Func: ILTypeRef[]
mkILTyFuncTy: ILType
addFieldGeneratedAttrs: ILFieldDef -> ILFieldDef
addFieldNeverAttrs: ILFieldDef -> ILFieldDef
addMethodGeneratedAttrs: ILMethodDef -> ILMethodDef
}
override _.ToString() = "<cenv>"
let addMethodGeneratedAttrsToTypeDef cenv (tdef: ILTypeDef) =
tdef.With(
methods =
(tdef.Methods.AsList()
|> List.map (fun md -> md |> cenv.addMethodGeneratedAttrs)
|> mkILMethods)
)
let newIlxPubCloEnv (ilg, addMethodGeneratedAttrs, addFieldGeneratedAttrs, addFieldNeverAttrs) =
{
ilg = ilg
tref_Func = Array.init 10 (fun i -> mkFuncTypeRef ilg.fsharpCoreAssemblyScopeRef (i + 1))
mkILTyFuncTy =
ILType.Boxed(mkILNonGenericTySpec (mkILTyRef (ilg.fsharpCoreAssemblyScopeRef, fsharpCoreNamespace + ".FSharpTypeFunc")))
addMethodGeneratedAttrs = addMethodGeneratedAttrs
addFieldGeneratedAttrs = addFieldGeneratedAttrs
addFieldNeverAttrs = addFieldNeverAttrs
}
let mkILTyFuncTy cenv = cenv.mkILTyFuncTy
let inline private (|IsVoidPtr|_|) ty =
match ty with
| ILType.Ptr ILType.Void -> true
| _ -> false
let private fixVoidPtrForGenericArg (ilg: ILGlobals) ty =
match ty with
| IsVoidPtr -> ilg.typ_IntPtr
| _ -> ty
let mkILFuncTy cenv dty rty =
let dty = fixVoidPtrForGenericArg cenv.ilg dty
let rty = fixVoidPtrForGenericArg cenv.ilg rty
mkILBoxedTy cenv.tref_Func[0] [ dty; rty ]
let mkILCurriedFuncTy cenv dtys rty =
List.foldBack (mkILFuncTy cenv) dtys rty
let typ_Func cenv (dtys: ILType list) rty =
let n = dtys.Length
let tref =
if n <= 10 then
cenv.tref_Func[n - 1]
else
mkFuncTypeRef cenv.ilg.fsharpCoreAssemblyScopeRef n
let dtys = dtys |> List.map (fixVoidPtrForGenericArg cenv.ilg)
let rty = fixVoidPtrForGenericArg cenv.ilg rty
mkILBoxedTy tref (dtys @ [ rty ])
let rec mkTyOfApps cenv apps =
match apps with
| Apps_tyapp _ -> cenv.mkILTyFuncTy
| Apps_app(dty, rest) -> mkILFuncTy cenv dty (mkTyOfApps cenv rest)
| Apps_done rty -> rty
let rec mkTyOfLambdas cenv lam =
match lam with
| Lambdas_return rty -> rty
| Lambdas_lambda(d, r) -> mkILFuncTy cenv d.Type (mkTyOfLambdas cenv r)
| Lambdas_forall _ -> cenv.mkILTyFuncTy
// --------------------------------------------------------------------
// Method to call for a particular multi-application
// --------------------------------------------------------------------
let mkMethSpecForMultiApp cenv (argTys: ILType list, retTy) =
let n = argTys.Length
let formalArgTys = List.mapi (fun i _ -> ILType.TypeVar(uint16 i)) argTys
let formalRetTy = ILType.TypeVar(uint16 n)
let argTys = argTys |> List.map (fixVoidPtrForGenericArg cenv.ilg)
let retTy = fixVoidPtrForGenericArg cenv.ilg retTy
let inst = argTys @ [ retTy ]
if n = 1 then
true, (mkILNonGenericInstanceMethSpecInTy (mkILBoxedTy cenv.tref_Func[0] inst, "Invoke", formalArgTys, formalRetTy))
else
false,
(mkILStaticMethSpecInTy (
mkILFuncTy cenv inst[0] inst[1],
"InvokeFast",
[ mkILCurriedFuncTy cenv formalArgTys formalRetTy ] @ formalArgTys,
formalRetTy,
inst.Tail.Tail
))
let mkCallBlockForMultiValueApp cenv doTailCall (argTys, retTy) =
let callvirt, mr = mkMethSpecForMultiApp cenv (argTys, retTy)
[
(if callvirt then
I_callvirt(doTailCall, mr, None)
else
I_call(doTailCall, mr, None))
]
// --------------------------------------------------------------------
// Translate instructions....
// --------------------------------------------------------------------
let mkLdFreeVar (clospec: IlxClosureSpec) (fv: IlxClosureFreeVar) =
[
mkLdarg0
mkNormalLdfld (mkILFieldSpecInTy (clospec.ILType, fv.fvName, fv.fvType))
]
let mkCallFunc cenv allocLocal numThisGenParams tailness apps =
// "callfunc" and "callclo" instructions become a series of indirect
// calls or a single direct call.
let varCount = numThisGenParams
// Unwind the stack until the arguments given in the apps have
// all been popped off. The apps given to this function is
// what remains after the first "strip" of suitable arguments for the
// first call.
// Loaders and storers are returned in groups. Storers are used to pop
// the arguments off the stack that correspond to all the arguments in
// the apps, and the loaders are used to load them back on.
let rec unwind apps =
match apps with
| Apps_tyapp(actual, rest) ->
let rest = instAppsAux varCount [ actual ] rest
let storers, loaders = unwind rest
[] :: storers, [] :: loaders
| Apps_app(arg, rest) ->
let storers, loaders = unwind rest
let argStorers, argLoaders =
let locn = allocLocal arg
[ mkStloc locn ], [ mkLdloc locn ]
argStorers :: storers, argLoaders :: loaders
| Apps_done _ -> [], []
let rec computePreCall fst n rest (loaders: ILInstr list) =
if fst then
let storers, (loaders2: ILInstr list list) = unwind rest
(List.rev (List.concat storers): ILInstr list), List.concat loaders2
else
stripUpTo
n
(function
| _x :: _y -> true
| _ -> false)
(function
| x :: y -> (x, y)
| _ -> failwith "no!")
loaders
let rec buildApp fst loaders apps =
// Strip off one valid indirect call. [fst] indicates if this is the
// first indirect call we're making. The code below makes use of the
// fact that term and type applications are never currently mixed for
// direct calls.
match stripSupportedIndirectCall apps with
// Type applications: REVIEW: get rid of curried tyapps - just tuple them
| tyargs, [], _ when not (isNil tyargs) ->
// strip again, instantiating as we go. we could do this while we count.
let revInstTyArgs, rest' =
(([], apps), tyargs)
||> List.fold (fun (revArgsSoFar, cs) _ ->
let actual, rest' = destTyFuncApp cs
let rest'' = instAppsAux varCount [ actual ] rest'
((actual :: revArgsSoFar), rest''))
let instTyargs = List.rev revInstTyArgs
let precall, loaders' = computePreCall fst 0 rest' loaders
let doTailCall = andTailness tailness false
let instrs1 =
precall
@ [
I_callvirt(
doTailCall,
(mkILInstanceMethSpecInTy (cenv.mkILTyFuncTy, "Specialize", [], cenv.ilg.typ_Object, instTyargs)),
None
)
]
let instrs1 =
// TyFunc are represented as Specialize<_> methods returning an object.
// For value types, recover result via unbox and load.
// For reference types, recover via cast.
let rtnTy = mkTyOfApps cenv rest'
instrs1 @ [ I_unbox_any rtnTy ]
if doTailCall = Tailcall then
instrs1
else
instrs1 @ buildApp false loaders' rest'
// Term applications
| [], args, rest when not (isNil args) ->
let precall, loaders' = computePreCall fst args.Length rest loaders
let isLast =
(match rest with
| Apps_done _ -> true
| _ -> false)
let rty = mkTyOfApps cenv rest
let doTailCall = andTailness tailness isLast
let preCallBlock = precall
if doTailCall = Tailcall then
let callBlock = mkCallBlockForMultiValueApp cenv doTailCall (args, rty)
preCallBlock @ callBlock
else
let callBlock = mkCallBlockForMultiValueApp cenv doTailCall (args, rty)
let restBlock = buildApp false loaders' rest
preCallBlock @ callBlock @ restBlock
| [], [], Apps_done _rty -> []
| _ -> failwith "*** Error: internal error: unknown indirect calling convention returned by stripSupportedIndirectCall"
buildApp true [] apps
// Fix up I_ret instruction. Generalise to selected instr. Remove tailcalls.
let convReturnInstr ty instr =
match instr with
| I_ret -> [ I_box ty; I_ret ]
| I_call(_, mspec, varargs) -> [ I_call(Normalcall, mspec, varargs) ]
| I_callvirt(_, mspec, varargs) -> [ I_callvirt(Normalcall, mspec, varargs) ]
| I_callconstraint(callvirt, _, ty, mspec, varargs) -> [ I_callconstraint(callvirt, Normalcall, ty, mspec, varargs) ]
| I_calli(_, csig, varargs) -> [ I_calli(Normalcall, csig, varargs) ]
| _ -> [ instr ]
let convILMethodBody (thisClo, boxReturnTy) (il: ILMethodBody) =
// This increase in maxstack is historical, though it's harmless
let newMax =
match thisClo with
| Some _ -> il.MaxStack + 2
| None -> il.MaxStack
let code = il.Code
// Box before returning? e.g. in the case of a TyFunc returning a struct, which
// compiles to a Specialise<_> method returning an object
let code =
match boxReturnTy with
| None -> code
| Some ty -> morphILInstrsInILCode (convReturnInstr ty) code
{ il with
MaxStack = newMax
Code = code
}
let convMethodBody thisClo =
function
| MethodBody.IL il ->
let convil = convILMethodBody (thisClo, None) il.Value
MethodBody.IL(notlazy convil)
| x -> x
let convMethodDef thisClo (md: ILMethodDef) =
let b' = convMethodBody thisClo md.Body
md.With(body = notlazy b')
// --------------------------------------------------------------------
// Make fields for free variables of a type abstraction.
// REVIEW: change type abstractions to use other closure mechanisms.
// --------------------------------------------------------------------
let mkILFreeVarForParam (p: ILParameter) =
let nm =
(match p.Name with
| Some x -> x
| None -> failwith "closure parameters must be given names")
mkILFreeVar (nm, false, p.Type)
let mkILLocalForFreeVar (p: IlxClosureFreeVar) = mkILLocal p.fvType None
let mkILCloFldSpecs _cenv flds =
flds |> Array.map (fun fv -> (fv.fvName, fv.fvType)) |> Array.toList
let mkILCloFldDefs cenv flds =
flds
|> Array.toList
|> List.map (fun fv ->
let fdef = mkILInstanceField (fv.fvName, fv.fvType, None, ILMemberAccess.Public)
if fv.fvCompilerGenerated then
fdef |> cenv.addFieldNeverAttrs |> cenv.addFieldGeneratedAttrs
else
fdef)
// --------------------------------------------------------------------
// Convert a closure. Split and chop if there are too many arguments,
// otherwise build the appropriate kind of thing depending on whether
// it's a type abstraction or a term abstraction.
// --------------------------------------------------------------------
let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo =
let newTypeDefs =
// the following are shared between cases 1 && 2
let nowFields = clo.cloFreeVars
let nowTypeRef = mkILNestedTyRef (ILScopeRef.Local, encl, td.Name)
let nowTy = mkILFormalBoxedTy nowTypeRef td.GenericParams
let nowCloRef = IlxClosureRef(nowTypeRef, clo.cloStructure, nowFields)
let nowCloSpec = mkILFormalCloRef td.GenericParams nowCloRef clo.cloUseStaticField
let nowMethods = List.map (convMethodDef (Some nowCloSpec)) (td.Methods.AsList())
let ilCloCode = clo.cloCode.Value
let cloDebugRange = ilCloCode.DebugRange
let cloImports = ilCloCode.DebugImports
let tyargsl, tmargsl, laterStruct = stripSupportedAbstraction clo.cloStructure
// Adjust all the argument and environment accesses
let rewriteCodeToAccessArgsFromEnv laterCloSpec (argToFreeVarMap: (int * IlxClosureFreeVar) list) =
let il = clo.cloCode.Value
let numLocals = il.Locals.Length
let rewriteInstrToAccessArgsFromEnv instr =
let fixupArg mkEnv mkArg n =
let rec findMatchingArg l c =
match l with
| (m, _) :: t -> if n = m then mkEnv c else findMatchingArg t (c + 1)
| [] -> mkArg (n - argToFreeVarMap.Length + 1)
findMatchingArg argToFreeVarMap 0
match instr with
| I_ldarg n -> fixupArg (fun x -> [ mkLdloc (uint16 (x + numLocals)) ]) (fun x -> [ mkLdarg (uint16 x) ]) (int n)
| I_starg n -> fixupArg (fun x -> [ mkStloc (uint16 (x + numLocals)) ]) (fun x -> [ I_starg(uint16 x) ]) (int n)
| I_ldarga n -> fixupArg (fun x -> [ I_ldloca(uint16 (x + numLocals)) ]) (fun x -> [ I_ldarga(uint16 x) ]) (int n)
| i -> [ i ]
let mainCode = morphILInstrsInILCode rewriteInstrToAccessArgsFromEnv il.Code
let ldenvCode =
argToFreeVarMap
|> List.mapi (fun n (_, fv) -> mkLdFreeVar laterCloSpec fv @ [ mkStloc (uint16 (n + numLocals)) ])
|> List.concat
let code = prependInstrsToCode ldenvCode mainCode
{ il with
Code = code
Locals = il.Locals @ (List.map (snd >> mkILLocalForFreeVar) argToFreeVarMap)
// maxstack may increase by 1 due to environment loads
MaxStack = il.MaxStack + 1
}
match tyargsl, tmargsl, laterStruct with
// CASE 1 - Type abstraction
| _ :: _, [], _ ->
let addedGenParams = tyargsl
let nowReturnTy = (mkTyOfLambdas cenv laterStruct)
// CASE 1a. Split a type abstraction.
// Adjust all the argument and environment accesses
// Actually that special to do here in the type abstraction case
// nb. should combine the term and type abstraction cases for
// to allow for term and type variables to be mixed in a single
// application.
if
(match laterStruct with
| Lambdas_return _ -> false
| _ -> true)
then
let nowStruct =
List.foldBack (fun x y -> Lambdas_forall(x, y)) tyargsl (Lambdas_return nowReturnTy)
let laterTypeName = td.Name + "T"
let laterTypeRef = mkILNestedTyRef (ILScopeRef.Local, encl, laterTypeName)
let laterGenericParams = td.GenericParams @ addedGenParams
let selfFreeVar =
let baseName = CompilerGeneratedName("self" + string nowFields.Length)
let existingNames = nowFields |> Array.map (fun fv -> fv.fvName) |> Set.ofArray
mkILFreeVar (ChooseUniqueName baseName existingNames, true, nowCloSpec.ILType)
let laterFields = Array.append nowFields [| selfFreeVar |]
let laterCloRef = IlxClosureRef(laterTypeRef, laterStruct, laterFields)
let laterCloSpec = mkILFormalCloRef laterGenericParams laterCloRef false
let laterCode = rewriteCodeToAccessArgsFromEnv laterCloSpec [ (0, selfFreeVar) ]
let laterTypeDefs =
convIlxClosureDef
cenv
encl
(td.With(genericParams = laterGenericParams, name = laterTypeName, methods = emptyILMethods, fields = emptyILFields))
{ clo with
cloStructure = laterStruct
cloFreeVars = laterFields
cloCode = notlazy laterCode
}
// This is the code which will get called when then "now"
// arguments get applied. Convert it with the information
// that it is the code for a closure...
let nowInstrs =
// Load up the environment, including self...
[
for fld in nowFields do
yield! mkLdFreeVar nowCloSpec fld
mkLdarg0
// Make the instance of the delegated closure && return it.
// This passes the method type params. as class type params.
I_newobj(laterCloSpec.Constructor, None)
]
let nowCode =
mkILMethodBody (false, [], nowFields.Length + 1, nonBranchingInstrsToCode nowInstrs, cloDebugRange, cloImports)
let nowTypeDefs =
convIlxClosureDef
cenv
encl
td
{ clo with
cloStructure = nowStruct
cloCode = notlazy nowCode
}
let nowTypeDefs = nowTypeDefs |> List.map (addMethodGeneratedAttrsToTypeDef cenv)
nowTypeDefs @ laterTypeDefs
else
// CASE 1b. Build a type application.
let boxReturnTy = Some nowReturnTy (* box prior to all I_ret *)
let convil = convILMethodBody (Some nowCloSpec, boxReturnTy) clo.cloCode.Value
let specializeGenParams = addedGenParams |> List.map stripILGenericParamConstraints
let nowApplyMethDef =
mkILGenericVirtualMethod (
"Specialize",
ILCallingConv.Instance,
ILMemberAccess.Public,
specializeGenParams,
[],
mkILReturn cenv.ilg.typ_Object,
MethodBody.IL(notlazy convil)
)
let ctorMethodDef =
mkILStorageCtor (
[ mkLdarg0; mkNormalCall (mkILCtorMethSpecForTy (cenv.mkILTyFuncTy, [])) ],
nowTy,
mkILCloFldSpecs cenv nowFields |> List.map (fun (name, t) -> (name, t, [])),
ILMemberAccess.Assembly,
None,
None
)
|> cenv.addMethodGeneratedAttrs
let cloTypeDef =
ILTypeDef(
name = td.Name,
genericParams = td.GenericParams,
attributes = td.Attributes,
implements = [],
nestedTypes = emptyILTypeDefs,
layout = ILTypeDefLayout.Auto,
extends = Some cenv.mkILTyFuncTy,
methods = mkILMethods (ctorMethodDef :: nowApplyMethDef :: nowMethods),
fields = mkILFields (mkILCloFldDefs cenv nowFields @ td.Fields.AsList()),
customAttrs = emptyILCustomAttrsStored,
methodImpls = emptyILMethodImpls,
properties = emptyILProperties,
events = emptyILEvents,
securityDecls = emptyILSecurityDecls
)
.WithSpecialName(false)
.WithImport(false)
.WithHasSecurity(false)
.WithAbstract(false)
.WithSealed(true)
.WithInitSemantics(ILTypeInit.BeforeField)
.WithEncoding(ILDefaultPInvokeEncoding.Ansi)
[ cloTypeDef ]
// CASE 2 - Term abstraction
| [], (_ :: _ as nowParams), _ ->
let nowReturnTy = mkTyOfLambdas cenv laterStruct
// CASE 2a - Too Many Term Arguments or Remaining Type arguments - Split the Closure Class in Two
if
(match laterStruct with
| Lambdas_return _ -> false
| _ -> true)
then
let nowStruct =
List.foldBack (fun l r -> Lambdas_lambda(l, r)) nowParams (Lambdas_return nowReturnTy)
let laterTypeName = td.Name + "D"
let laterTypeRef = mkILNestedTyRef (ILScopeRef.Local, encl, laterTypeName)
let laterGenericParams = td.GenericParams
// Number each argument left-to-right, adding one to account for the "this" pointer
let selfFreeVar =
let baseName = CompilerGeneratedName "self"
let existingNames = nowFields |> Array.map (fun fv -> fv.fvName) |> Set.ofArray
mkILFreeVar (ChooseUniqueName baseName existingNames, true, nowCloSpec.ILType)
let argToFreeVarMap =
(0, selfFreeVar)
:: (nowParams |> List.mapi (fun i p -> i + 1, mkILFreeVarForParam p))
let laterFreeVars = argToFreeVarMap |> List.map snd |> List.toArray
let laterFields = Array.append nowFields laterFreeVars
let laterCloRef = IlxClosureRef(laterTypeRef, laterStruct, laterFields)
let laterCloSpec = mkILFormalCloRef laterGenericParams laterCloRef false
let nowInstrs =
[ // Load up the environment
for nowField in nowFields do
yield! mkLdFreeVar nowCloSpec nowField
// Load up all the arguments (including self), which become free variables in the delegated closure
for n, _ in argToFreeVarMap do
mkLdarg (uint16 n)
// Make the instance of the delegated closure && return it.
I_newobj(laterCloSpec.Constructor, None)
]
// This is the code which will first get called.
let nowCode =
mkILMethodBody (
false,
[],
argToFreeVarMap.Length + nowFields.Length,
nonBranchingInstrsToCode nowInstrs,
cloDebugRange,
cloImports
)
let nowTypeDefs =
convIlxClosureDef
cenv
encl
td
{ clo with
cloStructure = nowStruct
cloCode = notlazy nowCode
}
let laterCode = rewriteCodeToAccessArgsFromEnv laterCloSpec argToFreeVarMap
let laterTypeDefs =
convIlxClosureDef
cenv
encl
(td.With(genericParams = laterGenericParams, name = laterTypeName, methods = emptyILMethods, fields = emptyILFields))
{ clo with
cloStructure = laterStruct
cloFreeVars = laterFields
cloCode = notlazy laterCode
}
// add 'compiler generated' to all the methods in the 'now' classes
let nowTypeDefs = nowTypeDefs |> List.map (addMethodGeneratedAttrsToTypeDef cenv)
nowTypeDefs @ laterTypeDefs
else
// CASE 2b - Build an Invoke method
let fixedNowParams =
nowParams
|> List.map (fun (p: ILParameter) ->
{ p with
Type = fixVoidPtrForGenericArg cenv.ilg p.Type
})
let fixedNowReturnTy = fixVoidPtrForGenericArg cenv.ilg nowReturnTy
let nowEnvParentClass =
typ_Func cenv (typesOfILParams fixedNowParams) fixedNowReturnTy
let cloTypeDef =
let convil = convILMethodBody (Some nowCloSpec, None) clo.cloCode.Value
let nowApplyMethDef =
mkILNonGenericVirtualInstanceMethod (
"Invoke",
ILMemberAccess.Public,
fixedNowParams,
mkILReturn fixedNowReturnTy,
MethodBody.IL(notlazy convil)
)
let ctorMethodDef =
mkILStorageCtor (
[ mkLdarg0; mkNormalCall (mkILCtorMethSpecForTy (nowEnvParentClass, [])) ],
nowTy,
mkILCloFldSpecs cenv nowFields |> List.map (fun (name, t) -> (name, t, [])),
ILMemberAccess.Assembly,
None,
cloImports
)
|> cenv.addMethodGeneratedAttrs
ILTypeDef(
name = td.Name,
genericParams = td.GenericParams,
attributes = td.Attributes,
implements = [],
layout = ILTypeDefLayout.Auto,
nestedTypes = emptyILTypeDefs,
extends = Some nowEnvParentClass,
methods = mkILMethods (ctorMethodDef :: nowApplyMethDef :: nowMethods),
fields = mkILFields (mkILCloFldDefs cenv nowFields @ td.Fields.AsList()),
customAttrs = emptyILCustomAttrsStored,
methodImpls = emptyILMethodImpls,
properties = emptyILProperties,
events = emptyILEvents,
securityDecls = emptyILSecurityDecls
)
.WithHasSecurity(false)
.WithSpecialName(false)
.WithAbstract(false)
.WithImport(false)
.WithEncoding(ILDefaultPInvokeEncoding.Ansi)
.WithSealed(true)
.WithInitSemantics(ILTypeInit.BeforeField)
[ cloTypeDef ]
| [], [], Lambdas_return _ ->
// No code is being declared: just bake a (mutable) environment
let cloCodeR =
match td.Extends.Value with
| None -> (mkILNonGenericEmptyCtor (cenv.ilg.typ_Object, None, cloImports)).MethodBody
| Some _ -> convILMethodBody (Some nowCloSpec, None) clo.cloCode.Value
let ctorMethodDef =
let flds = (mkILCloFldSpecs cenv nowFields)
mkILCtor (
ILMemberAccess.Public,
List.map mkILParamNamed flds,
mkMethodBody (
cloCodeR.IsZeroInit,
cloCodeR.Locals,
cloCodeR.MaxStack,
prependInstrsToCode
(List.concat (
List.mapi
(fun n (nm, ty) ->
[
mkLdarg0
mkLdarg (uint16 (n + 1))
mkNormalStfld (mkILFieldSpecInTy (nowTy, nm, ty))
])
flds
))
cloCodeR.Code,
None,
None
)
)
let cloTypeDef =
td.With(
implements = td.Implements,
extends =
(match td.Extends.Value with
| None -> Some cenv.ilg.typ_Object |> notlazy
| _ -> td.Extends),
name = td.Name,
genericParams = td.GenericParams,
methods = mkILMethods (ctorMethodDef :: nowMethods),
fields = mkILFields (mkILCloFldDefs cenv nowFields @ td.Fields.AsList())
)
[ cloTypeDef ]
| a, b, _ ->
failwith (
"Unexpected unsupported abstraction sequence, #tyabs = "
+ string a.Length
+ ", #tmabs = "
+ string b.Length
)
newTypeDefs