Bug 760 - LINQ statements employing JOIN clause cause JIT error
Summary: LINQ statements employing JOIN clause cause JIT error
Status: RESOLVED NOT_ON_ROADMAP
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll ()
Version: 4.x
Hardware: Macintosh Mac OS
: --- major
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2011-09-12 11:10 UTC by Brian Schuth
Modified: 2011-09-12 15:37 UTC (History)
2 users (show)

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

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED NOT_ON_ROADMAP

Description Brian Schuth 2011-09-12 11:10:20 UTC
It appears that the simplest JOIN clause in a LINQ for objects query will cause a JIT error. A full example can be found at http://github.com/bschuth/MT-JIT-Problem.git .

However, the problem can be quickly stated. Given minimal classes that only have basic int/string accessors for properties, the following LINQ causes JIT error:

from s in sessions
join score in scores
on s.SessionID equals score.OwnerID
select score.Name

...where "sessions" and "scores" are just List<Session> and List<Score> types, where Session and Score are simple classes.

This does not appear to be a dup of bug 300, as that was a problem that ran into the known problem with structs in LINQ.

The class file that generates this bug (including all referenced classes) is below for reference.

==== Source: PROBLEMLINQ.CS STARTS
using System;
using System.Linq;
using System.Collections.Generic;

namespace JIT20110908
{
  public class ProblemLinq
  {
    public IList<string> Execute ()
    {
      List<ActualScore> scores = new List<ActualScore> ();
      ActualScore score1 = new ActualScore () {
        Name = "ACTIVITIES",
        OwnerID = 666
      };
      scores.Add (score1);

      List<MySession> sessions = new List<MySession> ();
      MySession mysession = new MySession () {
        SessionID = 666,
      };
      sessions.Add (mysession);

      var summaryVar = from s in sessions
                             join score in scores
                             on s.SessionID equals score.OwnerID
                             select score.Name;

      List<string> summaryRows = summaryVar.ToList();
      return summaryRows;
    }
  }

  public class MySession
  {
    public int SessionID { get; set; }
  }

  public class ActualScore
  {
    public int OwnerID { get; set; }
    public string Name { get; set; }
  }
}
Comment 1 Sebastien Pouliot 2011-09-12 11:59:08 UTC
The power of LINQ is to allow a simple syntax to do complex things. So a "simple" JOIN turns into a far more complex, and generic heavy, thing than people expect.

You can get a hint of this by looking at the stack trace from the device (in "Application Output"). You'll see that the LINQ statement requires some non-obvious (to the static AOT compiler) generic types and that it match closely the description from bug #300 and
http://ios.xamarin.com/Documentation/Limitations#Value_types_as_Dictionary_Keys

[0x3e4a148c:] EXCEPTION handling: System.ExecutionEngineException: Attempting to JIT compile method 'System.Linq.Enumerable:<ToLookup`2>m__20<AbCrash.ActualScore, int> (AbCrash.ActualScore)' while running with --aot-only.


"<unnamed thread>" tid=0x0x3e4a148c this=0x0x9f5748 thread handle 0x103 state : not waiting owns ()
  at System.Linq.Enumerable.ToLookup<AbCrash.ActualScore, int, AbCrash.ActualScore> (System.Collections.Generic.IEnumerable`1<AbCrash.ActualScore>,System.Func`2<AbCrash.ActualScore, int>,System.Func`2<AbCrash.ActualScore, AbCrash.ActualScore>,System.Collections.Generic.IEqualityComparer`1<int>) [0x00079] in /Developer/MonoTouch/Source/mono/mcs/class/System.Core/System.Linq/Enumerable.cs:2915
  at System.Linq.Enumerable.ToLookup<AbCrash.ActualScore, int> (System.Collections.Generic.IEnumerable`1<AbCrash.ActualScore>,System.Func`2<AbCrash.ActualScore, int>,System.Collections.Generic.IEqualityComparer`1<int>) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/System.Core/System.Linq/Enumerable.cs:2883
  at System.Linq.Enumerable/<CreateJoinIterator>c__IteratorB`4<AbCrash.MySession, AbCrash.ActualScore, int, string>.MoveNext () [0x00023] in /Developer/MonoTouch/Source/mono/mcs/class/System.Core/System.Linq/Enumerable.cs:1153
  at System.Collections.Generic.List`1.AddEnumerable (System.Collections.Generic.IEnumerable`1<T>) <0x000bb>
  at System.Collections.Generic.List`1..ctor (System.Collections.Generic.IEnumerable`1<T>) <0x00083>
  at System.Linq.Enumerable.ToList<TSource> (System.Collections.Generic.IEnumerable`1<TSource>) <0x00043>
  at AbCrash.AppDelegate.Execute () [0x000b0] in /Users/sebastienpouliot/Downloads/AbCrash/AbCrash/Main.cs:77
  at AbCrash.AppDelegate.<FinishedLaunching>m__3 (object,System.EventArgs) [0x00000] in /Users/sebastienpouliot/Downloads/AbCrash/AbCrash/Main.cs:92
  at MonoTouch.UIKit.UIControlEventProxy.Activated () <0x00043>
  at (wrapper runtime-invoke) object.runtime_invoke_dynamic (intptr,intptr,intptr,intptr) <0xffffffff>
  at MonoTouch.UIKit.UIApplication.Main (string[],string,string) <0x0010f>
  at MonoTouch.UIKit.UIApplication.Main (string[]) <0x00023>
  at AbCrash.Application.Main (string[]) [0x00000] in /Users/sebastienpouliot/Downloads/AbCrash/AbCrash/Main.cs:18
  at (wrapper runtime-invoke) object.runtime_invoke_dynamic (intptr,intptr,intptr,intptr) <0xffffffff>
Comment 2 Brian Schuth 2011-09-12 14:26:41 UTC
I realize that under the hood LINQ does a lot of stuff that might ultimately cause the problem -- and I also understand that what looks simple in LINQ might be quite complex (that's why I use it!). 

But if we're saying that System.Linq.Enumerable.Join() is not usable in MT (and is not likely to ever be so), the Limitations doc should probably be more explicit -- that's a pretty big loss in LINQ. Although the stack trace is similar to bug 300, it's MT/core code that's causing the fail; everything looks kosher from the point of view of the calling code.

Off to de-LINQ my core code. Thank god for unit tests!
Comment 3 Sebastien Pouliot 2011-09-12 14:41:25 UTC
Confirmed, it's the same limitation wrt the use of value-types inside generic code. It has nothing to do with Join (or any other LINQ parts) but it's a global generic-related limitation (LINQ is only a heavy user of generics).

Possible workaround (easier than completely removing LINQ) are:

a) changing the [Owner|Session]ID members from 'int' (value type) to 'string' (reference) and keeping your existing LINQ expression;

b) change your LINQ expression to avoid 'int' like:

      var summaryVar = from s in sessions
                             join score in scores
                             on s.SessionID.ToString () equals score.OwnerID.ToString ()
                             select score.Name;
Comment 4 Brian Schuth 2011-09-12 15:37:45 UTC
Confirmed that this workaround succeeds in the case that brought me to bring this bug. Thanks for the time spent reviewing and the work around. bjs