Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed uninitialized mutable locals inside loops #6899

Merged
merged 2 commits into from
May 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/fsharp/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -826,8 +826,15 @@ and IlxGenEnv =

/// Are we under the scope of a try, catch or finally? If so we can't tailcall. SEH = structured exception handling
withinSEH: bool

/// Are we inside of a recursive let binding, while loop, or a for loop?
isInLoop: bool
}

let SetIsInLoop isInLoop eenv =
if eenv.isInLoop = isInLoop then eenv
else { eenv with isInLoop = isInLoop }

let ReplaceTyenv tyenv (eenv: IlxGenEnv) = {eenv with tyenv = tyenv }

let EnvForTypars tps eenv = {eenv with tyenv = TypeReprEnv.ForTypars tps }
Expand Down Expand Up @@ -3369,6 +3376,7 @@ and GenTryFinally cenv cgbuf eenv (bodyExpr, handlerExpr, m, resty, spTry, spFin
//--------------------------------------------------------------------------

and GenForLoop cenv cgbuf eenv (spFor, v, e1, dir, e2, loopBody, m) sequel =
let eenv = SetIsInLoop true eenv
let g = cenv.g

// The JIT/NGen eliminate array-bounds checks for C# loops of form:
Expand Down Expand Up @@ -3459,6 +3467,7 @@ and GenForLoop cenv cgbuf eenv (spFor, v, e1, dir, e2, loopBody, m) sequel =
//--------------------------------------------------------------------------

and GenWhileLoop cenv cgbuf eenv (spWhile, e1, e2, m) sequel =
let eenv = SetIsInLoop true eenv
let finish = CG.GenerateDelayMark cgbuf "while_finish"
let startTest = CG.GenerateMark cgbuf "startTest"

Expand Down Expand Up @@ -5083,6 +5092,7 @@ and GenLetRecFixup cenv cgbuf eenv (ilxCloSpec: IlxClosureSpec, e, ilField: ILFi

/// Generate letrec bindings
and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) =
let eenv = SetIsInLoop true eenv
// Fix up recursion for non-toplevel recursive bindings
let bindsPossiblyRequiringFixup =
allBinds |> List.filter (fun b ->
Expand Down Expand Up @@ -5324,8 +5334,8 @@ and GenBindingAfterSequencePoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) s
| _ ->
let storage = StorageForVal cenv.g m vspec eenv
match storage, rhsExpr with
// locals are zero-init, no need to initialize them
| Local (_, realloc, _), Expr.Const (Const.Zero, _, _) when not realloc ->
// locals are zero-init, no need to initialize them, except if you are in a loop and the local is mutable.
| Local (_, realloc, _), Expr.Const (Const.Zero, _, _) when not realloc && not (eenv.isInLoop && vspec.IsMutable) ->
CommitStartScope cgbuf startScopeMarkOpt
| _ ->
GenBindingRhs cenv cgbuf eenv SPSuppress vspec rhsExpr
Expand Down Expand Up @@ -7463,7 +7473,8 @@ let GetEmptyIlxGenEnv (ilg: ILGlobals) ccu =
liveLocals=IntMap.empty()
innerVals = []
sigToImplRemapInfo = [] (* "module remap info" *)
withinSEH = false }
withinSEH = false
isInLoop = false }

type IlxGenResults =
{ ilTypeDefs: ILTypeDef list
Expand Down
73 changes: 73 additions & 0 deletions tests/fsharp/Compiler/Regressions/ForInDoMutableRegressionTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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

[<TestFixture()>]
module ForInDoMutableRegressionTest =

/// This test is to ensure we initialize locals inside loops.
[<Test>]
let Script_ForInDoMutableRegressionTest() =
let script =
"""
open System.Collections.Generic

let bug() =
for a in [1;2;3;4] do
let mutable x = null
if x = null then
x <- HashSet<int>()
x.Add a |> ignore
let expected = [a]
let actual = List.ofSeq x
if expected <> actual then
failwith "Bug"

let not_a_bug() =
for a in [1;2;3;4] do
let x = ref null
if (!x) = null then
x := HashSet<int>()
(!x).Add a |> ignore
let expected = [a]
let actual = List.ofSeq (!x)
if expected <> actual then
failwith "Bug"

let rec test_rec xs =
let mutable x = null
match xs with
| [] -> ()
| a :: xs ->
if x = null then
x <- HashSet<int>()
x.Add a |> ignore
let expected = [a]
let actual = List.ofSeq x
if expected <> actual then
failwith "Bug"
test_rec xs

let test_for_loop () =
let xs = [|1;2;3;4|]
for i = 0 to xs.Length - 1 do
let a = xs.[i]
let mutable x = null
if x = null then
x <- HashSet<int>()
x.Add a |> ignore
let expected = [a]
let actual = List.ofSeq x
if expected <> actual then
failwith "Bug"

bug ()
not_a_bug ()
test_rec [1;2;3;4]
test_for_loop ()
"""

CompilerAssert.RunScript script []
1 change: 1 addition & 0 deletions tests/fsharp/FSharpSuite.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<Compile Include="Compiler\Language\SpanOptimizationTests.fs" />
<Compile Include="Compiler\Language\SpanTests.fs" />
<Compile Include="Compiler\Language\StringConcatOptimizationTests.fs" />
<Compile Include="Compiler\Regressions\ForInDoMutableRegressionTest.fs" />
<Content Include="packages.config" />
<None Include="app.config" />
<None Include="update.base.line.with.actuals.fsx" />
Expand Down