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

Check operator overloads for assignments on properties

This commit is contained in:
Brian Fiete 2022-02-14 16:07:11 -05:00
parent c887a69e7d
commit 9872ce989b
3 changed files with 178 additions and 87 deletions

View file

@ -18607,6 +18607,69 @@ void BfExprEvaluator::DoTupleAssignment(BfAssignmentExpression* assignExpr)
mResult = rightValue;
}
BfTypedValue BfExprEvaluator::PerformAssignment_CheckOp(BfAssignmentExpression* assignExpr, bool deferBinop, BfTypedValue& leftValue, BfTypedValue& rightValue, bool& evaluatedRight)
{
BfResolvedArgs argValues;
auto checkTypeInst = leftValue.mType->ToTypeInstance();
while (checkTypeInst != NULL)
{
for (auto operatorDef : checkTypeInst->mTypeDef->mOperators)
{
if (operatorDef->mOperatorDeclaration->mAssignOp != assignExpr->mOp)
continue;
auto methodInst = mModule->GetRawMethodInstanceAtIdx(checkTypeInst, operatorDef->mIdx);
if (methodInst->GetParamCount() != 1)
continue;
auto paramType = methodInst->GetParamType(0);
if (deferBinop)
{
if (argValues.mArguments == NULL)
{
SizedArray<BfExpression*, 2> argExprs;
argExprs.push_back(assignExpr->mRight);
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
argValues.Init(&sizedArgExprs);
ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval);
}
evaluatedRight = true;
rightValue = ResolveArgValue(argValues.mResolvedArgs[0], paramType);
if (!rightValue)
continue;
}
else
{
if (!mModule->CanCast(rightValue, paramType))
continue;
}
mModule->SetElementType(assignExpr->mOpToken, BfSourceElementType_Method);
auto autoComplete = GetAutoComplete();
if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(assignExpr->mOpToken)))
{
if (operatorDef->mOperatorDeclaration != NULL)
autoComplete->SetDefinitionLocation(operatorDef->mOperatorDeclaration->mOpTypeToken);
}
auto moduleMethodInstance = mModule->GetMethodInstance(checkTypeInst, operatorDef, BfTypeVector());
BfExprEvaluator exprEvaluator(mModule);
SizedArray<BfIRValue, 1> args;
exprEvaluator.PushThis(assignExpr->mLeft, leftValue, moduleMethodInstance.mMethodInstance, args);
exprEvaluator.PushArg(rightValue, args);
exprEvaluator.CreateCall(assignExpr, moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, false, args);
return leftValue;
}
checkTypeInst = mModule->GetBaseType(checkTypeInst);
}
return BfTypedValue();
}
void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool evaluatedLeft, BfTypedValue rightValue, BfTypedValue* outCascadeValue)
{
auto binaryOp = BfAssignOpToBinaryOp(assignExpr->mOp);
@ -18718,16 +18781,39 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool
autoComplete->mResultString += mPropDef->mName;
}
bool handled = false;
BfTypedValue convVal;
if (binaryOp != BfBinaryOp_None)
{
PerformBinaryOperation(assignExpr->mLeft, assignExpr->mRight, binaryOp, assignExpr->mOpToken, BfBinOpFlag_ForceLeftType);
if (!mResult)
return;
convVal = mResult;
mResult = BfTypedValue();
if (!convVal)
BfTypedValue leftValue = mModule->CreateValueFromExpression(assignExpr->mLeft, mExpectingType, (BfEvalExprFlags)((mBfEvalExprFlags & BfEvalExprFlags_InheritFlags) | BfEvalExprFlags_NoCast | BfEvalExprFlags_AllowIntUnknown));
if (!leftValue)
return;
bool evaluatedRight = false;
auto opResult = PerformAssignment_CheckOp(assignExpr, true, leftValue, rightValue, evaluatedRight);
if (opResult)
{
mResult = opResult;
return;
}
else
{
if (evaluatedRight)
{
if (!rightValue)
return;
PerformBinaryOperation(assignExpr->mLeft, assignExpr->mRight, binaryOp, assignExpr->mOpToken, BfBinOpFlag_ForceLeftType, leftValue, rightValue);
}
else
PerformBinaryOperation(assignExpr->mLeft, assignExpr->mRight, binaryOp, assignExpr->mOpToken, BfBinOpFlag_ForceLeftType, leftValue);
if (!mResult)
return;
convVal = mResult;
mResult = BfTypedValue();
if (!convVal)
return;
}
}
else
{
@ -18755,37 +18841,40 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool
}
}
if (mPropSrc != NULL)
mModule->UpdateExprSrcPos(mPropSrc);
BfResolvedArg valueArg;
valueArg.mTypedValue = convVal;
mIndexerValues.Insert(0, valueArg);
if (!setMethod->mIsStatic)
if (!handled)
{
auto owner = methodInstance.mMethodInstance->GetOwner();
if ((mPropTarget.mType != owner) ||
((mPropTarget.mValue.IsFake()) && (!mOrigPropTarget.mValue.IsFake())))
if (mPropSrc != NULL)
mModule->UpdateExprSrcPos(mPropSrc);
BfResolvedArg valueArg;
valueArg.mTypedValue = convVal;
mIndexerValues.Insert(0, valueArg);
if (!setMethod->mIsStatic)
{
if ((mPropDefBypassVirtual) || (!mPropTarget.mType->IsInterface()))
auto owner = methodInstance.mMethodInstance->GetOwner();
if ((mPropTarget.mType != owner) ||
((mPropTarget.mValue.IsFake()) && (!mOrigPropTarget.mValue.IsFake())))
{
mPropTarget = mModule->Cast(mPropSrc, mOrigPropTarget, owner);
if (!mPropTarget)
if ((mPropDefBypassVirtual) || (!mPropTarget.mType->IsInterface()))
{
mModule->Fail("Internal property error", mPropSrc);
return;
mPropTarget = mModule->Cast(mPropSrc, mOrigPropTarget, owner);
if (!mPropTarget)
{
mModule->Fail("Internal property error", mPropSrc);
return;
}
}
}
}
}
auto callFlags = mPropDefBypassVirtual ? BfCreateCallFlags_BypassVirtual : BfCreateCallFlags_None;
mResult = CreateCall(mPropSrc, mPropTarget, mOrigPropTarget, setMethod, methodInstance, callFlags, mIndexerValues, NULL);
mPropDef = NULL;
mResult = convVal;
mIndexerValues.Clear();
return;
auto callFlags = mPropDefBypassVirtual ? BfCreateCallFlags_BypassVirtual : BfCreateCallFlags_None;
mResult = CreateCall(mPropSrc, mPropTarget, mOrigPropTarget, setMethod, methodInstance, callFlags, mIndexerValues, NULL);
mPropDef = NULL;
mResult = convVal;
mIndexerValues.Clear();
return;
}
}
}
@ -18842,65 +18931,14 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool
if ((rightValue) || (deferBinop))
{
auto checkTypeInst = leftValue.mType->ToTypeInstance();
while (checkTypeInst != NULL)
bool evaluatedRight = false;
auto opResult = PerformAssignment_CheckOp(assignExpr, deferBinop, leftValue, rightValue, evaluatedRight);
if (opResult)
{
for (auto operatorDef : checkTypeInst->mTypeDef->mOperators)
{
if (operatorDef->mOperatorDeclaration->mAssignOp != assignExpr->mOp)
continue;
auto methodInst = mModule->GetRawMethodInstanceAtIdx(checkTypeInst, operatorDef->mIdx);
if (methodInst->GetParamCount() != 1)
continue;
auto paramType = methodInst->GetParamType(0);
if (deferBinop)
{
if (argValues.mArguments == NULL)
{
SizedArray<BfExpression*, 2> argExprs;
argExprs.push_back(assignExpr->mRight);
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
argValues.Init(&sizedArgExprs);
ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval);
}
rightValue = ResolveArgValue(argValues.mResolvedArgs[0], paramType);
if (!rightValue)
continue;
}
else
{
if (!mModule->CanCast(rightValue, paramType))
continue;
}
mModule->SetElementType(assignExpr->mOpToken, BfSourceElementType_Method);
if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(assignExpr->mOpToken)))
{
if (operatorDef->mOperatorDeclaration != NULL)
autoComplete->SetDefinitionLocation(operatorDef->mOperatorDeclaration->mOpTypeToken);
}
auto moduleMethodInstance = mModule->GetMethodInstance(checkTypeInst, operatorDef, BfTypeVector());
BfExprEvaluator exprEvaluator(mModule);
SizedArray<BfIRValue, 1> args;
exprEvaluator.PushThis(assignExpr->mLeft, leftValue, moduleMethodInstance.mMethodInstance, args);
exprEvaluator.PushArg(rightValue, args);
exprEvaluator.CreateCall(assignExpr, moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, false, args);
convVal = leftValue;
handled = true;
break;
}
if (handled)
break;
checkTypeInst = mModule->GetBaseType(checkTypeInst);
handled = true;
convVal = opResult;
}
if (!handled)
else
{
auto flags = BfBinOpFlag_ForceLeftType;
if (deferBinop)

View file

@ -501,7 +501,8 @@ public:
void FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType, const StringImpl& fieldName, bool isStatic);
void PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags);
BfTypedValue PerformUnaryOperation_TryOperator(const BfTypedValue& inValue, BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags);
void PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags);
void PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags);
BfTypedValue PerformAssignment_CheckOp(BfAssignmentExpression* assignExpr, bool deferBinop, BfTypedValue& leftValue, BfTypedValue& rightValue, bool& evaluatedRight);
void PerformAssignment(BfAssignmentExpression* assignExpr, bool evaluatedLeft, BfTypedValue rightValue, BfTypedValue* outCascadeValue = NULL);
void PopulateDeferrredTupleAssignData(BfTupleExpression* tupleExr, DeferredTupleAssignData& deferredTupleAssignData);
void AssignDeferrredTupleAssignData(BfAssignmentExpression* assignExpr, DeferredTupleAssignData& deferredTupleAssignData, BfTypedValue rightValue);

View file

@ -2,6 +2,22 @@
using System;
namespace System
{
public extension Event<T>
{
public implicit void operator+=(T action) mut
{
Add(action);
}
public implicit void operator-=(T action) mut
{
Remove(action, true);
}
}
}
namespace Tests
{
class Operators
@ -457,6 +473,26 @@ namespace Tests
public struct Vector2 : this(float x, float y);
public static Event<Action> sEvent ~ _.Dispose(); // Workaround for the lack of auto-destructor in properties
public static ref Event<Action> EventProp { get => ref sEvent; set => sEvent = value; };
static int sA = 123;
public static ref int RefVal => ref sA;
public static int Val
{
get
{
return sA;
}
set
{
sA = value;
}
}
[Test]
public static void TestBasics()
{
@ -657,6 +693,22 @@ namespace Tests
StructK sk = (.)123;
uint64 sku32 = sk;
Test.Assert(sku32 == 123);
int val2 = 10;
void Set()
{
val2 += 200;
}
EventProp += new => Set;
EventProp();
Test.Assert(val2 == 210);
RefVal += 99;
Test.Assert(sA == 222);
Val += 1000;
Test.Assert(sA == 1222);
}
struct IntStruct