question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

No compiler error for instance members on static class; produces invalid IL & causes runtime error

See original GitHub issue

Repro steps

  1. Define a static class using the combination of SealedAttribute and AbstractClassAttribute.
  2. Add fields, instance members, even constructors:[^1] a. Fields (mutable or not), properties, methods, indexers. b. Abstract, concrete, override…

Examples

E.g., this

// This compiles and produces nonsense IL.
[<Sealed; AbstractClass>]
type T =
    abstract A : int
    abstract B : int with get, set
    abstract C : i:int -> int
    abstract D : i:int -> int
    default _.D i = i + 3
    member _.E = 3
    val F : int
    val mutable G : int
    member _.H (i, j) = i + j
    member _.Item with get i = 3 and set i value = ()
    override _.ToString () = "🙃"
    new () = { F = 3; G = 3 }
    new (x, y) = { F = x; G = y }

generates this (SharpLab):

[Serializable]
[Sealed]
[AbstractClass]
[CompilationMapping(SourceConstructFlags.ObjectType)]
public static class T
{
    internal int F@;

    public int G;

    [CompilationMapping(SourceConstructFlags.Field, 0)]
    public int F
    {
        get
        {
            return F@;
        }
    }

    public abstract override int A { get; }

    public abstract override int B { get; set; }

    public int E
    {
        get
        {
            return 3;
        }
    }

    public int this[object i]
    {
        get
        {
            return 3;
        }
        set
        {
        }
    }

    public abstract override int C(int i);

    public override int D(int i)
    {
        return i + 3;
    }

    public int H(int i, int j)
    {
        return i + j;
    }

    public override string ToString()
    {
        return "\ud83d\ude43";
    }

    public T()
    {
        F@ = 3;
        G = 3;
    }

    public T(int x, int y)
    {
        F@ = x;
        G = y;
    }
}

There is also no error when making a class with a primary constructor static (SharpLab):

// Just throw [<Sealed; AbstractClass>] on the default
// SharpLab class and it still compiles!
[<Sealed; AbstractClass>]
type C() =
    member _.M() = ()

There is no error for fieldless single-case unions, either (SharpLab):[^2][^3][^4][^5]

[<Sealed; AbstractClass>]
type U = U
let u = U

There is likewise no warning or error when adding SealedAttribute to a struct fieldless single-case union (SharpLab):[^5]

[<Sealed; Struct>]
type U = U

Expected 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.[^6]

> Unchecked.defaultof<T>.A;;
System.BadImageFormatException: Bad IL format.
   at <StartupCode$FSI_0007>.$FSI_0007.main@()
Stopped due to error

Known workarounds

Don’t try weird things.

Related information

  • .NET SDK 6.0.300.

[^1]: Not auto-properties: using member val correctly gives FS3133, presumably because the check for a primary constructor is separate. member val is still let through if you do have a primary constructor (SharpLab). [^2]: It looks like the compiler ignores the combination of Sealed and AbstractClass here and just adds the AbstractClass attribute without actually making the class static. [^3]: The compiler correctly gives FS0942 and FS0939 when a field is added (SharpLab) or a second case is added (SharpLab). [^4]: The compiler does disallow this for records with FS0942 and FS0939 (SharpLab). [^5]: If you add a field or a bar | to either of these, the compiler does give you FS0942 and/or FS0939. [^6]: The compiler does disallow calling a constructor added to a static class with FS0759.

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:2
  • Comments:17 (15 by maintainers)

github_iconTop GitHub Comments

1reaction
brianrourkebollcommented, Oct 23, 2022

@abelbraaksma yes, that particular example doesn’t yield a BadImageFormatException because, I think, the method call is optimized away altogether by the F# compiler (x is directly assigned the value 42).

1reaction
vzarytovskiicommented, May 17, 2022

This should be relatively easy to add check for and fix, I’ll mark it as a good first issue.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why does the C# compiler not fault code where a static ...
The "right" method will be determined during runtime. The compiler can not know if there is a valid method there during runtime.
Read more >
Compiler Error CS0708
This error occurs if you declare a non-static member in a class that is declared static. It is not possible to create instances...
Read more >
CLR bug with static variable in struct could be warned by ...
This compiles without error, but crashes with a type load exception at both runtime and when running PEVerify on it. From this more...
Read more >
Java Error: Non-static Variable/Method X Cannot be ...
Attempting to access a non-static variable/method from a static context without a class instance creates ambiguity.
Read more >
List of the armcc error and warning messages
When the compiler is not in C99 mode, this error is produced by a function declaration f(V) where V is a void type....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found