Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit14f88dd

Browse files
committed
Fix fordotnet#69 - Invalid code generated when calling C# method with optional nullable args
Old codegen was improperly emitting default Nullable() for all nullable optional args that were not specified by the developer. This was correct when the default value was in fact "null" (i.e. Nullable()), but was incorrect when default value contains a real value (i.e. Nullable(42)). This change now detects when default value is a System.Nullable, and emits proper call to Nullable(xyz) constructor when required. This change is based on suggestion form Vlad in email a while back.Also noticed (from comment in the code) that VB allows byref optional args. Upon investigation, these were also being handled incorrectly, so I refactored a bit in order to handle those. (changeset 1280456)
1 parent9179d77 commit14f88dd

File tree

9 files changed

+145
-23
lines changed

9 files changed

+145
-23
lines changed

‎src/fsharp/tastops.fs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,11 @@ let (|StripNullableTy|) g ty =
721721
match tywith
722722
| AppTy g(tcr,[tyarg])when tyconRefEq g tcr g.system_Nullable_tcref-> tyarg
723723
|_-> ty
724+
725+
let(|ByrefTy|_|)g ty=
726+
match tywith
727+
| AppTy g(tcr,[tyarg])when tyconRefEq g tcr g.byref_tcr-> Some tyarg
728+
|_-> None
724729

725730
letmkInstForAppTy g typ=
726731
if isAppTy g typthen

‎src/fsharp/tastops.fsi‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,7 @@ val StripSelfRefCell : TcGlobals * ValBaseOrThisInfo * TType -> TType
983983
val(|AppTy|_|): TcGlobals-> TType->(TyconRef* TType list) option
984984
val(|NullableTy|_|): TcGlobals-> TType-> TType option
985985
val(|StripNullableTy|): TcGlobals-> TType-> TType
986+
val(|ByrefTy|_|): TcGlobals-> TType-> TType option
986987

987988
//-------------------------------------------------------------------------
988989
// Special semantic constraints

