From 037b2ac1e4b8b2546d31301dad662554c27c9064 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Sat, 11 Jul 2020 16:24:07 -0700 Subject: [PATCH] Added reflection category to distinct build options (wip) --- IDE/src/BuildOptions.bf | 51 ++++ IDE/src/Compiler/BfSystem.bf | 66 ++++- IDE/src/ui/BuildPropertiesDialog.bf | 28 ++ IDEHelper/Compiler/BfCompiler.cpp | 18 +- IDEHelper/Compiler/BfContext.cpp | 17 +- IDEHelper/Compiler/BfModule.cpp | 18 +- IDEHelper/Compiler/BfModule.h | 1 + IDEHelper/Compiler/BfModuleTypeUtils.cpp | 290 +++++++++++++++++++-- IDEHelper/Compiler/BfReducer.cpp | 2 +- IDEHelper/Compiler/BfResolvedTypeUtils.cpp | 11 + IDEHelper/Compiler/BfResolvedTypeUtils.h | 1 + IDEHelper/Compiler/BfStmtEvaluator.cpp | 2 +- IDEHelper/Compiler/BfSystem.cpp | 68 ++--- IDEHelper/Compiler/BfSystem.h | 42 ++- IDEHelper/Tests/src/Generics.bf | 17 ++ 15 files changed, 538 insertions(+), 94 deletions(-) diff --git a/IDE/src/BuildOptions.bf b/IDE/src/BuildOptions.bf index 7e54fa28..32f23d6a 100644 --- a/IDE/src/BuildOptions.bf +++ b/IDE/src/BuildOptions.bf @@ -64,6 +64,15 @@ namespace IDE Small, Big } + + public enum AlwaysIncludeKind + { + NotSet, + No, + IncludeType, + AssumeInstantiated, + IncludeAll + } } public class DistinctBuildOptions @@ -94,6 +103,20 @@ namespace IDE public bool? mEmitObjectAccessCheck; // Only valid with mObjectHasDebugFlags [Reflect] public int32? mAllocStackTraceDepth; + [Reflect] + public BuildOptions.AlwaysIncludeKind mReflectAlwaysInclude; + [Reflect] + public bool? mReflectStaticFields; + [Reflect] + public bool? mReflectNonStaticFields; + [Reflect] + public bool? mReflectStaticMethods; + [Reflect] + public bool? mReflectNonStaticMethods; + [Reflect] + public bool? mReflectConstructors; + [Reflect] + public String mReflectMethodFilter = new String() ~ delete _; public ~this() { @@ -111,6 +134,13 @@ namespace IDE newVal.mEmitDynamicCastCheck = mEmitDynamicCastCheck; newVal.mEmitObjectAccessCheck = mEmitObjectAccessCheck; newVal.mAllocStackTraceDepth = mAllocStackTraceDepth; + newVal.mReflectAlwaysInclude = mReflectAlwaysInclude; + newVal.mReflectStaticFields = mReflectStaticFields; + newVal.mReflectNonStaticFields = mReflectNonStaticFields; + newVal.mReflectStaticMethods = mReflectStaticMethods; + newVal.mReflectNonStaticMethods = mReflectNonStaticMethods; + newVal.mReflectConstructors = mReflectConstructors; + newVal.mReflectMethodFilter.Set(mReflectMethodFilter); return newVal; } @@ -133,6 +163,20 @@ namespace IDE mEmitObjectAccessCheck = data.GetBool("EmitObjectAccessCheck"); if (data.Contains("AllocStackTraceDepth")) mAllocStackTraceDepth = data.GetInt("AllocStackTraceDepth"); + + if (data.Contains("ReflectAlwaysInclude")) + mReflectAlwaysInclude = data.GetEnum("ReflectAlwaysInclude"); + if (data.Contains("ReflectStaticFields")) + mReflectStaticFields = data.GetBool("ReflectStaticFields"); + if (data.Contains("ReflectNonStaticFields")) + mReflectNonStaticFields = data.GetBool("ReflectNonStaticFields"); + if (data.Contains("ReflectStaticMethods")) + mReflectStaticMethods = data.GetBool("ReflectStaticMethods"); + if (data.Contains("ReflectNonStaticMethods")) + mReflectNonStaticMethods = data.GetBool("ReflectNonStaticMethods"); + if (data.Contains("ReflectConstructors")) + mReflectConstructors = data.GetBool("ReflectConstructors"); + data.GetString("ReflectMethodFilter", mReflectMethodFilter); } public void Serialize(StructuredData data) @@ -146,6 +190,13 @@ namespace IDE data.ConditionalAdd("EmitDynamicCastCheck", mEmitDynamicCastCheck); data.ConditionalAdd("EmitObjectAccessCheck", mEmitObjectAccessCheck); data.ConditionalAdd("AllocStackTraceDepth", mAllocStackTraceDepth); + data.ConditionalAdd("ReflectAlwaysInclude", mReflectAlwaysInclude); + data.ConditionalAdd("ReflectStaticFields", mReflectStaticFields); + data.ConditionalAdd("ReflectNonStaticFields", mReflectNonStaticFields); + data.ConditionalAdd("ReflectStaticMethods", mReflectStaticMethods); + data.ConditionalAdd("ReflectNonStaticMethods", mReflectNonStaticMethods); + data.ConditionalAdd("ReflectConstructors", mReflectConstructors); + data.ConditionalAdd("ReflectMethodFilter", mReflectMethodFilter); } } } diff --git a/IDE/src/Compiler/BfSystem.bf b/IDE/src/Compiler/BfSystem.bf index 9f4f9a9d..f48bec3e 100644 --- a/IDE/src/Compiler/BfSystem.bf +++ b/IDE/src/Compiler/BfSystem.bf @@ -9,6 +9,25 @@ namespace IDE.Compiler { public class BfSystem { + enum BfOptionFlags + { + RuntimeChecks = 1, + InitLocalVariables = 2, + EmitDynamicCastCheck = 4, + EmitObjectAccessCheck = 8, + + ReflectAlwaysIncludeType = 0x10, + ReflectAlwaysIncludeAll = 0x20, + ReflectAssumeInstantiated = 0x40, + ReflectStaticFields = 0x80, + ReflectNonStaticFields = 0x100, + ReflectStaticMethods = 0x200, + ReflectNonStaticMethods = 0x400, + ReflectConstructors = 0x800, + + All = 0xFFF + }; + [CallingConvention(.Stdcall), CLink] static extern void BfSystem_CheckLock(void* bfSystem); @@ -37,8 +56,8 @@ namespace IDE.Compiler static extern void BfSystem_ClearTypeOptions(void* bfSystem); [CallingConvention(.Stdcall), CLink] - static extern void BfSystem_AddTypeOptions(void* bfSystem, char8* filter, int32 simdSetting, int32 optimizationLevel, int32 emitDebugInfo, int32 runtimeChecks, - int32 initLocalVariables, int32 emitDynamicCastCheck, int32 emitObjectAccessCheck, int32 allocStackTraceDepth); + static extern void BfSystem_AddTypeOptions(void* bfSystem, char8* filter, int32 simdSetting, int32 optimizationLevel, int32 emitDebugInfo, int32 andFlags, int32 orFlags, + int32 allocStackTraceDepth, char8* reflectMethodFilter); [CallingConvention(.Stdcall), CLink] static extern void* BfSystem_CreateParser(void* bfSystem, void* bfProject); @@ -343,25 +362,52 @@ namespace IDE.Compiler BfSystem_ClearTypeOptions(mNativeBfSystem); } - public void AddTypeOptions(String filter, BuildOptions.SIMDSetting? simdSetting, BuildOptions.BfOptimizationLevel? optimizationLevel, BuildOptions.EmitDebugInfo? emitDebugInfo, bool? runtimeChecks, - bool? initLocalVariables, bool? emitDynamicCastCheck, bool? emitObjectAccessCheck, int32? allocStackTraceDepth) + public void AddTypeOptions(String filter, BuildOptions.SIMDSetting? simdSetting, BuildOptions.BfOptimizationLevel? optimizationLevel, BuildOptions.EmitDebugInfo? emitDebugInfo, BfOptionFlags andFlags, BfOptionFlags orFlags, int32? allocStackTraceDepth, String reflectMethodFilter) { int32 simdSettingInt = (simdSetting == null) ? -1 : (int32)simdSetting.Value; int32 optimizationLevelInt = (optimizationLevel == null) ? -1 : (int32)optimizationLevel.Value; int32 emitDebugInfoInt = (emitDebugInfo == null) ? -1 : (int32)emitDebugInfo.Value; - int32 runtimeChecksInt = (runtimeChecks == null) ? -1 : runtimeChecks.Value ? 1 : 0; + /*int32 runtimeChecksInt = (runtimeChecks == null) ? -1 : runtimeChecks.Value ? 1 : 0; int32 initLocalVariablesInt = (initLocalVariables == null) ? -1 : initLocalVariables.Value ? 1 : 0; int32 emitDynamicCastCheckInt = (emitDynamicCastCheck == null) ? -1 : emitDynamicCastCheck.Value ? 1 : 0; - int32 emitObjectAccessCheckInt = (emitObjectAccessCheck == null) ? -1 : emitObjectAccessCheck.Value ? 1 : 0; + int32 emitObjectAccessCheckInt = (emitObjectAccessCheck == null) ? -1 : emitObjectAccessCheck.Value ? 1 : 0;*/ int32 allocStackTraceDepthInt = (allocStackTraceDepth == null) ? -1 : allocStackTraceDepth.Value; - BfSystem_AddTypeOptions(mNativeBfSystem, filter, simdSettingInt, optimizationLevelInt, emitDebugInfoInt, runtimeChecksInt, - initLocalVariablesInt, emitDynamicCastCheckInt, emitObjectAccessCheckInt, allocStackTraceDepthInt); + BfSystem_AddTypeOptions(mNativeBfSystem, filter, simdSettingInt, optimizationLevelInt, emitDebugInfoInt, (.)andFlags, (.)orFlags, allocStackTraceDepthInt, reflectMethodFilter); } public void AddTypeOptions(DistinctBuildOptions typeOption) { - AddTypeOptions(typeOption.mFilter, typeOption.mBfSIMDSetting, typeOption.mBfOptimizationLevel, typeOption.mEmitDebugInfo, typeOption.mRuntimeChecks, - typeOption.mInitLocalVariables, typeOption.mEmitDynamicCastCheck, typeOption.mEmitObjectAccessCheck, typeOption.mAllocStackTraceDepth); + BfOptionFlags andFlags = .All; + BfOptionFlags orFlags = 0; + + void SetFlag(bool? val, BfOptionFlags flag) + { + if (val == false) + andFlags &= ~flag; + if (val == true) + orFlags |= flag; + } + + switch (typeOption.mReflectAlwaysInclude) + { + case .NotSet: + case .No: + andFlags &= ~(.ReflectAlwaysIncludeType | .ReflectAlwaysIncludeAll | .ReflectAssumeInstantiated); + case .IncludeType: + orFlags |= .ReflectAssumeInstantiated; + case .AssumeInstantiated: + orFlags |= .ReflectAssumeInstantiated; + case .IncludeAll: + orFlags |= .ReflectAlwaysIncludeType | .ReflectAlwaysIncludeAll | .ReflectAssumeInstantiated; + } + + SetFlag(typeOption.mReflectStaticFields, .ReflectStaticFields); + SetFlag(typeOption.mReflectNonStaticFields, .ReflectNonStaticFields); + SetFlag(typeOption.mReflectStaticMethods, .ReflectStaticMethods); + SetFlag(typeOption.mReflectNonStaticMethods, .ReflectNonStaticMethods); + SetFlag(typeOption.mReflectConstructors, .ReflectConstructors); + + AddTypeOptions(typeOption.mFilter, typeOption.mBfSIMDSetting, typeOption.mBfOptimizationLevel, typeOption.mEmitDebugInfo, andFlags, orFlags, typeOption.mAllocStackTraceDepth, typeOption.mReflectMethodFilter); } public void Log(String str) diff --git a/IDE/src/ui/BuildPropertiesDialog.bf b/IDE/src/ui/BuildPropertiesDialog.bf index ee7203c2..572bd5c6 100644 --- a/IDE/src/ui/BuildPropertiesDialog.bf +++ b/IDE/src/ui/BuildPropertiesDialog.bf @@ -145,6 +145,34 @@ namespace IDE.ui typeName.Clear(); typeName.Append(optionsName, "mAllocStackTraceDepth"); AddPropertiesItem(category, "Alloc Stack Trace Depth", typeName); + let (reflectItem, ?) = AddPropertiesItem(category, "Reflect"); + + typeName.Clear(); typeName.Append(optionsName, "mReflectAlwaysInclude"); + AddPropertiesItem(reflectItem, "Always Include", typeName); + + typeName.Clear(); typeName.Append(optionsName, "mReflectStaticFields"); + AddPropertiesItem(reflectItem, "Static Fields", typeName, + scope String[] { "No", "Yes" }); + + typeName.Clear(); typeName.Append(optionsName, "mReflectNonStaticFields"); + AddPropertiesItem(reflectItem, "Non-Static Fields", typeName, + scope String[] { "No", "Yes" }); + + typeName.Clear(); typeName.Append(optionsName, "mReflectStaticMethods"); + AddPropertiesItem(reflectItem, "Static Methods", typeName, + scope String[] { "No", "Yes" }); + + typeName.Clear(); typeName.Append(optionsName, "mReflectNonStaticMethods"); + AddPropertiesItem(reflectItem, "Non-Static Methods", typeName, + scope String[] { "No", "Yes" }); + + typeName.Clear(); typeName.Append(optionsName, "mReflectConstructors"); + AddPropertiesItem(reflectItem, "Constructors", typeName, + scope String[] { "No", "Yes" }); + + typeName.Clear(); typeName.Append(optionsName, "mReflectMethodFilter"); + AddPropertiesItem(reflectItem, "Method Filter", typeName); + category.Open(true, true); } diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 0ecf8eda..bfdc4b91 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -5906,8 +5906,13 @@ void BfCompiler::CompileReified() if (typeDef->mIsPartial) continue; + auto scratchModule = mContext->mScratchModule; bool isAlwaysInclude = (typeDef->mIsAlwaysInclude) || (typeDef->mProject->mAlwaysIncludeAll); + auto typeOptions = scratchModule->GetTypeOptions(typeDef); + if (typeOptions != NULL) + typeOptions->Apply(isAlwaysInclude, BfOptionFlags_ReflectAlwaysIncludeType); + if (typeDef->mProject->IsTestProject()) { for (auto methodDef : typeDef->mMethods) @@ -5922,8 +5927,7 @@ void BfCompiler::CompileReified() //TODO: Just because the type is required doesn't mean we want to reify it. Why did we have that check? if ((mOptions.mCompileOnDemandKind != BfCompileOnDemandKind_AlwaysInclude) && (!isAlwaysInclude)) continue; - - auto scratchModule = mContext->mScratchModule; + scratchModule->ResolveTypeDef(typeDef, BfPopulateType_Full); } @@ -6396,7 +6400,15 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) for (auto typeDef : mSystem->mTypeDefs) { - if ((typeDef->mIsAlwaysInclude) && (!typeDef->mIsPartial)) + if (typeDef->mIsPartial) + continue; + + bool isAlwaysInclude = (typeDef->mIsAlwaysInclude) || (typeDef->mProject->mAlwaysIncludeAll); + auto typeOptions = mContext->mScratchModule->GetTypeOptions(typeDef); + if (typeOptions != NULL) + isAlwaysInclude = typeOptions->Apply(isAlwaysInclude, BfOptionFlags_ReflectAlwaysIncludeType); + + if (isAlwaysInclude) { auto requiredType = mContext->mScratchModule->ResolveTypeDef(typeDef); if (requiredType != NULL) diff --git a/IDEHelper/Compiler/BfContext.cpp b/IDEHelper/Compiler/BfContext.cpp index 1c9cc6f2..2a328c69 100644 --- a/IDEHelper/Compiler/BfContext.cpp +++ b/IDEHelper/Compiler/BfContext.cpp @@ -1896,10 +1896,14 @@ void BfContext::UpdateRevisedTypes() workspaceConfigHashCtx.Mixin(typeOptions.mSIMDSetting); workspaceConfigHashCtx.Mixin(typeOptions.mOptimizationLevel); workspaceConfigHashCtx.Mixin(typeOptions.mEmitDebugInfo); - workspaceConfigHashCtx.Mixin(typeOptions.mRuntimeChecks); - workspaceConfigHashCtx.Mixin(typeOptions.mInitLocalVariables); - workspaceConfigHashCtx.Mixin(typeOptions.mEmitDynamicCastCheck); - workspaceConfigHashCtx.Mixin(typeOptions.mEmitObjectAccessCheck); + workspaceConfigHashCtx.Mixin(typeOptions.mAndFlags); + workspaceConfigHashCtx.Mixin(typeOptions.mOrFlags); + workspaceConfigHashCtx.Mixin(typeOptions.mReflectMethodFilters.size()); + for (auto& filter : typeOptions.mReflectMethodFilters) + workspaceConfigHashCtx.MixinStr(filter); + workspaceConfigHashCtx.Mixin(typeOptions.mReflectMethodAttributeFilters.size()); + for (auto& filter : typeOptions.mReflectMethodAttributeFilters) + workspaceConfigHashCtx.MixinStr(filter); workspaceConfigHashCtx.Mixin(typeOptions.mAllocStackTraceDepth); } @@ -2629,7 +2633,10 @@ void BfContext::TryUnreifyModules() bool isRequired = false; for (auto typeInst : module->mOwnedTypeInstances) { - if ((typeInst->mTypeDef->mIsAlwaysInclude) || (typeInst->mTypeDef->IsGlobalsContainer())) + if (typeInst->mTypeDef->IsGlobalsContainer()) + isRequired = true; + + if (typeInst->IsAlwaysInclude()) isRequired = true; } diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index c01cce10..642ff373 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -3341,7 +3341,7 @@ BfCheckedKind BfModule::GetDefaultCheckedKind() bool runtimeChecks = mCompiler->mOptions.mRuntimeChecks; auto typeOptions = GetTypeOptions(); if (typeOptions != NULL) - runtimeChecks = BfTypeOptions::Apply(runtimeChecks, typeOptions->mRuntimeChecks); + runtimeChecks = typeOptions->Apply(runtimeChecks, BfOptionFlags_RuntimeChecks); return runtimeChecks ? BfCheckedKind_Checked : BfCheckedKind_Unchecked; } @@ -4738,6 +4738,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin typeFlags |= BfTypeFlags_Union; if (type->IsDelegate()) typeFlags |= BfTypeFlags_Delegate; + if (type->IsFunction()) + typeFlags |= BfTypeFlags_Function; if (type->WantsGCMarking()) typeFlags |= BfTypeFlags_WantsMarking; @@ -4839,7 +4841,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin // Reserve position mTypeDataRefs[typeInstance] = BfIRValue(); - PopulateType(typeInstance, BfPopulateType_DataAndMethods); + if (typeInstance->IsReified()) + PopulateType(typeInstance, BfPopulateType_DataAndMethods); BfTypeDef* typeDef = typeInstance->mTypeDef; StringT<128> mangledName; @@ -6198,12 +6201,14 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin if ((typeInstance->IsTypedPrimitive()) || (typeInstance->IsBoxed())) { underlyingType = typeInstance->GetUnderlyingType()->mTypeId; - } + } int outerTypeId = 0; auto outerType = mContext->mUnreifiedModule->GetOuterType(typeInstance); if (outerType != NULL) + { outerTypeId = outerType->mTypeId; + } BfIRValue customAttrDataPtr; if (customAttrs.size() > 0) @@ -8753,7 +8758,7 @@ void BfModule::EmitObjectAccessCheck(BfTypedValue typedVal) bool emitObjectAccessCheck = mCompiler->mOptions.mEmitObjectAccessCheck; auto typeOptions = GetTypeOptions(); if (typeOptions != NULL) - emitObjectAccessCheck = BfTypeOptions::Apply(emitObjectAccessCheck, typeOptions->mEmitObjectAccessCheck); + emitObjectAccessCheck = typeOptions->Apply(emitObjectAccessCheck, BfOptionFlags_EmitObjectAccessCheck); if (!emitObjectAccessCheck) return; @@ -8875,7 +8880,7 @@ void BfModule::EmitDynamicCastCheck(BfTypedValue typedVal, BfType* type, bool al bool emitDynamicCastCheck = mCompiler->mOptions.mEmitDynamicCastCheck; auto typeOptions = GetTypeOptions(); if (typeOptions != NULL) - emitDynamicCastCheck = BfTypeOptions::Apply(emitDynamicCastCheck, typeOptions->mEmitDynamicCastCheck); + emitDynamicCastCheck = typeOptions->Apply(emitDynamicCastCheck, BfOptionFlags_EmitDynamicCastCheck); if (emitDynamicCastCheck) { @@ -12715,7 +12720,7 @@ BfIRValue BfModule::AllocLocalVariable(BfType* type, const StringImpl& name, boo bool initLocalVariables = mCompiler->mOptions.mInitLocalVariables; auto typeOptions = GetTypeOptions(); if (typeOptions != NULL) - initLocalVariables = BfTypeOptions::Apply(initLocalVariables, typeOptions->mInitLocalVariables); + initLocalVariables = typeOptions->Apply(initLocalVariables, BfOptionFlags_InitLocalVariables); // Local variable inits are implicitly handled in the Beef Backend if ((initLocalVariables) && (!IsTargetingBeefBackend())) { @@ -18448,6 +18453,7 @@ BfMethodDef* BfModule::GetLocalMethodDef(BfLocalMethod* localMethod) if (methodDeclaration != NULL) { BfDefBuilder defBuilder(mCompiler->mSystem); + defBuilder.mCurSource = localMethod->mMethodDeclaration->GetParser(); defBuilder.mPassInstance = mCompiler->mPassInstance; defBuilder.mCurTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType; diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 39d85abf..9c35af15 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1603,6 +1603,7 @@ public: void MarkDerivedDirty(BfTypeInstance* typeInst); void CheckAddFailType(); bool PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType = BfPopulateType_Data); + BfTypeOptions* GetTypeOptions(BfTypeDef* typeDef); int GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeInstance* typeInstance, bool checkTypeName); void SetTypeOptions(BfTypeInstance* typeInstance); BfModuleOptions GetModuleOptions(); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 973ca57d..daba7bcd 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -1234,6 +1234,245 @@ bool BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType return result; } +BfTypeOptions* BfModule::GetTypeOptions(BfTypeDef* typeDef) +{ + if (mContext->mSystem->mTypeOptions.size() == 0) + { + return NULL; + } + + Array matchedIndices; + + if (!mCompiler->mAttributeTypeOptionMap.IsEmpty()) + { + auto customAttributes = typeDef->mTypeDeclaration->mAttributes; + + while (customAttributes != NULL) + { + if (!mCompiler->mAttributeTypeOptionMap.IsEmpty()) + { + SetAndRestoreValue prevIgnoreErrors(mIgnoreErrors, true); + + auto typeRef = customAttributes->mAttributeTypeRef; + + // StringT<128> attrName; + // for (auto& customAttrs : customAttributes->mAttributeTypeRef) + // { + // attrName.Clear(); + // customAttrs.mType->mTypeDef->mFullName.ToString(attrName); + // Array* arrPtr; + // if (mCompiler->mAttributeTypeOptionMap.TryGetValue(attrName, &arrPtr)) + // { + // for (auto optionsIdx : *arrPtr) + // { + // matchedIndices.Add(optionsIdx); + // } + // } + // } + } + + customAttributes = customAttributes->mNextAttribute; + } + } + + int typeOptionsCount = (int)mContext->mSystem->mTypeOptions.size(); + + auto _CheckTypeName = [&](const StringImpl& typeName) + { + for (int optionIdx = 0; optionIdx < (int)mContext->mSystem->mTypeOptions.size(); optionIdx++) + { + auto& typeOptions = mContext->mSystem->mTypeOptions[optionIdx]; + + bool matched = false; + for (auto& filter : typeOptions.mTypeFilters) + { + int filterIdx = 0; + int typeNameIdx = 0; + + const char* filterPtr = filter.c_str(); + const char* namePtr = typeName.c_str(); + + char prevFilterC = 0; + while (true) + { + char filterC; + while (true) + { + filterC = *(filterPtr++); + if (filterC != ' ') + break; + } + + char nameC; + while (true) + { + nameC = *(namePtr++); + if (nameC != ' ') + break; + } + + if ((filterC == 0) || (nameC == 0)) + { + matched = (filterC == 0) && (nameC == 0); + break; + } + + bool doWildcard = false; + + if (nameC != filterC) + { + if (filterC == '*') + doWildcard = true; + else if (((filterC == ',') || (filterC == '>')) && + ((prevFilterC == '<') || (prevFilterC == ','))) + { + doWildcard = true; + filterPtr--; + } + + if (!doWildcard) + { + matched = false; + break; + } + } + + if (doWildcard) + { + int openDepth = 0; + + const char* startNamePtr = namePtr; + + while (true) + { + nameC = *(namePtr++); + if (nameC == 0) + { + namePtr--; + if (openDepth != 0) + matched = false; + break; + } + if ((nameC == '>') && (openDepth == 0)) + { + namePtr--; + break; + } + + if (nameC == '<') + openDepth++; + else if (nameC == '>') + openDepth--; + else if ((nameC == ',') && (openDepth == 0)) + { + namePtr--; + break; + } + } + + if (!matched) + break; + } + + prevFilterC = filterC; + } + } + + if (matched) + matchedIndices.push_back(optionIdx); + } + }; + +// if (typeInstance->IsTypedPrimitive()) +// { +// auto underlyingType = typeInstance->GetUnderlyingType(); +// if (underlyingType != NULL) +// { +// String typeName = TypeToString(underlyingType); +// _CheckTypeName(typeName); +// } +// else +// { +// // Can this only happen for functions that are being extended? +// } +// } +// +// if ((!typeInstance->IsBoxed()) && (typeInstance->mTypeDef == mCompiler->mPointerTTypeDef)) +// { +// BF_ASSERT(typeInstance->IsGenericTypeInstance()); +// auto innerType = typeInstance->mGenericTypeInfo->mTypeGenericArguments[0]; +// auto ptrType = CreatePointerType(innerType); +// String typeName = TypeToString(ptrType); +// _CheckTypeName(typeName); +// } + + String typeName = BfTypeUtils::TypeToString(typeDef); + _CheckTypeName(typeName); + + int matchedIdx = -1; + if (matchedIndices.size() == 1) + { + matchedIdx = matchedIndices[0]; + } + else if (matchedIndices.size() > 1) + { + // Try to find a merged typeoptions with these indices + for (int mergedIdx = 0; mergedIdx < (int)mContext->mSystem->mMergedTypeOptions.size(); mergedIdx++) + { + auto& typeOptions = mContext->mSystem->mMergedTypeOptions[mergedIdx]; + if (typeOptions.mMatchedIndices == matchedIndices) + { + matchedIdx = typeOptionsCount + mergedIdx; + break; + } + } + + // Otherwise make one... + if (matchedIdx == -1) + { + auto& first = mContext->mSystem->mTypeOptions[matchedIndices[0]]; + BfTypeOptions mergedTypeOptions; + mergedTypeOptions.mSIMDSetting = first.mSIMDSetting; + mergedTypeOptions.mOptimizationLevel = first.mOptimizationLevel; + mergedTypeOptions.mEmitDebugInfo = first.mEmitDebugInfo; + mergedTypeOptions.mAndFlags = first.mAndFlags; + mergedTypeOptions.mOrFlags = first.mOrFlags; + mergedTypeOptions.mAllocStackTraceDepth = first.mAllocStackTraceDepth; + mergedTypeOptions.mReflectMethodFilters = first.mReflectMethodFilters; + mergedTypeOptions.mReflectMethodAttributeFilters = first.mReflectMethodAttributeFilters; + + mergedTypeOptions.mMatchedIndices = matchedIndices; + for (int idx = 1; idx < (int)matchedIndices.size(); idx++) + { + auto& typeOptions = mContext->mSystem->mTypeOptions[matchedIndices[idx]]; + if (typeOptions.mSIMDSetting != -1) + mergedTypeOptions.mSIMDSetting = typeOptions.mSIMDSetting; + if (typeOptions.mOptimizationLevel != -1) + mergedTypeOptions.mOptimizationLevel = typeOptions.mOptimizationLevel; + if (typeOptions.mEmitDebugInfo != -1) + mergedTypeOptions.mEmitDebugInfo = typeOptions.mEmitDebugInfo; + + mergedTypeOptions.mOrFlags = (BfOptionFlags)(mergedTypeOptions.mOrFlags | typeOptions.mOrFlags); + mergedTypeOptions.mAndFlags = (BfOptionFlags)(mergedTypeOptions.mAndFlags | typeOptions.mOrFlags); + + mergedTypeOptions.mAndFlags = (BfOptionFlags)(mergedTypeOptions.mAndFlags & typeOptions.mAndFlags); + mergedTypeOptions.mOrFlags = (BfOptionFlags)(mergedTypeOptions.mOrFlags & typeOptions.mAndFlags); + + if (typeOptions.mAllocStackTraceDepth != -1) + mergedTypeOptions.mAllocStackTraceDepth = typeOptions.mAllocStackTraceDepth; + for (auto filter : typeOptions.mReflectMethodFilters) + mergedTypeOptions.mReflectMethodFilters.Add(filter); + for (auto filter : typeOptions.mReflectMethodAttributeFilters) + mergedTypeOptions.mReflectMethodAttributeFilters.Add(filter); + } + matchedIdx = typeOptionsCount + (int)mContext->mSystem->mMergedTypeOptions.size(); + mContext->mSystem->mMergedTypeOptions.push_back(mergedTypeOptions); + } + } + + return mSystem->GetTypeOptions( matchedIdx); +} + int BfModule::GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeInstance* typeInstance, bool checkTypeName) { if (mContext->mSystem->mTypeOptions.size() == 0) @@ -1379,14 +1618,14 @@ int BfModule::GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeIn } if (matched) - matchedIndices.push_back(optionIdx); + matchedIndices.push_back(optionIdx); } }; if (typeInstance->IsTypedPrimitive()) { auto underlyingType = typeInstance->GetUnderlyingType(); - if (underlyingType != NULL) + if (underlyingType != NULL) { String typeName = TypeToString(underlyingType); _CheckTypeName(typeName); @@ -1407,7 +1646,7 @@ int BfModule::GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeIn } String typeName = TypeToString(typeInstance); - _CheckTypeName(typeName); + _CheckTypeName(typeName); } int matchedIdx = -1; @@ -1436,11 +1675,11 @@ int BfModule::GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeIn mergedTypeOptions.mSIMDSetting = first.mSIMDSetting; mergedTypeOptions.mOptimizationLevel = first.mOptimizationLevel; mergedTypeOptions.mEmitDebugInfo = first.mEmitDebugInfo; - mergedTypeOptions.mRuntimeChecks = first.mRuntimeChecks; - mergedTypeOptions.mInitLocalVariables = first.mInitLocalVariables; - mergedTypeOptions.mEmitDynamicCastCheck = first.mEmitDynamicCastCheck; - mergedTypeOptions.mEmitObjectAccessCheck = first.mEmitObjectAccessCheck; - mergedTypeOptions.mAllocStackTraceDepth = first.mAllocStackTraceDepth; + mergedTypeOptions.mAndFlags = first.mAndFlags; + mergedTypeOptions.mOrFlags = first.mOrFlags; + mergedTypeOptions.mAllocStackTraceDepth = first.mAllocStackTraceDepth; + mergedTypeOptions.mReflectMethodFilters = first.mReflectMethodFilters; + mergedTypeOptions.mReflectMethodAttributeFilters = first.mReflectMethodAttributeFilters; mergedTypeOptions.mMatchedIndices = matchedIndices; for (int idx = 1; idx < (int)matchedIndices.size(); idx++) @@ -1452,16 +1691,19 @@ int BfModule::GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeIn mergedTypeOptions.mOptimizationLevel = typeOptions.mOptimizationLevel; if (typeOptions.mEmitDebugInfo != -1) mergedTypeOptions.mEmitDebugInfo = typeOptions.mEmitDebugInfo; - if (typeOptions.mRuntimeChecks != BfOptionalBool_NotSet) - mergedTypeOptions.mRuntimeChecks = typeOptions.mRuntimeChecks; - if (typeOptions.mInitLocalVariables != BfOptionalBool_NotSet) - mergedTypeOptions.mInitLocalVariables = typeOptions.mInitLocalVariables; - if (typeOptions.mEmitDynamicCastCheck != BfOptionalBool_NotSet) - mergedTypeOptions.mEmitDynamicCastCheck = typeOptions.mEmitDynamicCastCheck; - if (typeOptions.mEmitObjectAccessCheck != BfOptionalBool_NotSet) - mergedTypeOptions.mEmitObjectAccessCheck = typeOptions.mEmitObjectAccessCheck; + + mergedTypeOptions.mOrFlags = (BfOptionFlags)(mergedTypeOptions.mOrFlags | typeOptions.mOrFlags); + mergedTypeOptions.mAndFlags = (BfOptionFlags)(mergedTypeOptions.mAndFlags | typeOptions.mOrFlags); + + mergedTypeOptions.mAndFlags = (BfOptionFlags)(mergedTypeOptions.mAndFlags & typeOptions.mAndFlags); + mergedTypeOptions.mOrFlags = (BfOptionFlags)(mergedTypeOptions.mOrFlags & typeOptions.mAndFlags); + if (typeOptions.mAllocStackTraceDepth != -1) mergedTypeOptions.mAllocStackTraceDepth = typeOptions.mAllocStackTraceDepth; + for (auto filter : typeOptions.mReflectMethodFilters) + mergedTypeOptions.mReflectMethodFilters.Add(filter); + for (auto filter : typeOptions.mReflectMethodAttributeFilters) + mergedTypeOptions.mReflectMethodAttributeFilters.Add(filter); } matchedIdx = typeOptionsCount + (int)mContext->mSystem->mMergedTypeOptions.size(); mContext->mSystem->mMergedTypeOptions.push_back(mergedTypeOptions); @@ -1880,6 +2122,7 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { BfTypeReference* mTypeRef; BfTypeInstance* mGenericType; + bool mIgnoreErrors; }; Array<_DeferredValidate> deferredTypeValidateList; @@ -1930,7 +2173,7 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { // Specialized type variations don't need to validate their constraints if (!typeInstance->IsUnspecializedTypeVariation()) - deferredTypeValidateList.Add({ checkTypeRef, genericTypeInst }); + deferredTypeValidateList.Add({ checkTypeRef, genericTypeInst, false }); } auto checkTypeInst = checkType->ToTypeInstance(); @@ -2077,8 +2320,7 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { if ((genericTypeInst->IsSpecializedType()) && (!genericTypeInst->mGenericTypeInfo->mValidatedGenericConstraints) && (!typeInstance->IsBoxed())) { - SetAndRestoreValue ignoreErrors(mIgnoreErrors, true); - ValidateGenericConstraints(NULL, genericTypeInst, false); + deferredTypeValidateList.Add({ NULL, genericTypeInst, true }); } } @@ -2086,15 +2328,14 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { BfType* outerType = GetOuterType(typeInstance); if (outerType != NULL) + { + PopulateType(outerType, BfPopulateType_BaseType); AddDependency(outerType, typeInstance, BfDependencyMap::DependencyFlag_OuterType); + } } if ((baseTypeInst != NULL) && (typeInstance->mBaseType == NULL)) { - //curFieldDataIdx = 1; -// if (!typeInstance->mTypeFailed) -// PopulateType(baseTypeInst, BfPopulateType_Data); - if (typeInstance->mTypeFailed) { if (baseTypeInst->IsDataIncomplete()) @@ -2217,6 +2458,7 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy for (auto& validateEntry : deferredTypeValidateList) { + SetAndRestoreValue ignoreErrors(mIgnoreErrors, mIgnoreErrors | validateEntry.mIgnoreErrors); ValidateGenericConstraints(validateEntry.mTypeRef, validateEntry.mGenericType, false); } @@ -8564,6 +8806,8 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula resolvedEntry->mValue = delegateType; AddDependency(directTypeRef->mType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue); + if (delegateInfo->mFunctionThisType != NULL) + AddDependency(delegateInfo->mFunctionThisType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue); for (auto paramType : paramTypes) AddDependency(paramType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue); diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index d2c54e4e..4943951e 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -6936,7 +6936,7 @@ BfInvocationExpression* BfReducer::CreateInvocationExpression(BfAstNode* target, return invocationExpr; } -BfInitializerExpression * BfReducer::TryCreateInitializerExpression(BfExpression* target) +BfInitializerExpression* BfReducer::TryCreateInitializerExpression(BfExpression* target) { auto block = BfNodeDynCast(mVisitorPos.GetNext()); if (block == NULL) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 6c818017..80a7439d 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -1909,6 +1909,17 @@ void BfTypeInstance::GenerateProjectsReferenced() BfTypeUtils::GetProjectList(genericArgType, &mGenericTypeInfo->mProjectsReferenced, 0); } +bool BfTypeInstance::IsAlwaysInclude() +{ + bool alwaysInclude = mTypeDef->mIsAlwaysInclude || mTypeDef->mProject->mAlwaysIncludeAll; + if (mTypeOptionsIdx > 0) + { + auto typeOptions = mModule->mSystem->GetTypeOptions(mTypeOptionsIdx); + typeOptions->Apply(alwaysInclude, BfOptionFlags_ReflectAlwaysIncludeType); + } + return alwaysInclude; +} + bool BfTypeInstance::IsSpecializedByAutoCompleteMethod() { if (mGenericTypeInfo == NULL) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 4e10f269..27f84512 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -1878,6 +1878,7 @@ public: bool GetResultInfo(BfType*& valueType, int& okTagId); BfGenericTypeInfo::GenericParamsVector* GetGenericParamsVector(BfTypeDef* declaringTypeDef); void GenerateProjectsReferenced(); + bool IsAlwaysInclude(); virtual void ReportMemory(MemReporter* memReporter) override; }; diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index aa32bd84..1b60bf25 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -6412,7 +6412,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) bool boundsCheck = mCompiler->mOptions.mRuntimeChecks; auto typeOptions = GetTypeOptions(); if (typeOptions != NULL) - boundsCheck = BfTypeOptions::Apply(boundsCheck, typeOptions->mRuntimeChecks); + boundsCheck = typeOptions->Apply(boundsCheck, BfOptionFlags_RuntimeChecks); BfMethodMatcher methodMatcher(forEachStmt->mVariableName, this, "get__", argValues.mResolvedArgs, NULL); methodMatcher.mMethodType = BfMethodType_PropertyGetter; diff --git a/IDEHelper/Compiler/BfSystem.cpp b/IDEHelper/Compiler/BfSystem.cpp index feb59192..e67ce1c8 100644 --- a/IDEHelper/Compiler/BfSystem.cpp +++ b/IDEHelper/Compiler/BfSystem.cpp @@ -3617,51 +3617,57 @@ BF_EXPORT void BF_CALLTYPE BfSystem_ClearTypeOptions(BfSystem* bfSystem) bfSystem->mTypeOptions.Clear(); } -BF_EXPORT void BF_CALLTYPE BfSystem_AddTypeOptions(BfSystem* bfSystem, char* filter, int32 simdSetting, int32 optimizationLevel, int32 emitDebugInfo, int32 runtimeChecks, - int32 initLocalVariables, int32 emitDynamicCastCheck, int32 emitObjectAccessCheck, int32 allocStackTraceDepth) +BF_EXPORT void BF_CALLTYPE BfSystem_AddTypeOptions(BfSystem* bfSystem, char* filter, int32 simdSetting, int32 optimizationLevel, int32 emitDebugInfo, int32 andFlags, int32 orFlags, int32 allocStackTraceDepth, char* reflectMethodFilter) { AutoCrit autoCrit(bfSystem->mDataLock); BfTypeOptions typeOptions; - String filterStr = filter; - int idx = 0; - while (true) + auto _ParseFilters = [&](char* filter, Array& filters, Array& attributeFilters) { - int semiIdx = (int)filterStr.IndexOf(';', idx); - String newFilter; - if (semiIdx == -1) - newFilter = filterStr.Substring(idx); - else - newFilter = filterStr.Substring(idx, semiIdx - idx); - newFilter.Trim(); - if (!newFilter.IsEmpty()) + String filterStr = filter; + int idx = 0; + while (true) { - if (newFilter.StartsWith('[')) - { - newFilter.Remove(0); - if (newFilter.EndsWith(']')) - newFilter.Remove(newFilter.length() - 1); - newFilter.Trim(); - typeOptions.mAttributeFilters.Add(newFilter); - } + int semiIdx = (int)filterStr.IndexOf(';', idx); + String newFilter; + if (semiIdx == -1) + newFilter = filterStr.Substring(idx); else - typeOptions.mTypeFilters.Add(newFilter); - } + newFilter = filterStr.Substring(idx, semiIdx - idx); + newFilter.Trim(); + if (!newFilter.IsEmpty()) + { + if (newFilter.StartsWith('[')) + { + newFilter.Remove(0); + if (newFilter.EndsWith(']')) + newFilter.Remove(newFilter.length() - 1); + newFilter.Trim(); + attributeFilters.Add(newFilter); + } + else + filters.Add(newFilter); + } + + if (semiIdx == -1) + break; + idx = semiIdx + 1; + } + }; + + _ParseFilters(filter, typeOptions.mTypeFilters, typeOptions.mAttributeFilters); - if (semiIdx == -1) - break; - idx = semiIdx + 1; - } if ((typeOptions.mTypeFilters.IsEmpty()) && (typeOptions.mAttributeFilters.IsEmpty())) return; typeOptions.mSIMDSetting = simdSetting; typeOptions.mOptimizationLevel = optimizationLevel; typeOptions.mEmitDebugInfo = emitDebugInfo; - typeOptions.mRuntimeChecks = (BfOptionalBool)runtimeChecks; - typeOptions.mInitLocalVariables = (BfOptionalBool)initLocalVariables; - typeOptions.mEmitDynamicCastCheck = (BfOptionalBool)emitDynamicCastCheck; - typeOptions.mEmitObjectAccessCheck = (BfOptionalBool)emitObjectAccessCheck; + typeOptions.mAndFlags = (BfOptionFlags)andFlags; + typeOptions.mOrFlags = (BfOptionFlags)orFlags; typeOptions.mAllocStackTraceDepth = allocStackTraceDepth; + + _ParseFilters(reflectMethodFilter, typeOptions.mReflectMethodFilters, typeOptions.mReflectMethodAttributeFilters); + bfSystem->mTypeOptions.push_back(typeOptions); } diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index 1559f110..0f6289d4 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -180,7 +180,8 @@ enum BfTypeFlags // BfTypeFlags_WantsMarking = 0x8000, BfTypeFlags_Delegate = 0x10000, - BfTypeFlags_HasDestructor = 0x20000, + BfTypeFlags_Function = 0x20000, + BfTypeFlags_HasDestructor = 0x40000, }; enum BfObjectFlags : uint8 @@ -1267,11 +1268,23 @@ public: void WriteErrorSummary(); }; -enum BfOptionalBool +enum BfOptionFlags { - BfOptionalBool_NotSet = -1, - BfOptionalBool_False = 0, - BfOptionalBool_True = 1 + BfOptionFlags_None = 0, + BfOptionFlags_RuntimeChecks = 1, + BfOptionFlags_InitLocalVariables = 2, + BfOptionFlags_EmitDynamicCastCheck = 4, + BfOptionFlags_EmitObjectAccessCheck = 8, + + BfOptionFlags_ReflectAlwaysIncludeType = 0x10, + BfOptionFlags_ReflectAlwaysIncludeAll = 0x20, + BfOptionFlags_ReflectAssumeInstantiated = 0x40, + BfOptionFlags_ReflectStaticFields = 0x80, + BfOptionFlags_ReflectNonStaticFields = 0x100, + BfOptionFlags_ReflectStaticMethods = 0x200, + BfOptionFlags_ReflectNonStaticMethods = 0x400, + BfOptionFlags_ReflectConstructors = 0x800, + }; class BfTypeOptions @@ -1283,10 +1296,10 @@ public: int mSIMDSetting; int mOptimizationLevel; int mEmitDebugInfo; - BfOptionalBool mRuntimeChecks; - BfOptionalBool mInitLocalVariables; - BfOptionalBool mEmitDynamicCastCheck; - BfOptionalBool mEmitObjectAccessCheck; + BfOptionFlags mAndFlags; + BfOptionFlags mOrFlags; + Array mReflectMethodFilters; + Array mReflectMethodAttributeFilters; int mAllocStackTraceDepth; public: @@ -1295,13 +1308,14 @@ public: if (applyVal != -1) return applyVal; return val; - } + } - static bool Apply(bool val, BfOptionalBool applyVal) + bool Apply(bool val, BfOptionFlags flags) { - if (applyVal != BfOptionalBool_NotSet) - return applyVal == BfOptionalBool_True; - return val; + if (val) + return (mAndFlags & flags) != 0; + else + return (mOrFlags & flags) != 0; } }; diff --git a/IDEHelper/Tests/src/Generics.bf b/IDEHelper/Tests/src/Generics.bf index 1c539617..e009e2b7 100644 --- a/IDEHelper/Tests/src/Generics.bf +++ b/IDEHelper/Tests/src/Generics.bf @@ -47,6 +47,20 @@ namespace Tests } } + class Singleton where T : Singleton + { + public static T mInstance; + + protected this() + { + mInstance = (T)this; + } + } + + class ClassC : Singleton + { + } + static void DoDispose(mut T val) where T : IDisposable { val.Dispose(); @@ -119,6 +133,9 @@ namespace Tests Test.Assert(MethodA("") == 1); Test.Assert(MethodA(1.2f) == 2); Test.Assert(MethodA(TypeCode.Boolean) == 3); + + ClassC cc = scope .(); + Test.Assert(ClassC.mInstance == cc); } }