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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Mysterious crash for no reason. Can anyone test?

Discussion in 'Editor & General Support' started by alexzzzz, Aug 20, 2016.

  1. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    I have a simple A* pathfinding demo project that was working fine until I added some extra code to it. The code is not used yet but if it just exists it somehow makes Unity crash (both editor and builds).

    Can anyone confirm I'm not crazy? If it turns out I'm just an idiot, that's fine.

    How to reproduce:

    1. Open the attached AStar.zip project.
    2. Open and run "Demo" scene.

    astar.PNG

    3. Press "Find path" and "Randomize" a couple of times to be sure that everything works. Stop the scene.
    4. Open IntVector2.cs and uncomment the commented lines:
    Code (CSharp):
    1. using System;
    2.  
    3. public struct IntVector2
    4. {
    5.     public static readonly IntVector2 Zero = new IntVector2(0, 0);
    6.     public static readonly IntVector2 One = new IntVector2(1, 1);
    7.     public static readonly IntVector2 West = new IntVector2(-1, 0);
    8.     public static readonly IntVector2 East = new IntVector2(1, 0);
    9.     public static readonly IntVector2 North = new IntVector2(0, 1);
    10.     public static readonly IntVector2 South = new IntVector2(0, -1);
    11.     public static readonly IntVector2[] Directions8 = { North, South, East, West, North + West, North + East, South + West, South + East };
    12.     public static readonly IntVector2[] Directions4 = { North, South, East, West };
    13.  
    14. //    private static readonly IntVector2[][] directions8Shuffled =
    15. //    {
    16. //        new[] { North, South, East, West, North + West, North + East, South + West, South + East, },
    17. //        new[] { South, East, West, North + West, North + East, South + West, South + East, North, },
    18. //        new[] { East, West, North + West, North + East, South + West, South + East, North, South, },
    19. //        new[] { West, North + West, North + East, South + West, South + East, North, South, East, },
    20. //        new[] { North + West, North + East, South + West, South + East, North, South, East, West, },
    21. //        new[] { North + East, South + West, South + East, North, South, East, West, North + West, },
    22. //        new[] { South + West, South + East, North, South, East, West, North + West, North + East, },
    23. //        new[] { South + East, North, South, East, West, North + West, North + East, South + West, },
    24. //    };
    25.  
    26. //    private static readonly IntVector2[][] directions4Shuffled =
    27. //    {
    28. //        new[] { North, South, East, West, },
    29. //        new[] { South, East, West, North, },
    30. //        new[] { East, West, North, South, },
    31. //        new[] { West, North, South, East, },
    32. //    };
    33.  
    34. //    private static int i8;
    35. //    public static IntVector2[] AllDirections8Shuffled
    36. //    {
    37. //        get
    38. //        {
    39. //            i8 = (i8 + 1) % 8;
    40. //            return directions8Shuffled[i8];
    41. //        }
    42. //    }
    43.  
    44. //    private static int i4;
    45. //    public static IntVector2[] AllDirections4Shuffled
    46. //    {
    47. //        get
    48. //        {
    49. //            i4 = (i4 + 1) % 4;
    50. //            return directions4Shuffled[i4];
    51. //        }
    52. //    }
    53.  
    54.     private readonly int x;
    55.     private readonly int z;
    56.  
    57.     public int X
    58.     {
    59.         get { return x; }
    60.     }
    61.  
    62.     public int Z
    63.     {
    64.         get { return z; }
    65.     }
    66.  
    67.     public IntVector2(int x, int z)
    68.     {
    69.         this.x = x;
    70.         this.z = z;
    71.     }
    72.  
    73.     public static int ManhattanDistance(IntVector2 a, IntVector2 b)
    74.     {
    75.         return Math.Abs(a.x - b.x) + Math.Abs(a.z - b.z);
    76.     }
    77.  
    78.     public float Magnitude()
    79.     {
    80.         return (float)Math.Sqrt(x * x + z * z);
    81.     }
    82.  
    83.     public static IntVector2 operator >>(IntVector2 v, int shift)
    84.     {
    85.         return new IntVector2(v.x >> shift, v.z >> shift);
    86.     }
    87.  
    88.     public static IntVector2 operator <<(IntVector2 v, int shift)
    89.     {
    90.         return new IntVector2(v.x << shift, v.z << shift);
    91.     }
    92.  
    93.     public static IntVector2 operator +(IntVector2 a, IntVector2 b)
    94.     {
    95.         return new IntVector2(a.x + b.x, a.z + b.z);
    96.     }
    97.  
    98.     public static IntVector2 operator -(IntVector2 a, IntVector2 b)
    99.     {
    100.         return new IntVector2(a.x - b.x, a.z - b.z);
    101.     }
    102.  
    103.     public static IntVector2 operator *(IntVector2 v, float multiplier)
    104.     {
    105.         return new IntVector2((int)Math.Round(v.x * multiplier), (int)Math.Round(v.z * multiplier));
    106.     }
    107.  
    108.     public static IntVector2 operator /(IntVector2 v, float divisor)
    109.     {
    110.         return new IntVector2((int)Math.Round(v.x / divisor), (int)Math.Round(v.z / divisor));
    111.     }
    112.  
    113.     public static IntVector2 operator %(IntVector2 v, int mod)
    114.     {
    115.         int x = v.x % mod;
    116.         if (x < 0)
    117.         {
    118.             x += mod;
    119.         }
    120.         int z = v.z % mod;
    121.         if (z < 0)
    122.         {
    123.             z += mod;
    124.         }
    125.  
    126.         return new IntVector2(x, z);
    127.     }
    128.  
    129.     public static bool operator ==(IntVector2 v1, IntVector2 v2)
    130.     {
    131.         return v1.Equals(v2);
    132.     }
    133.  
    134.     public static bool operator !=(IntVector2 v1, IntVector2 v2)
    135.     {
    136.         return !(v1 == v2);
    137.     }
    138.  
    139.     private bool Equals(IntVector2 other)
    140.     {
    141.         return other.x == x && other.z == z;
    142.     }
    143.  
    144.     public override int GetHashCode()
    145.     {
    146.         unchecked
    147.         {
    148.             return (x * 397) ^ z;
    149.         }
    150.     }
    151.  
    152.     public int CompareTo(object obj)
    153.     {
    154.         var other = (IntVector2)obj;
    155.         int result = x.CompareTo(other.x);
    156.         if (result != 0)
    157.         {
    158.             return result;
    159.         }
    160.  
    161.         return z.CompareTo(other.z);
    162.     }
    163.  
    164.     public override string ToString()
    165.     {
    166.         return string.Format("[{0}, {1}]", x, z);
    167.     }
    168.  
    169.     public override bool Equals(object obj)
    170.     {
    171.         var other = (IntVector2)obj;
    172.         return other.x == x && other.z == z;
    173.     }
    174. }

    5. Run the scene again.
    6. Press "Find path". At this point my editor either crashes instantly or after pressing "Find path" for the second time or after stopping the scene.

    Tested on Windows 10 x64, Unity 5.4.0p1.

    --
    I have found that minimum extra code that causes problems is just
    Code (CSharp):
    1. private static IntVector2[][] foo;
    Put it inside IntVector2 type and Unity will start crashing.
     

    Attached Files:

  2. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    8,998
    Yup, after adding that 1 line, it throws error and then crashes.. (tested with 5.3.4f1 and 5.4.0p2)
    IndexOutOfRangeException: Array index is out of range.
    at AStarDemo.DrawPath (ICollection`1 path) [0x0001f] in D:\download\AStar\AStar\Assets\AStarDemo.cs:115
    at AStarDemo.FindPath () [0x0004a] in D:\download\AStar\AStar\Assets\AStarDemo.cs:81
     
  3. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    @mgear
    Thank you for proving I'm sane.

    I managed to reduce the repro project down to a single script:
    Code (CSharp):
    1. using System.Linq;
    2. using UnityEngine;
    3.  
    4. public class Demo : MonoBehaviour
    5. {
    6.     public void Start()
    7.     {
    8.         Debug.Log("West: " + IntVector2.West);
    9.         Debug.Log("East: " + IntVector2.East);
    10.         Debug.Log("North: " + IntVector2.North);
    11.         Debug.Log("South: " + IntVector2.South);
    12.         Debug.Log(IntVector2.Directions.Aggregate("Directions: ", (text, position) => text + position + ", "));
    13.     }
    14. }
    15.  
    16. public struct IntVector2
    17. {
    18.     // Uncommenting the following line causes problems
    19.     // private static IntVector2[][] foo;
    20.  
    21.     public static readonly IntVector2 West = new IntVector2(-1, 0);
    22.     public static readonly IntVector2 East = new IntVector2(1, 0);
    23.     public static readonly IntVector2 North = new IntVector2(0, 1);
    24.     public static readonly IntVector2 South = new IntVector2(0, -1);
    25.     public static readonly IntVector2[] Directions = { North, South, East, West };
    26.  
    27.     public readonly int x;
    28.     public readonly int z;
    29.  
    30.     public IntVector2(int x, int z)
    31.     {
    32.         this.x = x;
    33.         this.z = z;
    34.     }
    35.  
    36.     public override string ToString()
    37.     {
    38.         return string.Format("[{0}, {1}]", x, z);
    39.     }
    40. }

    The normal output of the script is
    1.PNG

    Uncommenting the commented line leads to
    2.PNG

    or sometimes to
    3.PNG
    (notice that the error is at line 8, but there's no way that line can cause null reference exception anywhere)

    Replaying the scene multiple times eventually crashes Unity. To me it looks like a memory corruption issue.

    writing a bug report...
     
  4. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    One more experiment. I compiled the code to a console application
    Code (CSharp):
    1. using System;
    2. using System.Linq;
    3.  
    4. public static class Program
    5. {
    6.     public static void Main()
    7.     {
    8.         Console.WriteLine("West: " + IntVector2.West);
    9.         Console.WriteLine("East: " + IntVector2.East);
    10.         Console.WriteLine("North: " + IntVector2.North);
    11.         Console.WriteLine("South: " + IntVector2.South);
    12.         Console.WriteLine(IntVector2.Directions.Aggregate("Directions: ", (text, position) => text + position + ", "));
    13.     }
    14. }
    15.  
    16. public struct IntVector2
    17. {
    18.     // Uncommenting the following line causes problems
    19.     private static IntVector2[][] foo;
    20.  
    21.     public static readonly IntVector2 West = new IntVector2(-1, 0);
    22.     public static readonly IntVector2 East = new IntVector2(1, 0);
    23.     public static readonly IntVector2 North = new IntVector2(0, 1);
    24.     public static readonly IntVector2 South = new IntVector2(0, -1);
    25.     public static readonly IntVector2[] Directions = { North, South, East, West };
    26.  
    27.     public readonly int x;
    28.     public readonly int z;
    29.  
    30.     public IntVector2(int x, int z)
    31.     {
    32.         this.x = x;
    33.         this.z = z;
    34.     }
    35.  
    36.     public override string ToString()
    37.     {
    38.         return string.Format("[{0}, {1}]", x, z);
    39.     }
    40. }
    and ran it under different runtimes.

    1. Unity's "usual" Mono:
    Code (csharp):
    1. West: [-1, 0]
    2. East: [1, 0]
    3. North: [0, 1]
    4. South: [0, -1]
    5. Directions: [0, 0], [0, 0], [0, 0], [0, 0],
    2. Unity's "MonoBleedingEdge":
    Code (csharp):
    1. Invalid type IntVector2[][] for instance field IntVector2:foo
    2. Unhandled Exception:
    3. System.TypeLoadException: A type load exception has occurred.
    4. [ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: A type load exception has occurred.
    3. Standalone Mono 4.4.2:
    Code (csharp):
    1. Invalid type IntVector2[][] for instance field IntVector2:foo
    2. Unhandled Exception:
    3. System.BadImageFormatException: Could not resolve field token 0x04000003
    4. File name: 'Program'
    5. [ERROR] FATAL UNHANDLED EXCEPTION: System.BadImageFormatException: Could not resolve field token 0x04000003
    6. File name: 'Program'
    4. Microsoft's .Net:
    Code (csharp):
    1. West: [-1, 0]
    2. East: [1, 0]
    3. North: [0, 1]
    4. South: [0, -1]
    5. Directions: [0, 1], [0, -1], [1, 0], [-1, 0],
     
  5. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Even more simple repro:
    Code (CSharp):
    1. using System;
    2.  
    3. public static class Program
    4. {
    5.     private struct Foo
    6.     {
    7.         private static Foo[][] foo;
    8.     }
    9.  
    10.     public static void Main()
    11.     {
    12.         Console.WriteLine(typeof(Foo).Name);
    13.     }
    14. }

    Standalone Mono 4.4.2 output:
    Code (CSharp):
    1. Invalid type Foo[][] for instance field Foo:foo
    2. Foo
     
  6. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    And the bug report goes...
     
  7. Oribow

    Oribow

    Joined:
    Nov 9, 2012
    Posts:
    38
    Have you ever programmed in C-class language? You know, classes/structures and pointers and such?

    If you had so, then remember: in C# "classes" are passed "as pointers", while "structs" are passed in they raw binary form, they are copied around just like anything non-pointerish non-referencish in C/C++. All other restrictions for nesting are preserved, just named differently.

    In C# you can have a 'class X' containing a field of type 'class X' because "classes" are never placed anywhere directly. Such field is actually a 4/8 byte pointer/reference. The size of 'class X' is easily determinable. In contrast, in C# you cannot have a 'struct X' that contains a field of type 'struct X'. Such field would be embeded in the outer struct directly, not by pointer/reference. What would be the size of the 'struct X' if it contained another 'struct X' which surely by its definition would contain another 'struct X' that would contain .... where would be the end? How would you mark the end? How the compiler/runtime would know how much space to allocate when you write "new X()" ? Just as in C/C++ you cannot create recursively nested types directly (you can do that only by pointers/references), here in C# you cannot create recursive structs.

    From our friends at stack overflow.
     
  8. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Correction: in C# you cannot have a 'struct X' that contains an instance field of type 'struct X'.

    Correction #2: 'struct X' can contain a field of type 'array of X' or 'array of arrays of X' etc, no matter static or instance.
     
    Last edited: Aug 20, 2016