diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index 4fc0c8844ef..c1e9d233357 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -3454,13 +3454,6 @@ let TcSequenceExpressionEntry (cenv: cenv) env (overallTy: OverallTy) tpenv (has let validateObjectSequenceOrRecordExpression = not implicitYieldEnabled match comp with - | SynExpr.New _ -> - try - TcExprUndelayed cenv overallTy env tpenv comp |> ignore - with RecoverableException e -> - errorRecovery e m - - errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), m)) | SimpleSemicolonSequence cenv false _ when validateObjectSequenceOrRecordExpression -> errorR (Error(FSComp.SR.tcInvalidObjectSequenceOrRecordExpression (), m)) | _ -> () diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 6a44feacb08..2e31342e8a7 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -6793,8 +6793,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 @@ -7159,8 +7159,11 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI if argopt.IsSome then error(Error(FSComp.SR.tcNoArgumentsForRecordValue(), mWholeExpr)) if not (isNil extraImpls) then error(Error(FSComp.SR.tcNoInterfaceImplementationForConstructionExpression(), mNewExpr)) - let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) - if isFSharpObjModelTy g objTy && GetCtorShapeCounter env <> 1 && not supportsObjectExpressionWithoutOverrides then + // let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) + + + let requiresConstructor = GetCtorShapeCounter env <> 1 + if isFSharpObjModelTy g objTy && requiresConstructor then error(Error(FSComp.SR.tcObjectConstructionCanOnlyBeUsedInClassTypes(), mNewExpr)) let fldsList = binds |> List.map (fun b -> @@ -7207,19 +7210,27 @@ 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) - + let isAbstractClass = + match tryTcrefOfAppTy g objTy with + | ValueNone -> false + | ValueSome tcref -> HasFSharpAttribute g g.attrib_AbstractClassAttribute tcref.Attribs + + if overrideSpecs.IsEmpty && not isAbstractClass && not (isInterfaceTy g objTy) then + errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mObjTy)) + if hasStaticMembers then errorR(Error(FSComp.SR.chkStaticMembersOnObjectExpressions(), mObjTy)) DispatchSlotChecking.CheckOverridesAreAllUsedOnce (env.DisplayEnv, g, cenv.infoReader, true, implTy, dispatchSlotsKeyed, availPriorOverrides, overrideSpecs) - if not hasStaticMembers then + if not hasStaticMembers && not isAbstractClass then DispatchSlotChecking.CheckDispatchSlotsAreImplemented (env.DisplayEnv, cenv.infoReader, m, env.NameEnv, cenv.tcSink, false, implTy, dispatchSlots, availPriorOverrides, overrideSpecs) |> ignore - ) + // 3. create the specs of overrides let allTypeImpls = @@ -10787,14 +10798,14 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt match rhsExpr with | SynExpr.Fixed (e, _) -> true, e, NewInferenceType g, overallTy | e -> - let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) - if supportsObjectExpressionWithoutOverrides then - match e with - | SynExpr.ComputationExpr(false, SynExpr.New(_, targetType, _, m), _) -> - false, SynExpr.ObjExpr(targetType, None, None, [], [], [], m, rhsExpr.Range), NewInferenceType g, overallTy - | _ -> false, e, overallTy, overallTy - else - false, e, overallTy, overallTy + // let supportsObjectExpressionWithoutOverrides = g.langVersion.SupportsFeature(LanguageFeature.AllowObjectExpressionWithoutOverrides) + // if supportsObjectExpressionWithoutOverrides then + match e with + | SynExpr.ComputationExpr(false, SynExpr.New(_, targetType, expr, m), _) -> + false, SynExpr.ObjExpr(targetType, Some(expr, None), None, [], [], [], m, rhsExpr.Range), overallTy, overallTy + | _ -> false, e, overallTy, overallTy + //else + // false, e, overallTy, overallTy // Check the attributes of the binding, parameters or return value let TcAttrs tgt isRet attrs = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs index 1a2b2b3ace8..788144c6fc7 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs @@ -58,36 +58,46 @@ let implSomeDU someDu = |> shouldSucceed [] - let ``Object expression can not implement an abstract class having no abstract members.`` () = + let ``Object expression can implement an abstract class having no abstract members.`` () = Fsx """ [] 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() } // hacky workaround let foo1 = { new Foo() with member __.ToString() = base.ToString() } + """ + |> withLangVersion80 + |> typecheck + |> shouldSucceed + + [] + let ``Error when using an object expression with a none abstract class`` () = + Fsx """ +type MyClass() = class end + +let foo = { new MyClass() } + """ |> withLangVersion80 |> typecheck |> shouldFail |> withDiagnostics [ - (Error 759, Line 5, Col 13, Line 5, Col 22, "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 740, Line 5, Col 11, Line 5, Col 24, "Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq { ... }'") + (Error 738, Line 4, Col 17, Line 4, Col 24, "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.`` () = + let ``No Error when object expression does not implement all abstract members of an abstract class`` () = Fsx """ [] -type Foo() = class end +type MyAbstractClass() = + abstract Bar: unit -> unit -let foo = { new Foo() } +let foo = { new MyAbstractClass() } -let foo1 = { new Foo() with member __.ToString() = base.ToString() } """ - |> withLangVersionPreview + |> withLangVersion80 |> typecheck |> shouldSucceed