diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index c18d969fa4c..1a6298e3a48 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -1,5 +1,6 @@ ### Fixed -* Various parenthesization API fixes. ([PR #16977](https://github.com/dotnet/fsharp/pull/16977)) +* Disallow calling abstract methods directly on interfaces. ([Issue #14012](https://github.com/dotnet/fsharp/issues/14012), [Issue #16299](https://github.com/dotnet/fsharp/issues/16299), [PR #17021](https://github.com/dotnet/fsharp/pull/17021)) +* Various parenthesization API fixes. ([PR #16977](https://github.com/dotnet/fsharp/pull/16977)) * Fix bug in optimization of for-loops over integral ranges with steps and units of measure. ([Issue #17025](https://github.com/dotnet/fsharp/issues/17025), [PR #17040](https://github.com/dotnet/fsharp/pull/17040)) * Fix calling an overridden virtual static method via the interface ([PR #17013](https://github.com/dotnet/fsharp/pull/17013)) diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 2667ea6ccfc..41c7e26f47e 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2942,10 +2942,23 @@ and ResolveOverloading let candidates = calledMethGroup |> List.filter (fun cmeth -> cmeth.IsCandidate(m, ad)) let calledMethOpt, errors, calledMethTrace = - match calledMethGroup, candidates with - | _, [calledMeth] when not isOpConversion -> - Some calledMeth, CompleteD, NoTrace + | _, [calledMeth] when not isOpConversion -> + // See what candidates we have based on static/virtual/abstract + + // If false then is a static method call directly on an interface e.g. + // IParsable.Parse(...) + // IAdditionOperators.(+) + // This is not allowed as Parse and (+) method are static abstract + let isStaticConstrainedCall = + match calledMeth.OptionalStaticType with + | Some ttype -> isTyparTy g ttype + | None -> false + + match calledMeth.Method with + | ILMeth(ilMethInfo= ilMethInfo) when not isStaticConstrainedCall && ilMethInfo.IsStatic && ilMethInfo.IsAbstract -> + None, ErrorD (Error (FSComp.SR.chkStaticAbstractInterfaceMembers(ilMethInfo.ILName), m)), NoTrace + | _ -> Some calledMeth, CompleteD, NoTrace | [], _ when not isOpConversion -> None, ErrorD (Error (FSComp.SR.csMethodNotFound(methodName), m)), NoTrace diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index acb3eecc210..e5cabbdc7e3 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1745,4 +1745,5 @@ featureReuseSameFieldsInStructUnions,"Share underlying fields in a [] di 3862,parsStaticMemberImcompleteSyntax,"Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration." 3863,parsExpectingField,"Expecting record field" 3864,tooManyMethodsInDotNetTypeWritingAssembly,"The type '%s' has too many methods. Found: '%d', maximum: '%d'" -3865,parsOnlySimplePatternsAreAllowedInConstructors,"Only simple patterns are allowed in primary constructors" \ No newline at end of file +3865,parsOnlySimplePatternsAreAllowedInConstructors,"Only simple patterns are allowed in primary constructors" +3866,chkStaticAbstractInterfaceMembers,"A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.%s)." \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 4a57c5a229d..a7f9808f488 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -122,6 +122,11 @@ Člen nebo funkce „{0}“ má atribut „TailCallAttribute“, ale nepoužívá se koncovým (tail) rekurzivním způsobem. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Objektové výrazy nemohou implementovat rozhraní se statickými abstraktními členy ani deklarovat statické členy. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 3875895aed7..534e047744f 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -122,6 +122,11 @@ Der Member oder die Funktion "{0}" weist das Attribut "TailCallAttribute" auf, wird jedoch nicht endrekursiv verwendet. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Objektausdrücke können keine Schnittstellen mit statischen abstrakten Membern implementieren oder statische Member deklarieren. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 229f3781a88..5d2517d1ca9 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -122,6 +122,11 @@ El miembro o la función “{0}” tiene el atributo “TailCallAttribute”, pero no se usa de forma de recursión de cola. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Las expresiones de objeto no pueden implementar interfaces con miembros abstractos estáticos ni declarar miembros estáticos. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 5ace1c2dc39..f5c4358314a 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -122,6 +122,11 @@ Le membre ou la fonction « {0} » possède l'attribut « TailCallAttribute », mais n'est pas utilisé de manière récursive. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Les expressions d’objet ne peuvent pas implémenter des interfaces avec des membres abstraits statiques ou déclarer des membres statiques. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index c5cb0876c15..350f2decb87 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -122,6 +122,11 @@ Il membro o la funzione "{0}" ha l'attributo "TailCallAttribute", ma non è in uso in modo ricorsivo finale. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Le espressioni di oggetto non possono implementare interfacce con membri astratti statici o dichiarare membri statici. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 04b73c10939..376d3d8d592 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -122,6 +122,11 @@ メンバーまたは関数 '{0}' には 'TailCallAttribute' 属性がありますが、末尾の再帰的な方法では使用されていません。 + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. オブジェクト式は、静的抽象メンバーを持つインターフェイスを実装したり、静的メンバーを宣言したりすることはできません。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index afd1f78ca86..f4a42405fcb 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -122,6 +122,11 @@ 멤버 또는 함수 '{0}'에 'TailCallAttribute' 특성이 있지만 비상 재귀적인 방식으로 사용되고 있지 않습니다. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. 개체 식은 정적 추상 멤버가 있는 인터페이스를 구현하거나 정적 멤버를 선언할 수 없습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 98611ebcc88..f081661bf4e 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -122,6 +122,11 @@ Składowa lub funkcja „{0}” ma atrybut „TailCallAttribute”, ale nie jest używana w sposób cykliczny końca. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Wyrażenia obiektów nie mogą implementować interfejsów ze statycznymi abstrakcyjnymi elementami członkowskimi ani deklarować statycznych elementów członkowskich. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 9356372d275..b17c0b76d24 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -122,6 +122,11 @@ O membro ou a função "{0}" tem o atributo "TailCallAttribute", mas não está sendo usado de maneira recursiva em cauda. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Expressões de objeto não podem implementar interfaces com membros abstratos estáticos ou declarar membros estáticos. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 6a08c7ada1a..7f83d96d646 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -122,6 +122,11 @@ Элемент или функция "{0}" содержит атрибут "TailCallAttribute", но не используется в рекурсивном хвостовом режиме. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Выражения объектов не могут реализовывать интерфейсы со статическими абстрактными членами или объявлять статические члены. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 7aab43de9c4..f74a800f285 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -122,6 +122,11 @@ Üye veya '{0}' işlevi, 'TailCallAttribute' özniteliğine sahip ancak kuyruk özyinelemeli bir şekilde kullanılmıyor. + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. Nesne ifadeleri, statik soyut üyeler içeren arabirimleri uygulayamaz veya statik üyeleri bildiremez. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 95715bd1b8a..19dae40d40a 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -122,6 +122,11 @@ 成员或函数“{0}”具有 "TailCallAttribute" 属性,但未以尾递归方式使用。 + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. 对象表达式无法实现具有静态抽象成员或声明静态成员的接口。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 5b95c7fe99e..4f332483292 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -122,6 +122,11 @@ 成員或函式 '{0}' 具有 'TailCallAttribute' 屬性,但未以尾遞迴方式使用。 + + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{0}). + + Object expressions cannot implement interfaces with static abstract members or declare static members. 物件運算式無法實作具有靜態抽象成員的介面或宣告靜態成員。 diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/ConstrainedAndInterfaceCalls.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/ConstrainedAndInterfaceCalls.fs new file mode 100644 index 00000000000..740eff41812 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/ConstrainedAndInterfaceCalls.fs @@ -0,0 +1,16 @@ +#nowarn "64" +open System +open System.Numerics + +module ConstrainedCall = + let ``'T.op_Addition``<'T & #IAdditionOperators<'T, 'T, 'T>> x y = 'T.op_Addition (x, y) + let ``'T.(+)``<'T & #IAdditionOperators<'T, 'T, 'T>> x y = 'T.(+) (x, y) + let ``'T.op_CheckedAddition``<'T & #IAdditionOperators<'T, 'T, 'T>> x y = 'T.op_CheckedAddition (x, y) + let ``'T.Parse``<'T & #IParsable<'T>> x = 'T.Parse (x, null) + +module InterfaceCall = + let ``IAdditionOperators.op_Addition`` x y = IAdditionOperators.op_Addition (x, y) + let ``IAdditionOperators.(+)`` x y = IAdditionOperators.(+) (x, y) + let ``IAdditionOperators.op_CheckedAddition`` x y = IAdditionOperators.op_CheckedAddition (x, y) + let ``IParsable.Parse``<'T & #IParsable<'T>> x : 'T = IParsable.Parse (x, null) + diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs index d3d7be2cf36..48c54e2c11e 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs @@ -19,6 +19,18 @@ module TypesAndTypeConstraints_IWSAMsAndSRTPs = |> asExe |> withLangVersion70 |> withReferences [typesModule] + + let verifyCompile compilation = + compilation + |> asExe + |> withOptions ["--nowarn:988"] + |> compile + + let verifyCompileAndRun compilation = + compilation + |> asExe + |> withOptions ["--nowarn:988"] + |> compileAndRun [] let ``Srtp call Zero property returns valid result`` () = @@ -1183,4 +1195,50 @@ let execute = IPrintable.Say("hello") |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] |> withLangVersion80 |> typecheck - |> shouldSucceed \ No newline at end of file + |> shouldSucceed + + [] + let ``Accessing to IWSAM(System.Numerics non virtual) produces a compilation error`` () = + Fsx """ +open System.Numerics + +IAdditionOperators.op_Addition (3, 6) + """ + |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] + |> withLangVersion80 + |> compile + |> shouldFail + |> withSingleDiagnostic (Error 3866, Line 4, Col 1, Line 4, Col 38, "A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.op_Addition).") + + [] + let ``Accessing to IWSAM(System.Numerics virtual member) compiles and runs`` () = + Fsx """ +open System.Numerics + +let res = IAdditionOperators.op_CheckedAddition (3, 6) + +printf "%A" res""" + |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] + |> withLangVersion80 + |> asExe + |> compile + |> shouldSucceed + |> run + |> verifyOutput "9" + +#if !NETCOREAPP + [] +#else + // SOURCE=ConstrainedAndInterfaceCalls.fs # ConstrainedAndInterfaceCalls.fs + [] +#endif + let ``ConstrainedAndInterfaceCalls.fs`` compilation = + compilation + |> withOptions [ "--nowarn:3536" ; "--nowarn:3535" ] + |> verifyCompile + |> shouldFail + |> withDiagnostics [ + (Error 3866, Line 12, Col 82, Line 12, Col 126, "A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.op_Addition).") + (Error 3866, Line 13, Col 82, Line 13, Col 126, "A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.op_Addition).") + (Error 3866, Line 15, Col 82, Line 15, Col 129, "A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.Parse).") + ] \ No newline at end of file