Skip to content

Commit fbfeb6d

Browse files
authored
Raise an error if we try to have interfaces with auto properties on constructor-less types (dotnet#16352)
1 parent dc40139 commit fbfeb6d

File tree

4 files changed

+110
-4
lines changed

4 files changed

+110
-4
lines changed

docs/release-notes/FSharp.Compiler.Service/8.0.200.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
- Parens: Keep parentheses around non-struct tuples in `&` patterns - https://github.com/dotnet/fsharp/pull/16391
33
- Parens analysis: fix some parenthesization corner-cases in record expressions - https://github.com/dotnet/fsharp/pull/16370
44
- Fixes #16359 - correctly handle imports with 0 length public key tokens - https://github.com/dotnet/fsharp/pull/16363
5+
- Raise a new error when interfaces with auto properties are implemented on constructor-less types - https://github.com/dotnet/fsharp/pull/16352

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4223,12 +4223,23 @@ module TcDeclarations =
42234223
| _ -> ()
42244224
| ds ->
42254225
// Check for duplicated parameters in abstract methods
4226+
// Check for an interface implementation with auto properties on constructor-less types
42264227
for slot in ds do
42274228
if isAbstractSlot slot then
42284229
match slot with
42294230
| SynMemberDefn.AbstractSlot (slotSig = synVal; range = m) ->
42304231
CheckDuplicatesArgNames synVal m
42314232
| _ -> ()
4233+
4234+
if isInterface slot then
4235+
match slot with
4236+
| SynMemberDefn.Interface (members= Some defs) ->
4237+
for au in defs do
4238+
match au with
4239+
| SynMemberDefn.AutoProperty(isStatic = false; range = m) ->
4240+
errorR(Error(FSComp.SR.tcAutoPropertyRequiresImplicitConstructionSequence(), m))
4241+
| _ -> ()
4242+
| _ -> ()
42324243

42334244
// Classic class construction
42344245
let _, ds = List.takeUntil (allFalse [isMember;isAbstractSlot;isInterface;isInherit;isField;isTycon]) ds
@@ -4253,9 +4264,6 @@ module TcDeclarations =
42534264
| SynMemberDefn.LetBindings (range=m) :: _ -> errorR(Error(FSComp.SR.tcTypeDefinitionsWithImplicitConstructionMustHaveLocalBindingsBeforeMembers(), m))
42544265
| _ -> ()
42554266

4256-
4257-
4258-
42594267
/// Split auto-properties into 'let' and 'member' bindings
42604268
let private SplitAutoProps members =
42614269
let membersIncludingAutoProps, vals_Inherits_Abstractslots =

tests/FSharp.Compiler.ComponentTests/Diagnostics/Records.fs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,23 @@ let t3 (x: RecTy) (a: AnotherNestedRecTy) = { x with D.C.c = { a with A = 3; B =
9292
|> withDiagnostics [
9393
(Warning 3560, Line 9, Col 26, Line 9, Col 65, "This copy-and-update record expression changes all fields of record type 'Test.NestdRecTy'. Consider using the record construction syntax instead.")
9494
(Warning 3560, Line 15, Col 62, Line 15, Col 85, "This copy-and-update record expression changes all fields of record type 'Test.AnotherNestedRecTy'. Consider using the record construction syntax instead.")
95-
]
95+
]
96+
97+
[<Fact>]
98+
let ``Error when implementing interface with auto property in record type``() =
99+
FSharp """
100+
type Foo =
101+
abstract member X : string with get, set
102+
abstract GetValue: unit -> string
103+
104+
type FooImpl =
105+
{ name: string }
106+
interface Foo with
107+
member val X = "" with get, set
108+
member x.GetValue() = x.name
109+
"""
110+
|> withLangVersion80
111+
|> asExe
112+
|> compile
113+
|> shouldFail
114+
|> withSingleDiagnostic (Error 912, Line 9, Col 5, Line 9, Col 36, "This declaration element is not permitted in an augmentation")

tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,4 +704,82 @@ type X =
704704
abstract member Bar<'T> : string -> unit
705705
"""
706706
|> typecheck
707+
|> shouldSucceed
708+
709+
[<Fact>]
710+
let ``Error if we try to have auto properties on constructor-less types`` () =
711+
Fsx """
712+
type Foo =
713+
abstract member X : string with get, set
714+
abstract member Y : string with get, set
715+
abstract member Z : string with get, set
716+
717+
type FooImpl =
718+
interface Foo with
719+
member val X = "" with get, set
720+
member val Y = "" with get, set
721+
member this.Z
722+
with get() = ""
723+
and set(value) = ()
724+
"""
725+
|> asExe
726+
|> compile
727+
|> shouldFail
728+
|> withDiagnostics [
729+
(Error 3133, Line 9, Col 9, Line 9, Col 40, "'member val' definitions are only permitted in types with a primary constructor. Consider adding arguments to your type definition, e.g. 'type X(args) = ...'.");
730+
(Error 3133, Line 10, Col 9, Line 10, Col 40, "'member val' definitions are only permitted in types with a primary constructor. Consider adding arguments to your type definition, e.g. 'type X(args) = ...'.")
731+
]
732+
733+
[<Fact>]
734+
let ``No error if we try to have auto properties on types with primary constructor`` () =
735+
Fsx """
736+
type Foo =
737+
abstract member X : string with get, set
738+
abstract member Y : string with get, set
739+
abstract member Z : string with get, set
740+
741+
type FooImpl() =
742+
interface Foo with
743+
member val X = "" with get, set
744+
member val Y = "" with get, set
745+
member this.Z
746+
with get() = ""
747+
and set(value) = ()
748+
"""
749+
|> typecheck
750+
|> shouldSucceed
751+
752+
[<Fact>]
753+
let ``No error if we try to have auto properties on types with primary constructor with args`` () =
754+
Fsx """
755+
type Foo =
756+
abstract member X : string with get, set
757+
abstract member Y : string with get, set
758+
abstract member Z : string with get, set
759+
760+
type FooImpl(x) =
761+
interface Foo with
762+
member val X = "" with get, set
763+
member val Y = "" with get, set
764+
member this.Z
765+
with get() = ""
766+
and set(value) = ()
767+
"""
768+
|> typecheck
769+
|> shouldSucceed
770+
771+
[<Fact>]
772+
let ``No error if we try to have static autoprop on a type without constructor`` () =
773+
Fsx """
774+
#nowarn "3535" //We accept that static abstracts are an advanced feature
775+
[<Interface>]
776+
type Foo =
777+
static abstract member X : string with get, set
778+
779+
[<Class>]
780+
type FooImpl =
781+
interface Foo with
782+
static member val X = "" with get, set
783+
"""
784+
|> typecheck
707785
|> shouldSucceed

0 commit comments

Comments
 (0)