1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

Delegate comparison expansion, hashable, == operator

This commit is contained in:
Brian Fiete 2022-02-15 09:31:23 -05:00
parent 9dcafb7db8
commit a3a8bfa40c
8 changed files with 157 additions and 18 deletions

View file

@ -1,17 +1,24 @@
namespace System namespace System
{ {
class Delegate class Delegate : IHashable
{ {
void* mFuncPtr; void* mFuncPtr;
void* mTarget; void* mTarget;
public static bool Equals(Delegate a, Delegate b) public static bool Equals(Delegate a, Delegate b)
{ {
if ((Object)a == (Object)b) if (a === null)
return b === null;
return a.Equals(b);
}
public virtual bool Equals(Delegate val)
{
if (this === val)
return true; return true;
if ((Object)a == null || (Object)b == null) if (val == null)
return false; return false;
return (a.mFuncPtr == b.mFuncPtr) && (a.mTarget == b.mTarget); return (mFuncPtr == val.mFuncPtr) && (mTarget == val.mTarget);
} }
public Result<void*> GetFuncPtr() public Result<void*> GetFuncPtr()
@ -37,6 +44,19 @@ namespace System
// Note- this is safe even if mTarget is not an object, because the GC does object address validation // Note- this is safe even if mTarget is not an object, because the GC does object address validation
GC.Mark(Internal.UnsafeCastToObject(mTarget)); GC.Mark(Internal.UnsafeCastToObject(mTarget));
} }
public int GetHashCode()
{
return (int)mFuncPtr;
}
[Commutable]
public static bool operator==(Delegate a, Delegate b)
{
if (a === null)
return b === null;
return a.Equals(b);
}
} }
delegate void Action(); delegate void Action();

View file

@ -418,7 +418,7 @@ namespace System
{ {
using (sMonitor.Val.Enter()) using (sMonitor.Val.Enter())
{ {
if (sErrorHandlers.Remove(handler)) if (sErrorHandlers.RemoveStrict(handler))
return .Ok; return .Ok;
} }
return .Err; return .Err;

View file

@ -11881,7 +11881,7 @@ namespace IDE
{ {
defer defer
{ {
if (mPendingDebugExprHandler != pendingHandler) if (mPendingDebugExprHandler !== pendingHandler)
delete pendingHandler; delete pendingHandler;
} }

View file

@ -38,7 +38,7 @@ public:
static BfMethodDef* AddMethod(BfTypeDef* typeDef, BfMethodType methodType, BfProtection protection, bool isStatic, const StringImpl& name, bool addedAfterEmit = false); static BfMethodDef* AddMethod(BfTypeDef* typeDef, BfMethodType methodType, BfProtection protection, bool isStatic, const StringImpl& name, bool addedAfterEmit = false);
static BfMethodDef* AddDtor(BfTypeDef* typeDef); static BfMethodDef* AddDtor(BfTypeDef* typeDef);
static void AddDynamicCastMethods(BfTypeDef* typeDef); static void AddDynamicCastMethods(BfTypeDef* typeDef);
void AddParam(BfMethodDef* methodDef, BfTypeReference* typeRef, const StringImpl& paramName); static void AddParam(BfMethodDef* methodDef, BfTypeReference* typeRef, const StringImpl& paramName);
BfTypeDef* ComparePrevTypeDef(BfTypeDef* prevTypeDef, BfTypeDef* checkTypeDef); BfTypeDef* ComparePrevTypeDef(BfTypeDef* prevTypeDef, BfTypeDef* checkTypeDef);
void FinishTypeDef(bool wantsToString); void FinishTypeDef(bool wantsToString);
void ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef); void ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef);

View file

@ -20,6 +20,7 @@
#include "BfVarDeclChecker.h" #include "BfVarDeclChecker.h"
#include "BfFixits.h" #include "BfFixits.h"
#include "CeMachine.h" #include "CeMachine.h"
#include "BfDefBuilder.h"
#pragma warning(pop) #pragma warning(pop)
#pragma warning(disable:4996) #pragma warning(disable:4996)
@ -13551,26 +13552,24 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
BfTypeInstance* useTypeInstance = delegateTypeInstance; BfTypeInstance* useTypeInstance = delegateTypeInstance;
BfClosureType* closureTypeInst = NULL; BfClosureType* closureTypeInst = NULL;
if ((capturedEntries.size() != 0) || (lambdaBindExpr->mDtor != NULL) || (copyOuterCaptures))
// If we are allowing hot swapping we may add a capture later. We also need an equal method that ignores 'target' even when we're capturing ourself
if ((capturedEntries.size() != 0) || (lambdaBindExpr->mDtor != NULL) || (copyOuterCaptures) || (mModule->mCompiler->mOptions.mAllowHotSwapping) || (closureState.mCapturedDelegateSelf))
{ {
hashCtx.MixinStr(curProject->mName); hashCtx.MixinStr(curProject->mName);
if (copyOuterCaptures) if (copyOuterCaptures)
{ {
// String typeName = mModule->DoTypeToString(outerClosure, BfTypeNameFlag_DisambiguateDups);
// hashCtx.MixinStr(typeName);
hashCtx.Mixin(outerClosure->mTypeId); hashCtx.Mixin(outerClosure->mTypeId);
} }
for (auto& capturedEntry : capturedEntries) for (auto& capturedEntry : capturedEntries)
{ {
// String typeName = mModule->DoTypeToString(capturedEntry.mType, BfTypeNameFlag_DisambiguateDups);
// hashCtx.MixinStr(typeName);
hashCtx.Mixin(capturedEntry.mType->mTypeId); hashCtx.Mixin(capturedEntry.mType->mTypeId);
hashCtx.MixinStr(capturedEntry.mName); hashCtx.MixinStr(capturedEntry.mName);
hashCtx.Mixin(capturedEntry.mExplicitlyByReference); hashCtx.Mixin(capturedEntry.mExplicitlyByReference);
} }
if (lambdaBindExpr->mDtor != NULL) if (lambdaBindExpr->mDtor != NULL)
{ {
// Has DTOR thunk // Has DTOR thunk
@ -13590,6 +13589,16 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
closureTypeInst->Init(curProject); closureTypeInst->Init(curProject);
closureTypeInst->mTypeDef->mProject = curProject; closureTypeInst->mTypeDef->mProject = curProject;
auto delegateDirectTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeDefReference>();
delegateDirectTypeRef->Init(mModule->mCompiler->mDelegateTypeDef);
closureTypeInst->mDirectAllocNodes.push_back(delegateDirectTypeRef);
BfMethodDef* methodDef = BfDefBuilder::AddMethod(closureTypeInst->mTypeDef, BfMethodType_Normal, BfProtection_Public, false, "Equals");
methodDef->mReturnTypeRef = mModule->mSystem->mDirectBoolTypeRef;
BfDefBuilder::AddParam(methodDef, delegateDirectTypeRef, "val");
methodDef->mIsVirtual = true;
methodDef->mIsOverride = true;
if (copyOuterCaptures) if (copyOuterCaptures)
{ {
for (auto& fieldInstance : outerClosure->mFieldInstances) for (auto& fieldInstance : outerClosure->mFieldInstances)
@ -13627,9 +13636,8 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
mModule->mBfIRBuilder->PopulateType(useTypeInstance); mModule->mBfIRBuilder->PopulateType(useTypeInstance);
mModule->PopulateType(useTypeInstance); mModule->PopulateType(useTypeInstance);
// If we are allowing hot swapping, we need to always mangle the name to non-static because if we add a capture
// later then we need to have the mangled names match methodDef->mIsStatic = closureTypeInst == NULL;
methodDef->mIsStatic = (closureTypeInst == NULL) && (!mModule->mCompiler->mOptions.mAllowHotSwapping) && (!closureState.mCapturedDelegateSelf);
SizedArray<BfIRType, 8> origParamTypes; SizedArray<BfIRType, 8> origParamTypes;
BfIRType origReturnType; BfIRType origReturnType;

View file

@ -3294,7 +3294,7 @@ void BfModule::CheckErrorAttributes(BfTypeInstance* typeInstance, BfMethodInstan
BfIRConstHolder* constHolder = typeInstance->mConstHolder; BfIRConstHolder* constHolder = typeInstance->mConstHolder;
auto customAttribute = customAttributes->Get(mCompiler->mObsoleteAttributeTypeDef); auto customAttribute = customAttributes->Get(mCompiler->mObsoleteAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty())) if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()) && (targetSrc != NULL))
{ {
String err; String err;
if (methodInstance != NULL) if (methodInstance != NULL)
@ -3349,7 +3349,7 @@ void BfModule::CheckErrorAttributes(BfTypeInstance* typeInstance, BfMethodInstan
} }
customAttribute = customAttributes->Get(mCompiler->mWarnAttributeTypeDef); customAttribute = customAttributes->Get(mCompiler->mWarnAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty())) if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()) && (targetSrc != NULL))
{ {
String err; String err;
if (methodInstance != NULL) if (methodInstance != NULL)
@ -4716,6 +4716,87 @@ void BfModule::CreateFakeCallerMethod(const String& funcName)
mBfIRBuilder->CreateRetVoid(); mBfIRBuilder->CreateRetVoid();
} }
void BfModule::CreateDelegateEqualsMethod()
{
if (mBfIRBuilder->mIgnoreWrites)
return;
auto refNode = mCurTypeInstance->mTypeDef->GetRefNode();
if (refNode == NULL)
refNode = mCompiler->mValueTypeTypeDef->GetRefNode();
UpdateSrcPos(refNode);
SetIllegalSrcPos();
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
auto resultVal = CreateAlloca(boolType);
mBfIRBuilder->CreateStore(GetConstValue(0, boolType), resultVal);
auto exitBB = mBfIRBuilder->CreateBlock("exit");
auto delegateType = ResolveTypeDef(mCompiler->mDelegateTypeDef)->ToTypeInstance();
mBfIRBuilder->PopulateType(delegateType);
BfExprEvaluator exprEvaluator(this);
BfTypedValue leftTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[0]);
BfTypedValue lhsDelegate = BfTypedValue(mBfIRBuilder->CreateBitCast(leftTypedVal.mValue, mBfIRBuilder->MapType(delegateType)), delegateType);
BfTypedValue rhsDelegate = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
rhsDelegate = LoadValue(rhsDelegate);
BfTypedValue rightTypedVal = BfTypedValue(mBfIRBuilder->CreateBitCast(rhsDelegate.mValue, mBfIRBuilder->MapType(mCurTypeInstance)), mCurTypeInstance);
auto& targetFieldInstance = delegateType->mFieldInstances[0];
BfTypedValue leftValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(lhsDelegate.mValue, 0, targetFieldInstance.mDataIdx), targetFieldInstance.mResolvedType, true);
BfTypedValue rightValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(rhsDelegate.mValue, 0, targetFieldInstance.mDataIdx), targetFieldInstance.mResolvedType, true);
leftValue = LoadValue(leftValue);
rightValue = LoadValue(rightValue);
EmitEquals(leftValue, rightValue, exitBB, false);
bool hadComparison = false;
for (auto& fieldRef : mCurTypeInstance->mFieldInstances)
{
BfFieldInstance* fieldInstance = &fieldRef;
if (fieldInstance->mDataOffset == -1)
continue;
auto fieldType = fieldInstance->mResolvedType;
if (fieldType->IsValuelessType())
continue;
if (fieldType->IsVar())
continue;
if (fieldType->IsMethodRef())
continue;
if (fieldType->IsRef())
fieldType = CreatePointerType(fieldType->GetUnderlyingType());
BfTypedValue leftValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(leftTypedVal.mValue, 0, fieldInstance->mDataIdx), fieldType, true);
BfTypedValue rightValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(rightTypedVal.mValue, 0, fieldInstance->mDataIdx), fieldType, true);
if (!fieldInstance->mResolvedType->IsComposite())
{
leftValue = LoadValue(leftValue);
rightValue = LoadValue(rightValue);
}
EmitEquals(leftValue, rightValue, exitBB, false);
}
mBfIRBuilder->CreateStore(GetConstValue(1, boolType), resultVal);
mBfIRBuilder->CreateBr(exitBB);
mBfIRBuilder->AddBlock(exitBB);
mBfIRBuilder->SetInsertPoint(exitBB);
auto loadedResult = mBfIRBuilder->CreateLoad(resultVal);
ClearLifetimeEnds();
mBfIRBuilder->CreateRet(loadedResult);
mCurMethodState->mHadReturn = true;
}
void BfModule::CreateValueTypeEqualsMethod(bool strictEquals) void BfModule::CreateValueTypeEqualsMethod(bool strictEquals)
{ {
if (mCurMethodInstance->mIsUnspecialized) if (mCurMethodInstance->mIsUnspecialized)
@ -20512,6 +20593,12 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
skipBody = true; skipBody = true;
skipEndChecks = true; skipEndChecks = true;
} }
else if ((methodDef->mName == BF_METHODNAME_EQUALS) && (mCurTypeInstance->IsDelegate()))
{
CreateDelegateEqualsMethod();
skipBody = true;
skipEndChecks = true;
}
else else
{ {
auto propertyDeclaration = methodDef->GetPropertyDeclaration(); auto propertyDeclaration = methodDef->GetPropertyDeclaration();

View file

@ -1937,6 +1937,7 @@ public:
void ProcessMethod_ProcessDeferredLocals(int startIdx = 0); void ProcessMethod_ProcessDeferredLocals(int startIdx = 0);
void ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup = false, bool forceIRWrites = false); void ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup = false, bool forceIRWrites = false);
void CreateDynamicCastMethod(); void CreateDynamicCastMethod();
void CreateDelegateEqualsMethod();
void CreateValueTypeEqualsMethod(bool strictEquals); void CreateValueTypeEqualsMethod(bool strictEquals);
BfIRFunction GetIntrinsic(BfMethodInstance* methodInstance, bool reportFailure = false); BfIRFunction GetIntrinsic(BfMethodInstance* methodInstance, bool reportFailure = false);
BfIRFunction GetBuiltInFunc(BfBuiltInFuncType funcType); BfIRFunction GetBuiltInFunc(BfBuiltInFuncType funcType);

View file

@ -182,6 +182,29 @@ namespace Tests
delegate Vector3f() vecDlg = scope => GetVector3f; delegate Vector3f() vecDlg = scope => GetVector3f;
Test.Assert(vecDlg().mX == 101); Test.Assert(vecDlg().mX == 101);
int allocCount = 0;
Action act = null;
for (int i = 0; i < 10; i++)
{
int j = i / 3;
Action newAct = scope:: () =>
{
Console.WriteLine($"act {j}");
};
if (act == null || act != newAct)
{
act = newAct;
allocCount++;
}
else
{
Test.Assert(act.GetHashCode() == newAct.GetHashCode());
}
}
Test.Assert(allocCount == 4);
} }
public static void Modify(ref int a, ref Splattable b) public static void Modify(ref int a, ref Splattable b)