diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index 94fbd7ff60f..0cd10e2fc4e 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -767,7 +767,7 @@ let BuildILMethInfoCall g amap m isProp (minfo: ILMethInfo) valUseFlags minst di let ilMethRef = minfo.ILMethodRef let newobj = ctor && (match valUseFlags with NormalValUse -> true | _ -> false) let exprTy = if ctor then minfo.ApparentEnclosingType else minfo.GetFSharpReturnTy(amap, m, minst) - let retTy = if not ctor && ilMethRef.ReturnType = ILType.Void then [] else [exprTy] + let retTy = if not ctor && (stripILModifiedFromTy ilMethRef.ReturnType) = ILType.Void then [] else [exprTy] let isDllImport = minfo.IsDllImport g Expr.Op (TOp.ILCall (useCallvirt, isProtected, valu, newobj, valUseFlags, isProp, isDllImport, ilMethRef, minfo.DeclaringTypeInst, minst, retTy), [], args, m), exprTy diff --git a/src/fsharp/absil/il.fs b/src/fsharp/absil/il.fs index a93aac6893b..a2e1f0ee3ec 100644 --- a/src/fsharp/absil/il.fs +++ b/src/fsharp/absil/il.fs @@ -2722,6 +2722,11 @@ let isILBoxedTy = function ILType.Boxed _ -> true | _ -> false let isILValueTy = function ILType.Value _ -> true | _ -> false +let rec stripILModifiedFromTy (ty: ILType) = + match ty with + | ILType.Modified(_, _, ty) -> stripILModifiedFromTy ty + | _ -> ty + let isBuiltInTySpec (ilg: ILGlobals) (tspec: ILTypeSpec) n = let tref = tspec.TypeRef let scoref = tref.Scope diff --git a/src/fsharp/absil/il.fsi b/src/fsharp/absil/il.fsi index 2f8b44a627b..14ce112b1d6 100644 --- a/src/fsharp/absil/il.fsi +++ b/src/fsharp/absil/il.fsi @@ -2002,6 +2002,9 @@ val internal instILType: ILGenericArgs -> ILType -> ILType /// This is a 'vendor neutral' way of referencing mscorlib. val internal ecmaPublicKey: PublicKey +/// Strips ILType.Modified from the ILType. +val internal stripILModifiedFromTy: ILType -> ILType + /// Discriminating different important built-in types. val internal isILObjectTy: ILGlobals -> ILType -> bool val internal isILStringTy: ILGlobals -> ILType -> bool diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index c80b05bbdb9..0233c3f3954 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -122,8 +122,8 @@ module rec Compiler = | null -> failwith "Source cannot be null" | _ -> { Source = Text source - LangVersion = CSharpLanguageVersion.CSharp8 - TargetFramework = TargetFramework.NetCoreApp31 + LangVersion = CSharpLanguageVersion.CSharp9 + TargetFramework = TargetFramework.Current Name = None References = [] } diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 240275139d4..c2f4e33607e 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -76,109 +76,6 @@ type CompilerAssert private () = static let checker = FSharpChecker.Create(suggestNamesForErrors=true) - static let config = TestFramework.initializeSuite () - - static let _ = config |> ignore - -// Do a one time dotnet sdk build to compute the proper set of reference assemblies to pass to the compiler - static let projectFile = """ - - - - Exe - $TARGETFRAMEWORK - true - true - - - - - - - - - - - - - - - - - - - - - - - -""" - - static let directoryBuildProps = """ - - -""" - - static let directoryBuildTargets = """ - - -""" - - static let programFs = """ -open System - -[] -let main argv = 0""" - - static let getNetCoreAppReferences = - let mutable output = "" - let mutable errors = "" - let mutable cleanUp = true - let pathToArtifacts = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../../../..")) - if Path.GetFileName(pathToArtifacts) <> "artifacts" then failwith "CompilerAssert did not find artifacts directory --- has the location changed????" - let pathToTemp = Path.Combine(pathToArtifacts, "Temp") - let projectDirectory = Path.Combine(pathToTemp, "CompilerAssert", Path.GetRandomFileName()) - let pathToFSharpCore = typeof.Assembly.Location - try - try - Directory.CreateDirectory(projectDirectory) |> ignore - let projectFileName = Path.Combine(projectDirectory, "ProjectFile.fsproj") - let programFsFileName = Path.Combine(projectDirectory, "Program.fs") - let directoryBuildPropsFileName = Path.Combine(projectDirectory, "Directory.Build.props") - let directoryBuildTargetsFileName = Path.Combine(projectDirectory, "Directory.Build.targets") - let frameworkReferencesFileName = Path.Combine(projectDirectory, "FrameworkReferences.txt") -#if NETCOREAPP - File.WriteAllText(projectFileName, projectFile.Replace("$TARGETFRAMEWORK", "net5.0").Replace("$FSHARPCORELOCATION", pathToFSharpCore)) -#else - File.WriteAllText(projectFileName, projectFile.Replace("$TARGETFRAMEWORK", "net472").Replace("$FSHARPCORELOCATION", pathToFSharpCore)) -#endif - File.WriteAllText(programFsFileName, programFs) - File.WriteAllText(directoryBuildPropsFileName, directoryBuildProps) - File.WriteAllText(directoryBuildTargetsFileName, directoryBuildTargets) - - let timeout = 30000 - let exitCode, output, errors = Commands.executeProcess (Some config.DotNetExe) "build" projectDirectory timeout - - if exitCode <> 0 || errors.Length > 0 then - printfn "Output:\n=======\n" - output |> Seq.iter(fun line -> printfn "STDOUT:%s\n" line) - printfn "Errors:\n=======\n" - errors |> Seq.iter(fun line -> printfn "STDERR:%s\n" line) - Assert.True(false, "Errors produced generating References") - - File.ReadLines(frameworkReferencesFileName) |> Seq.toArray - with | e -> - cleanUp <- false - printfn "Project directory: %s" projectDirectory - printfn "STDOUT: %s" output - File.WriteAllText(Path.Combine(projectDirectory, "project.stdout"), output) - printfn "STDERR: %s" errors - File.WriteAllText(Path.Combine(projectDirectory, "project.stderror"), errors) - raise (new Exception (sprintf "An error occurred getting netcoreapp references: %A" e)) - finally - if cleanUp then - try Directory.Delete(projectDirectory, recursive=true) with | _ -> () - #if FX_NO_APP_DOMAINS static let executeBuiltApp assembly deps = let ctxt = AssemblyLoadContext("ContextName", true) @@ -214,7 +111,7 @@ let main argv = 0""" ProjectId = None SourceFiles = [|"test.fs"|] OtherOptions = - let assemblies = getNetCoreAppReferences |> Array.map (fun x -> sprintf "-r:%s" x) + let assemblies = TargetFrameworkUtil.currentReferences |> Array.map (fun x -> sprintf "-r:%s" x) #if NETCOREAPP Array.append [|"--preferreduilang:en-US"; "--targetprofile:netcore"; "--noframework"; "--simpleresolution"; "--warn:5"|] assemblies #else @@ -510,7 +407,7 @@ let main argv = 0""" let dependencies = #if NETCOREAPP - Array.toList getNetCoreAppReferences + Array.toList TargetFrameworkUtil.currentReferences #else [] #endif @@ -534,7 +431,7 @@ let main argv = 0""" let dependencies = #if NETCOREAPP - Array.toList getNetCoreAppReferences + Array.toList TargetFrameworkUtil.currentReferences #else [] #endif diff --git a/tests/FSharp.Test.Utilities/Utilities.fs b/tests/FSharp.Test.Utilities/Utilities.fs index 762849037cf..fa02e829b7b 100644 --- a/tests/FSharp.Test.Utilities/Utilities.fs +++ b/tests/FSharp.Test.Utilities/Utilities.fs @@ -10,6 +10,8 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.CSharp open System.Diagnostics open FSharp.Test.Utilities +open TestFramework +open NUnit.Framework // This file mimics how Roslyn handles their compilation references for compilation testing @@ -19,6 +21,7 @@ module Utilities = type TargetFramework = | NetStandard20 | NetCoreApp31 + | Current let private getResourceStream name = let assembly = typeof.GetTypeInfo().Assembly @@ -77,6 +80,107 @@ module Utilities = [] module TargetFrameworkUtil = + let private config = TestFramework.initializeSuite () + + // Do a one time dotnet sdk build to compute the proper set of reference assemblies to pass to the compiler + let private projectFile = """ + + + + Exe + $TARGETFRAMEWORK + true + true + + + + + + + + + + + + + + + + + + + + + + + +""" + + let private directoryBuildProps = """ + + +""" + + let private directoryBuildTargets = """ + + +""" + + let private programFs = """ +open System + +[] +let main argv = 0""" + + let private getNetCoreAppReferences = + let mutable output = "" + let mutable errors = "" + let mutable cleanUp = true + let pathToArtifacts = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../../../..")) + if Path.GetFileName(pathToArtifacts) <> "artifacts" then failwith "CompilerAssert did not find artifacts directory --- has the location changed????" + let pathToTemp = Path.Combine(pathToArtifacts, "Temp") + let projectDirectory = Path.Combine(pathToTemp, "CompilerAssert", Path.GetRandomFileName()) + let pathToFSharpCore = typeof.Assembly.Location + try + try + Directory.CreateDirectory(projectDirectory) |> ignore + let projectFileName = Path.Combine(projectDirectory, "ProjectFile.fsproj") + let programFsFileName = Path.Combine(projectDirectory, "Program.fs") + let directoryBuildPropsFileName = Path.Combine(projectDirectory, "Directory.Build.props") + let directoryBuildTargetsFileName = Path.Combine(projectDirectory, "Directory.Build.targets") + let frameworkReferencesFileName = Path.Combine(projectDirectory, "FrameworkReferences.txt") +#if NETCOREAPP + File.WriteAllText(projectFileName, projectFile.Replace("$TARGETFRAMEWORK", "net5.0").Replace("$FSHARPCORELOCATION", pathToFSharpCore)) +#else + File.WriteAllText(projectFileName, projectFile.Replace("$TARGETFRAMEWORK", "net472").Replace("$FSHARPCORELOCATION", pathToFSharpCore)) +#endif + File.WriteAllText(programFsFileName, programFs) + File.WriteAllText(directoryBuildPropsFileName, directoryBuildProps) + File.WriteAllText(directoryBuildTargetsFileName, directoryBuildTargets) + + let timeout = 30000 + let exitCode, output, errors = Commands.executeProcess (Some config.DotNetExe) "build" projectDirectory timeout + + if exitCode <> 0 || errors.Length > 0 then + printfn "Output:\n=======\n" + output |> Seq.iter(fun line -> printfn "STDOUT:%s\n" line) + printfn "Errors:\n=======\n" + errors |> Seq.iter(fun line -> printfn "STDERR:%s\n" line) + Assert.True(false, "Errors produced generating References") + + File.ReadLines(frameworkReferencesFileName) |> Seq.toArray + with | e -> + cleanUp <- false + printfn "Project directory: %s" projectDirectory + printfn "STDOUT: %s" output + File.WriteAllText(Path.Combine(projectDirectory, "project.stdout"), output) + printfn "STDERR: %s" errors + File.WriteAllText(Path.Combine(projectDirectory, "project.stderror"), errors) + raise (new Exception (sprintf "An error occurred getting netcoreapp references: %A" e)) + finally + if cleanUp then + try Directory.Delete(projectDirectory, recursive=true) with | _ -> () + open TestReferences let private netStandard20References = @@ -84,10 +188,21 @@ module Utilities = let private netCoreApp31References = lazy ImmutableArray.Create(NetCoreApp31.netStandard.Value, NetCoreApp31.mscorlibRef.Value, NetCoreApp31.systemRuntimeRef.Value, NetCoreApp31.systemCoreRef.Value, NetCoreApp31.systemDynamicRuntimeRef.Value, NetCoreApp31.systemConsoleRef.Value) + let currentReferences = + getNetCoreAppReferences + + let currentReferencesAsPEs = + getNetCoreAppReferences + |> Seq.map (fun x -> + PortableExecutableReference.CreateFromFile(x) + ) + |> ImmutableArray.CreateRange + let getReferences tf = match tf with | TargetFramework.NetStandard20 -> netStandard20References.Value | TargetFramework.NetCoreApp31 -> netCoreApp31References.Value + | TargetFramework.Current -> currentReferencesAsPEs type RoslynLanguageVersion = LanguageVersion @@ -128,6 +243,7 @@ module Utilities = type CSharpLanguageVersion = | CSharp8 = 0 + | CSharp9 = 1 [] type CompilationUtil private () = @@ -136,6 +252,7 @@ module Utilities = let lv = match lv with | CSharpLanguageVersion.CSharp8 -> LanguageVersion.CSharp8 + | CSharpLanguageVersion.CSharp9 -> LanguageVersion.CSharp9 | _ -> LanguageVersion.Default let tf = defaultArg tf TargetFramework.NetStandard20 diff --git a/tests/fsharp/Compiler/Language/InitOnlyPropertyConsumptionTests.fs b/tests/fsharp/Compiler/Language/InitOnlyPropertyConsumptionTests.fs new file mode 100644 index 00000000000..eca26290cec --- /dev/null +++ b/tests/fsharp/Compiler/Language/InitOnlyPropertyConsumptionTests.fs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests + +open NUnit.Framework +open FSharp.Test.Utilities.Compiler +open FSharp.Test.Utilities +open FSharp.Test.Utilities.Utilities +open FSharp.Tests + +#if NETCOREAPP + +[] +module InitOnlyPropertyConsumptionTests = + + [] + let ``Should be able to set an init-only property from a C# record`` () = + let csharpSource = + """ +using System; + +namespace CSharpTest +{ + public record Test + { + public int X { get; init; } + } +} + """ + + let fsharpSource = + """ +open System +open System.Runtime.CompilerServices +open CSharpTest + +let test() = + Test(X = 123) + +[] +let main _ = + let x = test() + Console.Write(x.X) + 0 + """ + + let csCmpl = + CompilationUtil.CreateCSharpCompilation(csharpSource, CSharpLanguageVersion.CSharp9, TargetFramework.Current) + |> CompilationReference.Create + + let fsCmpl = + Compilation.Create(fsharpSource, Fs, Exe, options = [|"--langversion:5.0"|], cmplRefs = [csCmpl]) + + CompilerAssert.ExecutionHasOutput(fsCmpl, "123") + +#endif + diff --git a/tests/fsharp/FSharpSuite.Tests.fsproj b/tests/fsharp/FSharpSuite.Tests.fsproj index 6ba0c4a6a5d..ce4becaea00 100644 --- a/tests/fsharp/FSharpSuite.Tests.fsproj +++ b/tests/fsharp/FSharpSuite.Tests.fsproj @@ -40,6 +40,7 @@ +