Selaa lähdekoodia

Added constraints for operators, 'external' constraints for methods

Brian Fiete 5 vuotta sitten
vanhempi
commit
8945a906f7

+ 6 - 0
BeefLibs/corlib/src/Attribute.bf

@@ -340,6 +340,12 @@ namespace System
 		}
 		}
 	}
 	}
 
 
+	[AttributeUsage(.Method)]
+	public struct CommutableAttribute : Attribute
+	{
+
+	}
+
 	[AttributeUsage(.Method | .Constructor)]
 	[AttributeUsage(.Method | .Constructor)]
 	public struct ErrorAttribute : Attribute
 	public struct ErrorAttribute : Attribute
 	{
 	{

+ 164 - 0
IDEHelper/Compiler/BfAst.cpp

@@ -96,6 +96,11 @@ void BfStructuralVisitor::Visit(BfGenericParamsDeclaration* genericParams)
 	Visit(genericParams->ToBase());
 	Visit(genericParams->ToBase());
 }
 }
 
 
+void BfStructuralVisitor::Visit(BfGenericOperatorConstraint* genericConstraints)
+{
+	Visit(genericConstraints->ToBase());
+}
+
 void BfStructuralVisitor::Visit(BfGenericConstraintsDeclaration* genericConstraints)
 void BfStructuralVisitor::Visit(BfGenericConstraintsDeclaration* genericConstraints)
 {
 {
 	Visit(genericConstraints->ToBase());
 	Visit(genericConstraints->ToBase());
@@ -1567,6 +1572,165 @@ const char* Beefy::BfGetOpName(BfUnaryOp unaryOp)
 	}
 	}
 }
 }
 
 
+BfBinaryOp Beefy::BfTokenToBinaryOp(BfToken token)
+{
+	switch (token)
+	{
+	case BfToken_Plus:
+		return BfBinaryOp_Add;
+	case BfToken_Minus:
+		return BfBinaryOp_Subtract;
+	case BfToken_Star:
+		return BfBinaryOp_Multiply;
+	case BfToken_ForwardSlash:
+		return BfBinaryOp_Divide;
+	case BfToken_Modulus:
+		return BfBinaryOp_Modulus;
+	case BfToken_Ampersand:
+		return BfBinaryOp_BitwiseAnd;
+	case BfToken_Bar:
+		return BfBinaryOp_BitwiseOr;
+	case BfToken_Carat:
+		return BfBinaryOp_ExclusiveOr;
+	case BfToken_LDblChevron:
+		return BfBinaryOp_LeftShift;
+	case BfToken_RDblChevron:
+		return BfBinaryOp_RightShift;
+	case BfToken_CompareEquals:
+		return BfBinaryOp_Equality;
+	case BfToken_CompareNotEquals:
+		return BfBinaryOp_InEquality;
+	case BfToken_RChevron:
+		return BfBinaryOp_GreaterThan;
+	case BfToken_LChevron:
+		return BfBinaryOp_LessThan;
+	case BfToken_GreaterEquals:
+		return BfBinaryOp_GreaterThanOrEqual;
+	case BfToken_LessEquals:
+		return BfBinaryOp_LessThanOrEqual;
+	case BfToken_Spaceship:
+		return BfBinaryOp_Compare;
+	case BfToken_DblAmpersand:
+		return BfBinaryOp_ConditionalAnd;
+	case BfToken_DblBar:
+		return BfBinaryOp_ConditionalOr;
+	case BfToken_DblQuestion:
+		return BfBinaryOp_NullCoalesce;
+	default:
+		return BfBinaryOp_None;
+	}
+}
+
+BfUnaryOp Beefy::BfTokenToUnaryOp(BfToken token)
+{
+	switch (token)
+	{
+	case BfToken_Star:
+		return BfUnaryOp_Dereference;
+	case BfToken_Ampersand:
+		return BfUnaryOp_AddressOf;
+	case BfToken_Minus:
+		return BfUnaryOp_Negate;
+	case BfToken_Bang:
+		return BfUnaryOp_Not;
+	case BfToken_Plus:
+		return BfUnaryOp_Positive;
+	case BfToken_Tilde:
+		return BfUnaryOp_InvertBits;
+	case BfToken_DblPlus:
+		return BfUnaryOp_Increment;
+	case BfToken_DblMinus:
+		return BfUnaryOp_Decrement;
+	case BfToken_Ref:
+		return BfUnaryOp_Ref;
+	case BfToken_Mut:
+		return BfUnaryOp_Mut;
+	case BfToken_Out:
+		return BfUnaryOp_Out;
+	case BfToken_Params:
+		return BfUnaryOp_Params;
+	default:
+		return BfUnaryOp_None;
+	}
+}
+
+
+BfAssignmentOp Beefy::BfTokenToAssignmentOp(BfToken token)
+{
+	switch (token)
+	{
+	case BfToken_AssignEquals:
+		return BfAssignmentOp_Assign;
+	case BfToken_PlusEquals:
+		return BfAssignmentOp_Add;
+	case BfToken_MinusEquals:
+		return BfAssignmentOp_Subtract;
+	case BfToken_MultiplyEquals:
+		return BfAssignmentOp_Multiply;
+	case BfToken_DivideEquals:
+		return BfAssignmentOp_Divide;
+	case BfToken_ModulusEquals:
+		return BfAssignmentOp_Modulus;
+	case BfToken_ShiftLeftEquals:
+		return BfAssignmentOp_ShiftLeft;
+	case BfToken_ShiftRightEquals:
+		return BfAssignmentOp_ShiftRight;
+	case BfToken_AndEquals:
+		return BfAssignmentOp_BitwiseAnd;
+	case BfToken_OrEquals:
+		return BfAssignmentOp_BitwiseOr;
+	case BfToken_XorEquals:
+		return BfAssignmentOp_ExclusiveOr;
+	default:
+		return BfAssignmentOp_None;
+	}
+}
+
+BfBinaryOp Beefy::BfGetOppositeBinaryOp(BfBinaryOp origOp)
+{
+	switch (origOp)
+	{
+	case BfBinaryOp_Equality:
+		return BfBinaryOp_InEquality;
+	case BfBinaryOp_InEquality:
+		return BfBinaryOp_Equality;
+	case BfBinaryOp_LessThan:
+		return BfBinaryOp_GreaterThanOrEqual;
+	case BfBinaryOp_LessThanOrEqual:
+		return BfBinaryOp_GreaterThan;
+	case BfBinaryOp_GreaterThan:
+		return BfBinaryOp_LessThanOrEqual;
+	case BfBinaryOp_GreaterThanOrEqual:
+		return BfBinaryOp_LessThan;
+	default: break;
+	}
+
+	return BfBinaryOp_None;
+}
+
+BfBinaryOp Beefy::BfGetFlippedBinaryOp(BfBinaryOp origOp)
+{
+	switch (origOp)
+	{
+	case BfBinaryOp_Equality:
+		return BfBinaryOp_Equality;
+	case BfBinaryOp_InEquality:
+		return BfBinaryOp_InEquality;
+	case BfBinaryOp_LessThan:
+		return BfBinaryOp_GreaterThan;
+	case BfBinaryOp_LessThanOrEqual:
+		return BfBinaryOp_GreaterThanOrEqual;
+	case BfBinaryOp_GreaterThan:
+		return BfBinaryOp_LessThan;
+	case BfBinaryOp_GreaterThanOrEqual:
+		return BfBinaryOp_LessThanOrEqual;
+	default: break;
+	}
+
+	return BfBinaryOp_None;
+}
+
+
 BfInlineAsmInstruction::AsmArg::AsmArg()
 BfInlineAsmInstruction::AsmArg::AsmArg()
 	: mType(ARGTYPE_Immediate)
 	: mType(ARGTYPE_Immediate)
 	, mMemFlags(0)
 	, mMemFlags(0)

+ 22 - 4
IDEHelper/Compiler/BfAst.h

@@ -347,6 +347,7 @@ class BfCollectionInitializerExpression;
 class BfArraySizeSpecifier;
 class BfArraySizeSpecifier;
 class BfSizedArrayCreateExpression;
 class BfSizedArrayCreateExpression;
 class BfEmptyStatement;
 class BfEmptyStatement;
+class BfGenericOperatorConstraint;
 class BfGenericConstraintsDeclaration;
 class BfGenericConstraintsDeclaration;
 class BfAttributeDirective;
 class BfAttributeDirective;
 class BfNullableTypeRef;
 class BfNullableTypeRef;
@@ -409,6 +410,7 @@ public:
 
 
 	virtual void Visit(BfAttributeDirective* attributeDirective);
 	virtual void Visit(BfAttributeDirective* attributeDirective);
 	virtual void Visit(BfGenericParamsDeclaration* genericParams);
 	virtual void Visit(BfGenericParamsDeclaration* genericParams);
+	virtual void Visit(BfGenericOperatorConstraint* genericConstraints);
 	virtual void Visit(BfGenericConstraintsDeclaration* genericConstraints);
 	virtual void Visit(BfGenericConstraintsDeclaration* genericConstraints);
 	virtual void Visit(BfGenericArgumentsNode* genericArgumentsNode);
 	virtual void Visit(BfGenericArgumentsNode* genericArgumentsNode);
 
 
@@ -2762,15 +2764,26 @@ public:
 	BfTokenNode* mRight;
 	BfTokenNode* mRight;
 };	BF_AST_DECL(BfTokenPairNode, BfAstNode);
 };	BF_AST_DECL(BfTokenPairNode, BfAstNode);
 
 
+class BfGenericOperatorConstraint : public BfAstNode
+{
+public:
+	BF_AST_TYPE(BfGenericOperatorConstraint, BfAstNode);
+		
+	BfTokenNode* mOperatorToken;
+	BfTypeReference* mLeftType;
+	BfTokenNode* mOpToken;
+	BfTypeReference* mRightType;
+};  BF_AST_DECL(BfGenericOperatorConstraint, BfAstNode);
+
 class BfGenericConstraint : public BfAstNode
 class BfGenericConstraint : public BfAstNode
 {
 {
 public:
 public:
 	BF_AST_TYPE(BfGenericConstraint, BfAstNode);
 	BF_AST_TYPE(BfGenericConstraint, BfAstNode);
 
 
-	BfTokenNode* mWhereToken;	
-	BfIdentifierNode* mGenericParamName;
+	BfTokenNode* mWhereToken;
+	BfTypeReference* mTypeRef;
 	BfTokenNode* mColonToken;
 	BfTokenNode* mColonToken;
-	BfSizedArray<ASTREF(BfAstNode*)> mConstraintTypes;
+	BfSizedArray<BfAstNode*> mConstraintTypes;
 	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;	
 	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;	
 };	BF_AST_DECL(BfGenericConstraint, BfAstNode);
 };	BF_AST_DECL(BfGenericConstraint, BfAstNode);
 
 
@@ -2778,7 +2791,7 @@ class BfGenericConstraintsDeclaration : public BfAstNode
 {
 {
 public:
 public:
 	BF_AST_TYPE(BfGenericConstraintsDeclaration, BfAstNode);
 	BF_AST_TYPE(BfGenericConstraintsDeclaration, BfAstNode);
-	BfSizedArray<ASTREF(BfGenericConstraint*)> mGenericConstraints;
+	BfSizedArray<BfGenericConstraint*> mGenericConstraints;
 };	BF_AST_DECL(BfGenericConstraintsDeclaration, BfAstNode);
 };	BF_AST_DECL(BfGenericConstraintsDeclaration, BfAstNode);
 
 
 class BfMethodDeclaration : public BfMemberDeclaration
 class BfMethodDeclaration : public BfMemberDeclaration
@@ -3171,8 +3184,13 @@ public:
 const char* BfTokenToString(BfToken token);
 const char* BfTokenToString(BfToken token);
 bool BfTokenIsKeyword(BfToken token);
 bool BfTokenIsKeyword(BfToken token);
 BfBinaryOp BfAssignOpToBinaryOp(BfAssignmentOp assignmentOp);
 BfBinaryOp BfAssignOpToBinaryOp(BfAssignmentOp assignmentOp);
+BfBinaryOp BfGetOppositeBinaryOp(BfBinaryOp origOp);
+BfBinaryOp BfGetFlippedBinaryOp(BfBinaryOp origOp);
 int BfGetBinaryOpPrecendence(BfBinaryOp binOp);
 int BfGetBinaryOpPrecendence(BfBinaryOp binOp);
 const char* BfGetOpName(BfBinaryOp binOp);
 const char* BfGetOpName(BfBinaryOp binOp);
 const char* BfGetOpName(BfUnaryOp unaryOp);
 const char* BfGetOpName(BfUnaryOp unaryOp);
+BfBinaryOp BfTokenToBinaryOp(BfToken token);
+BfUnaryOp BfTokenToUnaryOp(BfToken token);
+BfAssignmentOp BfTokenToAssignmentOp(BfToken token);
 
 
 NS_BF_END
 NS_BF_END

+ 25 - 10
IDEHelper/Compiler/BfAutoComplete.cpp

@@ -1512,22 +1512,37 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken
 			}
 			}
 
 
 			// Statics, inner types
 			// Statics, inner types
-			//bool isStatic = (targetValue.mValue == NULL) && (!targetValue.mType->IsValuelessType());
 			
 			
 			auto checkType = targetValue.mType;
 			auto checkType = targetValue.mType;
-
+			
 			if (checkType->IsGenericParam())
 			if (checkType->IsGenericParam())
 			{
 			{
 				auto genericParamType = (BfGenericParamType*)checkType;
 				auto genericParamType = (BfGenericParamType*)checkType;
-				auto genericParamInst = mModule->GetGenericParamInstance(genericParamType);				
+				auto genericParamInstance = mModule->GetGenericParamInstance(genericParamType);
 				
 				
-				for (auto interfaceConstraint : genericParamInst->mInterfaceConstraints)
-					AddTypeMembers(interfaceConstraint, false, true, filter, interfaceConstraint, true, false);
-
-				if (genericParamInst->mTypeConstraint != NULL)
-					checkType = genericParamInst->mTypeConstraint;
-				else
-					checkType = mModule->mContext->mBfObjectType;
+				auto _HandleGenericParamInstance = [&](BfGenericParamInstance* genericParamInstance)
+				{
+					for (auto interfaceConstraint : genericParamInstance->mInterfaceConstraints)
+						AddTypeMembers(interfaceConstraint, false, true, filter, interfaceConstraint, true, false);
+
+					if (genericParamInstance->mTypeConstraint != NULL)
+						checkType = genericParamInstance->mTypeConstraint;
+					else
+						checkType = mModule->mContext->mBfObjectType;
+				};
+				_HandleGenericParamInstance(genericParamInstance);
+
+				// Check method generic constraints
+				if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL))
+				{
+					for (int genericParamIdx = (int)mModule->mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size();
+						genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
+					{
+						auto genericParam = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
+						if (genericParam->mExternType == genericParamType)
+							_HandleGenericParamInstance(genericParam);
+					}
+				}
 			}
 			}
 
 
 			if (checkType->IsPointer())
 			if (checkType->IsPointer())

+ 54 - 20
IDEHelper/Compiler/BfCompiler.cpp

@@ -2697,7 +2697,8 @@ void BfCompiler::GenerateDynCastData()
 }
 }
 
 
 void BfCompiler::UpdateRevisedTypes()
 void BfCompiler::UpdateRevisedTypes()
-{	
+{
+	BfLogSysM("UpdateRevisedTypes\n");
 	BP_ZONE("BfCompiler::UpdateRevisedTypes");	
 	BP_ZONE("BfCompiler::UpdateRevisedTypes");	
 		
 		
 	// See if we have any name conflicts and remove those
 	// See if we have any name conflicts and remove those
@@ -3251,13 +3252,25 @@ void BfCompiler::UpdateRevisedTypes()
 		mContext->ValidateDependencies();
 		mContext->ValidateDependencies();
 	}
 	}
 	mContext->RemoveInvalidWorkItems();	
 	mContext->RemoveInvalidWorkItems();	
-
-	for (auto typeDef : mSystem->mTypeDefs)
-	{
-		auto latestTypeDef = typeDef->GetLatest();
-		if ((latestTypeDef->mOuterType != NULL) && (latestTypeDef->mOuterType->mIsPartial))
-			latestTypeDef->mOuterType = mSystem->GetOuterTypeNonPartial(latestTypeDef);
-	}
+	
+	//for (auto typeDef : mSystem->mTypeDefs)
+	//{
+	//	auto latestTypeDef = typeDef->GetLatest();
+	//	if ((latestTypeDef->mOuterType != NULL) && (latestTypeDef->mOuterType->mIsPartial) && (latestTypeDef->mIsCombinedPartial))
+	//		//((!latestTypeDef->mIsPartial) || (latestTypeDef->mIsCombinedPartial)))
+	//		latestTypeDef->mOuterType = mSystem->GetOuterTypeNonPartial(latestTypeDef);
+
+	//	/*String fullName = typeDef->mFullNameEx.ToString();
+	//	if (fullName == "System.Collections.Generic.List`1.Enumerator`1")
+	//	{
+	//		NOP;
+	//	}
+
+	//	if ((typeDef->mOuterType != NULL) && (!typeDef->mIsPartial) && (typeDef->mOuterType->mIsPartial) && (!typeDef->mOuterType->mIsCombinedPartial))
+	//	{
+	//		NOP;
+	//	}*/
+	//}
 
 
 	mSystem->mNeedsTypesHandledByCompiler = false;
 	mSystem->mNeedsTypesHandledByCompiler = false;
 
 
@@ -3767,8 +3780,9 @@ void BfCompiler::ProcessAutocompleteTempType()
 	{	
 	{	
 		auto genericParamDef = tempTypeDef->mGenericParamDefs[genericParamIdx];
 		auto genericParamDef = tempTypeDef->mGenericParamDefs[genericParamIdx];
 
 
-		auto genericParamInstance = new BfGenericTypeParamInstance(tempTypeDef, genericParamIdx);		
-		module->ResolveGenericParamConstraints(genericParamInstance, tempTypeDef->mGenericParamDefs, genericParamIdx);
+		auto genericParamInstance = new BfGenericTypeParamInstance(tempTypeDef, genericParamIdx);
+		genericParamInstance->mExternType = module->GetGenericParamType(BfGenericParamKind_Type, genericParamIdx);
+		module->ResolveGenericParamConstraints(genericParamInstance, true);
 		delete genericParamInstance;
 		delete genericParamInstance;
 
 
 		for (auto nameNode : genericParamDef->mNameNodes)		
 		for (auto nameNode : genericParamDef->mNameNodes)		
@@ -3937,10 +3951,14 @@ void BfCompiler::ProcessAutocompleteTempType()
 			auto genericParamType = module->GetGenericParamType(BfGenericParamKind_Method, genericParamIdx);
 			auto genericParamType = module->GetGenericParamType(BfGenericParamKind_Method, genericParamIdx);
 			methodInstance->GetMethodInfoEx()->mMethodGenericArguments.push_back(genericParamType);
 			methodInstance->GetMethodInfoEx()->mMethodGenericArguments.push_back(genericParamType);
 
 
-			auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, genericParamIdx);
+			auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, genericParamIdx);			
 			methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);
 			methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);
+		}
 
 
-			//module->ResolveGenericParamConstraints(genericParamInstance, methodDef->mGenericParams[genericParamIdx]);
+		for (int externConstraintIdx = 0; externConstraintIdx < (int)methodDef->mExternalConstraints.size(); externConstraintIdx++)
+		{
+			auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, externConstraintIdx + (int)methodDef->mGenericParams.size());
+			methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);
 		}
 		}
 
 
 		SetAndRestoreValue<BfFilePosition> prevFilePos(module->mCurFilePosition);
 		SetAndRestoreValue<BfFilePosition> prevFilePos(module->mCurFilePosition);
@@ -4187,8 +4205,18 @@ void BfCompiler::GetSymbolReferences()
 
 
 							for (auto genericParam : checkTypeDef->mGenericParamDefs)
 							for (auto genericParam : checkTypeDef->mGenericParamDefs)
 							{
 							{
-								for (auto constraint : genericParam->mInterfaceConstraints)
-									module->ResolveTypeRef(constraint, BfPopulateType_Identity);
+								for (auto constraint : genericParam->mConstraints)
+								{
+									if (auto constraintTypeRef = BfNodeDynCast<BfTypeReference>(constraint))
+									{
+										module->ResolveTypeRef(constraintTypeRef, BfPopulateType_Identity);
+									}
+									else if (auto opConstraint = BfNodeDynCast<BfGenericOperatorConstraint>(constraint))
+									{
+										module->ResolveTypeRef(opConstraint->mLeftType, BfPopulateType_Identity);
+										module->ResolveTypeRef(opConstraint->mRightType, BfPopulateType_Identity);
+									}
+								}
 							}
 							}
 						}
 						}
 					}
 					}
@@ -4230,8 +4258,6 @@ void BfCompiler::GetSymbolReferences()
 					BfGenericTypeParamInstance genericParamInstance(genericTypeInstance->mTypeDef, genericParamIdx);
 					BfGenericTypeParamInstance genericParamInstance(genericTypeInstance->mTypeDef, genericParamIdx);
 					auto genericParamDef = typeDef->mGenericParamDefs[genericParamIdx];
 					auto genericParamDef = typeDef->mGenericParamDefs[genericParamIdx];
 
 
-					//BfGenericMethodParamInstance genericParamInstance(rebuildMethodInstance->mMethodDef, genericParamIdx);
-
 					if (mResolvePassData->mGetSymbolReferenceKind == BfGetSymbolReferenceKind_TypeGenericParam)
 					if (mResolvePassData->mGetSymbolReferenceKind == BfGetSymbolReferenceKind_TypeGenericParam)
 					{
 					{
 						for (auto nameNode : genericParamDef->mNameNodes)
 						for (auto nameNode : genericParamDef->mNameNodes)
@@ -4239,8 +4265,8 @@ void BfCompiler::GetSymbolReferences()
 								mResolvePassData->HandleTypeGenericParam(nameNode, typeDef, genericParamIdx);
 								mResolvePassData->HandleTypeGenericParam(nameNode, typeDef, genericParamIdx);
 					}
 					}
 
 
-					rebuildModule->ResolveGenericParamConstraints(&genericParamInstance, typeDef->mGenericParamDefs, genericParamIdx);
-				}
+					rebuildModule->ResolveGenericParamConstraints(&genericParamInstance, genericTypeInstance->IsGenericTypeInstance());
+				}				
 			}
 			}
 		}
 		}
 		
 		
@@ -4372,7 +4398,7 @@ void BfCompiler::GetSymbolReferences()
 				}
 				}
 
 
 				if (rebuildMethodInstance->mIgnoreBody)
 				if (rebuildMethodInstance->mIgnoreBody)
-				{					
+				{
 					auto methodDeclaration = methodDef->GetMethodDeclaration();
 					auto methodDeclaration = methodDef->GetMethodDeclaration();
 					if (methodDeclaration != NULL)
 					if (methodDeclaration != NULL)
 						mResolvePassData->HandleMethodReference(methodDeclaration->mNameNode, typeDef, methodDef);
 						mResolvePassData->HandleMethodReference(methodDeclaration->mNameNode, typeDef, methodDef);
@@ -4436,7 +4462,15 @@ void BfCompiler::GetSymbolReferences()
 									mResolvePassData->HandleMethodGenericParam(nameNode, typeDef, methodDef, genericParamIdx);
 									mResolvePassData->HandleMethodGenericParam(nameNode, typeDef, methodDef, genericParamIdx);
 						}
 						}
 
 
-						rebuildModule->ResolveGenericParamConstraints(&genericParamInstance, methodDef->mGenericParams, genericParamIdx);
+						rebuildModule->ResolveGenericParamConstraints(&genericParamInstance, rebuildMethodInstance->mIsUnspecialized);
+					}
+
+					for (int externConstraintIdx = 0; externConstraintIdx < (int)methodDef->mExternalConstraints.size(); externConstraintIdx++)
+					{
+						BfGenericMethodParamInstance genericParamInstance(rebuildMethodInstance->mMethodDef, externConstraintIdx + (int)methodDef->mGenericParams.size());
+						auto& externConstraintDef = methodDef->mExternalConstraints[externConstraintIdx];
+						CheckSymbolReferenceTypeRef(module, externConstraintDef.mTypeRef);
+						rebuildModule->ResolveGenericParamConstraints(&genericParamInstance, rebuildMethodInstance->mIsUnspecialized);
 					}
 					}
 
 
 					rebuildModule->ProcessMethod(rebuildMethodInstance);
 					rebuildModule->ProcessMethod(rebuildMethodInstance);

+ 108 - 41
IDEHelper/Compiler/BfDefBuilder.cpp

@@ -181,7 +181,7 @@ bool BfDefBuilder::WantsNode(BfAstNode* wholeNode, BfAstNode* startNode, int add
 
 
 static int sGenericParamIdx = 0;
 static int sGenericParamIdx = 0;
 
 
-void BfDefBuilder::ParseGenericParams(BfGenericParamsDeclaration* genericParamsDecl, BfGenericConstraintsDeclaration* genericConstraints, Array<BfGenericParamDef*>& genericParams, int outerGenericSize)
+void BfDefBuilder::ParseGenericParams(BfGenericParamsDeclaration* genericParamsDecl, BfGenericConstraintsDeclaration* genericConstraints, Array<BfGenericParamDef*>& genericParams, Array<BfExternalConstraintDef>* externConstraintDefs, int outerGenericSize)
 {		
 {		
 	if (genericParamsDecl != NULL)
 	if (genericParamsDecl != NULL)
 	{
 	{
@@ -203,7 +203,7 @@ void BfDefBuilder::ParseGenericParams(BfGenericParamsDeclaration* genericParamsD
 			while (checkTypeDef != NULL)
 			while (checkTypeDef != NULL)
 			{
 			{
 				if (&genericParams != &checkTypeDef->mGenericParamDefs)
 				if (&genericParams != &checkTypeDef->mGenericParamDefs)
-				{
+				{					
 					for (int checkParamsIdx = 0; checkParamsIdx < (int)checkTypeDef->mGenericParamDefs.size(); checkParamsIdx++)
 					for (int checkParamsIdx = 0; checkParamsIdx < (int)checkTypeDef->mGenericParamDefs.size(); checkParamsIdx++)
 					{
 					{
 						if (checkTypeDef->mGenericParamDefs[checkParamsIdx]->mName == name)
 						if (checkTypeDef->mGenericParamDefs[checkParamsIdx]->mName == name)
@@ -228,30 +228,53 @@ void BfDefBuilder::ParseGenericParams(BfGenericParamsDeclaration* genericParamsD
 	if (genericConstraints == NULL)
 	if (genericConstraints == NULL)
 		return;
 		return;
 
 
-	for (BfGenericConstraint* genericConstraint : genericConstraints->mGenericConstraints)
+	for (BfAstNode* genericConstraintNode : genericConstraints->mGenericConstraints)
 	{
 	{
-		if (genericConstraint->mGenericParamName == NULL)
+		auto genericConstraint = BfNodeDynCast<BfGenericConstraint>(genericConstraintNode);
+		if (genericConstraint == NULL)
+			continue;
+		if (genericConstraint->mTypeRef == NULL)
 			continue;
 			continue;
 
 
-		String findName = genericConstraint->mGenericParamName->ToString();
+		BfIdentifierNode* nameNode = NULL;
 		BfGenericParamDef* genericParamDef = NULL;
 		BfGenericParamDef* genericParamDef = NULL;
-		for (int genericParamIdx = outerGenericSize; genericParamIdx < (int) genericParams.size(); genericParamIdx++)
+		if (auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(genericConstraint->mTypeRef))
 		{
 		{
-			auto checkGenericParam = genericParams[genericParamIdx];
-			if (checkGenericParam->mName == findName)
-				genericParamDef = checkGenericParam;
+			nameNode = namedTypeRef->mNameNode;
+			String findName = nameNode->ToString();
+			for (int genericParamIdx = outerGenericSize; genericParamIdx < (int)genericParams.size(); genericParamIdx++)
+			{
+				auto checkGenericParam = genericParams[genericParamIdx];
+				if (checkGenericParam->mName == findName)
+					genericParamDef = checkGenericParam;
+			}
 		}
 		}
+		
+		BfConstraintDef* constraintDef = genericParamDef;
+
 		if (genericParamDef == NULL)
 		if (genericParamDef == NULL)
 		{
 		{
-			mPassInstance->Fail("Cannot find generic parameter in constraint", genericConstraint->mGenericParamName);
+			if (externConstraintDefs == NULL)
+			{
+				mPassInstance->Fail("Cannot find generic parameter in constraint", genericConstraint->mTypeRef);
 
 
-			if (genericParams.IsEmpty())
-				continue;
+				if (genericParams.IsEmpty())
+					continue;
 
 
-			genericParamDef = genericParams[0];
+				genericParamDef = genericParams[0];
+				constraintDef = genericParamDef;
+			}
+			else
+			{
+				externConstraintDefs->Add(BfExternalConstraintDef());
+				BfExternalConstraintDef* externConstraintDef = &externConstraintDefs->back();
+				externConstraintDef->mTypeRef = genericConstraint->mTypeRef;
+				constraintDef = externConstraintDef;
+			}
 		}
 		}
-		else
-			genericParamDef->mNameNodes.Add(genericConstraint->mGenericParamName);
+		
+		if (genericParamDef != NULL)
+			genericParamDef->mNameNodes.Add(nameNode);
 
 
 		for (BfAstNode* constraintNode : genericConstraint->mConstraintTypes)
 		for (BfAstNode* constraintNode : genericConstraint->mConstraintTypes)
 		{
 		{
@@ -266,11 +289,13 @@ void BfDefBuilder::ParseGenericParams(BfGenericParamsDeclaration* genericParamsD
 				name = tokenPairNode->mLeft->ToString() + tokenPairNode->mRight->ToString();
 				name = tokenPairNode->mLeft->ToString() + tokenPairNode->mRight->ToString();
 			}
 			}
 
 
+			bool hasEquals = (genericConstraint->mColonToken != NULL) && (genericConstraint->mColonToken->mToken == BfToken_AssignEquals);			
+
 			if (!name.empty())
 			if (!name.empty())
 			{
 			{
 				if ((name == "class") || (name == "struct") || (name == "struct*") || (name == "const") || (name == "var"))
 				if ((name == "class") || (name == "struct") || (name == "struct*") || (name == "const") || (name == "var"))
 				{
 				{
-					int prevFlags = genericParamDef->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Struct | BfGenericParamFlag_StructPtr);
+					int prevFlags = constraintDef->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Struct | BfGenericParamFlag_StructPtr);
 					if (prevFlags != 0)					
 					if (prevFlags != 0)					
 					{
 					{
 						String prevFlagName;
 						String prevFlagName;
@@ -289,38 +314,57 @@ void BfDefBuilder::ParseGenericParams(BfGenericParamsDeclaration* genericParamsD
 					}
 					}
 
 
 					if (name == "class")
 					if (name == "class")
-						genericParamDef->mGenericParamFlags = (BfGenericParamFlags)(genericParamDef->mGenericParamFlags | BfGenericParamFlag_Class);
+						constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_Class);
 					else if (name == "struct")
 					else if (name == "struct")
-						genericParamDef->mGenericParamFlags = (BfGenericParamFlags)(genericParamDef->mGenericParamFlags | BfGenericParamFlag_Struct);
+						constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_Struct);
 					else if (name == "struct*")
 					else if (name == "struct*")
-						genericParamDef->mGenericParamFlags = (BfGenericParamFlags)(genericParamDef->mGenericParamFlags | BfGenericParamFlag_StructPtr);
+						constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_StructPtr);
 					else if (name == "const")
 					else if (name == "const")
-						genericParamDef->mGenericParamFlags = (BfGenericParamFlags)(genericParamDef->mGenericParamFlags | BfGenericParamFlag_Const);
+						constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_Const);
 					else //if (name == "var")
 					else //if (name == "var")
-						genericParamDef->mGenericParamFlags = (BfGenericParamFlags)(genericParamDef->mGenericParamFlags | BfGenericParamFlag_Var);
+						constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_Var);
 										
 										
 					continue;
 					continue;
 				}				
 				}				
 				else if (name == "new")
 				else if (name == "new")
 				{
 				{
-					genericParamDef->mGenericParamFlags = (BfGenericParamFlags)(genericParamDef->mGenericParamFlags | BfGenericParamFlag_New);
+					constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_New);
 					continue;
 					continue;
 				}				
 				}				
 				else if (name == "delete")
 				else if (name == "delete")
 				{
 				{
-					genericParamDef->mGenericParamFlags = (BfGenericParamFlags)(genericParamDef->mGenericParamFlags | BfGenericParamFlag_Delete);
+					constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_Delete);
 					continue;
 					continue;
 				}
 				}
 			}			
 			}			
 
 
-			auto constraintType = BfNodeDynCast<BfTypeReference>(constraintNode);
-			if (constraintType == NULL)
+			if (auto genericOpConstraint = BfNodeDynCast<BfGenericOperatorConstraint>(constraintNode))
 			{
 			{
-				mPassInstance->Fail("Invalid constraint", constraintNode);
-				return;
+				// Ok
 			}
 			}
+			else
+			{
+				auto constraintType = BfNodeDynCast<BfTypeReference>(constraintNode);
+				if (constraintType == NULL)
+				{
+					mPassInstance->Fail("Invalid constraint", constraintNode);
+					return;
+				}				
+			}
+
+			if (hasEquals)
+			{				
+				if (constraintDef->mConstraints.IsEmpty())
+				{
+					constraintDef->mGenericParamFlags = (BfGenericParamFlags)(constraintDef->mGenericParamFlags | BfGenericParamFlag_Equals);
+				}
+				else
+				{
+					mPassInstance->Fail("Type assignment must be the first constraint", genericConstraint->mColonToken);
+				}
+			}			
 			
 			
-			genericParamDef->mInterfaceConstraints.push_back(constraintType);
+			constraintDef->mConstraints.Add(constraintNode);
 		}
 		}
 	}
 	}
 }
 }
@@ -538,7 +582,7 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio
 	int outerGenericSize = 0;
 	int outerGenericSize = 0;
 	if (outerMethodDef != NULL)
 	if (outerMethodDef != NULL)
 		outerGenericSize = (int)outerMethodDef->mGenericParams.size();
 		outerGenericSize = (int)outerMethodDef->mGenericParams.size();
-	ParseGenericParams(methodDeclaration->mGenericParams, methodDeclaration->mGenericConstraintsDeclaration, methodDef->mGenericParams, outerGenericSize);
+	ParseGenericParams(methodDeclaration->mGenericParams, methodDeclaration->mGenericConstraintsDeclaration, methodDef->mGenericParams, &methodDef->mExternalConstraints, outerGenericSize);
 
 
 	bool didDefaultsError = false;
 	bool didDefaultsError = false;
 	bool hadParams = false;
 	bool hadParams = false;
@@ -623,13 +667,25 @@ void BfDefBuilder::Visit(BfMethodDeclaration* methodDeclaration)
 	}
 	}
 
 
 	auto methodDef = CreateMethodDef(methodDeclaration);
 	auto methodDef = CreateMethodDef(methodDeclaration);
-	if (methodDef->mMethodType == BfMethodType_Operator)
-	{
-		mCurTypeDef->mOperators.push_back((BfOperatorDef*)methodDef);
-	}	
+	methodDef->mWantsBody = wantsBody;
+	if (methodDef->mMethodType == BfMethodType_Operator)	
+		mCurTypeDef->mOperators.push_back((BfOperatorDef*)methodDef);	
 	mCurTypeDef->mMethods.push_back(methodDef);	
 	mCurTypeDef->mMethods.push_back(methodDef);	
 
 
-	methodDef->mWantsBody = wantsBody;
+	if (methodDef->mCommutableKind == BfCommutableKind_Forward)
+	{				
+		auto revMethodDef = CreateMethodDef(methodDeclaration);
+		revMethodDef->mWantsBody = wantsBody;
+		if (revMethodDef->mMethodType == BfMethodType_Operator)
+			mCurTypeDef->mOperators.push_back((BfOperatorDef*)revMethodDef);
+
+		if (revMethodDef->mParams.size() >= 2)
+		{
+			BF_SWAP(revMethodDef->mParams[0], revMethodDef->mParams[1]);
+		}
+		revMethodDef->mCommutableKind = BfCommutableKind_Reverse;		
+		mCurTypeDef->mMethods.push_back(revMethodDef);		
+	}	
 }
 }
 
 
 void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef)
 void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef)
@@ -647,7 +703,7 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef
 			else if (typeRefName == "CVarArgs")
 			else if (typeRefName == "CVarArgs")
 				methodDef->mCallingConvention = BfCallingConvention_CVarArgs;
 				methodDef->mCallingConvention = BfCallingConvention_CVarArgs;
 			else if (typeRefName == "Inline")
 			else if (typeRefName == "Inline")
-				methodDef->mAlwaysInline = true;			
+				methodDef->mAlwaysInline = true;
 			else if (typeRefName == "AllowAppend")
 			else if (typeRefName == "AllowAppend")
 				methodDef->mHasAppend = true;
 				methodDef->mHasAppend = true;
 			else if (typeRefName == "Checked")
 			else if (typeRefName == "Checked")
@@ -655,12 +711,12 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef
 			else if (typeRefName == "Unchecked")
 			else if (typeRefName == "Unchecked")
 				methodDef->mCheckedKind = BfCheckedKind_Unchecked;
 				methodDef->mCheckedKind = BfCheckedKind_Unchecked;
 			else if (typeRefName == "Export")
 			else if (typeRefName == "Export")
-			{				
+			{
 				mCurTypeDef->mIsAlwaysInclude = true;
 				mCurTypeDef->mIsAlwaysInclude = true;
-				methodDef->mImportKind = BfImportKind_Export;				
+				methodDef->mImportKind = BfImportKind_Export;
 			}
 			}
 			else if (typeRefName == "Import")
 			else if (typeRefName == "Import")
-			{	
+			{
 				methodDef->mImportKind = BfImportKind_Static;
 				methodDef->mImportKind = BfImportKind_Static;
 				if (!attributes->mArguments.IsEmpty())
 				if (!attributes->mArguments.IsEmpty())
 				{
 				{
@@ -677,7 +733,7 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef
 							}
 							}
 						}
 						}
 					}
 					}
-				}				
+				}
 			}
 			}
 			else if (typeRefName == "NoReturn")
 			else if (typeRefName == "NoReturn")
 				methodDef->mNoReturn = true;
 				methodDef->mNoReturn = true;
@@ -687,6 +743,17 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef
 				methodDef->mIsNoShow = true;
 				methodDef->mIsNoShow = true;
 			else if (typeRefName == "NoDiscard")
 			else if (typeRefName == "NoDiscard")
 				methodDef->mIsNoDiscard = true;
 				methodDef->mIsNoDiscard = true;
+			else if (typeRefName == "Commutable")
+			{
+				if (methodDef->mParams.size() != 2)
+				{
+					mPassInstance->Fail("Commutable attributes can only be applied to methods with two arguments", attributes->mAttributeTypeRef);
+				}
+				else
+				{
+					methodDef->mCommutableKind = BfCommutableKind_Forward;
+				}				
+			}
 		}
 		}
 
 
 		attributes = attributes->mNextAttribute;
 		attributes = attributes->mNextAttribute;
@@ -1304,7 +1371,7 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration)
 			BfGenericParamDef* copiedGenericParamDef = new BfGenericParamDef();
 			BfGenericParamDef* copiedGenericParamDef = new BfGenericParamDef();
 			*copiedGenericParamDef = *outerGenericParamDef;
 			*copiedGenericParamDef = *outerGenericParamDef;
 			mCurTypeDef->mGenericParamDefs.Add(copiedGenericParamDef);
 			mCurTypeDef->mGenericParamDefs.Add(copiedGenericParamDef);
-		}
+		}		
 
 
 		BfTypeDef* parentType = outerTypeDef;
 		BfTypeDef* parentType = outerTypeDef;
 		while (parentType != NULL)
 		while (parentType != NULL)
@@ -1521,7 +1588,7 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration)
 	int outerGenericSize = 0;
 	int outerGenericSize = 0;
 	if (mCurTypeDef->mOuterType != NULL)
 	if (mCurTypeDef->mOuterType != NULL)
 		outerGenericSize = (int)mCurTypeDef->mOuterType->mGenericParamDefs.size();
 		outerGenericSize = (int)mCurTypeDef->mOuterType->mGenericParamDefs.size();
-	ParseGenericParams(typeDeclaration->mGenericParams, typeDeclaration->mGenericConstraintsDeclaration, mCurTypeDef->mGenericParamDefs, outerGenericSize);
+	ParseGenericParams(typeDeclaration->mGenericParams, typeDeclaration->mGenericConstraintsDeclaration, mCurTypeDef->mGenericParamDefs, NULL, outerGenericSize);
 	
 	
 	BF_ASSERT(mCurTypeDef->mNameEx == NULL);
 	BF_ASSERT(mCurTypeDef->mNameEx == NULL);
 	
 	

+ 1 - 1
IDEHelper/Compiler/BfDefBuilder.h

@@ -27,7 +27,7 @@ public:
 	HashContext* mSignatureHashCtx;
 	HashContext* mSignatureHashCtx;
 
 
 public:
 public:
-	void ParseGenericParams(BfGenericParamsDeclaration* genericParamsDecl, BfGenericConstraintsDeclaration* genericConstraints, Array<BfGenericParamDef*>& genericParams, int outerGenericSize);
+	void ParseGenericParams(BfGenericParamsDeclaration* genericParamsDecl, BfGenericConstraintsDeclaration* genericConstraints, Array<BfGenericParamDef*>& genericParams, Array<BfExternalConstraintDef>* externConstraintDefs, int outerGenericSize);
 	BfProtection GetProtection(BfTokenNode* protectionToken);
 	BfProtection GetProtection(BfTokenNode* protectionToken);
 	bool WantsNode(BfAstNode* wholeNode, BfAstNode* startNode = NULL, int addLen = 0);		
 	bool WantsNode(BfAstNode* wholeNode, BfAstNode* startNode = NULL, int addLen = 0);		
 	//static BfNamedTypeReference* AllocTypeReference(BfSource* bfSource, const StringImpl& typeName);
 	//static BfNamedTypeReference* AllocTypeReference(BfSource* bfSource, const StringImpl& typeName);

+ 13 - 3
IDEHelper/Compiler/BfElementVisitor.cpp

@@ -69,19 +69,29 @@ void BfElementVisitor::Visit(BfGenericParamsDeclaration* genericParams)
 	VisitChild(genericParams->mCloseChevron);
 	VisitChild(genericParams->mCloseChevron);
 }
 }
 
 
+void BfElementVisitor::Visit(BfGenericOperatorConstraint* genericConstraints)
+{
+	Visit(genericConstraints->ToBase());
+
+	VisitChild(genericConstraints->mOperatorToken);
+	VisitChild(genericConstraints->mLeftType);
+	VisitChild(genericConstraints->mOpToken);
+	VisitChild(genericConstraints->mRightType);
+}
+
 void BfElementVisitor::Visit(BfGenericConstraintsDeclaration* genericConstraints)
 void BfElementVisitor::Visit(BfGenericConstraintsDeclaration* genericConstraints)
 {
 {
 	Visit(genericConstraints->ToBase());
 	Visit(genericConstraints->ToBase());
 
 
 	for (auto genericConstraint : genericConstraints->mGenericConstraints)
 	for (auto genericConstraint : genericConstraints->mGenericConstraints)
-	{
+	{		
 		VisitChild(genericConstraint->mWhereToken);
 		VisitChild(genericConstraint->mWhereToken);
-		VisitChild(genericConstraint->mGenericParamName);
+		VisitChild(genericConstraint->mTypeRef);
 		VisitChild(genericConstraint->mColonToken);
 		VisitChild(genericConstraint->mColonToken);
 		for (auto val : genericConstraint->mConstraintTypes)
 		for (auto val : genericConstraint->mConstraintTypes)
 			VisitChild(val);
 			VisitChild(val);
 		for (auto val : genericConstraint->mCommas)
 		for (auto val : genericConstraint->mCommas)
-			VisitChild(val);
+			VisitChild(val);		
 	}		
 	}		
 }
 }
 
 

+ 1 - 0
IDEHelper/Compiler/BfElementVisitor.h

@@ -28,6 +28,7 @@ public:
 
 
 	virtual void Visit(BfAttributeDirective* attributeDirective);
 	virtual void Visit(BfAttributeDirective* attributeDirective);
 	virtual void Visit(BfGenericParamsDeclaration* genericParams);
 	virtual void Visit(BfGenericParamsDeclaration* genericParams);
+	virtual void Visit(BfGenericOperatorConstraint* genericConstraints);
 	virtual void Visit(BfGenericConstraintsDeclaration* genericConstraints);
 	virtual void Visit(BfGenericConstraintsDeclaration* genericConstraints);
 	virtual void Visit(BfGenericArgumentsNode* genericArgumentsNode);
 	virtual void Visit(BfGenericArgumentsNode* genericArgumentsNode);
 
 

+ 188 - 76
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -26,50 +26,6 @@
 USING_NS_BF;
 USING_NS_BF;
 using namespace llvm;
 using namespace llvm;
 
 
-static BfBinaryOp GetOppositeBinaryOp(BfBinaryOp origOp)
-{
-	switch (origOp)
-	{
-	case BfBinaryOp_Equality:
-		return BfBinaryOp_InEquality;
-	case BfBinaryOp_InEquality:
-		return BfBinaryOp_Equality;
-	case BfBinaryOp_LessThan:
-		return BfBinaryOp_GreaterThanOrEqual;
-	case BfBinaryOp_LessThanOrEqual:
-		return BfBinaryOp_GreaterThan;
-	case BfBinaryOp_GreaterThan:
-		return BfBinaryOp_LessThanOrEqual;
-	case BfBinaryOp_GreaterThanOrEqual:
-		return BfBinaryOp_LessThan;
-	default: break;
-	}
-
-	return BfBinaryOp_None;
-}
-
-static BfBinaryOp GetFlippedBinaryOp(BfBinaryOp origOp)
-{
-	switch (origOp)
-	{
-	case BfBinaryOp_Equality:
-		return BfBinaryOp_Equality;
-	case BfBinaryOp_InEquality:
-		return BfBinaryOp_InEquality;
-	case BfBinaryOp_LessThan:
-		return BfBinaryOp_GreaterThan;
-	case BfBinaryOp_LessThanOrEqual:
-		return BfBinaryOp_GreaterThanOrEqual;
-	case BfBinaryOp_GreaterThan:
-		return BfBinaryOp_LessThan;
-	case BfBinaryOp_GreaterThanOrEqual:
-		return BfBinaryOp_LessThanOrEqual;
-	default: break;
-	}
-
-	return BfBinaryOp_None;
-}
-
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 
 
 DeferredTupleAssignData::~DeferredTupleAssignData()
 DeferredTupleAssignData::~DeferredTupleAssignData()
@@ -197,6 +153,7 @@ BfMethodMatcher::BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMetho
 void BfMethodMatcher::Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments)
 void BfMethodMatcher::Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments)
 {
 {
 	//mArguments = arguments;	
 	//mArguments = arguments;	
+	mActiveTypeDef = NULL;
 	mBestMethodDef = NULL;	
 	mBestMethodDef = NULL;	
 	mBackupMethodDef = NULL;
 	mBackupMethodDef = NULL;
 	mBestMethodTypeInstance = NULL;
 	mBestMethodTypeInstance = NULL;
@@ -237,6 +194,22 @@ void BfMethodMatcher::Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSized
 	}
 	}
 }
 }
 
 
+bool BfMethodMatcher::IsMemberAccessible(BfTypeInstance* typeInst, BfTypeDef* declaringType)
+{
+	if (mActiveTypeDef == NULL)
+		mActiveTypeDef = mModule->GetActiveTypeDef();
+	if (!typeInst->IsTypeMemberIncluded(declaringType, mActiveTypeDef, mModule))
+		return false;
+
+	// This may not be completely correct - BUT if we don't have this then even Dictionary TKey's operator == won't be considered accessible
+	if (!mModule->IsInSpecializedSection())
+	{
+		if (!typeInst->IsTypeMemberAccessible(declaringType, mActiveTypeDef))
+			return false;
+	}
+	return true;
+}
+
 bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue)
 bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue)
 {
 {
 	if (argType == NULL)
 	if (argType == NULL)
@@ -828,7 +801,8 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp
 		}
 		}
 	}
 	}
 
 
-	RETURN_BETTER_OR_WORSE(newMethodDef->mCheckedKind == mCheckedKind, prevMethodDef->mCheckedKind == mCheckedKind);	
+	RETURN_BETTER_OR_WORSE(newMethodDef->mCheckedKind == mCheckedKind, prevMethodDef->mCheckedKind == mCheckedKind);
+	RETURN_BETTER_OR_WORSE(newMethodDef->mCommutableKind != BfCommutableKind_Reverse, prevMethodDef->mCommutableKind != BfCommutableKind_Reverse);
 
 
 	RETURN_RESULTS;
 	RETURN_RESULTS;
 }
 }
@@ -1030,6 +1004,11 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* typeInstance, BfMethodDef* che
 	mMethodCheckCount++;
 	mMethodCheckCount++;
 
 
 	BfMethodInstance* methodInstance = mModule->GetRawMethodInstance(typeInstance, checkMethod);
 	BfMethodInstance* methodInstance = mModule->GetRawMethodInstance(typeInstance, checkMethod);
+	if (methodInstance == NULL)
+	{		
+		BF_FATAL("Failed to get raw method in BfMethodMatcher::CheckMethod");
+		return false;
+	}
 
 
 	if ((mInterfaceMethodInstance != NULL) && (methodInstance->GetExplicitInterface() != NULL))
 	if ((mInterfaceMethodInstance != NULL) && (methodInstance->GetExplicitInterface() != NULL))
 	{
 	{
@@ -1203,6 +1182,24 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* typeInstance, BfMethodDef* che
 			auto& genericArg = mCheckMethodGenericArguments[genericArgIdx];
 			auto& genericArg = mCheckMethodGenericArguments[genericArgIdx];
 			if (genericArg == NULL)
 			if (genericArg == NULL)
 			{
 			{
+				auto genericParam = methodInstance->mMethodInfoEx->mGenericParams[genericArgIdx];
+				//mModule->CheckGenericConstraints(BfGenericParamSource(), NULL, NULL, genericParam, &mCheckMethodGenericArguments, NULL);
+				if (genericArg != NULL)
+					continue;
+
+// 				if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Equals) != 0)
+// 				{
+// 					if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Equals_Op) != 0)
+// 					{
+// 						
+// 					}
+// 
+// 					else if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Equals_Type) != 0)
+// 					{
+// 
+// 					}
+// 				}
+
 				if (!allowEmptyGenericSet.Contains(genericArgIdx))
 				if (!allowEmptyGenericSet.Contains(genericArgIdx))
 					goto NoMatch;
 					goto NoMatch;
 			}
 			}
@@ -1944,7 +1941,7 @@ void BfResolvedArgs::HandleFixits(BfModule* module)
 BfExprEvaluator::BfExprEvaluator(BfModule* module)
 BfExprEvaluator::BfExprEvaluator(BfModule* module)
 {
 {
 	mBfEvalExprFlags = BfEvalExprFlags_None;
 	mBfEvalExprFlags = BfEvalExprFlags_None;
-	mModule = module;
+	mModule = module;	
 	mPropDef = NULL;
 	mPropDef = NULL;
 	mPropSrc = NULL;
 	mPropSrc = NULL;
 	mPropGetMethodFlags = BfGetMethodInstanceFlag_None;
 	mPropGetMethodFlags = BfGetMethodInstanceFlag_None;
@@ -6009,22 +6006,39 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 			else if (lookupType->IsGenericParam())
 			else if (lookupType->IsGenericParam())
 			{
 			{
 				auto genericParamTarget = (BfGenericParamType*)lookupType;
 				auto genericParamTarget = (BfGenericParamType*)lookupType;
-				auto genericParamInstance = mModule->GetGenericParamInstance(genericParamTarget);
 
 
-				if (genericParamInstance->mTypeConstraint != NULL)
-					lookupTypeInst = genericParamInstance->mTypeConstraint->ToTypeInstance();
-				else
-					lookupTypeInst = mModule->mContext->mBfObjectType;
-
-				for (BfType* ifaceInst : genericParamInstance->mInterfaceConstraints)
+				auto _HandleGenericParamInstance = [&](BfGenericParamInstance* genericParamInstance)
 				{
 				{
-					if (ifaceInst->IsUnspecializedType())
-						ifaceInst = mModule->ResolveType(ifaceInst);
+					if (genericParamInstance->mTypeConstraint != NULL)
+						lookupTypeInst = genericParamInstance->mTypeConstraint->ToTypeInstance();
+					else
+						lookupTypeInst = mModule->mContext->mBfObjectType;
 
 
-					BfTypeInstance* typeInst = ifaceInst->ToTypeInstance();
-					BF_ASSERT(typeInst != NULL);
-					if (methodMatcher.CheckType(typeInst, target, false))
-						methodMatcher.mSelfType = lookupType;
+					for (BfType* ifaceInst : genericParamInstance->mInterfaceConstraints)
+					{
+						if (ifaceInst->IsUnspecializedType())
+							ifaceInst = mModule->ResolveType(ifaceInst);
+
+						BfTypeInstance* typeInst = ifaceInst->ToTypeInstance();
+						BF_ASSERT(typeInst != NULL);
+						if (methodMatcher.CheckType(typeInst, target, false))
+							methodMatcher.mSelfType = lookupType;
+					}
+				};
+
+				auto genericParamInstance = mModule->GetGenericParamInstance(genericParamTarget);
+				_HandleGenericParamInstance(genericParamInstance);
+				
+				// Check method generic constraints
+				if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL))
+				{
+					for (int genericParamIdx = (int)mModule->mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); 
+						genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
+					{
+						auto genericParam = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
+						if (genericParam->mExternType == lookupType)
+							_HandleGenericParamInstance(genericParam);						
+					}
 				}
 				}
 			}
 			}
 		}
 		}
@@ -11736,11 +11750,11 @@ BfModuleMethodInstance BfExprEvaluator::GetSelectedMethod(BfAstNode* targetSrc,
 		auto genericArg = methodMatcher.mBestMethodGenericArguments[checkGenericIdx];
 		auto genericArg = methodMatcher.mBestMethodGenericArguments[checkGenericIdx];
 		if (genericArg->IsVar())
 		if (genericArg->IsVar())
 			continue;
 			continue;
-		if (genericArg->IsPrimitiveType())
+		/*if (genericArg->IsPrimitiveType())
 		{
 		{
 			auto primType = (BfPrimitiveType*)genericArg;
 			auto primType = (BfPrimitiveType*)genericArg;
 			genericArg = mModule->GetPrimitiveStructType(primType->mTypeDef->mTypeCode);
 			genericArg = mModule->GetPrimitiveStructType(primType->mTypeDef->mTypeCode);
-		}
+		}*/
 
 
 		BfAstNode* paramSrc;
 		BfAstNode* paramSrc;
 		if (methodMatcher.mBestMethodGenericArgumentSrcs.size() == 0)
 		if (methodMatcher.mBestMethodGenericArgumentSrcs.size() == 0)
@@ -15506,6 +15520,9 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr)
 						if ((!isFailurePass) && (!methodMatcher.WantsCheckMethod(protectionCheckFlags, startCheckTypeInst, curCheckType, checkMethod)))
 						if ((!isFailurePass) && (!methodMatcher.WantsCheckMethod(protectionCheckFlags, startCheckTypeInst, curCheckType, checkMethod)))
 							continue;
 							continue;
 
 
+						if (!methodMatcher.IsMemberAccessible(curCheckType, checkMethod->mDeclaringType))
+							continue;
+
 						methodMatcher.mCheckedKind = checkedKind;
 						methodMatcher.mCheckedKind = checkedKind;
 						methodMatcher.CheckMethod(curCheckType, checkMethod, false);						
 						methodMatcher.CheckMethod(curCheckType, checkMethod, false);						
 
 
@@ -15794,10 +15811,17 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp
 			break;
 			break;
 		default:
 		default:
 			mExpectingType = NULL;
 			mExpectingType = NULL;
-		}		
-        VisitChild(unaryOpExpr);
+		}
+		VisitChild(unaryOpExpr);
 		mExpectingType = prevExpedcting;
 		mExpectingType = prevExpedcting;
 	}
 	}
+		
+	
+	BfExprEvaluator::PerformUnaryOperation_OnResult(unaryOpExpr, unaryOp, opToken);
+}
+
+void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken)
+{
 	BfAstNode* propSrc = mPropSrc;
 	BfAstNode* propSrc = mPropSrc;
 	BfTypedValue propTarget = mPropTarget;
 	BfTypedValue propTarget = mPropTarget;
 	BfPropertyDef* propDef = mPropDef;
 	BfPropertyDef* propDef = mPropDef;
@@ -15806,7 +15830,7 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp
 
 
 	GetResult();
 	GetResult();
 	if (!mResult)
 	if (!mResult)
-		return;
+		return;	
 
 
 	if (mResult.mType->IsRef())
 	if (mResult.mType->IsRef())
 		mResult.mType = mResult.mType->GetUnderlyingType();
 		mResult.mType = mResult.mType->GetUnderlyingType();
@@ -15851,12 +15875,17 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp
 			for (auto operatorDef : checkType->mTypeDef->mOperators)
 			for (auto operatorDef : checkType->mTypeDef->mOperators)
 			{
 			{
 				if (operatorDef->mOperatorDeclaration->mUnaryOp == findOp)
 				if (operatorDef->mOperatorDeclaration->mUnaryOp == findOp)
+				{
+					if (!methodMatcher.IsMemberAccessible(checkType, operatorDef->mDeclaringType))
+						continue;
 					if (methodMatcher.CheckMethod(checkType, operatorDef, false))
 					if (methodMatcher.CheckMethod(checkType, operatorDef, false))
 						methodMatcher.mSelfType = entry.mSrcType;
 						methodMatcher.mSelfType = entry.mSrcType;
+				}
 			}			
 			}			
 		}
 		}
+		
 		if (methodMatcher.mBestMethodDef != NULL)
 		if (methodMatcher.mBestMethodDef != NULL)
-		{			
+		{
 			if (!baseClassWalker.mMayBeFromInterface)
 			if (!baseClassWalker.mMayBeFromInterface)
 				mModule->SetElementType(opToken, BfSourceElementType_Method);
 				mModule->SetElementType(opToken, BfSourceElementType_Method);
 
 
@@ -15883,6 +15912,47 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp
 				mResult = args[0].mTypedValue;
 				mResult = args[0].mTypedValue;
 			return;
 			return;
 		}
 		}
+
+		// Check method generic constraints
+		if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL))
+		{
+			for (int genericParamIdx = 0; genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
+			{
+				auto genericParam = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
+				for (auto& opConstraint : genericParam->mOperatorConstraints)
+				{
+					if (opConstraint.mUnaryOp == findOp)
+					{
+						if (mModule->CanImplicitlyCast(args[0].mTypedValue, opConstraint.mRightType))
+						{
+							mResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType);
+							return;
+						}
+					}
+				}
+			}
+		}
+		
+		// Check type generic constraints
+		if ((mModule->mCurTypeInstance->IsGenericTypeInstance()) && (mModule->mCurTypeInstance->IsUnspecializedType()))
+		{
+			auto genericTypeInst = (BfGenericTypeInstance*)mModule->mCurTypeInstance;
+			for (int genericParamIdx = 0; genericParamIdx < genericTypeInst->mGenericParams.size(); genericParamIdx++)
+			{
+				auto genericParam = mModule->GetGenericTypeParamInstance(genericParamIdx);
+				for (auto& opConstraint : genericParam->mOperatorConstraints)
+				{
+					if (opConstraint.mUnaryOp == findOp)
+					{
+						if (mModule->CanImplicitlyCast(args[0].mTypedValue, opConstraint.mRightType))
+						{
+							mResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType);
+							return;
+						}
+					}
+				}
+			}
+		}
 	}
 	}
 
 
 	bool numericFail = false;
 	bool numericFail = false;
@@ -16612,7 +16682,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 	}
 	}
 	else if ((leftValue.mValue.IsConst()) && (!rightValue.mValue.IsConst()))
 	else if ((leftValue.mValue.IsConst()) && (!rightValue.mValue.IsConst()))
 	{
 	{
-		if (CheckConstCompare(GetOppositeBinaryOp(binaryOp), opToken, rightValue, leftValue))
+		if (CheckConstCompare(BfGetOppositeBinaryOp(binaryOp), opToken, rightValue, leftValue))
 			return;
 			return;
 	}
 	}
 
 
@@ -16914,7 +16984,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 
 
 				bool invertResult = false;				
 				bool invertResult = false;				
 				
 				
-				BfBinaryOp oppositeBinaryOp = GetOppositeBinaryOp(findBinaryOp);
+				BfBinaryOp oppositeBinaryOp = BfGetOppositeBinaryOp(findBinaryOp);				
 
 
 				while (true)
 				while (true)
 				{
 				{
@@ -16935,6 +17005,10 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 						if (allowOp)
 						if (allowOp)
 						{
 						{
 							foundOp = true;
 							foundOp = true;
+							
+							if (!methodMatcher.IsMemberAccessible(checkType, operatorDef->mDeclaringType))
+								continue;
+
 							if (methodMatcher.CheckMethod(checkType, operatorDef, false))
 							if (methodMatcher.CheckMethod(checkType, operatorDef, false))
 							{
 							{
 								methodMatcher.mSelfType = entry.mSrcType;
 								methodMatcher.mSelfType = entry.mSrcType;
@@ -17006,7 +17080,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 
 
 						auto useBinaryOp = binaryOp;
 						auto useBinaryOp = binaryOp;
 						if (pass == 1)
 						if (pass == 1)
-							useBinaryOp = GetFlippedBinaryOp(useBinaryOp);
+							useBinaryOp = BfGetFlippedBinaryOp(useBinaryOp);
 
 
 						auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean);
 						auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean);
 						switch (useBinaryOp)
 						switch (useBinaryOp)
@@ -17036,19 +17110,57 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 					return;
 					return;
 				}
 				}
 
 
+				// Check method generic constraints
+				if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL))
+				{
+					for (int genericParamIdx = 0; genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
+					{
+						auto genericParam = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
+						for (auto& opConstraint : genericParam->mOperatorConstraints)
+						{
+							if (opConstraint.mBinaryOp == findBinaryOp)
+							{
+								if ((mModule->CanImplicitlyCast(args[0].mTypedValue, opConstraint.mLeftType)) &&
+									(mModule->CanImplicitlyCast(args[1].mTypedValue, opConstraint.mRightType)))
+								{									
+									BF_ASSERT(genericParam->mExternType != NULL);
+									mResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType);									
+									return;
+								}
+							}
+						}
+					}
+				}
+				
+				// Check type generic constraints
+				if ((mModule->mCurTypeInstance->IsGenericTypeInstance()) && (mModule->mCurTypeInstance->IsUnspecializedType()))
+				{
+					auto genericTypeInst = (BfGenericTypeInstance*)mModule->mCurTypeInstance;
+					for (int genericParamIdx = 0; genericParamIdx < genericTypeInst->mGenericParams.size(); genericParamIdx++)
+					{
+						auto genericParam = mModule->GetGenericTypeParamInstance(genericParamIdx);
+						for (auto& opConstraint : genericParam->mOperatorConstraints)
+						{
+							if (opConstraint.mBinaryOp == findBinaryOp)
+							{
+								if ((mModule->CanImplicitlyCast(args[0].mTypedValue, opConstraint.mLeftType)) &&
+									(mModule->CanImplicitlyCast(args[1].mTypedValue, opConstraint.mRightType)))
+								{
+									mResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType);
+									return;
+								}
+							}
+						}
+					}
+				}
+
 				if ((!foundOp) || (pass == 1))
 				if ((!foundOp) || (pass == 1))
 					break;
 					break;
 								
 								
 				switch (findBinaryOp)
 				switch (findBinaryOp)
 				{
 				{
-				case BfBinaryOp_Add:
-				case BfBinaryOp_Multiply:
 				case BfBinaryOp_Equality:
 				case BfBinaryOp_Equality:
-				case BfBinaryOp_InEquality:
-				case BfBinaryOp_BitwiseAnd:
-				case BfBinaryOp_BitwiseOr:
-				case BfBinaryOp_ConditionalAnd:
-				case BfBinaryOp_ConditionalOr:
+				case BfBinaryOp_InEquality:			
 				case BfBinaryOp_Compare:
 				case BfBinaryOp_Compare:
 					// Still works
 					// Still works
 					break;
 					break;
@@ -17071,7 +17183,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 
 
 				if (findBinaryOp == BfBinaryOp_None)
 				if (findBinaryOp == BfBinaryOp_None)
 					break;
 					break;
-			}
+			}			
 		}
 		}
 	}
 	}
 
 

+ 3 - 0
IDEHelper/Compiler/BfExprEvaluator.h

@@ -120,6 +120,7 @@ public:
 public:
 public:
 	BfAstNode* mTargetSrc;
 	BfAstNode* mTargetSrc;
 	BfModule* mModule;
 	BfModule* mModule;
+	BfTypeDef* mActiveTypeDef;
 	String mMethodName;
 	String mMethodName;
 	BfMethodInstance* mInterfaceMethodInstance;
 	BfMethodInstance* mInterfaceMethodInstance;
 	SizedArrayImpl<BfResolvedArg>& mArguments;
 	SizedArrayImpl<BfResolvedArg>& mArguments;
@@ -164,6 +165,7 @@ public:
 	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, const StringImpl& methodName, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments);
 	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, const StringImpl& methodName, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments);
 	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* interfaceMethodInstance, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments = NULL);
 	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* interfaceMethodInstance, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments = NULL);
 	void Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments);
 	void Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSizedArray<ASTREF(BfTypeReference*)>* methodGenericArguments);
+	bool IsMemberAccessible(BfTypeInstance* typeInst, BfTypeDef* declaringType);
 	bool CheckType(BfTypeInstance* typeInstance, BfTypedValue target, bool isFailurePass);
 	bool CheckType(BfTypeInstance* typeInstance, BfTypedValue target, bool isFailurePass);
 	void CheckOuterTypeStaticMethods(BfTypeInstance* typeInstance, bool isFailurePass);
 	void CheckOuterTypeStaticMethods(BfTypeInstance* typeInstance, bool isFailurePass);
 	bool WantsCheckMethod(BfProtectionCheckFlags& flags, BfTypeInstance* startTypeInstance, BfTypeInstance* checkTypeInstance, BfMethodDef* methodDef);
 	bool WantsCheckMethod(BfProtectionCheckFlags& flags, BfTypeInstance* startTypeInstance, BfTypeInstance* checkTypeInstance, BfMethodDef* methodDef);
@@ -379,6 +381,7 @@ public:
 	void VisitLambdaBodies(BfAstNode* body, BfFieldDtorDeclaration* fieldDtor);	
 	void VisitLambdaBodies(BfAstNode* body, BfFieldDtorDeclaration* fieldDtor);	
 	void FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType, const StringImpl& fieldName, bool isStatic);	
 	void FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType, const StringImpl& fieldName, bool isStatic);	
 	void PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken);
 	void PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken);
+	void PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken);
 	void PerformAssignment(BfAssignmentExpression* assignExpr, bool evaluatedLeft, BfTypedValue rightValue, BfTypedValue* outCascadeValue = NULL);	
 	void PerformAssignment(BfAssignmentExpression* assignExpr, bool evaluatedLeft, BfTypedValue rightValue, BfTypedValue* outCascadeValue = NULL);	
 	void PopulateDeferrredTupleAssignData(BfTupleExpression* tupleExr, DeferredTupleAssignData& deferredTupleAssignData);
 	void PopulateDeferrredTupleAssignData(BfTupleExpression* tupleExr, DeferredTupleAssignData& deferredTupleAssignData);
 	void AssignDeferrredTupleAssignData(BfAssignmentExpression* assignExpr, DeferredTupleAssignData& deferredTupleAssignData, BfTypedValue rightValue);
 	void AssignDeferrredTupleAssignData(BfAssignmentExpression* assignExpr, DeferredTupleAssignData& deferredTupleAssignData, BfTypedValue rightValue);

+ 278 - 50
IDEHelper/Compiler/BfModule.cpp

@@ -2471,7 +2471,7 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers
  	if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecializedVariation))
  	if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecializedVariation))
 		return NULL; // Ignore errors on unspecialized variations, they are always dups
 		return NULL; // Ignore errors on unspecialized variations, they are always dups
 
 
- 	if (!mHadBuildError)
+	if (!mHadBuildError)
 		mHadBuildError = true;
 		mHadBuildError = true;
 	if (mParentModule != NULL)
 	if (mParentModule != NULL)
 		mParentModule->mHadBuildError = true;
 		mParentModule->mHadBuildError = true;
@@ -6150,17 +6150,19 @@ BfIRFunction BfModule::GetBuiltInFunc(BfBuiltInFuncType funcTypeId)
 	return mBuiltInFuncs[(int)funcTypeId];
 	return mBuiltInFuncs[(int)funcTypeId];
 }
 }
 
 
-void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericParamInstance, const Array<BfGenericParamDef*>& genericParamDefs, int genericParamIdx)
-{
-	BfGenericParamDef* genericParamDef = genericParamDefs[genericParamIdx];
+void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericParamInstance, bool isUnspecialized)
+{	
+	BfGenericParamDef* genericParamDef = genericParamInstance->GetGenericParamDef();	
+	BfExternalConstraintDef* externConstraintDef = genericParamInstance->GetExternConstraintDef();
+	BfConstraintDef* constraintDef = genericParamInstance->GetConstraintDef();
 
 
 	BfType* startingTypeConstraint = genericParamInstance->mTypeConstraint;
 	BfType* startingTypeConstraint = genericParamInstance->mTypeConstraint;
 
 
 	BfAutoComplete* bfAutocomplete = NULL;
 	BfAutoComplete* bfAutocomplete = NULL;
-	if (mCompiler->mResolvePassData != NULL)
+	if ((mCompiler->mResolvePassData != NULL) && (isUnspecialized))
 		bfAutocomplete = mCompiler->mResolvePassData->mAutoComplete;	
 		bfAutocomplete = mCompiler->mResolvePassData->mAutoComplete;	
 
 
-	if (bfAutocomplete != NULL)
+	if ((bfAutocomplete != NULL) && (genericParamDef != NULL))
 	{		
 	{		
 		for (int nameIdx = 0; nameIdx < (int)genericParamDef->mNameNodes.size(); nameIdx++)		
 		for (int nameIdx = 0; nameIdx < (int)genericParamDef->mNameNodes.size(); nameIdx++)		
 		{
 		{
@@ -6172,16 +6174,87 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar
 				bfAutocomplete->mInsertEndIdx = nameNode->GetSrcEnd();
 				bfAutocomplete->mInsertEndIdx = nameNode->GetSrcEnd();
 
 
 				if (nameIdx != 0)
 				if (nameIdx != 0)
-				{
-					for (auto checkGenericParam : genericParamDefs)
-						bfAutocomplete->AddEntry(AutoCompleteEntry("generic", checkGenericParam->mName.c_str()), filter);
+				{					
+					bfAutocomplete->AddEntry(AutoCompleteEntry("generic", nameNode->ToString().c_str()), filter);
 				}
 				}
 			}
 			}
 		}		
 		}		
 	}
 	}
 
 
-	for (auto constraintTypeRef : genericParamDef->mInterfaceConstraints)
+	for (auto constraint : constraintDef->mConstraints)
 	{
 	{
+		if (auto opConstraint = BfNodeDynCast<BfGenericOperatorConstraint>(constraint))
+		{
+			BfGenericOperatorConstraintInstance opConstraintInstance;
+			
+			if (opConstraint->mLeftType != NULL)
+			{
+				if (bfAutocomplete != NULL)
+					bfAutocomplete->CheckTypeRef(opConstraint->mLeftType, false);
+				opConstraintInstance.mLeftType = ResolveTypeRef(opConstraint->mLeftType);
+				if (opConstraintInstance.mLeftType == NULL)
+					continue;
+			}
+
+			if (opConstraint->mRightType == NULL)
+			{
+				// We had a failure in parsing
+				continue;
+			}
+
+			if (opConstraint->mRightType != NULL)
+			{
+				if (bfAutocomplete != NULL)
+					bfAutocomplete->CheckTypeRef(opConstraint->mRightType, false);
+				opConstraintInstance.mRightType = ResolveTypeRef(opConstraint->mRightType);
+				if (opConstraintInstance.mRightType == NULL)
+					continue;
+			}
+
+			if (opConstraint->mOpToken == NULL)
+			{				
+				FailAfter("Missing operator", (opConstraint->mLeftType != NULL) ? (BfAstNode*)opConstraint->mLeftType : (BfAstNode*)opConstraint->mOperatorToken);
+				continue;
+			}
+
+			if (opConstraint->mLeftType != NULL)
+			{
+				if (opConstraint->mRightType == NULL)
+				{
+					// Parse should have failed
+					continue;
+				}
+
+				opConstraintInstance.mBinaryOp = BfTokenToBinaryOp(opConstraint->mOpToken->mToken);
+				if (opConstraintInstance.mBinaryOp == BfBinaryOp_None)
+				{
+					Fail("Invalid binary operator", opConstraint->mOpToken);
+					continue;
+				}
+			}
+			else if ((opConstraint->mOpToken->mToken == BfToken_Implicit) || (opConstraint->mOpToken->mToken == BfToken_Explicit))
+			{
+				opConstraintInstance.mCastToken = opConstraint->mOpToken->mToken;
+			}
+			else
+			{
+				opConstraintInstance.mUnaryOp = BfTokenToUnaryOp(opConstraint->mOpToken->mToken);
+				if (opConstraintInstance.mUnaryOp == BfBinaryOp_None)
+				{
+					Fail("Invalid unary operator", opConstraint->mOpToken);
+					continue;
+				}
+			}
+
+			if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Equals) != 0)
+				genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_Equals_Op);
+			genericParamInstance->mOperatorConstraints.Add(opConstraintInstance);
+
+			continue;
+		}
+
+		auto constraintTypeRef = BfNodeDynCast<BfTypeReference>(constraint);
+
 		if (bfAutocomplete != NULL)
 		if (bfAutocomplete != NULL)
 			bfAutocomplete->CheckTypeRef(constraintTypeRef, true);
 			bfAutocomplete->CheckTypeRef(constraintTypeRef, true);
 		//TODO: Constraints may refer to other generic params (of either type or method)
 		//TODO: Constraints may refer to other generic params (of either type or method)
@@ -6189,7 +6262,7 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar
 		auto constraintType = ResolveTypeRef(constraintTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericMethodParamConstValue);
 		auto constraintType = ResolveTypeRef(constraintTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericMethodParamConstValue);
 		if (constraintType != NULL)
 		if (constraintType != NULL)
 		{
 		{
-			if ((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0)
+			if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0)
 			{
 			{
 				bool isValidTypeCode = false;				
 				bool isValidTypeCode = false;				
 				BfTypeCode typeCode = BfTypeCode_None;
 				BfTypeCode typeCode = BfTypeCode_None;
@@ -6238,26 +6311,57 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar
 			}
 			}
 			else
 			else
 			{
 			{
+				bool checkEquality = false;
+
 				if (constraintType->IsPrimitiveType())
 				if (constraintType->IsPrimitiveType())
 				{
 				{
-					Fail("Primitive constraints are not allowed unless preceded with 'const'", constraintTypeRef);
-					continue;
+					if (isUnspecialized)
+					{
+						Fail("Primitive constraints are not allowed unless preceded with 'const'", constraintTypeRef);
+						continue;
+					}					
+					checkEquality = true;
 				}
 				}
 
 
 				if (constraintType->IsArray())
 				if (constraintType->IsArray())
 				{
 				{
-					Fail("Array constraints are not allowed.  If a constant-sized array was intended, an type parameterized by a const generic param can be used (ie: where T : int[T2] where T2 : const int)", constraintTypeRef);
+					if (isUnspecialized)					
+					{
+						Fail("Array constraints are not allowed.  If a constant-sized array was intended, an type parameterized by a const generic param can be used (ie: where T : int[T2] where T2 : const int)", constraintTypeRef);
+						continue;
+					}
+					checkEquality = true;
+				}
+
+				if (constraintType->IsGenericParam())
+				{
 					continue;
 					continue;
 				}
 				}
 
 
 				if ((!constraintType->IsTypeInstance()) && (!constraintType->IsSizedArray()))
 				if ((!constraintType->IsTypeInstance()) && (!constraintType->IsSizedArray()))
 				{
 				{
-					Fail(StrFormat("Type '%s' is not allowed as a generic constraint", TypeToString(constraintType).c_str()), constraintTypeRef);
-					continue;
+					if (isUnspecialized)
+					{
+						Fail(StrFormat("Type '%s' is not allowed as a generic constraint", TypeToString(constraintType).c_str()), constraintTypeRef);
+						continue;
+					}
+					checkEquality = true;
 				}
 				}
 
 
-				if (constraintType->IsInterface())
+				if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Equals) != 0)
+				{
+					genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_Equals_Type);
+					checkEquality = true;
+				}
+
+				if (checkEquality)
+				{
+					genericParamInstance->mTypeConstraint = constraintType;
+				}
+				else if (constraintType->IsInterface())
 				{
 				{
+ 					if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Equals) != 0)
+ 						genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_Equals_IFace);
 					genericParamInstance->mInterfaceConstraints.push_back(constraintType->ToTypeInstance());
 					genericParamInstance->mInterfaceConstraints.push_back(constraintType->ToTypeInstance());
 				}
 				}
 				else
 				else
@@ -6292,7 +6396,7 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar
 		}
 		}
 	}
 	}
 
 
-	if (((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0) &&
+	if (((constraintDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0) &&
 		(genericParamInstance->mTypeConstraint == NULL))
 		(genericParamInstance->mTypeConstraint == NULL))
 		genericParamInstance->mTypeConstraint = GetPrimitiveType(BfTypeCode_IntPtr);
 		genericParamInstance->mTypeConstraint = GetPrimitiveType(BfTypeCode_IntPtr);
 }
 }
@@ -6342,7 +6446,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 		{
 		{
 			argMayBeReferenceType = true;
 			argMayBeReferenceType = true;
 		}
 		}
-	}	
+	}
 
 
 	if (checkArgType->IsObjectOrInterface())
 	if (checkArgType->IsObjectOrInterface())
 		argMayBeReferenceType = true;
 		argMayBeReferenceType = true;
