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

Added proper support for explicit 'this' in functions

This commit is contained in:
Brian Fiete 2020-07-10 06:40:24 -07:00
parent 4a08a9702e
commit 7f726ef9ba
8 changed files with 438 additions and 142 deletions

View file

@ -7307,7 +7307,7 @@ void BfCompiler::GenerateAutocompleteInfo()
int dispParamIdx = 0; int dispParamIdx = 0;
StringT<64> paramName; StringT<64> paramName;
for (int paramIdx = 0; paramIdx < (int)methodInstance->GetParamCount(); paramIdx++) for (int paramIdx = methodInstance->HasExplicitThis() ? -1 : 0; paramIdx < (int)methodInstance->GetParamCount(); paramIdx++)
{ {
auto paramKind = methodInstance->GetParamKind(paramIdx); auto paramKind = methodInstance->GetParamKind(paramIdx);
if ((paramKind == BfParamKind_ImplicitCapture) || (paramKind == BfParamKind_AppendIdx)) if ((paramKind == BfParamKind_ImplicitCapture) || (paramKind == BfParamKind_AppendIdx))

View file

@ -5359,7 +5359,15 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (!skipThis) if (!skipThis)
{ {
if (!methodDef->mIsStatic) if ((target) && (target.mType->IsFunction()))
{
CheckResultForReading(target);
target = mModule->LoadValue(target);
auto funcType = mModule->mBfIRBuilder->MapMethod(moduleMethodInstance.mMethodInstance);
auto funcPtrType = mModule->mBfIRBuilder->GetPointerTo(funcType);
moduleMethodInstance.mFunc = mModule->mBfIRBuilder->CreateIntToPtr(target.mValue, funcPtrType);
}
else if (!methodDef->mIsStatic)
{ {
if (!target) if (!target)
{ {
@ -5408,14 +5416,6 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
PushThis(targetSrc, target, moduleMethodInstance.mMethodInstance, irArgs, skipMutCheck); PushThis(targetSrc, target, moduleMethodInstance.mMethodInstance, irArgs, skipMutCheck);
} }
} }
else if ((target) && (target.mType->IsFunction()))
{
CheckResultForReading(target);
target = mModule->LoadValue(target);
auto funcType = mModule->mBfIRBuilder->MapMethod(moduleMethodInstance.mMethodInstance);
auto funcPtrType = mModule->mBfIRBuilder->GetPointerTo(funcType);
moduleMethodInstance.mFunc = mModule->mBfIRBuilder->CreateIntToPtr(target.mValue, funcPtrType);
}
else if (methodDef->mMethodType == BfMethodType_Extension) else if (methodDef->mMethodType == BfMethodType_Extension)
{ {
// Handled in args // Handled in args
@ -5462,6 +5462,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if ((boxScopeData == NULL) && (mModule->mCurMethodState != NULL)) if ((boxScopeData == NULL) && (mModule->mCurMethodState != NULL))
boxScopeData = mModule->mCurMethodState->mCurScope; boxScopeData = mModule->mCurMethodState->mCurScope;
if (methodInstance->HasExplicitThis())
paramIdx = -1;
bool failed = false; bool failed = false;
while (true) while (true)
{ {
@ -5500,7 +5503,11 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (errorRef == NULL) if (errorRef == NULL)
errorRef = targetSrc; errorRef = targetSrc;
BfError* error = mModule->Fail(StrFormat("Too many arguments, expected %d fewer.", (int)argValues.size() - argIdx), errorRef); BfError* error;
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);
else
error = mModule->Fail(StrFormat("Too many arguments, expected %d fewer.", (int)argValues.size() - argIdx), 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;
@ -5833,6 +5840,12 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (argValue) if (argValue)
{ {
if ((paramIdx == -1) && (argValue.mType->IsRef()))
{
// Convert a 'ref this' to a 'this*'
argValue.mType = mModule->CreatePointerType(argValue.mType->GetUnderlyingType());
}
BfAstNode* refNode = arg; BfAstNode* refNode = arg;
if (refNode == NULL) if (refNode == NULL)
refNode = targetSrc; refNode = targetSrc;
@ -5903,7 +5916,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (argValue) if (argValue)
{ {
if (wantsSplat) if (paramIdx == -1)
PushThis(targetSrc, argValue, methodInstance, irArgs);
else if (wantsSplat)
SplatArgs(argValue, irArgs); SplatArgs(argValue, irArgs);
else else
PushArg(argValue, irArgs, true, false); PushArg(argValue, irArgs, true, false);
@ -9719,6 +9734,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr)
BfFunctionBindResult bindResult; BfFunctionBindResult bindResult;
bindResult.mSkipMutCheck = true; // Allow operating on copies bindResult.mSkipMutCheck = true; // Allow operating on copies
bindResult.mBindType = delegateTypeInstance;
// //
{ {
SetAndRestoreValue<BfType*> prevExpectingType(mExpectingType, methodInstance->mReturnType); SetAndRestoreValue<BfType*> prevExpectingType(mExpectingType, methodInstance->mReturnType);
@ -12322,7 +12338,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
else else
autoComplete->mIsCapturingMethodMatchInfo = false; autoComplete->mIsCapturingMethodMatchInfo = false;
} }
else else if (!resolvedTypeRef->IsFunction())
{ {
MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInstance, argValues, false, true); MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInstance, argValues, false, true);
} }

View file

@ -238,6 +238,7 @@ public:
BfTypedValue mTarget; BfTypedValue mTarget;
BfIRValue mFunc; BfIRValue mFunc;
BfMethodInstance* mMethodInstance; BfMethodInstance* mMethodInstance;
BfType* mBindType;
bool mSkipThis; bool mSkipThis;
bool mSkipMutCheck; bool mSkipMutCheck;
bool mWantsArgs; bool mWantsArgs;
@ -248,6 +249,7 @@ public:
BfFunctionBindResult() BfFunctionBindResult()
{ {
mMethodInstance = NULL; mMethodInstance = NULL;
mBindType = NULL;
mSkipMutCheck = false; mSkipMutCheck = false;
mWantsArgs = false; mWantsArgs = false;
mSkipThis = false; mSkipThis = false;

View file

@ -309,7 +309,7 @@ void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name
name += "N8functionI"; name += "N8functionI";
SizedArray<BfType*, 8> typeVec; SizedArray<BfType*, 8> typeVec;
typeVec.push_back(invokeMethodInst->mReturnType); typeVec.push_back(invokeMethodInst->mReturnType);
for (int paramIdx = 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++) for (int paramIdx = invokeMethodInst->HasExplicitThis() ? -1 : 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++)
{ {
name += "_"; name += "_";
name += invokeMethodInst->GetParamName(paramIdx); name += invokeMethodInst->GetParamName(paramIdx);
@ -1156,44 +1156,45 @@ bool BfMSMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl&
{ {
BF_ASSERT(newNameSub.mTypeInst->mTypeDef->mMethods[0]->mName == "Invoke"); BF_ASSERT(newNameSub.mTypeInst->mTypeDef->mMethods[0]->mName == "Invoke");
// Why did we have this in here? It appears it was to fix some sort of bug...
// //
{
auto methodDef = newNameSub.mTypeInst->mTypeDef->mMethods[0];
if (newNameSub.mTypeInst->IsDelegate())
name += "?$delegate";
else
name += "?$function";
SizedArray<BfType*, 8> typeVec;
typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mReturnTypeRef)->mType);
for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++)
{
name += "_";
name += methodDef->mParams[paramIdx]->mName;
typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mParams[paramIdx]->mTypeRef)->mType);
}
name += '@';
if (!typeVec.empty())
AddGenericArgs(mangleContext, name, typeVec);
name += '@';
}
// auto invokeMethodInst = mangleContext.mModule->GetDelegateInvokeMethod(newNameSub.mTypeInst);
// if (newNameSub.mTypeInst->IsDelegate())
// name += "?$delegate";
// else
// name += "?$function";
// SizedArray<BfType*, 8> typeVec;
// typeVec.push_back(invokeMethodInst->mReturnType);
// for (int paramIdx = 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++)
// { // {
// name += "_"; // auto methodDef = newNameSub.mTypeInst->mTypeDef->mMethods[0];
// name += invokeMethodInst->GetParamName(paramIdx); // if (newNameSub.mTypeInst->IsDelegate())
// typeVec.push_back(invokeMethodInst->GetParamType(paramIdx)); // name += "?$delegate";
// else
// name += "?$function";
// SizedArray<BfType*, 8> typeVec;
// typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mReturnTypeRef)->mType);
// for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++)
// {
// name += "_";
// name += methodDef->mParams[paramIdx]->mName;
// typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mParams[paramIdx]->mTypeRef)->mType);
// }
// name += '@';
// if (!typeVec.empty())
// AddGenericArgs(mangleContext, name, typeVec);
// name += '@';
// } // }
// name += '@';
// if (!typeVec.empty()) auto invokeMethodInst = mangleContext.mModule->GetDelegateInvokeMethod(newNameSub.mTypeInst);
// AddGenericArgs(mangleContext, name, typeVec); if (newNameSub.mTypeInst->IsDelegate())
// name += '@'; name += "?$delegate";
else
name += "?$function";
SizedArray<BfType*, 8> typeVec;
typeVec.push_back(invokeMethodInst->mReturnType);
for (int paramIdx = invokeMethodInst->HasExplicitThis() ? -1 : 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++)
{
name += "_";
name += invokeMethodInst->GetParamName(paramIdx);
typeVec.push_back(invokeMethodInst->GetParamType(paramIdx));
}
name += '@';
if (!typeVec.empty())
AddGenericArgs(mangleContext, name, typeVec);
name += '@';
} }
else if (newNameSub.mTypeInst->IsBoxed()) else if (newNameSub.mTypeInst->IsBoxed())
{ {

View file

@ -7774,7 +7774,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
if ((resolvedType->IsValueType()) || (resolvedType->IsGenericParam())) if ((resolvedType->IsValueType()) || (resolvedType->IsGenericParam()))
needsRefWrap = true; needsRefWrap = true;
if ((InDefinitionSection()) && (!resolvedType->IsGenericParam())) if ((InDefinitionSection()) && (!resolvedType->IsGenericParam()) && ((resolveFlags & BfResolveTypeRefFlag_NoWarnOnMut) == 0))
{ {
if (!resolvedType->IsValueType()) if (!resolvedType->IsValueType())
Warn(0, StrFormat("Specified 'mut' has no effect on '%s' since reference types are always mutable", TypeToString(resolvedType).c_str()), refTypeRef->mRefToken); Warn(0, StrFormat("Specified 'mut' has no effect on '%s' since reference types are always mutable", TypeToString(resolvedType).c_str()), refTypeRef->mRefToken);
@ -8375,19 +8375,56 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
isUnspecialized = true; isUnspecialized = true;
}; };
bool failed = false;
auto returnType = ResolveTypeRef(delegateTypeRef->mReturnType); auto returnType = ResolveTypeRef(delegateTypeRef->mReturnType);
if (returnType == NULL) if (returnType == NULL)
returnType = GetPrimitiveType(BfTypeCode_Var); returnType = GetPrimitiveType(BfTypeCode_Var);
_CheckType(returnType); _CheckType(returnType);
BfType* functionThisType = NULL;
bool hasMutSpecifier = false;
bool isFirst = true;
bool isDelegate = delegateTypeRef->mTypeToken->GetToken() == BfToken_Delegate;
Array<BfType*> paramTypes; Array<BfType*> paramTypes;
for (auto param : delegateTypeRef->mParams) for (auto param : delegateTypeRef->mParams)
{ {
auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowRef); BfResolveTypeRefFlags resolveTypeFlags = BfResolveTypeRefFlag_AllowRef;
if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
resolveTypeFlags = (BfResolveTypeRefFlags)(resolveTypeFlags | BfResolveTypeRefFlag_NoWarnOnMut);
auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, resolveTypeFlags);
if (paramType == NULL) if (paramType == NULL)
{
failed = true;
paramType = GetPrimitiveType(BfTypeCode_Var); paramType = GetPrimitiveType(BfTypeCode_Var);
paramTypes.Add(paramType); }
_CheckType(paramType);
if ((!isDelegate) && (isFirst) && (param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
{
functionThisType = paramType;
if (functionThisType->IsRef())
{
auto refType = (BfRefType*)functionThisType;
if (refType->mRefKind != BfRefType::RefKind_Mut)
{
if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(param->mTypeRef))
{
failed = true;
Fail("Only 'mut' is allowed here", refTypeRef->mRefToken);
}
}
hasMutSpecifier = true;
functionThisType = refType->mElementType;
}
}
else
{
paramTypes.Add(paramType);
_CheckType(paramType);
}
isFirst = false;
} }
if ((mCurTypeInstance == NULL) || (!mCurTypeInstance->IsGenericTypeInstance())) if ((mCurTypeInstance == NULL) || (!mCurTypeInstance->IsGenericTypeInstance()))
@ -8414,25 +8451,6 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
CheckUnspecializedGenericType(genericTypeInst, populateType); CheckUnspecializedGenericType(genericTypeInst, populateType);
// for (auto paramType : paramTypes)
// {
// if (paramType->IsUnspecializedType())
// genericTypeInst->mGenericTypeInfo->mIsUnspecialized = true;
// if (paramType->IsUnspecializedTypeVariation())
// genericTypeInst->mGenericTypeInfo->mIsUnspecializedVariation = true;
// if (paramType->IsGenericParam())
// {
// BfGenericParamType* genericParamType = new BfGenericParamType();
// if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
// {
// if (genericParamType->mGenericParamIdx >= genericTypeInst->mGenericTypeInfo->mGenericParams.size())
// genericTypeInst->mGenericTypeInfo->mIsUnspecializedVariation = true;
// }
// else
// genericTypeInst->mGenericTypeInfo->mIsUnspecializedVariation = true;
// }
// }
// We don't ever need to do an actual pass over generic delegate methods, so it's safe to set the 'unspecialized variation' flag // We don't ever need to do an actual pass over generic delegate methods, so it's safe to set the 'unspecialized variation' flag
if (isUnspecialized) if (isUnspecialized)
{ {
@ -8474,7 +8492,11 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
methodDef->mName = "Invoke"; methodDef->mName = "Invoke";
methodDef->mProtection = BfProtection_Public; methodDef->mProtection = BfProtection_Public;
methodDef->mIdx = 0; methodDef->mIdx = 0;
methodDef->mIsStatic = !typeDef->mIsDelegate; methodDef->mIsStatic = !typeDef->mIsDelegate && (functionThisType == NULL);
methodDef->mIsMutating = true;
if ((functionThisType != NULL) && (functionThisType->IsValueType()) && (!hasMutSpecifier))
methodDef->mIsMutating = false;
auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>(); auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>();
delegateInfo->mDirectAllocNodes.push_back(directTypeRef); delegateInfo->mDirectAllocNodes.push_back(directTypeRef);
@ -8489,12 +8511,14 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
directTypeRef->Init(returnType); directTypeRef->Init(returnType);
methodDef->mReturnTypeRef = directTypeRef; methodDef->mReturnTypeRef = directTypeRef;
delegateInfo->mReturnType = returnType; delegateInfo->mReturnType = returnType;
delegateInfo->mFunctionThisType = functionThisType;
auto hashVal = mContext->mResolvedTypes.Hash(typeRef, &lookupCtx); auto hashVal = mContext->mResolvedTypes.Hash(typeRef, &lookupCtx);
int paramSrcOfs = (functionThisType != NULL) ? 1 : 0;
for (int paramIdx = 0; paramIdx < (int)paramTypes.size(); paramIdx++) for (int paramIdx = 0; paramIdx < (int)paramTypes.size(); paramIdx++)
{ {
auto param = delegateTypeRef->mParams[paramIdx]; auto param = delegateTypeRef->mParams[paramIdx + paramSrcOfs];
auto paramType = paramTypes[paramIdx]; auto paramType = paramTypes[paramIdx];
if (paramType == NULL) if (paramType == NULL)
paramType = GetPrimitiveType(BfTypeCode_Var); paramType = GetPrimitiveType(BfTypeCode_Var);
@ -8506,7 +8530,6 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
if (!paramType->IsReified()) if (!paramType->IsReified())
delegateType->mIsReified = false; delegateType->mIsReified = false;
auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>(); auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>();
delegateInfo->mDirectAllocNodes.push_back(directTypeRef); delegateInfo->mDirectAllocNodes.push_back(directTypeRef);
directTypeRef->Init(paramType); directTypeRef->Init(paramType);
@ -8522,6 +8545,13 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
typeDef->mMethods.push_back(methodDef); typeDef->mMethods.push_back(methodDef);
if (failed)
{
delete delegateType;
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
}
// //
BfDefBuilder::AddMethod(typeDef, BfMethodType_Ctor, BfProtection_Public, false, ""); BfDefBuilder::AddMethod(typeDef, BfMethodType_Ctor, BfProtection_Public, false, "");
@ -8537,14 +8567,15 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
for (auto paramType : paramTypes) for (auto paramType : paramTypes)
AddDependency(paramType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue); AddDependency(paramType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
// #ifdef _DEBUG #ifdef _DEBUG
// if (BfResolvedTypeSet::Hash(delegateType, &lookupCtx) != resolvedEntry->mHash) if (BfResolvedTypeSet::Hash(delegateType, &lookupCtx) != resolvedEntry->mHash)
// { {
// int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx); int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx);
// int typeHash = BfResolvedTypeSet::Hash(delegateType, &lookupCtx); int typeHash = BfResolvedTypeSet::Hash(delegateType, &lookupCtx);
// BF_ASSERT(refHash == typeHash); BF_ASSERT(refHash == typeHash);
// } }
// #endif BF_ASSERT(BfResolvedTypeSet::Equals(delegateType, typeRef, &lookupCtx));
#endif
BF_ASSERT(BfResolvedTypeSet::Hash(delegateType, &lookupCtx) == resolvedEntry->mHash); BF_ASSERT(BfResolvedTypeSet::Hash(delegateType, &lookupCtx) == resolvedEntry->mHash);
@ -8847,7 +8878,56 @@ BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, BfMethodInstance* methodI
} }
else if (invokeMethodInstance->IsExactMatch(methodInstance, false, false)) else if (invokeMethodInstance->IsExactMatch(methodInstance, false, false))
{ {
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider adding '%s this' to the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(methodInstance->GetParamType(-1)).c_str()), srcNode); bool handled = false;
auto thisType = methodInstance->GetParamType(-1);
if (thisType != NULL)
{
if (invokeMethodInstance->HasExplicitThis())
{
auto invokeThisType = invokeMethodInstance->GetParamType(-1);
bool thisWasPtr = false;
if (thisType->IsPointer())
{
thisType = thisType->GetUnderlyingType();
thisWasPtr = true;
}
bool invokeThisWasPtr = false;
if (invokeThisType->IsPointer())
{
invokeThisType = invokeThisType->GetUnderlyingType();
invokeThisWasPtr = true;
}
if (invokeThisType == thisType)
{
if (invokeThisWasPtr != thisWasPtr)
{
if (invokeThisWasPtr)
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider removing 'mut' from 'mut %s this' in the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(thisType).c_str()), srcNode);
else
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider adding 'mut' specifier to '%s this' in the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(thisType).c_str()), srcNode);
handled = true;
}
}
}
}
if ((!methodInstance->mMethodDef->mIsStatic) && (!invokeMethodInstance->HasExplicitThis()))
{
handled = true;
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider adding '%s this' to the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(thisType).c_str()), srcNode);
}
if (!handled)
{
if (invokeMethodInstance->mMethodDef->mIsStatic)
Fail(StrFormat("Static method '%s' cannot match '%s'", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str()).c_str(), srcNode);
else
Fail(StrFormat("Non-static method '%s' cannot match '%s'", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str()).c_str(), srcNode);
}
} }
} }
return BfIRValue(); return BfIRValue();
@ -10386,33 +10466,40 @@ BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, Bf
(fromMethodInst->mReturnType == toMethodInst->mReturnType) && (fromMethodInst->mReturnType == toMethodInst->mReturnType) &&
(fromMethodInst->GetParamCount() == toMethodInst->GetParamCount())) (fromMethodInst->GetParamCount() == toMethodInst->GetParamCount()))
{ {
bool matched = true; bool matched = true;
StringT<64> fromParamName; StringT<64> fromParamName;
StringT<64> toParamName; StringT<64> toParamName;
for (int paramIdx = 0; paramIdx < (int)fromMethodInst->GetParamCount(); paramIdx++) if (fromMethodInst->HasExplicitThis() != toMethodInst->HasExplicitThis())
{ {
bool nameMatches = true; matched = false;
}
else
{
for (int paramIdx = fromMethodInst->HasExplicitThis() ? -1 : 0; paramIdx < (int)fromMethodInst->GetParamCount(); paramIdx++)
{
bool nameMatches = true;
if (!explicitCast) if (!explicitCast)
{ {
fromMethodInst->GetParamName(paramIdx, fromParamName); fromMethodInst->GetParamName(paramIdx, fromParamName);
toMethodInst->GetParamName(paramIdx, toParamName); toMethodInst->GetParamName(paramIdx, toParamName);
if ((!fromParamName.IsEmpty()) && (!toParamName.IsEmpty())) if ((!fromParamName.IsEmpty()) && (!toParamName.IsEmpty()))
nameMatches = fromParamName == toParamName; nameMatches = fromParamName == toParamName;
} }
if ((fromMethodInst->GetParamKind(paramIdx) == toMethodInst->GetParamKind(paramIdx)) && if ((fromMethodInst->GetParamKind(paramIdx) == toMethodInst->GetParamKind(paramIdx)) &&
(fromMethodInst->GetParamType(paramIdx) == toMethodInst->GetParamType(paramIdx)) && (fromMethodInst->GetParamType(paramIdx) == toMethodInst->GetParamType(paramIdx)) &&
(nameMatches)) (nameMatches))
{ {
// Matched, required for implicit/explicit // Matched, required for implicit/explicit
} }
else else
{ {
matched = false; matched = false;
break; break;
}
} }
} }
@ -10939,9 +11026,20 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF
str += "function "; str += "function ";
DoTypeToString(str, delegateInfo->mReturnType, typeNameFlags, genericMethodNameOverrides); DoTypeToString(str, delegateInfo->mReturnType, typeNameFlags, genericMethodNameOverrides);
str += "("; str += "(";
bool isFirstParam = true;
if (delegateInfo->mFunctionThisType != NULL)
{
if ((methodDef->mIsMutating) && (delegateInfo->mFunctionThisType->IsValueType()))
str += "mut ";
DoTypeToString(str, delegateInfo->mFunctionThisType, typeNameFlags, genericMethodNameOverrides);
str += " this";
isFirstParam = false;
}
for (int paramIdx = 0; paramIdx < methodDef->mParams.size(); paramIdx++) for (int paramIdx = 0; paramIdx < methodDef->mParams.size(); paramIdx++)
{ {
if (paramIdx > 0) if (!isFirstParam)
str += ", "; str += ", ";
auto paramDef = methodDef->mParams[paramIdx]; auto paramDef = methodDef->mParams[paramIdx];
BfTypeNameFlags innerFlags = (BfTypeNameFlags)(typeNameFlags & ~(BfTypeNameFlag_OmitNamespace | BfTypeNameFlag_OmitOuterType)); BfTypeNameFlags innerFlags = (BfTypeNameFlags)(typeNameFlags & ~(BfTypeNameFlag_OmitNamespace | BfTypeNameFlag_OmitOuterType));
@ -10953,6 +11051,8 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF
str += " "; str += " ";
str += paramDef->mName; str += paramDef->mName;
} }
isFirstParam = false;
} }
str += ")"; str += ")";

View file

@ -714,6 +714,13 @@ bool BfMethodInstance::HasThis()
return (!mMethodInstanceGroup->mOwner->IsValuelessType()); return (!mMethodInstanceGroup->mOwner->IsValuelessType());
} }
bool BfMethodInstance::HasExplicitThis()
{
if (mMethodDef->mIsStatic)
return false;
return mMethodInstanceGroup->mOwner->IsFunction();
}
int BfMethodInstance::GetParamCount() int BfMethodInstance::GetParamCount()
{ {
return (int)mParams.size(); return (int)mParams.size();
@ -773,9 +780,13 @@ BfType* BfMethodInstance::GetParamType(int paramIdx, bool useResolvedType)
return mMethodInfoEx->mClosureInstanceInfo->mThisOverride; return mMethodInfoEx->mClosureInstanceInfo->mThisOverride;
BF_ASSERT(!mMethodDef->mIsStatic); BF_ASSERT(!mMethodDef->mIsStatic);
auto owner = mMethodInstanceGroup->mOwner; auto owner = mMethodInstanceGroup->mOwner;
if ((owner->IsValueType()) && ((mMethodDef->mIsMutating) || (!AllowsSplatting())) && (!owner->GetLoweredType(BfTypeUsage_Parameter))) auto delegateInfo = owner->GetDelegateInfo();
return owner->mModule->CreatePointerType(owner); BfType* thisType = owner;
return owner; if ((delegateInfo != NULL) && (delegateInfo->mFunctionThisType != NULL))
thisType = delegateInfo->mFunctionThisType;
if ((thisType->IsValueType()) && ((mMethodDef->mIsMutating) || (!AllowsSplatting())) && (!thisType->GetLoweredType(BfTypeUsage_Parameter)))
return owner->mModule->CreatePointerType(thisType);
return thisType;
} }
BfMethodParam* methodParam = &mParams[paramIdx]; BfMethodParam* methodParam = &mParams[paramIdx];
@ -809,6 +820,8 @@ bool BfMethodInstance::GetParamIsSplat(int paramIdx)
BfParamKind BfMethodInstance::GetParamKind(int paramIdx) BfParamKind BfMethodInstance::GetParamKind(int paramIdx)
{ {
if (paramIdx == -1)
return BfParamKind_Normal;
BfMethodParam* methodParam = &mParams[paramIdx]; BfMethodParam* methodParam = &mParams[paramIdx];
if (methodParam->mParamDefIdx == -1) if (methodParam->mParamDefIdx == -1)
return BfParamKind_ImplicitCapture; return BfParamKind_ImplicitCapture;
@ -820,12 +833,16 @@ BfParamKind BfMethodInstance::GetParamKind(int paramIdx)
bool BfMethodInstance::WasGenericParam(int paramIdx) bool BfMethodInstance::WasGenericParam(int paramIdx)
{ {
if (paramIdx == -1)
return false;
BfMethodParam* methodParam = &mParams[paramIdx]; BfMethodParam* methodParam = &mParams[paramIdx];
return methodParam->mWasGenericParam; return methodParam->mWasGenericParam;
} }
bool BfMethodInstance::IsParamSkipped(int paramIdx) bool BfMethodInstance::IsParamSkipped(int paramIdx)
{ {
if (paramIdx == -1)
return false;
BfType* paramType = GetParamType(paramIdx); BfType* paramType = GetParamType(paramIdx);
if ((paramType->CanBeValuelessType()) && (paramType->IsDataIncomplete())) if ((paramType->CanBeValuelessType()) && (paramType->IsDataIncomplete()))
GetModule()->PopulateType(paramType, BfPopulateType_Data); GetModule()->PopulateType(paramType, BfPopulateType_Data);
@ -836,6 +853,8 @@ bool BfMethodInstance::IsParamSkipped(int paramIdx)
bool BfMethodInstance::IsImplicitCapture(int paramIdx) bool BfMethodInstance::IsImplicitCapture(int paramIdx)
{ {
if (paramIdx == -1)
return false;
BfMethodParam* methodParam = &mParams[paramIdx]; BfMethodParam* methodParam = &mParams[paramIdx];
if (methodParam->mParamDefIdx == -1) if (methodParam->mParamDefIdx == -1)
return true; return true;
@ -844,6 +863,8 @@ bool BfMethodInstance::IsImplicitCapture(int paramIdx)
BfExpression* BfMethodInstance::GetParamInitializer(int paramIdx) BfExpression* BfMethodInstance::GetParamInitializer(int paramIdx)
{ {
if (paramIdx == -1)
return NULL;
BfMethodParam* methodParam = &mParams[paramIdx]; BfMethodParam* methodParam = &mParams[paramIdx];
if (methodParam->mParamDefIdx == -1) if (methodParam->mParamDefIdx == -1)
return NULL; return NULL;
@ -855,6 +876,8 @@ BfExpression* BfMethodInstance::GetParamInitializer(int paramIdx)
BfTypeReference* BfMethodInstance::GetParamTypeRef(int paramIdx) BfTypeReference* BfMethodInstance::GetParamTypeRef(int paramIdx)
{ {
if (paramIdx == -1)
return NULL;
BfMethodParam* methodParam = &mParams[paramIdx]; BfMethodParam* methodParam = &mParams[paramIdx];
if (methodParam->mParamDefIdx == -1) if (methodParam->mParamDefIdx == -1)
return NULL; return NULL;
@ -969,13 +992,22 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType,
checkType = module->mCurMethodState->mClosureState->mClosureType; checkType = module->mCurMethodState->mClosureState->mClosureType;
} }
else else
checkType = GetOwner(); {
if (HasExplicitThis())
checkType = GetParamType(-1);
else
checkType = GetOwner();
}
} }
else else
{ {
checkType = GetParamType(paramIdx); checkType = GetParamType(paramIdx);
} }
/*if (GetParamName(paramIdx) == "this")
{
NOP;
}*/
bool checkLowered = false; bool checkLowered = false;
bool doSplat = false; bool doSplat = false;
@ -1098,29 +1130,52 @@ bool BfMethodInstance::IsExactMatch(BfMethodInstance* other, bool ignoreImplicit
int implicitParamCountA = ignoreImplicitParams ? GetImplicitParamCount() : 0; int implicitParamCountA = ignoreImplicitParams ? GetImplicitParamCount() : 0;
int implicitParamCountB = ignoreImplicitParams ? other->GetImplicitParamCount() : 0; int implicitParamCountB = ignoreImplicitParams ? other->GetImplicitParamCount() : 0;
if (HasExplicitThis())
{
}
// if (other->HasExplicitThis())
// {
// if (!HasExplicitThis())
// return false;
// if (GetParamType(-1) != other->GetParamType(-1))
// return false;
// }
if (checkThis) if (checkThis)
{ {
if (other->mMethodDef->mIsStatic != mMethodDef->mIsStatic) if (other->mMethodDef->mIsStatic != mMethodDef->mIsStatic)
return false;
// {
// // If we are static and we have to match a non-static method, allow us to do so if we have an explicitly defined 'this' param that matches
//
// if (other->mMethodDef->mIsStatic)
// return false;
//
// if ((GetParamCount() > 0) && (GetParamName(0) == "this"))
// {
// auto thisType = GetParamType(0);
// auto otherThisType = other->GetParamType(-1);
// if (thisType != otherThisType)
// return false;
//
// implicitParamCountA++;
// }
// else
// {
// // Valueless types don't actually pass a 'this' anyway
// if (!other->GetOwner()->IsValuelessType())
// return false;
// }
// }
if (!mMethodDef->mIsStatic)
{ {
// If we are static and we have to match a non-static method, allow us to do so if we have an explicitly defined 'this' param that matches if (GetParamType(-1) != other->GetParamType(-1))
{
if (other->mMethodDef->mIsStatic)
return false; return false;
if ((GetParamCount() > 0) && (GetParamName(0) == "this"))
{
auto thisType = GetParamType(0);
auto otherThisType = other->GetParamType(-1);
if (thisType != otherThisType)
return false;
implicitParamCountA++;
}
else
{
// Valueless types don't actually pass a 'this' anyway
if (!other->GetOwner()->IsValuelessType())
return false;
} }
} }
} }
@ -2413,6 +2468,14 @@ int BfResolvedTypeSet::Hash(BfType* type, LookupContext* ctx, bool allowRef)
BF_ASSERT(methodDef->mName == "Invoke"); BF_ASSERT(methodDef->mName == "Invoke");
BF_ASSERT(delegateInfo->mParams.size() == methodDef->mParams.size()); BF_ASSERT(delegateInfo->mParams.size() == methodDef->mParams.size());
if (delegateInfo->mFunctionThisType != NULL)
{
hashVal = ((hashVal ^ (Hash(delegateInfo->mFunctionThisType, ctx))) << 5) - hashVal;
String paramName = "this";
int nameHash = (int)Hash64(paramName.c_str(), (int)paramName.length());
hashVal = ((hashVal ^ (nameHash)) << 5) - hashVal;
}
for (int paramIdx = 0; paramIdx < delegateInfo->mParams.size(); paramIdx++) for (int paramIdx = 0; paramIdx < delegateInfo->mParams.size(); paramIdx++)
{ {
// Parse attributes? // Parse attributes?
@ -2989,12 +3052,26 @@ int BfResolvedTypeSet::Hash(BfTypeReference* typeRef, LookupContext* ctx, BfHash
else else
ctx->mFailed = true; ctx->mFailed = true;
bool isFirstParam = true;
for (auto param : delegateTypeRef->mParams) for (auto param : delegateTypeRef->mParams)
{ {
// Parse attributes? // Parse attributes?
BfTypeReference* fieldType = param->mTypeRef; BfTypeReference* fieldType = param->mTypeRef;
if (isFirstParam)
{
if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
{
if (auto refNode = BfNodeDynCast<BfRefTypeRef>(fieldType))
fieldType = refNode->mElementType;
}
}
hashVal = ((hashVal ^ (Hash(fieldType, ctx, BfHashFlag_AllowRef))) << 5) - hashVal; hashVal = ((hashVal ^ (Hash(fieldType, ctx, BfHashFlag_AllowRef))) << 5) - hashVal;
hashVal = ((hashVal ^ (HashNode(param->mNameNode))) << 5) - hashVal; hashVal = ((hashVal ^ (HashNode(param->mNameNode))) << 5) - hashVal;
isFirstParam = true;
} }
return hashVal; return hashVal;
@ -3498,20 +3575,57 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext*
BfMethodInstance* lhsInvokeMethodInstance = ctx->mModule->GetRawMethodInstanceAtIdx(lhs->ToTypeInstance(), 0, "Invoke"); BfMethodInstance* lhsInvokeMethodInstance = ctx->mModule->GetRawMethodInstanceAtIdx(lhs->ToTypeInstance(), 0, "Invoke");
if ((lhs->IsDelegate()) != (rhsDelegateType->mTypeToken->GetToken() == BfToken_Delegate)) bool rhsIsDelegate = rhsDelegateType->mTypeToken->GetToken() == BfToken_Delegate;
if ((lhs->IsDelegate()) != rhsIsDelegate)
return false; return false;
if (!Equals(lhsInvokeMethodInstance->mReturnType, rhsDelegateType->mReturnType, ctx)) if (!Equals(lhsInvokeMethodInstance->mReturnType, rhsDelegateType->mReturnType, ctx))
return false; return false;
if (lhsInvokeMethodInstance->GetParamCount() != (int)rhsDelegateType->mParams.size())
bool isMutating = true;
int paramRefOfs = 0;
if (!rhsDelegateType->mParams.IsEmpty())
{
auto param0 = rhsDelegateType->mParams[0];
if ((param0->mNameNode != NULL) && (param0->mNameNode->Equals("this")))
{
bool handled = false;
auto lhsThisType = lhsInvokeMethodInstance->GetParamType(-1);
auto rhsThisType = ctx->mModule->ResolveTypeRef(param0->mTypeRef, BfPopulateType_Identity, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoWarnOnMut | BfResolveTypeRefFlag_AllowRef));
if (rhsThisType->IsRef())
{
if (!lhsThisType->IsPointer())
return false;
if (lhsThisType->GetUnderlyingType() != rhsThisType->GetUnderlyingType())
return false;
}
else
{
if (lhsThisType != rhsThisType)
return false;
}
paramRefOfs = 1;
}
}
if (!rhsIsDelegate)
{
if (lhsInvokeMethodInstance->mMethodDef->mIsStatic != (paramRefOfs == 0))
return false;
}
if (lhsInvokeMethodInstance->GetParamCount() != (int)rhsDelegateType->mParams.size() - paramRefOfs)
return false; return false;
for (int paramIdx = 0; paramIdx < lhsInvokeMethodInstance->GetParamCount(); paramIdx++) for (int paramIdx = 0; paramIdx < lhsInvokeMethodInstance->GetParamCount(); paramIdx++)
{ {
if (!Equals(lhsInvokeMethodInstance->GetParamType(paramIdx), rhsDelegateType->mParams[paramIdx]->mTypeRef, ctx)) if (!Equals(lhsInvokeMethodInstance->GetParamType(paramIdx), rhsDelegateType->mParams[paramIdx + paramRefOfs]->mTypeRef, ctx))
return false; return false;
StringView rhsParamName; StringView rhsParamName;
if (rhsDelegateType->mParams[paramIdx]->mNameNode != NULL) if (rhsDelegateType->mParams[paramIdx + paramRefOfs]->mNameNode != NULL)
rhsParamName = rhsDelegateType->mParams[paramIdx]->mNameNode->ToStringView(); rhsParamName = rhsDelegateType->mParams[paramIdx + paramRefOfs]->mNameNode->ToStringView();
if (lhsInvokeMethodInstance->GetParamName(paramIdx) != rhsParamName) if (lhsInvokeMethodInstance->GetParamName(paramIdx) != rhsParamName)
return false; return false;
} }

View file

@ -30,7 +30,8 @@ enum BfResolveTypeRefFlags
BfResolveTypeRefFlag_FromIndirectSource = 0x80, // Such as a type alias or a generic parameter BfResolveTypeRefFlag_FromIndirectSource = 0x80, // Such as a type alias or a generic parameter
BfResolveTypeRefFlag_Attribute = 0x100, BfResolveTypeRefFlag_Attribute = 0x100,
BfResolveTypeRefFlag_NoReify = 0x200, BfResolveTypeRefFlag_NoReify = 0x200,
BfResolveTypeRefFlag_NoCreate = 0x400 BfResolveTypeRefFlag_NoCreate = 0x400,
BfResolveTypeRefFlag_NoWarnOnMut = 0x800
}; };
enum BfTypeNameFlags : uint16 enum BfTypeNameFlags : uint16
@ -413,12 +414,14 @@ class BfDelegateInfo
public: public:
Array<BfAstNode*> mDirectAllocNodes; Array<BfAstNode*> mDirectAllocNodes;
BfType* mReturnType; BfType* mReturnType;
BfType* mFunctionThisType;
Array<BfType*> mParams; Array<BfType*> mParams;
public: public:
BfDelegateInfo() BfDelegateInfo()
{ {
mReturnType = NULL; mReturnType = NULL;
mFunctionThisType = NULL;
} }
~BfDelegateInfo() ~BfDelegateInfo()
@ -860,6 +863,7 @@ public:
bool IsSpecializedGenericMethodOrType(); bool IsSpecializedGenericMethodOrType();
bool IsSpecializedByAutoCompleteMethod(); bool IsSpecializedByAutoCompleteMethod();
bool HasThis(); bool HasThis();
bool HasExplicitThis();
bool HasParamsArray(); bool HasParamsArray();
int GetStructRetIdx(); int GetStructRetIdx();
bool HasSelf(); bool HasSelf();

View file

@ -0,0 +1,59 @@
#pragma warning disable 168
using System;
namespace Tests
{
class Functions
{
class ClassA
{
int mA = 123;
public int GetA(float f)
{
return mA + (int)f;
}
public int GetT<T>(T val) where T : var
{
return mA + (int)val;
}
}
struct StructA
{
int mA = 123;
int mB = 234;
public int GetA(float f)
{
return mA + mB*100 + (int)f;
}
public int GetA2(float f) mut
{
return mA + mB*100 + (int)f;
}
}
[Test]
public static void TestBasics()
{
ClassA ca = scope .();
StructA sa = .();
function int (ClassA this, float f) func0 = => ca.GetA;
function int (ClassA this, float) func0b = func0;
Test.Assert(func0(ca, 100.0f) == 223);
func0 = => ca.GetT<float>;
Test.Assert(func0(ca, 100.0f) == 223);
function int (StructA this, float f) func1 = => sa.GetA;
Test.Assert(func1(sa, 100.0f) == 23623);
function int (mut StructA this, float f) func2 = => sa.GetA2;
Test.Assert(func2(mut sa, 100.0f) == 23623);
}
}
}