Skip to content

Commit 00cbd3f

Browse files
TIHanKevinRansom
authored andcommitted
Fixed uninitialized mutable locals inside loops (#6899)
* Fixed uninitialized mutable locals inside loops * Update ForInDoMutableRegressionTest.fs
1 parent 42c125e commit 00cbd3f

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

src/fsharp/IlxGen.fs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -826,8 +826,15 @@ and IlxGenEnv =
826826

827827
/// Are we under the scope of a try, catch or finally? If so we can't tailcall. SEH = structured exception handling
828828
withinSEH: bool
829+
830+
/// Are we inside of a recursive let binding, while loop, or a for loop?
831+
isInLoop: bool
829832
}
830833

834+
let SetIsInLoop isInLoop eenv =
835+
if eenv.isInLoop = isInLoop then eenv
836+
else { eenv with isInLoop = isInLoop }
837+
831838
let ReplaceTyenv tyenv (eenv: IlxGenEnv) = {eenv with tyenv = tyenv }
832839

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

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

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

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

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

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

74687479
type IlxGenResults =
74697480
{ ilTypeDefs: ILTypeDef list
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace FSharp.Compiler.UnitTests
4+
5+
open System
6+
open NUnit.Framework
7+
8+
[<TestFixture()>]
9+
module ForInDoMutableRegressionTest =
10+
11+
/// This test is to ensure we initialize locals inside loops.
12+
[<Test>]
13+
let Script_ForInDoMutableRegressionTest() =
14+
let script =
15+
"""
16+
open System.Collections.Generic
17+
18+
let bug() =
19+
for a in [1;2;3;4] do
20+
let mutable x = null
21+
if x = null then
22+
x <- HashSet<int>()
23+
x.Add a |> ignore
24+
let expected = [a]
25+
let actual = List.ofSeq x
26+
if expected <> actual then
27+
failwith "Bug"
28+
29+
let not_a_bug() =
30+
for a in [1;2;3;4] do
31+
let x = ref null
32+
if (!x) = null then
33+
x := HashSet<int>()
34+
(!x).Add a |> ignore
35+
let expected = [a]
36+
let actual = List.ofSeq (!x)
37+
if expected <> actual then
38+
failwith "Bug"
39+
40+
let rec test_rec xs =
41+
let mutable x = null
42+
match xs with
43+
| [] -> ()
44+
| a :: xs ->
45+
if x = null then
46+
x <- HashSet<int>()
47+
x.Add a |> ignore
48+
let expected = [a]
49+
let actual = List.ofSeq x
50+
if expected <> actual then
51+
failwith "Bug"
52+
test_rec xs
53+
54+
let test_for_loop () =
55+
let xs = [|1;2;3;4|]
56+
for i = 0 to xs.Length - 1 do
57+
let a = xs.[i]
58+
let mutable x = null
59+
if x = null then
60+
x <- HashSet<int>()
61+
x.Add a |> ignore
62+
let expected = [a]
63+
let actual = List.ofSeq x
64+
if expected <> actual then
65+
failwith "Bug"
66+
67+
bug ()
68+
not_a_bug ()
69+
test_rec [1;2;3;4]
70+
test_for_loop ()
71+
"""
72+
73+
CompilerAssert.RunScript script []

tests/fsharp/FSharpSuite.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<Compile Include="Compiler\Language\SpanOptimizationTests.fs" />
3838
<Compile Include="Compiler\Language\SpanTests.fs" />
3939
<Compile Include="Compiler\Language\StringConcatOptimizationTests.fs" />
40+
<Compile Include="Compiler\Regressions\ForInDoMutableRegressionTest.fs" />
4041
<Content Include="packages.config" />
4142
<None Include="app.config" />
4243
<None Include="update.base.line.with.actuals.fsx" />

0 commit comments

Comments
 (0)