diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index c171aa4c..dcff9f82 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -20846,14 +20846,30 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod if ((flags & BfBinOpFlag_IsConstraintCheck) != 0) { - // We can't do CheckMethod because circular referencing means we may have to evaluate this before our type is complete, - // which means before method processing has occurred - auto returnType = mModule->CheckOperator(checkType, operatorDef, args[0].mTypedValue, args[1].mTypedValue); - if (returnType != NULL) + if (operatorDef->mGenericParams.IsEmpty()) { - operatorConstraintReturnType = returnType; - methodMatcher.mBestMethodDef = operatorDef; - foundExactMatch = true; + // Fast check + auto returnType = mModule->CheckOperator(checkType, operatorDef, args[0].mTypedValue, args[1].mTypedValue); + if (returnType != NULL) + { + operatorConstraintReturnType = returnType; + methodMatcher.mBestMethodDef = operatorDef; + methodMatcher.mBestMethodTypeInstance = checkType; + foundExactMatch = true; + } + } + else + { + if (methodMatcher.CheckMethod(NULL, checkType, operatorDef, false)) + { + auto rawMethodInstance = mModule->GetRawMethodInstance(checkType, operatorDef); + auto returnType = mModule->ResolveGenericType(rawMethodInstance->mReturnType, NULL, &methodMatcher.mBestMethodGenericArguments); + if (returnType != NULL) + { + operatorConstraintReturnType = returnType; + foundExactMatch = true; + } + } } } else @@ -20877,13 +20893,32 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod { if ((flags & BfBinOpFlag_IsConstraintCheck) != 0) { - auto returnType = mModule->CheckOperator(checkType, oppositeOperatorDef, args[0].mTypedValue, args[1].mTypedValue); - if (returnType != NULL) + if (oppositeOperatorDef->mGenericParams.IsEmpty()) { - operatorConstraintReturnType = returnType; - methodMatcher.mBestMethodDef = oppositeOperatorDef; - methodMatcher.mSelfType = entry.mSrcType; - wasTransformedUsage = true; + // Fast check + auto returnType = mModule->CheckOperator(checkType, oppositeOperatorDef, args[0].mTypedValue, args[1].mTypedValue); + if (returnType != NULL) + { + operatorConstraintReturnType = returnType; + methodMatcher.mBestMethodDef = oppositeOperatorDef; + methodMatcher.mBestMethodTypeInstance = checkType; + methodMatcher.mSelfType = entry.mSrcType; + wasTransformedUsage = true; + } + } + else + { + if (methodMatcher.CheckMethod(NULL, checkType, oppositeOperatorDef, false)) + { + auto rawMethodInstance = mModule->GetRawMethodInstance(checkType, oppositeOperatorDef); + auto returnType = mModule->ResolveGenericType(rawMethodInstance->mReturnType, NULL, &methodMatcher.mBestMethodGenericArguments); + if (returnType != NULL) + { + operatorConstraintReturnType = returnType; + methodMatcher.mSelfType = entry.mSrcType; + wasTransformedUsage = true; + } + } } } else diff --git a/IDEHelper/Tests/src/Nullable.bf b/IDEHelper/Tests/src/Nullable.bf index 331d96cb..852816f9 100644 --- a/IDEHelper/Tests/src/Nullable.bf +++ b/IDEHelper/Tests/src/Nullable.bf @@ -59,6 +59,11 @@ namespace Tests Test.Assert(i == 100); } + public static T DoAdd(T lhs, T rhs) where T : operator T + T + { + return lhs + rhs; + } + [Test] public static void TestOperators() { @@ -84,6 +89,9 @@ namespace Tests Test.Assert(iNull == 100); Test.Assert(iNull != 99); Test.Assert(!(iNull != 100)); + + Test.Assert(DoAdd(iNull, iNull) == 200); + Test.Assert(DoAdd(iNull, null) == null); } } }