‎src/fsharp/tc.fs‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8934,36 +8934,45 @@ and TcMethodApplication
89348934
| NotOptional ->
89358935
error(InternalError("Unexpected NotOptional",mItem))
89368936
| CallerSide dfltVal ->
8937-
let rec build = function
8937+
let rec build currCalledArgTy currDfltVal =
8938+
match currDfltVal with
89388939
| MissingValue ->
89398940
// Add an I_nop if this is an initonly field to make sure we never recognize it as an lvalue. See mkExprAddrOfExpr.
8940-
emptyPreBinder,mkAsmExpr ([ mkNormalLdsfld (fspec_Missing_Value cenv.g.ilg); AI_nop ],[],[],[calledArgTy],mMethExpr)
8941+
emptyPreBinder,mkAsmExpr ([ mkNormalLdsfld (fspec_Missing_Value cenv.g.ilg); AI_nop ],[],[],[currCalledArgTy],mMethExpr)
89418942
| DefaultValue ->
8942-
emptyPreBinder,mkDefault(mMethExpr,calledArgTy)
8943+
emptyPreBinder,mkDefault(mMethExpr,currCalledArgTy)
89438944
| Constant fieldInit ->
8944-
emptyPreBinder,Expr.Const(TcFieldInit mMethExpr fieldInit,mMethExpr,calledArgTy)
8945+
match currCalledArgTy with
8946+
| NullableTy cenv.g inst when fieldInit <> ILFieldInit.Null ->
8947+
let nullableTy = mkILNonGenericBoxedTy(mkILTyRef(cenv.g.ilg.traits.ScopeRef, "System.Nullable`1"))
8948+
let ctor = mkILCtorMethSpecForTy(nullableTy, [ILType.TypeVar 0us]).MethodRef
8949+
let ctorArgs = [Expr.Const(TcFieldInit mMethExpr fieldInit,mMethExpr, inst)]
8950+
emptyPreBinder,Expr.Op(TOp.ILCall(false, false, true, true, NormalValUse, false, false, ctor, [inst], [], [currCalledArgTy]), [], ctorArgs, mMethExpr)
8951+
| ByrefTy cenv.g inst ->
8952+
build inst (PassByRef(inst, currDfltVal))
8953+
| _ ->
8954+
emptyPreBinder,Expr.Const(TcFieldInit mMethExpr fieldInit,mMethExpr,currCalledArgTy)
89458955
| WrapperForIDispatch ->
89468956
match cenv.g.ilg.traits.SystemRuntimeInteropServicesScopeRef.Value with
89478957
| None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr))
89488958
| Some assemblyRef ->
89498959
let tref = mkILNonGenericBoxedTy(mkILTyRef(assemblyRef, "System.Runtime.InteropServices.DispatchWrapper"))
89508960
let mref = mkILCtorMethSpecForTy(tref,[cenv.g.ilg.typ_Object]).MethodRef
8951-
let expr = Expr.Op(TOp.ILCall(false,false,false,false,CtorValUsedAsSuperInit,false,false,mref,[],[],[cenv.g.obj_ty]),[],[mkDefault(mMethExpr,calledArgTy)],mMethExpr)
8961+
let expr = Expr.Op(TOp.ILCall(false,false,false,false,CtorValUsedAsSuperInit,false,false,mref,[],[],[cenv.g.obj_ty]),[],[mkDefault(mMethExpr,currCalledArgTy)],mMethExpr)
89528962
emptyPreBinder,expr
89538963
| WrapperForIUnknown ->
89548964
match cenv.g.ilg.traits.SystemRuntimeInteropServicesScopeRef.Value with
89558965
| None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr))
89568966
| Some assemblyRef ->
89578967
let tref = mkILNonGenericBoxedTy(mkILTyRef(assemblyRef, "System.Runtime.InteropServices.UnknownWrapper"))
89588968
let mref = mkILCtorMethSpecForTy(tref,[cenv.g.ilg.typ_Object]).MethodRef
8959-
let expr = Expr.Op(TOp.ILCall(false,false,false,false,CtorValUsedAsSuperInit,false,false,mref,[],[],[cenv.g.obj_ty]),[],[mkDefault(mMethExpr,calledArgTy)],mMethExpr)
8969+
let expr = Expr.Op(TOp.ILCall(false,false,false,false,CtorValUsedAsSuperInit,false,false,mref,[],[],[cenv.g.obj_ty]),[],[mkDefault(mMethExpr,currCalledArgTy)],mMethExpr)
89608970
emptyPreBinder,expr
89618971
| PassByRef (ty, dfltVal2) ->
89628972
let v,_ = mkCompGenLocal mMethExpr "defaultByrefArg" ty
8963-
let wrapper2,rhs = builddfltVal2
8973+
let wrapper2,rhs = buildcurrCalledArgTy dfltVal2
89648974
(wrapper2 >> mkCompGenLet mMethExpr v rhs), mkValAddr mMethExpr (mkLocalValRef v)
8965-
build dfltVal
8966-
8975+
build calledArgTy dfltVal
89678976
| CalleeSide ->
89688977
let calledNonOptTy =
89698978
if isOptionTy cenv.g calledArgTy then

‎tests/fsharpqa/Source/Conformance/DeclarationElements/MemberDefinitions/OptionalArguments/NullOptArgsFromCS.fs‎

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,46 @@
44
moduleM
55

66
openTestLib
7+
openSystem
8+
openSystem.Collections.Generic
79

