Bug 40060 - System.TypeSpec.Parse cannot parse generics + anonymous type name
Summary: System.TypeSpec.Parse cannot parse generics + anonymous type name
Alias: None
Product: Runtime
Classification: Mono
Component: Reflection (show other bugs)
Version: 4.2.0 (C6)
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Aleksey Kliger
Depends on:
Blocks: 40057
  Show dependency tree
Reported: 2016-04-01 18:24 UTC by Aaron Bockover [MSFT]
Modified: 2016-04-07 01:28 UTC (History)
3 users (show)

See Also:
Is this bug a regression?: ---
Last known good build:


Description Aaron Bockover [MSFT] 2016-04-01 18:24:10 UTC
It seems that some Type.GetType methods use Type.internal_from_name to parse a type name (e.g. Type.GetType(string)), but some use TypeNameParser.GetType (e.g. Type.GetType(string, Func<AssemblyName,Assembly>, Func<Assembly,string,bool,Type>)).

We require a Type.GetType call that ends up calling TypeSpec.Parse by way of TypeNameParser.GetType in the Inspector, which hits this limitation when given a type name that includes generics and anonymous types:

  Type.GetType (new [] { new { A = 1 } }
      .ToDictionary (x => x.A)
      .GetType ()
      .ToString (), null, null)

Will result in:

System.ArgumentException: Invalid generic arguments spec                        Parameter name: typeName
  at System.TypeSpec.Parse (System.String name, System.Int32& p, Boolean is_recurse, Boolean allow_aqn) <0x1b66c00 + 0x00ada> in <filename unknown>:0
  at System.TypeSpec.Parse (System.String typeName) <0x1b660e0 + 0x0004c> in <filename unknown>:0
  at System.TypeNameParser.GetType (System.String typeName, System.Func`2 assemblyResolver, System.Func`4 typeResolver, Boolean throwOnError, Boolean ignoreCase, System.Threading.StackCrawlMark& stackMark) <0x1a11d60 + 0x0001d> in <filename unknown>:0
  at System.Type.GetType (System.String typeName, System.Func`2 assemblyResolver, System.Func`4 typeResolver) <0x198ab10 + 0x00050> in <filename unknown>:0
  at <InteractiveExpressionClass>.Host (System.Object& $retval) <0x302a248 + 0x000e7> in <filename unknown>:0
  at Mono.CSharp.Evaluator.Evaluate (System.String input, System.Object& result, System.Boolean& result_set) <0x6bca58 + 0x000ab> in <filename unknown>:0
  at Mono.CSharpShell.Evaluate (System.String input) <0x6bc970 + 0x00043> in <filename unknown>:0

The type name it is attempting to parse is:

Comment 1 Aleksey Kliger 2016-04-04 15:57:29 UTC

I think Type.GetType(string) also can't parse anon types correctly (ie your code above without the ,null,null args).

Looks like the example parses correctly on .NET, however.

I'll work on updating both parsers.
Comment 3 Aleksey Kliger 2016-04-04 20:38:14 UTC
It's not the parsers. It's how we do name resolution.

Here's a repro where .NET resolves the type, but Mono doesn't:

using System;
using System.Collections.Generic;

sealed class Jones<T>
    public readonly T A;
    public Jones(T a)
        A = a;
namespace Bug40060
    class Program
        static Dictionary<int, T> MakeDict<T>(T[] a)
            return new Dictionary<int, T>();

        static void Main(string[] args)
        {   var a = new Jones<int>[0];
            var t = MakeDict(a).GetType();
            var s = t.ToString();
            var tout = Type.GetType(s);
            Console.WriteLine("{0} is {1}", s, tout);
Comment 4 Aaron Bockover [MSFT] 2016-04-04 21:07:00 UTC
I think there are two separate bugs though

1. Bug in the managed parser, TypeSpec.Parse, which is the reason for this bug
2. Type.GetType (string) is parsing properly (by way of native code in mono_reflection_parse_type), but not resolving (new bug)
Comment 5 Aleksey Kliger 2016-04-04 21:14:33 UTC
No, (1) is not a problem.  The following code works as expected.  Note that I'm using FullName instead of ToString() to get around the resolver issue.   (In any case this example first takes a trip through our managed parser and then down into the native code to get everything resolved.)

using System;
using System.Collections.Generic;

namespace Bug40060
    class Program
	    static Dictionary<int,T> MakeDict<T>(T[] a)
		    return new Dictionary <int, T>();
        static void Main(string[] args)
	    var a = new { A = 1 };
	    var d = new [] { a };
            var tin = MakeDict(d).GetType (); // new[] { new { A = 1 } }.ToDictionary(x => x.A).GetType();
	    var s = tin.FullName;
	    var tout = Type.GetType(s, null, (assm,w,b) => Type.GetType(w));
            Console.WriteLine("\"{0}\" is {1}", s, tout);


"System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089],[<>__AnonType0`1[[System.Int32, mscorlib, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089]], ReproFullNames, Version=, Culture=neutral, PublicKeyToken=null]]" is System.Collections.Generic.Dictionary`2[System.Int32,<>__AnonType0`1[System.Int32]]
Comment 7 Aleksey Kliger 2016-04-07 01:28:18 UTC
Fixed on mono master commit 895071150a68b0a1d704f3b99ac90ee288f6e792

Fixed on mono-4.4.0-branch commit b36eb0de890aceb64605734af63a370e45ba88d2

Note You need to log in before you can comment on or make changes to this bug.