-
Notifications
You must be signed in to change notification settings - Fork 831
Description
This is a collection of a few issues I found after I started investigating the first:
- If you define an
exceptionwith a fieldMessage, it will result in a type derived fromSystem.Exceptionand a "normal" propertypublic string Message. As a result, a cast to Exception and retrieving theMessageproperty will not retrieve the field. In effect, it is shadowed. - Conversely, you will get an error if you try to write
override this.Message = this.Message: FS0023: The member 'Message' can not be defined because the name 'Message' clashes with the field 'Message' in this type or module. - If you try to do something similar (that is, trying to create a member field that is also auto-generated) on, say, a discriminated union it will fail with the same error (this is expected): FS0023: The member 'Tag' can not be defined because the name 'Tag' clashes with the generated property 'Tag' in this type or module. However, this error is thrown twice if the situation happens only once.
- But, if you use such a field in the definition of a discriminated union itself (as opposed to using explicit syntax), you won't receive that error, but once you compile, you receive: FSC: error FS2014: A problem occurred writing the binary 'D:\Projects\Test\Experiments.exe': Error in pass2 for type Test.Override, error: Error in pass2 for type Oops, error: duplicate entry 'Tag' in property table.
Repro steps
Repro situation 1
The following code will create sub-optimal MSIL:
module Override =
exception SomeException of Message: string
Expected behavior
Either an error (FS0021) for trying to use a field for a property or field that is already defined in the inherited class (here Exception.Message), or better, an override for that same property.
My preference is the override, as that would have the added effect of being able to treat an F# exception as a regular exception and that the Message, Data and other members can be retrieved if defined in the fields as such. It would have the benefit of both worlds: having compliant exception types that work with F# match expressions and at the same time work in interop with other languages without requiring specific definitions.
Actual behavior
The result will contain something like the following (C# syntax):
[CompilationMapping(SourceConstructFlags.Field, 0)]
public string Message =>
this.Message@;
See above for why I think this is sub-par and should raise an error, or create the proper override instead.
Repro situation 2
You can adjust the above code as follows, in an attempt to create a monomorphic type that can be used both from F# (pattern matching) and C#/VB/Python (using the Message property):
module Override =
exception SomeException of Message: string with
override this.Message = this.Message
Expected behavior
I would expect (or hope?) that the syntax above is allowed and would create the proper type signature. Though I think the current behavior is by design, as it prevents name clashes with defined field names in the type signature.
However, it is an odd situation. SomeException inherits from Exception, which has a virtual property Message. Not being able to override it seems a shame.
Of course, if one were allowed to use minimal syntax, as in situation 1 above, and that would take care of everything, it would allow us to create very terse and simple exceptions that are F# and interop compatible.
Actual behavior
An error is raised:
FS0023: The member 'Message' can not be defined because the name 'Message' clashes with the field 'Message' in this type or module
Repro situation 3
This situation can be reproduced by the following:
type Oops =
| Foo
member this.Tag = "foo" // squiggly here, FS0021 (see above)
Expected behavior
One error FS0021 is expected.
Actual behavior
Two errors are risen, both identical:
error FS0023: The member 'Tag' can not be defined because the name 'Tag' clashes with the generated property 'Tag' in this type or module
error FS0023: The member 'Tag' can not be defined because the name 'Tag' clashes with the generated property 'Tag' in this type or module
Repro situation 4
The following code reproduces this behavior:
type Oops =
| Foo of Tag: int // no squiggly
member this.X = "foo"
Expected behavior
Either it should compile just fine, or it should throw the error in the code editor as well as a syntax error.
Actual behavior
The actual behavior is that the actual error is only thrown very late in the compile process, in effect in the second pass:
FSC: error FS2014: A problem occurred writing the binary 'D:\Projects\Test\Experiments.exe': Error in pass2 for type Test.Override, error: Error in pass2 for type Oops, error: duplicate entry 'Tag' in property table
Notice the nesting inside this error message and how cryptic it is. I know some errors have been improved for F# 4.1, I don't know if this is one of them.
Known workarounds
The workarounds for these issues is to use a different field name (i.e., lower-case message) and add the override to get the wanted behavior. I have, for instance, my (public & interoperable) exceptions defined as follows:
module Override =
exception SomeException of info: string with
override this.Message = this.info
While not very hard to do, it seems inconsistent with the beautiful terseness of F#.
Related information
I have tested all scenarios above with F# 4.0 on VS 2015 with Update 3 and all Windows and system updates up-to-date.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status