26

Why does the code below crash the .NET compiler? It was tested on csc.exe version 4.0.

See e.g. here for online demo on different version - it crashes in the same manner while it says dynamic is not supported https://dotnetfiddle.net/FMn59S:

Compilation error (line 0, col 0): Internal Compiler Error (0xc0000005 at address xy): likely culprit is 'TRANSFORM'.

The extension method works fine on List<dynamic> though.

using System;
using System.Collections.Generic;

static class F  {
    public static void M<T>(this IEnumerable<T> enumeration, Action<T> action){}

    static void U(C.K d) {
        d.M(kvp => Console.WriteLine(kvp));
    }
}

class C  {
    public class K : Dictionary<string, dynamic>{}
}

Update: this doesn't crash the compiler

static void U(Dictionary<string, dynamic> d)
{
    d.M(kvp => Console.WriteLine(kvp));
}

Update 2: the same bug was reported in http://connect.microsoft.com/VisualStudio/feedback/details/892372/compiler-error-with-dynamic-dictinoaries. The bug was reported for FirstOrDefault, but it seems the compiler crashes on any extension method applied to class derived from Dictionary<T1,T2>, where at least one of the parameter types is dynamic. See an even more general description of the problem below by Erik Funkenbusch.

Update 3: another non-standard behaviour. When I try to call extension method as a static method, that is, F.M(d, kvp => Console.WriteLine(kvp));, the compiler doesn't crash, but it cannot find the overload: Argument 1: cannot convert from 'C.K' to 'System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,dynamic>>'

Update 4 - SOLUTION (kind of): Hans sketched 2nd workaround, which is semantically equivalent to original code, but works only for extension method call and not for standard call. Since the bug is likely caused by the fact that the compiler fails to cast class derived from generic class with multiple parameters (with one being dynamic) to its supertype, the solution is to provide an explicit cast. See https://dotnetfiddle.net/oNvlcL:

