Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix object-expr untested cases #17476

Merged
merged 10 commits into from
Aug 7, 2024
32 changes: 15 additions & 17 deletions src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7119,12 +7119,14 @@ and CheckSuperType (cenv: cenv) ty m =
and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraImpls, mObjTy, mNewExpr, mWholeExpr) =

let g = cenv.g

match tryTcrefOfAppTy g objTy with
| ValueNone -> error(Error(FSComp.SR.tcNewMustBeUsedWithNamedType(), mNewExpr))
| ValueSome tcref ->
let isRecordTy = tcref.IsRecordTycon
if not isRecordTy && not (isInterfaceTy g objTy) && isSealedTy g objTy then errorR(Error(FSComp.SR.tcCannotCreateExtensionOfSealedType(), mNewExpr))
let isInterfaceTy = isInterfaceTy g objTy
let isFSharpObjModelTy = isFSharpObjModelTy g objTy
let isOverallTyAbstract = HasFSharpAttribute g g.attrib_AbstractClassAttribute tcref.Attribs
if not isRecordTy && not isInterfaceTy && isSealedTy g objTy then errorR(Error(FSComp.SR.tcCannotCreateExtensionOfSealedType(), mNewExpr))

CheckSuperType cenv objTy mObjTy

Expand All @@ -7135,14 +7137,14 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI
let env = EnterFamilyRegion tcref env
let ad = env.AccessRights

if // record construction ?
if // record construction ? e.g { A = 1; B = 2 }
isRecordTy ||
// object construction?
(isFSharpObjModelTy g objTy && not (isInterfaceTy g objTy) && argopt.IsNone) then
// object construction? e.g. new A() { ... }
(isFSharpObjModelTy && not isInterfaceTy && argopt.IsNone) then

if argopt.IsSome then error(Error(FSComp.SR.tcNoArgumentsForRecordValue(), mWholeExpr))
if not (isNil extraImpls) then error(Error(FSComp.SR.tcNoInterfaceImplementationForConstructionExpression(), mNewExpr))
if isFSharpObjModelTy g objTy && GetCtorShapeCounter env <> 1 then
if isFSharpObjModelTy && GetCtorShapeCounter env <> 1 then
error(Error(FSComp.SR.tcObjectConstructionCanOnlyBeUsedInClassTypes(), mNewExpr))
let fldsList =
binds |> List.map (fun b ->
Expand All @@ -7152,8 +7154,9 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI

TcRecordConstruction cenv objTy true env tpenv None objTy fldsList mWholeExpr
else
// object expression construction e.g. { new A() with ... } or { new IA with ... }
let ctorCall, baseIdOpt, tpenv =
if isInterfaceTy g objTy then
if isInterfaceTy then
match argopt with
| None ->
BuildObjCtorCall g mWholeExpr, None, tpenv
Expand All @@ -7162,7 +7165,7 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI
else
let item = ForceRaise (ResolveObjectConstructor cenv.nameResolver env.DisplayEnv mObjTy ad objTy)

if isFSharpObjModelTy g objTy && GetCtorShapeCounter env = 1 then
if isFSharpObjModelTy && GetCtorShapeCounter env = 1 then
error(Error(FSComp.SR.tcObjectsMustBeInitializedWithObjectExpression(), mNewExpr))

match item, argopt with
Expand Down Expand Up @@ -7193,14 +7196,6 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI
overridesAndVirts |> List.iter (fun (m, implTy, dispatchSlots, dispatchSlotsKeyed, availPriorOverrides, overrides) ->
let overrideSpecs = overrides |> List.map fst
let hasStaticMembers = dispatchSlots |> List.exists (fun reqdSlot -> not reqdSlot.MethodInfo.IsInstance)
let isOverallTyAbstract =
match tryTcrefOfAppTy g objTy with
| ValueNone -> false
| ValueSome tcref -> HasFSharpAttribute g g.attrib_AbstractClassAttribute tcref.Attribs

if overrideSpecs.IsEmpty && not (isInterfaceTy g objTy) then
errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr))

if hasStaticMembers then
errorR(Error(FSComp.SR.chkStaticMembersOnObjectExpressions(), mObjTy))

Expand Down Expand Up @@ -7240,8 +7235,11 @@ 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
errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr))

// 7. Build the implementation
// 4. Build the implementation
let expr = mkObjExpr(objtyR, baseValOpt, ctorCall, overrides', extraImpls, mWholeExpr)
let expr = mkCoerceIfNeeded g realObjTy objtyR expr
expr, tpenv
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,20 +444,12 @@ let TcSequenceExpressionEntry (cenv: TcFileState) env (overallTy: OverallTy) tpe
match RewriteRangeExpr comp with
| Some replacementExpr -> TcExpr cenv overallTy env tpenv replacementExpr
| None ->

let implicitYieldEnabled =
cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield

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))
| _ -> ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,70 @@ let implementer() ={ new IFirst }
|> withLangVersion80
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Object expression can construct an abstract class and also implement interfaces with and without abstract members.`` () =
Fsx """
type IFirst = interface end

type ISecond =
abstract member M : unit -> unit

[<AbstractClass>]
type MyClass() = class end

{ new MyClass() with
member x.ToString() = "OK"

interface IFirst

interface ISecond with
member this.M() = () } |> ignore
"""
|> withLangVersion80
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Object expression can construct an abstract class(missing with...) and also implement interfaces with and without abstract members.`` () =
Fsx """
type IFirst = interface end

type ISecond =
abstract member M : unit -> unit

[<AbstractClass>]
type MyClass() = class end

{ new MyClass() interface IFirst

interface ISecond with
member this.M() = () } |> ignore
"""
|> withLangVersion80
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Object expression can construct an abstract class(missing with... and interface in the next line) and also implement interfaces with and without abstract members.`` () =
Fsx """
type IFirst = interface end

type ISecond =
abstract member M : unit -> unit

[<AbstractClass>]
type MyClass() = class end

{ new MyClass()
interface IFirst

interface ISecond with
member this.M() = () } |> ignore
"""
|> withLangVersion80
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Parameterized object expression implementing an interface with members`` () =
Expand Down
Loading