@@ -236,12 +236,15 @@ type TypeDirectedConversion =
236236
237237[<RequireQualifiedAccess>] 
238238type  TypeDirectedConversionUsed  = 
239-     |  Yes of  ( DisplayEnv  ->  exn ) 
239+     |  Yes of  ( DisplayEnv  ->  exn )   *   isTwoStepConversion :   bool 
240240    |  No
241241    static member  Combine  a b  = 
242-         match  a with  
243-         |  Yes _  ->  a
244-         |  No ->  b
242+         match  a,  b with  
243+         |  Yes(_, true ),  _  ->  a
244+         |  _,  Yes(_, true )  ->  b
245+         |  Yes _,  _  ->  a
246+         |  _,  Yes _  ->  b
247+         |  No,  No ->  a
245248
246249let  MapCombineTDCD  mapper xs  = 
247250    MapReduceD mapper TypeDirectedConversionUsed.No TypeDirectedConversionUsed.Combine xs
@@ -279,21 +282,33 @@ let rec AdjustRequiredTypeForTypeDirectedConversions (infoReader: InfoReader) ad
279282
280283    // Adhoc int32 --> int64 
281284    elif  g.langVersion.SupportsFeature LanguageFeature.AdditionalTypeDirectedConversions &&  typeEquiv g g.int64_ ty reqdTy &&  typeEquiv g g.int32_ ty actualTy then  
282-        g.int32_ ty,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn),  None
285+          g.int32_ ty,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn,   false ),  None
283286
284287    // Adhoc int32 --> nativeint 
285288    elif  g.langVersion.SupportsFeature LanguageFeature.AdditionalTypeDirectedConversions &&  typeEquiv g g.nativeint_ ty reqdTy &&  typeEquiv g g.int32_ ty actualTy then  
286-        g.int32_ ty,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn),  None
289+          g.int32_ ty,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn,   false ),  None
287290
288291    // Adhoc int32 --> float64 
289292    elif  g.langVersion.SupportsFeature LanguageFeature.AdditionalTypeDirectedConversions &&  typeEquiv g g.float_ ty reqdTy &&  typeEquiv g g.int32_ ty actualTy then  
290-        g.int32_ ty,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn),  None
293+          g.int32_ ty,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn,   false ),  None
291294
295+     elif  g.langVersion.SupportsFeature LanguageFeature.NullableOptionalInterop &&  isMethodArg &&  isNullableTy g reqdTy &&  not  ( isNullableTy g actualTy)  then  
296+         let  underlyingTy  =  destNullableTy g reqdTy
297+         // shortcut 
298+         if  typeEquiv g underlyingTy actualTy then 
299+             actualTy,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn,  false ),  None
300+         else 
301+             let  adjustedTy ,  _ ,  _  =  AdjustRequiredTypeForTypeDirectedConversions infoReader ad isMethodArg isConstraint underlyingTy actualTy m
302+             if  typeEquiv g adjustedTy actualTy then  
303+                 actualTy,  TypeDirectedConversionUsed.Yes( warn TypeDirectedConversion.BuiltIn,  true ),  None
304+             else  
305+                 reqdTy,  TypeDirectedConversionUsed.No,  None
306+     
292307    // Adhoc based on op_Implicit, perhaps returing a new equational type constraint to  
293308    // eliminate articifical constrained type variables. 
294309    elif  g.langVersion.SupportsFeature LanguageFeature.AdditionalTypeDirectedConversions then 
295310         match  TryFindRelevantImplicitConversion infoReader ad reqdTy actualTy m with 
296-          |  Some ( minfo,  _ staticTy,  eqn)  ->  actualTy,  TypeDirectedConversionUsed.Yes( warn ( TypeDirectedConversion.Implicit minfo)),  Some eqn
311+          |  Some ( minfo,  _ staticTy,  eqn)  ->  actualTy,  TypeDirectedConversionUsed.Yes( warn ( TypeDirectedConversion.Implicit minfo),    false ),  Some eqn
297312         |  None ->  reqdTy,  TypeDirectedConversionUsed.No,  None
298313
299314    else  reqdTy,  TypeDirectedConversionUsed.No,  None
@@ -352,9 +367,8 @@ let AdjustCalledArgTypeForOptionals (infoReader: InfoReader) ad enforceNullableO
352367
353368                // If inference has worked out it's a struct (e.g. an int) then use this 
354369                elif  isStructTy g callerArgTy then 
355-                     let  calledArgTy2  =  destNullableTy g calledArgTy
356-                     AdjustRequiredTypeForTypeDirectedConversions infoReader ad true  false  calledArgTy2 callerArgTy m
357- 
370+                     AdjustRequiredTypeForTypeDirectedConversions infoReader ad true  false  calledArgTy callerArgTy m
371+                 
358372                // If neither and we are at the end of overload resolution then use the Nullable 
359373                elif  enforceNullableOptionalsKnownTypes then  
360374                    calledArgTy,  TypeDirectedConversionUsed.No,  None
@@ -1305,6 +1319,16 @@ let rec AdjustExprForTypeDirectedConversions tcVal (g: TcGlobals) amap infoReade
13051319
13061320       mkCallToDoubleOperator g m actualTy expr
13071321
1322+    elif  g.langVersion.SupportsFeature LanguageFeature.NullableOptionalInterop && 
1323+         isNullableTy g reqdTy &&  not  ( isNullableTy g actualTy)  then 
1324+ 
1325+        let  underlyingTy  =  destNullableTy g reqdTy
1326+        let  adjustedExpr  =  AdjustExprForTypeDirectedConversions tcVal g amap infoReader ad underlyingTy actualTy m expr
1327+        let  adjustedActualTy  =  tyOfExpr g adjustedExpr
1328+        
1329+        let  minfo  =  GetIntrinsicConstructorInfosOfType infoReader m reqdTy |>  List.head
1330+        let  callerArgExprCoerced  =  mkCoerceIfNeeded g underlyingTy adjustedActualTy adjustedExpr
1331+        MakeMethInfoCall amap m minfo []  [ callerArgExprCoerced]  None
13081332   else 
13091333       match  TryFindRelevantImplicitConversion infoReader ad reqdTy actualTy m with 
13101334       |  Some ( minfo,  staticTy,  _)  ->  
@@ -1313,9 +1337,7 @@ let rec AdjustExprForTypeDirectedConversions tcVal (g: TcGlobals) amap infoReade
13131337           let  callExpr ,  _  =  BuildMethodCall tcVal g amap Mutates.NeverMutates m false  minfo ValUseFlag.NormalValUse []  []  [ expr]  staticTyOpt
13141338           assert  ( let  resTy  =  tyOfExpr g callExpr in  typeEquiv g reqdTy resTy) 
13151339           callExpr
1316-        |  None ->  mkCoerceIfNeeded g reqdTy actualTy expr
1317-        // TODO: consider Nullable 
1318-        
1340+        |  None ->  mkCoerceIfNeeded g reqdTy actualTy expr 
13191341
13201342// Handle adhoc argument conversions 
13211343let  AdjustCallerArgExpr  tcVal  ( g :  TcGlobals )  amap infoReader ad isOutArg calledArgTy  ( reflArgInfo :  ReflectedArgInfo )  callerArgTy m callerArgExpr  =  
@@ -1450,17 +1472,6 @@ let GetDefaultExpressionForOptionalArg tcFieldInit g (calledArg: CalledArg) eCal
14501472    let  callerArg  =  CallerArg( calledArgTy,  mMethExpr,  false ,  expr) 
14511473    preBinder,  {  NamedArgIdOpt =  None;  CalledArg =  calledArg;  CallerArg =  callerArg } 
14521474
1453- let  MakeNullableExprIfNeeded   ( infoReader :  InfoReader )  calledArgTy callerArgTy callerArgExpr m  = 
1454-     let  g  =  infoReader.g
1455-     let  amap  =  infoReader.amap
1456-     if  isNullableTy g callerArgTy then  
1457-         callerArgExpr
1458-     else 
1459-         let  calledNonOptTy  =  destNullableTy g calledArgTy 
1460-         let  minfo  =  GetIntrinsicConstructorInfosOfType infoReader m calledArgTy |>  List.head
1461-         let  callerArgExprCoerced  =  mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr
1462-         MakeMethInfoCall amap m minfo []  [ callerArgExprCoerced]  None
1463- 
14641475// Adjust all the optional arguments, filling in values for defaults,  
14651476let  AdjustCallerArgForOptional  tcVal tcFieldInit eCallerMemberName  ( infoReader :  InfoReader )  ad  ( assignedArg :  AssignedCalledArg < _ >)  = 
14661477    let  g  =  infoReader.g
@@ -1492,14 +1503,9 @@ let AdjustCallerArgForOptional tcVal tcFieldInit eCallerMemberName (infoReader:
14921503            |  NotOptional -> 
14931504                //  T --> Nullable<T> widening at callsites 
14941505                if  isOptCallerArg then  errorR( Error( FSComp.SR.tcFormalArgumentIsNotOptional(),  m)) 
1495-                 if  isNullableTy g calledArgTy then  
1496-                     if  isNullableTy g callerArgTy then 
1497-                         callerArgExpr
1498-                     else 
1499-                         let  calledNonOptTy  =  destNullableTy g calledArgTy
1500-                         let  _ ,  callerArgExpr2  =  AdjustCallerArgExpr tcVal g amap infoReader ad isOutArg calledNonOptTy reflArgInfo callerArgTy m callerArgExpr
1501-                         let  callerArgTy2  =  tyOfExpr g callerArgExpr2
1502-                         MakeNullableExprIfNeeded infoReader calledArgTy callerArgTy2 callerArgExpr2 m
1506+                 if  isNullableTy g calledArgTy then 
1507+                     // AdjustCallerArgExpr later on will deal with the nullable conversion 
1508+                     callerArgExpr 
15031509                else 
15041510                    failwith " unreachable" // see case above 
15051511
@@ -1521,21 +1527,8 @@ let AdjustCallerArgForOptional tcVal tcFieldInit eCallerMemberName (infoReader:
15211527                        // This should be unreachable but the error will be reported elsewhere 
15221528                        callerArgExpr
15231529                else 
1524-                     if  isNullableTy g calledArgTy  then  
1525-                         if  isNullableTy g callerArgTy then 
1526-                             // CSharpMethod(x=b) when 'x' has nullable type 
1527-                             // CSharpMethod(x=b) when both 'x' and 'b' have nullable type --> CSharpMethod(x=b) 
1528-                             callerArgExpr
1529-                         else 
1530-                             // CSharpMethod(x=b) when 'x' has nullable type and 'b' does not --> CSharpMethod(x=Nullable(b)) 
1531-                             let  calledNonOptTy  =  destNullableTy g calledArgTy
1532-                             let  _ ,  callerArgExpr2  =  AdjustCallerArgExpr tcVal g amap infoReader ad isOutArg calledNonOptTy reflArgInfo callerArgTy m callerArgExpr
1533-                             let  callerArgTy2  =  tyOfExpr g callerArgExpr2
1534-                             MakeNullableExprIfNeeded infoReader calledArgTy callerArgTy2 callerArgExpr2 m
1535-                     else  
1536-                         // CSharpMethod(x=b) --> CSharpMethod(?x=b) 
1537-                         let  _ ,  callerArgExpr2  =  AdjustCallerArgExpr tcVal g amap infoReader ad isOutArg calledArgTy reflArgInfo callerArgTy m callerArgExpr
1538-                         callerArgExpr2
1530+                     // AdjustCallerArgExpr later on will deal with any nullable conversion 
1531+                     callerArgExpr
15391532
15401533            |  CalleeSide ->  
15411534                if  isOptCallerArg then  
0 commit comments