1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-14 14:24:10 +02:00

Fixed extension method and methodRef infererence issues

This commit is contained in:
Brian Fiete 2020-10-06 05:37:33 -07:00
parent efa44caee0
commit 3b9558a508
3 changed files with 122 additions and 48 deletions

View file

@ -311,10 +311,10 @@ bool BfGenericInferContext::InferGenericArgument(BfMethodInstance* methodInstanc
checkArgType = checkTypeInst->mBaseType; checkArgType = checkTypeInst->mBaseType;
} }
} }
} }
(*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] = argType;
} }
if ((*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] == NULL)
mInferredCount++;
(*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] = argType; (*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] = argType;
mPrevArgValues[wantGenericParam->mGenericParamIdx] = argValue; mPrevArgValues[wantGenericParam->mGenericParamIdx] = argValue;
}; };
@ -590,9 +590,9 @@ int BfMethodMatcher::GetMostSpecificType(BfType* lhs, BfType* rhs)
if (rhs->IsGenericParam()) if (rhs->IsGenericParam())
return 0; return 0;
if (lhs->IsUnspecializedType()) if ((lhs->IsUnspecializedType()) && (lhs->IsGenericTypeInstance()))
{ {
if (!rhs->IsUnspecializedType()) if ((!rhs->IsUnspecializedType()) || (!rhs->IsGenericTypeInstance()))
return 1; return 1;
auto lhsTypeInst = lhs->ToTypeInstance(); auto lhsTypeInst = lhs->ToTypeInstance();
@ -603,7 +603,7 @@ int BfMethodMatcher::GetMostSpecificType(BfType* lhs, BfType* rhs)
bool hadLHSMoreSpecific = false; bool hadLHSMoreSpecific = false;
bool hadRHSMoreSpecific = false; bool hadRHSMoreSpecific = false;
for (int generigArgIdx = 0; generigArgIdx < (int)lhsTypeInst->mGenericTypeInfo->mTypeGenericArguments.size(); generigArgIdx++) for (int generigArgIdx = 0; generigArgIdx < (int)lhsTypeInst->mGenericTypeInfo->mTypeGenericArguments.size(); generigArgIdx++)
{ {
int argMoreSpecific = GetMostSpecificType(lhsTypeInst->mGenericTypeInfo->mTypeGenericArguments[generigArgIdx], int argMoreSpecific = GetMostSpecificType(lhsTypeInst->mGenericTypeInfo->mTypeGenericArguments[generigArgIdx],
@ -1485,25 +1485,35 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
int paramOfs = methodInstance->GetImplicitParamCount(); int paramOfs = methodInstance->GetImplicitParamCount();
int paramCount = methodInstance->GetParamCount(); int paramCount = methodInstance->GetParamCount();
SizedArray<int, 8> deferredArgs;
int argIdx = 0; int argIdx = 0;
int paramIdx = 0; int paramIdx = 0;
if (checkMethod->mHasAppend) if (checkMethod->mHasAppend)
paramIdx++; paramIdx++;
if (checkMethod->mMethodType == BfMethodType_Extension) if (checkMethod->mMethodType == BfMethodType_Extension)
{ {
argIdx--; argIdx--;
} }
paramIdx += paramOfs; paramIdx += paramOfs;
for ( ; argIdx < (int)mArguments.size(); argIdx++) bool hadInferFailure = false;
int inferParamOffset = paramOfs - argIdx;
enum ResultKind
{ {
if (paramIdx >= paramCount) ResultKind_Ok,
break; // Possible for delegate 'params' type methods ResultKind_Failed,
ResultKind_Deferred,
};
auto _CheckArg = [&](int argIdx)
{
paramIdx = argIdx + inferParamOffset;
auto wantType = methodInstance->GetParamType(paramIdx); auto wantType = methodInstance->GetParamType(paramIdx);
auto checkType = wantType; auto checkType = wantType;
auto origCheckType = checkType; auto origCheckType = checkType;
@ -1514,19 +1524,24 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
checkType = NULL; checkType = NULL;
if (genericParamType->mGenericParamKind == BfGenericParamKind_Method) if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
{ {
if ((genericArgumentsSubstitute != NULL) && (genericParamType->mGenericParamIdx < (int)genericArgumentsSubstitute->size())) if ((genericArgumentsSubstitute != NULL) && (genericParamType->mGenericParamIdx < (int)genericArgumentsSubstitute->size()))
checkType = (*genericArgumentsSubstitute)[genericParamType->mGenericParamIdx]; checkType = (*genericArgumentsSubstitute)[genericParamType->mGenericParamIdx];
genericParamInstance = methodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx]; genericParamInstance = methodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx];
} }
else else
genericParamInstance = mModule->GetGenericParamInstance(genericParamType); genericParamInstance = mModule->GetGenericParamInstance(genericParamType);
if (checkType == NULL) if (checkType == NULL)
{
checkType = genericParamInstance->mTypeConstraint; checkType = genericParamInstance->mTypeConstraint;
origCheckType = checkType; // We can do "ResolveGenericType" on this type
}
} }
bool attemptedGenericResolve = false;
if ((checkType != NULL) && (genericArgumentsSubstitute != NULL) && (checkType->IsUnspecializedType())) if ((checkType != NULL) && (genericArgumentsSubstitute != NULL) && (checkType->IsUnspecializedType()))
{ {
attemptedGenericResolve = true;
checkType = mModule->ResolveGenericType(origCheckType, NULL, genericArgumentsSubstitute); checkType = mModule->ResolveGenericType(origCheckType, NULL, genericArgumentsSubstitute);
} }
@ -1541,18 +1556,53 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
{ {
auto type = argTypedValue.mType; auto type = argTypedValue.mType;
if (!argTypedValue) if (!argTypedValue)
goto NoMatch; {
if ((checkType == NULL) && (attemptedGenericResolve) && (genericInferContext.GetUnresolvedCount() >= 2))
{
deferredArgs.Add(argIdx);
return ResultKind_Deferred;
}
return ResultKind_Failed;
}
if (type->IsVar()) if (type->IsVar())
mHasVarArguments = true; mHasVarArguments = true;
genericInferContext.mCheckedTypeSet.Clear(); genericInferContext.mCheckedTypeSet.Clear();
if (!genericInferContext.InferGenericArgument(methodInstance, type, wantType, argTypedValue.mValue)) if (!genericInferContext.InferGenericArgument(methodInstance, type, wantType, argTypedValue.mValue))
goto NoMatch; return ResultKind_Failed;
} }
} }
return ResultKind_Ok;
};
paramIdx++; for (; argIdx < (int)mArguments.size(); argIdx++)
{
paramIdx = argIdx + inferParamOffset;
if (paramIdx >= paramCount)
break; // Possible for delegate 'params' type methods
auto resultKind = _CheckArg(argIdx);
if (resultKind == ResultKind_Failed)
goto NoMatch;
}
while (!deferredArgs.IsEmpty())
{
int prevDeferredSize = (int)deferredArgs.size();
for (int i = 0; i < prevDeferredSize; i++)
{
auto resultKind = _CheckArg(deferredArgs[i]);
if (resultKind == ResultKind_Failed)
goto NoMatch;
}
deferredArgs.RemoveRange(0, prevDeferredSize);
if (prevDeferredSize == deferredArgs.size())
{
// No progress
goto NoMatch;
}
} }
// //
@ -5619,6 +5669,10 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
bool failed = false; bool failed = false;
while (true) while (true)
{ {
int argExprIdx = argIdx;
if (methodDef->mMethodType == BfMethodType_Extension)
argExprIdx--;
bool isThis = (paramIdx == -1) || ((methodDef->mHasExplicitThis) && (paramIdx == 0)); bool isThis = (paramIdx == -1) || ((methodDef->mHasExplicitThis) && (paramIdx == 0));
bool isDirectPass = false; bool isDirectPass = false;
@ -5626,22 +5680,22 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
{ {
if (methodInstance->IsVarArgs()) if (methodInstance->IsVarArgs())
{ {
if (argIdx >= (int)argValues.size()) if (argExprIdx >= (int)argValues.size())
break; break;
BfTypedValue argValue = ResolveArgValue(argValues[argIdx], NULL); BfTypedValue argValue = ResolveArgValue(argValues[argExprIdx], NULL);
if (argValue) if (argValue)
{ {
auto typeInst = argValue.mType->ToTypeInstance(); auto typeInst = argValue.mType->ToTypeInstance();
if (argValue.mType == mModule->GetPrimitiveType(BfTypeCode_Float)) if (argValue.mType == mModule->GetPrimitiveType(BfTypeCode_Float))
argValue = mModule->Cast(argValues[argIdx].mExpression, argValue, mModule->GetPrimitiveType(BfTypeCode_Double)); argValue = mModule->Cast(argValues[argExprIdx].mExpression, argValue, mModule->GetPrimitiveType(BfTypeCode_Double));
if ((typeInst != NULL) && (typeInst->mTypeDef == mModule->mCompiler->mStringTypeDef)) if ((typeInst != NULL) && (typeInst->mTypeDef == mModule->mCompiler->mStringTypeDef))
{ {
BfType* charType = mModule->GetPrimitiveType(BfTypeCode_Char8); BfType* charType = mModule->GetPrimitiveType(BfTypeCode_Char8);
BfType* charPtrType = mModule->CreatePointerType(charType); BfType* charPtrType = mModule->CreatePointerType(charType);
argValue = mModule->Cast(argValues[argIdx].mExpression, argValue, charPtrType); argValue = mModule->Cast(argValues[argExprIdx].mExpression, argValue, charPtrType);
} }
PushArg(argValue, irArgs, true, false); PushArg(argValue, irArgs, true, false);
} }
@ -5649,9 +5703,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
continue; continue;
} }
if (argIdx < (int)argValues.size()) if (argExprIdx < (int)argValues.size())
{ {
BfAstNode* errorRef = argValues[argIdx].mExpression; BfAstNode* errorRef = argValues[argExprIdx].mExpression;
if (errorRef == NULL) if (errorRef == NULL)
errorRef = targetSrc; errorRef = targetSrc;
@ -5659,7 +5713,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if ((prevBindResult.mPrevVal != NULL) && (prevBindResult.mPrevVal->mBindType != NULL)) if ((prevBindResult.mPrevVal != NULL) && (prevBindResult.mPrevVal->mBindType != NULL))
error = mModule->Fail(StrFormat("Method '%s' has too many parameters to bind to '%s'.", mModule->MethodToString(methodInstance).c_str(), mModule->TypeToString(prevBindResult.mPrevVal->mBindType).c_str()), errorRef); error = mModule->Fail(StrFormat("Method '%s' has too many parameters to bind to '%s'.", mModule->MethodToString(methodInstance).c_str(), mModule->TypeToString(prevBindResult.mPrevVal->mBindType).c_str()), errorRef);
else else
error = mModule->Fail(StrFormat("Too many arguments, expected %d fewer.", (int)argValues.size() - argIdx), errorRef); error = mModule->Fail(StrFormat("Too many arguments, expected %d fewer.", (int)argValues.size() - argExprIdx), errorRef);
if ((error != NULL) && (methodInstance->mMethodDef->mMethodDeclaration != NULL)) if ((error != NULL) && (methodInstance->mMethodDef->mMethodDeclaration != NULL))
mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("See method declaration"), methodInstance->mMethodDef->GetRefNode()); mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("See method declaration"), methodInstance->mMethodDef->GetRefNode());
failed = true; failed = true;
@ -5795,15 +5849,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
} }
BfAstNode* arg = NULL; BfAstNode* arg = NULL;
bool hadMissingArg = false; bool hadMissingArg = false;
if (argExprIdx == -1)
int argExprIdx = argIdx; arg = targetSrc;
if (methodDef->mMethodType == BfMethodType_Extension)
{
argExprIdx--;
if (argExprIdx == -1)
arg = targetSrc;
}
if (argExprIdx >= 0) if (argExprIdx >= 0)
{ {
if (argExprIdx < (int)argValues.size()) if (argExprIdx < (int)argValues.size())
@ -6038,7 +6086,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (wantType->IsMethodRef()) if (wantType->IsMethodRef())
{ {
auto expr = argValues[argIdx].mExpression; auto expr = argValues[argExprIdx].mExpression;
if (expr != NULL) if (expr != NULL)
SetMethodElementType(expr); SetMethodElementType(expr);
@ -13249,7 +13297,7 @@ BfModuleMethodInstance BfExprEvaluator::GetSelectedMethod(BfAstNode* targetSrc,
if ((genericParam->mTypeConstraint != NULL) && (genericParam->mTypeConstraint->IsDelegate())) if ((genericParam->mTypeConstraint != NULL) && (genericParam->mTypeConstraint->IsDelegate()))
{ {
// The only other option was to bind to a MethodRef // The only other option was to bind to a MethodRef
genericArg = genericParam->mTypeConstraint; genericArg = mModule->ResolveGenericType(genericParam->mTypeConstraint, NULL, &methodMatcher.mBestMethodGenericArguments);
} }
else else
{ {
@ -13275,18 +13323,19 @@ BfModuleMethodInstance BfExprEvaluator::GetSelectedMethod(BfAstNode* targetSrc,
} }
} }
} }
} }
if (genericArg == NULL)
{
failed = true;
mModule->Fail(StrFormat("Unable to determine generic argument '%s'", methodDef->mGenericParams[checkGenericIdx]->mName.c_str()).c_str(), targetSrc);
if ((genericParam->mTypeConstraint != NULL) && (!genericParam->mTypeConstraint->IsUnspecializedType()))
genericArg = genericParam->mTypeConstraint;
else
genericArg = mModule->mContext->mBfObjectType;
}
} }
if (genericArg == NULL)
{
failed = true;
mModule->Fail(StrFormat("Unable to determine generic argument '%s'", methodDef->mGenericParams[checkGenericIdx]->mName.c_str()).c_str(), targetSrc);
if ((genericParam->mTypeConstraint != NULL) && (!genericParam->mTypeConstraint->IsUnspecializedType()))
genericArg = genericParam->mTypeConstraint;
else
genericArg = mModule->mContext->mBfObjectType;
}
resolvedGenericArguments.push_back(genericArg); resolvedGenericArguments.push_back(genericArg);
} }
else else

View file

@ -109,9 +109,19 @@ public:
BfModule* mModule; BfModule* mModule;
BfTypeVector* mCheckMethodGenericArguments; BfTypeVector* mCheckMethodGenericArguments;
SizedArray<BfIRValue, 4> mPrevArgValues; SizedArray<BfIRValue, 4> mPrevArgValues;
int mInferredCount;
public: public:
BfGenericInferContext()
{
mModule = NULL;
mInferredCount = 0;
}
bool InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue); bool InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue);
int GetUnresolvedCount()
{
return (int)mCheckMethodGenericArguments->size() - mInferredCount;
}
}; };
class BfMethodMatcher class BfMethodMatcher

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
namespace Tests namespace Tests
{ {
@ -46,6 +47,16 @@ namespace Tests
return total; return total;
} }
static T DoOnListA<T, TDlg>(List<T> val, TDlg dlg) where TDlg : delegate T(T a)
{
return dlg(val[0]);
}
static T DoOnListB<T, TDlg>(TDlg dlg, List<T> val) where TDlg : delegate T(T a)
{
return dlg(val[0]);
}
[Test] [Test]
public static void TestBasics() public static void TestBasics()
{ {
@ -63,6 +74,10 @@ namespace Tests
float val4 = Do10f(=> FuncA, 0.34f); float val4 = Do10f(=> FuncA, 0.34f);
Test.Assert(val4 == -34); Test.Assert(val4 == -34);
List<float> fList = scope .() { 1.2f, 2.3f };
Test.Assert(DoOnListA(fList, (val) => val + 100) == 101.2f);
Test.Assert(DoOnListB((val) => val + 200, fList) == 201.2f);
} }
struct MethodRefHolder<T> where T : delegate int(int num) struct MethodRefHolder<T> where T : delegate int(int num)