Skip to content
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
3 changes: 2 additions & 1 deletion src/fsharp/TastOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1773,7 +1773,8 @@ let isRefTy g ty =
isFunTy g ty ||
isReprHiddenTy g ty ||
isFSharpObjModelRefTy g ty ||
isUnitTy g ty
isUnitTy g ty ||
(isAnonRecdTy g ty && not (isStructAnonRecdTy g ty))
)

// ECMA C# LANGUAGE SPECIFICATION, 27.2
Expand Down
59 changes: 59 additions & 0 deletions tests/FSharp.Compiler.UnitTests/Compiler.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace FSharp.Compiler.UnitTests

open System
open Microsoft.FSharp.Compiler.Text
open Microsoft.FSharp.Compiler.SourceCodeServices

open NUnit.Framework

[<RequireQualifiedAccess>]
module Compiler =

let checker = FSharpChecker.Create()

let private defaultProjectOptions =
{
ProjectFileName = "Z:\\test.fsproj"
ProjectId = None
SourceFiles = [|"test.fs"|]
OtherOptions = [||]
ReferencedProjects = [||]
IsIncompleteTypeCheckEnvironment = false
UseScriptResolutionRules = false
LoadTime = DateTime()
UnresolvedReferences = None
OriginalLoadReferences = []
ExtraProjectInfo = None
Stamp = None
}

let AssertPass (source: string) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: lower case

let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions) |> Async.RunSynchronously

Assert.True(parseResults.Errors.Length = 0, sprintf "Parse errors: %A" parseResults.Errors)

match fileAnswer with
| FSharpCheckFileAnswer.Aborted _ -> Assert.Fail("Type Checker Aborted")
| FSharpCheckFileAnswer.Succeeded(typeCheckResults) ->

Assert.True(typeCheckResults.Errors.Length = 0, sprintf "Type Check errors: %A" typeCheckResults.Errors)

let AssertSingleErrorTypeCheck (source: string) (expectedErrorNumber: int) (expectedErrorRange: int * int * int * int) (expectedErrorMsg: string) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: lower case

let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions) |> Async.RunSynchronously

Assert.True(parseResults.Errors.Length = 0, sprintf "Parse errors: %A" parseResults.Errors)

match fileAnswer with
| FSharpCheckFileAnswer.Aborted _ -> Assert.Fail("Type Checker Aborted")
| FSharpCheckFileAnswer.Succeeded(typeCheckResults) ->

Assert.True(typeCheckResults.Errors.Length = 1, sprintf "Expected one type check error: %A" typeCheckResults.Errors)
typeCheckResults.Errors
|> Array.iter (fun info ->
Assert.AreEqual(FSharpErrorSeverity.Error, info.Severity)
Assert.AreEqual(expectedErrorNumber, info.ErrorNumber, "expectedErrorNumber")
Assert.AreEqual(expectedErrorRange, (info.StartLineAlternate, info.StartColumn, info.EndLineAlternate, info.EndColumn), "expectedErrorRange")
Assert.AreEqual(expectedErrorMsg, info.Message, "expectedErrorMsg")
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
<Compile Include="HashIfExpression.fs" />
<Compile Include="ProductVersion.fs" />
<Compile Include="EditDistance.fs" />
<Compile Include="Compiler.fs" />
<Compile Include="ILHelpers.fs" />
<Compile Include="Language\AnonRecords.fs" />
<Compile Include="Language\StringConcat.fs" />
<Compile Include="SourceTextTests.fs" />
</ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion tests/FSharp.Compiler.UnitTests/ILHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ open NUnit.Framework

open Microsoft.FSharp.Compiler.SourceCodeServices

[<RequireQualifiedAccess>]
module ILChecker =

let checker = FSharpChecker.Create()
let checker = Compiler.checker

let private (++) a b = Path.Combine(a,b)

Expand Down
47 changes: 47 additions & 0 deletions tests/FSharp.Compiler.UnitTests/Language/AnonRecords.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace FSharp.Compiler.UnitTests

open NUnit.Framework

[<TestFixture>]
module AnonRecords =

[<Test>]
let NotStructConstraintPass() =
Compiler.AssertPass
"""
type RefClass<'a when 'a : not struct>() = class end
let rAnon = RefClass<{| R: int |}>()
"""

[<Test>]
let StructConstraintPass() =
Compiler.AssertPass
"""
type StructClass<'a when 'a : struct>() = class end
let sAnon = StructClass<struct {| S: int |}>()
"""

[<Test>]
let NotStructConstraintFail() =
Compiler.AssertSingleErrorTypeCheck
"""
type RefClass<'a when 'a : not struct>() = class end
let rAnon = RefClass<struct {| R: int |}>()
"""
1
(3, 16, 3, 45)
"A generic construct requires that the type 'struct {|R : int|}' have reference semantics, but it does not, i.e. it is a struct"

[<Test>]
let StructConstraintFail() =
Compiler.AssertSingleErrorTypeCheck
"""
type StructClass<'a when 'a : struct>() = class end
let sAnon = StructClass<{| S: int |}>()
"""
1
(3, 12, 3, 37)
"A generic construct requires that the type '{|S : int|}' is a CLI or F# struct type"