Breaking the C# compiler, again

time to read 7 min | 1376 words

Take a look at the following code:

   1:  var docs = new dynamic[0];
   2:  var q = from doc in docs
   3:          where doc["@metadata"]["Raven-Entity-Name"] == "Cases"
   4:          where doc.AssociatedEntities != null
   5:          from entity in doc.AssociatedEntities
   6:          where entity.Tags != null // COMPILER ERROR HERE
   7:          from tag in entity.Tags
   8:          where tag.ReferencedAggregate != null
   9:          select new {tag.ReferencedAggregate.Id, doc.__document_id};        

It doesn’t compiles, complaining about an error in line 6 (The property '<>h__TransparentIdentifier0' cannot be used with type arguments).

Leaving aside the issue with the error itself, which is about as useful as C++ template errors, I have a strongly issue with this.

What do you think can fix this error? Well, if you remove line 8 (!), it just works:

   1:  var docs = new dynamic[0];
   2:  var q = from doc in docs
   3:          where doc["@metadata"]["Raven-Entity-Name"] == "Cases"
   4:          where doc.AssociatedEntities != null
   5:          from entity in doc.AssociatedEntities
   6:          where entity.Tags != null
   7:          from tag in entity.Tags
   8:          select new { tag.ReferencedAggregate.Id, doc.__document_id };

There are several things that I can’t figure out:

  • Why am I getting an error?
  • Why removing a line after the error fixes the error in the line before it.

What I suspect is that the error reporting (which in C# is usually damn good) is getting confused with the error reporting location.

To make things worse, this code will crash VS 2010 every single time. I am actually editing this in notepad!!!

Here is the set of extensions methods that I use here:

public static class LinqOnDynamic
{
    private static IEnumerable<dynamic> Select(this object self)
    {
        if (self == null)
            yield break;
        if (self is IEnumerable == false || self is string)
            throw new InvalidOperationException("Attempted to enumerate over " + self.GetType().Name);

        foreach (var item in ((IEnumerable) self))
        {
            yield return item;
        }
    }

    public static IEnumerable<dynamic> SelectMany(this object source,
                                                    Func<dynamic, int, IEnumerable<dynamic>> collectionSelector,
                                                    Func<dynamic, dynamic, dynamic> resultSelector)
    {
        return Enumerable.SelectMany(Select(source), collectionSelector, resultSelector);
    }

    public static IEnumerable<dynamic> SelectMany(this object source,
                                                    Func<dynamic, IEnumerable<dynamic>> collectionSelector,
                                                    Func<dynamic, dynamic, dynamic> resultSelector)
    {
        return Enumerable.SelectMany(Select(source), collectionSelector, resultSelector);
    }

    public static IEnumerable<dynamic> SelectMany(this object source,
                                                    Func<object, IEnumerable<dynamic>> selector)
    {
        return Select(source).SelectMany<object, object>(selector);
    }

    public static IEnumerable<dynamic> SelectMany(this object source,
                                                                    Func<object, int, IEnumerable<dynamic>> selector)
    {
        return Select(source).SelectMany<object, object>(selector);

    }
}

WTF?!