diff --git a/eng/Versions.props b/eng/Versions.props
index e4ae8526a0b..3cd88abc9bf 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -23,14 +23,14 @@
$(FSCorePackageVersion)-$(PreReleaseVersionLabel).*
- 10.4
- $(FSPackageMajorVersion).3
+ 10.5
+ $(FSPackageMajorVersion).0
$(FSPackageVersion)
$(FSPackageVersion).0
16
- 1
+ 2
$(VSMajorVersion).0
$(VSMajorVersion).$(VSMinorVersion).0
$(VSAssemblyVersionPrefix).0
diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props
index 5d145af11cf..6360088320f 100644
--- a/eng/targets/Settings.props
+++ b/eng/targets/Settings.props
@@ -8,7 +8,8 @@
- true
+
+ false
diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs
index c188b548073..826045231ac 100644
--- a/src/fsharp/IlxGen.fs
+++ b/src/fsharp/IlxGen.fs
@@ -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 }
@@ -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:
@@ -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"
@@ -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 ->
@@ -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
@@ -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
diff --git a/src/fsharp/fsi/fsi.fsproj b/src/fsharp/fsi/fsi.fsproj
index fb55d4d898f..cace3785d08 100644
--- a/src/fsharp/fsi/fsi.fsproj
+++ b/src/fsharp/fsi/fsi.fsproj
@@ -33,6 +33,9 @@
+
+
+
diff --git a/tests/fsharp/Compiler/Regressions/ForInDoMutableRegressionTest.fs b/tests/fsharp/Compiler/Regressions/ForInDoMutableRegressionTest.fs
new file mode 100644
index 00000000000..7319c2fab76
--- /dev/null
+++ b/tests/fsharp/Compiler/Regressions/ForInDoMutableRegressionTest.fs
@@ -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
+
+[]
+module ForInDoMutableRegressionTest =
+
+ /// This test is to ensure we initialize locals inside loops.
+ []
+ 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()
+ 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()
+ (!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()
+ 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()
+ 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 []
diff --git a/tests/fsharp/FSharpSuite.Tests.fsproj b/tests/fsharp/FSharpSuite.Tests.fsproj
index 56240b466b4..9376f62dcdf 100644
--- a/tests/fsharp/FSharpSuite.Tests.fsproj
+++ b/tests/fsharp/FSharpSuite.Tests.fsproj
@@ -37,6 +37,7 @@
+
diff --git a/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest b/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest
index e272923a3da..cae75397665 100644
--- a/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest
+++ b/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest
@@ -28,7 +28,10 @@
+
+
diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs
index 51cb4bbeec8..a555b47ff3a 100644
--- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs
+++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs
@@ -24,58 +24,30 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices
[]
module private FSharpProjectOptionsHelpers =
- let mapCpsProjectToSite(workspace:VisualStudioWorkspace, project:Project, serviceProvider:System.IServiceProvider, cpsCommandLineOptions: IDictionary) =
- let hier = workspace.GetHierarchy(project.Id)
+ let mapCpsProjectToSite(project:Project, cpsCommandLineOptions: IDictionary) =
let sourcePaths, referencePaths, options =
match cpsCommandLineOptions.TryGetValue(project.Id) with
| true, (sourcePaths, options) -> sourcePaths, [||], options
| false, _ -> [||], [||], [||]
+ let mutable errorReporter = Unchecked.defaultof<_>
{
- new IProvideProjectSite with
- member x.GetProjectSite() =
- let mutable errorReporter =
- let reporter = ProjectExternalErrorReporter(project.Id, "FS", serviceProvider)
- Some(reporter:> IVsLanguageServiceBuildErrorReporter2)
-
- {
- new IProjectSite with
- member __.Description = project.Name
- member __.CompilationSourceFiles = sourcePaths
- member __.CompilationOptions =
- Array.concat [options; referencePaths |> Array.map(fun r -> "-r:" + r)]
- member __.CompilationReferences = referencePaths
- member site.CompilationBinOutputPath = site.CompilationOptions |> Array.tryPick (fun s -> if s.StartsWith("-o:") then Some s.[3..] else None)
- member __.ProjectFileName = project.FilePath
- member __.AdviseProjectSiteChanges(_,_) = ()
- member __.AdviseProjectSiteCleaned(_,_) = ()
- member __.AdviseProjectSiteClosed(_,_) = ()
- member __.IsIncompleteTypeCheckEnvironment = false
- member __.TargetFrameworkMoniker = ""
- member __.ProjectGuid = project.Id.Id.ToString()
- member __.LoadTime = System.DateTime.Now
- member __.ProjectProvider = Some (x)
- member __.BuildErrorReporter with get () = errorReporter and set (v) = errorReporter <- v
- }
- interface IVsHierarchy with
- member __.SetSite(psp) = hier.SetSite(psp)
- member __.GetSite(psp) = hier.GetSite(ref psp)
- member __.QueryClose(pfCanClose)= hier.QueryClose(ref pfCanClose)
- member __.Close() = hier.Close()
- member __.GetGuidProperty(itemid, propid, pguid) = hier.GetGuidProperty(itemid, propid, ref pguid)
- member __.SetGuidProperty(itemid, propid, rguid) = hier.SetGuidProperty(itemid, propid, ref rguid)
- member __.GetProperty(itemid, propid, pvar) = hier.GetProperty(itemid, propid, ref pvar)
- member __.SetProperty(itemid, propid, var) = hier.SetProperty(itemid, propid, var)
- member __.GetNestedHierarchy(itemid, iidHierarchyNested, ppHierarchyNested, pitemidNested) =
- hier.GetNestedHierarchy(itemid, ref iidHierarchyNested, ref ppHierarchyNested, ref pitemidNested)
- member __.GetCanonicalName(itemid, pbstrName) = hier.GetCanonicalName(itemid, ref pbstrName)
- member __.ParseCanonicalName(pszName, pitemid) = hier.ParseCanonicalName(pszName, ref pitemid)
- member __.Unused0() = hier.Unused0()
- member __.AdviseHierarchyEvents(pEventSink, pdwCookie) = hier.AdviseHierarchyEvents(pEventSink, ref pdwCookie)
- member __.UnadviseHierarchyEvents(dwCookie) = hier.UnadviseHierarchyEvents(dwCookie)
- member __.Unused1() = hier.Unused1()
- member __.Unused2() = hier.Unused2()
- member __.Unused3() = hier.Unused3()
- member __.Unused4() = hier.Unused4()
+ new IProjectSite with
+ member __.Description = project.Name
+ member __.CompilationSourceFiles = sourcePaths
+ member __.CompilationOptions =
+ Array.concat [options; referencePaths |> Array.map(fun r -> "-r:" + r)]
+ member __.CompilationReferences = referencePaths
+ member site.CompilationBinOutputPath = site.CompilationOptions |> Array.tryPick (fun s -> if s.StartsWith("-o:") then Some s.[3..] else None)
+ member __.ProjectFileName = project.FilePath
+ member __.AdviseProjectSiteChanges(_,_) = ()
+ member __.AdviseProjectSiteCleaned(_,_) = ()
+ member __.AdviseProjectSiteClosed(_,_) = ()
+ member __.IsIncompleteTypeCheckEnvironment = false
+ member __.TargetFrameworkMoniker = ""
+ member __.ProjectGuid = project.Id.Id.ToString()
+ member __.LoadTime = System.DateTime.Now
+ member __.ProjectProvider = None
+ member __.BuildErrorReporter with get () = errorReporter and set (v) = errorReporter <- v
}
let hasProjectVersionChanged (oldProject: Project) (newProject: Project) =
@@ -108,11 +80,13 @@ type private FSharpProjectOptionsMessage =
| ClearSingleFileOptionsCache of DocumentId
[]
-type private FSharpProjectOptionsReactor (workspace: VisualStudioWorkspace, settings: EditorOptions, serviceProvider, checkerProvider: FSharpCheckerProvider) =
+type private FSharpProjectOptionsReactor (_workspace: VisualStudioWorkspace, settings: EditorOptions, _serviceProvider, checkerProvider: FSharpCheckerProvider) =
let cancellationTokenSource = new CancellationTokenSource()
// Hack to store command line options from HandleCommandLineChanges
- let cpsCommandLineOptions = new ConcurrentDictionary()
+ let cpsCommandLineOptions = ConcurrentDictionary()
+
+ let legacyProjectSites = ConcurrentDictionary()
let cache = Dictionary()
let singleFileCache = Dictionary()
@@ -158,6 +132,16 @@ type private FSharpProjectOptionsReactor (workspace: VisualStudioWorkspace, sett
else
return Some(parsingOptions, projectOptions)
}
+
+ let tryGetProjectSite (project: Project) =
+ // Cps
+ if cpsCommandLineOptions.ContainsKey project.Id then
+ Some (mapCpsProjectToSite(project, cpsCommandLineOptions))
+ else
+ // Legacy
+ match legacyProjectSites.TryGetValue project.Id with
+ | true, site -> Some site
+ | _ -> None
let rec tryComputeOptions (project: Project) =
async {
@@ -183,15 +167,9 @@ type private FSharpProjectOptionsReactor (workspace: VisualStudioWorkspace, sett
return None
else
- let hier = workspace.GetHierarchy(projectId)
- let projectSite =
- match hier with
- // Legacy
- | (:? IProvideProjectSite as provideSite) -> provideSite.GetProjectSite()
- // Cps
- | _ ->
- let provideSite = mapCpsProjectToSite(workspace, project, serviceProvider, cpsCommandLineOptions)
- provideSite.GetProjectSite()
+ match tryGetProjectSite project with
+ | None -> return None
+ | Some projectSite ->
let otherOptions =
project.ProjectReferences
@@ -283,6 +261,7 @@ type private FSharpProjectOptionsReactor (workspace: VisualStudioWorkspace, sett
| FSharpProjectOptionsMessage.ClearOptions(projectId) ->
cache.Remove(projectId) |> ignore
+ legacyProjectSites.TryRemove(projectId) |> ignore
| FSharpProjectOptionsMessage.ClearSingleFileOptionsCache(documentId) ->
singleFileCache.Remove(documentId) |> ignore
}
@@ -304,6 +283,9 @@ type private FSharpProjectOptionsReactor (workspace: VisualStudioWorkspace, sett
member __.SetCpsCommandLineOptions(projectId, sourcePaths, options) =
cpsCommandLineOptions.[projectId] <- (sourcePaths, options)
+ member __.SetLegacyProjectSite (projectId, projectSite) =
+ legacyProjectSites.[projectId] <- projectSite
+
member __.TryGetCachedOptionsByProjectId(projectId) =
match cache.TryGetValue(projectId) with
| true, result -> Some(result)
@@ -344,6 +326,9 @@ type internal FSharpProjectOptionsManager
| _ -> ()
)
+ member __.SetLegacyProjectSite (projectId, projectSite) =
+ reactor.SetLegacyProjectSite (projectId, projectSite)
+
/// Clear a project from the project table
member this.ClearInfoForProject(projectId:ProjectId) =
reactor.ClearOptionsByProjectId(projectId)
diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs
index ce08dd4f026..a3b7b6f7586 100644
--- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs
+++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs
@@ -19,6 +19,7 @@ open Microsoft.VisualStudio.LanguageServices.ProjectSystem
open Microsoft.VisualStudio.Shell
open Microsoft.VisualStudio.Shell.Interop
open Microsoft.VisualStudio.Text.Outlining
+open FSharp.NativeInterop
open Microsoft.CodeAnalysis.ExternalAccess.FSharp
#nowarn "9" // NativePtr.toNativeInt
diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LegacyProjectWorkspaceMap.fs b/vsintegration/src/FSharp.Editor/LanguageService/LegacyProjectWorkspaceMap.fs
index 0bf2e5c8063..426ecf9a5f2 100644
--- a/vsintegration/src/FSharp.Editor/LanguageService/LegacyProjectWorkspaceMap.fs
+++ b/vsintegration/src/FSharp.Editor/LanguageService/LegacyProjectWorkspaceMap.fs
@@ -46,6 +46,8 @@ type internal LegacyProjectWorkspaceMap(solution: IVsSolution,
let projectId = projectContext.Id
+ projectInfoManager.SetLegacyProjectSite (projectId, site)
+
// Sync the source files in projectContext. Note that these source files are __not__ maintained in order in projectContext
// as edits are made. It seems this is ok because the source file list is only used to drive roslyn per-file checking.
let updatedFiles = site.CompilationSourceFiles |> wellFormedFilePathSetIgnoreCase
diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs
index 8ac6f929186..7b90bb3aefc 100644
--- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs
+++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs
@@ -164,7 +164,7 @@ type internal FSharpAsyncQuickInfoSource
checkerProvider:FSharpCheckerProvider,
projectInfoManager:FSharpProjectOptionsManager,
textBuffer:ITextBuffer,
- settings: EditorOptions
+ _settings: EditorOptions
) =
static let joinWithLineBreaks segments =
@@ -206,9 +206,10 @@ type internal FSharpAsyncQuickInfoSource
// This method can be called from the background thread.
// Do not call IServiceProvider.GetService here.
override __.GetQuickInfoItemAsync(session:IAsyncQuickInfoSession, cancellationToken:CancellationToken) : Task =
- // if using LSP, just bail early
- if settings.Advanced.UsePreviewTextHover then Task.FromResult(null)
- else
+ // The following lines should be disabled for branch `release/dev16.2`, enabled otherwise
+ //// if using LSP, just bail early
+ //if settings.Advanced.UsePreviewTextHover then Task.FromResult(null)
+ //else
let triggerPoint = session.GetTriggerPoint(textBuffer.CurrentSnapshot)
match triggerPoint.HasValue with
| false -> Task.FromResult(null)
diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml
index a14d4cbadfb..f8dd82f7d06 100644
--- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml
+++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml
@@ -24,10 +24,13 @@
+
+