From 8e1bc3f474d13ddf750c48df729aabf6b4f616ab Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Thu, 25 Jun 2020 02:16:51 -0700 Subject: [PATCH 1/3] Fix 9565 --- src/fsharp/MethodCalls.fs | 5 +++-- tests/fsharp/tests.fs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index df06f0064ab..b6e4c2fc5a6 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -1169,8 +1169,9 @@ let AdjustCallerArgForOptional tcFieldInit eCallerMemberName (infoReader: InfoRe let calledArg = assignedArg.CalledArg let calledArgTy = calledArg.CalledArgumentType match calledArg.OptArgInfo with - | NotOptional when not (g.langVersion.SupportsFeature LanguageFeature.NullableOptionalInterop) -> - if isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m)) + | NotOptional -> + if not (g.langVersion.SupportsFeature LanguageFeature.NullableOptionalInterop) && + isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m)) assignedArg | _ -> diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index 1a2be765dc5..eed5118fabb 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -254,7 +254,7 @@ module CoreTests = begin use testOkFile = fileguard cfg "test.ok" - fsc cfg "%s -o:test.exe -g" cfg.fsc_flags ["test.fsx"] + fsc cfg "%s -o:test.exe -g --langversion:5.0" cfg.fsc_flags ["test.fsx"] singleNegTest cfg "test" From cfb667c439855f56e9044c0f607e60c8f36c7c00 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Thu, 25 Jun 2020 19:39:39 -0700 Subject: [PATCH 2/3] Fix 9564 --- src/fsharp/MethodCalls.fs | 119 ++++++++++++++++++++------------------ tests/fsharp/tests.fs | 16 ++++- 2 files changed, 75 insertions(+), 60 deletions(-) diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index b6e4c2fc5a6..975dec1dec0 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -1150,85 +1150,90 @@ let GetDefaultExpressionForOptionalArg tcFieldInit g (calledArg: CalledArg) eCal let callerArg = CallerArg(calledArgTy, mMethExpr, false, expr) preBinder, { NamedArgIdOpt = None; CalledArg = calledArg; CallerArg = callerArg } + let MakeNullableExprIfNeeded (infoReader: InfoReader) calledArgTy callerArgTy callerArgExpr m = let g = infoReader.g let amap = infoReader.amap if isNullableTy g callerArgTy then - callerArgExpr + None else + // this happens when the caller and callee are not both nullable, I.e the scenario enabled by LanguageFeature.NullableOptionalInterop + tryLanguageFeatureError g.langVersion LanguageFeature.NullableOptionalInterop m let calledNonOptTy = destNullableTy g calledArgTy let minfo = GetIntrinsicConstructorInfosOfType infoReader m calledArgTy |> List.head let callerArgExprCoerced = mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr - MakeMethInfoCall amap m minfo [] [callerArgExprCoerced] + Some (MakeMethInfoCall amap m minfo [] [callerArgExprCoerced]) // Adjust all the optional arguments, filling in values for defaults, let AdjustCallerArgForOptional tcFieldInit eCallerMemberName (infoReader: InfoReader) (assignedArg: AssignedCalledArg<_>) = + let g = infoReader.g let callerArg = assignedArg.CallerArg let (CallerArg(callerArgTy, m, isOptCallerArg, callerArgExpr)) = callerArg let calledArg = assignedArg.CalledArg let calledArgTy = calledArg.CalledArgumentType - match calledArg.OptArgInfo with - | NotOptional -> - if not (g.langVersion.SupportsFeature LanguageFeature.NullableOptionalInterop) && - isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m)) - assignedArg - | _ -> + // callerArgExprOpt + // For caller/callee's where we can generate nullable compensation using MakeNullableExprIfNeeded + // generate assignedarg. + // callerArgExprOpt either: + // Some the generated compensation expression, or None + let callerArgExprOpt = + match calledArg.OptArgInfo with + | NotOptional -> + // T --> Nullable widening at callsites + if isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m)) + if isNullableTy g calledArgTy then + MakeNullableExprIfNeeded infoReader calledArgTy callerArgTy callerArgExpr m + else + None - let callerArgExpr2 = - match calledArg.OptArgInfo with - | NotOptional -> - // T --> Nullable widening at callsites - if isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m)) - if isNullableTy g calledArgTy then - MakeNullableExprIfNeeded infoReader calledArgTy callerArgTy callerArgExpr m + | CallerSide dfltVal -> + let calledArgTy = calledArg.CalledArgumentType + + if isOptCallerArg then + // CSharpMethod(?x=b) + if isOptionTy g callerArgTy then + if isNullableTy g calledArgTy then + // CSharpMethod(?x=b) when 'b' has optional type and 'x' has nullable type --> CSharpMethod(x=Option.toNullable b) + Some (mkOptionToNullable g m (destOptionTy g callerArgTy) callerArgExpr) + else + // CSharpMethod(?x=b) when 'b' has optional type and 'x' has non-nullable type --> CSharpMethod(x=Option.defaultValue DEFAULT v) + let _wrapper, defaultExpr = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName m + let ty = destOptionTy g callerArgTy + Some (mkOptionDefaultValue g m ty defaultExpr callerArgExpr) else - callerArgExpr - - | CallerSide dfltVal -> - let calledArgTy = calledArg.CalledArgumentType - - if isOptCallerArg then - // CSharpMethod(?x=b) - if isOptionTy g callerArgTy then - if isNullableTy g calledArgTy then - // CSharpMethod(?x=b) when 'b' has optional type and 'x' has nullable type --> CSharpMethod(x=Option.toNullable b) - mkOptionToNullable g m (destOptionTy g callerArgTy) callerArgExpr - else - // CSharpMethod(?x=b) when 'b' has optional type and 'x' has non-nullable type --> CSharpMethod(x=Option.defaultValue DEFAULT v) - let _wrapper, defaultExpr = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName m - let ty = destOptionTy g callerArgTy - mkOptionDefaultValue g m ty defaultExpr callerArgExpr - else - // This should be unreachable but the error will be reported elsewhere - callerArgExpr + // This should be unreachable but the error will be reported elsewhere + None + else + if isNullableTy g calledArgTy then + // CSharpMethod(x=b) when 'x' has nullable type + // CSharpMethod(x=b) when both 'x' and 'b' have nullable type --> CSharpMethod(x=b) + // CSharpMethod(x=b) when 'x' has nullable type and 'b' does not --> CSharpMethod(x=Nullable(b)) + MakeNullableExprIfNeeded infoReader calledArgTy callerArgTy callerArgExpr m + else + // CSharpMethod(x=b) --> CSharpMethod(?x=b) + None + + | CalleeSide -> + if isOptCallerArg then + // CSharpMethod(?x=b) --> CSharpMethod(?x=b) + None + else + // CSharpMethod(x=b) when CSharpMethod(A) --> CSharpMethod(?x=Some(b :> A)) + if isOptionTy g calledArgTy then + let calledNonOptTy = destOptionTy g calledArgTy + let callerArgExprCoerced = mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr + Some (mkSome g calledNonOptTy callerArgExprCoerced m) else - if isNullableTy g calledArgTy then - // CSharpMethod(x=b) when 'x' has nullable type - // CSharpMethod(x=b) when both 'x' and 'b' have nullable type --> CSharpMethod(x=b) - // CSharpMethod(x=b) when 'x' has nullable type and 'b' does not --> CSharpMethod(x=Nullable(b)) - MakeNullableExprIfNeeded infoReader calledArgTy callerArgTy callerArgExpr m - else - // CSharpMethod(x=b) --> CSharpMethod(?x=b) - callerArgExpr - - | CalleeSide -> - if isOptCallerArg then - // CSharpMethod(?x=b) --> CSharpMethod(?x=b) - callerArgExpr - else - // CSharpMethod(x=b) when CSharpMethod(A) --> CSharpMethod(?x=Some(b :> A)) - if isOptionTy g calledArgTy then - let calledNonOptTy = destOptionTy g calledArgTy - let callerArgExprCoerced = mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr - mkSome g calledNonOptTy callerArgExprCoerced m - else - assert false - callerArgExpr // defensive code - this case is unreachable + assert false + None // defensive code - this case is unreachable + match callerArgExprOpt with + | None -> assignedArg + | Some callerArgExpr2 -> let callerArg2 = CallerArg(tyOfExpr g callerArgExpr2, m, isOptCallerArg, callerArgExpr2) - { assignedArg with CallerArg=callerArg2 } + { assignedArg with CallerArg = callerArg2 } // Handle CallerSide optional arguments. // diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index eed5118fabb..8214bb7ca5c 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -251,12 +251,23 @@ module CoreTests = let cfg = testConfig' "core/byrefs" + begin + use testOkFile = fileguard cfg "test.ok" + + fsc cfg "%s -o:test.exe -g --langversion:4.7" cfg.fsc_flags ["test.fsx"] + + singleVersionedNegTest cfg "4.7" "test" + exec cfg ("." ++ "test.exe") "" + + testOkFile.CheckExists() + end + begin use testOkFile = fileguard cfg "test.ok" fsc cfg "%s -o:test.exe -g --langversion:5.0" cfg.fsc_flags ["test.fsx"] - singleNegTest cfg "test" + singleVersionedNegTest cfg "5.0" "test" exec cfg ("." ++ "test.exe") "" @@ -482,7 +493,6 @@ module CoreTests = let ``enum-FSI_BASIC`` () = singleTestBuildAndRun' "core/enum" FSI_BASIC #if !NETCOREAPP - // Requires winforms will not run on coreclr [] let controlWpf () = singleTestBuildAndRun' "core/controlwpf" FSC_BASIC @@ -3009,4 +3019,4 @@ module OverloadResolution = let [] ``neg_known_return_type_and_known_type_arguments`` () = singleNegTest (testConfig' "typecheck/overloads") "neg_known_return_type_and_known_type_arguments" let [] ``neg_generic_known_argument_types`` () = singleNegTest (testConfig' "typecheck/overloads") "neg_generic_known_argument_types" let [] ``neg_tupled_arguments`` () = singleNegTest (testConfig' "typecheck/overloads") "neg_tupled_arguments" -#endif +#endif \ No newline at end of file From efc8986b88295da29707d1f96339f5f8d9c906d3 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Thu, 25 Jun 2020 20:08:23 -0700 Subject: [PATCH 3/3] typo --- src/fsharp/MethodCalls.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index 975dec1dec0..2d5e277fe69 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -1233,7 +1233,7 @@ let AdjustCallerArgForOptional tcFieldInit eCallerMemberName (infoReader: InfoRe | None -> assignedArg | Some callerArgExpr2 -> let callerArg2 = CallerArg(tyOfExpr g callerArgExpr2, m, isOptCallerArg, callerArgExpr2) - { assignedArg with CallerArg = callerArg2 } + { assignedArg with CallerArg=callerArg2 } // Handle CallerSide optional arguments. //