Skip to content

Commit ccee270

Browse files
Fix object-expr untested cases (#17476)
* Fix object-expr untested case * Update condition to cover new found cases * Better test names * one more test --------- Co-authored-by: Kevin Ransom (msft) <[email protected]>
1 parent 26645ae commit ccee270

File tree

3 files changed

+102
-25
lines changed

3 files changed

+102
-25
lines changed

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7119,12 +7119,14 @@ and CheckSuperType (cenv: cenv) ty m =
71197119
and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraImpls, mObjTy, mNewExpr, mWholeExpr) =
71207120

71217121
let g = cenv.g
7122-
71237122
match tryTcrefOfAppTy g objTy with
71247123
| ValueNone -> error(Error(FSComp.SR.tcNewMustBeUsedWithNamedType(), mNewExpr))
71257124
| ValueSome tcref ->
71267125
let isRecordTy = tcref.IsRecordTycon
7127-
if not isRecordTy && not (isInterfaceTy g objTy) && isSealedTy g objTy then errorR(Error(FSComp.SR.tcCannotCreateExtensionOfSealedType(), mNewExpr))
7126+
let isInterfaceTy = isInterfaceTy g objTy
7127+
let isFSharpObjModelTy = isFSharpObjModelTy g objTy
7128+
let isOverallTyAbstract = HasFSharpAttribute g g.attrib_AbstractClassAttribute tcref.Attribs
7129+
if not isRecordTy && not isInterfaceTy && isSealedTy g objTy then errorR(Error(FSComp.SR.tcCannotCreateExtensionOfSealedType(), mNewExpr))
71287130

71297131
CheckSuperType cenv objTy mObjTy
71307132

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

7138-
if // record construction ?
7140+
if // record construction ? e.g { A = 1; B = 2 }
71397141
isRecordTy ||
7140-
// object construction?
7141-
(isFSharpObjModelTy g objTy && not (isInterfaceTy g objTy) && argopt.IsNone) then
7142+
// object construction? e.g. new A() { ... }
7143+
(isFSharpObjModelTy && not isInterfaceTy && argopt.IsNone) then
71427144

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

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

7165-
if isFSharpObjModelTy g objTy && GetCtorShapeCounter env = 1 then
7168+
if isFSharpObjModelTy && GetCtorShapeCounter env = 1 then
71667169
error(Error(FSComp.SR.tcObjectsMustBeInitializedWithObjectExpression(), mNewExpr))
71677170

71687171
match item, argopt with
@@ -7193,14 +7196,6 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI
71937196
overridesAndVirts |> List.iter (fun (m, implTy, dispatchSlots, dispatchSlotsKeyed, availPriorOverrides, overrides) ->
71947197
let overrideSpecs = overrides |> List.map fst
71957198
let hasStaticMembers = dispatchSlots |> List.exists (fun reqdSlot -> not reqdSlot.MethodInfo.IsInstance)
7196-
let isOverallTyAbstract =
7197-
match tryTcrefOfAppTy g objTy with
7198-
| ValueNone -> false
7199-
| ValueSome tcref -> HasFSharpAttribute g g.attrib_AbstractClassAttribute tcref.Attribs
7200-
7201-
if overrideSpecs.IsEmpty && not (isInterfaceTy g objTy) then
7202-
errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr))
7203-
72047199
if hasStaticMembers then
72057200
errorR(Error(FSComp.SR.chkStaticMembersOnObjectExpressions(), mObjTy))
72067201

@@ -7240,8 +7235,11 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI
72407235
let objtyR, overrides' = allTypeImpls.Head
72417236
assert (typeEquiv g objTy objtyR)
72427237
let extraImpls = allTypeImpls.Tail
7238+
7239+
if not isInterfaceTy && (isOverallTyAbstract && overrides'.IsEmpty) && extraImpls.IsEmpty then
7240+
errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), mWholeExpr))
72437241

