diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 30756b8f..f1bd86a8 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -738,6 +738,8 @@ public: break; } } + + bool CanModify() const; }; #define BF_AST_TYPE(name, TBase) \ diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 9ea883cf..f9aab4f9 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -1015,6 +1015,7 @@ 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()); + RETURN_BETTER_OR_WORSE(newMethodDef->mIsMutating, prevMethodDef->mIsMutating); RETURN_RESULTS; } @@ -1437,6 +1438,13 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst else if (needInferGenericParams) genericArgumentsSubstitute = &mCheckMethodGenericArguments; + if ((checkMethod->mIsMutating) && (targetTypeInstance != NULL) && (targetTypeInstance->IsValueType()) && + ((mTarget.IsReadOnly()) || (!mTarget.IsAddr())) && + (!targetTypeInstance->IsValuelessType())) + { + goto NoMatch; + } + if (mSkipImplicitParams) { //paramOfs = methodInstance->GetImplicitParamCount(); @@ -4199,8 +4207,8 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar if (auto propertyDeclaration = BfNodeDynCast(mPropDef->mFieldDeclaration)) { if (curCheckType->mTypeDef->HasAutoProperty(propertyDeclaration)) - { - bool hasSetter = GetPropertyMethodDef(mPropDef, BfMethodType_PropertySetter, BfCheckedKind_NotSet) != NULL; + { + bool hasSetter = GetPropertyMethodDef(mPropDef, BfMethodType_PropertySetter, BfCheckedKind_NotSet, mPropTarget) != NULL; auto autoFieldName = curCheckType->mTypeDef->GetAutoPropertyName(propertyDeclaration); auto result = LookupField(targetSrc, target, autoFieldName, BfLookupFieldFlag_IgnoreProtection); if (result) @@ -14845,14 +14853,54 @@ void BfExprEvaluator::Visit(BfInvocationExpression* invocationExpr) mResult = cascadeValue; } -BfMethodDef* BfExprEvaluator::GetPropertyMethodDef(BfPropertyDef* propDef, BfMethodType methodType, BfCheckedKind checkedKind) +BfMethodDef* BfExprEvaluator::GetPropertyMethodDef(BfPropertyDef* propDef, BfMethodType methodType, BfCheckedKind checkedKind, BfTypedValue propTarget) { + bool allowMut = (propTarget) && (!propTarget.mType->IsValueType() || propTarget.CanModify()); + + int bestPri = -1000; BfMethodDef* matchedMethod = NULL; + + for (auto methodDef : propDef->mMethods) + { + if (methodDef->mMethodType != methodType) + continue; + + int curPri = 0; + + if (methodDef->mCheckedKind == checkedKind) + { + curPri = 5; + } + else if ((checkedKind == BfCheckedKind_NotSet) && (methodDef->mCheckedKind == mModule->GetDefaultCheckedKind())) + curPri = 3; + else + curPri = 1; + + if (methodDef->mIsMutating) + { + if (allowMut) + curPri++; + else + curPri -= 10; + } + + if (curPri > bestPri) + { + bestPri = curPri; + matchedMethod = methodDef; + } + } + + return matchedMethod; + + /*BfMethodDef* matchedMethod = NULL; BfMethodDef* backupMethod = NULL; for (auto methodDef : propDef->mMethods) { if (methodDef->mMethodType != methodType) continue; + + if (methodDef->mCheckedKind == checkedKind) { matchedMethod = methodDef; @@ -14865,7 +14913,7 @@ BfMethodDef* BfExprEvaluator::GetPropertyMethodDef(BfPropertyDef* propDef, BfMet } if (matchedMethod == NULL) matchedMethod = backupMethod; - return matchedMethod; + return matchedMethod;*/ } BfModuleMethodInstance BfExprEvaluator::GetPropertyMethodInstance(BfMethodDef* methodDef) @@ -15084,7 +15132,7 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp SetAndRestoreValue prevFunctionBindResult(mFunctionBindResult, NULL); SetAndRestoreValue prevDeferCallRef(mDeferCallRef, NULL); - BfMethodDef* matchedMethod = GetPropertyMethodDef(mPropDef, BfMethodType_PropertyGetter, mPropCheckedKind); + BfMethodDef* matchedMethod = GetPropertyMethodDef(mPropDef, BfMethodType_PropertyGetter, mPropCheckedKind, mPropTarget); if (matchedMethod == NULL) { mModule->Fail("Property has no getter", mPropSrc); @@ -15404,8 +15452,7 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod return true; } - bool canModify = (((typedVal.IsAddr()) || (typedVal.mType->IsValuelessType())) && - (!typedVal.IsReadOnly())); + bool canModify = typedVal.CanModify(); if (localVar != NULL) { @@ -15776,8 +15823,8 @@ void BfExprEvaluator::PopulateDeferrredTupleAssignData(BfTupleExpression* tupleE BfPointerType* pointerType = (BfPointerType*)exprEvaluator->mPropTarget.mType; propTypeInst = pointerType->mElementType->ToTypeInstance(); } - - auto setMethod = GetPropertyMethodDef(exprEvaluator->mPropDef, BfMethodType_PropertySetter, mPropCheckedKind); + + auto setMethod = GetPropertyMethodDef(exprEvaluator->mPropDef, BfMethodType_PropertySetter, mPropCheckedKind, mPropTarget); if (setMethod != NULL) { auto methodInstance = mModule->GetMethodInstance(propTypeInst, setMethod, BfTypeVector()); @@ -15785,7 +15832,7 @@ void BfExprEvaluator::PopulateDeferrredTupleAssignData(BfTupleExpression* tupleE } else { - auto getMethod = GetPropertyMethodDef(exprEvaluator->mPropDef, BfMethodType_PropertyGetter, mPropCheckedKind); + auto getMethod = GetPropertyMethodDef(exprEvaluator->mPropDef, BfMethodType_PropertyGetter, mPropCheckedKind, mPropTarget); if (getMethod != NULL) { auto methodInstance = mModule->GetMethodInstance(propTypeInst, getMethod, BfTypeVector()); @@ -15948,7 +15995,7 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool auto propDef = mPropDef; auto propTarget = mPropTarget; - auto setMethod = GetPropertyMethodDef(mPropDef, BfMethodType_PropertySetter, mPropCheckedKind); + auto setMethod = GetPropertyMethodDef(mPropDef, BfMethodType_PropertySetter, mPropCheckedKind, mPropTarget); if (setMethod == NULL) { // Allow for a ref return on the getter to be used if a setter is not available @@ -17306,6 +17353,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) continue; methodMatcher.mCheckedKind = checkedKind; + methodMatcher.mTarget = target; methodMatcher.CheckMethod(startCheckTypeInst, curCheckType, checkMethod, false); if ((methodMatcher.mBestMethodDef == checkMethod) || @@ -18191,7 +18239,7 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, if (writeToProp) { - auto setMethod = GetPropertyMethodDef(propDef, BfMethodType_PropertySetter, mPropCheckedKind); + auto setMethod = GetPropertyMethodDef(propDef, BfMethodType_PropertySetter, mPropCheckedKind, mPropTarget); if (setMethod == NULL) { mModule->Fail("Property has no setter", propSrc); diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 637e9ed4..29af3a4a 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -348,7 +348,7 @@ public: void ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveArgFlags flags = BfResolveArgFlag_None); BfAllocTarget ResolveAllocTarget(BfAstNode* newNode, BfTokenNode*& newToken, BfCustomAttributes** outCustomAttributes = NULL); BfTypedValue ResolveArgValue(BfResolvedArg& resolvedArg, BfType* wantType, BfTypedValue* receivingValue = NULL, BfParamKind paramKind = BfParamKind_Normal); - BfMethodDef* GetPropertyMethodDef(BfPropertyDef* propDef, BfMethodType methodType, BfCheckedKind checkedKind); + BfMethodDef* GetPropertyMethodDef(BfPropertyDef* propDef, BfMethodType methodType, BfCheckedKind checkedKind, BfTypedValue propTarget); BfModuleMethodInstance GetPropertyMethodInstance(BfMethodDef* methodDef); void CheckPropFail(BfMethodDef* propMethodDef, BfMethodInstance* methodInstance, bool checkProt); bool HasResult(); diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 9c26e442..6a945fc8 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -11385,7 +11385,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_Mixin) != (methodB->mMethodDef->mMethodType == BfMethodType_Mixin)) return false; @@ -20846,6 +20846,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool if (((checkMethodInstance->mChainType == BfMethodChainType_None) || (checkMethodInstance->mChainType == BfMethodChainType_ChainHead)) && (checkMethodInstance->GetExplicitInterface() == methodInstance->GetExplicitInterface()) && + (checkMethod->mIsMutating == methodDef->mIsMutating) && (CompareMethodSignatures(checkMethodInstance, mCurMethodInstance))) { bool canChain = false; diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 3ae9c40b..370386e6 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -56,6 +56,11 @@ bool BfTypedValue::IsValuelessType() const return mType->IsValuelessType(); } +bool BfTypedValue::CanModify() const +{ + return (((IsAddr()) || (mType->IsValuelessType())) && (!IsReadOnly())); +} + ////////////////////////////////////////////////////////////////////////// bool BfDependencyMap::AddUsedBy(BfType* dependentType, BfDependencyMap::DependencyFlags flags) diff --git a/IDEHelper/Tests/src/Indexers.bf b/IDEHelper/Tests/src/Indexers.bf index c8a62db1..6fa32bf7 100644 --- a/IDEHelper/Tests/src/Indexers.bf +++ b/IDEHelper/Tests/src/Indexers.bf @@ -31,6 +31,24 @@ namespace Tests } + struct StructA + { + int mA; + + public int this[int index] + { + get + { + return 1; + } + + get mut + { + return 2; + } + } + } + [Test] public static void Hey() { @@ -43,6 +61,11 @@ namespace Tests Test.Assert(value == 234); value = ca[0]; Test.Assert(value == 234); + + StructA sa = default; + let sa2 = sa; + Test.Assert(sa[0] == 2); + Test.Assert(sa2[0] == 1); } } } diff --git a/IDEHelper/Tests/src/Properties.bf b/IDEHelper/Tests/src/Properties.bf index 6d449143..a499b130 100644 --- a/IDEHelper/Tests/src/Properties.bf +++ b/IDEHelper/Tests/src/Properties.bf @@ -36,8 +36,35 @@ namespace Tests { public StructA B { get; } + public int C => 123; + [SkipCall] + public int D => 123; + + public int E + { + get + { + return 1; + } + + get mut + { + return 2; + } + } + int mZ = 9; + public int GetVal() + { + return 3; + } + + public int GetVal() mut + { + return 4; + } + public this() { B = .(); @@ -73,6 +100,15 @@ namespace Tests sa = sb.B; Test.Assert(sa.mA == 222); + StructC sc = default; + Test.Assert(sc.C == 123); + Test.Assert(sc.D == 0); + let sc2 = sc; + Test.Assert(sc.E == 2); + Test.Assert(sc.GetVal() == 4); + Test.Assert(sc2.E == 1); + Test.Assert(sc2.GetVal() == 3); + ClassB cb = scope .(); sa = cb.B; Test.Assert(sa.mA == 0);