1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-11 04:52:21 +02:00

Improved method selection with unspecialized arguments

This commit is contained in:
Brian Fiete 2020-08-01 07:48:59 -07:00
parent 1fc6b31ffa
commit ae89a29fd8
3 changed files with 132 additions and 85 deletions

View file

@ -537,6 +537,58 @@ bool BfGenericInferContext::InferGenericArgument(BfMethodInstance* methodInstanc
return true; return true;
} }
int BfMethodMatcher::GetMostSpecificType(BfType* lhs, BfType* rhs)
{
if ((lhs->IsRef()) && (rhs->IsRef()))
return GetMostSpecificType(lhs->GetUnderlyingType(), rhs->GetUnderlyingType());
if ((lhs->IsPointer()) && (rhs->IsPointer()))
return GetMostSpecificType(lhs->GetUnderlyingType(), rhs->GetUnderlyingType());
if ((lhs->IsSizedArray()) && (rhs->IsSizedArray()))
return GetMostSpecificType(lhs->GetUnderlyingType(), rhs->GetUnderlyingType());
if (lhs->IsGenericParam())
return rhs->IsGenericParam() ? -1 : 1;
if (rhs->IsGenericParam())
return 0;
if (lhs->IsUnspecializedType())
{
if (!rhs->IsUnspecializedType())
return 1;
auto lhsTypeInst = lhs->ToTypeInstance();
auto rhsTypeInst = rhs->ToTypeInstance();
if ((rhsTypeInst == NULL) || (lhsTypeInst == NULL) || (lhsTypeInst->mTypeDef != rhsTypeInst->mTypeDef))
return -1;
bool hadLHSMoreSpecific = false;
bool hadRHSMoreSpecific = false;
for (int generigArgIdx = 0; generigArgIdx < (int)lhsTypeInst->mGenericTypeInfo->mTypeGenericArguments.size(); generigArgIdx++)
{
int argMoreSpecific = GetMostSpecificType(lhsTypeInst->mGenericTypeInfo->mTypeGenericArguments[generigArgIdx],
rhsTypeInst->mGenericTypeInfo->mTypeGenericArguments[generigArgIdx]);
if (argMoreSpecific == 0)
hadLHSMoreSpecific = true;
else if (argMoreSpecific == 1)
hadRHSMoreSpecific = true;
}
if ((hadLHSMoreSpecific) && (!hadRHSMoreSpecific))
return 0;
if ((hadRHSMoreSpecific) && (!hadLHSMoreSpecific))
return 1;
return -1;
}
if (rhs->IsUnspecializedType())
return 0;
return -1;
}
void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTypeVector* prevGenericArgumentsSubstitute, void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTypeVector* prevGenericArgumentsSubstitute,
BfMethodInstance* newMethodInstance, BfTypeVector* genericArgumentsSubstitute, BfMethodInstance* newMethodInstance, BfTypeVector* genericArgumentsSubstitute,
bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail) bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail)
@ -644,8 +696,8 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp
int newArgIdx = argIdx + newImplicitParamCount; int newArgIdx = argIdx + newImplicitParamCount;
int prevArgIdx = argIdx + prevImplicitParamCount; int prevArgIdx = argIdx + prevImplicitParamCount;
bool wasGenericParam = (newArgIdx >= 0) && newMethodInstance->WasGenericParam(newArgIdx); bool wasGenericParam = (newArgIdx >= 0) && newMethodInstance->WasGenericParam(newArgIdx);
bool prevWasGenericParam = (prevArgIdx >= 0) && prevMethodInstance->WasGenericParam(prevArgIdx); bool prevWasGenericParam = (prevArgIdx >= 0) && prevMethodInstance->WasGenericParam(prevArgIdx);
BfType* paramType = newMethodInstance->GetParamType(newArgIdx); BfType* paramType = newMethodInstance->GetParamType(newArgIdx);
BfType* prevParamType = prevMethodInstance->GetParamType(prevArgIdx); BfType* prevParamType = prevMethodInstance->GetParamType(prevArgIdx);
@ -656,31 +708,28 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp
BfType* origParamType = paramType; BfType* origParamType = paramType;
BfType* origPrevParamType = prevParamType; BfType* origPrevParamType = prevParamType;
if ((genericArgumentsSubstitute != NULL) && (paramType->IsUnspecializedType())) bool paramWasUnspecialized = paramType->IsUnspecializedType();
{ if ((genericArgumentsSubstitute != NULL) && (paramWasUnspecialized))
{
paramType = mModule->ResolveGenericType(paramType, NULL, genericArgumentsSubstitute, allowSpecializeFail); paramType = mModule->ResolveGenericType(paramType, NULL, genericArgumentsSubstitute, allowSpecializeFail);
paramType = mModule->FixIntUnknown(paramType); paramType = mModule->FixIntUnknown(paramType);
} }
if ((prevGenericArgumentsSubstitute != NULL) && (prevParamType->IsUnspecializedType()))
bool prevParamWasUnspecialized = prevParamType->IsUnspecializedType();
if ((prevGenericArgumentsSubstitute != NULL) && (prevParamWasUnspecialized))
{ {
prevParamType = mModule->ResolveGenericType(prevParamType, NULL, prevGenericArgumentsSubstitute, allowSpecializeFail); prevParamType = mModule->ResolveGenericType(prevParamType, NULL, prevGenericArgumentsSubstitute, allowSpecializeFail);
prevParamType = mModule->FixIntUnknown(prevParamType); prevParamType = mModule->FixIntUnknown(prevParamType);
} }
bool paramsEquivalent = paramType == prevParamType;
if ((wasGenericParam) || (prevWasGenericParam))
{
if ((wasGenericParam) && (!prevWasGenericParam))
worseByGenericParam = true;
else if ((!wasGenericParam) && (prevWasGenericParam))
betterByGenericParam = true;
}
if ((prevParamType == NULL) || (paramType == NULL)) if ((prevParamType == NULL) || (paramType == NULL))
{ {
SET_BETTER_OR_WORSE(paramType != NULL, prevParamType != NULL); SET_BETTER_OR_WORSE(paramType != NULL, prevParamType != NULL);
} }
else if (paramType != prevParamType) else if (paramType != prevParamType)
{ {
bool isUnspecializedParam = paramType->IsUnspecializedType(); bool isUnspecializedParam = paramType->IsUnspecializedType();
bool isPrevUnspecializedParam = prevParamType->IsUnspecializedType(); bool isPrevUnspecializedParam = prevParamType->IsUnspecializedType();
SET_BETTER_OR_WORSE((!isUnspecializedParam) && (!paramType->IsVar()), SET_BETTER_OR_WORSE((!isUnspecializedParam) && (!paramType->IsVar()),
@ -706,6 +755,9 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp
bool canCastFromCurToPrev = mModule->CanCast(mModule->GetFakeTypedValue(paramType), prevParamType); bool canCastFromCurToPrev = mModule->CanCast(mModule->GetFakeTypedValue(paramType), prevParamType);
bool canCastFromPrevToCur = mModule->CanCast(mModule->GetFakeTypedValue(prevParamType), paramType); bool canCastFromPrevToCur = mModule->CanCast(mModule->GetFakeTypedValue(prevParamType), paramType);
if ((canCastFromCurToPrev) && (canCastFromPrevToCur))
paramsEquivalent = true;
if ((canCastFromCurToPrev) && (!canCastFromPrevToCur)) if ((canCastFromCurToPrev) && (!canCastFromPrevToCur))
isBetter = true; isBetter = true;
else if ((canCastFromPrevToCur) && (!canCastFromCurToPrev)) else if ((canCastFromPrevToCur) && (!canCastFromCurToPrev))
@ -727,43 +779,20 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp
else else
isWorse = true; isWorse = true;
} }
}
}
/*if ((paramType->IsIntegral()) && (prevParamType->IsIntegral()))
{
if (paramType == arg.mType)
isBetter = true;
else if (prevParamType == arg.mType)
isWorse = true;
else
{
if (paramType->mSize < prevParamType->mSize)
isBetter = true;
else if (paramType->mSize > prevParamType->mSize)
isWorse = true;
else if (paramType->IsSigned())
isBetter = true;
else
isWorse = true;
} }
} }
else
{
if (mModule->CanCast(mModule->GetFakeTypedValue(paramType), prevParamType))
isBetter = true;
if (mModule->CanCast(mModule->GetFakeTypedValue(prevParamType), paramType))
isWorse = true;
}*/
} }
} }
} }
else if ((wasGenericParam) || (prevWasGenericParam))
if (((!isBetter) && (!isWorse)) && (paramsEquivalent) &&
((paramWasUnspecialized) || (prevParamWasUnspecialized)))
{ {
if (!wasGenericParam) int origTypeMoreSpecific = GetMostSpecificType(origParamType, origPrevParamType);
isBetter = true; if (origTypeMoreSpecific == 0)
else if (!prevWasGenericParam) betterByGenericParam = true;
isWorse = true; else if (origTypeMoreSpecific == 1)
worseByGenericParam = true;
} }
if ((newArgIdx >= 0) && (newMethodInstance->GetParamKind(newArgIdx) == BfParamKind_Params)) if ((newArgIdx >= 0) && (newMethodInstance->GetParamKind(newArgIdx) == BfParamKind_Params))
@ -777,12 +806,17 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp
if ((!isBetter) && (!isWorse)) if ((!isBetter) && (!isWorse))
{ {
isBetter = betterByGenericParam; // Don't allow ambiguity
isWorse = worseByGenericParam; if ((betterByGenericParam && !worseByGenericParam) ||
(!betterByGenericParam && worseByGenericParam))
{
isBetter = betterByGenericParam;
isWorse = worseByGenericParam;
}
} }
if ((isBetter) || (isWorse)) if ((isBetter) || (isWorse))
{ {
RETURN_RESULTS; RETURN_RESULTS;
} }
} }
@ -790,26 +824,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp
// Check for unused extended params as next param - that still counts as using extended form // Check for unused extended params as next param - that still counts as using extended form
usedExtendedForm = newMethodInstance->HasParamsArray(); usedExtendedForm = newMethodInstance->HasParamsArray();
prevUsedExtendedForm = prevMethodInstance->HasParamsArray(); prevUsedExtendedForm = prevMethodInstance->HasParamsArray();
// Non-generic is better than generic.
// The only instance where we can disambiguate is when we have the same number of generic arguments, and the
// constraints for one method's generic argument is a superset of another's. More specific is better.
//TODO: WE changed this to compare the constraints of the ARGUMENTS
// if (newMethodInstance->GetNumGenericArguments() == prevMethodInstance->GetNumGenericArguments())
// {
// if (newMethodInstance->GetNumGenericArguments() != 0)
// {
// for (int genericIdx = 0; genericIdx < (int)newMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); genericIdx++)
// {
// auto newMethodGenericParam = newMethodInstance->mMethodInfoEx->mGenericParams[genericIdx];
// auto prevMethodGenericParam = prevMethodInstance->mMethodInfoEx->mGenericParams[genericIdx];
// RETURN_BETTER_OR_WORSE(mModule->AreConstraintsSubset(prevMethodGenericParam, newMethodGenericParam), mModule->AreConstraintsSubset(newMethodGenericParam, prevMethodGenericParam));
// }
// }
// }
// else
RETURN_BETTER_OR_WORSE(newMethodInstance->GetNumGenericArguments() == 0, prevMethodInstance->GetNumGenericArguments() == 0); RETURN_BETTER_OR_WORSE(newMethodInstance->GetNumGenericArguments() == 0, prevMethodInstance->GetNumGenericArguments() == 0);
// Not using generic delegate params is better // Not using generic delegate params is better
@ -1684,8 +1699,27 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
bool allowSpecializeFail = mModule->mCurTypeInstance->IsUnspecializedType(); bool allowSpecializeFail = mModule->mCurTypeInstance->IsUnspecializedType();
if (mModule->mCurMethodInstance != NULL) if (mModule->mCurMethodInstance != NULL)
allowSpecializeFail = mModule->mCurMethodInstance->mIsUnspecialized; allowSpecializeFail = mModule->mCurMethodInstance->mIsUnspecialized;
// if (mModule->mModuleName == "BeefTest_TestProgram")
// {
// OutputDebugStrF("?Prv: %s\n %s\n?New: %s\n %s\n\n",
// mModule->MethodToString(prevMethodInstance, BfMethodNameFlag_None, &mBestMethodGenericArguments).c_str(),
// mModule->MethodToString(prevMethodInstance, BfMethodNameFlag_None).c_str(),
// mModule->MethodToString(methodInstance, BfMethodNameFlag_None, genericArgumentsSubstitute).c_str(),
// mModule->MethodToString(methodInstance, BfMethodNameFlag_None).c_str());
// }
CompareMethods(prevMethodInstance, &mBestMethodGenericArguments, methodInstance, genericArgumentsSubstitute, &isBetter, &isWorse, allowSpecializeFail); CompareMethods(prevMethodInstance, &mBestMethodGenericArguments, methodInstance, genericArgumentsSubstitute, &isBetter, &isWorse, allowSpecializeFail);
// if (mModule->mModuleName == "BeefTest_TestProgram")
// {
// OutputDebugStrF("%sPrv: %s\n %s\n%sNew: %s\n %s\n\n",
// isWorse ? "*" : " ", mModule->MethodToString(prevMethodInstance, BfMethodNameFlag_None, &mBestMethodGenericArguments).c_str(),
// mModule->MethodToString(prevMethodInstance, BfMethodNameFlag_None).c_str(),
// isBetter ? "*" : " ", mModule->MethodToString(methodInstance, BfMethodNameFlag_None, genericArgumentsSubstitute).c_str(),
// mModule->MethodToString(methodInstance, BfMethodNameFlag_None).c_str());
// }
// If we had both a 'better' and 'worse', that's ambiguous because the methods are each better in different ways (not allowed) // If we had both a 'better' and 'worse', that's ambiguous because the methods are each better in different ways (not allowed)
// And if neither better nor worse then they are equally good, which is not allowed either // And if neither better nor worse then they are equally good, which is not allowed either
if (((!isBetter) && (!isWorse)) || ((isBetter) && (isWorse))) if (((!isBetter) && (!isWorse)) || ((isBetter) && (isWorse)))
@ -13989,20 +14023,27 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
if ((memberRefExpression->mTarget == NULL) && (memberRefExpression->mMemberName == NULL)) if ((memberRefExpression->mTarget == NULL) && (memberRefExpression->mMemberName == NULL))
{ {
if (mExpectingType != NULL) auto expectingType = mExpectingType;
if (expectingType->IsNullable())
{
auto underlyingType = expectingType->GetUnderlyingType();
expectingType = underlyingType;
}
if (expectingType != NULL)
{ {
if (mExpectingType->IsSizedArray()) if (expectingType->IsSizedArray())
{ {
if (mModule->mParentNodeEntry != NULL) if (mModule->mParentNodeEntry != NULL)
{ {
if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(mModule->mParentNodeEntry->mNode)) if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(mModule->mParentNodeEntry->mNode))
{ {
InitializedSizedArray((BfSizedArrayType*)mExpectingType, invocationExpr->mOpenParen, invocationExpr->mArguments, invocationExpr->mCommas, invocationExpr->mCloseParen); InitializedSizedArray((BfSizedArrayType*)expectingType, invocationExpr->mOpenParen, invocationExpr->mArguments, invocationExpr->mCommas, invocationExpr->mCloseParen);
return; return;
} }
} }
} }
else if (mExpectingType->IsStruct()) else if (expectingType->IsStruct())
{ {
if ((wasCapturingMethodInfo) && (autoComplete->mMethodMatchInfo != NULL)) if ((wasCapturingMethodInfo) && (autoComplete->mMethodMatchInfo != NULL))
{ {
@ -14019,14 +14060,6 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
BfResolveArgFlags resolveArgsFlags = BfResolveArgFlag_DeferParamEval; BfResolveArgFlags resolveArgsFlags = BfResolveArgFlag_DeferParamEval;
ResolveArgValues(argValues, resolveArgsFlags); ResolveArgValues(argValues, resolveArgsFlags);
auto expectingType = mExpectingType;
if (expectingType->IsNullable())
{
auto underlyingType = expectingType->GetUnderlyingType();
if (underlyingType->IsTypeInstance())
expectingType = underlyingType;
}
if ((mReceivingValue != NULL) && (mReceivingValue->mType == expectingType) && (mReceivingValue->IsAddr())) if ((mReceivingValue != NULL) && (mReceivingValue->mType == expectingType) && (mReceivingValue->IsAddr()))
{ {
mResult = *mReceivingValue; mResult = *mReceivingValue;
@ -14041,7 +14074,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
} }
} }
} }
else if (mExpectingType->IsGenericParam()) else if (expectingType->IsGenericParam())
{ {
if (mModule->mParentNodeEntry != NULL) if (mModule->mParentNodeEntry != NULL)
{ {
@ -14052,8 +14085,8 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
BfResolveArgFlags resolveArgsFlags = BfResolveArgFlag_None; BfResolveArgFlags resolveArgsFlags = BfResolveArgFlag_None;
ResolveArgValues(argValues, resolveArgsFlags); ResolveArgValues(argValues, resolveArgsFlags);
CheckGenericCtor((BfGenericParamType*)mExpectingType, argValues, invocationExpr->mTarget); CheckGenericCtor((BfGenericParamType*)expectingType, argValues, invocationExpr->mTarget);
mResult = mModule->GetDefaultTypedValue(mExpectingType); mResult = mModule->GetDefaultTypedValue(expectingType);
return; return;
} }
} }
@ -14061,7 +14094,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
else else
{ {
gaveUnqualifiedDotError = true; gaveUnqualifiedDotError = true;
mModule->Fail(StrFormat("Cannot use inferred constructor on type '%s'", mModule->TypeToString(mExpectingType).c_str()), memberRefExpression->mDotToken); mModule->Fail(StrFormat("Cannot use inferred constructor on type '%s'", mModule->TypeToString(expectingType).c_str()), memberRefExpression->mDotToken);
} }
} }
} }

View file

@ -175,6 +175,7 @@ public:
bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail); bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail);
void FlushAmbiguityError(); void FlushAmbiguityError();
bool IsType(BfTypedValue& val, BfType* type); bool IsType(BfTypedValue& val, BfType* type);
int GetMostSpecificType(BfType* lhs, BfType* rhs); // 0, 1, or -1
public: 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);

View file

@ -71,6 +71,16 @@ namespace Tests
return MethodB(val); return MethodB(val);
} }
public static int MethodD<T>(ref T[] x)
{
return 1;
}
public static int MethodD<T>(ref T[][] x)
{
return 2;
}
[Test] [Test]
public static void TestBasics() public static void TestBasics()
{ {
@ -104,6 +114,9 @@ namespace Tests
Test.Assert(MethodB(11) == 2); Test.Assert(MethodB(11) == 2);
Test.Assert(MethodB(("A", "B")) == 3); Test.Assert(MethodB(("A", "B")) == 3);
Test.Assert(MethodC(("A", "B")) == 3); Test.Assert(MethodC(("A", "B")) == 3);
int[][] arrArr = new int[1][];
Test.Assert(MethodD(ref arrArr) == 2);
} }
} }
} }