forked from auduchinok/fsharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMethodCalls.fs
More file actions
1643 lines (1426 loc) · 88.4 KB
/
Copy pathMethodCalls.fs
File metadata and controls
1643 lines (1426 loc) · 88.4 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
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
/// Logic associated with resolving method calls.
module internal FSharp.Compiler.MethodCalls
open FSharp.Compiler
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.AbstractIL.Internal.Library
open FSharp.Compiler.Range
open FSharp.Compiler.Ast
open FSharp.Compiler.ErrorLogger
open FSharp.Compiler.Lib
open FSharp.Compiler.Infos
open FSharp.Compiler.AccessibilityLogic
open FSharp.Compiler.NameResolution
open FSharp.Compiler.InfoReader
open FSharp.Compiler.Tast
open FSharp.Compiler.Tastops
open FSharp.Compiler.Tastops.DebugPrint
open FSharp.Compiler.TcGlobals
open FSharp.Compiler.TypeRelations
open FSharp.Compiler.AttributeChecking
open Internal.Utilities
#if !NO_EXTENSIONTYPING
open FSharp.Compiler.ExtensionTyping
#endif
//-------------------------------------------------------------------------
// Sets of methods involved in overload resolution and trait constraint
// satisfaction.
//-------------------------------------------------------------------------
/// In the following, 'T gets instantiated to:
/// 1. the expression being supplied for an argument
/// 2. "unit", when simply checking for the existence of an overload that satisfies
/// a signature, or when finding the corresponding witness.
/// Note the parametricity helps ensure that overload resolution doesn't depend on the
/// expression on the callside (though it is in some circumstances allowed
/// to depend on some type information inferred syntactically from that
/// expression, e.g. a lambda expression may be converted to a delegate as
/// an adhoc conversion.
///
/// The bool indicates if named using a '?'
type CallerArg<'T> =
/// CallerArg(ty, range, isOpt, exprInfo)
| CallerArg of TType * range * bool * 'T
member x.Type = (let (CallerArg(ty, _, _, _)) = x in ty)
member x.Range = (let (CallerArg(_, m, _, _)) = x in m)
member x.IsOptional = (let (CallerArg(_, _, isOpt, _)) = x in isOpt)
member x.Expr = (let (CallerArg(_, _, _, expr)) = x in expr)
/// Represents the information about an argument in the method being called
type CalledArg =
{ Position: (int * int)
IsParamArray : bool
OptArgInfo : OptionalArgInfo
CallerInfo : CallerInfo
IsInArg: bool
IsOutArg: bool
ReflArgInfo: ReflectedArgInfo
NameOpt: Ident option
CalledArgumentType : TType }
let CalledArg (pos, isParamArray, optArgInfo, callerInfo, isInArg, isOutArg, nameOpt, reflArgInfo, calledArgTy) =
{ Position=pos
IsParamArray=isParamArray
OptArgInfo=optArgInfo
CallerInfo=callerInfo
IsInArg=isInArg
IsOutArg=isOutArg
ReflArgInfo=reflArgInfo
NameOpt=nameOpt
CalledArgumentType=calledArgTy }
/// Represents a match between a caller argument and a called argument, arising from either
/// a named argument or an unnamed argument.
type AssignedCalledArg<'T> =
{ /// The identifier for a named argument, if any
NamedArgIdOpt : Ident option
/// The called argument in the method
CalledArg: CalledArg
/// The argument on the caller side
CallerArg: CallerArg<'T> }
member x.Position = x.CalledArg.Position
/// Represents the possibilities for a named-setter argument (a property, field, or a record field setter)
type AssignedItemSetterTarget =
| AssignedPropSetter of PropInfo * MethInfo * TypeInst (* the MethInfo is a non-indexer setter property *)
| AssignedILFieldSetter of ILFieldInfo
| AssignedRecdFieldSetter of RecdFieldInfo
/// Represents the resolution of a caller argument as a named-setter argument
type AssignedItemSetter<'T> = AssignedItemSetter of Ident * AssignedItemSetterTarget * CallerArg<'T>
type CallerNamedArg<'T> =
| CallerNamedArg of Ident * CallerArg<'T>
member x.Ident = (let (CallerNamedArg(id, _)) = x in id)
member x.Name = x.Ident.idText
member x.CallerArg = (let (CallerNamedArg(_, a)) = x in a)
//-------------------------------------------------------------------------
// Callsite conversions
//-------------------------------------------------------------------------
// F# supports three adhoc conversions at method callsites (note C# supports more, though ones
// such as implicit conversions interact badly with type inference).
//
// 1. The use of "(fun x y -> ...)" when a delegate it expected. This is not part of
// the ":>" coercion relationship or inference constraint problem as
// such, but is a special rule applied only to method arguments.
//
// The function AdjustCalledArgType detects this case based on types and needs to know that the type being applied
// is a function type.
//
// 2. The use of "(fun x y -> ...)" when Expression<delegate> it expected. This is similar to above.
//
// 3. Two ways to pass a value where a byref is expected. The first (default)
// is to use a reference cell, and the interior address is taken automatically
// The second is an explicit use of the "address-of" operator "&e". Here we detect the second case,
// and record the presence of the syntax "&e" in the pre-inferred actual type for the method argument.
// The function AdjustCalledArgType detects this and refuses to apply the default byref-to-ref transformation.
//
// The function AdjustCalledArgType also adjusts for optional arguments.
let AdjustCalledArgType (infoReader: InfoReader) isConstraint (calledArg: CalledArg) (callerArg: CallerArg<_>) =
let g = infoReader.g
// #424218 - when overload resolution is part of constraint solving - do not perform type-directed conversions
let calledArgTy = calledArg.CalledArgumentType
let callerArgTy = callerArg.Type
let m = callerArg.Range
if isConstraint then
calledArgTy
else
// If the called method argument is an inref type, then the caller may provide a byref or value
if isInByrefTy g calledArgTy then
#if IMPLICIT_ADDRESS_OF
if isByrefTy g callerArgTy then
calledArgTy
else
destByrefTy g calledArgTy
#else
calledArgTy
#endif
// If the called method argument is a (non inref) byref type, then the caller may provide a byref or ref.
elif isByrefTy g calledArgTy then
if isByrefTy g callerArgTy then
calledArgTy
else
mkRefCellTy g (destByrefTy g calledArgTy)
else
// If the called method argument is a delegate type, and the caller is known to be a function type, then the caller may provide a function
// If the called method argument is an Expression<T> type, and the caller is known to be a function type, then the caller may provide a T
// If the called method argument is an [<AutoQuote>] Quotations.Expr<T>, and the caller is not known to be a quoted expression type, then the caller may provide a T
let calledArgTy =
let adjustDelegateTy calledTy =
let (SigOfFunctionForDelegate(_, delArgTys, _, fty)) = GetSigOfFunctionForDelegate infoReader calledTy m AccessibleFromSomewhere
let delArgTys = if isNil delArgTys then [g.unit_ty] else delArgTys
if (fst (stripFunTy g callerArgTy)).Length = delArgTys.Length
then fty
else calledArgTy
if isDelegateTy g calledArgTy && isFunTy g callerArgTy then
adjustDelegateTy calledArgTy
elif isLinqExpressionTy g calledArgTy && isFunTy g callerArgTy then
let origArgTy = calledArgTy
let calledArgTy = destLinqExpressionTy g calledArgTy
if isDelegateTy g calledArgTy then
adjustDelegateTy calledArgTy
else
// BUG 435170: called arg is Expr<'t> where 't is not delegate - such conversion is not legal -> return original type
origArgTy
elif calledArg.ReflArgInfo.AutoQuote && isQuotedExprTy g calledArgTy && not (isQuotedExprTy g callerArgTy) then
destQuotedExprTy g calledArgTy
else calledArgTy
// Adjust the called argument type to take into account whether the caller's argument is M(?arg=Some(3)) or M(arg=1)
// If the called method argument is Callee-side optional with type Option<T>, and the caller argument is not explicitly optional (callerArg.IsOptional), then the caller may provide a T
// If the called method argument is Caller-side optional with type Nullable<T>, and the caller argument is not explicitly optional (callerArg.IsOptional), then the caller may provide a T
let calledArgTy =
match calledArg.OptArgInfo with
| NotOptional -> calledArgTy
| CalleeSide when not callerArg.IsOptional && isOptionTy g calledArgTy -> destOptionTy g calledArgTy
// This will be added in https://github.com/dotnet/fsharp/pull/7276
//| CallerSide _ when not callerArg.IsOptional && isNullableTy g calledArgTy -> destNullableTy g calledArgTy
| CalleeSide
| CallerSide _ -> calledArgTy
calledArgTy
//-------------------------------------------------------------------------
// CalledMeth
//-------------------------------------------------------------------------
type CalledMethArgSet<'T> =
{ /// The called arguments corresponding to "unnamed" arguments
UnnamedCalledArgs : CalledArg list
/// Any unnamed caller arguments not otherwise assigned
UnnamedCallerArgs : CallerArg<'T> list
/// The called "ParamArray" argument, if any
ParamArrayCalledArgOpt : CalledArg option
/// Any unnamed caller arguments assigned to a "param array" argument
ParamArrayCallerArgs : CallerArg<'T> list
/// Named args
AssignedNamedArgs: AssignedCalledArg<'T> list }
member x.NumUnnamedCallerArgs = x.UnnamedCallerArgs.Length
member x.NumAssignedNamedArgs = x.AssignedNamedArgs.Length
member x.NumUnnamedCalledArgs = x.UnnamedCalledArgs.Length
let MakeCalledArgs amap m (minfo: MethInfo) minst =
// Mark up the arguments with their position, so we can sort them back into order later
let paramDatas = minfo.GetParamDatas(amap, m, minst)
paramDatas |> List.mapiSquared (fun i j (ParamData(isParamArrayArg, isInArg, isOutArg, optArgInfo, callerInfoFlags, nmOpt, reflArgInfo, typeOfCalledArg)) ->
{ Position=(i,j)
IsParamArray=isParamArrayArg
OptArgInfo=optArgInfo
CallerInfo = callerInfoFlags
IsInArg=isInArg
IsOutArg=isOutArg
ReflArgInfo=reflArgInfo
NameOpt=nmOpt
CalledArgumentType=typeOfCalledArg })
/// Represents the syntactic matching between a caller of a method and the called method.
///
/// The constructor takes all the information about the caller and called side of a method, match up named arguments, property setters etc.,
/// and returns a CalledMeth object for further analysis.
type CalledMeth<'T>
(infoReader: InfoReader,
nameEnv: NameResolutionEnv option,
isCheckingAttributeCall,
freshenMethInfo, // a function to help generate fresh type variables the property setters methods in generic classes
m,
ad, // the access domain of the place where the call is taking place
minfo: MethInfo, // the method we're attempting to call
calledTyArgs, // the 'called type arguments', i.e. the fresh generic instantiation of the method we're attempting to call
callerTyArgs: TType list, // the 'caller type arguments', i.e. user-given generic instantiation of the method we're attempting to call
pinfoOpt: PropInfo option, // the property related to the method we're attempting to call, if any
callerObjArgTys: TType list, // the types of the actual object argument, if any
curriedCallerArgs: (CallerArg<'T> list * CallerNamedArg<'T> list) list, // the data about any arguments supplied by the caller
allowParamArgs: bool, // do we allow the use of a param args method in its "expanded" form?
allowOutAndOptArgs: bool, // do we allow the use of the transformation that converts out arguments as tuple returns?
tyargsOpt : TType option) // method parameters
=
let g = infoReader.g
let methodRetTy = minfo.GetFSharpReturnTy(infoReader.amap, m, calledTyArgs)
let fullCurriedCalledArgs = MakeCalledArgs infoReader.amap m minfo calledTyArgs
do assert (fullCurriedCalledArgs.Length = fullCurriedCalledArgs.Length)
let argSetInfos =
(curriedCallerArgs, fullCurriedCalledArgs) ||> List.map2 (fun (unnamedCallerArgs, namedCallerArgs) fullCalledArgs ->
// Find the arguments not given by name
let unnamedCalledArgs =
fullCalledArgs |> List.filter (fun calledArg ->
match calledArg.NameOpt with
| Some nm -> namedCallerArgs |> List.forall (fun (CallerNamedArg(nm2, _e)) -> nm.idText <> nm2.idText)
| None -> true)
// See if any of them are 'out' arguments being returned as part of a return tuple
let minArgs, unnamedCalledArgs, unnamedCalledOptArgs, unnamedCalledOutArgs =
let nUnnamedCallerArgs = unnamedCallerArgs.Length
let nUnnamedCalledArgs = unnamedCalledArgs.Length
if allowOutAndOptArgs && nUnnamedCallerArgs < nUnnamedCalledArgs then
let unnamedCalledArgsTrimmed, unnamedCalledOptOrOutArgs = List.splitAt nUnnamedCallerArgs unnamedCalledArgs
// Check if all optional/out arguments are byref-out args
if unnamedCalledOptOrOutArgs |> List.forall (fun x -> x.IsOutArg && isByrefTy g x.CalledArgumentType) then
nUnnamedCallerArgs - 1, unnamedCalledArgsTrimmed, [], unnamedCalledOptOrOutArgs
// Check if all optional/out arguments are optional args
elif unnamedCalledOptOrOutArgs |> List.forall (fun x -> x.OptArgInfo.IsOptional) then
nUnnamedCallerArgs - 1, unnamedCalledArgsTrimmed, unnamedCalledOptOrOutArgs, []
// Otherwise drop them on the floor
else
nUnnamedCalledArgs - 1, unnamedCalledArgs, [], []
else
nUnnamedCalledArgs - 1, unnamedCalledArgs, [], []
let (unnamedCallerArgs, paramArrayCallerArgs), unnamedCalledArgs, paramArrayCalledArgOpt =
let supportsParamArgs =
allowParamArgs &&
minArgs >= 0 &&
unnamedCalledArgs |> List.last |> (fun calledArg -> calledArg.IsParamArray && isArray1DTy g calledArg.CalledArgumentType)
if supportsParamArgs && unnamedCallerArgs.Length >= minArgs then
let a, b = List.frontAndBack unnamedCalledArgs
List.splitAt minArgs unnamedCallerArgs, a, Some(b)
else
(unnamedCallerArgs, []), unnamedCalledArgs, None
let assignedNamedArgs =
fullCalledArgs |> List.choose (fun calledArg ->
match calledArg.NameOpt with
| Some nm ->
namedCallerArgs |> List.tryPick (fun (CallerNamedArg(nm2, callerArg)) ->
if nm.idText = nm2.idText then Some { NamedArgIdOpt = Some nm2; CallerArg=callerArg; CalledArg=calledArg }
else None)
| _ -> None)
let unassignedNamedItems =
namedCallerArgs |> List.filter (fun (CallerNamedArg(nm, _e)) ->
fullCalledArgs |> List.forall (fun calledArg ->
match calledArg.NameOpt with
| Some nm2 -> nm.idText <> nm2.idText
| None -> true))
let attributeAssignedNamedItems =
if isCheckingAttributeCall then
// The process for assigning names-->properties is substantially different for attribute specifications
// because it permits the bindings of names to immutable fields. So we use the old
// code for this.
unassignedNamedItems
else
[]
let assignedNamedProps, unassignedNamedItems =
let returnedObjTy = if minfo.IsConstructor then minfo.ApparentEnclosingType else methodRetTy
unassignedNamedItems |> List.splitChoose (fun (CallerNamedArg(id, e) as arg) ->
let nm = id.idText
let pinfos = GetIntrinsicPropInfoSetsOfType infoReader (Some nm) ad AllowMultiIntfInstantiations.Yes IgnoreOverrides id.idRange returnedObjTy
let pinfos = pinfos |> ExcludeHiddenOfPropInfos g infoReader.amap m
match pinfos with
| [pinfo] when pinfo.HasSetter && not pinfo.IsIndexer ->
let pminfo = pinfo.SetterMethod
let pminst = freshenMethInfo m pminfo
Choice1Of2(AssignedItemSetter(id, AssignedPropSetter(pinfo, pminfo, pminst), e))
| _ ->
let epinfos =
match nameEnv with
| Some ne -> ExtensionPropInfosOfTypeInScope ResultCollectionSettings.AllResults infoReader ne (Some nm) ad m returnedObjTy
| _ -> []
match epinfos with
| [pinfo] when pinfo.HasSetter && not pinfo.IsIndexer ->
let pminfo = pinfo.SetterMethod
let pminst = match minfo with
| MethInfo.FSMeth(_, TType.TType_app(_, types), _, _) -> types
| _ -> freshenMethInfo m pminfo
let pminst = match tyargsOpt with
| Some(TType.TType_app(_, types)) -> types
| _ -> pminst
Choice1Of2(AssignedItemSetter(id, AssignedPropSetter(pinfo, pminfo, pminst), e))
| _ ->
match infoReader.GetILFieldInfosOfType(Some(nm), ad, m, returnedObjTy) with
| finfo :: _ ->
Choice1Of2(AssignedItemSetter(id, AssignedILFieldSetter(finfo), e))
| _ ->
match infoReader.TryFindRecdOrClassFieldInfoOfType(nm, m, returnedObjTy) with
| ValueSome rfinfo ->
Choice1Of2(AssignedItemSetter(id, AssignedRecdFieldSetter(rfinfo), e))
| _ ->
Choice2Of2(arg))
let names = namedCallerArgs |> List.map (fun (CallerNamedArg(nm, _)) -> nm.idText)
if (List.noRepeats String.order names).Length <> namedCallerArgs.Length then
errorR(Error(FSComp.SR.typrelNamedArgumentHasBeenAssignedMoreThenOnce(), m))
let argSet = { UnnamedCalledArgs=unnamedCalledArgs; UnnamedCallerArgs=unnamedCallerArgs; ParamArrayCalledArgOpt=paramArrayCalledArgOpt; ParamArrayCallerArgs=paramArrayCallerArgs; AssignedNamedArgs=assignedNamedArgs }
(argSet, assignedNamedProps, unassignedNamedItems, attributeAssignedNamedItems, unnamedCalledOptArgs, unnamedCalledOutArgs))
let argSets = argSetInfos |> List.map (fun (x, _, _, _, _, _) -> x)
let assignedNamedProps = argSetInfos |> List.collect (fun (_, x, _, _, _, _) -> x)
let unassignedNamedItems = argSetInfos |> List.collect (fun (_, _, x, _, _, _) -> x)
let attributeAssignedNamedItems = argSetInfos |> List.collect (fun (_, _, _, x, _, _) -> x)
let unnamedCalledOptArgs = argSetInfos |> List.collect (fun (_, _, _, _, x, _) -> x)
let unnamedCalledOutArgs = argSetInfos |> List.collect (fun (_, _, _, _, _, x) -> x)
member x.infoReader = infoReader
member x.amap = infoReader.amap
/// the method we're attempting to call
member x.Method = minfo
/// the instantiation of the method we're attempting to call
member x.CalledTyArgs = calledTyArgs
/// the instantiation of the method we're attempting to call
member x.CalledTyparInst =
let tps = minfo.FormalMethodTypars
if tps.Length = calledTyArgs.Length then mkTyparInst tps calledTyArgs else []
/// the formal instantiation of the method we're attempting to call
member x.CallerTyArgs = callerTyArgs
/// The types of the actual object arguments, if any
member x.CallerObjArgTys = callerObjArgTys
/// The argument analysis for each set of curried arguments
member x.ArgSets = argSets
/// return type after implicit deference of byref returns is taken into account
member x.CalledReturnTypeAfterByrefDeref =
let retTy = methodRetTy
if isByrefTy g retTy then destByrefTy g retTy else retTy
/// return type after tupling of out args is taken into account
member x.CalledReturnTypeAfterOutArgTupling =
let retTy = x.CalledReturnTypeAfterByrefDeref
if isNil unnamedCalledOutArgs then
retTy
else
let outArgTys = unnamedCalledOutArgs |> List.map (fun calledArg -> destByrefTy g calledArg.CalledArgumentType)
if isUnitTy g retTy then mkRefTupledTy g outArgTys
else mkRefTupledTy g (retTy :: outArgTys)
/// named setters
member x.AssignedItemSetters = assignedNamedProps
/// the property related to the method we're attempting to call, if any
member x.AssociatedPropertyInfo = pinfoOpt
/// unassigned args
member x.UnassignedNamedArgs = unassignedNamedItems
/// args assigned to specify values for attribute fields and properties (these are not necessarily "property sets")
member x.AttributeAssignedNamedArgs = attributeAssignedNamedItems
/// unnamed called optional args: pass defaults for these
member x.UnnamedCalledOptArgs = unnamedCalledOptArgs
/// unnamed called out args: return these as part of the return tuple
member x.UnnamedCalledOutArgs = unnamedCalledOutArgs
static member GetMethod (x: CalledMeth<'T>) = x.Method
member x.NumArgSets = x.ArgSets.Length
member x.HasOptArgs = not (isNil x.UnnamedCalledOptArgs)
member x.HasOutArgs = not (isNil x.UnnamedCalledOutArgs)
member x.UsesParamArrayConversion = x.ArgSets |> List.exists (fun argSet -> argSet.ParamArrayCalledArgOpt.IsSome)
member x.ParamArrayCalledArgOpt = x.ArgSets |> List.tryPick (fun argSet -> argSet.ParamArrayCalledArgOpt)
member x.ParamArrayCallerArgs = x.ArgSets |> List.tryPick (fun argSet -> if Option.isSome argSet.ParamArrayCalledArgOpt then Some argSet.ParamArrayCallerArgs else None )
member x.ParamArrayElementType =
assert (x.UsesParamArrayConversion)
x.ParamArrayCalledArgOpt.Value.CalledArgumentType |> destArrayTy x.amap.g
member x.NumAssignedProps = x.AssignedItemSetters.Length
member x.CalledObjArgTys(m) =
match x.Method.GetObjArgTypes(x.amap, m, x.CalledTyArgs) with
| [ thisArgTy ] when isByrefTy g thisArgTy -> [ destByrefTy g thisArgTy ]
| res -> res
member x.NumCalledTyArgs = x.CalledTyArgs.Length
member x.NumCallerTyArgs = x.CallerTyArgs.Length
member x.AssignsAllNamedArgs = isNil x.UnassignedNamedArgs
member x.HasCorrectArity =
(x.NumCalledTyArgs = x.NumCallerTyArgs) &&
x.ArgSets |> List.forall (fun argSet -> argSet.NumUnnamedCalledArgs = argSet.NumUnnamedCallerArgs)
member x.HasCorrectGenericArity =
(x.NumCalledTyArgs = x.NumCallerTyArgs)
member x.IsAccessible(m, ad) =
IsMethInfoAccessible x.amap m ad x.Method
member x.HasCorrectObjArgs(m) =
x.CalledObjArgTys(m).Length = x.CallerObjArgTys.Length
member x.IsCandidate(m, ad) =
x.IsAccessible(m, ad) &&
x.HasCorrectArity &&
x.HasCorrectObjArgs(m) &&
x.AssignsAllNamedArgs
member x.AssignedUnnamedArgs =
// We use Seq.map2 to tolerate there being mismatched caller/called args
x.ArgSets |> List.map (fun argSet ->
(argSet.UnnamedCalledArgs, argSet.UnnamedCallerArgs) ||> Seq.map2 (fun calledArg callerArg ->
{ NamedArgIdOpt=None; CalledArg=calledArg; CallerArg=callerArg }) |> Seq.toList)
member x.AssignedNamedArgs =
x.ArgSets |> List.map (fun argSet -> argSet.AssignedNamedArgs)
member x.AllUnnamedCalledArgs = x.ArgSets |> List.collect (fun x -> x.UnnamedCalledArgs)
member x.TotalNumUnnamedCalledArgs = x.ArgSets |> List.sumBy (fun x -> x.NumUnnamedCalledArgs)
member x.TotalNumUnnamedCallerArgs = x.ArgSets |> List.sumBy (fun x -> x.NumUnnamedCallerArgs)
member x.TotalNumAssignedNamedArgs = x.ArgSets |> List.sumBy (fun x -> x.NumAssignedNamedArgs)
let NamesOfCalledArgs (calledArgs: CalledArg list) =
calledArgs |> List.choose (fun x -> x.NameOpt)
//-------------------------------------------------------------------------
// Helpers dealing with propagating type information in method overload resolution
//-------------------------------------------------------------------------
type ArgumentAnalysis =
| NoInfo
| ArgDoesNotMatch
| CallerLambdaHasArgTypes of TType list
| CalledArgMatchesType of TType
let InferLambdaArgsForLambdaPropagation origRhsExpr =
let rec loop e =
match e with
| SynExpr.Lambda (_, _, _, rest, _) -> 1 + loop rest
| SynExpr.MatchLambda _ -> 1
| _ -> 0
loop origRhsExpr
let ExamineArgumentForLambdaPropagation (infoReader: InfoReader) (arg: AssignedCalledArg<SynExpr>) =
let g = infoReader.g
// Find the explicit lambda arguments of the caller. Ignore parentheses.
let argExpr = match arg.CallerArg.Expr with SynExpr.Paren (x, _, _, _) -> x | x -> x
let countOfCallerLambdaArg = InferLambdaArgsForLambdaPropagation argExpr
// Adjust for Expression<_>, Func<_, _>, ...
let adjustedCalledArgTy = AdjustCalledArgType infoReader false arg.CalledArg arg.CallerArg
if countOfCallerLambdaArg > 0 then
// Decompose the explicit function type of the target
let calledLambdaArgTys, _calledLambdaRetTy = Tastops.stripFunTy g adjustedCalledArgTy
if calledLambdaArgTys.Length >= countOfCallerLambdaArg then
// success
CallerLambdaHasArgTypes calledLambdaArgTys
elif isDelegateTy g (if isLinqExpressionTy g adjustedCalledArgTy then destLinqExpressionTy g adjustedCalledArgTy else adjustedCalledArgTy) then
ArgDoesNotMatch // delegate arity mismatch
else
NoInfo // not a function type on the called side - no information
else CalledArgMatchesType(adjustedCalledArgTy) // not a lambda on the caller side - push information from caller to called
let ExamineMethodForLambdaPropagation (x: CalledMeth<SynExpr>) =
let unnamedInfo = x.AssignedUnnamedArgs |> List.mapSquared (ExamineArgumentForLambdaPropagation x.infoReader)
let namedInfo = x.AssignedNamedArgs |> List.mapSquared (fun arg -> (arg.NamedArgIdOpt.Value, ExamineArgumentForLambdaPropagation x.infoReader arg))
if unnamedInfo |> List.existsSquared (function CallerLambdaHasArgTypes _ -> true | _ -> false) ||
namedInfo |> List.existsSquared (function (_, CallerLambdaHasArgTypes _) -> true | _ -> false) then
Some (unnamedInfo, namedInfo)
else
None
//-------------------------------------------------------------------------
// Adjust caller arguments as part of building a method call
//-------------------------------------------------------------------------
/// Build a call to the System.Object constructor taking no arguments,
let BuildObjCtorCall (g: TcGlobals) m =
let ilMethRef = (mkILCtorMethSpecForTy(g.ilg.typ_Object, [])).MethodRef
Expr.Op (TOp.ILCall (false, false, false, false, CtorValUsedAsSuperInit, false, true, ilMethRef, [], [], [g.obj_ty]), [], [], m)
/// Implements the elaborated form of adhoc conversions from functions to delegates at member callsites
let BuildNewDelegateExpr (eventInfoOpt: EventInfo option, g, amap, delegateTy, invokeMethInfo: MethInfo, delArgTys, f, fty, m) =
let slotsig = invokeMethInfo.GetSlotSig(amap, m)
let delArgVals, expr =
let topValInfo = ValReprInfo([], List.replicate (max 1 (List.length delArgTys)) ValReprInfo.unnamedTopArg, ValReprInfo.unnamedRetVal)
// Try to pull apart an explicit lambda and use it directly
// Don't do this in the case where we're adjusting the arguments of a function used to build a .NET-compatible event handler
let lambdaContents =
if Option.isSome eventInfoOpt then
None
else
tryDestTopLambda g amap topValInfo (f, fty)
match lambdaContents with
| None ->
if List.exists (isByrefTy g) delArgTys then
error(Error(FSComp.SR.tcFunctionRequiresExplicitLambda(List.length delArgTys), m))
let delArgVals = delArgTys |> List.mapi (fun i argty -> fst (mkCompGenLocal m ("delegateArg" + string i) argty))
let expr =
let args =
match eventInfoOpt with
| Some einfo ->
match delArgVals with
| [] -> error(nonStandardEventError einfo.EventName m)
| h :: _ when not (isObjTy g h.Type) -> error(nonStandardEventError einfo.EventName m)
| h :: t -> [exprForVal m h; mkRefTupledVars g m t]
| None ->
if isNil delArgTys then [mkUnit g m] else List.map (exprForVal m) delArgVals
mkApps g ((f, fty), [], args, m)
delArgVals, expr
| Some _ ->
let _, _, _, vsl, body, _ = IteratedAdjustArityOfLambda g amap topValInfo f
List.concat vsl, body
let meth = TObjExprMethod(slotsig, [], [], [delArgVals], expr, m)
mkObjExpr(delegateTy, None, BuildObjCtorCall g m, [meth], [], m)
let CoerceFromFSharpFuncToDelegate g amap infoReader ad callerArgTy m callerArgExpr delegateTy =
let (SigOfFunctionForDelegate(invokeMethInfo, delArgTys, _, _)) = GetSigOfFunctionForDelegate infoReader delegateTy m ad
BuildNewDelegateExpr (None, g, amap, delegateTy, invokeMethInfo, delArgTys, callerArgExpr, callerArgTy, m)
// Handle adhoc argument conversions
let AdjustCallerArgExprForCoercions (g: TcGlobals) amap infoReader ad isOutArg calledArgTy (reflArgInfo: ReflectedArgInfo) callerArgTy m callerArgExpr =
if isByrefTy g calledArgTy && isRefCellTy g callerArgTy then
None, Expr.Op (TOp.RefAddrGet false, [destRefCellTy g callerArgTy], [callerArgExpr], m)
#if IMPLICIT_ADDRESS_OF
elif isInByrefTy g calledArgTy && not (isByrefTy g callerArgTy) then
let wrap, callerArgExprAddress, _readonly, _writeonly = mkExprAddrOfExpr g true false NeverMutates callerArgExpr None m
Some wrap, callerArgExprAddress
#endif
elif isDelegateTy g calledArgTy && isFunTy g callerArgTy then
None, CoerceFromFSharpFuncToDelegate g amap infoReader ad callerArgTy m callerArgExpr calledArgTy
elif isLinqExpressionTy g calledArgTy && isDelegateTy g (destLinqExpressionTy g calledArgTy) && isFunTy g callerArgTy then
let delegateTy = destLinqExpressionTy g calledArgTy
let expr = CoerceFromFSharpFuncToDelegate g amap infoReader ad callerArgTy m callerArgExpr delegateTy
None, mkCallQuoteToLinqLambdaExpression g m delegateTy (Expr.Quote (expr, ref None, false, m, mkQuotedExprTy g delegateTy))
// auto conversions to quotations (to match auto conversions to LINQ expressions)
elif reflArgInfo.AutoQuote && isQuotedExprTy g calledArgTy && not (isQuotedExprTy g callerArgTy) then
match reflArgInfo with
| ReflectedArgInfo.Quote true ->
None, mkCallLiftValueWithDefn g m calledArgTy callerArgExpr
| ReflectedArgInfo.Quote false ->
None, Expr.Quote (callerArgExpr, ref None, false, m, calledArgTy)
| ReflectedArgInfo.None -> failwith "unreachable" // unreachable due to reflArgInfo.AutoQuote condition
// Note: out args do not need to be coerced
elif isOutArg then
None, callerArgExpr
// Note: not all these casts are reported in quotations
else
None, mkCoerceIfNeeded g calledArgTy callerArgTy callerArgExpr
// Handle CallerSide optional arguments.
//
// CallerSide optional arguments are largely for COM interop, e.g. to PIA assemblies for Word etc.
// As a result we follow the VB and C# behavior here.
//
// "1. If the parameter is statically typed as System.Object and does not have a value, then there are four cases:
// a. The parameter is marked with MarshalAs(IUnknown), MarshalAs(Interface), or MarshalAs(IDispatch). In this case we pass null.
// b. Else if the parameter is marked with IUnknownConstantAttribute. In this case we pass new System.Runtime.InteropServices.UnknownWrapper(null)
// c. Else if the parameter is marked with IDispatchConstantAttribute. In this case we pass new System.Runtime.InteropServices.DispatchWrapper(null)
// d. Else, we will pass Missing.Value.
// 2. Otherwise, if there is a value attribute, then emit the default value.
// 3. Otherwise, we emit default(T).
// 4. Finally, we apply conversions from the value to the parameter type. This is where the nullable conversions take place for VB.
// - VB allows you to mark ref parameters as optional. The semantics of this is that we create a temporary
// with type = type of parameter, load the optional value to it, and call the method.
// - VB also allows you to mark arrays with Nothing as the optional value.
// - VB also allows you to pass intrinsic values as optional values to parameters
// typed as Object. What we do in this case is we box the intrinsic value."
//
let AdjustOptionalCallerArgExprs tcFieldInit eCallerMemberName g (calledMeth: CalledMeth<_>) mItem mMethExpr =
let assignedNamedArgs = calledMeth.ArgSets |> List.collect (fun argSet -> argSet.AssignedNamedArgs)
let unnamedCalledArgs = calledMeth.ArgSets |> List.collect (fun argSet -> argSet.UnnamedCalledArgs)
let unnamedCallerArgs = calledMeth.ArgSets |> List.collect (fun argSet -> argSet.UnnamedCallerArgs)
let unnamedArgs =
(unnamedCalledArgs, unnamedCallerArgs) ||> List.map2 (fun called caller ->
{ NamedArgIdOpt = None; CalledArg=called; CallerArg=caller })
let emptyPreBinder (e: Expr) = e
// Adjust all the optional arguments that require a default value to be inserted into the call
let optArgs, optArgPreBinder =
(emptyPreBinder, calledMeth.UnnamedCalledOptArgs) ||> List.mapFold (fun wrapper calledArg ->
let calledArgTy = calledArg.CalledArgumentType
let wrapper2, expr =
match calledArg.OptArgInfo with
| NotOptional ->
error(InternalError("Unexpected NotOptional", mItem))
| CallerSide dfltVal ->
let rec build currCalledArgTy currDfltVal =
match currDfltVal with
| MissingValue ->
// Add an I_nop if this is an initonly field to make sure we never recognize it as an lvalue. See mkExprAddrOfExpr.
emptyPreBinder, mkAsmExpr ([ mkNormalLdsfld (fspec_Missing_Value g); AI_nop ], [], [], [currCalledArgTy], mMethExpr)
| DefaultValue ->
emptyPreBinder, mkDefault(mMethExpr, currCalledArgTy)
| Constant fieldInit ->
match currCalledArgTy with
| NullableTy g inst when fieldInit <> ILFieldInit.Null ->
let nullableTy = mkILNonGenericBoxedTy(g.FindSysILTypeRef "System.Nullable`1")
let ctor = mkILCtorMethSpecForTy(nullableTy, [ILType.TypeVar 0us]).MethodRef
let ctorArgs = [Expr.Const (tcFieldInit mMethExpr fieldInit, mMethExpr, inst)]
emptyPreBinder, Expr.Op (TOp.ILCall (false, false, true, true, NormalValUse, false, false, ctor, [inst], [], [currCalledArgTy]), [], ctorArgs, mMethExpr)
| ByrefTy g inst ->
build inst (PassByRef(inst, currDfltVal))
| _ ->
match calledArg.CallerInfo, eCallerMemberName with
| CallerLineNumber, _ when typeEquiv g currCalledArgTy g.int_ty ->
emptyPreBinder, Expr.Const (Const.Int32(mMethExpr.StartLine), mMethExpr, currCalledArgTy)
| CallerFilePath, _ when typeEquiv g currCalledArgTy g.string_ty ->
let fileName = mMethExpr.FileName |> FileSystem.GetFullPathShim |> PathMap.apply g.pathMap
emptyPreBinder, Expr.Const (Const.String fileName, mMethExpr, currCalledArgTy)
| CallerMemberName, Some callerName when (typeEquiv g currCalledArgTy g.string_ty) ->
emptyPreBinder, Expr.Const (Const.String callerName, mMethExpr, currCalledArgTy)
| _ ->
emptyPreBinder, Expr.Const (tcFieldInit mMethExpr fieldInit, mMethExpr, currCalledArgTy)
| WrapperForIDispatch ->
match g.TryFindSysILTypeRef "System.Runtime.InteropServices.DispatchWrapper" with
| None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr))
| Some tref ->
let ty = mkILNonGenericBoxedTy tref
let mref = mkILCtorMethSpecForTy(ty, [g.ilg.typ_Object]).MethodRef
let expr = Expr.Op (TOp.ILCall (false, false, false, true, NormalValUse, false, false, mref, [], [], [g.obj_ty]), [], [mkDefault(mMethExpr, currCalledArgTy)], mMethExpr)
emptyPreBinder, expr
| WrapperForIUnknown ->
match g.TryFindSysILTypeRef "System.Runtime.InteropServices.UnknownWrapper" with
| None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr))
| Some tref ->
let ty = mkILNonGenericBoxedTy tref
let mref = mkILCtorMethSpecForTy(ty, [g.ilg.typ_Object]).MethodRef
let expr = Expr.Op (TOp.ILCall (false, false, false, true, NormalValUse, false, false, mref, [], [], [g.obj_ty]), [], [mkDefault(mMethExpr, currCalledArgTy)], mMethExpr)
emptyPreBinder, expr
| PassByRef (ty, dfltVal2) ->
let v, _ = mkCompGenLocal mMethExpr "defaultByrefArg" ty
let wrapper2, rhs = build currCalledArgTy dfltVal2
(wrapper2 >> mkCompGenLet mMethExpr v rhs), mkValAddr mMethExpr false (mkLocalValRef v)
build calledArgTy dfltVal
| CalleeSide ->
let calledNonOptTy =
if isOptionTy g calledArgTy then
destOptionTy g calledArgTy
else
calledArgTy // should be unreachable
match calledArg.CallerInfo, eCallerMemberName with
| CallerLineNumber, _ when typeEquiv g calledNonOptTy g.int_ty ->
let lineExpr = Expr.Const(Const.Int32 mMethExpr.StartLine, mMethExpr, calledNonOptTy)
emptyPreBinder, mkSome g calledNonOptTy lineExpr mMethExpr
| CallerFilePath, _ when typeEquiv g calledNonOptTy g.string_ty ->
let fileName = mMethExpr.FileName |> FileSystem.GetFullPathShim |> PathMap.apply g.pathMap
let filePathExpr = Expr.Const (Const.String(fileName), mMethExpr, calledNonOptTy)
emptyPreBinder, mkSome g calledNonOptTy filePathExpr mMethExpr
| CallerMemberName, Some(callerName) when typeEquiv g calledNonOptTy g.string_ty ->
let memberNameExpr = Expr.Const (Const.String callerName, mMethExpr, calledNonOptTy)
emptyPreBinder, mkSome g calledNonOptTy memberNameExpr mMethExpr
| _ ->
emptyPreBinder, mkNone g calledNonOptTy mMethExpr
// Combine the variable allocators (if any)
let wrapper = (wrapper >> wrapper2)
let callerArg = CallerArg(calledArgTy, mMethExpr, false, expr)
{ NamedArgIdOpt = None; CalledArg = calledArg; CallerArg = callerArg }, wrapper)
// Adjust all the optional arguments
let wrapOptionalArg (assignedArg: AssignedCalledArg<_>) =
let (CallerArg(callerArgTy, m, isOptCallerArg, callerArgExpr)) = assignedArg.CallerArg
match assignedArg.CalledArg.OptArgInfo with
| NotOptional ->
if isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m))
assignedArg
| _ ->
let callerArgExpr2 =
match assignedArg.CalledArg.OptArgInfo with
| CallerSide _ ->
if isOptCallerArg then
// M(?x=bopt) when M(A) --> M(?x=bopt.Value) for caller-side
// STRUCT OPTIONS: if we allow struct options as optional arguments then we should take
// the address correctly.
mkUnionCaseFieldGetUnprovenViaExprAddr (callerArgExpr, mkSomeCase g, [destOptionTy g callerArgTy], 0, m)
else
// M(x=b) when M(A) --> M(?x=b) for caller-side
callerArgExpr
| CalleeSide ->
if isOptCallerArg then
// M(?x=bopt) when M(A) --> M(?x=Some(bopt.Value))
callerArgExpr
else
// M(x=b) when M(A) --> M(?x=Some(b :> A))
let calledArgTy = assignedArg.CalledArg.CalledArgumentType
if isOptionTy g calledArgTy then
let calledNonOptTy = destOptionTy g calledArgTy
mkSome g calledNonOptTy (mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr) m
else
callerArgExpr // should be unreachable
| _ -> failwith "Unreachable"
{ assignedArg with CallerArg=CallerArg(tyOfExpr g callerArgExpr2, m, isOptCallerArg, callerArgExpr2) }
let adjustedNormalUnnamedArgs = List.map wrapOptionalArg unnamedArgs
let adjustedAssignedNamedArgs = List.map wrapOptionalArg assignedNamedArgs
optArgs, optArgPreBinder, adjustedNormalUnnamedArgs, adjustedAssignedNamedArgs
/// Adjust any 'out' arguments, passing in the address of a mutable local
let AdjustOutCallerArgExprs g (calledMeth: CalledMeth<_>) mMethExpr =
calledMeth.UnnamedCalledOutArgs |> List.map (fun calledArg ->
let calledArgTy = calledArg.CalledArgumentType
let outArgTy = destByrefTy g calledArgTy
let outv, outArgExpr = mkMutableCompGenLocal mMethExpr PrettyNaming.outArgCompilerGeneratedName outArgTy // mutable!
let expr = mkDefault (mMethExpr, outArgTy)
let callerArg = CallerArg (calledArgTy, mMethExpr, false, mkValAddr mMethExpr false (mkLocalValRef outv))
let outArg = { NamedArgIdOpt=None;CalledArg=calledArg;CallerArg=callerArg }
outArg, outArgExpr, mkCompGenBind outv expr)
|> List.unzip3
let AdjustParamArrayCallerArgExprs g amap infoReader ad (calledMeth: CalledMeth<_>) mMethExpr =
let argSets = calledMeth.ArgSets
let paramArrayCallerArgs = argSets |> List.collect (fun argSet -> argSet.ParamArrayCallerArgs)
match calledMeth.ParamArrayCalledArgOpt with
| None ->
[], []
| Some paramArrayCalledArg ->
let paramArrayCalledArgElementType = destArrayTy g paramArrayCalledArg.CalledArgumentType
let paramArrayPreBinders, es =
paramArrayCallerArgs
|> List.map (fun callerArg ->
let (CallerArg(callerArgTy, m, isOutArg, callerArgExpr)) = callerArg
AdjustCallerArgExprForCoercions g amap infoReader ad isOutArg paramArrayCalledArgElementType paramArrayCalledArg.ReflArgInfo callerArgTy m callerArgExpr)
|> List.unzip
let arg =
[ { NamedArgIdOpt = None
CalledArg=paramArrayCalledArg
CallerArg=CallerArg(paramArrayCalledArg.CalledArgumentType, mMethExpr, false, Expr.Op (TOp.Array, [paramArrayCalledArgElementType], es, mMethExpr)) } ]
paramArrayPreBinders, arg
/// Build the argument list for a method call. Adjust for param array, optional arguments, byref arguments and coercions.
/// For example, if you pass an F# reference cell to a byref then we must get the address of the
/// contents of the ref. Likewise lots of adjustments are made for optional arguments etc.
let AdjustCallerArgExprs tcFieldInit eCallerMemberName g amap infoReader ad (calledMeth: CalledMeth<_>) objArgs lambdaVars mItem mMethExpr =
let calledMethInfo = calledMeth.Method
// Some of the code below must allocate temporary variables or bind other variables to particular values.
// As usual we represent variable allocators by expr -> expr functions
// which we then use to wrap the whole expression. These will either do nothing or pre-bind a variable. It doesn't
// matter what order they are applied in as long as they are all composed together.
let emptyPreBinder (e: Expr) = e
// For unapplied 'e.M' we first evaluate 'e' outside the lambda, i.e. 'let v = e in (fun arg -> v.M(arg))'
let objArgPreBinder, objArgs =
match objArgs, lambdaVars with
| [objArg], Some _ ->
if calledMethInfo.IsExtensionMember && calledMethInfo.ObjArgNeedsAddress(amap, mMethExpr) then
error(Error(FSComp.SR.tcCannotPartiallyApplyExtensionMethodForByref(calledMethInfo.DisplayName), mMethExpr))
let objArgTy = tyOfExpr g objArg
let v, ve = mkCompGenLocal mMethExpr "objectArg" objArgTy
(fun body -> mkCompGenLet mMethExpr v objArg body), [ve]
| _ ->
emptyPreBinder, objArgs
// Handle param array and optional arguments
let paramArrayPreBinders, paramArrayArgs =
AdjustParamArrayCallerArgExprs g amap infoReader ad calledMeth mMethExpr
let optArgs, optArgPreBinder, adjustedNormalUnnamedArgs, adjustedFinalAssignedNamedArgs =
AdjustOptionalCallerArgExprs tcFieldInit eCallerMemberName g calledMeth mItem mMethExpr
let outArgs, outArgExprs, outArgTmpBinds =
AdjustOutCallerArgExprs g calledMeth mMethExpr
let allArgs =
adjustedNormalUnnamedArgs @
adjustedFinalAssignedNamedArgs @
paramArrayArgs @
optArgs @
outArgs
let allArgs =
allArgs |> List.sortBy (fun x -> x.Position)
let allArgsPreBinders, allArgsCoerced =
allArgs
|> List.map (fun assignedArg ->
let isOutArg = assignedArg.CalledArg.IsOutArg
let reflArgInfo = assignedArg.CalledArg.ReflArgInfo
let calledArgTy = assignedArg.CalledArg.CalledArgumentType
let (CallerArg(callerArgTy, m, _, e)) = assignedArg.CallerArg
AdjustCallerArgExprForCoercions g amap infoReader ad isOutArg calledArgTy reflArgInfo callerArgTy m e)
|> List.unzip
objArgPreBinder, objArgs, allArgsPreBinders, allArgs, allArgsCoerced, optArgPreBinder, paramArrayPreBinders, outArgExprs, outArgTmpBinds
//-------------------------------------------------------------------------
// Additional helpers for building method calls and doing TAST generation
//-------------------------------------------------------------------------
/// Is this a 'base' call (in the sense of C#)
let IsBaseCall objArgs =
match objArgs with
| [Expr.Val (v, _, _)] when v.BaseOrThisInfo = BaseVal -> true
| _ -> false
/// Compute whether we insert a 'coerce' on the 'this' pointer for an object model call
/// For example, when calling an interface method on a struct, or a method on a constrained
/// variable type.
let ComputeConstrainedCallInfo g amap m (objArgs, minfo: MethInfo) =
match objArgs with
| [objArgExpr] when not minfo.IsExtensionMember ->
let methObjTy = minfo.ApparentEnclosingType
let objArgTy = tyOfExpr g objArgExpr
if TypeDefinitelySubsumesTypeNoCoercion 0 g amap m methObjTy objArgTy
// Constrained calls to class types can only ever be needed for the three class types that
// are base types of value types
|| (isClassTy g methObjTy &&
(not (typeEquiv g methObjTy g.system_Object_ty ||
typeEquiv g methObjTy g.system_Value_ty ||
typeEquiv g methObjTy g.system_Enum_ty))) then
None
else
// The object argument is a value type or variable type and the target method is an interface or System.Object
// type. A .NET 2.0 generic constrained call is required
Some objArgTy
| _ ->
None
/// Adjust the 'this' pointer before making a call
/// Take the address of a struct, and coerce to an interface/base/constraint type if necessary
let TakeObjAddrForMethodCall g amap (minfo: MethInfo) isMutable m objArgs f =
let ccallInfo = ComputeConstrainedCallInfo g amap m (objArgs, minfo)
let wrap, objArgs =
match objArgs with
| [objArgExpr] ->
let hasCallInfo = ccallInfo.IsSome
let mustTakeAddress = hasCallInfo || minfo.ObjArgNeedsAddress(amap, m)
let objArgTy = tyOfExpr g objArgExpr
let isMutable =
match isMutable with
| DefinitelyMutates
| NeverMutates
| AddressOfOp -> isMutable
| PossiblyMutates ->
// Check to see if the method is read-only. Perf optimization.
// If there is an extension member whose first arg is an inref, we must return NeverMutates.
if mustTakeAddress && (minfo.IsReadOnly || minfo.IsReadOnlyExtensionMember (amap, m)) then
NeverMutates
else
isMutable
let wrap, objArgExprAddr, isReadOnly, _isWriteOnly =
mkExprAddrOfExpr g mustTakeAddress hasCallInfo isMutable objArgExpr None m
// Extension members and calls to class constraints may need a coercion for their object argument
let objArgExprCoerced =
if not hasCallInfo &&
not (TypeDefinitelySubsumesTypeNoCoercion 0 g amap m minfo.ApparentEnclosingType objArgTy) then
mkCoerceExpr(objArgExprAddr, minfo.ApparentEnclosingType, m, objArgTy)
else
objArgExprAddr
// Check to see if the extension member uses the extending type as a byref.
// If so, make sure we don't allow readonly/immutable values to be passed byref from an extension member.
// An inref will work though.
if isReadOnly && mustTakeAddress && minfo.IsExtensionMember then
minfo.TryObjArgByrefType(amap, m, minfo.FormalMethodInst)
|> Option.iter (fun ty ->
if not (isInByrefTy g ty) then
errorR(Error(FSComp.SR.tcCannotCallExtensionMethodInrefToByref(minfo.DisplayName), m)))
wrap, [objArgExprCoerced]
| _ ->
id, objArgs
let e, ety = f ccallInfo objArgs
wrap e, ety
//-------------------------------------------------------------------------
// Build method calls.
//-------------------------------------------------------------------------
/// Build an expression node that is a call to a .NET method.
let BuildILMethInfoCall g amap m isProp (minfo: ILMethInfo) valUseFlags minst direct args =
let valu = isStructTy g minfo.ApparentEnclosingType
let ctor = minfo.IsConstructor
if minfo.IsClassConstructor then
error (InternalError (minfo.ILName+": cannot call a class constructor", m))
let useCallvirt =
not valu && not direct && minfo.IsVirtual
let isProtected = minfo.IsProtectedAccessibility
let ilMethRef = minfo.ILMethodRef
let newobj = ctor && (match valUseFlags with NormalValUse -> true | _ -> false)
let exprTy = if ctor then minfo.ApparentEnclosingType else minfo.GetFSharpReturnTy(amap, m, minst)
let retTy = if not ctor && ilMethRef.ReturnType = ILType.Void then [] else [exprTy]
let isDllImport = minfo.IsDllImport g