From 6cb2df65a6d8db5c8f60c4129e37ce454cd807e8 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Wed, 27 May 2020 09:46:09 -0700 Subject: [PATCH] Improvements to interfaces: extensions, better generics, statics --- IDEHelper/Compiler/BfAutoComplete.cpp | 70 ++++++++++--- IDEHelper/Compiler/BfCompiler.cpp | 10 +- IDEHelper/Compiler/BfExprEvaluator.cpp | 59 +++++++---- IDEHelper/Compiler/BfModule.cpp | 27 +++-- IDEHelper/Compiler/BfModule.h | 18 ++-- IDEHelper/Compiler/BfModuleTypeUtils.cpp | 60 ++++++++--- IDEHelper/Compiler/BfReducer.cpp | 10 +- IDEHelper/Compiler/BfReducer.h | 3 +- IDEHelper/Compiler/BfResolvedTypeUtils.cpp | 5 +- IDEHelper/Compiler/BfSystem.cpp | 6 +- IDEHelper/Tests/src/Interfaces.bf | 110 +++++++++++++++++++++ IDEHelper/Tests/src/Operators.bf | 12 +++ 12 files changed, 322 insertions(+), 68 deletions(-) diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index fef1e946..36271675 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -1546,8 +1546,10 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken auto _HandleGenericParamInstance = [&](BfGenericParamInstance* genericParamInstance) { + bool showStatics = !targetValue.mValue; + for (auto interfaceConstraint : genericParamInstance->mInterfaceConstraints) - AddTypeMembers(interfaceConstraint, false, true, filter, interfaceConstraint, true, false); + AddTypeMembers(interfaceConstraint, showStatics, !showStatics, filter, interfaceConstraint, true, false); if (genericParamInstance->mTypeConstraint != NULL) checkType = genericParamInstance->mTypeConstraint; @@ -1928,15 +1930,19 @@ void BfAutoComplete::CheckNode(BfAstNode* node) bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* showString, StringImpl* insertString, bool isImplementing, bool isExplicitInterface) { + SetAndRestoreValue prevMethodInstance(mModule->mCurMethodInstance, methodInst); + auto methodDef = methodInst->mMethodDef; bool isInterface = methodInst->GetOwner()->IsInterface(); + BfTypeNameFlags nameFlags = (BfTypeNameFlags)(BfTypeNameFlag_ReduceName | BfTypeNameFlag_ResolveGenericParamNames); + if (methodDef->mMethodType == BfMethodType_Normal) { StringT<128> methodPrefix; StringT<128> methodName; - StringT<256> impString; - + StringT<256> impString; + bool isAbstract = methodDef->mIsAbstract || isInterface;// (methodDef->mIsAbstract) && (!isInterface); if (isAbstract) @@ -1965,15 +1971,33 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho methodPrefix += methodDeclaration->mProtectionSpecifier->ToString() + " "; if (!isInterface) methodPrefix += "override "; - methodPrefix += mModule->TypeToString(methodInst->mReturnType, BfTypeNameFlag_ReduceName); + if (methodDef->mIsStatic) + methodPrefix += "static "; + + methodPrefix += mModule->TypeToString(methodInst->mReturnType, nameFlags); methodPrefix += " "; if (isExplicitInterface) { - methodName += mModule->TypeToString(methodInst->GetOwner(), BfTypeNameFlag_ReduceName); + methodName += mModule->TypeToString(methodInst->GetOwner(), nameFlags); methodName += "."; } methodName += methodDef->mName; + + if (methodInst->GetNumGenericArguments() > 0) + { + methodName += "<"; + for (int genericArgIdx = 0; genericArgIdx < (int)methodInst->mMethodInfoEx->mGenericParams.size(); genericArgIdx++) + { + if (genericArgIdx > 0) + methodName += ", "; + + auto genericParam = methodInst->mMethodInfoEx->mGenericParams[genericArgIdx]; + methodName += genericParam->GetName(); + } + methodName += ">"; + } + methodName += "("; for (int paramIdx = 0; paramIdx < (int)methodInst->GetParamCount(); paramIdx++) { @@ -1983,7 +2007,7 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho if (!isAbstract) impString += ", "; } - methodName += mModule->TypeToString(methodInst->GetParamType(paramIdx), BfTypeNameFlag_ReduceName); + methodName += mModule->TypeToString(methodInst->GetParamType(paramIdx), nameFlags); methodName += " "; methodName += methodDef->mParams[paramIdx]->mName; @@ -1999,6 +2023,33 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho } methodName += ")"; + if (methodInst->GetNumGenericArguments() > 0) + { + for (int genericArgIdx = 0; genericArgIdx < (int)methodInst->mMethodInfoEx->mGenericParams.size(); genericArgIdx++) + { + auto genericParam = methodInst->mMethodInfoEx->mGenericParams[genericArgIdx]; + + if (genericParam->mTypeConstraint != NULL) + methodName += " where " + genericParam->GetName() + " : " + mModule->TypeToString(genericParam->mTypeConstraint, nameFlags); + + for (auto ifaceConstraint : genericParam->mInterfaceConstraints) + methodName += " where " + genericParam->GetName() + " : " + mModule->TypeToString(ifaceConstraint, nameFlags); + + if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Class) != 0) + methodName += " where " + genericParam->GetName() + " : class"; + if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Struct) != 0) + methodName += " where " + genericParam->GetName() + " : struct"; + if ((genericParam->mGenericParamFlags & BfGenericParamFlag_StructPtr) != 0) + methodName += " where " + genericParam->GetName() + " : struct*"; + if ((genericParam->mGenericParamFlags & BfGenericParamFlag_New) != 0) + methodName += " where " + genericParam->GetName() + " : new"; + if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Delete) != 0) + methodName += " where " + genericParam->GetName() + " : delete"; + if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Var) != 0) + methodName += " where " + genericParam->GetName() + " : var"; + } + } + if (!isAbstract) impString += ");"; @@ -2037,11 +2088,11 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho BfType* propType = methodInst->mReturnType; if (methodDef->mMethodType == BfMethodType_PropertySetter) propType = methodInst->GetParamType(0); - impl += mModule->TypeToString(propType, BfTypeNameFlag_ReduceName); + impl += mModule->TypeToString(propType, nameFlags); impl += " "; if (isExplicitInterface) { - impl += mModule->TypeToString(methodInst->GetOwner(), BfTypeNameFlag_ReduceName); + impl += mModule->TypeToString(methodInst->GetOwner(), nameFlags); impl += "."; } @@ -2589,9 +2640,6 @@ void BfAutoComplete::CheckInterfaceFixit(BfTypeInstance* typeInstance, BfAstNode if (ifaceMethodInst == NULL) continue; - // Don't even try to match generics - if (!ifaceMethodInst->mMethodDef->mGenericParams.IsEmpty()) - continue; auto iReturnType = ifaceMethodInst->mReturnType; if (iReturnType->IsSelf()) iReturnType = typeInstance; diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 2c86caf1..656c2ce0 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -2954,10 +2954,11 @@ void BfCompiler::UpdateRevisedTypes() continue; } - // Only allow extending structs and objects + // Only allow extending structs, objects, and interfaces if ((checkTypeDef->mTypeCode == BfTypeCode_Struct) || (checkTypeDef->mTypeCode == BfTypeCode_Object) || - (checkTypeDef->mTypeCode == BfTypeCode_Enum)) + (checkTypeDef->mTypeCode == BfTypeCode_Enum) || + (checkTypeDef->mTypeCode == BfTypeCode_Interface)) { rootTypeDef = checkTypeDef; rootTypeDefEntry = checkTypeDefEntry; @@ -5016,7 +5017,10 @@ void BfCompiler::PopulateReified() BfMethodInstance* implMethod = *implMethodRef; if (implMethod == NULL) continue; - if (!implMethod->IsReifiedAndImplemented()) + + // Reify any interface methods that could be called dynamically + if ((!implMethod->IsReifiedAndImplemented()) && (implMethod->GetNumGenericParams() == 0) && (!implMethod->mMethodDef->mIsStatic) && + (!implMethod->mReturnType->IsConcreteInterfaceType())) { didWork = true; checkType->mModule->GetMethodInstance(implMethod); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index df47e44a..6f247054 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -1527,20 +1527,11 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst goto NoMatch; } -// if (genericArg->IsPrimitiveType()) -// { -// auto primType = (BfPrimitiveType*) genericArg; -// genericArg = mModule->GetPrimitiveStructType(primType->mTypeDef->mTypeCode); -// } - if (genericArg == NULL) goto NoMatch; - - //SetAndRestoreValue ignoreError(mModule->mIgnoreErrors, true); - if (!mModule->CheckGenericConstraints(BfGenericParamSource(methodInstance), genericArg, NULL, genericParams[checkGenericIdx], genericArgumentsSubstitute, NULL)) - { - goto NoMatch; - } + + if (!mModule->CheckGenericConstraints(BfGenericParamSource(methodInstance), genericArg, NULL, genericParams[checkGenericIdx], genericArgumentsSubstitute, NULL)) + goto NoMatch; } } @@ -1894,7 +1885,7 @@ bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue targe } void BfMethodMatcher::TryDevirtualizeCall(BfTypedValue target, BfTypedValue* origTarget, BfTypedValue* staticResult) -{ +{ if ((mBestMethodDef == NULL) || (target.mType == NULL)) return; @@ -1904,6 +1895,11 @@ 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); @@ -5680,10 +5676,22 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu { // Allow a base call to be defined } + else if ((methodInstance->IsSpecializedGenericMethod()) && (origTarget) && (!origTarget.mType->IsInterface())) + { + if (!mModule->mBfIRBuilder->mIgnoreWrites) + mModule->AssertErrorState(); + } else if ((!mModule->mCurMethodInstance->mIsUnspecialized)) { // Compiler error? - mModule->Fail(StrFormat("Unable to dynamically dispatch '%s'", mModule->MethodToString(methodInstance).c_str()), targetSrc); + + String errorString = "Unable to dynamically dispatch '%s'"; + if (methodInstance->IsSpecializedGenericMethod()) + errorString = "Unable to dynamically dispatch '%s' because generic methods can only be directly dispatched"; + if (methodInstance->mReturnType->IsConcreteInterfaceType()) + errorString = "Unable to dynamically dispatch '%s' because the concrete return type is unknown"; + + mModule->Fail(StrFormat(errorString.c_str(), mModule->MethodToString(methodInstance).c_str()), targetSrc); } //BF_ASSERT(mModule->mCurMethodInstance->mIsUnspecialized); @@ -6599,7 +6607,12 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp if (lookupTypeInst != NULL) methodMatcher.CheckType(lookupTypeInst, target, true); } - + + if ((methodMatcher.mBestMethodDef != NULL) && (methodMatcher.mBestMethodDef->mName == "Zorf")) + { + NOP; + } + BfTypedValue staticResult; methodMatcher.TryDevirtualizeCall(target, &origTarget, &staticResult); if (staticResult) @@ -7055,6 +7068,11 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp else MakeBaseConcrete(target); + if ((moduleMethodInstance.mMethodInstance->mMethodDef->mName == "GetVal") && (targetTypeInst != NULL) && (mModule->mCurTypeInstance->mTypeDef->mName->ToString() == "ClassD")) + { + NOP; + } + BfTypedValue callTarget; if (isSkipCall) { @@ -7070,6 +7088,13 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp } else callTarget = target; + + if ((callTarget) && (moduleMethodInstance.mMethodInstance->GetOwner()->IsInterface())) + { + auto wantThis = moduleMethodInstance.mMethodInstance->GetParamType(-1); + if ((callTarget.mType != wantThis) && (wantThis->IsInterface())) + callTarget = mModule->Cast(targetSrc, callTarget, wantThis, BfCastFlags_Explicit); + } } else if (target) { @@ -17776,7 +17801,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod // If one of these is a constant that can be converted into a smaller type, then do that if (rightValue.mValue.IsConst()) { - if (mModule->CanCast(rightValue, leftValue.mType)) + if (mModule->CanCast(rightValue, leftValue.mType, BfCastFlags_NoBox)) { resultType = leftValue.mType; handled = true; @@ -18392,7 +18417,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod } else { - auto convertedValue = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType); + auto convertedValue = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType, BfCastFlags_NoBox); if (!convertedValue) return; if (binaryOp == BfBinaryOp_Equality) diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index ec5fbb16..b1aeb743 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -2542,7 +2542,6 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecializedVariation)) return NULL; // Ignore errors on unspecialized variations, they are always dups - if (!mHadBuildError) mHadBuildError = true; if (mParentModule != NULL) @@ -6862,6 +6861,8 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS BfType* convCheckConstraint = genericParamInst->mTypeConstraint; if ((convCheckConstraint->IsUnspecializedType()) && (methodGenericArgs != NULL)) convCheckConstraint = ResolveGenericType(convCheckConstraint, NULL, methodGenericArgs); + if (convCheckConstraint == NULL) + return false; if ((checkArgType->IsMethodRef()) && (convCheckConstraint->IsDelegate())) { auto methodRefType = (BfMethodRefType*)checkArgType; @@ -6933,6 +6934,8 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS BfType* convCheckConstraint = checkConstraint; if (convCheckConstraint->IsUnspecializedType()) convCheckConstraint = ResolveGenericType(convCheckConstraint, NULL, methodGenericArgs); + if (convCheckConstraint == NULL) + return false; BfTypeInstance* typeConstraintInst = convCheckConstraint->ToTypeInstance(); @@ -10994,7 +10997,7 @@ bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstan else if (methodA->mMethodDef->mName != methodB->mMethodDef->mName) return false; if (methodA->mMethodDef->mCheckedKind != methodB->mMethodDef->mCheckedKind) - return false; + return false; if (methodA->mMethodDef->mMethodType == BfMethodType_Ctor) { @@ -11059,6 +11062,17 @@ bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstan return true; } +bool BfModule::StrictCompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB) +{ + if (!CompareMethodSignatures(methodA, methodB)) + return false; + if (methodA->mReturnType != methodB->mReturnType) + return false; + if (methodA->mMethodDef->mIsStatic != methodB->mMethodDef->mIsStatic) + return false; + return true; +} + bool BfModule::IsCompatibleInterfaceMethod(BfMethodInstance* iMethodInst, BfMethodInstance* methodInstance) { if (iMethodInst->mMethodDef->mName != methodInstance->mMethodDef->mName) @@ -13239,7 +13253,7 @@ void BfModule::EmitDefaultReturn() { if (mCurMethodInstance->mReturnType->IsVoid()) mBfIRBuilder->CreateRetVoid(); - else + else if (!mCurMethodInstance->HasStructRet()) mBfIRBuilder->CreateRet(GetDefaultValue(mCurMethodInstance->mReturnType)); } @@ -17564,8 +17578,8 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) } else { - // During autocomplete, the actual type may not have the proper field instance - mBfIRBuilder->CreateRet(GetDefaultValue(methodInstance->mReturnType)); + CreateReturn(GetDefaultValue(mCurMethodInstance->mReturnType)); + EmitDefaultReturn(); } mCurMethodState->SetHadReturn(true); } @@ -20963,7 +20977,8 @@ bool BfModule::SlotVirtualMethod(BfMethodInstance* methodInstance, BfAmbiguityCo if (storeIFaceMethod) { - _AddVirtualDecl(iMethodInst); + if (methodInstance->GetNumGenericParams() != 0) + _AddVirtualDecl(iMethodInst); *iMethodPtr = methodInstance; } diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index e42620cb..a2901f8f 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -76,13 +76,14 @@ enum BfCastFlags BfCastFlags_Unchecked = 2, BfCastFlags_Internal = 4, BfCastFlags_SilentFail = 8, - BfCastFlags_NoBoxDtor = 0x10, - BfCastFlags_NoConversionOperator = 0x20, - BfCastFlags_FromCompiler = 0x40, // Not user specified - BfCastFlags_Force = 0x80, - BfCastFlags_PreferAddr = 0x100, - BfCastFlags_WarnOnBox = 0x200, - BfCastFlags_IsCastCheck = 0x400 + BfCastFlags_NoBox = 0x10, + BfCastFlags_NoBoxDtor = 0x20, + BfCastFlags_NoConversionOperator = 0x40, + BfCastFlags_FromCompiler = 0x80, // Not user specified + BfCastFlags_Force = 0x100, + BfCastFlags_PreferAddr = 0x200, + BfCastFlags_WarnOnBox = 0x400, + BfCastFlags_IsCastCheck = 0x800 }; enum BfCastResultFlags @@ -1778,7 +1779,8 @@ public: BfModuleMethodInstance GetInternalMethod(const StringImpl& methodName, int paramCount = -1); bool IsMethodImplementedAndReified(BfTypeInstance* typeInstance, const StringImpl& methodName, int paramCount = -1, bool checkBase = false); bool HasMixin(BfTypeInstance* typeInstance, const StringImpl& methodName, int paramCount, bool checkBase = false); - bool CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB); // Doesn't compare return types + bool CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB); // Doesn't compare return types nor static + bool StrictCompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB); // Compares return types and static bool IsCompatibleInterfaceMethod(BfMethodInstance* methodA, BfMethodInstance* methodB); void UniqueSlotVirtualMethod(BfMethodInstance* methodInstance); void CompareDeclTypes(BfTypeDef* newDeclType, BfTypeDef* prevDeclType, bool& isBetter, bool& isWorse); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 5316b565..2b7f4dde 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -4093,10 +4093,6 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) if (ifaceMethodInst == NULL) continue; - // Don't even try to match generics - if (!ifaceMethodInst->mMethodDef->mGenericParams.IsEmpty()) - continue; - auto iReturnType = ifaceMethodInst->mReturnType; if (iReturnType->IsSelf()) iReturnType = typeInstance; @@ -4117,6 +4113,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) bool hadMatch = matchedMethod != NULL; bool hadPubFailure = false; + bool hadStaticFailure = false; bool hadMutFailure = false; if (hadMatch) @@ -4126,6 +4123,12 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) hadMatch = false; hadPubFailure = true; } + + if (matchedMethod->mMethodDef->mIsStatic != ifaceMethodInst->mMethodDef->mIsStatic) + { + hadMatch = false; + hadStaticFailure = true; + } if (ifaceMethodInst->mVirtualTableIdx != -1) { @@ -4207,7 +4210,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) if (bestMethodInst->mReturnType != ifaceMethodInst->mReturnType) { - auto error = Fail(StrFormat("Default interface method '%s' cannot be used does not have the return type '%s'", + auto error = Fail(StrFormat("Default interface method '%s' cannot be used because it doesn't have the return type '%s'", MethodToString(bestMethodInst).c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), declTypeDef->mTypeDeclaration->mNameNode); if (error != NULL) { @@ -4217,7 +4220,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) } } - if ((bestMethodInst->mMethodDef->HasBody()) && (bestMethodInst->mMethodDef->mGenericParams.size() == 0) && (matchedMethod == NULL)) + if ((bestMethodInst->mMethodDef->HasBody()) && (matchedMethod == NULL)) { auto methodDef = bestMethodInst->mMethodDef; BfGetMethodInstanceFlags flags = BfGetMethodInstanceFlag_ForeignMethodDef; @@ -4250,31 +4253,52 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) } } else - { + { + String methodString; + /// + { + SetAndRestoreValue prevMethodInstance(mCurMethodInstance, ifaceMethodInst); + methodString = MethodToString(ifaceMethodInst); + } + BfTypeDeclaration* typeDecl = declTypeDef->mTypeDeclaration; - BfError* error = Fail(StrFormat("'%s' does not implement interface member '%s'", TypeToString(typeInstance).c_str(), MethodToString(ifaceMethodInst).c_str()), typeDecl->mNameNode, true); + BfError* error = Fail(StrFormat("'%s' does not implement interface member '%s'", TypeToString(typeInstance).c_str(), methodString.c_str()), typeDecl->mNameNode, true); if ((matchedMethod != NULL) && (error != NULL)) { - if (hadPubFailure) + if (hadStaticFailure) { - mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it is not public", - MethodToString(matchedMethod).c_str()), matchedMethod->mMethodDef->mReturnTypeRef); + auto staticNodeRef = matchedMethod->mMethodDef->GetRefNode(); + if (auto methodDecl = BfNodeDynCast(matchedMethod->mMethodDef->mMethodDeclaration)) + if (methodDecl->mStaticSpecifier != NULL) + staticNodeRef = methodDecl->mStaticSpecifier; + + if (matchedMethod->mMethodDef->mIsStatic) + mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it's static", + methodString.c_str()), staticNodeRef); + else + mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it's not static", + methodString.c_str()), staticNodeRef); + } + else if (hadPubFailure) + { + mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it's not public", + methodString.c_str()), matchedMethod->mMethodDef->mReturnTypeRef); } else if (ifaceMethodInst->mReturnType->IsConcreteInterfaceType()) { mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it does not have a concrete return type that implements '%s'", - MethodToString(matchedMethod).c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), matchedMethod->mMethodDef->mReturnTypeRef); + methodString.c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), matchedMethod->mMethodDef->mReturnTypeRef); } else if (hadMutFailure) { - mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it is market as 'mut' but interface method does not allow it", - MethodToString(matchedMethod).c_str()), matchedMethod->mMethodDef->GetMutNode()); + mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it's market as 'mut' but interface method does not allow it", + methodString.c_str()), matchedMethod->mMethodDef->GetMutNode()); mCompiler->mPassInstance->MoreInfo(StrFormat("Declare the interface method as 'mut' to allow matching 'mut' implementations"), ifaceMethodInst->mMethodDef->mMethodDeclaration); } else { mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it does not have the return type '%s'", - MethodToString(matchedMethod).c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), matchedMethod->mMethodDef->mReturnTypeRef); + methodString.c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), matchedMethod->mMethodDef->mReturnTypeRef); if ((ifaceMethodInst->mVirtualTableIdx != -1) && (ifaceMethodInst->mReturnType->IsInterface())) mCompiler->mPassInstance->MoreInfo("Declare the interface method as 'concrete' to allow matching concrete return values", ifaceMethodInst->mMethodDef->GetMethodDeclaration()->mVirtualSpecifier); } @@ -6882,6 +6906,10 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula BfTypeInstance* contextTypeInstance = mCurTypeInstance; BfMethodInstance* contextMethodInstance = mCurMethodInstance; + + if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsForeignMethodDef)) + contextTypeInstance = mCurMethodInstance->mMethodInfoEx->mForeignType; + if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL)) { contextTypeInstance = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner(); @@ -9698,7 +9726,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp } } - if (mayBeBox) + if ((mayBeBox) && ((castFlags & BfCastFlags_NoBox) == 0)) { BfScopeData* scopeData = NULL; if (mCurMethodState != NULL) diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 9b62c1ef..764c9845 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -4660,6 +4660,11 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefF if (tokenNode->GetToken() == BfToken_RParen) { + if ((fieldTypes.size() == 1) && ((createTypeRefFlags & CreateTypeRefFlags_AllowSingleMemberTuple) == 0)) + { + Fail("Tuple types must contain more than one member", tokenNode); + } + MEMBER_SET(tupleTypeRef, mCloseParen, tokenNode); //return tupleTypeRef; firstNode = tupleTypeRef; @@ -5733,7 +5738,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, int depth) tokenNode = ExpectTokenAfter(enumEntry, BfToken_Comma, BfToken_AssignEquals, BfToken_LParen, BfToken_Semicolon); if ((tokenNode != NULL) && (tokenNode->GetToken() == BfToken_LParen)) { - auto typeRef = CreateTypeRef(tokenNode); + auto typeRef = CreateTypeRef(tokenNode, CreateTypeRefFlags_AllowSingleMemberTuple); tokenNode = NULL; auto tupleType = BfNodeDynCast(typeRef); if (tupleType != NULL) @@ -9202,7 +9207,10 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration( if (opConstraint->mOpToken == NULL) { if (opToken == NULL) + { + Fail("Conversion operators require either 'implicit' or 'explicit' qualifiers", opConstraint->mOperatorToken); break; + } MEMBER_SET(opConstraint, mOpToken, opToken); mVisitorPos.MoveNext(); } diff --git a/IDEHelper/Compiler/BfReducer.h b/IDEHelper/Compiler/BfReducer.h index 1feb96b7..4995caea 100644 --- a/IDEHelper/Compiler/BfReducer.h +++ b/IDEHelper/Compiler/BfReducer.h @@ -44,7 +44,8 @@ public: { CreateTypeRefFlags_None, CreateTypeRefFlags_NoParseArrayBrackets = 1, - CreateTypeRefFlags_SafeGenericParse = 2 + CreateTypeRefFlags_SafeGenericParse = 2, + CreateTypeRefFlags_AllowSingleMemberTuple = 4 }; struct BfVisitorPos diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index ff2be436..363838d5 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -192,7 +192,7 @@ BfMethodInstance* BfNonGenericMethodRef::operator->() const } BfNonGenericMethodRef& BfNonGenericMethodRef::operator=(BfMethodInstance* methodInstance) -{ +{ if (methodInstance == NULL) { mTypeInstance = NULL; @@ -202,7 +202,8 @@ BfNonGenericMethodRef& BfNonGenericMethodRef::operator=(BfMethodInstance* method { mTypeInstance = methodInstance->mMethodInstanceGroup->mOwner; mMethodNum = methodInstance->mMethodInstanceGroup->mMethodIdx; - BF_ASSERT(methodInstance->GetNumGenericArguments() == 0); + BF_ASSERT((methodInstance->GetNumGenericArguments() == 0) || + ((methodInstance->mIsUnspecialized) && (!methodInstance->mIsUnspecializedVariation))); mSignatureHash = (int)mTypeInstance->mTypeDef->mSignatureHash; } return *this; diff --git a/IDEHelper/Compiler/BfSystem.cpp b/IDEHelper/Compiler/BfSystem.cpp index 0de1a767..e0630628 100644 --- a/IDEHelper/Compiler/BfSystem.cpp +++ b/IDEHelper/Compiler/BfSystem.cpp @@ -1254,11 +1254,11 @@ void BfPassInstance::MessageAt(const StringImpl& msgPrefix, const StringImpl& er if (isFullUnderline) { - isFullUnderline = true; - for (int i = srcIdx; i < srcIdx + srcLen; VisibleAdvance(bfParser->mSrc, bfParser->mSrcLength, i)) + isFullUnderline = false; + for (int i = srcIdx; i <= srcIdx + srcLen; VisibleAdvance(bfParser->mSrc, bfParser->mSrcLength, i)) { char c = bfParser->mSrc[i]; - if (c == '\n') + if ((c == '\n') || (c == '\0')) { isFullUnderline = true; break; diff --git a/IDEHelper/Tests/src/Interfaces.bf b/IDEHelper/Tests/src/Interfaces.bf index 6a0340a0..c723da57 100644 --- a/IDEHelper/Tests/src/Interfaces.bf +++ b/IDEHelper/Tests/src/Interfaces.bf @@ -123,5 +123,115 @@ namespace Tests Test.Assert(UseIA2(cc) == 70); Test.Assert(UseIA2(cca) == 70); } + + //// + + interface IFaceD + { + T GetVal(); + + T Add(T lhs, T2 rhs) where T : operator T + T2 + { + return lhs + rhs; + } + + static T SMethod(T val); + + static T SMethod2(T val) + { + return val; + } + } + + extension IFaceD + { + T GetVal2() + { + return GetVal(); + } + } + + class ClassD : IFaceD + { + public int16 GetVal() + { + return 123; + } + + public int16 Add(int16 lhs, T2 rhs) where int16 : operator int16 + T2 + { + return lhs + rhs + 1000; + } + + public static int16 SMethod(int16 val) + { + return val + 2000; + } + } + + class ClassE : IFaceD + { + public int16 GetVal() + { + return 234; + } + + public static int16 SMethod(int16 val) + { + return val + 3000; + } + + public static int16 SMethod2(int16 val) + { + return val + 4000; + } + } + + public static int IDAdd(T val) where T : IFaceD + { + return val.Add((int16)23, (int8)100); + } + + public static T2 SGet(T val) where T : IFaceD + { + return T.SMethod(val.GetVal()); + } + + public static T2 SGet2(T val) where T : IFaceD + { + return T.SMethod2(val.GetVal()); + } + + [Test] + public static void TestDefaults() + { + ClassD cd = scope .(); + IFaceD ifd = cd; + + int v = ifd.GetVal(); + + Test.Assert(v == 123); + v = ifd.GetVal2(); + Test.Assert(v == 123); + v = IDAdd(cd); + Test.Assert(v == 1123); + v = SGet(cd); + Test.Assert(v == 2123); + v = SGet2(cd); + Test.Assert(v == 123); + + ClassE ce = scope .(); + ifd = ce; + v = ifd.GetVal(); + Test.Assert(v == 234); + v = ifd.GetVal2(); + Test.Assert(v == 234); + v = IDAdd(ce); + Test.Assert(v == 123); + v = SGet(ce); + Test.Assert(v == 3234); + v = SGet2(ce); + Test.Assert(v == 4234); + } } } diff --git a/IDEHelper/Tests/src/Operators.bf b/IDEHelper/Tests/src/Operators.bf index 3b5d0ff2..acc65ec1 100644 --- a/IDEHelper/Tests/src/Operators.bf +++ b/IDEHelper/Tests/src/Operators.bf @@ -270,5 +270,17 @@ namespace Tests TestDefaults(); } + + public static TTo Convert(TFrom val) where TTo : operator explicit TFrom + { + return (TTo)val; + } + + [Test] + public static void TestConversion() + { + int a = 123; + float f = Convert(a); + } } }