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

Throwing error on member references with ".." cascade token outside invocations (ie: "ts..mA = 123") Fixed 'Thread.ModuleTLSIndex' error - which caused us TLS lookup failures in Beef DLLs Fixed some hotswap errors Made BeefPerf shut down properly Fixed an 'int literal' FixIntUnknown issue where rhs was System.Object which caused an illegal boxing Fixed COFF::LocateSymbol issues with Win32 and also with linking to static libraries - showed up with hot-linking in fmod when hot-adding a floating point mod Fixed a couple memory leaks Fixed alignment issue in COFF::ParseCompileUnit
10626 lines
352 KiB
C++
10626 lines
352 KiB
C++
#include "BeefySysLib/util/AllocDebug.h"
|
|
|
|
#include "BfCompiler.h"
|
|
#include "BfSystem.h"
|
|
#include "BfParser.h"
|
|
#include "BfCodeGen.h"
|
|
#include "BfExprEvaluator.h"
|
|
#include <fcntl.h>
|
|
#include "BfConstResolver.h"
|
|
#include "BfMangler.h"
|
|
#include "BeefySysLib/util/PerfTimer.h"
|
|
#include "BeefySysLib/util/BeefPerf.h"
|
|
#include "BfSourceClassifier.h"
|
|
#include "BfAutoComplete.h"
|
|
#include "BfDemangler.h"
|
|
#include "BfResolvePass.h"
|
|
#include "BfFixits.h"
|
|
#include "BfIRCodeGen.h"
|
|
#include "BfDefBuilder.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
int32 GetNumLowZeroBits(int32 n)
|
|
{
|
|
if (n == 0)
|
|
return 32;
|
|
|
|
int i = 0;
|
|
while ((n & 1) == 0)
|
|
{
|
|
n = (int32)((uint32)n >> 1);
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
USING_NS_BF;
|
|
|
|
BfGenericExtensionEntry* BfModule::BuildGenericExtensionInfo(BfGenericTypeInstance* genericTypeInst, BfTypeDef* partialTypeDef)
|
|
{
|
|
if (!partialTypeDef->IsExtension())
|
|
return NULL;
|
|
|
|
if (partialTypeDef->mGenericParamDefs.size() != genericTypeInst->mTypeGenericArguments.size())
|
|
{
|
|
AssertErrorState();
|
|
return NULL;
|
|
}
|
|
|
|
BfGenericExtensionInfo* genericExtensionInfo = genericTypeInst->mGenericExtensionInfo;
|
|
if (genericExtensionInfo == NULL)
|
|
{
|
|
genericExtensionInfo = new BfGenericExtensionInfo();
|
|
genericTypeInst->mGenericExtensionInfo = genericExtensionInfo;
|
|
}
|
|
|
|
BfTypeState typeState;
|
|
typeState.mCurTypeDef = partialTypeDef;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
|
|
//auto genericExEntry = new BfGenericExtensionEntry();
|
|
//auto insertPair = genericExtensionInfo->mExtensionMap.insert(std::make_pair(partialTypeDef, BfGenericExtensionEntry()));
|
|
//auto genericExEntry = &insertPair.first->second;
|
|
|
|
BfGenericExtensionEntry* genericExEntry;
|
|
genericExtensionInfo->mExtensionMap.TryAdd(partialTypeDef, NULL, &genericExEntry);
|
|
|
|
int startDefGenericParamIdx = (int)genericExEntry->mGenericParams.size();
|
|
for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
|
|
{
|
|
auto genericParamInstance = new BfGenericTypeParamInstance(partialTypeDef, paramIdx);
|
|
genericExEntry->mGenericParams.push_back(genericParamInstance);
|
|
}
|
|
|
|
for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
|
|
{
|
|
auto genericParamInstance = genericExEntry->mGenericParams[paramIdx];
|
|
|
|
auto rootGenericParamInstance = genericTypeInst->mGenericParams[paramIdx];
|
|
genericParamInstance->mTypeConstraint = rootGenericParamInstance->mTypeConstraint;
|
|
genericParamInstance->mInterfaceConstraints = rootGenericParamInstance->mInterfaceConstraints;
|
|
genericParamInstance->mGenericParamFlags |= rootGenericParamInstance->mGenericParamFlags;
|
|
|
|
ResolveGenericParamConstraints(genericParamInstance, partialTypeDef->mGenericParamDefs, paramIdx);
|
|
}
|
|
|
|
for (auto genericParam : genericExEntry->mGenericParams)
|
|
{
|
|
for (auto constraintTypeInst : genericParam->mInterfaceConstraints)
|
|
AddDependency(constraintTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint);
|
|
if (genericParam->mTypeConstraint != NULL)
|
|
AddDependency(genericParam->mTypeConstraint, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint);
|
|
}
|
|
return genericExEntry;
|
|
}
|
|
|
|
bool BfModule::BuildGenericParams(BfType* resolvedTypeRef)
|
|
{
|
|
BfTypeState typeState;
|
|
typeState.mBuildingGenericParams = true;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
|
|
BF_ASSERT(mCurMethodInstance == NULL);
|
|
|
|
auto genericTypeInst = (BfGenericTypeInstance*)resolvedTypeRef;
|
|
|
|
if (genericTypeInst->mTypeGenericArguments[0]->IsGenericParam())
|
|
{
|
|
BF_ASSERT(genericTypeInst->mIsUnspecialized);
|
|
}
|
|
|
|
auto typeDef = genericTypeInst->mTypeDef;
|
|
int startDefGenericParamIdx = (int)genericTypeInst->mGenericParams.size();
|
|
for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
|
|
{
|
|
auto genericParamInstance = new BfGenericTypeParamInstance(typeDef, paramIdx);
|
|
genericTypeInst->mGenericParams.push_back(genericParamInstance);
|
|
}
|
|
|
|
if (!typeDef->mPartials.empty())
|
|
{
|
|
for (auto partialTypeDef : typeDef->mPartials)
|
|
{
|
|
if (!partialTypeDef->IsExtension())
|
|
{
|
|
typeState.mCurTypeDef = partialTypeDef;
|
|
for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
|
|
{
|
|
auto genericParamDef = typeDef->mGenericParamDefs[paramIdx];
|
|
auto genericParamInstance = genericTypeInst->mGenericParams[paramIdx];
|
|
ResolveGenericParamConstraints(genericParamInstance, typeDef->mGenericParamDefs, paramIdx);
|
|
|
|
for (auto nameNode : genericParamDef->mNameNodes)
|
|
{
|
|
HandleTypeGenericParamRef(nameNode, typeDef, paramIdx);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto genericExEntry = BuildGenericExtensionInfo(genericTypeInst, partialTypeDef);
|
|
if (genericExEntry == NULL)
|
|
continue;
|
|
if (!genericTypeInst->IsUnspecializedType())
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
for (int paramIdx = 0; paramIdx < genericExEntry->mGenericParams.size(); paramIdx++)
|
|
{
|
|
auto genericParamInstance = genericExEntry->mGenericParams[paramIdx];
|
|
BfGenericParamSource genericParamSource;
|
|
genericParamSource.mCheckAccessibility = false;
|
|
genericParamSource.mTypeInstance = genericTypeInst;
|
|
BfError* error = NULL;
|
|
if (!CheckGenericConstraints(genericParamSource, genericTypeInst->mTypeGenericArguments[paramIdx], NULL, genericParamInstance, NULL, &error))
|
|
{
|
|
genericExEntry->mConstraintsPassed = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int paramIdx = startDefGenericParamIdx; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
|
|
{
|
|
auto genericParamInstance = genericTypeInst->mGenericParams[paramIdx];
|
|
ResolveGenericParamConstraints(genericParamInstance, typeDef->mGenericParamDefs, paramIdx);
|
|
auto genericParamDef = typeDef->mGenericParamDefs[paramIdx];
|
|
|
|
for (auto nameNode : genericParamDef->mNameNodes)
|
|
{
|
|
HandleTypeGenericParamRef(nameNode, typeDef, paramIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto genericParam : genericTypeInst->mGenericParams)
|
|
{
|
|
for (auto constraintTypeInst : genericParam->mInterfaceConstraints)
|
|
AddDependency(constraintTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint);
|
|
if (genericParam->mTypeConstraint != NULL)
|
|
AddDependency(genericParam->mTypeConstraint, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BfModule::ValidateGenericConstraints(BfTypeReference* typeRef, BfGenericTypeInstance* genericTypeInst, bool ignoreErrors)
|
|
{
|
|
if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsTypeAlias()))
|
|
{
|
|
// Don't validate constraints during the population of a concrete generic type alias instance, we want to
|
|
// throw those errors at the usage sites
|
|
return true;
|
|
}
|
|
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, mIgnoreErrors || ignoreErrors);
|
|
genericTypeInst->mValidatedGenericConstraints = true;
|
|
|
|
if (genericTypeInst->IsTypeAlias())
|
|
{
|
|
auto underlyingType = genericTypeInst->GetUnderlyingType();
|
|
if ((underlyingType != NULL) && (underlyingType->IsGenericTypeInstance()))
|
|
return ValidateGenericConstraints(typeRef, (BfGenericTypeInstance*)underlyingType, ignoreErrors);
|
|
return true;
|
|
}
|
|
|
|
auto typeDef = genericTypeInst->mTypeDef;
|
|
for (int paramIdx = 0; paramIdx < (int)genericTypeInst->mTypeGenericArguments.size(); paramIdx++)
|
|
{
|
|
auto genericParamInstance = genericTypeInst->mGenericParams[paramIdx];
|
|
// Why did we remove this line? This breaks determining compatibility of one unspecialized type to another unspecialized type, called from ResolveTypeResult
|
|
//if (!genericTypeInst->mIsUnspecialized)
|
|
{
|
|
BfError* error = NULL;
|
|
if (!CheckGenericConstraints(BfGenericParamSource(genericTypeInst), genericTypeInst->mTypeGenericArguments[paramIdx], typeRef, genericParamInstance, NULL, &error))
|
|
{
|
|
genericTypeInst->mHadValidateErrors = true;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BfModule::AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGenericParamInstance* checkOuter)
|
|
{
|
|
// Added new flags?
|
|
if ((checkInner->mGenericParamFlags | checkOuter->mGenericParamFlags) != checkOuter->mGenericParamFlags)
|
|
{
|
|
// If the outer had a type flag and the inner has a specific type constraint, then see if those are compatible
|
|
auto outerFlags = checkOuter->mGenericParamFlags;
|
|
if (checkOuter->mTypeConstraint != NULL)
|
|
{
|
|
if (checkOuter->mTypeConstraint->IsStruct())
|
|
outerFlags |= BfGenericParamFlag_Struct;
|
|
else if (checkOuter->mTypeConstraint->IsStructOrStructPtr())
|
|
outerFlags |= BfGenericParamFlag_StructPtr;
|
|
else if (checkOuter->mTypeConstraint->IsObject())
|
|
outerFlags |= BfGenericParamFlag_Class;
|
|
}
|
|
|
|
if ((checkInner->mGenericParamFlags | outerFlags) != outerFlags)
|
|
return false;
|
|
}
|
|
|
|
if (checkInner->mTypeConstraint != NULL)
|
|
{
|
|
if (checkOuter->mTypeConstraint == NULL)
|
|
return false;
|
|
if (!TypeIsSubTypeOf(checkInner->mTypeConstraint->ToTypeInstance(), checkOuter->mTypeConstraint->ToTypeInstance()))
|
|
return false;
|
|
}
|
|
|
|
for (auto& innerIFace : checkInner->mInterfaceConstraints)
|
|
{
|
|
if (!checkOuter->mInterfaceConstraints.Contains(innerIFace))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BfModule::ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef)
|
|
{
|
|
if (firstDeclaringTypeDef == secondDeclaringTypeDef)
|
|
return false;
|
|
|
|
// Since we will use shared debugging info, we won't be able to differentiate between these two fields.
|
|
// If we created per-target debug info then we could "fix" this.
|
|
// Can these projects even see each other?
|
|
if ((!firstDeclaringTypeDef->mProject->ContainsReference(secondDeclaringTypeDef->mProject)) &&
|
|
(!secondDeclaringTypeDef->mProject->ContainsReference(firstDeclaringTypeDef->mProject)))
|
|
return true;
|
|
|
|
if (typeInst->IsUnspecializedType())
|
|
{
|
|
bool alwaysCoincide = true;
|
|
|
|
auto genericTypeInst = (BfGenericTypeInstance*)typeInst;
|
|
if (genericTypeInst->mGenericExtensionInfo != NULL)
|
|
{
|
|
auto firstConstraints = genericTypeInst->GetGenericParamsVector(firstDeclaringTypeDef);
|
|
auto secondConstraints = genericTypeInst->GetGenericParamsVector(secondDeclaringTypeDef);
|
|
|
|
for (int genericIdx = 0; genericIdx < (int)firstConstraints->size(); genericIdx++)
|
|
{
|
|
auto firstConstraint = (*firstConstraints)[genericIdx];
|
|
auto secondConstraint = (*secondConstraints)[genericIdx];
|
|
|
|
if ((!AreConstraintsSubset(firstConstraint, secondConstraint)) &&
|
|
(!AreConstraintsSubset(secondConstraint, firstConstraint)))
|
|
alwaysCoincide = false;
|
|
}
|
|
}
|
|
|
|
// Only show an error if we are certain both members will always appear at the same time
|
|
if (!alwaysCoincide)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType)
|
|
{
|
|
BP_ZONE("BfModule::InitType");
|
|
|
|
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, resolvedTypeRef->ToTypeInstance());
|
|
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, NULL);
|
|
|
|
if (mCompiler->mHotState != NULL)
|
|
mCompiler->mHotState->mHasNewTypes = true;
|
|
|
|
auto typeInst = resolvedTypeRef->ToTypeInstance();
|
|
if (typeInst != NULL)
|
|
{
|
|
if (typeInst->mBaseType != NULL)
|
|
BF_ASSERT((typeInst->mBaseType->mRebuildFlags & BfTypeRebuildFlag_Deleted) == 0);
|
|
|
|
if ((typeInst->mTypeDef != NULL) && (typeInst->mTypeDef->mDefState == BfTypeDef::DefState_New) &&
|
|
(typeInst->mTypeDef->mNextRevision == NULL))
|
|
{
|
|
mContext->HandleChangedTypeDef(typeInst->mTypeDef);
|
|
typeInst->mTypeDef->mDefState = BfTypeDef::DefState_Defined;
|
|
}
|
|
|
|
typeInst->mIsReified = mIsReified;
|
|
|
|
//BF_ASSERT(typeInst->mTypeDef->mTypeCode != BfTypeCode_Extension);
|
|
|
|
if (resolvedTypeRef->IsTuple())
|
|
{
|
|
auto tupleType = (BfTupleType*)resolvedTypeRef;
|
|
for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
auto fieldInstance = (BfFieldInstance*)&tupleType->mFieldInstances[fieldIdx];
|
|
if (fieldInstance->GetResolvedType()->IsUnspecializedType())
|
|
tupleType->mHasUnspecializedMembers = true;
|
|
}
|
|
}
|
|
|
|
typeInst->mRevision = mCompiler->mRevision;
|
|
if (typeInst->mTypeDef != NULL)
|
|
BF_ASSERT(typeInst->mTypeDef->mDefState != BfTypeDef::DefState_Deleted);
|
|
}
|
|
|
|
if (resolvedTypeRef->IsGenericTypeInstance())
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)resolvedTypeRef;
|
|
for (auto typeGenericArg : genericTypeInst->mTypeGenericArguments)
|
|
BF_ASSERT((typeGenericArg->mRebuildFlags & BfTypeRebuildFlag_Deleted) == 0);
|
|
}
|
|
|
|
if (!mContext->mSavedTypeDataMap.IsEmpty())
|
|
{
|
|
String typeName = BfSafeMangler::Mangle(resolvedTypeRef, this);
|
|
BfSavedTypeData* savedTypeData;
|
|
if (mContext->mSavedTypeDataMap.Remove(typeName, &savedTypeData))
|
|
{
|
|
// if (resolvedTypeRef->mTypeId != -1)
|
|
// {
|
|
// // If we have an ID and it as the last one assigned the roll back the ID counter
|
|
// if (resolvedTypeRef->mTypeId == mCompiler->mCurTypeId - 1)
|
|
// mCompiler->mCurTypeId--;
|
|
// }
|
|
|
|
mContext->mSavedTypeData[savedTypeData->mTypeId] = NULL;
|
|
|
|
resolvedTypeRef->mTypeId = savedTypeData->mTypeId;
|
|
BfLogSysM("Using mSavedTypeData for %p %s\n", resolvedTypeRef, typeName.c_str());
|
|
if (typeInst != NULL)
|
|
{
|
|
if (mCompiler->IsHotCompile())
|
|
{
|
|
BfLogSysM("Using mSavedTypeData HotTypeData %p for %p\n", savedTypeData->mHotTypeData, resolvedTypeRef);
|
|
typeInst->mHotTypeData = savedTypeData->mHotTypeData;
|
|
savedTypeData->mHotTypeData = NULL;
|
|
}
|
|
}
|
|
delete savedTypeData;
|
|
mContext->mTypes[resolvedTypeRef->mTypeId] = resolvedTypeRef;
|
|
}
|
|
else
|
|
{
|
|
BfLogSysM("No mSavedTypeData entry for %p %s\n", resolvedTypeRef, typeName.c_str());
|
|
}
|
|
}
|
|
|
|
resolvedTypeRef->mContext = mContext;
|
|
if (resolvedTypeRef->IsGenericTypeInstance())
|
|
{
|
|
auto genericTypeInstance = (BfGenericTypeInstance*)resolvedTypeRef;
|
|
|
|
// Do it here so the location we attempted to specialize this type will throw the failure if there is one
|
|
if (!BuildGenericParams(resolvedTypeRef))
|
|
return false;
|
|
}
|
|
|
|
BfLogSysM("%p InitType: %s Type: %p TypeDef: %p Revision:%d\n", mContext, TypeToString(resolvedTypeRef).c_str(), resolvedTypeRef, (typeInst != NULL) ? typeInst->mTypeDef : NULL, mCompiler->mRevision);
|
|
|
|
// When we're autocomplete, we can't do the method processing so we have to add this type to the type work list
|
|
if (((populateType < BfPopulateType_Full) || (mCompiler->IsAutocomplete())) /*&& (!resolvedTypeRef->IsUnspecializedTypeVariation())*/ && (resolvedTypeRef->IsTypeInstance()) &&
|
|
(!resolvedTypeRef->IsTypeAlias()))
|
|
{
|
|
BfTypeProcessRequest* typeProcessRequest = mContext->mPopulateTypeWorkList.Alloc();
|
|
typeProcessRequest->mType = resolvedTypeRef;
|
|
BF_ASSERT(resolvedTypeRef->mContext == mContext);
|
|
mCompiler->mStats.mTypesQueued++;
|
|
mCompiler->UpdateCompletion();
|
|
}
|
|
return PopulateType(resolvedTypeRef, populateType);
|
|
}
|
|
|
|
|
|
void BfModule::AddFieldDependency(BfTypeInstance* typeInstance, BfFieldInstance* fieldInstance, BfType* fieldType)
|
|
{
|
|
auto fieldTypeInstance = fieldType->ToTypeInstance();
|
|
|
|
if (fieldTypeInstance == NULL)
|
|
{
|
|
auto underlyingType = fieldType->GetUnderlyingType();
|
|
if (underlyingType != NULL)
|
|
AddFieldDependency(typeInstance, fieldInstance, underlyingType);
|
|
return;
|
|
}
|
|
|
|
auto depFlag = fieldTypeInstance->IsValueType() ? BfDependencyMap::DependencyFlag_ValueTypeMemberData : BfDependencyMap::DependencyFlag_PtrMemberData;
|
|
AddDependency(fieldTypeInstance, typeInstance, depFlag);
|
|
|
|
if ((fieldTypeInstance->IsStruct()) && (fieldTypeInstance->IsGenericTypeInstance()))
|
|
{
|
|
// When we're a generic struct, our data layout can depend on our generic parameters as well
|
|
auto genericTypeInstance = (BfGenericTypeInstance*)fieldTypeInstance;
|
|
for (auto typeGenericArg : genericTypeInstance->mTypeGenericArguments)
|
|
AddFieldDependency(typeInstance, fieldInstance, typeGenericArg);
|
|
}
|
|
}
|
|
|
|
void BfModule::CheckMemberNames(BfTypeInstance* typeInst)
|
|
{
|
|
struct MemberRef
|
|
{
|
|
BfMemberDef* mMemberDef;
|
|
String mName;
|
|
String mKindName;
|
|
BfTypeInstance* mTypeInst;
|
|
BfAstNode* mNameNode;
|
|
BfProtection mProtection;
|
|
BfTypeDef* mDeclaringType;
|
|
bool mIsOverride;
|
|
};
|
|
|
|
SizedArray<MemberRef, 64> memberList;
|
|
|
|
// Check base types first and then current type
|
|
auto checkType = typeInst;
|
|
while (checkType != NULL)
|
|
{
|
|
for (auto prop : checkType->mTypeDef->mProperties)
|
|
{
|
|
BfPropertyDeclaration* propDecl = (BfPropertyDeclaration*)prop->mFieldDeclaration;
|
|
if ((propDecl != NULL) && (propDecl->mExplicitInterface != NULL))
|
|
continue;
|
|
|
|
if (!typeInst->IsTypeMemberIncluded(prop->mDeclaringType))
|
|
continue;
|
|
|
|
MemberRef memberRef;
|
|
memberRef.mMemberDef = prop;
|
|
memberRef.mTypeInst = checkType;
|
|
memberRef.mProtection = prop->mProtection;
|
|
memberRef.mName = prop->mName;
|
|
memberRef.mKindName = "property";
|
|
if (prop->mFieldDeclaration != NULL)
|
|
memberRef.mNameNode = prop->mFieldDeclaration->mNameNode;
|
|
memberRef.mDeclaringType = prop->mDeclaringType;
|
|
auto propertyDeclaration = BfNodeDynCast<BfPropertyDeclaration>(prop->mFieldDeclaration);
|
|
if (propertyDeclaration != NULL)
|
|
memberRef.mIsOverride = (propertyDeclaration->mNewSpecifier != NULL) ||
|
|
((propertyDeclaration->mVirtualSpecifier != NULL) && (propertyDeclaration->mVirtualSpecifier->GetToken() == BfToken_Override));
|
|
memberList.push_back(memberRef);
|
|
}
|
|
|
|
for (auto field : checkType->mTypeDef->mFields)
|
|
{
|
|
if (!typeInst->IsTypeMemberIncluded(field->mDeclaringType))
|
|
continue;
|
|
|
|
MemberRef memberRef;
|
|
memberRef.mMemberDef = field;
|
|
memberRef.mTypeInst = checkType;
|
|
memberRef.mProtection = field->mProtection;
|
|
memberRef.mName = field->mName;
|
|
memberRef.mKindName = "field";
|
|
memberRef.mDeclaringType = field->mDeclaringType;
|
|
if (field->mFieldDeclaration != NULL)
|
|
{
|
|
memberRef.mNameNode = field->mFieldDeclaration->mNameNode;
|
|
memberRef.mIsOverride = field->mFieldDeclaration->mNewSpecifier != NULL;
|
|
}
|
|
memberList.push_back(memberRef);
|
|
}
|
|
|
|
checkType = checkType->mBaseType;
|
|
}
|
|
|
|
Dictionary<String, MemberRef> memberMap;
|
|
memberMap.Reserve(memberList.size());
|
|
|
|
for (int i = (int)memberList.size() - 1; i >= 0; i--)
|
|
{
|
|
MemberRef& memberRef = memberList[i];
|
|
if (memberRef.mName.empty())
|
|
continue;
|
|
if ((memberRef.mTypeInst == typeInst) && (!memberRef.mIsOverride))
|
|
{
|
|
MemberRef* prevMemberRef = NULL;
|
|
if (memberMap.TryGetValue(memberRef.mName, &prevMemberRef))
|
|
{
|
|
//auto& prevMemberRef = itr->second;
|
|
|
|
MemberRef* firstMemberRef = &memberRef;
|
|
MemberRef* secondMemberRef = prevMemberRef;
|
|
bool showPrevious = false;
|
|
|
|
BfError* error = NULL;
|
|
if (prevMemberRef->mTypeInst != typeInst)
|
|
{
|
|
if ((prevMemberRef->mProtection != BfProtection_Private) && (memberRef.mNameNode != NULL))
|
|
{
|
|
error = Warn(BfWarning_CS0108_MemberHidesInherited, StrFormat("%s hides inherited member '%s'. Use the 'new' keyword if hiding was intentional.", prevMemberRef->mKindName.c_str(), memberRef.mName.c_str()), memberRef.mNameNode, true);
|
|
showPrevious = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ShouldAllowMultipleDefinitions(typeInst, firstMemberRef->mDeclaringType, secondMemberRef->mDeclaringType))
|
|
{
|
|
if (firstMemberRef->mMemberDef != NULL)
|
|
{
|
|
firstMemberRef->mMemberDef->mHasMultiDefs = true;
|
|
secondMemberRef->mMemberDef->mHasMultiDefs = true;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
bool wantsSwap = false;
|
|
|
|
if ((secondMemberRef->mNameNode != NULL) && (firstMemberRef->mNameNode != NULL) &&
|
|
(secondMemberRef->mNameNode->GetSourceData() == firstMemberRef->mNameNode->GetSourceData()) &&
|
|
(secondMemberRef->mNameNode->GetSrcStart() < firstMemberRef->mNameNode->GetSrcStart()))
|
|
{
|
|
wantsSwap = true;
|
|
}
|
|
|
|
if (secondMemberRef->mDeclaringType->IsExtension() != firstMemberRef->mDeclaringType->IsExtension())
|
|
{
|
|
wantsSwap = firstMemberRef->mDeclaringType->IsExtension();
|
|
}
|
|
|
|
if (wantsSwap)
|
|
{
|
|
std::swap(firstMemberRef, secondMemberRef);
|
|
}
|
|
|
|
if (secondMemberRef->mNameNode != NULL)
|
|
error = Fail(StrFormat("A %s named '%s' has already been declared.", secondMemberRef->mKindName.c_str(), memberRef.mName.c_str()), secondMemberRef->mNameNode, true);
|
|
showPrevious = true;
|
|
}
|
|
if ((secondMemberRef->mNameNode != NULL) && (error != NULL))
|
|
mCompiler->mPassInstance->MoreInfo("Previous declaration", firstMemberRef->mNameNode);
|
|
}
|
|
}
|
|
memberMap.TryAdd(memberRef.mName, memberRef);
|
|
}
|
|
}
|
|
|
|
void BfModule::TypeFailed(BfTypeInstance* typeInstance)
|
|
{
|
|
BfLogSysM("TypeFailed: %p\n", typeInstance);
|
|
typeInstance->mTypeFailed = true;
|
|
// Punt on field types - just substitute System.Object where we have NULLs
|
|
for (auto& fieldInstance : typeInstance->mFieldInstances)
|
|
{
|
|
if ((fieldInstance.mResolvedType == NULL) || (fieldInstance.mResolvedType->IsNull()))
|
|
{
|
|
fieldInstance.mResolvedType = mContext->mBfObjectType;
|
|
}
|
|
if (fieldInstance.mOwner == NULL)
|
|
fieldInstance.mOwner = typeInstance;
|
|
}
|
|
if (typeInstance->mAlign == -1)
|
|
typeInstance->mAlign = 1;
|
|
if (typeInstance->mSize == -1)
|
|
typeInstance->mSize = 1;
|
|
mContext->mFailTypes.Add(typeInstance);
|
|
mHadBuildError = true;
|
|
}
|
|
|
|
bool BfModule::CheckCircularDataError()
|
|
{
|
|
bool hadError = false;
|
|
|
|
{
|
|
int count = 0;
|
|
auto checkTypeState = mContext->mCurTypeState;
|
|
while (checkTypeState != NULL)
|
|
{
|
|
checkTypeState = checkTypeState->mPrevState;
|
|
count++;
|
|
}
|
|
|
|
if (count > 20)
|
|
{
|
|
NOP;
|
|
}
|
|
}
|
|
|
|
|
|
int checkIdx = 0;
|
|
auto checkTypeState = mContext->mCurTypeState;
|
|
bool isPreBaseCheck = checkTypeState->mPopulateType == BfPopulateType_Declaration;
|
|
while (true)
|
|
{
|
|
if (checkTypeState == NULL)
|
|
return hadError;
|
|
|
|
if (isPreBaseCheck)
|
|
{
|
|
if (checkTypeState->mPopulateType != BfPopulateType_Declaration)
|
|
return hadError;
|
|
}
|
|
else
|
|
{
|
|
if (checkTypeState->mPopulateType == BfPopulateType_Declaration)
|
|
return hadError;
|
|
if ((checkIdx > 0) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL))
|
|
return hadError;
|
|
}
|
|
|
|
if ((checkTypeState->mTypeInstance == mCurTypeInstance) && (checkIdx > 0))
|
|
break;
|
|
checkTypeState = checkTypeState->mPrevState;
|
|
checkIdx++;
|
|
}
|
|
|
|
|
|
checkTypeState = mContext->mCurTypeState->mPrevState;
|
|
while (true)
|
|
{
|
|
if (checkTypeState == NULL)
|
|
return hadError;
|
|
if ((checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL))
|
|
return hadError;
|
|
|
|
// We only get one chance to fire off these errors, they can't be ignored.
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, false);
|
|
|
|
hadError = true;
|
|
if (checkTypeState->mCurAttributeTypeRef != NULL)
|
|
{
|
|
Fail(StrFormat("Attribute type '%s' causes a data cycle", BfTypeUtils::TypeToString(checkTypeState->mCurAttributeTypeRef).c_str()), checkTypeState->mCurAttributeTypeRef, true);
|
|
}
|
|
else if (checkTypeState->mCurBaseTypeRef != NULL)
|
|
{
|
|
Fail(StrFormat("Base type '%s' causes a data cycle", BfTypeUtils::TypeToString(checkTypeState->mCurBaseTypeRef).c_str()), checkTypeState->mCurBaseTypeRef, true);
|
|
}
|
|
else if (checkTypeState->mCurFieldDef->mFieldDeclaration != NULL)
|
|
{
|
|
Fail(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(checkTypeState->mTypeInstance).c_str(), checkTypeState->mCurFieldDef->mName.c_str()),
|
|
checkTypeState->mCurFieldDef->mFieldDeclaration->mTypeRef, true);
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(checkTypeState->mTypeInstance).c_str(), checkTypeState->mCurFieldDef->mName.c_str()));
|
|
}
|
|
|
|
auto module = GetModuleFor(checkTypeState->mTypeInstance);
|
|
if (module != NULL)
|
|
module->TypeFailed(checkTypeState->mTypeInstance);
|
|
else
|
|
checkTypeState->mTypeInstance->mTypeFailed = true;
|
|
|
|
checkTypeState = checkTypeState->mPrevState;
|
|
}
|
|
}
|
|
|
|
bool BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType)
|
|
{
|
|
if ((populateType == BfPopulateType_Declaration) && (resolvedTypeRef->mDefineState >= BfTypeDefineState_Declared))
|
|
return true;
|
|
|
|
// Are we "demanding" to reify a type that is currently resolve-only?
|
|
if (mIsReified)
|
|
{
|
|
if (resolvedTypeRef->IsTypeInstance())
|
|
{
|
|
auto typeModule = resolvedTypeRef->GetModule();
|
|
if ((typeModule != NULL) && (typeModule->mIsSpecialModule))
|
|
{
|
|
auto typeInst = resolvedTypeRef->ToTypeInstance();
|
|
if (!typeInst->mIsReified)
|
|
{
|
|
BfLogSysM("Reifying type %p in scratch module in PopulateType\n", resolvedTypeRef);
|
|
|
|
// It's important for unspecialized types to be in the correct module --
|
|
// when we process their methods, new types will be determined as
|
|
// resolve-only or reified based on the module the unresolved type is in
|
|
BF_ASSERT(typeInst->mModule == mContext->mUnreifiedModule);
|
|
typeInst->mIsReified = true;
|
|
typeInst->mModule = mContext->mScratchModule;
|
|
|
|
// Why did we need to do this at all? Why is just marking the type as reified not enough?
|
|
// This causes issues where we may delete a method instance that is currently being used as the generic bindings for
|
|
// a method of a specialized generic type
|
|
// if (typeInst->IsOnDemand())
|
|
// {
|
|
// RebuildMethods(typeInst);
|
|
// }
|
|
// else
|
|
// mContext->RebuildType(typeInst, false, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((typeModule != NULL) && (!typeModule->mIsReified) && (!typeModule->mReifyQueued))
|
|
{
|
|
BF_ASSERT((mCompiler->mCompileState != BfCompiler::CompileState_Unreified) && (mCompiler->mCompileState != BfCompiler::CompileState_VData));
|
|
|
|
BfLogSysM("Queued reification of type %p in module %p in PopulateType\n", resolvedTypeRef, typeModule);
|
|
|
|
BF_ASSERT(!typeModule->mIsSpecialModule);
|
|
// This caused issues - we may need to reify a type and then request a method
|
|
typeModule->mReifyQueued = true;
|
|
mContext->mReifyModuleWorkList.Add(typeModule);
|
|
//typeModule->ReifyModule();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!resolvedTypeRef->IsIncomplete())
|
|
return true;
|
|
|
|
auto typeInstance = resolvedTypeRef->ToTypeInstance();
|
|
if ((typeInstance != NULL) && (typeInstance->mTypeDef != NULL))
|
|
{
|
|
if (typeInstance->mTypeDef->mNextRevision != NULL)
|
|
{
|
|
// It's possible that our main compiler thread is generating a new typedef while we're autocompleting. This handles that case...
|
|
if (typeInstance->mDefineState == BfTypeDefineState_Undefined)
|
|
{
|
|
if (typeInstance->IsBoxed())
|
|
{
|
|
BfBoxedType* boxedType = (BfBoxedType*)typeInstance;
|
|
BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance();
|
|
PopulateType(innerType, BfPopulateType_Data);
|
|
}
|
|
else
|
|
{
|
|
mContext->HandleChangedTypeDef(typeInstance->mTypeDef);
|
|
mSystem->InjectNewRevision(typeInstance->mTypeDef);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(mCompiler->IsAutocomplete());
|
|
}
|
|
}
|
|
if ((!typeInstance->IsDeleting()) && (!mCompiler->IsAutocomplete()))
|
|
BF_ASSERT(typeInstance->mTypeDef->mDefState == BfTypeDef::DefState_Defined);
|
|
}
|
|
|
|
BF_ASSERT((resolvedTypeRef->mRebuildFlags & (BfTypeRebuildFlag_Deleted | BfTypeRebuildFlag_DeleteQueued)) == 0);
|
|
|
|
/*BfTypeRebuildFlags allowedFlags = (BfTypeRebuildFlags)(BfTypeRebuildFlag_AddedToWorkList | BfTypeRebuildFlag_AwaitingReference | BfTypeRebuildFlag_UnderlyingTypeDeferred);
|
|
if ((resolvedTypeRef->mRebuildFlags & ~allowedFlags) != 0)
|
|
{
|
|
// BfContext::UpdateAfterDeletingTypes should clear out all flags except for the Deleted flag
|
|
// If this type was deleted then we should never be able to reach PopulateType here.
|
|
// This may happen if dependent types were not properly rebuilt when a used type
|
|
// was deleted.
|
|
auto hadFlags = resolvedTypeRef->mRebuildFlags;
|
|
BF_ASSERT((resolvedTypeRef->mRebuildFlags & ~allowedFlags) == 0);
|
|
resolvedTypeRef->mRebuildFlags = (BfTypeRebuildFlags)(resolvedTypeRef->mRebuildFlags & ~allowedFlags);
|
|
}*/
|
|
|
|
bool isNew = resolvedTypeRef->mDefineState == BfTypeDefineState_Undefined;
|
|
if (isNew)
|
|
{
|
|
BP_ZONE("BfModule::PopulateType");
|
|
if (resolvedTypeRef->mTypeId == -1)
|
|
{
|
|
mCompiler->mTypeInitCount++;
|
|
|
|
auto typeInstance = resolvedTypeRef->ToTypeInstance();
|
|
|
|
if (!mCompiler->mTypeIdFreeList.IsEmpty())
|
|
{
|
|
resolvedTypeRef->mTypeId = mCompiler->mTypeIdFreeList.back();
|
|
mCompiler->mTypeIdFreeList.pop_back();
|
|
}
|
|
else
|
|
resolvedTypeRef->mTypeId = mCompiler->mCurTypeId++;
|
|
|
|
while (resolvedTypeRef->mTypeId >= (int)mContext->mTypes.size())
|
|
mContext->mTypes.Add(NULL);
|
|
mContext->mTypes[resolvedTypeRef->mTypeId] = resolvedTypeRef;
|
|
|
|
if (typeInstance != NULL)
|
|
{
|
|
typeInstance->mSignatureRevision = mCompiler->mRevision;
|
|
typeInstance->mLastNonGenericUsedRevision = mCompiler->mRevision;
|
|
}
|
|
}
|
|
|
|
BfLogSysM("PopulateType: %p %s populateType:%d ResolveOnly:%d Reified:%d AutoComplete:%d Ctx:%p Mod:%p TypeId:%d\n", resolvedTypeRef, TypeToString(resolvedTypeRef, BfTypeNameFlags_None).c_str(), populateType, mCompiler->mIsResolveOnly, mIsReified, mCompiler->IsAutocomplete(), mContext, this, resolvedTypeRef->mTypeId);
|
|
|
|
BF_ASSERT(!resolvedTypeRef->IsDeleting());
|
|
}
|
|
|
|
if (resolvedTypeRef->IsRef())
|
|
{
|
|
BfRefType* refType = (BfRefType*)resolvedTypeRef;
|
|
if (refType->mElementType->IsValueType())
|
|
{
|
|
PopulateType(refType->mElementType, populateType);
|
|
resolvedTypeRef->mDefineState = refType->mElementType->mDefineState;
|
|
}
|
|
else
|
|
{
|
|
PopulateType(refType->mElementType, BfPopulateType_Identity);
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
}
|
|
refType->mSize = refType->mAlign = mSystem->mPtrSize;
|
|
return true;
|
|
}
|
|
|
|
if (resolvedTypeRef->IsTypeAlias())
|
|
{
|
|
auto typeAlias = (BfTypeInstance*)resolvedTypeRef;
|
|
SetAndRestoreValue<BfTypeInstance*> prevCurType(mCurTypeInstance, typeAlias);
|
|
auto typeDef = typeAlias->mTypeDef;
|
|
auto typeAliasDecl = (BfTypeAliasDeclaration*)typeDef->mTypeDeclaration;
|
|
BfType* aliasToType = NULL;
|
|
|
|
BfTypeState typeState(mCurTypeInstance, mContext->mCurTypeState);
|
|
typeState.mPopulateType = populateType;
|
|
typeState.mCurBaseTypeRef = typeAliasDecl->mAliasToType;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
if (!CheckCircularDataError())
|
|
{
|
|
if (typeAliasDecl->mAliasToType != NULL)
|
|
aliasToType = ResolveTypeRef(typeAliasDecl->mAliasToType, BfPopulateType_IdentityNoRemapAlias);
|
|
}
|
|
//typeAlias->mModule = mContext->mScratchModule;
|
|
typeAlias->mTypeIncomplete = false;
|
|
typeAlias->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotted;
|
|
if (aliasToType != NULL)
|
|
{
|
|
AddDependency(aliasToType, typeAlias, BfDependencyMap::DependencyFlag_DerivedFrom);
|
|
}
|
|
else
|
|
mContext->mFailTypes.Add(typeAlias);
|
|
|
|
if (typeAlias->mTypeFailed)
|
|
aliasToType = NULL;
|
|
|
|
if (resolvedTypeRef->IsGenericTypeInstance())
|
|
((BfGenericTypeAliasType*)resolvedTypeRef)->mAliasToType = aliasToType;
|
|
else
|
|
((BfTypeAliasType*)resolvedTypeRef)->mAliasToType = aliasToType;
|
|
|
|
if (aliasToType != NULL)
|
|
{
|
|
resolvedTypeRef->mSize = aliasToType->mSize;
|
|
resolvedTypeRef->mAlign = aliasToType->mAlign;
|
|
|
|
if (auto aliasToTypeInst = aliasToType->ToTypeInstance())
|
|
{
|
|
typeAlias->mInstSize = aliasToTypeInst->mInstSize;
|
|
typeAlias->mInstAlign = aliasToTypeInst->mInstAlign;
|
|
}
|
|
else
|
|
{
|
|
typeAlias->mInstSize = aliasToType->mSize;
|
|
typeAlias->mInstAlign = aliasToType->mAlign;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resolvedTypeRef->mSize = 0;
|
|
resolvedTypeRef->mAlign = 1;
|
|
typeAlias->mInstSize = 0;
|
|
typeAlias->mInstAlign = 1;
|
|
}
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotted;
|
|
resolvedTypeRef->mRebuildFlags = BfTypeRebuildFlag_None;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (resolvedTypeRef->IsSizedArray())
|
|
{
|
|
resolvedTypeRef->mRevision = mRevision;
|
|
|
|
BfSizedArrayType* arrayType = (BfSizedArrayType*)resolvedTypeRef;
|
|
auto elementType = arrayType->mElementType;
|
|
if (elementType->IsValueType())
|
|
{
|
|
PopulateType(arrayType->mElementType, BfPopulateType_Data);
|
|
resolvedTypeRef->mDefineState = arrayType->mElementType->mDefineState;
|
|
AddDependency(elementType, resolvedTypeRef, BfDependencyMap::DependencyFlag_ValueTypeMemberData);
|
|
}
|
|
else
|
|
{
|
|
PopulateType(arrayType->mElementType, BfPopulateType_Identity);
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
AddDependency(elementType, resolvedTypeRef, BfDependencyMap::DependencyFlag_PtrMemberData);
|
|
}
|
|
if (arrayType->mElementCount > 0)
|
|
{
|
|
arrayType->mSize = (arrayType->mElementType->GetStride() * ((int)arrayType->mElementCount - 1)) + arrayType->mElementType->mSize;
|
|
arrayType->mAlign = std::max((int32)arrayType->mElementType->mAlign, 1);
|
|
}
|
|
else
|
|
{
|
|
arrayType->mSize = 0;
|
|
arrayType->mAlign = 1;
|
|
}
|
|
|
|
arrayType->mWantsGCMarking = elementType->WantsGCMarking();
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotted;
|
|
resolvedTypeRef->mRebuildFlags = BfTypeRebuildFlag_None;
|
|
|
|
bool isValueless = arrayType->IsValuelessType();
|
|
return true;
|
|
}
|
|
|
|
if (isNew)
|
|
{
|
|
BfTypeDef* typeDef = NULL;
|
|
if (typeInstance != NULL)
|
|
{
|
|
if ((populateType == BfPopulateType_Data) && (typeInstance->mNeedsMethodProcessing))
|
|
return true;
|
|
typeDef = typeInstance->mTypeDef;
|
|
}
|
|
|
|
if (resolvedTypeRef->IsMethodRef())
|
|
return true;
|
|
|
|
if (resolvedTypeRef->IsPointer())
|
|
{
|
|
BfPointerType* pointerType = (BfPointerType*)resolvedTypeRef;
|
|
if (pointerType->mElementType->IsIncomplete())
|
|
PopulateType(pointerType->mElementType, BfPopulateType_Declaration);
|
|
pointerType->mSize = pointerType->mAlign = mSystem->mPtrSize;
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
return true;
|
|
}
|
|
|
|
if (resolvedTypeRef->IsGenericParam())
|
|
{
|
|
BfGenericParamType* genericParamType = (BfGenericParamType*)resolvedTypeRef;
|
|
PopulateType(mContext->mBfObjectType);
|
|
genericParamType->mSize = mContext->mBfObjectType->mSize;
|
|
genericParamType->mAlign = mContext->mBfObjectType->mAlign;
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
return true;
|
|
}
|
|
|
|
if (resolvedTypeRef->IsRetTypeType())
|
|
{
|
|
BfRetTypeType* retTypeType = (BfRetTypeType*)resolvedTypeRef;
|
|
BF_ASSERT(retTypeType->mElementType->IsGenericParam());
|
|
resolvedTypeRef->mSize = mContext->mBfObjectType->mSize;
|
|
resolvedTypeRef->mAlign = mContext->mBfObjectType->mAlign;
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
return true;
|
|
}
|
|
|
|
if (resolvedTypeRef->IsConcreteInterfaceType())
|
|
{
|
|
BfConcreteInterfaceType* concreteInterfaceType = (BfConcreteInterfaceType*)resolvedTypeRef;
|
|
BF_ASSERT(concreteInterfaceType->mInterface->IsInterface());
|
|
resolvedTypeRef->mSize = concreteInterfaceType->mInterface->mSize;
|
|
resolvedTypeRef->mAlign = concreteInterfaceType->mInterface->mAlign;
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
return true;
|
|
}
|
|
|
|
if (resolvedTypeRef->IsConstExprValue())
|
|
{
|
|
resolvedTypeRef->mSize = 0;
|
|
resolvedTypeRef->mAlign = 0;
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
return true;
|
|
}
|
|
|
|
// The autocomplete pass doesn't need to do the method processing, allow type to be (partially) incomplete
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL) &&
|
|
(typeInstance != NULL) && (typeInstance->mNeedsMethodProcessing) && (!typeInstance->IsDelegate()))
|
|
return true;
|
|
|
|
BfPrimitiveType* primitiveType = NULL;
|
|
if (typeInstance == NULL)
|
|
{
|
|
BF_ASSERT(resolvedTypeRef->IsPrimitiveType());
|
|
primitiveType = (BfPrimitiveType*)resolvedTypeRef;
|
|
typeDef = primitiveType->mTypeDef;
|
|
}
|
|
|
|
#define PRIMITIVE_TYPE(name, llvmType, size, dType) \
|
|
primitiveType->mSize = primitiveType->mAlign = size; \
|
|
primitiveType->mDefineState = BfTypeDefineState_Defined;
|
|
|
|
switch (typeDef->mTypeCode)
|
|
{
|
|
case BfTypeCode_None:
|
|
primitiveType->mSize = primitiveType->mAlign = 0;
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
return true;
|
|
case BfTypeCode_Self:
|
|
case BfTypeCode_Dot:
|
|
case BfTypeCode_Var:
|
|
case BfTypeCode_Let:
|
|
{
|
|
auto objType = mContext->mBfObjectType;
|
|
primitiveType->mSize = objType->mSize;
|
|
primitiveType->mAlign = objType->mAlign;
|
|
resolvedTypeRef->mDefineState = BfTypeDefineState_Defined;
|
|
}
|
|
return true;
|
|
case BfTypeCode_NullPtr:
|
|
primitiveType->mSize = primitiveType->mAlign = mSystem->mPtrSize;
|
|
primitiveType->mDefineState = BfTypeDefineState_Defined;
|
|
return true;
|
|
case BfTypeCode_Boolean:
|
|
PRIMITIVE_TYPE("bool", Int1, 1, DW_ATE_boolean);
|
|
return true;
|
|
case BfTypeCode_Int8:
|
|
PRIMITIVE_TYPE("sbyte", Int8, 1, DW_ATE_signed);
|
|
return true;
|
|
case BfTypeCode_UInt8:
|
|
PRIMITIVE_TYPE("byte", Int8, 1, DW_ATE_unsigned);
|
|
return true;
|
|
case BfTypeCode_Int16:
|
|
PRIMITIVE_TYPE("short", Int16, 2, DW_ATE_signed);
|
|
return true;
|
|
case BfTypeCode_UInt16:
|
|
PRIMITIVE_TYPE("ushort", Int16, 2, DW_ATE_unsigned);
|
|
return true;
|
|
case BfTypeCode_Int32:
|
|
PRIMITIVE_TYPE("int", Int32, 4, DW_ATE_signed);
|
|
return true;
|
|
case BfTypeCode_UInt32:
|
|
PRIMITIVE_TYPE("uint", Int32, 4, DW_ATE_unsigned);
|
|
return true;
|
|
case BfTypeCode_Int64:
|
|
PRIMITIVE_TYPE("long", Int64, 8, DW_ATE_signed);
|
|
return true;
|
|
case BfTypeCode_UInt64:
|
|
PRIMITIVE_TYPE("ulong", Int64, 8, DW_ATE_unsigned);
|
|
return true;
|
|
case BfTypeCode_IntPtr:
|
|
if (mSystem->mPtrSize == 4)
|
|
{
|
|
PRIMITIVE_TYPE("intptr", Int32, 4, DW_ATE_signed);
|
|
}
|
|
else
|
|
{
|
|
PRIMITIVE_TYPE("intptr", Int64, 8, DW_ATE_signed);
|
|
}
|
|
return true;
|
|
case BfTypeCode_UIntPtr:
|
|
if (mSystem->mPtrSize == 4)
|
|
{
|
|
PRIMITIVE_TYPE("uintptr", Int32, 4, DW_ATE_unsigned);
|
|
}
|
|
else
|
|
{
|
|
PRIMITIVE_TYPE("uintptr", Int64, 8, DW_ATE_unsigned);
|
|
}
|
|
return true;
|
|
case BfTypeCode_IntUnknown:
|
|
case BfTypeCode_UIntUnknown:
|
|
return true;
|
|
case BfTypeCode_Char8:
|
|
PRIMITIVE_TYPE("char8", Int8, 1, DW_ATE_unsigned_char);
|
|
return true;
|
|
case BfTypeCode_Char16:
|
|
PRIMITIVE_TYPE("char16", Int16, 2, DW_ATE_unsigned_char);
|
|
return true;
|
|
case BfTypeCode_Char32:
|
|
PRIMITIVE_TYPE("char32", Int32, 4, DW_ATE_unsigned_char);
|
|
return true;
|
|
case BfTypeCode_Single:
|
|
PRIMITIVE_TYPE("float", Float, 4, DW_ATE_float);
|
|
return true;
|
|
case BfTypeCode_Double:
|
|
PRIMITIVE_TYPE("double", Double, 8, DW_ATE_float);
|
|
return true;
|
|
case BfTypeCode_Object:
|
|
case BfTypeCode_Struct:
|
|
case BfTypeCode_Interface:
|
|
case BfTypeCode_Enum:
|
|
// Implemented below
|
|
break;
|
|
case BfTypeCode_Extension:
|
|
// This can only happen if we didn't actually find the type the extension referred to
|
|
break;
|
|
default:
|
|
//NotImpl(resolvedTypeRef->mTypeRef);
|
|
BF_FATAL("Invalid type");
|
|
return false;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BF_ASSERT(typeInstance != NULL);
|
|
|
|
if (!typeInstance->IsArray())
|
|
{
|
|
BF_ASSERT(typeInstance->mTypeDef != mContext->mCompiler->mArray1TypeDef);
|
|
}
|
|
|
|
if (mContext->mBfObjectType == NULL)
|
|
{
|
|
if (typeInstance->mTypeDef == mCompiler->mBfObjectTypeDef)
|
|
mContext->mBfObjectType = typeInstance;
|
|
else
|
|
ResolveTypeDef(mCompiler->mBfObjectTypeDef);
|
|
}
|
|
|
|
if (typeInstance->mModule == NULL)
|
|
{
|
|
// Create a module for this type
|
|
mContext->HandleTypeWorkItem(resolvedTypeRef);
|
|
}
|
|
}
|
|
|
|
if (typeInstance == NULL)
|
|
return true;
|
|
|
|
auto result = typeInstance->mModule->DoPopulateType(typeInstance, populateType);
|
|
return result;
|
|
}
|
|
|
|
int BfModule::GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeInstance* typeInstance, bool checkTypeName)
|
|
{
|
|
if (mContext->mSystem->mTypeOptions.size() == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
Array<int> matchedIndices;
|
|
|
|
if ((!checkTypeName) && (typeInstance->mTypeOptionsIdx != -1))
|
|
{
|
|
// Methods should 'inherit' the owner's type options before applying type options from custom attributes
|
|
auto typeOptions = mSystem->GetTypeOptions(typeInstance->mTypeOptionsIdx);
|
|
if (typeOptions->mMatchedIndices.size() == 0)
|
|
matchedIndices.push_back(typeInstance->mTypeOptionsIdx);
|
|
else
|
|
matchedIndices = typeOptions->mMatchedIndices;
|
|
}
|
|
|
|
if (customAttributes != NULL)
|
|
{
|
|
if (!mCompiler->mAttributeTypeOptionMap.IsEmpty())
|
|
{
|
|
StringT<128> attrName;
|
|
for (auto& customAttrs : customAttributes->mAttributes)
|
|
{
|
|
attrName.Clear();
|
|
customAttrs.mType->mTypeDef->mFullName.ToString(attrName);
|
|
Array<int>* arrPtr;
|
|
if (mCompiler->mAttributeTypeOptionMap.TryGetValue(attrName, &arrPtr))
|
|
{
|
|
for (auto optionsIdx : *arrPtr)
|
|
{
|
|
matchedIndices.Add(optionsIdx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int typeOptionsCount = (int)mContext->mSystem->mTypeOptions.size();
|
|
if (checkTypeName)
|
|
{
|
|
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();
|
|
String typeName = TypeToString(underlyingType);
|
|
_CheckTypeName(typeName);
|
|
}
|
|
|
|
if ((!typeInstance->IsBoxed()) && (typeInstance->mTypeDef == mCompiler->mPointerTTypeDef))
|
|
{
|
|
BF_ASSERT(typeInstance->IsGenericTypeInstance());
|
|
auto innerType = ((BfGenericTypeInstance*)typeInstance)->mTypeGenericArguments[0];
|
|
auto ptrType = CreatePointerType(innerType);
|
|
String typeName = TypeToString(ptrType);
|
|
_CheckTypeName(typeName);
|
|
}
|
|
|
|
String typeName = TypeToString(typeInstance);
|
|
_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.mRuntimeChecks = first.mRuntimeChecks;
|
|
mergedTypeOptions.mInitLocalVariables = first.mInitLocalVariables;
|
|
mergedTypeOptions.mEmitDynamicCastCheck = first.mEmitDynamicCastCheck;
|
|
mergedTypeOptions.mEmitObjectAccessCheck = first.mEmitObjectAccessCheck;
|
|
mergedTypeOptions.mAllocStackTraceDepth = first.mAllocStackTraceDepth;
|
|
|
|
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;
|
|
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;
|
|
if (typeOptions.mAllocStackTraceDepth != -1)
|
|
mergedTypeOptions.mAllocStackTraceDepth = typeOptions.mAllocStackTraceDepth;
|
|
}
|
|
matchedIdx = typeOptionsCount + (int)mContext->mSystem->mMergedTypeOptions.size();
|
|
mContext->mSystem->mMergedTypeOptions.push_back(mergedTypeOptions);
|
|
}
|
|
}
|
|
|
|
return matchedIdx;
|
|
}
|
|
|
|
void BfModule::SetTypeOptions(BfTypeInstance* typeInstance)
|
|
{
|
|
typeInstance->mTypeOptionsIdx = GenerateTypeOptions(typeInstance->mCustomAttributes, typeInstance, true);
|
|
}
|
|
|
|
bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateType)
|
|
{
|
|
auto typeInstance = resolvedTypeRef->ToTypeInstance();
|
|
auto typeDef = typeInstance->mTypeDef;
|
|
|
|
BF_ASSERT((typeInstance->mTypeDef->mNextRevision == NULL) || (mCompiler->IsAutocomplete()));
|
|
|
|
// This is a special case where our base type has been rebuilt but we haven't
|
|
if ((typeInstance->mBaseTypeMayBeIncomplete) && (!typeInstance->mTypeIncomplete))
|
|
{
|
|
BfLogSysM("BaseTypeMayBeIncomplete processing. Type:%p -> Base:%p\n", typeInstance, typeInstance->mBaseType);
|
|
PopulateType(typeInstance->mBaseType, populateType);
|
|
if (!typeInstance->mBaseType->IsIncomplete())
|
|
typeInstance->mBaseTypeMayBeIncomplete = false;
|
|
if (!typeInstance->mTypeIncomplete)
|
|
return true;
|
|
}
|
|
typeInstance->mBaseTypeMayBeIncomplete = false;
|
|
|
|
BF_ASSERT(mIsModuleMutable);
|
|
|
|
// Don't do type instance method processing for an autocomplete pass - this will get handled later on during
|
|
// the PopulateType worklist pass in the full resolver. We do need to handle the methods for delegates, though,
|
|
// since those can affect method declarations of other methods
|
|
// TODO: Investigate this "Delegate" claim
|
|
bool canDoMethodProcessing = ((mCompiler->mResolvePassData == NULL) || (mCompiler->mResolvePassData->mAutoComplete == NULL) /*|| (typeInstance->IsDelegate())*/);
|
|
|
|
if (populateType == BfPopulateType_Full_Force)
|
|
canDoMethodProcessing = true;
|
|
|
|
if (typeInstance->mResolvingConstField)
|
|
return !typeInstance->mTypeFailed;
|
|
|
|
if (typeInstance->mNeedsMethodProcessing)
|
|
{
|
|
if ((canDoMethodProcessing) && (populateType >= BfPopulateType_DataAndMethods))
|
|
DoTypeInstanceMethodProcessing(typeInstance);
|
|
return true;
|
|
}
|
|
|
|
// Partial population break out point
|
|
if ((populateType >= BfPopulateType_Identity) && (populateType <= BfPopulateType_IdentityNoRemapAlias))
|
|
return true;
|
|
|
|
if (!resolvedTypeRef->IsValueType())
|
|
{
|
|
resolvedTypeRef->mSize = typeInstance->mAlign = mSystem->mPtrSize;
|
|
}
|
|
|
|
BF_ASSERT((typeInstance->mMethodInstanceGroups.size() == 0) || (typeInstance->mMethodInstanceGroups.size() == typeDef->mMethods.size()));
|
|
typeInstance->mMethodInstanceGroups.Resize(typeDef->mMethods.size());
|
|
for (int i = 0; i < (int)typeInstance->mMethodInstanceGroups.size(); i++)
|
|
{
|
|
typeInstance->mMethodInstanceGroups[i].mOwner = typeInstance;
|
|
typeInstance->mMethodInstanceGroups[i].mMethodIdx = i;
|
|
}
|
|
|
|
AutoDisallowYield disableYield(mSystem);
|
|
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
|
|
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, NULL);
|
|
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, NULL);
|
|
SetAndRestoreValue<bool> prevHadError(mHadBuildError, false);
|
|
SetAndRestoreValue<bool> prevHadWarning(mHadBuildWarning, false);
|
|
|
|
BfTypeState typeState(mCurTypeInstance, mContext->mCurTypeState);
|
|
typeState.mPopulateType = populateType;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
|
|
if (typeInstance->IsGenericTypeInstance())
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)typeInstance;
|
|
if (genericTypeInst->mGenericParams.size() == 0)
|
|
BuildGenericParams(resolvedTypeRef);
|
|
}
|
|
|
|
// Don't do TypeToString until down here. Otherwise we can infinitely loop on BuildGenericParams
|
|
|
|
bool isStruct = resolvedTypeRef->IsStruct();
|
|
|
|
bool reportErrors = true;
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
reportErrors = true;
|
|
|
|
// If we're not the defining context then we don't report errors for this type, but errors will still put the system
|
|
// into an errored state
|
|
SetAndRestoreValue<bool> prevReportErrors(mReportErrors, reportErrors);
|
|
|
|
CheckCircularDataError();
|
|
|
|
bool underlyingTypeDeferred = false;
|
|
BfType* underlyingType = NULL;
|
|
if (typeInstance->mBaseType != NULL)
|
|
{
|
|
if (typeInstance->IsTypedPrimitive())
|
|
underlyingType = typeInstance->GetUnderlyingType();
|
|
if ((typeInstance->mRebuildFlags & BfTypeRebuildFlag_UnderlyingTypeDeferred) != 0)
|
|
underlyingTypeDeferred = true;
|
|
}
|
|
else if (typeInstance->IsEnum())
|
|
{
|
|
bool hasPayloads = false;
|
|
for (auto fieldDef : typeDef->mFields)
|
|
{
|
|
if ((fieldDef->IsEnumCaseEntry()) && (fieldDef->mTypeRef != NULL))
|
|
{
|
|
hasPayloads = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasPayloads)
|
|
{
|
|
bool hadType = false;
|
|
|
|
for (auto baseTypeRef : typeDef->mBaseTypes)
|
|
{
|
|
auto baseType = ResolveTypeRef(baseTypeRef, BfPopulateType_Declaration);
|
|
if (baseType != NULL)
|
|
{
|
|
if (baseType->IsIntegral())
|
|
{
|
|
if (!hadType)
|
|
{
|
|
hadType = true;
|
|
underlyingType = baseType;
|
|
}
|
|
else
|
|
{
|
|
Fail("Underlying enum type already specified", baseTypeRef);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fail("Invalid underlying enum type", baseTypeRef);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
typeInstance->mTypeFailed = true;
|
|
}
|
|
}
|
|
|
|
if (underlyingType == NULL)
|
|
{
|
|
underlyingType = GetPrimitiveType(BfTypeCode_Int64);
|
|
underlyingTypeDeferred = true;
|
|
}
|
|
}
|
|
}
|
|
else if (((typeInstance->IsStruct()) || (typeInstance->IsTypedPrimitive())) &&
|
|
(!typeInstance->mTypeFailed))
|
|
{
|
|
for (auto baseTypeRef : typeDef->mBaseTypes)
|
|
{
|
|
SetAndRestoreValue<BfTypeReference*> prevTypeRef(mContext->mCurTypeState->mCurBaseTypeRef, baseTypeRef);
|
|
// We ignore errors here to avoid double-errors for type lookups, but this is where data cycles are detected
|
|
// but that type of error supercedes the mIgnorErrors setting
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
// Temporarily allow us to derive from private classes, to avoid infinite loop from TypeIsSubTypeOf
|
|
SetAndRestoreValue<bool> prevSkipTypeProtectionChecks(typeInstance->mSkipTypeProtectionChecks, true);
|
|
auto baseType = ResolveTypeRef(baseTypeRef, BfPopulateType_Declaration);
|
|
if (baseType != NULL)
|
|
{
|
|
if (baseType->IsPrimitiveType())
|
|
{
|
|
underlyingType = baseType;
|
|
}
|
|
else if (baseType->IsTypedPrimitive())
|
|
{
|
|
//PopulateType(baseType, true);
|
|
underlyingType = baseType->GetUnderlyingType();
|
|
BF_ASSERT(underlyingType != NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
typeInstance->mTypeFailed = true;
|
|
}
|
|
}
|
|
|
|
// Incase we had re-entry, work this through ourselves again here
|
|
typeInstance->mIsTypedPrimitive = false;
|
|
}
|
|
|
|
if (underlyingTypeDeferred)
|
|
typeInstance->mRebuildFlags = (BfTypeRebuildFlags)(typeInstance->mRebuildFlags | BfTypeRebuildFlag_UnderlyingTypeDeferred);
|
|
|
|
typeInstance->mIsTypedPrimitive = underlyingType != NULL;
|
|
int wantFieldCount = (int)typeDef->mFields.size() + (((underlyingType != NULL) || (typeInstance->IsPayloadEnum())) ? 1 : 0);
|
|
if ((int)typeInstance->mFieldInstances.size() < wantFieldCount)
|
|
{
|
|
// Closures don't include the enclosed fields on their first pass through PopulateType, and they have no typeDef of their own
|
|
// so we need to take care not to truncate their fieldInstance vector here (thus the 'wantFieldCount' check above)
|
|
typeInstance->mFieldInstances.Resize(wantFieldCount);
|
|
}
|
|
|
|
if (underlyingType != NULL)
|
|
{
|
|
auto fieldInstance = &typeInstance->mFieldInstances.back();
|
|
fieldInstance->mDataOffset = 0;
|
|
fieldInstance->mDataSize = underlyingType->mSize;
|
|
fieldInstance->mOwner = typeInstance;
|
|
fieldInstance->mResolvedType = underlyingType;
|
|
|
|
typeInstance->mSize = underlyingType->mSize;
|
|
typeInstance->mAlign = underlyingType->mAlign;
|
|
typeInstance->mInstSize = underlyingType->mSize;
|
|
typeInstance->mInstAlign = underlyingType->mAlign;
|
|
typeInstance->mHasPackingHoles = underlyingType->HasPackingHoles();
|
|
}
|
|
|
|
// Partial population break out point
|
|
if (typeInstance->mDefineState < BfTypeDefineState_Declared)
|
|
{
|
|
typeInstance->mDefineState = BfTypeDefineState_Declared;
|
|
|
|
if (typeInstance->IsGenericTypeInstance())
|
|
{
|
|
auto genericTypeInstance = (BfGenericTypeInstance*)typeInstance;
|
|
|
|
// Add generic dependencies if needed
|
|
for (auto genericType : genericTypeInstance->mTypeGenericArguments)
|
|
{
|
|
if (genericType->IsPrimitiveType())
|
|
genericType = GetWrappedStructType(genericType);
|
|
if (genericType != NULL)
|
|
{
|
|
AddDependency(genericType, genericTypeInstance, BfDependencyMap::DependencyFlag_TypeGenericArg);
|
|
BfLogSysM("Adding generic dependency of %p for type %p\n", genericType, genericTypeInstance);
|
|
}
|
|
}
|
|
if (genericTypeInstance->IsSpecializedType())
|
|
{
|
|
// This ensures we rebuild the unspecialized type whenever the specialized type rebuilds. This is important
|
|
// for generic type binding
|
|
auto unspecializedTypeInstance = GetUnspecializedTypeInstance(genericTypeInstance);
|
|
BF_ASSERT(!unspecializedTypeInstance->IsUnspecializedTypeVariation());
|
|
mContext->mScratchModule->AddDependency(genericTypeInstance, unspecializedTypeInstance, BfDependencyMap::DependencyFlag_UnspecializedType);
|
|
}
|
|
}
|
|
|
|
auto _AddStaticSearch = [&](BfTypeDef* typeDef)
|
|
{
|
|
if (typeDef->mStaticSearch.IsEmpty())
|
|
return;
|
|
BfStaticSearch* staticSearch;
|
|
if (typeInstance->mStaticSearchMap.TryAdd(typeDef, NULL, &staticSearch))
|
|
{
|
|
for (auto typeRef : typeDef->mStaticSearch)
|
|
{
|
|
auto staticType = ResolveTypeRef(typeRef, NULL, BfPopulateType_Declaration);
|
|
if (staticType != NULL)
|
|
{
|
|
auto staticTypeInst = staticType->ToTypeInstance();
|
|
if (staticTypeInst == NULL)
|
|
{
|
|
Fail(StrFormat("Type '%s' cannot be used in a 'using static' declaration", TypeToString(staticType).c_str()), typeRef);
|
|
}
|
|
else
|
|
{
|
|
AddDependency(staticTypeInst, typeInstance, BfDependencyMap::DependencyFlag_StaticValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
if (typeDef->mIsCombinedPartial)
|
|
{
|
|
for (auto partialTypeDef : typeDef->mPartials)
|
|
_AddStaticSearch(partialTypeDef);
|
|
}
|
|
else
|
|
_AddStaticSearch(typeDef);
|
|
}
|
|
if (populateType == BfPopulateType_Declaration)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ((!mCompiler->mIsResolveOnly) && (!typeInstance->mHasBeenInstantiated))
|
|
{
|
|
for (auto& dep : typeInstance->mDependencyMap)
|
|
{
|
|
auto& depEntry = dep.mValue;
|
|
if ((depEntry.mFlags & BfDependencyMap::DependencyFlag_Allocates) != 0)
|
|
{
|
|
auto depType = dep.mKey;
|
|
if (depType->mRevision == depEntry.mRevision)
|
|
{
|
|
BfLogSysM("Setting mHasBeenInstantiated for %p instantiated from %p\n", typeInstance, depType);
|
|
typeInstance->mHasBeenInstantiated = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BfLogSysM("Setting revision. Type: %p Revision: %d\n", typeInstance, mRevision);
|
|
typeInstance->mRevision = mRevision;
|
|
|
|
// Temporarily allow us to derive from private classes, to avoid infinite loop from TypeIsSubTypeOf
|
|
SetAndRestoreValue<bool> prevSkipTypeProtectionChecks(typeInstance->mSkipTypeProtectionChecks, true);
|
|
|
|
if ((typeDef->mOuterType != NULL) && (typeDef->mOuterType->IsGlobalsContainer()))
|
|
{
|
|
if ((typeDef->mTypeDeclaration != NULL) && (typeDef->mTypeDeclaration->mTypeNode != NULL))
|
|
Fail("Global blocks cannot contain type declarations", typeDef->mTypeDeclaration->mTypeNode);
|
|
}
|
|
|
|
/// Create DI data
|
|
SizedArray<BfIRType, 8> llvmFieldTypes;
|
|
|
|
int curFieldDataIdx = 0;
|
|
typeInstance->mBaseType = NULL;
|
|
BfTypeInstance* defaultBaseTypeInst = NULL;
|
|
|
|
// Find base type
|
|
BfType* baseType = NULL;
|
|
|
|
struct BfInterfaceDecl
|
|
{
|
|
BfTypeInstance* mIFaceTypeInst;
|
|
BfTypeReference* mTypeRef;
|
|
BfTypeDef* mDeclaringType;
|
|
};
|
|
|
|
SizedArray<BfInterfaceDecl, 8> interfaces;
|
|
HashSet<BfTypeInstance*> ifaceSet;
|
|
|
|
if (resolvedTypeRef == mContext->mBfObjectType)
|
|
{
|
|
baseType = NULL;
|
|
}
|
|
else if (typeInstance->IsEnum())
|
|
{
|
|
if (mCompiler->mEnumTypeDef == NULL)
|
|
{
|
|
Fail("Enum type required");
|
|
TypeFailed(typeInstance);
|
|
}
|
|
else
|
|
baseType = ResolveTypeDef(mCompiler->mEnumTypeDef)->ToTypeInstance();
|
|
}
|
|
else if (resolvedTypeRef->IsObject())
|
|
baseType = mContext->mBfObjectType;
|
|
else if (resolvedTypeRef->IsPointer())
|
|
{
|
|
baseType = ResolveTypeDef(mCompiler->mPointerTTypeDef, BfPopulateType_Data);
|
|
}
|
|
else if ((resolvedTypeRef->IsValueType()) && (typeDef != mCompiler->mValueTypeTypeDef))
|
|
{
|
|
baseType = ResolveTypeDef(mCompiler->mValueTypeTypeDef, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
|
|
if (baseType != NULL)
|
|
defaultBaseTypeInst = baseType->ToTypeInstance();
|
|
|
|
if (typeInstance->mTypeId == 260)
|
|
{
|
|
NOP;
|
|
}
|
|
|
|
BfTypeReference* baseTypeRef = NULL;
|
|
if ((typeDef->mIsDelegate) && (!typeInstance->IsClosure()))
|
|
{
|
|
if (mCompiler->mDelegateTypeDef == NULL)
|
|
{
|
|
Fail("Delegate type required");
|
|
TypeFailed(typeInstance);
|
|
}
|
|
else
|
|
baseType = ResolveTypeDef(mCompiler->mDelegateTypeDef)->ToTypeInstance();
|
|
}
|
|
else if (typeDef->mIsFunction)
|
|
{
|
|
if (mCompiler->mFunctionTypeDef == NULL)
|
|
{
|
|
Fail("Function type required");
|
|
TypeFailed(typeInstance);
|
|
}
|
|
else
|
|
baseType = ResolveTypeDef(mCompiler->mFunctionTypeDef)->ToTypeInstance();
|
|
}
|
|
else
|
|
{
|
|
for (auto checkTypeRef : typeDef->mBaseTypes)
|
|
{
|
|
SetAndRestoreValue<BfTypeReference*> prevTypeRef(mContext->mCurTypeState->mCurBaseTypeRef, checkTypeRef);
|
|
|
|
auto declTypeDef = typeDef;
|
|
if (typeDef->mIsCombinedPartial)
|
|
declTypeDef = typeDef->mPartials.front();
|
|
SetAndRestoreValue<BfTypeDef*> prevTypeDef(mContext->mCurTypeState->mCurTypeDef, declTypeDef);
|
|
|
|
bool populateBase = !typeInstance->mTypeFailed;
|
|
auto checkType = ResolveTypeRef(checkTypeRef, populateBase ? BfPopulateType_Data : BfPopulateType_Declaration);
|
|
if (checkType != NULL)
|
|
{
|
|
auto checkTypeInst = checkType->ToTypeInstance();
|
|
bool canDeriveFrom = checkTypeInst != NULL;
|
|
if ((typeInstance->IsStruct()) || (typeInstance->IsTypedPrimitive()) || (typeInstance->IsBoxed()))
|
|
canDeriveFrom |= checkType->IsPrimitiveType();
|
|
if ((typeInstance->IsEnum()) && (!checkType->IsInterface()))
|
|
{
|
|
if (typeInstance->IsTypedPrimitive())
|
|
continue;
|
|
if (checkType->IsPrimitiveType())
|
|
Fail(StrFormat("Enum '%s' cannot be specified as '%s' because it has a payload",
|
|
TypeToString(typeInstance).c_str(), TypeToString(checkType).c_str()),
|
|
checkTypeRef);
|
|
else
|
|
Fail("Enums cannot derive from other types", checkTypeRef);
|
|
continue;
|
|
}
|
|
|
|
if ((checkTypeInst != NULL) && (checkTypeInst->mTypeFailed))
|
|
{
|
|
// To keep circular references from breaking type invariants (ie: base type loops)
|
|
continue;
|
|
}
|
|
|
|
if (!canDeriveFrom)
|
|
{
|
|
Fail("Cannot derive from this type", checkTypeRef);
|
|
continue;
|
|
}
|
|
|
|
if (checkType->IsInterface())
|
|
{
|
|
auto ifaceInst = checkType->ToTypeInstance();
|
|
if (ifaceSet.Add(ifaceInst))
|
|
{
|
|
// Not base type
|
|
BfInterfaceDecl ifaceDecl;
|
|
ifaceDecl.mIFaceTypeInst = ifaceInst;
|
|
ifaceDecl.mTypeRef = checkTypeRef;
|
|
ifaceDecl.mDeclaringType = typeDef;
|
|
interfaces.push_back(ifaceDecl);
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Interface '%s' is already specified", TypeToString(checkType).c_str()), checkTypeRef);
|
|
}
|
|
}
|
|
else if (resolvedTypeRef == mContext->mBfObjectType)
|
|
{
|
|
Fail(StrFormat("Type '%s' cannot define a base type", TypeToString(baseType).c_str()), checkTypeRef);
|
|
}
|
|
else
|
|
{
|
|
if (baseTypeRef != NULL)
|
|
{
|
|
Fail(StrFormat("Base type '%s' already declared", TypeToString(baseType).c_str()), checkTypeRef);
|
|
}
|
|
else
|
|
{
|
|
baseTypeRef = checkTypeRef;
|
|
if (checkTypeInst != NULL)
|
|
{
|
|
baseType = checkTypeInst;
|
|
/*if ((resolvedTypeRef->IsBoxed()) && (baseType->IsValueType()))
|
|
{
|
|
baseType = CreateBoxedType(baseType);
|
|
}*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
// Why did we go around setting mTypeFailed on all these things?
|
|
//typeInstance->mTypeFailed = true;
|
|
}
|
|
}
|
|
|
|
for (auto partialTypeDef : typeDef->mPartials)
|
|
{
|
|
if (!typeInstance->IsTypeMemberIncluded(partialTypeDef))
|
|
continue;
|
|
if (partialTypeDef->mTypeDeclaration == typeInstance->mTypeDef->mTypeDeclaration)
|
|
continue;
|
|
|
|
for (auto checkTypeRef : partialTypeDef->mBaseTypes)
|
|
{
|
|
SetAndRestoreValue<BfTypeReference*> prevTypeRef(mContext->mCurTypeState->mCurBaseTypeRef, checkTypeRef);
|
|
SetAndRestoreValue<BfTypeDef*> prevTypeDef(mContext->mCurTypeState->mCurTypeDef, partialTypeDef);
|
|
bool populateBase = !typeInstance->mTypeFailed;
|
|
auto checkType = ResolveTypeRef(checkTypeRef, BfPopulateType_Declaration);
|
|
if (checkType != NULL)
|
|
{
|
|
if (checkType->IsInterface())
|
|
{
|
|
BfInterfaceDecl ifaceDecl;
|
|
ifaceDecl.mIFaceTypeInst = checkType->ToTypeInstance();
|
|
ifaceDecl.mTypeRef = checkTypeRef;
|
|
ifaceDecl.mDeclaringType = partialTypeDef;
|
|
interfaces.push_back(ifaceDecl);
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Extensions can only specify new interfaces, type '%s' is not a valid ", TypeToString(checkType).c_str()), checkTypeRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resolvedTypeRef->IsBoxed())
|
|
{
|
|
baseType = mContext->mBfObjectType;
|
|
}
|
|
|
|
BfTypeInstance* baseTypeInst = NULL;
|
|
|
|
if (baseType != NULL)
|
|
{
|
|
baseTypeInst = baseType->ToTypeInstance();
|
|
}
|
|
|
|
if (typeInstance->mBaseType != NULL)
|
|
{
|
|
BF_ASSERT(typeInstance->mBaseType == baseTypeInst);
|
|
}
|
|
|
|
BfType* outerType = GetOuterType(typeInstance);
|
|
if (outerType != NULL)
|
|
AddDependency(outerType, typeInstance, BfDependencyMap::DependencyFlag_OuterType);
|
|
|
|
if ((baseTypeInst != NULL) && (typeInstance->mBaseType == NULL))
|
|
{
|
|
//curFieldDataIdx = 1;
|
|
if (!typeInstance->mTypeFailed)
|
|
PopulateType(baseTypeInst, BfPopulateType_Data);
|
|
typeInstance->mBaseTypeMayBeIncomplete = false;
|
|
|
|
typeInstance->mMergedFieldDataCount = baseTypeInst->mMergedFieldDataCount;
|
|
|
|
if ((resolvedTypeRef->IsObject()) && (!baseTypeInst->IsObject()))
|
|
{
|
|
Fail("Class can only derive from another class", baseTypeRef, true);
|
|
//typeInstance->mTypeFailed = true;
|
|
baseTypeInst = defaultBaseTypeInst;
|
|
typeInstance->mBaseType = baseTypeInst;
|
|
}
|
|
else if ((resolvedTypeRef->IsStruct()) && (!baseTypeInst->IsValueType()))
|
|
{
|
|
Fail("Struct can only derive from another struct", baseTypeRef, true);
|
|
//typeInstance->mTypeFailed = true;
|
|
baseTypeInst = defaultBaseTypeInst;
|
|
typeInstance->mBaseType = baseTypeInst;
|
|
}
|
|
|
|
if (!typeInstance->IsIncomplete())
|
|
{
|
|
// Re-entry may cause this type to be completed already
|
|
return true;
|
|
}
|
|
|
|
//BfLogSysM("Adding DerivedFrom dependency. Used:%p Using:%p\n", baseType, typeInstance);
|
|
auto checkBaseType = baseTypeInst;
|
|
while (checkBaseType != NULL)
|
|
{
|
|
// Add 'DerivedFrom' dependency all the way up the inheritance chain
|
|
AddDependency(checkBaseType, typeInstance, BfDependencyMap::DependencyFlag_DerivedFrom);
|
|
checkBaseType = checkBaseType->mBaseType;
|
|
}
|
|
|
|
typeInstance->mBaseType = baseTypeInst;
|
|
typeInstance->mInheritDepth = baseTypeInst->mInheritDepth + 1;
|
|
typeInstance->mHasParameterizedBase = baseTypeInst->mHasParameterizedBase;
|
|
if ((baseTypeInst->IsArray()) || (baseTypeInst->IsSizedArray()) || (baseTypeInst->IsGenericTypeInstance()))
|
|
typeInstance->mHasParameterizedBase = true;
|
|
|
|
if (underlyingType == NULL)
|
|
{
|
|
typeInstance->mInstSize = baseTypeInst->mInstSize;
|
|
typeInstance->mInstAlign = baseTypeInst->mInstAlign;
|
|
typeInstance->mAlign = baseTypeInst->mAlign;
|
|
typeInstance->mSize = baseTypeInst->mSize;
|
|
typeInstance->mHasPackingHoles = baseTypeInst->mHasPackingHoles;
|
|
if (baseTypeInst->mIsTypedPrimitive)
|
|
typeInstance->mIsTypedPrimitive = true;
|
|
}
|
|
}
|
|
|
|
if (populateType <= BfPopulateType_BaseType)
|
|
return true;
|
|
|
|
if ((typeInstance->mBaseType != NULL) && (!typeInstance->IsTypedPrimitive()))
|
|
{
|
|
curFieldDataIdx++;
|
|
}
|
|
|
|
if (!interfaces.empty())
|
|
{
|
|
for (int iFaceIdx = 0; iFaceIdx < (int)interfaces.size(); iFaceIdx++)
|
|
{
|
|
auto checkInterface = interfaces[iFaceIdx].mIFaceTypeInst;
|
|
PopulateType(checkInterface, BfPopulateType_Data);
|
|
|
|
BfTypeInterfaceEntry* found = NULL;
|
|
bool foundExact = false;
|
|
for (auto& typeInterfaceInst : typeInstance->mInterfaces)
|
|
{
|
|
if (typeInterfaceInst.mInterfaceType == checkInterface)
|
|
{
|
|
if (typeInterfaceInst.mDeclaringType == interfaces[iFaceIdx].mDeclaringType)
|
|
{
|
|
foundExact = true;
|
|
break;
|
|
}
|
|
|
|
found = &typeInterfaceInst;
|
|
}
|
|
}
|
|
if (foundExact)
|
|
continue;
|
|
|
|
BfTypeInterfaceEntry typeInterfaceInst;
|
|
typeInterfaceInst.mDeclaringType = interfaces[iFaceIdx].mDeclaringType;
|
|
typeInterfaceInst.mInterfaceType = checkInterface;
|
|
typeInterfaceInst.mStartInterfaceTableIdx = -1;
|
|
typeInterfaceInst.mStartVirtualIdx = -1;
|
|
typeInterfaceInst.mIsRedeclared = false;
|
|
typeInstance->mInterfaces.push_back(typeInterfaceInst);
|
|
|
|
// Interfaces can list other interfaces in their declaration, so pull those in too
|
|
for (auto depIFace : checkInterface->mInterfaces)
|
|
{
|
|
auto depIFaceEntry = interfaces[iFaceIdx];
|
|
depIFaceEntry.mIFaceTypeInst = depIFace.mInterfaceType;
|
|
interfaces.push_back(depIFaceEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
typeInstance->mDefineState = BfTypeDefineState_HasInterfaces;
|
|
if (populateType <= BfPopulateType_Interfaces)
|
|
return true;
|
|
|
|
prevSkipTypeProtectionChecks.Restore();
|
|
typeInstance->mInstSize = std::max(0, typeInstance->mInstSize);
|
|
typeInstance->mInstAlign = std::max(0, typeInstance->mInstAlign);
|
|
|
|
if ((typeInstance->mCustomAttributes == NULL) && (typeDef->mTypeDeclaration != NULL) && (typeDef->mTypeDeclaration->mAttributes != NULL))
|
|
{
|
|
BfAttributeTargets attrTarget;
|
|
if ((typeDef->mIsDelegate) || (typeDef->mIsFunction))
|
|
attrTarget = BfAttributeTargets_Delegate;
|
|
else if (typeInstance->IsEnum())
|
|
attrTarget = BfAttributeTargets_Enum;
|
|
else if (typeInstance->IsInterface())
|
|
attrTarget = BfAttributeTargets_Interface;
|
|
else if (typeInstance->IsStruct())
|
|
attrTarget = BfAttributeTargets_Struct;
|
|
else
|
|
attrTarget = BfAttributeTargets_Class;
|
|
if (!typeInstance->mTypeFailed)
|
|
{
|
|
// This allows us to avoid reentrancy when checking for inner types
|
|
SetAndRestoreValue<bool> prevSkipTypeProtectionChecks(typeInstance->mSkipTypeProtectionChecks, true);
|
|
if (typeDef->mIsCombinedPartial)
|
|
{
|
|
for (auto partialTypeDef : typeDef->mPartials)
|
|
{
|
|
if (partialTypeDef->mTypeDeclaration->mAttributes == NULL)
|
|
continue;
|
|
BfTypeState typeState;
|
|
typeState.mCurTypeDef = partialTypeDef;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
|
|
if (typeInstance->mCustomAttributes == NULL)
|
|
typeInstance->mCustomAttributes = new BfCustomAttributes();
|
|
GetCustomAttributes(typeInstance->mCustomAttributes, partialTypeDef->mTypeDeclaration->mAttributes, attrTarget);
|
|
}
|
|
}
|
|
else
|
|
typeInstance->mCustomAttributes = GetCustomAttributes(typeDef->mTypeDeclaration->mAttributes, attrTarget);
|
|
}
|
|
}
|
|
|
|
if (typeInstance->mTypeOptionsIdx == -2)
|
|
{
|
|
SetTypeOptions(typeInstance);
|
|
}
|
|
|
|
ProcessCustomAttributeData();
|
|
bool isPacked = false;
|
|
bool isUnion = false;
|
|
bool isCRepr = false;
|
|
bool isOrdered = false;
|
|
ProcessTypeInstCustomAttributes(isPacked, isUnion, isCRepr, isOrdered);
|
|
typeInstance->mIsUnion = isUnion;
|
|
if ((typeInstance->IsEnum()) && (typeInstance->IsStruct()))
|
|
typeInstance->mIsUnion = true;
|
|
typeInstance->mIsPacked = isPacked;
|
|
typeInstance->mIsCRepr = isCRepr;
|
|
|
|
BfType* unionInnerType = NULL;
|
|
bool hadDeferredVars = false;
|
|
int dataPos;
|
|
if (resolvedTypeRef->IsBoxed())
|
|
{
|
|
BfBoxedType* boxedType = (BfBoxedType*)resolvedTypeRef;
|
|
BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance();
|
|
if (innerType->IsIncomplete())
|
|
PopulateType(innerType, BfPopulateType_Data);
|
|
|
|
auto baseType = typeInstance->mBaseType;
|
|
dataPos = baseType->mInstSize;
|
|
int alignSize = std::max(innerType->mInstAlign, baseType->mInstAlign);
|
|
if (alignSize > 1)
|
|
dataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
|
int dataSize = innerType->mInstSize;
|
|
|
|
typeInstance->mFieldInstances.push_back(BfFieldInstance());
|
|
BfFieldInstance* fieldInstance = &typeInstance->mFieldInstances.back();
|
|
fieldInstance->mDataOffset = dataPos;
|
|
fieldInstance->mDataSize = innerType->mSize;
|
|
fieldInstance->mOwner = typeInstance;
|
|
fieldInstance->mResolvedType = innerType;
|
|
|
|
if (!innerType->IsValuelessType())
|
|
{
|
|
curFieldDataIdx++;
|
|
}
|
|
dataPos += dataSize;
|
|
typeInstance->mInstAlign = std::max(baseType->mInstAlign, alignSize);
|
|
int instAlign = typeInstance->mInstAlign;
|
|
if (instAlign != 0)
|
|
{
|
|
int instSize = (dataPos + (instAlign - 1)) & ~(instAlign - 1);
|
|
if (instSize != typeInstance->mInstSize)
|
|
{
|
|
typeInstance->mInstSize = instSize;
|
|
typeInstance->mHasPackingHoles = true;
|
|
}
|
|
}
|
|
typeInstance->mInstSize = std::max(1, typeInstance->mInstSize);
|
|
}
|
|
else
|
|
{
|
|
dataPos = typeInstance->mInstSize;
|
|
|
|
if (underlyingType != NULL)
|
|
{
|
|
if (!underlyingType->IsValuelessType())
|
|
{
|
|
curFieldDataIdx++;
|
|
}
|
|
}
|
|
|
|
struct DeferredResolveEntry
|
|
{
|
|
BfFieldDef* mFieldDef;
|
|
int mTypeArrayIdx;
|
|
};
|
|
|
|
for (auto propDef : typeDef->mProperties)
|
|
{
|
|
if (!typeInstance->IsTypeMemberIncluded(propDef->mDeclaringType))
|
|
continue;
|
|
|
|
if (propDef->mFieldDeclaration != NULL)
|
|
{
|
|
if (propDef->mFieldDeclaration->mAttributes != NULL)
|
|
{
|
|
auto customAttrs = GetCustomAttributes(propDef->mFieldDeclaration->mAttributes, BfAttributeTargets_Property);
|
|
delete customAttrs;
|
|
}
|
|
|
|
if (propDef->mFieldDeclaration->mAttributes != NULL)
|
|
{
|
|
auto customAttrs = GetCustomAttributes(propDef->mFieldDeclaration->mAttributes, BfAttributeTargets_Property);
|
|
delete customAttrs;
|
|
}
|
|
|
|
auto propDecl = (BfPropertyDeclaration*)propDef->mFieldDeclaration;
|
|
if (propDecl->mExplicitInterface != NULL)
|
|
{
|
|
BfTypeState typeState;
|
|
typeState.mCurTypeDef = propDef->mDeclaringType;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckTypeRef(propDecl->mExplicitInterface, false);
|
|
auto explicitInterface = ResolveTypeRef(propDecl->mExplicitInterface, BfPopulateType_Declaration);
|
|
if (explicitInterface != NULL)
|
|
{
|
|
bool interfaceFound = false;
|
|
for (auto ifaceInst : typeInstance->mInterfaces)
|
|
interfaceFound |= ifaceInst.mInterfaceType == explicitInterface;
|
|
if (!interfaceFound)
|
|
{
|
|
Fail("Containing class has not declared to implement this interface", propDecl->mExplicitInterface, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (propDef->mMethods.IsEmpty())
|
|
{
|
|
auto nameNode = ((BfPropertyDeclaration*)propDef->mFieldDeclaration)->mNameNode;
|
|
|
|
if (nameNode != NULL)
|
|
{
|
|
Fail(StrFormat("Property or indexer '%s.%s' must have at least one accessor", TypeToString(typeInstance).c_str(), propDef->mName.c_str()),
|
|
nameNode, true); // CS0548
|
|
}
|
|
}
|
|
}
|
|
|
|
BfSizedVector<DeferredResolveEntry, 8> deferredVarResolves;
|
|
|
|
for (auto field : typeDef->mFields)
|
|
{
|
|
auto fieldInstance = &typeInstance->mFieldInstances[field->mIdx];
|
|
if (fieldInstance->mResolvedType != NULL)
|
|
continue;
|
|
|
|
if (!typeInstance->IsTypeMemberIncluded(field->mDeclaringType))
|
|
{
|
|
fieldInstance->mFieldIncluded = false;
|
|
continue;
|
|
}
|
|
|
|
fieldInstance->mOwner = typeInstance;
|
|
fieldInstance->mFieldIdx = field->mIdx;
|
|
|
|
if (typeInstance->IsInterface())
|
|
Fail("Interfaces cannot include fields. Consider making this a property", field->GetRefNode());
|
|
}
|
|
|
|
int enumCaseEntryIdx = 0;
|
|
for (auto field : typeDef->mFields)
|
|
{
|
|
auto fieldInstance = &typeInstance->mFieldInstances[field->mIdx];
|
|
if ((fieldInstance->mResolvedType != NULL) || (!fieldInstance->mFieldIncluded))
|
|
continue;
|
|
|
|
BfType* resolvedFieldType = NULL;
|
|
|
|
if (field->IsEnumCaseEntry())
|
|
{
|
|
fieldInstance->mDataIdx = -(enumCaseEntryIdx++) - 1;
|
|
resolvedFieldType = typeInstance;
|
|
|
|
BfType* payloadType = NULL;
|
|
if (field->mTypeRef != NULL)
|
|
payloadType = ResolveTypeRef(field->mTypeRef, BfPopulateType_Data, BfResolveTypeRefFlag_NoResolveGenericParam);
|
|
if (payloadType == NULL)
|
|
{
|
|
if (!typeInstance->IsTypedPrimitive())
|
|
payloadType = CreateTupleType(BfTypeVector(), Array<String>());
|
|
}
|
|
if (payloadType != NULL)
|
|
{
|
|
AddDependency(payloadType, typeInstance, BfDependencyMap::DependencyFlag_ValueTypeMemberData);
|
|
BF_ASSERT(payloadType->IsTuple());
|
|
resolvedFieldType = payloadType;
|
|
fieldInstance->mIsEnumPayloadCase = true;
|
|
}
|
|
}
|
|
else if ((field->mTypeRef != NULL) && ((field->mTypeRef->IsExact<BfVarTypeReference>()) || (field->mTypeRef->IsExact<BfLetTypeReference>()) || (field->mTypeRef->IsExact<BfDeclTypeRef>())))
|
|
{
|
|
resolvedFieldType = GetPrimitiveType(BfTypeCode_Var);
|
|
|
|
DeferredResolveEntry resolveEntry;
|
|
resolveEntry.mFieldDef = field;
|
|
resolveEntry.mTypeArrayIdx = (int)llvmFieldTypes.size();
|
|
deferredVarResolves.push_back(resolveEntry);
|
|
|
|
// For 'let', make read-only
|
|
}
|
|
else
|
|
{
|
|
SetAndRestoreValue<BfFieldDef*> prevTypeRef(mContext->mCurTypeState->mCurFieldDef, field);
|
|
resolvedFieldType = ResolveTypeRef(field->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam);
|
|
if (resolvedFieldType == NULL)
|
|
{
|
|
// Failed, just put in placeholder 'Object'
|
|
AssertErrorState();
|
|
resolvedFieldType = mContext->mBfObjectType;
|
|
}
|
|
}
|
|
|
|
if (resolvedFieldType->IsMethodRef())
|
|
{
|
|
auto methodRefType = (BfMethodRefType*)resolvedFieldType;
|
|
}
|
|
|
|
if (fieldInstance->mResolvedType == NULL)
|
|
fieldInstance->mResolvedType = resolvedFieldType;
|
|
|
|
if (field->mIsConst)
|
|
{
|
|
// Resolve in ResolveConstField after we finish populating entire FieldInstance list
|
|
}
|
|
else if (field->mIsStatic)
|
|
{
|
|
// Don't allocate this until after we're finished populating entire FieldInstance list,
|
|
// because we may have re-entry and create multiple instances of this static field
|
|
}
|
|
}
|
|
|
|
if (!resolvedTypeRef->IsIncomplete())
|
|
{
|
|
// We finished resolving ourselves through a re-entry, so we're actually done here
|
|
return true;
|
|
}
|
|
|
|
for (auto& resolveEntry : deferredVarResolves)
|
|
{
|
|
hadDeferredVars = true;
|
|
auto fieldType = ResolveVarFieldType(typeInstance, &typeInstance->mFieldInstances[resolveEntry.mFieldDef->mIdx], resolveEntry.mFieldDef);
|
|
if (fieldType == NULL)
|
|
{
|
|
fieldType = mContext->mBfObjectType;
|
|
// We used to set mTypeFailed, but mHasBuildError is enough to cause a type rebuild properly
|
|
mHadBuildError = true;
|
|
//typeInstance->mTypeFailed = true;
|
|
}
|
|
auto fieldInstance = &typeInstance->mFieldInstances[resolveEntry.mFieldDef->mIdx];
|
|
fieldInstance->SetResolvedType(fieldType);
|
|
}
|
|
|
|
if (typeInstance->mResolvingConstField)
|
|
return !typeInstance->mTypeFailed;
|
|
|
|
for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
|
|
{
|
|
auto fieldInstance = &fieldInstanceRef;
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
auto resolvedFieldType = fieldInstance->GetResolvedType();
|
|
|
|
if (!fieldInstance->mFieldIncluded)
|
|
continue;
|
|
|
|
if (resolvedFieldType == NULL)
|
|
{
|
|
if ((underlyingType != NULL) || (typeInstance->IsPayloadEnum()))
|
|
continue;
|
|
}
|
|
|
|
if (!fieldInstance->mFieldIncluded)
|
|
continue;
|
|
|
|
if (fieldDef == NULL)
|
|
continue;
|
|
|
|
if ((!fieldDef->mIsStatic) && (resolvedFieldType->IsValueType()))
|
|
{
|
|
// We need that type finished up for alignment and data size
|
|
// But if the type has failed then we need to avoid stack overflow so we don't finish it
|
|
SetAndRestoreValue<BfFieldDef*> prevTypeRef(mContext->mCurTypeState->mCurFieldDef, fieldDef);
|
|
bool populateChildType = !typeInstance->mTypeFailed;
|
|
//bool populateChildType = true;
|
|
PopulateType(resolvedFieldType, populateChildType ? BfPopulateType_Data : BfPopulateType_Declaration);
|
|
|
|
if (populateChildType)
|
|
{
|
|
BF_ASSERT(!resolvedFieldType->IsDataIncomplete());
|
|
}
|
|
else
|
|
{
|
|
if (resolvedFieldType->IsDataIncomplete())
|
|
{
|
|
AssertErrorState();
|
|
resolvedFieldType = mContext->mBfObjectType;
|
|
fieldInstance->SetResolvedType(resolvedFieldType);
|
|
|
|
// We used to set mTypeFailed, but mHasBuildError is enough to cause a type rebuild properly
|
|
mHadBuildError = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!typeInstance->IsIncomplete()) || (typeInstance->mNeedsMethodProcessing))
|
|
{
|
|
return !typeInstance->mTypeFailed;
|
|
}
|
|
|
|
BF_ASSERT(mContext->mCurTypeState == &typeState);
|
|
|
|
BF_ASSERT(!typeInstance->mIsFinishingType);
|
|
typeInstance->mIsFinishingType = true;
|
|
|
|
// No re-entry is allowed below here -- we will run all the way to the end at this point
|
|
BfSizedVector<BfIRMDNode, 8> diFieldTypes;
|
|
|
|
HashContext dataMemberHashCtx;
|
|
|
|
if (!resolvedTypeRef->IsBoxed())
|
|
{
|
|
bool isGlobalContainer = typeDef->IsGlobalsContainer();
|
|
|
|
if (typeInstance->mBaseType != NULL)
|
|
{
|
|
dataMemberHashCtx.Mixin(typeInstance->mBaseType->mTypeId);
|
|
if (typeInstance->mBaseType->mHotTypeData != NULL)
|
|
{
|
|
BfHotTypeVersion* ver = typeInstance->mBaseType->mHotTypeData->GetLatestVersion();
|
|
dataMemberHashCtx.Mixin(ver->mDataHash);
|
|
}
|
|
}
|
|
dataMemberHashCtx.Mixin(typeInstance->mIsPacked);
|
|
dataMemberHashCtx.Mixin(typeInstance->mIsCRepr);
|
|
dataMemberHashCtx.Mixin(typeInstance->mIsUnion);
|
|
|
|
int startDataPos = dataPos;
|
|
int maxDataPos = dataPos;
|
|
|
|
BfSizedVector<BfFieldInstance*, 16> dataFieldVec;
|
|
|
|
// We've resolved all the 'var' entries, so now build the actual composite type
|
|
for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
|
|
{
|
|
auto fieldInstance = &fieldInstanceRef;
|
|
if (!fieldInstance->mFieldIncluded)
|
|
continue;
|
|
auto resolvedFieldType = fieldInstance->GetResolvedType();
|
|
if (fieldInstance->mResolvedType == NULL)
|
|
{
|
|
if ((underlyingType == NULL) && (!typeInstance->IsPayloadEnum()))
|
|
BF_ASSERT(typeInstance->mTypeFailed);
|
|
continue;
|
|
}
|
|
if ((fieldInstance->GetFieldDef() != NULL) && (fieldInstance->GetFieldDef()->mIsConst))
|
|
{
|
|
// Resolve later
|
|
}
|
|
else if (fieldInstance->GetFieldDef() != NULL)
|
|
{
|
|
if (!fieldInstance->GetFieldDef()->mIsStatic)
|
|
AddFieldDependency(typeInstance, fieldInstance, resolvedFieldType);
|
|
else
|
|
AddDependency(resolvedFieldType, typeInstance, BfDependencyMap::DependencyFlag_StaticValue);
|
|
}
|
|
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
|
|
BF_ASSERT(fieldInstance->mCustomAttributes == NULL);
|
|
if ((fieldDef != NULL) && (fieldDef->mFieldDeclaration != NULL) && (fieldDef->mFieldDeclaration->mAttributes != NULL))
|
|
{
|
|
fieldInstance->mCustomAttributes = GetCustomAttributes(fieldDef->mFieldDeclaration->mAttributes, fieldDef->mIsStatic ? BfAttributeTargets_StaticField : BfAttributeTargets_Field);
|
|
|
|
for (auto customAttr : fieldInstance->mCustomAttributes->mAttributes)
|
|
{
|
|
if (TypeToString(customAttr.mType) == "System.ThreadStaticAttribute")
|
|
{
|
|
if ((!fieldDef->mIsStatic) || (fieldDef->mIsConst))
|
|
{
|
|
Fail("ThreadStatic attribute can only be used on static fields", fieldDef->mFieldDeclaration->mAttributes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fieldInstance->mResolvedType != NULL)
|
|
{
|
|
auto resolvedFieldType = fieldInstance->GetResolvedType();
|
|
if ((!typeInstance->IsBoxed()) && (fieldDef != NULL))
|
|
{
|
|
if (fieldInstance->mIsEnumPayloadCase)
|
|
{
|
|
PopulateType(resolvedFieldType, BfPopulateType_Data);
|
|
if (resolvedFieldType->WantsGCMarking())
|
|
typeInstance->mWantsGCMarking = true;
|
|
}
|
|
|
|
if ((!fieldDef->mIsConst) && (!fieldDef->mIsStatic))
|
|
{
|
|
PopulateType(resolvedFieldType, resolvedFieldType->IsValueType() ? BfPopulateType_Data : BfPopulateType_Declaration);
|
|
if (resolvedFieldType->WantsGCMarking())
|
|
typeInstance->mWantsGCMarking = true;
|
|
|
|
fieldInstance->mMergedDataIdx = typeInstance->mMergedFieldDataCount;
|
|
if (resolvedFieldType->IsStruct())
|
|
{
|
|
auto resolvedFieldTypeInstance = resolvedFieldType->ToTypeInstance();
|
|
typeInstance->mMergedFieldDataCount += resolvedFieldTypeInstance->mMergedFieldDataCount;
|
|
}
|
|
else if (!resolvedFieldType->IsValuelessType())
|
|
typeInstance->mMergedFieldDataCount++;
|
|
|
|
if (fieldDef->mIsExtern)
|
|
{
|
|
Fail("Cannot declare instance member as 'extern'", fieldDef->mFieldDeclaration->mExternSpecifier, true);
|
|
}
|
|
|
|
BfAstNode* nameRefNode = NULL;
|
|
if (fieldDef->mFieldDeclaration != NULL)
|
|
nameRefNode = fieldDef->mFieldDeclaration->mNameNode;
|
|
if (nameRefNode == NULL)
|
|
nameRefNode = fieldDef->mTypeRef;
|
|
|
|
if (underlyingType != NULL)
|
|
{
|
|
if (typeInstance->IsEnum())
|
|
Fail("Cannot declare instance members in an enum", nameRefNode, true);
|
|
else
|
|
Fail("Cannot declare instance members in a typed primitive struct", nameRefNode, true);
|
|
TypeFailed(typeInstance);
|
|
fieldInstance->mDataIdx = -1;
|
|
continue;
|
|
}
|
|
|
|
if (typeDef->mIsStatic)
|
|
{
|
|
//CS0708
|
|
Fail("Cannot declare instance members in a static class", nameRefNode, true);
|
|
}
|
|
|
|
if (resolvedFieldType->IsValueType())
|
|
{
|
|
BF_ASSERT(!resolvedFieldType->IsDataIncomplete());
|
|
}
|
|
|
|
if (!mCompiler->mIsResolveOnly)
|
|
{
|
|
dataMemberHashCtx.MixinStr(fieldDef->mName);
|
|
dataMemberHashCtx.Mixin(resolvedFieldType->mTypeId);
|
|
}
|
|
|
|
int dataSize = resolvedFieldType->mSize;
|
|
int alignSize = resolvedFieldType->mAlign;
|
|
fieldInstance->mDataSize = dataSize;
|
|
if (!isUnion)
|
|
{
|
|
if (!resolvedFieldType->IsValuelessType())
|
|
{
|
|
if (isCRepr)
|
|
{
|
|
dataFieldVec.push_back(fieldInstance);
|
|
}
|
|
else
|
|
{
|
|
dataFieldVec.push_back(fieldInstance);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(resolvedFieldType->mSize >= 0);
|
|
|
|
if ((alignSize > 1) && (!isPacked))
|
|
dataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
|
fieldInstance->mDataOffset = dataPos;
|
|
|
|
if (!isPacked)
|
|
typeInstance->mInstAlign = std::max(typeInstance->mInstAlign, alignSize);
|
|
dataPos += dataSize;
|
|
|
|
if (dataPos > maxDataPos)
|
|
{
|
|
maxDataPos = dataPos;
|
|
}
|
|
dataPos = startDataPos;
|
|
}
|
|
|
|
auto fieldTypeInst = resolvedFieldType->ToTypeInstance();
|
|
if (fieldTypeInst != NULL)
|
|
{
|
|
if ((fieldTypeInst->mRebuildFlags & BfTypeRebuildFlag_UnderlyingTypeDeferred) != 0)
|
|
{
|
|
BfAstNode* refNode = fieldDef->mFieldDeclaration;
|
|
String failStr;
|
|
failStr = StrFormat("Circular data reference detected between '%s' and '%s'", TypeToString(mCurTypeInstance).c_str(), TypeToString(fieldTypeInst).c_str());
|
|
if (!mContext->mFieldResolveReentrys.IsEmpty())
|
|
{
|
|
failStr += StrFormat(" with the following fields:", TypeToString(mCurTypeInstance).c_str());
|
|
for (int i = 0; i < (int)mContext->mFieldResolveReentrys.size(); i++)
|
|
{
|
|
auto checkField = mContext->mFieldResolveReentrys[i];
|
|
|
|
if (i > 0)
|
|
failStr += ",";
|
|
failStr += "\n '" + TypeToString(typeInstance) + "." + checkField->GetFieldDef()->mName + "'";
|
|
|
|
if (checkField->mOwner == fieldTypeInst)
|
|
refNode = checkField->GetFieldDef()->mFieldDeclaration;
|
|
}
|
|
}
|
|
BfError* err = Fail(failStr, refNode);
|
|
if (err)
|
|
err->mIsPersistent = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool useForUnion = false;
|
|
|
|
if (fieldInstance->mIsEnumPayloadCase)
|
|
{
|
|
if (!typeInstance->IsEnum())
|
|
{
|
|
Fail("Cases can only be used in enum types", fieldDef->mFieldDeclaration);
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(typeInstance->mIsUnion);
|
|
}
|
|
}
|
|
|
|
if ((!fieldDef->mIsStatic) && (!resolvedFieldType->IsValuelessType()))
|
|
{
|
|
if (isUnion)
|
|
{
|
|
fieldInstance->mDataIdx = curFieldDataIdx;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!typeInstance->IsSpecializedType()) && (!typeInstance->IsOnDemand()) && (fieldDef != NULL) && (!CheckDefineMemberProtection(fieldDef->mProtection, resolvedFieldType)))
|
|
{
|
|
//CS0052
|
|
Fail(StrFormat("Inconsistent accessibility: field type '%s' is less accessible than field '%s.%s'",
|
|
TypeToString(resolvedFieldType).c_str(), TypeToString(mCurTypeInstance).c_str(), fieldDef->mName.c_str()),
|
|
fieldDef->mTypeRef, true);
|
|
}
|
|
}
|
|
}
|
|
if (typeInstance->mIsUnion)
|
|
unionInnerType = typeInstance->GetUnionInnerType();
|
|
|
|
if (!isOrdered)
|
|
{
|
|
int dataFieldCount = (int)dataFieldVec.size();
|
|
|
|
Array<Deque<BfFieldInstance*>> alignBuckets;
|
|
for (auto fieldInst : dataFieldVec)
|
|
{
|
|
int alignBits = GetHighestBitSet(fieldInst->mResolvedType->mAlign);
|
|
while (alignBits >= alignBuckets.size())
|
|
alignBuckets.Add({});
|
|
alignBuckets[alignBits].Add(fieldInst);
|
|
}
|
|
dataFieldVec.clear();
|
|
|
|
int curSize = typeInstance->mInstSize;
|
|
while (dataFieldVec.size() != dataFieldCount)
|
|
{
|
|
// Clear out completed buckets
|
|
while (alignBuckets[alignBuckets.size() - 1].IsEmpty())
|
|
{
|
|
alignBuckets.pop_back();
|
|
}
|
|
|
|
int alignBits = GetNumLowZeroBits(curSize) + 1;
|
|
alignBits = BF_MIN(alignBits, (int)alignBuckets.size() - 1);
|
|
|
|
bool foundEntry = false;
|
|
while (alignBits >= 0)
|
|
{
|
|
if (alignBuckets[alignBits].IsEmpty())
|
|
{
|
|
alignBits--;
|
|
continue;
|
|
}
|
|
|
|
bool isHighestBucket = alignBits == alignBuckets.size() - 1;
|
|
|
|
auto fieldInst = alignBuckets[alignBits][0];
|
|
alignBuckets[alignBits].RemoveAt(0);
|
|
dataFieldVec.push_back(fieldInst);
|
|
curSize = BF_ALIGN(curSize, fieldInst->mResolvedType->mAlign);
|
|
curSize += fieldInst->mResolvedType->mSize;
|
|
foundEntry = true;
|
|
|
|
if (!isHighestBucket)
|
|
{
|
|
// We may have a larger type that can fit now...
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundEntry)
|
|
{
|
|
// If no entries will fit, then force an entry of the smallest alignment
|
|
for (int alignBits = 0; alignBits < alignBuckets.size(); alignBits++)
|
|
{
|
|
if (!alignBuckets[alignBits].IsEmpty())
|
|
{
|
|
auto fieldInst = alignBuckets[alignBits][0];
|
|
alignBuckets[alignBits].RemoveAt(0);
|
|
dataFieldVec.push_back(fieldInst);
|
|
curSize = BF_ALIGN(curSize, fieldInst->mResolvedType->mAlign);
|
|
curSize += fieldInst->mResolvedType->mSize;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto fieldInstance : dataFieldVec)
|
|
{
|
|
auto resolvedFieldType = fieldInstance->GetResolvedType();
|
|
|
|
BF_ASSERT(resolvedFieldType->mSize >= 0);
|
|
int dataSize = resolvedFieldType->mSize;
|
|
int alignSize = resolvedFieldType->mAlign;
|
|
fieldInstance->mDataSize = dataSize;
|
|
|
|
bool needsExplicitAlignment = !isCRepr || resolvedFieldType->NeedsExplicitAlignment();
|
|
|
|
int nextDataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
|
int padding = nextDataPos - dataPos;
|
|
if ((alignSize > 1) && (needsExplicitAlignment) && (padding > 0))
|
|
{
|
|
curFieldDataIdx++;
|
|
}
|
|
dataPos = nextDataPos;
|
|
fieldInstance->mDataOffset = dataPos;
|
|
fieldInstance->mDataIdx = curFieldDataIdx++;
|
|
|
|
if (!isPacked)
|
|
typeInstance->mInstAlign = std::max(typeInstance->mInstAlign, alignSize);
|
|
dataPos += dataSize;
|
|
}
|
|
|
|
if (unionInnerType != NULL)
|
|
{
|
|
dataPos = unionInnerType->mSize;
|
|
typeInstance->mInstAlign = BF_MAX(unionInnerType->mAlign, typeInstance->mInstAlign);
|
|
}
|
|
|
|
// Old dataMemberHash location
|
|
|
|
CheckMemberNames(typeInstance);
|
|
|
|
if (isPacked)
|
|
typeInstance->mInstAlign = 1;
|
|
else
|
|
typeInstance->mInstAlign = std::max(1, typeInstance->mInstAlign);
|
|
int alignSize = typeInstance->mInstAlign;
|
|
|
|
if (isCRepr)
|
|
{
|
|
// Align size to alignment
|
|
if (alignSize >= 1)
|
|
typeInstance->mInstSize = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
|
typeInstance->mIsCRepr = true;
|
|
}
|
|
else
|
|
{
|
|
typeInstance->mInstSize = dataPos;
|
|
typeInstance->mIsCRepr = false;
|
|
}
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
{
|
|
for (auto propDef : typeInstance->mTypeDef->mProperties)
|
|
if (propDef->mFieldDeclaration != NULL)
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckProperty(BfNodeDynCast<BfPropertyDeclaration>(propDef->mFieldDeclaration));
|
|
}
|
|
}
|
|
|
|
if (typeInstance->IsObjectOrInterface())
|
|
typeInstance->mWantsGCMarking = true;
|
|
if ((mCompiler->mOptions.mEnableRealtimeLeakCheck) && (!typeInstance->mWantsGCMarking))
|
|
{
|
|
typeInstance->mTypeDef->PopulateMemberSets();
|
|
BfMemberSetEntry* entry = NULL;
|
|
BfMethodDef* methodDef = NULL;
|
|
if (typeInstance->mTypeDef->mMethodSet.TryGetWith(String(BF_METHODNAME_MARKMEMBERS), &entry))
|
|
{
|
|
methodDef = (BfMethodDef*)entry->mMemberDef;
|
|
if (methodDef->HasBody())
|
|
typeInstance->mWantsGCMarking = true;
|
|
}
|
|
}
|
|
|
|
if (typeInstance->IsValueType())
|
|
{
|
|
typeInstance->mSize = typeInstance->mInstSize;
|
|
typeInstance->mAlign = typeInstance->mInstAlign;
|
|
}
|
|
|
|
if ((mCompiler->mOptions.mAllowHotSwapping) && (typeInstance->mDefineState < BfTypeDefineState_Defined))
|
|
{
|
|
if (typeInstance->mHotTypeData == NULL)
|
|
typeInstance->mHotTypeData = new BfHotTypeData();
|
|
|
|
// Clear any unused versions (if we have errors, etc)
|
|
if (mCompiler->mHotState != NULL)
|
|
typeInstance->mHotTypeData->ClearVersionsAfter(mCompiler->mHotState->mCommittedHotCompileIdx);
|
|
else
|
|
BF_ASSERT(typeInstance->mHotTypeData->mTypeVersions.IsEmpty()); // We should have created a new HotTypeData when rebuilding the type
|
|
|
|
BfHotTypeVersion* hotTypeVersion = new BfHotTypeVersion();
|
|
hotTypeVersion->mTypeId = typeInstance->mTypeId;
|
|
if (typeInstance->mBaseType != NULL)
|
|
hotTypeVersion->mBaseType = typeInstance->mBaseType->mHotTypeData->GetLatestVersion();
|
|
hotTypeVersion->mDeclHotCompileIdx = mCompiler->mOptions.mHotCompileIdx;
|
|
if (mCompiler->IsHotCompile())
|
|
hotTypeVersion->mCommittedHotCompileIdx = -1;
|
|
else
|
|
hotTypeVersion->mCommittedHotCompileIdx = 0;
|
|
hotTypeVersion->mRefCount++;
|
|
typeInstance->mHotTypeData->mTypeVersions.Add(hotTypeVersion);
|
|
|
|
if (typeInstance->mBaseType != NULL)
|
|
{
|
|
hotTypeVersion->mMembers.Add(typeInstance->mBaseType->mHotTypeData->GetLatestVersion());
|
|
}
|
|
|
|
for (auto& fieldInst : typeInstance->mFieldInstances)
|
|
{
|
|
auto fieldDef = fieldInst.GetFieldDef();
|
|
if ((fieldDef == NULL) || (fieldDef->mIsStatic))
|
|
continue;
|
|
auto depType = fieldInst.mResolvedType;
|
|
|
|
while (depType->IsSizedArray())
|
|
depType = ((BfSizedArrayType*)depType)->mElementType;
|
|
if (depType->IsStruct())
|
|
{
|
|
PopulateType(depType);
|
|
auto depTypeInst = depType->ToTypeInstance();
|
|
BF_ASSERT(depTypeInst->mHotTypeData != NULL);
|
|
if (depTypeInst->mHotTypeData != NULL)
|
|
hotTypeVersion->mMembers.Add(depTypeInst->mHotTypeData->GetLatestVersion());
|
|
}
|
|
}
|
|
|
|
for (auto member : hotTypeVersion->mMembers)
|
|
member->mRefCount++;
|
|
|
|
BfLogSysM("BfHotTypeVersion %p created for type %p\n", hotTypeVersion, typeInstance);
|
|
}
|
|
|
|
typeInstance->mDefineState = BfTypeDefineState_Defined;
|
|
|
|
if (typeInstance->mTypeFailed)
|
|
mHadBuildError = true;
|
|
|
|
CheckAddFailType();
|
|
|
|
typeInstance->mNeedsMethodProcessing = true;
|
|
typeInstance->mIsFinishingType = false;
|
|
|
|
///
|
|
|
|
// 'Splattable' means that we can be passed via 3 or fewer primitive/pointer values
|
|
if (typeInstance->IsStruct())
|
|
{
|
|
bool hadNonSplattable = false;
|
|
|
|
if (typeInstance->mBaseType != NULL)
|
|
PopulateType(typeInstance->mBaseType, BfPopulateType_Data);
|
|
if ((typeInstance->mBaseType == NULL) || (typeInstance->mBaseType->IsSplattable()))
|
|
{
|
|
int dataCount = 0;
|
|
|
|
std::function<void(BfType*)> splatIterate;
|
|
splatIterate = [&](BfType* checkType)
|
|
{
|
|
if (checkType->IsMethodRef())
|
|
{
|
|
// For simplicitly, any methodRef inside a struct makes the struct non-splattable. This reduces cases of needing to
|
|
// handle embedded methodRefs
|
|
hadNonSplattable = true;
|
|
}
|
|
else if (checkType->IsStruct())
|
|
{
|
|
PopulateType(checkType, BfPopulateType_Data);
|
|
auto checkTypeInstance = checkType->ToTypeInstance();
|
|
if (checkTypeInstance->mBaseType != NULL)
|
|
splatIterate(checkTypeInstance->mBaseType);
|
|
|
|
if (checkTypeInstance->mIsUnion)
|
|
{
|
|
bool wantSplat = false;
|
|
auto unionInnerType = checkTypeInstance->GetUnionInnerType(&wantSplat);
|
|
if (!wantSplat)
|
|
hadNonSplattable = true;
|
|
|
|
splatIterate(unionInnerType);
|
|
|
|
if (checkTypeInstance->IsEnum())
|
|
dataCount++; // Discriminator
|
|
}
|
|
else
|
|
{
|
|
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
|
|
if (fieldInstance->mDataIdx >= 0)
|
|
splatIterate(fieldInstance->GetResolvedType());
|
|
}
|
|
}
|
|
}
|
|
else if (!checkType->IsValuelessType())
|
|
{
|
|
if (checkType->IsSizedArray())
|
|
hadNonSplattable = true;
|
|
dataCount += checkType->GetSplatCount();
|
|
}
|
|
};
|
|
splatIterate(typeInstance);
|
|
|
|
if (isCRepr)
|
|
typeInstance->mIsSplattable = false;
|
|
else
|
|
typeInstance->mIsSplattable = (dataCount <= 3) && (!hadNonSplattable);
|
|
}
|
|
}
|
|
if (typeInstance->IsTypedPrimitive())
|
|
typeInstance->mIsSplattable = true;
|
|
|
|
BF_ASSERT(mContext->mCurTypeState == &typeState);
|
|
|
|
// This is only required for autocomplete and finding type references
|
|
if (!typeInstance->IsSpecializedType())
|
|
{
|
|
for (auto propDef : typeDef->mProperties)
|
|
{
|
|
if (propDef->mTypeRef == NULL)
|
|
continue;
|
|
|
|
BfTypeState typeState;
|
|
typeState.mCurTypeDef = propDef->mDeclaringType;
|
|
typeState.mTypeInstance = typeInstance;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
ResolveTypeRef(propDef->mTypeRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowRef);
|
|
}
|
|
}
|
|
|
|
for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
|
|
{
|
|
auto fieldInstance = &fieldInstanceRef;
|
|
if (!fieldInstance->mFieldIncluded)
|
|
continue;
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
if (fieldDef == NULL)
|
|
continue;
|
|
if ((fieldInstance->mConstIdx == -1) && (fieldDef->mIsConst))
|
|
{
|
|
SetAndRestoreValue<BfFieldDef*> prevTypeRef(mContext->mCurTypeState->mCurFieldDef, fieldDef);
|
|
typeInstance->mModule->ResolveConstField(typeInstance, fieldInstance, fieldDef);
|
|
}
|
|
}
|
|
|
|
if ((typeInstance->IsEnum()) && (!typeInstance->IsPayloadEnum()))
|
|
{
|
|
BfLogSysM("Setting underlying type %p %d\n", typeInstance, underlyingTypeDeferred);
|
|
}
|
|
|
|
if (underlyingTypeDeferred)
|
|
{
|
|
int64 min = 0;
|
|
int64 max = 0;
|
|
|
|
for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
|
|
{
|
|
auto fieldInstance = &fieldInstanceRef;
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
if ((fieldDef != NULL) && (fieldDef->IsEnumCaseEntry()))
|
|
{
|
|
auto constant = typeInstance->mConstHolder->GetConstantById(fieldInstance->mConstIdx);
|
|
BF_ASSERT(constant->mTypeCode == BfTypeCode_Int64);
|
|
|
|
min = BF_MIN(constant->mInt64, min);
|
|
max = BF_MAX(constant->mInt64, max);
|
|
}
|
|
}
|
|
|
|
BfTypeCode typeCode;
|
|
|
|
if ((min >= -0x80) && (max <= 0x7F))
|
|
typeCode = BfTypeCode_Int8;
|
|
else if ((min >= 0) && (max <= 0xFF))
|
|
typeCode = BfTypeCode_UInt8;
|
|
else if ((min >= -0x8000) && (max <= 0x7FFF))
|
|
typeCode = BfTypeCode_Int16;
|
|
else if ((min >= 0) && (max <= 0xFFFF))
|
|
typeCode = BfTypeCode_UInt16;
|
|
else if ((min >= -0x80000000LL) && (max <= 0x7FFFFFFF))
|
|
typeCode = BfTypeCode_Int32;
|
|
else if ((min >= 0) && (max <= 0xFFFFFFFFLL))
|
|
typeCode = BfTypeCode_UInt32;
|
|
else
|
|
typeCode = BfTypeCode_Int64;
|
|
|
|
if (typeCode != BfTypeCode_Int64)
|
|
{
|
|
for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
|
|
{
|
|
auto fieldInstance = &fieldInstanceRef;
|
|
if (fieldInstance->mConstIdx != -1)
|
|
{
|
|
auto constant = typeInstance->mConstHolder->GetConstantById(fieldInstance->mConstIdx);
|
|
BfIRValue newConstant = typeInstance->mConstHolder->CreateConst(typeCode, constant->mUInt64);
|
|
fieldInstance->mConstIdx = newConstant.mId;
|
|
}
|
|
}
|
|
}
|
|
|
|
underlyingType = GetPrimitiveType(typeCode);
|
|
auto fieldInstance = &typeInstance->mFieldInstances.back();
|
|
fieldInstance->mResolvedType = underlyingType;
|
|
fieldInstance->mDataSize = underlyingType->mSize;
|
|
|
|
typeInstance->mSize = underlyingType->mSize;
|
|
typeInstance->mAlign = underlyingType->mAlign;
|
|
typeInstance->mInstSize = underlyingType->mSize;
|
|
typeInstance->mInstAlign = underlyingType->mAlign;
|
|
|
|
typeInstance->mRebuildFlags = (BfTypeRebuildFlags)(typeInstance->mRebuildFlags & ~BfTypeRebuildFlag_UnderlyingTypeDeferred);
|
|
}
|
|
|
|
if ((typeInstance->IsPayloadEnum()) && (!typeInstance->IsBoxed()))
|
|
{
|
|
int lastTagId = -1;
|
|
for (auto& fieldInstanceRef : typeInstance->mFieldInstances)
|
|
{
|
|
auto fieldInstance = &fieldInstanceRef;
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
if ((fieldDef != NULL) && (fieldInstance->mDataIdx < 0))
|
|
lastTagId = -fieldInstance->mDataIdx - 1;
|
|
}
|
|
|
|
auto fieldInstance = &typeInstance->mFieldInstances.back();
|
|
BF_ASSERT(fieldInstance->mResolvedType == NULL);
|
|
BfPrimitiveType* discriminatorType;
|
|
if (lastTagId > 0x7FFFFFFF) // HOW?
|
|
discriminatorType = GetPrimitiveType(BfTypeCode_Int64);
|
|
else if (lastTagId > 0x7FFF)
|
|
discriminatorType = GetPrimitiveType(BfTypeCode_Int32);
|
|
else if (lastTagId > 0x7F)
|
|
discriminatorType = GetPrimitiveType(BfTypeCode_Int16);
|
|
else
|
|
discriminatorType = GetPrimitiveType(BfTypeCode_Int8);
|
|
fieldInstance->mResolvedType = discriminatorType;
|
|
|
|
fieldInstance->mDataOffset = unionInnerType->mSize;
|
|
fieldInstance->mDataIdx = 2; // 0 = base, 1 = payload, 2 = discriminator
|
|
if (!isPacked)
|
|
{
|
|
if ((fieldInstance->mDataOffset % discriminatorType->mAlign) != 0)
|
|
{
|
|
fieldInstance->mDataOffset = BF_ALIGN(fieldInstance->mDataOffset, discriminatorType->mAlign);
|
|
fieldInstance->mDataIdx++; // Add room for explicit padding
|
|
}
|
|
}
|
|
|
|
typeInstance->mAlign = BF_MAX(unionInnerType->mAlign, discriminatorType->mAlign);
|
|
typeInstance->mSize = fieldInstance->mDataOffset + discriminatorType->mSize;
|
|
|
|
typeInstance->mInstSize = typeInstance->mSize;
|
|
typeInstance->mInstAlign = typeInstance->mAlign;
|
|
|
|
dataMemberHashCtx.Mixin(unionInnerType->mTypeId);
|
|
dataMemberHashCtx.Mixin(discriminatorType->mTypeId);
|
|
|
|
typeInstance->mMergedFieldDataCount = 1; // Track it as a single entry
|
|
}
|
|
|
|
if (!typeInstance->IsBoxed())
|
|
{
|
|
if (typeInstance->IsTypedPrimitive())
|
|
{
|
|
auto underlyingType = typeInstance->GetUnderlyingType();
|
|
dataMemberHashCtx.Mixin(underlyingType->mTypeId);
|
|
}
|
|
|
|
Val128 dataMemberHash = dataMemberHashCtx.Finish128();
|
|
|
|
if (typeInstance->mHotTypeData != NULL)
|
|
{
|
|
auto newHotTypeVersion = typeInstance->mHotTypeData->GetLatestVersion();
|
|
newHotTypeVersion->mDataHash = dataMemberHash;
|
|
|
|
if (mCompiler->mHotState != NULL)
|
|
{
|
|
auto committedHotTypeVersion = typeInstance->mHotTypeData->GetTypeVersion(mCompiler->mHotState->mCommittedHotCompileIdx);
|
|
if (committedHotTypeVersion != NULL)
|
|
{
|
|
if ((newHotTypeVersion->mDataHash != committedHotTypeVersion->mDataHash) && (typeInstance->mIsReified))
|
|
{
|
|
BfLogSysM("Hot compile detected data changes in %p '%s'\n", resolvedTypeRef, TypeToString(typeInstance).c_str());
|
|
|
|
if (!typeInstance->mHotTypeData->mPendingDataChange)
|
|
{
|
|
mCompiler->mHotState->mPendingDataChanges.Add(typeInstance->mTypeId);
|
|
typeInstance->mHotTypeData->mPendingDataChange = true;
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(mCompiler->mHotState->mPendingDataChanges.Contains(typeInstance->mTypeId));
|
|
}
|
|
|
|
bool baseHadChanges = (typeInstance->mBaseType != NULL) && (typeInstance->mBaseType->mHotTypeData != NULL) && (typeInstance->mBaseType->mHotTypeData->mPendingDataChange);
|
|
if (!baseHadChanges)
|
|
Warn(0, StrFormat("Hot compile detected data changes in '%s'", TypeToString(typeInstance).c_str()), typeDef->GetRefNode());
|
|
}
|
|
else if (typeInstance->mHotTypeData->mPendingDataChange)
|
|
{
|
|
BfLogSysM("Hot compile removed pending data change for %p '%s'\n", resolvedTypeRef, TypeToString(typeInstance).c_str());
|
|
mCompiler->mHotState->RemovePendingChanges(typeInstance);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO: We moved this to much earlier in InitType
|
|
// This allows us to properly deleted a dependent generic type if a typeGenericArg gets deleted.
|
|
//...
|
|
// Add generic dependencies if needed
|
|
// auto genericTypeInstance = typeInstance->ToGenericTypeInstance();
|
|
// if (genericTypeInstance != NULL)
|
|
// {
|
|
// for (auto genericType : genericTypeInstance->mTypeGenericArguments)
|
|
// {
|
|
// if (genericType->IsPrimitiveType())
|
|
// genericType = GetWrappedStructType(genericType);
|
|
// if (genericType != NULL)
|
|
// {
|
|
// AddDependency(genericType, genericTypeInstance, BfDependencyMap::DependencyFlag_TypeGenericArg);
|
|
// BfLogSysM("Adding generic dependency of %p for type %p\n", genericTypeInstance, genericTypeInstance);
|
|
// }
|
|
// }
|
|
//
|
|
// if (typeInstance->IsSpecializedType())
|
|
// {
|
|
// // This ensures we rebuild the unspecialized type whenever the specialized type rebuilds. This is important
|
|
// // for generic type binding
|
|
// auto unspecializedTypeInstance = GetUnspecializedTypeInstance(typeInstance);
|
|
// BF_ASSERT(!unspecializedTypeInstance->IsUnspecializedTypeVariation());
|
|
// mContext->mScratchModule->AddDependency(typeInstance, unspecializedTypeInstance, BfDependencyMap::DependencyFlag_UnspecializedType);
|
|
// }
|
|
// }
|
|
|
|
if (populateType == BfPopulateType_Data)
|
|
return true;
|
|
|
|
disableYield.Release();
|
|
|
|
if (canDoMethodProcessing)
|
|
{
|
|
if (typeInstance->mNeedsMethodProcessing) // May have been handled by GetRawMethodInstanceAtIdx above
|
|
DoTypeInstanceMethodProcessing(typeInstance);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance)
|
|
{
|
|
if (typeInstance->IsSpecializedByAutoCompleteMethod())
|
|
return;
|
|
|
|
BF_ASSERT(typeInstance->mModule == this);
|
|
|
|
//TODO: This is new, make sure this is in the right place
|
|
/*if (mAwaitingInitFinish)
|
|
FinishInit();*/
|
|
|
|
AutoDisallowYield disableYield(mSystem);
|
|
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
|
|
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, NULL);
|
|
|
|
BfLogSysM("DoTypeInstanceMethodProcessing: %p %s Revision:%d\n", typeInstance, TypeToString(typeInstance).c_str(), typeInstance->mRevision);
|
|
|
|
auto typeDef = typeInstance->mTypeDef;
|
|
|
|
// Generate all methods. Pass 0
|
|
for (auto methodDef : typeDef->mMethods)
|
|
{
|
|
auto methodInstanceGroup = &typeInstance->mMethodInstanceGroups[methodDef->mIdx];
|
|
|
|
// This should still be set to the default value
|
|
BF_ASSERT((methodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_NotSet) || (methodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_AlwaysInclude));
|
|
}
|
|
|
|
|
|
if (typeInstance == mContext->mBfObjectType)
|
|
{
|
|
BF_ASSERT(typeInstance->mInterfaceMethodTable.size() == 0);
|
|
}
|
|
|
|
int newIntefaceStartIdx = 0;
|
|
auto baseType = typeInstance->mBaseType;
|
|
if (baseType != NULL)
|
|
{
|
|
auto baseTypeInst = baseType->ToTypeInstance();
|
|
if (baseType->IsIncomplete())
|
|
PopulateType(baseType, BfPopulateType_Full_Force);
|
|
|
|
typeInstance->mInterfaceMethodTable = baseTypeInst->mInterfaceMethodTable;
|
|
typeInstance->mVirtualMethodTable = baseType->mVirtualMethodTable;
|
|
typeInstance->mVirtualMethodTableSize = baseType->mVirtualMethodTableSize;
|
|
if ((!mCompiler->IsHotCompile()) && (!mCompiler->mPassInstance->HasFailed()) && ((mCompiler->mResolvePassData == NULL) || (mCompiler->mResolvePassData->mAutoComplete == NULL)))
|
|
{
|
|
BF_ASSERT(typeInstance->mVirtualMethodTable.size() == typeInstance->mVirtualMethodTableSize);
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(typeInstance->mVirtualMethodTableSize >= (int)typeInstance->mVirtualMethodTable.size());
|
|
}
|
|
}
|
|
|
|
// Add new interfaces
|
|
for (int iFaceIdx = 0; iFaceIdx < (int)typeInstance->mInterfaces.size(); iFaceIdx++)
|
|
{
|
|
BfTypeInterfaceEntry& typeInterfaceInst = typeInstance->mInterfaces[iFaceIdx];
|
|
|
|
auto checkInterface = typeInterfaceInst.mInterfaceType;
|
|
if (checkInterface->IsIncomplete())
|
|
PopulateType(checkInterface, BfPopulateType_Full_Force);
|
|
|
|
typeInterfaceInst.mStartInterfaceTableIdx = (int)typeInstance->mInterfaceMethodTable.size();
|
|
|
|
// We don't add to the vtable for interface declarations, we just reference the listed interfaces
|
|
if (!typeInstance->IsInterface())
|
|
{
|
|
auto interfaceTypeDef = checkInterface->mTypeDef;
|
|
BF_ASSERT(interfaceTypeDef->mMethods.size() == checkInterface->mMethodInstanceGroups.size());
|
|
|
|
// Reserve empty entries
|
|
for (int methodIdx = 0; methodIdx < (int)interfaceTypeDef->mMethods.size(); methodIdx++)
|
|
typeInstance->mInterfaceMethodTable.push_back(BfTypeInterfaceMethodEntry());
|
|
}
|
|
}
|
|
|
|
auto checkTypeInstance = typeInstance;
|
|
while (checkTypeInstance != NULL)
|
|
{
|
|
for (auto&& interfaceEntry : checkTypeInstance->mInterfaces)
|
|
{
|
|
AddDependency(interfaceEntry.mInterfaceType, typeInstance, BfDependencyMap::DependencyFlag_ImplementsInterface);
|
|
}
|
|
checkTypeInstance = checkTypeInstance->mBaseType;
|
|
}
|
|
|
|
//for (auto& intefaceInst : typeInstance->mInterfaces)
|
|
|
|
if (typeInstance == mContext->mBfObjectType)
|
|
{
|
|
BF_ASSERT(typeInstance->mInterfaceMethodTable.size() == 1);
|
|
}
|
|
|
|
// Slot interfaces method blocks in vtable
|
|
{
|
|
int ifaceVirtIdx = 0;
|
|
std::unordered_map<BfTypeInstance*, BfTypeInterfaceEntry*> interfaceMap;
|
|
BfTypeInstance* checkType = typeInstance->mBaseType;
|
|
while (checkType != NULL)
|
|
{
|
|
for (auto&& ifaceEntry : checkType->mInterfaces)
|
|
{
|
|
interfaceMap[ifaceEntry.mInterfaceType] = &ifaceEntry;
|
|
ifaceVirtIdx = std::max(ifaceVirtIdx, ifaceEntry.mStartVirtualIdx + ifaceEntry.mInterfaceType->mVirtualMethodTableSize);
|
|
}
|
|
checkType = checkType->mBaseType;
|
|
}
|
|
|
|
for (int iFaceIdx = 0; iFaceIdx < (int)typeInstance->mInterfaces.size(); iFaceIdx++)
|
|
{
|
|
BfTypeInterfaceEntry& typeInterfaceInst = typeInstance->mInterfaces[iFaceIdx];
|
|
auto itr = interfaceMap.find(typeInterfaceInst.mInterfaceType);
|
|
if (itr != interfaceMap.end())
|
|
{
|
|
auto prevEntry = itr->second;
|
|
typeInterfaceInst.mStartVirtualIdx = prevEntry->mStartVirtualIdx;
|
|
}
|
|
else
|
|
{
|
|
typeInterfaceInst.mStartVirtualIdx = ifaceVirtIdx;
|
|
ifaceVirtIdx += typeInterfaceInst.mInterfaceType->mVirtualMethodTableSize;
|
|
interfaceMap[typeInterfaceInst.mInterfaceType] = &typeInterfaceInst;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto isBoxed = typeInstance->IsBoxed();
|
|
|
|
typeInstance->mNeedsMethodProcessing = false;
|
|
typeInstance->mTypeIncomplete = false;
|
|
|
|
auto checkBaseType = typeInstance->mBaseType;
|
|
while (checkBaseType != NULL)
|
|
{
|
|
PopulateType(checkBaseType, BfPopulateType_Full_Force);
|
|
BF_ASSERT((!checkBaseType->IsIncomplete()) || (checkBaseType->mTypeFailed));
|
|
checkBaseType = checkBaseType->mBaseType;
|
|
}
|
|
|
|
if ((mCompiler->mOptions.mHasVDataExtender) && (!typeInstance->IsInterface()))
|
|
{
|
|
// This is the vExt entry for this type instance
|
|
BfVirtualMethodEntry entry;
|
|
entry.mDeclaringMethod.mMethodNum = -1;
|
|
entry.mDeclaringMethod.mTypeInstance = typeInstance;
|
|
typeInstance->mVirtualMethodTable.push_back(entry);
|
|
typeInstance->mVirtualMethodTableSize++;
|
|
}
|
|
|
|
// Fill out to correct size
|
|
if (typeInstance->mHotTypeData != NULL)
|
|
{
|
|
//auto hotLatestVersionHead = typeInstance->mHotTypeData->GetLatestVersionHead();
|
|
int wantVTableSize = typeInstance->GetBaseVTableSize() + (int)typeInstance->mHotTypeData->mVTableEntries.size();
|
|
while ((int)typeInstance->mVirtualMethodTable.size() < wantVTableSize)
|
|
{
|
|
typeInstance->mVirtualMethodTable.push_back(BfVirtualMethodEntry());
|
|
typeInstance->mVirtualMethodTableSize++;
|
|
}
|
|
}
|
|
|
|
BfAmbiguityContext ambiguityContext;
|
|
ambiguityContext.mTypeInstance = typeInstance;
|
|
ambiguityContext.mModule = this;
|
|
ambiguityContext.mIsProjectSpecific = false;
|
|
|
|
bool wantsOnDemandMethods = false;
|
|
//TODO: Testing having interface methods be "on demand"...
|
|
//if (!typeInstance->IsInterface())
|
|
|
|
//
|
|
{
|
|
if (typeInstance->IsSpecializedType())
|
|
wantsOnDemandMethods = true;
|
|
else if ((mCompiler->mOptions.mCompileOnDemandKind != BfCompileOnDemandKind_AlwaysInclude) &&
|
|
(!typeInstance->IsUnspecializedTypeVariation()))
|
|
{
|
|
//if (typeDef->mName->ToString() != "AttributeUsageAttribute")
|
|
|
|
auto attributeDef = mCompiler->mAttributeTypeDef;
|
|
auto attributeType = mContext->mUnreifiedModule->ResolveTypeDef(attributeDef, BfPopulateType_Identity)->ToTypeInstance();
|
|
if (!TypeIsSubTypeOf(mCurTypeInstance, attributeType, false))
|
|
{
|
|
wantsOnDemandMethods = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//bool allDeclsRequired = (mIsReified) && (mCompiler->mOptions.mEmitDebugInfo) && ();
|
|
bool allDeclsRequired = false;
|
|
//if ((mIsReified) && (mCompiler->mOptions.mEmitDebugInfo) && (!mCompiler->mWantsDeferMethodDecls))
|
|
// if ((mIsReified) && (mCompiler->mOptions.mEmitDebugInfo))
|
|
// {
|
|
// allDeclsRequired = true;
|
|
// }
|
|
|
|
HashSet<String> ifaceMethodNameSet;
|
|
if (wantsOnDemandMethods)
|
|
{
|
|
for (int iFaceIdx = newIntefaceStartIdx; iFaceIdx < (int)typeInstance->mInterfaces.size(); iFaceIdx++)
|
|
{
|
|
BfTypeInterfaceEntry& typeInterfaceInst = typeInstance->mInterfaces[iFaceIdx];
|
|
for (auto checkMethodDef : typeInterfaceInst.mInterfaceType->mTypeDef->mMethods)
|
|
{
|
|
ifaceMethodNameSet.Add(checkMethodDef->mName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate all methods. Pass 1
|
|
for (auto methodDef : typeDef->mMethods)
|
|
{
|
|
if (methodDef->mMethodType == BfMethodType_CtorClear)
|
|
{
|
|
NOP;
|
|
}
|
|
|
|
auto methodInstanceGroup = &typeInstance->mMethodInstanceGroups[methodDef->mIdx];
|
|
|
|
if (methodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_AlwaysInclude)
|
|
continue;
|
|
|
|
// This should still be set to the default value
|
|
BF_ASSERT(methodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_NotSet);
|
|
|
|
if ((isBoxed) && (!methodDef->mIsVirtual))
|
|
{
|
|
if (methodDef->mIsStatic)
|
|
continue;
|
|
bool boxedRequired = false;
|
|
if (((methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mParams.size() == 0)) ||
|
|
(methodDef->mMethodType == BfMethodType_Dtor) ||
|
|
((methodDef->mName == BF_METHODNAME_MARKMEMBERS) || (methodDef->mName == BF_METHODNAME_MARKMEMBERS_STATIC) || (methodDef->mName == BF_METHODNAME_INVOKE) || (methodDef->mName == BF_METHODNAME_DYNAMICCAST)) ||
|
|
(methodDef->mGenericParams.size() != 0))
|
|
boxedRequired = true;
|
|
if (!boxedRequired)
|
|
{
|
|
if (wantsOnDemandMethods)
|
|
methodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_NoDecl_AwaitingReference;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (methodDef->mMethodType == BfMethodType_Ignore)
|
|
continue;
|
|
|
|
if ((methodDef->mName == BF_METHODNAME_DYNAMICCAST) && (typeInstance->IsValueType()))
|
|
continue; // This is just a placeholder for boxed types
|
|
|
|
methodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_AlwaysInclude;
|
|
|
|
if (wantsOnDemandMethods)
|
|
{
|
|
bool implRequired = false;
|
|
bool declRequired = false;
|
|
|
|
if ((!typeInstance->IsGenericTypeInstance()) && (methodDef->mGenericParams.IsEmpty()))
|
|
{
|
|
// For non-generic methods, declare all methods. This is useful for debug info.
|
|
declRequired = true;
|
|
}
|
|
|
|
if (methodDef->mMethodType == BfMethodType_CtorNoBody)
|
|
declRequired = true;
|
|
|
|
if ((methodDef->mIsStatic) &&
|
|
((methodDef->mMethodType == BfMethodType_Dtor) || (methodDef->mMethodType == BfMethodType_Ctor)))
|
|
{
|
|
implRequired = true;
|
|
}
|
|
|
|
if (mCompiler->mOptions.mEnableRealtimeLeakCheck)
|
|
{
|
|
if ((methodDef->mName == BF_METHODNAME_MARKMEMBERS_STATIC) ||
|
|
(methodDef->mName == BF_METHODNAME_FIND_TLS_MEMBERS) ||
|
|
((methodDef->mName == BF_METHODNAME_MARKMEMBERS) && (typeInstance->IsObject())))
|
|
implRequired = true;
|
|
}
|
|
|
|
BfAttributeDirective* attributes = NULL;
|
|
if (auto methodDeclaration = methodDef->GetMethodDeclaration())
|
|
attributes = methodDeclaration->mAttributes;
|
|
if (auto propertyDeclaration = methodDef->GetPropertyDeclaration())
|
|
attributes = propertyDeclaration->mAttributes;
|
|
while (attributes != NULL)
|
|
{
|
|
if (attributes->mAttributeTypeRef != NULL)
|
|
{
|
|
auto typeRefName = attributes->mAttributeTypeRef->ToString();
|
|
|
|
if (typeRefName == "AlwaysInclude")
|
|
implRequired = true;
|
|
else if (typeRefName == "Export")
|
|
implRequired = true;
|
|
else if (typeRefName == "Test")
|
|
implRequired = true;
|
|
else
|
|
declRequired = true; // We need to create so we can check for AlwaysInclude in included attributes
|
|
}
|
|
|
|
attributes = attributes->mNextAttribute;
|
|
}
|
|
|
|
if (typeInstance->IsInterface())
|
|
declRequired = true;
|
|
|
|
if (methodDef->mIsVirtual)
|
|
declRequired = true;
|
|
|
|
if (!implRequired)
|
|
{
|
|
// Any interface with the same name causes us to not be on-demand
|
|
if (ifaceMethodNameSet.Contains(methodDef->mName))
|
|
declRequired = true;
|
|
}
|
|
|
|
// Is this strictly necessary? It will reduce our compilation speed in order to ensure methods are available for debug info
|
|
if (allDeclsRequired)
|
|
declRequired = true;
|
|
|
|
if (methodDef->mMethodDeclaration == NULL)
|
|
{
|
|
// Internal methods don't need decls
|
|
if (methodDef->mName == BF_METHODNAME_DEFAULT_EQUALS)
|
|
declRequired = false;
|
|
}
|
|
|
|
if (!implRequired)
|
|
{
|
|
if (!mIsScratchModule)
|
|
mOnDemandMethodCount++;
|
|
|
|
if (!declRequired)
|
|
{
|
|
methodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_NoDecl_AwaitingReference;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
methodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Decl_AwaitingDecl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BfLogSysM("Starting DoTypeInstanceMethodProcessing %p GetMethodInstance pass. OnDemandMethods: %d\n", typeInstance, mOnDemandMethodCount);
|
|
|
|
// Pass 2
|
|
for (auto methodDef : typeDef->mMethods)
|
|
{
|
|
auto methodInstanceGroup = &typeInstance->mMethodInstanceGroups[methodDef->mIdx];
|
|
|
|
if ((methodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_AlwaysInclude) &&
|
|
(methodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_Decl_AwaitingDecl))
|
|
{
|
|
BfLogSysM("Skipping GetMethodInstance on MethodDef: %p OnDemandKind: %d\n", methodDef, methodInstanceGroup->mOnDemandKind);
|
|
continue;
|
|
}
|
|
|
|
int prevWorklistSize = (int)mContext->mMethodWorkList.size();
|
|
auto moduleMethodInstance = GetMethodInstance(typeInstance, methodDef, BfTypeVector(), ((methodDef->mGenericParams.size() != 0) || (typeInstance->IsUnspecializedType())) ? BfGetMethodInstanceFlag_UnspecializedPass : BfGetMethodInstanceFlag_None);
|
|
|
|
auto methodInstance = moduleMethodInstance.mMethodInstance;
|
|
if (methodInstance == NULL)
|
|
{
|
|
BF_ASSERT(typeInstance->IsGenericTypeInstance() && (typeInstance->mTypeDef->mIsCombinedPartial));
|
|
continue;
|
|
}
|
|
|
|
if ((!mCompiler->mIsResolveOnly) &&
|
|
((methodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingReference) || (!typeInstance->IsReified())))
|
|
{
|
|
bool forceMethodImpl = false;
|
|
|
|
BfCustomAttributes* customAttributes = methodInstance->GetCustomAttributes();
|
|
if ((customAttributes != NULL) && (typeInstance->IsReified()))
|
|
{
|
|
for (auto& attr : customAttributes->mAttributes)
|
|
{
|
|
auto attrTypeInst = attr.mType->ToTypeInstance();
|
|
auto attrCustomAttributes = attrTypeInst->mCustomAttributes;
|
|
if (attrCustomAttributes == NULL)
|
|
continue;
|
|
|
|
for (auto& attrAttr : attrCustomAttributes->mAttributes)
|
|
{
|
|
if (attrAttr.mType->ToTypeInstance()->mTypeDef == mCompiler->mAttributeUsageAttributeTypeDef)
|
|
{
|
|
// Check for Flags arg
|
|
if (attrAttr.mCtorArgs.size() < 2)
|
|
continue;
|
|
auto constant = attrTypeInst->mConstHolder->GetConstant(attrAttr.mCtorArgs[1]);
|
|
if (constant == NULL)
|
|
continue;
|
|
if (constant->mTypeCode == BfTypeCode_Boolean)
|
|
continue;
|
|
if ((constant->mInt8 & BfCustomAttributeFlags_AlwaysIncludeTarget) != 0)
|
|
forceMethodImpl = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeInstance->mTypeDef->mProject->mTargetType == BfTargetType_BeefTest)
|
|
{
|
|
if ((customAttributes != NULL) && (customAttributes->Contains(mCompiler->mTestAttributeTypeDef)))
|
|
{
|
|
forceMethodImpl = true;
|
|
}
|
|
}
|
|
|
|
if (forceMethodImpl)
|
|
{
|
|
if (!typeInstance->IsReified())
|
|
mContext->mScratchModule->PopulateType(typeInstance, BfPopulateType_Data);
|
|
// Reify method
|
|
mContext->mScratchModule->GetMethodInstance(typeInstance, methodDef, BfTypeVector());
|
|
BF_ASSERT(methodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_Decl_AwaitingReference);
|
|
}
|
|
}
|
|
|
|
|
|
bool methodUsedVirtually = false;
|
|
if (typeInstance->IsInterface())
|
|
{
|
|
if ((!methodDef->mIsConcrete) && (!methodDef->mIsStatic) && (!methodInstance->HasSelf()))
|
|
SlotInterfaceMethod(methodInstance);
|
|
}
|
|
else if (!methodDef->IsEmptyPartial())
|
|
{
|
|
methodUsedVirtually = SlotVirtualMethod(methodInstance, &ambiguityContext);
|
|
}
|
|
|
|
// This is important for reducing latency of autocomplete popup, but it's important we don't allow the autocomplete
|
|
// thread to cause any reentry issues by re-populating a type at an "inopportune time". We do allow certain
|
|
// reentries in PopulateType, but not when we're resolving fields (for example)
|
|
if ((mContext->mFieldResolveReentrys.size() == 0) && (!mContext->mResolvingVarField))
|
|
{
|
|
disableYield.Release();
|
|
mSystem->CheckLockYield();
|
|
disableYield.Acquire();
|
|
}
|
|
}
|
|
|
|
|
|
BF_ASSERT(typeInstance->mVirtualMethodTable.size() == typeInstance->mVirtualMethodTableSize);
|
|
|
|
if ((isBoxed) && (!typeInstance->IsUnspecializedTypeVariation()))
|
|
{
|
|
// Any interface method that can be called virtually via an interface pointer needs to go into the boxed type
|
|
auto underlyingType = typeInstance->GetUnderlyingType();
|
|
BfTypeInstance* underlyingTypeInstance;
|
|
if (underlyingType->IsPrimitiveType())
|
|
underlyingTypeInstance = GetPrimitiveStructType(((BfPrimitiveType*)underlyingType)->mTypeDef->mTypeCode);
|
|
else
|
|
underlyingTypeInstance = underlyingType->ToTypeInstance();
|
|
if (underlyingTypeInstance != NULL)
|
|
{
|
|
PopulateType(underlyingTypeInstance, BfPopulateType_Full_Force);
|
|
for (int ifaceIdx = 0; ifaceIdx < (int)underlyingTypeInstance->mInterfaces.size(); ifaceIdx++)
|
|
{
|
|
auto& underlyingIFaceTypeInst = underlyingTypeInstance->mInterfaces[ifaceIdx];
|
|
auto& boxedIFaceTypeInst = typeInstance->mInterfaces[ifaceIdx];
|
|
BF_ASSERT(underlyingIFaceTypeInst.mInterfaceType == boxedIFaceTypeInst.mInterfaceType);
|
|
|
|
auto ifaceInst = underlyingIFaceTypeInst.mInterfaceType;
|
|
int startIdx = underlyingIFaceTypeInst.mStartInterfaceTableIdx;
|
|
int boxedStartIdx = boxedIFaceTypeInst.mStartInterfaceTableIdx;
|
|
int iMethodCount = (int)ifaceInst->mMethodInstanceGroups.size();
|
|
|
|
for (int iMethodIdx = 0; iMethodIdx < iMethodCount; iMethodIdx++)
|
|
{
|
|
auto matchedMethodRef = &underlyingTypeInstance->mInterfaceMethodTable[iMethodIdx + startIdx].mMethodRef;
|
|
auto boxedMatchedMethodRef = &typeInstance->mInterfaceMethodTable[iMethodIdx + boxedStartIdx].mMethodRef;
|
|
BfMethodInstance* matchedMethod = *matchedMethodRef;
|
|
auto ifaceMethodInst = ifaceInst->mMethodInstanceGroups[iMethodIdx].mDefault;
|
|
if (ifaceMethodInst->mVirtualTableIdx != -1)
|
|
{
|
|
if (matchedMethod == NULL)
|
|
{
|
|
AssertErrorState();
|
|
}
|
|
else
|
|
{
|
|
if (!matchedMethod->mIsForeignMethodDef)
|
|
{
|
|
BfMethodInstanceGroup* boxedMethodInstanceGroup = &typeInstance->mMethodInstanceGroups[matchedMethod->mMethodDef->mIdx];
|
|
if (boxedMethodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_NoDecl_AwaitingReference)
|
|
{
|
|
boxedMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Decl_AwaitingDecl;
|
|
if (!mIsScratchModule)
|
|
mOnDemandMethodCount++;
|
|
}
|
|
}
|
|
|
|
auto moduleMethodInstance = GetMethodInstance(typeInstance, matchedMethod->mMethodDef, BfTypeVector(),
|
|
matchedMethod->mIsForeignMethodDef ? BfGetMethodInstanceFlag_ForeignMethodDef : BfGetMethodInstanceFlag_None,
|
|
matchedMethod->GetForeignType());
|
|
auto methodInstance = moduleMethodInstance.mMethodInstance;
|
|
UniqueSlotVirtualMethod(methodInstance);
|
|
*boxedMatchedMethodRef = methodInstance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeInstance->mHotTypeData != NULL)
|
|
{
|
|
auto latestVersion = typeInstance->mHotTypeData->GetLatestVersion();
|
|
auto latestVersionHead = typeInstance->mHotTypeData->GetLatestVersionHead();
|
|
if (typeInstance->mHotTypeData->mVTableOrigLength != -1)
|
|
{
|
|
bool hasSlotError = false;
|
|
|
|
BF_ASSERT(mCompiler->IsHotCompile());
|
|
//typeInstance->mHotTypeData->mDirty = true;
|
|
//Val128 vtHash;
|
|
Array<int> ifaceMapping;
|
|
ifaceMapping.Resize(latestVersionHead->mInterfaceMapping.size());
|
|
typeInstance->CalcHotVirtualData(&ifaceMapping);
|
|
|
|
// Hot swapping allows for interfaces to be added to types or removed from types, but it doesn't allow
|
|
// interfaces to be added when the slot number has already been used -- even if the interface using
|
|
// that slot has been removed.
|
|
for (int slotIdx = 0; slotIdx < (int)ifaceMapping.size(); slotIdx++)
|
|
{
|
|
int newId = ifaceMapping[slotIdx];
|
|
int oldId = 0;
|
|
if (slotIdx < (int)latestVersionHead->mInterfaceMapping.size())
|
|
oldId = latestVersionHead->mInterfaceMapping[slotIdx];
|
|
|
|
if ((newId != oldId) && (newId != 0) && (oldId != 0))
|
|
{
|
|
String interfaceName;
|
|
for (auto iface : typeInstance->mInterfaces)
|
|
{
|
|
if (iface.mInterfaceType->mTypeId == newId)
|
|
interfaceName = TypeToString(iface.mInterfaceType);
|
|
}
|
|
|
|
Warn(0, StrFormat("Hot swap detected resolvable interface slot collision with '%s'.", interfaceName.c_str()), typeDef->mTypeDeclaration);
|
|
BF_ASSERT(latestVersion != latestVersionHead);
|
|
if (!hasSlotError)
|
|
{
|
|
latestVersion->mInterfaceMapping = ifaceMapping;
|
|
}
|
|
hasSlotError = true;
|
|
}
|
|
else if (hasSlotError)
|
|
{
|
|
if (oldId != 0)
|
|
latestVersion->mInterfaceMapping[slotIdx] = oldId;
|
|
}
|
|
|
|
if (oldId != 0)
|
|
ifaceMapping[slotIdx] = oldId;
|
|
}
|
|
latestVersionHead->mInterfaceMapping = ifaceMapping;
|
|
|
|
if (hasSlotError)
|
|
mCompiler->mHotState->mPendingFailedSlottings.Add(typeInstance->mTypeId);
|
|
else
|
|
mCompiler->mHotState->mPendingFailedSlottings.Remove(typeInstance->mTypeId);
|
|
}
|
|
}
|
|
|
|
if ((typeInstance->IsInterface()) && (!typeInstance->IsUnspecializedType()) && (typeInstance->mIsReified) && (typeInstance->mSlotNum == -1) && (mCompiler->IsHotCompile()))
|
|
{
|
|
mCompiler->mHotState->mHasNewInterfaceTypes = true;
|
|
}
|
|
|
|
if ((!typeInstance->IsInterface()) && (!typeInstance->IsUnspecializedTypeVariation()) && (!isBoxed))
|
|
{
|
|
if (!typeInstance->mTypeDef->mIsAbstract)
|
|
{
|
|
for (int methodIdx = 0; methodIdx < (int) typeInstance->mVirtualMethodTable.size(); methodIdx++)
|
|
{
|
|
auto& methodRef = typeInstance->mVirtualMethodTable[methodIdx].mImplementingMethod;
|
|
if (methodRef.mMethodNum == -1)
|
|
{
|
|
BF_ASSERT(mCompiler->mOptions.mHasVDataExtender);
|
|
if (methodRef.mTypeInstance == typeInstance)
|
|
{
|
|
if (typeInstance->mBaseType != NULL)
|
|
BF_ASSERT(methodIdx == (int)typeInstance->mBaseType->mVirtualMethodTableSize);
|
|
}
|
|
continue;
|
|
}
|
|
auto methodInstance = (BfMethodInstance*)methodRef;
|
|
if ((methodInstance != NULL) && (methodInstance->mMethodDef->mIsAbstract))
|
|
{
|
|
if (methodInstance->mMethodDef->mIsAbstract)
|
|
{
|
|
if (!typeInstance->IsUnspecializedTypeVariation())
|
|
{
|
|
if (Fail(StrFormat("'%s' does not implement inherited abstract method '%s'", TypeToString(typeInstance).c_str(), MethodToString(methodInstance).c_str()), typeDef->mTypeDeclaration->mNameNode, true) != NULL)
|
|
mCompiler->mPassInstance->MoreInfo("Abstract method declared", methodInstance->mMethodDef->GetRefNode());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!typeInstance->IsUnspecializedType())
|
|
AssertErrorState();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::unordered_set<String> missingIFaceMethodNames;
|
|
for (auto& ifaceTypeInst : typeInstance->mInterfaces)
|
|
{
|
|
auto ifaceInst = ifaceTypeInst.mInterfaceType;
|
|
int startIdx = ifaceTypeInst.mStartInterfaceTableIdx;
|
|
int iMethodCount = (int)ifaceInst->mMethodInstanceGroups.size();
|
|
auto declTypeDef = ifaceTypeInst.mDeclaringType;
|
|
|
|
for (int iMethodIdx = 0; iMethodIdx < iMethodCount; iMethodIdx++)
|
|
{
|
|
auto matchedMethodRef = &typeInstance->mInterfaceMethodTable[iMethodIdx + startIdx].mMethodRef;
|
|
BfMethodInstance* matchedMethod = *matchedMethodRef;
|
|
auto ifaceMethodInst = ifaceInst->mMethodInstanceGroups[iMethodIdx].mDefault;
|
|
if ((matchedMethod == NULL) && (ifaceMethodInst != NULL))
|
|
{
|
|
missingIFaceMethodNames.insert(ifaceMethodInst->mMethodDef->mName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!missingIFaceMethodNames.empty())
|
|
{
|
|
// Attempt to find matching entries in base types
|
|
ambiguityContext.mIsReslotting = true;
|
|
auto checkType = typeInstance->mBaseType;
|
|
while (checkType != NULL)
|
|
{
|
|
for (auto& methodGroup : checkType->mMethodInstanceGroups)
|
|
{
|
|
auto methodInstance = methodGroup.mDefault;
|
|
if (methodInstance != NULL)
|
|
{
|
|
if ((methodInstance->mMethodDef->mProtection != BfProtection_Private) &&
|
|
(!methodInstance->mMethodDef->mIsOverride) &&
|
|
(missingIFaceMethodNames.find(methodInstance->mMethodDef->mName) != missingIFaceMethodNames.end()))
|
|
{
|
|
SlotVirtualMethod(methodInstance, &ambiguityContext);
|
|
}
|
|
}
|
|
}
|
|
checkType = checkType->mBaseType;
|
|
}
|
|
}
|
|
|
|
for (auto& ifaceTypeInst : typeInstance->mInterfaces)
|
|
{
|
|
auto ifaceInst = ifaceTypeInst.mInterfaceType;
|
|
int startIdx = ifaceTypeInst.mStartInterfaceTableIdx;
|
|
int iMethodCount = (int)ifaceInst->mMethodInstanceGroups.size();
|
|
auto declTypeDef = ifaceTypeInst.mDeclaringType;
|
|
|
|
for (int iMethodIdx = 0; iMethodIdx < iMethodCount; iMethodIdx++)
|
|
{
|
|
auto matchedMethodRef = &typeInstance->mInterfaceMethodTable[iMethodIdx + startIdx].mMethodRef;
|
|
BfMethodInstance* matchedMethod = *matchedMethodRef;
|
|
auto ifaceMethodInst = ifaceInst->mMethodInstanceGroups[iMethodIdx].mDefault;
|
|
if (ifaceMethodInst == NULL)
|
|
continue;
|
|
|
|
auto iReturnType = ifaceMethodInst->mReturnType;
|
|
if (iReturnType->IsSelf())
|
|
iReturnType = typeInstance;
|
|
|
|
if (ifaceMethodInst->mMethodDef->mIsOverride)
|
|
continue; // Don't consider overrides here
|
|
|
|
// If we have "ProjA depends on LibBase", "ProjB depends on LibBase", then a type ClassC in LibBase implementing IFaceD,
|
|
// where IFaceD gets extended with MethodE in ProjA, an implementing MethodE is still required to exist on ClassC --
|
|
// the visibility is bidirectional. A type ClassF implementing IFaceD inside ProjB will not be required to implement
|
|
// MethodE, however
|
|
if ((!ifaceInst->IsTypeMemberAccessible(ifaceMethodInst->mMethodDef->mDeclaringType, ifaceTypeInst.mDeclaringType)) &&
|
|
(!ifaceInst->IsTypeMemberAccessible(ifaceTypeInst.mDeclaringType, ifaceMethodInst->mMethodDef->mDeclaringType)))
|
|
continue;
|
|
|
|
if (!ifaceInst->IsTypeMemberIncluded(ifaceMethodInst->mMethodDef->mDeclaringType, ifaceTypeInst.mDeclaringType))
|
|
continue;
|
|
|
|
bool hadMatch = matchedMethod != NULL;
|
|
bool hadPubFailure = false;
|
|
bool hadMutFailure = false;
|
|
|
|
if (hadMatch)
|
|
{
|
|
if ((matchedMethod->GetExplicitInterface() == NULL) && (matchedMethod->mMethodDef->mProtection != BfProtection_Public))
|
|
{
|
|
hadMatch = false;
|
|
hadPubFailure = true;
|
|
}
|
|
|
|
if (ifaceMethodInst->mVirtualTableIdx != -1)
|
|
{
|
|
if (matchedMethod->mReturnType != iReturnType)
|
|
hadMatch = false;
|
|
}
|
|
else
|
|
{
|
|
// Concrete
|
|
if (matchedMethod->mReturnType->IsInterface())
|
|
hadMatch = false;
|
|
else if (!CanImplicitlyCast(GetFakeTypedValue(matchedMethod->mReturnType), iReturnType))
|
|
hadMatch = false;
|
|
}
|
|
|
|
// If we have mExplicitInterface set then we already gave a mut error (if needed)
|
|
if ((typeInstance->IsValueType()) && (matchedMethod->GetExplicitInterface() == NULL) &&
|
|
(matchedMethod->mMethodDef->mIsMutating) && (!ifaceMethodInst->mMethodDef->mIsMutating))
|
|
{
|
|
hadMutFailure = true;
|
|
hadMatch = false;
|
|
}
|
|
}
|
|
|
|
if (!hadMatch)
|
|
{
|
|
if (!typeInstance->IsUnspecializedTypeVariation())
|
|
{
|
|
auto bestMethodInst = ifaceMethodInst;
|
|
auto bestInterface = ifaceInst;
|
|
|
|
if (matchedMethod == NULL)
|
|
{
|
|
bool searchFailed = false;
|
|
for (auto& checkIFaceTypeInst : typeInstance->mInterfaces)
|
|
{
|
|
auto checkIFaceInst = checkIFaceTypeInst.mInterfaceType;
|
|
int checkStartIdx = checkIFaceTypeInst.mStartInterfaceTableIdx;
|
|
int checkIMethodCount = (int)checkIFaceInst->mMethodInstanceGroups.size();
|
|
for (int checkIMethodIdx = 0; checkIMethodIdx < checkIMethodCount; checkIMethodIdx++)
|
|
{
|
|
auto checkIFaceMethodInst = checkIFaceInst->mMethodInstanceGroups[checkIMethodIdx].mDefault;
|
|
if ((checkIFaceMethodInst != NULL) && (checkIFaceMethodInst->mMethodDef->mIsOverride))
|
|
{
|
|
if (CompareMethodSignatures(checkIFaceMethodInst, ifaceMethodInst))
|
|
{
|
|
bool isBetter = TypeIsSubTypeOf(checkIFaceInst, bestInterface);
|
|
bool isWorse = TypeIsSubTypeOf(bestInterface, checkIFaceInst);
|
|
if (isBetter == isWorse)
|
|
{
|
|
CompareDeclTypes(checkIFaceMethodInst->mMethodDef->mDeclaringType, bestMethodInst->mMethodDef->mDeclaringType, isBetter, isWorse);
|
|
}
|
|
if ((isBetter) && (!isWorse))
|
|
{
|
|
bestInterface = checkIFaceInst;
|
|
bestMethodInst = checkIFaceMethodInst;
|
|
}
|
|
else if (isBetter == isWorse)
|
|
{
|
|
if (!searchFailed)
|
|
{
|
|
searchFailed = true;
|
|
auto error = Fail(StrFormat("There is no most-specific default implementation of '%s'", MethodToString(ifaceMethodInst).c_str()), declTypeDef->mTypeDeclaration->mNameNode);
|
|
if (error != NULL)
|
|
{
|
|
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' is a candidate",
|
|
MethodToString(bestMethodInst).c_str()), bestMethodInst->mMethodDef->GetRefNode());
|
|
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' is a candidate",
|
|
MethodToString(checkIFaceMethodInst).c_str()), checkIFaceMethodInst->mMethodDef->GetRefNode());
|
|
}
|
|
//candidate implementations include '%s' and '%s'",
|
|
//TypeToString(checkIFaceInst).c_str(), TypeToString(bestInterface).c_str()), );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestMethodInst->mReturnType != ifaceMethodInst->mReturnType)
|
|
{
|
|
auto error = Fail(StrFormat("Default interface method '%s' cannot be used does not have the return type '%s'",
|
|
MethodToString(bestMethodInst).c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), declTypeDef->mTypeDeclaration->mNameNode);
|
|
if (error != NULL)
|
|
{
|
|
mCompiler->mPassInstance->MoreInfo("See original method declaration", ifaceMethodInst->mMethodDef->GetRefNode());
|
|
mCompiler->mPassInstance->MoreInfo("See override method declaration", bestMethodInst->mMethodDef->GetRefNode());
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((bestMethodInst->mMethodDef->HasBody()) && (matchedMethod == NULL))
|
|
{
|
|
auto methodDef = bestMethodInst->mMethodDef;
|
|
BfGetMethodInstanceFlags flags = BfGetMethodInstanceFlag_ForeignMethodDef;
|
|
if ((methodDef->mGenericParams.size() != 0) || (typeInstance->IsUnspecializedType()))
|
|
flags = (BfGetMethodInstanceFlags)(flags | BfGetMethodInstanceFlag_UnspecializedPass);
|
|
auto methodInst = GetMethodInstance(typeInstance, methodDef, BfTypeVector(), flags, ifaceInst);
|
|
if (methodInst)
|
|
{
|
|
*matchedMethodRef = methodInst.mMethodInstance;
|
|
|
|
BfMethodInstance* newMethodInstance = *matchedMethodRef;
|
|
BF_ASSERT(newMethodInstance->mIsForeignMethodDef);
|
|
if (newMethodInstance->mMethodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingReference)
|
|
mOnDemandMethodCount++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (typeInstance->IsBoxed())
|
|
{
|
|
if (ifaceMethodInst->mMethodDef->mIsStatic)
|
|
{
|
|
// Skip the statics, those can't be invoked
|
|
}
|
|
else
|
|
{
|
|
// The unboxed version should have had the same error
|
|
if (!typeInstance->GetUnderlyingType()->IsIncomplete())
|
|
AssertErrorState();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BfError* error = Fail(StrFormat("'%s' does not implement interface member '%s'", TypeToString(typeInstance).c_str(), MethodToString(ifaceMethodInst).c_str()), declTypeDef->mTypeDeclaration->mNameNode, true);
|
|
if ((matchedMethod != NULL) && (error != NULL))
|
|
{
|
|
if (hadPubFailure)
|
|
{
|
|
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it is not public",
|
|
MethodToString(matchedMethod).c_str()), matchedMethod->mMethodDef->mReturnTypeRef);
|
|
}
|
|
else if (ifaceMethodInst->mReturnType->IsConcreteInterfaceType())
|
|
{
|
|
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it does not have a concrete return type that implements '%s'",
|
|
MethodToString(matchedMethod).c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), matchedMethod->mMethodDef->mReturnTypeRef);
|
|
}
|
|
else if (hadMutFailure)
|
|
{
|
|
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it is market as 'mut' but interface method does not allow it",
|
|
MethodToString(matchedMethod).c_str()), matchedMethod->mMethodDef->GetMutNode());
|
|
mCompiler->mPassInstance->MoreInfo(StrFormat("Declare the interface method as 'mut' to allow matching 'mut' implementations"), ifaceMethodInst->mMethodDef->mMethodDeclaration);
|
|
}
|
|
else
|
|
{
|
|
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because because it does not have the return type '%s'",
|
|
MethodToString(matchedMethod).c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), matchedMethod->mMethodDef->mReturnTypeRef);
|
|
if ((ifaceMethodInst->mVirtualTableIdx != -1) && (ifaceMethodInst->mReturnType->IsInterface()))
|
|
mCompiler->mPassInstance->MoreInfo("Declare the interface method as 'concrete' to allow matching concrete return values", ifaceMethodInst->mMethodDef->GetMethodDeclaration()->mVirtualSpecifier);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear out the entry
|
|
*matchedMethodRef = BfMethodRef();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ambiguityContext.Finish();
|
|
CheckAddFailType();
|
|
|
|
typeInstance->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotted;
|
|
mCompiler->mStats.mTypesPopulated++;
|
|
mCompiler->UpdateCompletion();
|
|
|
|
BfLogSysM("Finished DoTypeInstanceMethodProcessing %p. OnDemandMethods: %d Virtual Size: %d\n", typeInstance, mOnDemandMethodCount, typeInstance->mVirtualMethodTable.size());
|
|
}
|
|
|
|
void BfModule::RebuildMethods(BfTypeInstance* typeInstance)
|
|
{
|
|
if (typeInstance->IsIncomplete())
|
|
return;
|
|
|
|
typeInstance->mNeedsMethodProcessing = true;
|
|
typeInstance->mDefineState = BfTypeDefineState_Defined;
|
|
typeInstance->mTypeIncomplete = true;
|
|
|
|
for (auto& methodInstanceGroup : typeInstance->mMethodInstanceGroups)
|
|
{
|
|
delete methodInstanceGroup.mDefault;
|
|
methodInstanceGroup.mDefault = NULL;
|
|
delete methodInstanceGroup.mMethodSpecializationMap;
|
|
methodInstanceGroup.mMethodSpecializationMap = NULL;
|
|
methodInstanceGroup.mOnDemandKind = BfMethodOnDemandKind_NotSet;
|
|
}
|
|
|
|
BfTypeProcessRequest* typeProcessRequest = mContext->mPopulateTypeWorkList.Alloc();
|
|
typeProcessRequest->mType = typeInstance;
|
|
BF_ASSERT(typeInstance->mContext == mContext);
|
|
mCompiler->mStats.mTypesQueued++;
|
|
mCompiler->UpdateCompletion();
|
|
}
|
|
|
|
BfModule* BfModule::GetModuleFor(BfType* type)
|
|
{
|
|
auto typeInst = type->ToTypeInstance();
|
|
if (typeInst == NULL)
|
|
return NULL;
|
|
return typeInst->mModule;
|
|
}
|
|
|
|
void BfModule::AddMethodToWorkList(BfMethodInstance* methodInstance)
|
|
{
|
|
BF_ASSERT(!methodInstance->mMethodDef->mIsAbstract);
|
|
|
|
if (methodInstance->IsSpecializedByAutoCompleteMethod())
|
|
return;
|
|
|
|
BF_ASSERT(mCompiler->mCompileState != BfCompiler::CompileState_VData);
|
|
if (methodInstance->mIsReified)
|
|
{
|
|
BF_ASSERT(mCompiler->mCompileState != BfCompiler::CompileState_Unreified);
|
|
}
|
|
|
|
if (methodInstance->mIsUnspecializedVariation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
BF_ASSERT(methodInstance->mMethodProcessRequest == NULL);
|
|
|
|
auto defaultMethod = methodInstance->mMethodInstanceGroup->mDefault;
|
|
if (defaultMethod != methodInstance)
|
|
{
|
|
BF_ASSERT(defaultMethod != NULL);
|
|
if (methodInstance->mMethodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingReference)
|
|
{
|
|
AddMethodToWorkList(defaultMethod);
|
|
}
|
|
}
|
|
|
|
if (methodInstance->mDeclModule != NULL)
|
|
{
|
|
if (methodInstance->mDeclModule != this)
|
|
{
|
|
methodInstance->mDeclModule->AddMethodToWorkList(methodInstance);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto module = GetOrCreateMethodModule(methodInstance);
|
|
methodInstance->mDeclModule = module;
|
|
|
|
BfIRValue func = CreateFunctionFrom(methodInstance, false, methodInstance->mAlwaysInline);
|
|
methodInstance->mIRFunction = func;
|
|
|
|
module->mFuncReferences[methodInstance] = func;
|
|
if (module != this)
|
|
{
|
|
// For extension modules we need to keep track of our own methods so we can know which methods
|
|
// we have defined ourselves and which are from the parent module or other extensions
|
|
if (!func.IsFake())
|
|
mFuncReferences[methodInstance] = func;
|
|
}
|
|
|
|
module->AddMethodToWorkList(methodInstance);
|
|
return;
|
|
}
|
|
|
|
BF_ASSERT(methodInstance->mDeclModule == this);
|
|
|
|
if (defaultMethod == methodInstance)
|
|
{
|
|
if (methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_AlwaysInclude)
|
|
{
|
|
auto owningModule = methodInstance->GetOwner()->GetModule();
|
|
|
|
BF_ASSERT(methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_Referenced);
|
|
if (!mIsScratchModule)
|
|
{
|
|
if (owningModule->mParentModule != NULL)
|
|
BF_ASSERT(owningModule->mParentModule->mOnDemandMethodCount > 0);
|
|
else
|
|
BF_ASSERT(owningModule->mOnDemandMethodCount > 0);
|
|
}
|
|
methodInstance->mMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_InWorkList;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(defaultMethod->mMethodInstanceGroup->IsImplemented());
|
|
}
|
|
|
|
BF_ASSERT(methodInstance->mDeclModule != NULL);
|
|
|
|
auto typeInstance = methodInstance->GetOwner();
|
|
|
|
BfMethodProcessRequest* methodProcessRequest = mContext->mMethodWorkList.Alloc();
|
|
methodProcessRequest->mType = typeInstance;
|
|
methodProcessRequest->mMethodInstance = methodInstance;
|
|
methodProcessRequest->mRevision = typeInstance->mRevision;
|
|
methodProcessRequest->mFromModuleRebuildIdx = mRebuildIdx;
|
|
methodProcessRequest->mFromModule = this;
|
|
|
|
if ((!mCompiler->mIsResolveOnly) && (methodInstance->mIsReified))
|
|
BF_ASSERT(mIsModuleMutable || mReifyQueued);
|
|
|
|
BF_ASSERT(mBfIRBuilder != NULL);
|
|
|
|
if (methodInstance->mMethodDef->mName == "Hey")
|
|
{
|
|
NOP;
|
|
}
|
|
|
|
BfLogSysM("Adding to mMethodWorkList Module: %p IncompleteMethodCount: %d Type %p MethodInstance: %p Name:%s TypeRevision: %d ModuleRevision: %d ReqId:%d\n", this, mIncompleteMethodCount, typeInstance, methodInstance, methodInstance->mMethodDef->mName.c_str(), methodProcessRequest->mRevision, methodProcessRequest->mFromModuleRevision, methodProcessRequest->mReqId);
|
|
|
|
if (mAwaitingFinish)
|
|
{
|
|
BfLogSysM("Module: %p No longer awaiting finish\n", this);
|
|
mAwaitingFinish = false;
|
|
}
|
|
|
|
mCompiler->mStats.mMethodsQueued++;
|
|
mCompiler->UpdateCompletion();
|
|
mIncompleteMethodCount++;
|
|
if (methodInstance->GetNumGenericArguments() != 0)
|
|
mHasGenericMethods = true;
|
|
|
|
methodInstance->mMethodProcessRequest = methodProcessRequest;
|
|
}
|
|
|
|
BfArrayType* BfModule::CreateArrayType(BfType* resolvedType, int dimensions)
|
|
{
|
|
BF_ASSERT(!resolvedType->IsVar());
|
|
|
|
auto arrayType = mContext->mArrayTypePool.Get();
|
|
arrayType->mContext = mContext;
|
|
arrayType->mTypeDef = mCompiler->GetArrayTypeDef(dimensions);
|
|
arrayType->mDimensions = dimensions;
|
|
arrayType->mTypeGenericArguments.clear();
|
|
arrayType->mTypeGenericArguments.push_back(resolvedType);
|
|
auto resolvedArrayType = ResolveType(arrayType);
|
|
if (resolvedArrayType != arrayType)
|
|
mContext->mArrayTypePool.GiveBack(arrayType);
|
|
return (BfArrayType*)resolvedArrayType;
|
|
}
|
|
|
|
BfSizedArrayType* BfModule::CreateSizedArrayType(BfType * resolvedType, int size)
|
|
{
|
|
BF_ASSERT(!resolvedType->IsVar());
|
|
|
|
auto arrayType = mContext->mSizedArrayTypePool.Get();
|
|
arrayType->mContext = mContext;
|
|
arrayType->mElementType = resolvedType;
|
|
arrayType->mElementCount = size;
|
|
auto resolvedArrayType = ResolveType(arrayType);
|
|
if (resolvedArrayType != arrayType)
|
|
mContext->mSizedArrayTypePool.GiveBack(arrayType);
|
|
return (BfSizedArrayType*)resolvedArrayType;
|
|
}
|
|
|
|
BfUnknownSizedArrayType* BfModule::CreateUnknownSizedArrayType(BfType* resolvedType, BfType* sizeParam)
|
|
{
|
|
BF_ASSERT(!resolvedType->IsVar());
|
|
BF_ASSERT(sizeParam->IsGenericParam());
|
|
|
|
auto arrayType = mContext->mUnknownSizedArrayTypePool.Get();
|
|
arrayType->mContext = mContext;
|
|
arrayType->mElementType = resolvedType;
|
|
arrayType->mElementCount = -1;
|
|
arrayType->mElementCountSource = sizeParam;
|
|
auto resolvedArrayType = ResolveType(arrayType);
|
|
if (resolvedArrayType != arrayType)
|
|
mContext->mUnknownSizedArrayTypePool.GiveBack(arrayType);
|
|
return (BfUnknownSizedArrayType*)resolvedArrayType;
|
|
}
|
|
|
|
BfPointerType* BfModule::CreatePointerType(BfType* resolvedType)
|
|
{
|
|
auto pointerType = mContext->mPointerTypePool.Get();
|
|
pointerType->mContext = mContext;
|
|
pointerType->mElementType = resolvedType;
|
|
auto resolvedPointerType = (BfPointerType*)ResolveType(pointerType);
|
|
if (resolvedPointerType != pointerType)
|
|
mContext->mPointerTypePool.GiveBack(pointerType);
|
|
|
|
BF_ASSERT(resolvedPointerType->mElementType == resolvedType);
|
|
|
|
return resolvedPointerType;
|
|
}
|
|
|
|
BfConstExprValueType* BfModule::CreateConstExprValueType(const BfTypedValue& typedValue)
|
|
{
|
|
auto variant = TypedValueToVariant(NULL, typedValue);
|
|
if (variant.mTypeCode == BfTypeCode_None)
|
|
return NULL;
|
|
|
|
auto constExprValueType = mContext->mConstExprValueTypePool.Get();
|
|
constExprValueType->mContext = mContext;
|
|
constExprValueType->mType = typedValue.mType;
|
|
constExprValueType->mValue = variant;
|
|
|
|
auto resolvedConstExprValueType = (BfConstExprValueType*)ResolveType(constExprValueType);
|
|
if (resolvedConstExprValueType != constExprValueType)
|
|
mContext->mConstExprValueTypePool.GiveBack(constExprValueType);
|
|
|
|
BF_ASSERT(resolvedConstExprValueType->mValue.mInt64 == constExprValueType->mValue.mInt64);
|
|
|
|
return resolvedConstExprValueType;
|
|
}
|
|
|
|
BfTypeInstance* BfModule::GetWrappedStructType(BfType* type, bool allowSpecialized)
|
|
{
|
|
if (type->IsPointer())
|
|
{
|
|
if (allowSpecialized)
|
|
{
|
|
BfPointerType* pointerType = (BfPointerType*)type;
|
|
BfTypeVector typeVector;
|
|
typeVector.Add(pointerType->mElementType);
|
|
return ResolveTypeDef(mCompiler->mPointerTTypeDef, typeVector, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
else
|
|
return ResolveTypeDef(mCompiler->mPointerTTypeDef, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
else if (type->IsMethodRef())
|
|
{
|
|
if (allowSpecialized)
|
|
{
|
|
BfMethodRefType* methodRefType = (BfMethodRefType*)type;
|
|
BfTypeVector typeVector;
|
|
typeVector.Add(methodRefType);
|
|
return ResolveTypeDef(mCompiler->mMethodRefTypeDef, typeVector, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
else
|
|
return ResolveTypeDef(mCompiler->mMethodRefTypeDef, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
else if (type->IsSizedArray())
|
|
{
|
|
if (allowSpecialized)
|
|
{
|
|
BfSizedArrayType* sizedArrayType = (BfSizedArrayType*)type;
|
|
BfTypeVector typeVector;
|
|
typeVector.Add(sizedArrayType->mElementType);
|
|
auto sizeValue = BfTypedValue(GetConstValue(sizedArrayType->mElementCount), GetPrimitiveType(BfTypeCode_IntPtr));
|
|
typeVector.Add(CreateConstExprValueType(sizeValue));
|
|
return ResolveTypeDef(mCompiler->mSizedArrayTypeDef, typeVector, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
else
|
|
return ResolveTypeDef(mCompiler->mSizedArrayTypeDef, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
|
|
BF_ASSERT(type->IsPrimitiveType());
|
|
return GetPrimitiveStructType(((BfPrimitiveType*)type)->mTypeDef->mTypeCode);
|
|
}
|
|
|
|
BfPrimitiveType* BfModule::GetPrimitiveType(BfTypeCode typeCode)
|
|
{
|
|
BfPrimitiveType* primType = mContext->mPrimitiveTypes[typeCode];
|
|
if (primType == NULL)
|
|
{
|
|
switch (typeCode)
|
|
{
|
|
case BfTypeCode_NullPtr:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeNullPtr);
|
|
break;
|
|
case BfTypeCode_Self:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeSelf);
|
|
break;
|
|
case BfTypeCode_Dot:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeDot);
|
|
break;
|
|
case BfTypeCode_Var:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeVar);
|
|
break;
|
|
case BfTypeCode_Let:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeLet);
|
|
break;
|
|
case BfTypeCode_None:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeVoid);
|
|
break;
|
|
case BfTypeCode_Boolean:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeBool);
|
|
break;
|
|
case BfTypeCode_Int8:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeInt8);
|
|
break;
|
|
case BfTypeCode_UInt8:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeUInt8);
|
|
break;
|
|
case BfTypeCode_Int16:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeInt16);
|
|
break;
|
|
case BfTypeCode_UInt16:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeUInt16);
|
|
break;
|
|
case BfTypeCode_Int32:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeInt32);
|
|
break;
|
|
case BfTypeCode_UInt32:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeUInt32);
|
|
break;
|
|
case BfTypeCode_Int64:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeInt64);
|
|
break;
|
|
case BfTypeCode_UInt64:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeUInt64);
|
|
break;
|
|
case BfTypeCode_Char8:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeChar8);
|
|
break;
|
|
case BfTypeCode_Char16:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeChar16);
|
|
break;
|
|
case BfTypeCode_Char32:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeChar32);
|
|
break;
|
|
case BfTypeCode_Single:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeSingle);
|
|
break;
|
|
case BfTypeCode_Double:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeDouble);
|
|
break;
|
|
case BfTypeCode_IntPtr:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeIntPtr);
|
|
break;
|
|
case BfTypeCode_UIntPtr:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeUIntPtr);
|
|
break;
|
|
case BfTypeCode_IntUnknown:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeIntUnknown);
|
|
break;
|
|
case BfTypeCode_UIntUnknown:
|
|
primType = (BfPrimitiveType*)ResolveTypeDef(mSystem->mTypeUIntUnknown);
|
|
break;
|
|
}
|
|
mContext->mPrimitiveTypes[typeCode] = primType;
|
|
}
|
|
return primType;
|
|
}
|
|
|
|
BfMethodRefType* BfModule::CreateMethodRefType(BfMethodInstance* methodInstance, bool mustAlreadyExist)
|
|
{
|
|
auto methodRefType = new BfMethodRefType();
|
|
methodRefType->mContext = mContext;
|
|
//methodRefType->mCaptureType = NULL;
|
|
methodRefType->mMethodRef = methodInstance;
|
|
methodRefType->mOwner = methodInstance->GetOwner();
|
|
methodRefType->mOwnerRevision = methodRefType->mOwner->mRevision;
|
|
//methodRefType->mMangledName = BfMangler::Mangle(mCompiler->GetMangleKind(), methodInstance);
|
|
methodRefType->mIsAutoCompleteMethod = methodInstance->mIsAutocompleteMethod;
|
|
methodRefType->mIsUnspecialized = methodInstance->mIsUnspecialized;
|
|
methodRefType->mIsUnspecializedVariation = methodInstance->mIsUnspecializedVariation;
|
|
methodRefType->mSize = 0;
|
|
|
|
BfResolvedTypeSet::LookupContext lookupCtx;
|
|
lookupCtx.mModule = this;
|
|
BfResolvedTypeSet::Entry* typeEntry = NULL;
|
|
auto inserted = mContext->mResolvedTypes.Insert(methodRefType, &lookupCtx, &typeEntry);
|
|
if (typeEntry->mValue == NULL)
|
|
{
|
|
BF_ASSERT(!mustAlreadyExist);
|
|
BF_ASSERT(!methodInstance->mHasMethodRefType);
|
|
|
|
InitType(methodRefType, BfPopulateType_Identity);
|
|
methodRefType->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotted;
|
|
|
|
methodInstance->mHasMethodRefType = true;
|
|
methodInstance->mMethodInstanceGroup->mRefCount++;
|
|
typeEntry->mValue = methodRefType;
|
|
BfLogSysM("Create MethodRefType %p MethodInstance: %p\n", methodRefType, methodInstance);
|
|
methodRefType->mRevision = 0;
|
|
AddDependency(methodInstance->GetOwner(), methodRefType, BfDependencyMap::DependencyFlag_Calls);
|
|
|
|
BfTypeVector tupleTypes;
|
|
Array<String> tupleNames;
|
|
|
|
int offset = 0;
|
|
|
|
methodRefType->mAlign = 1;
|
|
|
|
int dataIdx = 0;
|
|
|
|
// CRepr, just because we're lazy (for now)
|
|
int implicitParamCount = methodInstance->GetImplicitParamCount();
|
|
for (int implicitParamIdx = methodInstance->HasThis() ? -1 : 0; implicitParamIdx < implicitParamCount; implicitParamIdx++)
|
|
{
|
|
auto paramType = methodInstance->GetParamType(implicitParamIdx);
|
|
if (!paramType->IsValuelessType())
|
|
{
|
|
methodRefType->mDataToParamIdx.Add(implicitParamIdx);
|
|
if (implicitParamIdx >= 0)
|
|
methodRefType->mParamToDataIdx.Add(dataIdx);
|
|
|
|
offset = BF_ALIGN(offset, paramType->mAlign);
|
|
offset += paramType->mSize;
|
|
methodRefType->mAlign = std::max(methodRefType->mAlign, paramType->mAlign);
|
|
|
|
dataIdx++;
|
|
}
|
|
else
|
|
{
|
|
methodRefType->mParamToDataIdx.Add(-1);
|
|
}
|
|
}
|
|
offset = BF_ALIGN(offset, methodRefType->mAlign);
|
|
methodRefType->mSize = offset;
|
|
|
|
// if (!tupleTypes.empty())
|
|
// {
|
|
// methodRefType->mCaptureType = CreateTupleType(tupleTypes, tupleNames);
|
|
// AddDependency(methodRefType->mCaptureType, methodRefType, BfDependencyMap::DependencyFlag_ReadFields);
|
|
//
|
|
// methodRefType->mSize = methodRefType->mCaptureType->mSize;
|
|
// methodRefType->mAlign = methodRefType->mCaptureType->mAlign;
|
|
// }
|
|
// else
|
|
// {
|
|
// methodRefType->mSize = 0;
|
|
// methodRefType->mAlign = 0;
|
|
// }
|
|
}
|
|
else
|
|
{
|
|
methodRefType->mMethodRef = NULL;
|
|
delete methodRefType;
|
|
methodRefType = (BfMethodRefType*)typeEntry->mValue;
|
|
}
|
|
return methodRefType;
|
|
}
|
|
|
|
BfType* BfModule::FixIntUnknown(BfType* type)
|
|
{
|
|
if ((type != NULL) && (type->IsPrimitiveType()))
|
|
{
|
|
auto primType = (BfPrimitiveType*)type;
|
|
if (primType->mTypeDef->mTypeCode == BfTypeCode_IntUnknown)
|
|
return GetPrimitiveType(BfTypeCode_IntPtr);
|
|
if (primType->mTypeDef->mTypeCode == BfTypeCode_UIntUnknown)
|
|
return GetPrimitiveType(BfTypeCode_UIntPtr);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
void BfModule::FixIntUnknown(BfTypedValue& typedVal)
|
|
{
|
|
if (!typedVal.mValue.IsConst())
|
|
{
|
|
if ((typedVal.mType != NULL) && (typedVal.mType->IsPrimitiveType()))
|
|
{
|
|
auto primType = (BfPrimitiveType*)typedVal.mType;
|
|
BF_ASSERT((primType->mTypeDef->mTypeCode != BfTypeCode_IntUnknown) && (primType->mTypeDef->mTypeCode != BfTypeCode_UIntUnknown));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!typedVal.mType->IsPrimitiveType())
|
|
return;
|
|
|
|
BfTypeCode wantTypeCode;
|
|
auto primType = (BfPrimitiveType*)typedVal.mType;
|
|
if (primType->mTypeDef->mTypeCode == BfTypeCode_IntUnknown)
|
|
wantTypeCode = BfTypeCode_IntPtr;
|
|
else if (primType->mTypeDef->mTypeCode == BfTypeCode_UIntUnknown)
|
|
wantTypeCode = BfTypeCode_UIntPtr;
|
|
else
|
|
return;
|
|
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (mSystem->mPtrSize == 4)
|
|
{
|
|
if (primType->mTypeDef->mTypeCode == BfTypeCode_IntUnknown)
|
|
{
|
|
if ((constant->mInt64 >= -0x80000000LL) && (constant->mInt64 <= 0x7FFFFFFFLL))
|
|
{
|
|
typedVal.mValue = mBfIRBuilder->CreateNumericCast(typedVal.mValue, true, BfTypeCode_IntPtr);
|
|
typedVal.mType = GetPrimitiveType(BfTypeCode_IntPtr);
|
|
}
|
|
else
|
|
typedVal.mType = GetPrimitiveType(BfTypeCode_Int64);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if ((constant->mInt64 >= 0) && (constant->mInt64 <= 0xFFFFFFFF))
|
|
{
|
|
typedVal.mValue = mBfIRBuilder->CreateNumericCast(typedVal.mValue, false, BfTypeCode_IntPtr);
|
|
typedVal.mType = GetPrimitiveType(BfTypeCode_UIntPtr);
|
|
}
|
|
else
|
|
typedVal.mType = GetPrimitiveType(BfTypeCode_UInt64);
|
|
return;
|
|
}
|
|
}
|
|
|
|
typedVal.mType = GetPrimitiveType(wantTypeCode);
|
|
}
|
|
|
|
void BfModule::FixIntUnknown(BfTypedValue& lhs, BfTypedValue& rhs)
|
|
{
|
|
if ((lhs.mType != NULL) && (lhs.mType->IsIntUnknown()) && (rhs.mType != NULL) && (rhs.mType->IsInteger()))
|
|
{
|
|
if (CanImplicitlyCast(lhs, rhs.mType))
|
|
{
|
|
lhs = Cast(NULL, lhs, rhs.mType, BfCastFlags_SilentFail);
|
|
if (!lhs)
|
|
lhs = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_IntPtr));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((rhs.mType != NULL) && (rhs.mType->IsIntUnknown()) && (lhs.mType != NULL) && (lhs.mType->IsInteger()))
|
|
{
|
|
if (CanImplicitlyCast(rhs, lhs.mType))
|
|
{
|
|
rhs = Cast(NULL, rhs, lhs.mType, BfCastFlags_SilentFail);
|
|
if (!rhs)
|
|
rhs = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_IntPtr));
|
|
return;
|
|
}
|
|
}
|
|
|
|
FixIntUnknown(lhs);
|
|
FixIntUnknown(rhs);
|
|
}
|
|
|
|
BfTypeInstance* BfModule::GetPrimitiveStructType(BfTypeCode typeCode)
|
|
{
|
|
BfTypeInstance* typeInst = NULL;
|
|
switch (typeCode)
|
|
{
|
|
case BfTypeCode_None:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Void"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Boolean:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Boolean"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Int8:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Int8"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_UInt8:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.UInt8"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Int16:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Int16"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_UInt16:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.UInt16"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Int32:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Int32"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_UInt32:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.UInt32"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Int64:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Int64"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_UInt64:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.UInt64"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_IntPtr:
|
|
case BfTypeCode_IntUnknown:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Int"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_UIntPtr:
|
|
case BfTypeCode_UIntUnknown:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.UInt"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Char8:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Char8"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Char16:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Char16"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Char32:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Char32"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Single:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Float"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
case BfTypeCode_Double:
|
|
typeInst = ResolveTypeDef(mSystem->FindTypeDef("System.Double"), BfPopulateType_Identity)->ToTypeInstance(); break;
|
|
default:
|
|
//BF_FATAL("not implemented");
|
|
break;
|
|
}
|
|
return typeInst;
|
|
}
|
|
|
|
BfBoxedType* BfModule::CreateBoxedType(BfType* resolvedTypeRef)
|
|
{
|
|
if (resolvedTypeRef->IsPointer())
|
|
resolvedTypeRef = ((BfPointerType*)resolvedTypeRef)->mElementType;
|
|
if (resolvedTypeRef->IsPrimitiveType())
|
|
{
|
|
auto primType = (BfPrimitiveType*)resolvedTypeRef;
|
|
resolvedTypeRef = GetPrimitiveStructType(primType->mTypeDef->mTypeCode);
|
|
if (resolvedTypeRef == NULL)
|
|
{
|
|
BF_FATAL("Unable to find primitive type");
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (resolvedTypeRef->IsPointer())
|
|
{
|
|
BfPointerType* pointerType = (BfPointerType*)resolvedTypeRef;
|
|
BfTypeVector typeVector;
|
|
typeVector.Add(pointerType->mElementType);
|
|
resolvedTypeRef = ResolveTypeDef(mCompiler->mPointerTTypeDef, typeVector, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
else if (resolvedTypeRef->IsMethodRef())
|
|
{
|
|
BfMethodRefType* methodRefType = (BfMethodRefType*)resolvedTypeRef;
|
|
BfTypeVector typeVector;
|
|
typeVector.Add(methodRefType);
|
|
resolvedTypeRef = ResolveTypeDef(mCompiler->mMethodRefTypeDef, typeVector, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
else if (resolvedTypeRef->IsSizedArray())
|
|
{
|
|
BfSizedArrayType* sizedArrayType = (BfSizedArrayType*)resolvedTypeRef;
|
|
BfTypeVector typeVector;
|
|
typeVector.Add(sizedArrayType->mElementType);
|
|
auto sizeValue = BfTypedValue(GetConstValue(sizedArrayType->mElementCount), GetPrimitiveType(BfTypeCode_IntPtr));
|
|
typeVector.Add(CreateConstExprValueType(sizeValue));
|
|
resolvedTypeRef = ResolveTypeDef(mCompiler->mSizedArrayTypeDef, typeVector, BfPopulateType_Data)->ToTypeInstance();
|
|
}
|
|
|
|
BfTypeInstance* typeInst = resolvedTypeRef->ToTypeInstance();
|
|
if (typeInst == NULL)
|
|
return NULL;
|
|
|
|
auto boxedType = mContext->mBoxedTypePool.Get();
|
|
boxedType->mContext = mContext;
|
|
boxedType->mElementType = typeInst;
|
|
boxedType->mTypeDef = boxedType->mElementType->mTypeDef;
|
|
auto resolvedBoxedType = ResolveType(boxedType);
|
|
if (resolvedBoxedType != boxedType)
|
|
mContext->mBoxedTypePool.GiveBack(boxedType);
|
|
return (BfBoxedType*)resolvedBoxedType;
|
|
}
|
|
|
|
BfTupleType* BfModule::CreateTupleType(const BfTypeVector& fieldTypes, const Array<String>& fieldNames)
|
|
{
|
|
auto tupleType = mContext->mTupleTypePool.Get();
|
|
tupleType->mContext = mContext;
|
|
tupleType->mFieldInstances.Resize(fieldTypes.size());
|
|
|
|
auto baseType = (BfTypeInstance*)ResolveTypeDef(mContext->mCompiler->mValueTypeTypeDef);
|
|
tupleType->Init(baseType->mTypeDef->mProject, baseType);
|
|
|
|
for (int fieldIdx = 0; fieldIdx < (int)fieldTypes.size(); fieldIdx++)
|
|
{
|
|
BfFieldInstance* fieldInstance = (BfFieldInstance*)&tupleType->mFieldInstances[fieldIdx];
|
|
fieldInstance->mFieldIdx = fieldIdx;
|
|
fieldInstance->SetResolvedType(fieldTypes[fieldIdx]);
|
|
fieldInstance->mOwner = tupleType;
|
|
|
|
String fieldName;
|
|
if (fieldIdx < (int)fieldNames.size())
|
|
fieldName = fieldNames[fieldIdx];
|
|
if (fieldName.empty())
|
|
fieldName = StrFormat("%d", fieldIdx);
|
|
BfFieldDef* fieldDef = tupleType->AddField(fieldName);
|
|
}
|
|
|
|
auto resolvedTupleType = ResolveType(tupleType);
|
|
if (resolvedTupleType != tupleType)
|
|
mContext->mTupleTypePool.GiveBack(tupleType);
|
|
return (BfTupleType*)resolvedTupleType;
|
|
}
|
|
|
|
BfTupleType * BfModule::SantizeTupleType(BfTupleType* tupleType)
|
|
{
|
|
bool needsSanitize = false;
|
|
for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
BfFieldInstance* fieldInstance = (BfFieldInstance*)&tupleType->mFieldInstances[fieldIdx];
|
|
if ((fieldInstance->mResolvedType->IsVar()) || (fieldInstance->mResolvedType->IsLet()))
|
|
{
|
|
needsSanitize = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!needsSanitize)
|
|
return tupleType;
|
|
|
|
BfTypeVector fieldTypes;
|
|
Array<String> fieldNames;
|
|
|
|
for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
BfFieldInstance* fieldInstance = (BfFieldInstance*)&tupleType->mFieldInstances[fieldIdx];
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
if ((fieldInstance->mResolvedType->IsVar()) || (fieldInstance->mResolvedType->IsLet()))
|
|
fieldTypes.Add(mContext->mBfObjectType);
|
|
else
|
|
fieldTypes.Add(fieldInstance->mResolvedType);
|
|
if (!fieldDef->IsUnnamedTupleField())
|
|
{
|
|
for (int i = 0; i < fieldIdx; i++)
|
|
fieldNames.Add(String());
|
|
fieldNames.Add(fieldDef->mName);
|
|
}
|
|
}
|
|
return CreateTupleType(fieldTypes, fieldNames);
|
|
}
|
|
|
|
BfRefType* BfModule::CreateRefType(BfType* resolvedTypeRef, BfRefType::RefKind refKind)
|
|
{
|
|
auto refType = mContext->mRefTypePool.Get();
|
|
refType->mContext = mContext;
|
|
refType->mElementType = resolvedTypeRef;
|
|
refType->mRefKind = refKind;
|
|
auto resolvedRefType = ResolveType(refType);
|
|
if (resolvedRefType != refType)
|
|
mContext->mRefTypePool.GiveBack(refType);
|
|
return (BfRefType*)resolvedRefType;
|
|
}
|
|
|
|
BfRetTypeType* BfModule::CreateRetTypeType(BfType* resolvedTypeRef)
|
|
{
|
|
auto retTypeType = mContext->mRetTypeTypePool.Get();
|
|
retTypeType->mContext = mContext;
|
|
retTypeType->mElementType = resolvedTypeRef;
|
|
auto resolvedRetTypeType = ResolveType(retTypeType);
|
|
if (resolvedRetTypeType != retTypeType)
|
|
mContext->mRetTypeTypePool.GiveBack(retTypeType);
|
|
return (BfRetTypeType*)resolvedRetTypeType;
|
|
}
|
|
|
|
BfConcreteInterfaceType* BfModule::CreateConcreteInterfaceType(BfTypeInstance* interfaceType)
|
|
{
|
|
auto concreteInterfaceType = mContext->mConcreteInterfaceTypePool.Get();
|
|
concreteInterfaceType->mContext = mContext;
|
|
concreteInterfaceType->mInterface = interfaceType;
|
|
auto resolvedConcreteInterfaceType = ResolveType(concreteInterfaceType);
|
|
if (resolvedConcreteInterfaceType != concreteInterfaceType)
|
|
mContext->mConcreteInterfaceTypePool.GiveBack(concreteInterfaceType);
|
|
return (BfConcreteInterfaceType*)resolvedConcreteInterfaceType;
|
|
}
|
|
|
|
BfPointerType* BfModule::CreatePointerType(BfTypeReference* typeRef)
|
|
{
|
|
auto resolvedTypeRef = ResolveTypeRef(typeRef);
|
|
if (resolvedTypeRef == NULL)
|
|
return NULL;
|
|
return CreatePointerType(resolvedTypeRef);
|
|
}
|
|
|
|
BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, BfPopulateType populateType)
|
|
{
|
|
//BF_ASSERT(typeDef->mTypeCode != BfTypeCode_Extension);
|
|
BF_ASSERT(!typeDef->mIsPartial || typeDef->mIsCombinedPartial);
|
|
|
|
if (typeDef->mGenericParamDefs.size() != 0)
|
|
return ResolveTypeDef(typeDef, BfTypeVector(), populateType);
|
|
|
|
auto typeDefTypeRef = mContext->mTypeDefTypeRefPool.Get();
|
|
typeDefTypeRef->mTypeDef = typeDef;
|
|
auto resolvedtypeDefType = ResolveTypeRef(typeDefTypeRef, populateType);
|
|
if (resolvedtypeDefType == NULL)
|
|
{
|
|
mContext->mTypeDefTypeRefPool.GiveBack(typeDefTypeRef);
|
|
return NULL;
|
|
}
|
|
|
|
mContext->mTypeDefTypeRefPool.GiveBack(typeDefTypeRef);
|
|
//BF_ASSERT(resolvedtypeDefType->IsTypeInstance() || resolvedtypeDefType->IsPrimitiveType());
|
|
return resolvedtypeDefType;
|
|
}
|
|
|
|
// Get BaseClass even when we haven't populated the type yet
|
|
BfTypeInstance* BfModule::GetBaseType(BfTypeInstance* typeInst)
|
|
{
|
|
if ((typeInst->mBaseType == NULL) && (typeInst != mContext->mBfObjectType))
|
|
PopulateType(typeInst, BfPopulateType_BaseType);
|
|
return typeInst->mBaseType;
|
|
}
|
|
|
|
void BfModule::HandleTypeGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, int typeGenericParamIdx)
|
|
{
|
|
if (mCompiler->IsAutocomplete())
|
|
{
|
|
BfAutoComplete* autoComplete = mCompiler->mResolvePassData->mAutoComplete;
|
|
if ((autoComplete != NULL) && (autoComplete->mIsGetDefinition) && (autoComplete->IsAutocompleteNode(refNode)))
|
|
{
|
|
if ((autoComplete->mDefMethod == NULL) && (autoComplete->mDefField == NULL) &&
|
|
(autoComplete->mDefProp == NULL))
|
|
{
|
|
autoComplete->mDefType = typeDef;
|
|
autoComplete->mDefTypeGenericParamIdx = typeGenericParamIdx;
|
|
autoComplete->SetDefinitionLocation(refNode);
|
|
}
|
|
}
|
|
}
|
|
if (mCompiler->mResolvePassData != NULL)
|
|
mCompiler->mResolvePassData->HandleTypeGenericParam(refNode, typeDef, typeGenericParamIdx);
|
|
}
|
|
|
|
void BfModule::HandleMethodGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, BfMethodDef* methodDef, int methodGenericParamIdx)
|
|
{
|
|
if (mCompiler->IsAutocomplete())
|
|
{
|
|
BfAutoComplete* autoComplete = mCompiler->mResolvePassData->mAutoComplete;
|
|
if ((autoComplete != NULL) && (autoComplete->mIsGetDefinition) && (autoComplete->IsAutocompleteNode(refNode)))
|
|
{
|
|
if ((autoComplete->mDefMethod == NULL) && (autoComplete->mDefField == NULL) &&
|
|
(autoComplete->mDefProp == NULL))
|
|
{
|
|
autoComplete->mDefType = typeDef;
|
|
autoComplete->mDefMethod = methodDef;
|
|
autoComplete->mDefMethodGenericParamIdx = methodGenericParamIdx;
|
|
autoComplete->SetDefinitionLocation(refNode);
|
|
}
|
|
}
|
|
}
|
|
if (mCompiler->mResolvePassData != NULL)
|
|
mCompiler->mResolvePassData->HandleMethodGenericParam(refNode, typeDef, methodDef, methodGenericParamIdx);
|
|
}
|
|
|
|
BfType* BfModule::ResolveInnerType(BfType* outerType, BfTypeReference* typeRef, BfPopulateType populateType, bool ignoreErrors)
|
|
{
|
|
BfTypeDef* nestedTypeDef = NULL;
|
|
|
|
if (outerType->IsBoxed())
|
|
outerType = outerType->GetUnderlyingType();
|
|
|
|
BfNamedTypeReference* namedTypeRef = NULL;
|
|
BfGenericInstanceTypeRef* genericTypeRef = NULL;
|
|
BfDirectStrTypeReference* directStrTypeRef = NULL;
|
|
if (namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(typeRef))
|
|
{
|
|
//TYPEDEF nestedTypeDef = namedTypeRef->mTypeDef;
|
|
}
|
|
else if (genericTypeRef = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef))
|
|
{
|
|
namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(genericTypeRef->mElementType);
|
|
//TYPEDEF nestedTypeDef = namedTypeRef->mTypeDef;
|
|
}
|
|
else if (directStrTypeRef = BfNodeDynCast<BfDirectStrTypeReference>(typeRef))
|
|
{
|
|
//
|
|
}
|
|
|
|
BF_ASSERT((namedTypeRef != NULL) || (directStrTypeRef != NULL));
|
|
|
|
if (nestedTypeDef == NULL)
|
|
{
|
|
StringView findName;
|
|
if (namedTypeRef != NULL)
|
|
findName = namedTypeRef->mNameNode->ToStringView();
|
|
else
|
|
findName = directStrTypeRef->mTypeName;
|
|
|
|
if (!findName.Contains('.'))
|
|
{
|
|
if (outerType->IsTypeInstance())
|
|
{
|
|
auto outerTypeInstance = outerType->ToTypeInstance();
|
|
|
|
for (int pass = 0; pass < 2; pass++)
|
|
{
|
|
bool isFailurePass = pass == 1;
|
|
bool allowPrivate = (mCurTypeInstance != NULL) &&
|
|
((mCurTypeInstance == outerTypeInstance) || TypeHasParent(mCurTypeInstance->mTypeDef, outerTypeInstance->mTypeDef));
|
|
bool allowProtected = allowPrivate;/*(mCurTypeInstance != NULL) &&
|
|
(allowPrivate || (mCurTypeInstance->mSkipTypeProtectionChecks) || TypeIsSubTypeOf(mCurTypeInstance, outerTypeInstance));*/
|
|
|
|
auto checkOuterType = outerTypeInstance;
|
|
while (checkOuterType != NULL)
|
|
{
|
|
for (auto checkType : checkOuterType->mTypeDef->mNestedTypes)
|
|
{
|
|
auto latestCheckType = checkType->GetLatest();
|
|
|
|
if ((!isFailurePass) && (!CheckProtection(latestCheckType->mProtection, allowProtected, allowPrivate)))
|
|
continue;
|
|
|
|
if (checkType->mName->mString == findName)
|
|
{
|
|
if (isFailurePass)
|
|
{
|
|
// This is the one error we don't ignore when ignoreErrors is set
|
|
Fail(StrFormat("'%s.%s' is inaccessible due to its protection level", TypeToString(checkOuterType).c_str(), BfTypeUtils::TypeToString(typeRef).c_str()), typeRef); // CS0122
|
|
}
|
|
|
|
nestedTypeDef = checkType;
|
|
break;
|
|
}
|
|
}
|
|
if (nestedTypeDef != NULL)
|
|
break;
|
|
allowPrivate = false;
|
|
checkOuterType = GetBaseType(checkOuterType);
|
|
}
|
|
if (nestedTypeDef != NULL)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nestedTypeDef == NULL)
|
|
{
|
|
if (!mIgnoreErrors && !ignoreErrors)
|
|
{
|
|
StringT<64> name;
|
|
name.Append(findName);
|
|
Fail(StrFormat("'%s' does not contain a definition for '%s'", TypeToString(outerType).c_str(), name.c_str()), typeRef);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, ignoreErrors || mIgnoreErrors);
|
|
if ((genericTypeRef != NULL) || (outerType->IsGenericTypeInstance()))
|
|
{
|
|
BfTypeVector genericArgs;
|
|
if (outerType->IsGenericTypeInstance())
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)outerType;
|
|
genericArgs = genericTypeInst->mTypeGenericArguments;
|
|
}
|
|
|
|
if (genericTypeRef != NULL)
|
|
{
|
|
for (auto genericArgTypeRef : genericTypeRef->mGenericArguments)
|
|
{
|
|
auto genericArgType = ResolveTypeRef(genericArgTypeRef, BfPopulateType_IdentityNoRemapAlias);
|
|
if (genericArgType == NULL)
|
|
return NULL;
|
|
genericArgs.push_back(genericArgType);
|
|
}
|
|
}
|
|
|
|
if (genericArgs.size() != nestedTypeDef->mGenericParamDefs.size())
|
|
{
|
|
if (populateType == BfPopulateType_TypeDef)
|
|
{
|
|
// Probably from inside ResolveGenericInstanceDef, just return unresolved typedef
|
|
genericArgs.clear();
|
|
}
|
|
else
|
|
{
|
|
ShowGenericArgCountError(typeRef, (int)nestedTypeDef->mGenericParamDefs.size() - (int)nestedTypeDef->mOuterType->mGenericParamDefs.size());
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (nestedTypeDef->mIsPartial)
|
|
{
|
|
nestedTypeDef = GetCombinedPartialTypeDef(nestedTypeDef);
|
|
if (nestedTypeDef == NULL)
|
|
return NULL;
|
|
}
|
|
return ResolveTypeDef(nestedTypeDef, genericArgs, BfPopulateType_IdentityNoRemapAlias);
|
|
}
|
|
else
|
|
{
|
|
if (nestedTypeDef->mIsPartial)
|
|
{
|
|
nestedTypeDef = GetCombinedPartialTypeDef(nestedTypeDef);
|
|
if (nestedTypeDef == NULL)
|
|
return NULL;
|
|
}
|
|
return ResolveTypeDef(nestedTypeDef, BfPopulateType_IdentityNoRemapAlias);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BfTypeDef* BfModule::GetCombinedPartialTypeDef(BfTypeDef* typeDef)
|
|
{
|
|
BF_ASSERT(!typeDef->mIsExplicitPartial);
|
|
if (!typeDef->mIsPartial)
|
|
return typeDef;
|
|
auto result = mSystem->FindTypeDef(typeDef->mFullName.ToString(), (int)typeDef->mGenericParamDefs.size());
|
|
return result;
|
|
}
|
|
|
|
BfTypeInstance* BfModule::GetOuterType(BfType* type)
|
|
{
|
|
if (type == NULL)
|
|
return NULL;
|
|
if (type->IsBoxed())
|
|
return GetOuterType(((BfBoxedType*)type)->mElementType);
|
|
auto typeInst = type->ToTypeInstance();
|
|
if ((typeInst == NULL) || (typeInst->mTypeDef->mOuterType == NULL))
|
|
return NULL;
|
|
auto outerTypeDef = typeInst->mTypeDef->mOuterType;
|
|
if (outerTypeDef->mIsPartial)
|
|
{
|
|
outerTypeDef = GetCombinedPartialTypeDef(outerTypeDef);
|
|
if (outerTypeDef == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
BfTypeVector typeGenericArguments;
|
|
if (type->IsGenericTypeInstance())
|
|
{
|
|
auto genericType = (BfGenericTypeInstance*)type;
|
|
typeGenericArguments = genericType->mTypeGenericArguments;
|
|
}
|
|
BF_ASSERT((intptr)typeGenericArguments.size() >= (intptr)outerTypeDef->mGenericParamDefs.size());
|
|
typeGenericArguments.resize(outerTypeDef->mGenericParamDefs.size());
|
|
|
|
auto outerType = ResolveTypeDef(outerTypeDef, typeGenericArguments, BfPopulateType_Declaration);
|
|
if (outerType == NULL)
|
|
return NULL;
|
|
return outerType->ToTypeInstance();
|
|
}
|
|
|
|
bool BfModule::IsInnerType(BfType* checkInnerType, BfType* checkOuterType)
|
|
{
|
|
BfType* outerType = GetOuterType(checkInnerType);
|
|
if (outerType == NULL)
|
|
return false;
|
|
if (outerType == checkOuterType)
|
|
return true;
|
|
return IsInnerType(outerType, checkOuterType);
|
|
}
|
|
|
|
bool BfModule::IsInnerType(BfTypeDef* checkInnerType, BfTypeDef* checkOuterType)
|
|
{
|
|
if (checkInnerType->mNestDepth <= checkOuterType->mNestDepth)
|
|
return false;
|
|
while (true)
|
|
{
|
|
BfTypeDef* outerType = checkInnerType->mOuterType;
|
|
if (outerType == NULL)
|
|
return false;
|
|
if (outerType == checkOuterType)
|
|
return true;
|
|
checkInnerType = checkInnerType->mOuterType;
|
|
}
|
|
}
|
|
|
|
BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, const BfTypeVector& genericArgs, BfPopulateType populateType)
|
|
{
|
|
if (typeDef->mGenericParamDefs.size() == 0)
|
|
return ResolveTypeDef(typeDef, populateType);
|
|
|
|
if ((typeDef == mCompiler->mArray1TypeDef) || (typeDef == mCompiler->mArray2TypeDef))
|
|
{
|
|
auto arrayInstType = mContext->mArrayTypeInstancePool.Get();
|
|
arrayInstType->mContext = mContext;
|
|
if (typeDef == mCompiler->mArray1TypeDef)
|
|
arrayInstType->mDimensions = 1;
|
|
else
|
|
arrayInstType->mDimensions = 2;
|
|
auto typeRef = mContext->mTypeDefTypeRefPool.Get();
|
|
typeRef->mTypeDef = typeDef;
|
|
arrayInstType->mTypeDef = typeDef;
|
|
arrayInstType->mIsUnspecialized = false;
|
|
arrayInstType->mTypeGenericArguments.clear();
|
|
for (auto genericArg : genericArgs)
|
|
{
|
|
arrayInstType->mIsUnspecialized |= genericArg->IsGenericParam();
|
|
arrayInstType->mTypeGenericArguments.push_back(genericArg);
|
|
}
|
|
|
|
if (genericArgs.size() == 0)
|
|
{
|
|
for (int i = 0; i < (int)typeDef->mGenericParamDefs.size(); i++)
|
|
{
|
|
auto genericParamTypeRef = GetGenericParamType(BfGenericParamKind_Type, i);
|
|
arrayInstType->mTypeGenericArguments.push_back(genericParamTypeRef);
|
|
arrayInstType->mIsUnspecialized = true;
|
|
}
|
|
}
|
|
|
|
auto resolvedType = ResolveType(arrayInstType, populateType);
|
|
if (resolvedType != arrayInstType)
|
|
{
|
|
mContext->mArrayTypeInstancePool.GiveBack(arrayInstType);
|
|
mContext->mTypeDefTypeRefPool.GiveBack(typeRef);
|
|
}
|
|
BF_ASSERT((resolvedType == NULL) || resolvedType->IsTypeInstance() || resolvedType->IsPrimitiveType());
|
|
return resolvedType;
|
|
}
|
|
|
|
BfGenericTypeInstance* genericInstType;
|
|
if (typeDef->mTypeCode == BfTypeCode_TypeAlias)
|
|
genericInstType = mContext->mGenericTypeAliasPool.Get();
|
|
else
|
|
genericInstType = mContext->mGenericTypeInstancePool.Get();
|
|
genericInstType->mContext = mContext;
|
|
auto typeRef = mContext->mTypeDefTypeRefPool.Get();
|
|
typeRef->mTypeDef = typeDef;
|
|
genericInstType->mTypeDef = typeDef;
|
|
genericInstType->mIsUnspecialized = false;
|
|
genericInstType->mTypeGenericArguments.clear();
|
|
for (auto genericArg : genericArgs)
|
|
{
|
|
genericInstType->mIsUnspecialized |= genericArg->IsGenericParam();
|
|
genericInstType->mTypeGenericArguments.push_back(genericArg);
|
|
}
|
|
|
|
if (genericArgs.size() == 0)
|
|
{
|
|
for (int i = 0; i < (int)typeDef->mGenericParamDefs.size(); i++)
|
|
{
|
|
auto genericParamTypeRef = GetGenericParamType(BfGenericParamKind_Type, i);
|
|
genericInstType->mTypeGenericArguments.push_back(genericParamTypeRef);
|
|
genericInstType->mIsUnspecialized = true;
|
|
}
|
|
}
|
|
|
|
auto resolvedType = ResolveType(genericInstType, populateType);
|
|
if (resolvedType != genericInstType)
|
|
{
|
|
if (typeDef->mTypeCode == BfTypeCode_TypeAlias)
|
|
mContext->mGenericTypeAliasPool.GiveBack((BfGenericTypeAliasType*)genericInstType);
|
|
else
|
|
mContext->mGenericTypeInstancePool.GiveBack(genericInstType);
|
|
mContext->mTypeDefTypeRefPool.GiveBack(typeRef);
|
|
}
|
|
BF_ASSERT((resolvedType == NULL) || resolvedType->IsTypeInstance() || resolvedType->IsPrimitiveType());
|
|
return resolvedType;
|
|
}
|
|
|
|
int checkIdx = 0;
|
|
|
|
BfTypeDef* BfModule::ResolveGenericInstanceDef(BfGenericInstanceTypeRef* genericTypeRef)
|
|
{
|
|
BfTypeReference* typeRef = genericTypeRef->mElementType;
|
|
int numGenericParams = genericTypeRef->GetGenericArgCount();
|
|
|
|
BfTypeDef* curTypeDef = NULL;
|
|
if (mCurTypeInstance != NULL)
|
|
curTypeDef = mCurTypeInstance->mTypeDef;
|
|
|
|
if (auto directTypeDef = BfNodeDynCast<BfDirectTypeReference>(typeRef))
|
|
{
|
|
auto typeInst = directTypeDef->mType->ToTypeInstance();
|
|
return typeInst->mTypeDef;
|
|
}
|
|
|
|
auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(typeRef);
|
|
auto directStrTypeDef = BfNodeDynCastExact<BfDirectStrTypeReference>(typeRef);
|
|
if ((namedTypeRef != NULL) || (directStrTypeDef != NULL))
|
|
{
|
|
|
|
BfTypeLookupError error;
|
|
error.mRefNode = typeRef;
|
|
BfTypeDef* typeDef = FindTypeDef(typeRef, NULL, &error, numGenericParams);
|
|
if (typeDef != NULL)
|
|
{
|
|
BfAutoComplete* autoComplete = NULL;
|
|
if (mCompiler->IsAutocomplete())
|
|
autoComplete = mCompiler->mResolvePassData->mAutoComplete;
|
|
|
|
if ((autoComplete != NULL) && (autoComplete->mIsGetDefinition) && (autoComplete->IsAutocompleteNode(typeRef)))
|
|
{
|
|
if ((autoComplete->mDefMethod == NULL) && (autoComplete->mDefField == NULL) &&
|
|
(autoComplete->mDefProp == NULL) && (typeDef->mTypeDeclaration != NULL))
|
|
{
|
|
autoComplete->mDefType = typeDef;
|
|
autoComplete->SetDefinitionLocation(typeDef->mTypeDeclaration->mNameNode);
|
|
}
|
|
}
|
|
|
|
if (mCompiler->mResolvePassData != NULL)
|
|
mCompiler->mResolvePassData->HandleTypeReference(typeRef, typeDef);
|
|
|
|
return typeDef;
|
|
}
|
|
|
|
if (mCurTypeInstance != NULL)
|
|
{
|
|
bool wasGenericParam = false;
|
|
|
|
// Check generics first
|
|
if (typeRef->IsA<BfNamedTypeReference>())
|
|
{
|
|
String findName = typeRef->ToString();
|
|
if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsGenericTypeInstance()))
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)mCurTypeInstance;
|
|
for (int genericParamIdx = 0; genericParamIdx < (int)curTypeDef->mGenericParamDefs.size(); genericParamIdx++)
|
|
{
|
|
String genericName = curTypeDef->mGenericParamDefs[genericParamIdx]->mName;
|
|
if (genericName == findName)
|
|
wasGenericParam = true;
|
|
}
|
|
}
|
|
|
|
if (mCurMethodInstance != NULL)
|
|
{
|
|
for (int genericParamIdx = 0; genericParamIdx < (int)mCurMethodInstance->mMethodDef->mGenericParams.size(); genericParamIdx++)
|
|
{
|
|
String genericName = mCurMethodInstance->mMethodDef->mGenericParams[genericParamIdx]->mName;
|
|
if (genericName == findName)
|
|
wasGenericParam = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wasGenericParam)
|
|
Fail("Cannot use generic param as generic instance type", typeRef);
|
|
}
|
|
|
|
//if (mCurTypeInstance != NULL)
|
|
//{
|
|
// String findName;
|
|
// if (directStrTypeDef != NULL)
|
|
// findName = directStrTypeDef->mTypeName;
|
|
// else
|
|
// findName = namedTypeRef->mNameNode->ToString();
|
|
// auto outerTypeInstance = mCurTypeInstance;
|
|
|
|
// for (int pass = 0; pass < 2; pass++)
|
|
// {
|
|
// bool isFailurePass = pass == 1;
|
|
// bool allowPrivate = true;
|
|
// bool allowProtected = true;
|
|
|
|
// auto checkOuterType = outerTypeInstance;
|
|
// while (checkOuterType != NULL)
|
|
// {
|
|
// for (auto checkType : checkOuterType->mTypeDef->mNestedTypes)
|
|
// {
|
|
// if ((!isFailurePass) && (!CheckProtection(checkType->mProtection, allowProtected, allowPrivate)))
|
|
// continue;
|
|
|
|
// if (checkType->mName->mString == findName)
|
|
// {
|
|
// if (isFailurePass)
|
|
// {
|
|
// // This is the one error we don't ignore when ignoreErrors is set
|
|
// Fail(StrFormat("'%s.%s' is inaccessible due to its protection level", TypeToString(checkOuterType).c_str(), BfTypeUtils::TypeToString(namedTypeRef).c_str()), namedTypeRef); // CS0122
|
|
// }
|
|
|
|
// return checkType;
|
|
// }
|
|
// }
|
|
// allowPrivate = false;
|
|
// if (checkOuterType == mContext->mBfObjectType)
|
|
// break;
|
|
// checkOuterType = GetBaseType(checkOuterType);
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
if (typeDef == NULL)
|
|
{
|
|
TypeRefNotFound(typeRef);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeRef))
|
|
{
|
|
BfAutoParentNodeEntry autoParentNodeEntry(this, genericTypeRef);
|
|
auto type = ResolveTypeRef(qualifiedTypeRef, BfPopulateType_TypeDef);
|
|
if (type == NULL)
|
|
return NULL;
|
|
auto typeInst = type->ToTypeInstance();
|
|
if (typeInst != NULL)
|
|
return typeInst->mTypeDef;
|
|
}
|
|
|
|
Fail("Invalid generic type", typeRef);
|
|
return NULL;
|
|
}
|
|
|
|
BfType* BfModule::ResolveGenericType(BfType* unspecializedType, const BfTypeVector& methodGenericArguments, bool allowFail)
|
|
{
|
|
if (unspecializedType->IsGenericParam())
|
|
{
|
|
auto genericParam = (BfGenericParamType*)unspecializedType;
|
|
if (genericParam->mGenericParamKind == BfGenericParamKind_Method)
|
|
{
|
|
if (genericParam->mGenericParamIdx < (int)methodGenericArguments.size())
|
|
{
|
|
return methodGenericArguments[genericParam->mGenericParamIdx];
|
|
}
|
|
BF_ASSERT(allowFail);
|
|
}
|
|
return unspecializedType;
|
|
}
|
|
|
|
if (unspecializedType->IsUnknownSizedArray())
|
|
{
|
|
auto* arrayType = (BfUnknownSizedArrayType*)unspecializedType;
|
|
auto elementType = ResolveGenericType(arrayType->mElementType, methodGenericArguments, allowFail);
|
|
if (elementType == NULL)
|
|
return NULL;
|
|
auto sizeType = ResolveGenericType(arrayType->mElementCountSource, methodGenericArguments, allowFail);
|
|
if (sizeType == NULL)
|
|
return NULL;
|
|
if (sizeType->IsConstExprValue())
|
|
{
|
|
return CreateSizedArrayType(elementType, ((BfConstExprValueType*)sizeType)->mValue.mInt32);
|
|
}
|
|
return CreateUnknownSizedArrayType(elementType, sizeType);
|
|
}
|
|
|
|
if (unspecializedType->IsSizedArray())
|
|
{
|
|
auto* arrayType = (BfSizedArrayType*)unspecializedType;
|
|
auto elementType = ResolveGenericType(arrayType->mElementType, methodGenericArguments, allowFail);
|
|
if (elementType == NULL)
|
|
return NULL;
|
|
return CreateSizedArrayType(elementType, (int)arrayType->mElementCount);
|
|
}
|
|
|
|
if (unspecializedType->IsRef())
|
|
{
|
|
auto refType = (BfRefType*)unspecializedType;
|
|
auto elementType = ResolveGenericType(refType->GetUnderlyingType(), methodGenericArguments, allowFail);
|
|
if (elementType == NULL)
|
|
return NULL;
|
|
return CreateRefType(elementType, refType->mRefKind);
|
|
}
|
|
|
|
if (unspecializedType->IsArray())
|
|
{
|
|
auto arrayType = (BfArrayType*)unspecializedType;
|
|
auto elementType = ResolveGenericType(arrayType->GetUnderlyingType(), methodGenericArguments, allowFail);
|
|
if (elementType == NULL)
|
|
return NULL;
|
|
return CreateArrayType(elementType, arrayType->mDimensions);
|
|
}
|
|
|
|
if (unspecializedType->IsGenericTypeInstance())
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)unspecializedType;
|
|
|
|
BfTypeVector genericArgs;
|
|
for (auto genericArg : genericTypeInst->mTypeGenericArguments)
|
|
{
|
|
if (genericArg->IsUnspecializedType())
|
|
{
|
|
auto resolvedArg = ResolveGenericType(genericArg, methodGenericArguments, allowFail);
|
|
if (resolvedArg == NULL)
|
|
return NULL;
|
|
genericArgs.push_back(resolvedArg);
|
|
}
|
|
else
|
|
genericArgs.push_back(genericArg);
|
|
}
|
|
|
|
return ResolveTypeDef(genericTypeInst->mTypeDef, genericArgs);
|
|
}
|
|
|
|
if (unspecializedType->IsTuple())
|
|
{
|
|
auto tupleType = (BfTupleType*)unspecializedType;
|
|
Array<String> names;
|
|
|
|
BfTypeVector genericArgs;
|
|
bool hadChange = false;
|
|
|
|
for (auto& fieldInstance : tupleType->mFieldInstances)
|
|
{
|
|
names.push_back(fieldInstance.GetFieldDef()->mName);
|
|
auto origGenericArg = fieldInstance.mResolvedType;
|
|
auto newGenericArg = ResolveGenericType(origGenericArg, methodGenericArguments, allowFail);
|
|
if (newGenericArg == NULL)
|
|
return NULL;
|
|
if (newGenericArg != origGenericArg)
|
|
hadChange = true;
|
|
genericArgs.push_back(newGenericArg);
|
|
}
|
|
if (!hadChange)
|
|
return unspecializedType;
|
|
|
|
return CreateTupleType(genericArgs, names);
|
|
}
|
|
|
|
return unspecializedType;
|
|
}
|
|
|
|
BfType* BfModule::ResolveType(BfType* lookupType, BfPopulateType populateType)
|
|
{
|
|
BfResolvedTypeSet::LookupContext lookupCtx;
|
|
lookupCtx.mModule = this;
|
|
BfResolvedTypeSet::Entry* resolvedEntry = NULL;
|
|
bool inserted = mContext->mResolvedTypes.Insert(lookupType, &lookupCtx, &resolvedEntry);
|
|
if (!inserted)
|
|
{
|
|
auto resolvedTypeRef = resolvedEntry->mValue;
|
|
PopulateType(resolvedTypeRef, populateType);
|
|
return resolvedTypeRef;
|
|
}
|
|
|
|
if (lookupType->IsGenericTypeInstance())
|
|
CheckUnspecializedGenericType((BfGenericTypeInstance*)lookupType, populateType);
|
|
|
|
if (lookupType->IsTuple())
|
|
{
|
|
auto tupleType = (BfTupleType*)lookupType;
|
|
tupleType->Finish();
|
|
}
|
|
|
|
resolvedEntry->mValue = lookupType;
|
|
if (!InitType(lookupType, populateType))
|
|
return NULL;
|
|
return lookupType;
|
|
}
|
|
|
|
|
|
bool BfModule::IsUnboundGeneric(BfType* type)
|
|
{
|
|
if (type->IsVar())
|
|
return true;
|
|
if (!type->IsGenericParam())
|
|
return false;
|
|
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)type);
|
|
return (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0;
|
|
}
|
|
|
|
BfGenericParamInstance* BfModule::GetGenericParamInstance(BfGenericParamType* type)
|
|
{
|
|
if (type->mGenericParamKind == BfGenericParamKind_Method)
|
|
return mCurMethodInstance->mMethodInfoEx->mGenericParams[type->mGenericParamIdx];
|
|
|
|
// When we're evaluating a method, make sure the params refer back to that method context
|
|
auto curTypeInstance = mCurTypeInstance;
|
|
if (mCurMethodInstance != NULL)
|
|
curTypeInstance = mCurMethodInstance->mMethodInstanceGroup->mOwner;
|
|
|
|
BfGenericTypeInstance* genericTypeInst = curTypeInstance->ToGenericTypeInstance();
|
|
if ((genericTypeInst->IsIncomplete()) && (genericTypeInst->mGenericParams.size() == 0))
|
|
{
|
|
// Set this to NULL so we don't recurse infinitely
|
|
SetAndRestoreValue<BfTypeInstance*> prevTypeInst(mCurTypeInstance, NULL);
|
|
PopulateType(genericTypeInst, BfPopulateType_Declaration);
|
|
}
|
|
|
|
if (genericTypeInst->mGenericExtensionInfo != NULL)
|
|
{
|
|
auto activeTypeDef = GetActiveTypeDef(NULL, true);
|
|
if ((activeTypeDef->mTypeDeclaration != genericTypeInst->mTypeDef->mTypeDeclaration) && (activeTypeDef->IsExtension()))
|
|
{
|
|
BfGenericExtensionEntry* genericExEntry;
|
|
if (genericTypeInst->mGenericExtensionInfo->mExtensionMap.TryGetValue(activeTypeDef, &genericExEntry))
|
|
{
|
|
return genericExEntry->mGenericParams[type->mGenericParamIdx];
|
|
}
|
|
else
|
|
{
|
|
if ((mCompiler->mResolvePassData == NULL) || (mCompiler->mResolvePassData->mAutoComplete == NULL))
|
|
{
|
|
BF_FATAL("Invalid GetGenericParamInstance with extention");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BF_ASSERT(genericTypeInst != NULL);
|
|
return genericTypeInst->mGenericParams[type->mGenericParamIdx];
|
|
}
|
|
|
|
BfType* BfModule::ResolveTypeResult(BfTypeReference* typeRef, BfType* resolvedTypeRef, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags)
|
|
{
|
|
if (mCompiler->mIsResolveOnly)
|
|
{
|
|
BfSourceData* typeRefSource = NULL;
|
|
if (typeRef->IsTemporary())
|
|
{
|
|
BfTypeReference* checkTypeRef = typeRef;
|
|
if (auto genericTypeRef = BfNodeDynCast<BfGenericInstanceTypeRef>(checkTypeRef))
|
|
checkTypeRef = genericTypeRef->mElementType;
|
|
if (auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(checkTypeRef))
|
|
typeRefSource = namedTypeRef->mNameNode->GetSourceData();
|
|
}
|
|
else
|
|
typeRefSource = typeRef->GetSourceData();
|
|
if ((mCompiler->mResolvePassData->mSourceClassifier != NULL) && (typeRefSource != NULL) && (mCompiler->mResolvePassData->mParser != NULL) &&
|
|
(typeRefSource == mCompiler->mResolvePassData->mParser->mSourceData))
|
|
{
|
|
//TODO: By only breaking out for "mIgnoreErrors", we classified elements (below) even when a resolvedTypeRef was not found!
|
|
//Why did we have this mIgnoreErrors check in there?
|
|
// if ((resolvedTypeRef == NULL) && (mIgnoreErrors))
|
|
// {
|
|
// return NULL;
|
|
// }
|
|
if ((resolvedTypeRef == NULL) /*&& (mIgnoreErrors)*/)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
BfTypeInstance* resolvedTypeInstance = NULL;
|
|
if (resolvedTypeRef != NULL)
|
|
resolvedTypeInstance = resolvedTypeRef->ToTypeInstance();
|
|
|
|
bool isNamespace = false;
|
|
auto checkTypeRef = typeRef;
|
|
if (auto elementedTypeRef = BfNodeDynCast<BfElementedTypeRef>(checkTypeRef))
|
|
checkTypeRef = elementedTypeRef->mElementType;
|
|
|
|
if (!mIsInsideAutoComplete)
|
|
{
|
|
if ((resolvedTypeInstance != NULL) && (resolvedTypeInstance->mTypeDef->IsGlobalsContainer()))
|
|
{
|
|
isNamespace = true;
|
|
}
|
|
else
|
|
{
|
|
//TODO: This broke colorizing of inner expressions for things like "T2[T3]"
|
|
//mCompiler->mResolvePassData->mSourceClassifier->VisitChildNoRef(typeRef);
|
|
}
|
|
}
|
|
|
|
while (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(checkTypeRef))
|
|
{
|
|
StringView leftString = qualifiedTypeRef->mLeft->ToStringView();
|
|
BfSizedAtomComposite leftComposite;
|
|
bool isValid = mSystem->ParseAtomComposite(leftString, leftComposite);
|
|
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(qualifiedTypeRef->mRight, isNamespace ? BfSourceElementType_Namespace : BfSourceElementType_TypeRef);
|
|
if (resolvedTypeInstance == NULL)
|
|
{
|
|
if ((isValid) && (mCompiler->mSystem->ContainsNamespace(leftComposite, mCurTypeInstance->mTypeDef->mProject)))
|
|
isNamespace = true;
|
|
}
|
|
else if ((isValid) && (resolvedTypeInstance->mTypeDef->mNamespace.EndsWith(leftComposite)))
|
|
isNamespace = true;
|
|
checkTypeRef = qualifiedTypeRef->mLeft;
|
|
}
|
|
|
|
if (auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(checkTypeRef))
|
|
{
|
|
auto checkNameNode = namedTypeRef->mNameNode;
|
|
|
|
while (auto qualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(checkNameNode))
|
|
{
|
|
StringView leftString = qualifiedNameNode->mLeft->ToStringView();
|
|
BfSizedAtomComposite leftComposite;
|
|
bool isValid = mSystem->ParseAtomComposite(leftString, leftComposite);
|
|
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(qualifiedNameNode->mRight, isNamespace ? BfSourceElementType_Namespace : BfSourceElementType_TypeRef);
|
|
if (resolvedTypeInstance == NULL)
|
|
{
|
|
if ((isValid) && (mCompiler->mSystem->ContainsNamespace(leftComposite, mCurTypeInstance->mTypeDef->mProject)))
|
|
isNamespace = true;
|
|
}
|
|
else if ((isValid) && (resolvedTypeInstance->mTypeDef->mNamespace.EndsWith(leftComposite)))
|
|
isNamespace = true;
|
|
checkNameNode = qualifiedNameNode->mLeft;
|
|
}
|
|
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(checkNameNode, isNamespace ? BfSourceElementType_Namespace : BfSourceElementType_TypeRef);
|
|
}
|
|
}
|
|
|
|
bool isGetDefinition = false;
|
|
BfAutoComplete* autoComplete = NULL;
|
|
if (mCompiler->IsAutocomplete())
|
|
autoComplete = mCompiler->mResolvePassData->mAutoComplete;
|
|
if (autoComplete != NULL)
|
|
{
|
|
isGetDefinition = autoComplete->mIsGetDefinition;
|
|
}
|
|
|
|
if (((mCompiler->mResolvePassData->mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Type) || (isGetDefinition)) &&
|
|
((resolveFlags & BfResolveTypeRefFlag_FromIndirectSource) == 0) && (resolvedTypeRef != NULL) && (typeRefSource != NULL))
|
|
{
|
|
BfAstNode* elementTypeRef = typeRef;
|
|
if (auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(elementTypeRef))
|
|
elementTypeRef = namedTypeRef->mNameNode;
|
|
|
|
if (elementTypeRef != NULL)
|
|
{
|
|
BfType* elementType = resolvedTypeRef;
|
|
if (BfTypeInstance* elementTypeInst = elementType->ToTypeInstance())
|
|
{
|
|
mCompiler->mResolvePassData->HandleTypeReference(elementTypeRef, elementTypeInst->mTypeDef);
|
|
if (mCompiler->IsAutocomplete())
|
|
{
|
|
BfAutoComplete* autoComplete = mCompiler->mResolvePassData->mAutoComplete;
|
|
if ((autoComplete->mIsGetDefinition) && (autoComplete->IsAutocompleteNode(elementTypeRef)))
|
|
{
|
|
BfAstNode* baseNode = elementTypeRef;
|
|
while (true)
|
|
{
|
|
if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(baseNode))
|
|
{
|
|
baseNode = qualifiedTypeRef->mRight;
|
|
}
|
|
else if (auto elementedTypeRef = BfNodeDynCast<BfElementedTypeRef>(baseNode))
|
|
{
|
|
baseNode = elementedTypeRef->mElementType;
|
|
}
|
|
else if (auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(baseNode))
|
|
{
|
|
baseNode = namedTypeRef->mNameNode;
|
|
}
|
|
else if (auto qualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(baseNode))
|
|
{
|
|
baseNode = qualifiedNameNode->mRight;
|
|
}
|
|
else if (auto declTypeRef = BfNodeDynCast<BfDeclTypeRef>(baseNode))
|
|
{
|
|
baseNode = NULL;
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
if ((baseNode != NULL) && (autoComplete->IsAutocompleteNode(baseNode)))
|
|
{
|
|
// We didn't have this mDefType check before - why? We always want to catch the FIRST definition,
|
|
// so 'Type?' will catch on 'Type' and not 'Type?'
|
|
if ((autoComplete->mDefType == NULL) &&
|
|
(autoComplete->mDefMethod == NULL) && (autoComplete->mDefField == NULL) &&
|
|
(autoComplete->mDefProp == NULL) && (elementTypeInst->mTypeDef->mTypeDeclaration != NULL))
|
|
{
|
|
autoComplete->mDefType = elementTypeInst->mTypeDef;
|
|
autoComplete->SetDefinitionLocation(elementTypeInst->mTypeDef->mTypeDeclaration->mNameNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resolvedTypeRef == NULL)
|
|
return NULL;
|
|
|
|
if (resolvedTypeRef->IsTuple())
|
|
{
|
|
// Add the fields from the tuple as references since those inner fields types would have been explicitly stated, so we need
|
|
// to make sure to record the current type instance as a referring type. This mostly matters for symbol renaming.
|
|
BfTupleType* payloadTupleType = (BfTupleType*)resolvedTypeRef;
|
|
for (auto& payloadFieldInst : payloadTupleType->mFieldInstances)
|
|
{
|
|
auto payloadFieldType = payloadFieldInst.mResolvedType;
|
|
AddDependency(payloadFieldType, mCurTypeInstance, BfDependencyMap::DependencyFlag_TypeReference);
|
|
}
|
|
}
|
|
else if (resolvedTypeRef->IsDelegateFromTypeRef() || resolvedTypeRef->IsFunctionFromTypeRef())
|
|
{
|
|
auto delegateType = (BfDelegateType*)resolvedTypeRef;
|
|
auto invokeMethod = GetDelegateInvokeMethod(delegateType);
|
|
|
|
AddDependency(invokeMethod->mReturnType, mCurTypeInstance, BfDependencyMap::DependencyFlag_TypeReference);
|
|
for (auto& param : invokeMethod->mParams)
|
|
{
|
|
AddDependency(param.mResolvedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_TypeReference);
|
|
}
|
|
}
|
|
|
|
BfGenericTypeInstance* genericTypeInstance = NULL;
|
|
if (resolvedTypeRef != NULL)
|
|
genericTypeInstance = resolvedTypeRef->ToGenericTypeInstance();
|
|
|
|
bool hadError = false;
|
|
hadError = !PopulateType(resolvedTypeRef, populateType);
|
|
|
|
if ((genericTypeInstance != NULL) && (populateType > BfPopulateType_Identity))
|
|
{
|
|
if (((genericTypeInstance->mHadValidateErrors) || (!genericTypeInstance->mValidatedGenericConstraints) || (genericTypeInstance->mIsUnspecializedVariation)) &&
|
|
((mCurMethodInstance == NULL) || (!mCurMethodInstance->mIsUnspecializedVariation)) &&
|
|
((mCurTypeInstance == NULL) || (!mCurTypeInstance->IsUnspecializedTypeVariation())))
|
|
ValidateGenericConstraints(typeRef, genericTypeInstance, false);
|
|
}
|
|
|
|
if (populateType != BfPopulateType_IdentityNoRemapAlias)
|
|
{
|
|
while ((resolvedTypeRef != NULL) && (resolvedTypeRef->IsTypeAlias()))
|
|
{
|
|
if (mCurTypeInstance != NULL)
|
|
AddDependency(resolvedTypeRef, mCurTypeInstance, BfDependencyMap::DependencyFlag_NameReference);
|
|
resolvedTypeRef = resolvedTypeRef->GetUnderlyingType();
|
|
}
|
|
}
|
|
|
|
return resolvedTypeRef;
|
|
}
|
|
|
|
void BfModule::ShowAmbiguousTypeError(BfAstNode* refNode, BfTypeDef* typeDef, BfTypeDef* otherTypeDef)
|
|
{
|
|
BfType* type = ResolveTypeDef(typeDef, BfPopulateType_Identity);
|
|
if (type == NULL)
|
|
return;
|
|
BfType* otherType = ResolveTypeDef(otherTypeDef, BfPopulateType_Identity);
|
|
if (otherType == NULL)
|
|
return;
|
|
|
|
auto error = Fail(StrFormat("'%s' is an ambiguous reference between '%s' and '%s'",
|
|
refNode->ToString().c_str(), TypeToString(type, BfTypeNameFlags_None).c_str(), TypeToString(otherType, BfTypeNameFlags_None).c_str()), refNode); // CS0104
|
|
if (error != NULL)
|
|
{
|
|
mCompiler->mPassInstance->MoreInfo("See first definition", typeDef->mTypeDeclaration->mNameNode);
|
|
mCompiler->mPassInstance->MoreInfo("See second definition", otherTypeDef->mTypeDeclaration->mNameNode);
|
|
}
|
|
}
|
|
|
|
void BfModule::ShowGenericArgCountError(BfTypeReference* typeRef, int wantedGenericParams)
|
|
{
|
|
BfGenericInstanceTypeRef* genericTypeInstRef = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef);
|
|
|
|
BfAstNode* lastNode = typeRef;
|
|
int genericArgDiffCount;
|
|
if (genericTypeInstRef != NULL)
|
|
{
|
|
genericArgDiffCount = (int)genericTypeInstRef->mGenericArguments.size() - wantedGenericParams;
|
|
lastNode = genericTypeInstRef->mOpenChevron;
|
|
if (genericTypeInstRef->mCloseChevron != NULL)
|
|
lastNode = genericTypeInstRef->mCloseChevron;
|
|
if (genericTypeInstRef->mGenericArguments.size() > wantedGenericParams)
|
|
{
|
|
lastNode = genericTypeInstRef->mGenericArguments[wantedGenericParams];
|
|
if (genericArgDiffCount == 1)
|
|
Fail("Too many generic parameters, expected one fewer", lastNode);
|
|
else
|
|
Fail(StrFormat("Too many generic parameters, expected %d fewer", genericArgDiffCount), lastNode);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
genericArgDiffCount = -wantedGenericParams;
|
|
|
|
if (wantedGenericParams == 1)
|
|
Fail("Too few generic parameters, expected one more", lastNode);
|
|
else
|
|
Fail(StrFormat("Too few generic parameters, expected %d more", -genericArgDiffCount), lastNode);
|
|
}
|
|
|
|
BfTypeDef* BfModule::GetActiveTypeDef(BfTypeInstance* typeInstanceOverride, bool useMixinDecl)
|
|
{
|
|
BfTypeDef* useTypeDef = NULL;
|
|
BfTypeInstance* typeInstance = (typeInstanceOverride != NULL) ? typeInstanceOverride : mCurTypeInstance;
|
|
if (typeInstance != NULL)
|
|
useTypeDef = typeInstance->mTypeDef;
|
|
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL) && (useMixinDecl))
|
|
useTypeDef = mCurMethodState->mMixinState->mMixinMethodInstance->mMethodDef->mDeclaringType;
|
|
else if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodDef->mDeclaringType != NULL))
|
|
useTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType;
|
|
else if (mContext->mCurTypeState != NULL)
|
|
{
|
|
if ((mContext->mCurTypeState->mCurFieldDef != NULL) && (mContext->mCurTypeState->mCurFieldDef->mDeclaringType != NULL))
|
|
useTypeDef = mContext->mCurTypeState->mCurFieldDef->mDeclaringType;
|
|
else if (mContext->mCurTypeState->mCurTypeDef != NULL)
|
|
useTypeDef = mContext->mCurTypeState->mCurTypeDef;
|
|
}
|
|
return useTypeDef;
|
|
}
|
|
|
|
BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstance, BfTypeDef* useTypeDef, BfTypeLookupError* error)
|
|
{
|
|
if ((findName.mSize == 1) && (findName.mParts[0]->mIsSystemType))
|
|
{
|
|
//BP_ZONE("BfModule::FindTypeDefRaw_1");
|
|
return mSystem->FindTypeDef(findName, 0, useTypeDef->mProject);
|
|
}
|
|
|
|
BfTypeInstance* skipCheckBaseType = NULL;
|
|
if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mCurBaseTypeRef != NULL))
|
|
skipCheckBaseType = mContext->mCurTypeState->mTypeInstance;
|
|
|
|
BfTypeDefLookupContext lookupCtx;
|
|
bool allowPrivate = true;
|
|
|
|
int curPri = 1000;
|
|
auto checkTypeInst = typeInstance;
|
|
|
|
BfTypeDef* protErrorTypeDef = NULL;
|
|
BfTypeInstance* protErrorOuterType = NULL;
|
|
|
|
if (!lookupCtx.HasValidMatch())
|
|
{
|
|
std::function<bool(BfTypeInstance*)> _CheckType = [&](BfTypeInstance* typeInstance)
|
|
{
|
|
auto checkTypeInst = typeInstance;
|
|
allowPrivate = true;
|
|
while (checkTypeInst != NULL)
|
|
{
|
|
if (!checkTypeInst->mTypeDef->mNestedTypes.IsEmpty())
|
|
{
|
|
if (mSystem->FindTypeDef(findName, numGenericArgs, useTypeDef->mProject, checkTypeInst->mTypeDef->mFullNameEx, allowPrivate, &lookupCtx))
|
|
{
|
|
if (lookupCtx.HasValidMatch())
|
|
return true;
|
|
|
|
if ((lookupCtx.mBestTypeDef->mProtection == BfProtection_Private) && (!allowPrivate))
|
|
{
|
|
protErrorTypeDef = lookupCtx.mBestTypeDef;
|
|
protErrorOuterType = checkTypeInst;
|
|
}
|
|
}
|
|
}
|
|
if (checkTypeInst == skipCheckBaseType)
|
|
break;
|
|
checkTypeInst = GetBaseType(checkTypeInst);
|
|
allowPrivate = false;
|
|
}
|
|
|
|
checkTypeInst = typeInstance;
|
|
allowPrivate = true;
|
|
while (checkTypeInst != NULL)
|
|
{
|
|
auto outerTypeInst = GetOuterType(checkTypeInst);
|
|
if (outerTypeInst != NULL)
|
|
{
|
|
if (_CheckType(outerTypeInst))
|
|
return true;
|
|
}
|
|
if (checkTypeInst == skipCheckBaseType)
|
|
break;
|
|
checkTypeInst = GetBaseType(checkTypeInst);
|
|
allowPrivate = false;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
_CheckType(typeInstance);
|
|
}
|
|
|
|
if (!lookupCtx.HasValidMatch())
|
|
{
|
|
if (mSystem->mTypeDefs.TryGet(findName, NULL))
|
|
mSystem->FindTypeDef(findName, numGenericArgs, useTypeDef->mProject, BfAtomComposite(), allowPrivate, &lookupCtx);
|
|
|
|
for (auto& checkNamespace : useTypeDef->mNamespaceSearch)
|
|
{
|
|
BfAtom* atom = findName.mParts[0];
|
|
BfAtom* prevAtom = checkNamespace.mParts[checkNamespace.mSize - 1];
|
|
if (atom->mPrevNamesMap.ContainsKey(prevAtom))
|
|
mSystem->FindTypeDef(findName, numGenericArgs, useTypeDef->mProject, checkNamespace, allowPrivate, &lookupCtx);
|
|
}
|
|
}
|
|
|
|
if ((error != NULL) && (lookupCtx.mAmbiguousTypeDef != NULL))
|
|
{
|
|
if (error->mErrorKind == BfTypeLookupError::BfErrorKind_None)
|
|
error->mErrorKind = BfTypeLookupError::BfErrorKind_Ambiguous;
|
|
error->mAmbiguousTypeDef = lookupCtx.mAmbiguousTypeDef;
|
|
|
|
if (error->mRefNode != NULL)
|
|
ShowAmbiguousTypeError(error->mRefNode, lookupCtx.mBestTypeDef, lookupCtx.mAmbiguousTypeDef);
|
|
}
|
|
|
|
if ((protErrorTypeDef != NULL) && (lookupCtx.mBestTypeDef == protErrorTypeDef) && (error != NULL) && (error->mRefNode != NULL))
|
|
Fail(StrFormat("'%s.%s' is inaccessible due to its protection level", TypeToString(protErrorOuterType).c_str(), findName.ToString().c_str()), error->mRefNode); // CS0122
|
|
|
|
return lookupCtx.mBestTypeDef;
|
|
}
|
|
|
|
BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstanceOverride, BfTypeLookupError* error)
|
|
{
|
|
BP_ZONE("BfModule::FindTypeDef_1");
|
|
|
|
BfTypeInstance* typeInstance = (typeInstanceOverride != NULL) ? typeInstanceOverride : mCurTypeInstance;
|
|
if (typeInstance == NULL)
|
|
{
|
|
BfProject* project = NULL;
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mParser != NULL))
|
|
project = mCompiler->mResolvePassData->mParser->mProject;
|
|
|
|
BP_ZONE("System.FindTypeDef_2");
|
|
BfTypeDef* ambiguousTypeDef = NULL;
|
|
BfTypeDef *result = mSystem->FindTypeDef(findName, numGenericArgs, project, Array<BfAtomComposite>(), &ambiguousTypeDef);
|
|
if ((ambiguousTypeDef != NULL) && (error != NULL))
|
|
{
|
|
error->mErrorKind = BfTypeLookupError::BfErrorKind_Ambiguous;
|
|
error->mAmbiguousTypeDef = ambiguousTypeDef;
|
|
if (error->mRefNode != NULL)
|
|
ShowAmbiguousTypeError(error->mRefNode, result, ambiguousTypeDef);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
auto useTypeDef = GetActiveTypeDef(typeInstanceOverride, true);
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
{
|
|
if (mCompiler->mResolvePassData->mAutoCompleteTempTypes.Contains(useTypeDef))
|
|
return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, error);
|
|
}
|
|
|
|
BfTypeLookupEntry typeLookupEntry;
|
|
typeLookupEntry.mName = findName;
|
|
typeLookupEntry.mNumGenericParams = numGenericArgs;
|
|
typeLookupEntry.mUseTypeDef = useTypeDef;
|
|
|
|
BfTypeLookupEntry* typeLookupEntryPtr = NULL;
|
|
BfTypeLookupResult* resultPtr = NULL;
|
|
if (typeInstance->mLookupResults.TryAdd(typeLookupEntry, &typeLookupEntryPtr, &resultPtr))
|
|
{
|
|
typeLookupEntryPtr->mAtomUpdateIdx = typeLookupEntry.mName.GetAtomUpdateIdx();
|
|
|
|
// FindTypeDefRaw may re-enter when finding base types, so we need to expect that resultPtr can change
|
|
resultPtr->mForceLookup = true;
|
|
resultPtr->mTypeDef = NULL;
|
|
int prevAllocSize = (int)typeInstance->mLookupResults.size();
|
|
|
|
BfTypeLookupError localError;
|
|
BfTypeLookupError* errorPtr = (error != NULL) ? error : &localError;
|
|
auto typeDef = FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, errorPtr);
|
|
|
|
if (prevAllocSize != typeInstance->mLookupResults.size())
|
|
{
|
|
bool found = typeInstance->mLookupResults.TryGetValue(typeLookupEntry, &resultPtr);
|
|
BF_ASSERT(found);
|
|
}
|
|
|
|
resultPtr->mTypeDef = typeDef;
|
|
resultPtr->mForceLookup = errorPtr->mErrorKind != BfTypeLookupError::BfErrorKind_None;
|
|
|
|
return typeDef;
|
|
}
|
|
else
|
|
{
|
|
if (resultPtr->mForceLookup)
|
|
return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, error);
|
|
else
|
|
return resultPtr->mTypeDef;
|
|
}
|
|
|
|
}
|
|
|
|
BfTypeDef* BfModule::FindTypeDef(const StringImpl& typeName, int numGenericArgs, BfTypeInstance* typeInstanceOverride, BfTypeLookupError* error)
|
|
{
|
|
BP_ZONE("BfModule::FindTypeDef_4");
|
|
|
|
BfSizedAtomComposite findName;
|
|
if (!mSystem->ParseAtomComposite(typeName, findName))
|
|
return NULL;
|
|
auto result = FindTypeDef(findName, numGenericArgs, typeInstanceOverride, error);
|
|
BF_ASSERT((result == NULL) || (result->mTypeCode != BfTypeCode_Extension));
|
|
return result;
|
|
}
|
|
|
|
BfTypeDef* BfModule::FindTypeDef(BfTypeReference* typeRef, BfTypeInstance* typeInstanceOverride, BfTypeLookupError* error, int numGenericParams)
|
|
{
|
|
BP_ZONE("BfModule::FindTypeDef_5");
|
|
|
|
if (auto typeDefTypeRef = BfNodeDynCast<BfDirectTypeDefReference>(typeRef))
|
|
{
|
|
if (typeDefTypeRef->mTypeDef != NULL)
|
|
return mSystem->FilterDeletedTypeDef(typeDefTypeRef->mTypeDef);
|
|
}
|
|
|
|
//TODO: When does this get called?
|
|
if (auto elementedType = BfNodeDynCast<BfElementedTypeRef>(typeRef))
|
|
return FindTypeDef(elementedType->mElementType, typeInstanceOverride, error);
|
|
|
|
BF_ASSERT(typeRef->IsA<BfNamedTypeReference>() || typeRef->IsA<BfQualifiedTypeReference>() || typeRef->IsA<BfDirectStrTypeReference>());
|
|
auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(typeRef);
|
|
|
|
StringView findNameStr;
|
|
if (namedTypeRef != NULL)
|
|
findNameStr = namedTypeRef->mNameNode->ToStringView();
|
|
else
|
|
{
|
|
auto directStrTypeDef = BfNodeDynCastExact<BfDirectStrTypeReference>(typeRef);
|
|
if (directStrTypeDef != NULL)
|
|
findNameStr = directStrTypeDef->mTypeName;
|
|
else
|
|
BF_FATAL("Error?");
|
|
}
|
|
|
|
if (findNameStr.mLength == 6)
|
|
{
|
|
if (findNameStr == "object")
|
|
{
|
|
findNameStr = "System.Object";
|
|
Fail("'object' alias not supported, use 'Object'", typeRef);
|
|
}
|
|
else if (findNameStr == "string")
|
|
{
|
|
findNameStr = "System.String";
|
|
Fail("'string' alias not supported, use 'String'", typeRef);
|
|
}
|
|
}
|
|
|
|
BfSizedAtomComposite findName;
|
|
if (!mSystem->ParseAtomComposite(findNameStr, findName))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef BF_AST_HAS_PARENT_MEMBER
|
|
if (auto parentGenericTypeRef = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef->mParent))
|
|
{
|
|
if (parentGenericTypeRef->mElementType == typeRef)
|
|
BF_ASSERT(numGenericParams == parentGenericTypeRef->GetGenericArgCount());
|
|
}
|
|
#endif
|
|
|
|
auto typeDef = FindTypeDef(findName, numGenericParams, typeInstanceOverride, error);
|
|
//TYPEDEF if (namedTypeRef != NULL)
|
|
// namedTypeRef->mTypeDef = typeDef;
|
|
|
|
return typeDef;
|
|
}
|
|
|
|
void BfModule::CheckTypeRefFixit(BfAstNode* typeRef, const char* appendName)
|
|
{
|
|
if ((mCompiler->IsAutocomplete()) && (mCompiler->mResolvePassData->mAutoComplete->CheckFixit((typeRef))))
|
|
{
|
|
String typeName = typeRef->ToString();
|
|
if (appendName != NULL)
|
|
typeName += appendName;
|
|
|
|
std::set<String> fixitNamespaces;
|
|
|
|
//TODO: Do proper value for numGenericArgs
|
|
|
|
//mSystem->FindFixitNamespaces(typeName, -1, typeRef->GetSourceData()->mProject, fixitNamespaces);
|
|
mSystem->FindFixitNamespaces(typeName, -1, mCompiler->mResolvePassData->mParser->mProject, fixitNamespaces);
|
|
|
|
int insertLoc = 0;
|
|
|
|
BfUsingFinder usingFinder;
|
|
usingFinder.VisitMembers(typeRef->GetSourceData()->mRootNode);
|
|
|
|
for (auto& namespaceStr : fixitNamespaces)
|
|
{
|
|
BfParserData* parser = typeRef->GetSourceData()->ToParserData();
|
|
if (parser != NULL)
|
|
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("using %s;\tusing|%s|%d||using %s;", namespaceStr.c_str(), parser->mFileName.c_str(), usingFinder.mLastIdx, namespaceStr.c_str()).c_str()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void BfModule::CheckIdentifierFixit(BfAstNode* node)
|
|
{
|
|
//TODO: Check globals, possibly spelling mistakes?
|
|
}
|
|
|
|
void BfModule::TypeRefNotFound(BfTypeReference* typeRef, const char* appendName)
|
|
{
|
|
if (typeRef->IsTemporary())
|
|
return;
|
|
|
|
Fail("Type could not be found (are you missing a using directive or library reference?)", typeRef);
|
|
|
|
if (!mIgnoreErrors)
|
|
{
|
|
while (auto elementedType = BfNodeDynCast<BfElementedTypeRef>(typeRef))
|
|
typeRef = elementedType->mElementType;
|
|
|
|
if (auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(typeRef))
|
|
{
|
|
String findNameStr = namedTypeRef->mNameNode->ToString();
|
|
if (appendName != NULL)
|
|
findNameStr += appendName;
|
|
BfSizedAtomComposite findName;
|
|
if ((!mSystem->ParseAtomComposite(findNameStr, findName)) && (mCurTypeInstance != NULL))
|
|
{
|
|
//BfTypeInstance* typeInstance = (typeInstanceOverride != NULL) ? typeInstanceOverride : mCurTypeInstance;
|
|
// We don't need a typeInstanceOverride because that is used to lookup references
|
|
// from mixins, but it's the type using the mixin (mCurTypeInstance) that needs
|
|
// rebuilding if the lookup fails
|
|
BfTypeInstance* typeInstance = mCurTypeInstance;
|
|
BfTypeLookupEntry typeLookupEntry;
|
|
typeLookupEntry.mNumGenericParams = 0;
|
|
typeLookupEntry.mAtomUpdateIdx = mSystem->mAtomUpdateIdx;
|
|
typeInstance->mLookupResults.TryAdd(typeLookupEntry, BfTypeLookupResult());
|
|
}
|
|
}
|
|
}
|
|
|
|
CheckTypeRefFixit(typeRef, appendName);
|
|
}
|
|
|
|
bool BfModule::ValidateTypeWildcard(BfTypeReference* typeRef, bool isAttributeRef)
|
|
{
|
|
if (typeRef == NULL)
|
|
return false;
|
|
|
|
if (auto wildcardTypeRef = BfNodeDynCast<BfWildcardTypeReference>(typeRef))
|
|
return true;
|
|
|
|
StringT<128> nameStr;
|
|
typeRef->ToString(nameStr);
|
|
if (isAttributeRef)
|
|
nameStr.Append("Attribute");
|
|
auto typeDef = mSystem->FindTypeDef(nameStr, (BfProject*)NULL);
|
|
if ((typeDef != NULL) && (typeDef->mGenericParamDefs.IsEmpty()))
|
|
return true;
|
|
|
|
if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeRef))
|
|
{
|
|
if (qualifiedTypeRef->mLeft == NULL)
|
|
return false;
|
|
|
|
StringT<128> leftNameStr;
|
|
|
|
BfType* leftType = NULL;
|
|
BfAtomComposite leftComposite;
|
|
|
|
qualifiedTypeRef->mLeft->ToString(leftNameStr);
|
|
if (!mSystem->ParseAtomComposite(leftNameStr, leftComposite))
|
|
return false;
|
|
|
|
if (auto wildcardTypeRef = BfNodeDynCast<BfWildcardTypeReference>(qualifiedTypeRef->mRight))
|
|
{
|
|
if (mSystem->ContainsNamespace(leftComposite, NULL))
|
|
return true;
|
|
|
|
return ValidateTypeWildcard(qualifiedTypeRef->mLeft, false);
|
|
}
|
|
}
|
|
|
|
if (auto genericTypeRef = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef))
|
|
{
|
|
StringT<128> nameStr;
|
|
genericTypeRef->mElementType->ToString(nameStr);
|
|
|
|
auto typeDef = mSystem->FindTypeDef(nameStr, (int)genericTypeRef->mGenericArguments.size(), NULL);
|
|
if (typeDef == NULL)
|
|
return false;
|
|
|
|
if (typeDef->mGenericParamDefs.size() != genericTypeRef->GetGenericArgCount())
|
|
return false;
|
|
|
|
for (auto genericArgTypeRef : genericTypeRef->mGenericArguments)
|
|
{
|
|
if ((genericTypeRef != NULL) && (!ValidateTypeWildcard(genericArgTypeRef, false)))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (auto elementedTypeRef = BfNodeDynCast<BfElementedTypeRef>(typeRef))
|
|
{
|
|
return ValidateTypeWildcard(elementedTypeRef->mElementType, false);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//int sResolveTypeRefIdx = 0;
|
|
|
|
BfTypedValue BfModule::TryLookupGenericConstVaue(BfIdentifierNode* identifierNode, BfType* expectingType)
|
|
{
|
|
BfTypeInstance* contextTypeInstance = mCurTypeInstance;
|
|
BfMethodInstance* contextMethodInstance = mCurMethodInstance;
|
|
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL))
|
|
{
|
|
contextTypeInstance = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner();
|
|
contextMethodInstance = mCurMethodState->mMixinState->mMixinMethodInstance;
|
|
}
|
|
BfTypeDef* curTypeDef = NULL;
|
|
if (contextTypeInstance != NULL)
|
|
{
|
|
curTypeDef = contextTypeInstance->mTypeDef;
|
|
|
|
StringT<128> findName;
|
|
identifierNode->ToString(findName);
|
|
|
|
auto genericCheckTypeInstance = contextTypeInstance;
|
|
if (contextTypeInstance->IsBoxed())
|
|
genericCheckTypeInstance = contextTypeInstance->GetUnderlyingType()->ToTypeInstance();
|
|
|
|
bool doFakeVal = false;
|
|
if (genericCheckTypeInstance->IsUnspecializedTypeVariation())
|
|
{
|
|
genericCheckTypeInstance = GetUnspecializedTypeInstance(genericCheckTypeInstance);
|
|
doFakeVal = true;
|
|
}
|
|
|
|
BfGenericParamDef* genericParamDef = NULL;
|
|
BfType* genericParamResult = NULL;
|
|
BfType* genericTypeConstraint = NULL;
|
|
bool disallowConstExprValue = false;
|
|
if ((genericCheckTypeInstance != NULL) && (genericCheckTypeInstance->IsGenericTypeInstance()))
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)genericCheckTypeInstance;
|
|
auto* genericParams = &curTypeDef->mGenericParamDefs;
|
|
|
|
if (genericTypeInst->mGenericExtensionInfo != NULL)
|
|
{
|
|
auto activeTypeDef = GetActiveTypeDef(NULL, true);
|
|
genericParams = &activeTypeDef->mGenericParamDefs;
|
|
}
|
|
|
|
for (int genericParamIdx = (int)genericParams->size() - 1; genericParamIdx >= 0; genericParamIdx--)
|
|
{
|
|
auto checkGenericParamDef = (*genericParams)[genericParamIdx];
|
|
String genericName = checkGenericParamDef->mName;
|
|
|
|
if (genericName == findName)
|
|
{
|
|
genericParamDef = checkGenericParamDef;
|
|
genericParamResult = genericTypeInst->mTypeGenericArguments[genericParamIdx];
|
|
genericTypeConstraint = genericTypeInst->mGenericParams[genericParamIdx]->mTypeConstraint;
|
|
|
|
HandleTypeGenericParamRef(identifierNode, genericTypeInst->mTypeDef, genericParamIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((contextMethodInstance != NULL) && (genericParamResult == NULL))
|
|
{
|
|
for (int genericParamIdx = (int)contextMethodInstance->mMethodDef->mGenericParams.size() - 1; genericParamIdx >= 0; genericParamIdx--)
|
|
{
|
|
auto checkGenericParamDef = contextMethodInstance->mMethodDef->mGenericParams[genericParamIdx];
|
|
String genericName = checkGenericParamDef->mName;
|
|
if (genericName == findName)
|
|
{
|
|
genericParamDef = checkGenericParamDef;
|
|
genericParamResult = contextMethodInstance->mMethodInfoEx->mMethodGenericArguments[genericParamIdx];
|
|
genericTypeConstraint = contextMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]->mTypeConstraint;
|
|
|
|
HandleMethodGenericParamRef(identifierNode, contextMethodInstance->GetOwner()->mTypeDef, contextMethodInstance->mMethodDef, genericParamIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (genericParamResult != NULL)
|
|
{
|
|
auto typeRefSource = identifierNode->GetSourceData();
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mSourceClassifier != NULL) && (typeRefSource != NULL) && (typeRefSource == mCompiler->mResolvePassData->mParser->mSourceData))
|
|
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(identifierNode, BfSourceElementType_TypeRef);
|
|
|
|
if (genericParamResult->IsConstExprValue())
|
|
{
|
|
BfConstExprValueType* constExprValueType = (BfConstExprValueType*)genericParamResult;
|
|
|
|
BfExprEvaluator exprEvaluator(this);
|
|
exprEvaluator.mExpectingType = genericTypeConstraint;
|
|
exprEvaluator.GetLiteral(identifierNode, constExprValueType->mValue);
|
|
|
|
// We don't want to validate type here
|
|
return exprEvaluator.mResult;
|
|
}
|
|
else if (genericParamResult->IsGenericParam())
|
|
{
|
|
if ((doFakeVal) && (genericTypeConstraint != NULL))
|
|
{
|
|
return BfTypedValue(mBfIRBuilder->GetFakeVal(), genericTypeConstraint);
|
|
}
|
|
|
|
if ((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) == 0)
|
|
Fail("Only const generic parameters can be used a value", identifierNode);
|
|
|
|
if ((genericTypeConstraint != NULL) && (expectingType != NULL))
|
|
{
|
|
if (!CanImplicitlyCast(BfTypedValue(mBfIRBuilder->GetFakeVal(), genericTypeConstraint), expectingType))
|
|
{
|
|
Fail(StrFormat("Generic constraint '%s' is not convertible to 'int'", TypeToString(genericTypeConstraint).c_str()), identifierNode);
|
|
}
|
|
}
|
|
|
|
BfTypedValue result;
|
|
result.mType = genericParamResult;
|
|
result.mKind = BfTypedValueKind_GenericConstValue;
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return BfTypedValue();
|
|
}
|
|
|
|
BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags)
|
|
{
|
|
BP_ZONE("BfModule::ResolveTypeRef");
|
|
|
|
if (typeRef == NULL)
|
|
{
|
|
AssertErrorState();
|
|
return NULL;
|
|
}
|
|
|
|
if (resolveFlags & BfResolveTypeRefFlag_AutoComplete)
|
|
{
|
|
resolveFlags = (BfResolveTypeRefFlags)(resolveFlags & ~BfResolveTypeRefFlag_AutoComplete);
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if (autoComplete != NULL)
|
|
autoComplete->CheckTypeRef(typeRef, false);
|
|
}
|
|
|
|
if ((resolveFlags & BfResolveTypeRefFlag_AllowRef) == 0)
|
|
{
|
|
if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(typeRef))
|
|
{
|
|
const char* refTypeStr = BfTokenToString(refTypeRef->mRefToken->mToken);
|
|
Fail(StrFormat("Invalid use of '%s'. Only method parameters, return types, and local variables can be declared as %s types", refTypeStr, refTypeStr), refTypeRef->mRefToken);
|
|
return ResolveTypeRef(refTypeRef->mElementType);
|
|
}
|
|
}
|
|
|
|
if (auto directTypeRef = BfNodeDynCastExact<BfDirectTypeReference>(typeRef))
|
|
{
|
|
return directTypeRef->mType;
|
|
}
|
|
|
|
if (auto dotType = BfNodeDynCastExact<BfDotTypeReference>(typeRef))
|
|
{
|
|
Fail("Invalid use of '.'", typeRef);
|
|
return NULL;
|
|
}
|
|
|
|
if (auto varRefType = BfNodeDynCastExact<BfVarRefTypeReference>(typeRef))
|
|
{
|
|
Fail("Invalid use of 'var ref'. Generally references are generated with a 'var' declaration with 'ref' applied to the initializer", typeRef);
|
|
return NULL;
|
|
}
|
|
|
|
if (mNoResolveGenericParams)
|
|
resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_NoResolveGenericParam);
|
|
SetAndRestoreValue<bool> prevNoResolveGenericParams(mNoResolveGenericParams, (resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0);
|
|
|
|
//
|
|
resolveFlags = (BfResolveTypeRefFlags)(resolveFlags & ~BfResolveTypeRefFlag_NoResolveGenericParam);
|
|
|
|
BfTypeInstance* contextTypeInstance = mCurTypeInstance;
|
|
BfMethodInstance* contextMethodInstance = mCurMethodInstance;
|
|
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL))
|
|
{
|
|
contextTypeInstance = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner();
|
|
contextMethodInstance = mCurMethodState->mMixinState->mMixinMethodInstance;
|
|
}
|
|
|
|
BfTypeDef* curTypeDef = NULL;
|
|
if (contextTypeInstance != NULL)
|
|
{
|
|
curTypeDef = contextTypeInstance->mTypeDef;
|
|
|
|
// Check generics first
|
|
auto namedTypeRef = BfNodeDynCastExact<BfNamedTypeReference>(typeRef);
|
|
auto directStrTypeRef = BfNodeDynCastExact<BfDirectStrTypeReference>(typeRef);
|
|
if (((namedTypeRef != NULL) && (namedTypeRef->mNameNode != NULL)) || (directStrTypeRef != NULL))
|
|
{
|
|
StringT<128> findName;
|
|
if (namedTypeRef != NULL)
|
|
namedTypeRef->mNameNode->ToString(findName);
|
|
else
|
|
findName = directStrTypeRef->mTypeName;
|
|
if (findName == "Self")
|
|
{
|
|
BfType* selfType = mCurTypeInstance;
|
|
if (selfType->IsInterface()) // For interfaces, 'Self' refers to the identity of the implementing type, so we use a placeholder
|
|
return GetPrimitiveType(BfTypeCode_Self);
|
|
else
|
|
resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource);
|
|
|
|
if (selfType->IsBoxed())
|
|
selfType = selfType->GetUnderlyingType();
|
|
if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0)
|
|
{
|
|
if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation()))
|
|
selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType);
|
|
}
|
|
if (selfType == NULL)
|
|
{
|
|
Fail("'Self' type is not usable here", typeRef);
|
|
}
|
|
return ResolveTypeResult(typeRef, selfType, populateType, resolveFlags);
|
|
}
|
|
else if (findName == "SelfBase")
|
|
{
|
|
BfType* selfType = mCurTypeInstance;
|
|
if (selfType->IsInterface())
|
|
resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource);
|
|
|
|
if (selfType->IsBoxed())
|
|
selfType = selfType->GetUnderlyingType();
|
|
if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0)
|
|
{
|
|
if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation()))
|
|
selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType);
|
|
}
|
|
BfType* baseType = NULL;
|
|
if (selfType != NULL)
|
|
{
|
|
if (selfType->IsTypedPrimitive())
|
|
baseType = selfType->GetUnderlyingType();
|
|
else
|
|
{
|
|
auto selfTypeInst = selfType->ToTypeInstance();
|
|
if (selfTypeInst != NULL)
|
|
{
|
|
baseType = selfTypeInst->mBaseType;
|
|
}
|
|
}
|
|
}
|
|
if (baseType == NULL)
|
|
{
|
|
Fail("'SelfBase' type is not usable here", typeRef);
|
|
}
|
|
return ResolveTypeResult(typeRef, baseType, populateType, resolveFlags);
|
|
}
|
|
else if (findName == "ExpectedType")
|
|
{
|
|
Fail("'ExpectedType' is not usable here", typeRef);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
auto genericCheckTypeInstance = contextTypeInstance;
|
|
if (contextTypeInstance->IsBoxed())
|
|
genericCheckTypeInstance = contextTypeInstance->GetUnderlyingType()->ToTypeInstance();
|
|
|
|
BfGenericParamDef* genericParamDef = NULL;
|
|
BfType* genericParamResult = NULL;
|
|
bool disallowConstExprValue = false;
|
|
if ((genericCheckTypeInstance != NULL) && (genericCheckTypeInstance->IsGenericTypeInstance()))
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)genericCheckTypeInstance;
|
|
auto* genericParams = &curTypeDef->mGenericParamDefs;
|
|
|
|
if (genericTypeInst->mGenericExtensionInfo != NULL)
|
|
{
|
|
auto activeTypeDef = GetActiveTypeDef(NULL, true);
|
|
genericParams = &activeTypeDef->mGenericParamDefs;
|
|
}
|
|
|
|
for (int genericParamIdx = (int)genericParams->size() - 1; genericParamIdx >= 0; genericParamIdx--)
|
|
{
|
|
auto checkGenericParamDef = (*genericParams)[genericParamIdx];
|
|
String genericName = checkGenericParamDef->mName;
|
|
if (genericName == findName)
|
|
{
|
|
genericParamDef = checkGenericParamDef;
|
|
if (((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0) &&
|
|
((resolveFlags & BfResolveTypeRefFlag_AllowGenericTypeParamConstValue) == 0))
|
|
disallowConstExprValue = true;
|
|
|
|
HandleTypeGenericParamRef(typeRef, curTypeDef, genericParamIdx);
|
|
|
|
if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0)
|
|
return GetGenericParamType(BfGenericParamKind_Type, genericParamIdx);
|
|
else
|
|
{
|
|
SetAndRestoreValue<BfGetSymbolReferenceKind> prevSymbolRefKind;
|
|
if (mCompiler->mResolvePassData != NULL) // Don't add these typeRefs, they are indirect
|
|
prevSymbolRefKind.Init(mCompiler->mResolvePassData->mGetSymbolReferenceKind, BfGetSymbolReferenceKind_None);
|
|
genericParamResult = genericTypeInst->mTypeGenericArguments[genericParamIdx];
|
|
if ((genericParamResult != NULL) &&
|
|
(genericParamResult->IsConstExprValue()) &&
|
|
((resolveFlags & BfResolveTypeRefFlag_AllowGenericTypeParamConstValue) == 0))
|
|
disallowConstExprValue = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((contextMethodInstance != NULL) && (genericParamResult == NULL))
|
|
{
|
|
BfMethodInstance* prevMethodInstance = NULL;
|
|
|
|
// If we're in a closure then use the outside method generic arguments
|
|
auto checkMethodInstance = contextMethodInstance;
|
|
if ((mCurMethodState != NULL) && (checkMethodInstance->mIsClosure))
|
|
{
|
|
auto checkMethodState = mCurMethodState;
|
|
while (checkMethodState != NULL)
|
|
{
|
|
if ((checkMethodState->mMethodInstance != NULL) && (checkMethodState->mMethodInstance->mIsClosure))
|
|
{
|
|
checkMethodInstance = checkMethodState->mPrevMethodState->mMethodInstance;
|
|
}
|
|
checkMethodState = checkMethodState->mPrevMethodState;
|
|
}
|
|
}
|
|
|
|
for (int genericParamIdx = (int)checkMethodInstance->mMethodDef->mGenericParams.size() - 1; genericParamIdx >= 0; genericParamIdx--)
|
|
{
|
|
auto checkGenericParamDef = checkMethodInstance->mMethodDef->mGenericParams[genericParamIdx];
|
|
String genericName = checkGenericParamDef->mName;
|
|
if (genericName == findName)
|
|
{
|
|
genericParamDef = checkGenericParamDef;
|
|
if (((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0) &&
|
|
((resolveFlags & BfResolveTypeRefFlag_AllowGenericMethodParamConstValue) == 0))
|
|
disallowConstExprValue = true;
|
|
|
|
HandleMethodGenericParamRef(typeRef, checkMethodInstance->GetOwner()->mTypeDef, checkMethodInstance->mMethodDef, genericParamIdx);
|
|
|
|
if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0)
|
|
return GetGenericParamType(BfGenericParamKind_Method, genericParamIdx);
|
|
else
|
|
{
|
|
SetAndRestoreValue<BfGetSymbolReferenceKind> prevSymbolRefKind;
|
|
if (mCompiler->mResolvePassData != NULL) // Don't add these typeRefs, they are indirect
|
|
prevSymbolRefKind.Init(mCompiler->mResolvePassData->mGetSymbolReferenceKind, BfGetSymbolReferenceKind_None);
|
|
genericParamResult = checkMethodInstance->mMethodInfoEx->mMethodGenericArguments[genericParamIdx];
|
|
if ((genericParamResult != NULL) &&
|
|
(genericParamResult->IsConstExprValue()) &&
|
|
((resolveFlags & BfResolveTypeRefFlag_AllowGenericMethodParamConstValue) == 0))
|
|
disallowConstExprValue = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (genericParamResult != NULL)
|
|
{
|
|
if (disallowConstExprValue)
|
|
{
|
|
Fail("Invalid use of constant generic value", typeRef);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
if (genericParamResult->IsRef())
|
|
{
|
|
if ((resolveFlags & BfResolveTypeRefFlag_AllowRefGeneric) == 0)
|
|
genericParamResult = genericParamResult->GetUnderlyingType();
|
|
}
|
|
|
|
return ResolveTypeResult(typeRef, genericParamResult, populateType, (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource));
|
|
}
|
|
}
|
|
}
|
|
|
|
BfTypeDef* typeDef = NULL;
|
|
|
|
if (typeRef->IsNamedTypeReference())
|
|
{
|
|
BfTypeLookupError error;
|
|
error.mRefNode = typeRef;
|
|
typeDef = FindTypeDef(typeRef, contextTypeInstance, &error);
|
|
|
|
if (auto namedTypeRef = BfNodeDynCast<BfNamedTypeReference>(typeRef))
|
|
{
|
|
if (auto qualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(namedTypeRef->mNameNode))
|
|
{
|
|
// This handles the case where we have an "BaseClass.InnerClass", but the name is qualified as "DerivedClass.InnerClass"
|
|
auto leftType = ResolveTypeRef(qualifiedNameNode->mLeft, NULL, BfPopulateType_Identity, (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowRef));
|
|
if ((leftType != NULL) && (qualifiedNameNode->mRight != NULL))
|
|
{
|
|
// Try searching within inner type
|
|
auto resolvedType = ResolveInnerType(leftType, qualifiedNameNode->mRight, populateType, true);
|
|
if (resolvedType != NULL)
|
|
{
|
|
if (mCurTypeInstance != NULL)
|
|
AddDependency(leftType, mCurTypeInstance, BfDependencyMap::DependencyFlag_NameReference);
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((typeDef == NULL) && (mCurTypeInstance != NULL))
|
|
{
|
|
// Try searching within inner type
|
|
auto checkOuterType = mCurTypeInstance;
|
|
while (checkOuterType != NULL)
|
|
{
|
|
// We check for mBaseType to not be NULL because we can't inherit from an inner type, so don't even search there
|
|
// Causes reference cycles (bad).
|
|
if ((checkOuterType != mCurTypeInstance) || (checkOuterType->mBaseType != NULL))
|
|
{
|
|
auto resolvedType = ResolveInnerType(checkOuterType, typeRef, populateType, true);
|
|
if (resolvedType != NULL)
|
|
{
|
|
if (mCurTypeInstance != NULL)
|
|
AddDependency(checkOuterType, mCurTypeInstance, BfDependencyMap::DependencyFlag_NameReference);
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
}
|
|
}
|
|
checkOuterType = GetOuterType(checkOuterType);
|
|
}
|
|
}
|
|
|
|
if (typeDef == NULL)
|
|
{
|
|
#ifdef BF_AST_HAS_PARENT_MEMBER
|
|
if (auto parentQualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeRef->mParent))
|
|
{
|
|
BF_ASSERT(typeRef->mParent == mParentNodeEntry->mNode);
|
|
}
|
|
#endif
|
|
if (mParentNodeEntry != NULL)
|
|
{
|
|
if (auto parentQualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(mParentNodeEntry->mNode))
|
|
{
|
|
if (typeRef = parentQualifiedTypeRef->mLeft)
|
|
{
|
|
if ((resolveFlags & BfResolveTypeRefFlag_IgnoreLookupError) == 0)
|
|
TypeRefNotFound(typeRef);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((resolveFlags & BfResolveTypeRefFlag_IgnoreLookupError) == 0)
|
|
{
|
|
TypeRefNotFound(typeRef);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (auto typeDefTypeRef = BfNodeDynCastExact<BfDirectTypeDefReference>(typeRef))
|
|
{
|
|
typeDef = typeDefTypeRef->mTypeDef;
|
|
}
|
|
|
|
if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeRef))
|
|
{
|
|
//TODO: Determine why we had this prevIgnoreErrors set here. It causes things like IEnumerator<Hey.Test<INVALIDNAME>> not fail
|
|
// properly on INVALIDNAME
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, /*true*/mIgnoreErrors);
|
|
|
|
StringView leftNameStr;
|
|
|
|
BfType* leftType = NULL;
|
|
BfSizedAtomComposite leftComposite;
|
|
|
|
bool leftIsValid = false;
|
|
//bool leftIsValid = (qualifiedTypeRef->mLeft != NULL) && mSystem->ParseAtomComposite(qualifiedTypeRef->mLeft->ToString(), leftComposite);
|
|
|
|
if (qualifiedTypeRef->mLeft != NULL)
|
|
{
|
|
leftNameStr = qualifiedTypeRef->mLeft->ToStringView();
|
|
if (mSystem->ParseAtomComposite(leftNameStr, leftComposite))
|
|
leftIsValid = true;
|
|
}
|
|
|
|
if ((leftIsValid) && (qualifiedTypeRef->mRight != NULL))
|
|
{
|
|
StringT<128> findName;
|
|
|
|
auto genericTypeRef = BfNodeDynCast<BfGenericInstanceTypeRef>(qualifiedTypeRef->mRight);
|
|
auto activeTypeDef = GetActiveTypeDef();
|
|
BfProject* bfProject = NULL;
|
|
if (activeTypeDef != NULL)
|
|
bfProject = activeTypeDef->mProject;
|
|
|
|
if (mSystem->ContainsNamespace(leftComposite, bfProject))
|
|
{
|
|
qualifiedTypeRef->mLeft->ToString(findName);
|
|
findName.Append('.');
|
|
if (genericTypeRef != NULL)
|
|
genericTypeRef->mElementType->ToString(findName);
|
|
else
|
|
qualifiedTypeRef->mRight->ToString(findName);
|
|
}
|
|
else if ((activeTypeDef != NULL) && (activeTypeDef->mNamespace.EndsWith(leftComposite)))
|
|
{
|
|
// Partial namespace reference, extend to a full reference
|
|
|
|
findName += activeTypeDef->mNamespace.ToString();
|
|
findName.Append('.');
|
|
qualifiedTypeRef->mRight->ToString(findName);
|
|
}
|
|
|
|
if (!findName.IsEmpty())
|
|
{
|
|
int wantNumGenericArgs = 0;
|
|
#ifdef BF_AST_HAS_PARENT_MEMBER
|
|
if (auto genericTypeParent = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef->mParent))
|
|
{
|
|
BF_ASSERT(mParentNodeEntry->mNode == genericTypeParent);
|
|
//wantNumGenericArgs = (int)genericTypeParent->mGenericArguments.size();
|
|
//genericTypeRef = genericTypeParent;
|
|
}
|
|
#endif
|
|
|
|
if (mParentNodeEntry != NULL)
|
|
{
|
|
if (auto genericTypeParent = BfNodeDynCast<BfGenericInstanceTypeRef>(mParentNodeEntry->mNode))
|
|
{
|
|
wantNumGenericArgs = (int)genericTypeParent->mGenericArguments.size();
|
|
genericTypeRef = genericTypeParent;
|
|
}
|
|
}
|
|
|
|
BfTypeDef* ambiguousTypeDef = NULL;
|
|
|
|
auto typeDef = mSystem->FindTypeDef(findName, wantNumGenericArgs, bfProject, {}, &ambiguousTypeDef);
|
|
if (typeDef != NULL)
|
|
{
|
|
if (ambiguousTypeDef != NULL)
|
|
ShowAmbiguousTypeError(typeRef, typeDef, ambiguousTypeDef);
|
|
|
|
BfTypeVector genericArgs;
|
|
if (populateType != BfPopulateType_TypeDef)
|
|
{
|
|
if (genericTypeRef != NULL)
|
|
{
|
|
for (auto genericParamTypeRef : genericTypeRef->mGenericArguments)
|
|
{
|
|
auto genericParam = ResolveTypeRef(genericParamTypeRef, BfPopulateType_Declaration);
|
|
if (genericParam == NULL)
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
genericArgs.push_back(genericParam);
|
|
}
|
|
}
|
|
|
|
if (typeDef->mGenericParamDefs.size() != genericArgs.size())
|
|
{
|
|
prevIgnoreErrors.Restore();
|
|
|
|
BfAstNode* refNode = typeRef;
|
|
if (genericTypeRef != NULL)
|
|
refNode = genericTypeRef->mOpenChevron;
|
|
|
|
int wantedGenericParams = (int)typeDef->mGenericParamDefs.size();
|
|
if (wantedGenericParams == 1)
|
|
Fail("Expected one generic argument", refNode);
|
|
else
|
|
Fail(StrFormat("Expected %d generic arguments", wantedGenericParams), refNode);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
}
|
|
|
|
return ResolveTypeResult(typeRef, ResolveTypeDef(typeDef, genericArgs, populateType), populateType, resolveFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (leftType == NULL)
|
|
{
|
|
BfAutoParentNodeEntry autoParentNodeEntry(this, qualifiedTypeRef);
|
|
leftType = ResolveTypeRef(qualifiedTypeRef->mLeft, BfPopulateType_Declaration, BfResolveTypeRefFlag_IgnoreLookupError); // We throw an error below if we can't find the type
|
|
}
|
|
|
|
if (leftType == NULL)
|
|
{
|
|
mIgnoreErrors = prevIgnoreErrors.mPrevVal;
|
|
BfTypeReference* errorRefNode = qualifiedTypeRef->mLeft;
|
|
if ((leftIsValid) && (mCurTypeInstance != NULL) && (mSystem->ContainsNamespace(leftComposite, mCurTypeInstance->mTypeDef->mProject)))
|
|
{
|
|
// The left was a namespace name, so throw an error on the whole string
|
|
errorRefNode = qualifiedTypeRef;
|
|
}
|
|
TypeRefNotFound(errorRefNode);
|
|
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
prevIgnoreErrors.Restore();
|
|
|
|
if (qualifiedTypeRef->mRight == NULL)
|
|
{
|
|
FailAfter("Expected identifier", qualifiedTypeRef->mDot);
|
|
//AssertErrorState();
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
auto resolvedType = ResolveInnerType(leftType, qualifiedTypeRef->mRight, populateType);
|
|
if ((resolvedType != NULL) && (mCurTypeInstance != NULL))
|
|
AddDependency(leftType, mCurTypeInstance, BfDependencyMap::DependencyFlag_NameReference);
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
|
|
// If we did a ResolveTypeResult, then that may process an alias as the alias-to type instead of the actual alias
|
|
//return ResolveInnerType(leftType, qualifiedTypeRef->mRight, populateType);
|
|
}
|
|
|
|
if (auto resolvedTypeRef = BfNodeDynCast<BfResolvedTypeReference>(typeRef))
|
|
{
|
|
return ResolveTypeResult(typeRef, resolvedTypeRef->mType, populateType, resolveFlags);
|
|
}
|
|
|
|
if (auto retTypeTypeRef = BfNodeDynCastExact<BfRetTypeTypeRef>(typeRef))
|
|
{
|
|
bool allowThrough = false;
|
|
BfType* resolvedType = NULL;
|
|
if (retTypeTypeRef->mElementType != NULL)
|
|
{
|
|
auto innerType = ResolveTypeRef(retTypeTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if (innerType != NULL)
|
|
{
|
|
if ((innerType->IsDelegate()) || (innerType->IsFunction()))
|
|
{
|
|
PopulateType(innerType, BfPopulateType_DataAndMethods);
|
|
BfMethodInstance* invokeMethodInstance = GetRawMethodInstanceAtIdx(innerType->ToTypeInstance(), 0, "Invoke");
|
|
if (invokeMethodInstance != NULL)
|
|
{
|
|
resolvedType = invokeMethodInstance->mReturnType;
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
}
|
|
}
|
|
else if (innerType->IsGenericParam())
|
|
{
|
|
if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsUnspecializedTypeVariation()))
|
|
{
|
|
// We could have case where we have "rettype(@T0)" and @T0 gets a type variation of @M0, but we can't do a
|
|
// GetGenericParamInstance on that
|
|
allowThrough = true;
|
|
}
|
|
else
|
|
{
|
|
auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)innerType);
|
|
if (genericParamInstance->mTypeConstraint != NULL)
|
|
{
|
|
if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()))
|
|
{
|
|
resolvedType = GetDelegateReturnType(genericParamInstance->mTypeConstraint);
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
}
|
|
else if ((genericParamInstance->mTypeConstraint->IsTypeInstance()) &&
|
|
((genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mDelegateTypeDef) ||
|
|
(genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mFunctionTypeDef)))
|
|
{
|
|
allowThrough = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (innerType->IsMethodRef())
|
|
{
|
|
auto methodRefType = (BfMethodRefType*)innerType;
|
|
resolvedType = methodRefType->mMethodRef->mReturnType;
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!allowThrough)
|
|
{
|
|
Fail("'rettype' can only be used on delegate or function types", retTypeTypeRef->mRetTypeToken);
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
}
|
|
}
|
|
|
|
if (auto refTypeRef = BfNodeDynCastExact<BfRefTypeRef>(typeRef))
|
|
{
|
|
if ((refTypeRef->mRefToken != NULL) && (refTypeRef->mRefToken->GetToken() == BfToken_Mut) && (refTypeRef->mElementType != NULL))
|
|
{
|
|
bool needsRefWrap = false;
|
|
|
|
auto resolvedType = ResolveTypeRef(refTypeRef->mElementType, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if (resolvedType != NULL)
|
|
{
|
|
if ((resolvedType->IsComposite()) || (resolvedType->IsGenericParam()))
|
|
needsRefWrap = true;
|
|
}
|
|
|
|
if (!needsRefWrap)
|
|
{
|
|
// Non-composites (including pointers) don't actually need ref-wrapping for 'mut'
|
|
return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
BfResolvedTypeSet::LookupContext lookupCtx;
|
|
lookupCtx.mRootTypeRef = typeRef;
|
|
lookupCtx.mRootTypeDef = typeDef;
|
|
lookupCtx.mModule = this;
|
|
BfResolvedTypeSet::Entry* resolvedEntry = NULL;
|
|
auto inserted = mContext->mResolvedTypes.Insert(typeRef, &lookupCtx, &resolvedEntry);
|
|
|
|
if (resolvedEntry == NULL)
|
|
{
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
if (!inserted)
|
|
{
|
|
BF_ASSERT(resolvedEntry->mValue != NULL);
|
|
return ResolveTypeResult(typeRef, resolvedEntry->mValue, populateType, resolveFlags);
|
|
}
|
|
|
|
if (typeRef->IsTypeDefTypeReference())
|
|
{
|
|
//BF_ASSERT(typeDefTypeRef->mTypeDef != NULL); // Resolved higher up
|
|
//auto typeDef = typeDefTypeRef->mTypeDef;
|
|
if ((typeDef->mTypeCode >= BfTypeCode_None) && (typeDef->mTypeCode <= BfTypeCode_Double))
|
|
{
|
|
BfPrimitiveType* primType = new BfPrimitiveType();
|
|
primType->mTypeDef = typeDef;
|
|
resolvedEntry->mValue = primType;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(primType, &lookupCtx, NULL) == resolvedEntry->mHash);
|
|
InitType(primType, populateType);
|
|
return ResolveTypeResult(typeRef, primType, populateType, resolveFlags);
|
|
}
|
|
|
|
if ((mCurTypeInstance != NULL) && (typeDef->mGenericParamDefs.size() != 0))
|
|
{
|
|
// Try to inherit generic params from current parent
|
|
|
|
auto outerType = typeDef->mOuterType;
|
|
BF_ASSERT(!outerType->mIsPartial);
|
|
if (TypeHasParent(mCurTypeInstance->mTypeDef, outerType))
|
|
|
|
{
|
|
BfType* checkCurType = mCurTypeInstance;
|
|
if (checkCurType->IsBoxed())
|
|
checkCurType = checkCurType->GetUnderlyingType();
|
|
if (checkCurType->IsTypeAlias())
|
|
checkCurType = GetOuterType(checkCurType);
|
|
|
|
BF_ASSERT(checkCurType->IsGenericTypeInstance());
|
|
int numParentGenericParams = (int)outerType->mGenericParamDefs.size();
|
|
int wantedGenericParams = (int)typeDef->mGenericParamDefs.size() - numParentGenericParams;
|
|
if (wantedGenericParams != 0)
|
|
{
|
|
if (wantedGenericParams == 1)
|
|
Fail("Expected generic argument", typeRef);
|
|
else
|
|
Fail(StrFormat("Expected %d generic arguments", wantedGenericParams), typeRef);
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
auto parentGenericTypeInstance = (BfGenericTypeInstance*)checkCurType;
|
|
|
|
BfGenericTypeInstance* genericTypeInst;
|
|
if (typeDef->mTypeCode == BfTypeCode_TypeAlias)
|
|
{
|
|
auto typeAliasType = new BfGenericTypeAliasType();
|
|
genericTypeInst = typeAliasType;
|
|
}
|
|
else
|
|
genericTypeInst = new BfGenericTypeInstance();
|
|
genericTypeInst->mTypeDef = typeDef;
|
|
for (int i = 0; i < numParentGenericParams; i++)
|
|
{
|
|
genericTypeInst->mGenericParams.push_back(parentGenericTypeInstance->mGenericParams[i]->AddRef());
|
|
genericTypeInst->mTypeGenericArguments.push_back(parentGenericTypeInstance->mTypeGenericArguments[i]);
|
|
}
|
|
|
|
CheckUnspecializedGenericType(genericTypeInst, populateType);
|
|
|
|
resolvedEntry->mValue = genericTypeInst;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(genericTypeInst, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(genericTypeInst, populateType);
|
|
return ResolveTypeResult(typeRef, genericTypeInst, populateType, resolveFlags);
|
|
}
|
|
}
|
|
|
|
BfTypeInstance* typeInst;
|
|
if (typeDef->mTypeCode == BfTypeCode_TypeAlias)
|
|
{
|
|
auto typeAliasType = new BfTypeAliasType();
|
|
typeInst = typeAliasType;
|
|
}
|
|
else
|
|
{
|
|
typeInst = new BfTypeInstance();
|
|
}
|
|
typeInst->mTypeDef = typeDef;
|
|
|
|
if (typeInst->mTypeDef->mGenericParamDefs.size() != 0)
|
|
{
|
|
Fail("Generic type arguments expected", typeRef);
|
|
delete typeInst;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
resolvedEntry->mValue = typeInst;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(typeInst, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(typeInst, populateType);
|
|
return ResolveTypeResult(typeRef, typeInst, populateType, resolveFlags);
|
|
}
|
|
else if (auto boxedTypeRef = BfNodeDynCast<BfBoxedTypeRef>(typeRef))
|
|
{
|
|
BfBoxedType* boxedType = new BfBoxedType();
|
|
auto innerType = ResolveTypeRef(boxedTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if ((innerType == NULL) || (!innerType->IsStruct()))
|
|
{
|
|
Fail("Invalid box target", boxedTypeRef->mElementType);
|
|
delete boxedType;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
boxedType->mElementType = innerType->ToTypeInstance();
|
|
boxedType->mTypeDef = boxedType->mElementType->mTypeDef;
|
|
resolvedEntry->mValue = boxedType;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(boxedType, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(boxedType, populateType);
|
|
return ResolveTypeResult(typeRef, boxedType, populateType, resolveFlags);
|
|
}
|
|
else if (auto arrayTypeRef = BfNodeDynCast<BfArrayTypeRef>(typeRef))
|
|
{
|
|
if (arrayTypeRef->mDimensions > 4)
|
|
{
|
|
Fail("Too many array dimensions, consider using a jagged array.", arrayTypeRef);
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
auto elementType = ResolveTypeRef(arrayTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if (elementType == NULL)
|
|
{
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
if ((arrayTypeRef->mDimensions == 1) && (arrayTypeRef->mParams.size() == 1))
|
|
{
|
|
intptr elementCount = -1;
|
|
BfExpression* sizeExpr = BfNodeDynCast<BfExpression>(arrayTypeRef->mParams[0]);
|
|
BF_ASSERT(sizeExpr != NULL);
|
|
if (sizeExpr != NULL)
|
|
{
|
|
BfConstResolver constResolver(this);
|
|
BfType* intType = GetPrimitiveType(BfTypeCode_IntPtr);
|
|
constResolver.mExpectingType = intType;
|
|
constResolver.mAllowGenericConstValue = true;
|
|
BfTypedValue typedVal;
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
typedVal = constResolver.Resolve(sizeExpr);
|
|
}
|
|
if (typedVal.mKind == BfTypedValueKind_GenericConstValue)
|
|
{
|
|
BfUnknownSizedArrayType* arrayType = new BfUnknownSizedArrayType();
|
|
arrayType->mContext = mContext;
|
|
arrayType->mElementType = elementType;
|
|
arrayType->mElementCount = -1;
|
|
arrayType->mElementCountSource = typedVal.mType;
|
|
resolvedEntry->mValue = arrayType;
|
|
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(arrayType, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(arrayType, populateType);
|
|
return ResolveTypeResult(typeRef, arrayType, populateType, resolveFlags);
|
|
}
|
|
|
|
if (typedVal)
|
|
typedVal = Cast(sizeExpr, typedVal, intType);
|
|
if (typedVal)
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (constant != NULL)
|
|
{
|
|
if (constant->mConstType == BfConstType_Undef)
|
|
elementCount = -1; // Undef marker
|
|
else if (BfIRBuilder::IsInt(constant->mTypeCode))
|
|
elementCount = constant->mInt32;
|
|
}
|
|
}
|
|
}
|
|
/*if (elementCount < 0)
|
|
{
|
|
Fail(StrFormat("Array length '%d' is illegal", elementCount), arrayTypeRef->mParams[0]);
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return CreateSizedArrayType(elementType, 0);
|
|
}*/
|
|
|
|
BfSizedArrayType* arrayType = new BfSizedArrayType();
|
|
arrayType->mContext = mContext;
|
|
arrayType->mElementType = elementType;
|
|
arrayType->mElementCount = elementCount;
|
|
arrayType->mWantsGCMarking = false; // Fill in in InitType
|
|
resolvedEntry->mValue = arrayType;
|
|
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(arrayType, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(arrayType, populateType);
|
|
return ResolveTypeResult(typeRef, arrayType, populateType, resolveFlags);
|
|
}
|
|
|
|
BfArrayType* arrayType = new BfArrayType();
|
|
arrayType->mContext = mContext;
|
|
arrayType->mDimensions = arrayTypeRef->mDimensions;
|
|
arrayType->mTypeDef = mCompiler->GetArrayTypeDef(arrayType->mDimensions);
|
|
arrayType->mTypeGenericArguments.push_back(elementType);
|
|
resolvedEntry->mValue = arrayType;
|
|
|
|
CheckUnspecializedGenericType(arrayType, populateType);
|
|
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(arrayType, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(arrayType, populateType);
|
|
return ResolveTypeResult(typeRef, arrayType, populateType, resolveFlags);
|
|
}
|
|
else if (auto genericTypeInstRef = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef))
|
|
{
|
|
int wantNumGenericParams = genericTypeInstRef->GetGenericArgCount();
|
|
BfTypeDef* ambiguousTypeDef = NULL;
|
|
|
|
BfTypeDef* typeDef = ResolveGenericInstanceDef(genericTypeInstRef);
|
|
|
|
BfGenericTypeInstance* genericTypeInst;
|
|
if ((typeDef != NULL) && (typeDef->mTypeCode == BfTypeCode_TypeAlias))
|
|
{
|
|
auto typeAliasType = new BfGenericTypeAliasType();
|
|
genericTypeInst = typeAliasType;
|
|
}
|
|
else
|
|
genericTypeInst = new BfGenericTypeInstance();
|
|
genericTypeInst->mContext = mContext;
|
|
|
|
if (ambiguousTypeDef != NULL)
|
|
ShowAmbiguousTypeError(typeRef, typeDef, ambiguousTypeDef);
|
|
if (typeDef == NULL)
|
|
{
|
|
Fail("Unable to resolve type", typeRef);
|
|
delete genericTypeInst;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
BF_ASSERT(typeDef->mDefState != BfTypeDef::DefState_Deleted);
|
|
|
|
if (typeDef->mGenericParamDefs.size() == 0)
|
|
{
|
|
Fail("Not a generic type", typeRef);
|
|
delete genericTypeInst;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
int startDefGenericParamIdx = 0;
|
|
genericTypeInst->mTypeDef = typeDef;
|
|
|
|
if (mCurTypeInstance != NULL)
|
|
{
|
|
// Copy generic params for our parent type if the current type instance shares that parent type
|
|
//auto outerType = mSystem->GetOuterTypeNonPartial(typeDef);
|
|
auto outerType = typeDef->mOuterType;
|
|
BfTypeDef* commonOuterType = FindCommonOuterType(mCurTypeInstance->mTypeDef, outerType);
|
|
if ((commonOuterType) && (mCurTypeInstance->IsGenericTypeInstance()))
|
|
{
|
|
startDefGenericParamIdx = (int)commonOuterType->mGenericParamDefs.size();
|
|
auto parentTypeInstance = (BfGenericTypeInstance*)mCurTypeInstance;
|
|
if (parentTypeInstance->IsTypeAlias())
|
|
parentTypeInstance = (BfGenericTypeInstance*)GetOuterType(parentTypeInstance)->ToTypeInstance();
|
|
for (int i = 0; i < startDefGenericParamIdx; i++)
|
|
{
|
|
genericTypeInst->mGenericParams.push_back(parentTypeInstance->mGenericParams[i]->AddRef());
|
|
genericTypeInst->mTypeGenericArguments.push_back(parentTypeInstance->mTypeGenericArguments[i]);
|
|
auto typeGenericArg = genericTypeInst->mTypeGenericArguments[i];
|
|
genericTypeInst->mIsUnspecialized |= typeGenericArg->IsGenericParam() || typeGenericArg->IsUnspecializedType();
|
|
}
|
|
}
|
|
}
|
|
|
|
Array<BfTypeReference*> genericArguments;
|
|
std::function<void(BfTypeReference*)> _GetTypeRefs = [&](BfTypeReference* typeRef)
|
|
{
|
|
if (auto elementedTypeRef = BfNodeDynCast<BfElementedTypeRef>(typeRef))
|
|
{
|
|
_GetTypeRefs(elementedTypeRef->mElementType);
|
|
}
|
|
else if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeRef))
|
|
{
|
|
_GetTypeRefs(qualifiedTypeRef->mLeft);
|
|
}
|
|
|
|
if (auto genericTypeRef = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef))
|
|
{
|
|
for (auto genericArg : genericTypeRef->mGenericArguments)
|
|
genericArguments.push_back(genericArg);
|
|
}
|
|
};
|
|
_GetTypeRefs(genericTypeInstRef);
|
|
|
|
int wantedGenericParams = (int)typeDef->mGenericParamDefs.size() - startDefGenericParamIdx;
|
|
int genericArgDiffCount = (int)genericArguments.size() - wantedGenericParams;
|
|
if (genericArgDiffCount != 0)
|
|
{
|
|
int innerWantedGenericParams = (int)typeDef->mGenericParamDefs.size();
|
|
if (typeDef->mOuterType != NULL)
|
|
innerWantedGenericParams -= (int)typeDef->mOuterType->mGenericParamDefs.size();
|
|
ShowGenericArgCountError(genericTypeInstRef, innerWantedGenericParams);
|
|
delete genericTypeInst;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
int genericParamIdx = 0;
|
|
for (auto genericArgRef : genericArguments)
|
|
{
|
|
auto genericArg = ResolveTypeRef(genericArgRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowGenericMethodParamConstValue);
|
|
if (genericArg == NULL)
|
|
{
|
|
delete genericTypeInst;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
genericTypeInst->mTypeGenericArguments.push_back(genericArg);
|
|
genericTypeInst->mTypeGenericArgumentRefs.push_back(genericArgRef);
|
|
genericParamIdx++;
|
|
}
|
|
|
|
resolvedEntry->mValue = genericTypeInst;
|
|
|
|
CheckUnspecializedGenericType(genericTypeInst, populateType);
|
|
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(genericTypeInst, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(genericTypeInst, populateType);
|
|
return ResolveTypeResult(typeRef, genericTypeInst, populateType, resolveFlags);
|
|
}
|
|
else if (auto tupleTypeRef = BfNodeDynCast<BfTupleTypeRef>(typeRef))
|
|
{
|
|
Array<BfType*> types;
|
|
Array<String> names;
|
|
|
|
for (int fieldIdx = 0; fieldIdx < (int)tupleTypeRef->mFieldTypes.size(); fieldIdx++)
|
|
{
|
|
BfTypeReference* typeRef = tupleTypeRef->mFieldTypes[fieldIdx];
|
|
auto type = ResolveTypeRef(typeRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if (type == NULL)
|
|
{
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
String fieldName;
|
|
BfIdentifierNode* identifierNode = NULL;
|
|
if (fieldIdx < (int)tupleTypeRef->mFieldNames.size())
|
|
identifierNode = tupleTypeRef->mFieldNames[fieldIdx];
|
|
|
|
if (identifierNode != NULL)
|
|
fieldName = identifierNode->ToString();
|
|
else
|
|
fieldName = StrFormat("%d", fieldIdx);
|
|
|
|
String typeName = TypeToString(type);
|
|
types.push_back(type);
|
|
names.push_back(fieldName);
|
|
}
|
|
|
|
auto baseType = (BfTypeInstance*)ResolveTypeDef(mContext->mCompiler->mValueTypeTypeDef, BfPopulateType_Identity);
|
|
BfTupleType* tupleType = new BfTupleType();
|
|
//TODO: Add to correct project
|
|
tupleType->Init(baseType->mTypeDef->mProject, baseType);
|
|
|
|
tupleType->mFieldInstances.Resize(types.size());
|
|
|
|
for (int fieldIdx = 0; fieldIdx < (int)types.size(); fieldIdx++)
|
|
{
|
|
BfFieldDef* fieldDef = tupleType->AddField(names[fieldIdx]);
|
|
fieldDef->mProtection = (names[fieldIdx][0] == '_') ? BfProtection_Private : BfProtection_Public;
|
|
|
|
BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[fieldIdx];
|
|
fieldInstance->mFieldIdx = fieldIdx;
|
|
fieldInstance->SetResolvedType(types[fieldIdx]);
|
|
fieldInstance->mOwner = tupleType;
|
|
}
|
|
|
|
tupleType->Finish();
|
|
|
|
resolvedEntry->mValue = tupleType;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(tupleType, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(tupleType, populateType);
|
|
return ResolveTypeResult(typeRef, tupleType, populateType, resolveFlags);
|
|
}
|
|
else if (auto nullableTypeRef = BfNodeDynCast<BfNullableTypeRef>(typeRef))
|
|
{
|
|
BfTypeReference* elementTypeRef = nullableTypeRef->mElementType;
|
|
auto typeDef = mCompiler->mNullableTypeDef;
|
|
|
|
auto elementType = ResolveTypeRef(elementTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if (elementType == NULL)
|
|
{
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
BfGenericTypeInstance* genericTypeInst = new BfGenericTypeInstance();
|
|
genericTypeInst->mContext = mContext;
|
|
genericTypeInst->mTypeDef = typeDef;
|
|
|
|
auto genericParamInstance = new BfGenericTypeParamInstance(typeDef, 0);
|
|
genericTypeInst->mGenericParams.push_back(genericParamInstance);
|
|
genericTypeInst->mTypeGenericArguments.push_back(elementType);
|
|
//genericTypeInst->mIsUnspecialized = elementType->IsGenericParam() || elementType->IsUnspecializedType();
|
|
|
|
CheckUnspecializedGenericType(genericTypeInst, populateType);
|
|
|
|
resolvedEntry->mValue = genericTypeInst;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(genericTypeInst, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(genericTypeInst, populateType);
|
|
return ResolveTypeResult(typeRef, genericTypeInst, populateType, resolveFlags);
|
|
}
|
|
else if (auto pointerTypeRef = BfNodeDynCast<BfPointerTypeRef>(typeRef))
|
|
{
|
|
BfPointerType* pointerType = new BfPointerType();
|
|
pointerType->mElementType = ResolveTypeRef(pointerTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
pointerType->mContext = mContext;
|
|
if (pointerType->mElementType == NULL)
|
|
{
|
|
delete pointerType;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
resolvedEntry->mValue = pointerType;
|
|
|
|
//int hashVal = mContext->mResolvedTypes.Hash(typeRef, &lookupCtx);
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(pointerType, &lookupCtx) == resolvedEntry->mHash);
|
|
|
|
InitType(pointerType, populateType);
|
|
return ResolveTypeResult(typeRef, pointerType, populateType, resolveFlags);
|
|
}
|
|
else if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(typeRef))
|
|
{
|
|
BfRefType* refType = new BfRefType();
|
|
refType->mRefKind = BfRefType::RefKind_Ref;
|
|
if (refTypeRef->mRefToken == NULL)
|
|
refType->mRefKind = BfRefType::RefKind_Ref;
|
|
else if (refTypeRef->mRefToken->GetToken() == BfToken_Out)
|
|
refType->mRefKind = BfRefType::RefKind_Out;
|
|
else if (refTypeRef->mRefToken->GetToken() == BfToken_Mut)
|
|
refType->mRefKind = BfRefType::RefKind_Mut;
|
|
refType->mElementType = ResolveTypeRef(refTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if (refType->mElementType == NULL)
|
|
{
|
|
delete refType;
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
resolvedEntry->mValue = refType;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(refType, &lookupCtx) == resolvedEntry->mHash);
|
|
InitType(refType, populateType);
|
|
return ResolveTypeResult(typeRef, refType, populateType, resolveFlags);
|
|
}
|
|
else if (auto delegateTypeRef = BfNodeDynCast<BfDelegateTypeRef>(typeRef))
|
|
{
|
|
auto returnType = ResolveTypeRef(delegateTypeRef->mReturnType);
|
|
if (returnType == NULL)
|
|
{
|
|
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
auto baseDelegateType = ResolveTypeDef(mCompiler->mDelegateTypeDef)->ToTypeInstance();
|
|
|
|
BfDelegateType* delegateType = new BfDelegateType();
|
|
|
|
Val128 hashContext;
|
|
|
|
BfTypeDef* typeDef = new BfTypeDef();
|
|
typeDef->mProject = baseDelegateType->mTypeDef->mProject;
|
|
typeDef->mSystem = mCompiler->mSystem;
|
|
typeDef->mName = mSystem->mEmptyAtom;
|
|
if (delegateTypeRef->mTypeToken->GetToken() == BfToken_Delegate)
|
|
{
|
|
typeDef->mIsDelegate = true;
|
|
typeDef->mTypeCode = BfTypeCode_Object;
|
|
}
|
|
else
|
|
{
|
|
typeDef->mIsFunction = true;
|
|
typeDef->mTypeCode = BfTypeCode_Struct;
|
|
}
|
|
|
|
BfMethodDef* methodDef = new BfMethodDef();
|
|
methodDef->mDeclaringType = typeDef;
|
|
methodDef->mName = "Invoke";
|
|
methodDef->mProtection = BfProtection_Public;
|
|
methodDef->mIdx = 0;
|
|
methodDef->mIsStatic = !typeDef->mIsDelegate;
|
|
|
|
auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>();
|
|
delegateType->mDirectAllocNodes.push_back(directTypeRef);
|
|
if (typeDef->mIsDelegate)
|
|
directTypeRef->Init(delegateType);
|
|
else
|
|
directTypeRef->Init(ResolveTypeDef(mCompiler->mFunctionTypeDef));
|
|
typeDef->mBaseTypes.push_back(directTypeRef);
|
|
|
|
directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>();
|
|
delegateType->mDirectAllocNodes.push_back(directTypeRef);
|
|
directTypeRef->Init(returnType);
|
|
methodDef->mReturnTypeRef = directTypeRef;
|
|
|
|
AddDependency(directTypeRef->mType, baseDelegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
|
|
|
|
auto hashVal = mContext->mResolvedTypes.Hash(typeRef, &lookupCtx);
|
|
|
|
int paramIdx = 0;
|
|
for (auto param : delegateTypeRef->mParams)
|
|
{
|
|
auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowRef);
|
|
String paramName;
|
|
if (param->mNameNode != NULL)
|
|
paramName = param->mNameNode->ToString();
|
|
|
|
if (paramType->IsUnspecializedType())
|
|
delegateType->mIsUnspecializedType = true;
|
|
if (paramType->IsUnspecializedTypeVariation())
|
|
delegateType->mIsUnspecializedTypeVariation = true;
|
|
if (!paramType->IsReified())
|
|
delegateType->mIsReified = false;
|
|
if (paramType->IsGenericParam())
|
|
{
|
|
delegateType->mIsUnspecializedTypeVariation = true;
|
|
}
|
|
|
|
auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>();
|
|
delegateType->mDirectAllocNodes.push_back(directTypeRef);
|
|
directTypeRef->Init(paramType);
|
|
|
|
BfParameterDef* paramDef = new BfParameterDef();
|
|
|
|
paramDef->mTypeRef = directTypeRef;
|
|
paramDef->mName = paramName;
|
|
methodDef->mParams.push_back(paramDef);
|
|
paramIdx++;
|
|
|
|
AddDependency(paramType, baseDelegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
|
|
}
|
|
|
|
typeDef->mMethods.push_back(methodDef);
|
|
|
|
//
|
|
|
|
BfDefBuilder::AddMethod(typeDef, BfMethodType_Ctor, BfProtection_Public, false, "");
|
|
if (typeDef->mIsDelegate)
|
|
BfDefBuilder::AddDynamicCastMethods(typeDef);
|
|
|
|
delegateType->mContext = mContext;
|
|
delegateType->mTypeDef = typeDef;
|
|
InitType(delegateType, BfPopulateType_DataAndMethods);
|
|
resolvedEntry->mValue = delegateType;
|
|
|
|
// #ifdef _DEBUG
|
|
// if (BfResolvedTypeSet::Hash(delegateType, &lookupCtx) != resolvedEntry->mHash)
|
|
// {
|
|
// int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx);
|
|
// int typeHash = BfResolvedTypeSet::Hash(delegateType, &lookupCtx);
|
|
// BF_ASSERT(refHash == typeHash);
|
|
// }
|
|
// #endif
|
|
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(delegateType, &lookupCtx) == resolvedEntry->mHash);
|
|
return ResolveTypeResult(typeRef, delegateType, populateType, resolveFlags);
|
|
}
|
|
else if (auto genericParamTypeRef = BfNodeDynCast<BfGenericParamTypeRef>(typeRef))
|
|
{
|
|
auto genericParamType = GetGenericParamType(genericParamTypeRef->mGenericParamKind, genericParamTypeRef->mGenericParamIdx);
|
|
resolvedEntry->mValue = genericParamType;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(genericParamType, &lookupCtx) == resolvedEntry->mHash);
|
|
return ResolveTypeResult(typeRef, genericParamType, populateType, resolveFlags);
|
|
}
|
|
else if (auto retTypeTypeRef = BfNodeDynCast<BfRetTypeTypeRef>(typeRef))
|
|
{
|
|
auto retTypeType = new BfRetTypeType();
|
|
retTypeType->mElementType = ResolveTypeRef(retTypeTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
// We know this is a generic param type, it can't fail to resolve
|
|
BF_ASSERT(retTypeType->mElementType);
|
|
|
|
resolvedEntry->mValue = retTypeType;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(retTypeType, &lookupCtx) == resolvedEntry->mHash);
|
|
|
|
InitType(retTypeType, populateType);
|
|
return ResolveTypeResult(typeRef, retTypeType, populateType, resolveFlags);
|
|
}
|
|
else if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeRef))
|
|
{
|
|
auto leftType = ResolveTypeRef(qualifiedTypeRef->mLeft, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue);
|
|
if (leftType == NULL)
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
return ResolveTypeResult(typeRef, ResolveInnerType(leftType, qualifiedTypeRef->mRight), populateType, resolveFlags);
|
|
}
|
|
else if (auto constTypeRef = BfNodeDynCastExact<BfConstTypeRef>(typeRef))
|
|
{
|
|
return ResolveTypeRef(constTypeRef->mElementType, populateType, (BfResolveTypeRefFlags)(resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam));
|
|
}
|
|
else if (auto constExprTypeRef = BfNodeDynCastExact<BfConstExprTypeRef>(typeRef))
|
|
{
|
|
auto constExprType = new BfConstExprValueType();
|
|
constExprType->mContext = mContext;
|
|
|
|
BfVariant result;
|
|
if (constExprTypeRef->mConstExpr != NULL)
|
|
{
|
|
BfType* constGenericParam = NULL;
|
|
result = mContext->mResolvedTypes.EvaluateToVariant(&lookupCtx, constExprTypeRef->mConstExpr, constGenericParam);
|
|
BF_ASSERT(constGenericParam == NULL);
|
|
}
|
|
|
|
constExprType->mType = GetPrimitiveType(result.mTypeCode);
|
|
constExprType->mValue = result;
|
|
|
|
resolvedEntry->mValue = constExprType;
|
|
BF_ASSERT(BfResolvedTypeSet::Hash(constExprType, &lookupCtx) == resolvedEntry->mHash);
|
|
|
|
InitType(constExprType, populateType);
|
|
return constExprType;
|
|
}
|
|
else
|
|
{
|
|
BF_FATAL("Not implemented!");
|
|
NotImpl(typeRef);
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
|
}
|
|
|
|
BfType* BfModule::ResolveTypeRefAllowUnboundGenerics(BfTypeReference* typeRef, BfPopulateType populateType, bool resolveGenericParam)
|
|
{
|
|
if (auto genericTypeRef = BfNodeDynCast<BfGenericInstanceTypeRef>(typeRef))
|
|
{
|
|
if (genericTypeRef->mGenericArguments.size() == 0)
|
|
{
|
|
auto genericTypeDef = ResolveGenericInstanceDef(genericTypeRef);
|
|
if (genericTypeDef == NULL)
|
|
return NULL;
|
|
|
|
BfTypeVector typeVector;
|
|
for (int i = 0; i < (int)genericTypeDef->mGenericParamDefs.size(); i++)
|
|
typeVector.push_back(GetGenericParamType(BfGenericParamKind_Type, i));
|
|
return ResolveTypeDef(genericTypeDef, typeVector);
|
|
}
|
|
}
|
|
|
|
return ResolveTypeRef(typeRef, populateType, resolveGenericParam ? (BfResolveTypeRefFlags)0 : BfResolveTypeRefFlag_NoResolveGenericParam);
|
|
}
|
|
|
|
// This finds non-default unspecialized generic type instances and converts them into a BfUnspecializedGenericTypeVariation
|
|
BfType* BfModule::CheckUnspecializedGenericType(BfGenericTypeInstance* genericTypeInst, BfPopulateType populateType)
|
|
{
|
|
int argCount = (int)genericTypeInst->mTypeGenericArguments.size();
|
|
|
|
bool isDefaultUnspecialized = true;
|
|
for (int argIdx = 0; argIdx < argCount; argIdx++)
|
|
{
|
|
auto argType = genericTypeInst->mTypeGenericArguments[argIdx];
|
|
if (argType->IsGenericParam())
|
|
{
|
|
auto genericParamType = (BfGenericParamType*)argType;
|
|
if ((genericParamType->mGenericParamKind != BfGenericParamKind_Type) || (genericParamType->mGenericParamIdx != argIdx))
|
|
isDefaultUnspecialized = false;
|
|
genericTypeInst->mIsUnspecialized = true;
|
|
}
|
|
else if (argType->IsUnspecializedType())
|
|
{
|
|
isDefaultUnspecialized = false;
|
|
genericTypeInst->mIsUnspecialized = true;
|
|
}
|
|
else
|
|
isDefaultUnspecialized = false;
|
|
}
|
|
|
|
if (genericTypeInst->mIsUnspecialized)
|
|
genericTypeInst->mIsUnspecializedVariation = !isDefaultUnspecialized;
|
|
return genericTypeInst;
|
|
}
|
|
|
|
BfTypeInstance* BfModule::GetUnspecializedTypeInstance(BfTypeInstance* typeInst)
|
|
{
|
|
if (!typeInst->IsGenericTypeInstance())
|
|
return typeInst;
|
|
|
|
auto genericTypeInst = (BfGenericTypeInstance*)typeInst;
|
|
auto result = ResolveTypeDef(genericTypeInst->mTypeDef, BfPopulateType_Declaration);
|
|
BF_ASSERT((result != NULL) && (result->IsUnspecializedType()));
|
|
if (result == NULL)
|
|
return NULL;
|
|
return result->ToTypeInstance();
|
|
}
|
|
|
|
BfType* BfModule::ResolveInnerType(BfType* outerType, BfIdentifierNode* identifier, BfPopulateType populateType, bool ignoreErrors)
|
|
{
|
|
BfDirectStrTypeReference typeRef;
|
|
typeRef.Init(identifier->ToString());
|
|
|
|
auto type = ResolveInnerType(outerType, &typeRef, populateType, ignoreErrors);
|
|
return type;
|
|
}
|
|
|
|
BfType* BfModule::ResolveTypeRef(BfAstNode* astNode, const BfSizedArray<BfTypeReference*>* genericArgs, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags)
|
|
{
|
|
if ((genericArgs == NULL) || (genericArgs->size() == 0))
|
|
{
|
|
if (auto identifier = BfNodeDynCast<BfIdentifierNode>(astNode))
|
|
{
|
|
BfNamedTypeReference typeRef;
|
|
typeRef.mNameNode = identifier;
|
|
typeRef.mSrcEnd = 0;
|
|
typeRef.mToken = BfToken_None;
|
|
auto type = ResolveTypeRef(&typeRef, populateType, resolveFlags);
|
|
return type;
|
|
}
|
|
}
|
|
|
|
BfAstAllocator alloc;
|
|
alloc.mSourceData = astNode->GetSourceData();
|
|
|
|
std::function<BfTypeReference*(BfAstNode*)> _ConvType = [&] (BfAstNode* astNode) -> BfTypeReference*
|
|
{
|
|
if (auto typeRef = BfNodeDynCast<BfTypeReference>(astNode))
|
|
return typeRef;
|
|
|
|
BfTypeReference* result = NULL;
|
|
if (auto identifier = BfNodeDynCast<BfIdentifierNode>(astNode))
|
|
{
|
|
auto* typeRef = alloc.Alloc<BfNamedTypeReference>();
|
|
typeRef->mNameNode = identifier;
|
|
result = typeRef;
|
|
}
|
|
else if (auto memberRefExpr = BfNodeDynCast<BfMemberReferenceExpression>(astNode))
|
|
{
|
|
auto qualifiedTypeRef = alloc.Alloc<BfQualifiedTypeReference>();
|
|
qualifiedTypeRef->mLeft = _ConvType(memberRefExpr->mTarget);
|
|
qualifiedTypeRef->mDot = memberRefExpr->mDotToken;
|
|
qualifiedTypeRef->mRight = _ConvType(memberRefExpr->mMemberName);
|
|
|
|
if ((qualifiedTypeRef->mLeft == NULL) || (qualifiedTypeRef->mRight == NULL))
|
|
return NULL;
|
|
result = qualifiedTypeRef;
|
|
}
|
|
|
|
if (result == NULL)
|
|
return NULL;
|
|
result->SetSrcStart(astNode->GetSrcStart());
|
|
result->SetSrcEnd(astNode->GetSrcEnd());
|
|
|
|
return result;
|
|
};
|
|
|
|
auto typeRef = _ConvType(astNode);
|
|
if (typeRef == NULL)
|
|
return NULL;
|
|
|
|
if ((genericArgs != NULL) && (genericArgs->size() != 0))
|
|
{
|
|
auto genericInstanceTypeRef = alloc.Alloc<BfGenericInstanceTypeRef>();
|
|
genericInstanceTypeRef->SetSrcStart(typeRef->GetSrcStart());
|
|
genericInstanceTypeRef->mElementType = typeRef;
|
|
#ifdef BF_AST_HAS_PARENT_MEMBER
|
|
typeRef->mParent = genericInstanceTypeRef;
|
|
#endif
|
|
|
|
BfDeferredAstSizedArray<BfTypeReference*> arguments(genericInstanceTypeRef->mGenericArguments, &alloc);
|
|
|
|
for (auto genericArg : *genericArgs)
|
|
{
|
|
if (genericArg != NULL)
|
|
{
|
|
arguments.push_back(genericArg);
|
|
genericInstanceTypeRef->SetSrcEnd(genericArg->GetSrcEnd());
|
|
}
|
|
}
|
|
|
|
typeRef = genericInstanceTypeRef;
|
|
}
|
|
|
|
return ResolveTypeRef(typeRef, populateType, resolveFlags);
|
|
}
|
|
|
|
// This flow should mirror CastToValue
|
|
bool BfModule::CanImplicitlyCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags)
|
|
{
|
|
BfType* fromType = typedVal.mType;
|
|
|
|
if (fromType == toType)
|
|
return true;
|
|
|
|
// Ref X to Ref Y, X* to Y*
|
|
{
|
|
bool checkUnderlying = false;
|
|
|
|
if (((typedVal.mType->IsRef()) && (toType->IsRef())))
|
|
{
|
|
auto fromRefType = (BfRefType*)typedVal.mType;
|
|
auto toRefType = (BfRefType*)toType;
|
|
if ((fromRefType->mRefKind == toRefType->mRefKind))
|
|
checkUnderlying = true;
|
|
else if ((fromRefType->mRefKind == BfRefType::RefKind_Ref) && (toRefType->mRefKind == BfRefType::RefKind_Mut))
|
|
checkUnderlying = true; // Allow a ref-to-mut implicit conversion
|
|
}
|
|
|
|
if ((typedVal.mType->IsPointer()) && (toType->IsPointer()))
|
|
checkUnderlying = true;
|
|
|
|
if (checkUnderlying)
|
|
{
|
|
auto fromInner = typedVal.mType->GetUnderlyingType();
|
|
auto toInner = toType->GetUnderlyingType();
|
|
|
|
if (fromInner == toInner)
|
|
return true;
|
|
|
|
// ref int <-> ref int64/int32 (of same size)
|
|
if (((fromInner->IsInteger()) && (toInner->IsInteger())) &&
|
|
(fromInner->mSize == toInner->mSize) &&
|
|
(fromInner->IsSigned() == toInner->IsSigned()))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Generic param -> *
|
|
if ((typedVal.mType->IsGenericParam()) && (!toType->IsGenericParam()))
|
|
{
|
|
if (toType == mContext->mBfObjectType)
|
|
{
|
|
//auto resolvedType = ResolveGenericType(typedVal.mType);
|
|
//auto resolvedType = typedVal.mType;
|
|
/*if (!resolvedType->IsGenericParam())
|
|
return CanImplicitlyCast(BfTypedValue(typedVal.mValue, resolvedType), toType);*/
|
|
// Can we never NOT do this?
|
|
return true;
|
|
}
|
|
|
|
// For these casts, it's just important we get *A* value to work with here,
|
|
// as this is just use for unspecialized parsing. We don't use the generated code
|
|
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)typedVal.mType);
|
|
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
|
|
{
|
|
return true;
|
|
}
|
|
if (toType->IsInterface())
|
|
{
|
|
for (auto iface : genericParamInst->mInterfaceConstraints)
|
|
if (TypeIsSubTypeOf(iface, toType->ToTypeInstance()))
|
|
return true;
|
|
}
|
|
|
|
if (genericParamInst->mTypeConstraint != NULL)
|
|
{
|
|
auto defaultFromValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint);
|
|
auto result = CanImplicitlyCast(defaultFromValue, toType);
|
|
|
|
if ((result) && (genericParamInst->mTypeConstraint->IsDelegate()) && (toType->IsDelegate()))
|
|
{
|
|
// Don't allow cast when we are constrained by a delegate type, because BfMethodRefs can match and we require an actual alloc
|
|
return false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Generic constrained with class or pointer type -> void*
|
|
if (toType->IsVoidPtr())
|
|
{
|
|
if ((genericParamInst->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_StructPtr)) ||
|
|
(genericParamInst->mTypeConstraint != NULL) &&
|
|
((genericParamInst->mTypeConstraint->IsPointer()) || (genericParamInst->mTypeConstraint->IsObjectOrInterface())))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// * -> Generic param
|
|
if (toType->IsGenericParam())
|
|
{
|
|
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)toType);
|
|
if (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var)
|
|
return true;
|
|
if (typedVal.mType->IsNull())
|
|
{
|
|
bool allowCast = (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) || (genericParamInst->mGenericParamFlags & BfGenericParamFlag_StructPtr);
|
|
if ((!allowCast) && (genericParamInst->mTypeConstraint != NULL))
|
|
allowCast = genericParamInst->mTypeConstraint->IsObject() || genericParamInst->mTypeConstraint->IsPointer();
|
|
if (allowCast)
|
|
return true;
|
|
}
|
|
|
|
if (genericParamInst->mTypeConstraint != NULL)
|
|
{
|
|
if (CanImplicitlyCast(typedVal, genericParamInst->mTypeConstraint))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Struct truncate
|
|
if ((fromType->IsStruct()) && (toType->IsStruct()))
|
|
{
|
|
auto fromTypeInstance = fromType->ToTypeInstance();
|
|
auto toTypeInstance = toType->ToTypeInstance();
|
|
if (TypeIsSubTypeOf(fromTypeInstance, toTypeInstance))
|
|
return true;
|
|
}
|
|
|
|
if ((fromType->IsVar()) || (toType->IsVar()))
|
|
return true;
|
|
|
|
// Null -> ObjectInst|IFace|ptr
|
|
if ((fromType->IsNull()) &&
|
|
((toType->IsObjectOrInterface()) || (toType->IsPointer()) || (toType->IsPointer())))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Object/struct -> object/struct|IFace
|
|
if ((fromType->IsTypeInstance()) && ((toType->IsTypeInstance() || (toType->IsInterface()))))
|
|
{
|
|
auto fromTypeInstance = fromType->ToTypeInstance();
|
|
auto toTypeInstance = toType->ToTypeInstance();
|
|
|
|
if (TypeIsSubTypeOf(fromTypeInstance, toTypeInstance))
|
|
return true;
|
|
}
|
|
|
|
// concrete IFace -> object|IFace
|
|
if ((fromType->IsConcreteInterfaceType()) && ((toType->IsObject() || (toType->IsInterface()))))
|
|
{
|
|
auto concreteInterfaceType = (BfConcreteInterfaceType*)fromType;
|
|
if ((toType->IsObject()) || (concreteInterfaceType->mInterface == toType))
|
|
return true;
|
|
}
|
|
|
|
// IFace -> object
|
|
if ((fromType->IsInterface()) && (toType == mContext->mBfObjectType))
|
|
return true;
|
|
|
|
if (toType->IsPointer())
|
|
{
|
|
// Ptr -> Ptr
|
|
if (fromType->IsPointer())
|
|
{
|
|
bool allowCast = false;
|
|
auto fromPointerType = (BfPointerType*)typedVal.mType;
|
|
auto toPointerType = (BfPointerType*)toType;
|
|
|
|
auto fromUnderlying = fromPointerType->mElementType;
|
|
auto toUnderlying = toPointerType->mElementType;
|
|
|
|
// Allow cast from T[size]* to T* implicitly
|
|
// And from T* to T[size]* explicitly
|
|
if (fromUnderlying->IsSizedArray())
|
|
fromUnderlying = fromUnderlying->GetUnderlyingType();
|
|
// if ((toUnderlying->IsSizedArray()) && (explicitCast))
|
|
// toUnderlying = toUnderlying->GetUnderlyingType();
|
|
|
|
if ((fromUnderlying == toUnderlying) ||
|
|
(TypeIsSubTypeOf(fromUnderlying->ToTypeInstance(), toUnderlying->ToTypeInstance())) ||
|
|
(toUnderlying->IsVoid()))
|
|
allowCast = true;
|
|
|
|
if (allowCast)
|
|
return true;
|
|
}
|
|
else if (fromType->IsObject())
|
|
{
|
|
auto fromTypeInst = fromType->ToTypeInstance();
|
|
auto charType = GetPrimitiveType(BfTypeCode_Char8);
|
|
auto charPtrType = CreatePointerType(charType);
|
|
if ((fromTypeInst->mTypeDef == mCompiler->mStringTypeDef) && (toType == charPtrType))
|
|
{
|
|
// String Object -> char* literal
|
|
return true;
|
|
}
|
|
}
|
|
/*else if (typedVal.mType->IsSizedArray())
|
|
{
|
|
if (typedVal.IsAddr())
|
|
{
|
|
BfSizedArrayType* arrayType = (BfSizedArrayType*)typedVal.mType;
|
|
BfTypedValue returnPointer(typedVal.mValue, CreatePointerType(arrayType->mElementType));
|
|
return CanImplicitlyCast(returnPointer, toType);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
// Boxing?
|
|
if (((fromType->IsValueType()) || (fromType->IsPointer()) || (fromType->IsValuelessType())) &&
|
|
((toType->IsInterface()) || (toType == mContext->mBfObjectType)))
|
|
{
|
|
// if (fromType->IsPointer())
|
|
// {
|
|
// if (toType == mContext->mBfObjectType)
|
|
// return true;
|
|
// return false;
|
|
// }
|
|
|
|
if (toType == mContext->mBfObjectType)
|
|
return true;
|
|
|
|
BfTypeInstance* fromStructTypeInstance = NULL;
|
|
fromStructTypeInstance = fromType->ToTypeInstance();
|
|
if (fromStructTypeInstance == NULL)
|
|
{
|
|
if (fromType->IsPrimitiveType())
|
|
{
|
|
auto primType = (BfPrimitiveType*)fromType;
|
|
fromStructTypeInstance = GetPrimitiveStructType(primType->mTypeDef->mTypeCode);
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
auto toTypeInstance = toType->ToTypeInstance();
|
|
// Need to box it
|
|
if (TypeIsSubTypeOf(fromStructTypeInstance, toTypeInstance))
|
|
return true;
|
|
}
|
|
|
|
if (fromType->IsRef())
|
|
{
|
|
if (toType->IsRef())
|
|
{
|
|
BfTypedValue unrefValue = BfTypedValue(typedVal.mValue, fromType->GetUnderlyingType(), true);
|
|
return CanImplicitlyCast(unrefValue, toType->GetUnderlyingType());
|
|
}
|
|
else
|
|
{
|
|
// ref T -> T
|
|
return fromType->GetUnderlyingType() == toType;
|
|
}
|
|
}
|
|
|
|
// Int -> Enum
|
|
if ((typedVal.mType->IsIntegral()) && (toType->IsEnum()))
|
|
{
|
|
// Allow implicit cast of zero
|
|
if (typedVal.mValue)
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if ((constant != NULL) && (BfIRBuilder::IsInt(constant->mTypeCode)))
|
|
{
|
|
int64 srcVal = constant->mInt64;
|
|
if (srcVal == 0)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tuple -> Tuple
|
|
if ((typedVal.mType->IsTuple()) && (toType->IsTuple()))
|
|
{
|
|
auto fromTupleType = (BfTupleType*)typedVal.mType;
|
|
auto toTupleType = (BfTupleType*)toType;
|
|
if (fromTupleType->mFieldInstances.size() == toTupleType->mFieldInstances.size())
|
|
{
|
|
for (int valueIdx = 0; valueIdx < (int)fromTupleType->mFieldInstances.size(); valueIdx++)
|
|
{
|
|
BfFieldInstance* fromFieldInstance = &fromTupleType->mFieldInstances[valueIdx];
|
|
BfFieldInstance* toFieldInstance = &toTupleType->mFieldInstances[valueIdx];
|
|
|
|
BfFieldDef* fromFieldDef = fromFieldInstance->GetFieldDef();
|
|
BfFieldDef* toFieldDef = toFieldInstance->GetFieldDef();
|
|
|
|
auto fromFieldType = fromFieldInstance->GetResolvedType();
|
|
auto toFieldType = toFieldInstance->GetResolvedType();
|
|
|
|
// Either the names have to match or one has to be unnamed
|
|
if ((!fromFieldDef->IsUnnamedTupleField()) && (!toFieldDef->IsUnnamedTupleField()) &&
|
|
(fromFieldDef->mName != toFieldDef->mName))
|
|
return false;
|
|
|
|
if (toFieldType->IsVoid())
|
|
continue; // Allow sinking to void
|
|
|
|
if (!CanImplicitlyCast(GetFakeTypedValue(fromFieldType), toFieldType))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// -> const <value>
|
|
if (toType->IsConstExprValue())
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (constant != NULL)
|
|
{
|
|
BfConstExprValueType* toConstExprValueType = (BfConstExprValueType*)toType;
|
|
|
|
auto variantVal = TypedValueToVariant(NULL, typedVal);
|
|
if ((mBfIRBuilder->IsInt(variantVal.mTypeCode)) && (mBfIRBuilder->IsInt(toConstExprValueType->mValue.mTypeCode)))
|
|
{
|
|
if (variantVal.mInt64 == toConstExprValueType->mValue.mInt64)
|
|
return true;
|
|
}
|
|
else if ((mBfIRBuilder->IsFloat(variantVal.mTypeCode)) && (mBfIRBuilder->IsFloat(toConstExprValueType->mValue.mTypeCode)))
|
|
{
|
|
if (variantVal.ToDouble() == toConstExprValueType->mValue.ToDouble())
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((fromType->IsPrimitiveType()) && (toType->IsPrimitiveType()))
|
|
{
|
|
auto fromPrimType = (BfPrimitiveType*)fromType;
|
|
auto toPrimType = (BfPrimitiveType*)toType;
|
|
|
|
BfTypeCode fromTypeCode = fromPrimType->mTypeDef->mTypeCode;
|
|
BfTypeCode toTypeCode = toPrimType->mTypeDef->mTypeCode;
|
|
|
|
// Must be from a default int to do an implicit constant cast, not casted by user, ie: (ushort)123
|
|
if ((toType->IsIntegral()) && (typedVal.mValue))
|
|
{
|
|
// Allow constant ints to be implicitly casted to a smaller type if they fit
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (constant != NULL)
|
|
{
|
|
if (BfIRBuilder::IsInt(constant->mTypeCode))
|
|
{
|
|
int64 srcVal = constant->mInt64;
|
|
|
|
if (toPrimType->IsChar())
|
|
{
|
|
if (srcVal == 0)
|
|
return true;
|
|
}
|
|
else if ((fromPrimType->IsChar()) && (!toPrimType->IsChar()))
|
|
{
|
|
// Never allow this
|
|
}
|
|
else if ((constant->mTypeCode == BfTypeCode_UInt64) && (srcVal < 0))
|
|
{
|
|
// There's nothing that this could fit into
|
|
}
|
|
else if (toType->IsSigned())
|
|
{
|
|
int64 minVal = -(1LL << (8 * toType->mSize - 1));
|
|
int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
return true;
|
|
}
|
|
else if (toType->mSize == 8) // ulong
|
|
{
|
|
if (srcVal >= 0)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
int64 minVal = 0;
|
|
int64 maxVal = (1LL << (8 * toType->mSize)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
return true;
|
|
}
|
|
}
|
|
else if (constant->mConstType == BfConstType_Undef)
|
|
{
|
|
BF_ASSERT(mBfIRBuilder->mIgnoreWrites);
|
|
|
|
auto undefConst = (BfConstantUndef*)constant;
|
|
|
|
auto fakeVal = GetFakeTypedValue(GetPrimitiveType(undefConst->mTypeCode));
|
|
if (CanImplicitlyCast(fakeVal, toType))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (toTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_Char16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Char8:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_Int16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
return true;
|
|
case BfTypeCode_UInt8:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_UInt16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_Int32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
return true;
|
|
case BfTypeCode_IntPtr:
|
|
if (mCompiler->mSystem->mPtrSize == 4)
|
|
return true;
|
|
break;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_Char32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Char8:
|
|
case BfTypeCode_Char16:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_UInt32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
return true;
|
|
case BfTypeCode_UIntPtr:
|
|
if (mCompiler->mSystem->mPtrSize == 4)
|
|
return true;
|
|
break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Int64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_IntPtr:
|
|
return true;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_UInt64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_UIntPtr:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_IntPtr:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
return true;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
return true;
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_Int64:
|
|
if (mCompiler->mSystem->mPtrSize == 8)
|
|
return true;
|
|
break;
|
|
}
|
|
break;
|
|
case BfTypeCode_UIntPtr:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
return true;
|
|
case BfTypeCode_UInt64:
|
|
if (mCompiler->mSystem->mPtrSize == 8)
|
|
return true;
|
|
break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Single:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_IntPtr:
|
|
case BfTypeCode_IntUnknown:
|
|
return true;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_UInt64:
|
|
case BfTypeCode_UIntPtr:
|
|
case BfTypeCode_UIntUnknown:
|
|
return true;
|
|
}
|
|
break;
|
|
case BfTypeCode_Double:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_IntPtr:
|
|
case BfTypeCode_IntUnknown:
|
|
return true;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_UInt64:
|
|
case BfTypeCode_UIntPtr:
|
|
case BfTypeCode_UIntUnknown:
|
|
return true;
|
|
case BfTypeCode_Single:
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// wrappable -> struct
|
|
// if ((fromType->IsWrappableType()) && (toType->IsStruct()))
|
|
// {
|
|
// auto wrappableType = GetWrappedStructType(fromType);
|
|
// if (TypeIsSubTypeOf(wrappableType, toType->ToTypeInstance()))
|
|
// return true;
|
|
// }
|
|
|
|
// Check user-defined operators
|
|
// {
|
|
// auto fromTypeInstance = fromType->ToTypeInstance();
|
|
// auto toTypeInstance = toType->ToTypeInstance();
|
|
//
|
|
// int bestFromDist = INT_MAX;
|
|
// BfType* bestFromType = NULL;
|
|
// int bestNegFromDist = INT_MAX;
|
|
// BfType* bestNegFromType = NULL;
|
|
//
|
|
// int bestToDist = INT_MAX;
|
|
// BfType* bestToType = NULL;
|
|
// int bestNegToDist = INT_MAX;
|
|
// BfType* bestNegToType = NULL;
|
|
//
|
|
// for (int pass = 0; pass < 2; pass++)
|
|
// {
|
|
// BfBaseClassWalker baseClassWalker(fromType, toType, this);
|
|
// while (true)
|
|
// {
|
|
// auto entry = baseClassWalker.Next();
|
|
// auto checkInstance = entry.mTypeInstance;
|
|
// if (checkInstance == NULL)
|
|
// break;
|
|
//
|
|
// for (auto operatorDef : checkInstance->mTypeDef->mOperators)
|
|
// {
|
|
// if (operatorDef->mOperatorDeclaration->mIsConvOperator)
|
|
// {
|
|
// if ((operatorDef->mOperatorDeclaration->mExplicitToken != NULL) &&
|
|
// (operatorDef->mOperatorDeclaration->mExplicitToken->GetToken() == BfToken_Explicit))
|
|
// continue;
|
|
//
|
|
// // Get in native module so our module doesn't get a reference to it - we may not end up calling it at all!
|
|
// auto methodInst = checkInstance->mModule->GetRawMethodInstanceAtIdx(checkInstance, operatorDef->mIdx);
|
|
//
|
|
// if (methodInst->GetParamCount() != 1)
|
|
// {
|
|
// //AssertErrorState();
|
|
// continue;
|
|
// }
|
|
//
|
|
// auto checkFromType = methodInst->GetParamType(0);
|
|
// if (checkFromType->IsSelf())
|
|
// checkFromType = entry.mSrcType;
|
|
//
|
|
// // Selection pass
|
|
// if (pass == 0)
|
|
// {
|
|
// int fromDist = GetTypeDistance(checkFromType, fromType);
|
|
// int toDist = GetTypeDistance(toType, methodInst->mReturnType);
|
|
//
|
|
// if ((fromDist == INT_MAX) || (toDist == INT_MAX))
|
|
// continue;
|
|
//
|
|
// if ((fromDist >= 0) && (toDist >= 0))
|
|
// {
|
|
// if ((fromDist >= 0) && (fromDist < bestFromDist))
|
|
// {
|
|
// bestFromDist = fromDist;
|
|
// bestFromType = checkFromType;
|
|
// }
|
|
//
|
|
// if ((fromDist >= 0) && (toDist < bestToDist))
|
|
// {
|
|
// bestToDist = toDist;
|
|
// bestToType = methodInst->mReturnType;
|
|
// }
|
|
// }
|
|
// }
|
|
// else // Execution Pass
|
|
// {
|
|
// auto returnType = methodInst->mReturnType;
|
|
// if (returnType->IsSelf())
|
|
// returnType = entry.mSrcType;
|
|
//
|
|
// if ((checkFromType == bestFromType) && (methodInst->mReturnType == bestToType))
|
|
// {
|
|
// return true;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (bestFromType == NULL)
|
|
// bestFromType = bestNegFromType;
|
|
// if (bestToType == NULL)
|
|
// bestToType = bestNegToType;
|
|
//
|
|
// if ((bestFromType == NULL) || (bestToType == NULL))
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// Check user-defined operators
|
|
if ((castFlags & BfCastFlags_NoConversionOperator) == 0)
|
|
{
|
|
auto fromType = typedVal.mType;
|
|
auto fromTypeInstance = fromType->ToTypeInstance();
|
|
auto toTypeInstance = toType->ToTypeInstance();
|
|
|
|
auto liftedFromType = ((fromTypeInstance != NULL) && fromTypeInstance->IsNullable()) ? fromTypeInstance->GetUnderlyingType() : NULL;
|
|
auto liftedToType = ((toTypeInstance != NULL) && toTypeInstance->IsNullable()) ? toTypeInstance->GetUnderlyingType() : NULL;
|
|
|
|
int bestFromDist = INT_MAX;
|
|
BfType* bestFromType = NULL;
|
|
int bestNegFromDist = INT_MAX;
|
|
BfType* bestNegFromType = NULL;
|
|
|
|
int bestToDist = INT_MAX;
|
|
BfType* bestToType = NULL;
|
|
int bestNegToDist = INT_MAX;
|
|
BfType* bestNegToType = NULL;
|
|
bool isAmbiguousCast = false;
|
|
|
|
BfIRValue conversionResult;
|
|
BfMethodInstance* opMethodInstance = NULL;
|
|
BfType* opMethodSrcType = NULL;
|
|
|
|
// Normal, lifted, execute
|
|
for (int pass = 0; pass < 3; pass++)
|
|
{
|
|
auto checkToType = toType;
|
|
auto checkFromType = fromType;
|
|
|
|
if (pass == 1)
|
|
{
|
|
if ((bestFromType != NULL) && (bestToType != NULL))
|
|
continue;
|
|
|
|
if (liftedFromType != NULL)
|
|
checkFromType = liftedFromType;
|
|
if (liftedToType != NULL)
|
|
checkToType = liftedToType;
|
|
}
|
|
else if (pass == 2)
|
|
{
|
|
if ((bestFromType == NULL) || (bestToType == NULL))
|
|
break;
|
|
}
|
|
|
|
BfBaseClassWalker baseClassWalker(fromType, toType, this);
|
|
|
|
while (true)
|
|
{
|
|
auto entry = baseClassWalker.Next();
|
|
auto checkInstance = entry.mTypeInstance;
|
|
if (checkInstance == NULL)
|
|
break;
|
|
|
|
for (auto operatorDef : checkInstance->mTypeDef->mOperators)
|
|
{
|
|
if (operatorDef->mOperatorDeclaration->mIsConvOperator)
|
|
{
|
|
if ((operatorDef->mOperatorDeclaration->mExplicitToken != NULL) &&
|
|
(operatorDef->mOperatorDeclaration->mExplicitToken->GetToken() == BfToken_Explicit))
|
|
continue;
|
|
|
|
auto methodInst = GetRawMethodInstanceAtIdx(checkInstance, operatorDef->mIdx);
|
|
|
|
if (methodInst->GetParamCount() != 1)
|
|
{
|
|
BF_ASSERT(mCompiler->mPassInstance->HasFailed());
|
|
continue;
|
|
}
|
|
|
|
auto methodFromType = methodInst->GetParamType(0);
|
|
auto methodToType = methodInst->mReturnType;
|
|
|
|
if (methodFromType->IsSelf())
|
|
methodFromType = entry.mSrcType;
|
|
if (methodToType->IsSelf())
|
|
methodToType = entry.mSrcType;
|
|
|
|
// Selection pass
|
|
if (pass < 2)
|
|
{
|
|
auto methodCheckFromType = methodFromType;
|
|
auto methodCheckToType = methodToType;
|
|
if (pass == 1)
|
|
{
|
|
// Only check inner type on lifted types when we aren't checking conversions within lifted class
|
|
// This avoid some infinite conversions
|
|
if ((methodCheckFromType->IsNullable()) && (!checkInstance->IsNullable()))
|
|
methodCheckFromType = methodCheckFromType->GetUnderlyingType();
|
|
if ((methodCheckToType->IsNullable()) && (!checkInstance->IsNullable()))
|
|
methodCheckToType = methodCheckToType->GetUnderlyingType();
|
|
}
|
|
|
|
int fromDist = GetTypeDistance(methodCheckFromType, checkFromType);
|
|
if (fromDist < 0)
|
|
{
|
|
// Allow us to cast a constant int to a smaller type if it satisfies the cast operator
|
|
if ((typedVal.mValue.IsConst()) && (methodCheckFromType != toType) && (CanImplicitlyCast(typedVal, methodCheckFromType)))
|
|
{
|
|
fromDist = 0;
|
|
}
|
|
}
|
|
|
|
int toDist = GetTypeDistance(methodCheckToType, checkToType);
|
|
|
|
if ((fromDist == INT_MAX) || (toDist == INT_MAX))
|
|
continue;
|
|
|
|
if ((fromDist >= 0) && (toDist >= 0))
|
|
{
|
|
if ((fromDist >= 0) && (fromDist < bestFromDist))
|
|
{
|
|
bestFromDist = fromDist;
|
|
bestFromType = methodFromType;
|
|
}
|
|
|
|
if ((toDist >= 0) && (toDist < bestToDist))
|
|
{
|
|
bestToDist = toDist;
|
|
bestToType = methodToType;
|
|
}
|
|
}
|
|
}
|
|
else if (pass == 2) // Execution Pass
|
|
{
|
|
if ((methodFromType == bestFromType) && (methodToType == bestToType))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isAmbiguousCast)
|
|
break;
|
|
}
|
|
|
|
if (bestFromType == NULL)
|
|
bestFromType = bestNegFromType;
|
|
if (bestToType == NULL)
|
|
bestToType = bestNegToType;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool BfModule::AreSplatsCompatible(BfType* fromType, BfType* toType, bool* outNeedsMemberCasting)
|
|
{
|
|
if ((fromType->IsTypeInstance()) && (!fromType->IsSplattable()))
|
|
return false;
|
|
if ((toType->IsTypeInstance()) && (!toType->IsSplattable()))
|
|
return false;
|
|
|
|
auto _GetTypes = [&](BfType* type, Array<BfType*>& types)
|
|
{
|
|
BfTypeUtils::SplatIterate([&](BfType* memberType) { types.Add(memberType); }, type);
|
|
};
|
|
|
|
Array<BfType*> fromTypes;
|
|
_GetTypes(fromType, fromTypes);
|
|
Array<BfType*> toTypes;
|
|
_GetTypes(toType, toTypes);
|
|
|
|
if (toTypes.size() > fromTypes.size())
|
|
return false;
|
|
|
|
for (int i = 0; i < toTypes.size(); i++)
|
|
{
|
|
BfType* fromMemberType = fromTypes[i];
|
|
BfType* toMemberType = toTypes[i];
|
|
|
|
if (fromMemberType != toMemberType)
|
|
{
|
|
if ((outNeedsMemberCasting != NULL) &&
|
|
(fromMemberType->IsIntPtrable()) && (toMemberType->IsIntPtrable()))
|
|
*outNeedsMemberCasting = true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, BfMethodInstance* methodInstance, BfType* toType, BfCastFlags castFlags)
|
|
{
|
|
auto invokeMethodInstance = GetDelegateInvokeMethod(toType->ToTypeInstance());
|
|
|
|
if (invokeMethodInstance->IsExactMatch(methodInstance, false, true))
|
|
{
|
|
BfModuleMethodInstance methodRefMethod;
|
|
if (methodInstance->mDeclModule == this)
|
|
methodRefMethod = methodInstance;
|
|
else
|
|
methodRefMethod = ReferenceExternalMethodInstance(methodInstance);
|
|
auto dataType = GetPrimitiveType(BfTypeCode_IntPtr);
|
|
if (!methodRefMethod.mFunc)
|
|
{
|
|
if (HasCompiledOutput())
|
|
AssertErrorState();
|
|
return GetDefaultValue(dataType);
|
|
}
|
|
auto bindFuncVal = methodRefMethod.mFunc;
|
|
if (mCompiler->mOptions.mAllowHotSwapping)
|
|
bindFuncVal = mBfIRBuilder->RemapBindFunction(bindFuncVal);
|
|
return mBfIRBuilder->CreatePtrToInt(bindFuncVal, BfTypeCode_IntPtr);
|
|
}
|
|
|
|
if ((castFlags & BfCastFlags_SilentFail) == 0)
|
|
{
|
|
if (invokeMethodInstance->IsExactMatch(methodInstance, true, true))
|
|
{
|
|
Fail(StrFormat("Non-static method '%s' cannot match '%s' because it contains captured variables, consider using a delegate or removing captures", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str()), srcNode);
|
|
}
|
|
else if (invokeMethodInstance->IsExactMatch(methodInstance, false, false))
|
|
{
|
|
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider adding '%s this' to the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(methodInstance->GetParamType(-1)).c_str()), srcNode);
|
|
}
|
|
}
|
|
return BfIRValue();
|
|
}
|
|
|
|
BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags, BfCastResultFlags* resultFlags)
|
|
{
|
|
bool explicitCast = (castFlags & BfCastFlags_Explicit) != 0;
|
|
|
|
if (typedVal.mType == toType)
|
|
{
|
|
if (resultFlags != NULL)
|
|
{
|
|
if (typedVal.IsAddr())
|
|
*resultFlags = (BfCastResultFlags)(*resultFlags | BfCastResultFlags_IsAddr);
|
|
if (typedVal.mKind == BfTypedValueKind_TempAddr)
|
|
*resultFlags = (BfCastResultFlags)(*resultFlags | BfCastResultFlags_IsTemp);
|
|
}
|
|
else if (typedVal.IsAddr())
|
|
typedVal = LoadValue(typedVal);
|
|
|
|
return typedVal.mValue;
|
|
}
|
|
|
|
BF_ASSERT(typedVal.mType->mContext == mContext);
|
|
BF_ASSERT(toType->mContext == mContext);
|
|
|
|
if ((typedVal.IsAddr()) && (!typedVal.mType->IsValueType()))
|
|
typedVal = LoadValue(typedVal);
|
|
|
|
//BF_ASSERT(!typedVal.IsAddr() || typedVal.mType->IsGenericParam() || typedVal.mType->IsValueType());
|
|
|
|
// Ref X to Ref Y, X* to Y*
|
|
{
|
|
bool checkUnderlying = false;
|
|
|
|
if (((typedVal.mType->IsRef()) && (toType->IsRef())))
|
|
{
|
|
auto fromRefType = (BfRefType*)typedVal.mType;
|
|
auto toRefType = (BfRefType*)toType;
|
|
if ((fromRefType->mRefKind == toRefType->mRefKind))
|
|
checkUnderlying = true;
|
|
else if ((fromRefType->mRefKind == BfRefType::RefKind_Ref) && (toRefType->mRefKind == BfRefType::RefKind_Mut))
|
|
checkUnderlying = true; // Allow a ref-to-mut implicit conversion
|
|
}
|
|
|
|
if ((typedVal.mType->IsPointer()) && (toType->IsPointer()))
|
|
checkUnderlying = true;
|
|
|
|
if (checkUnderlying)
|
|
{
|
|
auto fromInner = typedVal.mType->GetUnderlyingType();
|
|
auto toInner = toType->GetUnderlyingType();
|
|
|
|
if (fromInner == toInner)
|
|
{
|
|
return typedVal.mValue;
|
|
}
|
|
|
|
// ref int <-> ref int64/int32 (of same size)
|
|
if (((fromInner->IsInteger()) && (toInner->IsInteger())) &&
|
|
(fromInner->mSize == toInner->mSize) &&
|
|
(fromInner->IsSigned() == toInner->IsSigned()))
|
|
return typedVal.mValue;
|
|
}
|
|
}
|
|
|
|
// Null -> ObjectInst|IFace|ptr
|
|
if ((typedVal.mType->IsNull()) &&
|
|
((toType->IsObjectOrInterface()) || (toType->IsPointer() || (toType->IsFunction()))))
|
|
{
|
|
return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
|
|
if (explicitCast)
|
|
{
|
|
// Object -> void*
|
|
if ((typedVal.mType->IsObject()) && (toType->IsVoidPtr()))
|
|
{
|
|
return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
|
|
// Func -> void*
|
|
if ((typedVal.mType->IsFunction()) && (toType->IsVoidPtr()))
|
|
{
|
|
return mBfIRBuilder->CreateIntToPtr(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
|
|
// void* -> Func
|
|
if ((typedVal.mType->IsVoidPtr()) && (toType->IsFunction()))
|
|
{
|
|
return mBfIRBuilder->CreatePtrToInt(typedVal.mValue, BfTypeCode_IntPtr);
|
|
}
|
|
|
|
// * -> Valueless
|
|
if (toType->IsValuelessType())
|
|
return mBfIRBuilder->GetFakeVal();
|
|
|
|
// void* -> intptr
|
|
if ((typedVal.mType->IsPointer()) && (toType->IsIntPtr()))
|
|
{
|
|
//TODO: Put back
|
|
/*if ((!typedVal.mType->GetUnderlyingType()->IsVoid()) && ((castFlags & BfCastFlags_FromCompiler) == 0))
|
|
{
|
|
Fail(StrFormat("Unable to cast direct from '%s' to '%s', consider casting to void* first", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
|
|
}*/
|
|
|
|
auto toPrimitive = (BfPrimitiveType*)toType;
|
|
return mBfIRBuilder->CreatePtrToInt(typedVal.mValue, toPrimitive->mTypeDef->mTypeCode);
|
|
}
|
|
|
|
// intptr -> void*
|
|
if ((typedVal.mType->IsIntPtr()) && (toType->IsPointer()))
|
|
{
|
|
//TODO: Put back
|
|
/*if ((!toType->GetUnderlyingType()->IsVoid()) && ((castFlags & BfCastFlags_FromCompiler) == 0))
|
|
{
|
|
Fail(StrFormat("Unable to cast direct from '%s' to '%s', consider casting to void* first", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
|
|
}*/
|
|
|
|
return mBfIRBuilder->CreateIntToPtr(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
}
|
|
|
|
// * <-> Var
|
|
if ((typedVal.mType->IsVar()) || (toType->IsVar()))
|
|
{
|
|
BF_ASSERT((mCurMethodInstance->mIsUnspecialized) || (mCurMethodState->mClosureState != NULL));
|
|
return GetDefaultValue(toType);
|
|
}
|
|
|
|
// Generic param -> *
|
|
if ((typedVal.mType->IsGenericParam()) && (!toType->IsGenericParam()))
|
|
{
|
|
if (toType == mContext->mBfObjectType)
|
|
{
|
|
/*auto resolvedType = ResolveGenericType(typedVal.mType);
|
|
if (!resolvedType->IsGenericParam())
|
|
return CastToValue(srcNode, BfTypedValue(typedVal.mValue, resolvedType), toType, castFlags, silentFail);
|
|
return typedVal.mValue;*/
|
|
// Always allow casting to generic
|
|
return typedVal.mValue;
|
|
}
|
|
|
|
// For these casts, it's just important we get *A* value to work with here,
|
|
// as this is just use for unspecialized parsing. We don't use the generated code
|
|
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)typedVal.mType);
|
|
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
|
|
{
|
|
return typedVal.mValue;
|
|
}
|
|
if (toType->IsInterface())
|
|
{
|
|
for (auto iface : genericParamInst->mInterfaceConstraints)
|
|
if (TypeIsSubTypeOf(iface, toType->ToTypeInstance()))
|
|
return GetDefaultValue(toType);
|
|
}
|
|
|
|
if (genericParamInst->mTypeConstraint != NULL)
|
|
{
|
|
auto constraintTypeInst = genericParamInst->mTypeConstraint->ToTypeInstance();
|
|
if ((constraintTypeInst != NULL) && (constraintTypeInst->mTypeDef == mCompiler->mEnumTypeDef))
|
|
{
|
|
// Enum->int
|
|
if (toType->IsInteger())
|
|
return GetDefaultValue(toType);
|
|
}
|
|
|
|
auto defaultFromValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint);
|
|
auto result = CastToValue(srcNode, defaultFromValue, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
|
|
|
|
if (result)
|
|
{
|
|
if ((genericParamInst->mTypeConstraint->IsDelegate()) && (toType->IsDelegate()))
|
|
{
|
|
// Don't allow cast when we are constrained by a delegate type, because BfMethodRefs can match and we require an actual alloc
|
|
Fail(StrFormat("Unable to cast '%s' to '%s' because delegate constraints allow valueless direct method references", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
|
|
return BfIRValue();
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Generic constrained with class or pointer type -> void*
|
|
if (toType->IsVoidPtr())
|
|
{
|
|
if ((genericParamInst->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_StructPtr)) ||
|
|
(genericParamInst->mTypeConstraint != NULL) &&
|
|
((genericParamInst->mTypeConstraint->IsPointer()) || (genericParamInst->mTypeConstraint->IsObjectOrInterface())))
|
|
{
|
|
return GetDefaultValue(toType);
|
|
}
|
|
}
|
|
}
|
|
|
|
// * -> Generic param
|
|
if (toType->IsGenericParam())
|
|
{
|
|
if (explicitCast)
|
|
{
|
|
// Either an upcast or an unbox
|
|
if ((typedVal.mType == mContext->mBfObjectType) || (typedVal.mType->IsInterface()))
|
|
{
|
|
return GetDefaultValue(toType);
|
|
}
|
|
}
|
|
|
|
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)toType);
|
|
if (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var)
|
|
return GetDefaultValue(toType);
|
|
if (typedVal.mType->IsNull())
|
|
{
|
|
bool allowCast = (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) || (genericParamInst->mGenericParamFlags & BfGenericParamFlag_StructPtr);
|
|
if ((!allowCast) && (genericParamInst->mTypeConstraint != NULL))
|
|
allowCast = genericParamInst->mTypeConstraint->IsObject() || genericParamInst->mTypeConstraint->IsPointer();
|
|
if (allowCast)
|
|
return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
|
|
if (genericParamInst->mTypeConstraint != NULL)
|
|
{
|
|
auto castedVal = CastToValue(srcNode, typedVal, genericParamInst->mTypeConstraint, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
|
|
if (castedVal)
|
|
return castedVal;
|
|
|
|
//TODO: WHy did we do 'GetDefaultValue'? This messes up setting up method param defaults, which is important for inferring const generic params
|
|
//return GetDefaultValue(toType);
|
|
}
|
|
}
|
|
|
|
// ObjectInst|IFace -> object|IFace
|
|
if ((typedVal.mType->IsObject() || (typedVal.mType->IsInterface())) && ((toType->IsObject() || (toType->IsInterface()))))
|
|
{
|
|
bool allowCast = false;
|
|
|
|
auto fromTypeInstance = typedVal.mType->ToTypeInstance();
|
|
auto toTypeInstance = toType->ToTypeInstance();
|
|
|
|
if (TypeIsSubTypeOf(fromTypeInstance, toTypeInstance))
|
|
allowCast = true;
|
|
else if ((explicitCast) &&
|
|
((toType->IsInterface()) || (TypeIsSubTypeOf(toTypeInstance, fromTypeInstance))))
|
|
{
|
|
if (toType->IsObjectOrInterface())
|
|
{
|
|
if ((castFlags & BfCastFlags_Unchecked) == 0)
|
|
EmitDynamicCastCheck(typedVal, toType, true);
|
|
}
|
|
allowCast = true;
|
|
}
|
|
|
|
if (allowCast)
|
|
return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
|
|
// MethodRef -> Function
|
|
if ((typedVal.mType->IsMethodRef()) && (toType->IsFunction()))
|
|
{
|
|
BfMethodInstance* methodInstance = ((BfMethodRefType*)typedVal.mType)->mMethodRef;
|
|
auto result = CastToFunction(srcNode, methodInstance, toType, castFlags);
|
|
if (result)
|
|
return result;
|
|
}
|
|
|
|
// concrete IFace -> object|IFace
|
|
if ((typedVal.mType->IsConcreteInterfaceType()) && ((toType->IsObject() || (toType->IsInterface()))))
|
|
{
|
|
auto concreteInterfaceType = (BfConcreteInterfaceType*)typedVal.mType;
|
|
if ((toType->IsObject()) || (concreteInterfaceType->mInterface == toType))
|
|
return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
|
|
// IFace -> object
|
|
if ((typedVal.mType->IsInterface()) && (toType == mContext->mBfObjectType))
|
|
return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
|
|
// * -> Pointer
|
|
if (toType->IsPointer())
|
|
{
|
|
// Ptr -> Ptr
|
|
if (typedVal.mType->IsPointer())
|
|
{
|
|
bool allowCast = explicitCast;
|
|
auto fromPointerType = (BfPointerType*)typedVal.mType;
|
|
auto toPointerType = (BfPointerType*)toType;
|
|
|
|
auto fromUnderlying = fromPointerType->mElementType;
|
|
auto toUnderlying = toPointerType->mElementType;
|
|
|
|
// Allow cast from T[size]* to T* implicitly
|
|
// And from T* to T[size]* explicitly
|
|
while (fromUnderlying->IsSizedArray())
|
|
fromUnderlying = fromUnderlying->GetUnderlyingType();
|
|
while ((toUnderlying->IsSizedArray()) && (explicitCast))
|
|
toUnderlying = toUnderlying->GetUnderlyingType();
|
|
|
|
if ((fromUnderlying == toUnderlying) ||
|
|
(TypeIsSubTypeOf(fromUnderlying->ToTypeInstance(), toUnderlying->ToTypeInstance())) ||
|
|
(toUnderlying->IsVoid()))
|
|
allowCast = true;
|
|
|
|
if (allowCast)
|
|
return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
|
|
}
|
|
else if (typedVal.mType->IsObject())
|
|
{
|
|
// ???
|
|
}
|
|
/*else if (typedVal.mType->IsSizedArray())
|
|
{
|
|
if (typedVal.IsAddr())
|
|
{
|
|
BfSizedArrayType* arrayType = (BfSizedArrayType*)typedVal.mType;
|
|
auto ptrType = CreatePointerType(arrayType->mElementType);
|
|
BfTypedValue returnPointer(mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(ptrType)), ptrType);
|
|
return CastToValue(srcNode, returnPointer, toType, castFlags, silentFail);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
// Boxing?
|
|
bool mayBeBox = false;
|
|
if (((typedVal.mType->IsValueType()) || (typedVal.mType->IsPointer()) || (typedVal.mType->IsValuelessType())) &&
|
|
((toType->IsInterface()) || (toType == mContext->mBfObjectType)))
|
|
{
|
|
// Make sure there's no conversion operator before we box
|
|
if ((!typedVal.mType->IsRef()) && (!typedVal.mType->IsRetTypeType()))
|
|
mayBeBox = true;
|
|
}
|
|
|
|
//TODO: the IsGenericParam is not valid - why did we have that? The generic param could be a struct for example...
|
|
if ((explicitCast) && ((typedVal.mType->IsInterface()) || (typedVal.mType == mContext->mBfObjectType) /*|| (typedVal.mType->IsGenericParam())*/) &&
|
|
((toType->IsValueType()) || (toType->IsPointer())))
|
|
{
|
|
if (toType->IsValuelessType())
|
|
{
|
|
return mBfIRBuilder->GetFakeVal();
|
|
}
|
|
|
|
// Unbox!
|
|
if ((castFlags & BfCastFlags_Unchecked) == 0)
|
|
{
|
|
EmitDynamicCastCheck(typedVal, toType, false);
|
|
EmitObjectAccessCheck(typedVal);
|
|
}
|
|
|
|
if (toType->IsNullable())
|
|
{
|
|
auto toTypeInst = toType->ToTypeInstance();
|
|
int valueIdx = toTypeInst->mFieldInstances[0].mDataIdx;
|
|
int hasValueIdx = toTypeInst->mFieldInstances[1].mDataIdx;
|
|
|
|
typedVal = MakeAddressable(typedVal);
|
|
|
|
auto elementType = toType->GetUnderlyingType();
|
|
auto ptrElementType = CreatePointerType(elementType);
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
|
|
auto allocaInst = CreateAlloca(toType, true, "unboxN");
|
|
|
|
auto prevBB = mBfIRBuilder->GetInsertBlock();
|
|
auto nullBB = mBfIRBuilder->CreateBlock("unboxN.null");
|
|
auto notNullBB = mBfIRBuilder->CreateBlock("unboxN.notNull");
|
|
auto endBB = mBfIRBuilder->CreateBlock("unboxN.end");
|
|
|
|
auto isNull = mBfIRBuilder->CreateIsNull(typedVal.mValue);
|
|
|
|
mBfIRBuilder->CreateCondBr(isNull, nullBB, notNullBB);
|
|
|
|
int dataIdx = toTypeInst->mFieldInstances[1].mDataIdx;
|
|
|
|
mBfIRBuilder->AddBlock(nullBB);
|
|
mBfIRBuilder->SetInsertPoint(nullBB);
|
|
auto hasValueAddr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, hasValueIdx); // has_value
|
|
mBfIRBuilder->CreateStore(GetConstValue(0, boolType), hasValueAddr);
|
|
auto nullableValueAddr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, valueIdx); // value
|
|
auto nullableValueBits = mBfIRBuilder->CreateBitCast(nullableValueAddr, mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr));
|
|
mBfIRBuilder->CreateMemSet(nullableValueBits, GetConstValue(0, GetPrimitiveType(BfTypeCode_Int8)), GetConstValue(elementType->mSize), elementType->mAlign);
|
|
mBfIRBuilder->CreateBr(endBB);
|
|
|
|
mBfIRBuilder->AddBlock(notNullBB);
|
|
mBfIRBuilder->SetInsertPoint(notNullBB);
|
|
hasValueAddr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, hasValueIdx); // has_value
|
|
mBfIRBuilder->CreateStore(GetConstValue(1, boolType), hasValueAddr);
|
|
nullableValueAddr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, valueIdx); // value
|
|
auto srcObjBits = mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(ptrElementType));
|
|
auto boxedValueAddr = mBfIRBuilder->CreateInBoundsGEP(srcObjBits, 1); // Skip over vdata
|
|
auto boxedValue = mBfIRBuilder->CreateLoad(boxedValueAddr);
|
|
mBfIRBuilder->CreateStore(boxedValue, nullableValueAddr);
|
|
mBfIRBuilder->CreateBr(endBB);
|
|
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
|
|
if (resultFlags != NULL)
|
|
*resultFlags = (BfCastResultFlags)(BfCastResultFlags_IsAddr | BfCastResultFlags_IsTemp);
|
|
return allocaInst;
|
|
}
|
|
|
|
auto boxedType = CreateBoxedType(toType);
|
|
mBfIRBuilder->PopulateType(boxedType);
|
|
AddDependency(boxedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields);
|
|
auto boxedObj = mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(boxedType));
|
|
auto valPtr = mBfIRBuilder->CreateInBoundsGEP(boxedObj, 0, 1);
|
|
if ((toType->IsPrimitiveType()) || (toType->IsTypedPrimitive()) || (toType->IsPointer()) || (toType->IsSizedArray()) || (toType->IsMethodRef()))
|
|
{
|
|
valPtr = mBfIRBuilder->CreateBitCast(valPtr, mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapType(toType)));
|
|
}
|
|
if ((toType->IsComposite()) && (resultFlags != NULL))
|
|
{
|
|
*resultFlags = BfCastResultFlags_IsAddr;
|
|
return valPtr;
|
|
}
|
|
else
|
|
return mBfIRBuilder->CreateLoad(valPtr, false);
|
|
}
|
|
|
|
// Null -> Nullable<T>
|
|
if ((typedVal.mType->IsNull()) && (toType->IsNullable()))
|
|
{
|
|
if ((castFlags & BfCastFlags_PreferAddr) != 0)
|
|
{
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
auto toTypeInst = toType->ToTypeInstance();
|
|
int hasValueIdx = toTypeInst->mFieldInstances[1].mDataIdx;
|
|
|
|
auto allocaInst = CreateAlloca(toType);
|
|
auto hasValueAddr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, hasValueIdx); // has_value
|
|
mBfIRBuilder->CreateStore(GetConstValue(0, boolType), hasValueAddr);
|
|
|
|
auto typedValue = BfTypedValue(allocaInst, toType, true);
|
|
if (resultFlags != NULL)
|
|
*resultFlags = (BfCastResultFlags)(BfCastResultFlags_IsAddr | BfCastResultFlags_IsTemp);
|
|
return allocaInst;
|
|
}
|
|
|
|
auto zeroNullable = mBfIRBuilder->CreateConstStructZero(mBfIRBuilder->MapType(toType));
|
|
return zeroNullable;
|
|
}
|
|
|
|
// Nullable<A> -> Nullable<B>
|
|
if ((typedVal.mType->IsNullable()) && (toType->IsNullable()))
|
|
{
|
|
auto fromNullableType = (BfGenericTypeInstance*)typedVal.mType;
|
|
auto toNullableType = (BfGenericTypeInstance*)toType;
|
|
|
|
BfIRValue srcPtr = typedVal.mValue;
|
|
if (!typedVal.IsAddr())
|
|
{
|
|
auto srcAlloca = CreateAllocaInst(fromNullableType);
|
|
mBfIRBuilder->CreateStore(typedVal.mValue, srcAlloca);
|
|
srcPtr = srcAlloca;
|
|
}
|
|
|
|
auto srcAddr = mBfIRBuilder->CreateInBoundsGEP(srcPtr, 0, 1); // mValue
|
|
auto srcVal = mBfIRBuilder->CreateLoad(srcAddr);
|
|
|
|
auto toVal = CastToValue(srcNode, BfTypedValue(srcVal, fromNullableType->mTypeGenericArguments[0]), toNullableType->mTypeGenericArguments[0]);
|
|
if (!toVal)
|
|
return BfIRValue();
|
|
|
|
auto allocaInst = CreateAllocaInst(toNullableType);
|
|
|
|
auto destAddr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, 1); // mValue
|
|
mBfIRBuilder->CreateStore(toVal, destAddr);
|
|
|
|
srcAddr = mBfIRBuilder->CreateInBoundsGEP(srcPtr, 0, 2); // mHasValue
|
|
srcVal = mBfIRBuilder->CreateLoad(srcAddr);
|
|
destAddr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, 2); // mHasValue
|
|
mBfIRBuilder->CreateStore(srcVal, destAddr);
|
|
|
|
if (resultFlags != NULL)
|
|
*resultFlags = (BfCastResultFlags)(BfCastResultFlags_IsAddr | BfCastResultFlags_IsTemp);
|
|
return allocaInst;
|
|
}
|
|
|
|
// Tuple -> Tuple
|
|
if ((typedVal.mType->IsTuple()) && (toType->IsTuple()))
|
|
{
|
|
auto fromTupleType = (BfTupleType*)typedVal.mType;
|
|
auto toTupleType = (BfTupleType*)toType;
|
|
if (fromTupleType->mFieldInstances.size() == toTupleType->mFieldInstances.size())
|
|
{
|
|
typedVal = LoadValue(typedVal);
|
|
|
|
BfIRValue curTupleValue = mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(toTupleType));
|
|
for (int valueIdx = 0; valueIdx < (int)fromTupleType->mFieldInstances.size(); valueIdx++)
|
|
{
|
|
BfFieldInstance* fromFieldInstance = &fromTupleType->mFieldInstances[valueIdx];
|
|
BfFieldInstance* toFieldInstance = &toTupleType->mFieldInstances[valueIdx];
|
|
|
|
if (!explicitCast)
|
|
{
|
|
BfFieldDef* fromFieldDef = fromFieldInstance->GetFieldDef();
|
|
BfFieldDef* toFieldDef = toFieldInstance->GetFieldDef();
|
|
|
|
// Either the names have to match or one has to be unnamed
|
|
if ((!fromFieldDef->IsUnnamedTupleField()) && (!toFieldDef->IsUnnamedTupleField()) &&
|
|
(fromFieldDef->mName != toFieldDef->mName))
|
|
{
|
|
curTupleValue = BfIRValue();
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto fromFieldType = fromFieldInstance->GetResolvedType();
|
|
auto toFieldType = toFieldInstance->GetResolvedType();
|
|
|
|
if (toFieldType->IsVoid())
|
|
continue; // Allow sinking to void
|
|
|
|
BfIRValue fromFieldValue;
|
|
if (fromFieldInstance->mDataIdx >= 0)
|
|
fromFieldValue = mBfIRBuilder->CreateExtractValue(typedVal.mValue, fromFieldInstance->mDataIdx);
|
|
BfIRValue toFieldValue = CastToValue(srcNode, BfTypedValue(fromFieldValue, fromFieldType), toFieldType, (BfCastFlags)(castFlags | BfCastFlags_Explicit));
|
|
if (!toFieldValue)
|
|
{
|
|
curTupleValue = BfIRValue();
|
|
break;
|
|
}
|
|
|
|
if (toFieldInstance->mDataIdx >= 0)
|
|
curTupleValue = mBfIRBuilder->CreateInsertValue(curTupleValue, toFieldValue, toFieldInstance->mDataIdx);
|
|
}
|
|
|
|
if (curTupleValue)
|
|
return curTupleValue;
|
|
}
|
|
}
|
|
|
|
// -> const <value>
|
|
if (toType->IsConstExprValue())
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (constant != NULL)
|
|
{
|
|
BfConstExprValueType* toConstExprValueType = (BfConstExprValueType*)toType;
|
|
|
|
auto variantVal = TypedValueToVariant(srcNode, typedVal);
|
|
if ((mBfIRBuilder->IsInt(variantVal.mTypeCode)) && (mBfIRBuilder->IsInt(toConstExprValueType->mValue.mTypeCode)))
|
|
{
|
|
if (variantVal.mInt64 == toConstExprValueType->mValue.mInt64)
|
|
return typedVal.mValue;
|
|
}
|
|
else if ((mBfIRBuilder->IsFloat(variantVal.mTypeCode)) && (mBfIRBuilder->IsFloat(toConstExprValueType->mValue.mTypeCode)))
|
|
{
|
|
if (variantVal.ToDouble() == toConstExprValueType->mValue.ToDouble())
|
|
return typedVal.mValue;
|
|
}
|
|
|
|
if ((castFlags & BfCastFlags_SilentFail) == 0)
|
|
{
|
|
String valStr;
|
|
VariantToString(valStr, variantVal);
|
|
Fail(StrFormat("Unable to cast '%s %s' to '%s'", TypeToString(typedVal.mType).c_str(), valStr.c_str(), TypeToString(toType).c_str()), srcNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((typedVal.mType->IsPrimitiveType()) && (toType->IsPrimitiveType()))
|
|
{
|
|
auto fromPrimType = (BfPrimitiveType*)typedVal.mType;
|
|
auto toPrimType = (BfPrimitiveType*)toType;
|
|
|
|
BfTypeCode fromTypeCode = fromPrimType->mTypeDef->mTypeCode;
|
|
BfTypeCode toTypeCode = toPrimType->mTypeDef->mTypeCode;
|
|
|
|
if (toType->IsIntegral())
|
|
{
|
|
// Allow constant ints to be implicitly casted to a smaller type if they fit
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (constant != NULL)
|
|
{
|
|
if (mBfIRBuilder->IsInt(constant->mTypeCode))
|
|
{
|
|
int64 srcVal = constant->mInt64;
|
|
|
|
if (toPrimType->IsChar())
|
|
{
|
|
if (srcVal == 0)
|
|
explicitCast = true;
|
|
}
|
|
else if ((fromPrimType->IsChar()) && (!toPrimType->IsChar()))
|
|
{
|
|
// Never allow this
|
|
}
|
|
else if ((constant->mTypeCode == BfTypeCode_UInt64) && (srcVal < 0))
|
|
{
|
|
// There's nothing that this could fit into
|
|
}
|
|
else if (toType->IsSigned())
|
|
{
|
|
int64 minVal = -(1LL << (8 * toType->mSize - 1));
|
|
int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
explicitCast = true;
|
|
}
|
|
else if (toType->mSize == 8) // ulong
|
|
{
|
|
if (srcVal >= 0)
|
|
explicitCast = true;
|
|
}
|
|
else
|
|
{
|
|
int64 minVal = 0;
|
|
int64 maxVal = (1LL << (8 * toType->mSize)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
explicitCast = true;
|
|
}
|
|
}
|
|
else if (constant->mConstType == BfConstType_Undef)
|
|
{
|
|
BF_ASSERT(mBfIRBuilder->mIgnoreWrites);
|
|
|
|
auto undefConst = (BfConstantUndef*)constant;
|
|
|
|
auto fakeVal = GetFakeTypedValue(GetPrimitiveType(undefConst->mTypeCode));
|
|
auto val = CastToValue(srcNode, fakeVal, toType, (BfCastFlags)(castFlags | BfCastFlags_Explicit));
|
|
if (val)
|
|
return val;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool allowCast = false;
|
|
switch (toTypeCode)
|
|
{
|
|
case BfTypeCode_Char16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Char8:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Int16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UInt8:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_UInt16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Int32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
allowCast = true; break;
|
|
case BfTypeCode_IntPtr:
|
|
if (mCompiler->mSystem->mPtrSize == 4)
|
|
allowCast = true;
|
|
break;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Char32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Char8:
|
|
case BfTypeCode_Char16:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_UInt32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UIntPtr:
|
|
if (mCompiler->mSystem->mPtrSize == 4)
|
|
allowCast = true;
|
|
break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Int64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_IntPtr:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_UInt64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_UIntPtr:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_IntPtr:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_Int64:
|
|
// It may seem that we want this to require an explicit cast,
|
|
// but consider the case of
|
|
// int val = Math.Max(intA, intB)
|
|
// Math.Max has an int32 and int64 override, so we want the correct one to be chosen and
|
|
// to be able to have the int64 return value implicitly used in a 64-bit build
|
|
if (mCompiler->mSystem->mPtrSize == 8)
|
|
allowCast = true;
|
|
break;
|
|
}
|
|
break;
|
|
case BfTypeCode_UIntPtr:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UInt64:
|
|
if (mCompiler->mSystem->mPtrSize == 8)
|
|
allowCast = true;
|
|
break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Single:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_IntPtr:
|
|
case BfTypeCode_IntUnknown:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_UInt64:
|
|
case BfTypeCode_UIntPtr:
|
|
case BfTypeCode_UIntUnknown:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
case BfTypeCode_Double:
|
|
switch (fromTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_IntPtr:
|
|
case BfTypeCode_IntUnknown:
|
|
allowCast = true; break;
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_UInt64:
|
|
case BfTypeCode_UIntPtr:
|
|
case BfTypeCode_UIntUnknown:
|
|
allowCast = true; break;
|
|
case BfTypeCode_Single:
|
|
allowCast = true; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (explicitCast)
|
|
{
|
|
if (((fromPrimType->IsIntegral()) || (fromPrimType->IsFloat())) &&
|
|
((toType->IsIntegral()) || (toType->IsFloat())))
|
|
allowCast = true;
|
|
}
|
|
|
|
if (allowCast)
|
|
{
|
|
return mBfIRBuilder->CreateNumericCast(typedVal.mValue, typedVal.mType->IsSigned(), toTypeCode);
|
|
}
|
|
}
|
|
|
|
// Check user-defined operators
|
|
if ((castFlags & BfCastFlags_NoConversionOperator) == 0)
|
|
{
|
|
auto fromType = typedVal.mType;
|
|
auto fromTypeInstance = typedVal.mType->ToTypeInstance();
|
|
auto toTypeInstance = toType->ToTypeInstance();
|
|
|
|
auto liftedFromType = ((fromTypeInstance != NULL) && fromTypeInstance->IsNullable()) ? fromTypeInstance->GetUnderlyingType() : NULL;
|
|
auto liftedToType = ((toTypeInstance != NULL) && toTypeInstance->IsNullable()) ? toTypeInstance->GetUnderlyingType() : NULL;
|
|
|
|
int bestFromDist = INT_MAX;
|
|
BfType* bestFromType = NULL;
|
|
int bestNegFromDist = INT_MAX;
|
|
BfType* bestNegFromType = NULL;
|
|
|
|
int bestToDist = INT_MAX;
|
|
BfType* bestToType = NULL;
|
|
int bestNegToDist = INT_MAX;
|
|
BfType* bestNegToType = NULL;
|
|
bool isAmbiguousCast = false;
|
|
|
|
BfIRValue conversionResult;
|
|
BfMethodInstance* opMethodInstance = NULL;
|
|
BfType* opMethodSrcType = NULL;
|
|
|
|
// Normal, lifted, execute
|
|
for (int pass = 0; pass < 3; pass++)
|
|
{
|
|
auto checkToType = toType;
|
|
auto checkFromType = fromType;
|
|
|
|
if (pass == 1)
|
|
{
|
|
if ((bestFromType != NULL) && (bestToType != NULL))
|
|
continue;
|
|
|
|
if (liftedFromType != NULL)
|
|
checkFromType = liftedFromType;
|
|
if (liftedToType != NULL)
|
|
checkToType = liftedToType;
|
|
}
|
|
else if (pass == 2)
|
|
{
|
|
if ((bestFromType == NULL) || (bestToType == NULL))
|
|
break;
|
|
}
|
|
|
|
BfBaseClassWalker baseClassWalker(fromType, toType, this);
|
|
|
|
while (true)
|
|
{
|
|
auto entry = baseClassWalker.Next();
|
|
auto checkInstance = entry.mTypeInstance;
|
|
if (checkInstance == NULL)
|
|
break;
|
|
|
|
for (auto operatorDef : checkInstance->mTypeDef->mOperators)
|
|
{
|
|
if (operatorDef->mOperatorDeclaration->mIsConvOperator)
|
|
{
|
|
if ((!explicitCast) && (operatorDef->mOperatorDeclaration->mExplicitToken != NULL) &&
|
|
(operatorDef->mOperatorDeclaration->mExplicitToken->GetToken() == BfToken_Explicit))
|
|
continue;
|
|
|
|
auto methodInst = GetRawMethodInstanceAtIdx(checkInstance, operatorDef->mIdx);
|
|
|
|
if (methodInst->GetParamCount() != 1)
|
|
{
|
|
BF_ASSERT(mCompiler->mPassInstance->HasFailed());
|
|
continue;
|
|
}
|
|
|
|
auto methodFromType = methodInst->GetParamType(0);
|
|
auto methodToType = methodInst->mReturnType;
|
|
|
|
if (methodFromType->IsSelf())
|
|
methodFromType = entry.mSrcType;
|
|
if (methodToType->IsSelf())
|
|
methodToType = entry.mSrcType;
|
|
|
|
// Selection pass
|
|
if (pass < 2)
|
|
{
|
|
auto methodCheckFromType = methodFromType;
|
|
auto methodCheckToType = methodToType;
|
|
if (pass == 1)
|
|
{
|
|
// Only check inner type on lifted types when we aren't checking conversions within lifted class
|
|
// This avoid some infinite conversions
|
|
if ((methodCheckFromType->IsNullable()) && (!checkInstance->IsNullable()))
|
|
methodCheckFromType = methodCheckFromType->GetUnderlyingType();
|
|
if ((methodCheckToType->IsNullable()) && (!checkInstance->IsNullable()))
|
|
methodCheckToType = methodCheckToType->GetUnderlyingType();
|
|
}
|
|
|
|
int fromDist = GetTypeDistance(methodCheckFromType, checkFromType);
|
|
if (fromDist < 0)
|
|
{
|
|
// Allow us to cast a constant int to a smaller type if it satisfies the cast operator
|
|
if ((typedVal.mValue.IsConst()) && (CanImplicitlyCast(typedVal, methodCheckFromType)))
|
|
{
|
|
fromDist = 0;
|
|
}
|
|
}
|
|
|
|
int toDist = GetTypeDistance(methodCheckToType, checkToType);
|
|
|
|
if ((fromDist == INT_MAX) || (toDist == INT_MAX))
|
|
continue;
|
|
|
|
if (((fromDist >= 0) && (toDist >= 0)) || (explicitCast))
|
|
{
|
|
if ((fromDist >= 0) && (fromDist < bestFromDist))
|
|
{
|
|
bestFromDist = fromDist;
|
|
bestFromType = methodFromType;
|
|
}
|
|
|
|
if ((toDist >= 0) && (toDist < bestToDist))
|
|
{
|
|
bestToDist = toDist;
|
|
bestToType = methodToType;
|
|
}
|
|
}
|
|
|
|
if (explicitCast)
|
|
{
|
|
fromDist = abs(fromDist);
|
|
toDist = abs(toDist);
|
|
|
|
if ((fromDist >= 0) && (fromDist < bestNegFromDist))
|
|
{
|
|
bestNegFromDist = fromDist;
|
|
bestNegFromType = methodFromType;
|
|
}
|
|
|
|
if ((toDist >= 0) && (toDist < bestNegToDist))
|
|
{
|
|
bestNegToDist = toDist;
|
|
bestNegToType = methodInst->mReturnType;
|
|
}
|
|
}
|
|
}
|
|
else if (pass == 2) // Execution Pass
|
|
{
|
|
if ((methodFromType == bestFromType) && (methodToType == bestToType))
|
|
{
|
|
// Get in native module so our module doesn't get a reference to it - we may not end up calling it at all!
|
|
//BfModuleMethodInstance methodInstance = checkInstance->mModule->GetMethodInstanceAtIdx(checkInstance, operatorDef->mIdx);
|
|
BfMethodInstance* methodInstance = GetRawMethodInstanceAtIdx(checkInstance, operatorDef->mIdx);
|
|
|
|
if (opMethodInstance != NULL)
|
|
{
|
|
int prevGenericCount = GetGenericParamAndReturnCount(opMethodInstance);
|
|
int newGenericCount = GetGenericParamAndReturnCount(methodInstance);
|
|
if (newGenericCount > prevGenericCount)
|
|
{
|
|
// Prefer generic match
|
|
opMethodInstance = methodInstance;
|
|
opMethodSrcType = entry.mSrcType;
|
|
}
|
|
else if (newGenericCount < prevGenericCount)
|
|
{
|
|
// Previous was a generic match
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
isAmbiguousCast = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
opMethodInstance = methodInstance;
|
|
opMethodSrcType = entry.mSrcType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isAmbiguousCast)
|
|
break;
|
|
|
|
if (opMethodInstance != NULL)
|
|
{
|
|
if (mayBeBox)
|
|
{
|
|
if (Fail("Ambiguous cast, may be conversion operator or may be boxing request", srcNode) != NULL)
|
|
mCompiler->mPassInstance->MoreInfo("See conversion operator", opMethodInstance->mMethodDef->GetRefNode());
|
|
}
|
|
|
|
BfModuleMethodInstance moduleMethodInstance = GetMethodInstance(opMethodInstance->GetOwner(), opMethodInstance->mMethodDef, BfTypeVector());
|
|
|
|
auto methodDeclaration = moduleMethodInstance.mMethodInstance->mMethodDef->GetMethodDeclaration();
|
|
if (methodDeclaration->mBody == NULL)
|
|
{
|
|
// Handle the typedPrim<->underlying part implicitly
|
|
if (fromType->IsTypedPrimitive())
|
|
{
|
|
auto convTypedValue = BfTypedValue(typedVal.mValue, fromType->GetUnderlyingType());
|
|
return CastToValue(srcNode, convTypedValue, toType, (BfCastFlags)(castFlags & ~BfCastFlags_Explicit), NULL);
|
|
}
|
|
else if (toType->IsTypedPrimitive())
|
|
{
|
|
auto castedVal = CastToValue(srcNode, typedVal, toType->GetUnderlyingType(), (BfCastFlags)(castFlags & ~BfCastFlags_Explicit), NULL);
|
|
return castedVal;
|
|
}
|
|
|
|
// Cannot cast (was error)
|
|
return BfIRValue();
|
|
}
|
|
|
|
// Actually perform conversion
|
|
BfExprEvaluator exprEvaluator(this);
|
|
auto castedFromValue = Cast(srcNode, typedVal, bestFromType, castFlags);
|
|
if (!castedFromValue)
|
|
return BfIRValue();
|
|
|
|
SizedArray<BfIRValue, 1> args;
|
|
exprEvaluator.PushArg(castedFromValue, args);
|
|
auto operatorOut = exprEvaluator.CreateCall(moduleMethodInstance.mMethodInstance, mCompiler->IsSkippingExtraResolveChecks() ? BfIRValue() : moduleMethodInstance.mFunc, false, args);
|
|
if ((operatorOut.mType != NULL) && (operatorOut.mType->IsSelf()))
|
|
{
|
|
BF_ASSERT(IsInGeneric());
|
|
operatorOut = GetDefaultTypedValue(opMethodSrcType);
|
|
}
|
|
|
|
return CastToValue(srcNode, operatorOut, toType, castFlags, resultFlags);
|
|
}
|
|
}
|
|
|
|
if (bestFromType == NULL)
|
|
bestFromType = bestNegFromType;
|
|
if (bestToType == NULL)
|
|
bestToType = bestNegToType;
|
|
}
|
|
|
|
isAmbiguousCast |= ((bestFromType != NULL) && (bestToType != NULL));
|
|
if (isAmbiguousCast)
|
|
{
|
|
const char* errStr = "Ambiguous conversion operators for casting from '%s' to '%s'";
|
|
Fail(StrFormat(errStr, TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
|
|
return BfIRValue();
|
|
}
|
|
}
|
|
|
|
// Default typed primitive 'underlying casts' happen after checking cast operators
|
|
if (explicitCast)
|
|
{
|
|
// TypedPrimitive -> Primitive
|
|
if ((typedVal.mType->IsTypedPrimitive()) && (toType->IsPrimitiveType()))
|
|
{
|
|
auto fromTypedPrimitiveType = typedVal.mType->ToTypeInstance();
|
|
auto primTypedVal = BfTypedValue(typedVal.mValue, fromTypedPrimitiveType->mFieldInstances.back().mResolvedType, typedVal.IsAddr());
|
|
primTypedVal = LoadValue(primTypedVal);
|
|
return CastToValue(srcNode, primTypedVal, toType, castFlags);
|
|
}
|
|
|
|
// TypedPrimitive -> TypedPrimitive
|
|
if ((typedVal.mType->IsTypedPrimitive()) && (toType->IsTypedPrimitive()))
|
|
{
|
|
auto fromTypedPrimitiveType = typedVal.mType->ToTypeInstance();
|
|
auto toTypedPrimitiveType = toType->ToTypeInstance();
|
|
|
|
auto fromUnderlyingType = fromTypedPrimitiveType->GetUnderlyingType();
|
|
auto toUnderlyingType = toTypedPrimitiveType->GetUnderlyingType();
|
|
|
|
BfTypedValue underlyingTypedValue(typedVal.mValue, fromUnderlyingType, typedVal.IsAddr());
|
|
underlyingTypedValue = LoadValue(underlyingTypedValue);
|
|
BfIRValue castedToValue = CastToValue(srcNode, underlyingTypedValue, toUnderlyingType, (BfCastFlags)(castFlags | BfCastFlags_Explicit));
|
|
if (castedToValue)
|
|
return castedToValue;
|
|
}
|
|
}
|
|
else if ((typedVal.mType->IsTypedPrimitive()) && (toType->IsTypedPrimitive()))
|
|
{
|
|
if (TypeIsSubTypeOf(typedVal.mType->ToTypeInstance(), toType->ToTypeInstance()))
|
|
{
|
|
// These have the same underlying primitive type, just keep it all the same
|
|
|
|
if ((resultFlags != NULL) && (typedVal.IsAddr()))
|
|
*resultFlags = BfCastResultFlags_IsAddr;
|
|
return typedVal.mValue;
|
|
}
|
|
}
|
|
|
|
// Prim -> TypedPrimitive
|
|
if ((typedVal.mType->IsPrimitiveType()) && (toType->IsTypedPrimitive()))
|
|
{
|
|
bool allowCast = explicitCast;
|
|
if (toType == mCurTypeInstance)
|
|
allowCast = true;
|
|
if ((!allowCast) && (typedVal.mType->IsIntegral()) && (!toType->IsEnum()))
|
|
{
|
|
// Allow implicit cast of zero
|
|
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if ((constant != NULL) && (mBfIRBuilder->IsInt(constant->mTypeCode)))
|
|
{
|
|
allowCast = constant->mInt64 == 0;
|
|
}
|
|
}
|
|
if (allowCast)
|
|
{
|
|
return CastToValue(srcNode, typedVal, toType->GetUnderlyingType(), castFlags);
|
|
}
|
|
}
|
|
|
|
if (mayBeBox)
|
|
{
|
|
BfScopeData* scopeData = NULL;
|
|
if (mCurMethodState != NULL)
|
|
scopeData = mCurMethodState->mCurScope;
|
|
|
|
if ((castFlags & BfCastFlags_WarnOnBox) != 0)
|
|
{
|
|
Warn(0, "This implicit boxing will only be in scope during the constructor. Consider using a longer-term allocation such as 'box new'", srcNode);
|
|
}
|
|
|
|
auto value = BoxValue(srcNode, typedVal, toType, scopeData, (castFlags & BfCastFlags_NoBoxDtor) == 0);
|
|
if (value)
|
|
return value.mValue;
|
|
}
|
|
|
|
if ((castFlags & BfCastFlags_SilentFail) == 0)
|
|
{
|
|
const char* errStr = explicitCast ?
|
|
"Unable to cast '%s' to '%s'" :
|
|
"Unable to implicitly cast '%s' to '%s'";
|
|
auto error = Fail(StrFormat(errStr, TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
|
|
if ((error != NULL) && (srcNode != NULL))
|
|
{
|
|
if ((mCompiler->IsAutocomplete()) && (mCompiler->mResolvePassData->mAutoComplete->CheckFixit((srcNode))))
|
|
{
|
|
SetAndRestoreValue<bool> ignoreWrites(mBfIRBuilder->mIgnoreWrites);
|
|
SetAndRestoreValue<bool> ignoreErrors(mIgnoreErrors, true);
|
|
|
|
if (CastToValue(srcNode, typedVal, toType, (BfCastFlags)(BfCastFlags_Explicit | BfCastFlags_SilentFail)))
|
|
{
|
|
bool doWrap = false;
|
|
if (auto unaryOpExpr = BfNodeDynCast<BfUnaryOperatorExpression>(srcNode))
|
|
{
|
|
if ((unaryOpExpr->mOp != BfUnaryOp_AddressOf) && (unaryOpExpr->mOp != BfUnaryOp_Dereference))
|
|
doWrap = true;
|
|
}
|
|
if ((srcNode->IsA<BfCastExpression>()) ||
|
|
(srcNode->IsA<BfBinaryOperatorExpression>()))
|
|
doWrap = true;
|
|
|
|
BfParserData* parser = srcNode->GetSourceData()->ToParserData();
|
|
String typeName = TypeToString(toType);
|
|
|
|
if (doWrap)
|
|
{
|
|
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit",
|
|
StrFormat("(%s)\tcast|%s|%d|(%s)(|`%d|)", typeName.c_str(), parser->mFileName.c_str(), srcNode->GetSrcStart(), typeName.c_str(), srcNode->GetSrcLength()).c_str()));
|
|
}
|
|
else
|
|
{
|
|
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit",
|
|
StrFormat("(%s)\tcast|%s|%d|(%s)", typeName.c_str(), parser->mFileName.c_str(), srcNode->GetSrcStart(), typeName.c_str()).c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return BfIRValue();
|
|
}
|
|
|
|
BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, BfType* toType, BfCastFlags castFlags)
|
|
{
|
|
bool explicitCast = (castFlags & BfCastFlags_Explicit) != 0;
|
|
|
|
if (typedVal.mType == toType)
|
|
return typedVal;
|
|
|
|
PopulateType(toType, ((castFlags & BfCastFlags_NoConversionOperator) != 0) ? BfPopulateType_Data : BfPopulateType_DataAndMethods);
|
|
|
|
if ((castFlags & BfCastFlags_Force) != 0)
|
|
{
|
|
if (toType->IsValuelessType())
|
|
return BfTypedValue(mBfIRBuilder->GetFakeVal(), toType);
|
|
|
|
if ((typedVal.mType->IsValueType()) && (!typedVal.IsAddr()) && (typedVal.IsSplat()) && (toType->IsValueType()))
|
|
{
|
|
bool needsMemberCasting = false;
|
|
if (AreSplatsCompatible(typedVal.mType, toType, &needsMemberCasting))
|
|
{
|
|
return BfTypedValue(typedVal.mValue, toType, needsMemberCasting ? BfTypedValueKind_SplatHead_NeedsCasting : BfTypedValueKind_SplatHead);
|
|
}
|
|
}
|
|
|
|
if (typedVal.mType->IsValueType())
|
|
{
|
|
auto addrTypedValue = MakeAddressable(typedVal);
|
|
auto toPtrType = CreatePointerType(toType);
|
|
return BfTypedValue(mBfIRBuilder->CreateBitCast(addrTypedValue.mValue, mBfIRBuilder->MapType(toPtrType)), toType, BfTypedValueKind_Addr);
|
|
}
|
|
return BfTypedValue(mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType)), toType);
|
|
}
|
|
|
|
// This tuple cast may create a new type if the toType contains 'var' entries
|
|
if ((typedVal.mType->IsTuple()) && (toType->IsTuple()))
|
|
{
|
|
//auto loadedVal = LoadValue(typedVal);
|
|
PopulateType(toType);
|
|
|
|
auto fromTupleType = (BfTupleType*)typedVal.mType;
|
|
auto toTupleType = (BfTupleType*)toType;
|
|
if (fromTupleType == toTupleType)
|
|
return typedVal;
|
|
|
|
if (fromTupleType->mFieldInstances.size() == toTupleType->mFieldInstances.size())
|
|
{
|
|
BfTypeVector fieldTypes;
|
|
Array<String> fieldNames;
|
|
bool isCompatible = true;
|
|
bool isExactTypeMatch = true;
|
|
|
|
for (int fieldIdx = 0; fieldIdx < (int)fromTupleType->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
auto fromFieldInst = &fromTupleType->mFieldInstances[fieldIdx];
|
|
auto toFieldInst = &toTupleType->mFieldInstances[fieldIdx];
|
|
|
|
auto fromFieldDef = fromFieldInst->GetFieldDef();
|
|
auto toFieldDef = toFieldInst->GetFieldDef();
|
|
|
|
if (!toFieldDef->IsUnnamedTupleField())
|
|
{
|
|
if ((!fromFieldDef->IsUnnamedTupleField()) &&
|
|
(fromFieldDef->mName != toFieldDef->mName))
|
|
isCompatible = false;
|
|
fieldNames.push_back(toFieldDef->mName);
|
|
}
|
|
else
|
|
fieldNames.push_back("");
|
|
|
|
if (toFieldInst->mResolvedType->IsVar())
|
|
fieldTypes.push_back(fromFieldInst->mResolvedType);
|
|
else
|
|
{
|
|
if (fromFieldInst->mResolvedType != toFieldInst->mResolvedType)
|
|
isExactTypeMatch = false;
|
|
|
|
// The unused-token '?' comes out as 'void', so we allow that to match here. We may want to wrap that with a different fake type
|
|
// so we can give normal implicit-cast-to-void errors
|
|
if ((fromFieldInst->mResolvedType != toFieldInst->mResolvedType) && (!toFieldInst->mResolvedType->IsVoid()) &&
|
|
(!CanImplicitlyCast(GetFakeTypedValue(fromFieldInst->mResolvedType), toFieldInst->mResolvedType)))
|
|
isCompatible = false;
|
|
fieldTypes.push_back(toFieldInst->mResolvedType);
|
|
}
|
|
}
|
|
|
|
auto tupleType = CreateTupleType(fieldTypes, fieldNames);
|
|
AddDependency(tupleType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields);
|
|
mBfIRBuilder->PopulateType(tupleType);
|
|
|
|
if (isExactTypeMatch)
|
|
{
|
|
if (typedVal.mKind == BfTypedValueKind_TempAddr)
|
|
{
|
|
return BfTypedValue(mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapTypeInstPtr(tupleType)), tupleType, BfTypedValueKind_TempAddr);
|
|
}
|
|
else if (typedVal.IsAddr())
|
|
{
|
|
return BfTypedValue(mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapTypeInstPtr(tupleType)), tupleType, BfTypedValueKind_ReadOnlyAddr);
|
|
}
|
|
|
|
BfIRValue curTupleValue = CreateAlloca(tupleType);
|
|
auto loadedVal = LoadValue(typedVal);
|
|
mBfIRBuilder->CreateStore(loadedVal.mValue, mBfIRBuilder->CreateBitCast(curTupleValue, mBfIRBuilder->MapTypeInstPtr(fromTupleType)));
|
|
return BfTypedValue(curTupleValue, tupleType, BfTypedValueKind_TempAddr);
|
|
}
|
|
|
|
if (isCompatible)
|
|
{
|
|
BfIRValue curTupleValue = CreateAlloca(tupleType);
|
|
for (int fieldIdx = 0; fieldIdx < (int)fromTupleType->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
BfFieldInstance* fromFieldInstance = &fromTupleType->mFieldInstances[fieldIdx];
|
|
BfFieldInstance* toFieldInstance = &tupleType->mFieldInstances[fieldIdx];
|
|
if (toFieldInstance->mDataIdx >= 0)
|
|
{
|
|
if (fromFieldInstance->mDataIdx >= 0)
|
|
{
|
|
auto elementVal = ExtractValue(typedVal, fromFieldInstance, fromFieldInstance->mDataIdx);
|
|
elementVal = LoadValue(elementVal);
|
|
|
|
auto castedElementVal = Cast(srcNode, elementVal, toFieldInstance->GetResolvedType(), castFlags);
|
|
if (!castedElementVal)
|
|
return BfTypedValue();
|
|
auto fieldRef = mBfIRBuilder->CreateInBoundsGEP(curTupleValue, 0, toFieldInstance->mDataIdx);
|
|
mBfIRBuilder->CreateStore(castedElementVal.mValue, fieldRef);
|
|
}
|
|
else
|
|
isCompatible = false;
|
|
}
|
|
}
|
|
|
|
return BfTypedValue(curTupleValue, tupleType, BfTypedValueKind_TempAddr);
|
|
}
|
|
}
|
|
|
|
const char* errStr = explicitCast ?
|
|
"Unable to cast '%s' to '%s'" :
|
|
"Unable to implicitly cast '%s' to '%s'";
|
|
Fail(StrFormat(errStr, TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
|
|
return BfTypedValue();
|
|
}
|
|
|
|
// Struct truncate
|
|
if ((typedVal.mType->IsStruct()) && (toType->IsStruct()))
|
|
{
|
|
auto fromStructTypeInstance = typedVal.mType->ToTypeInstance();
|
|
auto toStructTypeInstance = toType->ToTypeInstance();
|
|
if (TypeIsSubTypeOf(fromStructTypeInstance, toStructTypeInstance))
|
|
{
|
|
if (typedVal.IsSplat())
|
|
{
|
|
BF_ASSERT(toStructTypeInstance->IsSplattable() || (toStructTypeInstance->mInstSize == 0));
|
|
return BfTypedValue(typedVal.mValue, toStructTypeInstance, typedVal.IsThis() ? BfTypedValueKind_ThisSplatHead : BfTypedValueKind_SplatHead);
|
|
}
|
|
|
|
if (typedVal.IsAddr())
|
|
{
|
|
BfIRValue castedIRValue;
|
|
if (typedVal.mValue.IsFake())
|
|
castedIRValue = typedVal.mValue;
|
|
else
|
|
castedIRValue = mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapTypeInstPtr(toStructTypeInstance));
|
|
|
|
return BfTypedValue(castedIRValue, toType, typedVal.IsThis() ?
|
|
(typedVal.IsReadOnly() ? BfTypedValueKind_ReadOnlyThisAddr : BfTypedValueKind_ThisAddr) :
|
|
(typedVal.IsReadOnly() ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr));
|
|
}
|
|
|
|
BfTypedValue curTypedVal = typedVal;
|
|
while (curTypedVal.mType != toStructTypeInstance)
|
|
{
|
|
mBfIRBuilder->PopulateType(curTypedVal.mType);
|
|
auto curTypeInstance = curTypedVal.mType->ToTypeInstance();
|
|
BfIRValue extractedValue;
|
|
if (toStructTypeInstance->IsValuelessType())
|
|
extractedValue = mBfIRBuilder->GetFakeVal();
|
|
else
|
|
extractedValue = mBfIRBuilder->CreateExtractValue(curTypedVal.mValue, 0);
|
|
curTypedVal = BfTypedValue(extractedValue, curTypeInstance->mBaseType, typedVal.IsThis() ?
|
|
(typedVal.IsReadOnly() ? BfTypedValueKind_ReadOnlyThisValue : BfTypedValueKind_ThisValue) :
|
|
BfTypedValueKind_Value);
|
|
}
|
|
return curTypedVal;
|
|
}
|
|
}
|
|
|
|
if ((explicitCast) && (toType->IsValuelessType()))
|
|
{
|
|
return BfTypedValue(mBfIRBuilder->GetFakeVal(), toType);
|
|
}
|
|
|
|
BfCastResultFlags castResultFlags = BfCastResultFlags_None;
|
|
auto castedValue = CastToValue(srcNode, typedVal, toType, castFlags, &castResultFlags);
|
|
if (!castedValue)
|
|
return BfTypedValue();
|
|
|
|
if ((castResultFlags & BfCastResultFlags_IsAddr) != 0)
|
|
{
|
|
if ((castResultFlags & BfCastResultFlags_IsTemp) != 0)
|
|
return BfTypedValue(castedValue, toType, BfTypedValueKind_TempAddr);
|
|
return BfTypedValue(castedValue, toType, BfTypedValueKind_Addr);
|
|
}
|
|
return BfTypedValue(castedValue, toType, BfTypedValueKind_Value);
|
|
}
|
|
|
|
BfPrimitiveType* BfModule::GetIntCoercibleType(BfType* type)
|
|
{
|
|
if (type->IsSizedArray())
|
|
{
|
|
auto sizedArray = (BfSizedArrayType*)type;
|
|
if ((sizedArray->mElementType->IsChar()) && (sizedArray->mElementType->mSize == 1))
|
|
{
|
|
auto primType = (BfPrimitiveType*)sizedArray->mElementType;
|
|
if (sizedArray->mElementCount == 1)
|
|
return GetPrimitiveType(BfTypeCode_UInt8);
|
|
if (sizedArray->mElementCount == 2)
|
|
return GetPrimitiveType(BfTypeCode_UInt16);
|
|
if (sizedArray->mElementCount == 4)
|
|
return GetPrimitiveType(BfTypeCode_UInt32);
|
|
if (sizedArray->mElementCount == 8)
|
|
return GetPrimitiveType(BfTypeCode_UInt64);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
BfTypedValue BfModule::GetIntCoercible(const BfTypedValue& typedValue)
|
|
{
|
|
auto intType = GetIntCoercibleType(typedValue.mType);
|
|
if (intType == NULL)
|
|
return BfTypedValue();
|
|
|
|
if (typedValue.mValue.IsConst())
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(typedValue.mValue);
|
|
if (constant->mConstType == BfConstType_Array)
|
|
{
|
|
uint64 intVal = 0;
|
|
|
|
auto constantArray = (BfConstantArray*)constant;
|
|
int memberIdx = 0;
|
|
for (int memberIdx = 0; memberIdx < (int)constantArray->mValues.size(); memberIdx++)
|
|
{
|
|
auto memberConstant = mBfIRBuilder->GetConstant(constantArray->mValues[memberIdx]);
|
|
if (memberConstant->mTypeCode == BfTypeCode_Char8)
|
|
{
|
|
intVal |= (uint64)(memberConstant->mUInt8) << (8 * memberIdx);
|
|
|
|
//intVal = (intVal << 8) | memberConstant->mUInt8;
|
|
}
|
|
}
|
|
|
|
return BfTypedValue(mBfIRBuilder->CreateConst(intType->mTypeDef->mTypeCode, intVal), intType);
|
|
}
|
|
}
|
|
|
|
auto convTypedValue = typedValue;
|
|
convTypedValue = MakeAddressable(convTypedValue);
|
|
auto intPtrType = CreatePointerType(intType);
|
|
|
|
auto addrVal = mBfIRBuilder->CreateBitCast(convTypedValue.mValue, mBfIRBuilder->MapType(intPtrType));
|
|
auto val = mBfIRBuilder->CreateLoad(addrVal);
|
|
|
|
return BfTypedValue(val, intType);
|
|
}
|
|
|
|
bool BfModule::TypeHasParent(BfTypeDef* checkChildTypeDef, BfTypeDef* checkParentTypeDef)
|
|
{
|
|
BfTypeDef* checkType = checkChildTypeDef;
|
|
while (checkType != NULL)
|
|
{
|
|
if (checkType == checkParentTypeDef)
|
|
return true;
|
|
checkType = checkType->mOuterType;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BfTypeDef* BfModule::FindCommonOuterType(BfTypeDef* type, BfTypeDef* type2)
|
|
{
|
|
if ((type == NULL) || (type2 == NULL))
|
|
return NULL;
|
|
int curNestDepth = std::min(type->mNestDepth, type2->mNestDepth);
|
|
while (type->mNestDepth > curNestDepth)
|
|
type = type->mOuterType;
|
|
while (type2->mNestDepth > curNestDepth)
|
|
type2 = type2->mOuterType;
|
|
|
|
while (curNestDepth >= 0)
|
|
{
|
|
if (type == type2)
|
|
return type;
|
|
type = type->mOuterType;
|
|
type2 = type2->mOuterType;
|
|
curNestDepth--;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool BfModule::TypeIsSubTypeOf(BfTypeInstance* srcType, BfTypeInstance* wantType, bool checkAccessibility)
|
|
{
|
|
if ((srcType == NULL) || (wantType == NULL))
|
|
return false;
|
|
if (srcType == wantType)
|
|
return true;
|
|
|
|
if (srcType->mDefineState < BfTypeDefineState_HasInterfaces)
|
|
{
|
|
// Type is incomplete. We don't do the IsIncomplete check here because of re-entry
|
|
// While handling 'var' resolution, we don't want to force a PopulateType reentry
|
|
// but we do have enough information for TypeIsSubTypeOf
|
|
PopulateType(srcType, BfPopulateType_Interfaces);
|
|
}
|
|
|
|
if (wantType->IsInterface())
|
|
{
|
|
BfTypeDef* checkActiveTypeDef = NULL;
|
|
bool checkAccessibility = true;
|
|
if (IsInSpecializedSection())
|
|
{
|
|
// When we have a specialized section, the generic params may not be considered "included"
|
|
// in the module that contains the generic type definition. We rely on any casting errors
|
|
// to be thrown on the unspecialized type pass. We have a similar issue with injecting mixins.
|
|
checkAccessibility = false;
|
|
}
|
|
|
|
auto checkType = srcType;
|
|
while (checkType != NULL)
|
|
{
|
|
for (auto ifaceInst : checkType->mInterfaces)
|
|
{
|
|
if (ifaceInst.mInterfaceType == wantType)
|
|
{
|
|
if (checkAccessibility)
|
|
{
|
|
if (checkActiveTypeDef == NULL)
|
|
checkActiveTypeDef = GetActiveTypeDef(NULL, false);
|
|
|
|
// We need to be lenient when validating generic constraints
|
|
// Otherwise "T<A> where T : IB" declared in a lib won't be able to match a type B in a using project 'C',
|
|
// because this check will see the lib using 'C', which it won't consider visible
|
|
if ((checkActiveTypeDef != NULL) &&
|
|
((mCurMethodInstance != NULL) && (mContext->mCurTypeState != NULL) && (!mContext->mCurTypeState->mBuildingGenericParams)))
|
|
{
|
|
if ((!srcType->IsTypeMemberAccessible(ifaceInst.mDeclaringType, checkActiveTypeDef)) ||
|
|
(!srcType->IsTypeMemberIncluded(ifaceInst.mDeclaringType, checkActiveTypeDef, this)))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
checkType = checkType->mBaseType;
|
|
if ((checkType != NULL) && (checkType->mDefineState < BfTypeDefineState_HasInterfaces))
|
|
{
|
|
PopulateType(checkType, BfPopulateType_Interfaces);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
auto srcBaseType = srcType->mBaseType;
|
|
return TypeIsSubTypeOf(srcBaseType, wantType);
|
|
}
|
|
|
|
// Positive value means that toType encompasses fromType, negative value means toType is encompassed by formType
|
|
// INT_MAX means the types are not related
|
|
int BfModule::GetTypeDistance(BfType* fromType, BfType* toType)
|
|
{
|
|
if (fromType == toType)
|
|
return 0;
|
|
|
|
if (fromType->IsPrimitiveType())
|
|
{
|
|
if (!toType->IsPrimitiveType())
|
|
return INT_MAX;
|
|
auto fromPrimType = (BfPrimitiveType*)fromType;
|
|
auto toPrimType = (BfPrimitiveType*)toType;
|
|
|
|
if ((fromPrimType->IsIntegral()) && (toPrimType->IsIntegral()))
|
|
{
|
|
int fromBitSize = fromPrimType->mSize * 8;
|
|
if (fromPrimType->IsSigned())
|
|
fromBitSize--;
|
|
int toBitSize = toPrimType->mSize * 8;
|
|
if (toPrimType->IsSigned())
|
|
toBitSize--;
|
|
return fromBitSize - toBitSize;
|
|
}
|
|
|
|
if ((fromPrimType->IsFloat()) && (toPrimType->IsFloat()))
|
|
{
|
|
return (fromPrimType->mSize * 8) - (toPrimType->mSize * 8);
|
|
}
|
|
|
|
if (((fromPrimType->IsIntegral()) || (fromPrimType->IsFloat())) &&
|
|
((toPrimType->IsIntegral()) || (toPrimType->IsFloat())))
|
|
{
|
|
int sizeDiff = (fromPrimType->mSize * 8) - (toPrimType->mSize * 8);
|
|
if (sizeDiff < 0)
|
|
sizeDiff--;
|
|
else
|
|
sizeDiff++;
|
|
return sizeDiff;
|
|
}
|
|
return INT_MAX;
|
|
}
|
|
|
|
auto fromTypeInstance = fromType->ToTypeInstance();
|
|
auto toTypeInstance = toType->ToTypeInstance();
|
|
|
|
if ((fromTypeInstance != NULL) != (toTypeInstance != NULL))
|
|
return INT_MAX; // Ever valid?
|
|
|
|
if ((fromTypeInstance != NULL) && (toTypeInstance != NULL))
|
|
{
|
|
if ((fromTypeInstance->IsNullable()) && (toTypeInstance->IsNullable()))
|
|
return GetTypeDistance(fromTypeInstance->GetUnderlyingType(), toTypeInstance->GetUnderlyingType());
|
|
|
|
int inheritDistance = toTypeInstance->mInheritDepth - fromTypeInstance->mInheritDepth;
|
|
auto mostSpecificInstance = (inheritDistance < 0) ? fromTypeInstance : toTypeInstance;
|
|
auto leastSpecificInstance = (inheritDistance < 0) ? toTypeInstance : fromTypeInstance;
|
|
|
|
while (mostSpecificInstance != NULL)
|
|
{
|
|
if (mostSpecificInstance == leastSpecificInstance)
|
|
return inheritDistance;
|
|
mostSpecificInstance = mostSpecificInstance->mBaseType;
|
|
}
|
|
}
|
|
|
|
return INT_MAX;
|
|
}
|
|
|
|
bool BfModule::IsTypeMoreSpecific(BfType* leftType, BfType* rightType)
|
|
{
|
|
if (leftType->IsGenericTypeInstance())
|
|
{
|
|
if (!rightType->IsGenericTypeInstance())
|
|
return true;
|
|
|
|
auto leftGenericType = (BfGenericTypeInstance*)leftType;
|
|
auto rightGenericType = (BfGenericTypeInstance*)rightType;
|
|
|
|
if (leftGenericType->mTypeDef != rightGenericType->mTypeDef)
|
|
return false;
|
|
|
|
bool isBetter = false;
|
|
bool isWorse = false;
|
|
for (int argIdx = 0; argIdx < (int)leftGenericType->mTypeGenericArguments.size(); argIdx++)
|
|
{
|
|
if (IsTypeMoreSpecific(leftGenericType->mTypeGenericArguments[argIdx], rightGenericType->mTypeGenericArguments[argIdx]))
|
|
isBetter = true;
|
|
if (IsTypeMoreSpecific(rightGenericType->mTypeGenericArguments[argIdx], leftGenericType->mTypeGenericArguments[argIdx]))
|
|
isWorse = true;
|
|
}
|
|
|
|
return (isBetter) && (!isWorse);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
StringT<128> BfModule::TypeToString(BfType* resolvedType)
|
|
{
|
|
BfTypeNameFlags flags = BfTypeNameFlags_None;
|
|
if ((mCurTypeInstance == NULL) || (!mCurTypeInstance->IsUnspecializedTypeVariation()))
|
|
flags = BfTypeNameFlag_ResolveGenericParamNames;
|
|
|
|
StringT<128> str;
|
|
DoTypeToString(str, resolvedType, flags);
|
|
return str;
|
|
}
|
|
|
|
StringT<128> BfModule::TypeToString(BfType* resolvedType, BfTypeNameFlags typeNameFlags, Array<String>* genericMethodParamNameOverrides)
|
|
{
|
|
StringT<128> str;
|
|
DoTypeToString(str, resolvedType, typeNameFlags, genericMethodParamNameOverrides);
|
|
return str;
|
|
}
|
|
|
|
void BfModule::VariantToString(StringImpl& str, const BfVariant& variant)
|
|
{
|
|
switch (variant.mTypeCode)
|
|
{
|
|
case BfTypeCode_Char8:
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_Int32:
|
|
str += StrFormat("%d", variant.mInt32);
|
|
break;
|
|
case BfTypeCode_UInt32:
|
|
str += StrFormat("%lu", variant.mUInt32);
|
|
break;
|
|
case BfTypeCode_Int64:
|
|
str += StrFormat("%lld", variant.mInt64);
|
|
break;
|
|
case BfTypeCode_UInt64:
|
|
str += StrFormat("%llu", variant.mInt64);
|
|
break;
|
|
case BfTypeCode_Single:
|
|
{
|
|
char cstr[64];
|
|
ExactMinimalFloatToStr(variant.mSingle, cstr);
|
|
str += cstr;
|
|
if (strchr(cstr, '.') == NULL)
|
|
str += ".0f";
|
|
else
|
|
str += "f";
|
|
}
|
|
break;
|
|
case BfTypeCode_Double:
|
|
{
|
|
char cstr[64];
|
|
ExactMinimalDoubleToStr(variant.mDouble, cstr);
|
|
str += cstr;
|
|
if (strchr(cstr, '.') == NULL)
|
|
str += ".0";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameFlags typeNameFlags, Array<String>* genericMethodNameOverrides)
|
|
{
|
|
BP_ZONE("BfModule::DoTypeToString");
|
|
|
|
// This is clearly wrong. If we pass in @T0 from a generic type, this would immediately disable the ability to get its name
|
|
/*if (resolvedType->IsUnspecializedType())
|
|
typeNameFlags = (BfTypeNameFlags)(typeNameFlags & ~BfTypeNameFlag_ResolveGenericParamNames);*/
|
|
|
|
if (resolvedType->IsBoxed())
|
|
{
|
|
auto boxedeType = (BfBoxedType*)resolvedType;
|
|
str += "boxed ";
|
|
DoTypeToString(str, boxedeType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
return;
|
|
}
|
|
else if ((resolvedType->IsArray()) && ((typeNameFlags & BfTypeNameFlag_UseArrayImplType) == 0))
|
|
{
|
|
auto arrayType = (BfArrayType*)resolvedType;
|
|
DoTypeToString(str, arrayType->mTypeGenericArguments[0], typeNameFlags, genericMethodNameOverrides);
|
|
str += "[";
|
|
for (int i = 1; i < arrayType->mDimensions; i++)
|
|
str += ",";
|
|
str += "]";
|
|
return;
|
|
}
|
|
else if ((resolvedType->IsNullable()) && (!resolvedType->IsUnspecializedType()))
|
|
{
|
|
auto genericType = (BfGenericTypeInstance*)resolvedType;
|
|
auto elementType = genericType->mTypeGenericArguments[0];
|
|
DoTypeToString(str, elementType, typeNameFlags, genericMethodNameOverrides);
|
|
str += "?";
|
|
return;
|
|
}
|
|
else if (resolvedType->IsTuple())
|
|
{
|
|
BfTupleType* tupleType = (BfTupleType*)resolvedType;
|
|
|
|
str += "(";
|
|
for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
if (fieldIdx > 0)
|
|
str += ", ";
|
|
|
|
BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[fieldIdx];
|
|
BfFieldDef* fieldDef = fieldInstance->GetFieldDef();
|
|
DoTypeToString(str, fieldInstance->GetResolvedType(), (BfTypeNameFlags)(typeNameFlags & ~(BfTypeNameFlag_OmitNamespace | BfTypeNameFlag_OmitOuterType)), genericMethodNameOverrides);
|
|
|
|
char c = fieldDef->mName[0];
|
|
if ((c < '0') || (c > '9'))
|
|
{
|
|
str += " ";
|
|
str += fieldDef->mName;
|
|
}
|
|
}
|
|
str += ")";
|
|
return;
|
|
}
|
|
else if (resolvedType->IsDelegateFromTypeRef() || resolvedType->IsFunctionFromTypeRef())
|
|
{
|
|
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance);
|
|
|
|
auto delegateType = (BfDelegateType*)resolvedType;
|
|
|
|
if (mCurTypeInstance == delegateType)
|
|
{
|
|
// Don't try to use ourselves for generic param resolution. This should only happen for debug printings from
|
|
// within InitType and such, not actual user-facing display
|
|
mCurTypeInstance = NULL;
|
|
}
|
|
|
|
auto methodDef = delegateType->mTypeDef->mMethods[0];
|
|
|
|
if (resolvedType->IsDelegateFromTypeRef())
|
|
str += "delegate ";
|
|
else
|
|
str += "function ";
|
|
DoTypeToString(str, ResolveTypeRef(methodDef->mReturnTypeRef));
|
|
str += "(";
|
|
for (int paramIdx = 0; paramIdx < methodDef->mParams.size(); paramIdx++)
|
|
{
|
|
if (paramIdx > 0)
|
|
str += ", ";
|
|
auto paramDef = methodDef->mParams[paramIdx];
|
|
BfTypeNameFlags innerFlags = (BfTypeNameFlags)(typeNameFlags & ~(BfTypeNameFlag_OmitNamespace | BfTypeNameFlag_OmitOuterType));
|
|
if (delegateType->mIsUnspecializedTypeVariation)
|
|
innerFlags = (BfTypeNameFlags)(innerFlags & ~BfTypeNameFlag_ResolveGenericParamNames);
|
|
DoTypeToString(str, ResolveTypeRef(paramDef->mTypeRef), innerFlags, genericMethodNameOverrides);
|
|
str += " ";
|
|
str += paramDef->mName;
|
|
}
|
|
|
|
str += ")";
|
|
return;
|
|
}
|
|
else if (resolvedType->IsMethodRef())
|
|
{
|
|
auto methodRefType = (BfMethodRefType*)resolvedType;
|
|
BfMethodInstance* methodInstance = methodRefType->mMethodRef;
|
|
if (methodRefType->IsDeleting())
|
|
{
|
|
str += "DELETED METHODREF";
|
|
return;
|
|
}
|
|
if (methodInstance == NULL)
|
|
{
|
|
str += "method NULL";
|
|
return;
|
|
}
|
|
str += "method ";
|
|
str += MethodToString(methodInstance);
|
|
return;
|
|
}
|
|
else if (resolvedType->IsTypeInstance())
|
|
{
|
|
BfTypeInstance* typeInstance = (BfTypeInstance*)resolvedType;
|
|
|
|
auto checkTypeInst = typeInstance;
|
|
auto checkTypeDef = typeInstance->mTypeDef;
|
|
|
|
auto checkCurTypeInst = mCurTypeInstance; // Only used for ReduceName
|
|
BfTypeDef* checkCurTypeDef = NULL;
|
|
if (checkCurTypeInst != NULL)
|
|
checkCurTypeDef = checkCurTypeInst->mTypeDef;
|
|
|
|
std::function<void(BfTypeDef*, int)> _AddTypeName = [&](BfTypeDef* checkTypeDef, int depth)
|
|
{
|
|
if (depth > 0)
|
|
{
|
|
if ((typeNameFlags & BfTypeNameFlag_OmitOuterType) != 0)
|
|
return;
|
|
|
|
if ((typeNameFlags & BfTypeNameFlag_ReduceName) != 0)
|
|
{
|
|
auto outerTypeInst = GetOuterType(checkTypeInst);
|
|
if (outerTypeInst == NULL)
|
|
return;
|
|
checkTypeInst = outerTypeInst;
|
|
|
|
while (checkCurTypeDef->mNestDepth > checkTypeDef->mNestDepth)
|
|
{
|
|
checkCurTypeInst = GetOuterType(checkCurTypeInst);
|
|
checkCurTypeDef = checkCurTypeInst->mTypeDef;
|
|
}
|
|
|
|
if (TypeIsSubTypeOf(checkCurTypeInst, checkTypeInst))
|
|
return; // Found outer type
|
|
}
|
|
}
|
|
|
|
auto parentTypeDef = checkTypeDef->mOuterType;
|
|
if (parentTypeDef != NULL)
|
|
{
|
|
_AddTypeName(parentTypeDef, depth + 1);
|
|
}
|
|
|
|
if (checkTypeDef->IsGlobalsContainer())
|
|
{
|
|
if ((typeNameFlags & BfTypeNameFlag_AddGlobalContainerName) != 0)
|
|
{
|
|
str += "G$";
|
|
str += checkTypeDef->mProject->mName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
checkTypeDef->mName->ToString(str);
|
|
if (!checkTypeDef->mGenericParamDefs.IsEmpty())
|
|
{
|
|
for (int ofs = 0; ofs < 3; ofs++)
|
|
{
|
|
int checkIdx = (int)str.length() - 1 - ofs;
|
|
if (checkIdx < 0)
|
|
break;
|
|
if (str[checkIdx] == '`')
|
|
{
|
|
str.RemoveToEnd(checkIdx);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (((typeNameFlags & BfTypeNameFlag_DisambiguateDups) != 0) && (checkTypeDef->mDupDetectedRevision != -1))
|
|
{
|
|
str += StrFormat("_%p", checkTypeDef);
|
|
}
|
|
}
|
|
|
|
int prevGenericParamCount = 0;
|
|
|
|
if (checkTypeDef->mOuterType != NULL)
|
|
{
|
|
prevGenericParamCount = (int)checkTypeDef->mOuterType->mGenericParamDefs.size();
|
|
}
|
|
|
|
if (resolvedType->IsGenericTypeInstance())
|
|
{
|
|
auto genericTypeInst = (BfGenericTypeInstance*)resolvedType;
|
|
if (prevGenericParamCount != (int)checkTypeDef->mGenericParamDefs.size())
|
|
{
|
|
str += '<';
|
|
for (int i = prevGenericParamCount; i < (int)checkTypeDef->mGenericParamDefs.size(); i++)
|
|
{
|
|
BfType* typeGenericArg = genericTypeInst->mTypeGenericArguments[i];
|
|
if (typeGenericArg->IsGenericParam())
|
|
{
|
|
if ((typeNameFlags & BfTypeNameFlag_ResolveGenericParamNames) == 0)
|
|
{
|
|
// We don't want the param names, just the commas (this is an unspecialized type reference)
|
|
if (i > prevGenericParamCount)
|
|
str += ',';
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (i > prevGenericParamCount)
|
|
str += ", ";
|
|
DoTypeToString(str, typeGenericArg, (BfTypeNameFlags)(typeNameFlags & ~(BfTypeNameFlag_OmitNamespace | BfTypeNameFlag_OmitOuterType)), genericMethodNameOverrides);
|
|
}
|
|
str += '>';
|
|
}
|
|
}
|
|
|
|
if (depth > 0)
|
|
str += '.';
|
|
};
|
|
|
|
bool omitNamespace = (typeNameFlags & BfTypeNameFlag_OmitNamespace) != 0;
|
|
if ((typeNameFlags & BfTypeNameFlag_ReduceName) != 0)
|
|
{
|
|
for (auto& checkNamespace : mCurTypeInstance->mTypeDef->mNamespaceSearch)
|
|
{
|
|
if (checkNamespace == typeInstance->mTypeDef->mNamespace)
|
|
omitNamespace = true;
|
|
}
|
|
}
|
|
|
|
if ((!typeInstance->mTypeDef->mNamespace.IsEmpty()) && (!omitNamespace))
|
|
{
|
|
if (!typeInstance->mTypeDef->mNamespace.IsEmpty())
|
|
{
|
|
typeInstance->mTypeDef->mNamespace.ToString(str);
|
|
if (!typeInstance->mTypeDef->IsGlobalsContainer())
|
|
str += '.';
|
|
}
|
|
}
|
|
_AddTypeName(typeInstance->mTypeDef, 0);
|
|
return;
|
|
}
|
|
else if (resolvedType->IsPrimitiveType())
|
|
{
|
|
auto primitiveType = (BfPrimitiveType*)resolvedType;
|
|
if (!primitiveType->mTypeDef->mNamespace.IsEmpty())
|
|
{
|
|
primitiveType->mTypeDef->mNamespace.ToString(str);
|
|
str += '.';
|
|
primitiveType->mTypeDef->mName->ToString(str);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
primitiveType->mTypeDef->mName->ToString(str);
|
|
return;
|
|
}
|
|
}
|
|
else if (resolvedType->IsPointer())
|
|
{
|
|
auto pointerType = (BfPointerType*)resolvedType;
|
|
DoTypeToString(str, pointerType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
str += '*';
|
|
return;
|
|
}
|
|
else if (resolvedType->IsGenericParam())
|
|
{
|
|
bool doResolveGenericParams = (typeNameFlags & BfTypeNameFlag_ResolveGenericParamNames) != 0;
|
|
if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsUnspecializedTypeVariation()))
|
|
doResolveGenericParams = false;
|
|
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecializedVariation))
|
|
doResolveGenericParams = false;
|
|
|
|
auto genericParam = (BfGenericParamType*)resolvedType;
|
|
if (!doResolveGenericParams)
|
|
{
|
|
if (genericParam->mGenericParamKind == BfGenericParamKind_Method)
|
|
{
|
|
str += StrFormat("@M%d", genericParam->mGenericParamIdx);
|
|
return;
|
|
}
|
|
str += StrFormat("@T%d", genericParam->mGenericParamIdx);
|
|
return;
|
|
}
|
|
|
|
if ((genericParam->mGenericParamKind == BfGenericParamKind_Type) && (mCurTypeInstance == NULL))
|
|
{
|
|
str += StrFormat("@T%d", genericParam->mGenericParamIdx);
|
|
return;
|
|
}
|
|
|
|
if (genericParam->mGenericParamKind == BfGenericParamKind_Method)
|
|
{
|
|
if (genericMethodNameOverrides != NULL)
|
|
{
|
|
str += (*genericMethodNameOverrides)[genericParam->mGenericParamIdx];
|
|
return;
|
|
}
|
|
|
|
if (mCurMethodInstance == NULL)
|
|
{
|
|
str += StrFormat("@M%d", genericParam->mGenericParamIdx);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//TEMPORARY
|
|
if (genericParam->mGenericParamKind == BfGenericParamKind_Type)
|
|
{
|
|
auto curTypeInstance = mCurTypeInstance;
|
|
if (mCurMethodInstance != NULL)
|
|
curTypeInstance = mCurMethodInstance->mMethodInstanceGroup->mOwner;
|
|
if ((curTypeInstance == NULL) || (!curTypeInstance->IsGenericTypeInstance()))
|
|
{
|
|
str += StrFormat("@T%d", genericParam->mGenericParamIdx);
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto genericParamInstance = GetGenericParamInstance(genericParam);
|
|
str += genericParamInstance->GetGenericParamDef()->mName;
|
|
return;
|
|
}
|
|
else if (resolvedType->IsRef())
|
|
{
|
|
auto refType = (BfRefType*)resolvedType;
|
|
if (refType->mRefKind == BfRefType::RefKind_Ref)
|
|
{
|
|
str += "ref ";
|
|
DoTypeToString(str, refType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
return;
|
|
}
|
|
else if (refType->mRefKind == BfRefType::RefKind_Out)
|
|
{
|
|
str += "out ";
|
|
DoTypeToString(str, refType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
str += "mut ";
|
|
DoTypeToString(str, refType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
return;
|
|
}
|
|
}
|
|
else if (resolvedType->IsRetTypeType())
|
|
{
|
|
auto retTypeType = (BfRetTypeType*)resolvedType;
|
|
str += "rettype(";
|
|
DoTypeToString(str, retTypeType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
str += ")";
|
|
return;
|
|
}
|
|
else if (resolvedType->IsConcreteInterfaceType())
|
|
{
|
|
auto concreteTypeType = (BfConcreteInterfaceType*)resolvedType;
|
|
str += "concrete ";
|
|
DoTypeToString(str, concreteTypeType->mInterface, typeNameFlags, genericMethodNameOverrides);
|
|
return;
|
|
}
|
|
else if (resolvedType->IsUnknownSizedArray())
|
|
{
|
|
auto arrayType = (BfUnknownSizedArrayType*)resolvedType;
|
|
DoTypeToString(str, arrayType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
str += "[";
|
|
DoTypeToString(str, arrayType->mElementCountSource, typeNameFlags, genericMethodNameOverrides);
|
|
str += "]";
|
|
return;
|
|
}
|
|
else if (resolvedType->IsSizedArray())
|
|
{
|
|
auto arrayType = (BfSizedArrayType*)resolvedType;
|
|
if (arrayType->mElementCount == -1)
|
|
{
|
|
DoTypeToString(str, arrayType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
str += "[?]";
|
|
return;
|
|
}
|
|
DoTypeToString(str, arrayType->mElementType, typeNameFlags, genericMethodNameOverrides);
|
|
str += StrFormat("[%d]", arrayType->mElementCount);
|
|
return;
|
|
}
|
|
else if (resolvedType->IsConstExprValue())
|
|
{
|
|
auto constExprValueType = (BfConstExprValueType*)resolvedType;
|
|
str += "const ";
|
|
|
|
DoTypeToString(str, constExprValueType->mType, typeNameFlags, genericMethodNameOverrides);
|
|
str += " ";
|
|
|
|
VariantToString(str, constExprValueType->mValue);
|
|
|
|
return;
|
|
}
|
|
BF_FATAL("Not implemented");
|
|
str += "???";
|
|
return;
|
|
}
|
|
|