-
Notifications
You must be signed in to change notification settings - Fork 789
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
No compiler error for instance members on static class; produces invalid IL & causes runtime error #13147
Comments
This should be relatively easy to add check for and fix, I'll mark it as a good first issue. |
Up to you, I'd say - the more granular - the better. Easier to reason about particular feature.
What does the specification say for these? |
@vzarytovskii Are we talking about this specification ? 4.1. or there is more updated one ? |
Yeah, in the this spec or any rfcs. But apparently there's nothing which mentions this. |
Quick question : Is there any specific rule to why Sealed and Abstract attributes can be used at the same time in the same type ? This is not allowed in C# [<Sealed;AbstractClass>]
type CompilerAssert private () = ..
[<Sealed; AbstractClass>]
type C() =
member _.M() = ()
[<Sealed; AbstractClass>]
type U = U
let u = U Or should raise a compiler error if both are used together in any type ? |
Sealed abstract types are pretty much just a way of having "static classes". |
Hmm I see thanks . There is a lot going on this issue . it difficult to identify what if any error/ warning are missing in this repro . I would suggest to split this in small ones with some extra description to make it a "real" good-first-issue. Im leaving this for now . Happy to help in the future :) |
Personally I would see it a lot cleaner to raise a 'dragons,beware'-style warning (not error because of backwards compat?) whenever [<Sealed;AbstractClass>] is used. For any kind of newcomer, a module with functions should be always preferred over this. Warning would mean it is only for when people know what and why they are doing (which I personally do not see at the moment). (google search for fsharp [<Sealed;AbstractClass>] finds me this GH issue only) |
Well if it now produces invalid IL, can be error by default guarded by language version (if someone, for some reason has this code). |
I meant an info/warning for [<Sealed;AbstractClass>] any time it is used (instance properties or not). |
Oh, no, we probably shouldn't by default. It's a quite known way of having a "static class", when people don't want to use modules. I've seen it many times in different codebases. Would be a good analyzer though, when we'll progress with the SDK. |
Yeah this is what was advised in similar cases . |
Yes, there are a few different things here. When I discovered one, I figured I might as well poke around and see what other related oddities I could find 🙃. Philosophically, though, they pretty much all have to do with one of the following:
Yes, putting This can be very useful when defining a set of overloaded static methods,1 for example, especially if such a type will be used from C#, since even though a type without a constructor like type T =
static member M () = () cannot be instantiated from F#, it can be instantiated from C# (see #8093, fsharp/fslang-suggestions#906), which is usually not intended. Footnotes
|
Related:
I'd like to emphasize that the So, if you add instance members to a sealed class, that's totally fine, but can never be executed. Just like it is totally fine to write code after a A warning may be nice, though. Here's another example showing that you can freely add extra constructors, or even let-bindings (which capture private-only fields). It may be non-trivial to detect all this: [<Sealed; AbstractClass>]
type MyStaticClass() =
let foo = 42 // huh?
new(a, b) = MyStaticClass()
member _.DoSomething() = () PS: there's literally nothing in the MSDN docs, and also nothing in the F# spec afaik. This is just a feature by accident: this combination of attributes is allowed by design, but doesn't necessarily limit what you can write (which is a bit unfortunate, really). PPS: the However, I cannot reproduce this. Here's what I tried (sharplab also shows correct, somewhat surprising IL): > [<Sealed; AbstractClass>]
type C() =
member _.M() = 42;;
type C =
new: unit -> C
member M: unit -> int
> let x = Unchecked.defaultof<C>.M();;
val x: int = 42 Though the result is surprising. The instance member is treated as a static member. Now, in terms of method-calling, this is correct, because that is how you usually say in IL that you want to call a static member. However, it is not a static member, sooo.... weird stuff. |
@abelbraaksma yes, that particular example doesn't yield a |
Co-authored-by: Petr <[email protected]> Fixes #13147
Repro steps
SealedAttribute
andAbstractClassAttribute
.a. Fields (mutable or not), properties, methods, indexers.
b. Abstract, concrete, override...
Examples
E.g., this
generates this (SharpLab):
There is also no error when making a class with a primary constructor static (SharpLab):
There is no error for fieldless single-case unions, either (SharpLab):2345
There is likewise no warning or error when adding
SealedAttribute
to a struct fieldless single-case union (SharpLab):5Expected behavior
The code should not compile.
Actual behavior
The code compiles, and calling it results in a
System.BadImageFormatException
with the message"Bad IL format."
at runtime.6Known workarounds
Don't try weird things.
Related information
Footnotes
Not auto-properties: using
member val
correctly givesFS3133
, presumably because the check for a primary constructor is separate.member val
is still let through if you do have a primary constructor (SharpLab). ↩It looks like the compiler ignores the combination of
Sealed
andAbstractClass
here and just adds theAbstractClass
attribute without actually making the class static. ↩The compiler correctly gives
FS0942
andFS0939
when a field is added (SharpLab) or a second case is added (SharpLab). ↩The compiler does disallow this for records with
FS0942
andFS0939
(SharpLab). ↩If you add a field or a bar
|
to either of these, the compiler does give youFS0942
and/orFS0939
. ↩ ↩2The compiler does disallow calling a constructor added to a static class with
FS0759
. ↩The text was updated successfully, but these errors were encountered: