Browse Source

New nullable support

Brian Fiete 5 năm trước cách đây
mục cha
commit
88adb3a1cd

+ 1 - 2
BeefLibs/corlib/src/Collections/Generic/Dictionary.bf

@@ -414,8 +414,7 @@ namespace System.Collections.Generic
 			int_cosize* newBuckets = new int_cosize[newSize]*;
 			int_cosize* newBuckets = new int_cosize[newSize]*;
 			for (int_cosize i = 0; i < newSize; i++) newBuckets[i] = -1;
 			for (int_cosize i = 0; i < newSize; i++) newBuckets[i] = -1;
 			Entry* newEntries = new Entry[newSize]*;
 			Entry* newEntries = new Entry[newSize]*;
-			//mEntries.CopyTo(newEntries, 0, 0, mCount);
-			Internal.MemCpy(newEntries, mEntries, mCount * sizeof(Entry), alignof(Entry));
+			Internal.MemCpy(newEntries, mEntries, mCount * strideof(Entry), alignof(Entry));
 
 
 			if (forceNewHashCodes)
 			if (forceNewHashCodes)
 			{
 			{

+ 382 - 50
BeefLibs/corlib/src/Nullable.bf

@@ -1,83 +1,62 @@
 using System.Reflection;
 using System.Reflection;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Runtime.CompilerServices;
 using System.Diagnostics;
 using System.Diagnostics;
 
 
 namespace System
 namespace System
 {
 {
-    public struct Nullable<T> where T : struct
+    struct Nullable<T> where T : struct
     {
     {
-		#region Sync with runtime code
-		internal T mValue;
+        internal T mValue;
         internal bool mHasValue;
         internal bool mHasValue;
-		#endregion
-
-		public this(T value)
+        
+        public this(T value)
         {
         {
             mHasValue = true;
             mHasValue = true;
             mValue = value;
             mValue = value;
         }
         }
-        
+
+        [Inline]
         public bool HasValue
         public bool HasValue
         {
         {
             get { return mHasValue; }
             get { return mHasValue; }
         }
         }
-        
+
+        [Inline]
         public T Value
         public T Value
         {
         {
             get
             get
             {
             {
-            	Debug.Assert(mHasValue, "Value cannot be retrieved on a null nullable.");
+                if (!mHasValue)
+                {
+                    Debug.FatalError("Nullable object must have a value.");
+                }
+                    
                 return mValue;
                 return mValue;
             }
             }
         }
         }
 
 
-		public ref T ValueRef
-		{
-		    get mut
-		    {
-				Debug.Assert(mHasValue, "Value cannot be retrieved on a null nullable.");
-		        return ref mValue;
-		    }
-		}
-        
-        /*public override bool Equals(object other)
+        [Inline]
+        public ref T ValueRef
         {
         {
-            if (other == null)
-                return mHasValue == false;
-            if (!(other is Nullable<T>))
-                return false;
-            
-            return Equals((Nullable<T>)other);
+            get mut
+            {
+                if (!mHasValue)
+                {
+                    Debug.FatalError("Nullable object must have a value.");
+                }
+                    
+                return ref mValue;
+            }
         }
         }
         
         
-        bool Equals(Nullable<T> other)
-        {
-            if (other.mHasValue != mHasValue)
-                return false;
-            
-            if (mHasValue == false)
-                return true;
-            
-            return other.mValue.Equals(mValue);
-        }*/
-        
-        /*public override int GetHashCode()
-        {
-            if (!mHasValue)
-                return 0;
-            
-            return mValue.GetHashCode();
-        }*/
-        
         public T GetValueOrDefault()
         public T GetValueOrDefault()
         {
         {
             return mValue;
             return mValue;
         }
         }
         
         
-        public T GetValueOrDefault(T defaultValue)
+        public T GetValueOrDefault(T defaultmValue)
         {
         {
-            return mHasValue ? mValue : defaultValue;
+            return mHasValue ? mValue : defaultmValue;
         }
         }
         
         
         public override void ToString(String str)
         public override void ToString(String str)
@@ -87,16 +66,369 @@ namespace System
             else
             else
                 str.Clear();
                 str.Clear();
         }
         }
-        
+
+        [Inline]
         public static implicit operator Nullable<T>(T value)
         public static implicit operator Nullable<T>(T value)
         {
         {
             return Nullable<T>(value);
             return Nullable<T>(value);
         }
         }
-        
+
+        [Inline]
         public static explicit operator T(Nullable<T> value)
         public static explicit operator T(Nullable<T> value)
         {
         {
-			Debug.Assert(value.mHasValue, "Value cannot be retrieved on a null nullable.");
             return value.mValue;
             return value.mValue;
         }
         }
+
+        [Inline]
+        public static bool operator==(Nullable<T> lhs, T rhs)
+        {
+            if (!lhs.mHasValue) return false;
+            return lhs.mValue == rhs;
+        }
+
+        ///
+
+        public static bool operator==<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T == TOther
+        {
+            if (!lhs.mHasValue) return false;
+            return lhs.mValue == rhs;
+        }
+
+        public static bool operator==<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther == T
+        {
+            if (!rhs.mHasValue) return false;
+            return lhs == rhs;
+        }
+
+        public static bool operator==<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T == TOther where TOther : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+            return lhs.mValue == rhs.mValue;
+        }
+
+        ///
+
+        public static bool operator!=<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T != TOther
+        {
+            if (!lhs.mHasValue) return false;
+            return lhs.mValue != rhs;
+        }
+
+        public static bool operator!=<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther != T
+        {
+            if (!rhs.mHasValue) return false;
+            return lhs != rhs;
+        }
+
+        public static bool operator!=<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T != TOther where TOther : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+            return lhs.mValue != rhs.mValue;
+        }
+
+        ///
+
+        public static bool operator< <TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T < TOther
+        {
+            if (!lhs.mHasValue) return false;
+            return lhs.mValue < rhs;
+        }
+
+        public static bool operator< <TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther < T
+        {
+            if (!rhs.mHasValue) return false;
+            return lhs < rhs;
+        }
+
+        public static bool operator< <TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T < TOther where TOther : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+            return lhs.mValue < rhs.mValue;
+        }
+
+        ///
+
+        public static bool operator<=<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T <= TOther
+        {
+            if (!lhs.mHasValue) return false;
+            return lhs.mValue <= rhs;
+        }
+
+        public static bool operator<=<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther <= T
+        {
+            if (!rhs.mHasValue) return false;
+            return lhs <= rhs;
+        }
+
+        public static bool operator<=<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T <= TOther where TOther : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+            return lhs.mValue <= rhs.mValue;
+        }
+
+        ///
+
+        public static bool operator><TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T > TOther
+        {
+            if (!lhs.mHasValue) return false;
+            return lhs.mValue > rhs;
+        }
+
+        public static bool operator><TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther > T
+        {
+            if (!rhs.mHasValue) return false;
+            return lhs > rhs;
+        }
+
+        public static bool operator><TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T > TOther where TOther : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+            return lhs.mValue > rhs.mValue;
+        }
+
+        ///
+
+        public static bool operator>=<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T >= TOther
+        {
+            if (!lhs.mHasValue) return false;
+            return lhs.mValue >= rhs;
+        }
+
+        public static bool operator>=<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther >= T
+        {
+            if (!rhs.mHasValue) return false;
+            return lhs >= rhs;
+        }
+
+        public static bool operator>=<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T >= TOther where TOther : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+            return lhs.mValue >= rhs.mValue;
+        }
+
+        ///
+
+        public static int operator<=><TOther>(Nullable<T> lhs, TOther rhs) where int : operator T <=> TOther
+        {
+            return lhs.mValue <=> rhs;
+        }
+
+        public static int operator<=><TOther>(TOther lhs, Nullable<T> rhs) where int : operator TOther <=> T
+        {
+            return lhs <=> rhs;
+        }
+
+        public static int operator<=><TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where int : operator T <=> TOther where TOther : struct
+        {
+            return lhs.mValue <=> rhs.mValue;
+        }
+
+        ///
+
+        public static TResult? operator+<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T + TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue + rhs);
+        }
+        public static TResult? operator+<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther + T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs + rhs.mValue);
+        }
+        public static TResult? operator+<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T + TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue + rhs.mValue);
+        }
+
+        ///
+
+        public static TResult? operator-<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther - T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs - rhs.mValue);
+        }
+
+        public static TResult? operator-<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T - TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue - rhs);
+        }
+
+        public static TResult? operator-<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T - TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue - rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator*<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther * T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs * rhs.mValue);
+        }
+
+        public static TResult? operator*<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T * TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue * rhs);
+        }
+
+        public static TResult? operator*<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T * TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue * rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator/<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther / T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs / rhs.mValue);
+        }
+
+        public static TResult? operator/<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T / TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue / rhs);
+        }
+
+        public static TResult? operator/<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T / TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue / rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator%<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther % T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs % rhs.mValue);
+        }
+
+        public static TResult? operator%<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T % TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue % rhs);
+        }
+
+        public static TResult? operator%<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T % TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue % rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator^<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther ^ T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs ^ rhs.mValue);
+        }
+
+        public static TResult? operator^<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T ^ TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue ^ rhs);
+        }
+
+        public static TResult? operator^<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T ^ TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue ^ rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator&<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther & T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs & rhs.mValue);
+        }
+
+        public static TResult? operator&<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T & TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue & rhs);
+        }
+
+        public static TResult? operator&<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T & TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue & rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator|<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther | T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs | rhs.mValue);
+        }
+
+        public static TResult? operator|<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T | TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue | rhs);
+        }
+
+        public static TResult? operator|<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T | TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue | rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator??<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther ?? T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs ?? rhs.mValue);
+        }
+
+        public static TResult? operator??<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T ?? TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue ?? rhs);
+        }
+
+        public static TResult? operator??<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T ?? TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue ?? rhs.mValue);
+        }
+
+        //
+
+        public static TResult? operator<< <TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther << T where TResult : struct
+        {
+            if (!rhs.mHasValue) return null;
+            return .(lhs << rhs.mValue);
+        }
+
+        public static TResult? operator<< <TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T << TOther where TResult : struct
+        {
+            if (!lhs.mHasValue) return null;
+            return .(lhs.mValue << rhs);
+        }
+
+        public static TResult? operator<< <TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T << TOther where TResult : struct
+        {
+            if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+            return .(lhs.mValue << rhs.mValue);
+        }
+    }
+
+    extension Nullable<T> : IHashable where T : IHashable
+    {
+        public int GetHashCode()
+        {
+            if (!mHasValue)
+                return 0;
+            return mValue.GetHashCode();
+        }
     }
     }
 }
 }

+ 2 - 2
IDE/dist/BeefDbgVis.toml

@@ -231,8 +231,8 @@ Name = "[Count]"
 Value = "mCount - mFreeCount"
 Value = "mCount - mFreeCount"
 [Type.Expand.DictionaryItems]
 [Type.Expand.DictionaryItems]
 Size = "mCount - mFreeCount"
 Size = "mCount - mFreeCount"
-Buckets = "&mBuckets.mFirstElement"
-Entries = "&mEntries.mFirstElement"
+Buckets = "mBuckets"
+Entries = "mEntries"
 Key = "mKey"
 Key = "mKey"
 Value = "mValue"
 Value = "mValue"
 Next = "mNext"
 Next = "mNext"

+ 45 - 0
IDE/mintest/minlib/src/System/IComparable.bf

@@ -25,11 +25,56 @@ namespace System
 		static int operator<=>(Self lhs, Self rhs);
 		static int operator<=>(Self lhs, Self rhs);
 	}
 	}
 
 
+	interface IOpComparable<TRight>
+	{
+		static int operator<=>(Self lhs, TRight rhs);
+	}
+
 	interface IOpAddable
 	interface IOpAddable
 	{
 	{
 		static Self operator+(Self lhs, Self rhs);
 		static Self operator+(Self lhs, Self rhs);
 	}
 	}
 
 
+	interface IOpSubtractable
+	{
+		static Self operator-(Self lhs, Self rhs);
+	}
+
+	interface IOpMultipliable
+	{
+		static Self operator*(Self lhs, Self rhs);
+	}
+
+	interface IOpDividable
+	{
+		static Self operator/(Self lhs, Self rhs);
+	}
+
+	interface IOpBitwiseAndable
+	{
+		static Self operator&(Self lhs, Self rhs);
+	}
+
+	interface IOpBitwiseOrable
+	{
+		static Self operator|(Self lhs, Self rhs);
+	}
+
+	interface IOpExclusiveOrable
+	{
+		static Self operator^(Self lhs, Self rhs);
+	}
+
+	interface IOpLeftShiftable
+	{
+		static Self operator^(Self lhs, int rhs);
+	}
+
+	interface IOpRightShiftable
+	{
+		static Self operator^(Self lhs, int rhs);
+	}
+
 	interface IOpNegatable
 	interface IOpNegatable
 	{									 
 	{									 
 		static Self operator-(Self value);
 		static Self operator-(Self value);

+ 355 - 32
IDE/mintest/minlib/src/System/Nullable.bf

@@ -4,7 +4,7 @@ using System.Diagnostics;
 
 
 namespace System
 namespace System
 {
 {
-    public struct Nullable<T> where T : struct
+    struct Nullable<T> where T : struct
     {
     {
 		internal T mValue;
 		internal T mValue;
         internal bool mHasValue;
         internal bool mHasValue;
@@ -49,35 +49,6 @@ namespace System
 		    }
 		    }
 		}
 		}
         
         
-        /*public override bool Equals(Object other)
-        {
-            if (other == null)
-                return mHasValue == false;
-            if (!(other is Nullable<T>))
-                return false;
-            
-            return Equals((Nullable<T>)other);
-        }*/
-        
-        /*bool Equals(Nullable<T> other)
-        {
-            if (other.mHasValue != mHasValue)
-                return false;
-            
-            if (mHasValue == false)
-                return true;
-            
-            return other.mValue.Equals(mValue);
-        }*/
-        
-        /*public override int GetHashCode()
-        {
-            if (!mHasValue)
-                return 0;
-            
-            return mValue.GetHashCode();
-        }*/
-        
         public T GetValueOrDefault()
         public T GetValueOrDefault()
         {
         {
             return mValue;
             return mValue;
@@ -96,16 +67,368 @@ namespace System
                 str.Clear();
                 str.Clear();
         }
         }
 
 
-		//[Inline]
+		[Inline]
         public static implicit operator Nullable<T>(T value)
         public static implicit operator Nullable<T>(T value)
         {
         {
             return Nullable<T>(value);
             return Nullable<T>(value);
         }
         }
 
 
-		//[Inline]
+		[Inline]
         public static explicit operator T(Nullable<T> value)
         public static explicit operator T(Nullable<T> value)
         {
         {
             return value.mValue;
             return value.mValue;
         }
         }
+
+		[Inline]
+		public static bool operator==(Nullable<T> lhs, T rhs)
+		{
+			if (!lhs.mHasValue) return false;
+			return lhs.mValue == rhs;
+		}
+
+		///
+
+		public static bool operator==<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T == TOther
+		{
+			if (!lhs.mHasValue) return false;
+			return lhs.mValue == rhs;
+		}
+
+		public static bool operator==<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther == T
+		{
+			if (!rhs.mHasValue) return false;
+			return lhs == rhs;
+		}
+
+		public static bool operator==<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T == TOther where TOther : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+			return lhs.mValue == rhs.mValue;
+		}
+
+		///
+
+		public static bool operator!=<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T != TOther
+		{
+			if (!lhs.mHasValue) return false;
+			return lhs.mValue != rhs;
+		}
+
+		public static bool operator!=<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther != T
+		{
+			if (!rhs.mHasValue) return false;
+			return lhs != rhs;
+		}
+
+		public static bool operator!=<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T != TOther where TOther : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+			return lhs.mValue != rhs.mValue;
+		}
+
+		///
+
+		public static bool operator< <TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T < TOther
+		{
+			if (!lhs.mHasValue) return false;
+			return lhs.mValue < rhs;
+		}
+
+		public static bool operator< <TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther < T
+		{
+			if (!rhs.mHasValue) return false;
+			return lhs < rhs;
+		}
+
+		public static bool operator< <TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T < TOther where TOther : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+			return lhs.mValue < rhs.mValue;
+		}
+
+		///
+
+		public static bool operator<=<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T <= TOther
+		{
+			if (!lhs.mHasValue) return false;
+			return lhs.mValue <= rhs;
+		}
+
+		public static bool operator<=<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther <= T
+		{
+			if (!rhs.mHasValue) return false;
+			return lhs <= rhs;
+		}
+
+		public static bool operator<=<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T <= TOther where TOther : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+			return lhs.mValue <= rhs.mValue;
+		}
+
+		///
+
+		public static bool operator><TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T > TOther
+		{
+			if (!lhs.mHasValue) return false;
+			return lhs.mValue > rhs;
+		}
+
+		public static bool operator><TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther > T
+		{
+			if (!rhs.mHasValue) return false;
+			return lhs > rhs;
+		}
+
+		public static bool operator><TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T > TOther where TOther : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+			return lhs.mValue > rhs.mValue;
+		}
+
+		///
+
+		public static bool operator>=<TOther>(Nullable<T> lhs, TOther rhs) where bool : operator T >= TOther
+		{
+			if (!lhs.mHasValue) return false;
+			return lhs.mValue >= rhs;
+		}
+
+		public static bool operator>=<TOther>(TOther lhs, Nullable<T> rhs) where bool : operator TOther >= T
+		{
+			if (!rhs.mHasValue) return false;
+			return lhs >= rhs;
+		}
+
+		public static bool operator>=<TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where bool : operator T >= TOther where TOther : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return false;
+			return lhs.mValue >= rhs.mValue;
+		}
+
+		///
+
+		public static int operator<=><TOther>(Nullable<T> lhs, TOther rhs) where int : operator T <=> TOther
+		{
+			return lhs.mValue <=> rhs;
+		}
+
+		public static int operator<=><TOther>(TOther lhs, Nullable<T> rhs) where int : operator TOther <=> T
+		{
+			return lhs <=> rhs;
+		}
+
+		public static int operator<=><TOther>(Nullable<T> lhs, Nullable<TOther> rhs) where int : operator T <=> TOther where TOther : struct
+		{
+			return lhs.mValue <=> rhs.mValue;
+		}
+
+		///
+
+		public static TResult? operator+<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T + TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue + rhs);
+		}
+		public static TResult? operator+<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther + T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs + rhs.mValue);
+		}
+		public static TResult? operator+<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T + TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue + rhs.mValue);
+		}
+
+		///
+
+		public static TResult? operator-<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther - T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs - rhs.mValue);
+		}
+
+		public static TResult? operator-<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T - TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue - rhs);
+		}
+
+		public static TResult? operator-<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T - TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue - rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator*<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther * T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs * rhs.mValue);
+		}
+
+		public static TResult? operator*<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T * TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue * rhs);
+		}
+
+		public static TResult? operator*<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T * TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue * rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator/<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther / T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs / rhs.mValue);
+		}
+
+		public static TResult? operator/<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T / TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue / rhs);
+		}
+
+		public static TResult? operator/<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T / TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue / rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator%<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther % T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs % rhs.mValue);
+		}
+
+		public static TResult? operator%<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T % TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue % rhs);
+		}
+
+		public static TResult? operator%<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T % TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue % rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator^<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther ^ T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs ^ rhs.mValue);
+		}
+
+		public static TResult? operator^<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T ^ TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue ^ rhs);
+		}
+
+		public static TResult? operator^<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T ^ TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue ^ rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator&<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther & T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs & rhs.mValue);
+		}
+
+		public static TResult? operator&<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T & TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue & rhs);
+		}
+
+		public static TResult? operator&<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T & TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue & rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator|<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther | T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs | rhs.mValue);
+		}
+
+		public static TResult? operator|<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T | TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue | rhs);
+		}
+
+		public static TResult? operator|<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T | TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue | rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator??<TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther ?? T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs ?? rhs.mValue);
+		}
+
+		public static TResult? operator??<TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T ?? TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue ?? rhs);
+		}
+
+		public static TResult? operator??<TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T ?? TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue ?? rhs.mValue);
+		}
+
+		//
+
+		public static TResult? operator<< <TOther, TResult>(TOther lhs, Nullable<T> rhs) where TResult = operator TOther << T where TResult : struct
+		{
+			if (!rhs.mHasValue) return null;
+			return .(lhs << rhs.mValue);
+		}
+
+		public static TResult? operator<< <TOther, TResult>(Nullable<T> lhs, TOther rhs) where TResult = operator T << TOther where TResult : struct
+		{
+			if (!lhs.mHasValue) return null;
+			return .(lhs.mValue << rhs);
+		}
+
+		public static TResult? operator<< <TOther, TResult>(Nullable<T> lhs, Nullable<TOther> rhs) where TOther : struct where TResult = operator T << TOther where TResult : struct
+		{
+			if ((!lhs.mHasValue) || (!rhs.mHasValue)) return null;
+			return .(lhs.mValue << rhs.mValue);
+		}
     }
     }
+
+	extension Nullable<T> : IHashable where T : IHashable
+	{
+		public int GetHashCode()
+		{
+			if (!mHasValue)
+				return 0;
+			return mValue.GetHashCode();
+		}
+	}
 }
 }

+ 1 - 1
IDE/src/ui/SourceEditWidgetContent.bf

@@ -1997,7 +1997,7 @@ namespace IDE.ui
 
 
 		public bool ToggleComment()
 		public bool ToggleComment()
 		{
 		{
-		    if (HasSelection())
+		    if ((HasSelection()) && (mSelection.Value.Length > 1))
 		    {
 		    {
 				var startLineAndCol = CursorLineAndColumn ;
 				var startLineAndCol = CursorLineAndColumn ;
 
 

+ 1 - 0
IDEHelper/Compiler/BfContext.cpp

@@ -46,6 +46,7 @@ BfContext::BfContext(BfCompiler* compiler) :
 	mLockModules = false;
 	mLockModules = false;
 
 
 	mCurTypeState = NULL;
 	mCurTypeState = NULL;
+	mCurConstraintState = NULL;
 	mResolvingVarField = false;
 	mResolvingVarField = false;
 	
 	
 	for (int i = 0; i < BfTypeCode_Length; i++)
 	for (int i = 0; i < BfTypeCode_Length; i++)

+ 28 - 1
IDEHelper/Compiler/BfContext.h

@@ -272,6 +272,32 @@ public:
 	}
 	}
 };
 };
 
 
+class BfConstraintState
+{
+public:
+	BfGenericParamInstance* mGenericParamInstance;
+	BfType* mLeftType;
+	BfType* mRightType;
+	BfConstraintState* mPrevState;
+
+public:
+	BfConstraintState()
+	{
+		mGenericParamInstance = NULL;
+		mLeftType = NULL;
+		mRightType = NULL;
+		mPrevState = NULL;
+	}
+
+	bool operator==(const BfConstraintState& other) const
+	{
+		return 
+			(mGenericParamInstance == other.mGenericParamInstance) &&
+			(mLeftType == other.mLeftType) &&
+			(mRightType == other.mRightType);
+	}
+};
+
 class BfContext
 class BfContext
 {
 {
 public:
 public:
@@ -279,6 +305,7 @@ public:
 	bool mDeleting;
 	bool mDeleting;
 
 
 	BfTypeState* mCurTypeState;
 	BfTypeState* mCurTypeState;
+	BfConstraintState* mCurConstraintState;
 	bool mResolvingVarField;
 	bool mResolvingVarField;
 	int mMappedObjectRevision;
 	int mMappedObjectRevision;
 
 
@@ -364,7 +391,7 @@ public:
 	void SaveDeletingType(BfType* type);		
 	void SaveDeletingType(BfType* type);		
 	BfType* FindType(const StringImpl& typeName);
 	BfType* FindType(const StringImpl& typeName);
 	String TypeIdToString(int typeId);
 	String TypeIdToString(int typeId);
-	BfHotTypeData* GetHotTypeData(int typeId);
+	BfHotTypeData* GetHotTypeData(int typeId);	
 
 
 public:
 public:
 	BfContext(BfCompiler* compiler);
 	BfContext(BfCompiler* compiler);

+ 15 - 3
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -1030,6 +1030,15 @@ bool BfMethodMatcher::InferFromGenericConstraints(BfGenericParamInstance* generi
 		if (rightType != NULL)
 		if (rightType != NULL)
 			rightType = mModule->FixIntUnknown(rightType);
 			rightType = mModule->FixIntUnknown(rightType);
 
 
+		BfConstraintState constraintSet;
+		constraintSet.mPrevState = mModule->mContext->mCurConstraintState;
+		constraintSet.mGenericParamInstance = genericParamInst;
+		constraintSet.mLeftType = leftType;
+		constraintSet.mRightType = rightType;
+		SetAndRestoreValue<BfConstraintState*> prevConstraintSet(mModule->mContext->mCurConstraintState, &constraintSet);
+		if (!mModule->CheckConstraintState(NULL))
+			return false;
+
 		if (checkOpConstraint.mBinaryOp != BfBinaryOp_None)
 		if (checkOpConstraint.mBinaryOp != BfBinaryOp_None)
 		{
 		{
 			BfExprEvaluator exprEvaluator(mModule);
 			BfExprEvaluator exprEvaluator(mModule);
@@ -3374,14 +3383,14 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar
 					return mModule->GetDefaultTypedValue(resolvedFieldType);
 					return mModule->GetDefaultTypedValue(resolvedFieldType);
 				}
 				}
 
 
-				bool isTemporary = false;
+				bool isTemporary = target.IsTempAddr();
 				bool wantsLoadValue = false;
 				bool wantsLoadValue = false;
 				if ((field->mIsReadOnly) && ((mModule->mCurMethodInstance->mMethodDef->mMethodType != BfMethodType_Ctor) || (!target.IsThis())))
 				if ((field->mIsReadOnly) && ((mModule->mCurMethodInstance->mMethodDef->mMethodType != BfMethodType_Ctor) || (!target.IsThis())))
 					wantsLoadValue = true;					
 					wantsLoadValue = true;					
 										
 										
 				bool isComposite = target.mType->IsComposite();
 				bool isComposite = target.mType->IsComposite();
 				if ((isComposite) && (!target.mType->IsTypedPrimitive()) && (!target.IsAddr()))
 				if ((isComposite) && (!target.mType->IsTypedPrimitive()) && (!target.IsAddr()))
-					isTemporary = true;					
+					isTemporary = true;	
 				if ((isComposite) && (!target.IsAddr()))
 				if ((isComposite) && (!target.IsAddr()))
 					wantsLoadValue = true;
 					wantsLoadValue = true;
 
 
@@ -4005,7 +4014,10 @@ BfTypedValue BfExprEvaluator::CreateCall(BfMethodInstance* methodInstance, BfIRV
 		}
 		}
 		else
 		else
 		{						
 		{						
-			return mModule->GetDefaultTypedValue(returnType, true, returnType->IsComposite() ? BfDefaultValueKind_Addr : BfDefaultValueKind_Value);
+			auto val = mModule->GetDefaultTypedValue(returnType, true, methodInstance->HasStructRet() ? BfDefaultValueKind_Addr : BfDefaultValueKind_Value);
+			if (val.mKind == BfTypedValueKind_Addr)
+				val.mKind = BfTypedValueKind_TempAddr;
+			return val;
 		}
 		}
 	};
 	};
 
 

+ 5 - 0
IDEHelper/Compiler/BfIRBuilder.cpp

@@ -2955,6 +2955,11 @@ void BfIRBuilder::CreateTypeDefinition(BfType* type, bool forceDefine)
 	if (!typeInstance->IsTypedPrimitive())
 	if (!typeInstance->IsTypedPrimitive())
 		StructSetBody(MapTypeInst(typeInstance), irFieldTypes, isPacked || !isCRepr);
 		StructSetBody(MapTypeInst(typeInstance), irFieldTypes, isPacked || !isCRepr);
 
 
+	if (typeInstance->IsNullable())
+	{
+		BF_ASSERT(irFieldTypes.size() <= 3);
+	}
+
 	for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
 	for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
 	{
 	{
 		auto fieldInstance = &fieldInstanceRef;
 		auto fieldInstance = &fieldInstanceRef;

+ 12 - 4
IDEHelper/Compiler/BfModule.cpp

@@ -6691,7 +6691,16 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 			rightType = ResolveGenericType(rightType, *methodGenericArgs);
 			rightType = ResolveGenericType(rightType, *methodGenericArgs);
 		if (rightType != NULL)
 		if (rightType != NULL)
 			rightType = FixIntUnknown(rightType);
 			rightType = FixIntUnknown(rightType);
-		
+
+		BfConstraintState constraintSet;
+		constraintSet.mPrevState = mContext->mCurConstraintState;
+		constraintSet.mGenericParamInstance = genericParamInst;
+		constraintSet.mLeftType = leftType;
+		constraintSet.mRightType = rightType;
+		SetAndRestoreValue<BfConstraintState*> prevConstraintSet(mContext->mCurConstraintState, &constraintSet);
+		if (!CheckConstraintState(NULL))
+			return false;
+
 		if (checkOpConstraint.mBinaryOp != BfBinaryOp_None)
 		if (checkOpConstraint.mBinaryOp != BfBinaryOp_None)
 		{						
 		{						
 			BfExprEvaluator exprEvaluator(this);						
 			BfExprEvaluator exprEvaluator(this);						
@@ -10585,10 +10594,9 @@ bool BfModule::CheckModifyValue(BfTypedValue& typedValue, BfAstNode* refNode, co
 		return false;
 		return false;
 	}
 	}
 
 
-
 	if (typedValue.mKind == BfTypedValueKind_TempAddr)
 	if (typedValue.mKind == BfTypedValueKind_TempAddr)
 	{
 	{
-		Fail(StrFormat("Cannot %s value", modifyType), refNode);
+		Fail(StrFormat("Cannot %s temporary value", modifyType), refNode);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -14224,7 +14232,7 @@ void BfModule::EmitCtorBody(bool& skipBody)
 					}
 					}
 
 
 					BfIRValue fieldAddr;
 					BfIRValue fieldAddr;
-					if (!mCurTypeInstance->IsTypedPrimitive())
+					if ((!mCurTypeInstance->IsTypedPrimitive()) && (!fieldInst->mResolvedType->IsVar()))
 					{
 					{
 						fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx /*, fieldDef->mName*/);
 						fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx /*, fieldDef->mName*/);
 					}
 					}

+ 3 - 3
IDEHelper/Compiler/BfModule.h

@@ -1443,8 +1443,7 @@ public:
 	void AddDeferredBlock(BfBlock* block, BfScopeData* scope, Array<BfDeferredCapture>* captures = NULL);
 	void AddDeferredBlock(BfBlock* block, BfScopeData* scope, Array<BfDeferredCapture>* captures = NULL);
 	BfDeferredCallEntry* AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl<BfIRValue>& llvmArgs, BfScopeData* scope, BfAstNode* srcNode = NULL, bool bypassVirtual = false, bool doNullCheck = false);	
 	BfDeferredCallEntry* AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl<BfIRValue>& llvmArgs, BfScopeData* scope, BfAstNode* srcNode = NULL, bool bypassVirtual = false, bool doNullCheck = false);	
 	void EmitDeferredCall(BfDeferredCallEntry& deferredCallEntry);
 	void EmitDeferredCall(BfDeferredCallEntry& deferredCallEntry);
-	void EmitDeferredCallProcessor(SLIList<BfDeferredCallEntry*>& callEntries, BfIRValue callTail);
-	bool DoCanImplicitlyCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags = BfCastFlags_None);
+	void EmitDeferredCallProcessor(SLIList<BfDeferredCallEntry*>& callEntries, BfIRValue callTail);	
 	bool CanImplicitlyCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags = BfCastFlags_None);
 	bool CanImplicitlyCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags = BfCastFlags_None);
 	bool AreSplatsCompatible(BfType* fromType, BfType* toType, bool* outNeedsMemberCasting);
 	bool AreSplatsCompatible(BfType* fromType, BfType* toType, bool* outNeedsMemberCasting);
 	BfTypedValue BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType /*Can be System.Object or interface*/, const BfAllocTarget& allocTarget, bool callDtor = true);
 	BfTypedValue BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType /*Can be System.Object or interface*/, const BfAllocTarget& allocTarget, bool callDtor = true);
@@ -1458,7 +1457,7 @@ public:
 	void CleanupFileInstances();
 	void CleanupFileInstances();
 	void AssertErrorState();
 	void AssertErrorState();
 	void AssertParseErrorState();
 	void AssertParseErrorState();
-	void InitTypeInst(BfTypedValue typedValue, BfScopeData* scope, bool zeroMemory, BfIRValue dataSize);	
+	void InitTypeInst(BfTypedValue typedValue, BfScopeData* scope, bool zeroMemory, BfIRValue dataSize);		
 	BfIRValue AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTarget, BfType* type, BfIRValue sizeValue, BfIRValue alignValue, BfAllocFlags allocFlags/*bool zeroMemory, bool defaultToMalloc*/);
 	BfIRValue AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTarget, BfType* type, BfIRValue sizeValue, BfIRValue alignValue, BfAllocFlags allocFlags/*bool zeroMemory, bool defaultToMalloc*/);
 	BfIRValue GetMarkFuncPtr(BfType* type);
 	BfIRValue GetMarkFuncPtr(BfType* type);
 	BfIRValue GetDbgRawAllocData(BfType* type);
 	BfIRValue GetDbgRawAllocData(BfType* type);
@@ -1542,6 +1541,7 @@ public:
 	bool BuildGenericParams(BfType* resolvedTypeRef);
 	bool BuildGenericParams(BfType* resolvedTypeRef);
 	bool ValidateGenericConstraints(BfTypeReference* typeRef, BfGenericTypeInstance* genericTypeInstance, bool ignoreErrors);
 	bool ValidateGenericConstraints(BfTypeReference* typeRef, BfGenericTypeInstance* genericTypeInstance, bool ignoreErrors);
 	bool AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGenericParamInstance* checkOuter);
 	bool AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGenericParamInstance* checkOuter);
+	bool CheckConstraintState(BfAstNode* refNode);
 	bool ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef);
 	bool ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef);
 	void CheckInjectNewRevision(BfTypeInstance* typeInstance);
 	void CheckInjectNewRevision(BfTypeInstance* typeInstance);
 	bool InitType(BfType* resolvedTypeRef, BfPopulateType populateType);
 	bool InitType(BfType* resolvedTypeRef, BfPopulateType populateType);

+ 24 - 0
IDEHelper/Compiler/BfModuleTypeUtils.cpp

@@ -275,6 +275,30 @@ bool BfModule::AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGeneri
 	return true;
 	return true;
 }
 }
 
 
+bool BfModule::CheckConstraintState(BfAstNode* refNode)
+{
+	if (mContext->mCurConstraintState == NULL)
+		return true;
+
+	auto checkState = mContext->mCurConstraintState->mPrevState;
+	while (checkState != NULL)
+	{
+		if (*checkState == *mContext->mCurConstraintState)
+		{
+			if (refNode != NULL)
+			{
+				Fail("Constraints cause circular operator invocations", refNode);
+			}
+
+			return false;
+		}
+
+		checkState = checkState->mPrevState;
+	}
+
+	return true;
+}
+
 bool BfModule::ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef)
 bool BfModule::ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef)
 {
 {
 	if (firstDeclaringTypeDef == secondDeclaringTypeDef)
 	if (firstDeclaringTypeDef == secondDeclaringTypeDef)

+ 57 - 42
IDEHelper/Compiler/BfReducer.cpp

@@ -4371,10 +4371,12 @@ BfTypeReference* BfReducer::DoCreateNamedTypeRef(BfIdentifierNode* identifierNod
 	}
 	}
 }
 }
 
 
-BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, bool parseArrayBracket)
+BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefFlags createTypeRefFlags)
 {
 {
 	AssertCurrentNode(firstNode);
 	AssertCurrentNode(firstNode);
 
 
+	bool parseArrayBracket = (createTypeRefFlags & CreateTypeRefFlags_NoParseArrayBrackets) == 0;
+
 	auto identifierNode = BfNodeDynCast<BfIdentifierNode>(firstNode);
 	auto identifierNode = BfNodeDynCast<BfIdentifierNode>(firstNode);
 	if (identifierNode == NULL)
 	if (identifierNode == NULL)
 	{
 	{
@@ -4475,7 +4477,7 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, bool parseArra
 					}
 					}
 					else
 					else
 					{
 					{
-						auto elementType = CreateTypeRefAfter(tokenNode, parseArrayBracket);
+						auto elementType = CreateTypeRefAfter(tokenNode, createTypeRefFlags);
 						auto constTypeRef = mAlloc->Alloc<BfConstTypeRef>();
 						auto constTypeRef = mAlloc->Alloc<BfConstTypeRef>();
 						ReplaceNode(firstNode, constTypeRef);
 						ReplaceNode(firstNode, constTypeRef);
 						MEMBER_SET(constTypeRef, mConstToken, tokenNode);
 						MEMBER_SET(constTypeRef, mConstToken, tokenNode);
@@ -4486,7 +4488,7 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, bool parseArra
 				else if (token == BfToken_Unsigned)
 				else if (token == BfToken_Unsigned)
 				{
 				{
 					BF_ASSERT(mCompatMode);
 					BF_ASSERT(mCompatMode);
-					auto elementType = CreateTypeRefAfter(tokenNode, parseArrayBracket);
+					auto elementType = CreateTypeRefAfter(tokenNode, createTypeRefFlags);
 
 
 					BfTypeReference* rootElementParent = NULL;
 					BfTypeReference* rootElementParent = NULL;
 					auto rootElement = elementType;
 					auto rootElement = elementType;
@@ -4525,7 +4527,7 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, bool parseArra
 					tokenNode = ExpectTokenAfter(retTypeTypeRef, BfToken_LParen);
 					tokenNode = ExpectTokenAfter(retTypeTypeRef, BfToken_LParen);
 					MEMBER_SET_CHECKED(retTypeTypeRef, mOpenParen, tokenNode);
 					MEMBER_SET_CHECKED(retTypeTypeRef, mOpenParen, tokenNode);
 
 
-					auto elementType = CreateTypeRefAfter(retTypeTypeRef, parseArrayBracket);
+					auto elementType = CreateTypeRefAfter(retTypeTypeRef, createTypeRefFlags);
 					MEMBER_SET_CHECKED(retTypeTypeRef, mElementType, elementType);
 					MEMBER_SET_CHECKED(retTypeTypeRef, mElementType, elementType);
 
 
 					tokenNode = ExpectTokenAfter(retTypeTypeRef, BfToken_RParen);
 					tokenNode = ExpectTokenAfter(retTypeTypeRef, BfToken_RParen);
@@ -4811,32 +4813,6 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, bool parseArra
 						}
 						}
 						MoveNode(sizeExpr, arrayType);
 						MoveNode(sizeExpr, arrayType);
 						params.push_back(sizeExpr);
 						params.push_back(sizeExpr);
-
-						// 						if (params.size() == 0)
-						// 						{
-						// 							BfExpression* sizeExpr = CreateExpressionAfter(arrayType);
-						// 							if (sizeExpr == NULL)
-						// 							{
-						// 								hasFailed = true;
-						// 								break;								
-						// 							}
-						// 							MoveNode(sizeExpr, arrayType);
-						// 							params.push_back(sizeExpr);
-						// 							tokenNode = ExpectTokenAfter(arrayType, BfToken_RBracket);
-						// 							if (tokenNode == NULL)
-						// 							{								
-						// 								hasFailed = true;
-						// 								break;
-						// 							}
-						// 							MoveNode(tokenNode, arrayType);
-						// 							arrayType->mCloseBracket = tokenNode;
-						// 							break;
-						// 						}
-						// 						else
-						// 						{							
-						// 							FailAfter("Either ',' or ']' expected", arrayType);
-						// 							return arrayType;							
-						// 						}
 					}
 					}
 				}
 				}
 
 
@@ -4954,8 +4930,34 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, bool parseArra
 	return typeRef;
 	return typeRef;
 }
 }
 
 
-BfTypeReference* BfReducer::CreateTypeRef(BfAstNode* firstNode, bool parseArrayBracket)
+BfTypeReference* BfReducer::CreateTypeRef(BfAstNode* firstNode, CreateTypeRefFlags createTypeRefFlags)
 {
 {
+	if ((createTypeRefFlags & CreateTypeRefFlags_SafeGenericParse) != 0)
+	{
+		createTypeRefFlags = (CreateTypeRefFlags)(createTypeRefFlags & ~CreateTypeRefFlags_SafeGenericParse);
+		
+		int outEndNode = -1;
+		bool isTypeRef = IsTypeReference(firstNode, BfToken_None, &outEndNode);
+		
+		if ((!isTypeRef) && (outEndNode != -1))
+		{
+			for (int checkIdx = outEndNode - 1; checkIdx > mVisitorPos.mReadPos; checkIdx--)
+			{
+				auto checkNode = mVisitorPos.Get(checkIdx);
+				if (auto checkToken = BfNodeDynCast<BfTokenNode>(checkNode))
+				{
+					if (checkToken->mToken == BfToken_LChevron)
+					{
+						checkToken->mToken = BfToken_Bar;
+						auto typeRef = CreateTypeRef(firstNode, createTypeRefFlags);
+						checkToken->mToken = BfToken_LChevron;
+						return typeRef;
+					}
+				}
+			}
+		}		
+	}
+
 	if (auto tokenNode = BfNodeDynCast<BfTokenNode>(firstNode))
 	if (auto tokenNode = BfNodeDynCast<BfTokenNode>(firstNode))
 	{
 	{
 		BfToken token = tokenNode->GetToken();
 		BfToken token = tokenNode->GetToken();
@@ -4963,7 +4965,7 @@ BfTypeReference* BfReducer::CreateTypeRef(BfAstNode* firstNode, bool parseArrayB
 		{
 		{
 			auto nextNode = mVisitorPos.GetNext();
 			auto nextNode = mVisitorPos.GetNext();
 			mVisitorPos.MoveNext();
 			mVisitorPos.MoveNext();
-			auto typeRef = DoCreateTypeRef(nextNode, parseArrayBracket);
+			auto typeRef = DoCreateTypeRef(nextNode, createTypeRefFlags);
 			if (typeRef == NULL)
 			if (typeRef == NULL)
 			{				
 			{				
 				mVisitorPos.mReadPos--;
 				mVisitorPos.mReadPos--;
@@ -4975,13 +4977,13 @@ BfTypeReference* BfReducer::CreateTypeRef(BfAstNode* firstNode, bool parseArrayB
 		else if ((token == BfToken_In) || (token == BfToken_As))
 		else if ((token == BfToken_In) || (token == BfToken_As))
 		{
 		{
 			// This is mostly to allow a partially typed 'int' to be parsed as 'in' to make autocomplete nicer
 			// This is mostly to allow a partially typed 'int' to be parsed as 'in' to make autocomplete nicer
-			return CreateTypeRef(ReplaceTokenStarter(firstNode), parseArrayBracket);
+			return CreateTypeRef(ReplaceTokenStarter(firstNode), createTypeRefFlags);
 		}
 		}
 	}
 	}
-	return DoCreateTypeRef(firstNode, parseArrayBracket);
+	return DoCreateTypeRef(firstNode, createTypeRefFlags);
 }
 }
 
 
-BfTypeReference* BfReducer::CreateTypeRefAfter(BfAstNode* astNode, bool parseArrayBracket)
+BfTypeReference* BfReducer::CreateTypeRefAfter(BfAstNode* astNode, CreateTypeRefFlags createTypeRefFlags)
 {
 {
 	AssertCurrentNode(astNode);
 	AssertCurrentNode(astNode);
 	auto nextNode = mVisitorPos.GetNext();
 	auto nextNode = mVisitorPos.GetNext();
@@ -4993,7 +4995,7 @@ BfTypeReference* BfReducer::CreateTypeRefAfter(BfAstNode* astNode, bool parseArr
 
 
 	mVisitorPos.MoveNext();
 	mVisitorPos.MoveNext();
 	int startPos = mVisitorPos.mReadPos;
 	int startPos = mVisitorPos.mReadPos;
-	BfTypeReference* typeRef = CreateTypeRef(nextNode, parseArrayBracket);
+	BfTypeReference* typeRef = CreateTypeRef(nextNode, createTypeRefFlags);
 	if (typeRef == NULL)
 	if (typeRef == NULL)
 	{
 	{
 		BF_ASSERT(mVisitorPos.mReadPos == startPos);
 		BF_ASSERT(mVisitorPos.mReadPos == startPos);
@@ -9010,18 +9012,31 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration(
 
 
 						auto opToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
 						auto opToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
 						if (opToken == NULL)
 						if (opToken == NULL)
-						{
-							auto typeRef = CreateTypeRefAfter(opConstraint);
+						{							
+							auto typeRef = CreateTypeRefAfter(opConstraint, BfReducer::CreateTypeRefFlags_SafeGenericParse);
 							if (typeRef == NULL)
 							if (typeRef == NULL)
 								break;
 								break;
 							MEMBER_SET(opConstraint, mLeftType, typeRef);
 							MEMBER_SET(opConstraint, mLeftType, typeRef);
 							opToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
 							opToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
+
+							if (opToken == NULL)
+							{
+								if (auto pointerTypeRef = BfNodeDynCast<BfPointerTypeRef>(typeRef))
+								{
+									MEMBER_SET(opConstraint, mLeftType, pointerTypeRef->mElementType);
+									opToken = pointerTypeRef->mStarNode;
+									MEMBER_SET(opConstraint, mOpToken, opToken);
+								}
+							}
 						}
 						}
 
 
-						if (opToken == NULL)
-							break;
-						MEMBER_SET(opConstraint, mOpToken, opToken);
-						mVisitorPos.MoveNext();
+						if (opConstraint->mOpToken == NULL)
+						{
+							if (opToken == NULL)
+								break;
+							MEMBER_SET(opConstraint, mOpToken, opToken);
+							mVisitorPos.MoveNext();
+						}
 
 
 						auto typeRef = CreateTypeRefAfter(opConstraint);
 						auto typeRef = CreateTypeRefAfter(opConstraint);
 						if (typeRef == NULL)
 						if (typeRef == NULL)

+ 10 - 3
IDEHelper/Compiler/BfReducer.h

@@ -40,6 +40,13 @@ public:
 		CreateStmtFlags_To_CreateExprFlags_Mask = 1
 		CreateStmtFlags_To_CreateExprFlags_Mask = 1
 	};
 	};
 
 
+	enum CreateTypeRefFlags
+	{
+		CreateTypeRefFlags_None,
+		CreateTypeRefFlags_NoParseArrayBrackets = 1,
+		CreateTypeRefFlags_SafeGenericParse = 2
+	};
+
 	struct BfVisitorPos
 	struct BfVisitorPos
 	{
 	{
 		BfBlock* mParent;
 		BfBlock* mParent;
@@ -204,9 +211,9 @@ public:
 	bool IsLocalMethod(BfAstNode* nameNode);
 	bool IsLocalMethod(BfAstNode* nameNode);
 	int QualifiedBacktrack(BfAstNode* endNode, int checkIdx, bool* outHadChevrons = NULL); // Backtracks to dot token
 	int QualifiedBacktrack(BfAstNode* endNode, int checkIdx, bool* outHadChevrons = NULL); // Backtracks to dot token
 	BfTypeReference* DoCreateNamedTypeRef(BfIdentifierNode* identifierNode);
 	BfTypeReference* DoCreateNamedTypeRef(BfIdentifierNode* identifierNode);
-	BfTypeReference* DoCreateTypeRef(BfAstNode* identifierNode, bool parseArrayBracket = true);
-	BfTypeReference* CreateTypeRef(BfAstNode* identifierNode, bool parseArrayBracket = true);
-	BfTypeReference* CreateTypeRefAfter(BfAstNode* astNode, bool parseArrayBracket = true);
+	BfTypeReference* DoCreateTypeRef(BfAstNode* identifierNode, CreateTypeRefFlags createTypeRefFlags = CreateTypeRefFlags_None);
+	BfTypeReference* CreateTypeRef(BfAstNode* identifierNode, CreateTypeRefFlags createTypeRefFlags = CreateTypeRefFlags_None);
+	BfTypeReference* CreateTypeRefAfter(BfAstNode* astNode, CreateTypeRefFlags createTypeRefFlags = CreateTypeRefFlags_None);
 	BfTypeReference* CreateRefTypeRef(BfTypeReference* elementType, BfTokenNode* refToken);
 	BfTypeReference* CreateRefTypeRef(BfTypeReference* elementType, BfTokenNode* refToken);
 	BfTypeReference* CreateConstTypeRef(BfTypeReference* elementType, BfTokenNode* refToken);
 	BfTypeReference* CreateConstTypeRef(BfTypeReference* elementType, BfTokenNode* refToken);
 	bool ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayImpl<BfParameterDeclaration*>* params, SizedArrayImpl<BfTokenNode*>* commas, bool alwaysIncludeBlock = false);
 	bool ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayImpl<BfParameterDeclaration*>* params, SizedArrayImpl<BfTokenNode*>* commas, bool alwaysIncludeBlock = false);

+ 22 - 0
IDEHelper/Tests/src/Nullable.bf

@@ -0,0 +1,22 @@
+using System;
+
+namespace Tests
+{
+	class Nullable
+	{
+
+		[Test]
+		public static void TestPrimitives()
+		{
+			float? fn = 9.0f;
+			int? intn = 100;
+			int? intn2 = null;
+
+			let fn2 = fn + intn;
+			Test.Assert(fn2 == 109);
+
+			let fn3 = fn + intn2;
+			Test.Assert(fn3 == null);
+		}
+	}
+}