From 7c6bdeffbe5344271a53bcd07b275e9d363c6682 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Wed, 19 Jan 2022 14:34:37 -0500 Subject: [PATCH] Improvements to new conversion operator overload invoker --- IDEHelper/Compiler/BfExprEvaluator.cpp | 50 ++++++++- IDEHelper/Compiler/BfModuleTypeUtils.cpp | 47 ++++++-- IDEHelper/Compiler/BfSystem.h | 4 +- IDEHelper/Tests/src/Operators.bf | 132 +++++++++++++++++++++++ 4 files changed, 220 insertions(+), 13 deletions(-) diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 37d19043..ecca4d3d 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -773,6 +773,8 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp anyIsExtension = true; } + BfCastFlags implicitCastFlags = ((mBfEvalExprFlags & BfEvalExprFlags_FromConversionOp) != 0) ? BfCastFlags_NoConversionOperator : BfCastFlags_None; + int newMethodParamCount = newMethodInstance->GetParamCount(); int prevMethodParamCount = prevMethodInstance->GetParamCount(); @@ -794,6 +796,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp bool someArgWasBetter = false; bool someArgWasWorse = false; + for (argIdx = anyIsExtension ? -1 : 0; argIdx < (int)mArguments.size(); argIdx++) { BfTypedValue arg; @@ -856,8 +859,8 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp { prevParamWasConstExpr = true; prevParamType = ((BfConstExprValueType*)prevParamType)->mType; - } - + } + bool paramsEquivalent = paramType == prevParamType; if ((prevParamType == NULL) || (paramType == NULL)) @@ -871,6 +874,14 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp SET_BETTER_OR_WORSE((!isUnspecializedParam) && (!paramType->IsVar()), (!isPrevUnspecializedParam) && (!prevParamType->IsVar())); + if ((mBfEvalExprFlags & BfEvalExprFlags_FromConversionOp_Explicit) != 0) + { + // Pick the one that can implicitly cast + SET_BETTER_OR_WORSE( + mModule->CanCast(arg, paramType, implicitCastFlags), + mModule->CanCast(arg, prevParamType, implicitCastFlags)); + } + // Why did we have this !isUnspecializedParam check? We need the 'canCast' logic still if ((!isBetter) && (!isWorse) /*&& (!isUnspecializedParam) && (!isPrevUnspecializedParam)*/) { @@ -887,8 +898,8 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp isWorse = true; else { - bool canCastFromCurToPrev = mModule->CanCast(mModule->GetFakeTypedValue(paramType), prevParamType); - bool canCastFromPrevToCur = mModule->CanCast(mModule->GetFakeTypedValue(prevParamType), paramType); + bool canCastFromCurToPrev = mModule->CanCast(mModule->GetFakeTypedValue(paramType), prevParamType, implicitCastFlags); + bool canCastFromPrevToCur = mModule->CanCast(mModule->GetFakeTypedValue(prevParamType), paramType, implicitCastFlags); if ((canCastFromCurToPrev) && (canCastFromPrevToCur)) paramsEquivalent = true; @@ -983,6 +994,37 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp RETURN_RESULTS; } } + + // Choose by return type for conversion operators + if (((mBfEvalExprFlags & BfEvalExprFlags_FromConversionOp) != 0) && (!isBetter) && (!isWorse)) + { + auto returnType = newMethodInstance->mReturnType; + auto prevReturnType = prevMethodInstance->mReturnType; + + if ((mBfEvalExprFlags & BfEvalExprFlags_FromConversionOp_Explicit) != 0) + { + // Pick the one that can implicitly cast + SET_BETTER_OR_WORSE( + mModule->CanCast(mModule->GetFakeTypedValue(returnType), mCheckReturnType, implicitCastFlags), + mModule->CanCast(mModule->GetFakeTypedValue(prevReturnType), mCheckReturnType, implicitCastFlags)); + } + + if ((!isBetter) && (!isWorse)) + { + bool canCastFromCurToPrev = mModule->CanCast(mModule->GetFakeTypedValue(returnType), prevReturnType, implicitCastFlags); + bool canCastFromPrevToCur = mModule->CanCast(mModule->GetFakeTypedValue(prevReturnType), returnType, implicitCastFlags); + + if ((canCastFromCurToPrev) && (!canCastFromPrevToCur)) + isWorse = true; + else if ((canCastFromPrevToCur) && (!canCastFromCurToPrev)) + isBetter = true; + } + + if ((isBetter) || (isWorse)) + { + RETURN_RESULTS; + } + } // Check for unused extended params as next param - that still counts as using extended form usedExtendedForm = newMethodInstance->HasParamsArray(); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 9cd263a1..11abd130 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -12649,9 +12649,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp } } } - - methodMatcher.FlushAmbiguityError(); - + if (methodMatcher.mBestMethodDef != NULL) { if (mayBeBox) @@ -12755,12 +12753,30 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp if (moduleMethodInstance.mMethodInstance != NULL) { auto returnType = moduleMethodInstance.mMethodInstance->mReturnType; - if (typedVal.mType->IsTypedPrimitive()) - { + auto paramType = moduleMethodInstance.mMethodInstance->GetParamType(0); + + BfCastFlags implicitCastFlags = (BfCastFlags)(castFlags & ~BfCastFlags_Explicit | BfCastFlags_NoConversionOperator); + + // Check typedPrimitive->underlying cast + if ((explicitCast) && (typedVal.mType->IsTypedPrimitive())) + { auto underlyingType = typedVal.mType->GetUnderlyingType(); if ((returnType != underlyingType) && (CanCast(GetFakeTypedValue(underlyingType), toType, (BfCastFlags)(castFlags | BfCastFlags_NoConversionOperator)))) { - if ((CanCast(GetFakeTypedValue(underlyingType), returnType)) && + float underlyingCanCast = CanCast(GetFakeTypedValue(underlyingType), toType, implicitCastFlags); + float returnCanCast = CanCast(GetFakeTypedValue(returnType), toType, implicitCastFlags); + + if ((underlyingCanCast) && + (!returnCanCast)) + { + doCall = false; + } + else if ((returnCanCast) && + (!underlyingCanCast)) + { + doCall = true; + } + else if ((CanCast(GetFakeTypedValue(underlyingType), returnType, implicitCastFlags)) && (!CanCast(GetFakeTypedValue(returnType), underlyingType))) { doCall = true; @@ -12770,9 +12786,26 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp } } + // Check underlying->typedPrimitive cast + if ((explicitCast) && (toType->IsTypedPrimitive())) + { + auto underlyingType = toType->GetUnderlyingType(); + if ((paramType != underlyingType) && (CanCast(typedVal, underlyingType, (BfCastFlags)(castFlags | BfCastFlags_NoConversionOperator)))) + { + if ((CanCast(GetFakeTypedValue(underlyingType), paramType, implicitCastFlags)) && + (!CanCast(GetFakeTypedValue(paramType), underlyingType, implicitCastFlags))) + { + doCall = true; + } + else + doCall = false; + } + } + if (doCall) { - auto paramType = moduleMethodInstance.mMethodInstance->GetParamType(0); + methodMatcher.FlushAmbiguityError(); + auto wantType = paramType; if (wantType->IsRef()) wantType = wantType->GetUnderlyingType(); diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index 2d358ddd..43ed0934 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -900,8 +900,8 @@ public: { if (mOperatorDeclaration->mExplicitToken != NULL) return mOperatorDeclaration->mExplicitToken->mToken == BfToken_Explicit; - if (mOperatorDeclaration->mOperatorToken != NULL) - return mOperatorDeclaration->mOperatorToken->mToken == BfToken_Explicit; + if (mOperatorDeclaration->mOpTypeToken != NULL) + return mOperatorDeclaration->mOpTypeToken->mToken == BfToken_Explicit; return false; } }; diff --git a/IDEHelper/Tests/src/Operators.bf b/IDEHelper/Tests/src/Operators.bf index 9dae0b52..f426c271 100644 --- a/IDEHelper/Tests/src/Operators.bf +++ b/IDEHelper/Tests/src/Operators.bf @@ -267,6 +267,74 @@ namespace Tests } } + + enum MyEnum + { + Case1, + Case2, + Case3 + } + + enum MyOtherEnum + { + case One; + case Two; + case Three; + + public static explicit operator MyEnum(Self self) + { + return .Case1; + } + } + + struct StructI : int32 + { + public static int8 operator implicit(Self self) + { + return 12; + } + + public static int16 operator implicit(Self self) + { + return 23; + } + } + + struct StructJ + { + public int mVal; + + public static int8 operator implicit(Self self) + { + return 12; + } + + public static int16 operator implicit(Self self) + { + return 23; + } + + public static int32 operator explicit(Self self) + { + return 34; + } + + public static Self operator explicit(int8 val) + { + return .(){mVal = 45}; + } + + public static Self operator implicit(int16 val) + { + return .(){mVal = 56}; + } + + public static Self operator implicit(int32 val) + { + return .(){mVal = 67}; + } + } + /*struct OuterOp { public struct InnerOp @@ -503,6 +571,70 @@ namespace Tests int8 a = 123; int32 b = a + 100; Test.Assert(b == 223); + + MyOtherEnum myEnum = .One; + MyEnum me = (MyEnum)myEnum; + Test.Assert(me == .Case1); + + // + { + StructI si = (.)123; + + int32 i32i = si; + int32 i32e = (int32)si; + int16 i16i = si; + int16 i16e = (int16)si; + int8 i8i = si; + int8 i8e = (int8)si; + float fi = si; + float fe = (float)si; + + Test.Assert(i32i == 23); + Test.Assert(i32e == 123); + Test.Assert(i16i == 23); + Test.Assert(i16e == 23); + Test.Assert(i8i == 12); + Test.Assert(i8e == 12); + Test.Assert(fi == 23); + Test.Assert(fe == 123); + } + + /// + { + StructJ sj = default; + + int32 i32i = sj; + int32 i32e = (int32)sj; + int16 i16i = sj; + int16 i16e = (int16)sj; + int8 i8i = sj; + int8 i8e = (int8)sj; + float fi = sj; + float fe = (float)sj; + + StructJ sji32i = i32i; + StructJ sji32e = (.)i32e; + StructJ sji16i = i16i; + StructJ sji16e = (.)i16e; + StructJ sji8i = i8i; + StructJ sji8e = (.)i8e; + + Test.Assert(i32i == 23); + Test.Assert(i32e == 32); + Test.Assert(i16i == 23); + Test.Assert(i16e == 23); + Test.Assert(i8i == 12); + Test.Assert(i8e == 12); + Test.Assert(fi == 23); + Test.Assert(fe == 34); + + Test.Assert(sji32i.mVal == 67); + Test.Assert(sji32e.mVal == 67); + Test.Assert(sji16i.mVal == 56); + Test.Assert(sji16e.mVal == 56); + Test.Assert(sji8i.mVal == 56); + Test.Assert(sji8e.mVal == 45); + } } struct IntStruct