Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Generic type parameter for IEnumerable not supported?

Discussion in 'Burst' started by FaithlessOne, May 15, 2023.

  1. FaithlessOne

    FaithlessOne

    Joined:
    Jun 19, 2017
    Posts:
    257
    Hello,

    I tried to provide IEnumerable as a generic type parameter for a method, but that does not work. Is this not supported by Burst or is there a way to get it work?

    Thats the code:
    Code (CSharp):
    1. public interface INativeSwappingStructList<TElement> : IDisposable where TElement : unmanaged
    2. {
    3.   void AddRange<TEnumerable>(int swapIndex, TEnumerable addedElements) where TEnumerable : unmanaged, IEnumerable<TElement>;
    4. }  
    5.  
    6. [BurstCompile]
    7. public struct NativeSwappingStructListCore<TElement> : INativeSwappingStructList<TElement> where TElement : unmanaged
    8. {
    9.   [BurstCompile]
    10.   public void AddRange<TEnumerable>(int swapIndex, TEnumerable addedElements) where TEnumerable : unmanaged, IEnumerable<TElement>
    11.   {
    12.     foreach (var element in addedElements)
    13.     {
    14.     }
    15.   }
    16. }
    17.  
    18. private struct MyStruct : IEquatable<MyStruct>
    19. {
    20.   public int X;
    21.   public float F;
    22.   // ...
    23. }
    24.  
    25. // Called within other burst compiled code:
    26. NativeArray<MyStruct> array = new(2, Allocator.Temp);
    27. array[0] = new();
    28. array[1] = new();
    29.  
    30. NativeSwappingStructListCore<MyStruct> list = new(/* ... */);
    31. list.AddRange(0, array);
    Thats the error:
    NativeArray itself supports IEnumerable<T>:
    Code (CSharp):
    1. public struct NativeArray<T> : IDisposable, IEnumerable<T>, IEnumerable, IEquatable<NativeArray<T>>
    2.     where T : struct
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,006
    First thing to check: does it compile without Burst?Does it also work correctly?

    I read somewhere that Burst does not support scheduling generic jobs from generic methods. Maybe that has something to do with it.
    I would simplify this test to see where the issue is coming from. Like making AddRange a static method in a non generic class.
     
  3. tim_jones

    tim_jones

    Unity Technologies

    Joined:
    May 2, 2019
    Posts:
    282
    Thanks @FaithlessOne - we'll take a look and see whether this should be supported (in which case it's a bug) or not.
     
  4. FaithlessOne

    FaithlessOne

    Joined:
    Jun 19, 2017
    Posts:
    257
    Yes, thats fine C# code. In this case I want to circumvent the not allowed managed type restriction of Burst. The use of IEnumerable<TElement> instead of TEnumerable would clearly not work, because IEnumerable (interfaces in general) is a managed type. Also this technique of using generic types for interfaces avoids boxing of structs in plain C#, so its also having its purpose.

    Thanks for looking into that issue.
     
  5. MiroLagom

    MiroLagom

    Unity Technologies

    Joined:
    Apr 28, 2023
    Posts:
    10
    Hi @FaithlessOne,

    First I just want to say thank you for bringing this to our attention. You were the first to report this problem but I believe more will encounter this in the future. People like you really help Burst and Unity become better!

    We've looked into the error you got and you have caught an edge case we discovered we don't support. My colleague @tim_jones put together this example to show more clearly what's happening and explained it like this:

    In the example
    method M
    is what you are trying to do. We can't support it because it ends up calling an interface method (
    IEnumerator.MoveNext()
    ) without a concrete constrained type.
    Method N
    is what we support. Roslyn ends up calling
    S_Enumerator.MoveNext
    (a method on a struct type, which Burst is fine with), instead of
    IEnumerator<int>
    .

    So as it stands right now you need to have a concrete collection type as the parameter (and need to find another way to do that code). We'll be adding documentation to alert future users of this edge case. We're also adding a new error message for this particular case - hopefully helping others too.

    Thank you again, and feel free to ask any follow up questions!
     
    FaithlessOne likes this.
  6. FaithlessOne

    FaithlessOne

    Joined:
    Jun 19, 2017
    Posts:
    257
    Thanks for looking into my issue and stating a workaround. This is indeed interessting that the IL-Code of Roslyn still does virtual calls for members of IEnumerable supplied as generic type parameter. So I will stick to your stated workaround to be Burst compatible.
     
    MiroLagom likes this.
  7. MiroLagom

    MiroLagom

    Unity Technologies

    Joined:
    Apr 28, 2023
    Posts:
    10
    Hi @FaithlessOne,

    First I just want to thank you for bringing this to our attention. I believe you are just one of the people who will encounter this. People like you really help burst and Unity tools become better.
    Yes, we also thought that was interesting and didn't expect it at first.
    Good luck with your project and hope you find a well-working workaround for now! :)
     
    FaithlessOne likes this.
  8. Deleted User

    Deleted User

    Guest

    Wanted to point out another alternative that may work. Instead of
    foreach
    , you can use

    Code (CSharp):
    1. var enumerator = enumerable.GetEnumerator();
    2. while (enumerator.MoveNext())
    3. {
    4.   var item = enumerator.Current;
    5. }
     
  9. MiroLagom

    MiroLagom

    Unity Technologies

    Joined:
    Apr 28, 2023
    Posts:
    10
    Hi,

    Just wanted to give a heads up that this will throw the same error! But it was very good to consider other iteration-formats and an interesting idea, thank you for bringing it up. Just to be sure I just now gave
    for
    ,
    foreach
    and
    while
    a try, using them to iterate through a generic collection parameter, and all of them will (as it stands now) fail in Burst.
     
    Last edited: Jun 21, 2023
    Deleted User and Anthiese like this.