It seems like ILAst is generating incorrect generic parameters for invocations of constructors on generic types. This doesn't seem to negatively affect the generated C# from decompilation but it seems to be semantically wrong - the MethodReference for the constructor uses generic method parameter references (like !!0) instead of type parameter references (T).
For example, this function:
public static List<string> MyToList1(IEnumerable<string> source)
return new List<string>(source);
Produces this IL:
.method public hidebysig static
class [mscorlib]System.Collections.Generic.List`1<string> MyToList1 (
class [mscorlib]System.Collections.Generic.IEnumerable`1<string> source
) cil managed
// Method begins at RVA 0x2050
// Code size 12 (0xc)
.locals init (
 class [mscorlib]System.Collections.Generic.List`1<string> CS$1$0000
IL_0002: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>)
IL_0008: br.s IL_000a
} // end of method Program::MyToList1
However, if you inspect the actual newobj ILExpression, the Operand is:
For comparison, the actual signature of that constructor according to ILSpy is:
.method public hidebysig specialname rtspecialname
instance void .ctor (
class System.Collections.Generic.IEnumerable`1<!T> collection
) cil managed
As I understand it, the !0 form of a generic parameter refers to a generic method's generic parameters - in this case, neither the calling method or the constructor are generic, so they cannot have any generic method parameters. I would expect the parameter to be !T or T here.
Is this a bug in ILSpy or am I just missing a detail about how MSIL works? In my case, I'm using the Operand from the newobj to identify which particular constructor overload is being invoked, and this discrepancy makes it impossible for me to locate the actual overload at work here. Is !0 a positional reference to something else in the expression?
Oh, I see... !0 is different from !!0, so I think that's a positional reference to a type parameter... so that means the first type parameter for the enclosing type, which would be T. Is this just a failure to expand the !0 to T?
Edit: Digging through the ECMA MSIL spec suggests this is the case. I've never seen the !0 form before this, though, so I assume it's intended for it not to be exposed.