8-
leta=new T()
9-
letx= a.NullOptArg()
10-
lety= a.NullOptArg("hi")
11-
letz= a.NullableOptArg()
10+
letvalidate case actual expected=
11+
printfn"Checking case '%s'" case
12+
if actual<> expectedthen
13+
printfn" Actual:%O" actual
14+
printfn" Expected:%O" expected
15+
exit1
16+
17+
leta= T()
18+
19+
validate"ValueTypeOptArg1"(a.ValueTypeOptArg())100
20+
validate"ValueTypeOptArg2"(a.ValueTypeOptArg(2))2
21+
22+
validate"NullableOptArgNullDefault1"(a.NullableOptArgNullDefault())(Nullable())
23+
validate"NullableOptArgNullDefault2"(a.NullableOptArgNullDefault(Nullable(10)))(Nullable(10))
24+
25+
validate"NullableOptArgWithDefault1"(a.NullableOptArgWithDefault())(Nullable(5.7))
26+
validate"NullableOptArgWithDefault2"(a.NullableOptArgWithDefault(Nullable(10.5)))(Nullable(10.5))
27+
28+
validate"NullOptArg1"(a.NullOptArg())null
29+
validate"NullOptArg2"(a.NullOptArg("qwerty"))"qwerty"
30+
31+
validate"NonNullOptArg1"(a.NonNullOptArg())"abc"
32+
validate"NonNullOptArg2"(a.NonNullOptArg("qwerty2"))"qwerty2"
33+
34+
letintList= List<int>()
35+
letstringList= List<string>()
36+
validate"GenericOptArg1"(a.GenericOptArg<int>())null
37+
validate"GenericOptArg2"(a.GenericOptArg<int>(intList)) intList
38+
validate"GenericOptArg3"(a.GenericOptArg<string>())null
39+
validate"GenericOptArg4"(a.GenericOptArg<string>(stringList)) stringList
40+
41+
validate"Combo1"(a.ComboOptionals("abc"))"[] [abc] [100] [200] [] []"
42+
validate"Combo2"(a.ComboOptionals("abc", a="123"))"[123] [abc] [100] [200] [] []"
43+
validate"Combo3"(a.ComboOptionals("abc", b="xyz"))"[] [xyz] [100] [200] [] []"
44+
validate"Combo4"(a.ComboOptionals("abc", c=33))"[] [abc] [33] [200] [] []"
45+
validate"Combo5"(a.ComboOptionals("abc", d= Nullable(33)))"[] [abc] [100] [33] [] []"
46+
validate"Combo6"(a.ComboOptionals("abc", e= Nullable(33.3)))"[] [abc] [100] [200] [33.3] []"
47+
validate"Combo7"(a.ComboOptionals("abc", f= intList))"[] [abc] [100] [200] [] [System.Collections.Generic.List`1[System.Int32]]"
1248

1349
exit0
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
moduleM
2+
3+
openTest
4+
openSystem
5+
6+
letvalidate case actual expected=
7+
printfn"Checking case '%s'" case
8+
if actual<> expectedthen
9+
printfn" Actual:%O" actual
10+
printfn" Expected:%O" expected
11+
exit1
12+
13+
leta= VBTestClass()
14+
15+
validate"ByRefNullable1"(a.OptionalNullableRefParam())(Nullable(130))
16+
validate"ByRefNullable2"(a.OptionalNullableRefParam(ref(Nullable())))(Nullable(55))
17+
validate"ByRefNullable3"(a.OptionalNullableRefParam(ref(Nullable(-100))))(Nullable(0))
18+
19+
validate"OptionalRefParam1"(a.OptionalRefParam())"superduper edited"
20+
validate"OptionalRefParam2"(a.OptionalRefParam(ref"qwerty"))"qwerty edited"
21+
let mutablex1="aaa"
22+
validate"OptionalRefParam3"(a.OptionalRefParam(ref x1))"aaa edited"
23+
validate"OptionalRefParam4" x1"aaa"
24+
validate"OptionalRefParam5"(a.OptionalRefParam(&x1))"aaa edited"
25+
validate"OptionalRefParam6" x1"aaa edited"
26+
27+
exit0
Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
1+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
14
namespaceTestLib{
25
publicclassT{
36
publicT(){
47
}
58

6-
publicstringValueTypeOptArg(intx=0){
7-
return"1";
9+
publicintValueTypeOptArg(intx=100){
10+
returnx;
811
}
912

10-
publicstringNullableOptArg(int?x=null){
11-
return"1";
13+
publicint?NullableOptArgNullDefault(int?x=null){
14+
returnx;
1215
}
13-
14-
publicintNullOptArg(stringx=null){
15-
return1;
16+
17+
publicdouble?NullableOptArgWithDefault(double?x=5.7){
18+
returnx;
19+
}
20+
21+
publicstringNullOptArg(stringx=null){
22+
returnx;
1623
}
1724

18-
publicintNonNullOptArg(stringx=""){
19-
return1;
25+
publicstringNonNullOptArg(stringx="abc"){
26+
returnx;
27+
}
28+
29+
publicList<T>GenericOptArg<T>(List<T>x=null){
30+
returnx;
2031
}
32+
33+
publicstringComboOptionals<T>(stringrequired,stringa=null,stringb="abc",intc=100,int?d=200,double?e=null,List<T>f=null){
34+
varresult=String.Format("[{0}] [{1}] [{2}] [{3}] [{4}] [{5}]",a,b,c,d,e,f);
35+
returnresult;
36+
}
2137
}
2238
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
NamespaceTest
2+
PublicClassVBTestClass
3+
PublicFunctionOptionalRefParam(OptionalByRefparam2AsString="superduper")AsString
4+
param2=param2+" edited"
5+
Returnparam2
6+
EndFunction
7+
8+
PublicFunctionOptionalNullableRefParam(OptionalByRefparam2AsSystem.Nullable(OfInt32)=30)AsNullable(OfInt32)
9+
Ifparam2.HasValueThen
10+
param2=param2+100
11+
Returnparam2
12+
Else
13+
param2=55
14+
Returnparam2
15+
EndIf
16+
EndFunction
17+
EndClass
18+
EndNamespace

‎tests/fsharpqa/Source/Conformance/DeclarationElements/MemberDefinitions/OptionalArguments/env.lst‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
SOURCE=E_SanityCheck.fs
55
SOURCE=E_SanityCheck02.fs
66
SOURCE=SanityCheck03.fs
7-
7+
8+
SOURCE=NullOptArgsFromVB.fs SCFLAGS="-r:TestLibVB.dll" PRECMD="\$VBC_PIPE /t:library TestLibVB.vb"# NullOptArgsFromVB.fs
89
SOURCE=NullOptArgsFromCS.fs SCFLAGS="-r:TestLib.dll" PRECMD="\$CSC_PIPE /t:library TestLib.cs"# NullOptArgsFromCS.fs
910
SOURCE=SanityOptArgsFromCS.fs SCFLAGS="-r:TestLib.dll" PRECMD="\$CSC_PIPE /t:library TestLib.cs"# SanityOptArgsFromCS.fs
1011
SOURCE=E_OptionalNamedArgs.fs SCFLAGS="-r:TestLib.dll" PRECMD="\$CSC_PIPE /t:library TestLib.cs"# E_OptionalNamedArgs.fs

‎tests/fsharpqa/Source/run.pl‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use constantFSI_NAME=>'fsiAnyCPU';
99
use constantFSI32_NAME=>'fsi';
1010
use constantCSC_NAME=>'csc';
11+
use constantVBC_NAME=>'vbc';
1112

1213
# Constant values for test result
1314
use constantTEST_PASS=> 0;
@@ -142,6 +143,12 @@
142143
$ENV{CSC_PIPE}=CSC_NAME;
143144
}
144145

146+
my$VBC_PIPE=$ENV{VBC_PIPE};
147+
unless(defined($VBC_PIPE) ){
148+
$VBC_PIPE = VBC_NAME;
149+
$ENV{VBC_PIPE}=VBC_NAME;
150+
}
151+
145152
#
146153
# Run pre-command if any
147154
#
@@ -157,6 +164,7 @@
157164
s/^\$FSI32_PIPE/$FSI32_PIPE/;
158165
s/\$ISCFLAGS/$ISCFLAGS/;
159166
s/^\$CSC_PIPE/$CSC_PIPE/;
167+
s/^\$VBC_PIPE/$VBC_PIPE/;
160168
RunExit(TEST_FAIL,"Fail to execute the PRECMD" .@CommandOutput ."\n")if RunCommand("PRECMD",$_ ,1);
161169
}
162170

@@ -726,6 +734,7 @@ sub RunExit {
726734
s/^\$FSI_PIPE/$FSI_PIPE/;
727735
s/^\$FSI32_PIPE/$FSI32_PIPE/;
728736
s/^\$CSC_PIPE/$CSC_PIPE/;
737+
s/^\$VBC_PIPE/$VBC_PIPE/;
729738

730739
if (RunCommand("POSTCMD",$_,1)){
731740
$exitVal = TEST_FAIL;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp