1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00

Fixed some comptime dependency rebuilding issues with aliases/extensions

This commit is contained in:
Brian Fiete 2022-02-16 18:28:23 -05:00
parent 1cd198cea9
commit 434a7406de
22 changed files with 394 additions and 86 deletions

View file

@ -4,28 +4,43 @@
NS_BF_BEGIN;
#define BF_BITSET_ELEM_SIZE (sizeof(uintptr)*8)
#define BF_BITSET_ELEM_BITCOUNT (sizeof(uintptr)*8)
class BitSet
{
public:
uintptr* mBits;
int mNumInts;
int mNumBits;
public:
BitSet()
{
mNumInts = 0;
mNumBits = 0;
mBits = NULL;
}
BitSet(int numBits)
{
mNumInts = 0;
mNumBits = 0;
mBits = NULL;
this->Resize(numBits);
}
BitSet(BitSet&& other)
{
mNumBits = other.mNumBits;
mBits = other.mBits;
other.mNumBits = 0;
other.mBits = NULL;
}
BitSet(const BitSet& other)
{
mNumBits = 0;
mBits = NULL;
*this = other;
}
~BitSet()
{
delete this->mBits;
@ -33,28 +48,68 @@ public:
void Resize(int numBits)
{
int numInts = (numBits + BF_BITSET_ELEM_SIZE - 1) / BF_BITSET_ELEM_SIZE;
if (numInts == mNumInts)
int numInts = (numBits + BF_BITSET_ELEM_BITCOUNT - 1) / BF_BITSET_ELEM_BITCOUNT;
int curNumInts = (mNumBits + BF_BITSET_ELEM_BITCOUNT - 1) / BF_BITSET_ELEM_BITCOUNT;
mNumBits = numBits;
if (numInts == curNumInts)
return;
this->mNumInts = numInts;
this->mNumBits = numBits;
delete this->mBits;
this->mBits = new uintptr[numInts];
memset(this->mBits, 0, numInts * sizeof(uintptr));
}
bool IsSet(int idx)
void Clear()
{
return (this->mBits[idx / BF_BITSET_ELEM_SIZE] & ((uintptr)1 << (idx % BF_BITSET_ELEM_SIZE))) != 0;
int curNumInts = (mNumBits + BF_BITSET_ELEM_BITCOUNT - 1) / BF_BITSET_ELEM_BITCOUNT;
memset(mBits, 0, curNumInts * sizeof(uintptr));
}
bool IsSet(int idx) const
{
BF_ASSERT((uintptr)idx < (uintptr)mNumBits);
return (this->mBits[idx / BF_BITSET_ELEM_BITCOUNT] & ((uintptr)1 << (idx % BF_BITSET_ELEM_BITCOUNT))) != 0;
}
void Set(int idx)
{
this->mBits[idx / BF_BITSET_ELEM_SIZE] |= ((uintptr)1 << (idx % BF_BITSET_ELEM_SIZE));
BF_ASSERT((uintptr)idx < (uintptr)mNumBits);
this->mBits[idx / BF_BITSET_ELEM_BITCOUNT] |= ((uintptr)1 << (idx % BF_BITSET_ELEM_BITCOUNT));
}
void Clear(int idx)
{
this->mBits[idx / BF_BITSET_ELEM_SIZE] &= ~((uintptr)1 << (idx % BF_BITSET_ELEM_SIZE));
BF_ASSERT((uintptr)idx < (uintptr)mNumBits);
this->mBits[idx / BF_BITSET_ELEM_BITCOUNT] &= ~((uintptr)1 << (idx % BF_BITSET_ELEM_BITCOUNT));
}
bool IsEmpty()
{
return mNumBits == 0;
}
bool operator==(const BitSet& other) const
{
int curNumInts = (mNumBits + BF_BITSET_ELEM_BITCOUNT - 1) / BF_BITSET_ELEM_BITCOUNT;
if (mNumBits != other.mNumBits)
return false;
for (int i = 0; i < curNumInts; i++)
if (mBits[i] != other.mBits[i])
return false;
return true;
}
bool operator!=(const BitSet& other) const
{
return !(*this == other);
}
BitSet& operator=(const BitSet& other)
{
Resize(other.mNumBits);
int curNumInts = (mNumBits + BF_BITSET_ELEM_BITCOUNT - 1) / BF_BITSET_ELEM_BITCOUNT;
memcpy(mBits, other.mBits, curNumInts * sizeof(uintptr));
return *this;
}
};

View file

@ -0,0 +1,5 @@
FileVersion = 1
[Project]
Name = "Bug"
StartupObject = "Bug.Program"

View file

@ -0,0 +1,6 @@
FileVersion = 1
Projects = {Bug = {Path = "."}}
[Workspace]
StartupProject = "Bug"

View file

@ -0,0 +1,15 @@
# This tests that comptime changes to generic passed extension constraints rebuilds dependencies
ShowFile("src/Program.bf")
GotoText("//End")
ToggleBreakpoint()
RunWithCompiling()
AssertEvalEquals("val", "1")
StopRunning()
ShowFile("src/Gen.bf")
ToggleCommentAt("Void")
ToggleCommentAt("String")
RunWithCompiling()
AssertEvalEquals("val", "2")
StopRunning()

View file

@ -0,0 +1,18 @@
using System;
namespace Bug
{
class Gen
{
public static Type Get()
{
//*Void
return typeof(void);
/*@*/
/*String
return typeof(String);
*/
}
}
}

View file

@ -0,0 +1,34 @@
#pragma warning disable 168
using System;
using System.Collections;
namespace Bug
{
class Zonk<T>
{
public int Call(float val)
{
return 1;
}
}
extension Zonk<T> where comptype(Gen.Get()) : String
{
public int Call(int val)
{
return 2;
}
}
class Program
{
public static int Main(String[] args)
{
Zonk<int> zk = scope .();
int val = zk.Call(1);
//End
return 0;
}
}
}

View file

@ -0,0 +1,5 @@
FileVersion = 1
[Project]
Name = "Bug"
StartupObject = "Bug.Program"

View file

@ -0,0 +1,6 @@
FileVersion = 1
Projects = {Bug = {Path = "."}}
[Workspace]
StartupProject = "Bug"

View file

@ -0,0 +1,15 @@
# This tests that comptime alias changes rebuild dependent types
ShowFile("src/Program.bf")
GotoText("//End")
ToggleBreakpoint()
RunWithCompiling()
AssertEvalEquals("val", "1")
StopRunning()
ShowFile("src/Gen.bf")
ToggleCommentAt("ClassA")
ToggleCommentAt("ClassB")
RunWithCompiling()
AssertEvalEquals("val", "2")
StopRunning()

View file

@ -0,0 +1,18 @@
using System;
namespace Bug
{
class Gen
{
public static Type Get()
{
//*ClassA
return typeof(ClassA);
/*@*/
/*ClassB
return typeof(ClassB);
*/
}
}
}

View file

@ -0,0 +1,42 @@
#pragma warning disable 168
using System;
using System.Collections;
namespace Bug
{
class ClassA
{
public int Call()
{
return 1;
}
}
class ClassB
{
public int Call()
{
return 2;
}
}
typealias Alias1 = comptype(Gen.Get());
typealias Alias2 = Alias1;
class Zonk<T> : Alias2
{
}
class Program
{
public static int Main(String[] args)
{
Zonk<int> zk = scope .();
int val = zk.Call();
//End
return 0;
}
}
}

View file

@ -7012,6 +7012,8 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
mStats.mTypesQueued += (int)mContext->mPopulateTypeWorkList.size();
mStats.mMethodsQueued += (int)mContext->mMethodWorkList.size();
while (true)
{
//
{
if (mBfObjectTypeDef != NULL)
@ -7030,6 +7032,9 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
BpLeave();
BpEnter("Compile_End");
mContext->mHasReifiedQueuedRebuildTypes = false;
//
{
BP_ZONE("ProcessingLiveness");
@ -7055,6 +7060,12 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
}
}
if (!mContext->mHasReifiedQueuedRebuildTypes)
break;
BfLogSysM("DoCompile looping over CompileReified due to mHasReifiedQueuedRebuildTypes\n");
}
ProcessPurgatory(true);
// Mark used modules

View file

@ -78,6 +78,7 @@ BfContext::BfContext(BfCompiler* compiler) :
mValueTypeDeinitSentinel = (BfMethodInstance*)1;
mCurStringObjectPoolId = 0;
mHasReifiedQueuedRebuildTypes = false;
}
void BfReportMemory();
@ -945,6 +946,9 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild
return;
}
if (typeInst->mIsReified)
mHasReifiedQueuedRebuildTypes = true;
typeInst->mRebuildFlags = (BfTypeRebuildFlags)(typeInst->mRebuildFlags & ~BfTypeRebuildFlag_AddedToWorkList);
bool addToWorkList = true;
@ -1131,8 +1135,8 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild
genericTypeInstance->mGenericTypeInfo->mGenericParams.Clear();
genericTypeInstance->mGenericTypeInfo->mValidatedGenericConstraints = false;
genericTypeInstance->mGenericTypeInfo->mHadValidateErrors = false;
delete genericTypeInstance->mGenericTypeInfo->mGenericExtensionInfo;
genericTypeInstance->mGenericTypeInfo->mGenericExtensionInfo = NULL;
if (genericTypeInstance->mGenericTypeInfo->mGenericExtensionInfo != NULL)
genericTypeInstance->mGenericTypeInfo->mGenericExtensionInfo->Clear();
genericTypeInstance->mGenericTypeInfo->mProjectsReferenced.Clear();
}
@ -1182,6 +1186,22 @@ void BfContext::RebuildDependentTypes(BfDependedType* dType)
TypeMethodSignaturesChanged(typeInst);
}
void BfContext::RebuildDependentTypes_MidCompile(BfDependedType* dType, const String& reason)
{
dType->mRebuildFlags = (BfTypeRebuildFlags)(dType->mRebuildFlags | BfTypeRebuildFlag_ChangedMidCompile);
int prevDeletedTypes = mCompiler->mStats.mTypesDeleted;
if (mCompiler->mIsResolveOnly)
mCompiler->mNeedsFullRefresh = true;
BfLogSysM("Rebuilding dependent types MidCompile Type:%p Reason:%s\n", dType, reason.c_str());
RebuildDependentTypes(dType);
if (mCompiler->mStats.mTypesDeleted != prevDeletedTypes)
{
BfLogSysM("Rebuilding dependent types MidCompile Type:%p Reason:%s - updating after deleting types\n", dType, reason.c_str());
UpdateAfterDeletingTypes();
}
}
// Dependencies cascade as such:
// DerivedFrom / StructMemberData: these change the layout of memory for the dependent classes,
// so not only do the dependent classes need to be rebuild, but any other classes relying on those derived classes
@ -1264,6 +1284,12 @@ void BfContext::TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChang
{
RebuildType(dependentType);
}
else if (((dependencyFlags & BfDependencyMap::DependencyFlag_NameReference) != 0) &&
((dType->mRebuildFlags & BfTypeRebuildFlag_ChangedMidCompile) != 0) &&
(dType->IsTypeAlias()))
{
RebuildType(dependentType);
}
}
}
else

View file

@ -397,6 +397,7 @@ public:
WorkQueue<BfTypeRefVerifyRequest> mTypeRefVerifyWorkList;
PtrWorkQueue<BfModule*> mFinishedSlotAwaitModuleWorkList;
PtrWorkQueue<BfModule*> mFinishedModuleWorkList;
bool mHasReifiedQueuedRebuildTypes;
Array<BfGenericParamType*> mGenericParamTypes[3];
@ -475,6 +476,7 @@ public:
void ValidateDependencies();
void RebuildType(BfType* type, bool deleteOnDemandTypes = true, bool rebuildModule = true, bool placeSpecializiedInPurgatory = true);
void RebuildDependentTypes(BfDependedType* dType);
void RebuildDependentTypes_MidCompile(BfDependedType* dType, const String& reason);
void TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChange);
void TypeMethodSignaturesChanged(BfTypeInstance* typeInst);
void TypeInlineMethodInternalsChanged(BfTypeInstance* typeInst);

View file

@ -1746,7 +1746,11 @@ String BfIRBuilder::ToString(BfIRValue irValue)
auto typeofConst = (BfTypeOf_WithData_Const*)constant;
return "typeof_withData " + mModule->TypeToString(typeofConst->mType);
}
else if (constant->mConstType == BfConstType_Undef)
{
auto constUndef = (BfConstantUndef*)constant;
return "undef " + ToString(constUndef->mType);
}
else
{
BF_FATAL("Unhandled");

View file

@ -1775,7 +1775,7 @@ public:
void ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* typeInst, BfCEOnCompileKind onCompileKind, bool underlyingTypeDeferred);
void DoCEEmit(BfTypeInstance* typeInstance, bool& hadNewMembers, bool underlyingTypeDeferred);
void DoCEEmit(BfMethodInstance* methodInstance);
void DoPopulateType_TypeAlias(BfTypeInstance* typeAlias);
void DoPopulateType_TypeAlias(BfTypeAliasType* typeAlias);
void DoPopulateType_InitSearches(BfTypeInstance* typeInstance);
void DoPopulateType_SetGenericDependencies(BfTypeInstance* genericTypeInstance);
void DoPopulateType_FinishEnum(BfTypeInstance* typeInstance, bool underlyingTypeDeferred, HashContext* dataMemberHashCtx, BfType* unionInnerType);

View file

@ -184,6 +184,21 @@ bool BfModule::FinishGenericParams(BfType* resolvedTypeRef)
if (!typeDef->mPartials.empty())
{
BitSet prevConstraintsPassedSet;
if (!genericTypeInst->IsUnspecializedType())
{
if (genericTypeInst->mGenericTypeInfo->mGenericExtensionInfo != NULL)
{
auto genericExtensionInfo = genericTypeInst->mGenericTypeInfo->mGenericExtensionInfo;
prevConstraintsPassedSet = genericExtensionInfo->mConstraintsPassedSet;
genericExtensionInfo->mConstraintsPassedSet.Clear();
}
}
int extensionCount = 0;
BfLogSysM("BfModule::FinishGenericParams %p\n", resolvedTypeRef);
for (auto partialTypeDef : typeDef->mPartials)
{
if (!partialTypeDef->IsExtension())
@ -222,6 +237,13 @@ bool BfModule::FinishGenericParams(BfType* resolvedTypeRef)
auto genericExEntry = BuildGenericExtensionInfo(genericTypeInst, partialTypeDef);
if (genericExEntry == NULL)
continue;
auto genericExtensionInfo = genericTypeInst->mGenericTypeInfo->mGenericExtensionInfo;
if (extensionCount == 0)
genericExtensionInfo->mConstraintsPassedSet.Resize(typeDef->mPartials.mSize);
extensionCount++;
if (!genericTypeInst->IsUnspecializedType())
{
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
@ -249,8 +271,19 @@ bool BfModule::FinishGenericParams(BfType* resolvedTypeRef)
}
}
}
if (genericExEntry->mConstraintsPassed)
genericExtensionInfo->mConstraintsPassedSet.Set(partialTypeDef->mPartialIdx);
BfLogSysM("BfModule::FinishGenericParams %p partialTypeDef:%p passed:%d\n", resolvedTypeRef, partialTypeDef, genericExEntry->mConstraintsPassed);
}
}
auto genericExtensionInfo = genericTypeInst->mGenericTypeInfo->mGenericExtensionInfo;
if ((extensionCount > 0) && (!prevConstraintsPassedSet.IsEmpty()) && (genericExtensionInfo->mConstraintsPassedSet != prevConstraintsPassedSet))
{
mContext->RebuildDependentTypes_MidCompile(genericTypeInst, "mConstraintsPassedSet changed");
}
}
else
{
@ -1224,6 +1257,7 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType
if (resolvedTypeRef->IsTypeAlias())
{
// Always populate these all the way
if (populateType != BfPopulateType_IdentityNoRemapAlias)
populateType = BfPopulateType_Data;
}
@ -2730,7 +2764,7 @@ void BfModule::DoPopulateType_SetGenericDependencies(BfTypeInstance* genericType
}
}
void BfModule::DoPopulateType_TypeAlias(BfTypeInstance* typeAlias)
void BfModule::DoPopulateType_TypeAlias(BfTypeAliasType* typeAlias)
{
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeAlias);
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, NULL);
@ -2766,6 +2800,8 @@ void BfModule::DoPopulateType_TypeAlias(BfTypeInstance* typeAlias)
aliasToType = ResolveTypeRef(typeAliasDecl->mAliasToType, BfPopulateType_IdentityNoRemapAlias);
}
BfLogSysM("DoPopulateType_TypeAlias %p %s = %p %s\n", typeAlias, TypeToString(typeAlias).c_str(), aliasToType, (aliasToType != NULL) ? TypeToString(aliasToType).c_str() : NULL);
if (aliasToType != NULL)
{
if (aliasToType->IsConstExprValue())
@ -2785,7 +2821,10 @@ void BfModule::DoPopulateType_TypeAlias(BfTypeInstance* typeAlias)
if (typeAlias->mTypeFailed)
aliasToType = NULL;
((BfTypeAliasType*)typeAlias)->mAliasToType = aliasToType;
if ((typeAlias->mAliasToType != NULL) && (typeAlias->mAliasToType != aliasToType) && (!typeAlias->mDependencyMap.IsEmpty()))
mContext->RebuildDependentTypes_MidCompile(typeAlias, "type alias remapped");
typeAlias->mAliasToType = aliasToType;
if (aliasToType != NULL)
{
@ -3188,9 +3227,10 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
if (resolvedTypeRef->IsTypeAlias())
{
prevTypeState.Restore();
DoPopulateType_TypeAlias(typeInstance);
DoPopulateType_TypeAlias((BfTypeAliasType*)typeInstance);
typeInstance->mTypeIncomplete = false;
resolvedTypeRef->mRebuildFlags = BfTypeRebuildFlag_None;
resolvedTypeRef->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotted;
return;
}
@ -4453,19 +4493,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
if (!typeInstance->mCeTypeInfo->mNext->mFailed)
{
if ((typeInstance->mCeTypeInfo->mHash != typeInstance->mCeTypeInfo->mNext->mHash) && (!typeInstance->mCeTypeInfo->mHash.IsZero()))
{
int prevDeletedTypes = mCompiler->mStats.mTypesDeleted;
if (mCompiler->mIsResolveOnly)
mCompiler->mNeedsFullRefresh = true;
BfLogSysM("Type %p hash changed, rebuilding dependent types\n", typeInstance);
mContext->RebuildDependentTypes(typeInstance);
if (mCompiler->mStats.mTypesDeleted != prevDeletedTypes)
{
BfLogSysM("Type %p hash changed, rebuilding dependent types - updating after deleting types\n", typeInstance);
mContext->UpdateAfterDeletingTypes();
}
}
mContext->RebuildDependentTypes_MidCompile(typeInstance, "comptime hash changed");
typeInstance->mCeTypeInfo->mOnCompileMap = typeInstance->mCeTypeInfo->mNext->mOnCompileMap;
typeInstance->mCeTypeInfo->mTypeIFaceMap = typeInstance->mCeTypeInfo->mNext->mTypeIFaceMap;
typeInstance->mCeTypeInfo->mHash = typeInstance->mCeTypeInfo->mNext->mHash;
@ -8922,6 +8950,8 @@ BfType* BfModule::ResolveTypeResult(BfTypeReference* typeRef, BfType* resolvedTy
{
if (mCurTypeInstance != NULL)
AddDependency(resolvedTypeRef, mCurTypeInstance, BfDependencyMap::DependencyFlag_NameReference);
if (resolvedTypeRef->mDefineState == BfTypeDefineState_Undefined)
PopulateType(resolvedTypeRef);
if ((typeInstance->mCustomAttributes != NULL) && (!typeRef->IsTemporary()))
CheckErrorAttributes(typeInstance, NULL, typeInstance->mCustomAttributes, typeRef);
resolvedTypeRef = resolvedTypeRef->GetUnderlyingType();
@ -11088,7 +11118,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
#ifdef _DEBUG
if (BfResolvedTypeSet::Hash(refType, &lookupCtx) != resolvedEntry->mHash)
{
int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx);
int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx, BfResolvedTypeSet::BfHashFlag_AllowRef);
int typeHash = BfResolvedTypeSet::Hash(refType, &lookupCtx);
BF_ASSERT(refHash == typeHash);
}

View file

@ -7,6 +7,7 @@
#include "BfSource.h"
#include "BfIRBuilder.h"
#include "BeefySysLib/util/MultiHashSet.h"
#include "BeefySysLib/util/BitSet.h"
NS_BF_BEGIN
@ -428,7 +429,8 @@ enum BfTypeRebuildFlags
BfTypeRebuildFlag_ResolvingBase = 0x8000,
BfTypeRebuildFlag_InFailTypes = 0x10000,
BfTypeRebuildFlag_RebuildQueued = 0x20000,
BfTypeRebuildFlag_ConstEvalCancelled = 0x40000
BfTypeRebuildFlag_ConstEvalCancelled = 0x40000,
BfTypeRebuildFlag_ChangedMidCompile = 0x80000,
};
class BfTypeDIReplaceCallback;
@ -1842,6 +1844,12 @@ class BfGenericExtensionInfo
{
public:
Dictionary<BfTypeDef*, BfGenericExtensionEntry> mExtensionMap;
BitSet mConstraintsPassedSet;
void Clear()
{
mExtensionMap.Clear();
}
};
// Note on nested generic types- mGenericParams is the accumulation of all generic params from outer to inner, so

View file

@ -6122,7 +6122,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
}
if (varType == NULL)
varType = mContext->mBfObjectType;
varType = GetPrimitiveType(BfTypeCode_Var);
bool isArray = target.mType->IsArray();
bool isSizedArray = target.mType->IsSizedArray();
bool isVarEnumerator = target.mType->IsVar();
@ -6437,7 +6437,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
}
}
if (nextEmbeddedType == NULL)
nextEmbeddedType = mContext->mBfObjectType;
nextEmbeddedType = GetPrimitiveType(BfTypeCode_Var);
BfLocalVariable* itrLocalDef = NULL;

View file

@ -14,7 +14,7 @@ namespace Tests
public struct Inner
{
public const EnumA cVal = EnumA.C("InnerTest");
public const EnumA cVal = .C("InnerTest");
}
}

View file

@ -67,6 +67,14 @@ PUSHD %~dp0..\
@CALL :TEST
@IF !ERRORLEVEL! NEQ 0 GOTO HADERROR
@SET TESTPATH=IDE\Tests\BugW008
@CALL :TEST
@IF !ERRORLEVEL! NEQ 0 GOTO HADERROR
@SET TESTPATH=IDE\Tests\BugW009
@CALL :TEST
@IF !ERRORLEVEL! NEQ 0 GOTO HADERROR
@SET TESTPATH=IDE\Tests\IndentTest
@CALL :TEST
@IF !ERRORLEVEL! NEQ 0 GOTO HADERROR