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 3d4b290dfd9..1f8033bbe6a 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md @@ -20,6 +20,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 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 diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 8789e92797c..9074e140a3b 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 not supportsObjectExpressionWithoutOverrides && tycon.MembersOfFSharpTyconByName |> NameMultiMap.existsInRange (fun v -> v.IsIncrClassConstructor) then errorR(Error(FSComp.SR.tcConstructorRequiresCall(tycon.DisplayName), m)) let fspecs = tycon.TrueInstanceFieldsAsList @@ -7139,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 @@ -7206,8 +7208,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,7 +7220,6 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI 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 = @@ -7249,8 +7251,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 && overrides'.IsEmpty && extraImpls.IsEmpty then errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr)) // 4. Build the implementation 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..ccf2796ca90 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) = @@ -217,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") @@ -372,6 +374,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 = 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 適用的計算運算式 diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 33fc4582f26..f29c32a7d43 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -16,13 +16,42 @@ 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 can not implement a class class end`` () = + Fsx """ +type Class() = class end + +let implementer = { new Class() } + """ + |> withLangVersion90 + |> 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`` () = Fsx """ @@ -30,7 +59,7 @@ type IFirst = interface end let implementer() ={ new IFirst } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -53,7 +82,7 @@ type MyClass() = class end interface ISecond with member this.M() = () } |> ignore """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -73,7 +102,7 @@ type MyClass() = class end interface ISecond with member this.M() = () } |> ignore """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -94,7 +123,7 @@ type MyClass() = class end interface ISecond with member this.M() = () } |> ignore """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -114,7 +143,7 @@ type MyClass() = let expr = { new MyClass() interface IFirst } (expr:> ISecond).M() """ - |> withLangVersion80 + |> withLangVersion90 |> compileExeAndRun |> shouldSucceed |> withStdOutContainsAllInOrder [ @@ -140,7 +169,7 @@ let implSomeDU someDu = | B b -> b | C c -> string c } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -150,21 +179,247 @@ let implSomeDU someDu = [] type Foo() = class end -let foo = { new Foo() } // Approved suggestion to allow this https://github.com/fsharp/fslang-suggestions/issues/632 +let foo = { new Foo() } let foo1 = new Foo() // hacky workaround let foo2 = { new Foo() with member __.ToString() = base.ToString() } """ - |> withLangVersion80 + |> withLangVersion90 |> 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.") + (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() } + """ + |> withLangVersion90 + |> 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 with default implementation`` () = + Fsx """ +[] +type AbstractClass() = + abstract member M : unit -> unit + default this.M() = printfn "Im a default implementation" + +let res = { new AbstractClass() } + """ + |> 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(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" } + """ + |> withLangVersion90 + |> typecheck + |> shouldSucceed + + [] + 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() } + """ + |> 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 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 + """ + |> withLangVersion90 + |> typecheck + |> shouldSucceed + + [] + 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() } + """ + |> 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 ``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() } +""" + |> withLangVersion90 + |> withName "FSLib" + |> withReferences [ csharp ] + + fsharp + |> compile + |> shouldFail + |> 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 and abstract method and default implementation can 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() } +""" + |> withLangVersion90 + |> 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'"); + (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 + """ +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() } +""" + |> withLangVersion90 + |> 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'"); + (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 ``Error when object expression does not implement all abstract members of the abstract class`` () = Fsx """ @@ -182,14 +437,14 @@ and [] let y = { new C() with member x.M(a:int) : float = 1.0 } """ - |> withLangVersion80 + |> withLangVersion90 |> 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`` () = Fsx """ @@ -206,7 +461,7 @@ let f() = { new BaseHashtable<_,_>(2) with override this.Next entries = 1 } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -249,7 +504,7 @@ let implSomeDU = | B b -> b | C c -> string c} """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -289,7 +544,7 @@ let implementer() = member this.F() = () member this.G() = () } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -307,7 +562,7 @@ let objExpr = member this.Execute1() = false member this.Property = 0 } """ - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldSucceed @@ -324,7 +579,7 @@ let objExpr = member _.Execute2() = () } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -347,7 +602,7 @@ let _ = } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -369,7 +624,7 @@ let _ = } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -396,7 +651,7 @@ let _ = } """ |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] - |> withLangVersion80 + |> withLangVersion90 |> typecheck |> shouldFail |> withDiagnostics [ @@ -421,7 +676,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.") @@ -443,7 +698,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.") @@ -464,7 +719,7 @@ let consoleLogger = consoleLogger.Log("Hello World") """ - |> withLangVersion80 + |> withLangVersion90 |> compileExeAndRun |> shouldSucceed @@ -495,3 +750,284 @@ Please restrict it to one of the following: (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") ] + +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 """ +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