diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs
index 599392383d719ac3ccff76a01b35bf1cb52d7049..116911167ebb61e8e5902eca5e2f959ab00157d4 100644
--- a/src/fsharp/Optimizer.fs
+++ b/src/fsharp/Optimizer.fs
@@ -1764,7 +1764,15 @@ let TryDetectQueryQuoteAndRun cenv (expr:Expr) =
//printfn "Not eliminating because no Run found"
None
-
+let IsSystemStringConcatOverload (methRef: ILMethodRef) =
+ methRef.Name = "Concat" && methRef.DeclaringTypeRef.FullName = "System.String" &&
+ methRef.ReturnType.BasicQualifiedName = "System.String" &&
+ methRef.ArgTypes |> List.forall(fun ilty -> ilty.BasicQualifiedName = "System.String")
+
+let IsSystemStringConcatArray (methRef: ILMethodRef) =
+ methRef.Name = "Concat" && methRef.DeclaringTypeRef.FullName = "System.String" &&
+ methRef.ReturnType.BasicQualifiedName = "System.String" &&
+ methRef.ArgTypes.Length = 1 && methRef.ArgTypes.Head.BasicQualifiedName = "System.String[]"
//-------------------------------------------------------------------------
// The traversal
@@ -1824,7 +1832,6 @@ let rec OptimizeExpr cenv (env:IncrementalOptimizationEnv) expr =
assert ("unexpected reclink" = "")
failwith "Unexpected reclink"
-
//-------------------------------------------------------------------------
// Optimize/analyze an object expression
//-------------------------------------------------------------------------
@@ -1874,9 +1881,54 @@ and OptimizeInterfaceImpl cenv env baseValOpt (ty, overrides) =
Info=UnknownValue}
//-------------------------------------------------------------------------
-// Optimize/analyze an application of an intrinsic operator to arguments
+// Make and optimize String.Concat calls
//-------------------------------------------------------------------------
+and MakeOptimizedSystemStringConcatCall cenv env m args =
+ let rec optimizeArg e accArgs =
+ match e, accArgs with
+ | Expr.Op(TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, [ Expr.Op(TOp.Array, _, args, _) ], _), _ when IsSystemStringConcatArray methRef ->
+ optimizeArgs args accArgs
+
+ | Expr.Op(TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, args, _), _ when IsSystemStringConcatOverload methRef ->
+ optimizeArgs args accArgs
+
+ // Optimize string constants, e.g. "1" + "2" will turn into "12"
+ | Expr.Const(Const.String str1, _, _), Expr.Const(Const.String str2, _, _) :: accArgs ->
+ mkString cenv.g m (str1 + str2) :: accArgs
+
+ | arg, _ -> arg :: accArgs
+
+ and optimizeArgs args accArgs =
+ (args, accArgs)
+ ||> List.foldBack (fun arg accArgs -> optimizeArg arg accArgs)
+
+ let args = optimizeArgs args []
+
+ let e =
+ match args with
+ | [ arg ] ->
+ arg
+ | [ arg1; arg2 ] ->
+ mkStaticCall_String_Concat2 cenv.g m arg1 arg2
+ | [ arg1; arg2; arg3 ] ->
+ mkStaticCall_String_Concat3 cenv.g m arg1 arg2 arg3
+ | [ arg1; arg2; arg3; arg4 ] ->
+ mkStaticCall_String_Concat4 cenv.g m arg1 arg2 arg3 arg4
+ | args ->
+ let arg = mkArray (cenv.g.string_ty, args, m)
+ mkStaticCall_String_Concat_Array cenv.g m arg
+
+ match e with
+ | Expr.Op(TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _) as op, tyargs, args, m) when IsSystemStringConcatOverload methRef || IsSystemStringConcatArray methRef ->
+ OptimizeExprOpReductions cenv env (op, tyargs, args, m)
+ | _ ->
+ OptimizeExpr cenv env e
+
+//-------------------------------------------------------------------------
+// Optimize/analyze an application of an intrinsic operator to arguments
+//-------------------------------------------------------------------------
+
and OptimizeExprOp cenv env (op, tyargs, args, m) =
// Special cases
@@ -1940,22 +1992,30 @@ and OptimizeExprOp cenv env (op, tyargs, args, m) =
// if the types match up.
| TOp.ILAsm([], [ty]), _, [a] when typeEquiv cenv.g (tyOfExpr cenv.g a) ty -> OptimizeExpr cenv env a
- | _ ->
- // Reductions
- let args', arginfos = OptimizeExprsThenConsiderSplits cenv env args
- let knownValue =
- match op, arginfos with
- | TOp.ValFieldGet (rf), [e1info] -> TryOptimizeRecordFieldGet cenv env (e1info, rf, tyargs, m)
- | TOp.TupleFieldGet (tupInfo, n), [e1info] -> TryOptimizeTupleFieldGet cenv env (tupInfo, e1info, tyargs, n, m)
- | TOp.UnionCaseFieldGet (cspec, n), [e1info] -> TryOptimizeUnionCaseGet cenv env (e1info, cspec, tyargs, n, m)
- | _ -> None
- match knownValue with
- | Some valu ->
- match TryOptimizeVal cenv env (false, valu, m) with
- | Some res -> OptimizeExpr cenv env res (* discard e1 since guard ensures it has no effects *)
- | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos valu
- | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos UnknownValue
+ // Optimize calls when concatenating strings, e.g. "1" + "2" + "3" + "4" .. etc.
+ | TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, [ Expr.Op(TOp.Array, _, args, _) ] when IsSystemStringConcatArray methRef ->
+ MakeOptimizedSystemStringConcatCall cenv env m args
+ | TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, args when IsSystemStringConcatOverload methRef ->
+ MakeOptimizedSystemStringConcatCall cenv env m args
+ | _ ->
+ // Reductions
+ OptimizeExprOpReductions cenv env (op, tyargs, args, m)
+
+and OptimizeExprOpReductions cenv env (op, tyargs, args, m) =
+ let args', arginfos = OptimizeExprsThenConsiderSplits cenv env args
+ let knownValue =
+ match op, arginfos with
+ | TOp.ValFieldGet (rf), [e1info] -> TryOptimizeRecordFieldGet cenv env (e1info, rf, tyargs, m)
+ | TOp.TupleFieldGet (tupInfo, n), [e1info] -> TryOptimizeTupleFieldGet cenv env (tupInfo, e1info, tyargs, n, m)
+ | TOp.UnionCaseFieldGet (cspec, n), [e1info] -> TryOptimizeUnionCaseGet cenv env (e1info, cspec, tyargs, n, m)
+ | _ -> None
+ match knownValue with
+ | Some valu ->
+ match TryOptimizeVal cenv env (false, valu, m) with
+ | Some res -> OptimizeExpr cenv env res (* discard e1 since guard ensures it has no effects *)
+ | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos valu
+ | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos UnknownValue
and OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos valu =
// The generic case - we may collect information, but the construction/projection doesn't disappear
@@ -2682,7 +2742,7 @@ and TryInlineApplication cenv env finfo (tyargs: TType list, args: Expr list, m)
// Inlining: beta reducing
let expr' = MakeApplicationAndBetaReduce cenv.g (f2', f2ty, [tyargs], args', m)
// Inlining: reoptimizing
- Some (OptimizeExpr cenv {env with dontInline= Zset.add lambdaId env.dontInline} expr')
+ Some(OptimizeExpr cenv {env with dontInline= Zset.add lambdaId env.dontInline} expr')
| _ -> None
diff --git a/src/fsharp/TastOps.fs b/src/fsharp/TastOps.fs
index dfebdb9bae0399c99e19568ace17a1eae2faf8af..c50593dd7a77b8f8e500048f6fdd053e3807179b 100644
--- a/src/fsharp/TastOps.fs
+++ b/src/fsharp/TastOps.fs
@@ -6366,6 +6366,18 @@ let mkIsInst ty e m = mkAsmExpr ([ isinst ], [ty], [e], [ ty ], m)
let mspec_Type_GetTypeFromHandle (g: TcGlobals) = IL.mkILNonGenericStaticMethSpecInTy(g.ilg.typ_Type, "GetTypeFromHandle", [g.iltyp_RuntimeTypeHandle], g.ilg.typ_Type)
let mspec_String_Length (g: TcGlobals) = mkILNonGenericInstanceMethSpecInTy (g.ilg.typ_String, "get_Length", [], g.ilg.typ_Int32)
+let mspec_String_Concat2 (g: TcGlobals) =
+ mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ g.ilg.typ_String; g.ilg.typ_String ], g.ilg.typ_String)
+
+let mspec_String_Concat3 (g: TcGlobals) =
+ mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ g.ilg.typ_String; g.ilg.typ_String; g.ilg.typ_String ], g.ilg.typ_String)
+
+let mspec_String_Concat4 (g: TcGlobals) =
+ mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ g.ilg.typ_String; g.ilg.typ_String; g.ilg.typ_String; g.ilg.typ_String ], g.ilg.typ_String)
+
+let mspec_String_Concat_Array (g: TcGlobals) =
+ mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ mkILArr1DTy g.ilg.typ_String ], g.ilg.typ_String)
+
let fspec_Missing_Value (g: TcGlobals) = IL.mkILFieldSpecInTy(g.iltyp_Missing, "Value", g.iltyp_Missing)
let mkInitializeArrayMethSpec (g: TcGlobals) =
@@ -6587,6 +6599,21 @@ let mkGetStringLength g m e =
/// ILCall(useCallvirt, isProtected, valu, newobj, valUseFlags, isProp, noTailCall, mref, actualTypeInst, actualMethInst, retTy)
Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, true, false, mspec.MethodRef, [], [], [g.int32_ty]), [], [e], m)
+let mkStaticCall_String_Concat2 g m arg1 arg2 =
+ let mspec = mspec_String_Concat2 g
+ Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg1; arg2], m)
+
+let mkStaticCall_String_Concat3 g m arg1 arg2 arg3 =
+ let mspec = mspec_String_Concat3 g
+ Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg1; arg2; arg3], m)
+
+let mkStaticCall_String_Concat4 g m arg1 arg2 arg3 arg4 =
+ let mspec = mspec_String_Concat4 g
+ Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg1; arg2; arg3; arg4], m)
+
+let mkStaticCall_String_Concat_Array g m arg =
+ let mspec = mspec_String_Concat_Array g
+ Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg], m)
// Quotations can't contain any IL.
// As a result, we aim to get rid of all IL generation in the typechecker and pattern match
diff --git a/src/fsharp/TastOps.fsi b/src/fsharp/TastOps.fsi
index 8856119d7be5c18f4c0f6f7ec2b0a2d68bacaba3..0731970c1958b856095c0cb9f8a00f3d11c9fa1a 100755
--- a/src/fsharp/TastOps.fsi
+++ b/src/fsharp/TastOps.fsi
@@ -1386,6 +1386,11 @@ val mkCallNewQuerySource : TcGlobals -> range -> TType -> TType -> Expr -> Expr
val mkArray : TType * Exprs * range -> Expr
+val mkStaticCall_String_Concat2 : TcGlobals -> range -> Expr -> Expr -> Expr
+val mkStaticCall_String_Concat3 : TcGlobals -> range -> Expr -> Expr -> Expr -> Expr
+val mkStaticCall_String_Concat4 : TcGlobals -> range -> Expr -> Expr -> Expr -> Expr -> Expr
+val mkStaticCall_String_Concat_Array : TcGlobals -> range -> Expr -> Expr
+
//-------------------------------------------------------------------------
// operations primarily associated with the optimization to fix
// up loops to generate .NET code that does not include array bound checks
diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj
index 06b8a4b422a7f7121f484d739c95d2e95850f1a2..da3db8e565f970bd7679aece5337ba29830b133b 100644
--- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj
+++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj
@@ -44,7 +44,7 @@
- ..\..\..\packages\System.ValueTuple.$(SystemValueTuplePackageVersion)\lib\netstandard1.0\System.ValueTuple.dll
+ ..\..\..\packages\System.ValueTuple.$(SystemValueTuplePackageVersion)\lib\netstandard1.0\System.ValueTuple.dll
@@ -54,6 +54,8 @@
+
+
diff --git a/tests/FSharp.Compiler.UnitTests/ILHelpers.fs b/tests/FSharp.Compiler.UnitTests/ILHelpers.fs
new file mode 100644
index 0000000000000000000000000000000000000000..32c4e209d8b04c152acfdc2244dec7f0c94ec6c8
--- /dev/null
+++ b/tests/FSharp.Compiler.UnitTests/ILHelpers.fs
@@ -0,0 +1,117 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace FSharp.Compiler.UnitTests
+
+open System
+open System.IO
+open System.Diagnostics
+
+open NUnit.Framework
+
+open Microsoft.FSharp.Compiler.SourceCodeServices
+
+module ILChecker =
+
+ let checker = FSharpChecker.Create()
+
+ let private (++) a b = Path.Combine(a,b)
+
+ let private getfullpath workDir path =
+ let rooted =
+ if Path.IsPathRooted(path) then path
+ else Path.Combine(workDir, path)
+ rooted |> Path.GetFullPath
+
+ let private fileExists workDir path =
+ if path |> getfullpath workDir |> File.Exists then Some path else None
+
+ let private requireFile nm =
+ if fileExists __SOURCE_DIRECTORY__ nm |> Option.isSome then nm else failwith (sprintf "couldn't find %s. Running 'build test' once might solve this issue" nm)
+
+ let private exec exe args =
+ let startInfo = ProcessStartInfo(exe, String.concat " " args)
+ startInfo.RedirectStandardError <- true
+ startInfo.UseShellExecute <- false
+ use p = Process.Start(startInfo)
+ p.WaitForExit()
+ p.StandardError.ReadToEnd(), p.ExitCode
+
+ /// Compile the source and check to see if the expected IL exists.
+ /// The first line of each expected IL string is found first.
+ let check source expectedIL =
+ let SCRIPT_ROOT = __SOURCE_DIRECTORY__
+ let packagesDir = SCRIPT_ROOT ++ ".." ++ ".." ++ "packages"
+ let Is64BitOperatingSystem = sizeof = 8
+ let architectureMoniker = if Is64BitOperatingSystem then "x64" else "x86"
+ let ildasmExe = requireFile (packagesDir ++ ("runtime.win-" + architectureMoniker + ".Microsoft.NETCore.ILDAsm.2.0.3") ++ "runtimes" ++ ("win-" + architectureMoniker) ++ "native" ++ "ildasm.exe")
+ let coreclrDll = requireFile (packagesDir ++ ("runtime.win-" + architectureMoniker + ".Microsoft.NETCore.Runtime.CoreCLR.2.0.3") ++ "runtimes" ++ ("win-" + architectureMoniker) ++ "native" ++ "coreclr.dll")
+
+ let tmp = Path.GetTempFileName()
+ let tmpFs = Path.ChangeExtension(tmp, ".fs")
+ let tmpDll = Path.ChangeExtension(tmp, ".dll")
+ let tmpIL = Path.ChangeExtension(tmp, ".il")
+
+ let mutable errorMsgOpt = None
+ try
+ // ildasm requires coreclr.dll to run which has already been restored to the packages directory
+ File.Copy(coreclrDll, Path.GetDirectoryName(ildasmExe) ++ "coreclr.dll", overwrite=true)
+
+ File.WriteAllText(tmpFs, source)
+
+ let errors, exitCode = checker.Compile([| "fsc.exe"; "--optimize+"; "-o"; tmpDll; "-a"; tmpFs |]) |> Async.RunSynchronously
+ let errors =
+ String.concat "\n" (errors |> Array.map (fun x -> x.Message))
+
+ if exitCode = 0 then
+ exec ildasmExe [ sprintf "%s /out=%s" tmpDll tmpIL ] |> ignore
+
+ let text = File.ReadAllText(tmpIL)
+ let blockComments = @"/\*(.*?)\*/"
+ let lineComments = @"//(.*?)\r?\n"
+ let strings = @"""((\\[^\n]|[^""\n])*)"""
+ let verbatimStrings = @"@(""[^""]*"")+"
+ let textNoComments =
+ System.Text.RegularExpressions.Regex.Replace(text,
+ blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings,
+ (fun me ->
+ if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) then
+ if me.Value.StartsWith("//") then Environment.NewLine else String.Empty
+ else
+ me.Value), System.Text.RegularExpressions.RegexOptions.Singleline)
+
+ expectedIL
+ |> List.iter (fun (ilCode: string) ->
+ let expectedLines = ilCode.Split('\n')
+ let startIndex = textNoComments.IndexOf(expectedLines.[0])
+ if startIndex = -1 || textNoComments.Length < startIndex + ilCode.Length then
+ errorMsgOpt <- Some("==EXPECTED CONTAINS==\n" + ilCode + "\n")
+ else
+ let errors = ResizeArray()
+ let actualLines = textNoComments.Substring(startIndex, textNoComments.Length - startIndex).Split('\n')
+ for i = 0 to expectedLines.Length - 1 do
+ let expected = expectedLines.[i].Trim()
+ let actual = actualLines.[i].Trim()
+ if expected <> actual then
+ errors.Add(sprintf "\n==\nName: %s\n\nExpected:\t %s\nActual:\t\t %s\n==" actualLines.[0] expected actual)
+
+ if errors.Count > 0 then
+ let msg = String.concat "\n" errors + "\n\n\n==EXPECTED==\n" + ilCode + "\n"
+ errorMsgOpt <- Some(msg + "\n\n\n==ACTUAL==\n" + String.Join("\n", actualLines, 0, expectedLines.Length))
+ )
+
+ match errorMsgOpt with
+ | Some(msg) -> errorMsgOpt <- Some(msg + "\n\n\n==ENTIRE ACTUAL==\n" + textNoComments)
+ | _ -> ()
+ else
+ errorMsgOpt <- Some(errors)
+ finally
+ try File.Delete(tmp) with | _ -> ()
+ try File.Delete(tmpFs) with | _ -> ()
+ try File.Delete(tmpDll) with | _ -> ()
+ try File.Delete(tmpIL) with | _ -> ()
+
+ match errorMsgOpt with
+ | Some(errorMsg) ->
+ Assert.Fail(errorMsg)
+ | _ -> ()
+
diff --git a/tests/FSharp.Compiler.UnitTests/Language/StringConcat.fs b/tests/FSharp.Compiler.UnitTests/Language/StringConcat.fs
new file mode 100644
index 0000000000000000000000000000000000000000..fa18e7bb4279cfbb56a89342d3340190654b87ee
--- /dev/null
+++ b/tests/FSharp.Compiler.UnitTests/Language/StringConcat.fs
@@ -0,0 +1,845 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace FSharp.Compiler.UnitTests
+
+open System
+open NUnit.Framework
+
+[]
+module StringConcat =
+
+ []
+ let Optimizations () =
+ let baseSource = """
+module Test
+
+open System
+
+let arr = ResizeArray()
+
+let inline ss (x: int) =
+ arr.Add(x)
+ "_" + x.ToString() + "_"
+"""
+
+ let test1Source = """
+let test1 () =
+ ss 1 + ss 2 + ss 3
+"""
+ let test1IL = """.method public static string test1() cil managed
+ {
+
+ .maxstack 7
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.1
+ IL_0001: stloc.0
+ IL_0002: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0007: ldc.i4.1
+ IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_000d: ldstr "_"
+ IL_0012: ldloca.s V_0
+ IL_0014: constrained. [mscorlib]System.Int32
+ IL_001a: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_001f: ldstr "_"
+ IL_0024: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0029: ldc.i4.2
+ IL_002a: stloc.0
+ IL_002b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0030: ldc.i4.2
+ IL_0031: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0036: ldstr "_"
+ IL_003b: ldloca.s V_0
+ IL_003d: constrained. [mscorlib]System.Int32
+ IL_0043: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0048: ldstr "_"
+ IL_004d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0052: ldc.i4.3
+ IL_0053: stloc.0
+ IL_0054: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0059: ldc.i4.3
+ IL_005a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_005f: ldstr "_"
+ IL_0064: ldloca.s V_0
+ IL_0066: constrained. [mscorlib]System.Int32
+ IL_006c: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0071: ldstr "_"
+ IL_0076: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_007b: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0080: ret
+ }"""
+
+ let test2Source = """
+let test2 () =
+ ss 1 + ss 2 + ss 3 + ss 4
+"""
+ let test2IL = """.method public static string test2() cil managed
+ {
+
+ .maxstack 8
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.1
+ IL_0001: stloc.0
+ IL_0002: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0007: ldc.i4.1
+ IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_000d: ldstr "_"
+ IL_0012: ldloca.s V_0
+ IL_0014: constrained. [mscorlib]System.Int32
+ IL_001a: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_001f: ldstr "_"
+ IL_0024: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0029: ldc.i4.2
+ IL_002a: stloc.0
+ IL_002b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0030: ldc.i4.2
+ IL_0031: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0036: ldstr "_"
+ IL_003b: ldloca.s V_0
+ IL_003d: constrained. [mscorlib]System.Int32
+ IL_0043: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0048: ldstr "_"
+ IL_004d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0052: ldc.i4.3
+ IL_0053: stloc.0
+ IL_0054: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0059: ldc.i4.3
+ IL_005a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_005f: ldstr "_"
+ IL_0064: ldloca.s V_0
+ IL_0066: constrained. [mscorlib]System.Int32
+ IL_006c: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0071: ldstr "_"
+ IL_0076: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_007b: ldc.i4.4
+ IL_007c: stloc.0
+ IL_007d: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0082: ldc.i4.4
+ IL_0083: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0088: ldstr "_"
+ IL_008d: ldloca.s V_0
+ IL_008f: constrained. [mscorlib]System.Int32
+ IL_0095: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_009a: ldstr "_"
+ IL_009f: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00a4: call string [mscorlib]System.String::Concat(string,
+ string,
+ string,
+ string)
+ IL_00a9: ret
+ }"""
+
+ let test3Source = """
+let test3 () =
+ ss 1 + ss 2 + ss 3 + ss 4 + ss 5
+"""
+ let test3IL = """.method public static string test3() cil managed
+ {
+
+ .maxstack 8
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.5
+ IL_0001: newarr [mscorlib]System.String
+ IL_0006: dup
+ IL_0007: ldc.i4.0
+ IL_0008: ldc.i4.1
+ IL_0009: stloc.0
+ IL_000a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_000f: ldc.i4.1
+ IL_0010: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0015: ldstr "_"
+ IL_001a: ldloca.s V_0
+ IL_001c: constrained. [mscorlib]System.Int32
+ IL_0022: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0027: ldstr "_"
+ IL_002c: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0031: stelem [mscorlib]System.String
+ IL_0036: dup
+ IL_0037: ldc.i4.1
+ IL_0038: ldc.i4.2
+ IL_0039: stloc.0
+ IL_003a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_003f: ldc.i4.2
+ IL_0040: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0045: ldstr "_"
+ IL_004a: ldloca.s V_0
+ IL_004c: constrained. [mscorlib]System.Int32
+ IL_0052: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0057: ldstr "_"
+ IL_005c: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0061: stelem [mscorlib]System.String
+ IL_0066: dup
+ IL_0067: ldc.i4.2
+ IL_0068: ldc.i4.3
+ IL_0069: stloc.0
+ IL_006a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_006f: ldc.i4.3
+ IL_0070: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0075: ldstr "_"
+ IL_007a: ldloca.s V_0
+ IL_007c: constrained. [mscorlib]System.Int32
+ IL_0082: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0087: ldstr "_"
+ IL_008c: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0091: stelem [mscorlib]System.String
+ IL_0096: dup
+ IL_0097: ldc.i4.3
+ IL_0098: ldc.i4.4
+ IL_0099: stloc.0
+ IL_009a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_009f: ldc.i4.4
+ IL_00a0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_00a5: ldstr "_"
+ IL_00aa: ldloca.s V_0
+ IL_00ac: constrained. [mscorlib]System.Int32
+ IL_00b2: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_00b7: ldstr "_"
+ IL_00bc: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00c1: stelem [mscorlib]System.String
+ IL_00c6: dup
+ IL_00c7: ldc.i4.4
+ IL_00c8: ldc.i4.5
+ IL_00c9: stloc.0
+ IL_00ca: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_00cf: ldc.i4.5
+ IL_00d0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_00d5: ldstr "_"
+ IL_00da: ldloca.s V_0
+ IL_00dc: constrained. [mscorlib]System.Int32
+ IL_00e2: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_00e7: ldstr "_"
+ IL_00ec: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00f1: stelem [mscorlib]System.String
+ IL_00f6: call string [mscorlib]System.String::Concat(string[])
+ IL_00fb: ret
+ }"""
+
+ let test4Source = """
+let test4 () =
+ ss 5 + ss 6 + ss 7 + String.Concat(ss 8, ss 9) + ss 10 + "_50_" + "_60_" + String.Concat(ss 100, String.Concat(ss 101, ss 102), ss 103) + String.Concat([|"_104_";"_105_"|]) + ss 106
+"""
+ let test4IL = """.method public static string test4() cil managed
+ {
+
+ .maxstack 8
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.s 13
+ IL_0002: newarr [mscorlib]System.String
+ IL_0007: dup
+ IL_0008: ldc.i4.0
+ IL_0009: ldc.i4.5
+ IL_000a: stloc.0
+ IL_000b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0010: ldc.i4.5
+ IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0016: ldstr "_"
+ IL_001b: ldloca.s V_0
+ IL_001d: constrained. [mscorlib]System.Int32
+ IL_0023: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0028: ldstr "_"
+ IL_002d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0032: stelem [mscorlib]System.String
+ IL_0037: dup
+ IL_0038: ldc.i4.1
+ IL_0039: ldc.i4.6
+ IL_003a: stloc.0
+ IL_003b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0040: ldc.i4.6
+ IL_0041: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0046: ldstr "_"
+ IL_004b: ldloca.s V_0
+ IL_004d: constrained. [mscorlib]System.Int32
+ IL_0053: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0058: ldstr "_"
+ IL_005d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0062: stelem [mscorlib]System.String
+ IL_0067: dup
+ IL_0068: ldc.i4.2
+ IL_0069: ldc.i4.7
+ IL_006a: stloc.0
+ IL_006b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0070: ldc.i4.7
+ IL_0071: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0076: ldstr "_"
+ IL_007b: ldloca.s V_0
+ IL_007d: constrained. [mscorlib]System.Int32
+ IL_0083: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0088: ldstr "_"
+ IL_008d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0092: stelem [mscorlib]System.String
+ IL_0097: dup
+ IL_0098: ldc.i4.3
+ IL_0099: ldc.i4.8
+ IL_009a: stloc.0
+ IL_009b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_00a0: ldc.i4.8
+ IL_00a1: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_00a6: ldstr "_"
+ IL_00ab: ldloca.s V_0
+ IL_00ad: constrained. [mscorlib]System.Int32
+ IL_00b3: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_00b8: ldstr "_"
+ IL_00bd: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00c2: stelem [mscorlib]System.String
+ IL_00c7: dup
+ IL_00c8: ldc.i4.4
+ IL_00c9: ldc.i4.s 9
+ IL_00cb: stloc.0
+ IL_00cc: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_00d1: ldc.i4.s 9
+ IL_00d3: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_00d8: ldstr "_"
+ IL_00dd: ldloca.s V_0
+ IL_00df: constrained. [mscorlib]System.Int32
+ IL_00e5: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_00ea: ldstr "_"
+ IL_00ef: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00f4: stelem [mscorlib]System.String
+ IL_00f9: dup
+ IL_00fa: ldc.i4.5
+ IL_00fb: ldc.i4.s 10
+ IL_00fd: stloc.0
+ IL_00fe: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0103: ldc.i4.s 10
+ IL_0105: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_010a: ldstr "_"
+ IL_010f: ldloca.s V_0
+ IL_0111: constrained. [mscorlib]System.Int32
+ IL_0117: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_011c: ldstr "_"
+ IL_0121: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0126: stelem [mscorlib]System.String
+ IL_012b: dup
+ IL_012c: ldc.i4.6
+ IL_012d: ldstr "_50__60_"
+ IL_0132: stelem [mscorlib]System.String
+ IL_0137: dup
+ IL_0138: ldc.i4.7
+ IL_0139: ldc.i4.s 100
+ IL_013b: stloc.0
+ IL_013c: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0141: ldc.i4.s 100
+ IL_0143: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0148: ldstr "_"
+ IL_014d: ldloca.s V_0
+ IL_014f: constrained. [mscorlib]System.Int32
+ IL_0155: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_015a: ldstr "_"
+ IL_015f: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0164: stelem [mscorlib]System.String
+ IL_0169: dup
+ IL_016a: ldc.i4.8
+ IL_016b: ldc.i4.s 101
+ IL_016d: stloc.0
+ IL_016e: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0173: ldc.i4.s 101
+ IL_0175: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_017a: ldstr "_"
+ IL_017f: ldloca.s V_0
+ IL_0181: constrained. [mscorlib]System.Int32
+ IL_0187: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_018c: ldstr "_"
+ IL_0191: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0196: stelem [mscorlib]System.String
+ IL_019b: dup
+ IL_019c: ldc.i4.s 9
+ IL_019e: ldc.i4.s 102
+ IL_01a0: stloc.0
+ IL_01a1: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_01a6: ldc.i4.s 102
+ IL_01a8: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_01ad: ldstr "_"
+ IL_01b2: ldloca.s V_0
+ IL_01b4: constrained. [mscorlib]System.Int32
+ IL_01ba: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_01bf: ldstr "_"
+ IL_01c4: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_01c9: stelem [mscorlib]System.String
+ IL_01ce: dup
+ IL_01cf: ldc.i4.s 10
+ IL_01d1: ldc.i4.s 103
+ IL_01d3: stloc.0
+ IL_01d4: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_01d9: ldc.i4.s 103
+ IL_01db: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_01e0: ldstr "_"
+ IL_01e5: ldloca.s V_0
+ IL_01e7: constrained. [mscorlib]System.Int32
+ IL_01ed: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_01f2: ldstr "_"
+ IL_01f7: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_01fc: stelem [mscorlib]System.String
+ IL_0201: dup
+ IL_0202: ldc.i4.s 11
+ IL_0204: ldstr "_104__105_"
+ IL_0209: stelem [mscorlib]System.String
+ IL_020e: dup
+ IL_020f: ldc.i4.s 12
+ IL_0211: ldc.i4.s 106
+ IL_0213: stloc.0
+ IL_0214: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0219: ldc.i4.s 106
+ IL_021b: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0220: ldstr "_"
+ IL_0225: ldloca.s V_0
+ IL_0227: constrained. [mscorlib]System.Int32
+ IL_022d: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0232: ldstr "_"
+ IL_0237: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_023c: stelem [mscorlib]System.String
+ IL_0241: call string [mscorlib]System.String::Concat(string[])
+ IL_0246: ret
+ }"""
+
+ let test5Source = """
+let test5 () =
+ ss 5 + ss 6 + ss 7 + String.Concat(ss 8, ss 9) + ss 10 + "_50_" + "_60_" + String.Concat(ss 100, (let x = String.Concat(ss 101, ss 102) in Console.WriteLine(x);x), ss 103) + String.Concat([|"_104_";"_105_"|]) + ss 106
+"""
+ let test5IL = """.method public static string test5() cil managed
+ {
+
+ .maxstack 9
+ .locals init (int32 V_0,
+ string V_1)
+ IL_0000: ldc.i4.s 12
+ IL_0002: newarr [mscorlib]System.String
+ IL_0007: dup
+ IL_0008: ldc.i4.0
+ IL_0009: ldc.i4.5
+ IL_000a: stloc.0
+ IL_000b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0010: ldc.i4.5
+ IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0016: ldstr "_"
+ IL_001b: ldloca.s V_0
+ IL_001d: constrained. [mscorlib]System.Int32
+ IL_0023: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0028: ldstr "_"
+ IL_002d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0032: stelem [mscorlib]System.String
+ IL_0037: dup
+ IL_0038: ldc.i4.1
+ IL_0039: ldc.i4.6
+ IL_003a: stloc.0
+ IL_003b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0040: ldc.i4.6
+ IL_0041: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0046: ldstr "_"
+ IL_004b: ldloca.s V_0
+ IL_004d: constrained. [mscorlib]System.Int32
+ IL_0053: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0058: ldstr "_"
+ IL_005d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0062: stelem [mscorlib]System.String
+ IL_0067: dup
+ IL_0068: ldc.i4.2
+ IL_0069: ldc.i4.7
+ IL_006a: stloc.0
+ IL_006b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0070: ldc.i4.7
+ IL_0071: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0076: ldstr "_"
+ IL_007b: ldloca.s V_0
+ IL_007d: constrained. [mscorlib]System.Int32
+ IL_0083: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0088: ldstr "_"
+ IL_008d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0092: stelem [mscorlib]System.String
+ IL_0097: dup
+ IL_0098: ldc.i4.3
+ IL_0099: ldc.i4.8
+ IL_009a: stloc.0
+ IL_009b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_00a0: ldc.i4.8
+ IL_00a1: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_00a6: ldstr "_"
+ IL_00ab: ldloca.s V_0
+ IL_00ad: constrained. [mscorlib]System.Int32
+ IL_00b3: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_00b8: ldstr "_"
+ IL_00bd: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00c2: stelem [mscorlib]System.String
+ IL_00c7: dup
+ IL_00c8: ldc.i4.4
+ IL_00c9: ldc.i4.s 9
+ IL_00cb: stloc.0
+ IL_00cc: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_00d1: ldc.i4.s 9
+ IL_00d3: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_00d8: ldstr "_"
+ IL_00dd: ldloca.s V_0
+ IL_00df: constrained. [mscorlib]System.Int32
+ IL_00e5: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_00ea: ldstr "_"
+ IL_00ef: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00f4: stelem [mscorlib]System.String
+ IL_00f9: dup
+ IL_00fa: ldc.i4.5
+ IL_00fb: ldc.i4.s 10
+ IL_00fd: stloc.0
+ IL_00fe: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0103: ldc.i4.s 10
+ IL_0105: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_010a: ldstr "_"
+ IL_010f: ldloca.s V_0
+ IL_0111: constrained. [mscorlib]System.Int32
+ IL_0117: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_011c: ldstr "_"
+ IL_0121: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0126: stelem [mscorlib]System.String
+ IL_012b: dup
+ IL_012c: ldc.i4.6
+ IL_012d: ldstr "_50__60_"
+ IL_0132: stelem [mscorlib]System.String
+ IL_0137: dup
+ IL_0138: ldc.i4.7
+ IL_0139: ldc.i4.s 100
+ IL_013b: stloc.0
+ IL_013c: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0141: ldc.i4.s 100
+ IL_0143: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0148: ldstr "_"
+ IL_014d: ldloca.s V_0
+ IL_014f: constrained. [mscorlib]System.Int32
+ IL_0155: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_015a: ldstr "_"
+ IL_015f: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0164: stelem [mscorlib]System.String
+ IL_0169: dup
+ IL_016a: ldc.i4.8
+ IL_016b: ldc.i4.s 101
+ IL_016d: stloc.0
+ IL_016e: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0173: ldc.i4.s 101
+ IL_0175: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_017a: ldstr "_"
+ IL_017f: ldloca.s V_0
+ IL_0181: constrained. [mscorlib]System.Int32
+ IL_0187: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_018c: ldstr "_"
+ IL_0191: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0196: ldc.i4.s 102
+ IL_0198: stloc.0
+ IL_0199: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_019e: ldc.i4.s 102
+ IL_01a0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_01a5: ldstr "_"
+ IL_01aa: ldloca.s V_0
+ IL_01ac: constrained. [mscorlib]System.Int32
+ IL_01b2: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_01b7: ldstr "_"
+ IL_01bc: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_01c1: call string [mscorlib]System.String::Concat(string,
+ string)
+ IL_01c6: stloc.1
+ IL_01c7: ldloc.1
+ IL_01c8: call void [mscorlib]System.Console::WriteLine(string)
+ IL_01cd: ldloc.1
+ IL_01ce: stelem [mscorlib]System.String
+ IL_01d3: dup
+ IL_01d4: ldc.i4.s 9
+ IL_01d6: ldc.i4.s 103
+ IL_01d8: stloc.0
+ IL_01d9: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_01de: ldc.i4.s 103
+ IL_01e0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_01e5: ldstr "_"
+ IL_01ea: ldloca.s V_0
+ IL_01ec: constrained. [mscorlib]System.Int32
+ IL_01f2: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_01f7: ldstr "_"
+ IL_01fc: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0201: stelem [mscorlib]System.String
+ IL_0206: dup
+ IL_0207: ldc.i4.s 10
+ IL_0209: ldstr "_104__105_"
+ IL_020e: stelem [mscorlib]System.String
+ IL_0213: dup
+ IL_0214: ldc.i4.s 11
+ IL_0216: ldc.i4.s 106
+ IL_0218: stloc.0
+ IL_0219: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_021e: ldc.i4.s 106
+ IL_0220: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0225: ldstr "_"
+ IL_022a: ldloca.s V_0
+ IL_022c: constrained. [mscorlib]System.Int32
+ IL_0232: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0237: ldstr "_"
+ IL_023c: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0241: stelem [mscorlib]System.String
+ IL_0246: call string [mscorlib]System.String::Concat(string[])
+ IL_024b: ret
+ }"""
+
+ let test6Source = """
+let inline inlineStringConcat str1 str2 = str1 + str2
+
+let test6 () =
+ inlineStringConcat (inlineStringConcat (ss 1) (ss 2)) (ss 3) + ss 4
+"""
+ let test6IL = """.method public static string test6() cil managed
+ {
+
+ .maxstack 8
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.1
+ IL_0001: stloc.0
+ IL_0002: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0007: ldc.i4.1
+ IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_000d: ldstr "_"
+ IL_0012: ldloca.s V_0
+ IL_0014: constrained. [mscorlib]System.Int32
+ IL_001a: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_001f: ldstr "_"
+ IL_0024: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0029: ldc.i4.2
+ IL_002a: stloc.0
+ IL_002b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0030: ldc.i4.2
+ IL_0031: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0036: ldstr "_"
+ IL_003b: ldloca.s V_0
+ IL_003d: constrained. [mscorlib]System.Int32
+ IL_0043: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0048: ldstr "_"
+ IL_004d: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_0052: ldc.i4.3
+ IL_0053: stloc.0
+ IL_0054: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0059: ldc.i4.3
+ IL_005a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_005f: ldstr "_"
+ IL_0064: ldloca.s V_0
+ IL_0066: constrained. [mscorlib]System.Int32
+ IL_006c: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0071: ldstr "_"
+ IL_0076: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_007b: ldc.i4.4
+ IL_007c: stloc.0
+ IL_007d: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr()
+ IL_0082: ldc.i4.4
+ IL_0083: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0)
+ IL_0088: ldstr "_"
+ IL_008d: ldloca.s V_0
+ IL_008f: constrained. [mscorlib]System.Int32
+ IL_0095: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_009a: ldstr "_"
+ IL_009f: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_00a4: call string [mscorlib]System.String::Concat(string,
+ string,
+ string,
+ string)
+ IL_00a9: ret
+ }"""
+
+ let test7Source = """
+let test7 () =
+ let x = 1
+ x.ToString() + x.ToString() + x.ToString()
+"""
+ let test7IL = """.method public static string test7() cil managed
+ {
+
+ .maxstack 5
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.1
+ IL_0001: stloc.0
+ IL_0002: ldloca.s V_0
+ IL_0004: constrained. [mscorlib]System.Int32
+ IL_000a: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_000f: ldloca.s V_0
+ IL_0011: constrained. [mscorlib]System.Int32
+ IL_0017: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_001c: ldloca.s V_0
+ IL_001e: constrained. [mscorlib]System.Int32
+ IL_0024: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0029: call string [mscorlib]System.String::Concat(string,
+ string,
+ string)
+ IL_002e: ret
+ }"""
+
+ let test8Source = """
+let test8 () =
+ let x = 1
+ x.ToString() + x.ToString() + x.ToString() + x.ToString()
+"""
+ let test8IL = """.method public static string test8() cil managed
+ {
+
+ .maxstack 6
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.1
+ IL_0001: stloc.0
+ IL_0002: ldloca.s V_0
+ IL_0004: constrained. [mscorlib]System.Int32
+ IL_000a: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_000f: ldloca.s V_0
+ IL_0011: constrained. [mscorlib]System.Int32
+ IL_0017: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_001c: ldloca.s V_0
+ IL_001e: constrained. [mscorlib]System.Int32
+ IL_0024: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0029: ldloca.s V_0
+ IL_002b: constrained. [mscorlib]System.Int32
+ IL_0031: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0036: call string [mscorlib]System.String::Concat(string,
+ string,
+ string,
+ string)
+ IL_003b: ret
+ }"""
+
+ let test9Source = """
+let test9 () =
+ let x = 1
+ x.ToString() + x.ToString() + x.ToString() + x.ToString() + x.ToString()
+"""
+ let test9IL = """.method public static string test9() cil managed
+ {
+
+ .maxstack 6
+ .locals init (int32 V_0)
+ IL_0000: ldc.i4.1
+ IL_0001: stloc.0
+ IL_0002: ldc.i4.5
+ IL_0003: newarr [mscorlib]System.String
+ IL_0008: dup
+ IL_0009: ldc.i4.0
+ IL_000a: ldloca.s V_0
+ IL_000c: constrained. [mscorlib]System.Int32
+ IL_0012: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0017: stelem [mscorlib]System.String
+ IL_001c: dup
+ IL_001d: ldc.i4.1
+ IL_001e: ldloca.s V_0
+ IL_0020: constrained. [mscorlib]System.Int32
+ IL_0026: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_002b: stelem [mscorlib]System.String
+ IL_0030: dup
+ IL_0031: ldc.i4.2
+ IL_0032: ldloca.s V_0
+ IL_0034: constrained. [mscorlib]System.Int32
+ IL_003a: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_003f: stelem [mscorlib]System.String
+ IL_0044: dup
+ IL_0045: ldc.i4.3
+ IL_0046: ldloca.s V_0
+ IL_0048: constrained. [mscorlib]System.Int32
+ IL_004e: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0053: stelem [mscorlib]System.String
+ IL_0058: dup
+ IL_0059: ldc.i4.4
+ IL_005a: ldloca.s V_0
+ IL_005c: constrained. [mscorlib]System.Int32
+ IL_0062: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_0067: stelem [mscorlib]System.String
+ IL_006c: call string [mscorlib]System.String::Concat(string[])
+ IL_0071: ret
+ }"""
+
+ let sources =
+ [
+ baseSource
+ test1Source
+ test2Source
+ test3Source
+ test4Source
+ test5Source
+ test6Source
+ test7Source
+ test8Source
+ test9Source
+ ]
+ let source = String.Join("", sources)
+ ILChecker.check source
+ [
+ test1IL
+ test2IL
+ test3IL
+ test4IL
+ test5IL
+ test6IL
+ test7IL
+ test8IL
+ test9IL
+ ]
diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs
index 4dbb5242b598271df94ee8d165a82b853c2732dc..c79abb4f7914993397647f0c3b25ee504b4a59b7 100644
--- a/tests/fsharp/tests.fs
+++ b/tests/fsharp/tests.fs
@@ -1878,7 +1878,7 @@ module OptimizationTests =
|> Seq.filter (fun line -> line.Contains(".locals init"))
|> Seq.length
- log "Ran ok - optimizations removed %d textual occurrences of optimizable identifiers from target IL" numElim
+ log "Ran ok - optimizations removed %d textual occurrences of optimizable identifiers from target IL" numElim
[]
let stats () =