diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 39c2c63d3c4..8a7d5d4b4fb 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -967,6 +967,9 @@ and IlxGenEnv = /// Are we inside of a recursive let binding, while loop, or a for loop? isInLoop: bool + + /// Indicates that the .locals init flag should be set on a method and all its nested methods and lambdas + initLocals: bool } override _.ToString() = "" @@ -2105,7 +2108,7 @@ let CodeGenMethod cenv mgbuf (entryPointInfo, methodName, eenv, alreadyUsedArgs, let maxStack = maxStack + 2 // Build an Abstract IL method - instrs, mkILMethodBody (true, locals, maxStack, code, sourceRange) + instrs, mkILMethodBody (eenv.initLocals, locals, maxStack, code, sourceRange) let StartDelayedLocalScope nm cgbuf = let startScope = CG.GenerateDelayMark cgbuf ("start_" + nm) @@ -5609,7 +5612,8 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) star | Some _, Some e -> cgbuf.mgbuf.AddReflectedDefinition(vspec, e) | _ -> () - let eenv = {eenv with letBoundVars= (mkLocalValRef vspec) :: eenv.letBoundVars} + let eenv = { eenv with letBoundVars = (mkLocalValRef vspec) :: eenv.letBoundVars; + initLocals = eenv.initLocals && (match vspec.ApparentEnclosingEntity with Parent ref -> not (HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute ref.Attribs) | _ -> true) } let access = ComputeMethodAccessRestrictedBySig eenv vspec @@ -6181,6 +6185,7 @@ and GenMethodForBinding let eenvForMeth = eenvForMeth |> AddStorageForLocalWitnesses (methLambdaWitnessInfos |> List.mapi (fun i w -> (w, Arg (numArgsUsed+i)))) let numArgsUsed = numArgsUsed + methLambdaWitnessInfos.Length let eenvForMeth = eenvForMeth |> AddStorageForLocalVals cenv.g (List.mapi (fun i v -> (v, Arg (numArgsUsed+i))) nonUnitNonSelfMethodVars) + let eenvForMeth = if eenvForMeth.initLocals && HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute v.Attribs then { eenvForMeth with initLocals = false } else eenvForMeth eenvForMeth let tailCallInfo = @@ -6953,7 +6958,7 @@ and GenModuleBinding cenv (cgbuf: CodeGenBuffer) (qname: QualifiedNameOfFile) la let eenvinner = if mspec.IsNamespace then eenv else - {eenv with cloc = CompLocForFixedModule cenv.opts.fragName qname.Text mspec } + { eenv with cloc = CompLocForFixedModule cenv.opts.fragName qname.Text mspec; initLocals = eenv.initLocals && not (HasFSharpAttribute cenv.g cenv.g.attrib_SkipLocalsInitAttribute mspec.Attribs) } // Create the class to hold the contents of this module. No class needed if // we're compiling it as a namespace. @@ -8044,7 +8049,8 @@ let GetEmptyIlxGenEnv (g: TcGlobals) ccu = innerVals = [] sigToImplRemapInfo = [] (* "module remap info" *) withinSEH = false - isInLoop = false } + isInLoop = false + initLocals = true } type IlxGenResults = { ilTypeDefs: ILTypeDef list diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index a21e5e11399..e6ffa6dd522 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -1193,6 +1193,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val attrib_CallerLineNumberAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerLineNumberAttribute" member val attrib_CallerFilePathAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerFilePathAttribute" member val attrib_CallerMemberNameAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerMemberNameAttribute" + member val attrib_SkipLocalsInitAttribute = findSysAttrib "System.Runtime.CompilerServices.SkipLocalsInitAttribute" member val attrib_ProjectionParameterAttribute = mk_MFCore_attrib "ProjectionParameterAttribute" member val attrib_CustomOperationAttribute = mk_MFCore_attrib "CustomOperationAttribute" diff --git a/src/fsharp/ilx/EraseClosures.fs b/src/fsharp/ilx/EraseClosures.fs index 8f87eac097d..13cd0d6cb5c 100644 --- a/src/fsharp/ilx/EraseClosures.fs +++ b/src/fsharp/ilx/EraseClosures.fs @@ -314,7 +314,7 @@ let convILMethodBody (thisClo, boxReturnTy) (il: ILMethodBody) = match boxReturnTy with | None -> code | Some ty -> morphILInstrsInILCode (convReturnInstr ty) code - {il with MaxStack=newMax; IsZeroInit=true; Code= code } + { il with MaxStack = newMax; Code = code } let convMethodBody thisClo = function | MethodBody.IL il -> diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs new file mode 100644 index 00000000000..cd484f2cfe7 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.ComponentTests.EmittedIL + +open Xunit +open FSharp.Test.Utilities.Compiler + +#if NETCOREAPP +module ``SkipLocalsInit`` = + [] + let ``Init in function and closure not emitted when applied on function``() = + FSharp """ +module SkipLocalsInit + +[] +let x () = + [||] |> Array.filter (fun x -> let y = "".Length in y + y = x) |> ignore +""" + |> compile + |> shouldSucceed + |> verifyIL [""" +.method public static void x() cil managed +{ + .custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 4 + .locals (int32[] V_0) +""" + + """ +.method public strict virtual instance bool + Invoke(int32 x) cil managed +{ + + .maxstack 6 + .locals (int32 V_0)"""] + + [] + let ``Init in static method not emitted when applied on class``() = + FSharp """ +module SkipLocalsInit + +[] +type X () = + static member Y () = + let x = "ssa".Length + x + x + """ + |> compile + |> shouldSucceed + |> verifyIL [""" +.custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) +.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) +""" + + """ +.method public static int32 Y() cil managed +{ + + .maxstack 4 + .locals (int32 V_0)"""] + + [] + let ``Init in static method and function not emitted when applied on module``() = + FSharp """ +[] +module SkipLocalsInit + +let x () = + let x = "ssa".Length + x + x + +type X () = + static member Y () = + let x = "ssa".Length + x + x + """ + |> compile + |> shouldSucceed + |> verifyIL [""" +.custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) +.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) +""" + + """ +.method public static int32 x() cil managed +{ + + .maxstack 4 + .locals (int32 V_0) +""" + + """ +.method public static int32 Y() cil managed +{ + + .maxstack 4 + .locals (int32 V_0)"""] + + [] + let ``Init in method and closure not emitted when applied on method``() = + FSharp """ +module SkipLocalsInit + +type X () = + [] + member _.Y () = + [||] |> Array.filter (fun x -> let y = "".Length in y + y = x) |> ignore + let x = "ssa".Length + x + x + """ + |> compile + |> shouldSucceed + |> verifyIL [""" +.method public hidebysig instance int32 + Y() cil managed +{ + .custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 4 + .locals (int32[] V_0, + int32 V_1) +""" + + """ +.method public strict virtual instance bool + Invoke(int32 x) cil managed +{ + + .maxstack 6 + .locals (int32 V_0) +"""] +#endif \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index d5ef0fd4b62..912181214b3 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -22,6 +22,7 @@ +