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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 適用できる計算式
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
+
+ 적용 가능한 계산 식
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
+
+ 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
+
+ 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
+
+ применимые вычислительные выражения
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
+
+ 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
+
+ 适用的计算表达式
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
+
+ 適用的計算運算式
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