@@ -6356,7 +6460,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 	{
 	{
 		if (!ignoreErrors)
 		if (!ignoreErrors)
 			*errorOut = Fail(StrFormat("The type '%s' must be a value type in order to use it as parameter '%s' for '%s'",
 			*errorOut = Fail(StrFormat("The type '%s' must be a value type in order to use it as parameter '%s' for '%s'",
-				TypeToString(origCheckArgType).c_str(), genericParamInst->GetGenericParamDef()->mName.c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
+				TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 		return false;
 		return false;
 	}
 	}
 	
 	
@@ -6365,7 +6469,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 	{
 	{
 		if (!ignoreErrors)
 		if (!ignoreErrors)
 			*errorOut = Fail(StrFormat("The type '%s' must be a pointer type in order to use it as parameter '%s' for '%s'",
 			*errorOut = Fail(StrFormat("The type '%s' must be a pointer type in order to use it as parameter '%s' for '%s'",
-				TypeToString(origCheckArgType).c_str(), genericParamInst->GetGenericParamDef()->mName.c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
+				TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -6374,7 +6478,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 	{
 	{
 		if (!ignoreErrors)
 		if (!ignoreErrors)
 			*errorOut = Fail(StrFormat("The type '%s' must be a reference type in order to use it as parameter '%s' for '%s'",
 			*errorOut = Fail(StrFormat("The type '%s' must be a reference type in order to use it as parameter '%s' for '%s'",
-				TypeToString(origCheckArgType).c_str(), genericParamInst->GetGenericParamDef()->mName.c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
+				TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -6384,7 +6488,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 		{
 		{
 			if (!ignoreErrors)
 			if (!ignoreErrors)
 				*errorOut = Fail(StrFormat("The type '%s' must be a const value in order to use it as parameter '%s' for '%s'",
 				*errorOut = Fail(StrFormat("The type '%s' must be a const value in order to use it as parameter '%s' for '%s'",
-					TypeToString(origCheckArgType).c_str(), genericParamInst->GetGenericParamDef()->mName.c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
+					TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 			return false;
 			return false;
 		}		
 		}		
 	}
 	}
@@ -6394,12 +6498,12 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 		{
 		{
 			if (!ignoreErrors)
 			if (!ignoreErrors)
 				*errorOut = Fail(StrFormat("The value '%s' cannot be used for generic type parameter '%s' for '%s'",
 				*errorOut = Fail(StrFormat("The value '%s' cannot be used for generic type parameter '%s' for '%s'",
-					TypeToString(origCheckArgType).c_str(), genericParamInst->GetGenericParamDef()->mName.c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
+					TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 			return false;
 			return false;
 		}
 		}
 	}
 	}
 
 
-	if ((genericParamInst->mInterfaceConstraints.size() == 0) && (genericParamInst->mTypeConstraint == NULL))
+	if ((genericParamInst->mInterfaceConstraints.IsEmpty()) && (genericParamInst->mOperatorConstraints.IsEmpty()) && (genericParamInst->mTypeConstraint == NULL))
 		return true;
 		return true;
 	
 	
 	if (checkArgType->IsPointer())
 	if (checkArgType->IsPointer())
@@ -6435,7 +6539,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 								if (!mCompiler->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, constExprValueType->mValue.mInt64))
 								if (!mCompiler->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, constExprValueType->mValue.mInt64))
 								{
 								{
 									if (!ignoreErrors)
 									if (!ignoreErrors)
-										*errorOut = Fail(StrFormat("Const generic argument '%s', declared with const '%lld', does not fit into const constraint '%s' for '%s'", genericParamInst->GetGenericParamDef()->mName.c_str(),
+										*errorOut = Fail(StrFormat("Const generic argument '%s', declared with const '%lld', does not fit into const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
 											constExprValueType->mValue.mInt64, TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 											constExprValueType->mValue.mInt64, TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 									return false;
 									return false;
 								}
 								}
@@ -6443,7 +6547,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 							else
 							else
 							{
 							{
 								if (!ignoreErrors)
 								if (!ignoreErrors)
-									*errorOut = Fail(StrFormat("Const generic argument '%s', declared with integer const '%lld', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetGenericParamDef()->mName.c_str(),
+									*errorOut = Fail(StrFormat("Const generic argument '%s', declared with integer const '%lld', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
 										constExprValueType->mValue.mInt64, TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 										constExprValueType->mValue.mInt64, TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 								return false;
 								return false;
 							}
 							}
@@ -6455,7 +6559,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 								char valStr[64];
 								char valStr[64];
 								ExactMinimalDoubleToStr(constExprValueType->mValue.mDouble, valStr);
 								ExactMinimalDoubleToStr(constExprValueType->mValue.mDouble, valStr);
 								if (!ignoreErrors)
 								if (!ignoreErrors)
-									*errorOut = Fail(StrFormat("Const generic argument '%s', declared with floating point const '%s', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetGenericParamDef()->mName.c_str(),
+									*errorOut = Fail(StrFormat("Const generic argument '%s', declared with floating point const '%s', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
 										valStr, TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 										valStr, TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 								return false;
 								return false;
 							}
 							}
@@ -6514,7 +6618,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 			if (!constraintMatched)
 			if (!constraintMatched)
 			{
 			{
 				if (!ignoreErrors)
 				if (!ignoreErrors)
-					*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must derive from '%s'", genericParamInst->GetGenericParamDef()->mName.c_str(),
+					*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must derive from '%s'", genericParamInst->GetName().c_str(),
 						TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(convCheckConstraint).c_str(),
 						TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(convCheckConstraint).c_str(),
 						TypeToString(genericParamInst->mTypeConstraint).c_str()), checkArgTypeRef);
 						TypeToString(genericParamInst->mTypeConstraint).c_str()), checkArgTypeRef);
 				return false;
 				return false;
@@ -6529,7 +6633,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 		if (checkTypeInst->mTypeDef->mIsConcrete)
 		if (checkTypeInst->mTypeDef->mIsConcrete)
 		{
 		{
 			if (!ignoreErrors)
 			if (!ignoreErrors)
-				*errorOut = Fail(StrFormat("Generic argument '%s', declared to be concrete interface '%s' for '%s', must be a concrete type", genericParamInst->GetGenericParamDef()->mName.c_str(),
+				*errorOut = Fail(StrFormat("Generic argument '%s', declared to be concrete interface '%s' for '%s', must be a concrete type", genericParamInst->GetName().c_str(),
 					TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 					TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
 			return false;
 			return false;
 		}
 		}
@@ -6570,11 +6674,100 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
 		if (!implementsInterface)
 		if (!implementsInterface)
 		{
 		{
 			if (!ignoreErrors)
 			if (!ignoreErrors)
-				*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must implement '%s'", genericParamInst->GetGenericParamDef()->mName.c_str(), 
+				*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must implement '%s'", genericParamInst->GetName().c_str(), 
 					TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(checkConstraint).c_str()), checkArgTypeRef);
 					TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(checkConstraint).c_str()), checkArgTypeRef);
 			return false;
 			return false;
 		}
 		}
 	}
 	}
+
+	for (auto& checkOpConstraint : genericParamInst->mOperatorConstraints)
+	{
+		auto leftType = checkOpConstraint.mLeftType;
+		if ((leftType != NULL) && (leftType->IsUnspecializedType()))
+			leftType = ResolveGenericType(leftType, *methodGenericArgs);
+		if (leftType != NULL)
+			leftType = FixIntUnknown(leftType);		
+		
+		auto rightType = checkOpConstraint.mRightType;
+		if ((rightType != NULL) && (rightType->IsUnspecializedType()))
+			rightType = ResolveGenericType(rightType, *methodGenericArgs);
+		if (rightType != NULL)
+			rightType = FixIntUnknown(rightType);
+		
+		if (checkOpConstraint.mBinaryOp != BfBinaryOp_None)
+		{						
+			BfExprEvaluator exprEvaluator(this);						
+
+			BfTypedValue leftValue(mBfIRBuilder->GetFakeVal(), leftType);
+			BfTypedValue rightValue(mBfIRBuilder->GetFakeVal(), rightType);
+
+			//
+			{
+				SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
+				SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
+				exprEvaluator.PerformBinaryOperation(NULL, NULL, checkOpConstraint.mBinaryOp, NULL, BfBinOpFlag_NoClassify, leftValue, rightValue);
+			}
+
+			if ((exprEvaluator.mResult == NULL) || 
+				(!CanImplicitlyCast(exprEvaluator.mResult, origCheckArgType)))
+			{
+				if (!ignoreErrors)
+					*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must result from binary operation '%s %s %s'", genericParamInst->GetName().c_str(),
+						TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), 
+						TypeToString(leftType).c_str(), BfGetOpName(checkOpConstraint.mBinaryOp), TypeToString(rightType).c_str()
+					), checkArgTypeRef);
+				return false;
+			}
+			
+		}
+		else
+		{
+			BfTypedValue rightValue(mBfIRBuilder->GetFakeVal(), rightType);
+			
+			StringT<128> failedOpName;
+
+			if (checkOpConstraint.mCastToken == BfToken_Implicit)
+			{
+				if (!CanImplicitlyCast(rightValue, origCheckArgType, BfCastFlags_SilentFail))
+					failedOpName = "implicit conversion from '";
+			}
+			else
+			{
+				SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
+				SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
+
+				if (checkOpConstraint.mCastToken == BfToken_Explicit)
+				{
+					if (!CastToValue(NULL, rightValue, origCheckArgType, (BfCastFlags)(BfCastFlags_Explicit | BfCastFlags_SilentFail)))
+						failedOpName = "explicit conversion from '";					
+				}
+				else
+				{
+					BfExprEvaluator exprEvaluator(this);
+					exprEvaluator.mResult = rightValue;
+					exprEvaluator.PerformUnaryOperation(NULL, checkOpConstraint.mUnaryOp, NULL);
+
+					if ((exprEvaluator.mResult == NULL) ||
+						(!CanImplicitlyCast(exprEvaluator.mResult, origCheckArgType)))
+					{
+						failedOpName += "unary operation '";
+						failedOpName += BfGetOpName(checkOpConstraint.mUnaryOp);
+					}
+				}
+			}
+
+			if (!failedOpName.IsEmpty())
+			{
+				if (!ignoreErrors)
+					*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must result from %s%s'", genericParamInst->GetName().c_str(),
+						TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(),
+						failedOpName.c_str(), TypeToString(rightType).c_str()
+					), checkArgTypeRef);
+				return false;
+			}
+		}		
+	}
+
 	return true;
 	return true;
 }
 }
 
 
@@ -10389,6 +10582,9 @@ bool BfModule::CheckModifyValue(BfTypedValue& typedValue, BfAstNode* refNode, co
 
 
 bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB)
 bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB)
 {
 {
+	// If one is an interface and the other is an impl, B is the impl
+	auto implOwner = methodB->GetOwner();
+
 	if (methodA->mMethodDef->mIsLocalMethod)
 	if (methodA->mMethodDef->mIsLocalMethod)
 	{
 	{
 		int sepPosA = (int)BF_MIN(methodA->mMethodDef->mName.IndexOf('@'), methodA->mMethodDef->mName.length());
 		int sepPosA = (int)BF_MIN(methodA->mMethodDef->mName.IndexOf('@'), methodA->mMethodDef->mName.length());
@@ -10421,7 +10617,7 @@ bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstan
 			return false;
 			return false;
 		if (operatorA->mOperatorDeclaration->mIsConvOperator)
 		if (operatorA->mOperatorDeclaration->mIsConvOperator)
 		{
 		{
-			if (methodA->mReturnType != methodB->mReturnType)
+			if (!BfTypeUtils::TypeEquals(methodA->mReturnType, methodB->mReturnType, implOwner))
 				return false;
 				return false;
 		}
 		}
 	}
 	}
@@ -10437,13 +10633,11 @@ bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstan
 
 
 	for (int paramIdx = 0; paramIdx < (int)methodA->GetParamCount() - implicitParamCountA; paramIdx++)	
 	for (int paramIdx = 0; paramIdx < (int)methodA->GetParamCount() - implicitParamCountA; paramIdx++)	
 	{
 	{
-		if ((methodA->GetParamType(paramIdx + implicitParamCountA) != methodB->GetParamType(paramIdx + implicitParamCountB)) ||
+		if ((!BfTypeUtils::TypeEquals(methodA->GetParamType(paramIdx + implicitParamCountA), methodB->GetParamType(paramIdx + implicitParamCountB), implOwner)) ||
 			(methodA->GetParamKind(paramIdx + implicitParamCountA) != methodB->GetParamKind(paramIdx + implicitParamCountB)))
 			(methodA->GetParamKind(paramIdx + implicitParamCountA) != methodB->GetParamKind(paramIdx + implicitParamCountB)))
 			return false;
 			return false;
 	}
 	}
-
 	
 	
-
 	// Compare generic params.  Generic params are part of the method signature here
 	// Compare generic params.  Generic params are part of the method signature here
 	if (methodA->GetNumGenericParams() != methodB->GetNumGenericParams())
 	if (methodA->GetNumGenericParams() != methodB->GetNumGenericParams())
 		return false;	
 		return false;	
@@ -11403,6 +11597,12 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM
 		auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, genericParamIdx);
 		auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, genericParamIdx);
 		methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);		
 		methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);		
 	}
 	}
+
+	for (int externConstraintIdx = 0; externConstraintIdx < (int)methodDef->mExternalConstraints.size(); externConstraintIdx++)
+	{
+		auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, externConstraintIdx + (int)methodDef->mGenericParams.size());
+		methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);
+	}
 	
 	
 	bool addToWorkList = !processNow;
 	bool addToWorkList = !processNow;
 	if (mCompiler->GetAutoComplete() != NULL)
 	if (mCompiler->GetAutoComplete() != NULL)
@@ -18578,23 +18778,48 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
 	BfAutoComplete* bfAutocomplete = NULL;
 	BfAutoComplete* bfAutocomplete = NULL;
 	if (mCompiler->mResolvePassData != NULL)
 	if (mCompiler->mResolvePassData != NULL)
 		bfAutocomplete = mCompiler->mResolvePassData->mAutoComplete;
 		bfAutocomplete = mCompiler->mResolvePassData->mAutoComplete;
-	
-	for (int genericParamIdx = 0; genericParamIdx < (int)methodInstance->GetNumGenericArguments(); genericParamIdx++)
-	{			
-		auto genericParamDef = methodDef->mGenericParams[genericParamIdx];
-		ResolveGenericParamConstraints(methodInstance->mMethodInfoEx->mGenericParams[genericParamIdx], methodDef->mGenericParams, genericParamIdx);
-
-		if (bfAutocomplete != NULL)
+		
+	if (methodInstance->mMethodInfoEx != NULL)
+	{
+		for (int genericParamIdx = 0; genericParamIdx < (int)methodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
 		{
 		{
-			for (auto nameNode : genericParamDef->mNameNodes)
+			auto genericParam = methodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
+			if (genericParamIdx < (int)methodDef->mGenericParams.size())
 			{
 			{
-				HandleMethodGenericParamRef(nameNode, typeDef, methodDef, genericParamIdx);
+				genericParam->mExternType = GetGenericParamType(BfGenericParamKind_Method, genericParamIdx);
+			}
+			else
+			{
+				auto externConstraintDef = genericParam->GetExternConstraintDef();
+				genericParam->mExternType = ResolveTypeRef(externConstraintDef->mTypeRef);
+
+				auto autoComplete = mCompiler->GetAutoComplete();
+				if (autoComplete != NULL)
+					autoComplete->CheckTypeRef(externConstraintDef->mTypeRef, false);
+
+				if (genericParam->mExternType != NULL)
+				{
+					//
+				}
+				else
+					genericParam->mExternType = GetPrimitiveType(BfTypeCode_Var);
+			}
+
+			ResolveGenericParamConstraints(genericParam, methodInstance->mIsUnspecialized);
+
+			if (genericParamIdx < (int)methodDef->mGenericParams.size())
+			{
+				auto genericParamDef = methodDef->mGenericParams[genericParamIdx];
+				if (bfAutocomplete != NULL)
+				{
+					for (auto nameNode : genericParamDef->mNameNodes)
+					{
+						HandleMethodGenericParamRef(nameNode, typeDef, methodDef, genericParamIdx);
+					}
+				}
 			}
 			}
 		}
 		}
-	}
 
 
-	if (methodInstance->mMethodInfoEx != NULL)
-	{
 		for (auto genericParam : methodInstance->mMethodInfoEx->mGenericParams)
 		for (auto genericParam : methodInstance->mMethodInfoEx->mGenericParams)
 		{
 		{
 			for (auto constraintTypeInst : genericParam->mInterfaceConstraints)
 			for (auto constraintTypeInst : genericParam->mInterfaceConstraints)
@@ -19412,7 +19637,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
 						{
 						{
 							if (!typeInstance->IsTypeMemberAccessible(checkMethod->mDeclaringType, methodDef->mDeclaringType))
 							if (!typeInstance->IsTypeMemberAccessible(checkMethod->mDeclaringType, methodDef->mDeclaringType))
 								continue;
 								continue;
-							
+														
 							bool silentlyAllow = false;							
 							bool silentlyAllow = false;							
 							if (checkMethod->mDeclaringType != methodDef->mDeclaringType)
 							if (checkMethod->mDeclaringType != methodDef->mDeclaringType)
 							{								
 							{								
@@ -19424,10 +19649,13 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
 								else
 								else
 									silentlyAllow = true;
 									silentlyAllow = true;
 							}
 							}
+
+							if ((checkMethod->mCommutableKind == BfCommutableKind_Reverse) || (methodDef->mCommutableKind == BfCommutableKind_Reverse))
+								silentlyAllow = true;
 							
 							
 							if (!silentlyAllow)
 							if (!silentlyAllow)
-							{	
-								if (!methodDef->mName.IsEmpty())
+							{
+								if ((!methodDef->mName.IsEmpty()) || (checkMethodInstance->mMethodDef->mIsOperator))
 								{
 								{
 									auto refNode = methodDef->GetRefNode();
 									auto refNode = methodDef->GetRefNode();
 									auto bfError = Fail("Method already declared with the same parameter types", refNode, true);
 									auto bfError = Fail("Method already declared with the same parameter types", refNode, true);

+ 3 - 1
IDEHelper/Compiler/BfModule.h

@@ -1537,6 +1537,7 @@ public:
 	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 ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef);
 	bool ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef);
+	void CheckInjectNewRevision(BfTypeInstance* typeInstance);
 	bool InitType(BfType* resolvedTypeRef, BfPopulateType populateType);
 	bool InitType(BfType* resolvedTypeRef, BfPopulateType populateType);
 	bool CheckAccessMemberProtection(BfProtection protection, BfType* memberType);
 	bool CheckAccessMemberProtection(BfProtection protection, BfType* memberType);
 	bool CheckDefineMemberProtection(BfProtection protection, BfType* memberType);	
 	bool CheckDefineMemberProtection(BfProtection protection, BfType* memberType);	
@@ -1589,7 +1590,7 @@ public:
 	void FixIntUnknown(BfTypedValue& lhs, BfTypedValue& rhs);
 	void FixIntUnknown(BfTypedValue& lhs, BfTypedValue& rhs);
 	BfTypeDef* ResolveGenericInstanceDef(BfGenericInstanceTypeRef* genericTypeRef);	
 	BfTypeDef* ResolveGenericInstanceDef(BfGenericInstanceTypeRef* genericTypeRef);	
 	BfType* ResolveType(BfType* lookupType, BfPopulateType populateType = BfPopulateType_Data);	
 	BfType* ResolveType(BfType* lookupType, BfPopulateType populateType = BfPopulateType_Data);	
-	void ResolveGenericParamConstraints(BfGenericParamInstance* genericParamInstance, const Array<BfGenericParamDef*>& genericParamDefs, int genericParamIdx);
+	void ResolveGenericParamConstraints(BfGenericParamInstance* genericParamInstance, bool isUnspecialized);
 	String GenericParamSourceToString(const BfGenericParamSource& genericParamSource);
 	String GenericParamSourceToString(const BfGenericParamSource& genericParamSource);
 	bool CheckGenericConstraints(const BfGenericParamSource& genericParamSource, BfType* checkArgType, BfAstNode* checkArgTypeRef, BfGenericParamInstance* genericParamInst, BfTypeVector* methodGenericArgs = NULL, BfError** errorOut = NULL);
 	bool CheckGenericConstraints(const BfGenericParamSource& genericParamSource, BfType* checkArgType, BfAstNode* checkArgTypeRef, BfGenericParamInstance* genericParamInst, BfTypeVector* methodGenericArgs = NULL, BfError** errorOut = NULL);
 	BfIRValue AllocLocalVariable(BfType* type, const StringImpl& name, bool doLifetimeEnd = true);
 	BfIRValue AllocLocalVariable(BfType* type, const StringImpl& name, bool doLifetimeEnd = true);
@@ -1619,6 +1620,7 @@ public:
 	BfGenericParamType* GetGenericParamType(BfGenericParamKind paramKind, int paramIdx);
 	BfGenericParamType* GetGenericParamType(BfGenericParamKind paramKind, int paramIdx);
 	BfType* ResolveGenericType(BfType* unspecializedType, const BfTypeVector& methodGenericArguments, bool allowFail = false);	
 	BfType* ResolveGenericType(BfType* unspecializedType, const BfTypeVector& methodGenericArguments, bool allowFail = false);	
 	bool IsUnboundGeneric(BfType* type);
 	bool IsUnboundGeneric(BfType* type);
+	BfGenericParamInstance* GetGenericTypeParamInstance(int paramIdx);
 	BfGenericParamInstance* GetGenericParamInstance(BfGenericParamType* type);	
 	BfGenericParamInstance* GetGenericParamInstance(BfGenericParamType* type);	
 	BfTypeInstance* GetBaseType(BfTypeInstance* typeInst);
 	BfTypeInstance* GetBaseType(BfTypeInstance* typeInst);
 	void HandleTypeGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, int typeGenericParamIdx);
 	void HandleTypeGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, int typeGenericParamIdx);

+ 226 - 113
IDEHelper/Compiler/BfModuleTypeUtils.cpp

@@ -57,6 +57,7 @@ BfGenericExtensionEntry* BfModule::BuildGenericExtensionInfo(BfGenericTypeInstan
 	}
 	}
 
 
 	BfTypeState typeState;
 	BfTypeState typeState;
+	typeState.mTypeInstance = genericTypeInst;
 	typeState.mCurTypeDef = partialTypeDef;
 	typeState.mCurTypeDef = partialTypeDef;
 	SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
 	SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
 
 
@@ -71,6 +72,7 @@ BfGenericExtensionEntry* BfModule::BuildGenericExtensionInfo(BfGenericTypeInstan
 	for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
 	for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
 	{
 	{
 		auto genericParamInstance = new BfGenericTypeParamInstance(partialTypeDef, paramIdx);
 		auto genericParamInstance = new BfGenericTypeParamInstance(partialTypeDef, paramIdx);
+		genericParamInstance->mExternType = GetGenericParamType(BfGenericParamKind_Type, paramIdx);
 		genericExEntry->mGenericParams.push_back(genericParamInstance);
 		genericExEntry->mGenericParams.push_back(genericParamInstance);
 	}
 	}
 
 
@@ -83,7 +85,7 @@ BfGenericExtensionEntry* BfModule::BuildGenericExtensionInfo(BfGenericTypeInstan
 		genericParamInstance->mInterfaceConstraints = rootGenericParamInstance->mInterfaceConstraints;
 		genericParamInstance->mInterfaceConstraints = rootGenericParamInstance->mInterfaceConstraints;
 		genericParamInstance->mGenericParamFlags |= rootGenericParamInstance->mGenericParamFlags;
 		genericParamInstance->mGenericParamFlags |= rootGenericParamInstance->mGenericParamFlags;
 
 
-		ResolveGenericParamConstraints(genericParamInstance, partialTypeDef->mGenericParamDefs, paramIdx);
+		ResolveGenericParamConstraints(genericParamInstance, genericTypeInst->IsUnspecializedType());
 	}
 	}
 
 
 	for (auto genericParam : genericExEntry->mGenericParams)
 	for (auto genericParam : genericExEntry->mGenericParams)
@@ -116,8 +118,9 @@ bool BfModule::BuildGenericParams(BfType* resolvedTypeRef)
 	for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
 	for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
 	{
 	{
 		auto genericParamInstance = new BfGenericTypeParamInstance(typeDef, paramIdx);
 		auto genericParamInstance = new BfGenericTypeParamInstance(typeDef, paramIdx);
+		genericParamInstance->mExternType = GetGenericParamType(BfGenericParamKind_Type, paramIdx);
 		genericTypeInst->mGenericParams.push_back(genericParamInstance);
 		genericTypeInst->mGenericParams.push_back(genericParamInstance);
-	}
+	}	
 
 
 	if (!typeDef->mPartials.empty())
 	if (!typeDef->mPartials.empty())
 	{
 	{
@@ -130,7 +133,7 @@ bool BfModule::BuildGenericParams(BfType* resolvedTypeRef)
 				{
 				{
 					auto genericParamDef = typeDef->mGenericParamDefs[paramIdx];
 					auto genericParamDef = typeDef->mGenericParamDefs[paramIdx];
 					auto genericParamInstance = genericTypeInst->mGenericParams[paramIdx];
 					auto genericParamInstance = genericTypeInst->mGenericParams[paramIdx];
-					ResolveGenericParamConstraints(genericParamInstance, typeDef->mGenericParamDefs, paramIdx);
+					ResolveGenericParamConstraints(genericParamInstance, genericTypeInst->IsUnspecializedType());
 
 
 					for (auto nameNode : genericParamDef->mNameNodes)
 					for (auto nameNode : genericParamDef->mNameNodes)
 					{
 					{
@@ -164,15 +167,17 @@ bool BfModule::BuildGenericParams(BfType* resolvedTypeRef)
 	}
 	}
 	else
 	else
 	{
 	{
-		for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
+		for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mGenericParams.size(); paramIdx++)
 		{			
 		{			
 			auto genericParamInstance = genericTypeInst->mGenericParams[paramIdx];
 			auto genericParamInstance = genericTypeInst->mGenericParams[paramIdx];
-			ResolveGenericParamConstraints(genericParamInstance, typeDef->mGenericParamDefs, paramIdx);
-			auto genericParamDef = typeDef->mGenericParamDefs[paramIdx];
-
-			for (auto nameNode : genericParamDef->mNameNodes)
+			ResolveGenericParamConstraints(genericParamInstance, genericTypeInst->IsUnspecializedType());
+			auto genericParamDef = genericParamInstance->GetGenericParamDef();
+			if (genericParamDef != NULL)
 			{
 			{
-				HandleTypeGenericParamRef(nameNode, typeDef, paramIdx);
+				for (auto nameNode : genericParamDef->mNameNodes)
+				{
+					HandleTypeGenericParamRef(nameNode, typeDef, paramIdx);
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -261,6 +266,12 @@ bool BfModule::AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGeneri
 			return false;
 			return false;
 	}
 	}
 
 
+	for (auto& innerOp : checkInner->mOperatorConstraints)
+	{
+		if (!checkOuter->mOperatorConstraints.Contains(innerOp))
+			return false;
+	}
+
 	return true;
 	return true;
 }
 }
 
 
@@ -305,6 +316,37 @@ bool BfModule::ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDe
 	return false;
 	return false;
 }
 }
 
 
+void BfModule::CheckInjectNewRevision(BfTypeInstance* typeInstance)
+{
+	if ((typeInstance != NULL) && (typeInstance->mTypeDef != NULL))
+	{
+		if (typeInstance->mTypeDef->mNextRevision != NULL)
+		{
+			// It's possible that our main compiler thread is generating a new typedef while we're autocompleting. This handles that case...
+			if (typeInstance->mDefineState == BfTypeDefineState_Undefined)
+			{
+				if (typeInstance->IsBoxed())
+				{
+					BfBoxedType* boxedType = (BfBoxedType*)typeInstance;
+					BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance();
+					PopulateType(innerType, BfPopulateType_Data);
+				}
+				else
+				{
+					mContext->HandleChangedTypeDef(typeInstance->mTypeDef);
+					mSystem->InjectNewRevision(typeInstance->mTypeDef);
+				}
+			}
+			else
+			{
+				BF_ASSERT(mCompiler->IsAutocomplete());
+			}
+		}
+		if ((!typeInstance->IsDeleting()) && (!mCompiler->IsAutocomplete()))
+			BF_ASSERT((typeInstance->mTypeDef->mDefState == BfTypeDef::DefState_Defined) || (typeInstance->mTypeDef->mDefState == BfTypeDef::DefState_New));
+	}
+}
+
 bool BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType)
 bool BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType)
 {
 {
 	BP_ZONE("BfModule::InitType");
 	BP_ZONE("BfModule::InitType");
@@ -318,6 +360,8 @@ bool BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType)
 	auto typeInst = resolvedTypeRef->ToTypeInstance();
 	auto typeInst = resolvedTypeRef->ToTypeInstance();
 	if (typeInst != NULL)
 	if (typeInst != NULL)
 	{
 	{
+		CheckInjectNewRevision(typeInst);
+
 		if (typeInst->mBaseType != NULL)
 		if (typeInst->mBaseType != NULL)
 			BF_ASSERT((typeInst->mBaseType->mRebuildFlags & BfTypeRebuildFlag_Deleted) == 0);
 			BF_ASSERT((typeInst->mBaseType->mRebuildFlags & BfTypeRebuildFlag_Deleted) == 0);
 
 
@@ -606,22 +650,6 @@ bool BfModule::CheckCircularDataError()
 {
 {
 	bool hadError = false;
 	bool hadError = false;
 
 
-	{
-		int count = 0;
-		auto checkTypeState = mContext->mCurTypeState;
-		while (checkTypeState != NULL)
-		{
-			checkTypeState = checkTypeState->mPrevState;
-			count++;
-		}
-
-		if (count > 20)
-		{
-			NOP;
-		}
-	}
-
-
 	int checkIdx = 0;
 	int checkIdx = 0;
 	auto checkTypeState = mContext->mCurTypeState;
 	auto checkTypeState = mContext->mCurTypeState;
 	bool isPreBaseCheck = checkTypeState->mPopulateType == BfPopulateType_Declaration;
 	bool isPreBaseCheck = checkTypeState->mPopulateType == BfPopulateType_Declaration;
@@ -748,33 +776,7 @@ bool BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType
 		return true;
 		return true;
 
 
 	auto typeInstance = resolvedTypeRef->ToTypeInstance();
 	auto typeInstance = resolvedTypeRef->ToTypeInstance();
-	if ((typeInstance != NULL) && (typeInstance->mTypeDef != NULL))
-	{		
-		if (typeInstance->mTypeDef->mNextRevision != NULL)
-		{
-			// It's possible that our main compiler thread is generating a new typedef while we're autocompleting. This handles that case...
-			if (typeInstance->mDefineState == BfTypeDefineState_Undefined)
-			{				
-				if (typeInstance->IsBoxed())
-				{
-					BfBoxedType* boxedType = (BfBoxedType*)typeInstance;
-					BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance();					
-					PopulateType(innerType, BfPopulateType_Data);
-				}
-				else
-				{
-					mContext->HandleChangedTypeDef(typeInstance->mTypeDef);
-					mSystem->InjectNewRevision(typeInstance->mTypeDef);
-				}
-			}
-			else
-			{
-				BF_ASSERT(mCompiler->IsAutocomplete());
-			}
-		}		
-		if ((!typeInstance->IsDeleting()) && (!mCompiler->IsAutocomplete()))
-			BF_ASSERT(typeInstance->mTypeDef->mDefState == BfTypeDef::DefState_Defined);
-	}
+	CheckInjectNewRevision(typeInstance);	
 
 
 	BF_ASSERT((resolvedTypeRef->mRebuildFlags & (BfTypeRebuildFlag_Deleted | BfTypeRebuildFlag_DeleteQueued)) == 0);
 	BF_ASSERT((resolvedTypeRef->mRebuildFlags & (BfTypeRebuildFlag_Deleted | BfTypeRebuildFlag_DeleteQueued)) == 0);
 
 
@@ -2167,6 +2169,10 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 
 
 			if (propDef->mFieldDeclaration != NULL)
 			if (propDef->mFieldDeclaration != NULL)
 			{
 			{
+				BfTypeState typeState;
+				typeState.mCurTypeDef = propDef->mDeclaringType;
+				SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
+
 				if (propDef->mFieldDeclaration->mAttributes != NULL)
 				if (propDef->mFieldDeclaration->mAttributes != NULL)
 				{
 				{
 					auto customAttrs = GetCustomAttributes(propDef->mFieldDeclaration->mAttributes, BfAttributeTargets_Property);
 					auto customAttrs = GetCustomAttributes(propDef->mFieldDeclaration->mAttributes, BfAttributeTargets_Property);
@@ -2181,11 +2187,7 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 
 
 				auto propDecl = (BfPropertyDeclaration*)propDef->mFieldDeclaration;
 				auto propDecl = (BfPropertyDeclaration*)propDef->mFieldDeclaration;
 				if (propDecl->mExplicitInterface != NULL)
 				if (propDecl->mExplicitInterface != NULL)
-				{
-					BfTypeState typeState;
-					typeState.mCurTypeDef = propDef->mDeclaringType;
-					SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
-
+				{					
 					if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
 					if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
 						mCompiler->mResolvePassData->mAutoComplete->CheckTypeRef(propDecl->mExplicitInterface, false);
 						mCompiler->mResolvePassData->mAutoComplete->CheckTypeRef(propDecl->mExplicitInterface, false);
 					auto explicitInterface = ResolveTypeRef(propDecl->mExplicitInterface, BfPopulateType_Declaration);
 					auto explicitInterface = ResolveTypeRef(propDecl->mExplicitInterface, BfPopulateType_Declaration);
@@ -4841,9 +4843,15 @@ BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, BfPopulateType populateType
 	return resolvedtypeDefType;
 	return resolvedtypeDefType;
 }
 }
 
 
-// Get BaseClass even when we haven't populated the type yet
+// Get BaseClass even when we haven't populated the type yet2
 BfTypeInstance* BfModule::GetBaseType(BfTypeInstance* typeInst)
 BfTypeInstance* BfModule::GetBaseType(BfTypeInstance* typeInst)
 {
 {
+	if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mTypeInstance == typeInst))
+	{
+		if (typeInst->mBaseType == NULL)
+			return NULL;
+	}
+
 	if ((typeInst->mBaseType == NULL) && (typeInst != mContext->mBfObjectType))	
 	if ((typeInst->mBaseType == NULL) && (typeInst != mContext->mBfObjectType))	
 		PopulateType(typeInst, BfPopulateType_BaseType);
 		PopulateType(typeInst, BfPopulateType_BaseType);
 	return typeInst->mBaseType;
 	return typeInst->mBaseType;
@@ -5093,6 +5101,7 @@ bool BfModule::IsInnerType(BfType* checkInnerType, BfType* checkOuterType)
 
 
 bool BfModule::IsInnerType(BfTypeDef* checkInnerType, BfTypeDef* checkOuterType)
 bool BfModule::IsInnerType(BfTypeDef* checkInnerType, BfTypeDef* checkOuterType)
 {
 {
+	BF_ASSERT(!checkOuterType->mIsPartial);
 	if (checkInnerType->mNestDepth <= checkOuterType->mNestDepth)
 	if (checkInnerType->mNestDepth <= checkOuterType->mNestDepth)
 		return false;
 		return false;
 	while (true)
 	while (true)
@@ -5100,6 +5109,8 @@ bool BfModule::IsInnerType(BfTypeDef* checkInnerType, BfTypeDef* checkOuterType)
 		BfTypeDef* outerType = checkInnerType->mOuterType;
 		BfTypeDef* outerType = checkInnerType->mOuterType;
 		if (outerType == NULL)
 		if (outerType == NULL)
 			return false;
 			return false;
+		if (outerType->mIsPartial)
+			outerType = mSystem->GetCombinedPartial(outerType);
 		if (outerType == checkOuterType)
 		if (outerType == checkOuterType)
 			return true;
 			return true;
 		checkInnerType = checkInnerType->mOuterType;
 		checkInnerType = checkInnerType->mOuterType;
@@ -5482,11 +5493,8 @@ bool BfModule::IsUnboundGeneric(BfType* type)
 	return (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0;
 	return (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0;
 }
 }
 
 
-BfGenericParamInstance* BfModule::GetGenericParamInstance(BfGenericParamType* type)
+BfGenericParamInstance* BfModule::GetGenericTypeParamInstance(int genericParamIdx)
 {
 {
-	if (type->mGenericParamKind == BfGenericParamKind_Method)
-		return mCurMethodInstance->mMethodInfoEx->mGenericParams[type->mGenericParamIdx];
-
 	// When we're evaluating a method, make sure the params refer back to that method context
 	// When we're evaluating a method, make sure the params refer back to that method context
 	auto curTypeInstance = mCurTypeInstance;
 	auto curTypeInstance = mCurTypeInstance;
 	if (mCurMethodInstance != NULL)
 	if (mCurMethodInstance != NULL)
@@ -5505,23 +5513,35 @@ BfGenericParamInstance* BfModule::GetGenericParamInstance(BfGenericParamType* ty
 		auto activeTypeDef = GetActiveTypeDef(NULL, true);
 		auto activeTypeDef = GetActiveTypeDef(NULL, true);
 		if ((activeTypeDef->mTypeDeclaration != genericTypeInst->mTypeDef->mTypeDeclaration) && (activeTypeDef->IsExtension()))
 		if ((activeTypeDef->mTypeDeclaration != genericTypeInst->mTypeDef->mTypeDeclaration) && (activeTypeDef->IsExtension()))
 		{
 		{
+			BfTypeDef* lookupTypeDef = activeTypeDef;
+			while (lookupTypeDef->mNestDepth > genericTypeInst->mTypeDef->mNestDepth)
+				lookupTypeDef = lookupTypeDef->mOuterType;
+
 			BfGenericExtensionEntry* genericExEntry;
 			BfGenericExtensionEntry* genericExEntry;
-			if (genericTypeInst->mGenericExtensionInfo->mExtensionMap.TryGetValue(activeTypeDef, &genericExEntry))
+			if (genericTypeInst->mGenericExtensionInfo->mExtensionMap.TryGetValue(lookupTypeDef, &genericExEntry))
 			{
 			{
-				return genericExEntry->mGenericParams[type->mGenericParamIdx];
+				return genericExEntry->mGenericParams[genericParamIdx];
 			}
 			}
 			else
 			else
 			{
 			{
 				if ((mCompiler->mResolvePassData == NULL) || (mCompiler->mResolvePassData->mAutoComplete == NULL))
 				if ((mCompiler->mResolvePassData == NULL) || (mCompiler->mResolvePassData->mAutoComplete == NULL))
 				{
 				{
-					BF_FATAL("Invalid GetGenericParamInstance with extention");
+					BF_FATAL("Invalid GetGenericParamInstance with extension");
 				}
 				}
 			}
 			}
 		}
 		}
 	}
 	}
 
 
 	BF_ASSERT(genericTypeInst != NULL);
 	BF_ASSERT(genericTypeInst != NULL);
-	return genericTypeInst->mGenericParams[type->mGenericParamIdx];
+	return genericTypeInst->mGenericParams[genericParamIdx];
+}
+
+BfGenericParamInstance* BfModule::GetGenericParamInstance(BfGenericParamType* type)
+{
+	if (type->mGenericParamKind == BfGenericParamKind_Method)
+		return mCurMethodInstance->mMethodInfoEx->mGenericParams[type->mGenericParamIdx];
+
+	return GetGenericTypeParamInstance(type->mGenericParamIdx);
 }
 }
 
 
 BfType* BfModule::ResolveTypeResult(BfTypeReference* typeRef, BfType* resolvedTypeRef, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags)
 BfType* BfModule::ResolveTypeResult(BfTypeReference* typeRef, BfType* resolvedTypeRef, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags)
@@ -5717,7 +5737,7 @@ BfType* BfModule::ResolveTypeResult(BfTypeReference* typeRef, BfType* resolvedTy
 	bool hadError = false;
 	bool hadError = false;
 	hadError = !PopulateType(resolvedTypeRef, populateType);
 	hadError = !PopulateType(resolvedTypeRef, populateType);
 	
 	
-	if ((genericTypeInstance != NULL) && (populateType > BfPopulateType_Identity))
+	if ((genericTypeInstance != NULL) && (genericTypeInstance != mCurTypeInstance) && (populateType > BfPopulateType_Identity))
 	{
 	{
 		if (((genericTypeInstance->mHadValidateErrors) || (!genericTypeInstance->mValidatedGenericConstraints) || (genericTypeInstance->mIsUnspecializedVariation)) &&
 		if (((genericTypeInstance->mHadValidateErrors) || (!genericTypeInstance->mValidatedGenericConstraints) || (genericTypeInstance->mIsUnspecializedVariation)) &&
 			((mCurMethodInstance == NULL) || (!mCurMethodInstance->mIsUnspecializedVariation)) &&
 			((mCurMethodInstance == NULL) || (!mCurMethodInstance->mIsUnspecializedVariation)) &&
@@ -5852,6 +5872,12 @@ BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGene
 				}
 				}
 				if (checkTypeInst == skipCheckBaseType)
 				if (checkTypeInst == skipCheckBaseType)
 					break;
 					break;
+
+				if (checkTypeInst->mTypeDef == mCompiler->mNullableTypeDef)
+				{
+					NOP;
+				}				
+
 				checkTypeInst = GetBaseType(checkTypeInst);
 				checkTypeInst = GetBaseType(checkTypeInst);
 				allowPrivate = false;
 				allowPrivate = false;
 			}			
 			}			
@@ -5868,6 +5894,7 @@ BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGene
 				}
 				}
 				if (checkTypeInst == skipCheckBaseType)
 				if (checkTypeInst == skipCheckBaseType)
 					break;
 					break;
+				
 				checkTypeInst = GetBaseType(checkTypeInst);
 				checkTypeInst = GetBaseType(checkTypeInst);
 				allowPrivate = false;
 				allowPrivate = false;
 			}
 			}
@@ -6931,8 +6958,8 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
 		if ((mCurTypeInstance != NULL) && (typeDef->mGenericParamDefs.size() != 0))
 		if ((mCurTypeInstance != NULL) && (typeDef->mGenericParamDefs.size() != 0))
 		{
 		{
 			// Try to inherit generic params from current parent
 			// Try to inherit generic params from current parent
-			
-			auto outerType = typeDef->mOuterType;
+						
+			BfTypeDef* outerType = mSystem->GetCombinedPartial(typeDef->mOuterType);
 			BF_ASSERT(!outerType->mIsPartial);
 			BF_ASSERT(!outerType->mIsPartial);
 			if (TypeHasParent(mCurTypeInstance->mTypeDef, outerType))			
 			if (TypeHasParent(mCurTypeInstance->mTypeDef, outerType))			
 			{
 			{
@@ -8699,62 +8726,87 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
 	if ((typedVal.mType->IsGenericParam()) && (!toType->IsGenericParam()))
 	if ((typedVal.mType->IsGenericParam()) && (!toType->IsGenericParam()))
 	{
 	{
 		if (toType == mContext->mBfObjectType)
 		if (toType == mContext->mBfObjectType)
-		{
-			/*auto resolvedType = ResolveGenericType(typedVal.mType);
-			if (!resolvedType->IsGenericParam())
-			return CastToValue(srcNode, BfTypedValue(typedVal.mValue, resolvedType), toType, castFlags, silentFail);
-			return typedVal.mValue;*/
-			// Always allow casting to generic
-			return typedVal.mValue;
-		}
-
-		// For these casts, it's just important we get *A* value to work with here, 
-		//  as this is just use for unspecialized parsing.  We don't use the generated code
-		auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)typedVal.mType);
-		if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
-		{
+		{			
+			// Always allow casting from generic to object
 			return typedVal.mValue;
 			return typedVal.mValue;
 		}
 		}
-		if (toType->IsInterface())
-		{
-			for (auto iface : genericParamInst->mInterfaceConstraints)
-				if (TypeIsSubTypeOf(iface, toType->ToTypeInstance()))
-					return GetDefaultValue(toType);
-		}
 
 
-		if (genericParamInst->mTypeConstraint != NULL)
+		auto _CheckGenericParamInstance = [&](BfGenericParamInstance* genericParamInst)
 		{
 		{
-			auto constraintTypeInst = genericParamInst->mTypeConstraint->ToTypeInstance();
-			if ((constraintTypeInst != NULL) && (constraintTypeInst->mTypeDef == mCompiler->mEnumTypeDef))
+			if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
 			{
 			{
-				// Enum->int
-				if (toType->IsInteger())
-					return GetDefaultValue(toType);
+				return typedVal.mValue;
+			}
+			if (toType->IsInterface())
+			{
+				for (auto iface : genericParamInst->mInterfaceConstraints)
+					if (TypeIsSubTypeOf(iface, toType->ToTypeInstance()))
+						return GetDefaultValue(toType);
 			}
 			}
 
 
-			auto defaultFromValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint);
-			auto result = CastToValue(srcNode, defaultFromValue, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
+			if (genericParamInst->mTypeConstraint != NULL)
+			{
+				auto constraintTypeInst = genericParamInst->mTypeConstraint->ToTypeInstance();
+				if ((constraintTypeInst != NULL) && (constraintTypeInst->mTypeDef == mCompiler->mEnumTypeDef))
+				{
+					// Enum->int
+					if (toType->IsInteger())
+						return GetDefaultValue(toType);
+				}
+
+				auto defaultFromValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint);
+				auto result = CastToValue(srcNode, defaultFromValue, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
 
 
-			if (result)
+				if (result)
+				{
+					if ((genericParamInst->mTypeConstraint->IsDelegate()) && (toType->IsDelegate()))
+					{
+						// Don't allow cast when we are constrained by a delegate type, because BfMethodRefs can match and we require an actual alloc
+						Fail(StrFormat("Unable to cast '%s' to '%s' because delegate constraints allow valueless direct method references", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
+						return BfIRValue();
+					}
+					return result;
+				}
+			}
+
+			// Generic constrained with class or pointer type -> void*
+			if (toType->IsVoidPtr())
 			{
 			{
-				if ((genericParamInst->mTypeConstraint->IsDelegate()) && (toType->IsDelegate()))
+				if ((genericParamInst->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_StructPtr)) ||
+					((genericParamInst->mTypeConstraint != NULL) &&
+					((genericParamInst->mTypeConstraint->IsPointer()) || (genericParamInst->mTypeConstraint->IsObjectOrInterface()))))
 				{
 				{
-					// Don't allow cast when we are constrained by a delegate type, because BfMethodRefs can match and we require an actual alloc
-					Fail(StrFormat("Unable to cast '%s' to '%s' because delegate constraints allow valueless direct method references", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
-					return BfIRValue();
+					return GetDefaultValue(toType);
 				}
 				}
-				return result;
 			}
 			}
+
+			return BfIRValue();
+		};
+
+		BfIRValue retVal;
+
+		// For these casts, it's just important we get *A* value to work with here, 
+		//  as this is just use for unspecialized parsing.  We don't use the generated code
+		{
+			auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)typedVal.mType);
+			retVal = _CheckGenericParamInstance(genericParamInst);
+			if (retVal)
+				return retVal;
 		}
 		}
 
 
-		// Generic constrained with class or pointer type -> void*
-		if (toType->IsVoidPtr())
+		// Check method generic constraints
+		if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL))
 		{
 		{
-			if ((genericParamInst->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_StructPtr)) ||
-				((genericParamInst->mTypeConstraint != NULL) &&
-				((genericParamInst->mTypeConstraint->IsPointer()) || (genericParamInst->mTypeConstraint->IsObjectOrInterface()))))
+			for (int genericParamIdx = (int)mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size();
+				genericParamIdx < mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
 			{
 			{
-				return GetDefaultValue(toType);
+				auto genericParamInst = mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
+				if (genericParamInst->mExternType == typedVal.mType)
+				{
+					retVal = _CheckGenericParamInstance(genericParamInst);
+					if (retVal)
+						return retVal;
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -9612,7 +9664,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
 					}
 					}
 
 
 					return CastToValue(srcNode, operatorOut, toType, castFlags, resultFlags);
 					return CastToValue(srcNode, operatorOut, toType, castFlags, resultFlags);
-				}
+				}				
 			}
 			}
 
 
 			if (bestFromType == NULL)
 			if (bestFromType == NULL)
@@ -9628,6 +9680,55 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
 			Fail(StrFormat(errStr, TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
 			Fail(StrFormat(errStr, TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
 			return BfIRValue();
 			return BfIRValue();
 		}
 		}
+
+		// Check method generic constraints
+		if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL))
+		{
+			for (int genericParamIdx = 0; genericParamIdx < mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
+			{
+				auto genericParam = mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
+				for (auto& opConstraint : genericParam->mOperatorConstraints)
+				{
+					if ((opConstraint.mCastToken == BfToken_Implicit) ||
+						((explicitCast) && (opConstraint.mCastToken == BfToken_Explicit)))
+					{
+						// If we can convert OUR fromVal to the constraint's fromVal then we may match
+						if (CanImplicitlyCast(typedVal, opConstraint.mRightType))
+						{
+							// .. and we can convert the constraint's toType to OUR toType then we're good
+							auto opToVal = genericParam->mExternType;
+							if (CanImplicitlyCast(opToVal, toType))
+								return mBfIRBuilder->GetFakeVal();
+						}
+					}
+				}
+			}
+		}
+
+		// Check type generic constraints
+		if ((mCurTypeInstance->IsGenericTypeInstance()) && (mCurTypeInstance->IsUnspecializedType()))
+		{
+			auto genericTypeInst = (BfGenericTypeInstance*)mCurTypeInstance;
+			for (int genericParamIdx = 0; genericParamIdx < genericTypeInst->mGenericParams.size(); genericParamIdx++)
+			{
+				auto genericParam = GetGenericTypeParamInstance(genericParamIdx);
+				for (auto& opConstraint : genericParam->mOperatorConstraints)
+				{
+					if ((opConstraint.mCastToken == BfToken_Implicit) ||
+						((explicitCast) && (opConstraint.mCastToken == BfToken_Explicit)))
+					{
+						// If we can convert OUR fromVal to the constraint's fromVal then we may match
+						if (CanImplicitlyCast(typedVal, opConstraint.mRightType))
+						{
+							// .. and we can convert the constraint's toType to OUR toType then we're good
+							auto opToVal = genericParam->mExternType;
+							if (CanImplicitlyCast(opToVal, toType))
+								return mBfIRBuilder->GetFakeVal();
+						}
+					}
+				}
+			}
+		}
 	}
 	}
 
 
 	// Default typed primitive 'underlying casts' happen after checking cast operators
 	// Default typed primitive 'underlying casts' happen after checking cast operators
@@ -10047,8 +10148,16 @@ BfTypeDef* BfModule::FindCommonOuterType(BfTypeDef* type, BfTypeDef* type2)
 
 
 	while (curNestDepth >= 0)
 	while (curNestDepth >= 0)
 	{
 	{
-		if (type == type2)
-			return type;
+		if ((!type->mIsPartial) && (!type2->mIsPartial))
+		{
+			if (type == type2)
+				return type;
+		}
+		else
+		{
+			if (type->mFullNameEx == type2->mFullNameEx)
+				return type;
+		}
 		type = type->mOuterType;
 		type = type->mOuterType;
 		type2 = type2->mOuterType;
 		type2 = type2->mOuterType;
 		curNestDepth--;
 		curNestDepth--;
@@ -10627,7 +10736,11 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF
 		}
 		}
 
 
 		auto genericParamInstance = GetGenericParamInstance(genericParam);
 		auto genericParamInstance = GetGenericParamInstance(genericParam);
-		str += genericParamInstance->GetGenericParamDef()->mName;
+		auto genericParamDef = genericParamInstance->GetGenericParamDef();
+		if (genericParamDef != NULL)
+			str += genericParamInstance->GetGenericParamDef()->mName;
+		else
+			str += "external generic " + TypeToString(genericParamInstance->mExternType, typeNameFlags, genericMethodNameOverrides);
 		return;
 		return;
 	}
 	}
 	else if (resolvedType->IsRef())
 	else if (resolvedType->IsRef())

+ 1 - 0
IDEHelper/Compiler/BfParser.cpp

@@ -53,6 +53,7 @@ static bool IsWhitespaceOrPunctuation(char c)
 	case '!':
 	case '!':
 	case '%':
 	case '%':
 	case '&':
 	case '&':
+	case '|':
 	case '#':
 	case '#':
 	case '@':
 	case '@':
 	case '`':
 	case '`':

+ 4 - 2
IDEHelper/Compiler/BfPrinter.cpp

@@ -746,12 +746,14 @@ void BfPrinter::Visit(BfGenericConstraintsDeclaration* genericConstraints)
 {
 {
 	Visit(genericConstraints->ToBase());
 	Visit(genericConstraints->ToBase());
 
 
-	for (auto genericConstraint : genericConstraints->mGenericConstraints)
+	for (auto genericConstraintNode : genericConstraints->mGenericConstraints)
 	{
 	{
+		auto genericConstraint = BfNodeDynCast<BfGenericConstraint>(genericConstraintNode);
+
 		ExpectSpace();
 		ExpectSpace();
 		VisitChild(genericConstraint->mWhereToken);
 		VisitChild(genericConstraint->mWhereToken);
 		ExpectSpace();
 		ExpectSpace();
-		VisitChild(genericConstraint->mGenericParamName);
+		VisitChild(genericConstraint->mTypeRef);
 		ExpectSpace();
 		ExpectSpace();
 		VisitChild(genericConstraint->mColonToken);
 		VisitChild(genericConstraint->mColonToken);
 		ExpectSpace();
 		ExpectSpace();

+ 72 - 147
IDEHelper/Compiler/BfReducer.cpp

@@ -26,120 +26,6 @@ USING_NS_BF;
 	dest->member = src; \
 	dest->member = src; \
 	MoveNode(src, dest); }
 	MoveNode(src, dest); }
 
 
-static BfBinaryOp TokenToBinaryOp(BfToken token)
-{
-	switch (token)
-	{
-	case BfToken_Plus:
-		return BfBinaryOp_Add;
-	case BfToken_Minus:
-		return BfBinaryOp_Subtract;
-	case BfToken_Star:
-		return BfBinaryOp_Multiply;
-	case BfToken_ForwardSlash:
-		return BfBinaryOp_Divide;
-	case BfToken_Modulus:
-		return BfBinaryOp_Modulus;
-	case BfToken_Ampersand:
-		return BfBinaryOp_BitwiseAnd;
-	case BfToken_Bar:
-		return BfBinaryOp_BitwiseOr;
-	case BfToken_Carat:
-		return BfBinaryOp_ExclusiveOr;
-	case BfToken_LDblChevron:
-		return BfBinaryOp_LeftShift;
-	case BfToken_RDblChevron:
-		return BfBinaryOp_RightShift;
-	case BfToken_CompareEquals:
-		return BfBinaryOp_Equality;
-	case BfToken_CompareNotEquals:
-		return BfBinaryOp_InEquality;
-	case BfToken_RChevron:
-		return BfBinaryOp_GreaterThan;
-	case BfToken_LChevron:
-		return BfBinaryOp_LessThan;
-	case BfToken_GreaterEquals:
-		return BfBinaryOp_GreaterThanOrEqual;
-	case BfToken_LessEquals:
-		return BfBinaryOp_LessThanOrEqual;
-	case BfToken_Spaceship:
-		return BfBinaryOp_Compare;
-	case BfToken_DblAmpersand:
-		return BfBinaryOp_ConditionalAnd;
-	case BfToken_DblBar:
-		return BfBinaryOp_ConditionalOr;
-	case BfToken_DblQuestion:
-		return BfBinaryOp_NullCoalesce;
-	default:
-		return BfBinaryOp_None;
-	}
-}
-
-static BfUnaryOp TokenToUnaryOp(BfToken token)
-{
-	switch (token)
-	{
-	case BfToken_Star:
-		return BfUnaryOp_Dereference;
-	case BfToken_Ampersand:
-		return BfUnaryOp_AddressOf;
-	case BfToken_Minus:
-		return BfUnaryOp_Negate;
-	case BfToken_Bang:
-		return BfUnaryOp_Not;
-	case BfToken_Plus:
-		return BfUnaryOp_Positive;
-	case BfToken_Tilde:
-		return BfUnaryOp_InvertBits;
-	case BfToken_DblPlus:
-		return BfUnaryOp_Increment;
-	case BfToken_DblMinus:
-		return BfUnaryOp_Decrement;
-	case BfToken_Ref:
-		return BfUnaryOp_Ref;
-	case BfToken_Mut:
-		return BfUnaryOp_Mut;
-	case BfToken_Out:
-		return BfUnaryOp_Out;
-	case BfToken_Params:
-		return BfUnaryOp_Params;
-	default:
-		return BfUnaryOp_None;
-	}
-}
-
-
-static BfAssignmentOp TokenToAssignmentOp(BfToken token)
-{
-	switch (token)
-	{
-	case BfToken_AssignEquals:
-		return BfAssignmentOp_Assign;
-	case BfToken_PlusEquals:
-		return BfAssignmentOp_Add;
-	case BfToken_MinusEquals:
-		return BfAssignmentOp_Subtract;
-	case BfToken_MultiplyEquals:
-		return BfAssignmentOp_Multiply;
-	case BfToken_DivideEquals:
-		return BfAssignmentOp_Divide;
-	case BfToken_ModulusEquals:
-		return BfAssignmentOp_Modulus;
-	case BfToken_ShiftLeftEquals:
-		return BfAssignmentOp_ShiftLeft;
-	case BfToken_ShiftRightEquals:
-		return BfAssignmentOp_ShiftRight;
-	case BfToken_AndEquals:
-		return BfAssignmentOp_BitwiseAnd;
-	case BfToken_OrEquals:
-		return BfAssignmentOp_BitwiseOr;
-	case BfToken_XorEquals:
-		return BfAssignmentOp_ExclusiveOr;
-	default:
-		return BfAssignmentOp_None;
-	}
-}
-
 BfReducer::BfReducer()
 BfReducer::BfReducer()
 {
 {
 	mCurTypeDecl = NULL;
 	mCurTypeDecl = NULL;
@@ -2122,7 +2008,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
 						{
 						{
 							BfToken nextToken = nextTokenNode->GetToken();
 							BfToken nextToken = nextTokenNode->GetToken();
 							//TODO: Hm. What other tokens make it into a cast expr?
 							//TODO: Hm. What other tokens make it into a cast expr?
-							auto binaryOp = TokenToBinaryOp(nextToken);
+							auto binaryOp = BfTokenToBinaryOp(nextToken);
 							// When we have a binary operator token following, it COULD either be a "(double)-val" or it COULD be a "(val2)-val"
 							// When we have a binary operator token following, it COULD either be a "(double)-val" or it COULD be a "(val2)-val"
 							//  But we can't tell until we determine whether the thing inside the paren is a type name or a value name, so we
 							//  But we can't tell until we determine whether the thing inside the paren is a type name or a value name, so we
 							//  have special code in BfExprEvaluator that can fix those cases at evaluation time
 							//  have special code in BfExprEvaluator that can fix those cases at evaluation time
@@ -2232,7 +2118,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
 
 
 			if (exprLeft == NULL)
 			if (exprLeft == NULL)
 			{
 			{
-				BfUnaryOp unaryOp = TokenToUnaryOp(tokenNode->GetToken());
+				BfUnaryOp unaryOp = BfTokenToUnaryOp(tokenNode->GetToken());
 
 
 				if (unaryOp != BfUnaryOp_None)
 				if (unaryOp != BfUnaryOp_None)
 				{
 				{
@@ -2624,7 +2510,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
 				}
 				}
 			}
 			}
 
 
-			BfBinaryOp binOp = TokenToBinaryOp(tokenNode->GetToken());
+			BfBinaryOp binOp = BfTokenToBinaryOp(tokenNode->GetToken());
 			if (binOp != BfBinaryOp_None)
 			if (binOp != BfBinaryOp_None)
 			{				
 			{				
 				auto binOpExpression = mAlloc->Alloc<BfBinaryOperatorExpression>();
 				auto binOpExpression = mAlloc->Alloc<BfBinaryOperatorExpression>();
@@ -2648,7 +2534,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
 				return CheckBinaryOperatorPrecedence(binOpExpression);
 				return CheckBinaryOperatorPrecedence(binOpExpression);
 			}
 			}
 
 
-			auto assignmentOp = TokenToAssignmentOp(tokenNode->GetToken());
+			auto assignmentOp = BfTokenToAssignmentOp(tokenNode->GetToken());
 			if (assignmentOp != BfAssignmentOp_None)
 			if (assignmentOp != BfAssignmentOp_None)
 			{
 			{
 				if ((createExprFlags & CreateExprFlags_NoAssignment) != 0)
 				if ((createExprFlags & CreateExprFlags_NoAssignment) != 0)
@@ -3870,21 +3756,7 @@ BfAstNode* BfReducer::DoCreateStatement(BfAstNode* node, CreateStmtFlags createS
 			auto nameNode = ExpectIdentifierAfter(methodDecl);
 			auto nameNode = ExpectIdentifierAfter(methodDecl);
 			if (nameNode != NULL)
 			if (nameNode != NULL)
 			{
 			{
-				MEMBER_SET(methodDecl, mNameNode, nameNode);
-
-				auto nextNode = mVisitorPos.GetNext();
-				if ((tokenNode = BfNodeDynCast<BfTokenNode>(nextNode)))
-				{
-					if (tokenNode->GetToken() == BfToken_LChevron)
-					{
-						auto genericParams = CreateGenericParamsDeclaration(tokenNode);
-						if (genericParams != NULL)
-						{
-							MEMBER_SET(methodDecl, mGenericParams, genericParams);
-						}
-					}
-				}
-
+				MEMBER_SET(methodDecl, mNameNode, nameNode);				
 				ParseMethod(methodDecl, &params, &commas, true);
 				ParseMethod(methodDecl, &params, &commas, true);
 			}
 			}
 
 
@@ -6493,7 +6365,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, int depth)
 			auto nextNode = mVisitorPos.GetNext();
 			auto nextNode = mVisitorPos.GetNext();
 			if (auto nextToken = BfNodeDynCast<BfTokenNode>(nextNode))
 			if (auto nextToken = BfNodeDynCast<BfTokenNode>(nextNode))
 			{
 			{
-				operatorDecl->mBinOp = TokenToBinaryOp(nextToken->GetToken());
+				operatorDecl->mBinOp = BfTokenToBinaryOp(nextToken->GetToken());
 				if (operatorDecl->mBinOp != BfBinaryOp_None)
 				if (operatorDecl->mBinOp != BfBinaryOp_None)
 				{
 				{
 					MEMBER_SET(operatorDecl, mOpTypeToken, nextToken);
 					MEMBER_SET(operatorDecl, mOpTypeToken, nextToken);
@@ -6501,7 +6373,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, int depth)
 				}
 				}
 				else
 				else
 				{
 				{
-					operatorDecl->mUnaryOp = TokenToUnaryOp(nextToken->GetToken());
+					operatorDecl->mUnaryOp = BfTokenToUnaryOp(nextToken->GetToken());
 					if (operatorDecl->mUnaryOp != BfUnaryOp_None)
 					if (operatorDecl->mUnaryOp != BfUnaryOp_None)
 					{
 					{
 						MEMBER_SET(operatorDecl, mOpTypeToken, nextToken);
 						MEMBER_SET(operatorDecl, mOpTypeToken, nextToken);
@@ -6509,7 +6381,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, int depth)
 					}
 					}
 					else
 					else
 					{
 					{
-						operatorDecl->mAssignOp = TokenToAssignmentOp(nextToken->GetToken());
+						operatorDecl->mAssignOp = BfTokenToAssignmentOp(nextToken->GetToken());
 						if (operatorDecl->mAssignOp == BfAssignmentOp_Assign)
 						if (operatorDecl->mAssignOp == BfAssignmentOp_Assign)
 						{
 						{
 							Fail("The assignment operator '=' cannot be overridden", nextToken);
 							Fail("The assignment operator '=' cannot be overridden", nextToken);
@@ -8768,7 +8640,24 @@ BfTokenNode* BfReducer::ParseMethodParams(BfAstNode* node, SizedArrayImpl<BfPara
 
 
 bool BfReducer::ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayImpl<BfParameterDeclaration*>* params, SizedArrayImpl<BfTokenNode*>* commas, bool alwaysIncludeBlock)
 bool BfReducer::ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayImpl<BfParameterDeclaration*>* params, SizedArrayImpl<BfTokenNode*>* commas, bool alwaysIncludeBlock)
 {
 {
-	auto tokenNode = ExpectTokenAfter(methodDeclaration, BfToken_LParen, BfToken_Bang);
+	BfTokenNode* tokenNode;
+	auto nextNode = mVisitorPos.GetNext();
+	if (methodDeclaration->mGenericParams == NULL)
+	{
+		if ((tokenNode = BfNodeDynCast<BfTokenNode>(nextNode)))
+		{
+			if (tokenNode->GetToken() == BfToken_LChevron)
+			{
+				auto genericParams = CreateGenericParamsDeclaration(tokenNode);
+				if (genericParams != NULL)
+				{
+					MEMBER_SET(methodDeclaration, mGenericParams, genericParams);
+				}
+			}
+		}
+	}
+
+	tokenNode = ExpectTokenAfter(methodDeclaration, BfToken_LParen, BfToken_Bang);
 	if (tokenNode == NULL)
 	if (tokenNode == NULL)
 		return false;
 		return false;
 	if (tokenNode->GetToken() == BfToken_Bang)
 	if (tokenNode->GetToken() == BfToken_Bang)
@@ -8811,7 +8700,7 @@ bool BfReducer::ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayIm
 	mVisitorPos.MoveNext();
 	mVisitorPos.MoveNext();
 
 
 	auto typeDecl = mCurTypeDecl;
 	auto typeDecl = mCurTypeDecl;
-	auto nextNode = mVisitorPos.GetNext();
+	nextNode = mVisitorPos.GetNext();
 	if ((tokenNode = BfNodeDynCast<BfTokenNode>(nextNode)))
 	if ((tokenNode = BfNodeDynCast<BfTokenNode>(nextNode)))
 	{
 	{
 		if (tokenNode->GetToken() == BfToken_Mut)
 		if (tokenNode->GetToken() == BfToken_Mut)
@@ -9038,7 +8927,7 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration(
 
 
 	bool isDone = false;
 	bool isDone = false;
 	for (int constraintIdx = 0; !isDone; constraintIdx++)
 	for (int constraintIdx = 0; !isDone; constraintIdx++)
-	{
+	{		
 		BfGenericConstraint* genericConstraint = mAlloc->Alloc<BfGenericConstraint>();
 		BfGenericConstraint* genericConstraint = mAlloc->Alloc<BfGenericConstraint>();
 		BfDeferredAstSizedArray<BfAstNode*> constraintTypes(genericConstraint->mConstraintTypes, mAlloc);
 		BfDeferredAstSizedArray<BfAstNode*> constraintTypes(genericConstraint->mConstraintTypes, mAlloc);
 		BfDeferredAstSizedArray<BfTokenNode*> commas(genericConstraint->mCommas, mAlloc);
 		BfDeferredAstSizedArray<BfTokenNode*> commas(genericConstraint->mCommas, mAlloc);
@@ -9047,11 +8936,12 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration(
 		genericConstraint->mWhereToken = tokenNode;
 		genericConstraint->mWhereToken = tokenNode;
 
 
 		genericConstraintsArr.push_back(genericConstraint);
 		genericConstraintsArr.push_back(genericConstraint);
-		auto genericParamName = ExpectIdentifierAfter(genericConstraint, "generic parameter name");
+
+		auto genericParamName = CreateTypeRefAfter(genericConstraint);
 		if (genericParamName != NULL)
 		if (genericParamName != NULL)
 		{
 		{
-			MEMBER_SET(genericConstraint, mGenericParamName, genericParamName);
-			tokenNode = ExpectTokenAfter(genericConstraint, BfToken_Colon);
+			MEMBER_SET(genericConstraint, mTypeRef, genericParamName);
+			tokenNode = ExpectTokenAfter(genericConstraint, BfToken_Colon, BfToken_AssignEquals);
 		}
 		}
 		else
 		else
 			isDone = true;
 			isDone = true;
@@ -9076,7 +8966,6 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration(
 				}
 				}
 
 
 				tokenNode = ExpectTokenAfter(genericConstraint, BfToken_Comma, BfToken_LBrace, BfToken_Where, BfToken_Semicolon);
 				tokenNode = ExpectTokenAfter(genericConstraint, BfToken_Comma, BfToken_LBrace, BfToken_Where, BfToken_Semicolon);
-
 				if (tokenNode == NULL)
 				if (tokenNode == NULL)
 				{
 				{
 					isDone = true;
 					isDone = true;
@@ -9098,6 +8987,8 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration(
 			auto nextNode = mVisitorPos.GetNext();
 			auto nextNode = mVisitorPos.GetNext();
 			if (auto constraintToken = BfNodeDynCast<BfTokenNode>(nextNode))
 			if (auto constraintToken = BfNodeDynCast<BfTokenNode>(nextNode))
 			{
 			{
+				BfAstNode* constraintNode = NULL;
+
 				bool addToConstraint = false;
 				bool addToConstraint = false;
 				switch (constraintToken->GetToken())
 				switch (constraintToken->GetToken())
 				{
 				{
@@ -9107,13 +8998,43 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration(
 				case BfToken_Var:
 				case BfToken_Var:
 					addToConstraint = true;
 					addToConstraint = true;
 					break;
 					break;
+				case BfToken_Operator:					
+					{
+						BfGenericOperatorConstraint* opConstraint = mAlloc->Alloc<BfGenericOperatorConstraint>();
+						constraintNode = opConstraint;
+						
+						ReplaceNode(constraintToken, opConstraint);						
+						
+						MEMBER_SET(opConstraint, mOperatorToken, constraintToken);
+						mVisitorPos.MoveNext();
+
+						auto opToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
+						if (opToken == NULL)
+						{
+							auto typeRef = CreateTypeRefAfter(opConstraint);
+							if (typeRef == NULL)
+								break;
+							MEMBER_SET(opConstraint, mLeftType, typeRef);
+							opToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
+						}
+
+						if (opToken == NULL)
+							break;
+						MEMBER_SET(opConstraint, mOpToken, opToken);
+						mVisitorPos.MoveNext();
+
+						auto typeRef = CreateTypeRefAfter(opConstraint);
+						if (typeRef == NULL)
+							break;
+						MEMBER_SET(opConstraint, mRightType, typeRef);											
+					}
+					break;
 				default: break;
 				default: break;
 				}
 				}
 
 
 				if (addToConstraint)
 				if (addToConstraint)
 				{
 				{
-					BfAstNode* constraintNode = constraintToken;
-					MoveNode(constraintToken, genericConstraint);
+					constraintNode = constraintToken;
 					bool addToConstraint = false;
 					bool addToConstraint = false;
 
 
 					mVisitorPos.MoveNext();
 					mVisitorPos.MoveNext();
@@ -9152,8 +9073,12 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration(
 						constraintTypes.push_back(typeRef);
 						constraintTypes.push_back(typeRef);
 
 
 						continue;
 						continue;
-					}
-
+					}					
+				}
+				
+				if (constraintNode != NULL)
+				{
+					MoveNode(constraintNode, genericConstraint);
 					constraintTypes.push_back(constraintNode);
 					constraintTypes.push_back(constraintNode);
 					continue;
 					continue;
 				}
 				}

+ 9 - 0
IDEHelper/Compiler/BfResolvedTypeUtils.cpp

@@ -3754,6 +3754,15 @@ String BfTypeUtils::TypeToString(BfTypeReference* typeRef)
 	return "???";
 	return "???";
 }
 }
 
 
+bool BfTypeUtils::TypeEquals(BfType* typeA, BfType* typeB, BfType* selfType)
+{
+	if (typeA->IsSelf())
+		typeA = selfType;
+	if (typeB->IsSelf())
+		typeB = selfType;
+	return typeA == typeB;
+}
+
 String BfTypeUtils::TypeToString(BfTypeDef* typeDef, BfTypeNameFlags typeNameFlags)
 String BfTypeUtils::TypeToString(BfTypeDef* typeDef, BfTypeNameFlags typeNameFlags)
 {
 {
 	String str;
 	String str;

+ 89 - 4
IDEHelper/Compiler/BfResolvedTypeUtils.h

@@ -930,16 +930,49 @@ public:
 	virtual BfType* GetUnderlyingType() override { return mElementType; }
 	virtual BfType* GetUnderlyingType() override { return mElementType; }
 };
 };
 
 
+class BfGenericOperatorConstraintInstance
+{
+public:
+	BfType* mLeftType;
+	BfBinaryOp mBinaryOp;
+	BfUnaryOp mUnaryOp;
+	BfToken mCastToken;
+	BfType* mRightType;
+
+public:
+	BfGenericOperatorConstraintInstance()
+	{
+		mLeftType = NULL;
+		mBinaryOp = BfBinaryOp_None;
+		mUnaryOp = BfUnaryOp_None;
+		mCastToken = BfToken_None;
+		mRightType = NULL;
+	}
+
+	bool operator==(const BfGenericOperatorConstraintInstance& other) const
+	{
+		return
+			(mLeftType == other.mLeftType) &&
+			(mBinaryOp == other.mBinaryOp) &&
+			(mUnaryOp == other.mUnaryOp) &&
+			(mCastToken == other.mCastToken) &&
+			(mRightType == other.mRightType);
+	}
+};
+
 class BfGenericParamInstance 
 class BfGenericParamInstance 
 {
 {
 public:	
 public:	
 	int mGenericParamFlags;
 	int mGenericParamFlags;
+	BfType* mExternType;
 	Array<BfTypeInstance*> mInterfaceConstraints;
 	Array<BfTypeInstance*> mInterfaceConstraints;
+	Array<BfGenericOperatorConstraintInstance> mOperatorConstraints;
 	BfType* mTypeConstraint;
 	BfType* mTypeConstraint;
 	int mRefCount;
 	int mRefCount;
 
 
 	BfGenericParamInstance()
 	BfGenericParamInstance()
 	{
 	{
+		mExternType = NULL;
 		mGenericParamFlags = 0;
 		mGenericParamFlags = 0;
 		mTypeConstraint = NULL;
 		mTypeConstraint = NULL;
 		mRefCount = 1;
 		mRefCount = 1;
@@ -954,7 +987,10 @@ public:
 	virtual ~BfGenericParamInstance()
 	virtual ~BfGenericParamInstance()
 	{
 	{
 	}
 	}
+	virtual BfConstraintDef* GetConstraintDef() = 0;
 	virtual BfGenericParamDef* GetGenericParamDef() = 0;
 	virtual BfGenericParamDef* GetGenericParamDef() = 0;
+	virtual BfExternalConstraintDef* GetExternConstraintDef() = 0;
+	virtual String GetName() = 0;
 };
 };
 
 
 class BfGenericTypeParamInstance : public BfGenericParamInstance
 class BfGenericTypeParamInstance : public BfGenericParamInstance
@@ -962,12 +998,13 @@ class BfGenericTypeParamInstance : public BfGenericParamInstance
 public:
 public:
 	BfTypeDef* mTypeDef;
 	BfTypeDef* mTypeDef;
 	int mGenericIdx;
 	int mGenericIdx;
+
 public:
 public:
 	BfGenericTypeParamInstance(BfTypeDef* typeDef, int genericIdx)
 	BfGenericTypeParamInstance(BfTypeDef* typeDef, int genericIdx)
 	{
 	{
 		mTypeDef = typeDef;
 		mTypeDef = typeDef;
 		mGenericIdx = genericIdx;
 		mGenericIdx = genericIdx;
-		mGenericParamFlags = GetGenericParamDef()->mGenericParamFlags;
+		mGenericParamFlags = GetConstraintDef()->mGenericParamFlags;
 		mTypeConstraint = NULL;
 		mTypeConstraint = NULL;
 	}
 	}
 
 
@@ -977,9 +1014,32 @@ public:
 		return this;
 		return this;
 	}
 	}
 
 
+	virtual BfConstraintDef* GetConstraintDef() override
+	{
+		if (mGenericIdx < (int)mTypeDef->mGenericParamDefs.size())
+			return mTypeDef->mGenericParamDefs[mGenericIdx];
+		return NULL;
+	}
+
 	virtual BfGenericParamDef* GetGenericParamDef() override
 	virtual BfGenericParamDef* GetGenericParamDef() override
 	{
 	{
-		return mTypeDef->mGenericParamDefs[mGenericIdx];
+		if (mGenericIdx < (int)mTypeDef->mGenericParamDefs.size())
+			return mTypeDef->mGenericParamDefs[mGenericIdx];
+		return NULL;
+	}
+
+	virtual BfExternalConstraintDef* GetExternConstraintDef() override
+	{
+		if (mGenericIdx < (int)mTypeDef->mGenericParamDefs.size())
+			return NULL;
+		return NULL;
+	}
+
+	virtual String GetName() override
+	{
+		if (mGenericIdx < (int)mTypeDef->mGenericParamDefs.size())
+			return mTypeDef->mGenericParamDefs[mGenericIdx]->mName;
+		return NULL;
 	}
 	}
 };
 };
 
 
@@ -988,12 +1048,13 @@ class BfGenericMethodParamInstance : public BfGenericParamInstance
 public:
 public:
 	BfMethodDef* mMethodDef;
 	BfMethodDef* mMethodDef;
 	int mGenericIdx;
 	int mGenericIdx;
+
 public:
 public:
 	BfGenericMethodParamInstance(BfMethodDef* methodDef, int genericIdx)
 	BfGenericMethodParamInstance(BfMethodDef* methodDef, int genericIdx)
 	{
 	{
 		mMethodDef = methodDef;
 		mMethodDef = methodDef;
 		mGenericIdx = genericIdx;
 		mGenericIdx = genericIdx;
-		mGenericParamFlags = GetGenericParamDef()->mGenericParamFlags;
+		mGenericParamFlags = GetConstraintDef()->mGenericParamFlags;
 		mTypeConstraint = NULL;
 		mTypeConstraint = NULL;
 	}
 	}
 
 
@@ -1003,9 +1064,32 @@ public:
 		return this;
 		return this;
 	}
 	}
 
 
+	virtual BfConstraintDef* GetConstraintDef() override
+	{
+		if (mGenericIdx < (int)mMethodDef->mGenericParams.size())
+			return mMethodDef->mGenericParams[mGenericIdx];
+		return &mMethodDef->mExternalConstraints[mGenericIdx - (int)mMethodDef->mGenericParams.size()];
+	}
+
 	virtual BfGenericParamDef* GetGenericParamDef() override
 	virtual BfGenericParamDef* GetGenericParamDef() override
 	{
 	{
-		return mMethodDef->mGenericParams[mGenericIdx];
+		if (mGenericIdx < (int)mMethodDef->mGenericParams.size())
+			return mMethodDef->mGenericParams[mGenericIdx];
+		return NULL;
+	}
+
+	virtual BfExternalConstraintDef* GetExternConstraintDef() override
+	{
+		if (mGenericIdx < (int)mMethodDef->mGenericParams.size())
+			return NULL;
+		return &mMethodDef->mExternalConstraints[mGenericIdx - (int)mMethodDef->mGenericParams.size()];
+	}
+
+	virtual String GetName() override
+	{
+		if (mGenericIdx < (int)mMethodDef->mGenericParams.size())
+			return mMethodDef->mGenericParams[mGenericIdx]->mName;
+		return mMethodDef->mExternalConstraints[mGenericIdx - (int)mMethodDef->mGenericParams.size()].mTypeRef->ToString();
 	}
 	}
 };
 };
 
 
@@ -2246,6 +2330,7 @@ public:
 	static String TypeToString(BfTypeReference* typeRef);
 	static String TypeToString(BfTypeReference* typeRef);
 	static String TypeToString(BfTypeDef* typeDef, BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None);
 	static String TypeToString(BfTypeDef* typeDef, BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None);
 	static bool TypeToString(StringImpl& str, BfTypeDef* typeDef, BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None);
 	static bool TypeToString(StringImpl& str, BfTypeDef* typeDef, BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None);
+	static bool TypeEquals(BfType* typeA, BfType* typeB, BfType* selfType);
 
 
 	static void GetProjectList(BfType* checkType, Array<BfProject*>* projectVector, int immutableLength);
 	static void GetProjectList(BfType* checkType, Array<BfProject*>* projectVector, int immutableLength);
 
 

+ 13 - 11
IDEHelper/Compiler/BfSourceClassifier.cpp

@@ -468,11 +468,8 @@ void BfSourceClassifier::Visit(BfMethodDeclaration* methodDeclaration)
 	SetAndRestoreValue<BfAstNode*> prevMember(mCurMember, methodDeclaration);
 	SetAndRestoreValue<BfAstNode*> prevMember(mCurMember, methodDeclaration);
 
 
 	BfElementVisitor::Visit(methodDeclaration);	
 	BfElementVisitor::Visit(methodDeclaration);	
-
-	BfIdentifierNode* identifier = methodDeclaration->mNameNode;
-	if (identifier == NULL)
-		return;
-	SetElementType(identifier, BfSourceElementType_Method);
+	
+	SetElementType(methodDeclaration->mNameNode, BfSourceElementType_Method);
 
 
 	if (methodDeclaration->mGenericParams != NULL)
 	if (methodDeclaration->mGenericParams != NULL)
 	{
 	{
@@ -485,11 +482,14 @@ void BfSourceClassifier::Visit(BfMethodDeclaration* methodDeclaration)
 
 
 	if (methodDeclaration->mGenericConstraintsDeclaration != NULL)
 	if (methodDeclaration->mGenericConstraintsDeclaration != NULL)
 	{
 	{
-		for (auto constraint : methodDeclaration->mGenericConstraintsDeclaration->mGenericConstraints)
+		for (auto constraintNode : methodDeclaration->mGenericConstraintsDeclaration->mGenericConstraints)
 		{
 		{
-			BfIdentifierNode* typeRef = constraint->mGenericParamName;
-			if (typeRef != NULL)
-				SetElementType(typeRef, BfSourceElementType_TypeRef);
+			if (auto genericConstraint = BfNodeDynCast<BfGenericConstraint>(constraintNode))
+			{
+				BfTypeReference* typeRef = genericConstraint->mTypeRef;
+				if (typeRef != NULL)
+					SetElementType(typeRef, BfSourceElementType_TypeRef);
+			}
 		}
 		}
 	}
 	}
 }
 }
@@ -563,9 +563,11 @@ void BfSourceClassifier::Handle(BfTypeDeclaration* typeDeclaration)
 
 
 	if (typeDeclaration->mGenericConstraintsDeclaration != NULL)
 	if (typeDeclaration->mGenericConstraintsDeclaration != NULL)
 	{
 	{
-		for (auto constraint : typeDeclaration->mGenericConstraintsDeclaration->mGenericConstraints)
+		for (auto constraintNode : typeDeclaration->mGenericConstraintsDeclaration->mGenericConstraints)
 		{
 		{
-			BfIdentifierNode* typeRef = constraint->mGenericParamName;
+			auto genericConstraint = BfNodeDynCast<BfGenericConstraint>(constraintNode);
+
+			BfTypeReference* typeRef = genericConstraint->mTypeRef;
 			if (typeRef != NULL)
 			if (typeRef != NULL)
 				SetElementType(typeRef, BfSourceElementType_TypeRef);
 				SetElementType(typeRef, BfSourceElementType_TypeRef);
 		}
 		}

+ 7 - 6
IDEHelper/Compiler/BfSystem.cpp

@@ -2379,11 +2379,12 @@ BfTypeDef* BfSystem::FindTypeDefEx(const StringImpl& fullTypeName)
 
 
 	int numGenericArgs = 0;
 	int numGenericArgs = 0;
 	String typeName = fullTypeName.Substring(colonPos + 1);
 	String typeName = fullTypeName.Substring(colonPos + 1);
-	int tildePos = (int)typeName.IndexOf('`');
+	int tildePos = (int)typeName.LastIndexOf('`');	
 	if (tildePos != -1)
 	if (tildePos != -1)
 	{
 	{
+		BF_ASSERT(tildePos > (int)typeName.LastIndexOf('.'));
 		numGenericArgs = atoi(typeName.c_str() + tildePos + 1);
 		numGenericArgs = atoi(typeName.c_str() + tildePos + 1);
-		typeName.RemoveToEnd(tildePos);
+		typeName.RemoveToEnd(tildePos);		
 	}
 	}
 
 
 	return FindTypeDef(typeName, numGenericArgs, project);
 	return FindTypeDef(typeName, numGenericArgs, project);
@@ -2587,13 +2588,13 @@ void BfSystem::InjectNewRevision(BfTypeDef* typeDef)
 	typeDef->mGenericParamDefs.Clear();
 	typeDef->mGenericParamDefs.Clear();
 	
 	
 	typeDef->mGenericParamDefs = nextTypeDef->mGenericParamDefs;
 	typeDef->mGenericParamDefs = nextTypeDef->mGenericParamDefs;
-	nextTypeDef->mGenericParamDefs.Clear();
+	nextTypeDef->mGenericParamDefs.Clear();	
 
 
 	typeDef->mBaseTypes = nextTypeDef->mBaseTypes;		
 	typeDef->mBaseTypes = nextTypeDef->mBaseTypes;		
 	typeDef->mNestedTypes = nextTypeDef->mNestedTypes;	
 	typeDef->mNestedTypes = nextTypeDef->mNestedTypes;	
 	
 	
 	// If we are a partial then the mOuterType gets set to the combined partial so don't do that here
 	// If we are a partial then the mOuterType gets set to the combined partial so don't do that here
-	if (!typeDef->mIsPartial)
+	if (!typeDef->mIsCombinedPartial)
 	{
 	{
 		for (auto nestedType : typeDef->mNestedTypes)
 		for (auto nestedType : typeDef->mNestedTypes)
 		{
 		{
@@ -2658,7 +2659,7 @@ void BfSystem::AddToCompositePartial(BfPassInstance* passInstance, BfTypeDef* co
 			BfGenericParamDef* newGeneric = new BfGenericParamDef();
 			BfGenericParamDef* newGeneric = new BfGenericParamDef();
 			*newGeneric = *generic;
 			*newGeneric = *generic;
 			typeDef->mGenericParamDefs.push_back(newGeneric);
 			typeDef->mGenericParamDefs.push_back(newGeneric);
-		}
+		}		
 		
 		
 		typeDef->mBaseTypes = partialTypeDef->mBaseTypes;				
 		typeDef->mBaseTypes = partialTypeDef->mBaseTypes;				
 
 
@@ -2675,7 +2676,7 @@ void BfSystem::AddToCompositePartial(BfPassInstance* passInstance, BfTypeDef* co
 		{
 		{
 			typeDef->mTypeCode = partialTypeDef->mTypeCode;
 			typeDef->mTypeCode = partialTypeDef->mTypeCode;
 			typeDef->mTypeDeclaration = partialTypeDef->mTypeDeclaration;
 			typeDef->mTypeDeclaration = partialTypeDef->mTypeDeclaration;
-		}		
+		}				
 	}	
 	}	
 
 
 	// Merge attributes together
 	// Merge attributes together

+ 43 - 15
IDEHelper/Compiler/BfSystem.h

@@ -574,34 +574,52 @@ public:
 	BfAstNode* GetRefNode();
 	BfAstNode* GetRefNode();
 };
 };
 
 
-enum BfGenericParamFlags : uint8
+enum BfGenericParamFlags : uint16
 {
 {
-	BfGenericParamFlag_None = 0,
-	BfGenericParamFlag_Class = 1,
-	BfGenericParamFlag_Struct = 2,
+	BfGenericParamFlag_None      = 0,
+	BfGenericParamFlag_Class     = 1,
+	BfGenericParamFlag_Struct    = 2,
 	BfGenericParamFlag_StructPtr = 4,
 	BfGenericParamFlag_StructPtr = 4,
-	BfGenericParamFlag_New = 8,
-	BfGenericParamFlag_Delete = 16,
-	BfGenericParamFlag_Var = 32,
-	BfGenericParamFlag_Const = 64
+	BfGenericParamFlag_New       = 8,
+	BfGenericParamFlag_Delete    = 0x10,
+	BfGenericParamFlag_Var       = 0x20,
+	BfGenericParamFlag_Const     = 0x40,
+	BfGenericParamFlag_Equals    = 0x80,
+	BfGenericParamFlag_Equals_Op    = 0x100,
+	BfGenericParamFlag_Equals_Type  = 0x200,
+	BfGenericParamFlag_Equals_IFace = 0x400
 };
 };
 
 
-class BfGenericParamDef
+class BfConstraintDef
 {
 {
 public:
 public:
-	//BfTypeDef* mOwner;	
+	BfGenericParamFlags mGenericParamFlags;
+	Array<BfAstNode*> mConstraints;
+
+	BfConstraintDef()
+	{
+		mGenericParamFlags = BfGenericParamFlag_None;
+	}
+};
+
+class BfGenericParamDef : public BfConstraintDef
+{
+public:	
 	String mName;		
 	String mName;		
-	Array<BfIdentifierNode*> mNameNodes; // 0 is always the def name
-	BfGenericParamFlags mGenericParamFlags;	
-	Array<BfTypeReference*> mInterfaceConstraints;	
+	Array<BfIdentifierNode*> mNameNodes; // 0 is always the def name	
+};
+
+class BfExternalConstraintDef : public BfConstraintDef
+{
+public:
+	BfTypeReference* mTypeRef;	
 };
 };
 
 
 // CTOR is split into two for Objects - Ctor clears and sets up VData, Ctor_Body executes ctor body code
 // CTOR is split into two for Objects - Ctor clears and sets up VData, Ctor_Body executes ctor body code
 enum BfMethodType : uint8
 enum BfMethodType : uint8
 {
 {
 	BfMethodType_Ignore,
 	BfMethodType_Ignore,
-	BfMethodType_Normal,
-	//BfMethodType_Lambda,
+	BfMethodType_Normal,	
 	BfMethodType_PropertyGetter,
 	BfMethodType_PropertyGetter,
 	BfMethodType_PropertySetter,
 	BfMethodType_PropertySetter,
 	BfMethodType_CtorCalcAppend,
 	BfMethodType_CtorCalcAppend,
@@ -658,6 +676,13 @@ enum BfCheckedKind : int8
 	BfCheckedKind_Unchecked
 	BfCheckedKind_Unchecked
 };
 };
 
 
+enum BfCommutableKind : int8
+{
+	BfCommutableKind_None,
+	BfCommutableKind_Forward,
+	BfCommutableKind_Reverse,
+};
+
 class BfMethodDef : public BfMemberDef
 class BfMethodDef : public BfMemberDef
 {
 {
 public:		
 public:		
@@ -668,6 +693,7 @@ public:
 	BfTypeReference* mReturnTypeRef;
 	BfTypeReference* mReturnTypeRef;
 	Array<BfParameterDef*> mParams;
 	Array<BfParameterDef*> mParams;
 	Array<BfGenericParamDef*> mGenericParams;
 	Array<BfGenericParamDef*> mGenericParams;
+	Array<BfExternalConstraintDef> mExternalConstraints;
 	BfMethodDef* mNextWithSameName;
 	BfMethodDef* mNextWithSameName;
 	Val128 mFullHash;
 	Val128 mFullHash;
 
 
@@ -694,6 +720,7 @@ public:
 	bool mIsOperator;
 	bool mIsOperator;
 	bool mIsExtern;	
 	bool mIsExtern;	
 	bool mIsNoDiscard;
 	bool mIsNoDiscard;
+	BfCommutableKind mCommutableKind;
 	BfCheckedKind mCheckedKind;
 	BfCheckedKind mCheckedKind;
 	BfImportKind mImportKind;	
 	BfImportKind mImportKind;	
 	BfCallingConvention mCallingConvention;	
 	BfCallingConvention mCallingConvention;	
@@ -726,6 +753,7 @@ public:
 		mMethodDeclaration = NULL;		
 		mMethodDeclaration = NULL;		
 		mCodeChanged = false;
 		mCodeChanged = false;
 		mWantsBody = true;
 		mWantsBody = true;
+		mCommutableKind = BfCommutableKind_None;
 		mCheckedKind = BfCheckedKind_NotSet;
 		mCheckedKind = BfCheckedKind_NotSet;
 		mImportKind = BfImportKind_None;
 		mImportKind = BfImportKind_None;
 		mMethodType = BfMethodType_Normal;
 		mMethodType = BfMethodType_Normal;

+ 45 - 0
IDEHelper/Tests/src/Generics.bf

@@ -0,0 +1,45 @@
+using System;
+
+namespace Tests
+{
+	class Generics
+	{
+		class ClassA : IDisposable
+		{
+			public void Dispose()
+			{
+
+			}
+		}
+
+		static void DoDispose<T>(mut T val) where T : IDisposable
+		{
+			val.Dispose();
+		}
+
+		struct Disposer<T>
+		{
+			static void UseDispose(IDisposable disp)
+			{
+
+			}
+
+			static void DoDisposeA(mut T val) where T : IDisposable
+			{
+				val.Dispose();
+				UseDispose(val);
+			}
+
+			static void DoDisposeB(mut T val) where T : IDisposable
+			{
+				val.Dispose();
+			}
+		}
+		
+		[Test]
+		public static void TestBasics()
+		{
+
+		}
+	}
+}

+ 181 - 0
IDEHelper/Tests/src/Operators.bf

@@ -1,3 +1,5 @@
+#pragma warning disable 168
+
 using System;
 using System;
 
 
 namespace Tests
 namespace Tests
@@ -23,6 +25,151 @@ namespace Tests
 			}
 			}
 		}
 		}
 
 
+		struct StructB
+		{
+			public int mB;
+
+			public static StructA operator+(StructA sa, StructB sb)
+			{
+				StructA result;
+				result.mA = sa.mA + sb.mB + 1000;
+				return result;
+			}
+		}
+
+		struct StructOp<T, T2> where T : operator T + T2
+		{
+			public T DoIt(T val, T2 val2)
+			{
+				return val + val2;
+			}
+		}
+
+		struct StructOp2<T>
+		{
+			public T mVal;
+
+			public static T operator+<T2>(StructOp2<T> lhs, T2 rhs) where T : operator T + T2
+			{
+				return lhs.mVal + rhs;
+			}
+
+			public static T operator|<T2>(StructOp2<T> lhs, T2 rhs) where T : operator implicit T2
+			{
+				T temp = rhs;
+				return temp;
+			}
+
+			public T GetNeg<T2>(T2 val) where T : operator -T2
+			{
+				return -val;
+			}
+
+			public T GetInt() where T : Int
+			{
+				return mVal;
+			}
+		}
+
+		/*struct OuterOp<T>
+		{
+			public struct InnerOp<T2>
+				where T : operator T + T2
+				where T : operator -T
+				where T : operator implicit T2
+			{
+				public static T Op(T val, T2 val2)
+				{
+					return val + val2;
+				}
+
+				public static T Neg(T val)
+				{
+					return -val;
+				}
+
+				public static T Cast(T2 val)
+				{
+					return val;
+				}
+
+				struct InnerOp2
+				{
+					public static T Op(T val, T2 val2)
+					{
+						return val + val2;
+					}
+
+					public static T Neg(T val)
+					{
+						return -val;
+					}
+
+					public static T Cast(T2 val)
+					{
+						return val;
+					}
+				}
+
+				struct InnerOp3<T3>
+				{
+					public static T Op(T val, T2 val2)
+					{
+						return val + val2;
+					}
+
+					public static T Neg(T val)
+					{
+						return -val;
+					}
+
+					public static T Cast(T2 val)
+					{
+						return val;
+					}
+				}
+			}
+		}
+
+		struct OuterOp2<T>
+		{
+			public struct InnerOp<T2>
+			{
+
+			}
+
+			extension InnerOp<T2>
+				where T : operator T + T2
+				where T : operator -T
+			{
+				public static T Op(T val, T2 val2)
+				{
+					return val + val2;
+				}
+
+				public static T Neg(T val)
+				{
+					return -val;
+				}
+			}
+		}*/
+
+		public static T Op<T, T2>(T val, T2 val2) where T : operator T + T2
+		{
+			return val + val2;
+		}
+
+		public static T Complex<T, T2>(T val, T2 val2)
+			where T : operator T + T2
+			where T : operator -T
+			where T : operator implicit T2
+		{
+			T conv = val2;
+			T result = val + val2;
+			result = -result;
+			return result;
+		}
+
 		[Test]
 		[Test]
 		public static void TestBasics()
 		public static void TestBasics()
 		{
 		{
@@ -33,6 +180,40 @@ namespace Tests
 
 
 			StructA sa2 = sa0 + sa1;
 			StructA sa2 = sa0 + sa1;
 			Test.Assert(sa2.mA == 3);
 			Test.Assert(sa2.mA == 3);
+
+			StructB sb0;
+			sb0.mB = 11;
+			StructB sb1;
+			sb1.mB = 12;
+
+			StructA sa3 = sa0 + sb0;
+			Test.Assert(sa3.mA == 1012);
+
+			StructA sa4 = Op(sa0, sb0);
+			Test.Assert(sa4.mA == 1012);
+
+			float val = Op((int32)100, (int16)23);
+			Test.Assert(val == 123);
+
+			int32 i32res = Complex((int32)100, (int16)23);
+			Test.Assert(i32res == -123);
+
+			StructOp<StructA, StructB> sOp;
+			let sa5 = sOp.DoIt(sa1, sb1);
+			Test.Assert(sa5.mA == 1014);
+
+			StructOp2<int32> sOp2;
+			sOp2.mVal = 100;
+			int32 res6 = sOp2 + (int16)40;
+			Test.Assert(res6 == 140);
+			int32 res7 = sOp2.GetInt();
+			Test.Assert(res7 == 100);
+
+			/*let oai = OuterOp<float>.InnerOp<int>.Op(1.0f, 100);
+			Test.Assert(oai == 101.0f);
+
+			let oai2 = OuterOp2<float>.InnerOp<int>.Op(2.0f, 200);
+			Test.Assert(oai2 == 202.0f);*/
 		}
 		}
 	}
 	}
 }
 }