7244-
// 7. Build the implementation
7242+
// 4. Build the implementation
72457243
let expr = mkObjExpr(objtyR, baseValOpt, ctorCall, overrides', extraImpls, mWholeExpr)
72467244
let expr = mkCoerceIfNeeded g realObjTy objtyR expr
72477245
expr, tpenv

src/Compiler/Checking/Expressions/CheckSequenceExpressions.fs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,20 +444,12 @@ let TcSequenceExpressionEntry (cenv: TcFileState) env (overallTy: OverallTy) tpe
444444
match RewriteRangeExpr comp with
445445
| Some replacementExpr -> TcExpr cenv overallTy env tpenv replacementExpr
446446
| None ->
447-
448447
let implicitYieldEnabled =
449448
cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield
450449

451450
let validateObjectSequenceOrRecordExpression = not implicitYieldEnabled
452451

453452
match comp with
454-
| SynExpr.New _ ->
455-
try
456-
TcExprUndelayed cenv overallTy env tpenv comp |> ignore
457-
with RecoverableException e ->
458-
errorRecovery e m
459-
460-
errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), m))
461453
| SimpleSemicolonSequence cenv false _ when validateObjectSequenceOrRecordExpression ->
462454
errorR (Error(FSComp.SR.tcInvalidObjectSequenceOrRecordExpression (), m))
463455
| _ -> ()

tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ObjectExpressions/ObjectExpressions.fs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,93 @@ let implementer() ={ new IFirst }
3333
|> withLangVersion80
3434
|> typecheck
3535
|> shouldSucceed
36+
37+
[<Fact>]
38+
let ``Object expression can construct an abstract class and also implement interfaces with and without abstract members.`` () =
39+
Fsx """
40+
type IFirst = interface end
41+
42+
type ISecond =
43+
abstract member M : unit -> unit
44+
45+
[<AbstractClass>]
46+
type MyClass() = class end
47+
48+
{ new MyClass() with
49+
member x.ToString() = "OK"
50+
51+
interface IFirst
52+
53+
interface ISecond with
54+
member this.M() = () } |> ignore
55+
"""
56+
|> withLangVersion80
57+
|> typecheck
58+
|> shouldSucceed
59+
60+
[<Fact>]
61+
let ``Object expression can construct an abstract class(missing with...) and also implement interfaces with and without abstract members.`` () =
62+
Fsx """
63+
type IFirst = interface end
64+
65+
type ISecond =
66+
abstract member M : unit -> unit
67+
68+
[<AbstractClass>]
69+
type MyClass() = class end
70+
71+
{ new MyClass() interface IFirst
72+
73+
interface ISecond with
74+
member this.M() = () } |> ignore
75+
"""
76+
|> withLangVersion80
77+
|> typecheck
78+
|> shouldSucceed
79+
80+
[<Fact>]
81+
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.`` () =
82+
Fsx """
83+
type IFirst = interface end
84+
85+
type ISecond =
86+
abstract member M : unit -> unit
87+
88+
[<AbstractClass>]
89+
type MyClass() = class end
90+
91+
{ new MyClass()
92+
interface IFirst
93+
94+
interface ISecond with
95+
member this.M() = () } |> ignore
96+
"""
97+
|> withLangVersion80
98+
|> typecheck
99+
|> shouldSucceed
100+
101+
[<Fact>]
102+
let ``Verifies that the object expression built type has the interface.`` () =
103+
Fsx """
104+
type IFirst = interface end
105+
106+
type ISecond =
107+
abstract member M : unit -> unit
108+
109+
[<AbstractClass>]
110+
type MyClass() =
111+
interface ISecond with
112+
member this.M() = printfn "It works"
113+
114+
let expr = { new MyClass() interface IFirst }
115+
(expr:> ISecond).M()
116+
"""
117+
|> withLangVersion80
118+
|> compileExeAndRun
119+
|> shouldSucceed
120+
|> withStdOutContainsAllInOrder [
121+
"It works"
122+
]
36123

37124
[<Fact>]
38125
let ``Parameterized object expression implementing an interface with members`` () =

0 commit comments

Comments
 (0)