diff --git a/src/fsharp/TastOps.fs b/src/fsharp/TastOps.fs index 23242b1aff6..1c2361f711b 100644 --- a/src/fsharp/TastOps.fs +++ b/src/fsharp/TastOps.fs @@ -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 diff --git a/tests/FSharp.Compiler.UnitTests/Compiler.fs b/tests/FSharp.Compiler.UnitTests/Compiler.fs new file mode 100644 index 00000000000..a83d8a6f7c3 --- /dev/null +++ b/tests/FSharp.Compiler.UnitTests/Compiler.fs @@ -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 + +[] +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) = + 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) = + 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") + ) \ No newline at end of file diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index bbeac7ee59f..39f58117621 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -16,7 +16,9 @@ + + diff --git a/tests/FSharp.Compiler.UnitTests/ILHelpers.fs b/tests/FSharp.Compiler.UnitTests/ILHelpers.fs index 372e11d0702..ddaf4c6fd60 100644 --- a/tests/FSharp.Compiler.UnitTests/ILHelpers.fs +++ b/tests/FSharp.Compiler.UnitTests/ILHelpers.fs @@ -10,9 +10,10 @@ open NUnit.Framework open Microsoft.FSharp.Compiler.SourceCodeServices +[] module ILChecker = - let checker = FSharpChecker.Create() + let checker = Compiler.checker let private (++) a b = Path.Combine(a,b) diff --git a/tests/FSharp.Compiler.UnitTests/Language/AnonRecords.fs b/tests/FSharp.Compiler.UnitTests/Language/AnonRecords.fs new file mode 100644 index 00000000000..b507fa9afe2 --- /dev/null +++ b/tests/FSharp.Compiler.UnitTests/Language/AnonRecords.fs @@ -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 + +[] +module AnonRecords = + + [] + let NotStructConstraintPass() = + Compiler.AssertPass + """ +type RefClass<'a when 'a : not struct>() = class end +let rAnon = RefClass<{| R: int |}>() + """ + + [] + let StructConstraintPass() = + Compiler.AssertPass + """ +type StructClass<'a when 'a : struct>() = class end +let sAnon = StructClass() + """ + + [] + let NotStructConstraintFail() = + Compiler.AssertSingleErrorTypeCheck + """ + type RefClass<'a when 'a : not struct>() = class end + let rAnon = RefClass() + """ + 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" + + [] + 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" +