diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 3839de56..0ecf8eda 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -7307,7 +7307,7 @@ void BfCompiler::GenerateAutocompleteInfo() int dispParamIdx = 0; 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); if ((paramKind == BfParamKind_ImplicitCapture) || (paramKind == BfParamKind_AppendIdx)) diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index cd5ada5f..6f4213f5 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -5359,8 +5359,16 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu 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) { FinishDeferredEvals(argValues); @@ -5407,15 +5415,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu else 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) { // Handled in args @@ -5462,6 +5462,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu if ((boxScopeData == NULL) && (mModule->mCurMethodState != NULL)) boxScopeData = mModule->mCurMethodState->mCurScope; + if (methodInstance->HasExplicitThis()) + paramIdx = -1; + bool failed = false; while (true) { @@ -5500,7 +5503,11 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu if (errorRef == NULL) 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)) mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("See method declaration"), methodInstance->mMethodDef->GetRefNode()); failed = true; @@ -5833,6 +5840,12 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu 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; if (refNode == NULL) refNode = targetSrc; @@ -5903,7 +5916,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu if (argValue) { - if (wantsSplat) + if (paramIdx == -1) + PushThis(targetSrc, argValue, methodInstance, irArgs); + else if (wantsSplat) SplatArgs(argValue, irArgs); else PushArg(argValue, irArgs, true, false); @@ -9719,6 +9734,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) BfFunctionBindResult bindResult; bindResult.mSkipMutCheck = true; // Allow operating on copies + bindResult.mBindType = delegateTypeInstance; // { SetAndRestoreValue prevExpectingType(mExpectingType, methodInstance->mReturnType); @@ -12322,7 +12338,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr) else autoComplete->mIsCapturingMethodMatchInfo = false; } - else + else if (!resolvedTypeRef->IsFunction()) { MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInstance, argValues, false, true); } diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 44045118..00fab3b2 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -238,6 +238,7 @@ public: BfTypedValue mTarget; BfIRValue mFunc; BfMethodInstance* mMethodInstance; + BfType* mBindType; bool mSkipThis; bool mSkipMutCheck; bool mWantsArgs; @@ -248,6 +249,7 @@ public: BfFunctionBindResult() { mMethodInstance = NULL; + mBindType = NULL; mSkipMutCheck = false; mWantsArgs = false; mSkipThis = false; diff --git a/IDEHelper/Compiler/BfMangler.cpp b/IDEHelper/Compiler/BfMangler.cpp index cee67307..50ac6740 100644 --- a/IDEHelper/Compiler/BfMangler.cpp +++ b/IDEHelper/Compiler/BfMangler.cpp @@ -309,7 +309,7 @@ void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name name += "N8functionI"; SizedArray typeVec; 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 += invokeMethodInst->GetParamName(paramIdx); @@ -1156,44 +1156,45 @@ bool BfMSMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl& { 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 typeVec; - typeVec.push_back(BfNodeDynCast(methodDef->mReturnTypeRef)->mType); - for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++) - { - name += "_"; - name += methodDef->mParams[paramIdx]->mName; - typeVec.push_back(BfNodeDynCast(methodDef->mParams[paramIdx]->mTypeRef)->mType); - } - name += '@'; - if (!typeVec.empty()) - AddGenericArgs(mangleContext, name, typeVec); - name += '@'; - } +// { +// auto methodDef = newNameSub.mTypeInst->mTypeDef->mMethods[0]; +// if (newNameSub.mTypeInst->IsDelegate()) +// name += "?$delegate"; +// else +// name += "?$function"; +// SizedArray typeVec; +// typeVec.push_back(BfNodeDynCast(methodDef->mReturnTypeRef)->mType); +// for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++) +// { +// name += "_"; +// name += methodDef->mParams[paramIdx]->mName; +// typeVec.push_back(BfNodeDynCast(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 typeVec; -// typeVec.push_back(invokeMethodInst->mReturnType); -// for (int paramIdx = 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 += '@'; + auto invokeMethodInst = mangleContext.mModule->GetDelegateInvokeMethod(newNameSub.mTypeInst); + if (newNameSub.mTypeInst->IsDelegate()) + name += "?$delegate"; + else + name += "?$function"; + SizedArray 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()) { diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 5ab10c86..e75cb61a 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -7774,7 +7774,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula if ((resolvedType->IsValueType()) || (resolvedType->IsGenericParam())) needsRefWrap = true; - if ((InDefinitionSection()) && (!resolvedType->IsGenericParam())) + if ((InDefinitionSection()) && (!resolvedType->IsGenericParam()) && ((resolveFlags & BfResolveTypeRefFlag_NoWarnOnMut) == 0)) { 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); @@ -8364,7 +8364,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula return ResolveTypeResult(typeRef, refType, populateType, resolveFlags); } else if (auto delegateTypeRef = BfNodeDynCast(typeRef)) - { + { bool wantGeneric = false; bool isUnspecialized = false; auto _CheckType = [&](BfType* type) @@ -8375,19 +8375,56 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula isUnspecialized = true; }; + bool failed = false; + auto returnType = ResolveTypeRef(delegateTypeRef->mReturnType); if (returnType == NULL) returnType = GetPrimitiveType(BfTypeCode_Var); _CheckType(returnType); + BfType* functionThisType = NULL; + bool hasMutSpecifier = false; + bool isFirst = true; + bool isDelegate = delegateTypeRef->mTypeToken->GetToken() == BfToken_Delegate; + Array paramTypes; 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) + { + failed = true; 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(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())) @@ -8414,25 +8451,6 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula 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 if (isUnspecialized) { @@ -8468,13 +8486,17 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula typeDef->mIsFunction = true; typeDef->mTypeCode = BfTypeCode_Struct; } - + BfMethodDef* methodDef = new BfMethodDef(); methodDef->mDeclaringType = typeDef; methodDef->mName = "Invoke"; methodDef->mProtection = BfProtection_Public; 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(); delegateInfo->mDirectAllocNodes.push_back(directTypeRef); @@ -8489,12 +8511,14 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula directTypeRef->Init(returnType); methodDef->mReturnTypeRef = directTypeRef; delegateInfo->mReturnType = returnType; + delegateInfo->mFunctionThisType = functionThisType; auto hashVal = mContext->mResolvedTypes.Hash(typeRef, &lookupCtx); + int paramSrcOfs = (functionThisType != NULL) ? 1 : 0; for (int paramIdx = 0; paramIdx < (int)paramTypes.size(); paramIdx++) { - auto param = delegateTypeRef->mParams[paramIdx]; + auto param = delegateTypeRef->mParams[paramIdx + paramSrcOfs]; auto paramType = paramTypes[paramIdx]; if (paramType == NULL) paramType = GetPrimitiveType(BfTypeCode_Var); @@ -8504,8 +8528,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula paramName = param->mNameNode->ToString(); if (!paramType->IsReified()) - delegateType->mIsReified = false; - + delegateType->mIsReified = false; auto directTypeRef = BfAstNode::ZeroedAlloc(); delegateInfo->mDirectAllocNodes.push_back(directTypeRef); @@ -8522,6 +8545,13 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula 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, ""); @@ -8531,20 +8561,21 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula delegateType->mContext = mContext; delegateType->mTypeDef = typeDef; populateModule->InitType(delegateType, populateType); - resolvedEntry->mValue = delegateType; + resolvedEntry->mValue = delegateType; AddDependency(directTypeRef->mType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue); for (auto paramType : paramTypes) AddDependency(paramType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue); -// #ifdef _DEBUG -// if (BfResolvedTypeSet::Hash(delegateType, &lookupCtx) != resolvedEntry->mHash) -// { -// int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx); -// int typeHash = BfResolvedTypeSet::Hash(delegateType, &lookupCtx); -// BF_ASSERT(refHash == typeHash); -// } -// #endif +#ifdef _DEBUG + if (BfResolvedTypeSet::Hash(delegateType, &lookupCtx) != resolvedEntry->mHash) + { + int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx); + int typeHash = BfResolvedTypeSet::Hash(delegateType, &lookupCtx); + BF_ASSERT(refHash == typeHash); + } + BF_ASSERT(BfResolvedTypeSet::Equals(delegateType, typeRef, &lookupCtx)); +#endif 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)) { - 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(); @@ -10386,34 +10466,41 @@ BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, Bf (fromMethodInst->mReturnType == toMethodInst->mReturnType) && (fromMethodInst->GetParamCount() == toMethodInst->GetParamCount())) { - bool matched = true; + bool matched = true; StringT<64> fromParamName; StringT<64> toParamName; - for (int paramIdx = 0; paramIdx < (int)fromMethodInst->GetParamCount(); paramIdx++) + if (fromMethodInst->HasExplicitThis() != toMethodInst->HasExplicitThis()) { - bool nameMatches = true; - - if (!explicitCast) + matched = false; + } + else + { + for (int paramIdx = fromMethodInst->HasExplicitThis() ? -1 : 0; paramIdx < (int)fromMethodInst->GetParamCount(); paramIdx++) { - fromMethodInst->GetParamName(paramIdx, fromParamName); - toMethodInst->GetParamName(paramIdx, toParamName); - if ((!fromParamName.IsEmpty()) && (!toParamName.IsEmpty())) - nameMatches = fromParamName == toParamName; + bool nameMatches = true; + + if (!explicitCast) + { + fromMethodInst->GetParamName(paramIdx, fromParamName); + toMethodInst->GetParamName(paramIdx, toParamName); + if ((!fromParamName.IsEmpty()) && (!toParamName.IsEmpty())) + nameMatches = fromParamName == toParamName; + } + + if ((fromMethodInst->GetParamKind(paramIdx) == toMethodInst->GetParamKind(paramIdx)) && + (fromMethodInst->GetParamType(paramIdx) == toMethodInst->GetParamType(paramIdx)) && + (nameMatches)) + { + // Matched, required for implicit/explicit + } + else + { + matched = false; + break; + } } - - if ((fromMethodInst->GetParamKind(paramIdx) == toMethodInst->GetParamKind(paramIdx)) && - (fromMethodInst->GetParamType(paramIdx) == toMethodInst->GetParamType(paramIdx)) && - (nameMatches)) - { - // Matched, required for implicit/explicit - } - else - { - matched = false; - break; - } } if (matched) @@ -10939,9 +11026,20 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF str += "function "; DoTypeToString(str, delegateInfo->mReturnType, typeNameFlags, genericMethodNameOverrides); 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++) { - if (paramIdx > 0) + if (!isFirstParam) str += ", "; auto paramDef = methodDef->mParams[paramIdx]; BfTypeNameFlags innerFlags = (BfTypeNameFlags)(typeNameFlags & ~(BfTypeNameFlag_OmitNamespace | BfTypeNameFlag_OmitOuterType)); @@ -10953,6 +11051,8 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF str += " "; str += paramDef->mName; } + + isFirstParam = false; } str += ")"; diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index ca7be36e..cecd6c6e 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -714,6 +714,13 @@ bool BfMethodInstance::HasThis() return (!mMethodInstanceGroup->mOwner->IsValuelessType()); } +bool BfMethodInstance::HasExplicitThis() +{ + if (mMethodDef->mIsStatic) + return false; + return mMethodInstanceGroup->mOwner->IsFunction(); +} + int BfMethodInstance::GetParamCount() { return (int)mParams.size(); @@ -773,9 +780,13 @@ BfType* BfMethodInstance::GetParamType(int paramIdx, bool useResolvedType) return mMethodInfoEx->mClosureInstanceInfo->mThisOverride; BF_ASSERT(!mMethodDef->mIsStatic); auto owner = mMethodInstanceGroup->mOwner; - if ((owner->IsValueType()) && ((mMethodDef->mIsMutating) || (!AllowsSplatting())) && (!owner->GetLoweredType(BfTypeUsage_Parameter))) - return owner->mModule->CreatePointerType(owner); - return owner; + auto delegateInfo = owner->GetDelegateInfo(); + BfType* thisType = 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]; @@ -809,6 +820,8 @@ bool BfMethodInstance::GetParamIsSplat(int paramIdx) BfParamKind BfMethodInstance::GetParamKind(int paramIdx) { + if (paramIdx == -1) + return BfParamKind_Normal; BfMethodParam* methodParam = &mParams[paramIdx]; if (methodParam->mParamDefIdx == -1) return BfParamKind_ImplicitCapture; @@ -820,12 +833,16 @@ BfParamKind BfMethodInstance::GetParamKind(int paramIdx) bool BfMethodInstance::WasGenericParam(int paramIdx) { + if (paramIdx == -1) + return false; BfMethodParam* methodParam = &mParams[paramIdx]; return methodParam->mWasGenericParam; } bool BfMethodInstance::IsParamSkipped(int paramIdx) { + if (paramIdx == -1) + return false; BfType* paramType = GetParamType(paramIdx); if ((paramType->CanBeValuelessType()) && (paramType->IsDataIncomplete())) GetModule()->PopulateType(paramType, BfPopulateType_Data); @@ -836,6 +853,8 @@ bool BfMethodInstance::IsParamSkipped(int paramIdx) bool BfMethodInstance::IsImplicitCapture(int paramIdx) { + if (paramIdx == -1) + return false; BfMethodParam* methodParam = &mParams[paramIdx]; if (methodParam->mParamDefIdx == -1) return true; @@ -844,6 +863,8 @@ bool BfMethodInstance::IsImplicitCapture(int paramIdx) BfExpression* BfMethodInstance::GetParamInitializer(int paramIdx) { + if (paramIdx == -1) + return NULL; BfMethodParam* methodParam = &mParams[paramIdx]; if (methodParam->mParamDefIdx == -1) return NULL; @@ -855,6 +876,8 @@ BfExpression* BfMethodInstance::GetParamInitializer(int paramIdx) BfTypeReference* BfMethodInstance::GetParamTypeRef(int paramIdx) { + if (paramIdx == -1) + return NULL; BfMethodParam* methodParam = &mParams[paramIdx]; if (methodParam->mParamDefIdx == -1) return NULL; @@ -969,13 +992,22 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, checkType = module->mCurMethodState->mClosureState->mClosureType; } else - checkType = GetOwner(); + { + if (HasExplicitThis()) + checkType = GetParamType(-1); + else + checkType = GetOwner(); + } } else { checkType = GetParamType(paramIdx); } + /*if (GetParamName(paramIdx) == "this") + { + NOP; + }*/ bool checkLowered = false; bool doSplat = false; @@ -1098,29 +1130,52 @@ bool BfMethodInstance::IsExactMatch(BfMethodInstance* other, bool ignoreImplicit int implicitParamCountA = ignoreImplicitParams ? 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 (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 (other->mMethodDef->mIsStatic) + if (GetParamType(-1) != other->GetParamType(-1)) + { 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(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++) { // Parse attributes? @@ -2989,12 +3052,26 @@ int BfResolvedTypeSet::Hash(BfTypeReference* typeRef, LookupContext* ctx, BfHash else ctx->mFailed = true; + + bool isFirstParam = true; + for (auto param : delegateTypeRef->mParams) { // Parse attributes? BfTypeReference* fieldType = param->mTypeRef; + + if (isFirstParam) + { + if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this"))) + { + if (auto refNode = BfNodeDynCast(fieldType)) + fieldType = refNode->mElementType; + } + } + hashVal = ((hashVal ^ (Hash(fieldType, ctx, BfHashFlag_AllowRef))) << 5) - hashVal; hashVal = ((hashVal ^ (HashNode(param->mNameNode))) << 5) - hashVal; + isFirstParam = true; } return hashVal; @@ -3498,20 +3575,57 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* 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; if (!Equals(lhsInvokeMethodInstance->mReturnType, rhsDelegateType->mReturnType, ctx)) 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; 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; StringView rhsParamName; - if (rhsDelegateType->mParams[paramIdx]->mNameNode != NULL) - rhsParamName = rhsDelegateType->mParams[paramIdx]->mNameNode->ToStringView(); - + if (rhsDelegateType->mParams[paramIdx + paramRefOfs]->mNameNode != NULL) + rhsParamName = rhsDelegateType->mParams[paramIdx + paramRefOfs]->mNameNode->ToStringView(); if (lhsInvokeMethodInstance->GetParamName(paramIdx) != rhsParamName) return false; } diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 852a0c55..4e10f269 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -30,7 +30,8 @@ enum BfResolveTypeRefFlags BfResolveTypeRefFlag_FromIndirectSource = 0x80, // Such as a type alias or a generic parameter BfResolveTypeRefFlag_Attribute = 0x100, BfResolveTypeRefFlag_NoReify = 0x200, - BfResolveTypeRefFlag_NoCreate = 0x400 + BfResolveTypeRefFlag_NoCreate = 0x400, + BfResolveTypeRefFlag_NoWarnOnMut = 0x800 }; enum BfTypeNameFlags : uint16 @@ -413,12 +414,14 @@ class BfDelegateInfo public: Array mDirectAllocNodes; BfType* mReturnType; - Array mParams; + BfType* mFunctionThisType; + Array mParams; public: BfDelegateInfo() { mReturnType = NULL; + mFunctionThisType = NULL; } ~BfDelegateInfo() @@ -860,6 +863,7 @@ public: bool IsSpecializedGenericMethodOrType(); bool IsSpecializedByAutoCompleteMethod(); bool HasThis(); + bool HasExplicitThis(); bool HasParamsArray(); int GetStructRetIdx(); bool HasSelf(); diff --git a/IDEHelper/Tests/src/Functions.bf b/IDEHelper/Tests/src/Functions.bf new file mode 100644 index 00000000..5ec24c15 --- /dev/null +++ b/IDEHelper/Tests/src/Functions.bf @@ -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 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; + 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); + } + } +}