From e1c340a7118f31b3097307818c1d169ed4d22da6 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Wed, 3 Jun 2020 05:22:11 -0700 Subject: [PATCH] Added extension methods --- IDEHelper/Compiler/BfAst.h | 41 +-- IDEHelper/Compiler/BfAutoComplete.cpp | 190 ++++++++---- IDEHelper/Compiler/BfAutoComplete.h | 1 + IDEHelper/Compiler/BfCompiler.cpp | 9 +- IDEHelper/Compiler/BfDefBuilder.cpp | 15 +- IDEHelper/Compiler/BfElementVisitor.cpp | 1 + IDEHelper/Compiler/BfExprEvaluator.cpp | 376 ++++++++++++++++-------- IDEHelper/Compiler/BfExprEvaluator.h | 29 +- IDEHelper/Compiler/BfPrinter.cpp | 1 + IDEHelper/Compiler/BfSystem.cpp | 3 + IDEHelper/Compiler/BfSystem.h | 7 +- IDEHelper/Tests/src/ExtensionMethods.bf | 59 ++++ 12 files changed, 507 insertions(+), 225 deletions(-) create mode 100644 IDEHelper/Tests/src/ExtensionMethods.bf diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 59ece5bc..d50763de 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -2810,26 +2810,27 @@ public: BF_AST_TYPE(BfMethodDeclaration, BfMemberDeclaration); BfCommentNode* mDocumentation; - ASTREF(BfAttributeDirective*) mReturnAttributes; - ASTREF(BfTokenNode*) mExternSpecifier; - ASTREF(BfTokenNode*) mVirtualSpecifier; // either 'virtual', 'override', or 'abstract' - ASTREF(BfTokenNode*) mNewSpecifier; - ASTREF(BfTokenNode*) mMixinSpecifier; - ASTREF(BfTokenNode*) mPartialSpecifier; - ASTREF(BfTokenNode*) mMutSpecifier; - ASTREF(BfTypeReference*) mReturnType; - ASTREF(BfTypeReference*) mExplicitInterface; - ASTREF(BfTokenNode*) mExplicitInterfaceDotToken; - ASTREF(BfIdentifierNode*) mNameNode; - ASTREF(BfTokenNode*) mOpenParen; - BfSizedArray mParams; - BfSizedArray mCommas; - ASTREF(BfTokenNode*) mCloseParen; - ASTREF(BfGenericParamsDeclaration*) mGenericParams; - ASTREF(BfGenericConstraintsDeclaration*) mGenericConstraintsDeclaration; - ASTREF(BfAstNode*) mEndSemicolon; - ASTREF(BfTokenNode*) mFatArrowToken; - ASTREF(BfAstNode*) mBody; // Either expression or block + BfAttributeDirective* mReturnAttributes; + BfTokenNode* mExternSpecifier; + BfTokenNode* mVirtualSpecifier; // either 'virtual', 'override', or 'abstract' + BfTokenNode* mNewSpecifier; + BfTokenNode* mMixinSpecifier; + BfTokenNode* mPartialSpecifier; + BfTokenNode* mMutSpecifier; + BfTypeReference* mReturnType; + BfTypeReference* mExplicitInterface; + BfTokenNode* mExplicitInterfaceDotToken; + BfIdentifierNode* mNameNode; + BfTokenNode* mOpenParen; + BfTokenNode* mThisToken; + BfSizedArray mParams; + BfSizedArray mCommas; + BfTokenNode* mCloseParen; + BfGenericParamsDeclaration* mGenericParams; + BfGenericConstraintsDeclaration* mGenericConstraintsDeclaration; + BfAstNode* mEndSemicolon; + BfTokenNode* mFatArrowToken; + BfAstNode* mBody; // Either expression or block //BfMethodDef* mMethodDef; diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 144a0864..c4304ee3 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -910,16 +910,104 @@ void BfAutoComplete::AddEnumTypeMembers(BfTypeInstance* typeInst, const StringIm } } -// bool BfAutoComplete::IsInExpression(BfAstNode* node) -// { -// if (mModule->mCurMethodInstance != NULL) -// return true; -// if (node == NULL) -// return false; -// if ((node->IsA()) && (!node->IsA()) && (!node->IsA())) -// return true; -// return IsInExpression(node->mParent); -// } +void BfAutoComplete::AddExtensionMethods(BfTypeInstance* targetType, BfTypeInstance* extensionContainer, const StringImpl & filter, bool allowProtected, bool allowPrivate) +{ + if (!extensionContainer->mTypeDef->mHasExtensionMethods) + return; + + mModule->PopulateType(extensionContainer, BfPopulateType_Data); + + for (auto methodDef : extensionContainer->mTypeDef->mMethods) + { + if (methodDef->mMethodType != BfMethodType_Extension) + continue; + if (methodDef->mIsNoShow) + continue; + if (methodDef->mName.IsEmpty()) + continue; + + bool canUseMethod = true; + canUseMethod &= CheckProtection(methodDef->mProtection, allowProtected, allowPrivate); + + auto methodInstance = mModule->GetRawMethodInstanceAtIdx(extensionContainer, methodDef->mIdx); + if (methodInstance == NULL) + continue; + + // Do filter match first- may be cheaper than generic validation + if (!DoesFilterMatch(methodDef->mName.c_str(), filter.c_str())) + continue; + + auto thisType = methodInstance->GetParamType(0); + bool paramValidated = false; + if (methodInstance->GetNumGenericParams() > 0) + { + if ((thisType->IsGenericParam()) && (methodInstance->GetNumGenericParams() == 1)) + { + auto genericParamType = (BfGenericParamType*)thisType; + if (genericParamType->mGenericParamKind == BfGenericParamKind_Method) + { + auto& genericParams = methodInstance->mMethodInfoEx->mGenericParams; + if (!mModule->CheckGenericConstraints(BfGenericParamSource(methodInstance), targetType, NULL, genericParams[genericParamType->mGenericParamIdx], NULL, NULL)) + continue; + paramValidated = true; + } + } + + if (((thisType->IsUnspecializedTypeVariation()) || (thisType->IsGenericParam())) && + (!paramValidated)) + { + BfTypeVector genericTypeVector; + genericTypeVector.resize(methodInstance->GetNumGenericParams()); + BfGenericInferContext genericInferContext; + genericInferContext.mCheckMethodGenericArguments = &genericTypeVector; + genericInferContext.mModule = mModule; + genericInferContext.mPrevArgValues.resize(methodInstance->GetNumGenericParams()); + + if (!genericInferContext.InferGenericArgument(methodInstance, targetType, thisType, BfIRValue())) + continue; + + thisType = mModule->ResolveGenericType(thisType, NULL, &genericTypeVector, false); + if (thisType == NULL) + continue; + + auto& genericParams = methodInstance->mMethodInfoEx->mGenericParams; + bool validateError = false; + for (int genericIdx = 0; genericIdx < (int)genericTypeVector.size(); genericIdx++) + { + auto genericArg = genericTypeVector[genericIdx]; + if (genericArg == NULL) + continue; + if (!mModule->CheckGenericConstraints(BfGenericParamSource(methodInstance), genericArg, NULL, genericParams[genericIdx], &genericTypeVector, NULL)) + { + validateError = true; + break; + } + } + + if (validateError) + continue; + } + } + + if (!paramValidated) + { + if (!mModule->CanCast(BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), targetType), thisType)) + continue; + } + + if (canUseMethod) + { + if (auto methodDeclaration = methodDef->GetMethodDeclaration()) + { + String replaceName; + AutoCompleteEntry entry("method", methodDef->mName, methodDeclaration->mDocumentation); + if ((AddEntry(entry)) && (mIsGetDefinition)) + { + } + } + } + } +} void BfAutoComplete::AddTopLevelNamespaces(BfAstNode* identifierNode) { @@ -988,35 +1076,6 @@ void BfAutoComplete::AddTopLevelTypes(BfAstNode* identifierNode, bool onlyAttrib } AddCurrentTypes(mModule->mCurTypeInstance, filter, true, true, onlyAttribute); - - // Do inners -// bool allowInnerPrivate = false; -// auto checkTypeInst = mModule->mCurTypeInstance; -// while (checkTypeInst != NULL) -// { -// auto checkTypeDef = checkTypeInst->mTypeDef; -// for (auto nestedTypeDef : checkTypeDef->mNestedTypes) -// { -// if (CheckProtection(nestedTypeDef->mProtection, true, allowInnerPrivate)) -// AddTypeDef(nestedTypeDef, filter, onlyAttribute); -// } -// -// checkTypeInst = mModule->GetOuterType(mModule->mCurTypeInstance); -// if (checkTypeInst != NULL) -// AddInnerTypes(checkTypeInst, filter, true, true); -// -// AddOuterTypes(checkTypeInst, filter, true, true); -// -// checkTypeInst = mModule->GetBaseType(checkTypeInst); -// allowInnerPrivate = false; -// } - -// allowInnerPrivate = true; -// checkTypeInst = mModule->GetOuterType(mModule->mCurTypeInstance); -// if (checkTypeInst != NULL) -// AddInnerTypes(checkTypeInst, filter, true, true); -// -// AddOuterTypes(mModule->mCurTypeInstance, filter, true, true); } if (mModule->mCurMethodInstance != NULL) @@ -1090,29 +1149,6 @@ void BfAutoComplete::AddTopLevelTypes(BfAstNode* identifierNode, bool onlyAttrib } } } - -// BfStaticSearch* staticSearch; -// if (mModule->mCurTypeInstance->mStaticSearchMap.TryGetValue(activeTypeDef, &staticSearch)) -// { -// for (auto typeInst : staticSearch->mStaticTypes) -// { -// AddTypeDef(typeInst->mTypeDef, filter, onlyAttribute); -// } -// } -// else if (!activeTypeDef->mStaticSearch.IsEmpty()) -// { -// BF_ASSERT(mModule->mCompiler->IsAutocomplete()); -// for (auto typeRef : activeTypeDef->mStaticSearch) -// { -// auto type = mModule->ResolveTypeRef(typeRef, NULL, BfPopulateType_Declaration); -// if (type != NULL) -// { -// auto typeInst = type->ToTypeInstance(); -// if (typeInst != NULL) -// AddTypeDef(typeInst->mTypeDef, filter, onlyAttribute); -// } -// } -// } } else { @@ -1567,8 +1603,38 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken AddInnerTypes(typeInst, filter, allowProtected, allowPrivate); if (!onlyShowTypes) + { AddTypeMembers(typeInst, isStatic, !isStatic, filter, typeInst, false, false); + if (!isStatic) + { + auto checkTypeInst = mModule->mCurTypeInstance; + while (checkTypeInst != NULL) + { + AddExtensionMethods(typeInst, checkTypeInst, filter, allowProtected, allowPrivate); + checkTypeInst = mModule->GetOuterType(checkTypeInst); + } + + if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mTypeInstance != NULL)) + { + BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mTypeInstance); + + BfGlobalLookup globalLookup; + globalLookup.mKind = BfGlobalLookup::Kind_All; + mModule->PopulateGlobalContainersList(globalLookup); + for (auto& globalContainer : mModule->mContext->mCurTypeState->mGlobalContainers) + AddExtensionMethods(typeInst, globalContainer.mTypeInst, filter, false, false); + } + + BfStaticSearch* staticSearch = mModule->GetStaticSearch(); + if (staticSearch != NULL) + { + for (auto staticTypeInst : staticSearch->mStaticTypes) + AddExtensionMethods(typeInst, staticTypeInst, filter, false, false); + } + } + } + if (typeInst->IsInterface()) { AddTypeMembers(mModule->mContext->mBfObjectType, isStatic, !isStatic, filter, mModule->mContext->mBfObjectType, true, false); diff --git a/IDEHelper/Compiler/BfAutoComplete.h b/IDEHelper/Compiler/BfAutoComplete.h index 1005170e..1333d9c8 100644 --- a/IDEHelper/Compiler/BfAutoComplete.h +++ b/IDEHelper/Compiler/BfAutoComplete.h @@ -210,6 +210,7 @@ public: void AddSelfResultTypeMembers(BfTypeInstance* typeInst, BfTypeInstance* selfType, const StringImpl& filter, bool allowPrivate); bool InitAutocomplete(BfAstNode* dotNode, BfAstNode* nameNode, String& filter); void AddEnumTypeMembers(BfTypeInstance* typeInst, const StringImpl& filter, bool allowProtected, bool allowPrivate); + void AddExtensionMethods(BfTypeInstance* targetType, BfTypeInstance* extensionContainer, const StringImpl& filter, bool allowProtected, bool allowPrivate); void AddTopLevelNamespaces(BfAstNode* identifierNode); void AddTopLevelTypes(BfAstNode* identifierNode, bool onlyAttribute = false); void AddOverrides(const StringImpl& filter); diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 83528b38..d2679ef2 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -3666,6 +3666,10 @@ void BfCompiler::ProcessAutocompleteTempType() return; } + SetAndRestoreValue prevMethodState(module->mCurMethodState, NULL); + SetAndRestoreValue prevTypeInstance(module->mCurTypeInstance, NULL); + SetAndRestoreValue prevMethodInstance(module->mCurMethodInstance, NULL); + // >>> VisitExteriorIdentifiers mResolvePassData->mAutoComplete->SetModule(module); { @@ -3705,8 +3709,6 @@ void BfCompiler::ProcessAutocompleteTempType() BfLogSysM("ProcessAutocompleteTempType - project disabled\n"); return; } - - SetAndRestoreValue prevMethodState(module->mCurMethodState, NULL); BfTypeState typeState; typeState.mCurTypeDef = tempTypeDef; @@ -4096,6 +4098,9 @@ void BfCompiler::ProcessAutocompleteTempType() methodInstance->mMethodDef = methodDef; methodInstance->mMethodInstanceGroup = &methodInstanceGroup; methodInstance->mIsAutocompleteMethod = true; + + methodInstanceGroup.mDefault = methodInstance; + defer(methodInstanceGroup.mDefault = NULL); for (int genericParamIdx = 0; genericParamIdx < (int)methodDef->mGenericParams.size(); genericParamIdx++) { diff --git a/IDEHelper/Compiler/BfDefBuilder.cpp b/IDEHelper/Compiler/BfDefBuilder.cpp index fcb542ce..724454ee 100644 --- a/IDEHelper/Compiler/BfDefBuilder.cpp +++ b/IDEHelper/Compiler/BfDefBuilder.cpp @@ -409,7 +409,10 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio methodDef->mIsNew = methodDeclaration->mNewSpecifier != NULL; methodDef->mIsMutating = methodDeclaration->mMutSpecifier != NULL; methodDef->mIsExtern = methodDeclaration->mExternSpecifier != NULL; - methodDef->mBody = methodDeclaration->mBody; + methodDef->mBody = methodDeclaration->mBody; + + if (methodDeclaration->mThisToken != NULL) + methodDef->mMethodType = BfMethodType_Extension; HashContext signatureHashCtx; HashNode(signatureHashCtx, methodDeclaration, methodDef->mBody); @@ -553,15 +556,19 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio { if (methodDeclaration->mNameNode != NULL) methodDef->mName = methodDeclaration->mNameNode->ToString(); - methodDef->mMethodType = BfMethodType_Mixin; - /*if (!methodDef->mIsStatic) - Fail("Mixin must be declared static", methodDeclaration->mMixinSpecifier);*/ + methodDef->mMethodType = BfMethodType_Mixin; } else { if (methodDeclaration->mNameNode != NULL) methodDef->mName = methodDeclaration->mNameNode->ToString(); methodDef->mMethodType = BfMethodType_Normal; + + if (methodDeclaration->mThisToken != NULL) + { + methodDef->mMethodType = BfMethodType_Extension; + mCurTypeDef->mHasExtensionMethods = true; + } } if (outerMethodDef != NULL) diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index af97f643..a49da055 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -981,6 +981,7 @@ void BfElementVisitor::Visit(BfMethodDeclaration* methodDeclaration) VisitChild(methodDeclaration->mExplicitInterfaceDotToken); VisitChild(methodDeclaration->mNameNode); VisitChild(methodDeclaration->mOpenParen); + VisitChild(methodDeclaration->mThisToken); for (auto& param : methodDeclaration->mParams) VisitChild(param); for (auto& comma : methodDeclaration->mCommas) diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index ffe84efa..e0dcee4e 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -171,8 +171,8 @@ void BfMethodMatcher::Init(/*SizedArrayImpl& arguments, */BfSized mSkipImplicitParams = false; mAllowImplicitThis = false; mHadVarConflictingReturnType = false; - mMethodCheckCount = 0; - mInferGenericProgressIdx = 0; + mAutoFlushAmbiguityErrors = true; + mMethodCheckCount = 0; mCheckedKind = BfCheckedKind_NotSet; mMatchFailKind = MatchFailKind_None; @@ -218,7 +218,7 @@ bool BfMethodMatcher::IsMemberAccessible(BfTypeInstance* typeInst, BfTypeDef* de return true; } -bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue, HashSet& checkedTypeSet) +bool BfGenericInferContext::InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue) { if (argType == NULL) return false; @@ -249,7 +249,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT return; } - if (mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] != argType) + if ((*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] != argType) { if (methodGenericTypeConstraint != NULL) { @@ -266,7 +266,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT if (argGenericType->mTypeDef == wantGenericType->mTypeDef) { for (int genericArgIdx = 0; genericArgIdx < (int)argGenericType->mTypeGenericArguments.size(); genericArgIdx++) - InferGenericArgument(methodInstance, argGenericType->mTypeGenericArguments[genericArgIdx], wantGenericType->mTypeGenericArguments[genericArgIdx], BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, argGenericType->mTypeGenericArguments[genericArgIdx], wantGenericType->mTypeGenericArguments[genericArgIdx], BfIRValue()); } } else if (checkArgType->IsSizedArray()) @@ -274,10 +274,10 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT auto sizedArrayType = (BfSizedArrayType*)checkArgType; if (wantGenericType->mTypeDef == mModule->mCompiler->mSizedArrayTypeDef) { - InferGenericArgument(methodInstance, sizedArrayType->mElementType, wantGenericType->mTypeGenericArguments[0], BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, sizedArrayType->mElementType, wantGenericType->mTypeGenericArguments[0], BfIRValue()); auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); BfTypedValue arraySize = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, (uint64)sizedArrayType->mElementCount), intType); - InferGenericArgument(methodInstance, mModule->CreateConstExprValueType(arraySize), wantGenericType->mTypeGenericArguments[1], BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, mModule->CreateConstExprValueType(arraySize), wantGenericType->mTypeGenericArguments[1], BfIRValue()); } } else if (checkArgType->IsPointer()) @@ -285,7 +285,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT auto pointerType = (BfPointerType*)checkArgType; if (wantGenericType->mTypeDef == mModule->mCompiler->mPointerTTypeDef) { - InferGenericArgument(methodInstance, pointerType->mElementType, wantGenericType->mTypeGenericArguments[0], BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, pointerType->mElementType, wantGenericType->mTypeGenericArguments[0], BfIRValue()); } } @@ -296,11 +296,10 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT } } } - - mInferGenericProgressIdx++; - mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = argType; + + (*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] = argType; } - mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = argType; + (*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] = argType; mPrevArgValues[wantGenericParam->mGenericParamIdx] = argValue; }; @@ -334,7 +333,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT } } - auto prevGenericMethodArg = mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx]; + auto prevGenericMethodArg = (*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx]; auto prevArgValue = mPrevArgValues[wantGenericParam->mGenericParamIdx]; if (prevGenericMethodArg == NULL) { @@ -382,7 +381,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT } // No implicit conversion, FAIL! - mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = NULL; + (*mCheckMethodGenericArguments)[wantGenericParam->mGenericParamIdx] = NULL; return false; } return true; @@ -396,12 +395,12 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT auto genericParam = mModule->GetGenericParamInstance((BfGenericParamType*)argType); if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Var) != 0) { - InferGenericArgument(methodInstance, mModule->GetPrimitiveType(BfTypeCode_Var), wantType, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, mModule->GetPrimitiveType(BfTypeCode_Var), wantType, BfIRValue()); return true; } if ((genericParam->mTypeConstraint != NULL) && (genericParam->mTypeConstraint->IsGenericTypeInstance())) - InferGenericArgument(methodInstance, genericParam->mTypeConstraint, wantType, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, genericParam->mTypeConstraint, wantType, BfIRValue()); } if (argType->IsVar()) @@ -411,7 +410,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT BfType* wantGenericArgument = wantGenericType->mTypeGenericArguments[genericArgIdx]; if (!wantGenericArgument->IsUnspecializedType()) continue; - InferGenericArgument(methodInstance, mModule->GetPrimitiveType(BfTypeCode_Var), wantGenericArgument, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, mModule->GetPrimitiveType(BfTypeCode_Var), wantGenericArgument, BfIRValue()); } return true; } @@ -420,16 +419,16 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT return true; auto argGenericType = (BfGenericTypeInstance*)argType; if (argGenericType->mTypeDef != wantGenericType->mTypeDef) - return true; + return false; for (int genericArgIdx = 0; genericArgIdx < (int)argGenericType->mTypeGenericArguments.size(); genericArgIdx++) { BfType* wantGenericArgument = wantGenericType->mTypeGenericArguments[genericArgIdx]; if (!wantGenericArgument->IsUnspecializedType()) continue; - if (!_AddToCheckedSet(argType, checkedTypeSet, alreadyChecked)) + if (!_AddToCheckedSet(argType, mCheckedTypeSet, alreadyChecked)) return true; - InferGenericArgument(methodInstance, argGenericType->mTypeGenericArguments[genericArgIdx], wantGenericArgument, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, argGenericType->mTypeGenericArguments[genericArgIdx], wantGenericArgument, BfIRValue()); } return true; } @@ -440,15 +439,14 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT if (!argType->IsRef()) { // Match to non-ref - InferGenericArgument(methodInstance, argType, wantRefType->mElementType, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, argType, wantRefType->mElementType, BfIRValue()); return true; } auto argRefType = (BfRefType*)argType; //TODO: We removed this check so we still infer even if we have the wrong ref kind //if (wantRefType->mRefKind != argRefType->mRefKind) //return true; - InferGenericArgument(methodInstance, argRefType->mElementType, wantRefType->mElementType, BfIRValue(), checkedTypeSet); - return true; + return InferGenericArgument(methodInstance, argRefType->mElementType, wantRefType->mElementType, BfIRValue()); } if (wantType->IsPointer()) @@ -457,8 +455,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT return true; auto wantPointerType = (BfPointerType*) wantType; auto argPointerType = (BfPointerType*) argType; - InferGenericArgument(methodInstance, argPointerType->mElementType, wantPointerType->mElementType, BfIRValue(), checkedTypeSet); - return true; + return InferGenericArgument(methodInstance, argPointerType->mElementType, wantPointerType->mElementType, BfIRValue()); } if (wantType->IsUnknownSizedArray()) @@ -467,14 +464,14 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT if (argType->IsUnknownSizedArray()) { auto argArrayType = (BfUnknownSizedArrayType*)argType; - InferGenericArgument(methodInstance, argArrayType->mElementCountSource, wantArrayType->mElementCountSource, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, argArrayType->mElementCountSource, wantArrayType->mElementCountSource, BfIRValue()); } else if (argType->IsSizedArray()) { auto argArrayType = (BfSizedArrayType*)argType; BfTypedValue sizeValue(mModule->GetConstValue(argArrayType->mElementCount), mModule->GetPrimitiveType(BfTypeCode_IntPtr)); auto sizedType = mModule->CreateConstExprValueType(sizeValue); - InferGenericArgument(methodInstance, sizedType, wantArrayType->mElementCountSource, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, sizedType, wantArrayType->mElementCountSource, BfIRValue()); } } @@ -484,7 +481,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT { auto wantArrayType = (BfSizedArrayType*)wantType; auto argArrayType = (BfSizedArrayType*)argType; - InferGenericArgument(methodInstance, argArrayType->mElementType, wantArrayType->mElementType, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, argArrayType->mElementType, wantArrayType->mElementType, BfIRValue()); } } @@ -493,7 +490,7 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT if (((argType->IsDelegate()) || (argType->IsFunction())) && (wantType->IsDelegate() == argType->IsDelegate())) { - if (!_AddToCheckedSet(argType, checkedTypeSet, alreadyChecked)) + if (!_AddToCheckedSet(argType, mCheckedTypeSet, alreadyChecked)) return true; auto argInvokeMethod = mModule->GetRawMethodByName(argType->ToTypeInstance(), "Invoke"); @@ -501,9 +498,9 @@ bool BfMethodMatcher::InferGenericArgument(BfMethodInstance* methodInstance, BfT if ((argInvokeMethod != NULL) && (wantInvokeMethod != NULL) && (argInvokeMethod->GetParamCount() == wantInvokeMethod->GetParamCount())) { - InferGenericArgument(methodInstance, argInvokeMethod->mReturnType, wantInvokeMethod->mReturnType, BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, argInvokeMethod->mReturnType, wantInvokeMethod->mReturnType, BfIRValue()); for (int argIdx = 0; argIdx < (int)argInvokeMethod->GetParamCount(); argIdx++) - InferGenericArgument(methodInstance, argInvokeMethod->GetParamType(argIdx), wantInvokeMethod->GetParamType(argIdx), BfIRValue(), checkedTypeSet); + InferGenericArgument(methodInstance, argInvokeMethod->GetParamType(argIdx), wantInvokeMethod->GetParamType(argIdx), BfIRValue()); } } } @@ -515,6 +512,13 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp BfMethodInstance* newMethodInstance, BfTypeVector* genericArgumentsSubstitute, bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail) { + if (prevMethodInstance == newMethodInstance) + { + *outNewIsBetter = false; + *outNewIsWorse = true; + return; + } + #define SET_BETTER_OR_WORSE(lhs, rhs) \ if ((!isBetter) && (lhs) && !(rhs)) isBetter = true; \ if ((!isWorse) && !(lhs) && (rhs)) isWorse = true; @@ -551,12 +555,25 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp } } + bool anyIsExtension = false; + int newImplicitParamCount = newMethodInstance->GetImplicitParamCount(); if (newMethodInstance->mMethodDef->mHasAppend) newImplicitParamCount++; + if (newMethodInstance->mMethodDef->mMethodType == BfMethodType_Extension) + { + newImplicitParamCount++; + anyIsExtension = true; + } + int prevImplicitParamCount = prevMethodInstance->GetImplicitParamCount(); if (prevMethodInstance->mMethodDef->mHasAppend) prevImplicitParamCount++; + if (prevMethodInstance->mMethodDef->mMethodType == BfMethodType_Extension) + { + prevImplicitParamCount++; + anyIsExtension = true; + } bool hadEnoughArgs = newMethodInstance->GetParamCount() - newImplicitParamCount < (int)mArguments.size(); bool prevHadEnoughArgs = prevMethodInstance->GetParamCount() - prevImplicitParamCount < (int)mArguments.size(); @@ -570,22 +587,32 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp auto activeDef = mModule->GetActiveTypeDef(); RETURN_BETTER_OR_WORSE(newMethodDef->mDeclaringType == activeDef, prevMethodDef->mDeclaringType == activeDef); RETURN_BETTER_OR_WORSE(newMethodDef->mDeclaringType->IsExtension(), prevMethodDef->mDeclaringType->IsExtension()); - + if ((!isBetter) && (!isWorse)) { bool betterByGenericParam = false; bool worseByGenericParam = false; - for (argIdx = 0; argIdx < (int)mArguments.size(); argIdx++) + for (argIdx = anyIsExtension ? -1 : 0; argIdx < (int)mArguments.size(); argIdx++) { - BfResolvedArg resolvedArg = mArguments[argIdx];; - BfTypedValue arg = resolvedArg.mTypedValue; + BfTypedValue arg; + BfResolvedArg* resolvedArg = NULL; + + if (argIdx == -1) + { + arg = mTarget; + } + else + { + resolvedArg = &mArguments[argIdx]; + arg = resolvedArg->mTypedValue; + } int newArgIdx = argIdx + newImplicitParamCount; int prevArgIdx = argIdx + prevImplicitParamCount; - bool wasGenericParam = newMethodInstance->WasGenericParam(newArgIdx); - bool prevWasGenericParam = prevMethodInstance->WasGenericParam(prevArgIdx); + bool wasGenericParam = (newArgIdx >= 0) && newMethodInstance->WasGenericParam(newArgIdx); + bool prevWasGenericParam = (prevArgIdx >= 0) && prevMethodInstance->WasGenericParam(prevArgIdx); BfType* paramType = newMethodInstance->GetParamType(newArgIdx); BfType* prevParamType = prevMethodInstance->GetParamType(prevArgIdx); @@ -637,7 +664,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp { // The resolved argument type may actually match for both considered functions. IE: // Method(int8 val) and Method(int16 val) called with Method(0) will create arguments that match their param types - if ((IsType(arg, paramType)) && (prevParamType != resolvedArg.mBestBoundType)) + if ((IsType(arg, paramType)) && ((resolvedArg == NULL) || (prevParamType != resolvedArg->mBestBoundType))) isBetter = true; else if ((IsType(arg, prevParamType)) && (!IsType(arg, paramType))) isWorse = true; @@ -706,9 +733,9 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp isWorse = true; } - if (newMethodInstance->GetParamKind(newArgIdx) == BfParamKind_Params) + if ((newArgIdx >= 0) && (newMethodInstance->GetParamKind(newArgIdx) == BfParamKind_Params)) usedExtendedForm = true; - if (prevMethodInstance->GetParamKind(prevArgIdx) == BfParamKind_Params) + if ((prevArgIdx >= 0) && (prevMethodInstance->GetParamKind(prevArgIdx) == BfParamKind_Params)) prevUsedExtendedForm = true; if ((usedExtendedForm) || (prevUsedExtendedForm)) @@ -1236,6 +1263,10 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst } } + BfGenericInferContext genericInferContext; + genericInferContext.mModule = mModule; + genericInferContext.mCheckMethodGenericArguments = &mCheckMethodGenericArguments; + HashSet allowEmptyGenericSet; BfAutoComplete* autoComplete = NULL; if ((mModule->mCompiler->mResolvePassData != NULL) && (!isFailurePass)) @@ -1279,8 +1310,9 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst for (auto& checkGenericArgRef : mCheckMethodGenericArguments) checkGenericArgRef = NULL; - mCheckMethodGenericArguments.resize(checkMethod->mGenericParams.size()); - mPrevArgValues.resize(checkMethod->mGenericParams.size()); + mCheckMethodGenericArguments.resize(checkMethod->mGenericParams.size()); + + //mPrevArgValues.resize(checkMethod->mGenericParams.size()); for (auto& genericArgRef : mCheckMethodGenericArguments) genericArgRef = NULL; @@ -1313,18 +1345,28 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst if (needInferGenericParams) { + genericInferContext.mPrevArgValues.resize(checkMethod->mGenericParams.size()); + int paramOfs = methodInstance->GetImplicitParamCount(); int paramCount = methodInstance->GetParamCount(); + + int argIdx = 0; + int paramIdx = 0; + if (checkMethod->mHasAppend) - paramOfs++; - for (int argIdx = 0; argIdx < (int)mArguments.size(); argIdx++) + paramIdx++; + + if (checkMethod->mMethodType == BfMethodType_Extension) + { + argIdx--; + paramOfs++; + } + + for ( ; argIdx < (int)mArguments.size(); argIdx++) { - if (argIdx >= (int)checkMethod->mParams.size()) - break; - int paramIdx = argIdx + paramOfs; if (paramIdx >= paramCount) break; // Possible for delegate 'params' type methods - auto wantType = methodInstance->GetParamType(argIdx + paramOfs); + auto wantType = methodInstance->GetParamType(paramIdx); auto checkType = wantType; auto origCheckType = checkType; @@ -1354,17 +1396,25 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst if (wantType->IsUnspecializedType()) { - BfTypedValue argTypedValue = ResolveArgTypedValue(mArguments[argIdx], checkType, genericArgumentsSubstitute); + BfTypedValue argTypedValue; + if (argIdx == -1) + argTypedValue = mTarget; + else + argTypedValue = ResolveArgTypedValue(mArguments[argIdx], checkType, genericArgumentsSubstitute); if (!argTypedValue.IsUntypedValue()) { auto type = argTypedValue.mType; if (!argTypedValue) goto NoMatch; - HashSet checkedTypeSet; - if (!InferGenericArgument(methodInstance, type, wantType, argTypedValue.mValue, checkedTypeSet)) + //HashSet checkedTypeSet; + + genericInferContext.mCheckedTypeSet.Clear(); + if (!genericInferContext.InferGenericArgument(methodInstance, type, wantType, argTypedValue.mValue)) goto NoMatch; } } + + paramIdx++; } // @@ -1414,6 +1464,9 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst } } + if (checkMethod->mMethodType == BfMethodType_Extension) + argIdx--; + // Iterate through params while (true) { @@ -1425,7 +1478,7 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst bool isDeferredEval = false; - if ((methodInstance->GetParamKind(paramIdx) == BfParamKind_Params) && (paramsElementType == NULL)) + if ((argIdx >= 0) && (methodInstance->GetParamKind(paramIdx) == BfParamKind_Params) && (paramsElementType == NULL)) { if (paramIdx >= (int) mArguments.size()) break; // No params @@ -1501,12 +1554,17 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst if (wantType->IsSelf()) wantType = typeInstance; - if ((mArguments[argIdx].mArgFlags & BfArgFlag_ParamsExpr) != 0) + if ((argIdx >= 0) && ((mArguments[argIdx].mArgFlags & BfArgFlag_ParamsExpr) != 0)) { // We had a 'params' expression but this method didn't have a params slot in this parameter goto NoMatch; } - BfTypedValue argTypedValue = ResolveArgTypedValue(mArguments[argIdx], wantType, genericArgumentsSubstitute); + + BfTypedValue argTypedValue; + if (argIdx == -1) + argTypedValue = mTarget; + else + argTypedValue = ResolveArgTypedValue(mArguments[argIdx], wantType, genericArgumentsSubstitute); if (!argTypedValue.IsUntypedValue()) { @@ -1775,17 +1833,13 @@ bool BfMethodMatcher::IsType(BfTypedValue& typedVal, BfType* type) } // This method checks all base classes before checking interfaces. Is that correct? -bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue target, bool isFailurePass) +bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue target, bool isFailurePass, bool forceOuterCheck) { auto curTypeInst = typeInstance; auto curTypeDef = typeInstance->mTypeDef; - + int checkInterfaceIdx = 0; - - bool allowExplicitInterface = curTypeInst->IsInterface() && mBypassVirtual; - auto activeTypeDef = mModule->GetActiveTypeDef(); - bool isDelegate = typeInstance->IsDelegate(); - + bool targetIsBase = target.IsBase(); bool checkExtensionBase = false; if (targetIsBase) @@ -1805,16 +1859,26 @@ bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue targe targetTypeInstance = target.mType->ToTypeInstance(); while (true) - { - curTypeDef->PopulateMemberSets(); + { + bool doSearch = true; + if ((mMethodType == BfMethodType_Extension) && (!curTypeDef->mHasExtensionMethods)) + doSearch = false; + BfMethodDef* nextMethodDef = NULL; - BfMemberSetEntry* entry; - if (curTypeDef->mMethodSet.TryGetWith(mMethodName, &entry)) - nextMethodDef = (BfMethodDef*)entry->mMemberDef; - + if (doSearch) + { + curTypeDef->PopulateMemberSets(); + BfMemberSetEntry* entry; + if (curTypeDef->mMethodSet.TryGetWith(mMethodName, &entry)) + nextMethodDef = (BfMethodDef*)entry->mMemberDef; + } BfProtectionCheckFlags protectionCheckFlags = BfProtectionCheckFlag_None; while (nextMethodDef != NULL) { + bool allowExplicitInterface = curTypeInst->IsInterface() && mBypassVirtual; + auto activeTypeDef = mModule->GetActiveTypeDef(); + bool isDelegate = typeInstance->IsDelegate(); + auto checkMethod = nextMethodDef; nextMethodDef = nextMethodDef->mNextWithSameName; @@ -1914,9 +1978,10 @@ bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue targe mMatchFailKind = matchFailKind; } - if (mBestMethodDef != NULL) + if ((mBestMethodDef != NULL) && (mMethodType != BfMethodType_Extension)) { - FlushAmbiguityError(); + if (mAutoFlushAmbiguityErrors) + FlushAmbiguityError(); return true; } @@ -1953,7 +2018,8 @@ bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue targe mBestMethodDef = mBackupMethodDef; } - if ((mBestMethodDef == NULL) && (!target) && (mAllowImplicitThis)) + if (((mBestMethodDef == NULL) && (!target) && (mAllowImplicitThis)) || + (forceOuterCheck)) { // No explicit target - maybe this was a static call in the outer type? auto outerType = mModule->GetOuterType(typeInstance); @@ -1961,7 +2027,8 @@ bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue targe CheckOuterTypeStaticMethods(outerType, isFailurePass); } - FlushAmbiguityError(); + if (mAutoFlushAmbiguityErrors) + FlushAmbiguityError(); return mBestMethodDef != NULL; } @@ -1977,11 +2044,6 @@ void BfMethodMatcher::TryDevirtualizeCall(BfTypedValue target, BfTypedValue* ori if (mModule->mBfIRBuilder->mIgnoreWrites) return; - if (mBestMethodDef->mName == "Quab") - { - NOP; - } - if (mBestMethodTypeInstance->IsInterface()) { mModule->PopulateType(mBestMethodTypeInstance, BfPopulateType_DataAndMethods); @@ -2165,7 +2227,7 @@ void BfMethodMatcher::CheckOuterTypeStaticMethods(BfTypeInstance* typeInstance, // These can only be invoked when the target itself is the interface if (checkMethod->mExplicitInterface != NULL) continue; - if ((checkMethod->mMethodType != BfMethodType_Normal) || (!checkMethod->mIsStatic)) + if ((checkMethod->mMethodType != mMethodType) || (!checkMethod->mIsStatic)) continue; if (checkMethod->mName != mMethodName) continue; @@ -5240,7 +5302,10 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu skipMutCheck = true; } - PushThis(targetSrc, target, moduleMethodInstance.mMethodInstance, irArgs, skipMutCheck); + if (methodDef->mMethodType == BfMethodType_Extension) + PushArg(target, irArgs); + else + PushThis(targetSrc, target, moduleMethodInstance.mMethodInstance, irArgs, skipMutCheck); } } else if ((target) && (target.mType->IsFunction())) @@ -5251,6 +5316,10 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu auto funcPtrType = mModule->mBfIRBuilder->GetPointerTo(funcType); moduleMethodInstance.mFunc = mModule->mBfIRBuilder->CreateIntToPtr(target.mValue, funcPtrType); } + else if (methodDef->mMethodType == BfMethodType_Extension) + { + // Handled in args + } else { mModule->CheckStaticAccess(methodInstance->mMethodInstanceGroup->mOwner); @@ -5463,14 +5532,25 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu BfAstNode* arg = NULL; bool hadMissingArg = false; - if (argIdx < (int)argValues.size()) + + int argExprIdx = argIdx; + if ((methodDef->mMethodType == BfMethodType_Extension)) { - arg = argValues[argIdx].mExpression; - if ((arg == NULL) && (argValues[0].mExpression != NULL)) - hadMissingArg = true; + argExprIdx--; + if (argExprIdx == -1) + arg = targetSrc; + } + if (argExprIdx >= 0) + { + if (argExprIdx < (int)argValues.size()) + { + arg = argValues[argExprIdx].mExpression; + if ((arg == NULL) && (argValues[argExprIdx].mExpression != NULL)) + hadMissingArg = true; + } + else + hadMissingArg = true; } - else - hadMissingArg = true; BfTypedValue argValue; @@ -5588,12 +5668,15 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu } } else - { - argValue = argValues[argIdx].mTypedValue; + { + if (argExprIdx == -1) + argValue = target; + else + argValue = argValues[argExprIdx].mTypedValue; if ((argValue.IsParams()) && (!isDirectPass)) { - BfAstNode* refNode = argValues[argIdx].mExpression; + BfAstNode* refNode = arg; if (auto unaryOperatorExpr = BfNodeDynCast(refNode)) refNode = unaryOperatorExpr->mOpToken; mModule->Warn(0, "Unused 'params' expression", refNode); @@ -5606,7 +5689,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu SetMethodElementType(expr); if (!argValue) - argValue = mModule->CreateValueFromExpression(BfNodeDynCast(argValues[argIdx].mExpression), wantType, BfEvalExprFlags_NoCast); + argValue = mModule->CreateValueFromExpression(BfNodeDynCast(arg), wantType, BfEvalExprFlags_NoCast); // Add any implicit captures now auto methodRefType = (BfMethodRefType*)wantType; @@ -5615,7 +5698,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu for (int dataIdx = 0; dataIdx < methodRefType->GetCaptureDataCount(); dataIdx++) { int paramIdx = methodRefType->GetParamIdxFromDataIdx(dataIdx); - auto lookupVal = DoImplicitArgCapture(argValues[argIdx].mExpression, useMethodInstance, paramIdx, failed, BfImplicitParamKind_General, argValue); + auto lookupVal = DoImplicitArgCapture(arg, useMethodInstance, paramIdx, failed, BfImplicitParamKind_General, argValue); if (lookupVal) { if (methodRefType->WantsDataPassedAsSplat(dataIdx)) @@ -5633,14 +5716,14 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu argIdx++; continue; } - else + else if (argExprIdx >= 0) { BfParamKind paramKind = BfParamKind_Normal; if (paramIdx < methodInstance->GetParamCount()) paramKind = methodInstance->GetParamKind(paramIdx); - argValues[argIdx].mExpectedType = wantType; - argValue = ResolveArgValue(argValues[argIdx], wantType, NULL, paramKind); + argValues[argExprIdx].mExpectedType = wantType; + argValue = ResolveArgValue(argValues[argExprIdx], wantType, NULL, paramKind); } } @@ -6449,6 +6532,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp SetAndRestoreValue prevNoBind(mNoBind, mNoBind || isUnboundCall); + bool wantsExtensionCheck = target; auto targetType = target.mType; BfTypeDef* curTypeDef = NULL; BfTypeInstance* targetTypeInst = NULL; @@ -6544,10 +6628,12 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp BfTypeInstance* curTypeInst = targetTypeInst; BfMethodMatcher methodMatcher(targetSrc, mModule, methodName, argValues.mResolvedArgs, methodGenericArguments); + methodMatcher.mTarget = target; methodMatcher.mCheckedKind = checkedKind; methodMatcher.mAllowImplicitThis = allowImplicitThis; methodMatcher.mAllowStatic = !target.mValue; methodMatcher.mAllowNonStatic = !methodMatcher.mAllowStatic; + methodMatcher.mAutoFlushAmbiguityErrors = !wantsExtensionCheck; if (allowImplicitThis) { if (mModule->mCurMethodState == NULL) @@ -7051,50 +7137,77 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp return structInst; } + } + + // For for extensions in current type + if (wantsExtensionCheck) + { + auto checkTypeInst = mModule->mCurTypeInstance; + methodMatcher.mMethodType = BfMethodType_Extension; + methodMatcher.CheckType(checkTypeInst, BfTypedValue(), false, true); + if (methodMatcher.mBestMethodDef != NULL) + { + isFailurePass = false; + curTypeInst = methodMatcher.mBestMethodTypeInstance; + methodDef = methodMatcher.mBestMethodDef; + } } - if ((methodDef == NULL) && (!target) && (mModule->mContext->mCurTypeState != NULL)) - { - //BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mTypeInstance); - - BfGlobalLookup globalLookup; - globalLookup.mKind = BfGlobalLookup::Kind_Method; - globalLookup.mName = methodName; - mModule->PopulateGlobalContainersList(globalLookup); - for (auto& globalContainer : mModule->mContext->mCurTypeState->mGlobalContainers) + // Look in globals. Always check for extension methods. + if ((methodDef == NULL) || (wantsExtensionCheck)) + { + if (mModule->mContext->mCurTypeState != NULL) { - if (globalContainer.mTypeInst == NULL) - continue; - methodMatcher.CheckType(globalContainer.mTypeInst, BfTypedValue(), false); - if (methodMatcher.mBestMethodDef != NULL) + BfGlobalLookup globalLookup; + globalLookup.mKind = BfGlobalLookup::Kind_Method; + globalLookup.mName = methodName; + mModule->PopulateGlobalContainersList(globalLookup); + for (auto& globalContainer : mModule->mContext->mCurTypeState->mGlobalContainers) { - isFailurePass = false; - curTypeInst = methodMatcher.mBestMethodTypeInstance; - methodDef = methodMatcher.mBestMethodDef; - break; - } - } - - if (methodDef == NULL) - { - BfStaticSearch* staticSearch = mModule->GetStaticSearch(); - if (staticSearch != NULL) - { - for (auto typeInst : staticSearch->mStaticTypes) + if (globalContainer.mTypeInst == NULL) + continue; + methodMatcher.mMethodType = wantsExtensionCheck ? BfMethodType_Extension : BfMethodType_Normal; + methodMatcher.CheckType(globalContainer.mTypeInst, BfTypedValue(), false); + + if (methodMatcher.mBestMethodDef != NULL) { - methodMatcher.CheckType(typeInst, BfTypedValue(), false); - if (methodMatcher.mBestMethodDef != NULL) - { - isFailurePass = false; - curTypeInst = methodMatcher.mBestMethodTypeInstance; - methodDef = methodMatcher.mBestMethodDef; + isFailurePass = false; + curTypeInst = methodMatcher.mBestMethodTypeInstance; + methodDef = methodMatcher.mBestMethodDef; + // Extension check must check all possible extensions, no early bailout + if (!wantsExtensionCheck) + break; + } + } + } + } + + // Look in static search. Always check for extension methods. + if ((methodDef == NULL) || (wantsExtensionCheck)) + { + BfStaticSearch* staticSearch = mModule->GetStaticSearch(); + if (staticSearch != NULL) + { + for (auto typeInst : staticSearch->mStaticTypes) + { + methodMatcher.mMethodType = wantsExtensionCheck ? BfMethodType_Extension : BfMethodType_Normal; + methodMatcher.CheckType(typeInst, BfTypedValue(), false); + if (methodMatcher.mBestMethodDef != NULL) + { + isFailurePass = false; + curTypeInst = methodMatcher.mBestMethodTypeInstance; + methodDef = methodMatcher.mBestMethodDef; + // Extension check must check all possible extensions, no early bailout + if (!wantsExtensionCheck) break; - } } } } } + // This will flush out any new ambiguity errors from extension methods + methodMatcher.FlushAmbiguityError(); + if (methodDef == NULL) { FinishDeferredEvals(argValues.mResolvedArgs); @@ -7205,6 +7318,14 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp else MakeBaseConcrete(target); + if (methodDef->mMethodType == BfMethodType_Extension) + { + auto thisType = moduleMethodInstance.mMethodInstance->GetParamType(0); + curTypeInst = thisType->ToTypeInstance(); + if (curTypeInst == NULL) + curTypeInst = mModule->mContext->mBfObjectType; + } + BfTypedValue callTarget; if (isSkipCall) { @@ -12741,6 +12862,9 @@ void BfExprEvaluator::CheckLocalMethods(BfAstNode* targetSrc, BfTypeInstance* ty void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, bool allowImplicitThis, const StringImpl& name, const BfSizedArray& arguments, BfSizedArray* methodGenericArgs) { + if (mModule->mCurMethodState == NULL) + return; + BfAstNode* origTargetSrc = targetSrc; BfScopedInvocationTarget* scopedInvocationTarget = NULL; @@ -13041,7 +13165,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo //auto prevDebugLoc = mModule->mBfIRBuilder->getCurrentDebugLocation(); // This is so when we debug we can hit a steppoint on the inlined "call line" mModule->EmitEnsureInstructionAt(); - + auto rootMethodState = mModule->mCurMethodState->GetRootMethodState(); BfMixinState* mixinState = rootMethodState->mMixinStates.Alloc(); diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 7b64d4a3..c6d27a9f 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -101,6 +101,18 @@ public: void HandleFixits(BfModule* module); }; +class BfGenericInferContext +{ +public: + HashSet mCheckedTypeSet; + BfModule* mModule; + BfTypeVector* mCheckMethodGenericArguments; + SizedArray mPrevArgValues; + +public: + bool InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue); +}; + class BfMethodMatcher { public: @@ -119,9 +131,10 @@ public: public: BfAstNode* mTargetSrc; + BfTypedValue mTarget; BfModule* mModule; BfTypeDef* mActiveTypeDef; - String mMethodName; + String mMethodName; BfMethodInstance* mInterfaceMethodInstance; SizedArrayImpl& mArguments; BfMethodType mMethodType; @@ -134,13 +147,12 @@ public: bool mAllowStatic; bool mAllowNonStatic; bool mSkipImplicitParams; - int mMethodCheckCount; - int mInferGenericProgressIdx; + bool mAutoFlushAmbiguityErrors; + int mMethodCheckCount; BfType* mExplicitInterfaceCheck; MatchFailKind mMatchFailKind; - BfTypeVector mCheckMethodGenericArguments; - SizedArray mPrevArgValues; + BfTypeVector mCheckMethodGenericArguments; BfType* mSelfType; // Only when matching interfaces when 'Self' needs to refer back to the implementing type BfMethodDef* mBackupMethodDef; @@ -153,11 +165,10 @@ public: BfTypeVector mBestMethodGenericArguments; BfTypeVector mExplicitMethodGenericArguments; bool mFakeConcreteTarget; - Array mAmbiguousEntries; + Array mAmbiguousEntries; public: - BfTypedValue ResolveArgTypedValue(BfResolvedArg& resolvedArg, BfType* checkType, BfTypeVector* genericArgumentsSubstitute); - bool InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue, HashSet& checkedTypeSet); + BfTypedValue ResolveArgTypedValue(BfResolvedArg& resolvedArg, BfType* checkType, BfTypeVector* genericArgumentsSubstitute); bool InferFromGenericConstraints(BfGenericParamInstance* genericParamInst, BfTypeVector* methodGenericArgs); void CompareMethods(BfMethodInstance* prevMethodInstance, BfTypeVector* prevGenericArgumentsSubstitute, BfMethodInstance* newMethodInstance, BfTypeVector* genericArgumentsSubstitute, @@ -170,7 +181,7 @@ public: BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* interfaceMethodInstance, SizedArrayImpl& arguments, BfSizedArray* methodGenericArguments = NULL); void Init(/*SizedArrayImpl& arguments, */BfSizedArray* methodGenericArguments); bool IsMemberAccessible(BfTypeInstance* typeInst, BfTypeDef* declaringType); - bool CheckType(BfTypeInstance* typeInstance, BfTypedValue target, bool isFailurePass); + bool CheckType(BfTypeInstance* typeInstance, BfTypedValue target, bool isFailurePass, bool forceOuterCheck = false); void CheckOuterTypeStaticMethods(BfTypeInstance* typeInstance, bool isFailurePass); bool WantsCheckMethod(BfProtectionCheckFlags& flags, BfTypeInstance* startTypeInstance, BfTypeInstance* checkTypeInstance, BfMethodDef* methodDef); bool CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInstance* typeInstance, BfMethodDef* checkMethod, bool isFailurePass); diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index a3238665..bc3ff267 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -2290,6 +2290,7 @@ void BfPrinter::QueueMethodDeclaration(BfMethodDeclaration* methodDeclaration) } QueueVisitChild(methodDeclaration->mOpenParen); + QueueVisitChild(methodDeclaration->mThisToken); for (int i = 0; i < (int) methodDeclaration->mParams.size(); i++) { if (i > 0) diff --git a/IDEHelper/Compiler/BfSystem.cpp b/IDEHelper/Compiler/BfSystem.cpp index 3694c2f2..840099ca 100644 --- a/IDEHelper/Compiler/BfSystem.cpp +++ b/IDEHelper/Compiler/BfSystem.cpp @@ -2618,6 +2618,7 @@ void BfSystem::InjectNewRevision(BfTypeDef* typeDef) typeDef->mIsStatic = nextTypeDef->mIsStatic; typeDef->mHasAppendCtor = nextTypeDef->mHasAppendCtor; typeDef->mHasOverrideMethods = nextTypeDef->mHasOverrideMethods; + typeDef->mHasExtensionMethods = nextTypeDef->mHasExtensionMethods; typeDef->mIsOpaque = nextTypeDef->mIsOpaque; typeDef->mDupDetectedRevision = nextTypeDef->mDupDetectedRevision; @@ -2718,6 +2719,7 @@ void BfSystem::AddToCompositePartial(BfPassInstance* passInstance, BfTypeDef* co typeDef->mIsConcrete = partialTypeDef->mIsConcrete; typeDef->mIsStatic = partialTypeDef->mIsStatic; typeDef->mHasAppendCtor = partialTypeDef->mHasAppendCtor; + typeDef->mHasExtensionMethods = partialTypeDef->mHasExtensionMethods; typeDef->mHasOverrideMethods = partialTypeDef->mHasOverrideMethods; for (auto generic : partialTypeDef->mGenericParamDefs) @@ -2750,6 +2752,7 @@ void BfSystem::AddToCompositePartial(BfPassInstance* passInstance, BfTypeDef* co typeDef->mIsConcrete |= partialTypeDef->mIsConcrete; typeDef->mIsStatic |= partialTypeDef->mIsStatic; typeDef->mHasAppendCtor |= partialTypeDef->mHasAppendCtor; + typeDef->mHasExtensionMethods |= partialTypeDef->mHasExtensionMethods; typeDef->mHasOverrideMethods |= partialTypeDef->mHasOverrideMethods; typeDef->mProtection = BF_MIN(typeDef->mProtection, partialTypeDef->mProtection); diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index a65e7763..a3130eac 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -631,7 +631,8 @@ enum BfMethodType : uint8 BfMethodType_CtorClear, BfMethodType_Dtor, BfMethodType_Operator, - BfMethodType_Mixin + BfMethodType_Mixin, + BfMethodType_Extension }; enum BfCallingConvention : uint8 @@ -898,7 +899,8 @@ public: bool mIsConcrete; bool mIsStatic; bool mHasAppendCtor; - bool mHasOverrideMethods; + bool mHasExtensionMethods; + bool mHasOverrideMethods; bool mIsOpaque; bool mIsNextRevision; @@ -934,6 +936,7 @@ public: mIsClosure = false; mIsStatic = false; mHasAppendCtor = false; + mHasExtensionMethods = false; mHasOverrideMethods = false; mIsOpaque = false; mPartialUsed = false; diff --git a/IDEHelper/Tests/src/ExtensionMethods.bf b/IDEHelper/Tests/src/ExtensionMethods.bf new file mode 100644 index 00000000..8d37e89c --- /dev/null +++ b/IDEHelper/Tests/src/ExtensionMethods.bf @@ -0,0 +1,59 @@ +using System; +using System.Collections; + +using static Tests.ExtensionMethods.ClassB; + +namespace Tests +{ + class ExtensionMethods + { + public static int Remove(this String str, float val, float val2) + { + return 123; + } + + class ClassA + { + public static void Test() + { + Test.Assert("Abc".Remove(1.2f, 2.3f) == 123); + } + } + + public class ClassB + { + public static int Flop(this String str, int a) + { + return a + 1000; + } + + } + + [Test] + public static void TestBasics() + { + ClassA.Test(); + + Test.Assert("Test".Flop(234) == 1234); + + List iList = scope .(); + iList.Add(3); + iList.Add(20); + iList.Add(100); + + Test.Assert(iList.Total() == 123); + } + } + + static + { + public static T Total(this List list) where T : IOpAddable + { + T total = default; + for (let val in list) + total += val; + return total; + } + } +} +