((Dictionary<string, dynamic>)d).M(kvp => Console.WriteLine(kvp));
M((Dictionary<string, dynamic>)d, kvp => Console.WriteLine(kvp));
Franta
  • 524
  • 5
  • 13
  • 40
    Nice choice of class names. – H H Jun 21 '14 at 19:43
  • It's a bug in the compiler (that it crashes) - can you isolate and describe the single change that prevents a crash? I.e. would `Dictionary{}` "work"? – user2864740 Jun 21 '14 at 19:51
  • 8
    FWIW, Roslyn compiles this. – Jon Skeet Jun 21 '14 at 19:53
  • 1
    Check out [/bugreport CSC option](http://msdn.microsoft.com/en-us/library/kaky3xy9.aspx) so you can send report... – Alexei Levenkov Jun 21 '14 at 19:54
  • The relevant question here if it's on Microsoft Connect already. – H H Jun 21 '14 at 19:55
  • user2864740: strangely the extension method works fine on object of type Dictionary and on class derived from Dictionary – Franta Jun 21 '14 at 19:59
  • @Franta - Umm.. the method that crashes DOES operate on `Dictionary`, I'm not sure what you're meaning. and C.K *IS* derived from `Dictionary` – Erik Funkenbusch Jun 21 '14 at 20:05
  • @Erik Funkenbusch - I mean directly on Dictionary, not on class derived from it. – Franta Jun 21 '14 at 20:06
  • 2
    @Henk Holterman it seems that this bug is the same - FirstOrDefault is also extension method http://connect.microsoft.com/VisualStudio/feedback/details/892372/compiler-error-with-dynamic-dictinoaries – Franta Jun 21 '14 at 20:08
  • @Franta - Yes, it's the same bug. In fact, when I run the test case from that report, it gives the same address for the violation as when I run the testcase here. – Erik Funkenbusch Jun 21 '14 at 20:54
  • Looks like this is the same bug as well, they say it's been fixed in internal builds, and will be in the next release of the compiler. https://connect.microsoft.com/VisualStudio/feedback/details/814645/calling-todictionary-on-a-class-that-inherits-from-dictionary-that-has-a-dynamic-value-causes-compiler-crash – Erik Funkenbusch Jun 21 '14 at 21:01
  • 3
    I have a hard time figuring out what answer would actually answer the question "why did a program I don't have the source code to crash?" What are you intending to learn from any answer to this question? – Eric Lippert Jun 22 '14 at 02:52
  • 2
    @Eric - I hope to learn something about internal workings of the compiler and to find out what is the actual extent of this bug, since it manifests itself in more cases than I originally thought. – Franta Jun 22 '14 at 08:07

2 Answers2

18

It is dynamic that is triggering the instability, the crash disappears when you replace it by object.

Which is one workaround, the other is to help it infer the correct T:

static void U(C.K d) {
    d.M(new Action<KeyValuePair<string, dynamic>>(kvp => Console.WriteLine(kvp)));
}

The feedback report that you found is a strong match, no need to file your own I'd say.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Not to mention it's already been fixed in internal builds, and seems to be fixed in Roslyn. https://connect.microsoft.com/VisualStudio/feedback/details/814645/calling-todictionary-on-a-class-that-inherits-from-dictionary-that-has-a-dynamic-value-causes-compiler-crash – Erik Funkenbusch Jun 21 '14 at 22:23
  • Well, if you're writing interpreter then it's much harder to do it without use of `dynamic`. Currently the best workaround I know is to define method which expects `Dictionary`, but that's also pretty bad solution. The bug seems to happen when there's a need to cast one generic class with more than one parameter to another interface and at least one parameter is dynamic. In one case it crashes compiler (extension method), in another it cannot find overload. – Franta Jun 22 '14 at 08:29
  • Erm, my workaround is entirely reasonable, just help it infer the type. Works fine with *dynamic*, no need to pick a "T2". Why don't you want to use it? – Hans Passant Jun 22 '14 at 08:46
  • Forgive my ignorance, your second workaround is fine. With delegates it works well. If there's no actual answer what went wrong in compiler, I'll select this as an answer. So it seems compiler converts the class to wrong interface if there's only lambda argument, right? – Franta Jun 22 '14 at 09:14
  • 4
    The C# compiler has a hard problem to solve in this code, the double type inference combined with *dynamic* being substitutable because Dictionary implements covariant interfaces explodes the number of possible matches it has to consider. I'd hate to have to write code like that myself, impossible to test completely. The underlying crash is just a NullReferenceException, happens every day in a C# program, it just looks a heckofalot uglier when it happens in C++. It takes a Microsoft engineer to diagnose it completely. Simplify the job it has to do and the crash disappears. – Hans Passant Jun 22 '14 at 09:28
  • @HansPassant - I tried to apply your 2nd workaround to situation in Update 3, i.e. call of static method (`F.M(d, new Action>(kvp => Console.WriteLine(kvp)));` - https://dotnetfiddle.net/RBNzOW), but it doesn't help. Any idea for another workaround? – Franta Jun 23 '14 at 20:00
15

Well, the answer to your question as to WHY it crashes the compiler, it's because you've encountered a bug that.... crashes the compiler.

The VS2013 compiler says "Internal Compiler Error (0xc0000005 at address 012DC5B5): likely culprit is 'TRANSFORM'", so clearly it's a bug.

C0000005 is typically a null pointer, or referencing unallocated, or deleted memory. It's a general protection fault.

EDIT:

The problem is also present in pretty much any kind of multiple parameter generic type where the any parameter is dynamic. For instance it crashes on:

List<Tuple<string, dynamic>>{}

It also crashes on

List<KeyValuePair<dynamic, string>>{}

But does not crash on

List<dynamic>{}

but does crash on

List<List<dynamic>>{}
Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • 7
    So... this is a repeat of the question? Where is the answer part? – H H Jun 21 '14 at 19:54
  • 9
    @HenkHolterman - First, when I answered this, the question did not include the same information. He added that afterwards. Second, the answer is the first sentence, which clearly says "The answer to your question". I know it sounds stupid, but that IS the answer. Ask a stupid question, you get a stupid answer. We don't have the source code to the compiler, so we have no way of knowing much else. – Erik Funkenbusch Jun 21 '14 at 20:01
  • 3
    You may want to add this information to the question, but it doesn't really answer it. – CodeCaster Jun 21 '14 at 20:16
  • @ErikFunkenbusch - just to clarify, I have general knowledge about interrupt 0n13/14 and SEH/VEH, but that's just a manifestation of some problem. Hans' second workaround is more informative about the nature of the bug. – Franta Jun 22 '14 at 10:22