Skip to content

Fields clash with inherited or auto-generated fields #1602

@abelbraaksma

Description

@abelbraaksma

This is a collection of a few issues I found after I started investigating the first:

  1. If you define an exception with a field Message, it will result in a type derived from System.Exception and a "normal" property public string Message. As a result, a cast to Exception and retrieving the Message property will not retrieve the field. In effect, it is shadowed.
  2. 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.
  3. 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.
  4. 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

No one assigned

    Labels

    Area-Compiler-CheckingType checking, attributes and all aspects of logic checkingBugImpact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.

    Type

    No type

    Projects

    Status

    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions