mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
Delegate comparison expansion, hashable, == operator
This commit is contained in:
parent
9dcafb7db8
commit
a3a8bfa40c
8 changed files with 157 additions and 18 deletions
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -11881,7 +11881,7 @@ namespace IDE
|
||||||
{
|
{
|
||||||
defer
|
defer
|
||||||
{
|
{
|
||||||
if (mPendingDebugExprHandler != pendingHandler)
|
if (mPendingDebugExprHandler !== pendingHandler)
|
||||||
delete pendingHandler;
|
delete pendingHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,21 +13552,19 @@ 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);
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue