diff --git a/eng/Build.ps1 b/eng/Build.ps1 index 9a97f5a93f..00328d5c63 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -497,7 +497,7 @@ try { Create-Directory $resultsRoot UpdatePath $env:HOSTED_COMPILER = 1 - $env:CSC_PIPE = "$nugetPackages\Microsoft.Net.Compilers\2.7.0\tools\csc.exe" + $env:CSC_PIPE = "$nugetPackages\Microsoft.Net.Compilers\3.9.0\tools\csc.exe" $env:FSCOREDLLPATH = "$ArtifactsDir\bin\fsc\$configuration\net472\FSharp.Core.dll" $env:LINK_EXE = "$RepoRoot\tests\fsharpqa\testenv\bin\link\link.exe" $env:OSARCH = $env:PROCESSOR_ARCHITECTURE diff --git a/eng/Versions.props b/eng/Versions.props index dfab66e55a..be0e3114f9 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -188,7 +188,7 @@ 4.3.0.0 1.0.30 8.0.0 - 2.7.0 + 3.9.0 3.1.0 5.0.0-preview.7.20364.11 5.0.0-preview.7.20364.11 diff --git a/src/fsharp/CheckExpressions.fs b/src/fsharp/CheckExpressions.fs index b06a6d2bcc..861a1bdaf3 100644 --- a/src/fsharp/CheckExpressions.fs +++ b/src/fsharp/CheckExpressions.fs @@ -8243,6 +8243,8 @@ and TcLookupThen cenv overallTy env tpenv mObjExpr objExpr objExprTy longId dela if isNil meths then error (Error (FSComp.SR.tcPropertyIsNotReadable nm, mItem)) TcMethodApplicationThen cenv env overallTy None tpenv tyargsOpt objArgs mExprAndItem mItem nm ad PossiblyMutates true meths afterResolution NormalValUse args atomicFlag delayed else + if pinfo.SetterIsInitOnly then + errorR (Error (FSComp.SR.tcPropertyCannotBeSet1 nm, mItem)) let args = if pinfo.IsIndexer then args else [] let mut = (if isStructTy cenv.g (tyOfExpr cenv.g objExpr) then DefinitelyMutates else PossiblyMutates) TcMethodApplicationThen cenv env overallTy None tpenv tyargsOpt objArgs mStmt mItem nm ad mut true meths afterResolution NormalValUse (args @ [e2]) atomicFlag [] diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index 0cd10e2fc4..cdf9e9922d 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -107,7 +107,7 @@ type AssignedItemSetterTarget = | AssignedRecdFieldSetter of RecdFieldInfo /// Represents the resolution of a caller argument as a named-setter argument -type AssignedItemSetter<'T> = AssignedItemSetter of Ident * AssignedItemSetterTarget * CallerArg<'T> +type AssignedItemSetter<'T> = AssignedItemSetter of name: Ident * setterTarget: AssignedItemSetterTarget * callerArg: CallerArg<'T> type CallerNamedArg<'T> = | CallerNamedArg of Ident * CallerArg<'T> @@ -435,7 +435,7 @@ type CalledMeth<'T> let pinfos = GetIntrinsicPropInfoSetsOfType infoReader (Some nm) ad AllowMultiIntfInstantiations.Yes IgnoreOverrides id.idRange returnedObjTy let pinfos = pinfos |> ExcludeHiddenOfPropInfos g infoReader.amap m match pinfos with - | [pinfo] when pinfo.HasSetter && not pinfo.IsIndexer -> + | [pinfo] when pinfo.HasSetter && not pinfo.IsIndexer && not pinfo.SetterIsInitOnly -> let pminfo = pinfo.SetterMethod let pminst = freshenMethInfo m pminfo Choice1Of2(AssignedItemSetter(id, AssignedPropSetter(pinfo, pminfo, pminst), e)) diff --git a/src/fsharp/MethodCalls.fsi b/src/fsharp/MethodCalls.fsi index ac96b7b30d..6e0688bfef 100644 --- a/src/fsharp/MethodCalls.fsi +++ b/src/fsharp/MethodCalls.fsi @@ -77,7 +77,12 @@ type AssignedItemSetterTarget = /// Represents the resolution of a caller argument as a named-setter argument type AssignedItemSetter<'T> = | AssignedItemSetter of - Ident * AssignedItemSetterTarget * CallerArg<'T> + /// named-setter identifier + name: Ident * + /// named-setter target + setterTarget: AssignedItemSetterTarget * + /// caller argument that resolved into AssignedItemSetter + callerArg: CallerArg<'T> type CallerNamedArg<'T> = | CallerNamedArg of Ident * CallerArg<'T> diff --git a/src/fsharp/NameResolution.fs b/src/fsharp/NameResolution.fs index 196bef8823..e65ce83e14 100644 --- a/src/fsharp/NameResolution.fs +++ b/src/fsharp/NameResolution.fs @@ -185,7 +185,7 @@ type Item = | Event of EventInfo /// Represents the resolution of a name to a property - | Property of string * PropInfo list + | Property of propertyName: string * propertyInfos: PropInfo list /// Represents the resolution of a name to a group of methods. | MethodGroup of displayName: string * methods: MethInfo list * uninstantiatedMethodOpt: MethInfo option diff --git a/src/fsharp/NameResolution.fsi b/src/fsharp/NameResolution.fsi index 54efeb92e4..7abb0dac80 100755 --- a/src/fsharp/NameResolution.fsi +++ b/src/fsharp/NameResolution.fsi @@ -80,7 +80,7 @@ type Item = | Event of EventInfo /// Represents the resolution of a name to a property - | Property of string * PropInfo list + | Property of propertyName: string * propertyInfos: PropInfo list /// Represents the resolution of a name to a group of methods. | MethodGroup of displayName: string * methods: MethInfo list * uninstantiatedMethodOpt: MethInfo option diff --git a/src/fsharp/absil/il.fs b/src/fsharp/absil/il.fs index a2e1f0ee3e..f8ca4208e2 100644 --- a/src/fsharp/absil/il.fs +++ b/src/fsharp/absil/il.fs @@ -719,7 +719,7 @@ and [ + modifierClass.BasicQualifiedName = "System.Runtime.CompilerServices.IsExternalInit" + | _ -> false + /// Indicates if the IL property is static member x.IsStatic = (x.RawMetadata.CallingConv = ILThisConvention.Static) @@ -2016,6 +2022,14 @@ type PropInfo = | ProvidedProp(_, pi, m) -> pi.PUntaint((fun pi -> pi.CanWrite), m) #endif + member x.SetterIsInitOnly = + match x with + | ILProp ilpinfo -> ilpinfo.SetterIsInitOnly + | FSProp _ -> false // F# doesn't support this +#if !NO_EXTENSIONTYPING + | ProvidedProp _ -> false +#endif + /// Indicates if this is an extension member member x.IsExtensionMember = match x.ArbitraryValRef with diff --git a/src/fsharp/infos.fsi b/src/fsharp/infos.fsi index fb543c2c19..29519e4cc0 100644 --- a/src/fsharp/infos.fsi +++ b/src/fsharp/infos.fsi @@ -723,6 +723,9 @@ type ILPropInfo = /// Indicates if the IL property has a 'set' method member HasSetter: bool + /// Indicates if the IL property setter is marked as 'init' + member SetterIsInitOnly: bool + /// Get the declaring IL type of the IL property, including any generic instantiation member ILTypeInfo: ILTypeInfo @@ -818,6 +821,9 @@ type PropInfo = /// Indicates if this property has an associated setter method. member HasSetter: bool + /// Indicates ... + member SetterIsInitOnly: bool + member ImplementedSlotSignatures: SlotSig list /// Indicates if this property is marked 'override' and thus definitely overrides another property. diff --git a/tests/FSharp.Test.Utilities/TestFramework.fs b/tests/FSharp.Test.Utilities/TestFramework.fs index d2a3e812c7..2523000fb0 100644 --- a/tests/FSharp.Test.Utilities/TestFramework.fs +++ b/tests/FSharp.Test.Utilities/TestFramework.fs @@ -285,8 +285,8 @@ let config configurationName envVars = let packagesDir = getPackagesDir () let requirePackage = requireFile packagesDir let requireArtifact = requireFile artifactsBinPath - let CSC = requirePackage ("Microsoft.Net.Compilers" ++ "2.7.0" ++ "tools" ++ "csc.exe") - let VBC = requirePackage ("Microsoft.Net.Compilers" ++ "2.7.0" ++ "tools" ++ "vbc.exe") + let CSC = requirePackage ("Microsoft.Net.Compilers" ++ "3.9.0" ++ "tools" ++ "csc.exe") + let VBC = requirePackage ("Microsoft.Net.Compilers" ++ "3.9.0" ++ "tools" ++ "vbc.exe") let ILDASM_EXE = if operatingSystem = "win" then "ildasm.exe" else "ildasm" let ILDASM = requirePackage (("runtime." + operatingSystem + "-" + architectureMoniker + ".Microsoft.NETCore.ILDAsm") ++ coreClrRuntimePackageVersion ++ "runtimes" ++ (operatingSystem + "-" + architectureMoniker) ++ "native" ++ ILDASM_EXE) let ILASM_EXE = if operatingSystem = "win" then "ilasm.exe" else "ilasm" diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index 253703752a..634b059fd6 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -1851,6 +1851,12 @@ module CoreTests = fsc cfg @"%s /target:library /out:fs.dll" cfg.fsc_flags ["fs.fs"] singleNegTest cfg "calls" + [] + let ``play nice with init properties and assignment syntax``() = + let cfg = testConfig "typecheck/init-property" + csc cfg "%s /target:library /out:niceinit.dll" cfg.csc_flags ["niceinit.cs"] + singleNegTest cfg "niceinit" + #endif module VersionTests = diff --git a/tests/fsharp/typecheck/init-property/niceinit.bsl b/tests/fsharp/typecheck/init-property/niceinit.bsl new file mode 100644 index 0000000000..4ed4e6b041 --- /dev/null +++ b/tests/fsharp/typecheck/init-property/niceinit.bsl @@ -0,0 +1,12 @@ + +niceinit.fsx(3,25,3,28): typecheck error FS0495: The object constructor 'NiceInit' has no argument or settable return property 'Val'. The required signature is NiceInit() : NiceInit. + +niceinit.fsx(6,1,6,14): typecheck error FS0810: Property 'Val' cannot be set + +niceinit.fsx(13,47,13,50): typecheck error FS0495: The member or object constructor 'mkInlineNiceInit' has no argument or settable return property 'Val'. The required signature is static member StraightNewFactory.mkInlineNiceInit : unit -> NiceInit. + +niceinit.fsx(14,41,14,44): typecheck error FS0495: The member or object constructor 'mkNiceInit' has no argument or settable return property 'Val'. The required signature is static member StraightNewFactory.mkNiceInit : unit -> NiceInit. + +niceinit.fsx(26,73,26,76): typecheck error FS0495: The member or object constructor 'mkInlineNiceInit' has no argument or settable return property 'Val'. The required signature is static member IntermediateVariableWithInterleavedOpFactory.mkInlineNiceInit : unit -> NiceInit. + +niceinit.fsx(27,67,27,70): typecheck error FS0495: The member or object constructor 'mkNiceInit' has no argument or settable return property 'Val'. The required signature is static member IntermediateVariableWithInterleavedOpFactory.mkNiceInit : unit -> NiceInit. diff --git a/tests/fsharp/typecheck/init-property/niceinit.cs b/tests/fsharp/typecheck/init-property/niceinit.cs new file mode 100644 index 0000000000..2694c16b09 --- /dev/null +++ b/tests/fsharp/typecheck/init-property/niceinit.cs @@ -0,0 +1,10 @@ +namespace System.Runtime.CompilerServices{ + + internal class IsExternalInit {} +} + +public class NiceInit { + public NiceInit() {} + public int Val {get; init;} + public int OkVal {get; set;} +} \ No newline at end of file diff --git a/tests/fsharp/typecheck/init-property/niceinit.fsx b/tests/fsharp/typecheck/init-property/niceinit.fsx new file mode 100644 index 0000000000..9bf33bb386 --- /dev/null +++ b/tests/fsharp/typecheck/init-property/niceinit.fsx @@ -0,0 +1,27 @@ +#r @"niceinit.dll" + +let niceinit = NiceInit(Val = 1);; + +let niceinit2 = NiceInit();; +niceinit2.Val <- 5;; +niceinit2.set_Val 5;; + +type StraightNewFactory = + static member inline mkInlineNiceInit () = NiceInit() + static member mkNiceInit () = NiceInit() +;; +let ni1 = StraightNewFactory.mkInlineNiceInit(Val=1);; +let ni2 = StraightNewFactory.mkNiceInit(Val=1);; + +type IntermediateVariableWithInterleavedOpFactory = + static member inline mkInlineNiceInit () = + let n = NiceInit() + printfn "inline nice init" + n + static member mkNiceInit () = + let n = NiceInit() + printfn "nice init" + n +;; +let ni3 = IntermediateVariableWithInterleavedOpFactory.mkInlineNiceInit(Val=1);; +let ni4 = IntermediateVariableWithInterleavedOpFactory.mkNiceInit(Val=1);; \ No newline at end of file