From f72567b9bed93b04d3642f194281b20b5af6d842 Mon Sep 17 00:00:00 2001 From: edgarfgp Date: Fri, 5 Jul 2024 22:07:01 +0100 Subject: [PATCH 01/20] Allow object expression without overrides --- src/Compiler/Checking/Expressions/CheckExpressions.fs | 3 ++- src/Compiler/FSComp.txt | 1 + src/Compiler/Facilities/LanguageFeatures.fs | 2 ++ src/Compiler/Facilities/LanguageFeatures.fsi | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 132f8b01df4..e378bed80a0 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -6791,7 +6791,8 @@ and TcRecordConstruction (cenv: cenv) (overallTy: TType) isObjExpr env tpenv wit UnifyTypes cenv env m overallTy objTy // Types with implicit constructors can't use record or object syntax: all constructions must go through the implicit constructor - if tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) then + let supportsObjectExpressionWithoutOverrides = isObjExpr && g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) + if tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) && not supportsObjectExpressionWithoutOverrides then errorR(Error(FSComp.SR.tcConstructorRequiresCall(tycon.DisplayName), m)) let fspecs = tycon.TrueInstanceFieldsAsList diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index e452d21f100..33a2deb0681 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1778,3 +1778,4 @@ featureEmptyBodiedComputationExpressions,"Support for computation expressions wi 3870,parsExpectingUnionCaseField,"Expecting union case field" featureAllowAccessModifiersToAutoPropertiesGettersAndSetters,"Allow access modifiers to auto properties getters and setters" 3871,tcAccessModifiersNotAllowedInSRTPConstraint,"Access modifiers cannot be applied to an SRTP constraint." +featureAllowObjectExpressionWithoutOverrides,"Allow object expressions without overrides" diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 6067cbe597a..9ab3292ff3e 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -93,6 +93,7 @@ type LanguageFeature = | LowerSimpleMappingsInComprehensionsToFastLoops | ParsedHashDirectiveArgumentNonQuotes | EmptyBodiedComputationExpressions + | AllowObjectExpressionWithoutOverrides /// LanguageVersion management type LanguageVersion(versionText) = @@ -372,6 +373,7 @@ type LanguageVersion(versionText) = FSComp.SR.featureLowerSimpleMappingsInComprehensionsToFastLoops () | LanguageFeature.ParsedHashDirectiveArgumentNonQuotes -> FSComp.SR.featureParsedHashDirectiveArgumentNonString () | LanguageFeature.EmptyBodiedComputationExpressions -> FSComp.SR.featureEmptyBodiedComputationExpressions () + | LanguageFeature.AllowObjectExpressionWithoutOverrides -> FSComp.SR.featureAllowObjectExpressionWithoutOverrides () /// Get a version string associated with the given feature. static member GetFeatureVersionString feature = diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index 470157474b0..7408300b943 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -84,6 +84,7 @@ type LanguageFeature = | LowerSimpleMappingsInComprehensionsToFastLoops | ParsedHashDirectiveArgumentNonQuotes | EmptyBodiedComputationExpressions + | AllowObjectExpressionWithoutOverrides /// LanguageVersion management type LanguageVersion = From 10baa90fcd73a8e0e2da936475782b992082aef7 Mon Sep 17 00:00:00 2001 From: edgarfgp Date: Sat, 6 Jul 2024 11:23:30 +0100 Subject: [PATCH 02/20] release notes --- docs/release-notes/.FSharp.Compiler.Service/8.0.400.md | 2 ++ docs/release-notes/.Language/preview.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index 60394d22e23..23ba2c3110c 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -29,6 +29,8 @@ * Expose inner exception information of TypeProviders to help diagnostics in IDE ([PR #17251](https://github.com/dotnet/fsharp/pull/17251)) * Parser: recover on empty match clause ([PR #17233](https://github.com/dotnet/fsharp/pull/17233)) * Support empty-bodied computation expressions. ([Language suggestion #1232](https://github.com/fsharp/fslang-suggestions/issues/1232), [RFC FS-1144 (PR #774)](https://github.com/fsharp/fslang-design/pull/774), [PR #17352](https://github.com/dotnet/fsharp/pull/17352)) +* Allow object expression without overrides. ([Language suggestion #632](https://github.com/fsharp/fslang-suggestions/issues/632), [PR #17387](https://github.com/dotnet/fsharp/pull/17387)) + ### Changed * Enforce `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173)) diff --git a/docs/release-notes/.Language/preview.md b/docs/release-notes/.Language/preview.md index 9359ac25d82..dc79bbfe7e1 100644 --- a/docs/release-notes/.Language/preview.md +++ b/docs/release-notes/.Language/preview.md @@ -11,6 +11,8 @@ * Allow #nowarn to support the FS prefix on error codes to disable warnings ([Issue #17206](https://github.com/dotnet/fsharp/issues/16447), [PR #17209](https://github.com/dotnet/fsharp/pull/17209)) * Allow ParsedHashDirectives to have argument types other than strings ([Issue #17240](https://github.com/dotnet/fsharp/issues/16447), [PR #17209](https://github.com/dotnet/fsharp/pull/17209)) * Support empty-bodied computation expressions. ([Language suggestion #1232](https://github.com/fsharp/fslang-suggestions/issues/1232), [PR #17352](https://github.com/dotnet/fsharp/pull/17352)) +* Allow object expression without overrides. ([Language suggestion #632](https://github.com/fsharp/fslang-suggestions/issues/632), [PR #17387](https://github.com/dotnet/fsharp/pull/17387)) + ### Fixed From cdb0f081ee20ca79416a58967081697abb98eb15 Mon Sep 17 00:00:00 2001 From: edgarfgp Date: Sat, 6 Jul 2024 21:15:50 +0100 Subject: [PATCH 03/20] Lets try this version --- src/Compiler/Checking/Expressions/CheckExpressions.fs | 11 +++++++---- .../ObjectExpressions/ObjectExpressions.fs | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index e378bed80a0..fb49e325438 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -6791,8 +6791,8 @@ and TcRecordConstruction (cenv: cenv) (overallTy: TType) isObjExpr env tpenv wit UnifyTypes cenv env m overallTy objTy // Types with implicit constructors can't use record or object syntax: all constructions must go through the implicit constructor - let supportsObjectExpressionWithoutOverrides = isObjExpr && g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) - if tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) && not supportsObjectExpressionWithoutOverrides then + // let supportsObjectExpressionWithoutOverrides = isObjExpr && g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) + if tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) then errorR(Error(FSComp.SR.tcConstructorRequiresCall(tycon.DisplayName), m)) let fspecs = tycon.TrueInstanceFieldsAsList @@ -7207,8 +7207,9 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI let overridesAndVirts, tpenv = ComputeObjectExprOverrides cenv env tpenv impls - // 2. check usage conditions - overridesAndVirts |> List.iter (fun (m, implTy, dispatchSlots, dispatchSlotsKeyed, availPriorOverrides, overrides) -> + // 2. check usage conditions + for ovd in overridesAndVirts do + let (m, implTy, dispatchSlots, dispatchSlotsKeyed, availPriorOverrides, overrides) = ovd let overrideSpecs = overrides |> List.map fst let hasStaticMembers = dispatchSlots |> List.exists (fun reqdSlot -> not reqdSlot.MethodInfo.IsInstance) if hasStaticMembers then @@ -7217,6 +7218,8 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI DispatchSlotChecking.CheckOverridesAreAllUsedOnce (env.DisplayEnv, g, cenv.infoReader, true, implTy, dispatchSlotsKeyed, availPriorOverrides, overrideSpecs) if not hasStaticMembers then + DispatchSlotChecking.CheckDispatchSlotsAreImplemented (env.DisplayEnv, cenv.infoReader, m, env.NameEnv, cenv.tcSink, isAbstractClass, implTy, dispatchSlots, availPriorOverrides, overrideSpecs) |> ignore + DispatchSlotChecking.CheckDispatchSlotsAreImplemented (env.DisplayEnv, cenv.infoReader, m, env.NameEnv, cenv.tcSink, isOverallTyAbstract, true, implTy, dispatchSlots, availPriorOverrides, overrideSpecs) |> ignore ) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 33fc4582f26..de1074f62cf 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -150,6 +150,7 @@ let implSomeDU someDu = [] type Foo() = class end +let foo = { new Foo() } let foo = { new Foo() } // Approved suggestion to allow this https://github.com/fsharp/fslang-suggestions/issues/632 let foo1 = new Foo() From 26334eb8696df4a8edbeb1dc229d308031eeabe7 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 29 Jul 2024 20:00:29 +0100 Subject: [PATCH 04/20] WIP --- .../Checking/Expressions/CheckExpressions.fs | 9 +++---- .../ObjectExpressions/ObjectExpressions.fs | 24 ++++++++++++++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index fb49e325438..75c35f66889 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -6791,8 +6791,8 @@ and TcRecordConstruction (cenv: cenv) (overallTy: TType) isObjExpr env tpenv wit UnifyTypes cenv env m overallTy objTy // Types with implicit constructors can't use record or object syntax: all constructions must go through the implicit constructor - // let supportsObjectExpressionWithoutOverrides = isObjExpr && g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) - if tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) then + let supportsObjectExpressionWithoutOverrides = isObjExpr && g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) + if tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) && not supportsObjectExpressionWithoutOverrides then errorR(Error(FSComp.SR.tcConstructorRequiresCall(tycon.DisplayName), m)) let fspecs = tycon.TrueInstanceFieldsAsList @@ -7217,11 +7217,8 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI DispatchSlotChecking.CheckOverridesAreAllUsedOnce (env.DisplayEnv, g, cenv.infoReader, true, implTy, dispatchSlotsKeyed, availPriorOverrides, overrideSpecs) - if not hasStaticMembers then - DispatchSlotChecking.CheckDispatchSlotsAreImplemented (env.DisplayEnv, cenv.infoReader, m, env.NameEnv, cenv.tcSink, isAbstractClass, implTy, dispatchSlots, availPriorOverrides, overrideSpecs) |> ignore - + if not hasStaticMembers then DispatchSlotChecking.CheckDispatchSlotsAreImplemented (env.DisplayEnv, cenv.infoReader, m, env.NameEnv, cenv.tcSink, isOverallTyAbstract, true, implTy, dispatchSlots, availPriorOverrides, overrideSpecs) |> ignore - ) // 3. create the specs of overrides let allTypeImpls = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index de1074f62cf..cf7ae250f1b 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -151,7 +151,6 @@ let implSomeDU someDu = type Foo() = class end let foo = { new Foo() } -let foo = { new Foo() } // Approved suggestion to allow this https://github.com/fsharp/fslang-suggestions/issues/632 let foo1 = new Foo() @@ -162,7 +161,26 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } |> typecheck |> shouldFail |> withDiagnostics [ - (Error 738, Line 5, Col 11, Line 5, Col 24, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") + (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") + ] + + [] + let ``Object expression can implement an abstract class having no abstract members.`` () = + Fsx """ +[] +type Foo() = class end + +let foo = { new Foo() } + +let foo1 = new Foo() + +// hacky workaround +let foo2 = { new Foo() with member __.ToString() = base.ToString() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") ] @@ -495,4 +513,4 @@ Please restrict it to one of the following: (Error 358, Line 8, Col 19, Line 8, Col 29, "The override for 'Overloaded: int -> bool' was ambiguous") (Error 358, Line 8, Col 19, Line 8, Col 29, "The override for 'Overloaded: string -> bool' was ambiguous") (Error 783, Line 7, Col 11, Line 7, Col 19, "At least one override did not correctly implement its corresponding abstract member") - ] + ] \ No newline at end of file From 6489ae4b31a74c37a6ac0da1a2dc74323e1d6d72 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 29 Jul 2024 20:39:43 +0100 Subject: [PATCH 05/20] release notes --- docs/release-notes/.FSharp.Compiler.Service/8.0.400.md | 2 -- docs/release-notes/.FSharp.Compiler.Service/9.0.100.md | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index 23ba2c3110c..60394d22e23 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -29,8 +29,6 @@ * Expose inner exception information of TypeProviders to help diagnostics in IDE ([PR #17251](https://github.com/dotnet/fsharp/pull/17251)) * Parser: recover on empty match clause ([PR #17233](https://github.com/dotnet/fsharp/pull/17233)) * Support empty-bodied computation expressions. ([Language suggestion #1232](https://github.com/fsharp/fslang-suggestions/issues/1232), [RFC FS-1144 (PR #774)](https://github.com/fsharp/fslang-design/pull/774), [PR #17352](https://github.com/dotnet/fsharp/pull/17352)) -* Allow object expression without overrides. ([Language suggestion #632](https://github.com/fsharp/fslang-suggestions/issues/632), [PR #17387](https://github.com/dotnet/fsharp/pull/17387)) - ### Changed * Enforce `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173)) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md index a11ec24aba8..eff13a9823d 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md @@ -17,6 +17,7 @@ * Sink: report function domain type ([PR #17470](https://github.com/dotnet/fsharp/pull/17470)) * Allow access modifies to auto properties getters and setters ([Language suggestion #430](https://github.com/fsharp/fslang-suggestions/issues/430), [PR 16687](https://github.com/dotnet/fsharp/pull/16687), [PR 16861](https://github.com/dotnet/fsharp/pull/16861), [PR 17522](https://github.com/dotnet/fsharp/pull/17522)) * Render C# nullable-analysis attributes in tooltips ([PR #17485](https://github.com/dotnet/fsharp/pull/17485)) +* Allow object expression without overrides. ([Language suggestion #632](https://github.com/fsharp/fslang-suggestions/issues/632), [PR #17387](https://github.com/dotnet/fsharp/pull/17387)) ### Changed From 8022d392dd3324ee0dd83783b779ddb4dd196333 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 29 Jul 2024 20:58:20 +0100 Subject: [PATCH 06/20] Update tests --- .../Expressions/ObjectExpressions/ObjectExpressions.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index cf7ae250f1b..c045a58b430 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -161,7 +161,8 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } |> typecheck |> shouldFail |> withDiagnostics [ - (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") + (Error 738, Line 5, Col 17, Line 5, Col 20, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces."); + (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") ] [] From 8c4ae6ce3659f076dae3a399348b2c3574a9f339 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 30 Jul 2024 15:59:14 +0100 Subject: [PATCH 07/20] more tests --- .../ObjectExpressions/ObjectExpressions.fs | 118 +++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index c045a58b430..62c1d0edc95 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -23,6 +23,21 @@ let x = { new _ with member this.MyMember() = 42 } (Error 772, Line 5, Col 11, Line 5, Col 16, "'new' must be used with a named type") ] + [] + let ``Object expression cannot implement unnamed interface preview`` () = + Fsx """ +type IFirst = + abstract member MyMember: unit -> int + +let x = { new _ with member this.MyMember() = 42 } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 772, Line 5, Col 11, Line 5, Col 16, "'new' must be used with a named type") + ] + [] let ``Object expression implementing an interface without members`` () = Fsx """ @@ -121,6 +136,17 @@ let expr = { new MyClass() interface IFirst } "It works" ] + [] + let ``Object expression implementing an interface without members preview`` () = + Fsx """ +type IFirst = interface end + +let implementer() ={ new IFirst } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + [] let ``Parameterized object expression implementing an interface with members`` () = Fsx """ @@ -144,6 +170,30 @@ let implSomeDU someDu = |> typecheck |> shouldSucceed + [] + let ``Parameterized object expression implementing an interface with members preview`` () = + Fsx """ +type AsString = + abstract member asString: unit -> string + +type SomeDu = + | A of int + | B of string + | C of float + +let implSomeDU someDu = + { new AsString with + member self.asString () = + match someDu with + | A a -> string a + | B b -> b + | C c -> string c } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] let ``Object expression can not implement an abstract class having no abstract members.`` () = Fsx """ @@ -163,7 +213,25 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } |> withDiagnostics [ (Error 738, Line 5, Col 17, Line 5, Col 20, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces."); (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") - ] + ] + + // FIXME This tests should succeed + [] + let ``Object expression can not implement an abstract class and interface having no abstract members.`` () = + Fsx """ +type IFirst = interface end + +[] +type MyClass() = class end + +{ new MyClass() with + member x.ToString() = "OK" + + interface IFirst } |> ignore + """ + |> withLangVersion80 + |> typecheck + |> shouldSucceed [] let ``Object expression can implement an abstract class having no abstract members.`` () = @@ -209,6 +277,31 @@ let y = { new C() with (Error 365, Line 9, Col 20, Line 10, Col 60, "No implementation was given for 'abstract B.M: string -> unit'") (Error 365, Line 13, Col 9, Line 14, Col 49, "No implementation was given for 'abstract B.M: string -> unit'") ] + + [] + let ``Error when object expression does not implement all abstract members of the abstract class preview`` () = + Fsx """ +[] +type B() = + abstract M : int -> float + abstract M : string -> unit +and [] + C() = + inherit B() + static let v = { new C() with + member x.M(a:int) : float = 1.0 } + default x.M(a:int) : float = 1.0 + +let y = { new C() with + member x.M(a:int) : float = 1.0 } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 9, Col 20, Line 10, Col 60, "No implementation was given for 'abstract B.M: string -> unit'") + (Error 365, Line 13, Col 9, Line 14, Col 49, "No implementation was given for 'abstract B.M: string -> unit'") + ] [] let ``Error when object expression does not implement all abstract members of a generic abstract class`` () = @@ -233,6 +326,29 @@ let f() = { new BaseHashtable<_,_>(2) with (Error 359, Line 10, Col 11, Line 13, Col 12, "More than one override implements 'Next: StrongToWeakEntry<'a> array -> int when 'a: not struct'") ] + [] + let ``Error when object expression does not implement all abstract members of a generic abstract class preview`` () = + Fsx """ +[] +type BaseHashtable<'Entry, 'Key>(initialCapacity) = + abstract member Next : entries : 'Entry array -> int + +[] +type StrongToWeakEntry<'Value when 'Value : not struct> = + val mutable public next : int + +let f() = { new BaseHashtable<_,_>(2) with + override this.Next (entries:StrongToWeakEntry<_> array) = 1 + override this.Next entries = 1 + } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 359, Line 10, Col 11, Line 13, Col 12, "More than one override implements 'Next: StrongToWeakEntry<'a> array -> int when 'a: not struct'") + ] + [] let ``Object expression can not implementing an interface when it contains a method with no types that can refer to the type for which the implementation is being used`` () = Fsx """ From 217e6ffbc8c3a16035e3d003318da86cd8a363c2 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 30 Jul 2024 21:32:59 +0100 Subject: [PATCH 08/20] more tests --- .../ObjectExpressions/ObjectExpressions.fs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 62c1d0edc95..22da900a325 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -233,6 +233,23 @@ type MyClass() = class end |> typecheck |> shouldSucceed + [] + let ``Object expression can not implement an abstract class and interface having no abstract members preview`` () = + Fsx """ +type IFirst = interface end + +[] +type MyClass() = class end + +{ new MyClass() with + member x.ToString() = "OK" + + interface IFirst } |> ignore + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + [] let ``Object expression can implement an abstract class having no abstract members.`` () = Fsx """ From 49486ee47b9c9a9ef698d36d943ce3bf24af2d8a Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Wed, 7 Aug 2024 17:41:42 +0100 Subject: [PATCH 09/20] LanguageFeature.AllowObjectExpressionWithoutOverrides --- .../Checking/Expressions/CheckExpressions.fs | 5 +- .../ObjectExpressions/ObjectExpressions.fs | 88 +++++++++++++++++-- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 75c35f66889..618f33cc17d 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7250,8 +7250,9 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI let objtyR, overrides' = allTypeImpls.Head assert (typeEquiv g objTy objtyR) let extraImpls = allTypeImpls.Tail - - if not isInterfaceTy && (isOverallTyAbstract && overrides'.IsEmpty) && extraImpls.IsEmpty then + let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) + + if not supportsObjectExpressionWithoutOverrides && not isInterfaceTy && (isOverallTyAbstract && overrides'.IsEmpty) && extraImpls.IsEmpty then errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr)) // 4. Build the implementation diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 22da900a325..19835e2eb8f 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -136,6 +136,29 @@ let expr = { new MyClass() interface IFirst } "It works" ] + [] + let ``Verifies that the object expression built type has the interface in preview lang version.`` () = + Fsx """ +type IFirst = interface end + +type ISecond = + abstract member M : unit -> unit + +[] +type MyClass() = + interface ISecond with + member this.M() = printfn "It works" + +let expr = { new MyClass() } +(expr:> ISecond).M() + """ + |> withLangVersionPreview + |> compileExeAndRun + |> shouldSucceed + |> withStdOutContainsAllInOrder [ + "It works" + ] + [] let ``Object expression implementing an interface without members preview`` () = Fsx """ @@ -193,7 +216,6 @@ let implSomeDU someDu = |> typecheck |> shouldSucceed - [] let ``Object expression can not implement an abstract class having no abstract members.`` () = Fsx """ @@ -211,11 +233,24 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } |> typecheck |> shouldFail |> withDiagnostics [ - (Error 738, Line 5, Col 17, Line 5, Col 20, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces."); - (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") - ] + (Error 738, Line 5, Col 11, Line 5, Col 24, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces."); + (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") + ] + + [] + let ``Object expression can implement an abstract class having no abstract members in preview lang version.`` () = + Fsx """ +[] +type Foo() = class end + +let foo = { new Foo() } + +let foo2 = { new Foo() with member __.ToString() = base.ToString() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed - // FIXME This tests should succeed [] let ``Object expression can not implement an abstract class and interface having no abstract members.`` () = Fsx """ @@ -268,6 +303,49 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } |> shouldFail |> withDiagnostics [ (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") + ] + + [] + let ``Object expression can not implement an abstract class having no abstract members. But error when object expression does not implement all abstract members of the abstract class`` () = + Fsx """ +type ISecond = + abstract member M : unit -> unit + +[] +type MyClass() = + abstract member M : unit -> unit + interface ISecond with + member this.M() = printfn "It works" + +let res = { new MyClass() } + """ + |> withLangVersion80 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'"); + (Error 738, Line 11, Col 11, Line 11, Col 28, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") + ] + + [] + let ``Object expression can implement an abstract class having no abstract members. But error when object expression does not implement all abstract members of the abstract class`` () = + Fsx """ +type ISecond = + abstract member M : unit -> unit + +[] +type MyClass() = + abstract member M : unit -> unit + interface ISecond with + member this.M() = printfn "It works" + +let res = { new MyClass() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'") ] [] From 2fabb99ff2854b0ccfcb2c3eb27ba70cccac8982 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Wed, 7 Aug 2024 17:44:56 +0100 Subject: [PATCH 10/20] reduce diff --- src/Compiler/Checking/Expressions/CheckExpressions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 618f33cc17d..5171f472acf 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7217,7 +7217,7 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI DispatchSlotChecking.CheckOverridesAreAllUsedOnce (env.DisplayEnv, g, cenv.infoReader, true, implTy, dispatchSlotsKeyed, availPriorOverrides, overrideSpecs) - if not hasStaticMembers then + if not hasStaticMembers then DispatchSlotChecking.CheckDispatchSlotsAreImplemented (env.DisplayEnv, cenv.infoReader, m, env.NameEnv, cenv.tcSink, isOverallTyAbstract, true, implTy, dispatchSlots, availPriorOverrides, overrideSpecs) |> ignore // 3. create the specs of overrides From 1397d3213b2647deec23c77dbf584dab17b8d298 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 8 Aug 2024 21:30:05 +0100 Subject: [PATCH 11/20] one more test --- .../ObjectExpressions/ObjectExpressions.fs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 19835e2eb8f..081ad4d57a2 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -347,6 +347,41 @@ let res = { new MyClass() } |> withDiagnostics [ (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'") ] + [] + let ``C# abstract class with protected constructor can be implemented by F# object expression lang version preview`` () = + + let csharp = + CSharp + """ +namespace CSLib +{ + using System; + public abstract class Animal + { + protected Animal() + { + Console.WriteLine("Animal is created"); + } + } +} +""" + |> withName "CSLib" + + let fsharp = + FSharp + """ +module FSLib +open CSLib + +let res = { new Animal() } +""" + |> withLangVersionPreview + |> withName "FSLib" + |> withReferences [ csharp ] + + fsharp + |> compile + |> shouldSucceed [] let ``Error when object expression does not implement all abstract members of the abstract class`` () = From 2d2b7dec1abb74aafa8e20773a3bbd401b01b8a1 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 9 Aug 2024 09:47:09 +0100 Subject: [PATCH 12/20] more tests --- .../ObjectExpressions/ObjectExpressions.fs | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 081ad4d57a2..39d152f5673 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -346,7 +346,8 @@ let res = { new MyClass() } |> shouldFail |> withDiagnostics [ (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'") - ] + ] + [] let ``C# abstract class with protected constructor can be implemented by F# object expression lang version preview`` () = @@ -383,6 +384,88 @@ let res = { new Animal() } |> compile |> shouldSucceed + [] + let ``C# abstract class with protected constructor and abstract method can not be implemented by F# object expression unless the abstract method is implemented in lang version preview.`` () = + + let csharp = + CSharp + """ +namespace CSLib +{ + using System; + public abstract class Animal + { + protected Animal() + { + Console.WriteLine("Animal is created"); + } + + public abstract void M(); + } +} +""" + |> withName "CSLib" + + let fsharp = + FSharp + """ +module FSLib +open CSLib + +let res = { new Animal() } +""" + |> withLangVersionPreview + |> withName "FSLib" + |> withReferences [ csharp ] + + fsharp + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'") + ] + + [] + let ``C# abstract class with protected constructor and abstract method can not be implemented by F# object expression unless the abstract method is implemented.`` () = + + let csharp = + CSharp + """ +namespace CSLib +{ + using System; + public abstract class Animal + { + protected Animal() + { + Console.WriteLine("Animal is created"); + } + + public abstract void M(); + } +} +""" + |> withName "CSLib" + + let fsharp = + FSharp + """ +module FSLib +open CSLib + +let res = { new Animal() } +""" + |> withLangVersion80 + |> withName "FSLib" + |> withReferences [ csharp ] + + fsharp + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'") + ] + [] let ``Error when object expression does not implement all abstract members of the abstract class`` () = Fsx """ From 0766d365fb873e1a0a48c5736438adfb6fa1bb80 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 13 Aug 2024 15:26:07 +0100 Subject: [PATCH 13/20] WIP more tests --- .../ObjectExpressions/ObjectExpressions.fs | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 39d152f5673..3e60ffb6df7 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -252,7 +252,7 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } |> shouldSucceed [] - let ``Object expression can not implement an abstract class and interface having no abstract members.`` () = + let ``Object expression can implement an abstract class and interface having no abstract members.`` () = Fsx """ type IFirst = interface end @@ -306,7 +306,7 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } ] [] - let ``Object expression can not implement an abstract class having no abstract members. But error when object expression does not implement all abstract members of the abstract class`` () = + let ``Object expression shows error when object expression does not implement all abstract members of the abstract class`` () = Fsx """ type ISecond = abstract member M : unit -> unit @@ -328,7 +328,7 @@ let res = { new MyClass() } ] [] - let ``Object expression can implement an abstract class having no abstract members. But error when object expression does not implement all abstract members of the abstract class`` () = + let ``Object expression shows error when object expression does not implement all abstract members of the abstract class preview`` () = Fsx """ type ISecond = abstract member M : unit -> unit @@ -347,6 +347,45 @@ let res = { new MyClass() } |> withDiagnostics [ (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'") ] + + [] + let ``C# abstract class with protected constructor can not be implemented by F# object expression`` () = + + let csharp = + CSharp + """ +namespace CSLib +{ + using System; + public abstract class Animal + { + protected Animal() + { + Console.WriteLine("Animal is created"); + } + } +} +""" + |> withName "CSLib" + + let fsharp = + FSharp + """ +module FSLib +open CSLib + +let res = { new Animal() } +""" + |> withLangVersion80 + |> withName "FSLib" + |> withReferences [ csharp ] + + fsharp + |> compile + |> shouldFail + |> withDiagnostics [ + // Error missing + ] [] let ``C# abstract class with protected constructor can be implemented by F# object expression lang version preview`` () = @@ -463,6 +502,7 @@ let res = { new Animal() } |> compile |> shouldFail |> withDiagnostics [ + // Error missing (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'") ] From 1ae3ff5c327d4eabd316afb179636fc788dec099 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 13 Aug 2024 15:31:31 +0100 Subject: [PATCH 14/20] fix merge --- src/Compiler/xlf/FSComp.txt.cs.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.de.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.es.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.fr.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.it.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.ja.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.ko.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.pl.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.ru.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.tr.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 +++++ 13 files changed, 65 insertions(+) diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index d00017d2697..b3a0c5fb474 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions aplikativní výpočetní výrazy diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 2bf293d961c..b1484de4e27 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions applikative Berechnungsausdrücke diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 6923afa068f..2398a2fb639 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions expresiones de cálculo aplicativas diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 4207041ad15..20160aab0a8 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions expressions de calcul applicatives diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 9c2171b9217..29fbd5d295f 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions espressioni di calcolo applicativo diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 3ac536db2b4..b6152359c8e 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions 適用できる計算式 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 41047438142..ad64e3c19a4 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions 적용 가능한 계산 식 diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 9e64dbf33d8..996fc39a048 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions praktyczne wyrażenia obliczeniowe diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 59330e37600..5718f211dcb 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions expressões de computação aplicáveis diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 71995741697..516b241b4bd 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions применимые вычислительные выражения diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 4d6a7456a03..ff69b9ba7eb 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions uygulama hesaplama ifadeleri diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 6d942aa6526..42aed5e6b70 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions 适用的计算表达式 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index fa8f6a00187..1347f58f1b0 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -262,6 +262,11 @@ Allow access modifiers to auto properties getters and setters + + Allow object expressions without overrides + Allow object expressions without overrides + + applicative computation expressions 適用的計算運算式 From 4a26619f6b3d664983816615121286f6e837f6bd Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 13 Aug 2024 16:36:10 +0100 Subject: [PATCH 15/20] More tests --- src/Compiler/Facilities/LanguageFeatures.fs | 1 + .../ObjectExpressions/ObjectExpressions.fs | 68 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 9ab3292ff3e..ccf2796ca90 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -218,6 +218,7 @@ type LanguageVersion(versionText) = LanguageFeature.EnforceAttributeTargets, previewVersion // not enabled because: https://github.com/dotnet/fsharp/issues/17514 LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work LanguageFeature.AllowAccessModifiersToAutoPropertiesGettersAndSetters, previewVersion + LanguageFeature.AllowObjectExpressionWithoutOverrides, previewVersion ] static let defaultLanguageVersion = LanguageVersion("default") diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 3e60ffb6df7..d9db8772bf9 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -235,6 +235,72 @@ let foo2 = { new Foo() with member __.ToString() = base.ToString() } |> withDiagnostics [ (Error 738, Line 5, Col 11, Line 5, Col 24, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces."); (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") + ] + + [] + let ``Object expression can not implement an abstract class having no abstract members`` () = + Fsx """ +[] +type AbstractClass() = + do printfn "AbstractClass constructor" + +let res = { new AbstractClass() } + """ + |> withLangVersion80 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 738, Line 6, Col 11, Line 6, Col 34, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") + ] + + [] + let ``Object expression can not implement an abstract class having abstract members`` () = + Fsx """ +[] +type AbstractClass() = + do printfn "AbstractClass constructor" + abstract member M : unit -> unit + +let res = { new AbstractClass() } + """ + |> withLangVersion80 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 7, Col 11, Line 7, Col 34, "No implementation was given for 'abstract AbstractClass.M: unit -> unit'"); + (Error 738, Line 7, Col 11, Line 7, Col 34, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") + ] + + [] + let ``Object expression can implement an abstract class having no abstract members preview`` () = + Fsx """ +[] +type AbstractClass() = + do printfn "AbstractClass constructor" + +let res = { new AbstractClass() } + """ + |> withLangVersionPreview + |> compileExeAndRun + |> withStdOutContainsAllInOrder [ + "AbstractClass constructor" + ] + + [] + let ``Object expression can not implement an abstract class having abstract members preview.`` () = + Fsx """ +[] +type AbstractClass() = + do printfn "AbstractClass constructor" + abstract member M : unit -> unit + +let res = { new AbstractClass() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 7, Col 11, Line 7, Col 34, "No implementation was given for 'abstract AbstractClass.M: unit -> unit'") ] [] @@ -384,7 +450,7 @@ let res = { new Animal() } |> compile |> shouldFail |> withDiagnostics [ - // Error missing + // FIXME ] [] From cb9b78e3051dcbd03c838528e55d0b5f1d04929f Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 13 Aug 2024 19:34:17 +0100 Subject: [PATCH 16/20] Fix failing tests --- src/Compiler/Checking/Expressions/CheckExpressions.fs | 1 + .../Expressions/ObjectExpressions/ObjectExpressions.fs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 5171f472acf..431aee06ca1 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7251,6 +7251,7 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI assert (typeEquiv g objTy objtyR) let extraImpls = allTypeImpls.Tail let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) + let isOverallTyAbstract = isOverallTyAbstract || isAbstractTycon tcref.Deref if not supportsObjectExpressionWithoutOverrides && not isInterfaceTy && (isOverallTyAbstract && overrides'.IsEmpty) && extraImpls.IsEmpty then errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr)) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index d9db8772bf9..4d42d4d8f08 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -450,7 +450,7 @@ let res = { new Animal() } |> compile |> shouldFail |> withDiagnostics [ - // FIXME + (Error 738, Line 5, Col 11, Line 5, Col 27, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] [] @@ -568,8 +568,8 @@ let res = { new Animal() } |> compile |> shouldFail |> withDiagnostics [ - // Error missing - (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'") + (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'"); + (Error 738, Line 5, Col 11, Line 5, Col 27, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] [] From 2233c5f4963786c8be45e43e8026786fd7c69260 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Wed, 14 Aug 2024 08:50:39 +0100 Subject: [PATCH 17/20] more tests --- .../Checking/Expressions/CheckExpressions.fs | 6 +- .../ObjectExpressions/ObjectExpressions.fs | 61 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 431aee06ca1..e655b5487bf 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -6792,7 +6792,7 @@ and TcRecordConstruction (cenv: cenv) (overallTy: TType) isObjExpr env tpenv wit // Types with implicit constructors can't use record or object syntax: all constructions must go through the implicit constructor let supportsObjectExpressionWithoutOverrides = isObjExpr && g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) - if tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) && not supportsObjectExpressionWithoutOverrides then + if not supportsObjectExpressionWithoutOverrides && tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) then errorR(Error(FSComp.SR.tcConstructorRequiresCall(tycon.DisplayName), m)) let fspecs = tycon.TrueInstanceFieldsAsList @@ -7140,7 +7140,8 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI let isRecordTy = tcref.IsRecordTycon let isInterfaceTy = isInterfaceTy g objTy let isFSharpObjModelTy = isFSharpObjModelTy g objTy - let isOverallTyAbstract = HasFSharpAttribute g g.attrib_AbstractClassAttribute tcref.Attribs + let isOverallTyAbstract = HasFSharpAttribute g g.attrib_AbstractClassAttribute tcref.Attribs || isAbstractTycon tcref.Deref + if not isRecordTy && not isInterfaceTy && isSealedTy g objTy then errorR(Error(FSComp.SR.tcCannotCreateExtensionOfSealedType(), mNewExpr)) CheckSuperType cenv objTy mObjTy @@ -7251,7 +7252,6 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI assert (typeEquiv g objTy objtyR) let extraImpls = allTypeImpls.Tail let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) - let isOverallTyAbstract = isOverallTyAbstract || isAbstractTycon tcref.Deref if not supportsObjectExpressionWithoutOverrides && not isInterfaceTy && (isOverallTyAbstract && overrides'.IsEmpty) && extraImpls.IsEmpty then errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr)) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 4d42d4d8f08..55abd6ef03e 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -252,7 +252,68 @@ let res = { new AbstractClass() } |> withDiagnostics [ (Error 738, Line 6, Col 11, Line 6, Col 34, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] + + [] + let ``Object expression can not implement an abstract class having abstract members with default implementation`` () = + Fsx """ +[] +type AbstractClass() = + abstract member M : unit -> unit + default this.M() = printfn "Im a default implementation" + +let res = { new AbstractClass() } + """ + |> withLangVersion80 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 738, Line 7, Col 11, Line 7, Col 34, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") + ] + + [] + let ``Object expression can implement an abstract class having abstract members with default implementation preview`` () = + Fsx """ +[] +type AbstractClass() = + abstract member M : unit -> unit + default this.M() = printfn "Im a default implementation" + +let res = { new AbstractClass() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Object expression can implement an abstract class(overriding a member) having abstract members with default implementation`` () = + Fsx """ +[] +type AbstractClass() = + abstract member M : unit -> unit + default this.M() = printfn "Im a default implementation" + +let res = { new AbstractClass() with + override this.ToString() = "ConcreteMethod" } + """ + |> withLangVersion80 + |> typecheck + |> shouldSucceed + [] + let ``Object expression can implement an abstract class(overriding a member) having abstract members with default implementation preview`` () = + Fsx """ +[] +type AbstractClass() = + abstract member M : unit -> unit + default this.M() = printfn "Im a default implementation" + +let res = { new AbstractClass() with + override this.ToString() = "ConcreteMethod" } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + [] let ``Object expression can not implement an abstract class having abstract members`` () = Fsx """ From e0116ce89adae22316769c531d259d59f96579d3 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 15 Aug 2024 10:01:57 +0100 Subject: [PATCH 18/20] re-structure tests to make easier to review --- .../ObjectExpressions/ObjectExpressions.fs | 620 +++++++++--------- 1 file changed, 294 insertions(+), 326 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 55abd6ef03e..a1a303820fe 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -16,28 +16,13 @@ type IFirst = let x = { new _ with member this.MyMember() = 42 } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ (Error 772, Line 5, Col 11, Line 5, Col 16, "'new' must be used with a named type") ] - - [] - let ``Object expression cannot implement unnamed interface preview`` () = - Fsx """ -type IFirst = - abstract member MyMember: unit -> int - -let x = { new _ with member this.MyMember() = 42 } - """ - |> withLangVersionPreview - |> typecheck - |> shouldFail - |> withDiagnostics [ - (Error 772, Line 5, Col 11, Line 5, Col 16, "'new' must be used with a named type") - ] - + [] let ``Object expression implementing an interface without members`` () = Fsx """ @@ -45,7 +30,7 @@ type IFirst = interface end let implementer() ={ new IFirst } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -68,7 +53,7 @@ type MyClass() = class end interface ISecond with member this.M() = () } |> ignore """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -88,7 +73,7 @@ type MyClass() = class end interface ISecond with member this.M() = () } |> ignore """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -109,7 +94,7 @@ type MyClass() = class end interface ISecond with member this.M() = () } |> ignore """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -129,47 +114,13 @@ type MyClass() = let expr = { new MyClass() interface IFirst } (expr:> ISecond).M() """ - |> withLangVersion80 - |> compileExeAndRun - |> shouldSucceed - |> withStdOutContainsAllInOrder [ - "It works" - ] - - [] - let ``Verifies that the object expression built type has the interface in preview lang version.`` () = - Fsx """ -type IFirst = interface end - -type ISecond = - abstract member M : unit -> unit - -[] -type MyClass() = - interface ISecond with - member this.M() = printfn "It works" - -let expr = { new MyClass() } -(expr:> ISecond).M() - """ - |> withLangVersionPreview + |> withLangVersion90 |> compileExeAndRun |> shouldSucceed |> withStdOutContainsAllInOrder [ "It works" ] - [] - let ``Object expression implementing an interface without members preview`` () = - Fsx """ -type IFirst = interface end - -let implementer() ={ new IFirst } - """ - |> withLangVersionPreview - |> typecheck - |> shouldSucceed - [] let ``Parameterized object expression implementing an interface with members`` () = Fsx """ @@ -189,30 +140,7 @@ let implSomeDU someDu = | B b -> b | C c -> string c } """ - |> withLangVersion80 - |> typecheck - |> shouldSucceed - - [] - let ``Parameterized object expression implementing an interface with members preview`` () = - Fsx """ -type AsString = - abstract member asString: unit -> string - -type SomeDu = - | A of int - | B of string - | C of float - -let implSomeDU someDu = - { new AsString with - member self.asString () = - match someDu with - | A a -> string a - | B b -> b - | C c -> string c } - """ - |> withLangVersionPreview + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -229,7 +157,7 @@ let foo1 = new Foo() // hacky workaround let foo2 = { new Foo() with member __.ToString() = base.ToString() } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -246,7 +174,7 @@ type AbstractClass() = let res = { new AbstractClass() } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -263,26 +191,12 @@ type AbstractClass() = let res = { new AbstractClass() } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ (Error 738, Line 7, Col 11, Line 7, Col 34, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] - - [] - let ``Object expression can implement an abstract class having abstract members with default implementation preview`` () = - Fsx """ -[] -type AbstractClass() = - abstract member M : unit -> unit - default this.M() = printfn "Im a default implementation" - -let res = { new AbstractClass() } - """ - |> withLangVersionPreview - |> typecheck - |> shouldSucceed [] let ``Object expression can implement an abstract class(overriding a member) having abstract members with default implementation`` () = @@ -295,22 +209,7 @@ type AbstractClass() = let res = { new AbstractClass() with override this.ToString() = "ConcreteMethod" } """ - |> withLangVersion80 - |> typecheck - |> shouldSucceed - - [] - let ``Object expression can implement an abstract class(overriding a member) having abstract members with default implementation preview`` () = - Fsx """ -[] -type AbstractClass() = - abstract member M : unit -> unit - default this.M() = printfn "Im a default implementation" - -let res = { new AbstractClass() with - override this.ToString() = "ConcreteMethod" } - """ - |> withLangVersionPreview + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -324,59 +223,13 @@ type AbstractClass() = let res = { new AbstractClass() } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ (Error 365, Line 7, Col 11, Line 7, Col 34, "No implementation was given for 'abstract AbstractClass.M: unit -> unit'"); (Error 738, Line 7, Col 11, Line 7, Col 34, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] - - [] - let ``Object expression can implement an abstract class having no abstract members preview`` () = - Fsx """ -[] -type AbstractClass() = - do printfn "AbstractClass constructor" - -let res = { new AbstractClass() } - """ - |> withLangVersionPreview - |> compileExeAndRun - |> withStdOutContainsAllInOrder [ - "AbstractClass constructor" - ] - - [] - let ``Object expression can not implement an abstract class having abstract members preview.`` () = - Fsx """ -[] -type AbstractClass() = - do printfn "AbstractClass constructor" - abstract member M : unit -> unit - -let res = { new AbstractClass() } - """ - |> withLangVersionPreview - |> typecheck - |> shouldFail - |> withDiagnostics [ - (Error 365, Line 7, Col 11, Line 7, Col 34, "No implementation was given for 'abstract AbstractClass.M: unit -> unit'") - ] - - [] - let ``Object expression can implement an abstract class having no abstract members in preview lang version.`` () = - Fsx """ -[] -type Foo() = class end - -let foo = { new Foo() } - -let foo2 = { new Foo() with member __.ToString() = base.ToString() } - """ - |> withLangVersionPreview - |> typecheck - |> shouldSucceed [] let ``Object expression can implement an abstract class and interface having no abstract members.`` () = @@ -391,46 +244,9 @@ type MyClass() = class end interface IFirst } |> ignore """ - |> withLangVersion80 - |> typecheck - |> shouldSucceed - - [] - let ``Object expression can not implement an abstract class and interface having no abstract members preview`` () = - Fsx """ -type IFirst = interface end - -[] -type MyClass() = class end - -{ new MyClass() with - member x.ToString() = "OK" - - interface IFirst } |> ignore - """ - |> withLangVersionPreview + |> withLangVersion90 |> typecheck |> shouldSucceed - - [] - let ``Object expression can implement an abstract class having no abstract members.`` () = - Fsx """ -[] -type Foo() = class end - -let foo = { new Foo() } - -let foo1 = new Foo() - -// hacky workaround -let foo2 = { new Foo() with member __.ToString() = base.ToString() } - """ - |> withLangVersionPreview - |> typecheck - |> shouldFail - |> withDiagnostics [ - (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") - ] [] let ``Object expression shows error when object expression does not implement all abstract members of the abstract class`` () = @@ -446,34 +262,13 @@ type MyClass() = let res = { new MyClass() } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'"); (Error 738, Line 11, Col 11, Line 11, Col 28, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] - - [] - let ``Object expression shows error when object expression does not implement all abstract members of the abstract class preview`` () = - Fsx """ -type ISecond = - abstract member M : unit -> unit - -[] -type MyClass() = - abstract member M : unit -> unit - interface ISecond with - member this.M() = printfn "It works" - -let res = { new MyClass() } - """ - |> withLangVersionPreview - |> typecheck - |> shouldFail - |> withDiagnostics [ - (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'") - ] [] let ``C# abstract class with protected constructor can not be implemented by F# object expression`` () = @@ -503,7 +298,7 @@ open CSLib let res = { new Animal() } """ - |> withLangVersion80 + |> withLangVersion90 |> withName "FSLib" |> withReferences [ csharp ] @@ -513,46 +308,9 @@ let res = { new Animal() } |> withDiagnostics [ (Error 738, Line 5, Col 11, Line 5, Col 27, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] - - [] - let ``C# abstract class with protected constructor can be implemented by F# object expression lang version preview`` () = - - let csharp = - CSharp - """ -namespace CSLib -{ - using System; - public abstract class Animal - { - protected Animal() - { - Console.WriteLine("Animal is created"); - } - } -} -""" - |> withName "CSLib" - - let fsharp = - FSharp - """ -module FSLib -open CSLib - -let res = { new Animal() } -""" - |> withLangVersionPreview - |> withName "FSLib" - |> withReferences [ csharp ] - - fsharp - |> compile - |> shouldSucceed - + [] - let ``C# abstract class with protected constructor and abstract method can not be implemented by F# object expression unless the abstract method is implemented in lang version preview.`` () = - + let ``C# abstract class with protected constructor and abstract method and default implementation can be implemented by F# object expression unless the abstract method is implemented.`` () = let csharp = CSharp """ @@ -580,7 +338,7 @@ open CSLib let res = { new Animal() } """ - |> withLangVersionPreview + |> withLangVersion90 |> withName "FSLib" |> withReferences [ csharp ] @@ -588,12 +346,12 @@ let res = { new Animal() } |> compile |> shouldFail |> withDiagnostics [ - (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'") + (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'"); + (Error 738, Line 5, Col 11, Line 5, Col 27, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") ] [] let ``C# abstract class with protected constructor and abstract method can not be implemented by F# object expression unless the abstract method is implemented.`` () = - let csharp = CSharp """ @@ -621,7 +379,7 @@ open CSLib let res = { new Animal() } """ - |> withLangVersion80 + |> withLangVersion90 |> withName "FSLib" |> withReferences [ csharp ] @@ -650,7 +408,7 @@ and [] let y = { new C() with member x.M(a:int) : float = 1.0 } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -659,32 +417,7 @@ let y = { new C() with ] [] - let ``Error when object expression does not implement all abstract members of the abstract class preview`` () = - Fsx """ -[] -type B() = - abstract M : int -> float - abstract M : string -> unit -and [] - C() = - inherit B() - static let v = { new C() with - member x.M(a:int) : float = 1.0 } - default x.M(a:int) : float = 1.0 - -let y = { new C() with - member x.M(a:int) : float = 1.0 } - """ - |> withLangVersionPreview - |> typecheck - |> shouldFail - |> withDiagnostics [ - (Error 365, Line 9, Col 20, Line 10, Col 60, "No implementation was given for 'abstract B.M: string -> unit'") - (Error 365, Line 13, Col 9, Line 14, Col 49, "No implementation was given for 'abstract B.M: string -> unit'") - ] - - [] - let ``Error when object expression does not implement all abstract members of a generic abstract class`` () = + let ``Error when object expression does not implement all abstract members of a generic abstract class`` () = Fsx """ [] type BaseHashtable<'Entry, 'Key>(initialCapacity) = @@ -699,30 +432,7 @@ let f() = { new BaseHashtable<_,_>(2) with override this.Next entries = 1 } """ - |> withLangVersion80 - |> typecheck - |> shouldFail - |> withDiagnostics [ - (Error 359, Line 10, Col 11, Line 13, Col 12, "More than one override implements 'Next: StrongToWeakEntry<'a> array -> int when 'a: not struct'") - ] - - [] - let ``Error when object expression does not implement all abstract members of a generic abstract class preview`` () = - Fsx """ -[] -type BaseHashtable<'Entry, 'Key>(initialCapacity) = - abstract member Next : entries : 'Entry array -> int - -[] -type StrongToWeakEntry<'Value when 'Value : not struct> = - val mutable public next : int - -let f() = { new BaseHashtable<_,_>(2) with - override this.Next (entries:StrongToWeakEntry<_> array) = 1 - override this.Next entries = 1 - } - """ - |> withLangVersionPreview + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -765,7 +475,7 @@ let implSomeDU = | B b -> b | C c -> string c} """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -805,7 +515,7 @@ let implementer() = member this.F() = () member this.G() = () } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -823,7 +533,7 @@ let objExpr = member this.Execute1() = false member this.Property = 0 } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -840,7 +550,7 @@ let objExpr = member _.Execute2() = () } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -863,7 +573,7 @@ let _ = } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -885,7 +595,7 @@ let _ = } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -912,7 +622,7 @@ let _ = } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -937,7 +647,7 @@ let consoleLogger = consoleLogger.Log("Hello World") """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> compile |> shouldFail |> withSingleDiagnostic (Error 3860, Line 7, Col 11, Line 7, Col 18, "Object expressions cannot implement interfaces with static abstract members or declare static members.") @@ -959,7 +669,7 @@ let consoleLogger = consoleLogger.Log("Hello World") """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> compile |> shouldFail |> withSingleDiagnostic (Error 3860, Line 8, Col 11, Line 8, Col 18, "Object expressions cannot implement interfaces with static abstract members or declare static members.") @@ -980,7 +690,7 @@ let consoleLogger = consoleLogger.Log("Hello World") """ - |> withLangVersion80 + |> withLangVersion90 |> compileExeAndRun |> shouldSucceed @@ -1010,4 +720,262 @@ Please restrict it to one of the following: (Error 358, Line 8, Col 19, Line 8, Col 29, "The override for 'Overloaded: int -> bool' was ambiguous") (Error 358, Line 8, Col 19, Line 8, Col 29, "The override for 'Overloaded: string -> bool' was ambiguous") (Error 783, Line 7, Col 11, Line 7, Col 19, "At least one override did not correctly implement its corresponding abstract member") - ] \ No newline at end of file + ] + +module AllowObjectExpressionWithoutOverrides = + [] + let ``Object expression cannot implement unnamed interface`` () = + Fsx """ +type IFirst = + abstract member MyMember: unit -> int + +let x = { new _ with member this.MyMember() = 42 } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 772, Line 5, Col 11, Line 5, Col 16, "'new' must be used with a named type") + ] + + [] + let ``Verifies that the object expression built type has the interface`` () = + Fsx """ +type IFirst = interface end + +type ISecond = + abstract member M : unit -> unit + +[] +type MyClass() = + interface ISecond with + member this.M() = printfn "It works" + +let expr = { new MyClass() } +(expr:> ISecond).M() + """ + |> withLangVersionPreview + |> compileExeAndRun + |> shouldSucceed + |> withStdOutContainsAllInOrder [ + "It works" + ] + + [] + let ``Object expression implementing an interface without members`` () = + Fsx """ +type IFirst = interface end + +let implementer() ={ new IFirst } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Object expression can implement an abstract class having abstract members with default implementation`` () = + Fsx """ +[] +type AbstractClass() = + abstract member M : unit -> unit + default this.M() = printfn "Im a default implementation" + +let res = { new AbstractClass() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Object expression can implement an abstract class(overriding a member) having abstract members with default implementation`` () = + Fsx """ +[] +type AbstractClass() = + abstract member M : unit -> unit + default this.M() = printfn "Im a default implementation" + +let res = { new AbstractClass() with + override this.ToString() = "ConcreteMethod" } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Object expression can implement an abstract class with a protected constructor`` () = + Fsx """ +[] +type AbstractClass() = + do printfn "AbstractClass constructor" + +let res = { new AbstractClass() } + """ + |> withLangVersionPreview + |> compileExeAndRun + |> withStdOutContainsAllInOrder [ + "AbstractClass constructor" + ] + + [] + let ``Object expression can not implement an abstract class having abstract members, unless the abstract members are implemented`` () = + Fsx """ +[] +type AbstractClass() = + do printfn "AbstractClass constructor" + abstract member M : unit -> unit + +let res = { new AbstractClass() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 7, Col 11, Line 7, Col 34, "No implementation was given for 'abstract AbstractClass.M: unit -> unit'") + ] + + [] + let ``Object expression can implement an abstract class having no abstract members, only if the object expression has an override`` () = + Fsx """ +[] +type Foo() = class end + +let foo = { new Foo() } + +let foo2 = { new Foo() with member __.ToString() = base.ToString() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Object expression can not implement an abstract class and interface having no abstract members`` () = + Fsx """ +type IFirst = interface end + +[] +type MyClass() = class end + +{ new MyClass() with + member x.ToString() = "OK" + + interface IFirst } |> ignore + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Object expression can implement an abstract class having no abstract members. But trying to instantiate an abstract class will fail`` () = + Fsx """ +[] +type Foo() = class end + +let foo = { new Foo() } + +let foo1 = new Foo() + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 759, Line 7, Col 12, Line 7, Col 21, "Instances of this type cannot be created since it has been marked abstract or not all methods have been given implementations. Consider using an object expression '{ new ... with ... }' instead.") + ] + + [] + let ``Object expression shows error when object expression does not implement all abstract members of the abstract class`` () = + Fsx """ +type ISecond = + abstract member M : unit -> unit + +[] +type MyClass() = + abstract member M : unit -> unit + interface ISecond with + member this.M() = printfn "It works" + +let res = { new MyClass() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 11, Col 11, Line 11, Col 28, "No implementation was given for 'abstract MyClass.M: unit -> unit'") + ] + + [] + let ``C# abstract class with protected constructor can be implemented by F# object expression`` () = + + let csharp = + CSharp + """ +namespace CSLib +{ + using System; + public abstract class Animal + { + protected Animal() + { + Console.WriteLine("Animal is created"); + } + } +} +""" + |> withName "CSLib" + + let fsharp = + FSharp + """ +module FSLib +open CSLib + +let res = { new Animal() } +""" + |> withLangVersionPreview + |> withName "FSLib" + |> withReferences [ csharp ] + + fsharp + |> compile + |> shouldSucceed + + [] + let ``C# abstract class with protected constructor and abstract method can not be implemented by F# object expression unless the abstract method is implemented`` () = + + let csharp = + CSharp + """ +namespace CSLib +{ + using System; + public abstract class Animal + { + protected Animal() + { + Console.WriteLine("Animal is created"); + } + + public abstract void M(); + } +} +""" + |> withName "CSLib" + + let fsharp = + FSharp + """ +module FSLib +open CSLib + +let res = { new Animal() } +""" + |> withLangVersionPreview + |> withName "FSLib" + |> withReferences [ csharp ] + + fsharp + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 365, Line 5, Col 11, Line 5, Col 27, "No implementation was given for 'Animal.M() : unit'") + ] + \ No newline at end of file From 5d9d4b1a4d6ce4ee6e45f7a57df66143b30ce5e2 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 15 Aug 2024 18:12:45 +0100 Subject: [PATCH 19/20] Support non abstract too --- .../Checking/Expressions/CheckExpressions.fs | 2 +- .../ObjectExpressions/ObjectExpressions.fs | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 2801320d463..9074e140a3b 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7253,7 +7253,7 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI let extraImpls = allTypeImpls.Tail let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) - if not supportsObjectExpressionWithoutOverrides && not isInterfaceTy && (isOverallTyAbstract && overrides'.IsEmpty) && extraImpls.IsEmpty then + if not supportsObjectExpressionWithoutOverrides && not isInterfaceTy && overrides'.IsEmpty && extraImpls.IsEmpty then errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr)) // 4. Build the implementation diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index a1a303820fe..7e23a8bf21d 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -22,6 +22,35 @@ let x = { new _ with member this.MyMember() = 42 } |> withDiagnostics [ (Error 772, Line 5, Col 11, Line 5, Col 16, "'new' must be used with a named type") ] + + [] + let ``Object expression can not implement a class class end`` () = + Fsx """ +type Class() = class end + +let implementer = { new Class() } + """ + |> withLangVersion80 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 738, Line 4, Col 19, Line 4, Col 35, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") + ] + + [] + let ``Object expression can not implement a class without members`` () = + Fsx """ +type Class() = + member this.Do() = () + +let implementer = { new Class() } + """ + |> withLangVersion90 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 738, Line 5, Col 19, Line 5, Col 35, "Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces.") + ] [] let ``Object expression implementing an interface without members`` () = @@ -723,6 +752,29 @@ Please restrict it to one of the following: ] module AllowObjectExpressionWithoutOverrides = + [] + let ``Object expression can implement a class class end`` () = + Fsx """ +type Class() = class end + +let implementer = { new Class() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Object expression can implement a class without members`` () = + Fsx """ +type Class() = + member this.Do() = () + +let implementer = { new Class() } + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + [] let ``Object expression cannot implement unnamed interface`` () = Fsx """ From 17277d10463e7406806bbfdecc48d3b3dfdd3974 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 19 Aug 2024 17:41:19 +0100 Subject: [PATCH 20/20] withLangVersion90 --- .../Expressions/ObjectExpressions/ObjectExpressions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 7e23a8bf21d..f29c32a7d43 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -30,7 +30,7 @@ type Class() = class end let implementer = { new Class() } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [