1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00
Beef/IDEHelper/Compiler/BfModule.cpp

21717 lines
734 KiB
C++
Raw Normal View History

2019-08-23 11:56:54 -07:00
//#define USE_THUNKED_MddLLOC..
#include "BeefySysLib/util/AllocDebug.h"
#pragma warning(push) // 6
#pragma warning(disable:4996)
#include "BfCompiler.h"
#include "BfSystem.h"
#include "BfParser.h"
#include "BfCodeGen.h"
#include "BfExprEvaluator.h"
#include "../Backend/BeLibManger.h"
#include <fcntl.h>
#include "BfConstResolver.h"
#include "BfMangler.h"
#include "BeefySysLib/util/PerfTimer.h"
#include "BeefySysLib/util/BeefPerf.h"
#include "BeefySysLib/util/StackHelper.h"
#include "BfSourceClassifier.h"
#include "BfAutoComplete.h"
#include "BfDemangler.h"
#include "BfResolvePass.h"
#include "BfFixits.h"
#include "BfIRCodeGen.h"
#include "BfDefBuilder.h"
#include "BfDeferEvalChecker.h"
#pragma warning(pop)
//////////////////////////////////////////////////////////////////////////
static bool gDebugStuff = false;
USING_NS_BF;
//////////////////////////////////////////////////////////////////////////
void BfLocalVariable::Init()
{
if (mResolvedType->IsValuelessType())
{
mIsAssigned = true;
return;
}
if (mIsAssigned)
return;
bool isStruct = mResolvedType->IsStruct();
if (mResolvedType->IsRef())
isStruct = mResolvedType->GetUnderlyingType()->IsStruct();
if ((isStruct) || ((mIsThis) && (mResolvedType->IsStructOrStructPtr())))
{
auto resolvedTypeRef = mResolvedType;
if ((resolvedTypeRef->IsPointer()) || (resolvedTypeRef->IsRef()))
resolvedTypeRef = resolvedTypeRef->GetUnderlyingType();
auto typeInstance = resolvedTypeRef->ToTypeInstance();
mUnassignedFieldFlags = (1 << typeInstance->mMergedFieldDataCount) - 1;
if ((mIsThis) && (typeInstance->mBaseType != NULL))
{
// Base ctor is responsible for initializing its own fields
mUnassignedFieldFlags &= ~(((int64)1 << typeInstance->mBaseType->mMergedFieldDataCount) - 1);
}
if (mUnassignedFieldFlags == 0)
mIsAssigned = true;
}
else
{
mUnassignedFieldFlags = 1;
}
}
BfLocalMethod::~BfLocalMethod()
{
BfLogSys(mSystem, "~BfLocalMethod %p\n", this);
if (mMethodDeclaration != NULL)
{
mSource->mRefCount--;
BF_ASSERT(mSource->mRefCount >= 0);
}
delete mMethodDef;
delete mMethodInstanceGroup;
}
void BfDeferredLocalAssignData::ExtendFrom(BfDeferredLocalAssignData* outerLocalAssignData, bool doChain)
{
mIsChained = doChain;
if (outerLocalAssignData == NULL)
return;
mChainedAssignData = outerLocalAssignData;
if (!doChain)
{
outerLocalAssignData->BreakExtendChain();
mAssignedLocals = outerLocalAssignData->mAssignedLocals;
}
mVarIdBarrier = outerLocalAssignData->mVarIdBarrier;
}
// The "extend chain" is broken when we have a conditional where the variable may not be defined after the block.
// IE: "a" will be defined after the following, but "b" will not necessarily be defined.
// if ((GetValue(out a)) && (GetValue(out b)) {}
void BfDeferredLocalAssignData::BreakExtendChain()
{
if (!mIsChained)
return;
mIsChained = false;
if (mChainedAssignData == NULL)
return;
mChainedAssignData->BreakExtendChain();
mAssignedLocals = mChainedAssignData->mAssignedLocals;
}
void BfDeferredLocalAssignData::SetIntersection(const BfDeferredLocalAssignData& otherLocalAssignData)
{
BreakExtendChain();
//TODO: We got rid of this case because we now set the proper assigned data when we do a return
// If one of these had a return then treat that case as if it did have an assign -- because it doesn't
// cause an UNASSIGNED value to be used
// if (mHadReturn || otherLocalAssignData.mHadReturn)
// {
// SetUnion(otherLocalAssignData);
// mHadFallthrough = mHadFallthrough && otherLocalAssignData.mHadFallthrough;
// return;
// }
for (int i = 0; i < (int)mAssignedLocals.size(); )
{
auto& local = mAssignedLocals[i];
if (!otherLocalAssignData.mAssignedLocals.Contains(local))
{
mAssignedLocals.RemoveAt(i);
}
else
i++;
}
mHadFallthrough = mHadFallthrough && otherLocalAssignData.mHadFallthrough;
}
void BfDeferredLocalAssignData::Validate() const
{
for (auto var : mAssignedLocals)
{
BF_ASSERT((uintptr)var.mLocalVar->mName.length() < 100000);
}
}
void BfDeferredLocalAssignData::SetUnion(const BfDeferredLocalAssignData& otherLocalAssignData)
{
BreakExtendChain();
Validate();
otherLocalAssignData.Validate();
auto otherItr = otherLocalAssignData.mAssignedLocals.begin();
while (otherItr != otherLocalAssignData.mAssignedLocals.end())
{
if (!mAssignedLocals.Contains(*otherItr))
mAssignedLocals.push_back(*otherItr);
++otherItr;
}
mHadFallthrough = mHadFallthrough || otherLocalAssignData.mHadFallthrough;
}
BfMethodState::~BfMethodState()
{
BF_ASSERT(mPendingNullConditional == NULL);
if (mPrevMethodState != NULL)
{
BF_ASSERT(mCurAccessId == 1);
BF_ASSERT(mCurLocalVarId <= 0);
}
for (auto local : mLocals)
delete local;
for (auto& kv : mLambdaCache)
delete kv.mValue;
}
BfMethodState* BfMethodState::GetMethodStateForLocal(BfLocalVariable* localVar)
{
auto checkMethodState = this;
while (checkMethodState != NULL)
{
if ((localVar->mLocalVarIdx < checkMethodState->mLocals.size()) &&
(checkMethodState->mLocals[localVar->mLocalVarIdx] == localVar))
return checkMethodState;
checkMethodState = checkMethodState->mPrevMethodState;
}
BF_FATAL("Failed to find method state for localvar");
return NULL;
}
void BfMethodState::LocalDefined(BfLocalVariable* localVar, int fieldIdx)
{
auto localVarMethodState = GetMethodStateForLocal(localVar);
if (localVarMethodState != this)
{
return;
}
//BF_ASSERT(localVarMethodState == this);
if (!localVar->mIsAssigned)
{
BfDeferredLocalAssignData* ifDeferredLocalAssignData = NULL;
// 'a' is always defined, but 'b' isn't:
// if (Check(out a) || Check(out b)) { } int z = a;
auto deferredLocalAssignData = mDeferredLocalAssignData;
if ((deferredLocalAssignData != NULL) &&
(deferredLocalAssignData->mIsIfCondition) &&
(!deferredLocalAssignData->mIfMayBeSkipped))
{
ifDeferredLocalAssignData = deferredLocalAssignData;
deferredLocalAssignData = deferredLocalAssignData->mChainedAssignData;
}
while ((deferredLocalAssignData != NULL) &&
((deferredLocalAssignData->mIsChained) || (deferredLocalAssignData->mIsUnconditional)))
deferredLocalAssignData = deferredLocalAssignData->mChainedAssignData;
if ((deferredLocalAssignData == NULL) || (localVar->mLocalVarId >= deferredLocalAssignData->mVarIdBarrier))
{
if (fieldIdx >= 0)
{
localVar->mUnassignedFieldFlags &= ~((int64)1 << fieldIdx);
/*if ((localVar->mResolvedTypeRef != NULL) && (localVar->mResolvedTypeRef->IsUnion()))
{
}*/
if (localVar->mUnassignedFieldFlags == 0)
localVar->mIsAssigned = true;
}
else
{
localVar->mIsAssigned = true;
}
}
else
{
BF_ASSERT(deferredLocalAssignData->mVarIdBarrier != -1);
BfAssignedLocal defineVal = {localVar, fieldIdx + 1};
auto& assignedLocals = deferredLocalAssignData->mAssignedLocals;
if (!assignedLocals.Contains(defineVal))
assignedLocals.push_back(defineVal);
if (ifDeferredLocalAssignData != NULL)
{
auto& assignedLocals = ifDeferredLocalAssignData->mAssignedLocals;
if (!assignedLocals.Contains(defineVal))
assignedLocals.push_back(defineVal);
}
}
}
localVar->mWrittenToId = GetRootMethodState()->mCurAccessId++;
}
void BfMethodState::ApplyDeferredLocalAssignData(const BfDeferredLocalAssignData& deferredLocalAssignData)
{
BF_ASSERT(&deferredLocalAssignData != mDeferredLocalAssignData);
for (auto& assignedLocal : deferredLocalAssignData.mAssignedLocals)
{
LocalDefined(assignedLocal.mLocalVar);
}
}
void BfMethodState::Reset()
{
mHeadScope.mDeferredCallEntries.DeleteAll();
}
//////////////////////////////////////////////////////////////////////////
void BfAmbiguityContext::Add(int id, BfTypeInterfaceEntry* interfaceEntry, int methodIdx, BfMethodInstance* candidateA, BfMethodInstance* candidateB)
{
Entry* entry = NULL;
if (mEntries.TryAdd(id, NULL, &entry))
{
entry->mInterfaceEntry = interfaceEntry;
entry->mMethodIdx = methodIdx;
}
if (!entry->mCandidates.Contains(candidateA))
entry->mCandidates.push_back(candidateA);
if (!entry->mCandidates.Contains(candidateB))
entry->mCandidates.push_back(candidateB);
}
void BfAmbiguityContext::Remove(int id)
{
mEntries.Remove(id);
}
void BfAmbiguityContext::Finish()
{
for (auto& entryPair : mEntries)
{
int id = entryPair.mKey;
auto entry = &entryPair.mValue;
if (id >= 0)
{
auto declMethodInstance = mTypeInstance->mVirtualMethodTable[id].mDeclaringMethod;
auto error = mModule->Fail(StrFormat("Method '%s' has ambiguous overrides", mModule->MethodToString(declMethodInstance).c_str()), declMethodInstance->mMethodDef->GetRefNode());
for (auto candidate : entry->mCandidates)
{
mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' is a candidate", mModule->MethodToString(candidate).c_str()), candidate->mMethodDef->GetRefNode());
}
}
else
{
auto iMethodInst = entry->mInterfaceEntry->mInterfaceType->mMethodInstanceGroups[entry->mMethodIdx].mDefault;
auto error = mModule->Fail(StrFormat("Interface method '%s' has ambiguous implementations", mModule->MethodToString(iMethodInst).c_str()), entry->mInterfaceEntry->mDeclaringType->GetRefNode());
for (auto candidate : entry->mCandidates)
{
mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' is a candidate", mModule->MethodToString(candidate).c_str()), candidate->mMethodDef->GetRefNode());
}
}
}
}
//////////////////////////////////////////////////////////////////////////
class HasAppendAllocVisitor : BfElementVisitor
{
public:
bool mHas;
public:
HasAppendAllocVisitor()
{
mHas = false;
}
void Visit(BfObjectCreateExpression* objCreateExpr)
{
if (auto newToken = BfNodeDynCast<BfTokenNode>(objCreateExpr->mNewNode))
{
if (newToken->GetToken() == BfToken_Append)
{
mHas = true;
}
}
}
bool HasAppendAlloc(BfAstNode* node)
{
mHas = false;
VisitChild(node);
return mHas;
}
};
class AppendAllocVisitor : public BfStructuralVisitor
{
public:
BfModule* mModule;
HasAppendAllocVisitor mHasAppendAllocVisitor;
BfTypedValue mConstAccum;
bool mFailed;
bool mIsFirstConstPass;
int mCurAppendAlign;
public:
AppendAllocVisitor()
{
mFailed = false;
mIsFirstConstPass = false;
mCurAppendAlign = 0;
}
void EmitAppendAlign(int align, int sizeMultiple = 0)
{
if (mIsFirstConstPass)
mModule->mCurMethodInstance->mAppendAllocAlign = BF_MAX((int)mModule->mCurMethodInstance->mAppendAllocAlign, align);
if (sizeMultiple == 0)
sizeMultiple = align;
if (mCurAppendAlign == 0)
mCurAppendAlign = sizeMultiple;
else
{
if (sizeMultiple % align == 0)
mCurAppendAlign = align;
else
mCurAppendAlign = sizeMultiple % align;
}
if (mConstAccum)
{
auto constant = mModule->mBfIRBuilder->GetConstant(mConstAccum.mValue);
if (constant != NULL)
mConstAccum.mValue = mModule->GetConstValue(BF_ALIGN(constant->mInt64, align));
else
BF_ASSERT(mIsFirstConstPass);
return;
}
mModule->EmitAppendAlign(align, sizeMultiple);
}
void Visit(BfAstNode* astNode) override
2019-08-23 11:56:54 -07:00
{
mModule->VisitChild(astNode);
}
void DoObjectCreate(BfObjectCreateExpression* objCreateExpr, BfVariableDeclaration* variableDecl)
{
if (auto newToken = BfNodeDynCast<BfTokenNode>(objCreateExpr->mNewNode))
{
if (newToken->GetToken() == BfToken_Append)
{
//auto variableDecl = BfNodeDynCast<BfVariableDeclaration>(objCreateExpr->mParent);
auto accumValuePtr = mModule->mCurMethodState->mRetVal.mValue;
auto intPtrType = mModule->GetPrimitiveType(BfTypeCode_IntPtr);
BfArrayType* arrayType = NULL;
bool isArrayAlloc = false;
bool isRawArrayAlloc = objCreateExpr->mStarToken != NULL;
SizedArray<BfIRValue, 2> dimLengthVals;
BfType* origResolvedTypeRef = NULL;
if (auto dotTypeRef = BfNodeDynCast<BfDotTypeReference>(objCreateExpr->mTypeRef))
{
if (variableDecl != NULL)
{
origResolvedTypeRef = mModule->ResolveTypeRef(variableDecl->mTypeRef, BfPopulateType_Data, BfResolveTypeRefFlag_NoResolveGenericParam);
if (origResolvedTypeRef->IsObject())
{
if (origResolvedTypeRef->IsArray())
{
arrayType = (BfArrayType*)origResolvedTypeRef;
origResolvedTypeRef = origResolvedTypeRef->GetUnderlyingType();
isArrayAlloc = true;
}
}
else if (origResolvedTypeRef->IsPointer())
{
origResolvedTypeRef = origResolvedTypeRef->GetUnderlyingType();
}
}
}
else if (auto arrayTypeRef = BfNodeDynCast<BfArrayTypeRef>(objCreateExpr->mTypeRef))
{
isArrayAlloc = true;
origResolvedTypeRef = mModule->ResolveTypeRef(arrayTypeRef->mElementType);
int dimensions = 1;
if (arrayTypeRef->mParams.size() != 0)
{
auto intType = mModule->ResolveTypeDef(mModule->mSystem->mTypeIntPtr);
for (auto arg : arrayTypeRef->mParams)
{
if (auto tokenNode = BfNodeDynCastExact<BfTokenNode>(arg))
{
if (tokenNode->GetToken() == BfToken_Comma)
{
if (isRawArrayAlloc)
{
mModule->Fail("Sized arrays cannot be multidimensional", tokenNode);
continue;
}
dimensions++;
continue;
}
}
auto expr = BfNodeDynCast<BfExpression>(arg);
if ((isRawArrayAlloc) && (!dimLengthVals.IsEmpty()))
{
mModule->CreateValueFromExpression(expr, intType);
continue;
}
BfTypedValue dimLength;
if (expr == NULL)
{
// Not specified
dimLengthVals.push_back(BfIRValue());
continue;
}
if (arg != NULL)
dimLength = mModule->CreateValueFromExpression(expr, intType);
if (!dimLength)
{
dimLength = mModule->GetDefaultTypedValue(intType);
}
dimLengthVals.push_back(dimLength.mValue);
}
}
if (!isRawArrayAlloc)
arrayType = mModule->CreateArrayType(origResolvedTypeRef, dimensions);
}
if (origResolvedTypeRef == NULL)
origResolvedTypeRef = mModule->ResolveTypeRef(objCreateExpr->mTypeRef, BfPopulateType_Data, BfResolveTypeRefFlag_NoResolveGenericParam);
if (origResolvedTypeRef == NULL)
{
mModule->AssertErrorState();
return;
}
bool isGenericParam = origResolvedTypeRef->IsGenericParam();
auto resolvedTypeRef = origResolvedTypeRef;
auto resultType = resolvedTypeRef;
BfIRValue sizeValue;
int curAlign = 0;
if (isArrayAlloc)
{
int dimensions = 1;
if (arrayType != NULL)
dimensions = arrayType->mDimensions;
bool sizeFailed = false;
//
{
BfAstNode* initNode = objCreateExpr;
for (int dim = 0; dim < dimensions; dim++)
{
BfAstNode* nextInitNode = NULL;
int dimSize = -1;
if (initNode == objCreateExpr)
{
dimSize = objCreateExpr->mArguments.size();
if (!objCreateExpr->mArguments.IsEmpty())
nextInitNode = objCreateExpr->mArguments[0];
}
else if (auto tupleNode = BfNodeDynCast<BfTupleExpression>(initNode))
{
dimSize = (int)tupleNode->mCommas.size() + 1;
}
if (dimSize >= 0)
{
if (dim >= dimLengthVals.size())
dimLengthVals.Add(mModule->GetConstValue(dimSize));
}
else
{
sizeFailed = true;
}
}
}
BfIRValue allocCount;
if (!sizeFailed)
{
if (!dimLengthVals.IsEmpty())
{
allocCount = dimLengthVals[0];
for (int dim = 1; dim < (int)dimLengthVals.size(); dim++)
allocCount = mModule->mBfIRBuilder->CreateMul(allocCount, dimLengthVals[dim]);
}
}
if (!allocCount)
{
mFailed = true;
if (!mIsFirstConstPass)
{
if (!mConstAccum)
mModule->Fail("Unable to determine append alloc size", objCreateExpr->mNewNode);
return;
}
}
if (isRawArrayAlloc)
{
curAlign = resultType->mAlign;
EmitAppendAlign(resultType->mAlign);
sizeValue = mModule->mBfIRBuilder->CreateMul(mModule->GetConstValue(resultType->mSize), allocCount);
}
else
{
if (arrayType == NULL)
arrayType = mModule->CreateArrayType(resultType, 1);
// Array is a special case where the total size isn't aligned with mAlign
// since we add arbitrary elements to the end without padding the end to align
EmitAppendAlign(arrayType->mAlign, resultType->mAlign);
curAlign = arrayType->mAlign;
auto firstElementField = &arrayType->mFieldInstances.back();
int arrayClassSize = arrayType->mInstSize - firstElementField->mDataSize;
sizeValue = mModule->GetConstValue(arrayClassSize);
BfIRValue elementDataSize = mModule->mBfIRBuilder->CreateMul(mModule->GetConstValue(resultType->mSize), allocCount);
sizeValue = mModule->mBfIRBuilder->CreateAdd(sizeValue, elementDataSize);
}
}
else
{
auto typeInst = resultType->ToTypeInstance();
if (typeInst != NULL)
{
curAlign = typeInst->mInstAlign;
EmitAppendAlign(typeInst->mInstAlign, typeInst->mInstSize);
sizeValue = mModule->GetConstValue(typeInst->mInstSize);
}
else
{
curAlign = resultType->mAlign;
EmitAppendAlign(resultType->mAlign, resultType->mSize);
sizeValue = mModule->GetConstValue(resultType->mSize);
}
BfTypedValue emtpyThis(mModule->mBfIRBuilder->GetFakeVal(), resultType, (typeInst == NULL) || (typeInst->IsComposite()));
BfExprEvaluator exprEvaluator(mModule);
SetAndRestoreValue<bool> prevNoBind(exprEvaluator.mNoBind, exprEvaluator.mNoBind || isGenericParam);
BfFunctionBindResult bindResult;
bindResult.mWantsArgs = true;
exprEvaluator.mFunctionBindResult = &bindResult;
SizedArray<BfExpression*, 8> copiedArgs;
for (BfExpression* arg : objCreateExpr->mArguments)
copiedArgs.push_back(arg);
BfSizedArray<BfExpression*> sizedArgExprs(copiedArgs);
BfResolvedArgs argValues(&sizedArgExprs);
if (typeInst != NULL)
{
exprEvaluator.ResolveArgValues(argValues);
exprEvaluator.MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInst, argValues, false, true);
}
exprEvaluator.mFunctionBindResult = NULL;
if (bindResult.mMethodInstance != NULL)
{
if (bindResult.mMethodInstance->mMethodDef->mHasAppend)
{
auto calcAppendArgs = bindResult.mIRArgs;
BF_ASSERT(calcAppendArgs[0].IsFake());
calcAppendArgs.RemoveRange(0, 2); // Remove 'this' and 'appendIdx'
auto calcAppendMethodModule = mModule->GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND);
auto subDependSize = mModule->TryConstCalcAppend(bindResult.mMethodInstance, calcAppendArgs);
if (calcAppendMethodModule.mMethodInstance->mAppendAllocAlign > 0)
{
EmitAppendAlign(calcAppendMethodModule.mMethodInstance->mAppendAllocAlign);
BF_ASSERT(calcAppendMethodModule.mMethodInstance->mEndingAppendAllocAlign > -1);
mModule->mCurMethodState->mCurAppendAlign = BF_MAX(calcAppendMethodModule.mMethodInstance->mEndingAppendAllocAlign, 0);
}
curAlign = std::max(curAlign, (int)calcAppendMethodModule.mMethodInstance->mAppendAllocAlign);
if ((!subDependSize) && (!mConstAccum))
{
BF_ASSERT(calcAppendMethodModule.mFunc);
subDependSize = exprEvaluator.CreateCall(calcAppendMethodModule.mMethodInstance, calcAppendMethodModule.mFunc, false, calcAppendArgs);
BF_ASSERT(subDependSize.mType == mModule->GetPrimitiveType(BfTypeCode_IntPtr));
}
if (subDependSize)
sizeValue = mModule->mBfIRBuilder->CreateAdd(sizeValue, subDependSize.mValue);
else
mFailed = true;
}
}
}
if (sizeValue)
{
if (mConstAccum)
{
if (!sizeValue.IsConst())
{
mFailed = true;
if (!mIsFirstConstPass)
return;
}
mConstAccum.mValue = mModule->mBfIRBuilder->CreateAdd(sizeValue, mConstAccum.mValue);
}
else
{
auto prevVal = mModule->mBfIRBuilder->CreateLoad(accumValuePtr);
auto addedVal = mModule->mBfIRBuilder->CreateAdd(sizeValue, prevVal);
mModule->mBfIRBuilder->CreateStore(addedVal, accumValuePtr);
}
}
}
}
}
void Visit(BfObjectCreateExpression* objCreateExpr) override
{
DoObjectCreate(objCreateExpr, NULL);
}
void Visit(BfVariableDeclaration* varDecl) override
{
if (mHasAppendAllocVisitor.HasAppendAlloc(varDecl))
{
if (auto objectCreateExpr = BfNodeDynCast<BfObjectCreateExpression>(varDecl->mInitializer))
DoObjectCreate(objectCreateExpr, varDecl);
else
VisitChild(varDecl->mInitializer);
if (varDecl->mNameNode != NULL)
{
BfLocalVariable* localDef = new BfLocalVariable();
localDef->mName = varDecl->mNameNode->ToString();
localDef->mNameNode = BfNodeDynCast<BfIdentifierNode>(varDecl->mNameNode);
localDef->mResolvedType = mModule->GetPrimitiveType(BfTypeCode_None);
localDef->mIsReadOnly = true;
localDef->mParamFailed = true;
localDef->mReadFromId = 0;
mModule->AddLocalVariableDef(localDef);
}
}
else
{
mModule->Visit(varDecl);
}
}
void Visit(BfExpressionStatement* exprStatement) override
{
VisitChild(exprStatement->mExpression);
}
// void Visit(BfIfStatement* ifStmt) override
// {
// mModule->DoIfStatement(ifStmt,
// mHasAppendAllocVisitor.HasAppendAlloc(ifStmt->mTrueStatement),
// mHasAppendAllocVisitor.HasAppendAlloc(ifStmt->mFalseStatement));
// }
void Visit(BfBlock* block) override
{
if (!mHasAppendAllocVisitor.HasAppendAlloc(block))
return;
int appendAllocIdx = -1;
for (int childIdx = block->mChildArr.mSize - 1; childIdx >= 0; childIdx--)
{
auto child = block->mChildArr.mVals[childIdx];
if (mHasAppendAllocVisitor.HasAppendAlloc(child))
{
for (int emitIdx = 0; emitIdx <= childIdx; emitIdx++)
{
auto emitChild = block->mChildArr.mVals[emitIdx];
mModule->UpdateSrcPos(emitChild);
VisitChild(emitChild);
if ((mFailed) && (!mIsFirstConstPass))
break;
}
break;
}
}
}
};
//////////////////////////////////////////////////////////////////////////
BfModule* gLastCreatedModule = NULL;
BfModule::BfModule(BfContext* context, const StringImpl& moduleName)
{
BfLogSys(context->mSystem, "BfModule::BFModule %p %s\n", this, moduleName.c_str());
2019-08-23 11:56:54 -07:00
gLastCreatedModule = this;
mContext = context;
mModuleName = moduleName;
if (!moduleName.empty())
mContext->mUsedModuleNames.Add(ToUpper(moduleName));
mAddedToCount = false;
mParentModule = NULL;
mNextAltModule = NULL;
mBfIRBuilder = NULL;
mWantsIRIgnoreWrites = false;
mModuleOptions = NULL;
mLastUsedRevision = -1;
mUsedSlotCount = -1;
2019-11-02 06:02:36 -07:00
2019-08-23 11:56:54 -07:00
mIsReified = true;
mReifyQueued = false;
mIsSpecialModule = false;
mIsScratchModule = false;
2019-11-02 06:02:36 -07:00
mIsSpecializedMethodModuleRoot = false; // There may be mNextAltModules extending from this
2019-08-23 11:56:54 -07:00
mHadBuildError = false;
mHadBuildWarning = false;
mIgnoreErrors = false;
mIgnoreWarnings = false;
2019-08-23 11:56:54 -07:00
mReportErrors = true;
mIsInsideAutoComplete = false;
mIsDeleting = false;
mSkipInnerLookup = false;
mIsHotModule = false;
mSetIllegalSrcPosition = false;
mNoResolveGenericParams = false;
mWroteToLib = false;
mContext = context;
mCompiler = context->mCompiler;
mSystem = mCompiler->mSystem;
mProject = NULL;
mCurMethodState = NULL;
2019-08-23 11:56:54 -07:00
mAttributeState = NULL;
mCurLocalMethodId = 0;
mParentNodeEntry = NULL;
mRevision = -1;
mRebuildIdx = 0;
mLastModuleWrittenRevision = 0;
mIsModuleMutable = false;
mExtensionCount = 0;
mIncompleteMethodCount = 0;
mOnDemandMethodCount = 0;
mHasGenericMethods = false;
mCurMethodInstance = NULL;
mCurTypeInstance = NULL;
mAwaitingInitFinish = false;
mAwaitingFinish = false;
mHasFullDebugInfo = false;
mHadHotObjectWrites = false;
for (int i = 0; i < BfBuiltInFuncType_Count; i++)
mBuiltInFuncs[i] = BfIRFunction();
BfLogSysM("Creating module %p: %s Reified: %d ResolveOnly: %d\n", this, mModuleName.c_str(), mIsReified, mCompiler->mIsResolveOnly);
}
void BfReportMemory();
BfModule::~BfModule()
{
BfLogSysM("Deleting module %p: %s \n", this, mModuleName.c_str());
if (!mIsDeleting)
RemoveModuleData();
}
void BfModule::RemoveModuleData()
{
BF_ASSERT(!mIsDeleting);
if (!mModuleName.empty())
{
// Note: module names not necessarily unique
mContext->mUsedModuleNames.Remove(ToUpper(mModuleName));
}
CleanupFileInstances();
delete mBfIRBuilder;
mBfIRBuilder = NULL;
//delete mCpu;
for (auto prevModule : mPrevIRBuilders)
delete prevModule;
mPrevIRBuilders.Clear();
for (auto& pairVal : mDeferredMethodCallData)
delete pairVal.mValue;
mDeferredMethodCallData.Clear();
mDeferredMethodIds.Clear();
for (auto& specModulePair : mSpecializedMethodModules)
delete specModulePair.mValue;
mSpecializedMethodModules.Clear();
if (mNextAltModule != NULL)
delete mNextAltModule;
mNextAltModule = NULL;
if (mModuleOptions != NULL)
delete mModuleOptions;
mModuleOptions = NULL;
BfReportMemory();
}
void BfModule::Init(bool isFullRebuild)
{
mContext->mFinishedModuleWorkList.Remove(this);
if ((mCompiler->mIsResolveOnly) && (this != mContext->mUnreifiedModule))
BF_ASSERT(mIsReified);
if (!mIsScratchModule)
{
mCompiler->mStats.mModulesStarted++;
if (mIsReified)
mCompiler->mStats.mReifiedModuleCount++;
mAddedToCount = true;
}
mIsHotModule = (mProject != NULL) && (mCompiler->mOptions.mHotProject != NULL) && (mCompiler->mOptions.mHotProject->ContainsReference(mProject));
mFuncReferences.Clear();
mClassVDataRefs.Clear();
mClassVDataExtRefs.Clear();
//mTypeDataRefs.Clear();
mDbgRawAllocDataRefs.Clear();
CleanupFileInstances();
mStaticFieldRefs.Clear();
//mInterfaceSlotRefs.Clear();
// If we are just doing an extension then the ownede types aren't rebuilt.
// If we set mRevision then QueueMethodSpecializations wouldn't actually queue up required specializations
// and we'd end up with link errors if the original module uniquely referred to any generic methods
if (isFullRebuild)
mRevision = mCompiler->mRevision;
BF_ASSERT(mCurTypeInstance == NULL);
mIsModuleMutable = true;
BF_ASSERT((mBfIRBuilder == NULL) || (mCompiler->mIsResolveOnly));
#ifdef _DEBUG
EnsureIRBuilder(mCompiler->mLastAutocompleteModule == this);
#else
EnsureIRBuilder(false);
#endif
mCurMethodState = NULL;
mAwaitingInitFinish = true;
mOnDemandMethodCount = 0;
mAwaitingFinish = false;
mHasForceLinkMarker = false;
mUsedSlotCount = -1;
}
bool BfModule::WantsFinishModule()
{
return (mIncompleteMethodCount == 0) && (mOnDemandMethodCount == 0) && (!mHasGenericMethods) && (!mIsScratchModule) && (mExtensionCount == 0) && (mParentModule == NULL);
}
void BfModule::FinishInit()
{
BF_ASSERT(mAwaitingInitFinish);
auto moduleOptions = GetModuleOptions();
mBfIRBuilder->Start(mModuleName, mCompiler->mSystem->mPtrSize, IsOptimized());
mBfIRBuilder->Module_SetTargetTriple(mCompiler->mOptions.mTargetTriple);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->SetBackend(IsTargetingBeefBackend());
if (moduleOptions.mOptLevel == BfOptLevel_OgPlus)
{
// Og+ requires debug info
moduleOptions.mEmitDebugInfo = 1;
}
mHasFullDebugInfo = moduleOptions.mEmitDebugInfo == 1;
if ((!mCompiler->mIsResolveOnly) && (!mIsScratchModule) && (moduleOptions.mEmitDebugInfo != 0) && (mIsReified))
{
mBfIRBuilder->DbgInit();
}
else
mHasFullDebugInfo = false;
if ((mBfIRBuilder->DbgHasInfo()) && (mModuleName != "") &&
((moduleOptions.mEmitDebugInfo != 0)))
{
if (mCompiler->mOptions.IsCodeView())
{
mBfIRBuilder->Module_AddModuleFlag("CodeView", 1);
}
else
{
mBfIRBuilder->Module_AddModuleFlag("Dwarf Version", 4);
}
mBfIRBuilder->Module_AddModuleFlag("Debug Info Version", 3);
2020-04-27 15:15:47 -07:00
mDICompileUnit = mBfIRBuilder->DbgCreateCompileUnit(llvm::dwarf::DW_LANG_C_plus_plus, mModuleName, ".", "Beef Compiler 0.42.3", /*moduleOptions.mOptLevel > 0*/false, "", 0, !mHasFullDebugInfo);
2019-08-23 11:56:54 -07:00
}
mAwaitingInitFinish = false;
}
void BfModule::ReifyModule()
{
BF_ASSERT((mCompiler->mCompileState != BfCompiler::CompileState_Unreified) && (mCompiler->mCompileState != BfCompiler::CompileState_VData));
BfLogSysM("ReifyModule %@ %s\n", this, mModuleName.c_str());
BF_ASSERT((this != mContext->mScratchModule) && (this != mContext->mUnreifiedModule));
mIsReified = true;
mReifyQueued = false;
StartNewRevision(RebuildKind_SkipOnDemandTypes, true);
mCompiler->mStats.mModulesReified++;
}
void BfModule::UnreifyModule()
{
BfLogSysM("UnreifyModule %p %s\n", this, mModuleName.c_str());
BF_ASSERT((this != mContext->mScratchModule) && (this != mContext->mUnreifiedModule));
mIsReified = false;
mReifyQueued = false;
StartNewRevision(RebuildKind_None, true);
mCompiler->mStats.mModulesUnreified++;
}
void BfModule::CleanupFileInstances()
{
for (auto& itr : mNamedFileInstanceMap)
{
BfLogSysM("FileInstance deleted: %p\n", itr.mValue);
delete itr.mValue;
}
mCurFilePosition.mFileInstance = NULL;//
mFileInstanceMap.Clear();
mNamedFileInstanceMap.Clear();
}
void BfModule::Cleanup()
{
CleanupFileInstances();
}
void BfModule::PrepareForIRWriting(BfTypeInstance* typeInst)
{
if (HasCompiledOutput())
{
// It's possible that the target's code hasn't changed but we're requesting a new generic method specialization
if ((!mIsModuleMutable) && (!typeInst->IsUnspecializedType()) && (!typeInst->mResolvingVarField))
{
StartExtension();
}
}
else
{
EnsureIRBuilder();
}
}
2019-08-23 11:56:54 -07:00
void BfModule::EnsureIRBuilder(bool dbgVerifyCodeGen)
{
BF_ASSERT(!mIsDeleting);
if (mBfIRBuilder == NULL)
{
if ((!mIsScratchModule) && (!mAddedToCount))
{
mCompiler->mStats.mModulesStarted++;
mAddedToCount = true;
}
BF_ASSERT(mStaticFieldRefs.GetCount() == 0);
/*if (mCompiler->mIsResolveOnly)
BF_ASSERT(mIsResolveOnly);*/
mBfIRBuilder = new BfIRBuilder(this);
BfLogSysM("Created mBfIRBuilder %p in %p %s Reified: %d\n", mBfIRBuilder, this, mModuleName.c_str(), mIsReified);
if (mIsScratchModule)
{
mBfIRBuilder->mIgnoreWrites = true;
BF_ASSERT(!dbgVerifyCodeGen);
}
#ifdef _DEBUG
if (mCompiler->mIsResolveOnly)
{
// For "deep" verification testing
/*if (!mIsSpecialModule)
dbgVerifyCodeGen = true;*/
// We only want to turn this off on smaller builds for testing
mBfIRBuilder->mIgnoreWrites = true;
if (dbgVerifyCodeGen)
mBfIRBuilder->mIgnoreWrites = false;
if (!mBfIRBuilder->mIgnoreWrites)
{
// The only purpose of not ignoring writes is so we can verify the codegen one instruction at a time
mBfIRBuilder->mDbgVerifyCodeGen = true;
}
}
else if (!mIsReified)
{
2019-08-23 11:56:54 -07:00
mBfIRBuilder->mIgnoreWrites = true;
}
else
{
// We almost always want this to be 'false' unless we need need to be able to inspect the generated LLVM
// code as we walk the AST
//mBfIRBuilder->mDbgVerifyCodeGen = true;
if (
(mModuleName == "-")
//|| (mModuleName == "Tests_FuncRefs_Class")
//|| (mModuleName == "Tests_FuncRefs")
2019-08-23 11:56:54 -07:00
)
mBfIRBuilder->mDbgVerifyCodeGen = true;
// Just for testing!
//mBfIRBuilder->mIgnoreWrites = true;
}
#else
if (mCompiler->mIsResolveOnly)
{
// Always ignore writes in resolveOnly for release builds
mBfIRBuilder->mIgnoreWrites = true;
}
else
{
// Just for memory testing! This breaks everything.
//mBfIRBuilder->mIgnoreWrites = true;
// For "deep" verification testing
//mBfIRBuilder->mDbgVerifyCodeGen = true;
}
#endif
mWantsIRIgnoreWrites = mBfIRBuilder->mIgnoreWrites;
}
}
BfIRValue BfModule::CreateForceLinkMarker(BfModule* module, String* outName)
{
String name = "FORCELINKMOD_" + module->mModuleName;
if (outName != NULL)
*outName = name;
auto markerType = GetPrimitiveType(BfTypeCode_Int8);
return mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(markerType), true, BfIRLinkageType_External, (module == this) ? mBfIRBuilder->CreateConst(BfTypeCode_Int8, 0) : BfIRValue(), name);
}
void BfModule::StartNewRevision(RebuildKind rebuildKind, bool force)
{
BP_ZONE("BfModule::StartNewRevision");
// The project MAY be deleted because disabling a project can cause types to be deleted which
// causes other types rebuild BEFORE they get deleted, which is okay (though wasteful)
//BF_ASSERT((mProject == NULL) || (!mProject->mDisabled));
// Already on new revision?
if ((mRevision == mCompiler->mRevision) && (!force))
return;
mHadBuildError = false;
2019-11-02 06:02:36 -07:00
mHadBuildWarning = false;
2019-08-23 11:56:54 -07:00
mExtensionCount = 0;
mRevision = mCompiler->mRevision;
mRebuildIdx++;
ClearModuleData();
// Clear this here, not in ClearModuleData, so we preserve those references even after writing out module
if (rebuildKind != BfModule::RebuildKind_None) // Leave string pool refs for when we need to use things like [LinkName("")] methods bofore re-reification
mStringPoolRefs.Clear();
mDllImportEntries.Clear();
mImportFileNames.Clear();
for (auto& pairVal : mDeferredMethodCallData)
delete pairVal.mValue;
mDeferredMethodCallData.Clear();
mDeferredMethodIds.Clear();
mModuleRefs.Clear();
mOutFileNames.Clear();
mTypeDataRefs.Clear();
mInterfaceSlotRefs.Clear();
if (rebuildKind == BfModule::RebuildKind_None)
{
for (auto& specPair : mSpecializedMethodModules)
{
auto specModule = specPair.mValue;
if (specModule->mIsReified != mIsReified)
{
if (mIsReified)
specModule->ReifyModule();
else
specModule->UnreifyModule();
}
}
}
else
{
for (auto& specPair : mSpecializedMethodModules)
{
auto specModule = specPair.mValue;
delete specModule;
}
}
mSpecializedMethodModules.Clear();
delete mNextAltModule;
mNextAltModule = NULL;
BF_ASSERT(mModuleOptions == NULL);
BfLogSysM("Mod:%p StartNewRevision: %s Revision: %d\n", this, mModuleName.c_str(), mRevision);
bool needsTypePopulated = false;
int oldOnDemandCount = 0;
// We don't have to rebuild types in the unspecialized module because there is no
// data that's needed -- their method instances are all NULL and the module
// doesn't get included in the build
if (this != mContext->mScratchModule)
{
for (int typeIdx = 0; typeIdx < (int)mOwnedTypeInstances.size(); typeIdx++)
{
auto typeInst = mOwnedTypeInstances[typeIdx];
if (!typeInst->IsDeleting())
{
typeInst->mIsReified = mIsReified;
if (rebuildKind != BfModule::RebuildKind_None)
{
if (typeInst->IsOnDemand())
{
// Changing config should require on demand types to be recreated, but reifying a module shouldn't cause the type to be deleted
if (rebuildKind == BfModule::RebuildKind_All)
mContext->DeleteType(typeInst);
else
{
RebuildMethods(typeInst);
}
}
else
//TODO: Why weren't we placing specialzied in purgatory here originally? This caused failed types to stick around too long
mContext->RebuildType(typeInst, false, false, true);
//mContext->RebuildType(typeInst, false, false, false);
2019-08-23 11:56:54 -07:00
}
else
{
for (auto& methodGroup : typeInst->mMethodInstanceGroups)
if ((methodGroup.mOnDemandKind == BfMethodOnDemandKind_NoDecl_AwaitingReference) ||
(methodGroup.mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingReference))
{
oldOnDemandCount++;
}
}
if (typeInst->IsDeleting())
typeIdx--;
}
}
}
if (!mIsDeleting)
Init();
mOnDemandMethodCount += oldOnDemandCount;
}
void BfModule::StartExtension()
{
BF_ASSERT(!mIsModuleMutable);
BfLogSysM("Extension started of module %p\n", this);
mExtensionCount++;
if (mBfIRBuilder != NULL)
mPrevIRBuilders.push_back(mBfIRBuilder);
mBfIRBuilder = NULL;
mWantsIRIgnoreWrites = false;
mFuncReferences.Clear();
mClassVDataRefs.Clear();
mClassVDataExtRefs.Clear();
for (auto& kv : mTypeDataRefs)
kv.mValue = BfIRValue();
mDbgRawAllocDataRefs.Clear();
mStringCharPtrPool.Clear();
mStringObjectPool.Clear();
mStaticFieldRefs.Clear();
for (auto& kv : mInterfaceSlotRefs)
kv.mValue = BfIRValue();
mDeferredMethodCallData.Clear();
mDeferredMethodIds.Clear();
DisownMethods();
BfLogSysM("Mod:%p StartExtension: %s\n", this, mModuleName.c_str());
bool wasAwaitingInitFinish = mAwaitingInitFinish;
int prevOnDemandMethodCount = mOnDemandMethodCount;
Init(false);
if (!wasAwaitingInitFinish)
FinishInit();
mOnDemandMethodCount = prevOnDemandMethodCount;
}
void BfModule::GetConstClassValueParam(BfIRValue classVData, SizedArrayImpl<BfIRValue>& typeValueParams)
{
BfIRValue vDataValue;
if (mCompiler->mOptions.mObjectHasDebugFlags)
vDataValue = mBfIRBuilder->CreatePtrToInt(classVData, BfTypeCode_IntPtr);
else
vDataValue = mBfIRBuilder->CreateBitCast(classVData, mBfIRBuilder->MapType(mContext->mBfClassVDataPtrType));
typeValueParams.push_back(vDataValue);
if (mCompiler->mOptions.mObjectHasDebugFlags)
{
auto primType = GetPrimitiveType(BfTypeCode_IntPtr);
typeValueParams.push_back(GetDefaultValue(primType));
}
}
BfIRValue BfModule::GetConstValue(int64 val)
{
return mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, (uint64)val);
}
BfIRValue BfModule::GetConstValue(int64 val, BfType* type)
{
BfType* checkType = type;
if (type->IsTypedPrimitive())
{
checkType = type->GetUnderlyingType();
}
if (checkType->IsPrimitiveType())
{
auto primType = (BfPrimitiveType*)checkType;
if (checkType->IsFloat())
return mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, (double)val);
else
return mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, (uint64)val);
}
return BfIRValue();
}
BfIRValue BfModule::GetConstValue8(int val)
{
return mBfIRBuilder->CreateConst(BfTypeCode_Int8, (uint64)val);
}
BfIRValue BfModule::GetConstValue32(int32 val)
{
return mBfIRBuilder->CreateConst(BfTypeCode_Int32, (uint64)val);
}
BfIRValue BfModule::GetConstValue64(int64 val)
{
return mBfIRBuilder->CreateConst(BfTypeCode_Int64, (uint64)val);
}
BfIRValue BfModule::GetDefaultValue(BfType* type)
{
PopulateType(type, BfPopulateType_Data);
mBfIRBuilder->PopulateType(type, BfIRPopulateType_Declaration);
if (type->IsTypedPrimitive())
{
auto underlyingType = type->GetUnderlyingType();
if (underlyingType == NULL)
return mBfIRBuilder->CreateConst(BfTypeCode_Int64, 0);
return GetDefaultValue(type->GetUnderlyingType());
}
if (type->IsPointer() || type->IsObjectOrInterface() || type->IsGenericParam() || type->IsVar() || type->IsRef() || type->IsNull() ||
type->IsModifiedTypeType() || type->IsConcreteInterfaceType())
2019-08-23 11:56:54 -07:00
return mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(type));
if ((type->IsIntegral()) || (type->IsBoolean()))
{
auto primType = (BfPrimitiveType*)type;
return mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, (uint64)0);
}
if (type->IsFloat())
{
auto primType = (BfPrimitiveType*)type;
return mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, 0.0);
}
if (type->IsVoid())
return mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(type));
return mBfIRBuilder->CreateConstStructZero(mBfIRBuilder->MapType(type));
}
BfTypedValue BfModule::GetFakeTypedValue(BfType* type)
{
// This is a conservative "IsValueless", since it's not an error to use a fakeVal even if we don't need one
if (type->mSize == 0)
return BfTypedValue(BfIRValue::sValueless, type);
2019-08-23 11:56:54 -07:00
else
return BfTypedValue(mBfIRBuilder->GetFakeVal(), type);
}
BfTypedValue BfModule::GetDefaultTypedValue(BfType* type, bool allowRef, BfDefaultValueKind defaultValueKind)
{
if (type->IsVar())
return BfTypedValue(mBfIRBuilder->GetFakeVal(), type);
2019-08-23 11:56:54 -07:00
PopulateType(type, BfPopulateType_Data);
mBfIRBuilder->PopulateType(type, type->IsValueType() ? BfIRPopulateType_Full : BfIRPopulateType_Declaration);
if (defaultValueKind == BfDefaultValueKind_Undef)
{
auto primType = type->ToPrimitiveType();
if (primType != NULL)
{
return BfTypedValue(mBfIRBuilder->GetUndefConstValue(primType->mTypeDef->mTypeCode), type);
}
return BfTypedValue(mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(type)), type);
}
BfTypedValue typedValue;
if ((defaultValueKind != BfDefaultValueKind_Const) && (!type->IsValuelessType()))
{
if (type->IsRef())
{
BfRefType* refType = (BfRefType*)type;
BfType* underlyingType = refType->GetUnderlyingType();
typedValue = BfTypedValue(CreateAlloca(underlyingType), underlyingType, BfTypedValueKind_Addr);
}
else
{
typedValue = BfTypedValue(CreateAlloca(type), type, BfTypedValueKind_Addr);
}
if (!mBfIRBuilder->mIgnoreWrites)
{
mBfIRBuilder->CreateMemSet(typedValue.mValue, GetConstValue(0, GetPrimitiveType(BfTypeCode_Int8)),
GetConstValue(type->mSize), type->mAlign);
}
if ((defaultValueKind == BfDefaultValueKind_Value) && (!type->IsRef()))
typedValue = LoadValue(typedValue);
return typedValue;
}
if ((type->IsRef()) && (!allowRef))
{
BfRefType* refType = (BfRefType*)type;
BfType* underlyingType = refType->GetUnderlyingType();
if (underlyingType->IsValuelessType())
typedValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), underlyingType, true);
else
typedValue = BfTypedValue(mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(type)), underlyingType, true);
}
else
{
typedValue = BfTypedValue(GetDefaultValue(type), type, (defaultValueKind == BfDefaultValueKind_Addr) ? BfTypedValueKind_Addr : BfTypedValueKind_Value);
}
return typedValue;
}
BfIRValue BfModule::CreateStringCharPtr(const StringImpl& str, int stringId, bool define)
{
String stringDataName = StrFormat("__bfStrData%d", stringId);
auto charType = GetPrimitiveType(BfTypeCode_Char8);
BfIRType irStrCharType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->MapType(charType), (int)str.length() + 1);
2019-08-23 11:56:54 -07:00
BfIRValue strConstant;
if (define)
{
strConstant = mBfIRBuilder->CreateConstString(str);
}
BfIRValue gv = mBfIRBuilder->CreateGlobalVariable(irStrCharType,
2019-08-23 11:56:54 -07:00
true, BfIRLinkageType_External,
strConstant, stringDataName);
if (define)
mBfIRBuilder->GlobalVar_SetUnnamedAddr(gv, true);
return mBfIRBuilder->CreateInBoundsGEP(gv, 0, 0);
}
BfIRValue BfModule::CreateStringObjectValue(const StringImpl& str, int stringId, bool define)
{
auto stringTypeInst = ResolveTypeDef(mCompiler->mStringTypeDef, define ? BfPopulateType_Data : BfPopulateType_Declaration)->ToTypeInstance();
mBfIRBuilder->PopulateType(stringTypeInst);
auto classVDataGlobal = CreateClassVDataGlobal(stringTypeInst);
String stringObjName = StrFormat("__bfStrObj%d", stringId);
BfIRValue stringValData;
if (define)
{
BfIRValue stringCharsVal = CreateStringCharPtr(str, stringId, define);
mStringCharPtrPool[stringId] = stringCharsVal;
SizedArray<BfIRValue, 8> typeValueParams;
GetConstClassValueParam(classVDataGlobal, typeValueParams);
auto objData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(stringTypeInst->mBaseType, BfIRPopulateType_Full), typeValueParams);
auto lenByteCount = stringTypeInst->mFieldInstances[0].mResolvedType->mSize;
typeValueParams.clear();
typeValueParams.push_back(objData);
if (lenByteCount == 4)
{
typeValueParams.push_back(GetConstValue32((int)str.length())); // mLength
typeValueParams.push_back(GetConstValue32((int32)(0x40000000 + str.length() + 1))); // mAllocSizeAndFlags
}
else
{
typeValueParams.push_back(GetConstValue64(str.length())); // mLength
typeValueParams.push_back(GetConstValue64(0x4000000000000000LL + str.length() + 1)); // mAllocSizeAndFlags
}
typeValueParams.push_back(stringCharsVal); // mPtr
for (int fieldIdx = 0; fieldIdx < (int)stringTypeInst->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = &stringTypeInst->mFieldInstances[fieldIdx];
if (fieldInstance->mDataIdx < 4)
continue;
while (fieldInstance->mDataIdx >= typeValueParams.size())
typeValueParams.Add(BfIRValue());
typeValueParams[fieldInstance->mDataIdx] = GetDefaultValue(fieldInstance->mResolvedType);
}
2019-08-23 11:56:54 -07:00
stringValData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(stringTypeInst, BfIRPopulateType_Full), typeValueParams);
}
mBfIRBuilder->PopulateType(stringTypeInst);
2019-08-23 11:56:54 -07:00
auto stringValLiteral = mBfIRBuilder->CreateGlobalVariable(
mBfIRBuilder->MapTypeInst(stringTypeInst),
true,
BfIRLinkageType_External,
define ? stringValData : BfIRValue(),
stringObjName);
auto stringVal = mBfIRBuilder->CreateBitCast(stringValLiteral, mBfIRBuilder->MapType(stringTypeInst, BfIRPopulateType_Full));
return stringVal;
}
int BfModule::GetStringPoolIdx(BfIRValue constantStr, BfIRConstHolder* constHolder)
{
BF_ASSERT(constantStr.IsConst());
if (constHolder == NULL)
constHolder = mBfIRBuilder;
auto constant = constHolder->GetConstant(constantStr);
if (constant->mTypeCode == BfTypeCode_StringId)
2019-08-23 11:56:54 -07:00
{
return constant->mInt32;
}
while (constant->mConstType == BfConstType_BitCast)
{
auto constBitCast = (BfConstantBitCast*)constant;
constant = constHolder->GetConstantById(constBitCast->mTarget);
}
if (constant->mConstType == BfConstType_GEP32_2)
{
auto constGEP = (BfConstantGEP32_2*)constant;
constant = constHolder->GetConstantById(constGEP->mTarget);
}
if (constant->mConstType == BfConstType_GlobalVar)
{
auto constGV = (BfGlobalVar*)constant;
const char* strDataPrefix = "__bfStrData";
if (strncmp(constGV->mName, strDataPrefix, strlen(strDataPrefix)) == 0)
return atoi(constGV->mName + strlen(strDataPrefix));
const char* strObjPrefix = "__bfStrObj";
if (strncmp(constGV->mName, strObjPrefix, strlen(strObjPrefix)) == 0)
return atoi(constGV->mName + strlen(strObjPrefix));
}
return -1;
}
2019-10-09 16:16:01 -07:00
String* BfModule::GetStringPoolString(BfIRValue constantStr, BfIRConstHolder * constHolder)
{
int strId = GetStringPoolIdx(constantStr, constHolder);
if (strId != -1)
{
auto& entry = mContext->mStringObjectIdMap[strId];
return &entry.mString;
}
2019-10-09 16:16:01 -07:00
return NULL;
}
2019-08-23 11:56:54 -07:00
BfIRValue BfModule::GetStringCharPtr(int stringId)
{
BfIRValue* irValue = NULL;
if (!mStringCharPtrPool.TryAdd(stringId, NULL, &irValue))
return *irValue;
// If this wasn't in neither dictionary yet, add to mStringPoolRefs
if (!mStringObjectPool.ContainsKey(stringId))
mStringPoolRefs.Add(stringId);
const StringImpl& str = mContext->mStringObjectIdMap[stringId].mString;
BfIRValue strCharPtrConst = CreateStringCharPtr(str, stringId, false);
*irValue = strCharPtrConst;
return strCharPtrConst;
}
BfIRValue BfModule::GetStringCharPtr(BfIRValue strValue)
{
if (strValue.IsConst())
{
int stringId = GetStringPoolIdx(strValue);
BF_ASSERT(stringId != -1);
return GetStringCharPtr(stringId);
}
BfIRValue charPtrPtr = mBfIRBuilder->CreateInBoundsGEP(strValue, 0, 1);
BfIRValue charPtr = mBfIRBuilder->CreateLoad(charPtrPtr);
return charPtr;
}
BfIRValue BfModule::GetStringCharPtr(const StringImpl& str)
{
return GetStringCharPtr(GetStringObjectValue(str));
}
BfIRValue BfModule::GetStringObjectValue(int strId)
{
BfIRValue* objValue;
if (mStringObjectPool.TryGetValue(strId, &objValue))
return *objValue;
auto stringPoolEntry = mContext->mStringObjectIdMap[strId];
return GetStringObjectValue(stringPoolEntry.mString, true);
}
BfIRValue BfModule::GetStringObjectValue(const StringImpl& str, bool define)
{
auto stringType = ResolveTypeDef(mCompiler->mStringTypeDef, define ? BfPopulateType_Data : BfPopulateType_Declaration);
mBfIRBuilder->PopulateType(stringType);
int strId = mContext->GetStringLiteralId(str);
BfIRValue* irValuePtr = NULL;
if (!mStringObjectPool.TryAdd(strId, NULL, &irValuePtr))
return *irValuePtr;
// If this wasn't in neither dictionary yet, add to mStringPoolRefs
if (!mStringCharPtrPool.ContainsKey(strId))
mStringPoolRefs.Add(strId);
BfIRValue strObject = CreateStringObjectValue(str, strId, define);
*irValuePtr = strObject;
mStringPoolRefs.Add(strId);
return strObject;
}
BfIRValue BfModule::CreateGlobalConstValue(const StringImpl& name, BfIRValue constant, BfIRType type, bool external)
{
auto newConst = mBfIRBuilder->CreateGlobalVariable(
type,
true,
external ? BfIRLinkageType_External : BfIRLinkageType_Internal,
constant,
name.c_str());
return newConst;
}
void BfModule::NewScopeState(bool createLexicalBlock, bool flushValueScope)
{
auto curScope = mCurMethodState->mCurScope;
if (curScope->mLabelNode != NULL)
{
2019-08-23 11:56:54 -07:00
curScope->mLabelNode->ToString(curScope->mLabel);
auto rootMethodState = mCurMethodState->GetRootMethodState();
curScope->mScopeLocalId = rootMethodState->mCurLocalVarId++;
auto autoComplete = mCompiler->GetAutoComplete();
if (autoComplete != NULL)
autoComplete->CheckLabel(curScope->mLabelNode, NULL, curScope);
}
2019-08-23 11:56:54 -07:00
if (!mCurMethodState->mCurScope->mLabel.IsEmpty())
{
auto checkScope = mCurMethodState->mCurScope->mPrevScope;
while (checkScope != NULL)
{
if (checkScope->mLabel == mCurMethodState->mCurScope->mLabel)
{
auto errorNode = Fail("Duplicate scope label", curScope->mLabelNode);
if (errorNode != NULL)
mCompiler->mPassInstance->MoreInfo("See previous scope label", checkScope->mLabelNode);
break;
}
checkScope = checkScope->mPrevScope;
}
}
auto prevScope = mCurMethodState->mCurScope->mPrevScope;
if ((flushValueScope) && (prevScope != NULL) && (prevScope->mValueScopeStart))
{
if (prevScope->mHadScopeValueRetain)
mBfIRBuilder->CreateValueScopeSoftEnd(prevScope->mValueScopeStart);
else
mBfIRBuilder->CreateValueScopeHardEnd(prevScope->mValueScopeStart);
}
mCurMethodState->mBlockNestLevel++;
if ((createLexicalBlock) && (mBfIRBuilder->DbgHasInfo()) && (mHasFullDebugInfo))
{
// DIScope could be NULL if we're in an unspecialized method (for example)
if (mCurMethodState->mCurScope->mDIScope)
mCurMethodState->mCurScope->mDIScope = mBfIRBuilder->DbgCreateLexicalBlock(mCurMethodState->mCurScope->mDIScope, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mCurFilePosition.mCurColumn);
}
mCurMethodState->mCurScope->mLocalVarStart = (int)mCurMethodState->mLocals.size();
mCurMethodState->mCurScope->mBlock = mBfIRBuilder->MaybeChainNewBlock((!mCurMethodState->mCurScope->mLabel.empty()) ? mCurMethodState->mCurScope->mLabel : "newScope");
}
void BfModule::RestoreScoreState_LocalVariables()
{
while (mCurMethodState->mCurScope->mLocalVarStart < (int)mCurMethodState->mLocals.size())
{
auto localVar = mCurMethodState->mLocals.back();
LocalVariableDone(localVar, false);
mCurMethodState->mLocals.pop_back();
if (localVar->mShadowedLocal != NULL)
{
BfLocalVarEntry* localVarEntryPtr;
if (mCurMethodState->mLocalVarSet.TryGet(BfLocalVarEntry(localVar), &localVarEntryPtr))
{
localVarEntryPtr->mLocalVar = localVar->mShadowedLocal;
}
}
else
{
mCurMethodState->mLocalVarSet.Remove(BfLocalVarEntry(localVar));
}
delete localVar;
}
}
void BfModule::RestoreScopeState()
{
BfScopeData* prevScopeData = mCurMethodState->mCurScope->mPrevScope;
mCurMethodState->mBlockNestLevel--;
if (!mCurMethodState->mCurScope->mAtEndBlocks.empty())
{
BfIRBlock afterEndBlock;
if (!mCurMethodState->mLeftBlockUncond)
{
afterEndBlock = mBfIRBuilder->CreateBlock("scopeAfterEnd");
mBfIRBuilder->CreateBr(afterEndBlock);
mBfIRBuilder->ClearDebugLocation_Last();
}
for (auto preExitBlock : mCurMethodState->mCurScope->mAtEndBlocks)
mBfIRBuilder->MoveBlockToEnd(preExitBlock);
if (afterEndBlock)
{
mBfIRBuilder->AddBlock(afterEndBlock);
mBfIRBuilder->SetInsertPoint(afterEndBlock);
}
}
if (!mCurMethodState->mLeftBlockUncond)
EmitDeferredScopeCalls(true, mCurMethodState->mCurScope);
RestoreScoreState_LocalVariables();
if (mCurMethodState->mCurScope->mValueScopeStart)
mBfIRBuilder->CreateValueScopeHardEnd(mCurMethodState->mCurScope->mValueScopeStart);
mCurMethodState->mCurScope = prevScopeData;
mCurMethodState->mTailScope = mCurMethodState->mCurScope;
}
BfIRValue BfModule::CreateAlloca(BfType* type, bool addLifetime, const char* name, BfIRValue arraySize)
{
if (mBfIRBuilder->mIgnoreWrites)
return mBfIRBuilder->GetFakeVal();
BF_ASSERT((*(int8*)&addLifetime == 1) || (*(int8*)&addLifetime == 0));
mBfIRBuilder->PopulateType(type);
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
if (!mBfIRBuilder->mIgnoreWrites)
BF_ASSERT(!prevInsertBlock.IsFake());
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
BfIRValue allocaInst;
if (arraySize)
allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(type), arraySize);
else
allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(type));
mBfIRBuilder->SetAllocaAlignment(allocaInst, type->mAlign);
mBfIRBuilder->ClearDebugLocation(allocaInst);
if (name != NULL)
mBfIRBuilder->SetName(allocaInst, name);
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
if ((addLifetime) && (WantsLifetimes()))
2019-08-23 11:56:54 -07:00
{
auto lifetimeStart = mBfIRBuilder->CreateLifetimeStart(allocaInst);
mBfIRBuilder->ClearDebugLocation(lifetimeStart);
mCurMethodState->mCurScope->mDeferredLifetimeEnds.push_back(allocaInst);
}
return allocaInst;
}
BfIRValue BfModule::CreateAllocaInst(BfTypeInstance* typeInst, bool addLifetime, const char* name)
{
if (mBfIRBuilder->mIgnoreWrites)
return mBfIRBuilder->GetFakeVal();
BF_ASSERT((*(int8*)&addLifetime == 1) || (*(int8*)&addLifetime == 0));
mBfIRBuilder->PopulateType(typeInst);
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
auto allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapTypeInst(typeInst));
mBfIRBuilder->SetAllocaAlignment(allocaInst, typeInst->mInstAlign);
mBfIRBuilder->ClearDebugLocation(allocaInst);
if (name != NULL)
mBfIRBuilder->SetName(allocaInst, name);
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
if ((addLifetime) && (WantsLifetimes()))
2019-08-23 11:56:54 -07:00
{
auto lifetimeStart = mBfIRBuilder->CreateLifetimeStart(allocaInst);
mBfIRBuilder->ClearDebugLocation(lifetimeStart);
mCurMethodState->mCurScope->mDeferredLifetimeEnds.push_back(allocaInst);
}
return allocaInst;
}
void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* refNode, BfScopeData* scopeData, bool condAlloca, bool mayEscape)
2019-08-23 11:56:54 -07:00
{
//This was removed because we want the alloc to be added to the __deferred list if it's actually a "stack"
// 'stack' in a head scopeData is really the same as 'scopeData', so use the simpler scopeData handling
/*if (mCurMethodState->mInHeadScope)
isScopeAlloc = true;*/
if (scopeData == NULL)
return;
auto checkBaseType = val.mType->ToTypeInstance();
if ((checkBaseType != NULL) && (checkBaseType->IsObject()))
{
bool hadDtorCall = false;
while (checkBaseType != NULL)
{
if ((checkBaseType->mTypeDef->mDtorDef != NULL) /*&& (checkBaseType != mContext->mBfObjectType)*/)
{
bool isDynAlloc = (scopeData != NULL) && (mCurMethodState->mCurScope->IsDyn(scopeData));
auto dtorMethodInstance = GetMethodInstance(checkBaseType, checkBaseType->mTypeDef->mDtorDef, BfTypeVector());
BfIRValue useVal = val.mValue;
useVal = mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapTypeInstPtr(checkBaseType));
if (isDynAlloc)
{
//
}
else if (condAlloca)
{
BF_ASSERT(!IsTargetingBeefBackend());
BF_ASSERT(!isDynAlloc);
auto valPtr = CreateAlloca(checkBaseType);
mBfIRBuilder->CreateStore(useVal, valPtr);
useVal = valPtr;
}
SizedArray<BfIRValue, 1> llvmArgs;
llvmArgs.push_back(useVal);
auto deferredCall = AddDeferredCall(dtorMethodInstance, llvmArgs, scopeData, refNode, true);
if (deferredCall != NULL)
{
deferredCall->mCastThis = (val.mType != checkBaseType) && (!isDynAlloc);
if (condAlloca)
deferredCall->mArgsNeedLoad = true;
}
hadDtorCall = true;
break;
}
checkBaseType = checkBaseType->mBaseType;
}
return;
}
//TODO: In the future we could be smarter about statically determining that our value hasn't escaped and eliding this
if (mayEscape)
{
if ((!IsOptimized()) && (!val.mType->IsValuelessType()) && (!mBfIRBuilder->mIgnoreWrites) && (!mCompiler->mIsResolveOnly))
{
auto nullPtrType = GetPrimitiveType(BfTypeCode_NullPtr);
bool isDyn = mCurMethodState->mCurScope->IsDyn(scopeData);
if (!isDyn)
{
const char* methodName = arraySize ? "SetDeletedArray" : "SetDeleted";
BfModuleMethodInstance dtorMethodInstance = GetInternalMethod(methodName);
BF_ASSERT(dtorMethodInstance.mMethodInstance != NULL);
if (!dtorMethodInstance.mMethodInstance)
{
Fail(StrFormat("Unable to find %s", methodName));
}
else
{
SizedArray<BfIRValue, 1> llvmArgs;
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(nullPtrType)));
llvmArgs.push_back(GetConstValue(val.mType->mSize));
llvmArgs.push_back(GetConstValue32(val.mType->mAlign));
if (arraySize)
llvmArgs.push_back(arraySize);
AddDeferredCall(dtorMethodInstance, llvmArgs, scopeData, refNode, true);
}
}
else
{
if ((arraySize) && (!arraySize.IsConst()) && (val.mType->mSize < mSystem->mPtrSize))
{
BfIRValue clearSize = arraySize;
if (val.mType->GetStride() > 1)
clearSize = mBfIRBuilder->CreateMul(clearSize, GetConstValue(val.mType->GetStride()));
const char* methodName = "SetDeletedX";
BfModuleMethodInstance dtorMethodInstance = GetInternalMethod(methodName);
BF_ASSERT(dtorMethodInstance);
if (!dtorMethodInstance)
{
Fail(StrFormat("Unable to find %s", methodName));
}
else
{
SizedArray<BfIRValue, 1> llvmArgs;
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(nullPtrType)));
llvmArgs.push_back(clearSize);
AddDeferredCall(dtorMethodInstance, llvmArgs, scopeData, refNode, true);
}
}
else
{
intptr clearSize = val.mType->mSize;
auto constant = mBfIRBuilder->GetConstant(arraySize);
if (constant != NULL)
clearSize = (intptr)(clearSize * constant->mInt64);
const char* funcName = "SetDeleted1";
if (clearSize >= 16)
funcName = "SetDeleted16";
else if (clearSize >= 8)
funcName = "SetDeleted8";
else if (clearSize >= 4)
funcName = "SetDeleted4";
BfModuleMethodInstance dtorMethodInstance = GetInternalMethod(funcName);
BF_ASSERT(dtorMethodInstance.mMethodInstance != NULL);
if (!dtorMethodInstance)
{
Fail(StrFormat("Unable to find %s", funcName));
}
else
{
SizedArray<BfIRValue, 1> llvmArgs;
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(nullPtrType)));
AddDeferredCall(dtorMethodInstance, llvmArgs, scopeData, refNode, true);
}
}
}
}
2019-08-23 11:56:54 -07:00
}
}
bool BfModule::TryLocalVariableInit(BfLocalVariable* localVar)
{
auto startTypeInstance = localVar->mResolvedType->ToTypeInstance();
if ((startTypeInstance == NULL) || (!startTypeInstance->IsStruct()))
return false;
auto checkTypeInstance = startTypeInstance;
while (checkTypeInstance != NULL)
{
for (auto& fieldInstance : checkTypeInstance->mFieldInstances)
{
if (fieldInstance.mMergedDataIdx != -1)
{
int64 checkMask = (int64)1 << fieldInstance.mMergedDataIdx;
if ((localVar->mUnassignedFieldFlags & checkMask) != 0)
{
// For fields added in extensions, we automatically initialize those if necessary
auto fieldDef = fieldInstance.GetFieldDef();
if (!fieldDef->mDeclaringType->IsExtension())
return false;
if ((fieldInstance.mDataIdx != -1) && (!mBfIRBuilder->mIgnoreWrites) && (!mCompiler->mIsResolveOnly))
{
auto curInsertBlock = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SaveDebugLocation();
if (localVar->IsParam())
mBfIRBuilder->SetInsertPointAtStart(mCurMethodState->mIRInitBlock);
else
{
BF_ASSERT(localVar->mDeclBlock);
mBfIRBuilder->SetInsertPointAtStart(localVar->mDeclBlock);
}
mBfIRBuilder->ClearDebugLocation();
BfIRValue curVal;
if (localVar->mIsThis)
curVal = mBfIRBuilder->GetArgument(0);
else
curVal = localVar->mAddr;
auto subTypeInstance = startTypeInstance;
while (subTypeInstance != checkTypeInstance)
{
curVal = mBfIRBuilder->CreateInBoundsGEP(curVal, 0, 0);
subTypeInstance = subTypeInstance->mBaseType;
}
auto fieldPtr = mBfIRBuilder->CreateInBoundsGEP(curVal, 0, fieldInstance.mDataIdx);
auto defVal = GetDefaultValue(fieldInstance.mResolvedType);
auto storeInst = mBfIRBuilder->CreateStore(defVal, fieldPtr);
mBfIRBuilder->SetInsertPoint(curInsertBlock);
mBfIRBuilder->RestoreDebugLocation();
}
localVar->mUnassignedFieldFlags &= ~checkMask;
if (localVar->mUnassignedFieldFlags == 0)
localVar->mIsAssigned = true;
}
}
}
checkTypeInstance = checkTypeInstance->mBaseType;
}
return localVar->mIsAssigned;
}
void BfModule::LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit)
{
//if (localVar->mAddr == NULL)
/*if ((localVar->mValue) && (!localVar->mAddr) && (IsTargetingBeefBackend()))
{
if ((!localVar->mValue.IsConst()) && (!localVar->mValue.IsArg()) && (!localVar->mValue.IsFake()))
{
if (mCurMethodInstance->mMethodDef->mName == "FuncA")
mBfIRBuilder->CreateLifetimeEnd(localVar->mValue);
}
}*/
BfAstNode* localNameNode = localVar->mNameNode;
if (localVar->mIsThis)
{
localNameNode = mCurMethodInstance->mMethodDef->GetRefNode();
}
if (localNameNode != NULL)
{
bool isOut = false;
if (localVar->mResolvedType->IsRef())
{
auto refType = (BfRefType*)localVar->mResolvedType;
isOut = refType->mRefKind == BfRefType::RefKind_Out;
}
if ((localVar->mReadFromId == -1) || (isOut) || ((localVar->mIsThis) && (mCurTypeInstance->IsStruct())))
{
if ((!localVar->mIsAssigned) & (localVar->IsParam()))
TryLocalVariableInit(localVar);
// We may skip processing of local methods, so we won't know if it bind to any of our local variables or not
bool deferFullAnalysis = mCurMethodState->GetRootMethodState()->mLocalMethodCache.size() != 0;
//bool deferFullAnalysis = true;
bool deferUsageWarning = deferFullAnalysis && mCompiler->IsAutocomplete();
if (!localVar->mIsAssigned)
{
if (deferUsageWarning)
{
// Ignore
}
else if (localVar->mIsThis)
{
bool foundFields = false;
auto checkTypeInstance = mCurTypeInstance;
while (checkTypeInstance != NULL)
{
for (auto& fieldInstance : checkTypeInstance->mFieldInstances)
{
if (fieldInstance.mMergedDataIdx != -1)
{
int checkMask = 1 << fieldInstance.mMergedDataIdx;
if ((localVar->mUnassignedFieldFlags & checkMask) != 0)
{
auto fieldDef = fieldInstance.GetFieldDef();
2020-04-10 07:53:56 -07:00
if (auto propertyDeclaration = BfNodeDynCast<BfPropertyDeclaration>(fieldDef->mFieldDeclaration))
{
String propName;
if (propertyDeclaration->mNameNode != NULL)
propertyDeclaration->mNameNode->ToString(propName);
if (checkTypeInstance == mCurTypeInstance)
{
Fail(StrFormat("Auto-implemented property '%s' must be fully assigned before control is returned to the caller",
propName.c_str()), localNameNode, deferFullAnalysis); // 0171
}
else
{
Fail(StrFormat("Auto-implemented property '%s.%s' must be fully assigned before control is returned to the caller",
TypeToString(checkTypeInstance).c_str(),
propName.c_str()), localNameNode, deferFullAnalysis); // 0171
}
}
else
2019-08-23 11:56:54 -07:00
{
if (checkTypeInstance == mCurTypeInstance)
{
Fail(StrFormat("Field '%s' must be fully assigned before control is returned to the caller",
fieldDef->mName.c_str()), localNameNode, deferFullAnalysis); // 0171
}
else
{
Fail(StrFormat("Field '%s.%s' must be fully assigned before control is returned to the caller",
TypeToString(checkTypeInstance).c_str(),
fieldDef->mName.c_str()), localNameNode, deferFullAnalysis); // 0171
}
}
foundFields = true;
}
}
}
checkTypeInstance = checkTypeInstance->mBaseType;
}
if (!foundFields)
{
if (mCurTypeInstance->mInstSize > 0)
Fail(StrFormat("The variable '%s' must be fully assigned to before control leaves the current method", localVar->mName.c_str()), localNameNode); // 0177
}
}
else if (localVar->IsParam())
Fail(StrFormat("The out parameter '%s' must be assigned to before control leaves the current method", localVar->mName.c_str()), localNameNode, deferFullAnalysis); // 0177
else if (!deferUsageWarning)
Warn(BfWarning_CS0168_VariableDeclaredButNeverUsed, StrFormat("The variable '%s' is declared but never used", localVar->mName.c_str()), localNameNode, deferFullAnalysis);
}
else if ((localVar->mReadFromId == -1) && (!localVar->IsParam()) && (!deferUsageWarning))
Warn(BfWarning_CS0168_VariableDeclaredButNeverUsed, StrFormat("The variable '%s' is assigned but its value is never used", localVar->mName.c_str()), localNameNode, deferFullAnalysis);
}
}
}
void BfModule::MarkDynStack(BfScopeData* scopeData)
{
auto checkScope = mCurMethodState->mCurScope;
while (true)
{
if (checkScope == scopeData)
break;
checkScope->mHadOuterDynStack = true;
checkScope = checkScope->mPrevScope;
}
}
void BfModule::SaveStackState(BfScopeData* scopeData)
{
// If we push a value on the stack that needs to survive until the end of scopeData, we need to make sure that no
// scopes within scopeData restore the stack
auto checkScope = mCurMethodState->mCurScope;
while (true)
{
if (checkScope == scopeData)
{
if ((!checkScope->mSavedStack) && (checkScope->mBlock) && (!checkScope->mIsScopeHead) && (!checkScope->mHadOuterDynStack))
{
if (mBfIRBuilder->mHasDebugInfo)
mBfIRBuilder->SaveDebugLocation();
auto prevPos = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPointAtStart(checkScope->mBlock);
checkScope->mSavedStack = mBfIRBuilder->CreateStackSave();
mBfIRBuilder->ClearDebugLocation(checkScope->mSavedStack);
mBfIRBuilder->SetInsertPoint(prevPos);
if (mBfIRBuilder->mHasDebugInfo)
mBfIRBuilder->RestoreDebugLocation();
}
break;
}
if (checkScope->mSavedStack)
{
for (auto usage : checkScope->mSavedStackUses)
mBfIRBuilder->EraseInstFromParent(usage);
checkScope->mSavedStackUses.Clear();
2019-08-23 11:56:54 -07:00
mBfIRBuilder->EraseInstFromParent(checkScope->mSavedStack);
checkScope->mSavedStack = BfIRValue();
2019-08-23 11:56:54 -07:00
}
checkScope = checkScope->mPrevScope;
}
}
BfIRValue BfModule::ValueScopeStart()
{
if (IsTargetingBeefBackend())
return mBfIRBuilder->CreateValueScopeStart();
return BfIRValue();
}
void BfModule::ValueScopeEnd(BfIRValue valueScopeStart)
{
if (valueScopeStart)
mBfIRBuilder->CreateValueScopeHardEnd(valueScopeStart);
}
BfFileInstance* BfModule::GetFileFromNode(BfAstNode* astNode)
{
auto bfParser = astNode->GetSourceData()->ToParserData();
if (bfParser == NULL)
return NULL;
BfFileInstance** fileInstancePtr = NULL;
if (!mFileInstanceMap.TryAdd(bfParser, NULL, &fileInstancePtr))
{
return *fileInstancePtr;
}
else
{
// It's possible two parsers have the same file name (ie: mNextRevision)
BfFileInstance** namedFileInstancePtr = NULL;
if (!mNamedFileInstanceMap.TryAdd(bfParser->mFileName, NULL, &namedFileInstancePtr))
{
auto bfFileInstance = *namedFileInstancePtr;
*fileInstancePtr = bfFileInstance;
return bfFileInstance;
}
int slashPos = (int)bfParser->mFileName.LastIndexOf(DIR_SEP_CHAR);
auto bfFileInstance = new BfFileInstance();
*fileInstancePtr = bfFileInstance;
*namedFileInstancePtr = bfFileInstance;
bfFileInstance->mPrevPosition.mFileInstance = bfFileInstance;
bfFileInstance->mParser = bfParser;
BfLogSysM("GetFileFromNode new file. Mod: %p FileInstance: %p Parser: %p\n", this, bfFileInstance, bfParser);
if ((mBfIRBuilder != NULL) && (mBfIRBuilder->DbgHasInfo()))
{
String fileName = bfParser->mFileName;
for (int i = 0; i < (int)fileName.length(); i++)
{
if (fileName[i] == DIR_SEP_CHAR_ALT)
fileName[i] = DIR_SEP_CHAR;
}
2020-03-23 12:07:05 -07:00
bfFileInstance->mDIFile = mBfIRBuilder->DbgCreateFile(fileName.Substring(slashPos + 1), fileName.Substring(0, slashPos), bfParser->mMD5Hash);
2019-08-23 11:56:54 -07:00
}
return bfFileInstance;
}
}
void BfModule::UpdateSrcPos(BfAstNode* astNode, BfSrcPosFlags flags, int debugLocOffset)
{
// We set force to true when we MUST get the line position set (for navigation purposes, for example)
if (((mBfIRBuilder->mIgnoreWrites)) && ((flags & BfSrcPosFlag_Force) == 0))
return;
BF_ASSERT((!mAwaitingInitFinish) || (mBfIRBuilder->mIgnoreWrites));
if (astNode == NULL)
return;
auto bfParser = astNode->GetSourceData()->ToParserData();
if (bfParser == NULL)
return;
if ((mCurFilePosition.mFileInstance == NULL) || (mCurFilePosition.mFileInstance->mParser != bfParser))
{
if (mCurFilePosition.mFileInstance != NULL)
{
BF_ASSERT(mFileInstanceMap.GetCount() != 0);
mCurFilePosition.mFileInstance->mPrevPosition = mCurFilePosition;
}
auto bfFileInstance = GetFileFromNode(astNode);
if (bfFileInstance->mPrevPosition.mFileInstance != NULL)
{
mCurFilePosition = bfFileInstance->mPrevPosition;
}
else
{
mCurFilePosition.mFileInstance = bfFileInstance;
mCurFilePosition.mCurLine = 0;
mCurFilePosition.mCurSrcPos = 0;
}
}
int srcPos = astNode->GetSrcStart() + debugLocOffset;
BF_ASSERT(srcPos < bfParser->mSrcLength);
auto* jumpEntry = bfParser->mJumpTable + (srcPos / PARSER_JUMPTABLE_DIVIDE);
if (jumpEntry->mCharIdx > srcPos)
jumpEntry--;
mCurFilePosition.mCurLine = jumpEntry->mLineNum;
mCurFilePosition.mCurSrcPos = jumpEntry->mCharIdx;
mCurFilePosition.mCurColumn = 0;
while (mCurFilePosition.mCurSrcPos < srcPos)
{
if (bfParser->mSrc[mCurFilePosition.mCurSrcPos] == '\n')
{
mCurFilePosition.mCurLine++;
mCurFilePosition.mCurColumn = 0;
}
else
{
mCurFilePosition.mCurColumn++;
}
mCurFilePosition.mCurSrcPos++;
}
//TODO: if we bail on the "mCurMethodState == NULL" case then we don't get it set during type declarations
if (((flags & BfSrcPosFlag_NoSetDebugLoc) == 0) && (mBfIRBuilder->DbgHasInfo()) && (mCurMethodState != NULL))
{
int column = mCurFilePosition.mCurColumn + 1;
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend))
{
// Set to illegal position
column = 0;
}
if (mSetIllegalSrcPosition)
column = 0;
BfIRMDNode useDIScope = mCurMethodState->mCurScope->mDIScope;
auto wantDIFile = mCurFilePosition.mFileInstance->mDIFile;
if ((wantDIFile != mCurMethodState->mDIFile) && (mCurMethodState->mDIFile))
{
// This is from a different scope...
if (wantDIFile != mCurMethodState->mCurScope->mAltDIFile)
{
mCurMethodState->mCurScope->mAltDIFile = wantDIFile;
mCurMethodState->mCurScope->mAltDIScope = mBfIRBuilder->DbgCreateLexicalBlock(mCurMethodState->mCurScope->mDIScope, wantDIFile, mCurFilePosition.mCurLine + 1, column);
}
if (mCurMethodState->mCurScope->mAltDIScope) // This may not be set if mAltDIFile is set by a mixin
useDIScope = mCurMethodState->mCurScope->mAltDIScope;
}
else
{
if (mCurMethodState->mCurScope->mAltDIFile)
{
mCurMethodState->mCurScope->mAltDIFile = BfIRMDNode();
mCurMethodState->mCurScope->mAltDIScope = BfIRMDNode();
}
}
auto inlineAt = mCurMethodState->mCurScope->mDIInlinedAt;
if (mCurMethodState->mCrossingMixin)
inlineAt = BfIRMDNode();
mBfIRBuilder->SetCurrentDebugLocation(mCurFilePosition.mCurLine + 1, column, useDIScope, inlineAt);
if ((flags & BfSrcPosFlag_Expression) == 0)
mBfIRBuilder->CreateStatementStart();
}
}
void BfModule::UpdateExprSrcPos(BfAstNode* astNode, BfSrcPosFlags flags)
{
// We've turned off expr src pos (for now?)
//UpdateSrcPos(astNode, (BfSrcPosFlags)(flags | BfSrcPosFlag_Expression));
}
void BfModule::UseDefaultSrcPos(BfSrcPosFlags flags, int debugLocOffset)
{
UpdateSrcPos(mCompiler->mBfObjectTypeDef->mTypeDeclaration, flags, debugLocOffset);
SetIllegalSrcPos();
2019-08-23 11:56:54 -07:00
}
void BfModule::SetIllegalSrcPos(BfSrcPosFlags flags)
{
if ((mBfIRBuilder->DbgHasInfo()) && (mCurMethodState != NULL))
{
if ((mCurMethodState->mCurScope->mDIInlinedAt) && (mCompiler->mOptions.IsCodeView()))
{
// Currently, CodeView does not record column positions so we can't use an illegal column position as an "invalid" marker
mBfIRBuilder->SetCurrentDebugLocation(0, 0, mCurMethodState->mCurScope->mDIScope, mCurMethodState->mCurScope->mDIInlinedAt);
}
else
{
// Set to whatever it previously was but at column zero, which we will know to be illegal
mBfIRBuilder->SetCurrentDebugLocation(mCurFilePosition.mCurLine + 1, 0, mCurMethodState->mCurScope->mDIScope, mCurMethodState->mCurScope->mDIInlinedAt);
}
if ((flags & BfSrcPosFlag_Expression) == 0)
mBfIRBuilder->CreateStatementStart();
}
}
void BfModule::SetIllegalExprSrcPos(BfSrcPosFlags flags)
{
// We've turned off expr src pos (for now?)
//SetIllegalSrcPos((BfSrcPosFlags)(flags | BfSrcPosFlag_Expression));
}
bool BfModule::CheckProtection(BfProtection protection, bool allowProtected, bool allowPrivate)
{
if ((protection == BfProtection_Public) ||
((protection == BfProtection_Protected) && (allowProtected)) ||
((protection == BfProtection_Private) && (allowPrivate)))
return true;
if ((mAttributeState != NULL) && (mAttributeState->mCustomAttributes != NULL) && (mAttributeState->mCustomAttributes->Contains(mCompiler->mFriendAttributeTypeDef)))
return true;
return false;
}
void BfModule::GetAccessAllowed(BfTypeInstance* checkType, bool &allowProtected, bool &allowPrivate)
{
allowPrivate = (checkType == mCurTypeInstance) || (IsInnerType(mCurTypeInstance, checkType));
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL))
{
auto mixinOwner = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner();
allowPrivate |= (checkType == mixinOwner) || (IsInnerType(mixinOwner, checkType));
}
allowProtected = allowPrivate;
}
bool BfModule::CheckProtection(BfProtectionCheckFlags& flags, BfTypeInstance* memberOwner, BfProject* memberProject, BfProtection memberProtection, BfTypeInstance* lookupStartType)
{
2019-08-23 11:56:54 -07:00
if (memberProtection == BfProtection_Hidden)
return false;
if (memberProtection == BfProtection_Public)
return true;
if ((flags & BfProtectionCheckFlag_CheckedPrivate) == 0)
{
BfTypeInstance* curCheckType = mCurTypeInstance;
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL))
{
auto mixinOwner = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner();
curCheckType = mixinOwner;
}
2020-04-30 14:37:39 -07:00
bool allowPrivate = (curCheckType != NULL) && (memberOwner->mTypeDef == curCheckType->mTypeDef);
if (curCheckType != NULL)
allowPrivate |= IsInnerType(curCheckType->mTypeDef, memberOwner->mTypeDef);
2019-08-23 11:56:54 -07:00
if (allowPrivate)
flags = (BfProtectionCheckFlags)(flags | BfProtectionCheckFlag_AllowPrivate | BfProtectionCheckFlag_CheckedPrivate);
else
flags = (BfProtectionCheckFlags)(flags | BfProtectionCheckFlag_CheckedPrivate);
}
if ((flags & BfProtectionCheckFlag_AllowPrivate) != 0)
return true;
if (memberProtection == BfProtection_Protected)
{
if ((flags & BfProtectionCheckFlag_CheckedProtected) == 0)
{
bool allowProtected = false;
auto memberOwnerTypeDef = memberOwner->mTypeDef;
auto curCheckType = mCurTypeInstance;
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL))
{
auto mixinOwner = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner();
curCheckType = mixinOwner;
}
auto lookupCheckType = lookupStartType;
while (lookupCheckType->mInheritDepth >= curCheckType->mInheritDepth)
{
if (lookupCheckType == curCheckType)
{
allowProtected = true;
break;
}
if (lookupCheckType == memberOwner)
break;
lookupCheckType = lookupCheckType->mBaseType;
if (lookupCheckType == NULL)
break;
}
if (!allowProtected)
{
// It's possible our outer type is derived from 'memberOwner'
while (curCheckType->mTypeDef->mNestDepth > 0)
{
curCheckType = GetOuterType(curCheckType);
if (curCheckType == NULL)
break;
auto lookupCheckType = lookupStartType;
while (lookupCheckType->mInheritDepth >= curCheckType->mInheritDepth)
{
if (lookupCheckType == curCheckType)
{
allowProtected = true;
break;
}
if (lookupCheckType == memberOwner)
break;
lookupCheckType = lookupCheckType->mBaseType;
if (lookupCheckType == NULL)
break;
}
}
}
if (allowProtected)
flags = (BfProtectionCheckFlags)(flags | BfProtectionCheckFlag_CheckedProtected | BfProtectionCheckFlag_AllowProtected);
else
flags = (BfProtectionCheckFlags)(flags | BfProtectionCheckFlag_CheckedProtected);
}
if ((flags & BfProtectionCheckFlag_AllowProtected) != 0)
return true;
}
if ((mAttributeState != NULL) && (mAttributeState->mCustomAttributes != NULL) && (mAttributeState->mCustomAttributes->Contains(mCompiler->mFriendAttributeTypeDef)))
return true;
return false;
}
void BfModule::SetElementType(BfAstNode* astNode, BfSourceElementType elementType)
{
if ((mCompiler->mResolvePassData != NULL) &&
(mCompiler->mResolvePassData->mSourceClassifier != NULL) &&
(astNode->IsFromParser( mCompiler->mResolvePassData->mParser)))
{
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(astNode, elementType);
}
}
BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPersistent)
{
BP_ZONE("BfModule::Fail");
if (mIgnoreErrors)
{
2019-08-23 11:56:54 -07:00
return NULL;
}
if (!mReportErrors)
{
mCompiler->mPassInstance->SilentFail();
return NULL;
}
if (refNode != NULL)
refNode = BfNodeToNonTemporary(refNode);
//BF_ASSERT(refNode != NULL);
if (mCurMethodInstance != NULL)
mCurMethodInstance->mHasFailed = true;
if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsUnspecializedTypeVariation()))
return NULL;
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecializedVariation))
return NULL; // Ignore errors on unspecialized variations, they are always dups
if (!mHadBuildError)
2019-08-23 11:56:54 -07:00
mHadBuildError = true;
if (mParentModule != NULL)
mParentModule->mHadBuildError = true;
if (mCurTypeInstance != NULL)
AddFailType(mCurTypeInstance);
BfLogSysM("BfModule::Fail module %p type %p %s\n", this, mCurTypeInstance, error.c_str());
String errorString = error;
2019-08-23 11:56:54 -07:00
bool isWhileSpecializing = false;
bool isWhileSpecializingMethod = false;
if ((mIsSpecialModule) && (mModuleName == "vdata"))
errorString += StrFormat("\n while generating vdata for project '%s'", mProject->mName.c_str());
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend))
errorString += StrFormat("\n while generating append size calculating method");
else if (refNode == NULL)
{
if (mCurTypeInstance != NULL)
errorString += StrFormat("\n while compiling '%s'", TypeToString(mCurTypeInstance, BfTypeNameFlags_None).c_str());
else if (mProject != NULL)
errorString += StrFormat("\n while compiling project '%s'", mProject->mName.c_str());
}
2019-08-23 11:56:54 -07:00
if (mCurTypeInstance != NULL)
{
auto _CheckMethodInstance = [&](BfMethodInstance* methodInstance)
2019-08-23 11:56:54 -07:00
{
// Propogatea the fail all the way to the main method (assuming we're in a local method or lambda)
methodInstance->mHasFailed = true;
bool isSpecializedMethod = ((methodInstance != NULL) && (!methodInstance->mIsUnspecialized) && (methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mMethodGenericArguments.size() != 0));
if (isSpecializedMethod)
{
//auto unspecializedMethod = &mCurMethodInstance->mMethodInstanceGroup->mMethodSpecializationMap.begin()->second;
auto unspecializedMethod = methodInstance->mMethodInstanceGroup->mDefault;
if (unspecializedMethod->mHasFailed)
return false; // At least SOME error has already been reported
2019-08-23 11:56:54 -07:00
}
if (isSpecializedMethod)
{
errorString += StrFormat("\n while specializing method '%s'", MethodToString(methodInstance).c_str());
isWhileSpecializing = true;
isWhileSpecializingMethod = true;
}
else if ((methodInstance != NULL) && (methodInstance->mIsForeignMethodDef))
{
errorString += StrFormat("\n while implementing default interface method '%s'", MethodToString(methodInstance).c_str());
isWhileSpecializing = true;
isWhileSpecializingMethod = true;
}
else if ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType()))
{
errorString += StrFormat("\n while specializing type '%s'", TypeToString(mCurTypeInstance).c_str());
isWhileSpecializing = true;
}
return true;
};
2019-08-23 11:56:54 -07:00
if (mCurMethodState != NULL)
{
auto checkMethodState = mCurMethodState;
while (checkMethodState != NULL)
{
auto methodInstance = checkMethodState->mMethodInstance;
if (methodInstance == NULL)
{
checkMethodState = checkMethodState->mPrevMethodState;
continue;
}
if (!_CheckMethodInstance(methodInstance))
return NULL;
checkMethodState = checkMethodState->mPrevMethodState;
}
}
else if (mCurMethodInstance != NULL)
{
if (!_CheckMethodInstance(mCurMethodInstance))
return NULL;
2019-08-23 11:56:54 -07:00
}
// Keep in mind that all method specializations with generic type instances as its method generic params
// need to be deferred because the specified generic type instance might get deleted at the end of the
// compilation due to no longer having indirect references removed, and we have to ignore errors from
// those method specializations if that occurs
if (isWhileSpecializing)
{
BfError* bfError = mCompiler->mPassInstance->DeferFail(errorString, refNode);
if (bfError != NULL)
bfError->mIsWhileSpecializing = isWhileSpecializing;
return bfError;
}
}
2019-11-28 10:00:29 -08:00
if ((!isWhileSpecializing) && (mCurTypeInstance != NULL) && ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType())))
{
errorString += StrFormat("\n while specializing type '%s'", TypeToString(mCurTypeInstance).c_str());
isWhileSpecializing = true;
}
2019-08-23 11:56:54 -07:00
if (!mHadBuildError)
mHadBuildError = true;
// Check mixins
{
auto checkMethodInstance = mCurMethodState;
while (checkMethodInstance != NULL)
{
auto rootMixinState = checkMethodInstance->GetRootMixinState();
if (rootMixinState != NULL)
{
BfError* bfError = mCompiler->mPassInstance->Fail(StrFormat("Failed to inject mixin '%s'", MethodToString(rootMixinState->mMixinMethodInstance).c_str()), rootMixinState->mSource);
mCompiler->mPassInstance->MoreInfo(errorString, refNode);
auto mixinState = checkMethodInstance->mMixinState;
while ((mixinState != NULL) && (mixinState->mPrevMixinState != NULL))
{
mCompiler->mPassInstance->MoreInfo(StrFormat("Injected from mixin '%s'", MethodToString(mixinState->mPrevMixinState->mMixinMethodInstance).c_str()), mixinState->mSource);
mixinState = mixinState->mPrevMixinState;
}
return bfError;
}
checkMethodInstance = checkMethodInstance->mPrevMethodState;
}
}
BfError* bfError = NULL;
if (refNode == NULL)
bfError = mCompiler->mPassInstance->Fail(errorString);
else
{
bfError = mCompiler->mPassInstance->Fail(errorString, refNode);
}
if (bfError != NULL)
{
bfError->mProject = mProject;
2019-08-23 11:56:54 -07:00
bfError->mIsPersistent = isPersistent;
bfError->mIsWhileSpecializing = isWhileSpecializing;
if ((mCurMethodState != NULL) && (mCurMethodState->mDeferredCallEmitState != NULL) && (mCurMethodState->mDeferredCallEmitState->mCloseNode != NULL))
mCompiler->mPassInstance->MoreInfo("Error during deferred statement handling", mCurMethodState->mDeferredCallEmitState->mCloseNode);
2019-08-23 11:56:54 -07:00
}
return bfError;
}
BfError* BfModule::FailInternal(const StringImpl& error, BfAstNode* refNode)
{
if (mHadBuildError)
return NULL;
return Fail(error, refNode);
}
2019-08-23 11:56:54 -07:00
BfError* BfModule::FailAfter(const StringImpl& error, BfAstNode* refNode)
{
if (mIgnoreErrors)
return NULL;
if (refNode != NULL)
refNode = BfNodeToNonTemporary(refNode);
mHadBuildError = true;
BfError* bfError = mCompiler->mPassInstance->FailAfter(error, refNode);
if (bfError != NULL)
bfError->mProject = mProject;
return bfError;
2019-08-23 11:56:54 -07:00
}
BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* refNode, bool isPersistent)
{
if (mIgnoreErrors || mIgnoreWarnings)
return NULL;
if (!mReportErrors)
{
mCompiler->mPassInstance->SilentFail();
return NULL;
}
BfAstNode* unwarnNode = refNode;
//
{
BfParentNodeEntry* parentNodeEntry = mParentNodeEntry;
while (parentNodeEntry != NULL)
{
if (auto block = BfNodeDynCast<BfBlock>(parentNodeEntry->mNode))
break;
unwarnNode = parentNodeEntry->mNode;
parentNodeEntry = parentNodeEntry->mPrev;
}
}
auto parser = unwarnNode->GetSourceData()->ToParserData();
if ((parser != NULL) && (parser->IsUnwarnedAt(unwarnNode)))
{
return NULL;
}
// Right now we're only warning on the unspecialized declarations, we may revisit this
if (mCurMethodInstance != NULL)
{
if (mCurMethodInstance->IsSpecializedGenericMethodOrType())
return NULL;
if (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend)
return NULL; // No ctorCalcAppend warnings
}
if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsSpecializedType()))
return NULL;
if (refNode != NULL)
refNode = BfNodeToNonTemporary(refNode);
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL))
{
// We used to bubble up warnings into the mixin injection site, BUT
// we are now assuming any relevant warnings will be given at the declaration site
return NULL;
}
BfError* bfError;
if (refNode != NULL)
bfError = mCompiler->mPassInstance->WarnAt(warningNum, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength());
else
bfError = mCompiler->mPassInstance->Warn(warningNum, warning);
2019-08-23 11:56:54 -07:00
if (bfError != NULL)
{
bfError->mProject = mProject;
2019-08-23 11:56:54 -07:00
AddFailType(mCurTypeInstance);
mHadBuildWarning = true;
bfError->mIsPersistent = isPersistent;
if ((mCompiler->IsAutocomplete()) && (mCompiler->mResolvePassData->mAutoComplete->CheckFixit((refNode))))
{
BfParserData* parser = refNode->GetSourceData()->ToParserData();
if (parser != NULL)
{
int fileLoc = BfFixitFinder::FindLineStartBefore(refNode);
2020-01-30 10:25:16 -08:00
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("#unwarn\tunwarn|%s|%d|#unwarn|", parser->mFileName.c_str(), fileLoc).c_str()));
if (warningNum != 0)
{
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("#pragma warning disable %d\tusing|%s|%d||#pragma warning disable %d",
warningNum, parser->mFileName.c_str(), 0, warningNum).c_str()));
}
}
2019-08-23 11:56:54 -07:00
}
}
return bfError;
}
void BfModule::CheckErrorAttributes(BfTypeInstance* typeInstance, BfMethodInstance* methodInstance, BfCustomAttributes* customAttributes, BfAstNode* targetSrc)
{
auto _AddDeclarationMoreInfo = [&]()
{
if (methodInstance != NULL)
{
if (methodInstance->mMethodDef->mMethodDeclaration != NULL)
mCompiler->mPassInstance->MoreInfo(
StrFormat("See method declaration '%s'", MethodToString(methodInstance).c_str()),
methodInstance->mMethodDef->GetRefNode());
}
else
{
mCompiler->mPassInstance->MoreInfo(
StrFormat("See type declaration '%s'", TypeToString(typeInstance, BfTypeNameFlag_UseUnspecializedGenericParamNames).c_str()),
typeInstance->mTypeDef->GetRefNode());
}
};
BfIRConstHolder* constHolder = typeInstance->mConstHolder;
auto customAttribute = customAttributes->Get(mCompiler->mObsoleteAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()))
{
String err;
if (methodInstance != NULL)
err = StrFormat("'%s' is obsolete", MethodToString(methodInstance).c_str());
else
err = StrFormat("'%s' is obsolete", TypeToString(typeInstance, BfTypeNameFlag_UseUnspecializedGenericParamNames).c_str());
bool isError = false;
auto constant = constHolder->GetConstant(customAttribute->mCtorArgs[0]);
if (constant->mTypeCode == BfTypeCode_Boolean)
{
isError = constant->mBool;
}
else if (customAttribute->mCtorArgs.size() >= 2)
{
String* str = GetStringPoolString(customAttribute->mCtorArgs[0], constHolder);
if (str != NULL)
{
err += ":\n '";
err += *str;
err += "'";
}
constant = constHolder->GetConstant(customAttribute->mCtorArgs[1]);
isError = constant->mBool;
}
BfError* error = NULL;
if (isError)
error = Fail(err, targetSrc);
else
error = Warn(0, err, targetSrc);
if (error != NULL)
_AddDeclarationMoreInfo();
}
customAttribute = customAttributes->Get(mCompiler->mErrorAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()))
{
String err;
if (methodInstance != NULL)
StrFormat("Method error: '", MethodToString(methodInstance).c_str());
else
StrFormat("Type error: '", TypeToString(typeInstance, BfTypeNameFlag_UseUnspecializedGenericParamNames).c_str());
String* str = GetStringPoolString(customAttribute->mCtorArgs[0], constHolder);
if (str != NULL)
err += *str;
err += "'";
if (Fail(err, targetSrc) != NULL)
_AddDeclarationMoreInfo();
}
customAttribute = customAttributes->Get(mCompiler->mWarnAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()))
{
String err;
if (methodInstance != NULL)
StrFormat("Method warning: '", MethodToString(methodInstance).c_str());
else
StrFormat("Type warning: '", TypeToString(typeInstance, BfTypeNameFlag_UseUnspecializedGenericParamNames).c_str());
String* str = GetStringPoolString(customAttribute->mCtorArgs[0], constHolder);
if (str != NULL)
err += *str;
err += "'";
if (Warn(0, err, targetSrc) != NULL)
_AddDeclarationMoreInfo();
}
}
2019-08-23 11:56:54 -07:00
void BfModule::CheckRangeError(BfType* type, BfAstNode* refNode)
{
if (mBfIRBuilder->mOpFailed)
Fail(StrFormat("Result out of range for type '%s'", TypeToString(type).c_str()), refNode);
}
void BfModule::FatalError(const StringImpl& error, const char* file, int line)
{
String fullError = error;
if (file != NULL)
fullError += StrFormat(" at %s:%d", file, line);
fullError += StrFormat("\nModule: %s", mModuleName.c_str());
if (mCurTypeInstance != NULL)
fullError += StrFormat("\nType: %s", TypeToString(mCurTypeInstance).c_str());
if (mCurMethodInstance != NULL)
fullError += StrFormat("\nMethod: %s", MethodToString(mCurMethodInstance).c_str());
if ((mCurFilePosition.mFileInstance != NULL) && (mCurFilePosition.mFileInstance->mParser != NULL))
fullError += StrFormat("\nSource Location: %s:%d", mCurFilePosition.mFileInstance->mParser->mFileName.c_str(), mCurFilePosition.mCurLine + 1);
BfpSystem_FatalError(fullError.c_str(), "FATAL MODULE ERROR");
}
2019-08-23 11:56:54 -07:00
void BfModule::NotImpl(BfAstNode* astNode)
{
Fail("INTERNAL ERROR: Not implemented", astNode);
}
bool BfModule::CheckAccessMemberProtection(BfProtection protection, BfType* memberType)
{
bool allowPrivate = (memberType == mCurTypeInstance) || (IsInnerType(mCurTypeInstance, memberType));
if (!allowPrivate)
allowPrivate |= memberType->IsInterface();
bool allowProtected = allowPrivate;
//TODO: We had this commented out, but this makes accessing protected properties fail
if (mCurTypeInstance != NULL)
allowProtected |= TypeIsSubTypeOf(mCurTypeInstance, memberType->ToTypeInstance());
2019-08-23 11:56:54 -07:00
if (!CheckProtection(protection, allowProtected, allowPrivate))
{
return false;
}
return true;
}
bool BfModule::CheckDefineMemberProtection(BfProtection protection, BfType* memberType)
{
// Use 'min' - exporting a 'public' from a 'private' class is really just 'private' still
protection = std::min(protection, mCurTypeInstance->mTypeDef->mProtection);
auto memberTypeInstance = memberType->ToTypeInstance();
if (memberTypeInstance == NULL)
{
auto underlyingType = memberType->GetUnderlyingType();
if (underlyingType != NULL)
return CheckDefineMemberProtection(protection, underlyingType);
return true;
}
if (memberTypeInstance->mTypeDef->mProtection < protection)
return false;
if (memberTypeInstance->IsGenericTypeInstance())
{
// When we're a generic struct, our data layout can depend on our generic parameters as well
auto genericTypeInstance = (BfTypeInstance*) memberTypeInstance;
for (auto typeGenericArg : genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments)
2019-08-23 11:56:54 -07:00
{
if (!CheckDefineMemberProtection(protection, typeGenericArg))
return false;
}
}
return true;
}
void BfModule::AddDependency(BfType* usedType, BfType* userType, BfDependencyMap::DependencyDependencyFlag flags)
{
if (usedType == userType)
2019-08-23 11:56:54 -07:00
return;
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsAutocompleteMethod))
return;
if (usedType->IsSpecializedByAutoCompleteMethod())
return;
// if (usedType->IsBoxed())
// {
// NOP;
// auto underlyingType = usedType->GetUnderlyingType()->ToTypeInstance();
// if ((underlyingType != NULL) && (underlyingType->IsInstanceOf(mCompiler->mSizedArrayTypeDef)))
// {
// BfLogSysM("AddDependency UsedType:%p UserType:%p Method:%p\n", usedType, userType, mCurMethodInstance);
// }
// }
BfType* origUsedType = usedType;
bool isDataAccess = ((flags & (BfDependencyMap::DependencyFlag_ReadFields | BfDependencyMap::DependencyFlag_LocalUsage | BfDependencyMap::DependencyFlag_Allocates)) != 0);
if (isDataAccess)
2019-08-23 11:56:54 -07:00
{
while (true)
{
if ((usedType->IsPointer()) || (usedType->IsRef()) || (usedType->IsSizedArray()))
usedType = usedType->GetUnderlyingType();
else if (usedType->IsArray())
2019-08-23 11:56:54 -07:00
{
usedType = ((BfTypeInstance*)usedType)->mGenericTypeInfo->mTypeGenericArguments[0];
2019-08-23 11:56:54 -07:00
}
else
break;
}
}
if ((mCurMethodState != NULL) && (mCurMethodState->mHotDataReferenceBuilder != NULL) && (usedType != mCurTypeInstance) && (isDataAccess))
{
bool addType = true;
auto checkType = usedType;
2019-08-23 11:56:54 -07:00
PopulateType(checkType, BfPopulateType_Data);
if (checkType->IsValuelessType())
addType = false;
else if (checkType->IsPrimitiveType())
addType = false;
else
{
auto checkTypeInst = checkType->ToTypeInstance();
if (checkTypeInst == NULL)
{
addType = false;
}
else
{
if (TypeIsSubTypeOf(mCurTypeInstance, checkTypeInst))
addType = false;
}
}
if (addType)
{
auto checkTypeInst = checkType->ToTypeInstance();
auto hotTypeVersion = checkTypeInst->mHotTypeData->GetLatestVersion();
BF_ASSERT(hotTypeVersion != NULL);
bool isAllocation = ((flags & BfDependencyMap::DependencyFlag_Allocates) != 0);
if (((flags & BfDependencyMap::DependencyFlag_LocalUsage) != 0) &&
(checkType->IsComposite()))
isAllocation = true;
if (isAllocation)
{
mCurMethodState->mHotDataReferenceBuilder->mAllocatedData.Add(hotTypeVersion);
}
else
mCurMethodState->mHotDataReferenceBuilder->mUsedData.Add(hotTypeVersion);
}
}
if ((!mCompiler->mIsResolveOnly) && (mIsReified))
{
auto usingModule = userType->GetModule();
2019-08-23 11:56:54 -07:00
BfModule* usedModule;
if (usedType->IsFunction())
{
auto typeInst = usedType->ToTypeInstance();
if (typeInst->mBaseType == NULL)
PopulateType(typeInst);
usedModule = typeInst->mBaseType->GetModule();
}
else
usedModule = usedType->GetModule();
if ((usingModule != NULL) && (!usingModule->mIsSpecialModule) &&
(usedModule != NULL) && (!usedModule->mIsSpecialModule))
{
usingModule->mModuleRefs.Add(usedModule);
}
}
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodInfoEx != NULL) && (flags != BfDependencyMap::DependencyFlag_MethodGenericArg))
{
// When we are specializing a method, usage of that specialized type is already handled with DependencyFlag_MethodGenericArg
for (auto genericArg : mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments)
if (genericArg == usedType)
return;
}
auto underlyingType = usedType->GetUnderlyingType();
if (underlyingType != NULL) // Not really a "GenericArg", but... same purpose.
AddDependency(underlyingType, userType, BfDependencyMap::DependencyFlag_GenericArgRef);
2019-08-23 11:56:54 -07:00
BfDependedType* checkDType = usedType->ToDependedType();
if (checkDType == NULL)
return;
if ((usedType->mRebuildFlags & BfTypeRebuildFlag_AwaitingReference) != 0)
mContext->MarkAsReferenced(checkDType);
if (!checkDType->mDependencyMap.AddUsedBy(userType, flags))
return;
2019-08-23 11:56:54 -07:00
if (checkDType->IsGenericTypeInstance())
{
auto genericTypeInstance = (BfTypeInstance*) checkDType;
for (auto genericArg : genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments)
2019-08-23 11:56:54 -07:00
{
AddDependency(genericArg, userType, BfDependencyMap::DependencyFlag_GenericArgRef);
2019-08-23 11:56:54 -07:00
}
}
if (checkDType->IsTuple())
{
for (auto& field : checkDType->ToTypeInstance()->mFieldInstances)
{
if (field.mDataIdx != -1)
AddDependency(field.mResolvedType, userType, BfDependencyMap::DependencyFlag_GenericArgRef);
}
}
2019-08-23 11:56:54 -07:00
}
void BfModule::AddCallDependency(BfMethodInstance* methodInstance, bool devirtualized)
{
if ((mCurMethodState != NULL) && (mCurMethodState->mHotDataReferenceBuilder != NULL))
{
if (methodInstance->mHotMethod == NULL)
CheckHotMethod(methodInstance, "");
BF_ASSERT(methodInstance->mHotMethod != NULL);
if (devirtualized)
mCurMethodState->mHotDataReferenceBuilder->mDevirtualizedCalledMethods.Add(methodInstance->mHotMethod);
else
mCurMethodState->mHotDataReferenceBuilder->mCalledMethods.Add(methodInstance->mHotMethod);
}
}
bool BfModule::IsAttribute(BfTypeInstance* typeInst)
{
auto checkTypeInst = typeInst;
while (checkTypeInst != NULL)
{
if (checkTypeInst->mTypeDef == mCompiler->mAttributeTypeDef)
return true;
checkTypeInst = checkTypeInst->mBaseType;
}
return false;
}
void BfModule::PopulateGlobalContainersList(const BfGlobalLookup& globalLookup)
{
if (mContext->mCurTypeState == NULL)
return;
BP_ZONE("PopulateGlobalContainersList");
BfTypeDef* userTypeDef = mContext->mCurTypeState->mCurTypeDef;
if (userTypeDef == NULL)
userTypeDef = mCurTypeInstance->mTypeDef;
if (mContext->mCurTypeState->mGlobalContainerCurUserTypeDef != userTypeDef)
{
mContext->mCurTypeState->mGlobalContainers.Clear();
for (int namespaceIdx = -1; namespaceIdx < (int)userTypeDef->mNamespaceSearch.size(); namespaceIdx++)
{
BfAtomComposite findStr;
if (namespaceIdx != -1)
findStr.Reference(userTypeDef->mNamespaceSearch[namespaceIdx]);
auto typeDefItr = mSystem->mTypeDefs.TryGet(findStr);
while (typeDefItr)
{
auto typeDef = *typeDefItr;
if ((typeDef->mFullName == findStr) && (typeDef->mIsCombinedPartial) && (typeDef->IsGlobalsContainer()))
{
if (typeDef->mProject->ContainsReference(typeDef->mProject))
{
BfGlobalContainerEntry globalEntry;
globalEntry.mTypeDef = typeDef;
globalEntry.mTypeInst = NULL;
typeDef->PopulateMemberSets();
mContext->mCurTypeState->mGlobalContainers.Add(globalEntry);
}
}
else if (!typeDef->mIsPartial)
{
//break;
}
typeDefItr.MoveToNextHashMatch();
}
}
mContext->mCurTypeState->mGlobalContainerCurUserTypeDef = userTypeDef;
}
// Only create actual typeInst when we have an applicable member. This helps in keeping unused globals from
// reifing types
for (auto& globalEntry : mContext->mCurTypeState->mGlobalContainers)
{
if (globalEntry.mTypeInst == NULL)
{
bool include = false;
if (globalLookup.mKind == BfGlobalLookup::Kind_All)
include = true;
else if (globalLookup.mKind == BfGlobalLookup::Kind_Field)
{
if (globalEntry.mTypeDef->mFieldSet.ContainsWith(globalLookup.mName))
include = true;
if (globalEntry.mTypeDef->mPropertySet.ContainsWith(globalLookup.mName))
include = true;
}
else if (globalLookup.mKind == BfGlobalLookup::Kind_Method)
{
if (globalEntry.mTypeDef->mMethodSet.ContainsWith(globalLookup.mName))
include = true;
}
if (include)
{
auto type = ResolveTypeDef(globalEntry.mTypeDef);
if (type != NULL)
globalEntry.mTypeInst = type->ToTypeInstance();
}
}
}
}
2020-06-02 05:46:56 -07:00
BfStaticSearch* BfModule::GetStaticSearch()
{
auto activeTypeDef = GetActiveTypeDef();
BfStaticSearch* staticSearch = NULL;
if ((mCurTypeInstance != NULL) && (mCurTypeInstance->mStaticSearchMap.TryGetValue(activeTypeDef, &staticSearch)))
return staticSearch;
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mStaticSearchMap.TryGetValue(activeTypeDef, &staticSearch)))
return staticSearch;
return NULL;
}
2019-08-23 11:56:54 -07:00
void PrintUsers(llvm::MDNode* md);
BfModuleOptions BfModule::GetModuleOptions()
{
if (mIsScratchModule)
return BfModuleOptions();
if (mModuleOptions != NULL)
return *mModuleOptions;
BfModuleOptions moduleOptions;
moduleOptions.mEmitDebugInfo = mCompiler->mOptions.mEmitDebugInfo ? 1 : mCompiler->mOptions.mEmitLineInfo ? 2 : 0;
if (mProject != NULL)
{
moduleOptions.mSIMDSetting = mProject->mCodeGenOptions.mSIMDSetting;
moduleOptions.mOptLevel = mProject->mCodeGenOptions.mOptLevel;
}
auto headModule = this;
while (headModule->mParentModule != NULL)
headModule = headModule->mParentModule;
BF_ASSERT((headModule->mOwnedTypeInstances.size() > 0) || (mModuleName == "vdata"));
if (headModule->mOwnedTypeInstances.size() > 0)
{
auto typeInst = headModule->mOwnedTypeInstances[0];
PopulateType(typeInst);
if (typeInst->mTypeOptionsIdx != -1)
{
auto typeOptions = mSystem->GetTypeOptions(typeInst->mTypeOptionsIdx);
moduleOptions.mSIMDSetting = (BfSIMDSetting)BfTypeOptions::Apply((int)moduleOptions.mSIMDSetting, typeOptions->mSIMDSetting);
moduleOptions.mEmitDebugInfo = BfTypeOptions::Apply(moduleOptions.mEmitDebugInfo, typeOptions->mEmitDebugInfo);
moduleOptions.mOptLevel = (BfOptLevel)BfTypeOptions::Apply((int)moduleOptions.mOptLevel, (int)typeOptions->mOptimizationLevel);
}
}
return moduleOptions;
}
BfCheckedKind BfModule::GetDefaultCheckedKind()
{
if ((mCurMethodState != NULL) && (mCurMethodState->mDisableChecks))
return BfCheckedKind_Unchecked;
2019-08-23 11:56:54 -07:00
bool runtimeChecks = mCompiler->mOptions.mRuntimeChecks;
auto typeOptions = GetTypeOptions();
if (typeOptions != NULL)
runtimeChecks = BfTypeOptions::Apply(runtimeChecks, typeOptions->mRuntimeChecks);
return runtimeChecks ? BfCheckedKind_Checked : BfCheckedKind_Unchecked;
}
void BfModule::AddFailType(BfTypeInstance* typeInstance)
{
mContext->mFailTypes.Add(typeInstance);
}
void BfModule::CheckAddFailType()
{
//TODO: We removed this line, because failures in resolve-only generic method can occur in non-owning modules
// but we still need to add the owning type to the FailTypes list
//.. OLD:
// While we're inlining, it's possible that mCurTypeInstance belongs to another module, ignore it
/*if (mCurTypeInstance->mModule != this)
return;*/
// Using mHadBuildWarning to insert into mFailTypes is our core of our strategy for reasserting
// warnings on rebuilds where otherwise the warnings wouldn't be generated because no code was
// being recompiled. This forces types with warnings to be recompiled.
// We had this 'mIsResolveOnly' check for speed in resolveOnly, but we removed it so the IDE can be
// constantly warning-aware
if ((mHadBuildError) ||
(mHadBuildWarning /*&& !mCompiler->mIsResolveOnly*/))
{
//mContext->mFailTypes.Add(mCurTypeInstance);
}
}
void BfModule::MarkDerivedDirty(BfTypeInstance* typeInst)
{
if (!mCompiler->IsHotCompile())
return;
typeInst->mDirty = true;
for (auto& dep : typeInst->mDependencyMap)
{
auto depType = dep.mKey;
auto depFlags = dep.mValue.mFlags;
if ((depFlags & BfDependencyMap::DependencyFlag_DerivedFrom) != 0)
{
MarkDerivedDirty(depType->ToTypeInstance());
}
}
}
void BfModule::CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLocal)
{
auto fieldType = fieldInstance->GetResolvedType();
auto field = fieldInstance->GetFieldDef();
if (fieldType->IsVar())
return;
BfIRValue initValue;
if (field->mIsConst)
{
if (fieldType->IsPointer())
fieldType = fieldType->GetUnderlyingType();
}
if (!field->mIsExtern)
initValue = GetDefaultValue(fieldType);
if (fieldInstance->mOwner->IsUnspecializedType())
{
// Placeholder
auto ptrVal = CreatePointerType(fieldType);
mStaticFieldRefs[fieldInstance] = GetDefaultValue(ptrVal);
}
else
{
BfLogSysM("Creating static field Module:%p Type:%p\n", this, fieldType);
StringT<128> staticVarName;
BfMangler::Mangle(staticVarName, mCompiler->GetMangleKind(), fieldInstance);
if (!fieldType->IsValuelessType())
{
BfIRValue globalVar = mBfIRBuilder->CreateGlobalVariable(
mBfIRBuilder->MapType(fieldType, BfIRPopulateType_Eventually_Full),
false,
BfIRLinkageType_External,
initValue,
staticVarName,
isThreadLocal);
mBfIRBuilder->GlobalVar_SetAlignment(globalVar, fieldType->mAlign);
2019-08-23 11:56:54 -07:00
BF_ASSERT(globalVar);
mStaticFieldRefs[fieldInstance] = globalVar;
}
}
}
void BfModule::ResolveConstField(BfTypeInstance* typeInstance, BfFieldInstance* fieldInstance, BfFieldDef* fieldDef, bool forceResolve)
{
bool autoCompleteOnly = mCompiler->IsAutocomplete();
BfType* fieldType = NULL;
if (fieldInstance != NULL)
{
fieldType = fieldInstance->GetResolvedType();
if (fieldType == NULL)
fieldType = ResolveTypeRef(fieldDef->mTypeRef);
}
else
{
BF_ASSERT(mCompiler->IsAutocomplete());
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
if ((typeInstance->IsEnum()) && (fieldDef->mTypeRef == NULL))
{
fieldType = typeInstance;
}
else
{
auto isLet = (fieldDef->mTypeRef != NULL) && (fieldDef->mTypeRef->IsExact<BfLetTypeReference>());
auto isVar = (fieldDef->mTypeRef != NULL) && (fieldDef->mTypeRef->IsExact<BfVarTypeReference>());
if (isLet || isVar)
fieldType = GetPrimitiveType(BfTypeCode_Var);
else
fieldType = ResolveTypeRef(fieldDef->mTypeRef);
if (fieldType == NULL)
fieldType = mContext->mBfObjectType;
}
}
if ((fieldInstance != NULL) && (fieldInstance->mConstIdx != -1) && (!forceResolve))
return;
if (mContext->mFieldResolveReentrys.size() > 1)
{
if (mContext->mFieldResolveReentrys.IndexOf(fieldInstance, 1) != -1)
{
String failStr = "Circular data reference detected between the following fields:";
for (int i = 1; i < (int)mContext->mFieldResolveReentrys.size(); i++)
{
if (i > 1)
failStr += ",";
failStr += "\n '" + TypeToString(typeInstance) + "." + mContext->mFieldResolveReentrys[i]->GetFieldDef()->mName + "'";
}
BfError* err = Fail(failStr, fieldDef->mFieldDeclaration);
if (err)
err->mIsPersistent = true;
return;
}
}
mContext->mFieldResolveReentrys.push_back(fieldInstance);
AutoPopBack<decltype (mContext->mFieldResolveReentrys)> popTypeResolveReentry(&mContext->mFieldResolveReentrys);
if (fieldInstance == NULL)
popTypeResolveReentry.Pop();
auto typeDef = typeInstance->mTypeDef;
BfIRValue constValue;
if (fieldDef->mIsExtern)
{
if (!fieldDef->mTypeRef->IsA<BfPointerTypeRef>())
{
Fail("Extern consts must be pointer types", fieldDef->mFieldDeclaration->mTypeRef);
}
if (fieldDef->mInitializer != NULL)
{
Fail("Extern consts cannot have initializers", fieldDef->mFieldDeclaration->mNameNode);
}
}
else if (fieldDef->mInitializer == NULL)
{
if (fieldDef->IsEnumCaseEntry())
{
if (typeInstance->IsTypedPrimitive())
{
BfFieldInstance* prevFieldInstance = NULL;
for (int i = fieldDef->mIdx - 1; i >= 0; i--)
{
BfFieldInstance* checkFieldInst = &typeInstance->mFieldInstances[i];
if (checkFieldInst->GetFieldDef()->IsEnumCaseEntry())
{
prevFieldInstance = checkFieldInst;
break;
}
}
if (prevFieldInstance == NULL)
constValue = GetConstValue(0, typeInstance);
else
{
ResolveConstField(typeInstance, prevFieldInstance, prevFieldInstance->GetFieldDef());
if (prevFieldInstance->mConstIdx == -1)
{
if (!mCompiler->IsAutocomplete())
AssertErrorState();
constValue = GetConstValue(0, typeInstance);
}
else
{
auto prevConstant = ConstantToCurrent(typeInstance->mConstHolder->GetConstantById(prevFieldInstance->mConstIdx), typeInstance->mConstHolder, typeInstance);
constValue = mBfIRBuilder->CreateAdd(prevConstant, GetConstValue(1, typeInstance));
}
}
}
}
else
{
Fail("Const requires initializer", fieldDef->mFieldDeclaration->mNameNode);
}
}
else
{
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
SetAndRestoreValue<bool> prevIgnoreWrite(mBfIRBuilder->mIgnoreWrites, true);
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, NULL);
BfMethodState methodState;
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
methodState.mTempKind = BfMethodState::TempKind_Static;
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
if (fieldType->IsVar())
{
auto initValue = GetFieldInitializerValue(fieldInstance, fieldDef->mInitializer, fieldDef, fieldType);
if (!initValue)
{
AssertErrorState();
initValue = GetDefaultTypedValue(mContext->mBfObjectType);
}
if (fieldInstance != NULL)
{
fieldInstance->mResolvedType = initValue.mType;
}
constValue = initValue.mValue;
}
else
{
auto uncastedInitValue = GetFieldInitializerValue(fieldInstance, fieldDef->mInitializer, fieldDef, fieldType);
constValue = uncastedInitValue.mValue;
}
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
}
if (fieldInstance != NULL)
{
fieldType = fieldInstance->GetResolvedType();
if ((fieldType == NULL) || (fieldType->IsVar()))
{
2020-03-10 12:02:47 -07:00
AssertErrorState();
2019-08-23 11:56:54 -07:00
// Default const type is 'int'
BfTypedValue initValue = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_IntPtr));
if (fieldInstance != NULL)
{
fieldInstance->mResolvedType = initValue.mType;
}
constValue = initValue.mValue;
}
if ((constValue) && (fieldInstance->mConstIdx == -1))
{
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
CurrentAddToConstHolder(constValue);
fieldInstance->mConstIdx = constValue.mId;
}
}
}
BfType* BfModule::ResolveVarFieldType(BfTypeInstance* typeInstance, BfFieldInstance* fieldInstance, BfFieldDef* field)
{
bool isDeclType = BfNodeDynCastExact<BfDeclTypeRef>(field->mFieldDeclaration->mTypeRef) != NULL;
auto fieldType = fieldInstance->GetResolvedType();
if ((field->mIsConst) && (!isDeclType))
{
ResolveConstField(typeInstance, fieldInstance, field);
return fieldInstance->GetResolvedType();
}
bool staticOnly = (field->mIsStatic) && (!isDeclType);
if (!fieldInstance->mIsInferredType)
return fieldType;
2019-08-23 11:56:54 -07:00
if (!fieldType->IsVar())
return fieldType;
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
BfTypeState typeState(mCurTypeInstance, mContext->mCurTypeState);
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState, mContext->mCurTypeState->mTypeInstance != typeInstance);
auto typeDef = typeInstance->mTypeDef;
if ((!field->mIsStatic) && (typeDef->mIsStatic))
{
2020-03-10 12:02:47 -07:00
AssertErrorState();
return GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
}
bool hadInferenceCycle = false;
if (mContext->mFieldResolveReentrys.size() > 1)
{
if (mContext->mFieldResolveReentrys.IndexOf(fieldInstance, 1) != -1)
{
for (int i = 1; i < (int)mContext->mFieldResolveReentrys.size(); i++)
{
auto fieldInst = mContext->mFieldResolveReentrys[i];
auto fieldDef = fieldInst->GetFieldDef();
auto fieldOwner = fieldInst->mOwner;
auto fieldModule = fieldOwner->mModule;
SetAndRestoreValue<bool> prevIgnoreError(fieldModule->mIgnoreErrors, false);
fieldModule->Fail(StrFormat("Field '%s.%s' creates a type inference cycle", TypeToString(fieldOwner).c_str(), fieldDef->mName.c_str()), fieldDef->mTypeRef, true);
}
2019-11-02 06:02:36 -07:00
return GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
}
}
mContext->mFieldResolveReentrys.push_back(fieldInstance);
AutoPopBack<decltype (mContext->mFieldResolveReentrys)> popTypeResolveReentry(&mContext->mFieldResolveReentrys);
SetAndRestoreValue<bool> prevResolvingVar(typeInstance->mResolvingVarField, true);
SetAndRestoreValue<bool> prevCtxResolvingVar(mContext->mResolvingVarField, true);
if ((field->mInitializer == NULL) && (!isDeclType))
{
if ((field->mTypeRef->IsA<BfVarTypeReference>()) || (field->mTypeRef->IsA<BfLetTypeReference>()))
2019-11-02 06:02:36 -07:00
Fail("Implicitly-typed fields must be initialized", field->GetRefNode());
return GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
}
BfType* resolvedType = NULL;
if (!hadInferenceCycle)
{
BfTypeState typeState;
typeState.mPrevState = mContext->mCurTypeState;
typeState.mTypeInstance = typeInstance;
typeState.mCurFieldDef = field;
typeState.mResolveKind = BfTypeState::ResolveKind_ResolvingVarType;
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
SetAndRestoreValue<bool> prevIgnoreWrite(mBfIRBuilder->mIgnoreWrites, true);
2019-08-23 11:56:54 -07:00
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, NULL/*ctorMethod.mMethodInstance*/);
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
BfMethodState methodState;
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
methodState.mTempKind = staticOnly ? BfMethodState::TempKind_Static : BfMethodState::TempKind_NonStatic;
if (!staticOnly)
{
//BfLocalVariable localVar;
//methodState.mLocals.push_back(localVar);
}
if (isDeclType)
{
auto fieldDef = fieldInstance->GetFieldDef();
resolvedType = ResolveTypeRef(fieldDef->mTypeRef, BfPopulateType_Identity);
}
else
{
BfTypedValue valueFromExpr;
valueFromExpr = GetFieldInitializerValue(fieldInstance);
FixIntUnknown(valueFromExpr);
resolvedType = valueFromExpr.mType;
}
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
}
if (resolvedType == NULL)
return GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
fieldInstance->SetResolvedType(resolvedType);
if (field->mIsStatic)
{
}
else if (fieldInstance->mDataIdx >= 0)
2019-08-23 11:56:54 -07:00
{
}
else
{
BF_ASSERT(typeInstance->IsIncomplete());
}
return resolvedType;
}
void BfModule::MarkFieldInitialized(BfFieldInstance* fieldInstance)
{
BF_ASSERT(fieldInstance->mOwner == mCurTypeInstance);
auto fieldType = fieldInstance->GetResolvedType();
if ((fieldInstance->mMergedDataIdx != -1) && (mCurMethodState != NULL))
{
int fieldDataCount = 1;
if (fieldType->IsStruct())
{
auto structTypeInst = fieldType->ToTypeInstance();
fieldDataCount = structTypeInst->mMergedFieldDataCount;
}
//TODO: Under what circumstances could 'thisVariable' be NULL?
auto thisVariable = GetThisVariable();
if (thisVariable != NULL)
{
for (int i = 0; i < fieldDataCount; i++)
mCurMethodState->LocalDefined(thisVariable, fieldInstance->mMergedDataIdx + i);
}
}
}
bool BfModule::IsThreadLocal(BfFieldInstance * fieldInstance)
{
if (fieldInstance->mCustomAttributes == NULL)
return false;
auto fieldDef = fieldInstance->GetFieldDef();
if (!fieldDef->mIsStatic)
return false;
bool isThreadLocal = false;
if (fieldInstance->mCustomAttributes != NULL)
{
for (auto customAttr : fieldInstance->mCustomAttributes->mAttributes)
{
if (customAttr.mType->ToTypeInstance()->mTypeDef == mCompiler->mThreadStaticAttributeTypeDef)
return true;
}
}
return false;
}
BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, BfExpression* initializer, BfFieldDef* fieldDef, BfType* fieldType, bool doStore)
2019-08-23 11:56:54 -07:00
{
if (fieldDef == NULL)
fieldDef = fieldInstance->GetFieldDef();
if (fieldType == NULL)
fieldType = fieldInstance->GetResolvedType();
if (initializer == NULL)
{
if (fieldDef == NULL)
return BfTypedValue();
initializer = fieldDef->mInitializer;
}
BfTypedValue staticVarRef;
2019-08-23 11:56:54 -07:00
BfTypedValue result;
if (initializer == NULL)
{
result = GetDefaultTypedValue(fieldInstance->mResolvedType);
}
else
{
if ((mCompiler->mIsResolveOnly) && (mCompiler->mResolvePassData->mSourceClassifier != NULL) && (initializer->IsFromParser(mCompiler->mResolvePassData->mParser)))
{
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(initializer, BfSourceElementType_Normal);
mCompiler->mResolvePassData->mSourceClassifier->VisitChildNoRef(initializer);
}
if ((mCurTypeInstance->IsPayloadEnum()) && (fieldDef->IsEnumCaseEntry()))
{
Fail("Enums with payloads cannot have explicitly-defined discriminators", initializer);
BfConstResolver constResolver(this);
constResolver.Resolve(initializer);
return GetDefaultTypedValue(fieldType);
}
else if (fieldDef->mIsConst)
{
BfTypeState typeState;
typeState.mTypeInstance = mCurTypeInstance;
2019-08-23 11:56:54 -07:00
typeState.mCurTypeDef = fieldDef->mDeclaringType;
typeState.mCurFieldDef = fieldDef;
2019-08-23 11:56:54 -07:00
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
BfConstResolver constResolver(this);
if (fieldType->IsVar())
return constResolver.Resolve(initializer);
else
{
BfConstResolveFlags resolveFlags = BfConstResolveFlag_None;
if ((mCompiler->mResolvePassData != NULL) && (mCurTypeInstance->IsEnum()) && (fieldDef->IsEnumCaseEntry()) &&
(mCompiler->mResolvePassData->mAutoCompleteTempTypes.Contains(fieldDef->mDeclaringType)))
{
// We avoid doing the cast here because the value we're typing may not be in the range of the current
// auto-created underlying type and it will cause an 'error flash'. We defer errors until the full resolve for that purpose
resolveFlags = BfConstResolveFlag_NoCast;
}
2020-06-03 05:22:29 -07:00
UpdateSrcPos(initializer);
return constResolver.Resolve(initializer, fieldType, resolveFlags);
}
2019-08-23 11:56:54 -07:00
}
BfExprEvaluator exprEvaluator(this);
if (doStore)
{
staticVarRef = ReferenceStaticField(fieldInstance);
exprEvaluator.mReceivingValue = &staticVarRef;
}
2019-08-23 11:56:54 -07:00
if (fieldType->IsVar())
result = CreateValueFromExpression(exprEvaluator, initializer, NULL, (BfEvalExprFlags)(BfEvalExprFlags_NoValueAddr | BfEvalExprFlags_FieldInitializer));
2019-08-23 11:56:54 -07:00
else
result = CreateValueFromExpression(exprEvaluator, initializer, fieldType, (BfEvalExprFlags)(BfEvalExprFlags_NoValueAddr | BfEvalExprFlags_FieldInitializer));
if (doStore)
{
if (exprEvaluator.mReceivingValue == NULL)
doStore = false; // Already stored
}
2019-08-23 11:56:54 -07:00
}
if (fieldInstance != NULL)
MarkFieldInitialized(fieldInstance);
if (doStore)
{
result = LoadValue(result);
if (!result.mType->IsValuelessType())
mBfIRBuilder->CreateStore(result.mValue, staticVarRef.mValue);
}
2019-08-23 11:56:54 -07:00
return result;
}
void BfModule::CheckInterfaceMethod(BfMethodInstance* methodInstance)
{
}
void BfModule::FindSubTypes(BfTypeInstance* classType, SizedArrayImpl<int>* outVals, SizedArrayImpl<BfTypeInstance*>* exChecks, bool isInterfacePass)
{
PopulateType(classType);
if (isInterfacePass)
{
for (auto ifaceInst : classType->mInterfaces)
{
if (exChecks->Contains(ifaceInst.mInterfaceType))
continue;
if (outVals->Contains(ifaceInst.mInterfaceType->mTypeId))
continue;
if (ifaceInst.mDeclaringType->IsExtension())
{
bool needsExCheck = false;
if (ifaceInst.mDeclaringType->mProject != classType->mTypeDef->mProject)
{
exChecks->push_back(ifaceInst.mInterfaceType);
continue;
}
}
outVals->push_back(ifaceInst.mInterfaceType->mTypeId);
}
}
else
{
outVals->push_back(classType->mTypeId);
}
if (classType->mBaseType != NULL)
{
FindSubTypes(classType->mBaseType, outVals, exChecks, isInterfacePass);
}
}
void BfModule::CreateDynamicCastMethod()
{
auto objType = mContext->mBfObjectType;
if ((mCompiler->mIsResolveOnly) || (!mIsReified))
{
// The main reason to punt on this method for ResolveOnly is because types can be created
// and destroyed quickly during autocomplete and the typeId creep can generate lots of
// entries in the LLVM ConstantInt pool, primarily from the FindSubTypes call. We can
// remove this punt when we recycle typeId's
mBfIRBuilder->CreateRet(mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapTypeInstPtr(objType)));
mCurMethodState->mHadReturn = true;
return;
}
bool isInterfacePass = mCurMethodInstance->mMethodDef->mName == BF_METHODNAME_DYNAMICCAST_INTERFACE;
auto func = mCurMethodState->mIRFunction;
auto thisValue = mBfIRBuilder->GetArgument(0);
auto typeIdValue = mBfIRBuilder->GetArgument(1);
auto intPtrType = GetPrimitiveType(BfTypeCode_IntPtr);
auto int32Type = GetPrimitiveType(BfTypeCode_Int32);
typeIdValue = CastToValue(NULL, BfTypedValue(typeIdValue, intPtrType), int32Type, (BfCastFlags)(BfCastFlags_Explicit | BfCastFlags_SilentFail));
auto thisObject = mBfIRBuilder->CreateBitCast(thisValue, mBfIRBuilder->MapType(objType));
auto trueBB = mBfIRBuilder->CreateBlock("check.true");
//auto falseBB = mBfIRBuilder->CreateBlock("check.false");
auto exitBB = mBfIRBuilder->CreateBlock("exit");
SizedArray<int, 8> typeMatches;
SizedArray<BfTypeInstance*, 8> exChecks;
FindSubTypes(mCurTypeInstance, &typeMatches, &exChecks, isInterfacePass);
if ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType()))
{
// Add 'unbound' type id to cast list so things like "List<int> is List<>" work
auto genericTypeInst = mCurTypeInstance->mTypeDef;
BfTypeVector genericArgs;
for (int i = 0; i < (int) genericTypeInst->mGenericParamDefs.size(); i++)
genericArgs.push_back(GetGenericParamType(BfGenericParamKind_Type, i));
auto unboundType = ResolveTypeDef(mCurTypeInstance->mTypeDef, genericArgs, BfPopulateType_Declaration);
typeMatches.push_back(unboundType->mTypeId);
}
if (mCurTypeInstance->IsBoxed())
{
BfBoxedType* boxedType = (BfBoxedType*)mCurTypeInstance;
BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance();
2019-08-23 11:56:54 -07:00
FindSubTypes(innerType, &typeMatches, &exChecks, isInterfacePass);
if (innerType->IsTypedPrimitive())
{
auto underlyingType = innerType->GetUnderlyingType();
typeMatches.push_back(underlyingType->mTypeId);
}
auto innerTypeInst = innerType->ToTypeInstance();
if ((innerTypeInst->mTypeDef == mCompiler->mSizedArrayTypeDef) ||
(innerTypeInst->mTypeDef == mCompiler->mPointerTTypeDef) ||
(innerTypeInst->mTypeDef == mCompiler->mMethodRefTypeDef))
{
PopulateType(innerTypeInst);
//TODO: What case was this supposed to handle?
//typeMatches.push_back(innerTypeInst->mFieldInstances[0].mResolvedType->mTypeId);
}
}
auto curBlock = mBfIRBuilder->GetInsertBlock();
BfIRValue vDataPtr;
if (!exChecks.empty())
{
BfType* intPtrType = GetPrimitiveType(BfTypeCode_IntPtr);
auto ptrPtrType = mBfIRBuilder->GetPointerTo(mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapType(intPtrType)));
auto vDataPtrPtr = mBfIRBuilder->CreateBitCast(thisValue, ptrPtrType);
vDataPtr = FixClassVData(mBfIRBuilder->CreateLoad(vDataPtrPtr/*, "vtable"*/));
}
auto switchStatement = mBfIRBuilder->CreateSwitch(typeIdValue, exitBB, (int)typeMatches.size() + (int)exChecks.size());
for (auto typeMatch : typeMatches)
mBfIRBuilder->AddSwitchCase(switchStatement, GetConstValue32(typeMatch), trueBB);
Array<BfIRValue> incomingFalses;
for (auto ifaceTypeInst : exChecks)
{
BfIRBlock nextBB = mBfIRBuilder->CreateBlock("exCheck", true);
mBfIRBuilder->AddSwitchCase(switchStatement, GetConstValue32(ifaceTypeInst->mTypeId), nextBB);
mBfIRBuilder->SetInsertPoint(nextBB);
BfIRValue slotOfs = GetInterfaceSlotNum(ifaceTypeInst);
auto ifacePtrPtr = mBfIRBuilder->CreateInBoundsGEP(vDataPtr, slotOfs/*, "iface"*/);
auto ifacePtr = mBfIRBuilder->CreateLoad(ifacePtrPtr);
auto cmpResult = mBfIRBuilder->CreateCmpNE(ifacePtr, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0));
mBfIRBuilder->CreateCondBr(cmpResult, trueBB, exitBB);
incomingFalses.push_back(nextBB);
}
mBfIRBuilder->AddBlock(trueBB);
mBfIRBuilder->SetInsertPoint(trueBB);
mBfIRBuilder->CreateBr(exitBB);
mBfIRBuilder->AddBlock(exitBB);
mBfIRBuilder->SetInsertPoint(exitBB);
auto phi = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapTypeInstPtr(objType), 2);
mBfIRBuilder->AddPhiIncoming(phi, thisObject, trueBB);
auto nullValue = mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapTypeInstPtr(objType));
for (auto incomingFalseBlock : incomingFalses)
mBfIRBuilder->AddPhiIncoming(phi, nullValue, incomingFalseBlock);
mBfIRBuilder->AddPhiIncoming(phi, nullValue, curBlock);
mBfIRBuilder->CreateRet(phi);
mCurMethodState->mHadReturn = true;
}
void BfModule::EmitEquals(BfTypedValue leftValue, BfTypedValue rightValue, BfIRBlock exitBB)
{
BfExprEvaluator exprEvaluator(this);
exprEvaluator.mExpectingType = mCurMethodInstance->mReturnType;
exprEvaluator.PerformBinaryOperation((BfAstNode*)NULL, (BfAstNode*)NULL, BfBinaryOp_Equality, NULL, BfBinOpFlag_None, leftValue, rightValue);
BfTypedValue result = exprEvaluator.GetResult();
if ((result) && (!result.mType->IsVar()))
2019-08-23 11:56:54 -07:00
{
auto nextBB = mBfIRBuilder->CreateBlock("next");
mBfIRBuilder->CreateCondBr(result.mValue, nextBB, exitBB);
mBfIRBuilder->AddBlock(nextBB);
mBfIRBuilder->SetInsertPoint(nextBB);
}
}
void BfModule::CreateFakeCallerMethod(const String& funcName)
{
if (mCurMethodInstance->mHasFailed)
return;
if (mCurMethodInstance->mMethodDef->mIsSkipCall)
return;
2019-08-23 11:56:54 -07:00
BF_ASSERT(mCurMethodInstance->mIRFunction);
2019-08-23 11:56:54 -07:00
auto voidType = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_None));
SizedArray<BfIRType, 4> paramTypes;
BfIRFunctionType funcType = mBfIRBuilder->CreateFunctionType(voidType, paramTypes);
BfIRFunction func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_Internal, "FORCELINK_" + funcName);
mBfIRBuilder->SetActiveFunction(func);
auto entryBlock = mBfIRBuilder->CreateBlock("main", true);
mBfIRBuilder->SetInsertPoint(entryBlock);
BfMethodState methodState;
methodState.mIRHeadBlock = entryBlock;
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
2019-08-23 11:56:54 -07:00
SizedArray<BfIRValue, 8> args;
BfExprEvaluator exprEvaluator(this);
if (mCurMethodInstance->GetStructRetIdx() != -1)
2020-03-11 13:59:35 -07:00
{
auto retPtrType = CreatePointerType(mCurMethodInstance->mReturnType);
exprEvaluator.PushArg(GetDefaultTypedValue(retPtrType, true, BfDefaultValueKind_Const), args);
}
2019-08-23 11:56:54 -07:00
if (mCurMethodInstance->HasThis())
{
auto thisValue = GetDefaultTypedValue(mCurMethodInstance->GetOwner(), true, mCurTypeInstance->IsComposite() ? BfDefaultValueKind_Addr : BfDefaultValueKind_Const);
2019-08-23 11:56:54 -07:00
exprEvaluator.PushThis(NULL, thisValue, mCurMethodInstance, args);
}
for (int paramIdx = 0; paramIdx < mCurMethodInstance->GetParamCount(); paramIdx++)
{
auto paramType = mCurMethodInstance->GetParamType(paramIdx);
if (paramType->IsValuelessType())
continue;
exprEvaluator.PushArg(GetDefaultTypedValue(paramType, true, paramType->IsComposite() ? BfDefaultValueKind_Addr : BfDefaultValueKind_Const), args);
2019-08-23 11:56:54 -07:00
}
mBfIRBuilder->CreateCall(mCurMethodInstance->mIRFunction, args);
mBfIRBuilder->CreateRetVoid();
}
void BfModule::CreateValueTypeEqualsMethod()
{
if (mCurMethodInstance->mIsUnspecialized)
return;
if (mCurTypeInstance->IsTypedPrimitive())
return;
if (mBfIRBuilder->mIgnoreWrites)
return;
BF_ASSERT(!mCurTypeInstance->IsBoxed());
auto compareType = mCurMethodInstance->mParams[0].mResolvedType;
bool isValid = true;
auto compareDType = compareType->ToDependedType();
BfTypeInstance* compareTypeInst = compareType->ToTypeInstance();
if (compareTypeInst != NULL)
{
if (compareType->IsPrimitiveType())
compareTypeInst = GetWrappedStructType(compareType);
if ((compareTypeInst == NULL) || (!compareTypeInst->IsValueType()))
{
isValid = false;
}
mBfIRBuilder->PopulateType(compareTypeInst);
}
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
if (!isValid)
{
ClearLifetimeEnds();
mBfIRBuilder->CreateRet(GetDefaultValue(boolType));
return;
}
AddDependency(compareDType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields);
auto refNode = mCurTypeInstance->mTypeDef->GetRefNode();
if (refNode == NULL)
refNode = mCompiler->mValueTypeTypeDef->GetRefNode();
UpdateSrcPos(refNode);
SetIllegalSrcPos();
auto resultVal = CreateAlloca(boolType);
mBfIRBuilder->CreateStore(GetConstValue(0, boolType), resultVal);
auto exitBB = mBfIRBuilder->CreateBlock("exit");
if (compareType->IsSizedArray())
{
auto sizedArrayType = (BfSizedArrayType*)compareType;
if (sizedArrayType->mElementCount > 6)
{
auto intPtrType = GetPrimitiveType(BfTypeCode_IntPtr);
auto itr = CreateAlloca(intPtrType);
mBfIRBuilder->CreateStore(GetDefaultValue(intPtrType), itr);
auto loopBB = mBfIRBuilder->CreateBlock("loop", true);
auto bodyBB = mBfIRBuilder->CreateBlock("body", true);
auto doneBB = mBfIRBuilder->CreateBlock("done", true);
mBfIRBuilder->CreateBr(loopBB);
mBfIRBuilder->SetInsertPoint(loopBB);
auto loadedItr = mBfIRBuilder->CreateLoad(itr);
auto cmpRes = mBfIRBuilder->CreateCmpGTE(loadedItr, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, (uint64)sizedArrayType->mElementCount), true);
mBfIRBuilder->CreateCondBr(cmpRes, doneBB, bodyBB);
mBfIRBuilder->SetInsertPoint(bodyBB);
BfTypedValue leftValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, GetDefaultValue(intPtrType), loadedItr), sizedArrayType->mElementType, BfTypedValueKind_Addr);
BfTypedValue rightValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[1]->mValue, GetDefaultValue(intPtrType), loadedItr), sizedArrayType->mElementType, BfTypedValueKind_Addr);
EmitEquals(leftValue, rightValue, exitBB);
auto incValue = mBfIRBuilder->CreateAdd(loadedItr, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1));
mBfIRBuilder->CreateStore(incValue, itr);
mBfIRBuilder->CreateBr(loopBB);
mBfIRBuilder->SetInsertPoint(doneBB);
}
else
{
for (int dataIdx = 0; dataIdx < sizedArrayType->mElementCount; dataIdx++)
{
BfTypedValue leftValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, dataIdx), sizedArrayType->mElementType, BfTypedValueKind_Addr);
BfTypedValue rightValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[1]->mValue, 0, dataIdx), sizedArrayType->mElementType, BfTypedValueKind_Addr);
EmitEquals(leftValue, rightValue, exitBB);
}
}
}
else if (compareType->IsMethodRef())
{
auto methodRefType = (BfMethodRefType*)compareType;
BfMethodInstance* methodInstance = methodRefType->mMethodRef;
BfExprEvaluator exprEvaluator(this);
BfTypedValue leftTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[0]);
BfTypedValue rightTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
// We only need to compare the 'this' capture. The rationale is that it's impossible to capture any other non-matching
// values for the same method reference -- they will always refer back to the same local variables.
int dataIdx = methodRefType->GetDataIdxFromParamIdx(-1);
if (dataIdx != -1)
{
bool failed = false;
BfTypedValue leftValue = exprEvaluator.DoImplicitArgCapture(NULL, methodInstance, methodRefType->GetParamIdxFromDataIdx(dataIdx), failed, BfImplicitParamKind_General, leftTypedVal);
BfTypedValue rightValue = exprEvaluator.DoImplicitArgCapture(NULL, methodInstance, methodRefType->GetParamIdxFromDataIdx(dataIdx), failed, BfImplicitParamKind_General, rightTypedVal);
BF_ASSERT(!failed);
EmitEquals(leftValue, rightValue, exitBB);
}
}
else if (compareTypeInst->IsEnum())
{
//auto intType = GetPrimitiveType(BfTypeCode_Int32);
BfExprEvaluator exprEvaluator(this);
BfTypedValue leftTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[0]);
BfTypedValue rightTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
auto dscrType = compareTypeInst->GetDiscriminatorType();
BfTypedValue leftValue = ExtractValue(leftTypedVal, NULL, 2);
leftValue = LoadValue(leftValue);
BfTypedValue rightValue = ExtractValue(rightTypedVal, NULL, 2);
rightValue = LoadValue(rightValue);
BfTypedValue leftPayload = ExtractValue(leftTypedVal, NULL, 1);
BfTypedValue rightPayload = ExtractValue(rightTypedVal, NULL, 1);
EmitEquals(leftValue, rightValue, exitBB);
int enumCount = 0;
for (auto& fieldRef : compareTypeInst->mFieldInstances)
{
BfFieldInstance* fieldInstance = &fieldRef;
BfFieldDef* fieldDef = fieldInstance->GetFieldDef();
if ((fieldDef != NULL) && (fieldDef->IsEnumCaseEntry()))
{
enumCount = -fieldInstance->mDataIdx;
}
}
if (enumCount > 0)
{
BfIRBlock matchedBlock = mBfIRBuilder->CreateBlock("matched");
auto switchVal = mBfIRBuilder->CreateSwitch(leftValue.mValue, exitBB, enumCount);
for (auto& fieldRef : compareTypeInst->mFieldInstances)
{
BfFieldInstance* fieldInstance = &fieldRef;
BfFieldDef* fieldDef = fieldInstance->GetFieldDef();
if ((fieldDef != NULL) && (fieldDef->IsEnumCaseEntry()))
{
int enumIdx = -fieldInstance->mDataIdx - 1;
if (fieldInstance->mResolvedType->mSize == 0)
{
mBfIRBuilder->AddSwitchCase(switchVal, mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, enumIdx), matchedBlock);
}
else
{
auto caseBlock = mBfIRBuilder->CreateBlock("case", true);
mBfIRBuilder->SetInsertPoint(caseBlock);
auto tuplePtr = CreatePointerType(fieldInstance->mResolvedType);
BfTypedValue leftTuple;
BfTypedValue rightTuple;
if (leftPayload.IsAddr())
{
leftTuple = BfTypedValue(mBfIRBuilder->CreateBitCast(leftPayload.mValue, mBfIRBuilder->MapType(tuplePtr)), fieldInstance->mResolvedType, true);
rightTuple = BfTypedValue(mBfIRBuilder->CreateBitCast(rightPayload.mValue, mBfIRBuilder->MapType(tuplePtr)), fieldInstance->mResolvedType, true);
}
else
{
leftTuple = Cast(NULL, leftPayload, fieldInstance->mResolvedType, BfCastFlags_Force);
rightTuple = Cast(NULL, rightPayload, fieldInstance->mResolvedType, BfCastFlags_Force);
}
EmitEquals(leftTuple, rightTuple, exitBB);
mBfIRBuilder->CreateBr(matchedBlock);
mBfIRBuilder->AddSwitchCase(switchVal, mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, enumIdx), caseBlock);
}
}
}
mBfIRBuilder->AddBlock(matchedBlock);
mBfIRBuilder->SetInsertPoint(matchedBlock);
}
}
else if (compareTypeInst->IsUnion())
{
auto innerType = compareTypeInst->GetUnionInnerType();
if (!innerType->IsValuelessType())
{
BfExprEvaluator exprEvaluator(this);
BfTypedValue leftTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[0]);
BfTypedValue leftUnionTypedVal = ExtractValue(leftTypedVal, NULL, 1);
BfTypedValue rightTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
BfTypedValue rightUnionTypedVal = ExtractValue(rightTypedVal, NULL, 1);
EmitEquals(leftUnionTypedVal, rightUnionTypedVal, exitBB);
}
}
else
{
BfExprEvaluator exprEvaluator(this);
BfTypedValue leftTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[0]);
BfTypedValue rightTypedVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
bool hadComparison = false;
for (auto& fieldRef : compareTypeInst->mFieldInstances)
{
BfFieldInstance* fieldInstance = &fieldRef;
if (fieldInstance->mDataOffset == -1)
continue;
if (fieldInstance->mResolvedType->IsValuelessType())
continue;
BfTypedValue leftValue = ExtractValue(leftTypedVal, fieldInstance, fieldInstance->mDataIdx);
BfTypedValue rightValue = ExtractValue(rightTypedVal, fieldInstance, fieldInstance->mDataIdx);
if (!fieldInstance->mResolvedType->IsComposite())
{
leftValue = LoadValue(leftValue);
rightValue = LoadValue(rightValue);
}
EmitEquals(leftValue, rightValue, exitBB);
}
auto baseTypeInst = compareTypeInst->mBaseType;
if ((baseTypeInst != NULL) && (baseTypeInst->mTypeDef != mCompiler->mValueTypeTypeDef))
{
BfTypedValue leftOrigValue(mCurMethodState->mLocals[0]->mValue, compareTypeInst, true);
BfTypedValue rightOrigValue(mCurMethodState->mLocals[1]->mValue, compareTypeInst, true);
BfTypedValue leftValue = Cast(NULL, leftOrigValue, baseTypeInst);
BfTypedValue rightValue = Cast(NULL, rightOrigValue, baseTypeInst);
EmitEquals(leftValue, rightValue, exitBB);
}
}
mBfIRBuilder->CreateStore(GetConstValue(1, boolType), resultVal);
mBfIRBuilder->CreateBr(exitBB);
mBfIRBuilder->AddBlock(exitBB);
mBfIRBuilder->SetInsertPoint(exitBB);
auto loadedResult = mBfIRBuilder->CreateLoad(resultVal);
ClearLifetimeEnds();
mBfIRBuilder->CreateRet(loadedResult);
mCurMethodState->mHadReturn = true;
}
BfIRValue BfModule::CreateClassVDataGlobal(BfTypeInstance* typeInstance, int* outNumElements, String* outMangledName)
{
if (mBfIRBuilder->mIgnoreWrites)
return mBfIRBuilder->GetFakeVal();
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
BfType* classVDataType = ResolveTypeDef(mCompiler->mClassVDataTypeDef);
BfIRValue* globalVariablePtr = NULL;
mClassVDataRefs.TryGetValue(typeInstance, &globalVariablePtr);
int numElements = 1;
if ((outNumElements != NULL) || (globalVariablePtr == NULL))
{
numElements += mCompiler->mMaxInterfaceSlots;
auto maxIFaceVirtIdx = 0;
if (!typeInstance->IsInterface())
{
int dynCastDataElems = 1 + mCompiler->mMaxInterfaceSlots;
numElements += ((dynCastDataElems * 4) + mSystem->mPtrSize - 1) / mSystem->mPtrSize;
numElements += typeInstance->GetOrigVTableSize();
int ifaceMethodLen = typeInstance->GetIFaceVMethodSize();
if (typeInstance->mHotTypeData != NULL)
{
if (typeInstance->mHotTypeData->mOrigInterfaceMethodsLength != -1)
ifaceMethodLen = typeInstance->mHotTypeData->mOrigInterfaceMethodsLength;
}
numElements += ifaceMethodLen;
}
if (outNumElements != NULL)
*outNumElements = numElements;
}
StringT<128> classVDataName;
String memberName = "sBfClassVData";
if ((typeInstance->mHotTypeData != NULL) && ((typeInstance->mHotTypeData->mHadDataChange) || (typeInstance->mHotTypeData->mPendingDataChange)))
{
auto curVersion = typeInstance->mHotTypeData->GetLatestVersion();
memberName += "_";
memberName += BfTypeUtils::HashEncode64(curVersion->mDataHash.mLow);
memberName += BfTypeUtils::HashEncode64(curVersion->mDataHash.mHigh);
}
BfMangler::MangleStaticFieldName(classVDataName, mCompiler->GetMangleKind(), typeInstance, memberName, classVDataType);
if (outMangledName != NULL)
*outMangledName = classVDataName;
/*if (itr != mClassVDataRefs.end())
{
globalVariable = itr->second;
}*/
BfIRValue globalVariable;
if (globalVariablePtr != NULL)
{
globalVariable = *globalVariablePtr;
}
else
{
BfLogSysM("Creating VData %s\n", classVDataName.c_str());
auto arrayType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr), numElements);
globalVariable = mBfIRBuilder->CreateGlobalVariable(
arrayType,
true,
BfIRLinkageType_External,
BfIRValue(),
classVDataName);
2019-08-23 11:56:54 -07:00
mClassVDataRefs[typeInstance] = globalVariable;
}
return globalVariable;
}
BfIRValue BfModule::GetClassVDataPtr(BfTypeInstance* typeInstance)
{
auto classVDataType = ResolveTypeDef(mCompiler->mClassVDataTypeDef);
return mBfIRBuilder->CreateBitCast(CreateClassVDataGlobal(typeInstance), mBfIRBuilder->MapType(CreatePointerType(classVDataType)));
}
BfIRValue BfModule::CreateClassVDataExtGlobal(BfTypeInstance* declTypeInst, BfTypeInstance* implTypeInst, int startVirtIdx)
{
if (mBfIRBuilder->mIgnoreWrites)
return mBfIRBuilder->GetFakeVal();
int numElements = declTypeInst->GetSelfVTableSize() - declTypeInst->GetOrigSelfVTableSize();
BF_ASSERT(numElements >= 0);
if (numElements <= 0)
return BfIRValue();
BF_ASSERT(implTypeInst->mVirtualMethodTable[startVirtIdx].mDeclaringMethod.mMethodNum == -1);
BF_ASSERT(implTypeInst->mVirtualMethodTable[startVirtIdx].mDeclaringMethod.mTypeInstance == declTypeInst);
if (declTypeInst->mInheritDepth == implTypeInst->mInheritDepth)
BF_ASSERT(declTypeInst == implTypeInst);
else
BF_ASSERT(implTypeInst->mInheritDepth > declTypeInst->mInheritDepth);
if (declTypeInst != implTypeInst)
{
BfTypeInstance* highestImpl = declTypeInst;
for (int virtIdx = startVirtIdx + 1; virtIdx < (int)declTypeInst->mVirtualMethodTable.size(); virtIdx++)
{
if (implTypeInst->mVirtualMethodTable[virtIdx].mDeclaringMethod.mMethodNum == -1)
break; // Start of an ext entry for another type
auto checkImplTypeInst = implTypeInst->mVirtualMethodTable[virtIdx].mImplementingMethod.mTypeInstance;
if ((checkImplTypeInst != NULL) && (checkImplTypeInst->mInheritDepth > highestImpl->mInheritDepth))
highestImpl = checkImplTypeInst;
}
if (highestImpl != implTypeInst)
return CreateClassVDataExtGlobal(declTypeInst, highestImpl, startVirtIdx);
}
BfVDataExtEntry mapEntry;
mapEntry.mDeclTypeInst = declTypeInst;
mapEntry.mImplTypeInst = implTypeInst;
BfIRValue* irValuePtr = NULL;
if (mClassVDataExtRefs.TryGetValue(mapEntry, &irValuePtr))
return *irValuePtr;
PopulateType(declTypeInst, BfPopulateType_DataAndMethods);
PopulateType(implTypeInst, BfPopulateType_DataAndMethods);
StringT<128> classVDataName;
BfMangler::MangleStaticFieldName(classVDataName, mCompiler->GetMangleKind(), implTypeInst, "bf_hs_replace_VDataExt");
if (declTypeInst != implTypeInst)
{
classVDataName += StrFormat("_%d", implTypeInst->mInheritDepth - declTypeInst->mInheritDepth);
}
auto voidPtrType = GetPrimitiveType(BfTypeCode_NullPtr);
auto voidPtrIRType = mBfIRBuilder->MapType(voidPtrType);
SizedArray<BfIRValue, 32> vData;
auto voidPtrNull = GetDefaultValue(voidPtrType);
for (int virtIdx = startVirtIdx + declTypeInst->GetOrigSelfVTableSize(); virtIdx < (int)declTypeInst->mVirtualMethodTable.size(); virtIdx++)
{
if (implTypeInst->mVirtualMethodTable[virtIdx].mDeclaringMethod.mMethodNum == -1)
break; // Start of an ext entry for another type
BfIRValue vValue;
auto& entry = implTypeInst->mVirtualMethodTable[virtIdx];
BfMethodInstance* declaringMethodInstance = (BfMethodInstance*)entry.mDeclaringMethod;
if ((declaringMethodInstance != NULL) && (declaringMethodInstance->mMethodInstanceGroup->IsImplemented()) && (declaringMethodInstance->mIsReified))
{
BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].IsImplemented());
BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].mDefault->mIsReified);
BfMethodInstance* methodInstance = (BfMethodInstance*)entry.mImplementingMethod;
if ((methodInstance != NULL) && (!methodInstance->mMethodDef->mIsAbstract))
{
BF_ASSERT(implTypeInst->IsTypeMemberAccessible(methodInstance->mMethodDef->mDeclaringType, mProject));
auto moduleMethodInst = GetMethodInstanceAtIdx(methodInstance->mMethodInstanceGroup->mOwner, methodInstance->mMethodInstanceGroup->mMethodIdx, NULL, BfGetMethodInstanceFlag_NoInline);
2019-08-23 11:56:54 -07:00
auto funcPtr = mBfIRBuilder->CreateBitCast(moduleMethodInst.mFunc, voidPtrIRType);
vValue = funcPtr;
}
}
if (!vValue)
vValue = voidPtrNull;
vData.Add(vValue);
}
BF_ASSERT((int)vData.size() == numElements);
BfLogSysM("Creating VDataExt %s\n", classVDataName.c_str());
auto arrayType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr), numElements);
auto globalVariable = mBfIRBuilder->CreateGlobalVariable(arrayType, true,
BfIRLinkageType_External, BfIRValue(), classVDataName);
BfIRType extVTableType = mBfIRBuilder->GetSizedArrayType(voidPtrIRType, (int)vData.size());
BfIRValue extVTableConst = mBfIRBuilder->CreateConstArray(extVTableType, vData);
mBfIRBuilder->GlobalVar_SetInitializer(globalVariable, extVTableConst);
mClassVDataExtRefs[mapEntry] = globalVariable;
return globalVariable;
}
BfIRValue BfModule::CreateTypeDataRef(BfType* type)
{
if (mBfIRBuilder->mIgnoreWrites)
{
return mBfIRBuilder->CreateTypeOf(type);
}
BfIRValue globalVariable;
BfIRValue* globalVariablePtr = NULL;
if (mTypeDataRefs.TryGetValue(type, &globalVariablePtr))
{
globalVariable = *globalVariablePtr;
if (globalVariable)
return globalVariable;
}
auto typeTypeDef = ResolveTypeDef(mCompiler->mTypeTypeDef);
auto typeTypeInst = typeTypeDef->ToTypeInstance();
auto typeInstance = type->ToTypeInstance();
StringT<128> typeDataName;
if (typeInstance != NULL)
{
BfMangler::MangleStaticFieldName(typeDataName, mCompiler->GetMangleKind(), typeInstance, "sBfTypeData");
}
else
{
typeDataName += "sBfTypeData.";
2020-04-04 06:20:55 -07:00
BfMangler::Mangle(typeDataName, mCompiler->GetMangleKind(), type, this);
2019-08-23 11:56:54 -07:00
}
BfLogSysM("Creating TypeData %s\n", typeDataName.c_str());
globalVariable = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapTypeInst(typeTypeInst, BfIRPopulateType_Full), true, BfIRLinkageType_External, BfIRValue(), typeDataName);
mTypeDataRefs[type] = globalVariable;
return globalVariable;
}
BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary<int, int>& usedStringIdMap, bool forceReflectFields, bool needsTypeData, bool needsTypeNames, bool needsVData)
{
if ((mCompiler->IsHotCompile()) && (!type->mDirty))
return BfIRValue();
BfIRValue* irValuePtr = NULL;
if (mTypeDataRefs.TryGetValue(type, &irValuePtr))
{
return *irValuePtr;
}
BfTypeInstance* typeInstance = type->ToTypeInstance();
BfType* typeInstanceType = ResolveTypeDef(mCompiler->mReflectTypeInstanceTypeDef);
mBfIRBuilder->PopulateType(typeInstanceType, BfIRPopulateType_Full_ForceDefinition);
2019-08-23 11:56:54 -07:00
if (typeInstanceType == NULL)
{
AssertErrorState();
return BfIRValue();
}
BfIRValue typeTypeData;
int typeFlags = 0;
if (needsTypeData)
{
BfTypeInstance* typeInstanceTypeInstance = typeInstanceType->ToTypeInstance();
BfTypeInstance* typeDataSource = NULL;
if (typeInstance != NULL)
{
if (typeInstance->IsUnspecializedType())
{
typeDataSource = ResolveTypeDef(mCompiler->mReflectUnspecializedGenericType)->ToTypeInstance();
typeFlags |= BfTypeFlags_UnspecializedGeneric;
}
else if (typeInstance->IsArray())
{
typeDataSource = ResolveTypeDef(mCompiler->mReflectArrayType)->ToTypeInstance();
typeFlags |= BfTypeFlags_Array;
}
else if (typeInstance->IsGenericTypeInstance())
{
typeDataSource = ResolveTypeDef(mCompiler->mReflectSpecializedGenericType)->ToTypeInstance();
typeFlags |= BfTypeFlags_SpecializedGeneric;
}
else
typeDataSource = typeInstanceTypeInstance;
}
else if (type->IsPointer())
typeDataSource = ResolveTypeDef(mCompiler->mReflectPointerType)->ToTypeInstance();
else if (type->IsSizedArray())
typeDataSource = ResolveTypeDef(mCompiler->mReflectSizedArrayType)->ToTypeInstance();
else
typeDataSource = mContext->mBfTypeType;
if ((!mTypeDataRefs.ContainsKey(typeDataSource)) && (typeDataSource != type))
{
CreateTypeData(typeDataSource, usedStringIdMap, false, true, needsTypeNames, true);
}
typeTypeData = CreateClassVDataGlobal(typeDataSource);
}
else
typeTypeData = CreateClassVDataGlobal(typeInstanceType->ToTypeInstance());
BfType* longType = GetPrimitiveType(BfTypeCode_Int64);
BfType* intType = GetPrimitiveType(BfTypeCode_Int32);
BfType* shortType = GetPrimitiveType(BfTypeCode_Int16);
BfType* byteType = GetPrimitiveType(BfTypeCode_Int8);
BfType* typeIdType = intType;
auto voidPtrType = GetPrimitiveType(BfTypeCode_NullPtr);
auto voidPtrIRType = mBfIRBuilder->MapType(voidPtrType);
auto voidPtrPtrIRType = mBfIRBuilder->GetPointerTo(voidPtrIRType);
auto voidPtrNull = GetDefaultValue(voidPtrType);
SizedArray<BfIRValue, 4> typeValueParams;
GetConstClassValueParam(typeTypeData, typeValueParams);
BfIRValue objectData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(mContext->mBfObjectType, BfIRPopulateType_Full), typeValueParams);
StringT<128> typeDataName;
if ((typeInstance != NULL) && (!typeInstance->IsTypeAlias()))
{
BfMangler::MangleStaticFieldName(typeDataName, mCompiler->GetMangleKind(), typeInstance, "sBfTypeData");
if (typeInstance->mTypeDef->IsGlobalsContainer())
typeDataName += "`G`" + typeInstance->mTypeDef->mProject->mName;
2019-08-23 11:56:54 -07:00
}
else
{
typeDataName += "sBfTypeData.";
BfMangler::Mangle(typeDataName, mCompiler->GetMangleKind(), type, mContext->mScratchModule);
2019-08-23 11:56:54 -07:00
}
int typeCode = BfTypeCode_None;
if (typeInstance != NULL)
{
typeCode = typeInstance->mTypeDef->mTypeCode;
}
else if (type->IsPrimitiveType())
{
BfPrimitiveType* primType = (BfPrimitiveType*)type;
typeCode = primType->mTypeDef->mTypeCode;
}
else if (type->IsPointer())
{
typeCode = BfTypeCode_Pointer;
typeFlags |= BfTypeFlags_Pointer;
}
if (type->IsObject())
{
typeFlags |= BfTypeFlags_Object;
BfMethodInstance* methodInstance = typeInstance->mVirtualMethodTable[mCompiler->GetVTableMethodOffset() + 0].mImplementingMethod;
if (methodInstance->GetOwner() != mContext->mBfObjectType)
typeFlags |= BfTypeFlags_HasDestructor;
}
if (type->IsStruct())
typeFlags |= BfTypeFlags_Struct;
if (type->IsBoxed())
2019-10-09 16:16:01 -07:00
{
typeFlags |= BfTypeFlags_Boxed;
if (((BfBoxedType*)type)->IsBoxedStructPtr())
typeFlags |= BfTypeFlags_Pointer;
}
2019-08-23 11:56:54 -07:00
if (type->IsPrimitiveType())
typeFlags |= BfTypeFlags_Primitive;
if (type->IsTypedPrimitive())
typeFlags |= BfTypeFlags_TypedPrimitive;
if (type->IsTuple())
typeFlags |= BfTypeFlags_Tuple;
if (type->IsNullable())
typeFlags |= BfTypeFlags_Nullable;
if (type->IsSizedArray())
typeFlags |= BfTypeFlags_SizedArray;
if (type->IsSplattable())
typeFlags |= BfTypeFlags_Splattable;
if (type->IsUnion())
typeFlags |= BfTypeFlags_Union;
if (type->IsDelegate())
typeFlags |= BfTypeFlags_Delegate;
if (type->WantsGCMarking())
typeFlags |= BfTypeFlags_WantsMarking;
int memberDataOffset = 0;
if (typeInstance != NULL)
{
for (auto& fieldInstance : typeInstance->mFieldInstances)
{
if (fieldInstance.mDataOffset != -1)
{
memberDataOffset = fieldInstance.mDataOffset;
break;
}
}
}
SizedArray<BfIRValue, 8> typeDataParams =
{
objectData,
GetConstValue(type->mSize, intType), // mSize
GetConstValue(type->mTypeId, typeIdType), // mTypeId
GetConstValue(typeFlags, intType), // mTypeFlags
GetConstValue(memberDataOffset, intType), // mMemberDataOffset
GetConstValue(typeCode, byteType), // mTypeCode
GetConstValue(type->mAlign, byteType),
};
auto typeData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(mContext->mBfTypeType, BfIRPopulateType_Full), typeDataParams);
if (typeInstance == NULL)
{
BfIRValue typeDataVar;
if (needsTypeData)
{
if (type->IsPointer())
{
auto pointerType = (BfPointerType*)type;
SizedArray<BfIRValue, 3> pointerTypeDataParms =
{
typeData,
GetConstValue(pointerType->mElementType->mTypeId, typeIdType),
};
auto reflectPointerType = ResolveTypeDef(mCompiler->mReflectPointerType)->ToTypeInstance();
auto pointerTypeData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectPointerType, BfIRPopulateType_Full), pointerTypeDataParms);
typeDataVar = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapTypeInst(reflectPointerType), true,
BfIRLinkageType_External, pointerTypeData, typeDataName);
mBfIRBuilder->GlobalVar_SetAlignment(typeDataVar, mSystem->mPtrSize);
2019-08-23 11:56:54 -07:00
typeDataVar = mBfIRBuilder->CreateBitCast(typeDataVar, mBfIRBuilder->MapType(mContext->mBfTypeType));
}
else if (type->IsSizedArray())
{
auto sizedArrayType = (BfSizedArrayType*)type;
SizedArray<BfIRValue, 3> sizedArrayTypeDataParms =
{
typeData,
GetConstValue(sizedArrayType->mElementType->mTypeId, typeIdType),
GetConstValue(sizedArrayType->mElementCount, intType)
};
auto reflectSizedArrayType = ResolveTypeDef(mCompiler->mReflectSizedArrayType)->ToTypeInstance();
auto sizedArrayTypeData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectSizedArrayType, BfIRPopulateType_Full), sizedArrayTypeDataParms);
typeDataVar = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapTypeInst(reflectSizedArrayType), true,
BfIRLinkageType_External, sizedArrayTypeData, typeDataName);
mBfIRBuilder->GlobalVar_SetAlignment(typeDataVar, mSystem->mPtrSize);
2019-08-23 11:56:54 -07:00
typeDataVar = mBfIRBuilder->CreateBitCast(typeDataVar, mBfIRBuilder->MapType(mContext->mBfTypeType));
}
else
{
typeDataVar = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapTypeInst(mContext->mBfTypeType), true,
BfIRLinkageType_External, typeData, typeDataName);
mBfIRBuilder->GlobalVar_SetAlignment(typeDataVar, mSystem->mPtrSize);
2019-08-23 11:56:54 -07:00
}
}
else
typeDataVar = mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(mContext->mBfTypeType));
2019-08-23 11:56:54 -07:00
mTypeDataRefs[typeInstance] = typeDataVar;
return typeDataVar;
}
// Reserve position
mTypeDataRefs[typeInstance] = BfIRValue();
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
BfTypeDef* typeDef = typeInstance->mTypeDef;
StringT<128> mangledName;
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), typeInstance, typeInstance->mModule);
2019-08-23 11:56:54 -07:00
for (int methodIdx = 0; methodIdx < (int)typeDef->mMethods.size(); methodIdx++)
{
auto methodDef = typeDef->mMethods[methodIdx];
auto methodInstance = typeInstance->mMethodInstanceGroups[methodIdx].mDefault;
if (methodInstance == NULL)
continue;
if (typeInstance->IsUnspecializedType())
continue;
if (!typeInstance->IsTypeMemberAccessible(methodDef->mDeclaringType, mProject))
{
if (methodInstance->mChainType == BfMethodChainType_ChainMember)
{
BF_ASSERT(!methodInstance->GetOwner()->IsUnspecializedType());
// We need to create an empty thunk for this chained method
BfIRFunction func = CreateFunctionFrom(methodInstance, false, methodInstance->mAlwaysInline);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->SetActiveFunction(func);
auto block = mBfIRBuilder->CreateBlock("entry", true);
mBfIRBuilder->SetInsertPoint(block);
mBfIRBuilder->CreateRetVoid();
mBfIRBuilder->SetActiveFunction(BfIRFunction());
}
}
}
SizedArray<BfIRValue, 32> vData;
BfIRValue classVDataVar;
String classVDataName;
if (typeInstance->mSlotNum >= 0)
{
// For interfaces we ONLY emit the slot num
int virtSlotIdx = typeInstance->mSlotNum + 1 + mCompiler->GetDynCastVDataCount();
StringT<128> slotVarName;
BfMangler::MangleStaticFieldName(slotVarName, mCompiler->GetMangleKind(), typeInstance, "sBfSlotOfs");
auto intType = GetPrimitiveType(BfTypeCode_Int32);
auto slotNumVar = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(intType), true, BfIRLinkageType_External,
GetConstValue32(virtSlotIdx), slotVarName);
}
else if ((typeInstance->IsObject()) && (!typeInstance->IsUnspecializedType()) && (needsVData))
{
int dynCastDataElems = 0;
int numElements = 1;
int vDataOfs = 1; // The number of intptrs before the iface slot map
numElements += mCompiler->mMaxInterfaceSlots;
if (!typeInstance->IsInterface())
{
dynCastDataElems = 1 + mCompiler->mMaxInterfaceSlots;
numElements += mCompiler->GetDynCastVDataCount();
numElements += typeInstance->mVirtualMethodTableSize;
}
int expectNumElements = 0;
if (!typeDef->mIsStatic)
{
classVDataVar = CreateClassVDataGlobal(typeInstance, &expectNumElements, &classVDataName);
}
vData.push_back(BfIRValue()); // Type*
SizedArray<BfIRValue, 1> extVTableData;
SizedArrayImpl<BfIRValue>* vFuncDataExt = &extVTableData;
2020-05-09 11:31:57 -07:00
2019-08-23 11:56:54 -07:00
if (!typeInstance->IsInterface())
{
Array<int32> dynCastData;
if (typeInstance->IsBoxed())
{
auto underlyingType = typeInstance->GetUnderlyingType()->ToTypeInstance();
dynCastData.Add(underlyingType->mInheritanceId);
}
else
dynCastData.Add(typeInstance->mInheritanceId);
for (int i = 0; i < mCompiler->mMaxInterfaceSlots; i++)
dynCastData.Add(0);
dynCastData.Add(0);
2019-10-09 16:16:01 -07:00
2019-08-23 11:56:54 -07:00
auto checkTypeInst = typeInstance;
while (checkTypeInst != NULL)
{
for (auto&& interfaceEntry : checkTypeInst->mInterfaces)
{
if (interfaceEntry.mInterfaceType->mSlotNum >= 0)
{
2019-10-09 16:16:01 -07:00
if (dynCastData[interfaceEntry.mInterfaceType->mSlotNum + 1] == 0)
dynCastData[interfaceEntry.mInterfaceType->mSlotNum + 1] = interfaceEntry.mInterfaceType->mTypeId;
2019-08-23 11:56:54 -07:00
}
}
2019-10-09 16:16:01 -07:00
checkTypeInst = checkTypeInst->GetImplBaseType();
2019-08-23 11:56:54 -07:00
}
if (mSystem->mPtrSize == 8)
{
int intPtrCount = (dynCastDataElems + 1) / 2;
vDataOfs += intPtrCount;
uint64* intPtrData = (uint64*)&dynCastData[0];
for (int i = 0; i < intPtrCount; i++)
{
uint64 val = intPtrData[i];
if (val == 0)
{
vData.push_back(voidPtrNull);
}
else
{
auto intPtrVal = mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, val);
vData.push_back(mBfIRBuilder->CreateBitCast(intPtrVal, voidPtrIRType));
}
}
}
else
{
int intPtrCount = dynCastDataElems;
vDataOfs += intPtrCount;
uint32* intPtrData = (uint32*)&dynCastData[0];
for (int i = 0; i < intPtrCount; i++)
{
uint32 val = intPtrData[i];
if (val == 0)
{
vData.push_back(voidPtrNull);
}
else
{
auto intPtrVal = mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, (int)val);
vData.push_back(mBfIRBuilder->CreateBitCast(intPtrVal, voidPtrIRType));
}
}
}
}
for (int iSlotIdx = 0; iSlotIdx < mCompiler->mMaxInterfaceSlots; iSlotIdx++)
vData.push_back(voidPtrNull);
struct _InterfaceMatchEntry
{
BfTypeInterfaceEntry* mEntry;
BfTypeInstance* mEntrySource;
Array<BfTypeInterfaceEntry*> mAmbiguousEntries;
};
int highestIFaceVirtIdx = 0;
Dictionary<BfTypeInstance*, _InterfaceMatchEntry> interfaceMap;
auto checkTypeInst = typeInstance;
2019-10-09 16:16:01 -07:00
bool forceInterfaceSet = false;
2019-08-23 11:56:54 -07:00
while (checkTypeInst != NULL)
{
for (auto&& interfaceEntry : checkTypeInst->mInterfaces)
{
highestIFaceVirtIdx = BF_MAX(highestIFaceVirtIdx, interfaceEntry.mStartVirtualIdx + interfaceEntry.mInterfaceType->mVirtualMethodTableSize);
if (!typeInstance->IsTypeMemberAccessible(interfaceEntry.mDeclaringType, mProject))
continue;
_InterfaceMatchEntry* matchEntry = NULL;
if (interfaceMap.TryGetValue(interfaceEntry.mInterfaceType, &matchEntry))
{
auto prevEntry = matchEntry->mEntry;
bool isBetter = TypeIsSubTypeOf(checkTypeInst, matchEntry->mEntrySource);
bool isWorse = TypeIsSubTypeOf(matchEntry->mEntrySource, checkTypeInst);
2019-10-09 16:16:01 -07:00
if (forceInterfaceSet)
{
isBetter = true;
isWorse = false;
}
2019-08-23 11:56:54 -07:00
if (isBetter == isWorse)
CompareDeclTypes(interfaceEntry.mDeclaringType, prevEntry->mDeclaringType, isBetter, isWorse);
if (isBetter == isWorse)
{
if (matchEntry->mAmbiguousEntries.empty())
matchEntry->mAmbiguousEntries.push_back(prevEntry);
matchEntry->mAmbiguousEntries.push_back(&interfaceEntry);
continue;
}
else if (isBetter)
{
matchEntry->mEntry = &interfaceEntry;
matchEntry->mEntrySource = checkTypeInst;
matchEntry->mAmbiguousEntries.Clear();
}
else
continue;
}
else
{
_InterfaceMatchEntry matchEntry;
matchEntry.mEntry = &interfaceEntry;
matchEntry.mEntrySource = checkTypeInst;
interfaceMap[interfaceEntry.mInterfaceType] = matchEntry;
}
}
2019-10-09 16:16:01 -07:00
checkTypeInst = checkTypeInst->GetImplBaseType();
}
2019-08-23 11:56:54 -07:00
for (auto interfacePair : interfaceMap)
{
auto& interfaceMatchEntry = interfacePair.mValue;
if (!interfaceMatchEntry.mAmbiguousEntries.empty())
{
auto error = Fail(StrFormat("Type '%s' has an ambiguous declaration for interface '%s'", TypeToString(typeInstance).c_str(), TypeToString(interfaceMatchEntry.mEntry->mInterfaceType).c_str()), interfaceMatchEntry.mEntry->mDeclaringType->GetRefNode());
if (error != NULL)
{
for (auto ambiguiousEntry : interfaceMatchEntry.mAmbiguousEntries)
mCompiler->mPassInstance->MoreInfo("See other declaration", ambiguiousEntry->mDeclaringType->GetRefNode());
}
}
}
if ((!typeInstance->IsInterface()) && (typeInstance->mVirtualMethodTableSize > 0) && (needsVData))
{
int startTabIdx = (int)vData.size();
SizedArray<BfTypeInstance*, 8> origVirtTypeStack;
BfTypeInstance* checkTypeInst = typeInstance;
while (checkTypeInst != NULL)
{
origVirtTypeStack.push_back(checkTypeInst);
2019-10-09 16:16:01 -07:00
checkTypeInst = checkTypeInst->GetImplBaseType();
2019-08-23 11:56:54 -07:00
}
Array<BfVirtualMethodEntry> origVTable;
int origVirtIdx = 0;
if (typeInstance->mTypeDef->mIsCombinedPartial)
{
HashSet<String> reslotNames;
for (int virtIdx = 0; virtIdx < (int)typeInstance->mVirtualMethodTable.size(); virtIdx++)
{
auto& entry = typeInstance->mVirtualMethodTable[virtIdx];
if (entry.mDeclaringMethod.mMethodNum == -1)
continue;
BfMethodInstance* methodInstance = (BfMethodInstance*)entry.mImplementingMethod;
if ((methodInstance == NULL) || (!typeInstance->IsTypeMemberAccessible(methodInstance->mMethodDef->mDeclaringType, mProject)))
{
if (origVTable.empty())
origVTable = typeInstance->mVirtualMethodTable;
BfMethodInstance* declMethodInstance = entry.mDeclaringMethod;
if (typeInstance->IsTypeMemberAccessible(declMethodInstance->mMethodDef->mDeclaringType, mProject))
{
// Prepare to reslot...
entry.mImplementingMethod = entry.mDeclaringMethod;
reslotNames.Add(declMethodInstance->mMethodDef->mName);
}
else
{
// Decl isn't accessible, null out entry
entry.mImplementingMethod = BfMethodRef();
}
}
}
if (!reslotNames.IsEmpty())
{
BfAmbiguityContext ambiguityContext;
ambiguityContext.mTypeInstance = typeInstance;
ambiguityContext.mModule = this;
ambiguityContext.mIsProjectSpecific = true;
ambiguityContext.mIsReslotting = true;
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
for (auto& methodGroup : typeInstance->mMethodInstanceGroups)
{
auto methodInstance = methodGroup.mDefault;
if ((methodInstance == NULL) || (!methodInstance->mMethodDef->mIsOverride))
continue;
if (!reslotNames.Contains(methodInstance->mMethodDef->mName))
continue;
if (!typeInstance->IsTypeMemberAccessible(methodInstance->mMethodDef->mDeclaringType, mProject))
continue;
if ((methodInstance->mChainType != BfMethodChainType_None) && (methodInstance->mChainType != BfMethodChainType_ChainHead))
continue;
SlotVirtualMethod(methodInstance, &ambiguityContext);
}
ambiguityContext.Finish();
}
}
bool isInExts = false;
for (int virtIdx = 0; virtIdx < (int)typeInstance->mVirtualMethodTable.size(); virtIdx++)
{
BfIRValue vValue = voidPtrNull;
auto& entry = typeInstance->mVirtualMethodTable[virtIdx];
BfModuleMethodInstance moduleMethodInst;
if (entry.mDeclaringMethod.mMethodNum == -1)
{
// mMethodNum of -1 indicates a vExt slot rather than an actual method reference
isInExts = true;
BfIRValue vExt = CreateClassVDataExtGlobal(entry.mDeclaringMethod.mTypeInstance, typeInstance, virtIdx);
if (vExt)
vValue = mBfIRBuilder->CreateBitCast(vExt, voidPtrIRType);
}
else
{
BfMethodInstance* declaringMethodInstance = (BfMethodInstance*)entry.mDeclaringMethod;
if ((declaringMethodInstance != NULL) && (declaringMethodInstance->mMethodInstanceGroup->IsImplemented()) && (declaringMethodInstance->mIsReified))
{
BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].IsImplemented());
BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].mDefault->mIsReified);
BfMethodInstance* methodInstance = (BfMethodInstance*)entry.mImplementingMethod;
if ((methodInstance != NULL) && (!methodInstance->mMethodDef->mIsAbstract))
{
BF_ASSERT(typeInstance->IsTypeMemberAccessible(methodInstance->mMethodDef->mDeclaringType, mProject));
moduleMethodInst = GetMethodInstanceAtIdx(methodInstance->mMethodInstanceGroup->mOwner, methodInstance->mMethodInstanceGroup->mMethodIdx, NULL, BfGetMethodInstanceFlag_NoInline);
2019-08-23 11:56:54 -07:00
auto funcPtr = mBfIRBuilder->CreateBitCast(moduleMethodInst.mFunc, voidPtrIRType);
vValue = funcPtr;
}
}
}
while (origVirtTypeStack.size() != 0)
{
BfTypeInstance* useTypeInst = origVirtTypeStack[origVirtTypeStack.size() - 1];
if (virtIdx >= useTypeInst->mVirtualMethodTableSize)
{
// Moved past current base vtable size
origVirtTypeStack.pop_back();
continue;
}
if (origVirtIdx < useTypeInst->GetOrigVTableSize())
{
// We are within the original vtable size
int idx = startTabIdx + origVirtIdx;
origVirtIdx++;
vData.push_back(vValue);
}
else
{
// This is a new entry, it only gets added to the ext
BF_ASSERT(isInExts);
}
break;
}
}
if (!origVTable.empty())
typeInstance->mVirtualMethodTable = origVTable;
}
int ifaceMethodExtStart = (int)typeInstance->GetIFaceVMethodSize();
if (typeInstance->mHotTypeData != NULL)
{
if (typeInstance->mHotTypeData->mOrigInterfaceMethodsLength != -1)
ifaceMethodExtStart = typeInstance->mHotTypeData->mOrigInterfaceMethodsLength;
}
SizedArray<BfIRValue, 32> ifaceMethodExtData;
int iFaceMethodStartIdx = (int)vData.size();
vData.resize(iFaceMethodStartIdx + ifaceMethodExtStart);
for (int i = iFaceMethodStartIdx; i < iFaceMethodStartIdx + ifaceMethodExtStart; i++)
vData[i] = voidPtrNull;
if (expectNumElements != 0)
BF_ASSERT(expectNumElements == vData.size());
//BF_ASSERT(vData.size() == expectNumElements);
// for (int i = 0; i < (int)typeInstance->mInterfaceMethodTable.size() - ifaceMethodExtStart; i++)
// ifaceMethodExtData.Add(voidPtrNull);
int ifaceEntryIdx = iFaceMethodStartIdx;
for (auto& interfacePair : interfaceMap)
{
auto interfaceEntry = interfacePair.mValue.mEntry;
bool makeEmpty = false;
if (!typeInstance->IsTypeMemberAccessible(interfaceEntry->mDeclaringType, mProject))
makeEmpty = true;
int endVirtualIdx = interfaceEntry->mStartVirtualIdx + interfaceEntry->mInterfaceType->mVirtualMethodTableSize;
bool useExt = endVirtualIdx > ifaceMethodExtStart;
for (int methodIdx = 0; methodIdx < (int)interfaceEntry->mInterfaceType->mMethodInstanceGroups.size(); methodIdx++)
{
BfIRValue pushValue;
BfMethodInstance* ifaceMethodInstance = interfaceEntry->mInterfaceType->mMethodInstanceGroups[methodIdx].mDefault;
if ((ifaceMethodInstance == NULL) || (ifaceMethodInstance->mVirtualTableIdx == -1))
continue;
if ((!ifaceMethodInstance->mIsReified) || (!ifaceMethodInstance->mMethodInstanceGroup->IsImplemented()))
continue;
if (makeEmpty)
{
pushValue = voidPtrNull;
}
else
{
auto& methodRef = typeInstance->mInterfaceMethodTable[interfaceEntry->mStartInterfaceTableIdx + methodIdx].mMethodRef;
auto methodInstance = (BfMethodInstance*)methodRef;
if ((methodInstance != NULL) && (!methodInstance->mMethodDef->mIsAbstract))
{
BF_ASSERT(methodInstance->mIsReified);
// This doesn't work because we may have FOREIGN methods from implicit interface methods
2019-10-09 16:16:01 -07:00
//auto moduleMethodInst = GetMethodInstanceAtIdx(methodRef.mTypeInstance, methodRef.mMethodNum);
auto moduleMethodInst = ReferenceExternalMethodInstance(methodInstance, BfGetMethodInstanceFlag_NoInline);
2019-08-23 11:56:54 -07:00
auto funcPtr = mBfIRBuilder->CreateBitCast(moduleMethodInst.mFunc, voidPtrIRType);
pushValue = funcPtr;
}
else
{
pushValue = voidPtrNull;
}
}
int idx = interfaceEntry->mStartVirtualIdx + ifaceMethodInstance->mVirtualTableIdx;
if (idx < ifaceMethodExtStart)
{
BF_ASSERT(iFaceMethodStartIdx + idx < (int)vData.size());
vData[iFaceMethodStartIdx + idx] = pushValue;
}
else
{
BF_ASSERT(useExt);
int extIdx = idx - ifaceMethodExtStart;
while (extIdx >= ifaceMethodExtData.size())
ifaceMethodExtData.Add(voidPtrNull);
ifaceMethodExtData[extIdx] = pushValue;
}
}
2019-10-09 16:16:01 -07:00
}
if (typeInstance->IsBoxed())
{
BfBoxedType* boxedType = (BfBoxedType*)typeInstance;
if (boxedType->IsBoxedStructPtr())
{
// Force override of GetHashCode so we use the pointer address as the hash code
checkTypeInst = CreateBoxedType(ResolveTypeDef(mCompiler->mPointerTypeDef));
// Force override of GetHashCode so we use the pointer address as the hash code
for (auto& checkIFace : checkTypeInst->mInterfaces)
{
for (int methodIdx = 0; methodIdx < (int)checkIFace.mInterfaceType->mMethodInstanceGroups.size(); methodIdx++)
{
BfIRValue pushValue;
BfMethodInstance* ifaceMethodInstance = checkIFace.mInterfaceType->mMethodInstanceGroups[methodIdx].mDefault;
if ((ifaceMethodInstance == NULL) || (ifaceMethodInstance->mVirtualTableIdx == -1))
continue;
if ((!ifaceMethodInstance->mIsReified) || (!ifaceMethodInstance->mMethodInstanceGroup->IsImplemented()))
continue;
auto& methodRef = checkTypeInst->mInterfaceMethodTable[checkIFace.mStartInterfaceTableIdx + methodIdx].mMethodRef;
auto methodInstance = (BfMethodInstance*)methodRef;
BF_ASSERT(methodInstance->mIsReified);
// This doesn't work because we may have FOREIGN methods from implicit interface methods
//auto moduleMethodInst = GetMethodInstanceAtIdx(methodRef.mTypeInstance, methodRef.mMethodNum);
auto moduleMethodInst = ReferenceExternalMethodInstance(methodInstance, BfGetMethodInstanceFlag_NoInline);
2019-10-09 16:16:01 -07:00
auto funcPtr = mBfIRBuilder->CreateBitCast(moduleMethodInst.mFunc, voidPtrIRType);
int idx = checkIFace.mStartVirtualIdx + ifaceMethodInstance->mVirtualTableIdx;
vData[iFaceMethodStartIdx + idx] = funcPtr;
}
}
}
}
2019-08-23 11:56:54 -07:00
if ((needsVData) && (!typeInstance->mTypeDef->mIsStatic))
{
BfIRValue ifaceMethodExtVar;
if (!ifaceMethodExtData.IsEmpty())
{
StringT<128> classVDataName;
BfMangler::MangleStaticFieldName(classVDataName, mCompiler->GetMangleKind(), typeInstance, "bf_hs_replace_IFaceExt");
auto arrayType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr), (int)ifaceMethodExtData.size());
ifaceMethodExtVar = mBfIRBuilder->CreateGlobalVariable(arrayType, true,
BfIRLinkageType_External, BfIRValue(), classVDataName);
BfIRType extVTableType = mBfIRBuilder->GetSizedArrayType(voidPtrIRType, (int)ifaceMethodExtData.size());
BfIRValue extVTableConst = mBfIRBuilder->CreateConstArray(extVTableType, ifaceMethodExtData);
mBfIRBuilder->GlobalVar_SetInitializer(ifaceMethodExtVar, extVTableConst);
}
for (auto& ifaceInstPair : interfaceMap)
{
auto interfaceEntry = ifaceInstPair.mValue.mEntry;
int slotNum = interfaceEntry->mInterfaceType->mSlotNum;
if (slotNum >= 0)
{
BfIRValue vtablePtr;
int idx = interfaceEntry->mStartVirtualIdx;
int endVirtualIdx = interfaceEntry->mStartVirtualIdx + interfaceEntry->mInterfaceType->mVirtualMethodTableSize;
if (endVirtualIdx > ifaceMethodExtStart)
vtablePtr = mBfIRBuilder->CreateInBoundsGEP(ifaceMethodExtVar, 0, interfaceEntry->mStartVirtualIdx - ifaceMethodExtStart);
else
vtablePtr = mBfIRBuilder->CreateInBoundsGEP(classVDataVar, 0, iFaceMethodStartIdx + interfaceEntry->mStartVirtualIdx);
int slotVirtIdx = slotNum + vDataOfs;
if (vData[slotVirtIdx] == voidPtrNull)
{
vData[slotVirtIdx] = mBfIRBuilder->CreateBitCast(vtablePtr, voidPtrIRType);
}
else
{
if (mCompiler->IsHotCompile())
Fail("Interface slot collision error. Restart program or undo interface changes.", typeDef->GetRefNode());
else
Fail("Interface slot collision error", typeDef->GetRefNode());
}
}
}
}
}
BfIRValue typeNameConst;
BfIRValue namespaceConst;
if (needsTypeNames)
{
typeNameConst = GetStringObjectValue(typeDef->mName->mString, true);
namespaceConst = GetStringObjectValue(typeDef->mNamespace.ToString(), true);
}
else
{
auto stringType = ResolveTypeDef(mCompiler->mStringTypeDef);
typeNameConst = mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(stringType));
namespaceConst = mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(stringType));
}
int baseTypeId = 0;
if (typeInstance->mBaseType != NULL)
{
baseTypeId = typeInstance->mBaseType->mTypeId;
}
SizedArray<BfIRValue, 16> customAttrs;
BfTypeInstance* attributeType = mContext->mUnreifiedModule->ResolveTypeDef(mCompiler->mAttributeTypeDef)->ToTypeInstance();
BfIRValue castedClassVData;
if (classVDataVar)
castedClassVData = mBfIRBuilder->CreateBitCast(classVDataVar, mBfIRBuilder->MapType(mContext->mBfClassVDataPtrType));
else
castedClassVData = GetDefaultValue(mContext->mBfClassVDataPtrType);
bool freflectIncludeTypeData = false;
bool reflectIncludeAllFields = false;
bool reflectIncludeAllMethods = false;
if (TypeIsSubTypeOf(typeInstance, attributeType))
{
reflectIncludeAllFields = true;
reflectIncludeAllMethods = true;
}
enum ReflectKind
{
ReflectKind_None = 0,
ReflectKind_Type = 1,
ReflectKind_NonStaticFields = 2,
ReflectKind_StaticFields = 4,
ReflectKind_DefaultConstructor = 8,
ReflectKind_Constructors = 0x10,
ReflectKind_StaticMethods = 0x20,
ReflectKind_Methods = 0x40,
ReflectKind_User = 0x80,
ReflectKind_All = 0x7F,
ReflectKind_ApplyToInnerTypes = 0x80
};
ReflectKind reflectKind = ReflectKind_Type;
auto _GetReflectUser = [&](BfTypeInstance* attrType)
{
ReflectKind reflectKind = ReflectKind_None;
if (attrType->mCustomAttributes != NULL)
{
for (auto& customAttr : attrType->mCustomAttributes->mAttributes)
{
if (customAttr.mType->mTypeDef->mName->ToString() == "AttributeUsageAttribute")
{
for (auto& prop : customAttr.mSetProperties)
{
auto propDef = prop.mPropertyRef.mTypeInstance->mTypeDef->mProperties[prop.mPropertyRef.mPropIdx];
if (propDef->mName == "ReflectUser")
{
if (prop.mParam.mValue.IsConst())
{
auto constant = attrType->mConstHolder->GetConstant(prop.mParam.mValue);
reflectKind = (ReflectKind)(reflectKind | (ReflectKind)constant->mInt32);
}
}
}
// Check for Flags arg
if (customAttr.mCtorArgs.size() < 2)
continue;
auto constant = attrType->mConstHolder->GetConstant(customAttr.mCtorArgs[1]);
if (constant == NULL)
continue;
if (constant->mTypeCode == BfTypeCode_Boolean)
continue;
if ((constant->mInt8 & BfCustomAttributeFlags_ReflectAttribute) != 0)
reflectKind = (ReflectKind)(reflectKind | ReflectKind_User);
}
}
}
return reflectKind;
};
auto _GetReflectUserFromDirective = [&](BfAttributeDirective* attributesDirective, BfAttributeTargets attrTarget)
{
ReflectKind reflectKind = ReflectKind_None;
auto customAttrs = GetCustomAttributes(attributesDirective, attrTarget);
if (customAttrs != NULL)
{
for (auto& attr : customAttrs->mAttributes)
{
reflectKind = (ReflectKind)(reflectKind | _GetReflectUser(attr.mType));
}
}
return reflectKind;
};
//
{
auto checkTypeInstance = typeInstance;
while (checkTypeInstance != NULL)
{
if (checkTypeInstance->mCustomAttributes != NULL)
{
auto checkReflectKind = ReflectKind_None;
for (auto& customAttr : checkTypeInstance->mCustomAttributes->mAttributes)
{
if (customAttr.mType->mTypeDef->mName->ToString() == "ReflectAttribute")
{
if (customAttr.mCtorArgs.size() > 0)
{
auto constant = checkTypeInstance->mConstHolder->GetConstant(customAttr.mCtorArgs[0]);
checkReflectKind = (ReflectKind)((int)checkReflectKind | constant->mInt32);
}
else
{
checkReflectKind = ReflectKind_All;
}
}
else
{
auto userReflectKind = _GetReflectUser(customAttr.mType);
checkReflectKind = (ReflectKind)(checkReflectKind | userReflectKind);
}
}
if ((checkTypeInstance == typeInstance) ||
((checkReflectKind & ReflectKind_ApplyToInnerTypes) != 0))
{
reflectKind = (ReflectKind)(reflectKind | checkReflectKind);
}
}
checkTypeInstance = mContext->mUnreifiedModule->GetOuterType(checkTypeInstance);
2019-08-23 11:56:54 -07:00
}
}
// Fields
BfType* reflectFieldDataType = ResolveTypeDef(mCompiler->mReflectFieldDataDef);
BfIRValue emptyValueType = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectFieldDataType->ToTypeInstance()->mBaseType), SizedArray<BfIRValue, 1>());
auto _HandleCustomAttrs = [&](BfCustomAttributes* customAttributes)
{
if (customAttributes == NULL)
return -1;
Array<BfCustomAttribute*> reflectAttributes;
for (auto& attr : customAttributes->mAttributes)
{
bool wantAttribute = false;
if (attr.mType->mCustomAttributes != NULL)
{
auto usageAttribute = attr.mType->mCustomAttributes->Get(mCompiler->mAttributeUsageAttributeTypeDef);
if (usageAttribute != NULL)
{
// Check for Flags arg
if (usageAttribute->mCtorArgs.size() < 2)
continue;
auto constant = attr.mType->mConstHolder->GetConstant(usageAttribute->mCtorArgs[1]);
if (constant == NULL)
continue;
if (constant->mTypeCode == BfTypeCode_Boolean)
continue;
if ((constant->mInt8 & BfCustomAttributeFlags_ReflectAttribute) != 0)
wantAttribute = true;
}
}
if ((wantAttribute) && (attr.mType->mIsReified))
{
reflectAttributes.Add(&attr);
}
}
if (reflectAttributes.size() == 0)
return -1;
#define PUSH_INT8(val) data.push_back((uint8)val)
#define PUSH_INT16(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); }
#define PUSH_INT32(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); data.push_back((val >> 16) & 0xFF); data.push_back((val >> 24) & 0xFF); }
#define PUSH_INT64(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); data.push_back((val >> 16) & 0xFF); data.push_back((val >> 24) & 0xFF); \
data.push_back((val >> 32) & 0xFF); data.push_back((val >> 40) & 0xFF); data.push_back((val >> 48) & 0xFF); data.push_back((val >> 56) & 0xFF); }
SizedArray<uint8, 16> data;
int customAttrIdx = (int)customAttrs.size();
data.push_back((uint8)reflectAttributes.size());
for (auto attr : reflectAttributes)
{
// Size prefix
int sizeIdx = (int)data.size();
PUSH_INT16(0); // mSize
PUSH_INT32(attr->mType->mTypeId); // mType
PUSH_INT16(attr->mCtor->mIdx);
auto ctorMethodInstance = GetRawMethodInstanceAtIdx(attr->mType, attr->mCtor->mIdx);
int argIdx = 0;
for (auto arg : attr->mCtorArgs)
{
auto constant = typeInstance->mConstHolder->GetConstant(arg);
bool handled = false;
if (constant != NULL)
{
auto argType = ctorMethodInstance->GetParamType(argIdx);
if (argType->IsObject())
{
BF_ASSERT(argType->ToTypeInstance()->mTypeDef == mCompiler->mStringTypeDef);
int stringId = constant->mInt32;
int* orderedIdPtr;
if (usedStringIdMap.TryAdd(stringId, NULL, &orderedIdPtr))
{
*orderedIdPtr = (int)usedStringIdMap.size() - 1;
}
GetStringObjectValue(stringId);
PUSH_INT8(0xFF); // String
PUSH_INT32(*orderedIdPtr);
continue;
}
PUSH_INT8(constant->mTypeCode);
if ((constant->mTypeCode == BfTypeCode_Int64) ||
(constant->mTypeCode == BfTypeCode_UInt64) ||
(constant->mTypeCode == BfTypeCode_Double))
{
PUSH_INT64(constant->mInt64);
}
else if ((constant->mTypeCode == BfTypeCode_Int32) ||
(constant->mTypeCode == BfTypeCode_UInt32))
{
PUSH_INT32(constant->mInt32);
}
else if (constant->mTypeCode == BfTypeCode_Single)
{
float val = (float)constant->mDouble;
PUSH_INT32(*(int*)&val);
}
else if ((constant->mTypeCode == BfTypeCode_Int16) ||
(constant->mTypeCode == BfTypeCode_UInt16))
{
PUSH_INT16(constant->mInt16);
}
else if ((constant->mTypeCode == BfTypeCode_Int8) ||
(constant->mTypeCode == BfTypeCode_UInt8))
{
PUSH_INT8(constant->mInt8);
}
else if (constant->mTypeCode == BfTypeCode_Object)
{
BFMODULE_FATAL(this, "Unhandled");
2019-08-23 11:56:54 -07:00
}
else
{
BFMODULE_FATAL(this, "Unhandled");
2019-08-23 11:56:54 -07:00
}
}
else if (!handled)
{
BFMODULE_FATAL(this, "Unhandled");
2019-08-23 11:56:54 -07:00
}
argIdx++;
}
int size = (int)data.size() - sizeIdx;
*((uint16*)&data[sizeIdx]) = size;
}
#undef PUSH_INT8
#undef PUSH_INT16
#undef PUSH_INT32
SizedArray<BfIRValue, 16> dataValues;
for (uint8 val : data)
dataValues.push_back(GetConstValue8(val));
auto dataArrayType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->MapType(byteType), (int)data.size());
auto customAttrConst = mBfIRBuilder->CreateConstArray(dataArrayType, dataValues);
BfIRValue customAttrArray = mBfIRBuilder->CreateGlobalVariable(dataArrayType, true, BfIRLinkageType_Internal,
customAttrConst, typeDataName + StrFormat(".customAttr%d", customAttrIdx));
BfIRValue customAttrArrayPtr = mBfIRBuilder->CreateBitCast(customAttrArray, voidPtrIRType);
customAttrs.push_back(customAttrArrayPtr);
return customAttrIdx;
};
int typeCustomAttrIdx = _HandleCustomAttrs(typeInstance->mCustomAttributes);
SizedArray<int, 16> reflectFieldIndices;
for (int pass = 0; pass < 2; pass++)
{
bool skippedField = false;
reflectFieldIndices.clear();
for (int fieldIdx = 0; fieldIdx < (int)typeInstance->mFieldInstances.size(); fieldIdx++)
{
BfFieldInstance* fieldInstance = &typeInstance->mFieldInstances[fieldIdx];
BfFieldDef* fieldDef = fieldInstance->GetFieldDef();
if (fieldDef == NULL)
continue;
auto fieldReflectKind = (ReflectKind)(reflectKind & ~ReflectKind_User);
bool includeField = reflectIncludeAllFields;
if (fieldInstance->mCustomAttributes != NULL)
{
for (auto& customAttr : fieldInstance->mCustomAttributes->mAttributes)
{
if (customAttr.mType->mTypeDef->mName->ToString() == "ReflectAttribute")
{
includeField = true;
}
else
{
auto userReflectKind = _GetReflectUser(customAttr.mType);
fieldReflectKind = (ReflectKind)(fieldReflectKind | userReflectKind);
}
}
}
if ((fieldReflectKind & ReflectKind_User) != 0)
includeField = true;
if ((!fieldDef->mIsStatic) && ((fieldReflectKind & ReflectKind_NonStaticFields) != 0))
includeField = true;
if ((fieldDef->mIsStatic) && ((fieldReflectKind & ReflectKind_StaticFields) != 0))
includeField = true;
includeField |= forceReflectFields;
if (!includeField)
{
skippedField = true;
continue;
}
reflectFieldIndices.Add(fieldIdx);
}
// For a splattable type, we need to either include all fields or zero fields. This is to allow us to correctly splat
// params for reflected method calls even if we don't have reflection field info for the splatted type. This is
// possible because we write out a SplatData structure containing just TypeIds in that case
if (pass == 0)
{
if (!typeInstance->IsSplattable())
break;
if (!skippedField)
break;
if (reflectFieldIndices.size() == 0)
break;
}
}
SizedArray<BfIRValue, 16> fieldTypes;
enum FieldFlags
{
FieldFlags_Protected = 3,
FieldFlags_Public = 6,
FieldFlags_Static = 0x10,
FieldFlags_Const = 0x40,
FieldFlags_SpecialName = 0x80,
FieldFlags_EnumPayload = 0x100,
FieldFlags_EnumDiscriminator = 0x200
};
if ((typeInstance->IsPayloadEnum()) && (!typeInstance->IsBoxed()))
{
BfType* payloadType = typeInstance->GetUnionInnerType();
if (!payloadType->IsValuelessType())
{
BfIRValue payloadNameConst = GetStringObjectValue("$payload", true);
SizedArray<BfIRValue, 8> payloadFieldVals =
{
emptyValueType,
payloadNameConst, // mName
GetConstValue(0, longType), // mConstValue
GetConstValue(0, intType), // mDataOffset
GetConstValue(payloadType->mTypeId, typeIdType), // mFieldTypeId
GetConstValue(FieldFlags_SpecialName | FieldFlags_EnumPayload, shortType), // mFlags
GetConstValue(-1, intType), // mCustomAttributesIdx
};
auto payloadFieldData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectFieldDataType->ToTypeInstance(), BfIRPopulateType_Full), payloadFieldVals);
fieldTypes.push_back(payloadFieldData);
}
BfType* dscrType = typeInstance->GetDiscriminatorType();
BfIRValue dscrNameConst = GetStringObjectValue("$discriminator", true);
SizedArray<BfIRValue, 8> dscrFieldVals =
{
emptyValueType,
dscrNameConst, // mName
GetConstValue(0, longType), // mConstValue
GetConstValue(BF_ALIGN(payloadType->mSize, dscrType->mAlign), intType), // mDataOffset
GetConstValue(dscrType->mTypeId, typeIdType), // mFieldTypeId
GetConstValue(FieldFlags_SpecialName | FieldFlags_EnumDiscriminator, shortType), // mFlags
GetConstValue(-1, intType), // mCustomAttributesIdx
};
auto dscrFieldData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectFieldDataType->ToTypeInstance(), BfIRPopulateType_Full), dscrFieldVals);
fieldTypes.push_back(dscrFieldData);
}
for (auto fieldIdx : reflectFieldIndices)
{
if (!needsTypeData)
break;
BfFieldInstance* fieldInstance = &typeInstance->mFieldInstances[fieldIdx];
BfFieldDef* fieldDef = fieldInstance->GetFieldDef();
BfIRValue fieldNameConst = GetStringObjectValue(fieldDef->mName, true);
int typeId = 0;
auto fieldType = fieldInstance->GetResolvedType();
if (fieldType->IsGenericParam())
{
//TODO:
}
else
typeId = fieldType->mTypeId;
FieldFlags fieldFlags = (FieldFlags)0;
if (fieldDef->mProtection == BfProtection_Protected)
fieldFlags = (FieldFlags)(fieldFlags | FieldFlags_Protected);
if (fieldDef->mProtection == BfProtection_Public)
fieldFlags = (FieldFlags)(fieldFlags | FieldFlags_Public);
if (fieldDef->mIsStatic)
fieldFlags = (FieldFlags)(fieldFlags | FieldFlags_Static);
if (fieldDef->mIsConst)
fieldFlags = (FieldFlags)(fieldFlags | FieldFlags_Const);
int customAttrIdx = _HandleCustomAttrs(fieldInstance->mCustomAttributes);
BfIRValue constValue = GetConstValue(0, longType);
if (fieldInstance->GetFieldDef()->mIsConst)
{
if (fieldInstance->mConstIdx != -1)
{
auto constant = typeInstance->mConstHolder->GetConstantById(fieldInstance->mConstIdx);
constValue = mBfIRBuilder->CreateConst(BfTypeCode_UInt64, constant->mUInt64);
}
}
SizedArray<BfIRValue, 8> fieldVals =
{
emptyValueType,
fieldNameConst, // mName
constValue, // mConstValue
GetConstValue(fieldInstance->mDataOffset, intType), // mDataOffset
GetConstValue(typeId, typeIdType), // mFieldTypeId
GetConstValue(fieldFlags, shortType), // mFlags
GetConstValue(customAttrIdx, intType), // mCustomAttributesIdx
};
auto fieldData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectFieldDataType->ToTypeInstance(), BfIRPopulateType_Full), fieldVals);
fieldTypes.push_back(fieldData);
}
auto reflectFieldDataIRType = mBfIRBuilder->MapType(reflectFieldDataType);
BfIRValue fieldDataPtr;
BfIRType fieldDataPtrType = mBfIRBuilder->GetPointerTo(reflectFieldDataIRType);
if (fieldTypes.size() == 0)
{
if ((type->IsSplattable()) && (type->IsStruct()) && (!type->IsValuelessType()) && (!typeInstance->mIsCRepr))
2019-08-23 11:56:54 -07:00
{
BfTypeInstance* reflectFieldSplatDataType = ResolveTypeDef(mCompiler->mReflectFieldSplatDataDef)->ToTypeInstance();
SizedArray<BfIRValue, 3> splatTypes;
SizedArray<BfIRValue, 3> splatOffsets;
std::function<void(BfType*, int)> _CheckSplat = [&](BfType* checkType, int offset)
{
auto checkTypeInstance = checkType->ToTypeInstance();
if ((checkTypeInstance != NULL) && (checkTypeInstance->IsValueType()) && (checkTypeInstance->IsDataIncomplete()))
PopulateType(checkTypeInstance);
if (checkType->IsStruct())
{
int dataEnd = offset;
if (checkTypeInstance->mBaseType != NULL)
_CheckSplat(checkTypeInstance->mBaseType, offset);
if (checkTypeInstance->mIsUnion)
{
BfType* unionInnerType = checkTypeInstance->GetUnionInnerType();
_CheckSplat(unionInnerType, offset);
}
else
{
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
if (fieldInstance->mDataIdx >= 0)
{
_CheckSplat(fieldInstance->GetResolvedType(), offset + fieldInstance->mDataOffset);
dataEnd = offset + fieldInstance->mDataOffset + fieldInstance->mResolvedType->mSize;
}
}
}
if (checkTypeInstance->IsEnum())
{
// Add discriminator
auto dscrType = checkTypeInstance->GetDiscriminatorType();
if (!checkTypeInstance->mIsPacked)
dataEnd = BF_ALIGN(dataEnd, dscrType->mAlign);
_CheckSplat(dscrType, dataEnd);
}
}
else if (checkType->IsMethodRef())
{
// auto methodRefType = (BfMethodRefType*)checkType;
// for (int dataIdx = 0; dataIdx < methodRefType->GetCaptureDataCount(); dataIdx++)
// {
// if (methodRefType->WantsDataPassedAsSplat(dataIdx))
// _CheckSplat(methodRefType->GetCaptureType(dataIdx), offsetof);
// else
// dataLambda(methodRefType->GetCaptureType(dataIdx));
// }
}
else if (!checkType->IsValuelessType())
{
splatTypes.Add(GetConstValue(checkType->mTypeId, typeIdType));
splatOffsets.Add(GetConstValue(offset, intType));
}
};
_CheckSplat(type, 0);
while (splatTypes.size() < 3)
{
splatTypes.Add(GetConstValue(0, typeIdType));
splatOffsets.Add(GetConstValue(0, intType));
}
BfIRValue splatTypesConst = mBfIRBuilder->CreateConstArray(mBfIRBuilder->MapType(reflectFieldSplatDataType->mFieldInstances[0].mResolvedType), splatTypes);
BfIRValue splatOffsetsConst = mBfIRBuilder->CreateConstArray(mBfIRBuilder->MapType(reflectFieldSplatDataType->mFieldInstances[1].mResolvedType), splatOffsets);
SizedArray<BfIRValue, 8> splatVals =
{
emptyValueType,
splatTypesConst,
splatOffsetsConst
};
auto fieldSplatData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectFieldSplatDataType->ToTypeInstance(), BfIRPopulateType_Full), splatVals);
BfIRValue fieldDataArray = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(reflectFieldSplatDataType), true, BfIRLinkageType_Internal,
fieldSplatData, typeDataName + ".splats");
fieldDataPtr = mBfIRBuilder->CreateBitCast(fieldDataArray, fieldDataPtrType);
}
else
fieldDataPtr = mBfIRBuilder->CreateConstNull(fieldDataPtrType);
}
else
{
BfIRType fieldDataConstType = mBfIRBuilder->GetSizedArrayType(reflectFieldDataIRType, (int)fieldTypes.size());
BfIRValue fieldDataConst = mBfIRBuilder->CreateConstArray(fieldDataConstType, fieldTypes);
BfIRValue fieldDataArray = mBfIRBuilder->CreateGlobalVariable(fieldDataConstType, true, BfIRLinkageType_Internal,
fieldDataConst, "fields." + typeDataName);
fieldDataPtr = mBfIRBuilder->CreateBitCast(fieldDataArray, fieldDataPtrType);
}
/// Methods
BfType* reflectMethodDataType = ResolveTypeDef(mCompiler->mReflectMethodDataDef);
BfType* reflectParamDataType = ResolveTypeDef(mCompiler->mReflectParamDataDef);
BfType* reflectParamDataPtrType = CreatePointerType(reflectParamDataType);
2020-05-01 09:11:13 -07:00
SizedArray<BfIRValue, 16> methodTypes;
2019-08-23 11:56:54 -07:00
for (int methodIdx = 0; methodIdx < (int)typeDef->mMethods.size(); methodIdx++)
{
if (!needsTypeData)
break;
auto methodInstanceGroup = &typeInstance->mMethodInstanceGroups[methodIdx];
if (!methodInstanceGroup->IsImplemented())
continue;
auto methodDef = typeDef->mMethods[methodIdx];
if (methodDef->mNoReflect)
continue;
auto defaultMethod = methodInstanceGroup->mDefault;
if (defaultMethod == NULL)
continue;
if (defaultMethod->mIsUnspecialized)
continue;
if (!defaultMethod->mIsReified)
continue;
if ((defaultMethod->mChainType == BfMethodChainType_ChainMember) || (defaultMethod->mChainType == BfMethodChainType_ChainSkip))
continue;
if (defaultMethod->mMethodDef->mMethodType == BfMethodType_CtorNoBody)
continue;
auto methodReflectKind = (ReflectKind)(reflectKind & ~ReflectKind_User);
bool includeMethod = reflectIncludeAllMethods;
BfCustomAttributes* methodCustomAttributes = NULL;
if ((defaultMethod->mMethodInfoEx != NULL) && (defaultMethod->mMethodInfoEx->mMethodCustomAttributes != NULL) && (defaultMethod->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes != NULL))
{
methodCustomAttributes = defaultMethod->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes;
for (auto& customAttr : defaultMethod->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes->mAttributes)
{
if (customAttr.mType->mTypeDef->mName->ToString() == "ReflectAttribute")
{
includeMethod = true;
}
else
{
auto userReflectKind = _GetReflectUser(customAttr.mType);
methodReflectKind = (ReflectKind)(methodReflectKind | userReflectKind);
}
}
}
if (!typeInstance->IsTypeMemberAccessible(methodDef->mDeclaringType, mProject))
continue;
if (auto methodDeclaration = methodDef->GetMethodDeclaration())
{
for (BfParameterDeclaration* paramDecl : methodDeclaration->mParams)
{
if (paramDecl->mAttributes != NULL)
methodReflectKind = (ReflectKind)(methodReflectKind | _GetReflectUserFromDirective(paramDecl->mAttributes, BfAttributeTargets_Parameter));
}
if (methodDeclaration->mReturnAttributes != NULL)
methodReflectKind = (ReflectKind)(methodReflectKind | _GetReflectUserFromDirective(methodDeclaration->mReturnAttributes, BfAttributeTargets_ReturnValue));
}
if ((methodReflectKind & (ReflectKind_Methods | ReflectKind_User)) != 0)
includeMethod = true;
if ((methodDef->mIsStatic) && ((methodReflectKind & ReflectKind_StaticMethods) != 0))
includeMethod = true;
if (!includeMethod)
continue;
BfModuleMethodInstance moduleMethodInstance;
BfIRValue funcVal = voidPtrNull;
if (((!typeInstance->IsBoxed()) || (!methodDef->mIsStatic)) &&
(!typeInstance->IsUnspecializedType()) &&
(methodDef->mMethodType != BfMethodType_Ignore) &&
(methodDef->mMethodType != BfMethodType_Mixin) &&
(!methodDef->mIsAbstract) &&
(methodDef->mGenericParams.size() == 0))
{
moduleMethodInstance = GetMethodInstanceAtIdx(typeInstance, methodIdx, NULL, BfGetMethodInstanceFlag_NoInline);
2019-08-23 11:56:54 -07:00
if (moduleMethodInstance.mFunc)
funcVal = mBfIRBuilder->CreateBitCast(moduleMethodInstance.mFunc, voidPtrIRType);
}
BfIRValue methodNameConst = GetStringObjectValue(methodDef->mName, true);
enum MethodFlags
{
MethodFlags_Protected = 3,
MethodFlags_Public = 6,
MethodFlags_Static = 0x10,
MethodFlags_Virtual = 0x40,
MethodFlags_StdCall = 0x1000,
MethodFlags_FastCall = 0x2000,
MethodFlags_ThisCall = 0x3000,
2019-08-23 11:56:54 -07:00
MethodFlags_Mutating = 0x4000,
2020-05-01 09:11:13 -07:00
MethodFlags_Constructor = 0x8000,
2019-08-23 11:56:54 -07:00
};
MethodFlags methodFlags = (MethodFlags)0;
if (methodDef->mProtection == BfProtection_Protected)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_Protected);
if (methodDef->mProtection == BfProtection_Public)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_Public);
if (methodDef->mIsStatic)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_Static);
if (methodDef->mIsVirtual)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_Virtual);
if (methodDef->mCallingConvention == BfCallingConvention_Fastcall)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_FastCall);
if (methodDef->mIsMutating)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_Mutating);
2020-05-01 09:11:13 -07:00
if (methodDef->mMethodType == BfMethodType_Ctor)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_Constructor);
auto callingConvention = GetIRCallingConvention(defaultMethod);
if (callingConvention == BfIRCallingConv_ThisCall)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_ThisCall);
else if (callingConvention == BfIRCallingConv_StdCall)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_StdCall);
else if (callingConvention == BfIRCallingConv_FastCall)
methodFlags = (MethodFlags)(methodFlags | MethodFlags_FastCall);
2019-08-23 11:56:54 -07:00
int customAttrIdx = _HandleCustomAttrs(methodCustomAttributes);
enum ParamFlags
{
ParamFlag_None = 0,
ParamFlag_Splat = 1,
ParamFlag_Implicit = 2,
};
SizedArray<BfIRValue, 8> paramVals;
for (int paramIdx = 0; paramIdx < defaultMethod->GetParamCount(); paramIdx++)
{
String paramName = defaultMethod->GetParamName(paramIdx);
BfType* paramType = defaultMethod->GetParamType(paramIdx);
ParamFlags paramFlags = ParamFlag_None;
if (defaultMethod->GetParamIsSplat(paramIdx))
paramFlags = (ParamFlags)(paramFlags | ParamFlag_Splat);
BfIRValue paramNameConst = GetStringObjectValue(paramName, true);
SizedArray<BfIRValue, 8> paramDataVals =
{
emptyValueType,
paramNameConst,
GetConstValue(paramType->mTypeId, typeIdType),
GetConstValue((int32)paramFlags, shortType),
GetConstValue(customAttrIdx, intType) // defaultIdx
};
auto paramData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapType(reflectParamDataType, BfIRPopulateType_Full), paramDataVals);
paramVals.Add(paramData);
}
BfIRValue paramsVal;
if (paramVals.size() > 0)
{
BfIRType paramDataArrayType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->MapType(reflectParamDataType, BfIRPopulateType_Full), (int)paramVals.size());
BfIRValue paramDataConst = mBfIRBuilder->CreateConstArray(paramDataArrayType, paramVals);
BfIRValue paramDataArray = mBfIRBuilder->CreateGlobalVariable(paramDataArrayType, true, BfIRLinkageType_Internal,
paramDataConst, typeDataName + StrFormat(".params%d", methodIdx));
paramsVal = mBfIRBuilder->CreateBitCast(paramDataArray, mBfIRBuilder->MapType(reflectParamDataPtrType));
}
else
paramsVal = mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(reflectParamDataPtrType));
int vDataVal = -1;
if (defaultMethod->mVirtualTableIdx != -1)
{
int vDataIdx = -1;
vDataIdx = 1 + mCompiler->GetDynCastVDataCount() + mCompiler->mMaxInterfaceSlots;
if ((mCompiler->mOptions.mHasVDataExtender) && (mCompiler->IsHotCompile()))
{
auto typeInst = defaultMethod->mMethodInstanceGroup->mOwner;
int extMethodIdx = (defaultMethod->mVirtualTableIdx - typeInst->GetImplBaseVTableSize()) - typeInst->GetOrigSelfVTableSize();
2019-08-23 11:56:54 -07:00
if (extMethodIdx >= 0)
{
// Extension?
int vExtOfs = typeInst->GetOrigImplBaseVTableSize();
2019-08-23 11:56:54 -07:00
vDataVal = ((vDataIdx + vExtOfs + 1) << 20) | (extMethodIdx);
}
else
{
// Map this new virtual index back to the original index
vDataIdx += (defaultMethod->mVirtualTableIdx - typeInst->GetImplBaseVTableSize()) + typeInst->GetOrigImplBaseVTableSize();
2019-08-23 11:56:54 -07:00
}
}
else
{
vDataIdx += defaultMethod->mVirtualTableIdx;
}
if (vDataVal == -1)
vDataVal = vDataIdx * mSystem->mPtrSize;
}
2020-05-28 07:37:55 -07:00
2019-08-23 11:56:54 -07:00
SizedArray<BfIRValue, 8> methodDataVals =
{
emptyValueType,
methodNameConst, // mName
funcVal, // mFuncPtr
paramsVal,
GetConstValue(defaultMethod->mReturnType->mTypeId, typeIdType),
GetConstValue((int)paramVals.size(), shortType),
GetConstValue(methodFlags, shortType),
GetConstValue(vDataVal, intType),
2020-05-28 07:37:55 -07:00
GetConstValue(customAttrIdx, intType),
2019-08-23 11:56:54 -07:00
};
2020-05-01 09:11:13 -07:00
auto methodData = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapTypeInst(reflectMethodDataType->ToTypeInstance(), BfIRPopulateType_Full), methodDataVals);
2019-08-23 11:56:54 -07:00
methodTypes.push_back(methodData);
}
BfIRValue methodDataPtr;
BfIRType methodDataPtrType = mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapType(reflectMethodDataType));
if (methodTypes.size() == 0)
methodDataPtr = mBfIRBuilder->CreateConstNull(methodDataPtrType);
else
{
BfIRType methodDataArrayType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->MapType(reflectMethodDataType, BfIRPopulateType_Full), (int)methodTypes.size());
BfIRValue methodDataConst = mBfIRBuilder->CreateConstArray(methodDataArrayType, methodTypes);
BfIRValue methodDataArray = mBfIRBuilder->CreateGlobalVariable(methodDataArrayType, true, BfIRLinkageType_Internal,
methodDataConst, "methods." + typeDataName);
methodDataPtr = mBfIRBuilder->CreateBitCast(methodDataArray, methodDataPtrType);
}
2020-05-01 09:11:13 -07:00
2019-08-23 11:56:54 -07:00
/////
int underlyingType = 0;
if ((typeInstance->IsTypedPrimitive()) || (typeInstance->IsBoxed()))
{
underlyingType = typeInstance->GetUnderlyingType()->mTypeId;
}
int outerTypeId = 0;
auto outerType = mContext->mUnreifiedModule->GetOuterType(typeInstance);
2019-08-23 11:56:54 -07:00
if (outerType != NULL)
outerTypeId = outerType->mTypeId;
BfIRValue customAttrDataPtr;
if (customAttrs.size() > 0)
{
BfIRType customAttrsArrayType = mBfIRBuilder->GetSizedArrayType(voidPtrIRType, (int)customAttrs.size());
BfIRValue customAttrsConst = mBfIRBuilder->CreateConstArray(customAttrsArrayType, customAttrs);
BfIRValue customAttrsArray = mBfIRBuilder->CreateGlobalVariable(customAttrsArrayType, true, BfIRLinkageType_Internal,
customAttrsConst, "customAttrs." + typeDataName);
customAttrDataPtr = mBfIRBuilder->CreateBitCast(customAttrsArray, voidPtrPtrIRType);
}
else
{
customAttrDataPtr = mBfIRBuilder->CreateConstNull(voidPtrPtrIRType);
}
SizedArray<BfIRValue, 32> typeDataVals =
{
typeData,
castedClassVData, // mTypeClassVData
typeNameConst, // mName
namespaceConst, // mNamespace
GetConstValue(typeInstance->mInstSize, intType), // mInstSize
GetConstValue(typeInstance->mInstAlign, intType), // mInstAlign
GetConstValue(typeCustomAttrIdx, intType), // mCustomAttributes
GetConstValue(baseTypeId, typeIdType), // mBaseType
GetConstValue(underlyingType, typeIdType), // mUnderlyingType
GetConstValue(outerTypeId, typeIdType), // mOuterType
GetConstValue(typeInstance->mInheritanceId, intType), // mInheritanceId
GetConstValue(typeInstance->mInheritanceCount, intType), // mInheritanceCount
GetConstValue(typeInstance->mSlotNum, byteType), // mInterfaceSlot
GetConstValue(0, byteType), // mInterfaceCount
GetConstValue((int)methodTypes.size(), shortType), // mMethodDataCount
GetConstValue(0, shortType), // mPropertyDataCount
2020-05-01 09:11:13 -07:00
GetConstValue((int)fieldTypes.size(), shortType), // mFieldDataCount
2019-08-23 11:56:54 -07:00
voidPtrNull, // mInterfaceDataPtr
methodDataPtr, // mMethodDataPtr
voidPtrNull, // mPropertyDataPtr
2020-05-01 09:11:13 -07:00
fieldDataPtr, // mFieldDataPtr
2019-08-23 11:56:54 -07:00
customAttrDataPtr, // mCustomAttrDataPtr
};
BfIRType typeInstanceDataType = mBfIRBuilder->MapTypeInst(typeInstanceType->ToTypeInstance(), BfIRPopulateType_Full);
auto typeInstanceData = mBfIRBuilder->CreateConstStruct(typeInstanceDataType, typeDataVals);
if (!needsTypeData)
{
// No need for anything beyond typeInstanceData
}
else if ((typeInstance->IsGenericTypeInstance()) && (typeInstance->IsUnspecializedType()))
{
auto genericTypeInstance = typeInstance->ToGenericTypeInstance();
SizedArray<BfIRValue, 4> unspecializedData =
{
typeInstanceData,
GetConstValue((int)genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments.size(), byteType), // mGenericParamCount
2019-08-23 11:56:54 -07:00
};
auto reflectUnspecializedGenericType = ResolveTypeDef(mCompiler->mReflectUnspecializedGenericType);
typeInstanceDataType = mBfIRBuilder->MapTypeInst(reflectUnspecializedGenericType->ToTypeInstance(), BfIRPopulateType_Full);
typeInstanceData = mBfIRBuilder->CreateConstStruct(typeInstanceDataType, unspecializedData);
}
else if (typeInstance->IsGenericTypeInstance())
{
auto genericTypeInstance = typeInstance->ToGenericTypeInstance();
auto reflectSpecializedGenericType = ResolveTypeDef(mCompiler->mReflectSpecializedGenericType);
auto unspecializedType = ResolveTypeDef(typeInstance->mTypeDef);
SizedArray<BfIRValue, 4> resolvedTypes;
for (auto typeGenericArg : genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments)
2019-08-23 11:56:54 -07:00
resolvedTypes.push_back(GetConstValue(typeGenericArg->mTypeId, typeIdType));
auto typeIRType = mBfIRBuilder->MapType(typeIdType);
auto typePtrIRType = mBfIRBuilder->GetPointerTo(typeIRType);
auto genericArrayType = mBfIRBuilder->GetSizedArrayType(typeIRType, (int)resolvedTypes.size());
BfIRValue resolvedTypesConst = mBfIRBuilder->CreateConstArray(genericArrayType, resolvedTypes);
BfIRValue resolvedTypesArray = mBfIRBuilder->CreateGlobalVariable(genericArrayType, true, BfIRLinkageType_Internal,
resolvedTypesConst, "resolvedTypes." + typeDataName);
BfIRValue resovledTypesPtr = mBfIRBuilder->CreateBitCast(resolvedTypesArray, typePtrIRType);
SizedArray<BfIRValue, 3> specGenericData =
{
typeInstanceData,
GetConstValue(unspecializedType->mTypeId, typeIdType), // mUnspecialziedType
resovledTypesPtr, // mFieldDataPtr
};
typeInstanceDataType = mBfIRBuilder->MapTypeInst(reflectSpecializedGenericType->ToTypeInstance(), BfIRPopulateType_Full);
typeInstanceData = mBfIRBuilder->CreateConstStruct(typeInstanceDataType, specGenericData);
if (typeInstance->IsArray())
{
auto arrayType = (BfArrayType*)typeInstance;
BfType* elementType = genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments[0];
2019-08-23 11:56:54 -07:00
BfFieldInstance* elementFieldInstance = &genericTypeInstance->mFieldInstances[0];
SizedArray<BfIRValue, 4> arrayData =
{
typeInstanceData,
GetConstValue(elementType->mSize, intType), // mElementSize
GetConstValue(1, byteType), // mRank
GetConstValue(elementFieldInstance->mDataOffset, byteType), // mElementsDataOffset
};
auto reflectArrayType = ResolveTypeDef(mCompiler->mReflectArrayType);
typeInstanceDataType = mBfIRBuilder->MapTypeInst(reflectArrayType->ToTypeInstance(), BfIRPopulateType_Full);
typeInstanceData = mBfIRBuilder->CreateConstStruct(typeInstanceDataType, arrayData);
}
}
BfIRValue typeDataVar;
if (needsTypeData)
{
typeDataVar = mBfIRBuilder->CreateGlobalVariable(typeInstanceDataType, true,
BfIRLinkageType_External, typeInstanceData, typeDataName);
if (mBfIRBuilder->DbgHasInfo())
{
mBfIRBuilder->DbgCreateGlobalVariable(mDICompileUnit, typeDataName, typeDataName, NULL, 0, mBfIRBuilder->DbgGetTypeInst(typeInstanceType->ToTypeInstance()), false, typeDataVar);
}
}
else
{
typeDataVar = mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(mContext->mBfTypeType));
}
typeDataVar = mBfIRBuilder->CreateBitCast(typeDataVar, mBfIRBuilder->MapType(mContext->mBfTypeType));
mTypeDataRefs[typeInstance] = typeDataVar;
if (classVDataVar)
{
BF_ASSERT(!classVDataName.IsEmpty());
vData[0] = mBfIRBuilder->CreateBitCast(typeDataVar, voidPtrIRType);
auto classVDataConstDataType = mBfIRBuilder->GetSizedArrayType(voidPtrIRType, (int)vData.size());
auto classVDataConstData = mBfIRBuilder->CreateConstArray(classVDataConstDataType, vData);
mBfIRBuilder->GlobalVar_SetInitializer(classVDataVar, classVDataConstData);
if (mCompiler->mOptions.mObjectHasDebugFlags)
mBfIRBuilder->GlobalVar_SetAlignment(classVDataVar, 256);
else
mBfIRBuilder->GlobalVar_SetAlignment(classVDataVar, mContext->mBfClassVDataPtrType->mAlign);
if (mBfIRBuilder->DbgHasInfo())
{
BfType* voidType = GetPrimitiveType(BfTypeCode_None);
BfType* voidPtrType = CreatePointerType(voidType);
BfType* classVDataType = ResolveTypeDef(mCompiler->mClassVDataTypeDef);
BfIRMDNode arrayType = mBfIRBuilder->DbgCreateArrayType(vData.size() * mSystem->mPtrSize * 8, mSystem->mPtrSize * 8, mBfIRBuilder->DbgGetType(voidPtrType), vData.size());
mBfIRBuilder->DbgCreateGlobalVariable(mDICompileUnit, classVDataName, classVDataName, NULL, 0, arrayType, false, classVDataVar);
}
}
return typeDataVar;
}
BfIRValue BfModule::FixClassVData(BfIRValue value)
{
if (!mCompiler->mOptions.mObjectHasDebugFlags)
return value;
auto intptrValue = mBfIRBuilder->CreatePtrToInt(value, BfTypeCode_IntPtr);
auto maskedValue = mBfIRBuilder->CreateAnd(intptrValue, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, (uint64)~0xFFULL));
return mBfIRBuilder->CreateIntToPtr(maskedValue, mBfIRBuilder->GetType(value));
}
void BfModule::CheckStaticAccess(BfTypeInstance* typeInstance)
{
// Note: this is not just for perf, it fixes a field var-type resolution issue
2020-04-07 09:03:27 -07:00
if (mBfIRBuilder->mIgnoreWrites)
return;
2019-08-23 11:56:54 -07:00
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
//TODO: Create a hashset of these, we don't need to repeatedly call static ctors for a given type
// When we access classes with static constructors FROM a static constructor,
// we call that other class's static constructor first. Recursion cannot occur, since
// we simply ignore subsequent attempts to re-enter the constructor
if (!typeInstance->mHasStaticInitMethod)
return;
if ((mCurMethodInstance == NULL) || ((mCurMethodInstance->mMethodDef->mMethodType != BfMethodType_Ctor) || (!mCurMethodInstance->mMethodDef->mIsStatic)))
return;
if (mCurMethodInstance->mMethodInstanceGroup->mOwner == typeInstance)
return;
auto checkTypeDef = typeInstance->mTypeDef;
checkTypeDef->PopulateMemberSets();
BfMethodDef* nextMethodDef = NULL;
BfMemberSetEntry* entry;
if (checkTypeDef->mMethodSet.TryGetWith(String("__BfStaticCtor"), &entry))
nextMethodDef = (BfMethodDef*)entry->mMemberDef;
while (nextMethodDef != NULL)
{
auto checkMethod = nextMethodDef;
nextMethodDef = nextMethodDef->mNextWithSameName;
auto methodModule = GetMethodInstance(typeInstance, checkMethod, BfTypeVector());
if (methodModule)
{
auto methodInstance = methodModule.mMethodInstance;
if ((methodInstance != NULL) &&
(methodInstance->mMethodDef->mIsStatic) &&
(methodInstance->mMethodDef->mMethodType == BfMethodType_Ctor) &&
((methodInstance->mChainType == BfMethodChainType_ChainHead) || (methodInstance->mChainType == BfMethodChainType_None)))
{
mBfIRBuilder->CreateCall(methodModule.mFunc, SizedArray<BfIRValue, 0>());
break;
}
}
}
}
BfIRFunction BfModule::GetIntrinsic(BfMethodInstance* methodInstance, bool reportFailure)
{
auto methodOwner = methodInstance->GetOwner();
auto methodDef = methodInstance->mMethodDef;
auto methodDeclaration = methodDef->GetMethodDeclaration();
bool hasExternSpecifier = (methodDeclaration != NULL) && (methodDeclaration->mExternSpecifier != NULL);
if (!hasExternSpecifier)
return BfIRFunction();
if (methodInstance->GetCustomAttributes() == NULL)
return BfIRFunction();
for (auto& customAttribute : methodInstance->GetCustomAttributes()->mAttributes)
{
String typeName = TypeToString(customAttribute.mType);
if ((typeName == "System.IntrinsicAttribute") && (customAttribute.mCtorArgs.size() > 0))
{
methodInstance->mIsIntrinsic = true;
auto constant = methodOwner->mConstHolder->GetConstant(customAttribute.mCtorArgs[0]);
String error;
if ((constant != NULL) && (constant->mTypeCode = BfTypeCode_StringId))
2019-08-23 11:56:54 -07:00
{
int stringId = constant->mInt32;
auto entry = mContext->mStringObjectIdMap[stringId];
int intrinId = BfIRCodeGen::GetIntrinsicId(entry.mString);
if (intrinId != -1)
{
if (intrinId == BfIRIntrinsic_Malloc)
{
return GetBuiltInFunc(BfBuiltInFuncType_Malloc);
}
else if (intrinId == BfIRIntrinsic_Free)
{
return GetBuiltInFunc(BfBuiltInFuncType_Free);
}
SizedArray<BfIRType, 2> paramTypes;
for (auto& param : methodInstance->mParams)
paramTypes.push_back(mBfIRBuilder->MapType(param.mResolvedType));
2020-02-19 13:16:33 -08:00
return mBfIRBuilder->GetIntrinsic(intrinId, mBfIRBuilder->MapType(methodInstance->mReturnType), paramTypes);
2019-08-23 11:56:54 -07:00
}
else if (reportFailure)
error = StrFormat("Unable to find intrinsic '%s'", entry.mString.c_str());
}
else if (reportFailure)
error = "Intrinsic name must be a constant string";
if (reportFailure)
{
BfAstNode* ref = methodDeclaration->mAttributes;
Fail(error, ref);
}
}
}
return BfIRFunction();
}
BfIRFunction BfModule::GetBuiltInFunc(BfBuiltInFuncType funcTypeId)
{
if (mBfIRBuilder->mIgnoreWrites)
return mBfIRBuilder->GetFakeFunction();
if (!mBuiltInFuncs[(int)funcTypeId])
{
SizedArray<BfIRType, 4> paramTypes;
auto nullPtrType = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_NullPtr));
auto intType = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_IntPtr));
auto int32Type = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_Int32));
auto intPtrType = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_IntPtr));
auto byteType = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_Int8));
auto voidType = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_None));
auto objType = mBfIRBuilder->MapType(mContext->mBfObjectType);
BfIRFunctionType funcType;
BfIRFunction func;
switch (funcTypeId)
{
case BfBuiltInFuncType_PrintF:
funcType = mBfIRBuilder->CreateFunctionType(int32Type, paramTypes, true);
func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_External, "PrintF");
break;
case BfBuiltInFuncType_Malloc:
{
if (mCompiler->mOptions.mDebugAlloc)
{
func = GetInternalMethod("Dbg_RawAlloc", 1).mFunc;
}
else
{
String funcName = mCompiler->mOptions.mMallocLinkName;
if (funcName.IsEmpty())
funcName = "malloc";
func = mBfIRBuilder->GetFunction(funcName);
if (!func)
{
paramTypes.clear();
paramTypes.push_back(intType);
funcType = mBfIRBuilder->CreateFunctionType(nullPtrType, paramTypes);
func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_External, funcName);
mBfIRBuilder->Func_SetParamName(func, 1, "size");
}
}
}
break;
case BfBuiltInFuncType_Free:
{
if (mCompiler->mOptions.mDebugAlloc)
{
func = GetInternalMethod("Dbg_RawFree").mFunc;
}
else
{
String funcName = mCompiler->mOptions.mFreeLinkName;
if (funcName.IsEmpty())
funcName = "free";
func = mBfIRBuilder->GetFunction(funcName);
if (!func)
{
paramTypes.clear();
paramTypes.push_back(nullPtrType);
funcType = mBfIRBuilder->CreateFunctionType(voidType, paramTypes);
func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_External, funcName);
mBfIRBuilder->Func_SetParamName(func, 1, "ptr");
}
}
}
break;
case BfBuiltInFuncType_LoadSharedLibraries:
paramTypes.clear();
funcType = mBfIRBuilder->CreateFunctionType(voidType, paramTypes);
func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_External, "BfLoadSharedLibraries");
break;
default: break;
2019-08-23 11:56:54 -07:00
}
mBuiltInFuncs[(int)funcTypeId] = func;
return func;
}
return mBuiltInFuncs[(int)funcTypeId];
}
void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericParamInstance, bool isUnspecialized)
{
BfGenericParamDef* genericParamDef = genericParamInstance->GetGenericParamDef();
BfExternalConstraintDef* externConstraintDef = genericParamInstance->GetExternConstraintDef();
BfConstraintDef* constraintDef = genericParamInstance->GetConstraintDef();
2019-08-23 11:56:54 -07:00
BfType* startingTypeConstraint = genericParamInstance->mTypeConstraint;
BfAutoComplete* bfAutocomplete = NULL;
if ((mCompiler->mResolvePassData != NULL) && (isUnspecialized))
2019-08-23 11:56:54 -07:00
bfAutocomplete = mCompiler->mResolvePassData->mAutoComplete;
if ((bfAutocomplete != NULL) && (genericParamDef != NULL))
2019-08-23 11:56:54 -07:00
{
for (int nameIdx = 0; nameIdx < (int)genericParamDef->mNameNodes.size(); nameIdx++)
{
auto nameNode = genericParamDef->mNameNodes[nameIdx];
if (bfAutocomplete->IsAutocompleteNode(nameNode))
{
String filter = nameNode->ToString();
bfAutocomplete->mInsertStartIdx = nameNode->GetSrcStart();
bfAutocomplete->mInsertEndIdx = nameNode->GetSrcEnd();
if (nameIdx != 0)
{
bfAutocomplete->AddEntry(AutoCompleteEntry("generic", nameNode->ToString().c_str()), filter);
2019-08-23 11:56:54 -07:00
}
}
}
}
for (auto constraint : constraintDef->mConstraints)
2019-08-23 11:56:54 -07:00
{
if (auto opConstraint = BfNodeDynCast<BfGenericOperatorConstraint>(constraint))
{
BfGenericOperatorConstraintInstance opConstraintInstance;
if (opConstraint->mLeftType != NULL)
{
if (bfAutocomplete != NULL)
bfAutocomplete->CheckTypeRef(opConstraint->mLeftType, false);
opConstraintInstance.mLeftType = ResolveTypeRef(opConstraint->mLeftType);
if (opConstraintInstance.mLeftType == NULL)
continue;
}
if (opConstraint->mRightType == NULL)
{
// We had a failure in parsing
continue;
}
if (opConstraint->mRightType != NULL)
{
if (bfAutocomplete != NULL)
bfAutocomplete->CheckTypeRef(opConstraint->mRightType, false);
opConstraintInstance.mRightType = ResolveTypeRef(opConstraint->mRightType);
if (opConstraintInstance.mRightType == NULL)
continue;
}
if (opConstraint->mOpToken == NULL)
{
FailAfter("Missing operator", (opConstraint->mLeftType != NULL) ? (BfAstNode*)opConstraint->mLeftType : (BfAstNode*)opConstraint->mOperatorToken);
continue;
}
if (opConstraint->mLeftType != NULL)
{
if (opConstraint->mRightType == NULL)
{
// Parse should have failed
continue;
}
opConstraintInstance.mBinaryOp = BfTokenToBinaryOp(opConstraint->mOpToken->mToken);
if (opConstraintInstance.mBinaryOp == BfBinaryOp_None)
{
Fail("Invalid binary operator", opConstraint->mOpToken);
continue;
}
}
else if ((opConstraint->mOpToken->mToken == BfToken_Implicit) || (opConstraint->mOpToken->mToken == BfToken_Explicit))
{
opConstraintInstance.mCastToken = opConstraint->mOpToken->mToken;
}
else
{
opConstraintInstance.mUnaryOp = BfTokenToUnaryOp(opConstraint->mOpToken->mToken);
2019-11-19 13:36:51 -08:00
if (opConstraintInstance.mUnaryOp == BfUnaryOp_None)
{
Fail("Invalid unary operator", opConstraint->mOpToken);
continue;
}
}
if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Equals) != 0)
genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_Equals_Op);
genericParamInstance->mOperatorConstraints.Add(opConstraintInstance);
continue;
}
auto constraintTypeRef = BfNodeDynCast<BfTypeReference>(constraint);
2019-08-23 11:56:54 -07:00
if (bfAutocomplete != NULL)
bfAutocomplete->CheckTypeRef(constraintTypeRef, true);
//TODO: Constraints may refer to other generic params (of either type or method)
// TO allow resolution, perhaps move this generic param initalization into GetMethodInstance (passing a genericPass bool)
auto constraintType = ResolveTypeRef(constraintTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericMethodParamConstValue);
if (constraintType != NULL)
{
if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0)
2019-08-23 11:56:54 -07:00
{
bool isValidTypeCode = false;
BfTypeCode typeCode = BfTypeCode_None;
if (constraintType->IsTypedPrimitive())
{
auto underlyingType = constraintType->GetUnderlyingType();
if (underlyingType->IsPrimitiveType())
typeCode = ((BfPrimitiveType*)underlyingType)->mTypeDef->mTypeCode;
}
if (constraintType->IsPrimitiveType())
typeCode = ((BfPrimitiveType*)constraintType)->mTypeDef->mTypeCode;
switch (typeCode)
{
case BfTypeCode_Int8:
case BfTypeCode_UInt8:
case BfTypeCode_Int16:
case BfTypeCode_UInt16:
case BfTypeCode_Int32:
case BfTypeCode_UInt32:
case BfTypeCode_Int64:
case BfTypeCode_UInt64:
case BfTypeCode_IntPtr:
case BfTypeCode_UIntPtr:
case BfTypeCode_IntUnknown:
case BfTypeCode_UIntUnknown:
case BfTypeCode_Single:
case BfTypeCode_Double:
case BfTypeCode_Char8:
case BfTypeCode_Char16:
case BfTypeCode_Char32:
isValidTypeCode = true;
break;
default: break;
2019-08-23 11:56:54 -07:00
}
if (isValidTypeCode)
{
genericParamInstance->mTypeConstraint = constraintType;
}
else
{
Fail("Const constraint must be a primitive type", constraintTypeRef);
}
}
else
{
bool checkEquality = false;
2019-08-23 11:56:54 -07:00
if (constraintType->IsPrimitiveType())
{
if (isUnspecialized)
{
Fail("Primitive constraints are not allowed unless preceded with 'const'", constraintTypeRef);
continue;
}
checkEquality = true;
2019-08-23 11:56:54 -07:00
}
if (constraintType->IsArray())
{
if (isUnspecialized)
{
Fail("Array constraints are not allowed. If a constant-sized array was intended, an type parameterized by a const generic param can be used (ie: where T : int[T2] where T2 : const int)", constraintTypeRef);
continue;
}
checkEquality = true;
}
if (constraintType->IsGenericParam())
{
2019-08-23 11:56:54 -07:00
continue;
}
if ((!constraintType->IsTypeInstance()) && (!constraintType->IsSizedArray()))
{
if (isUnspecialized)
{
Fail(StrFormat("Type '%s' is not allowed as a generic constraint", TypeToString(constraintType).c_str()), constraintTypeRef);
continue;
}
checkEquality = true;
2019-08-23 11:56:54 -07:00
}
if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Equals) != 0)
{
genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_Equals_Type);
checkEquality = true;
}
if (checkEquality)
{
genericParamInstance->mTypeConstraint = constraintType;
}
else if (constraintType->IsInterface())
2019-08-23 11:56:54 -07:00
{
if ((constraintDef->mGenericParamFlags & BfGenericParamFlag_Equals) != 0)
genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_Equals_IFace);
2019-08-23 11:56:54 -07:00
genericParamInstance->mInterfaceConstraints.push_back(constraintType->ToTypeInstance());
}
else
{
auto constraintTypeInst = constraintType->ToTypeInstance();
if (genericParamInstance->mTypeConstraint != NULL)
{
if ((constraintTypeInst != NULL) && (TypeIsSubTypeOf(constraintTypeInst, genericParamInstance->mTypeConstraint->ToTypeInstance(), false)))
{
// Allow more specific type
genericParamInstance->mTypeConstraint = constraintTypeInst;
}
else if ((constraintTypeInst != NULL) && (TypeIsSubTypeOf(genericParamInstance->mTypeConstraint->ToTypeInstance(), constraintTypeInst, false)))
{
// Silently ignore less-specific type
if ((startingTypeConstraint != NULL) && (startingTypeConstraint->IsDelegate()))
{
// 'System.Delegate' means that we are expecting an actual delegate instance. Simulate this by wanting a class.
genericParamInstance->mGenericParamFlags |= BfGenericParamFlag_Class;
}
}
else
{
Fail("Only one concrete type constraint may be specified", constraintTypeRef);
return;
}
}
else
genericParamInstance->mTypeConstraint = constraintType;
}
}
}
}
if (((constraintDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0) &&
2019-08-23 11:56:54 -07:00
(genericParamInstance->mTypeConstraint == NULL))
genericParamInstance->mTypeConstraint = GetPrimitiveType(BfTypeCode_IntPtr);
}
String BfModule::GenericParamSourceToString(const BfGenericParamSource & genericParamSource)
{
if (genericParamSource.mMethodInstance != NULL)
{
auto methodInst = GetUnspecializedMethodInstance(genericParamSource.mMethodInstance);
SetAndRestoreValue<BfMethodInstance*> prevMethodInst(mCurMethodInstance, methodInst);
return MethodToString(methodInst);
}
else
{
auto typeInst = GetUnspecializedTypeInstance(genericParamSource.mTypeInstance);
SetAndRestoreValue<BfMethodInstance*> prevMethodInst(mCurMethodInstance, NULL);
SetAndRestoreValue<BfTypeInstance*> prevTypeInst(mCurTypeInstance, typeInst);
return TypeToString(typeInst);
}
}
bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamSource, BfType* checkArgType, BfAstNode* checkArgTypeRef, BfGenericParamInstance* genericParamInst, BfTypeVector* methodGenericArgs, BfError** errorOut)
{
Array<String> methodParamNameOverrides;
auto _TypeToString = [&](BfType* type)
{
2020-02-29 09:45:46 -08:00
if (genericParamSource.mMethodInstance != NULL)
{
2020-02-29 09:45:46 -08:00
if (methodParamNameOverrides.IsEmpty())
{
for (auto genericParam : genericParamSource.mMethodInstance->mMethodDef->mGenericParams)
methodParamNameOverrides.Add(genericParam->mName);
}
2020-02-29 09:45:46 -08:00
return TypeToString(type, &methodParamNameOverrides);
}
2020-02-29 09:45:46 -08:00
return TypeToString(type);
};
2019-08-23 11:56:54 -07:00
bool ignoreErrors = mIgnoreErrors || (errorOut == NULL) ||
((genericParamSource.mMethodInstance == NULL) && (genericParamSource.mTypeInstance == NULL));
BfType* origCheckArgType = checkArgType;
if (origCheckArgType->IsRef())
origCheckArgType = origCheckArgType->GetUnderlyingType();
bool argMayBeReferenceType = false;
int checkGenericParamFlags = 0;
if (checkArgType->IsGenericParam())
{
auto checkGenericParamInst = GetGenericParamInstance((BfGenericParamType*)checkArgType);
checkGenericParamFlags = checkGenericParamInst->mGenericParamFlags;
if (checkGenericParamInst->mTypeConstraint != NULL)
checkArgType = checkGenericParamInst->mTypeConstraint;
if ((checkGenericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_StructPtr)) != 0)
{
argMayBeReferenceType = false;
}
else
{
argMayBeReferenceType = true;
}
}
2019-08-23 11:56:54 -07:00
if (checkArgType->IsObjectOrInterface())
argMayBeReferenceType = true;
BfTypeInstance* typeConstraintInst = NULL;
if (genericParamInst->mTypeConstraint != NULL)
typeConstraintInst = genericParamInst->mTypeConstraint->ToTypeInstance();
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Struct) &&
((checkGenericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsValueType()))
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("The type '%s' must be a value type in order to use it as parameter '%s' for '%s'",
_TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_StructPtr) &&
((checkGenericParamFlags & (BfGenericParamFlag_StructPtr | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsPointer()))
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("The type '%s' must be a pointer type in order to use it as parameter '%s' for '%s'",
_TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) &&
((checkGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Var)) == 0) && (!argMayBeReferenceType))
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("The type '%s' must be a reference type in order to use it as parameter '%s' for '%s'",
_TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Const) != 0)
{
if (((checkGenericParamFlags & BfGenericParamFlag_Const) == 0) && (!checkArgType->IsConstExprValue()))
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("The type '%s' must be a const value in order to use it as parameter '%s' for '%s'",
_TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
else
{
if (checkArgType->IsConstExprValue())
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("The value '%s' cannot be used for generic type parameter '%s' for '%s'",
_TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
if ((genericParamInst->mInterfaceConstraints.IsEmpty()) && (genericParamInst->mOperatorConstraints.IsEmpty()) && (genericParamInst->mTypeConstraint == NULL))
2019-08-23 11:56:54 -07:00
return true;
if (checkArgType->IsPointer())
{
auto ptrType = (BfPointerType*)checkArgType;
checkArgType = ptrType->mElementType;
}
if (genericParamInst->mTypeConstraint != NULL)
{
bool constraintMatched = false;
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Const) != 0)
{
if (checkArgType->IsConstExprValue())
{
auto constExprValueType = (BfConstExprValueType*)checkArgType;
if (genericParamInst->mTypeConstraint->IsPrimitiveType())
{
auto primType = (BfPrimitiveType*)genericParamInst->mTypeConstraint;
// Let either an exact typematch or an undef pass through. Eventually instead of undefs we may want to do
// actual expression comparisons, but we are overly permissive now and then we may fail on specialization
if ((constExprValueType->mValue.mTypeCode != primType->mTypeDef->mTypeCode) &&
(constExprValueType->mValue.mTypeCode != BfTypeCode_Let) &&
(primType->mTypeDef->mTypeCode != BfTypeCode_Let))
{
if (BfIRConstHolder::IsInt(constExprValueType->mValue.mTypeCode))
{
if (BfIRConstHolder::IsInt(primType->mTypeDef->mTypeCode))
{
if (!mCompiler->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, constExprValueType->mValue.mInt64))
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Const generic argument '%s', declared with const '%lld', does not fit into const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
else
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Const generic argument '%s', declared with integer const '%lld', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
else
{
if (BfIRConstHolder::IsInt(primType->mTypeDef->mTypeCode))
{
char valStr[64];
ExactMinimalDoubleToStr(constExprValueType->mValue.mDouble, valStr);
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Const generic argument '%s', declared with floating point const '%s', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
valStr, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
}
}
}
}
else
{
BfType* convCheckConstraint = genericParamInst->mTypeConstraint;
if ((convCheckConstraint->IsUnspecializedType()) && (methodGenericArgs != NULL))
convCheckConstraint = ResolveGenericType(convCheckConstraint, NULL, methodGenericArgs);
if (convCheckConstraint == NULL)
return false;
2019-08-23 11:56:54 -07:00
if ((checkArgType->IsMethodRef()) && (convCheckConstraint->IsDelegate()))
{
auto methodRefType = (BfMethodRefType*)checkArgType;
auto invokeMethod = GetRawMethodInstanceAtIdx(convCheckConstraint->ToTypeInstance(), 0, "Invoke");
BfExprEvaluator exprEvaluator(this);
if (exprEvaluator.IsExactMethodMatch(methodRefType->mMethodRef, invokeMethod))
constraintMatched = true;
}
else if (CanCast(GetFakeTypedValue(checkArgType), convCheckConstraint))
2019-08-23 11:56:54 -07:00
{
constraintMatched = true;
}
else if (origCheckArgType->IsWrappableType())
{
if (origCheckArgType->IsSizedArray())
{
auto sizedArrayType = (BfSizedArrayType*)origCheckArgType;
if (convCheckConstraint->IsGenericTypeInstance())
{
auto convCheckConstraintInst = (BfTypeInstance*)convCheckConstraint;
2019-08-23 11:56:54 -07:00
if (convCheckConstraintInst->mTypeDef == mCompiler->mSizedArrayTypeDef)
{
if (convCheckConstraintInst->mGenericTypeInfo->mTypeGenericArguments[0] == sizedArrayType->mElementType)
2019-08-23 11:56:54 -07:00
{
auto constExprValueType = (BfConstExprValueType*)convCheckConstraintInst->mGenericTypeInfo->mTypeGenericArguments[1];
2019-08-23 11:56:54 -07:00
if (sizedArrayType->mElementCount == constExprValueType->mValue.mInt64)
constraintMatched = true;
}
}
}
}
if (!constraintMatched)
{
BfType* wrappedStructType = GetWrappedStructType(origCheckArgType, false);
if (CanCast(GetFakeTypedValue(wrappedStructType), convCheckConstraint))
2019-08-23 11:56:54 -07:00
constraintMatched = true;
}
}
if (!constraintMatched)
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must derive from '%s'", genericParamInst->GetName().c_str(),
_TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(convCheckConstraint).c_str(),
_TypeToString(genericParamInst->mTypeConstraint).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
}
if ((genericParamInst->mInterfaceConstraints.size() > 0) && (origCheckArgType->IsInterface()))
{
PopulateType(origCheckArgType);
auto checkTypeInst = origCheckArgType->ToTypeInstance();
if (checkTypeInst->mTypeDef->mIsConcrete)
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be concrete interface '%s' for '%s', must be a concrete type", genericParamInst->GetName().c_str(),
_TypeToString(origCheckArgType).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
for (auto checkConstraint : genericParamInst->mInterfaceConstraints)
{
BfType* convCheckConstraint = checkConstraint;
if (convCheckConstraint->IsUnspecializedType())
convCheckConstraint = ResolveGenericType(convCheckConstraint, NULL, methodGenericArgs);
if (convCheckConstraint == NULL)
return false;
2019-08-23 11:56:54 -07:00
BfTypeInstance* typeConstraintInst = convCheckConstraint->ToTypeInstance();
2019-10-09 16:16:01 -07:00
bool implementsInterface = false;
if (origCheckArgType != checkArgType)
{
implementsInterface = CanCast(BfTypedValue(BfIRValue::sValueless, origCheckArgType), convCheckConstraint);
2019-10-09 16:16:01 -07:00
}
if (!implementsInterface)
implementsInterface = CanCast(BfTypedValue(BfIRValue::sValueless, checkArgType), convCheckConstraint);
2019-10-09 16:16:01 -07:00
2019-08-23 11:56:54 -07:00
if ((!implementsInterface) && (origCheckArgType->IsWrappableType()))
{
BfTypeInstance* wrappedStructType = GetWrappedStructType(origCheckArgType, false);
if (TypeIsSubTypeOf(wrappedStructType, typeConstraintInst))
implementsInterface = true;
}
if ((!implementsInterface) && (origCheckArgType->IsTypedPrimitive()))
{
auto underlyingType = origCheckArgType->GetUnderlyingType();
BfTypeInstance* wrappedStructType = GetWrappedStructType(underlyingType, false);
if (TypeIsSubTypeOf(wrappedStructType, typeConstraintInst))
implementsInterface = true;
}
if (!implementsInterface)
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must implement '%s'", genericParamInst->GetName().c_str(),
_TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), _TypeToString(checkConstraint).c_str()), checkArgTypeRef);
2019-08-23 11:56:54 -07:00
return false;
}
}
for (auto& checkOpConstraint : genericParamInst->mOperatorConstraints)
{
auto leftType = checkOpConstraint.mLeftType;
if ((leftType != NULL) && (leftType->IsUnspecializedType()))
leftType = ResolveGenericType(leftType, NULL, methodGenericArgs);
if (leftType != NULL)
leftType = FixIntUnknown(leftType);
auto rightType = checkOpConstraint.mRightType;
if ((rightType != NULL) && (rightType->IsUnspecializedType()))
rightType = ResolveGenericType(rightType, NULL, methodGenericArgs);
if (rightType != NULL)
rightType = FixIntUnknown(rightType);
2019-11-21 08:23:18 -08:00
BfConstraintState constraintSet;
constraintSet.mPrevState = mContext->mCurConstraintState;
constraintSet.mGenericParamInstance = genericParamInst;
constraintSet.mLeftType = leftType;
constraintSet.mRightType = rightType;
SetAndRestoreValue<BfConstraintState*> prevConstraintSet(mContext->mCurConstraintState, &constraintSet);
if (!CheckConstraintState(NULL))
return false;
if (checkOpConstraint.mBinaryOp != BfBinaryOp_None)
{
BfExprEvaluator exprEvaluator(this);
BfTypedValue leftValue(mBfIRBuilder->GetFakeVal(), leftType);
BfTypedValue rightValue(mBfIRBuilder->GetFakeVal(), rightType);
//
{
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
exprEvaluator.PerformBinaryOperation(NULL, NULL, checkOpConstraint.mBinaryOp, NULL, BfBinOpFlag_NoClassify, leftValue, rightValue);
}
2019-11-19 13:36:51 -08:00
if ((!exprEvaluator.mResult) ||
(!CanCast(exprEvaluator.mResult, origCheckArgType)))
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must result from binary operation '%s %s %s'", genericParamInst->GetName().c_str(),
TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(),
TypeToString(leftType).c_str(), BfGetOpName(checkOpConstraint.mBinaryOp), TypeToString(rightType).c_str()
), checkArgTypeRef);
return false;
}
}
else
{
BfTypedValue rightValue(mBfIRBuilder->GetFakeVal(), rightType);
StringT<128> failedOpName;
if (checkOpConstraint.mCastToken == BfToken_Implicit)
{
if (!CanCast(rightValue, origCheckArgType, BfCastFlags_SilentFail))
failedOpName = "implicit conversion from '";
}
else
{
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
if (checkOpConstraint.mCastToken == BfToken_Explicit)
{
if (!CastToValue(NULL, rightValue, origCheckArgType, (BfCastFlags)(BfCastFlags_Explicit | BfCastFlags_SilentFail)))
failedOpName = "explicit conversion from '";
}
else
{
BfExprEvaluator exprEvaluator(this);
exprEvaluator.mResult = rightValue;
exprEvaluator.PerformUnaryOperation(NULL, checkOpConstraint.mUnaryOp, NULL);
2019-11-19 13:36:51 -08:00
if ((!exprEvaluator.mResult) ||
(!CanCast(exprEvaluator.mResult, origCheckArgType)))
{
failedOpName += "unary operation '";
failedOpName += BfGetOpName(checkOpConstraint.mUnaryOp);
}
}
}
if (!failedOpName.IsEmpty())
{
if (!ignoreErrors)
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must result from %s%s'", genericParamInst->GetName().c_str(),
TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(),
failedOpName.c_str(), TypeToString(rightType).c_str()
), checkArgTypeRef);
return false;
}
}
}
2019-08-23 11:56:54 -07:00
return true;
}
BfGenericParamType* BfModule::GetGenericParamType(BfGenericParamKind paramKind, int paramIdx)
{
if (paramIdx < (int)mContext->mGenericParamTypes[paramKind].size())
{
auto genericParamType = mContext->mGenericParamTypes[paramKind][paramIdx];
return genericParamType;
}
auto genericParamType = new BfGenericParamType();
genericParamType->mContext = mContext;
genericParamType->mGenericParamKind = paramKind;
genericParamType->mGenericParamIdx = paramIdx;
PopulateType(genericParamType);
BF_ASSERT(paramIdx == (int)mContext->mGenericParamTypes[paramKind].size());
mContext->mGenericParamTypes[paramKind].push_back(genericParamType);
BfResolvedTypeSet::LookupContext lookupCtx;
lookupCtx.mModule = this;
BfResolvedTypeSet::Entry* typeEntry = NULL;
auto inserted = mContext->mResolvedTypes.Insert(genericParamType, &lookupCtx, &typeEntry);
BF_ASSERT(inserted);
typeEntry->mValue = genericParamType;
return genericParamType;
}
static int sValueFromExprIdx = 0;
BfTypedValue BfModule::FlushNullConditional(BfTypedValue result, bool ignoreNullable)
{
auto pendingNullCond = mCurMethodState->mPendingNullConditional;
if ((result) && (!ignoreNullable))
{
auto notNullBB = mBfIRBuilder->GetInsertBlock();
//TODO: Make this work, needed for 'void' and such
BfType* nullableType = NULL;
if ((result.mType->IsValueType()) && (!result.mType->IsNullable()))
{
BfTypeVector typeVec;
typeVec.push_back(result.mType);
nullableType = ResolveTypeDef(mCompiler->mNullableTypeDef, typeVec);
BF_ASSERT(nullableType != NULL);
}
// Go back to start and do any setup we need
mBfIRBuilder->SetInsertPoint(pendingNullCond->mPrevBB);
BfTypedValue nullableTypedValue;
if (nullableType != NULL)
{
nullableTypedValue = BfTypedValue(CreateAlloca(nullableType, true, "nullCond.nullable"), nullableType, true);
mBfIRBuilder->CreateMemSet(nullableTypedValue.mValue, GetConstValue(0, GetPrimitiveType(BfTypeCode_Int8)),
GetConstValue(nullableType->mSize), nullableType->mAlign);
}
mBfIRBuilder->CreateBr(pendingNullCond->mCheckBB);
// Now that we have the nullableTypedValue we can set that, or just jump if we didn't need it
mBfIRBuilder->SetInsertPoint(notNullBB);
result = LoadValue(result);
if (nullableTypedValue)
{
auto elementType = nullableType->GetUnderlyingType();
2020-01-12 09:21:50 -08:00
if (elementType->IsVar())
{
// Do nothing
}
else if (elementType->IsValuelessType())
2019-08-23 11:56:54 -07:00
{
BfIRValue ptrValue = mBfIRBuilder->CreateInBoundsGEP(nullableTypedValue.mValue, 0, 1); // mHasValue
2020-01-12 09:21:50 -08:00
mBfIRBuilder->CreateStore(GetConstValue(1, GetPrimitiveType(BfTypeCode_Boolean)), ptrValue);
2019-08-23 11:56:54 -07:00
}
else
{
BfIRValue ptrValue = mBfIRBuilder->CreateInBoundsGEP(nullableTypedValue.mValue, 0, 1); // mValue
mBfIRBuilder->CreateStore(result.mValue, ptrValue);
ptrValue = mBfIRBuilder->CreateInBoundsGEP(nullableTypedValue.mValue, 0, 2); // mHasValue
2020-01-12 09:21:50 -08:00
mBfIRBuilder->CreateStore(GetConstValue(1, GetPrimitiveType(BfTypeCode_Boolean)), ptrValue);
2019-08-23 11:56:54 -07:00
}
2020-01-12 09:21:50 -08:00
result = nullableTypedValue;
2019-08-23 11:56:54 -07:00
}
mBfIRBuilder->CreateBr(pendingNullCond->mDoneBB);
AddBasicBlock(pendingNullCond->mDoneBB);
if (nullableType == NULL)
{
2019-12-03 17:35:53 -08:00
auto phi = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(result.mType), 1 + (int)pendingNullCond->mNotNullBBs.size());
2019-08-23 11:56:54 -07:00
mBfIRBuilder->AddPhiIncoming(phi, result.mValue, notNullBB);
2019-12-03 17:35:53 -08:00
2019-08-23 11:56:54 -07:00
mBfIRBuilder->AddPhiIncoming(phi, GetDefaultValue(result.mType), pendingNullCond->mCheckBB);
2019-12-03 17:35:53 -08:00
for (int notNullIdx = 0; notNullIdx < (int)pendingNullCond->mNotNullBBs.size() - 1; notNullIdx++)
{
auto prevNotNullBB = pendingNullCond->mNotNullBBs[notNullIdx];
mBfIRBuilder->AddPhiIncoming(phi, GetDefaultValue(result.mType), prevNotNullBB);
}
2019-08-23 11:56:54 -07:00
result.mValue = phi;
}
}
else
{
mBfIRBuilder->CreateBr(pendingNullCond->mDoneBB);
mBfIRBuilder->SetInsertPoint(pendingNullCond->mPrevBB);
mBfIRBuilder->CreateBr(pendingNullCond->mCheckBB);
AddBasicBlock(pendingNullCond->mDoneBB);
}
delete mCurMethodState->mPendingNullConditional;
mCurMethodState->mPendingNullConditional = NULL;
return result;
}
BF_NOINLINE void BfModule::EvaluateWithNewScope(BfExprEvaluator& exprEvaluator, BfExpression* expr, BfEvalExprFlags flags)
{
BfScopeData newScope;
newScope.mOuterIsConditional = true;
newScope.mAllowTargeting = false;
mCurMethodState->AddScope(&newScope);
NewScopeState();
exprEvaluator.mBfEvalExprFlags = flags;
exprEvaluator.Evaluate(expr, (flags & BfEvalExprFlags_PropogateNullConditional) != 0, (flags & BfEvalExprFlags_IgnoreNullConditional) != 0, true);
RestoreScopeState();
}
BfTypedValue BfModule::CreateValueFromExpression(BfExprEvaluator& exprEvaluator, BfExpression* expr, BfType* wantTypeRef, BfEvalExprFlags flags, BfType** outOrigType)
{
//
{
BP_ZONE("CreateValueFromExpression:CheckStack");
StackHelper stackHelper;
if (!stackHelper.CanStackExpand(64 * 1024))
{
BfTypedValue result;
if (!stackHelper.Execute([&]()
{
result = CreateValueFromExpression(exprEvaluator, expr, wantTypeRef, flags, outOrigType);
}))
{
Fail("Expression too complex to compile", expr);
}
return result;
}
}
BP_ZONE("BfModule::CreateValueFromExpression");
if (outOrigType != NULL)
*outOrigType = NULL;
exprEvaluator.mExpectingType = wantTypeRef;
exprEvaluator.mBfEvalExprFlags = flags;
exprEvaluator.mExplicitCast = (flags & BfEvalExprFlags_ExplicitCast) != 0;
if ((flags & BfEvalExprFlags_CreateConditionalScope) != 0)
{
EvaluateWithNewScope(exprEvaluator, expr, flags);
}
else
exprEvaluator.Evaluate(expr, (flags & BfEvalExprFlags_PropogateNullConditional) != 0, (flags & BfEvalExprFlags_IgnoreNullConditional) != 0, true);
if (!exprEvaluator.mResult)
{
if (!mCompiler->mPassInstance->HasFailed())
Fail("INTERNAL ERROR: No expression result returned but no error caught in expression evaluator", expr);
return BfTypedValue();
}
auto typedVal = exprEvaluator.mResult;
if (typedVal.mKind == BfTypedValueKind_GenericConstValue)
{
BF_ASSERT(typedVal.mType->IsGenericParam());
auto genericParamDef = GetGenericParamInstance((BfGenericParamType*)typedVal.mType);
bool handled = false;
if ((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0)
{
auto genericTypeConstraint = genericParamDef->mTypeConstraint;
if (genericTypeConstraint != NULL)
{
auto primType = genericTypeConstraint->ToPrimitiveType();
if (primType != NULL)
{
BfTypedValue result;
result.mKind = BfTypedValueKind_Value;
result.mType = genericTypeConstraint;
result.mValue = mBfIRBuilder->GetUndefConstValue(primType->mTypeDef->mTypeCode);
typedVal = result;
handled = true;
}
}
}
if (!handled)
{
//Fail("Only const generic parameters can be used as values", expr);
AssertErrorState();
return BfTypedValue();
}
}
if ((flags & BfEvalExprFlags_AllowIntUnknown) == 0)
FixIntUnknown(typedVal);
exprEvaluator.CheckResultForReading(typedVal);
if ((wantTypeRef == NULL) || (!wantTypeRef->IsRef()))
{
if ((flags & BfEvalExprFlags_NoCast) == 0)
{
// Only allow a 'ref' type if we have an explicit 'ref' operator
bool allowRef = false;
BfExpression* checkExpr = expr;
while (auto parenExpr = BfNodeDynCast<BfParenthesizedExpression>(checkExpr))
checkExpr = parenExpr->mExpression;
if (auto unaryOp = BfNodeDynCast<BfUnaryOperatorExpression>(checkExpr))
{
if ((unaryOp->mOp == BfUnaryOp_Ref) || (unaryOp->mOp == BfUnaryOp_Mut))
allowRef = true;
}
if (!allowRef)
typedVal = RemoveRef(typedVal);
}
}
if (!typedVal.mType->IsComposite()) // Load non-structs by default
2020-04-12 08:37:22 -07:00
{
if ((!mBfIRBuilder->mIgnoreWrites) && (!typedVal.mType->IsValuelessType()) && (!typedVal.mType->IsVar()))
{
BF_ASSERT(!typedVal.mValue.IsFake());
}
2019-08-23 11:56:54 -07:00
typedVal = LoadValue(typedVal, 0, exprEvaluator.mIsVolatileReference);
2020-04-12 08:37:22 -07:00
}
2019-08-23 11:56:54 -07:00
if (wantTypeRef != NULL)
{
if (outOrigType != NULL)
*outOrigType = typedVal.mType;
if ((flags & BfEvalExprFlags_NoCast) == 0)
{
BfCastFlags castFlags = ((flags & BfEvalExprFlags_ExplicitCast) != 0) ? BfCastFlags_Explicit : BfCastFlags_None;
if ((flags & BfEvalExprFlags_FieldInitializer) != 0)
castFlags = (BfCastFlags)(castFlags | BfCastFlags_WarnOnBox);
typedVal = Cast(expr, typedVal, wantTypeRef, castFlags);
if (!typedVal)
return typedVal;
}
//WTF- why did we have this?
/*if ((flags & BfEvalExprFlags_ExplicitCast) == 0)
typedVal = LoadValue(typedVal, 0, exprEvaluator.mIsVolatileReference);*/
if (exprEvaluator.mIsVolatileReference)
typedVal = LoadValue(typedVal, 0, exprEvaluator.mIsVolatileReference);
}
if ((typedVal.mType->IsValueType()) && ((flags & BfEvalExprFlags_NoValueAddr) != 0))
typedVal = LoadValue(typedVal, 0, exprEvaluator.mIsVolatileReference);
return typedVal;
}
BfTypedValue BfModule::CreateValueFromExpression(BfExpression* expr, BfType* wantTypeRef, BfEvalExprFlags flags, BfType** outOrigType)
{
BfExprEvaluator exprEvaluator(this);
return CreateValueFromExpression(exprEvaluator, expr, wantTypeRef, flags, outOrigType);
}
BfTypedValue BfModule::GetOrCreateVarAddr(BfExpression* expr)
{
BfExprEvaluator exprEvaluator(this);
exprEvaluator.Evaluate(expr);
if (!exprEvaluator.mResult)
{
Fail("Invalid expression type", expr);
return BfTypedValue();
}
if (!exprEvaluator.mResult.IsAddr())
{
Fail("Cannot assign to value", expr);
return BfTypedValue();
}
return exprEvaluator.mResult;
}
// Clear memory, set classVData, call init. Actual ctor is called elsewhere.
void BfModule::InitTypeInst(BfTypedValue typedValue, BfScopeData* scopeData, bool zeroMemory, BfIRValue sizeValue)
{
auto typeInstance = typedValue.mType->ToTypeInstance();
if (zeroMemory)
{
int zeroAlign = typedValue.mType->mAlign;
if (typeInstance != NULL)
zeroAlign = typeInstance->mInstAlign;
mBfIRBuilder->CreateMemSet(typedValue.mValue, GetConstValue8(0), sizeValue, zeroAlign);
}
if (!typedValue.mType->IsObject())
{
return;
}
if ((scopeData == NULL) && (mCurMethodState != NULL))
return; // Handled in heap alloc funcs
auto typeDef = typeInstance->mTypeDef;
mBfIRBuilder->PopulateType(typedValue.mType);
auto vObjectAddr = mBfIRBuilder->CreateInBoundsGEP(typedValue.mValue, 0, 0);
bool isAutocomplete = mCompiler->IsAutocomplete();
BfIRValue vDataRef;
if (!isAutocomplete)
{
vDataRef = GetClassVDataPtr(typeInstance);
}
auto i8Type = GetPrimitiveType(BfTypeCode_Int8);
auto ptrType = CreatePointerType(i8Type);
auto ptrPtrType = CreatePointerType(ptrType);
PopulateType(ptrPtrType, BfPopulateType_Declaration);
PopulateType(ptrType, BfPopulateType_Declaration);
auto destAddr = mBfIRBuilder->CreateBitCast(vObjectAddr, mBfIRBuilder->MapType(ptrPtrType));
if (!isAutocomplete)
{
if (mCompiler->mOptions.mObjectHasDebugFlags)
{
auto objectPtr = mBfIRBuilder->CreateBitCast(destAddr, mBfIRBuilder->MapType(mContext->mBfObjectType));
SizedArray<BfIRValue, 4> llvmArgs;
llvmArgs.push_back(objectPtr);
llvmArgs.push_back(vDataRef);
auto objectStackInitMethod = GetInternalMethod("Dbg_ObjectStackInit");
if (objectStackInitMethod)
mBfIRBuilder->CreateCall(objectStackInitMethod.mFunc, llvmArgs);
}
else
{
auto srcVal = mBfIRBuilder->CreateBitCast(vDataRef, mBfIRBuilder->MapType(ptrType));
auto objectPtr = mBfIRBuilder->CreateBitCast(destAddr, mBfIRBuilder->MapType(ptrType));
mBfIRBuilder->CreateStore(srcVal, destAddr);
}
}
}
BfIRValue BfModule::AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTarget, BfType* type, BfIRValue sizeValue, BfIRValue alignValue, BfAllocFlags allocFlags/*, bool zeroMemory, bool defaultToMalloc*/)
{
BfIRValue result;
BfType* ptrType;
if ((type->IsObject()) && ((allocFlags & BfAllocFlags_RawArray) == 0))
ptrType = type;
else
ptrType = CreatePointerType(type);
if ((allocTarget.mScopedInvocationTarget != NULL) || (allocTarget.mCustomAllocator))
{
auto intType = GetPrimitiveType(BfTypeCode_IntPtr);
SizedArray<BfExpression*, 2> argExprs;
BfTypedValueExpression typedValueExpr;
typedValueExpr.Init(BfTypedValue(sizeValue, intType));
typedValueExpr.mRefNode = refNode;
argExprs.push_back(&typedValueExpr);
BfTypedValueExpression appendSizeValueExpr;
appendSizeValueExpr.Init(BfTypedValue(alignValue, intType));
appendSizeValueExpr.mRefNode = refNode;
argExprs.push_back(&appendSizeValueExpr);
BfExprEvaluator exprEvaluator(this);
BfTypedValue allocResult;
if (allocTarget.mScopedInvocationTarget != NULL)
{
SizedArray<BfTypeReference*, 2> genericArgs;
exprEvaluator.DoInvocation(allocTarget.mScopedInvocationTarget, NULL, argExprs, NULL);
allocResult = LoadValue(exprEvaluator.mResult);
}
else if (allocTarget.mCustomAllocator)
{
auto customTypeInst = allocTarget.mCustomAllocator.mType->ToTypeInstance();
if (customTypeInst != NULL)
{
BfTypedValueExpression typeValueExpr;
String allocMethodName = "Alloc";
if (GetRawMethodByName(customTypeInst, "AllocTyped", -1, true, true))
{
allocMethodName = "AllocTyped";
auto typeType = ResolveTypeDef(mCompiler->mTypeTypeDef);
auto typeRefVal = CreateTypeDataRef(type);
typeValueExpr.Init(BfTypedValue(typeRefVal, typeType));
typeValueExpr.mRefNode = refNode;
argExprs.Insert(0, &typeValueExpr);
}
if (HasMixin(customTypeInst, allocMethodName, 2))
{
BfSizedArray<BfExpression*> argExprArr;
argExprArr.mSize = (int)argExprs.size();
argExprArr.mVals = &argExprs[0];
exprEvaluator.InjectMixin(refNode, allocTarget.mCustomAllocator, false, allocMethodName, argExprs, {});
allocResult = exprEvaluator.GetResult();
}
else
{
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
BfResolvedArgs argValues(&sizedArgExprs);
exprEvaluator.ResolveArgValues(argValues);
SetAndRestoreValue<bool> prevNoBind(mCurMethodState->mNoBind, true);
allocResult = exprEvaluator.MatchMethod(refNode, NULL, allocTarget.mCustomAllocator, false, false, allocMethodName, argValues, NULL);
}
}
}
if (allocResult)
{
if (allocResult.mType->IsVoidPtr())
result = mBfIRBuilder->CreateBitCast(allocResult.mValue, mBfIRBuilder->MapType(ptrType));
else
result = CastToValue(refNode, allocResult, ptrType, BfCastFlags_Explicit);
return result;
}
}
if ((allocFlags & BfAllocFlags_NoDefaultToMalloc) != 0)
return result;
if (mCompiler->mOptions.mDebugAlloc)
{
BfIRValue allocData = GetDbgRawAllocData(type);
BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_RawAlloc", 2);
SizedArray<BfIRValue, 2> llvmArgs;
llvmArgs.push_back(sizeValue);
llvmArgs.push_back(allocData);
BfIRValue bitData = mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs);
return mBfIRBuilder->CreateBitCast(bitData, mBfIRBuilder->MapType(ptrType));
}
BfModuleMethodInstance moduleMethodInstance;
moduleMethodInstance = GetInternalMethod("Malloc");
SizedArray<BfIRValue, 1> llvmArgs;
llvmArgs.push_back(sizeValue);
auto func = moduleMethodInstance.mFunc;
if ((!func) || (func.IsFake()))
{
BF_ASSERT((mCompiler->mIsResolveOnly) || (mBfIRBuilder->mIgnoreWrites));
return mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(ptrType));
}
BfIRValue bitData = mBfIRBuilder->CreateCall(func, llvmArgs);
if ((allocFlags & BfAllocFlags_ZeroMemory) != 0)
mBfIRBuilder->CreateMemSet(bitData, GetConstValue8(0), sizeValue, alignValue);
result = mBfIRBuilder->CreateBitCast(bitData, mBfIRBuilder->MapType(ptrType));
return result;
}
BfIRValue BfModule::GetMarkFuncPtr(BfType* type)
{
if (type->IsStruct())
{
auto typeInstance = type->ToTypeInstance();
BfModuleMethodInstance moduleMethodInst = GetMethodByName(typeInstance, BF_METHODNAME_MARKMEMBERS, 0, true);
return mBfIRBuilder->CreateBitCast(moduleMethodInst.mFunc, mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_NullPtr)));
}
else if (type->IsObjectOrInterface())
{
auto gcType = ResolveTypeDef(mCompiler->mGCTypeDef)->ToTypeInstance();
BfModuleMethodInstance moduleMethodInst = GetMethodByName(gcType, "MarkDerefedObject");
BF_ASSERT(moduleMethodInst.mFunc);
return mBfIRBuilder->CreateBitCast(moduleMethodInst.mFunc, mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_NullPtr)));
}
else
{
auto gcType = ResolveTypeDef(mCompiler->mGCTypeDef)->ToTypeInstance();
BfExprEvaluator exprEvaluator(this);
SizedArray<BfResolvedArg, 1> resolvedArgs;
BfResolvedArg resolvedArg;
resolvedArg.mTypedValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), type, type->IsComposite());
resolvedArgs.Add(resolvedArg);
BfMethodMatcher methodMatcher(NULL, this, "Mark", resolvedArgs, NULL);
methodMatcher.CheckType(gcType, BfTypedValue(), false);
BfModuleMethodInstance moduleMethodInst = exprEvaluator.GetSelectedMethod(NULL, methodMatcher.mBestMethodTypeInstance, methodMatcher.mBestMethodDef, methodMatcher);
BF_ASSERT(moduleMethodInst.mFunc);
return mBfIRBuilder->CreateBitCast(moduleMethodInst.mFunc, mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_NullPtr)));
}
return BfIRValue();
}
BfIRValue BfModule::GetDbgRawAllocData(BfType* type)
{
BfIRValue allocDataValue;
if (mDbgRawAllocDataRefs.TryGetValue(type, &allocDataValue))
return allocDataValue;
BfIRValue markFuncPtr;
if (type->WantsGCMarking())
markFuncPtr = GetMarkFuncPtr(type);
else
markFuncPtr = mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_NullPtr)));
BfIRValue typeDataRef = CreateTypeDataRef(type);
auto dbgRawAllocDataType = ResolveTypeDef(mCompiler->mDbgRawAllocDataTypeDef)->ToTypeInstance();
BfTypeInstance* typeInstance = type->ToTypeInstance();
if (typeInstance == NULL)
{
typeInstance = GetWrappedStructType(type);
if (typeInstance != NULL)
AddDependency(typeInstance, mCurTypeInstance, BfDependencyMap::DependencyFlag_Allocates);
}
if (typeInstance != NULL)
PopulateType(typeInstance);
int stackCount = mCompiler->mOptions.mAllocStackCount;
if ((typeInstance != NULL) && (typeInstance->mTypeOptionsIdx != -1))
{
auto typeOptions = mSystem->GetTypeOptions(typeInstance->mTypeOptionsIdx);
stackCount = BfTypeOptions::Apply(stackCount, typeOptions->mAllocStackTraceDepth);
}
SizedArray<BfIRValue, 2> dataValues;
dataValues.Add(mBfIRBuilder->CreateConstStructZero(mBfIRBuilder->MapType(dbgRawAllocDataType->mBaseType, BfIRPopulateType_Full)));
dataValues.Add(typeDataRef);
dataValues.Add(markFuncPtr);
dataValues.Add(mBfIRBuilder->CreateConst(BfTypeCode_Int32, stackCount));
BfIRValue dataStruct = mBfIRBuilder->CreateConstStruct(mBfIRBuilder->MapType(dbgRawAllocDataType, BfIRPopulateType_Full), dataValues);
allocDataValue = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(dbgRawAllocDataType), true, BfIRLinkageType_Internal, dataStruct, "__allocData_" + BfSafeMangler::Mangle(type));
mDbgRawAllocDataRefs.TryAdd(type, allocDataValue);
return allocDataValue;
}
BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget, BfIRValue appendSizeValue, BfIRValue arraySize, int arrayDim, BfAllocFlags allocFlags, int alignOverride)
{
BP_ZONE("AllocFromType");
BfScopeData* scopeData = allocTarget.mScopeData;
BF_ASSERT(!type->IsVar());
2019-08-23 11:56:54 -07:00
auto typeInstance = type->ToTypeInstance();
if ((typeInstance == NULL) && (type->IsGenericParam()))
typeInstance = mContext->mBfObjectType;
bool isHeapAlloc = scopeData == NULL;
bool isScopeAlloc = scopeData != NULL;
bool isLoopedAlloc = (scopeData != NULL) && (mCurMethodState->mCurScope->IsLooped(scopeData));
bool isDynAlloc = (scopeData != NULL) && (mCurMethodState->mCurScope->IsDyn(scopeData));
bool isRawArrayAlloc = (allocFlags & BfAllocFlags_RawArray) != 0;
bool zeroMemory = (allocFlags & BfAllocFlags_ZeroMemory) != 0;
bool noDtorCall = (allocFlags & BfAllocFlags_NoDtorCall) != 0;
if ((type->IsValuelessType()) && ((!arraySize) || (isRawArrayAlloc)))
{
BfPointerType* ptrType = CreatePointerType(type);
auto val = mBfIRBuilder->CreateIntToPtr(mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1), mBfIRBuilder->MapType(ptrType));
return val;
}
if (typeInstance != NULL)
mBfIRBuilder->PopulateType(typeInstance);
bool hasCustomAllocator = (allocTarget.mCustomAllocator) || (allocTarget.mScopedInvocationTarget != NULL);
if ((!hasCustomAllocator) && (mBfIRBuilder->mIgnoreWrites))
{
if (arraySize)
{
if (isRawArrayAlloc)
{
BfPointerType* ptrType = CreatePointerType(type);
return GetDefaultValue(ptrType);
}
BfArrayType* arrayType = CreateArrayType(type, arrayDim);
return GetDefaultValue(arrayType);
}
if (type->IsValueType())
{
BfPointerType* ptrType = CreatePointerType(type);
return GetDefaultValue(ptrType);
}
return mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(type));
}
AddDependency(type, mCurTypeInstance, BfDependencyMap::DependencyFlag_Allocates);
BfIRValue sizeValue;
BfIRType allocType = mBfIRBuilder->MapType(type);
int allocSize = type->mSize;
int allocAlign = type->mAlign;
if (typeInstance != NULL)
{
if (!mBfIRBuilder->mIgnoreWrites)
typeInstance->mHasBeenInstantiated = true;
allocSize = typeInstance->mInstSize;
allocAlign = typeInstance->mInstAlign;
//if (typeInstance->IsEnum())
//allocType = typeInstance->mIRType;
allocType = mBfIRBuilder->MapTypeInst(typeInstance);
}
if (alignOverride != -1)
allocAlign = alignOverride;
// The "dynSize" cases:
// If we are not dyn, but we are a variable size and could be looped over multiple times then we attempt to reuse
// the last stack space. We won't use StackSave and StackRestore because a scoped alloc that occurs after this but
// needs to be retained after the current scopeData (ie: scopeData::), then it would cause these allocs to accumulate even
// though we'd expect their stack space to be released
auto _CreateDynAlloc = [&](const BfIRValue& sizeValue, int align)
{
MarkDynStack(scopeData);
BfType* intType = GetPrimitiveType(BfTypeCode_IntPtr);
// For dynamically sized scoped allocs, we need to check the previous size to see if we need to realloc
BfType* voidType = GetPrimitiveType(BfTypeCode_None);
BfType* voidPtrType = CreatePointerType(voidType);
if (!mCurMethodState->mDynStackRevIdx)
{
mCurMethodState->mDynStackRevIdx = CreateAlloca(intType, false, "curDynStackRevIdx");
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRInitBlock);
auto storeInst = mBfIRBuilder->CreateStore(GetConstValue(0), mCurMethodState->mDynStackRevIdx);
mBfIRBuilder->ClearDebugLocation(storeInst);
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
}
auto byteType = GetPrimitiveType(BfTypeCode_Int8);
auto bytePtrType = CreatePointerType(byteType);
auto dynSizeAllocaInst = CreateAlloca(intType, false, "dynSize");
auto dynStackRevIdx = CreateAlloca(intType, false, "dynStackRevIdx");
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRInitBlock);
auto storeInst = mBfIRBuilder->CreateStore(GetConstValue(0), dynSizeAllocaInst);
mBfIRBuilder->ClearDebugLocation(storeInst);
storeInst = mBfIRBuilder->CreateStore(GetConstValue(0), dynStackRevIdx);
mBfIRBuilder->ClearDebugLocation(storeInst);
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
auto dynPtrAllocaInst = CreateAlloca(bytePtrType, false, "dynPtr");
if (IsTargetingBeefBackend())
{
// Since we don't allocate a debug variable for this, with LifetimeExtends, the easiest way
// to ensure there isn't any register collision during looping is just to not allow it to
// bind to a register at all
mBfIRBuilder->SetAllocaForceMem(dynSizeAllocaInst);
mBfIRBuilder->SetAllocaForceMem(dynPtrAllocaInst);
}
auto resizeBB = mBfIRBuilder->CreateBlock("dynAlloc.resize");
auto endBB = mBfIRBuilder->CreateBlock("dynAlloc.end");
auto verCheckBB = mBfIRBuilder->CreateBlock("dynAlloc.verCheck");
auto curDynStackRevIdx = mBfIRBuilder->CreateLoad(mCurMethodState->mDynStackRevIdx);
auto prevSize = mBfIRBuilder->CreateLoad(dynSizeAllocaInst);
auto isGreater = mBfIRBuilder->CreateCmpGT(sizeValue, prevSize, true);
mBfIRBuilder->CreateCondBr(isGreater, resizeBB, verCheckBB);
mBfIRBuilder->AddBlock(verCheckBB);
mBfIRBuilder->SetInsertPoint(verCheckBB);
auto prevDynStackRevIdx = mBfIRBuilder->CreateLoad(dynStackRevIdx);
auto dynStackChanged = mBfIRBuilder->CreateCmpNE(prevDynStackRevIdx, curDynStackRevIdx);
mBfIRBuilder->CreateCondBr(dynStackChanged, resizeBB, endBB);
mBfIRBuilder->AddBlock(resizeBB);
mBfIRBuilder->SetInsertPoint(resizeBB);
auto allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(byteType), sizeValue);
mBfIRBuilder->SetAllocaAlignment(allocaInst, align);
mBfIRBuilder->CreateStore(allocaInst, dynPtrAllocaInst);
mBfIRBuilder->CreateStore(sizeValue, dynSizeAllocaInst);
mBfIRBuilder->CreateStore(curDynStackRevIdx, dynStackRevIdx);
mBfIRBuilder->CreateBr(endBB);
mBfIRBuilder->AddBlock(endBB);
mBfIRBuilder->SetInsertPoint(endBB);
return mBfIRBuilder->CreateLoad(dynPtrAllocaInst);
};
if (isScopeAlloc)
{
if (arraySize)
{
bool isConstantArraySize = arraySize.IsConst();
if (isRawArrayAlloc)
{
BfPointerType* ptrType = CreatePointerType(type);
if ((!isDynAlloc) && (!isConstantArraySize) && (mCurMethodState->mCurScope->IsLooped(NULL)))
{
BfIRValue sizeValue = mBfIRBuilder->CreateMul(GetConstValue(type->GetStride()), arraySize);
auto loadedPtr = _CreateDynAlloc(sizeValue, type->mAlign);
InitTypeInst(BfTypedValue(loadedPtr, ptrType), scopeData, zeroMemory, sizeValue);
return mBfIRBuilder->CreateBitCast(loadedPtr, mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapType(type)));
}
else
{
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
isDynAlloc = (isDynAlloc) || (!arraySize.IsConst());
if (!isDynAlloc)
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
else
{
MarkDynStack(scopeData);
SaveStackState(scopeData);
}
int typeSize = type->GetStride();
BfIRValue allocaInst;
BfIRValue result;
if ((typeInstance == NULL) || (typeInstance->mIsCRepr))
{
sizeValue = mBfIRBuilder->CreateMul(GetConstValue(typeSize), arraySize);
allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(type), arraySize);
result = allocaInst;
}
else
{
if (typeInstance->IsStruct())
typeSize = typeInstance->GetInstStride();
auto byteType = GetPrimitiveStructType(BfTypeCode_Int8);
sizeValue = mBfIRBuilder->CreateMul(GetConstValue(typeSize), arraySize);
allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(byteType), sizeValue);
auto ptrType = CreatePointerType(typeInstance);
result = mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapType(ptrType));
if (!isDynAlloc)
mBfIRBuilder->ClearDebugLocation(result);
}
if (!isDynAlloc)
mBfIRBuilder->ClearDebugLocation(allocaInst);
mBfIRBuilder->SetAllocaAlignment(allocaInst, allocAlign);
2019-08-23 11:56:54 -07:00
if (!isDynAlloc)
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
auto typedVal = BfTypedValue(result, type, BfTypedValueKind_Addr);
if (arraySize.IsConst())
{
auto constant = mBfIRBuilder->GetConstant(arraySize);
if (constant->mInt64 == 0)
noDtorCall = true;
}
if (!noDtorCall)
{
bool isConstSize = arraySize.IsConst();
BfIRBlock clearBlock;
BfIRBlock contBlock;
if (!isConstSize)
{
clearBlock = mBfIRBuilder->CreateBlock("clear");
contBlock = mBfIRBuilder->CreateBlock("clearCont");
mBfIRBuilder->CreateCondBr(mBfIRBuilder->CreateCmpNE(arraySize, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0)), clearBlock, contBlock);
mBfIRBuilder->AddBlock(clearBlock);
mBfIRBuilder->SetInsertPoint(clearBlock);
}
AddStackAlloc(typedVal, arraySize, NULL, scopeData, false, true);
2019-08-23 11:56:54 -07:00
if (!isConstSize)
{
mBfIRBuilder->CreateBr(contBlock);
mBfIRBuilder->AddBlock(contBlock);
mBfIRBuilder->SetInsertPoint(contBlock);
}
}
//InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue);
return result;
}
}
else
{
if ((!isDynAlloc) && (!arraySize.IsConst()) && (mCurMethodState->mCurScope->IsLooped(NULL)))
{
// Generate and check new size
BfArrayType* arrayType = CreateArrayType(type, arrayDim);
auto firstElementField = &arrayType->mFieldInstances.back();
int arrayClassSize = firstElementField->mDataOffset;
sizeValue = GetConstValue(arrayClassSize);
BfIRValue elementDataSize = mBfIRBuilder->CreateMul(GetConstValue(type->GetStride()), arraySize);
sizeValue = mBfIRBuilder->CreateAdd(sizeValue, elementDataSize);
auto loadedPtr = _CreateDynAlloc(sizeValue, arrayType->mAlign);
auto typedVal = BfTypedValue(mBfIRBuilder->CreateBitCast(loadedPtr, mBfIRBuilder->MapType(arrayType)), arrayType);
if (!noDtorCall)
AddStackAlloc(typedVal, arraySize, NULL, scopeData, false, true);
2019-08-23 11:56:54 -07:00
InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue);
return typedVal.mValue;
}
else
{
BfArrayType* arrayType = CreateArrayType(type, arrayDim);
2020-02-17 14:49:59 -08:00
auto firstElementField = &arrayType->mFieldInstances.back();
if (!type->IsValuelessType())
{
int arrayClassSize = firstElementField->mDataOffset;
sizeValue = GetConstValue(arrayClassSize);
BfIRValue elementDataSize = mBfIRBuilder->CreateMul(GetConstValue(type->GetStride()), arraySize);
sizeValue = mBfIRBuilder->CreateAdd(sizeValue, elementDataSize);
}
else
sizeValue = GetConstValue(arrayType->mInstSize);
2019-08-23 11:56:54 -07:00
auto prevBlock = mBfIRBuilder->GetInsertBlock();
isDynAlloc = (isDynAlloc) || (!sizeValue.IsConst());
if (!isDynAlloc)
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
else
{
MarkDynStack(scopeData);
SaveStackState(scopeData);
}
BfType* byteType = GetPrimitiveType(BfTypeCode_Int8);
auto allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(byteType), sizeValue);
if (!isDynAlloc)
mBfIRBuilder->ClearDebugLocation(allocaInst);
mBfIRBuilder->SetAllocaAlignment(allocaInst, allocAlign);
2019-08-23 11:56:54 -07:00
auto typedVal = BfTypedValue(mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapType(arrayType)), arrayType);
mBfIRBuilder->ClearDebugLocation_Last();
if (!isDynAlloc)
mBfIRBuilder->SetInsertPoint(prevBlock);
if (!noDtorCall)
AddStackAlloc(typedVal, arraySize, NULL, scopeData, false, true);
2019-08-23 11:56:54 -07:00
InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue);
return typedVal.mValue;
}
}
}
else if (appendSizeValue)
{
// It may seem that we should check IsLooped against only the requested scope, BUT- if later on we perform
// a scoped alloc that crosses our boundary then we won't be able to reclaim the stack space, so we always
// use the "dynSize" version conservatively
if ((!isDynAlloc) && (!appendSizeValue.IsConst()) && (mCurMethodState->mCurScope->IsLooped(NULL)))
{
// Generate and check new size
sizeValue = GetConstValue(typeInstance->mInstSize);
sizeValue = mBfIRBuilder->CreateAdd(sizeValue, appendSizeValue);
auto loadedPtr = _CreateDynAlloc(sizeValue, typeInstance->mAlign);
auto typedVal = BfTypedValue(mBfIRBuilder->CreateBitCast(loadedPtr, mBfIRBuilder->MapTypeInstPtr(typeInstance)), typeInstance);
if (!noDtorCall)
AddStackAlloc(typedVal, arraySize, NULL, scopeData, mCurMethodState->mInConditionalBlock, true);
2019-08-23 11:56:54 -07:00
InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue);
return typedVal.mValue;
}
else // stack alloc, unlooped, with append
{
sizeValue = GetConstValue(typeInstance->mInstSize);
sizeValue = mBfIRBuilder->CreateAdd(sizeValue, appendSizeValue);
auto prevBlock = mBfIRBuilder->GetInsertBlock();
bool wasDynAlloc = isDynAlloc;
isDynAlloc = (isDynAlloc) || (!sizeValue.IsConst());
if (!isDynAlloc)
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
else
{
MarkDynStack(scopeData);
SaveStackState(scopeData);
}
auto sbyteType = GetPrimitiveType(BfTypeCode_Int8);
auto allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(sbyteType), sizeValue);
if (!isDynAlloc)
{
mBfIRBuilder->ClearDebugLocation(allocaInst);
}
mBfIRBuilder->SetAllocaAlignment(allocaInst, allocAlign);
bool mayBeLarge = false;
if (sizeValue.IsConst())
{
auto constantInt = mBfIRBuilder->GetConstant(sizeValue);
if (constantInt->mInt64 >= 4096)
mayBeLarge = true;
}
else
mayBeLarge = true;
if (((type->IsObject() && (!mayBeLarge))) ||
// If we create a memset that can fill in backwards (or at least 4k chunked backward) then we can remove the '!mayBeLarge' cond
((zeroMemory) && (!mayBeLarge)))
{
mBfIRBuilder->SetAllocaNoChkStkHint(allocaInst);
}
BfIRValue castedVal;
if (!isDynAlloc)
{
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRInitBlock);
castedVal = mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapTypeInstPtr(typeInstance));
mBfIRBuilder->ClearDebugLocation(castedVal);
mBfIRBuilder->SetInsertPoint(prevBlock);
if (WantsLifetimes())
{
mBfIRBuilder->CreateLifetimeStart(allocaInst);
scopeData->mDeferredLifetimeEnds.push_back(allocaInst);
}
2019-08-23 11:56:54 -07:00
}
else
{
castedVal = mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapTypeInstPtr(typeInstance));
}
auto typedVal = BfTypedValue(castedVal, typeInstance);
if (!noDtorCall)
{
bool doCondAlloca = false;
if (!IsTargetingBeefBackend())
doCondAlloca = !wasDynAlloc && isDynAlloc && mCurMethodState->mInConditionalBlock;
AddStackAlloc(typedVal, arraySize, NULL, scopeData, doCondAlloca, true);
2019-08-23 11:56:54 -07:00
}
InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue);
return typedVal.mValue;
}
}
else // "Normal" stack alloc
{
BF_ASSERT(!sizeValue);
//TODO: If sizeValue is a constant then do this in the head IR builder
auto prevBlock = mBfIRBuilder->GetInsertBlock();
if (!isLoopedAlloc)
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
else
{
MarkDynStack(scopeData);
SaveStackState(scopeData);
}
auto allocaInst = mBfIRBuilder->CreateAlloca(allocType);
if (!isLoopedAlloc)
mBfIRBuilder->ClearDebugLocation(allocaInst);
mBfIRBuilder->SetAllocaAlignment(allocaInst, allocAlign);
bool mayBeLarge = allocSize >= 4096;
if (((typeInstance != NULL) && (typeInstance->IsObject()) && (!mayBeLarge)) ||
// If we create a memset that can fill in backwards (or at least 4k chunked backward) then we can remove the '!mayBeLarge' cond
((zeroMemory) && (!mayBeLarge)))
{
// With an object, we know we will at least set the vdata pointer which will probe the start of the new address,
// and memset is always safe
mBfIRBuilder->SetAllocaNoChkStkHint(allocaInst);
}
auto typedVal = BfTypedValue(allocaInst, type);
if (!isLoopedAlloc)
{
mBfIRBuilder->ClearDebugLocation(allocaInst);
mBfIRBuilder->SetInsertPoint(prevBlock);
if (WantsLifetimes())
{
mBfIRBuilder->CreateLifetimeStart(allocaInst);
scopeData->mDeferredLifetimeEnds.push_back(allocaInst);
}
2019-08-23 11:56:54 -07:00
}
if (!noDtorCall)
AddStackAlloc(typedVal, BfIRValue(), NULL, scopeData, false, true);
2019-08-23 11:56:54 -07:00
if (typeInstance != NULL)
{
InitTypeInst(typedVal, scopeData, zeroMemory, GetConstValue(typeInstance->mInstSize));
}
else
{
mBfIRBuilder->CreateMemSet(allocaInst, GetConstValue8(0), GetConstValue(allocSize), allocAlign);
}
return allocaInst;
}
}
else if (arraySize)
{
if (isRawArrayAlloc)
{
sizeValue = mBfIRBuilder->CreateMul(GetConstValue(type->GetStride()), arraySize);
return AllocBytes(allocTarget.mRefNode, allocTarget, type, sizeValue, GetConstValue(allocAlign), allocFlags);
}
else
{
BfArrayType* arrayType = CreateArrayType(type, arrayDim);
typeInstance = arrayType;
BfIRValue arraySizeMinusOne = mBfIRBuilder->CreateSub(arraySize, GetConstValue(1));
BfIRValue elementDataSize = mBfIRBuilder->CreateMul(GetConstValue(type->GetStride()), arraySizeMinusOne);
appendSizeValue = elementDataSize;
if (!type->IsSizeAligned())
{
appendSizeValue = mBfIRBuilder->CreateAdd(appendSizeValue, GetConstValue(type->GetStride() - type->mSize));
}
}
}
if ((typeInstance != NULL) && (typeInstance->IsObject()))
{
auto vDataRef = GetClassVDataPtr(typeInstance);
BfIRValue result;
bool isResultInitialized = false;
int stackCount = mCompiler->mOptions.mAllocStackCount;
if (typeInstance->mTypeOptionsIdx != -1)
{
auto typeOptions = mSystem->GetTypeOptions(typeInstance->mTypeOptionsIdx);
stackCount = BfTypeOptions::Apply(stackCount, typeOptions->mAllocStackTraceDepth);
}
if (!sizeValue)
sizeValue = GetConstValue(typeInstance->mInstSize);
if (appendSizeValue)
sizeValue = mBfIRBuilder->CreateAdd(sizeValue, appendSizeValue);
BfIRValue origSizeValue = sizeValue;
bool isAllocEx = hasCustomAllocator && (stackCount > 1);
if (isAllocEx)
{
auto prepareStackTraceMethod = GetInternalMethod("Dbg_PrepareStackTrace");
if (prepareStackTraceMethod)
{
SizedArray<BfIRValue, 2> irArgs;
irArgs.Add(sizeValue);
irArgs.Add(GetConstValue(stackCount));
auto addlBytes = mBfIRBuilder->CreateCall(prepareStackTraceMethod.mFunc, irArgs);
sizeValue = mBfIRBuilder->CreateAdd(sizeValue, addlBytes);
}
}
if (allocTarget.mCustomAllocator)
{
auto customAlloc = allocTarget.mCustomAllocator.mType->ToTypeInstance();
// Use AllocObject if we have it, otherwise we just use AllocBytes further down
if ((customAlloc != NULL) && (GetRawMethodByName(customAlloc, "AllocObject", -1, true, true) != NULL))
{
auto classVDataType = ResolveTypeDef(mCompiler->mClassVDataTypeDef);
mBfIRBuilder->PopulateType(classVDataType);
auto typeInstType = ResolveTypeDef(mCompiler->mReflectTypeInstanceTypeDef)->ToTypeInstance();
mBfIRBuilder->PopulateType(typeInstType);
auto typePtrPtr = mBfIRBuilder->CreateInBoundsGEP(vDataRef, 0, 1); // mType
auto typePtr = mBfIRBuilder->CreateLoad(typePtrPtr);
auto typeInstPtr = mBfIRBuilder->CreateBitCast(typePtr, mBfIRBuilder->MapTypeInstPtr(typeInstType));
BfTypedValueExpression typedValueExpr;
typedValueExpr.Init(BfTypedValue(typeInstPtr, typeInstType));
typedValueExpr.mRefNode = allocTarget.mRefNode;
BfExprEvaluator exprEvaluator(this);
SizedArray<BfExpression*, 2> argExprs;
argExprs.push_back(&typedValueExpr);
BfTypedValueExpression sizeValueExpr;
sizeValueExpr.Init(BfTypedValue(sizeValue, GetPrimitiveType(BfTypeCode_IntPtr)));
sizeValueExpr.mRefNode = allocTarget.mRefNode;
argExprs.push_back(&sizeValueExpr);
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
BfResolvedArgs argValues(&sizedArgExprs);
exprEvaluator.ResolveArgValues(argValues);
exprEvaluator.mNoBind = true;
BfTypedValue allocResult = exprEvaluator.MatchMethod(allocTarget.mRefNode, NULL, allocTarget.mCustomAllocator, false, false, "AllocObject", argValues, NULL);
if (allocResult)
{
if ((allocResult.mType->IsVoidPtr()) || (allocResult.mType == mContext->mBfObjectType))
result = mBfIRBuilder->CreateBitCast(allocResult.mValue, mBfIRBuilder->MapType(typeInstance));
else
result = CastToValue(allocTarget.mRefNode, allocResult, typeInstance, BfCastFlags_Explicit);
isResultInitialized = true;
}
}
}
bool wasAllocated = false;
2019-08-23 11:56:54 -07:00
if (!result)
{
if (hasCustomAllocator)
result = AllocBytes(allocTarget.mRefNode, allocTarget, typeInstance, sizeValue, GetConstValue(typeInstance->mInstAlign), (BfAllocFlags)(BfAllocFlags_ZeroMemory | BfAllocFlags_NoDefaultToMalloc));
else if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mCompiler->mOptions.mDebugAlloc))
{
SizedArray<BfIRValue, 4> llvmArgs;
llvmArgs.push_back(sizeValue);
auto irFunc = GetBuiltInFunc(BfBuiltInFuncType_Malloc);
result = mBfIRBuilder->CreateCall(irFunc, llvmArgs);
result = mBfIRBuilder->CreateBitCast(result, mBfIRBuilder->MapTypeInstPtr(typeInstance));
wasAllocated = true;
}
2019-08-23 11:56:54 -07:00
}
if (result)
{
if (mCompiler->mOptions.mObjectHasDebugFlags)
{
auto objectPtr = mBfIRBuilder->CreateBitCast(result, mBfIRBuilder->MapTypeInstPtr(mContext->mBfObjectType));
SizedArray<BfIRValue, 4> llvmArgs;
llvmArgs.push_back(objectPtr);
llvmArgs.push_back(origSizeValue);
llvmArgs.push_back(vDataRef);
auto objectCreatedMethod = GetInternalMethod(isAllocEx ?
(isResultInitialized ? "Dbg_ObjectCreatedEx" : "Dbg_ObjectAllocatedEx") :
(isResultInitialized ? "Dbg_ObjectCreated" : "Dbg_ObjectAllocated"));
mBfIRBuilder->CreateCall(objectCreatedMethod.mFunc, llvmArgs);
if (wasAllocated)
{
auto byteType = GetPrimitiveType(BfTypeCode_UInt8);
auto bytePtrType = CreatePointerType(byteType);
auto flagsPtr = mBfIRBuilder->CreateBitCast(result, mBfIRBuilder->MapType(bytePtrType));
auto flagsVal = mBfIRBuilder->CreateLoad(flagsPtr);
auto modifiedFlagsVal = mBfIRBuilder->CreateOr(flagsVal, mBfIRBuilder->CreateConst(BfTypeCode_UInt8, /*BF_OBJECTFLAG_ALLOCATED*/4));
mBfIRBuilder->CreateStore(modifiedFlagsVal, flagsPtr);
}
2019-08-23 11:56:54 -07:00
}
else
{
auto ptrType = mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr);
auto vDataPtr = mBfIRBuilder->CreateBitCast(vDataRef, ptrType);
auto vDataMemberPtr = mBfIRBuilder->CreateBitCast(result, mBfIRBuilder->GetPointerTo(ptrType));
mBfIRBuilder->CreateStore(vDataPtr, vDataMemberPtr);
}
}
else
{
if ((mBfIRBuilder->mIgnoreWrites) || (mCompiler->mIsResolveOnly))
return GetDefaultValue(typeInstance);
auto classVDataType = ResolveTypeDef(mCompiler->mClassVDataTypeDef);
auto vData = mBfIRBuilder->CreateBitCast(vDataRef, mBfIRBuilder->MapTypeInstPtr(classVDataType->ToTypeInstance()));
if (mCompiler->mOptions.mObjectHasDebugFlags)
{
SizedArray<BfIRValue, 4> llvmArgs;
llvmArgs.push_back(vData);
llvmArgs.push_back(sizeValue);
llvmArgs.push_back(GetConstValue(typeInstance->mAlign));
llvmArgs.push_back(GetConstValue(stackCount));
auto moduleMethodInstance = GetInternalMethod("Dbg_ObjectAlloc", 4);
BfIRValue objectVal = mBfIRBuilder->CreateCall(moduleMethodInstance.mFunc, llvmArgs);
result = mBfIRBuilder->CreateBitCast(objectVal, mBfIRBuilder->MapType(typeInstance));
}
else
{
SizedArray<BfIRValue, 4> llvmArgs;
llvmArgs.push_back(sizeValue);
2019-08-23 11:56:54 -07:00
BfIRFunction irFunc;
if (mCompiler->mOptions.mDebugAlloc)
{
auto moduleMethodInstance = GetInternalMethod("Dbg_RawObjectAlloc", 1);
irFunc = moduleMethodInstance.mFunc;
}
if (!irFunc)
irFunc = GetBuiltInFunc(BfBuiltInFuncType_Malloc);
BfIRValue objectVal = mBfIRBuilder->CreateCall(irFunc, llvmArgs);
auto objResult = mBfIRBuilder->CreateBitCast(objectVal, mBfIRBuilder->MapType(mContext->mBfObjectType, BfIRPopulateType_Full));
auto vdataPtr = mBfIRBuilder->CreateInBoundsGEP(objResult, 0, 0);
mBfIRBuilder->CreateStore(vData, vdataPtr);
result = mBfIRBuilder->CreateBitCast(objectVal, mBfIRBuilder->MapType(typeInstance));
}
}
if ((zeroMemory) && (arraySize))
{
BfArrayType* arrayType = CreateArrayType(type, arrayDim);
typeInstance = arrayType;
auto firstElementField = &arrayType->mFieldInstances.back();
int arrayClassSize = firstElementField->mDataOffset;
BfIRValue elementDataSize;
bool skipZero = false;
if (arraySize.IsConst())
{
BfIRValue arraySizeMinusOne = mBfIRBuilder->CreateSub(arraySize, GetConstValue(1));
elementDataSize = mBfIRBuilder->CreateMul(GetConstValue(type->GetStride()), arraySizeMinusOne);
elementDataSize = mBfIRBuilder->CreateAdd(elementDataSize, GetConstValue(type->mSize));
auto constVal = mBfIRBuilder->GetConstant(elementDataSize);
if (constVal->mInt64 <= 0)
skipZero = true;
}
else
{
// For non const, don't use an unpadded last element - saves us checking against negative numbers going into memset for the zero-length case
elementDataSize = mBfIRBuilder->CreateMul(GetConstValue(type->GetStride()), arraySize);
}
if (!skipZero)
{
auto i8Type = GetPrimitiveType(BfTypeCode_Int8);
auto ptrType = CreatePointerType(i8Type);
auto ptrVal = mBfIRBuilder->CreateBitCast(result, mBfIRBuilder->MapType(ptrType));
ptrVal = mBfIRBuilder->CreateInBoundsGEP(ptrVal, arrayClassSize);
mBfIRBuilder->CreateMemSet(ptrVal, GetConstValue8(0), elementDataSize, type->mAlign);
appendSizeValue = elementDataSize;
}
}
return result;
}
else
{
if (!sizeValue)
sizeValue = GetConstValue(allocSize);
return AllocBytes(allocTarget.mRefNode, allocTarget, type, sizeValue, GetConstValue(allocAlign), zeroMemory ? BfAllocFlags_ZeroMemory : BfAllocFlags_None);
}
}
void BfModule::ValidateAllocation(BfType* type, BfAstNode* refNode)
{
if ((type == NULL) || (refNode == NULL))
return;
auto typeInstance = type->ToTypeInstance();
if ((typeInstance != NULL) && (typeInstance->mTypeDef->mIsOpaque))
Fail(StrFormat("Unable to allocate opaque type '%s'", TypeToString(type).c_str()), refNode);
}
void BfModule::EmitAlign(BfIRValue& appendCurIdx, int align)
{
appendCurIdx = mBfIRBuilder->CreateAdd(appendCurIdx, GetConstValue(align - 1));
appendCurIdx = mBfIRBuilder->CreateAnd(appendCurIdx, GetConstValue(~(align - 1)));
}
void BfModule::EmitAppendAlign(int align, int sizeMultiple)
{
if (sizeMultiple == 0)
sizeMultiple = align;
if ((mCurMethodState->mCurAppendAlign != 0) && (mCurMethodState->mCurAppendAlign % align != 0))
{
if (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Ctor)
{
auto localVar = mCurMethodState->GetRootMethodState()->mLocals[1];
BF_ASSERT(localVar->mName == "appendIdx");
auto appendIdxVal = BfTypedValue(localVar->mValue, localVar->mResolvedType, true);
BfIRValue appendCurIdx = mBfIRBuilder->CreateLoad(appendIdxVal.mValue);
if (align > 1)
{
appendCurIdx = mBfIRBuilder->CreateAdd(appendCurIdx, GetConstValue(align - 1));
appendCurIdx = mBfIRBuilder->CreateAnd(appendCurIdx, GetConstValue(~(align - 1)));
}
mBfIRBuilder->CreateStore(appendCurIdx, appendIdxVal.mValue);
}
else
{
BF_ASSERT(mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend);
BfIRValue appendCurIdxPtr = mCurMethodState->mRetVal.mValue;
BfIRValue appendCurIdx = mBfIRBuilder->CreateLoad(appendCurIdxPtr);
appendCurIdx = mBfIRBuilder->CreateAdd(appendCurIdx, GetConstValue(align - 1));
appendCurIdx = mBfIRBuilder->CreateAnd(appendCurIdx, GetConstValue(~(align - 1)));
mBfIRBuilder->CreateStore(appendCurIdx, appendCurIdxPtr);
}
}
if (mCurMethodState->mCurAppendAlign == 0)
mCurMethodState->mCurAppendAlign = sizeMultiple;
else
{
if (sizeMultiple % align == 0)
mCurMethodState->mCurAppendAlign = align;
else
mCurMethodState->mCurAppendAlign = sizeMultiple % align;
}
}
BfIRValue BfModule::AppendAllocFromType(BfType* type, BfIRValue appendSizeValue, int appendAllocAlign, BfIRValue arraySize, int arrayDim, bool isRawArrayAlloc, bool zeroMemory)
{
auto localVar = mCurMethodState->GetRootMethodState()->mLocals[1];
BF_ASSERT(localVar->mName == "appendIdx");
BfTypedValue appendIdxVal(localVar->mValue, localVar->mResolvedType, true);
BfIRValue retValue;
BfTypeInstance* retTypeInstance = NULL;
auto intPtrType = GetPrimitiveType(BfTypeCode_IntPtr);
auto voidPtrType = GetPrimitiveType(BfTypeCode_NullPtr);
if (arraySize)
{
if (isRawArrayAlloc)
{
EmitAppendAlign(type->mAlign);
BfPointerType* ptrType = CreatePointerType(type);
BfIRValue sizeValue = mBfIRBuilder->CreateMul(GetConstValue(type->mSize), arraySize);
auto curIdxVal = mBfIRBuilder->CreateLoad(appendIdxVal.mValue);
if (mSystem->mPtrSize != 4)
sizeValue = mBfIRBuilder->CreateNumericCast(sizeValue, true, (intPtrType->mSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64);
auto newIdxVal = mBfIRBuilder->CreateAdd(curIdxVal, sizeValue);
mBfIRBuilder->CreateStore(newIdxVal, appendIdxVal.mValue);
if (zeroMemory)
{
auto ptr = mBfIRBuilder->CreateIntToPtr(curIdxVal, mBfIRBuilder->MapType(voidPtrType));
mBfIRBuilder->CreateMemSet(ptr, GetConstValue8(0), sizeValue, type->mAlign);
}
return mBfIRBuilder->CreateIntToPtr(curIdxVal, mBfIRBuilder->MapType(ptrType));
}
else
{
BfArrayType* arrayType = CreateArrayType(type, arrayDim);
EmitAppendAlign(arrayType->mAlign, type->mSize);
auto firstElementField = &arrayType->mFieldInstances.back();
int arrayClassSize = arrayType->mInstSize - firstElementField->mDataSize;
BfIRValue sizeValue = GetConstValue(arrayClassSize);
BfIRValue elementDataSize = mBfIRBuilder->CreateMul(GetConstValue(type->mSize), arraySize);
sizeValue = mBfIRBuilder->CreateAdd(sizeValue, elementDataSize);
auto curIdxVal = mBfIRBuilder->CreateLoad(appendIdxVal.mValue);
if (mSystem->mPtrSize != 4)
sizeValue = mBfIRBuilder->CreateNumericCast(sizeValue, true, (intPtrType->mSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64);
auto newIdxVal = mBfIRBuilder->CreateAdd(curIdxVal, sizeValue);
mBfIRBuilder->CreateStore(newIdxVal, appendIdxVal.mValue);
if (zeroMemory)
{
// Only zero out the actual elements
int dataOffset = arrayType->mFieldInstances.back().mDataOffset;
auto newOffset = mBfIRBuilder->CreateAdd(curIdxVal, GetConstValue(dataOffset));
auto newSize = mBfIRBuilder->CreateSub(sizeValue, GetConstValue(dataOffset));
auto ptr = mBfIRBuilder->CreateIntToPtr(newOffset, mBfIRBuilder->MapType(voidPtrType));
mBfIRBuilder->CreateMemSet(ptr, GetConstValue8(0), newSize, type->mAlign);
}
retTypeInstance = arrayType;
retValue = mBfIRBuilder->CreateIntToPtr(curIdxVal, mBfIRBuilder->MapType(arrayType));
}
}
else
{
auto typeInst = type->ToTypeInstance();
BfIRValue sizeValue;
BfIRType toType;
if (typeInst != NULL)
{
EmitAppendAlign(typeInst->mInstAlign, typeInst->mInstSize);
sizeValue = GetConstValue(typeInst->mInstSize);
toType = mBfIRBuilder->MapTypeInstPtr(typeInst);
}
else
{
EmitAppendAlign(type->mAlign, type->mSize);
sizeValue = GetConstValue(type->mSize);
auto toPtrType = CreatePointerType(type);
toType = mBfIRBuilder->MapType(toPtrType);
}
auto curIdxVal = mBfIRBuilder->CreateLoad(appendIdxVal.mValue);
auto newIdxVal = mBfIRBuilder->CreateAdd(curIdxVal, sizeValue);
mBfIRBuilder->CreateStore(newIdxVal, appendIdxVal.mValue);
retTypeInstance = typeInst;
retValue = mBfIRBuilder->CreateIntToPtr(curIdxVal, toType);
}
if ((retTypeInstance != NULL) && (retTypeInstance->IsObject()))
{
mBfIRBuilder->PopulateType(retTypeInstance);
auto int8Type = mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8);
auto ptrType = mBfIRBuilder->GetPointerTo(int8Type);
auto ptrPtrType = mBfIRBuilder->GetPointerTo(ptrType);
auto curThis = GetThis();
BfIRValue newFlags;
if (mCompiler->mOptions.mObjectHasDebugFlags)
{
auto thisFlagsPtr = mBfIRBuilder->CreateBitCast(curThis.mValue, ptrType);
auto thisFlags = mBfIRBuilder->CreateLoad(thisFlagsPtr);
auto maskedThisFlags = mBfIRBuilder->CreateAnd(thisFlags, GetConstValue8(BfObjectFlag_StackAlloc)); // Take 'stack' flag from 'this'
newFlags = mBfIRBuilder->CreateOr(maskedThisFlags, GetConstValue8(BfObjectFlag_AppendAlloc));
}
auto vObjectAddr = mBfIRBuilder->CreateInBoundsGEP(retValue, 0, 0);
auto vDataRef = CreateClassVDataGlobal(retTypeInstance);
auto destAddr = mBfIRBuilder->CreateBitCast(vObjectAddr, ptrPtrType);
auto srcVal = mBfIRBuilder->CreateBitCast(vDataRef, ptrType);
mBfIRBuilder->CreateStore(srcVal, destAddr);
if (mCompiler->mOptions.mObjectHasDebugFlags)
{
auto flagsPtr = mBfIRBuilder->CreateBitCast(destAddr, ptrType);
mBfIRBuilder->CreateStore(newFlags, flagsPtr);
}
}
if (appendSizeValue)
{
EmitAppendAlign(appendAllocAlign);
auto curIdxVal = mBfIRBuilder->CreateLoad(appendIdxVal.mValue);
auto newIdxVal = mBfIRBuilder->CreateAdd(curIdxVal, appendSizeValue);
mBfIRBuilder->CreateStore(newIdxVal, appendIdxVal.mValue);
}
return retValue;
}
bool BfModule::IsOptimized()
{
if (mProject == NULL)
return false;
int optLevel = GetModuleOptions().mOptLevel;
return (optLevel >= BfOptLevel_O1) && (optLevel <= BfOptLevel_O3);
}
bool BfModule::IsTargetingBeefBackend()
{
if (mProject == NULL)
return false;
return GetModuleOptions().mOptLevel == BfOptLevel_OgPlus;
}
bool BfModule::WantsLifetimes()
{
if (mProject == NULL)
return false;
return GetModuleOptions().mOptLevel == BfOptLevel_OgPlus;
2019-08-23 11:56:54 -07:00
}
bool BfModule::HasCompiledOutput()
{
return (!mSystem->mIsResolveOnly) && (mIsReified);
}
// We will skip the object access check for any occurances of this value
void BfModule::SkipObjectAccessCheck(BfTypedValue typedVal)
{
if ((mBfIRBuilder->mIgnoreWrites) || (!typedVal.mType->IsObjectOrInterface()) || (mCurMethodState == NULL) || (mCurMethodState->mIgnoreObjectAccessCheck))
return;
if (!mCompiler->mOptions.mObjectHasDebugFlags)
return;
if ((typedVal.mValue.mFlags & BfIRValueFlags_Value) == 0)
return;
mCurMethodState->mSkipObjectAccessChecks.Add(typedVal.mValue.mId);
}
void BfModule::EmitObjectAccessCheck(BfTypedValue typedVal)
{
if ((mBfIRBuilder->mIgnoreWrites) || (!typedVal.mType->IsObjectOrInterface()) || (mCurMethodState == NULL) || (mCurMethodState->mIgnoreObjectAccessCheck))
return;
if (!mCompiler->mOptions.mObjectHasDebugFlags)
return;
bool emitObjectAccessCheck = mCompiler->mOptions.mEmitObjectAccessCheck;
auto typeOptions = GetTypeOptions();
if (typeOptions != NULL)
emitObjectAccessCheck = BfTypeOptions::Apply(emitObjectAccessCheck, typeOptions->mEmitObjectAccessCheck);
if (!emitObjectAccessCheck)
return;
if ((typedVal.mValue.mFlags & BfIRValueFlags_Value) != 0)
{
if (mCurMethodState->mSkipObjectAccessChecks.Contains(typedVal.mValue.mId))
return;
}
if (typedVal.IsAddr())
typedVal = LoadValue(typedVal);
mBfIRBuilder->CreateObjectAccessCheck(typedVal.mValue, !IsOptimized());
}
void BfModule::EmitEnsureInstructionAt()
{
if (mProject == NULL)
return;
if ((mBfIRBuilder->mIgnoreWrites) || (!mHasFullDebugInfo) || (IsOptimized()) || (mCompiler->mOptions.mOmitDebugHelpers))
2019-08-23 11:56:54 -07:00
return;
mBfIRBuilder->CreateEnsureInstructionAt();
}
void BfModule::EmitDynamicCastCheck(const BfTypedValue& targetValue, BfType* targetType, BfIRBlock trueBlock, BfIRBlock falseBlock, bool nullSucceeds)
{
if (mBfIRBuilder->mIgnoreWrites)
return; // Nothing needed here
auto irb = mBfIRBuilder;
auto checkBB = irb->CreateBlock("as.check");
auto isNull = irb->CreateIsNull(targetValue.mValue);
mBfIRBuilder->CreateCondBr(isNull, nullSucceeds ? trueBlock : falseBlock, checkBB);
auto intType = GetPrimitiveType(BfTypeCode_IntPtr);
auto intPtrType = CreatePointerType(intType);
auto intPtrPtrType = CreatePointerType(intPtrType);
auto int32Type = GetPrimitiveType(BfTypeCode_Int32);
auto int32PtrType = CreatePointerType(int32Type);
auto typeTypeInstance = ResolveTypeDef(mCompiler->mReflectTypeInstanceTypeDef)->ToTypeInstance();
if (mCompiler->mOptions.mAllowHotSwapping)
{
BfExprEvaluator exprEvaluator(this);
AddBasicBlock(checkBB);
auto objectParam = mBfIRBuilder->CreateBitCast(targetValue.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType));
auto moduleMethodInstance = GetMethodByName(mContext->mBfObjectType, targetType->IsInterface() ? "DynamicCastToInterface" : "DynamicCastToTypeId");
SizedArray<BfIRValue, 4> irArgs;
irArgs.push_back(objectParam);
irArgs.push_back(GetConstValue32(targetType->mTypeId));
auto callResult = exprEvaluator.CreateCall(moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, false, irArgs);
auto resultType = ResolveTypeDef(mSystem->mTypeBool);
auto cmpResult = mBfIRBuilder->CreateCmpNE(callResult.mValue, GetDefaultValue(callResult.mType));
irb->CreateCondBr(cmpResult, trueBlock, falseBlock);
}
else
{
AddBasicBlock(checkBB);
BfIRValue vDataPtr = irb->CreateBitCast(targetValue.mValue, irb->MapType(intPtrType));
vDataPtr = irb->CreateLoad(vDataPtr);
if (mCompiler->mOptions.mObjectHasDebugFlags)
vDataPtr = irb->CreateAnd(vDataPtr, irb->CreateConst(BfTypeCode_IntPtr, (uint64)~0xFFULL));
if (targetType->IsInterface())
{
auto targetTypeInst = targetType->ToTypeInstance();
AddDependency(targetType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference);
// Skip past mType, but since a 'GetDynCastVDataCount()' data is included in the sSlotOfs, we need to remove that
//int inheritanceIdOfs = mSystem->mPtrSize - (mCompiler->GetDynCastVDataCount())*4;
//vDataPtr = irb->CreateAdd(vDataPtr, irb->CreateConst(BfTypeCode_IntPtr, inheritanceIdOfs));
vDataPtr = irb->CreateAdd(vDataPtr, irb->GetConfigConst(BfIRConfigConst_DynSlotOfs, BfTypeCode_IntPtr));
BfIRValue slotOfs = GetInterfaceSlotNum(targetType->ToTypeInstance());
BfIRValue slotByteOfs = irb->CreateMul(slotOfs, irb->CreateConst(BfTypeCode_Int32, 4));
slotByteOfs = irb->CreateNumericCast(slotByteOfs, false, BfTypeCode_IntPtr);
vDataPtr = irb->CreateAdd(vDataPtr, slotByteOfs);
vDataPtr = irb->CreateIntToPtr(vDataPtr, irb->MapType(int32PtrType));
BfIRValue typeId = irb->CreateLoad(vDataPtr);
BfIRValue cmpResult = irb->CreateCmpEQ(typeId, irb->CreateConst(BfTypeCode_Int32, targetType->mTypeId));
irb->CreateCondBr(cmpResult, trueBlock, falseBlock);
}
else
{
if (!targetType->IsTypeInstance())
targetType = GetWrappedStructType(targetType);
AddDependency(targetType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference);
int inheritanceIdOfs = mSystem->mPtrSize;
vDataPtr = irb->CreateAdd(vDataPtr, irb->CreateConst(BfTypeCode_IntPtr, inheritanceIdOfs));
vDataPtr = irb->CreateIntToPtr(vDataPtr, irb->MapType(int32PtrType));
BfIRValue objInheritanceId = irb->CreateLoad(vDataPtr);
BfIRValue typeDataRef = CreateTypeDataRef(targetType);
BfIRValue typeInstDataRef = irb->CreateBitCast(typeDataRef, irb->MapType(typeTypeInstance, BfIRPopulateType_Full));
auto fieldInst = &typeTypeInstance->mFieldInstances[9];
BF_ASSERT(fieldInst->GetFieldDef()->mName == "mInheritanceId");
BfIRValue gepValue = irb->CreateInBoundsGEP(typeInstDataRef, 0, fieldInst->mDataIdx);
BfIRValue wantInheritanceId = irb->CreateLoad(gepValue);
fieldInst = &typeTypeInstance->mFieldInstances[10];
BF_ASSERT(fieldInst->GetFieldDef()->mName == "mInheritanceCount");
gepValue = irb->CreateInBoundsGEP(typeInstDataRef, 0, fieldInst->mDataIdx);
BfIRValue inheritanceCount = irb->CreateLoad(gepValue);
BfIRValue idDiff = irb->CreateSub(objInheritanceId, wantInheritanceId);
BfIRValue cmpResult = irb->CreateCmpLTE(idDiff, inheritanceCount, false);
irb->CreateCondBr(cmpResult, trueBlock, falseBlock);
}
}
}
void BfModule::EmitDynamicCastCheck(BfTypedValue typedVal, BfType* type, bool allowNull)
{
if (mBfIRBuilder->mIgnoreWrites)
return;
2019-08-23 11:56:54 -07:00
bool emitDynamicCastCheck = mCompiler->mOptions.mEmitDynamicCastCheck;
auto typeOptions = GetTypeOptions();
if (typeOptions != NULL)
emitDynamicCastCheck = BfTypeOptions::Apply(emitDynamicCastCheck, typeOptions->mEmitDynamicCastCheck);
if (emitDynamicCastCheck)
{
int wantTypeId = 0;
if (!type->IsGenericParam())
wantTypeId = type->mTypeId;
BfIRBlock failBlock = mBfIRBuilder->CreateBlock("dynFail");
BfIRBlock endBlock = mBfIRBuilder->CreateBlock("dynEnd");
EmitDynamicCastCheck(typedVal, type, endBlock, failBlock, allowNull ? true : false);
AddBasicBlock(failBlock);
SizedArray<BfIRValue, 8> llvmArgs;
auto bitAddr = mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType));
llvmArgs.push_back(bitAddr);
llvmArgs.push_back(GetConstValue32(wantTypeId));
auto objDynCheck = GetInternalMethod("ObjectDynCheckFailed");
BF_ASSERT(objDynCheck);
if (objDynCheck)
{
auto callInst = mBfIRBuilder->CreateCall(objDynCheck.mFunc, llvmArgs);
}
mBfIRBuilder->CreateBr(endBlock);
AddBasicBlock(endBlock);
}
}
BfTypedValue BfModule::BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType, const BfAllocTarget& allocTarget, bool callDtor)
{
if (mBfIRBuilder->mIgnoreWrites)
{
if (toType == mContext->mBfObjectType)
return BfTypedValue(mBfIRBuilder->GetFakeVal(), toType);
if (toType->IsBoxed())
{
BfBoxedType* boxedType = (BfBoxedType*)toType;
2020-01-30 10:25:16 -08:00
if (boxedType->mElementType->IsGenericParam())
{
if (typedVal.mType == boxedType->mElementType)
return BfTypedValue(mBfIRBuilder->GetFakeVal(), toType);
else
return BfTypedValue();
}
}
}
2019-08-23 11:56:54 -07:00
BP_ZONE("BoxValue");
BfTypeInstance* fromStructTypeInstance = typedVal.mType->ToTypeInstance();
if (typedVal.mType->IsNullable())
{
2019-08-23 11:56:54 -07:00
typedVal = MakeAddressable(typedVal);
auto innerType = typedVal.mType->GetUnderlyingType();
if (!innerType->IsValueType())
{
if (!mIgnoreErrors)
Fail("Only value types can be boxed", srcNode);
2019-08-23 11:56:54 -07:00
return BfTypedValue();
}
auto boxedType = CreateBoxedType(innerType);
auto resultType = toType;
if (resultType == NULL)
resultType = boxedType;
if (mBfIRBuilder->mIgnoreWrites)
return BfTypedValue(mBfIRBuilder->GetFakeVal(), resultType);
2019-08-23 11:56:54 -07:00
auto prevBB = mBfIRBuilder->GetInsertBlock();
auto boxBB = mBfIRBuilder->CreateBlock("boxedN.notNull");
auto endBB = mBfIRBuilder->CreateBlock("boxedN.end");
mBfIRBuilder->PopulateType(typedVal.mType);
auto hasValueAddr = mBfIRBuilder->CreateInBoundsGEP(typedVal.mValue, 0, innerType->IsValuelessType() ? 1 : 2); // has_value
auto hasValue = mBfIRBuilder->CreateLoad(hasValueAddr);
mBfIRBuilder->CreateCondBr(hasValue, boxBB, endBB);
2019-08-23 11:56:54 -07:00
AddDependency(boxedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->AddBlock(boxBB);
mBfIRBuilder->SetInsertPoint(boxBB);
BfScopeData newScope;
newScope.mOuterIsConditional = true;
mCurMethodState->AddScope(&newScope);
NewScopeState();
BfIRValue nullableValueAddr;
BfIRValue loadedVal;
if (innerType->IsValuelessType())
{
loadedVal = mBfIRBuilder->GetFakeVal();
}
else
{
nullableValueAddr = mBfIRBuilder->CreateInBoundsGEP(typedVal.mValue, 0, 1); // value
loadedVal = mBfIRBuilder->CreateLoad(nullableValueAddr);
}
auto boxedVal = BoxValue(srcNode, BfTypedValue(loadedVal, fromStructTypeInstance->GetUnderlyingType()), resultType, allocTarget, callDtor);
RestoreScopeState();
if (!boxedVal)
return BfTypedValue();
mBfIRBuilder->CreateBr(endBB);
auto boxBBEnd = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->AddBlock(endBB);
mBfIRBuilder->SetInsertPoint(endBB);
auto phi = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(resultType), 2);
mBfIRBuilder->AddPhiIncoming(phi, boxedVal.mValue, boxBBEnd);
mBfIRBuilder->AddPhiIncoming(phi, GetDefaultValue(resultType), prevBB);
return BfTypedValue(phi, resultType);
}
2019-10-09 16:16:01 -07:00
bool alreadyCheckedCast = false;
BfTypeInstance* toTypeInstance = NULL;
if (toType != NULL)
toTypeInstance = toType->ToTypeInstance();
bool isStructPtr = typedVal.mType->IsStructPtr();
2019-08-23 11:56:54 -07:00
if (fromStructTypeInstance == NULL)
2019-10-09 16:16:01 -07:00
{
auto primType = (BfPrimitiveType*)typedVal.mType;
if ((typedVal.mType->IsPointer()) && (toTypeInstance->IsInstanceOf(mCompiler->mIHashableTypeDef)))
{
// Can always do IHashable
alreadyCheckedCast = true;
}
if ((!typedVal.mType->IsPointer()) || (toTypeInstance == mContext->mBfObjectType))
fromStructTypeInstance = GetWrappedStructType(typedVal.mType);
2019-10-09 16:16:01 -07:00
if (isStructPtr)
{
if ((toTypeInstance != NULL) && (fromStructTypeInstance != NULL) && (TypeIsSubTypeOf(fromStructTypeInstance, toTypeInstance)))
2019-10-09 16:16:01 -07:00
alreadyCheckedCast = true;
fromStructTypeInstance = typedVal.mType->GetUnderlyingType()->ToTypeInstance();
}
if ((fromStructTypeInstance == NULL) && (alreadyCheckedCast))
fromStructTypeInstance = GetWrappedStructType(typedVal.mType);
2019-08-23 11:56:54 -07:00
}
if (fromStructTypeInstance == NULL)
2019-10-09 16:16:01 -07:00
return BfTypedValue();
2019-08-23 11:56:54 -07:00
// Need to box it
bool isBoxedType = (fromStructTypeInstance != NULL) && (toType->IsBoxed());
2019-10-09 16:16:01 -07:00
if ((toType == NULL) || (toType == mContext->mBfObjectType) || (isBoxedType) || (alreadyCheckedCast) || (TypeIsSubTypeOf(fromStructTypeInstance, toTypeInstance)))
{
if (mBfIRBuilder->mIgnoreWrites)
return BfTypedValue(mBfIRBuilder->GetFakeVal(), (toType != NULL) ? toType : CreateBoxedType(typedVal.mType));
auto boxedType = CreateBoxedType(typedVal.mType);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->PopulateType(boxedType);
AddDependency(boxedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields);
auto allocaInst = AllocFromType(boxedType, allocTarget, BfIRValue(), BfIRValue(), 0, callDtor ? BfAllocFlags_None : BfAllocFlags_NoDtorCall);
BfTypedValue boxedTypedValue(allocaInst, boxedType);
mBfIRBuilder->SetName(allocaInst, "boxed." + fromStructTypeInstance->mTypeDef->mName->ToString());
if (boxedType->IsUnspecializedType())
{
BF_ASSERT((srcNode == NULL) || (mCurMethodInstance->mIsUnspecialized));
2019-08-23 11:56:54 -07:00
}
else
{
PopulateType(fromStructTypeInstance);
if (!fromStructTypeInstance->IsValuelessType())
{
typedVal = LoadValue(typedVal);
auto valPtr = mBfIRBuilder->CreateInBoundsGEP(allocaInst, 0, 1);
2019-10-09 16:16:01 -07:00
if ((typedVal.mType != fromStructTypeInstance) && (!isStructPtr))
2019-08-23 11:56:54 -07:00
{
auto ptrType = CreatePointerType(typedVal.mType);
valPtr = mBfIRBuilder->CreateBitCast(valPtr, mBfIRBuilder->MapType(ptrType));
}
if (typedVal.IsSplat())
{
AggregateSplatIntoAddr(typedVal, valPtr);
}
else
mBfIRBuilder->CreateStore(typedVal.mValue, valPtr);
}
}
if (toType == NULL)
{
return BfTypedValue(allocaInst, boxedType);
}
else
{
auto castedValue = mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapType(toType));
return BfTypedValue(castedValue, toType);
}
}
return BfTypedValue();
}
bool BfModule::GetBasePropertyDef(BfPropertyDef*& propDef, BfTypeInstance*& typeInst)
{
BfTypeInstance* checkTypeInst = typeInst;
while (checkTypeInst != NULL)
{
for (auto checkProp : checkTypeInst->mTypeDef->mProperties)
{
if (checkProp->mName == propDef->mName)
{
auto checkPropDeclaration = BfNodeDynCast<BfPropertyDeclaration>(checkProp->mFieldDeclaration);
if ((checkPropDeclaration == NULL) || (checkPropDeclaration->mVirtualSpecifier == NULL) || (checkPropDeclaration->mVirtualSpecifier->GetToken() == BfToken_Virtual))
{
propDef = checkProp;
typeInst = checkTypeInst;
return true;
}
}
}
checkTypeInst = checkTypeInst->mBaseType;
}
return false;
}
BfMethodInstance* BfModule::GetRawMethodInstanceAtIdx(BfTypeInstance* typeInstance, int methodIdx, const char* assertName)
{
if (!typeInstance->mResolvingVarField)
{
if (typeInstance->IsIncomplete())
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
}
else
{
if (methodIdx >= (int)typeInstance->mMethodInstanceGroups.size())
{
AssertErrorState();
return NULL;
}
}
if (assertName != NULL)
{
BF_ASSERT(typeInstance->mTypeDef->mMethods[methodIdx]->mName == assertName);
}
auto& methodGroup = typeInstance->mMethodInstanceGroups[methodIdx];
if (methodGroup.mDefault == NULL)
{
if (!mCompiler->mIsResolveOnly)
{
BF_ASSERT((methodGroup.mOnDemandKind == BfMethodOnDemandKind_NoDecl_AwaitingReference) || (methodGroup.mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingDecl));
2019-08-23 11:56:54 -07:00
methodGroup.mOnDemandKind = BfMethodOnDemandKind_Decl_AwaitingDecl;
// Get it from the owning module so we don't create a reference prematurely...
auto declModule = typeInstance->mModule;
return declModule->GetMethodInstance(typeInstance, typeInstance->mTypeDef->mMethods[methodIdx], BfTypeVector(), (BfGetMethodInstanceFlags)(BfGetMethodInstanceFlag_UnspecializedPass | BfGetMethodInstanceFlag_Unreified)).mMethodInstance;
}
else
{
auto declModule = typeInstance->mModule;
return declModule->GetMethodInstance(typeInstance, typeInstance->mTypeDef->mMethods[methodIdx], BfTypeVector(), (BfGetMethodInstanceFlags)(BfGetMethodInstanceFlag_UnspecializedPass)).mMethodInstance;
}
}
auto methodInstance = typeInstance->mMethodInstanceGroups[methodIdx].mDefault;
//TODO: Why did we have this adding methods to the work list? This should only happen if we actually attempt to USE the method, which should
// be from a call to the NON-raw version
// if (!methodInstance->mMethodInstanceGroup->IsImplemented())
// {
// methodInstance->mIsReified = mIsReified;
// if (methodInstance->mMethodProcessRequest == NULL)
// typeInstance->mModule->AddMethodToWorkList(methodInstance);
// }
return methodInstance;
}
BfMethodInstance* BfModule::GetRawMethodInstance(BfTypeInstance* typeInstance, BfMethodDef* methodDef)
{
if (methodDef->mIsLocalMethod)
{
return GetMethodInstance(typeInstance, methodDef, BfTypeVector()).mMethodInstance;
}
return GetRawMethodInstanceAtIdx(typeInstance, methodDef->mIdx);
}
BfMethodInstance* BfModule::GetRawMethodByName(BfTypeInstance* typeInstance, const StringImpl& methodName, int paramCount, bool checkBase, bool allowMixin)
{
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
while (typeInstance != NULL)
{
typeInstance->mTypeDef->PopulateMemberSets();
BfMemberSetEntry* entry = NULL;
BfMethodDef* methodDef = NULL;
if (typeInstance->mTypeDef->mMethodSet.TryGetWith(methodName, &entry))
methodDef = (BfMethodDef*)entry->mMemberDef;
while (methodDef != NULL)
{
if (((allowMixin) || (methodDef->mMethodType != BfMethodType_Mixin)) &&
(methodDef->mGenericParams.size() == 0) &&
((paramCount == -1) || (paramCount == (int)methodDef->mParams.size())))
return GetRawMethodInstance(typeInstance, methodDef);
methodDef = methodDef->mNextWithSameName;
}
if (!checkBase)
break;
typeInstance = typeInstance->mBaseType;
}
return NULL;
}
BfMethodInstance* BfModule::GetUnspecializedMethodInstance(BfMethodInstance* methodInstance)
{
if ((methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mMethodGenericArguments.size() != 0))
methodInstance = methodInstance->mMethodInstanceGroup->mDefault;
2019-08-23 11:56:54 -07:00
auto owner = methodInstance->mMethodInstanceGroup->mOwner;
if (!owner->IsGenericTypeInstance())
return methodInstance;
BF_ASSERT(!owner->IsDelegateFromTypeRef());
BF_ASSERT(!owner->IsTuple());
auto genericType = (BfTypeInstance*)owner;
2019-08-23 11:56:54 -07:00
if (genericType->IsUnspecializedType())
return methodInstance;
auto unspecializedType = ResolveTypeDef(genericType->mTypeDef);
if (unspecializedType == NULL)
{
AssertErrorState();
return methodInstance;
}
if (unspecializedType == NULL)
return methodInstance;
auto unspecializedTypeInst = unspecializedType->ToTypeInstance();
return GetRawMethodInstanceAtIdx(unspecializedTypeInst, methodInstance->mMethodDef->mIdx);
}
int BfModule::GetGenericParamAndReturnCount(BfMethodInstance* methodInstance)
{
int genericCount = 0;
auto unspecializedMethodInstance = GetUnspecializedMethodInstance(methodInstance);
for (int paramIdx = 0; paramIdx < unspecializedMethodInstance->GetParamCount(); paramIdx++)
{
auto param = unspecializedMethodInstance->GetParamType(paramIdx);
if (param->IsGenericParam())
genericCount++;
}
if (unspecializedMethodInstance->mReturnType->IsGenericParam())
genericCount++;
return genericCount;
}
BfModule* BfModule::GetSpecializedMethodModule(const SizedArrayImpl<BfProject*>& projectList)
2019-08-23 11:56:54 -07:00
{
BF_ASSERT(!mIsScratchModule);
BF_ASSERT(mIsReified);
BfModule* mainModule = this;
if (mParentModule != NULL)
mainModule = mParentModule;
BfModule* specModule = NULL;
BfModule** specModulePtr = NULL;
if (mainModule->mSpecializedMethodModules.TryGetValueWith(projectList, &specModulePtr))
2019-08-23 11:56:54 -07:00
{
return *specModulePtr;
}
else
{
String specModuleName = mModuleName;
for (auto bfProject : projectList)
specModuleName += StrFormat("@%s", bfProject->mName.c_str());
specModule = new BfModule(mContext, specModuleName);
specModule->mProject = mainModule->mProject;
specModule->mParentModule = mainModule;
specModule->mIsSpecializedMethodModuleRoot = true;
specModule->Init();
Array<BfProject*> projList;
for (auto project : projectList)
projList.Add(project);
mainModule->mSpecializedMethodModules[projList] = specModule;
2019-08-23 11:56:54 -07:00
}
return specModule;
}
BfIRValue BfModule::CreateFunctionFrom(BfMethodInstance* methodInstance, bool tryExisting, bool isInlined)
{
if (mCompiler->IsSkippingExtraResolveChecks())
return BfIRValue();
if (methodInstance->mMethodInstanceGroup->mOwner->IsInterface())
{
//BF_ASSERT(!methodInstance->mIRFunction);
return BfIRValue();
}
auto methodDef = methodInstance->mMethodDef;
StringT<128> methodName;
BfMangler::Mangle(methodName, mCompiler->GetMangleKind(), methodInstance);
if (isInlined != methodInstance->mAlwaysInline)
{
if (isInlined)
methodName += "__INLINE";
else
methodName += "__NOINLINE";
}
if (tryExisting)
{
auto func = mBfIRBuilder->GetFunction(methodName);
if (func)
return func;
}
if (auto methodDeclaration = methodDef->GetMethodDeclaration())
{
if (methodDeclaration->mExternSpecifier != NULL)
{
auto intrinsic = GetIntrinsic(methodInstance);
if (intrinsic)
return intrinsic;
// If we have multiple entries with the same name, they could have different arguments and will generate other errors...
/*auto func = mBfIRBuilder->GetFunction(methodName);
if (func)
return func;*/
}
}
if (methodInstance->GetImportCallKind() != BfImportCallKind_None)
{
return CreateDllImportGlobalVar(methodInstance, false);
}
BfIRType returnType;
SizedArray<BfIRType, 8> paramTypes;
methodInstance->GetIRFunctionInfo(this, returnType, paramTypes);
auto funcType = mBfIRBuilder->CreateFunctionType(returnType, paramTypes, methodInstance->IsVarArgs());
2019-08-23 11:56:54 -07:00
auto func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_External, methodName);
auto callingConv = GetIRCallingConvention(methodInstance);
2019-08-23 11:56:54 -07:00
if (callingConv != BfIRCallingConv_CDecl)
mBfIRBuilder->SetFuncCallingConv(func, callingConv);
SetupIRMethod(methodInstance, func, isInlined);
// auto srcModule = methodInstance->GetOwner()->GetModule();
// if ((srcModule != NULL) && (srcModule->mProject != mProject))
// {
// if (srcModule->mProject->mTargetType == BfTargetType_BeefDynLib)
// {
// mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_DllImport);
// }
// }
2019-08-23 11:56:54 -07:00
return func;
}
BfModuleMethodInstance BfModule::GetMethodInstanceAtIdx(BfTypeInstance* typeInstance, int methodIdx, const char* assertName, BfGetMethodInstanceFlags flags)
2019-08-23 11:56:54 -07:00
{
if (assertName != NULL)
{
BF_ASSERT(typeInstance->mTypeDef->mMethods[methodIdx]->mName == assertName);
}
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
auto methodInstance = typeInstance->mMethodInstanceGroups[methodIdx].mDefault;
if ((methodInstance != NULL) && (mIsReified) && (!methodInstance->mIsReified))
{
// Can't use it, not reified
methodInstance = NULL;
}
if ((methodInstance != NULL) && (mBfIRBuilder != NULL) && (mBfIRBuilder->mIgnoreWrites) &&
(methodInstance->mMethodInstanceGroup->IsImplemented()))
{
BfIRFunction func(mBfIRBuilder->GetFakeVal());
return BfModuleMethodInstance(methodInstance, func);
}
return GetMethodInstance(typeInstance, typeInstance->mTypeDef->mMethods[methodIdx], BfTypeVector(), flags);
2019-08-23 11:56:54 -07:00
}
BfModuleMethodInstance BfModule::GetMethodByName(BfTypeInstance* typeInstance, const StringImpl& methodName, int paramCount, bool checkBase)
{
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
while (typeInstance != NULL)
{
typeInstance->mTypeDef->PopulateMemberSets();
BfMemberSetEntry* entry = NULL;
BfMethodDef* methodDef = NULL;
if (typeInstance->mTypeDef->mMethodSet.TryGetWith(methodName, &entry))
methodDef = (BfMethodDef*)entry->mMemberDef;
while (methodDef != NULL)
{
if ((methodDef->mMethodType != BfMethodType_Mixin) &&
(methodDef->mGenericParams.size() == 0) &&
((paramCount == -1) || (paramCount == (int)methodDef->mParams.size())))
return GetMethodInstanceAtIdx(typeInstance, methodDef->mIdx);
methodDef = methodDef->mNextWithSameName;
}
if (!checkBase)
break;
typeInstance = typeInstance->mBaseType;
}
return BfModuleMethodInstance();
}
BfModuleMethodInstance BfModule::GetMethodByName(BfTypeInstance* typeInstance, const StringImpl& methodName, const Array<BfType*>& paramTypes, bool checkBase)
{
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
while (typeInstance != NULL)
{
typeInstance->mTypeDef->PopulateMemberSets();
BfMemberSetEntry* entry = NULL;
BfMethodDef* methodDef = NULL;
if (typeInstance->mTypeDef->mMethodSet.TryGetWith(methodName, &entry))
methodDef = (BfMethodDef*)entry->mMemberDef;
while (methodDef != NULL)
{
if ((methodDef->mMethodType != BfMethodType_Mixin) &&
(methodDef->mGenericParams.size() == 0) &&
(paramTypes.size() == methodDef->mParams.size()))
{
auto moduleMethodInstance = GetMethodInstanceAtIdx(typeInstance, methodDef->mIdx);
if (moduleMethodInstance.mMethodInstance != NULL)
{
bool matches = true;
for (int paramIdx = 0; paramIdx < (int)paramTypes.size(); paramIdx++)
{
if (moduleMethodInstance.mMethodInstance->GetParamType(paramIdx) != paramTypes[paramIdx])
matches = false;
}
if (matches)
return moduleMethodInstance;
}
}
methodDef = methodDef->mNextWithSameName;
}
if (!checkBase)
break;
typeInstance = typeInstance->mBaseType;
}
return BfModuleMethodInstance();
}
BfModuleMethodInstance BfModule::GetInternalMethod(const StringImpl& methodName, int paramCount)
{
auto internalType = ResolveTypeDef(mCompiler->mInternalTypeDef);
PopulateType(internalType);
auto moduleMethodInstance = GetMethodByName(internalType->ToTypeInstance(), methodName, paramCount);
if (!moduleMethodInstance)
{
Fail(StrFormat("Failed to find System.Internal method '%s'", methodName.c_str()));
}
return moduleMethodInstance;
}
bool BfModule::IsMethodImplementedAndReified(BfTypeInstance* typeInstance, const StringImpl& methodName, int paramCount, bool checkBase)
{
while (typeInstance != NULL)
{
typeInstance->mTypeDef->PopulateMemberSets();
BfMemberSetEntry* entry = NULL;
BfMethodDef* methodDef = NULL;
if (typeInstance->mTypeDef->mMethodSet.TryGetWith(methodName, &entry))
methodDef = (BfMethodDef*)entry->mMemberDef;
while (methodDef != NULL)
{
if ((methodDef->mMethodType != BfMethodType_Mixin) &&
(methodDef->mGenericParams.size() == 0) &&
((paramCount == -1) || (paramCount == (int)methodDef->mParams.size())))
{
auto& methodInstanceGroup = typeInstance->mMethodInstanceGroups[methodDef->mIdx];
if (!methodInstanceGroup.IsImplemented())
return false;
if (methodInstanceGroup.mDefault == NULL)
return false;
return methodInstanceGroup.mDefault->mIsReified;
}
methodDef = methodDef->mNextWithSameName;
}
if (!checkBase)
break;
typeInstance = typeInstance->mBaseType;
}
return false;
}
bool BfModule::HasMixin(BfTypeInstance* typeInstance, const StringImpl& methodName, int paramCount, bool checkBase)
{
PopulateType(typeInstance, BfPopulateType_DataAndMethods);
while (typeInstance != NULL)
{
typeInstance->mTypeDef->PopulateMemberSets();
BfMemberSetEntry* entry = NULL;
BfMethodDef* methodDef = NULL;
if (typeInstance->mTypeDef->mMethodSet.TryGetWith(methodName, &entry))
methodDef = (BfMethodDef*)entry->mMemberDef;
while (methodDef != NULL)
{
if ((methodDef->mMethodType == BfMethodType_Mixin) &&
((paramCount == -1) || (paramCount == (int)methodDef->mParams.size())))
return true;
methodDef = methodDef->mNextWithSameName;
}
if (!checkBase)
break;
typeInstance = typeInstance->mBaseType;
}
return BfModuleMethodInstance();
}
String BfModule::MethodToString(BfMethodInstance* methodInst, BfMethodNameFlags methodNameFlags, BfTypeVector* methodGenericArgs)
{
auto methodDef = methodInst->mMethodDef;
bool allowResolveGenericParamNames = ((methodNameFlags & BfMethodNameFlag_ResolveGenericParamNames) != 0);
BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None;
if ((mCurTypeInstance == NULL) || (!mCurTypeInstance->IsUnspecializedTypeVariation()))
typeNameFlags = BfTypeNameFlag_ResolveGenericParamNames;
BfType* type = methodInst->mMethodInstanceGroup->mOwner;
if ((methodGenericArgs != NULL) && (type->IsUnspecializedType()))
type = ResolveGenericType(type, NULL, methodGenericArgs);
2019-08-23 11:56:54 -07:00
String methodName;
if ((methodNameFlags & BfMethodNameFlag_OmitTypeName) == 0)
{
methodName = TypeToString(type, typeNameFlags);
if (methodName == "$")
methodName = "";
2020-06-03 05:22:29 -07:00
else if (!methodName.IsEmpty())
2019-08-23 11:56:54 -07:00
methodName += ".";
}
String accessorString;
StringT<64> methodDefName = methodInst->mMethodDef->mName;
if (methodInst->mMethodDef->mIsLocalMethod)
{
int atPos = (int)methodDefName.IndexOf('$');
methodDefName.RemoveToEnd(atPos);
methodDefName.Replace("@", ".");
}
else
{
int atPos = (int)methodDefName.IndexOf('$');
if (atPos != -1)
{
accessorString = methodDefName.Substring(0, atPos);
if ((accessorString == "get") || (accessorString == "set"))
{
methodDefName = methodDefName.Substring(atPos + 1);
}
else
accessorString = "";
}
}
if ((methodInst->mMethodInfoEx != NULL) && (methodInst->mMethodInfoEx->mForeignType != NULL))
{
BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None;
if (!methodInst->mIsUnspecializedVariation && allowResolveGenericParamNames)
typeNameFlags = BfTypeNameFlag_ResolveGenericParamNames;
methodName += TypeToString(methodInst->mMethodInfoEx->mForeignType, typeNameFlags);
methodName += ".";
}
if ((methodInst->mMethodInfoEx != NULL) && (methodInst->mMethodInfoEx->mExplicitInterface != NULL))
{
BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None;
if (!methodInst->mIsUnspecializedVariation && allowResolveGenericParamNames)
typeNameFlags = BfTypeNameFlag_ResolveGenericParamNames;
2019-12-13 14:22:23 -08:00
methodName += "[";
2019-08-23 11:56:54 -07:00
methodName += TypeToString(methodInst->mMethodInfoEx->mExplicitInterface, typeNameFlags);
2019-12-13 14:22:23 -08:00
methodName += "].";
2019-08-23 11:56:54 -07:00
}
if (methodDef->mMethodType == BfMethodType_Operator)
{
BfOperatorDef* operatorDef = (BfOperatorDef*)methodDef;
if (operatorDef->mOperatorDeclaration->mIsConvOperator)
{
// Don't add explicit/implicit part, since that's not available in GCC mangling
/*if (operatorDef->mOperatorDeclaration->mExplicitToken != NULL)
{
if (operatorDef->mOperatorDeclaration->mExplicitToken->GetToken() == BfToken_Explicit)
methodName += "explicit ";
else if (operatorDef->mOperatorDeclaration->mExplicitToken->GetToken() == BfToken_Explicit)
methodName += "explicit ";
}*/
methodName += "operator ";
if (methodInst->mReturnType != NULL)
methodName += TypeToString(methodInst->mReturnType);
}
else
{
methodName += "operator";
if (operatorDef->mOperatorDeclaration->mOpTypeToken != NULL)
methodName += BfTokenToString(operatorDef->mOperatorDeclaration->mOpTypeToken->GetToken());
}
}
else if (methodDef->mMethodType == BfMethodType_Ctor)
{
if (methodDef->mIsStatic)
methodName += "__BfStaticCtor";
else
methodName += "this";
accessorString = "";
}
else if (methodDef->mMethodType == BfMethodType_PropertyGetter)
{
2019-12-13 14:22:23 -08:00
auto propertyDecl = methodDef->GetPropertyDeclaration();
if ((propertyDecl != NULL) && (propertyDecl->mNameNode != NULL))
propertyDecl->mNameNode->ToString(methodName);
2019-08-23 11:56:54 -07:00
methodName += " get accessor";
return methodName;
}
else if (methodDef->mMethodType == BfMethodType_PropertySetter)
{
2019-12-13 14:22:23 -08:00
auto propertyDecl = methodDef->GetPropertyDeclaration();
if ((propertyDecl != NULL) && (propertyDecl->mNameNode != NULL))
propertyDecl->mNameNode->ToString(methodName);
2019-08-23 11:56:54 -07:00
methodName += " set accessor";
return methodName;
}
else
methodName += methodDefName;
BfTypeVector newMethodGenericArgs;
if ((methodInst->mMethodInfoEx != NULL) && (methodInst->mMethodInfoEx->mMethodGenericArguments.size() != 0))
{
methodName += "<";
for (int i = 0; i < (int) methodInst->mMethodInfoEx->mMethodGenericArguments.size(); i++)
{
if (i > 0)
methodName += ", ";
BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None;
//Why did we have this methodInst->mIsUnspecializedVariation check? Sometimes we do need to show errors calling methods that refer back to our types
//if (!methodInst->mIsUnspecializedVariation && allowResolveGenericParamNames)
if (allowResolveGenericParamNames)
typeNameFlags = BfTypeNameFlag_ResolveGenericParamNames;
BfType* type = methodInst->mMethodInfoEx->mMethodGenericArguments[i];
if ((methodGenericArgs != NULL) && (type->IsUnspecializedType()))
{
bool hasEmpty = false;
for (auto arg : *methodGenericArgs)
{
if (arg == NULL)
hasEmpty = true;
}
if (hasEmpty)
{
for (int genericIdx = 0; genericIdx < (int)methodGenericArgs->size(); genericIdx++)
{
auto arg = (*methodGenericArgs)[genericIdx];
if (arg != NULL)
newMethodGenericArgs.push_back(arg);
else
{
auto genericParamInst = methodInst->mMethodInfoEx->mGenericParams[genericIdx];
if (genericParamInst->mTypeConstraint != NULL)
newMethodGenericArgs.push_back(genericParamInst->mTypeConstraint);
else
newMethodGenericArgs.push_back(mContext->mBfObjectType); // Default
}
}
methodGenericArgs = &newMethodGenericArgs;
}
if (type->IsUnspecializedType())
type = ResolveGenericType(type, NULL, methodGenericArgs);
2020-02-24 05:42:41 -08:00
}
if ((methodGenericArgs == NULL) && (mCurMethodInstance == NULL) && (mCurTypeInstance == NULL))
{
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, methodInst->GetOwner());
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, methodInst);
methodName += TypeToString(type, typeNameFlags);
}
else
methodName += TypeToString(type, typeNameFlags);
2019-08-23 11:56:54 -07:00
}
methodName += ">";
}
if (accessorString.length() == 0)
{
int dispParamIdx = 0;
methodName += "(";
for (int paramIdx = 0; paramIdx < (int) methodInst->GetParamCount(); paramIdx++)
{
int paramKind = methodInst->GetParamKind(paramIdx);
if (paramKind == BfParamKind_ImplicitCapture)
continue;
if (dispParamIdx > 0)
methodName += ", ";
if (paramKind == BfParamKind_Params)
methodName += "params ";
typeNameFlags = BfTypeNameFlag_ResolveGenericParamNames;
BfType* type = methodInst->GetParamType(paramIdx);
if ((methodGenericArgs != NULL) && (type->IsUnspecializedType()))
type = ResolveGenericType(type, NULL, methodGenericArgs);
2019-08-23 11:56:54 -07:00
methodName += TypeToString(type, typeNameFlags);
methodName += " ";
methodName += methodInst->GetParamName(paramIdx);
2020-05-17 06:10:56 -07:00
auto paramInitializer = methodInst->GetParamInitializer(paramIdx);
if (paramInitializer != NULL)
{
methodName += " = ";
methodName += paramInitializer->ToString();
}
2019-08-23 11:56:54 -07:00
dispParamIdx++;
}
methodName += ")";
}
if (accessorString.length() != 0)
{
methodName += " " + accessorString;
}
return methodName;
}
static void AddAttributeTargetName(BfAttributeTargets& flagsLeft, BfAttributeTargets checkFlag, String& str, String addString)
{
if ((flagsLeft & checkFlag) == 0)
return;
if (!str.empty())
{
if (flagsLeft == checkFlag)
{
if ((int)str.IndexOf(',') != -1)
str += ", and ";
else
str += " and ";
}
else
str += ", ";
}
str += addString;
flagsLeft = (BfAttributeTargets)(flagsLeft & ~checkFlag);
}
static String GetAttributesTargetListString(BfAttributeTargets attrTarget)
{
String resultStr;
auto flagsLeft = attrTarget;
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Assembly, resultStr, "assembly declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Module, resultStr, "module declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Class, resultStr, "class declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Struct, resultStr, "struct declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Enum, resultStr, "enum declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Constructor, resultStr, "constructor declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Method, resultStr, "method declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Property, resultStr, "property declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Field, resultStr, "field declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_StaticField, resultStr, "static field declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Interface, resultStr, "interface declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Parameter, resultStr, "parameter declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Delegate, resultStr, "delegate declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Function, resultStr, "function declarations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_ReturnValue, resultStr, "return value");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_GenericParameter, resultStr, "generic parameters");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Invocation, resultStr, "invocations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_MemberAccess, resultStr, "member access");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Alloc, resultStr, "allocations");
AddAttributeTargetName(flagsLeft, BfAttributeTargets_Alias, resultStr, "aliases");
2019-08-23 11:56:54 -07:00
if (resultStr.IsEmpty())
return "<nothing>";
return resultStr;
}
void BfModule::CurrentAddToConstHolder(BfIRValue& irVal)
{
auto constant = mBfIRBuilder->GetConstant(irVal);
int stringPoolIdx = GetStringPoolIdx(irVal, mBfIRBuilder);
if (stringPoolIdx != -1)
{
irVal = mCurTypeInstance->GetOrCreateConstHolder()->CreateConst(BfTypeCode_StringId, stringPoolIdx);
2019-08-23 11:56:54 -07:00
return;
}
if (constant->mConstType == BfConstType_Array)
{
auto constArray = (BfConstantArray*)constant;
SizedArray<BfIRValue, 8> newVals;
for (auto val : constArray->mValues)
{
auto newVal = val;
CurrentAddToConstHolder(newVal);
newVals.push_back(newVal);
}
irVal = mCurTypeInstance->mConstHolder->CreateConstArray(constArray->mType, newVals);
return;
}
auto origConst = irVal;
if ((constant->mConstType == BfConstType_BitCast) || (constant->mConstType == BfConstType_BitCastNull))
{
auto bitcast = (BfConstantBitCast*)constant;
constant = mBfIRBuilder->GetConstantById(bitcast->mTarget);
}
irVal = mCurTypeInstance->CreateConst(constant, mBfIRBuilder);
}
void BfModule::ClearConstData()
{
mBfIRBuilder->ClearConstData();
mStringObjectPool.Clear();
mStringCharPtrPool.Clear();
mStringPoolRefs.Clear();
}
BfTypedValue BfModule::GetTypedValueFromConstant(BfConstant* constant, BfIRConstHolder* constHolder, BfType* wantType)
{
switch (constant->mTypeCode)
{
case BfTypeCode_StringId:
case BfTypeCode_Boolean:
case BfTypeCode_Int8:
case BfTypeCode_UInt8:
case BfTypeCode_Int16:
case BfTypeCode_UInt16:
case BfTypeCode_Int32:
case BfTypeCode_UInt32:
case BfTypeCode_Int64:
case BfTypeCode_UInt64:
case BfTypeCode_IntPtr:
case BfTypeCode_UIntPtr:
case BfTypeCode_IntUnknown:
case BfTypeCode_UIntUnknown:
case BfTypeCode_Char8:
case BfTypeCode_Char16:
case BfTypeCode_Char32:
case BfTypeCode_Single:
case BfTypeCode_Double:
{
auto constVal = mBfIRBuilder->CreateConst(constant, constHolder);
BfTypedValue typedValue;
if (constant->mTypeCode == BfTypeCode_StringId)
{
if ((wantType->IsInstanceOf(mCompiler->mStringTypeDef)) ||
((wantType->IsPointer()) && (wantType->GetUnderlyingType() == GetPrimitiveType(BfTypeCode_Char8))))
{
typedValue = BfTypedValue(ConstantToCurrent(constant, constHolder, wantType), wantType);
return typedValue;
}
2019-08-23 11:56:54 -07:00
auto stringType = ResolveTypeDef(mCompiler->mStringTypeDef);
typedValue = BfTypedValue(ConstantToCurrent(constant, constHolder, stringType), stringType);
}
if (!typedValue)
2019-08-23 11:56:54 -07:00
{
auto constVal = mBfIRBuilder->CreateConst(constant, constHolder);
typedValue = BfTypedValue(constVal, GetPrimitiveType(constant->mTypeCode));
2019-08-23 11:56:54 -07:00
}
if (typedValue.mType == wantType)
return typedValue;
if (wantType->IsTypedPrimitive())
{
if (typedValue.mType == wantType->GetUnderlyingType())
{
typedValue.mType = wantType;
return typedValue;
}
}
auto castedTypedValue = Cast(NULL, typedValue, wantType, (BfCastFlags)(BfCastFlags_SilentFail | BfCastFlags_Explicit));
if (!castedTypedValue)
return BfTypedValue();
return castedTypedValue;
2019-08-23 11:56:54 -07:00
}
break;
default: break;
2019-08-23 11:56:54 -07:00
}
2019-08-23 11:56:54 -07:00
BfIRValue irValue = ConstantToCurrent(constant, constHolder, wantType);
BF_ASSERT(irValue);
if (!irValue)
return BfTypedValue();
return BfTypedValue(irValue, wantType, false);
}
BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* constHolder, BfType* wantType)
{
if (constant->mTypeCode == BfTypeCode_NullPtr)
{
return GetDefaultValue(wantType);
}
if (constant->mTypeCode == BfTypeCode_StringId)
{
if ((wantType->IsInstanceOf(mCompiler->mStringTypeDef)) ||
((wantType->IsPointer()) && (wantType->GetUnderlyingType() == GetPrimitiveType(BfTypeCode_Char8))))
{
const StringImpl& str = mContext->mStringObjectIdMap[constant->mInt32].mString;
BfIRValue stringObjConst = GetStringObjectValue(str);
if (wantType->IsPointer())
return GetStringCharPtr(stringObjConst);
return stringObjConst;
}
}
2019-08-23 11:56:54 -07:00
if (constant->mConstType == BfConstType_Array)
{
auto elementType = wantType->GetUnderlyingType();
auto constArray = (BfConstantArray*)constant;
SizedArray<BfIRValue, 8> newVals;
for (auto val : constArray->mValues)
{
newVals.push_back(ConstantToCurrent(constHolder->GetConstant(val), constHolder, elementType));
}
return mBfIRBuilder->CreateConstArray(mBfIRBuilder->MapType(wantType), newVals);
}
return mBfIRBuilder->CreateConst(constant, constHolder);
}
void BfModule::ValidateCustomAttributes(BfCustomAttributes* customAttributes, BfAttributeTargets attrTarget)
{
if (attrTarget == BfAttributeTargets_SkipValidate)
return;
for (auto& customAttribute : customAttributes->mAttributes)
{
if (!customAttribute.mAwaitingValidation)
continue;
if ((customAttribute.mType->mAttributeData->mAttributeTargets & attrTarget) == 0)
{
Fail(StrFormat("Attribute '%s' is not valid on this declaration type. It is only valid on %s.",
customAttribute.GetRefNode()->ToString().c_str(), GetAttributesTargetListString(customAttribute.mType->mAttributeData->mAttributeTargets).c_str()), customAttribute.mRef->mAttributeTypeRef); // CS0592
2019-08-23 11:56:54 -07:00
}
customAttribute.mAwaitingValidation = false;
}
}
void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttributeDirective* attributesDirective, BfAttributeTargets attrTarget, bool allowNonConstArgs, BfCaptureInfo* captureInfo)
2019-09-07 06:41:19 -07:00
{
2019-08-23 11:56:54 -07:00
if ((attributesDirective != NULL) && (mCompiler->mResolvePassData != NULL) &&
(attributesDirective->IsFromParser(mCompiler->mResolvePassData->mParser)) && (mCompiler->mResolvePassData->mSourceClassifier != NULL))
{
mCompiler->mResolvePassData->mSourceClassifier->VisitChild(attributesDirective);
}
SetAndRestoreValue<bool> prevIsCapturingMethodMatchInfo;
if (mCompiler->IsAutocomplete())
prevIsCapturingMethodMatchInfo.Init(mCompiler->mResolvePassData->mAutoComplete->mIsCapturingMethodMatchInfo, false);
BfTypeInstance* baseAttrTypeInst = mContext->mUnreifiedModule->ResolveTypeDef(mCompiler->mAttributeTypeDef)->ToTypeInstance();
BfAttributeTargets targetOverride = (BfAttributeTargets)0;
BfAutoComplete* autoComplete = NULL;
if (mCompiler->mResolvePassData != NULL)
autoComplete = mCompiler->mResolvePassData->mAutoComplete;
for (; attributesDirective != NULL; attributesDirective = attributesDirective->mNextAttribute)
{
if (auto tokenNode = BfNodeDynCast<BfTokenNode>(attributesDirective->mAttributeTargetSpecifier))
{
if (captureInfo == NULL)
{
Fail("Capture specifiers can only be used in lambda allocations", attributesDirective);
continue;
}
BfCaptureInfo::Entry captureEntry;
captureEntry.mCaptureType = (tokenNode->mToken == BfToken_Ampersand) ? BfCaptureType_Reference : BfCaptureType_Copy;
if (!attributesDirective->mArguments.IsEmpty())
{
captureEntry.mNameNode = BfNodeDynCast<BfIdentifierNode>(attributesDirective->mArguments[0]);
if ((captureEntry.mNameNode != NULL) && (autoComplete != NULL))
autoComplete->CheckIdentifier(captureEntry.mNameNode);
}
captureInfo->mCaptures.Add(captureEntry);
continue;
}
2019-08-23 11:56:54 -07:00
BfAutoParentNodeEntry autoParentNodeEntry(this, attributesDirective);
BfCustomAttribute customAttribute;
customAttribute.mAwaitingValidation = true;
customAttribute.mRef = attributesDirective;
if (attributesDirective->mAttrOpenToken != NULL)
targetOverride = (BfAttributeTargets)0;
if (attributesDirective->mAttributeTypeRef == NULL)
{
AssertErrorState();
continue;
}
2020-05-18 17:20:03 -07:00
BfType* attrType = ResolveTypeRef(attributesDirective->mAttributeTypeRef, BfPopulateType_Identity, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_Attribute | BfResolveTypeRefFlag_NoReify));
BfTypeDef* attrTypeDef = NULL;
if ((attrType != NULL) && (attrType->IsTypeInstance()))
attrTypeDef = attrType->ToTypeInstance()->mTypeDef;
2019-08-23 11:56:54 -07:00
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
{
mCompiler->mResolvePassData->mAutoComplete->CheckAttributeTypeRef(attributesDirective->mAttributeTypeRef);
if (attrTypeDef != NULL)
mCompiler->mResolvePassData->HandleTypeReference(attributesDirective->mAttributeTypeRef, attrTypeDef);
}
bool isBypassedAttr = false;
if (attrTypeDef != NULL)
{
2019-08-23 11:56:54 -07:00
// 'Object' has some dependencies on some attributes, but those attributes are classes so we have a circular dependency issue
// We solve it by having a 'bypass' for known attributes that Object depends on
if ((attributesDirective->mArguments.empty()) && (autoComplete == NULL) && (attrType != NULL) && (attrType->IsTypeInstance()))
{
//if (attrTypeDef == mCompiler->mCReprAttributeTypeDef)
if (attrType->IsInstanceOf(mCompiler->mCReprAttributeTypeDef))
{
2019-08-23 11:56:54 -07:00
for (auto methodDef : attrTypeDef->mMethods)
{
if ((methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mProtection == BfProtection_Public))
{
customAttribute.mType = attrType->ToTypeInstance();
customAttribute.mCtor = methodDef;
isBypassedAttr = true;
break;
}
}
}
}
if (isBypassedAttr)
{
customAttribute.mAwaitingValidation = false;
customAttributes->mAttributes.push_back(customAttribute);
continue;
}
if (mContext->mCurTypeState != NULL)
{
SetAndRestoreValue<BfTypeReference*> prevTypeRef(mContext->mCurTypeState->mCurAttributeTypeRef, attributesDirective->mAttributeTypeRef);
mContext->mUnreifiedModule->ResolveTypeResult(attributesDirective->mAttributeTypeRef, attrType, BfPopulateType_BaseType, (BfResolveTypeRefFlags)0);
}
else
{
mContext->mUnreifiedModule->ResolveTypeResult(attributesDirective->mAttributeTypeRef, attrType, BfPopulateType_BaseType, (BfResolveTypeRefFlags)0);
}
}
BfTypeInstance* attrTypeInst = NULL;
if (attrType == NULL)
continue;
mContext->mUnreifiedModule->PopulateType(attrType, BfPopulateType_DataAndMethods);
attrTypeInst = attrType->ToTypeInstance();
if ((attrTypeInst == NULL) || (!TypeIsSubTypeOf(attrTypeInst, baseAttrTypeInst)) || (attrTypeInst->mAttributeData == NULL))
{
Fail(StrFormat("'%s' is not an attribute class", TypeToString(attrType).c_str()), attributesDirective->mAttributeTypeRef); //CS0616
continue;
}
if (mCurTypeInstance != NULL)
AddDependency(attrTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_CustomAttribute);
customAttribute.mType = attrTypeInst;
BfConstResolver constResolver(this);
if (allowNonConstArgs)
constResolver.mBfEvalExprFlags = (BfEvalExprFlags)(constResolver.mBfEvalExprFlags | BfEvalExprFlags_AllowNonConst);
2019-08-23 11:56:54 -07:00
bool inPropSet = false;
SizedArray<BfResolvedArg, 2> argValues;
for (BfExpression* arg : attributesDirective->mArguments)
{
if (arg == NULL)
{
continue;
}
if (autoComplete != NULL)
autoComplete->mShowAttributeProperties = attrTypeInst;
if (auto assignExpr = BfNodeDynCast<BfAssignmentExpression>(arg))
{
inPropSet = true;
if (autoComplete != NULL)
autoComplete->CheckNode(assignExpr->mLeft);
String findName = assignExpr->mLeft->ToString();
BfPropertyDef* bestProp = NULL;
BfTypeInstance* bestPropTypeInst = NULL;
BfFieldDef* bestField = NULL;
BfTypeInstance* bestFieldTypeInst = NULL;
auto checkTypeInst = attrTypeInst;
while (checkTypeInst != NULL)
{
mContext->mUnreifiedModule->PopulateType(checkTypeInst, BfPopulateType_Data);
for (auto prop : checkTypeInst->mTypeDef->mProperties)
{
if (prop->mName == findName)
{
if ((bestProp == NULL) || (bestProp->mProtection != BfProtection_Public))
{
bestProp = prop;
bestPropTypeInst = checkTypeInst;
}
}
}
for (auto field : checkTypeInst->mTypeDef->mFields)
{
if (field->mName == findName)
{
if ((bestField == NULL) || (bestField->mProtection != BfProtection_Public))
{
bestField = field;
bestFieldTypeInst = checkTypeInst;
}
}
}
if ((bestProp != NULL) || (bestField != NULL))
break;
checkTypeInst = checkTypeInst->mBaseType;
}
bool handledExpr = false;
if (bestField != NULL)
{
handledExpr = true;
if (mCompiler->mResolvePassData != NULL)
{
mCompiler->mResolvePassData->HandleFieldReference(assignExpr->mLeft, attrTypeDef, bestField);
if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(assignExpr->mLeft)))
{
autoComplete->mDefField = bestField;
autoComplete->mDefType = attrTypeDef;
}
}
if (bestField->mProtection != BfProtection_Public)
Fail(StrFormat("'%s.%s' is inaccessible due to its protection level", TypeToString(bestFieldTypeInst).c_str(), findName.c_str()), assignExpr->mLeft); // CS0122
AddDependency(bestFieldTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_CustomAttribute);
BfCustomAttributeSetField setField;
setField.mFieldRef = BfFieldRef(bestFieldTypeInst, bestField);
auto& fieldTypeInst = checkTypeInst->mFieldInstances[bestField->mIdx];
if (assignExpr->mRight != NULL)
{
BfTypedValue result = constResolver.Resolve(assignExpr->mRight, fieldTypeInst.mResolvedType);
if (result)
{
CurrentAddToConstHolder(result.mValue);
setField.mParam = result;
customAttribute.mSetField.push_back(setField);
}
}
}
else if (bestProp == NULL)
{
Fail(StrFormat("'%s' does not contain a field or property named '%s'", TypeToString(attrTypeInst).c_str(), findName.c_str()), assignExpr->mLeft);
}
else
{
BfMethodDef* setMethod = NULL;
for (auto methodDef : bestProp->mMethods)
{
if (methodDef->mMethodType == BfMethodType_PropertySetter)
{
setMethod = methodDef;
break;
}
}
if (setMethod == NULL)
{
Fail("Property has no setter", assignExpr->mLeft);
}
else
{
if (mCompiler->mResolvePassData != NULL)
{
mCompiler->mResolvePassData->HandlePropertyReference(assignExpr->mLeft, attrTypeDef, bestProp);
if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(assignExpr->mLeft)))
{
autoComplete->mDefProp = bestProp;
autoComplete->mDefType = attrTypeDef;
}
}
handledExpr = true;
if (bestProp->mProtection != BfProtection_Public)
Fail(StrFormat("'%s.%s' is inaccessible due to its protection level", TypeToString(bestPropTypeInst).c_str(), findName.c_str()), assignExpr->mLeft); // CS0122
BfResolvedArg resolvedArg;
AddDependency(bestPropTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_CustomAttribute);
BfCustomAttributeSetProperty setProperty;
setProperty.mPropertyRef = BfPropertyRef(bestPropTypeInst, bestProp);
// We don't actually need the mFunc, so get the ModuleMethodInstance from the native module
auto methodInstance = bestPropTypeInst->mModule->GetMethodInstance(bestPropTypeInst, setMethod, BfTypeVector());
if (methodInstance.mMethodInstance != NULL)
{
auto propType = methodInstance.mMethodInstance->GetParamType(0);
if (assignExpr->mRight != NULL)
{
BfTypedValue result = constResolver.Resolve(assignExpr->mRight, propType);
if (result)
{
BF_ASSERT(result.mType == propType);
CurrentAddToConstHolder(result.mValue);
setProperty.mParam = result;
customAttribute.mSetProperties.push_back(setProperty);
}
}
}
}
}
if ((!handledExpr) && (assignExpr->mRight != NULL))
constResolver.Resolve(assignExpr->mRight);
}
else
{
if (inPropSet)
{
Fail("Named attribute argument expected", arg); // CS1016
}
BfDeferEvalChecker deferEvalChecker;
arg->Accept(&deferEvalChecker);
bool deferParamEval = deferEvalChecker.mNeedsDeferEval;
BfResolvedArg resolvedArg;
resolvedArg.mExpression = arg;
if (deferParamEval)
{
resolvedArg.mArgFlags = BfArgFlag_DeferredEval;
}
else
resolvedArg.mTypedValue = constResolver.Resolve(arg);
if (!inPropSet)
argValues.push_back(resolvedArg);
}
if (autoComplete != NULL)
autoComplete->mShowAttributeProperties = NULL;
}
auto wasCapturingMethodInfo = false;
if (autoComplete != NULL)
{
wasCapturingMethodInfo = autoComplete->mIsCapturingMethodMatchInfo;
if (attributesDirective->mCtorOpenParen != NULL)
autoComplete->CheckInvocation(attributesDirective, attributesDirective->mCtorOpenParen, attributesDirective->mCtorCloseParen, attributesDirective->mCommas);
}
BfMethodMatcher methodMatcher(attributesDirective, this, "", argValues, NULL);
attrTypeDef = attrTypeInst->mTypeDef;
bool success = true;
bool isFailurePass = false;
for (int pass = 0; pass < 2; pass++)
{
bool isFailurePass = pass == 1;
for (auto checkMethod : attrTypeDef->mMethods)
{
if ((isFailurePass) && (checkMethod->mMethodDeclaration == NULL))
continue; // Don't match private default ctor if there's a user-defined one
if ((checkMethod->mIsStatic) || (checkMethod->mMethodType != BfMethodType_Ctor))
continue;
if ((!isFailurePass) && (!CheckProtection(checkMethod->mProtection, false, false)))
continue;
methodMatcher.CheckMethod(NULL, attrTypeInst, checkMethod, isFailurePass);
2019-08-23 11:56:54 -07:00
}
if ((methodMatcher.mBestMethodDef != NULL) || (methodMatcher.mBackupMethodDef != NULL))
break;
}
if (autoComplete != NULL)
{
if ((wasCapturingMethodInfo) && (!autoComplete->mIsCapturingMethodMatchInfo))
{
autoComplete->mIsCapturingMethodMatchInfo = true;
BF_ASSERT(autoComplete->mMethodMatchInfo != NULL);
}
else
autoComplete->mIsCapturingMethodMatchInfo = false;
}
if (methodMatcher.mBestMethodDef == NULL)
methodMatcher.mBestMethodDef = methodMatcher.mBackupMethodDef;
BF_ASSERT(methodMatcher.mBestMethodDef != NULL);
customAttribute.mCtor = methodMatcher.mBestMethodDef;
if (methodMatcher.mBestMethodTypeInstance == mCurTypeInstance)
{
Fail("Custom attribute circular reference", attributesDirective);
}
if (mCurTypeInstance != NULL)
AddDependency(methodMatcher.mBestMethodTypeInstance, mCurTypeInstance, BfDependencyMap::DependencyFlag_CustomAttribute);
if (!constResolver.PrepareMethodArguments(attributesDirective->mAttributeTypeRef, &methodMatcher, customAttribute.mCtorArgs))
success = false;
for (auto& arg : argValues)
{
if ((arg.mArgFlags & BfArgFlag_DeferredEval) != 0)
{
if (auto expr = BfNodeDynCast<BfExpression>(arg.mExpression))
constResolver.Resolve(expr);
}
}
// Move all those to the constHolder
for (auto& ctorArg : customAttribute.mCtorArgs)
{
if (ctorArg.IsConst())
CurrentAddToConstHolder(ctorArg);
2019-08-23 11:56:54 -07:00
}
if (attributesDirective->mAttributeTargetSpecifier != NULL)
{
targetOverride = BfAttributeTargets_ReturnValue;
if (attrTarget != BfAttributeTargets_Method)
{
Fail(StrFormat("'%s' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are '%s'. All attributes in this block will be ignored.",
GetAttributesTargetListString(targetOverride).c_str(), GetAttributesTargetListString(attrTarget).c_str()), attributesDirective->mAttributeTargetSpecifier); // CS0657
}
success = false;
}
if ((success) && (targetOverride != (BfAttributeTargets)0))
{
if ((targetOverride == BfAttributeTargets_ReturnValue) && (attrTarget == BfAttributeTargets_Method))
{
auto methodInfoEx = mCurMethodInstance->GetMethodInfoEx();
if (methodInfoEx->mMethodCustomAttributes == NULL)
methodInfoEx->mMethodCustomAttributes = new BfMethodCustomAttributes();
if (success)
{
if (methodInfoEx->mMethodCustomAttributes->mReturnCustomAttributes == NULL)
methodInfoEx->mMethodCustomAttributes->mReturnCustomAttributes = new BfCustomAttributes();
methodInfoEx->mMethodCustomAttributes->mReturnCustomAttributes->mAttributes.push_back(customAttribute);
}
}
else
{
// Failed - ignore
success = false;
}
}
if (success)
{
if (!attrTypeInst->mAttributeData->mAllowMultiple)
{
for (auto& prevCustomAttribute : customAttributes->mAttributes)
{
if (prevCustomAttribute.mType == attrTypeInst)
{
Fail(StrFormat("Duplicate '%s' attribute", attributesDirective->mAttributeTypeRef->ToString().c_str()), attributesDirective->mAttributeTypeRef); // CS0579
}
}
}
}
if (success)
{
customAttributes->mAttributes.push_back(customAttribute);
}
}
ValidateCustomAttributes(customAttributes, attrTarget);
}
BfCustomAttributes* BfModule::GetCustomAttributes(BfAttributeDirective* attributesDirective, BfAttributeTargets attrType, bool allowNonConstArgs, BfCaptureInfo* captureInfo)
2019-08-23 11:56:54 -07:00
{
BfCustomAttributes* customAttributes = new BfCustomAttributes();
GetCustomAttributes(customAttributes, attributesDirective, attrType, allowNonConstArgs, captureInfo);
2019-08-23 11:56:54 -07:00
return customAttributes;
}
void BfModule::ProcessTypeInstCustomAttributes(bool& isPacked, bool& isUnion, bool& isCRepr, bool& isOrdered)
{
if (mCurTypeInstance->mCustomAttributes != NULL)
{
for (auto& customAttribute : mCurTypeInstance->mCustomAttributes->mAttributes)
{
String typeName = TypeToString(customAttribute.mType);
if (typeName == "System.PackedAttribute")
{
isPacked = true;
}
else if (typeName == "System.UnionAttribute")
{
isUnion = true;
}
else if (typeName == "System.CReprAttribute")
{
isCRepr = true;
isOrdered = true;
}
else if (typeName == "System.OrderedAttribute")
{
isOrdered = true;
}
else if (typeName == "System.AlwaysIncludeAttribute")
{
for (auto setProp : customAttribute.mSetProperties)
{
BfPropertyDef* propertyDef = setProp.mPropertyRef;
if (propertyDef->mName == "AssumeInstantiated")
{
auto constant = mCurTypeInstance->mConstHolder->GetConstant(setProp.mParam.mValue);
if ((constant != NULL) && (constant->mBool))
mCurTypeInstance->mHasBeenInstantiated = true;
}
2020-05-01 09:11:13 -07:00
else if (propertyDef->mName == "IncludeAllMethods")
{
auto constant = mCurTypeInstance->mConstHolder->GetConstant(setProp.mParam.mValue);
if ((constant != NULL) && (constant->mBool))
mCurTypeInstance->mIncludeAllMethods = true;
}
2019-08-23 11:56:54 -07:00
}
}
}
}
}
// Checking to see if we're an attribute or not
void BfModule::ProcessCustomAttributeData()
{
bool isAttribute = false;
auto checkTypeInst = mCurTypeInstance->mBaseType;
while (checkTypeInst != NULL)
{
if (checkTypeInst->mTypeDef == mCompiler->mAttributeTypeDef)
isAttribute = true;
checkTypeInst = checkTypeInst->mBaseType;
}
if (!isAttribute)
return;
auto attributeData = new BfAttributeData();
bool hasCustomAttribute = false;
if (mCurTypeInstance->mCustomAttributes != NULL)
{
for (auto& customAttribute : mCurTypeInstance->mCustomAttributes->mAttributes)
{
if (customAttribute.mType->mTypeDef == mCompiler->mAttributeUsageAttributeTypeDef)
{
if (customAttribute.mCtorArgs.size() > 0)
{
auto constant = mCurTypeInstance->mConstHolder->GetConstant(customAttribute.mCtorArgs[0]);
if ((constant != NULL) && (mBfIRBuilder->IsInt(constant->mTypeCode)))
attributeData->mAttributeTargets = (BfAttributeTargets)constant->mInt32;
}
for (auto& setProp : customAttribute.mSetProperties)
{
BfPropertyDef* propDef = setProp.mPropertyRef;
if (propDef->mName == "AllowMultiple")
{
auto constant = mCurTypeInstance->mConstHolder->GetConstant(setProp.mParam.mValue);
if (constant != NULL)
attributeData->mAllowMultiple = constant->mBool;
}
else if (propDef->mName == "Inherited")
{
auto constant = mCurTypeInstance->mConstHolder->GetConstant(setProp.mParam.mValue);
if (constant != NULL)
attributeData->mInherited = constant->mBool;
}
else if (propDef->mName == "ValidOn")
{
auto constant = mCurTypeInstance->mConstHolder->GetConstant(setProp.mParam.mValue);
if (constant != NULL)
attributeData->mAttributeTargets = (BfAttributeTargets)constant->mInt32;
}
}
hasCustomAttribute = true;
}
}
}
if ((!hasCustomAttribute) && (mCurTypeInstance->mBaseType->mAttributeData != NULL))
{
attributeData->mAttributeTargets = mCurTypeInstance->mBaseType->mAttributeData->mAttributeTargets;
attributeData->mInherited = mCurTypeInstance->mBaseType->mAttributeData->mInherited;
attributeData->mAllowMultiple = mCurTypeInstance->mBaseType->mAttributeData->mAllowMultiple;
}
mCurTypeInstance->mAttributeData = attributeData;
}
bool BfModule::TryGetConstString(BfIRConstHolder* constHolder, BfIRValue irValue, StringImpl& str)
{
auto constant = constHolder->GetConstant(irValue);
if (constant == NULL)
return false;
2020-03-30 14:09:02 -07:00
if (constant->mTypeCode != BfTypeCode_StringId)
2019-08-23 11:56:54 -07:00
return false;
int stringId = constant->mInt32;
BfStringPoolEntry* entry = NULL;
if (mContext->mStringObjectIdMap.TryGetValue(stringId, &entry))
{
str = entry->mString;
}
else
{
BF_DBG_FATAL("Failed to find string by id");
}
return true;
}
BfVariant BfModule::TypedValueToVariant(BfAstNode* refNode, const BfTypedValue& value, bool allowUndef)
{
BfVariant variant;
variant.mTypeCode = BfTypeCode_None;
BfType* primType = NULL;
if (value.mType->IsPrimitiveType())
primType = (BfPrimitiveType*)value.mType;
else if (value.mType->IsTypedPrimitive())
primType = value.mType->GetUnderlyingType();
if (primType)
{
auto constant = mBfIRBuilder->GetConstant(value.mValue);
if (constant != NULL)
{
if ((allowUndef) && (constant->mConstType == BfConstType_Undef))
{
variant.mTypeCode = BfTypeCode_Let;
return variant;
}
switch (constant->mTypeCode)
{
case BfTypeCode_Int8:
case BfTypeCode_UInt8:
case BfTypeCode_Int16:
case BfTypeCode_UInt16:
case BfTypeCode_Int32:
case BfTypeCode_UInt32:
case BfTypeCode_Int64:
case BfTypeCode_UInt64:
case BfTypeCode_IntPtr:
case BfTypeCode_UIntPtr:
case BfTypeCode_IntUnknown:
case BfTypeCode_UIntUnknown:
case BfTypeCode_Single:
case BfTypeCode_Double:
case BfTypeCode_Char8:
case BfTypeCode_Char16:
case BfTypeCode_Char32:
variant.mTypeCode = constant->mTypeCode;
variant.mInt64 = constant->mInt64;
break;
default:
if (refNode != NULL)
Fail("Invalid const expression type", refNode);
break;
}
}
}
return variant;
}
BfTypedValue BfModule::RemoveRef(BfTypedValue typedValue)
{
if ((typedValue.mType != NULL) && (typedValue.mType->IsRef()))
{
auto elementType = typedValue.mType->GetUnderlyingType();
if (typedValue.IsAddr())
{
if (elementType->IsValuelessType())
{
BF_ASSERT(!typedValue.mValue);
typedValue = BfTypedValue(typedValue.mValue, elementType, true);
}
else
typedValue = BfTypedValue(mBfIRBuilder->CreateLoad(typedValue.mValue), elementType, true);
}
else
typedValue = BfTypedValue(typedValue.mValue, elementType, true);
if (typedValue.mType->IsValuelessType())
{
BF_ASSERT(typedValue.mValue.IsFake());
}
}
return typedValue;
}
BfTypedValue BfModule::LoadValue(BfTypedValue typedValue, BfAstNode* refNode, bool isVolatile)
{
if (!typedValue.IsAddr())
return typedValue;
2020-05-11 10:14:39 -07:00
PopulateType(typedValue.mType);
2020-04-12 08:37:22 -07:00
if ((typedValue.mType->IsValuelessType()) || (typedValue.mType->IsVar()))
2019-08-23 11:56:54 -07:00
return BfTypedValue(mBfIRBuilder->GetFakeVal(), typedValue.mType, false);
BfIRValue loadedVal = typedValue.mValue;
if (loadedVal)
{
2019-11-02 06:02:36 -07:00
if (typedValue.mType->IsVar())
{
return BfTypedValue(loadedVal, typedValue.mType, false);
}
2019-08-23 11:56:54 -07:00
/*if (isVolatile)
mBfIRBuilder->CreateFence(BfIRFenceType_AcquireRelease);*/
PopulateType(typedValue.mType, BfPopulateType_Data);
loadedVal = mBfIRBuilder->CreateAlignedLoad(loadedVal, std::max(1, (int)typedValue.mType->mAlign), isVolatile);
}
return BfTypedValue(loadedVal, typedValue.mType, false);
}
BfTypedValue BfModule::LoadOrAggregateValue(BfTypedValue typedValue)
{
if (typedValue.IsSplat())
return AggregateSplat(typedValue);
if (typedValue.IsAddr())
return LoadValue(typedValue);
return typedValue;
}
BfTypedValue BfModule::AggregateSplat(BfTypedValue typedValue, BfIRValue* valueArrPtr)
{
if (!typedValue.IsSplat())
return typedValue;
if (typedValue.mType->IsValuelessType())
return typedValue;
int elementIdx = 0;
auto _ExtractValue = [&](BfType* checkType)
{
if (valueArrPtr != NULL)
return valueArrPtr[elementIdx++];
return ExtractSplatValue(typedValue, elementIdx++, checkType);
};
std::function<BfIRValue(BfType*)> checkTypeLambda = [&](BfType* checkType)
{
if (checkType->IsStruct())
{
BfIRValue curValue = mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(checkType, BfIRPopulateType_Full));
auto checkTypeInstance = checkType->ToTypeInstance();
if (checkTypeInstance->mBaseType != NULL)
{
mBfIRBuilder->PopulateType(checkTypeInstance->mBaseType, BfIRPopulateType_Full);
BfIRValue baseValue = checkTypeLambda(checkTypeInstance->mBaseType);
curValue = mBfIRBuilder->CreateInsertValue(curValue, baseValue, 0);
}
if (checkTypeInstance->mIsUnion)
{
auto unionInnerType = checkTypeInstance->GetUnionInnerType();
if (!unionInnerType->IsValuelessType())
{
BfIRValue fieldValue = checkTypeLambda(unionInnerType);
curValue = mBfIRBuilder->CreateInsertValue(curValue, fieldValue, 1);
}
if (checkTypeInstance->IsEnum())
{
auto dscrType = checkTypeInstance->GetDiscriminatorType();
BfIRValue fieldValue = checkTypeLambda(dscrType);
curValue = mBfIRBuilder->CreateInsertValue(curValue, fieldValue, 2);
}
}
else
{
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
if (fieldInstance->mDataIdx >= 0)
{
BfIRValue fieldValue = checkTypeLambda(fieldInstance->GetResolvedType());
curValue = mBfIRBuilder->CreateInsertValue(curValue, fieldValue, fieldInstance->mDataIdx);
}
}
}
return curValue;
}
else if (checkType->IsMethodRef())
{
BfMethodRefType* methodRefType = (BfMethodRefType*)checkType;
BfIRValue curValue = mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(checkType, BfIRPopulateType_Full));
for (int dataIdx = 0; dataIdx < methodRefType->GetCaptureDataCount(); dataIdx++)
{
BfIRValue fieldValue;
auto checkType = methodRefType->GetCaptureType(dataIdx);
if (methodRefType->WantsDataPassedAsSplat(dataIdx))
{
fieldValue = checkTypeLambda(checkType);
}
else
{
fieldValue = _ExtractValue(checkType);
}
curValue = mBfIRBuilder->CreateInsertValue(curValue, fieldValue, dataIdx);
}
return curValue;
}
else if (!checkType->IsValuelessType())
{
return _ExtractValue(checkType);
}
return BfIRValue();
};
BfIRValue value = checkTypeLambda(typedValue.mType);
return BfTypedValue(value, typedValue.mType, typedValue.IsThis() ? BfTypedValueKind_ThisValue : BfTypedValueKind_Value);
}
void BfModule::AggregateSplatIntoAddr(BfTypedValue typedValue, BfIRValue addrVal)
{
if (!typedValue.IsSplat())
return;
if (typedValue.mType->IsValuelessType())
return;
/*static int sCallIdx = 0;
if (!mCompiler->mIsResolveOnly)
sCallIdx++;
int callIdx = sCallIdx;*/
int elementIdx = 0;
std::function<void(BfType*, BfIRValue)> checkTypeLambda = [&](BfType* checkType, BfIRValue curAddrVal)
{
if (checkType->IsStruct())
{
auto checkTypeInstance = checkType->ToTypeInstance();
if (checkTypeInstance->mBaseType != NULL)
{
mBfIRBuilder->PopulateType(checkTypeInstance->mBaseType);
auto baseAddrVal = mBfIRBuilder->CreateInBoundsGEP(curAddrVal, 0, 0);
checkTypeLambda(checkTypeInstance->mBaseType, baseAddrVal);
}
if (checkTypeInstance->mIsUnion)
{
auto unionInnerType = checkTypeInstance->GetUnionInnerType();
if (!unionInnerType->IsValuelessType())
{
auto fieldAddrVal = mBfIRBuilder->CreateInBoundsGEP(curAddrVal, 0, 1);
checkTypeLambda(unionInnerType, fieldAddrVal);
}
if (checkTypeInstance->IsEnum())
{
auto dscrType = checkTypeInstance->GetDiscriminatorType();
auto fieldAddrVal = mBfIRBuilder->CreateInBoundsGEP(curAddrVal, 0, 2);
checkTypeLambda(dscrType, fieldAddrVal);
}
}
else
{
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
if (fieldInstance->mDataIdx >= 0)
{
auto fieldAddrVal = mBfIRBuilder->CreateInBoundsGEP(curAddrVal, 0, fieldInstance->mDataIdx);
checkTypeLambda(fieldInstance->mResolvedType, fieldAddrVal);
}
}
}
}
else if (checkType->IsMethodRef())
{
BfMethodRefType* methodRefType = (BfMethodRefType*)checkType;
for (int dataIdx = 0; dataIdx < methodRefType->GetCaptureDataCount(); dataIdx++)
{
auto dataType = methodRefType->GetCaptureType(dataIdx);
auto fieldAddrVal = mBfIRBuilder->CreateInBoundsGEP(curAddrVal, 0, dataIdx);
if (methodRefType->WantsDataPassedAsSplat(dataIdx))
{
checkTypeLambda(methodRefType->GetCaptureType(dataIdx), fieldAddrVal);
}
else
{
// static int sItrCount = 0;
// sItrCount++;
// int curItr = sItrCount;
// if (curItr == 1)
// {
// NOP;
// }
auto val = ExtractSplatValue(typedValue, elementIdx++, dataType);
mBfIRBuilder->CreateStore(val, fieldAddrVal);
}
}
}
else if (!checkType->IsValuelessType())
{
auto val = ExtractSplatValue(typedValue, elementIdx++, checkType);
mBfIRBuilder->CreateStore(val, curAddrVal);
}
};
checkTypeLambda(typedValue.mType, addrVal);
}
BfTypedValue BfModule::MakeAddressable(BfTypedValue typedVal)
{
bool wasReadOnly = typedVal.IsReadOnly();
if ((typedVal.mType->IsValueType()) &&
(!typedVal.mType->IsValuelessType()))
{
wasReadOnly = true; // Any non-addr is implicitly read-only
//static int gCallIdx = 0;
if (typedVal.IsAddr())
return typedVal;
BfType* type = typedVal.mType;
PopulateType(type);
auto tempVar = CreateAlloca(type);
if (typedVal.IsSplat())
AggregateSplatIntoAddr(typedVal, tempVar);
else
mBfIRBuilder->CreateAlignedStore(typedVal.mValue, tempVar, type->mAlign);
return BfTypedValue(tempVar, type,
typedVal.IsThis() ?
(wasReadOnly ? BfTypedValueKind_ReadOnlyThisAddr : BfTypedValueKind_ThisAddr) :
(wasReadOnly ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr));
}
return LoadValue(typedVal);
}
BfTypedValue BfModule::RemoveReadOnly(BfTypedValue typedValue)
{
if (typedValue.mKind == BfTypedValueKind_ReadOnlyAddr)
typedValue.mKind = BfTypedValueKind_Addr;
return typedValue;
}
BfIRValue BfModule::ExtractSplatValue(BfTypedValue typedValue, int componentIdx, BfType* wantType, bool* isAddr)
{
BfIRValue val;
if (typedValue.mValue.IsArg())
{
val = mBfIRBuilder->GetArgument(typedValue.mValue.mId + componentIdx);
if (isAddr != NULL)
{
if (wantType != NULL)
*isAddr = wantType->IsComposite();
else
*isAddr = false;
}
else if (wantType->IsComposite())
val = mBfIRBuilder->CreateLoad(val);
}
if (!val)
{
auto checkMethodState = mCurMethodState;
while (checkMethodState != NULL)
{
for (int idx = 0; idx < (int)checkMethodState->mSplatDecompAddrs.size(); idx++)
{
auto decompAddr = checkMethodState->mSplatDecompAddrs[idx];
if (decompAddr == typedValue.mValue)
{
val = checkMethodState->mSplatDecompAddrs[idx + componentIdx];
if ((wantType->IsComposite()) && (typedValue.mType->IsMethodRef()))
{
// This is really backing for a POINTER to a composite inside a methodRef
val = mBfIRBuilder->CreateLoad(val);
}
if ((isAddr != NULL) && (typedValue.mKind != BfTypedValueKind_SplatHead_NeedsCasting))
{
*isAddr = true;
return val;
}
else
{
val = mBfIRBuilder->CreateLoad(val);
break;
}
}
}
if (val)
break;
checkMethodState = checkMethodState->mPrevMethodState;
}
}
if (val)
{
if (typedValue.mKind == BfTypedValueKind_SplatHead_NeedsCasting)
{
BF_ASSERT(wantType != NULL);
if (wantType != NULL)
return mBfIRBuilder->CreateBitCast(val, mBfIRBuilder->MapType(wantType));
}
return val;
}
BFMODULE_FATAL(this, "Splat not found");
2019-08-23 11:56:54 -07:00
return BfIRValue();
}
BfTypedValue BfModule::ExtractValue(BfTypedValue typedValue, BfFieldInstance* fieldInstance, int fieldIdx)
{
int useFieldIdx = fieldIdx;
BfType* fieldType = NULL;
if (fieldInstance != NULL)
{
fieldType = fieldInstance->mResolvedType;
if (typedValue.mType->IsUnion())
{
if (fieldIdx == 1)
{
if (typedValue.IsSplat())
{
BfTypedValue innerVal = typedValue;
innerVal.mType = fieldType;
return innerVal;
}
}
}
}
else
{
if (typedValue.mType->IsPayloadEnum())
{
auto typeInst = typedValue.mType->ToTypeInstance();
if (fieldIdx == 1)
fieldType = typeInst->GetUnionInnerType();
else if (fieldIdx == 2)
{
fieldType = typeInst->GetDiscriminatorType(&useFieldIdx);
}
}
else if (typedValue.mType->IsUnion())
{
if (fieldIdx == 1)
{
fieldType = typedValue.mType->ToTypeInstance()->GetUnionInnerType();
if (typedValue.IsSplat())
{
BfTypedValue innerVal = typedValue;
innerVal.mType = fieldType;
return innerVal;
}
}
}
}
BF_ASSERT(typedValue.mType->IsStruct());
if (typedValue.IsSplat())
{
if (typedValue.mType->IsPayloadEnum())
{
if (fieldIdx == 1)
{
// Payload
auto typeInst = typedValue.mType->ToTypeInstance();
auto unionInnerType = typeInst->GetUnionInnerType();
bool isAddr = false;
BfIRValue irVal = ExtractSplatValue(typedValue, 0, unionInnerType, &isAddr);
return BfTypedValue(irVal, unionInnerType, BfTypedValueKind_SplatHead);
}
if (fieldIdx == 2)
{
// Discriminator
auto typeInst = typedValue.mType->ToTypeInstance();
auto unionInnerType = typeInst->GetUnionInnerType();
auto dscrType = typeInst->GetDiscriminatorType();
int argIdx = typedValue.mValue.mId;
int componentIdx = 0;
BfTypeUtils::SplatIterate([&](BfType* checkType) { componentIdx++; }, unionInnerType);
bool isAddr;
BfIRValue irVal = ExtractSplatValue(typedValue, componentIdx, dscrType, &isAddr);
return BfTypedValue(irVal, dscrType, isAddr);
}
}
int componentIdx = 0;
BfTypedValue retVal;
BfFieldInstance* wantFieldInstance = fieldInstance;
std::function<void(BfType*)> checkTypeLambda = [&](BfType* checkType)
{
if (checkType->IsStruct())
{
auto checkTypeInstance = checkType->ToTypeInstance();
if ((checkType->IsUnion()) || (checkType->IsEnum()))
{
//TODO: Why did we have this removed? It messed up an extraction from a nullable splattable
fieldType = checkTypeInstance->GetUnionInnerType();
checkTypeLambda(fieldType);
if (checkType->IsEnum())
{
// Past discriminator...
componentIdx++;
}
return;
}
if (checkTypeInstance->mBaseType != NULL)
checkTypeLambda(checkTypeInstance->mBaseType);
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
if (fieldInstance == wantFieldInstance)
{
bool isAddr = false;
BfIRValue val = ExtractSplatValue(typedValue, componentIdx, fieldInstance->mResolvedType, &isAddr);
retVal = BfTypedValue(val, fieldInstance->mResolvedType,
fieldInstance->mResolvedType->IsComposite() ? BfTypedValueKind_SplatHead :
isAddr ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Value);
if ((retVal.IsAddr()) && (!fieldInstance->mResolvedType->IsValueType()))
{
retVal = LoadValue(retVal);
}
break;
}
if (fieldInstance->mDataIdx >= 0)
checkTypeLambda(fieldInstance->GetResolvedType());
}
}
else if (!checkType->IsValuelessType())
{
componentIdx++;
//argIdx++;
}
};
checkTypeLambda(typedValue.mType);
BF_ASSERT(retVal);
return retVal;
}
if (typedValue.IsAddr())
{
BF_ASSERT(fieldType != NULL);
auto valRef = mBfIRBuilder->CreateInBoundsGEP(typedValue.mValue, 0, useFieldIdx);
return BfTypedValue(valRef, fieldType, BfTypedValueKind_ReadOnlyAddr);
}
else
{
BF_ASSERT(fieldType != NULL);
return BfTypedValue(mBfIRBuilder->CreateExtractValue(typedValue.mValue, useFieldIdx), fieldType, BfTypedValueKind_Value);
}
}
BfIRValue BfModule::ExtractValue(BfTypedValue typedValue, int dataIdx)
{
if (typedValue.IsAddr())
{
auto addrVal = mBfIRBuilder->CreateInBoundsGEP(typedValue.mValue, 0, dataIdx);
return mBfIRBuilder->CreateLoad(addrVal);
}
return mBfIRBuilder->CreateExtractValue(typedValue.mValue, dataIdx);
}
BfIRValue BfModule::CreateIndexedValue(BfType* elementType, BfIRValue value, BfIRValue indexValue, bool isElementIndex)
{
if (elementType->IsSizeAligned())
{
if (isElementIndex)
return mBfIRBuilder->CreateInBoundsGEP(value, GetConstValue(0), indexValue);
else
return mBfIRBuilder->CreateInBoundsGEP(value, indexValue);
}
auto ptrType = CreatePointerType(elementType);
auto ofsVal = mBfIRBuilder->CreateNumericCast(indexValue, true, BfTypeCode_IntPtr);
auto ofsScaledVal = mBfIRBuilder->CreateMul(ofsVal, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, elementType->GetStride()));
auto i8PtrType = mBfIRBuilder->GetPointerTo(mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8));
BfIRValue origPtrValue = mBfIRBuilder->CreateBitCast(value, i8PtrType);
BfIRValue newPtrValue = mBfIRBuilder->CreateInBoundsGEP(origPtrValue, ofsScaledVal);
return mBfIRBuilder->CreateBitCast(newPtrValue, mBfIRBuilder->MapType(ptrType));
2019-08-23 11:56:54 -07:00
}
BfIRValue BfModule::CreateIndexedValue(BfType* elementType, BfIRValue value, int indexValue, bool isElementIndex)
{
if (elementType->IsSizeAligned())
{
if (isElementIndex)
return mBfIRBuilder->CreateInBoundsGEP(value, 0, indexValue);
else
return mBfIRBuilder->CreateInBoundsGEP(value, indexValue);
}
return CreateIndexedValue(elementType, value, GetConstValue(indexValue), isElementIndex);
}
bool BfModule::CheckModifyValue(BfTypedValue& typedValue, BfAstNode* refNode, const char* modifyType)
{
if (modifyType == NULL)
modifyType = "modify";
if (typedValue.mType->IsVar())
return true;
if (typedValue.IsReadOnly())
{
Fail(StrFormat("Cannot %s read-only variable", modifyType), refNode);
return false;
}
if ((!typedValue.IsAddr()) && (!typedValue.mType->IsValuelessType()))
{
Fail(StrFormat("Cannot %s value", modifyType), refNode);
return false;
}
if (typedValue.mKind == BfTypedValueKind_TempAddr)
{
2019-11-21 08:23:18 -08:00
Fail(StrFormat("Cannot %s temporary value", modifyType), refNode);
2019-08-23 11:56:54 -07:00
return false;
}
return true;
}
bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB)
{
// If one is an interface and the other is an impl, B is the impl
auto implOwner = methodB->GetOwner();
2019-08-23 11:56:54 -07:00
if (methodA->mMethodDef->mIsLocalMethod)
{
int sepPosA = (int)BF_MIN(methodA->mMethodDef->mName.IndexOf('@'), methodA->mMethodDef->mName.length());
int sepPosB = (int)BF_MIN(methodB->mMethodDef->mName.IndexOf('@'), methodB->mMethodDef->mName.length());
if (sepPosA != sepPosB)
return false;
if (strncmp(methodA->mMethodDef->mName.c_str(), methodB->mMethodDef->mName.c_str(), sepPosA) != 0)
return false;
}
else if (methodA->mMethodDef->mName != methodB->mMethodDef->mName)
return false;
if (methodA->mMethodDef->mCheckedKind != methodB->mMethodDef->mCheckedKind)
return false;
2019-08-23 11:56:54 -07:00
if (methodA->mMethodDef->mMethodType == BfMethodType_Ctor)
{
if (methodA->mMethodDef->mIsStatic != methodB->mMethodDef->mIsStatic)
return false;
}
if (methodA->mMethodDef->mMethodType == BfMethodType_Operator)
{
if (methodB->mMethodDef->mMethodType != BfMethodType_Operator)
return false;
auto operatorA = (BfOperatorDef*)methodA->mMethodDef;
auto operatorB = (BfOperatorDef*)methodB->mMethodDef;
if (operatorA->mOperatorDeclaration->mUnaryOp != operatorB->mOperatorDeclaration->mUnaryOp)
return false;
2020-04-28 09:58:02 -07:00
if (operatorA->mOperatorDeclaration->mBinOp != operatorB->mOperatorDeclaration->mBinOp)
return false;
if (operatorA->mOperatorDeclaration->mAssignOp != operatorB->mOperatorDeclaration->mAssignOp)
return false;
2019-08-23 11:56:54 -07:00
if (operatorA->mOperatorDeclaration->mIsConvOperator)
{
if (!BfTypeUtils::TypeEquals(methodA->mReturnType, methodB->mReturnType, implOwner))
2019-08-23 11:56:54 -07:00
return false;
}
}
int implicitParamCountA = methodA->GetImplicitParamCount();
int implicitParamCountB = methodB->GetImplicitParamCount();
if (methodA->GetParamCount() - implicitParamCountA != methodB->GetParamCount() - implicitParamCountB)
return false;
if (methodA->mHadGenericDelegateParams != methodB->mHadGenericDelegateParams)
return false;
for (int paramIdx = 0; paramIdx < (int)methodA->GetParamCount() - implicitParamCountA; paramIdx++)
{
if ((!BfTypeUtils::TypeEquals(methodA->GetParamType(paramIdx + implicitParamCountA), methodB->GetParamType(paramIdx + implicitParamCountB), implOwner)) ||
2019-08-23 11:56:54 -07:00
(methodA->GetParamKind(paramIdx + implicitParamCountA) != methodB->GetParamKind(paramIdx + implicitParamCountB)))
return false;
}
// Compare generic params. Generic params are part of the method signature here
if (methodA->GetNumGenericParams() != methodB->GetNumGenericParams())
return false;
for (int genericParamIdx = 0; genericParamIdx < (int)methodA->GetNumGenericParams(); genericParamIdx++)
{
auto genericParamA = methodA->mMethodInfoEx->mGenericParams[genericParamIdx];
auto genericParamB = methodB->mMethodInfoEx->mGenericParams[genericParamIdx];
if (genericParamA->mGenericParamFlags != genericParamB->mGenericParamFlags)
return false;
if (genericParamA->mTypeConstraint != genericParamB->mTypeConstraint)
return false;
if (genericParamA->mInterfaceConstraints.size() != genericParamB->mInterfaceConstraints.size())
return false;
for (int interfaceIdx = 0; interfaceIdx < (int)genericParamA->mInterfaceConstraints.size(); interfaceIdx++)
if (genericParamA->mInterfaceConstraints[interfaceIdx] != genericParamB->mInterfaceConstraints[interfaceIdx])
return false;
}
return true;
}
bool BfModule::StrictCompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstance* methodB)
{
if (!CompareMethodSignatures(methodA, methodB))
return false;
if (methodA->mReturnType != methodB->mReturnType)
return false;
if (methodA->mMethodDef->mIsStatic != methodB->mMethodDef->mIsStatic)
return false;
return true;
}
2019-08-23 11:56:54 -07:00
bool BfModule::IsCompatibleInterfaceMethod(BfMethodInstance* iMethodInst, BfMethodInstance* methodInstance)
{
if (iMethodInst->mMethodDef->mName != methodInstance->mMethodDef->mName)
return false;
if (iMethodInst->mMethodDef->mIsOperator != methodInstance->mMethodDef->mIsOperator)
return false;
if (iMethodInst->mMethodDef->mIsOperator)
{
auto iOperatorDef = (BfOperatorDef*)iMethodInst->mMethodDef;
auto operatorDef = (BfOperatorDef*)methodInstance->mMethodDef;
if (iOperatorDef->mOperatorDeclaration->mUnaryOp != operatorDef->mOperatorDeclaration->mUnaryOp)
return false;
if (iOperatorDef->mOperatorDeclaration->mBinOp != operatorDef->mOperatorDeclaration->mBinOp)
return false;
}
if (iMethodInst->GetParamCount() != methodInstance->GetParamCount())
return false;
for (int paramIdx = 0; paramIdx < (int)iMethodInst->GetParamCount(); paramIdx++)
{
if (iMethodInst->GetParamKind(paramIdx) != methodInstance->GetParamKind(paramIdx))
return false;
BfType* iParamType = iMethodInst->GetParamType(paramIdx);
BfType* methodParamType = methodInstance->GetParamType(paramIdx);
if (iParamType->IsSelf())
{
if (methodParamType != methodInstance->GetOwner())
return false;
}
else if (!iParamType->IsGenericParam())
{
if (methodParamType != iParamType)
return false;
}
else
{
if (!methodParamType->IsGenericParam())
return false;
auto genericParamType = (BfGenericParamType*)methodParamType;
if (genericParamType->mGenericParamKind == BfGenericParamKind_Type)
return false;
auto genericParam = methodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx];
bool hadInterface = false;
for (auto interfaceConstraint : genericParam->mInterfaceConstraints)
if (interfaceConstraint == iParamType)
hadInterface = true;
if (!hadInterface)
return false;
}
}
return true;
}
void BfModule::AddMethodReference(const BfMethodRef& methodRef, BfGetMethodInstanceFlags flags)
{
BfMethodInstance* methodInstance = methodRef;
if ((mCurTypeInstance != NULL) && (!mCompiler->IsAutocomplete()))
{
auto methodInstanceGroup = methodInstance->mMethodInstanceGroup;
if ((methodInstance->IsSpecializedGenericMethod()) || (methodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_AlwaysInclude))
{
BF_ASSERT(!methodRef.mTypeInstance->IsFunction());
BfMethodRef methodRef = methodInstance;
BfSpecializedMethodRefInfo* specializedMethodRefInfo = NULL;
bool isNew = mCurTypeInstance->mSpecializedMethodReferences.TryAdd(methodRef, NULL, &specializedMethodRefInfo);
2019-08-23 11:56:54 -07:00
if (((flags & BfGetMethodInstanceFlag_Unreified) == 0) &&
((flags & BfGetMethodInstanceFlag_NoForceReification) == 0) &&
(mIsReified))
{
specializedMethodRefInfo->mHasReifiedRef = true;
}
}
}
}
BfModuleMethodInstance BfModule::ReferenceExternalMethodInstance(BfMethodInstance* methodInstance, BfGetMethodInstanceFlags flags)
{
2019-08-23 11:56:54 -07:00
if ((flags & BfGetMethodInstanceFlag_ResultNotUsed) != 0)
return BfModuleMethodInstance(methodInstance, BfIRValue());
if (mBfIRBuilder == NULL)
return BfModuleMethodInstance(methodInstance, BfIRValue());
if (methodInstance->GetOwner()->IsFunction())
{
// No actual ref needed- not an actual generated function
return BfModuleMethodInstance(methodInstance, BfIRValue());
}
bool isGenFunction = methodInstance->mMethodInstanceGroup->mMethodIdx < 0;
if (!isGenFunction)
AddMethodReference(methodInstance, flags);
if (mBfIRBuilder->mIgnoreWrites)
return BfModuleMethodInstance(methodInstance, mBfIRBuilder->GetFakeVal());
if (mCompiler->IsSkippingExtraResolveChecks())
return BfModuleMethodInstance(methodInstance, BfIRFunction());
if (methodInstance->mMethodDef->mMethodType == BfMethodType_Mixin)
{
return BfModuleMethodInstance(methodInstance, BfIRFunction());
}
bool isInlined = (methodInstance->mAlwaysInline) || ((flags & BfGetMethodInstanceFlag_ForceInline) != 0);
BfMethodRef methodRef = methodInstance;
if (isInlined)
methodRef.mMethodRefFlags = (BfMethodRefFlags)(methodRef.mMethodRefFlags | BfMethodRefFlag_AlwaysInclude);
else
methodRef.mMethodRefFlags = BfMethodRefFlag_None;
if (!isGenFunction)
{
BfIRValue* irFuncPtr = NULL;
if (mFuncReferences.TryGetValue(methodRef, &irFuncPtr))
return BfModuleMethodInstance(methodInstance, *irFuncPtr);
}
if (mAwaitingInitFinish)
FinishInit();
BfIRValue func = CreateFunctionFrom(methodInstance, isGenFunction, isInlined);
if (!isGenFunction)
mFuncReferences[methodRef] = func;
BfLogSysM("Adding func reference. Module:%p MethodInst:%p NewLLVMFunc:%d OldLLVMFunc:%d\n", this, methodInstance, func.mId, methodInstance->mIRFunction.mId);
if ((isInlined) && (!mIsScratchModule) && ((flags & BfGetMethodInstanceFlag_NoInline) == 0))
2019-08-23 11:56:54 -07:00
{
// We can't just add a dependency to mCurTypeInstance because we may have nested inlined functions, and
// mCurTypeInstance will just reflect the owner of the method currently being inlined, not the top-level
// type instance
// Be smarter about this if we ever insert a lot of type instances into a single module - track in a field
BF_ASSERT(mOwnedTypeInstances.size() <= 1);
for (auto ownedTypeInst : mOwnedTypeInstances)
AddDependency(methodInstance->GetOwner(), ownedTypeInst, BfDependencyMap::DependencyFlag_InlinedCall);
if ((!mCompiler->mIsResolveOnly) && (mIsReified) && (!methodInstance->mIsUnspecialized))
{
mIncompleteMethodCount++;
BfInlineMethodRequest* inlineMethodRequest = mContext->mInlineMethodWorkList.Alloc();
inlineMethodRequest->mType = methodInstance->GetOwner();
inlineMethodRequest->mFromModule = this;
inlineMethodRequest->mFunc = func;
inlineMethodRequest->mFromModuleRevision = mRevision;
inlineMethodRequest->mMethodInstance = methodInstance;
2019-08-23 11:56:54 -07:00
BF_ASSERT(mIsModuleMutable);
BfLogSysM("mInlineMethodWorkList %p for method %p in module %p in ReferenceExternalMethodInstance\n", inlineMethodRequest, methodInstance, this);
}
}
return BfModuleMethodInstance(methodInstance, func);
}
BfModule* BfModule::GetOrCreateMethodModule(BfMethodInstance* methodInstance)
{
BfTypeInstance* typeInst = methodInstance->mMethodInstanceGroup->mOwner;
if (mBfIRBuilder == NULL)
{
// To allow for custom attribute data
PrepareForIRWriting(typeInst);
}
2019-08-23 11:56:54 -07:00
BfModule* declareModule = this;
// Get to the optionless branch head -- but this could still be a specialized method module, not the true root
while ((declareModule->mParentModule != NULL) && (!declareModule->mIsSpecializedMethodModuleRoot))
declareModule = declareModule->mParentModule;
// Check for attributes
if (typeInst == mContext->mBfObjectType)
{
auto methodDecl = methodInstance->mMethodDef->GetMethodDeclaration();
if (methodDecl != NULL)
{
auto attributesDirective = methodDecl->mAttributes;
if ((attributesDirective != NULL) && (mCompiler->mResolvePassData != NULL) &&
(attributesDirective->IsFromParser(mCompiler->mResolvePassData->mParser)) && (mCompiler->mResolvePassData->mSourceClassifier != NULL))
{
mCompiler->mResolvePassData->mSourceClassifier->VisitChild(attributesDirective);
}
}
}
else
{
// Only allow attributes on System.Object methods that can be handled inside the DefBuilder
GetMethodCustomAttributes(methodInstance);
}
BF_ASSERT(mModuleOptions == NULL);
if (methodInstance->GetCustomAttributes() != NULL)
{
auto project = typeInst->mTypeDef->mProject;
BfModuleOptions moduleOptions = declareModule->GetModuleOptions();
BfModuleOptions wantOptions = moduleOptions;
auto typeOptions = mSystem->GetTypeOptions(typeInst->mTypeOptionsIdx);
if (typeOptions != NULL)
{
wantOptions.mOptLevel = (BfOptLevel)BfTypeOptions::Apply((int)wantOptions.mOptLevel, (int)typeOptions->mOptimizationLevel);
wantOptions.mSIMDSetting = (BfSIMDSetting)BfTypeOptions::Apply((int)wantOptions.mSIMDSetting, typeOptions->mSIMDSetting);
wantOptions.mEmitDebugInfo = (BfSIMDSetting)BfTypeOptions::Apply((int)wantOptions.mEmitDebugInfo, typeOptions->mEmitDebugInfo);
}
if (!mCompiler->mAttributeTypeOptionMap.IsEmpty())
{
StringT<128> attrName;
for (auto& customAttrs : methodInstance->GetCustomAttributes()->mAttributes)
{
attrName.Clear();
customAttrs.mType->mTypeDef->mFullName.ToString(attrName);
Array<int>* arrPtr;
if (mCompiler->mAttributeTypeOptionMap.TryGetValue(attrName, &arrPtr))
{
for (auto optionsIdx : *arrPtr)
{
auto& typeOptions = mCompiler->mSystem->mTypeOptions[optionsIdx];
wantOptions.mOptLevel = (BfOptLevel)BfTypeOptions::Apply((int)wantOptions.mOptLevel, (int)typeOptions.mOptimizationLevel);
wantOptions.mSIMDSetting = (BfSIMDSetting)BfTypeOptions::Apply((int)wantOptions.mSIMDSetting, typeOptions.mSIMDSetting);
wantOptions.mEmitDebugInfo = (BfSIMDSetting)BfTypeOptions::Apply((int)wantOptions.mEmitDebugInfo, typeOptions.mEmitDebugInfo);
}
}
}
}
if ((HasCompiledOutput()) && (wantOptions != moduleOptions) && (!mIsScratchModule))
{
auto lastCheckModule = this;
auto checkModule = this->mNextAltModule;
while (checkModule != NULL)
{
if (*checkModule->mModuleOptions == wantOptions)
{
declareModule = checkModule;
break;
}
lastCheckModule = checkModule;
checkModule = checkModule->mNextAltModule;
}
// Not found?
if (declareModule == this)
{
String specModuleName = mModuleName + "@@";
if (wantOptions.mOptLevel != moduleOptions.mOptLevel)
specModuleName += StrFormat("O%d", wantOptions.mOptLevel);
if (wantOptions.mSIMDSetting != moduleOptions.mSIMDSetting)
specModuleName += StrFormat("SIMD%d", wantOptions.mSIMDSetting);
declareModule = new BfModule(mContext, specModuleName);
declareModule->mProject = project;
declareModule->mModuleOptions = new BfModuleOptions();
*declareModule->mModuleOptions = wantOptions;
declareModule->mParentModule = lastCheckModule;
declareModule->Init();
lastCheckModule->mNextAltModule = declareModule;
}
}
}
declareModule->PrepareForIRWriting(typeInst);
2019-08-23 11:56:54 -07:00
return declareModule;
}
BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfMethodDef* methodDef, const BfTypeVector& methodGenericArguments, BfGetMethodInstanceFlags flags, BfTypeInstance* foreignType)
2020-04-16 00:32:00 -07:00
{
2019-08-23 11:56:54 -07:00
if (((flags & BfGetMethodInstanceFlag_ForceInline) != 0) && (mCompiler->mIsResolveOnly))
{
// Don't bother inlining for resolve-only
flags = (BfGetMethodInstanceFlags)(flags & ~BfGetMethodInstanceFlag_ForceInline);
}
bool processNow = false;
bool keepInCurrentModule = false;
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized))
flags = (BfGetMethodInstanceFlags)(flags | BfGetMethodInstanceFlag_NoForceReification);
if (!typeInst->IsTypeMemberIncluded(methodDef->mDeclaringType))
{
return BfModuleMethodInstance();
}
if (methodDef->mIsLocalMethod)
{
auto rootMethodState = mCurMethodState->GetRootMethodState();
BfLocalMethod* localMethod;
if (rootMethodState->mLocalMethodCache.TryGetValue(methodDef->mName, &localMethod))
{
// Handle the def in the correct method state
return GetLocalMethodInstance(localMethod, methodGenericArguments);
}
BFMODULE_FATAL(this, "Cannot find local method");
2019-08-23 11:56:54 -07:00
return BfModuleMethodInstance();
}
#ifdef _DEBUG
for (auto arg : methodGenericArguments)
BF_ASSERT(!arg->IsVar());
#endif
BF_ASSERT(methodDef->mMethodType != BfMethodType_Ignore);
// We need to do the 'mNeedsMethodProcessing' check because we want to do a proper initial "awaiting reference" population
// on the methods before we handle an on-demand situation. This also ensures that our type options are set before doing
// a FinishInit
if ((typeInst->IsIncomplete()) &&
(!typeInst->mResolvingVarField) && (!typeInst->mTypeFailed))
{
// For autocomplete, we still may not actually generate methods. This shouldn't matter, and on-demand works differently
// for resolve-only because we don't differentiate between reified/unreified there
PopulateType(typeInst, BfPopulateType_Full);
}
bool tryModuleMethodLookup = false;
BfModuleMethodInstance moduleMethodInst;
BfModule* instModule = typeInst->mModule;
if (keepInCurrentModule)
{
// Stay here
instModule = this;
}
if (this == mContext->mUnreifiedModule)
{
// Stay in this 'resolve only' module here
}
else if (flags & BfGetMethodInstanceFlag_ExplicitResolveOnlyPass)
{
BF_ASSERT(this == mContext->mUnreifiedModule);
}
else if (flags & BfGetMethodInstanceFlag_ExplicitSpecializedModule)
{
BF_ASSERT(instModule == mParentModule);
}
else if (instModule != this)
2019-11-25 06:47:41 -08:00
{
2019-08-23 11:56:54 -07:00
if ((!mIsReified) && (instModule->mIsReified))
{
BF_ASSERT(!mCompiler->mIsResolveOnly);
// A resolve-only module is specializing a method from a type in a reified module,
// we need to take care that this doesn't cause anything new to become reified
return mContext->mUnreifiedModule->GetMethodInstance(typeInst, methodDef, methodGenericArguments, (BfGetMethodInstanceFlags)(flags | BfGetMethodInstanceFlag_ExplicitResolveOnlyPass), foreignType);
}
else
{
if ((mIsReified) && (!instModule->mIsReified))
{
if (!typeInst->IsUnspecializedTypeVariation())
2019-08-23 11:56:54 -07:00
{
if (!instModule->mReifyQueued)
{
BF_ASSERT((instModule != mContext->mUnreifiedModule) && (instModule != mContext->mScratchModule));
BF_ASSERT((mCompiler->mCompileState != BfCompiler::CompileState_Unreified) && (mCompiler->mCompileState != BfCompiler::CompileState_VData));
BfLogSysM("Queueing ReifyModule: %p\n", instModule);
mContext->mReifyModuleWorkList.Add(instModule);
instModule->mReifyQueued = true;
}
2019-08-23 11:56:54 -07:00
// This ensures that the method will actually be created when it gets reified
BfMethodSpecializationRequest* specializationRequest = mContext->mMethodSpecializationWorkList.Alloc();
specializationRequest->mFromModule = typeInst->mModule;
specializationRequest->mFromModuleRevision = typeInst->mModule->mRevision;
specializationRequest->mMethodDef = methodDef;
specializationRequest->mMethodGenericArguments = methodGenericArguments;
specializationRequest->mType = typeInst;
specializationRequest->mFlags = flags;
}
2019-08-23 11:56:54 -07:00
}
auto defFlags = (BfGetMethodInstanceFlags)(flags & ~BfGetMethodInstanceFlag_ForceInline);
2019-11-25 06:47:41 -08:00
2019-08-23 11:56:54 -07:00
// Not extern
// Create the instance in the proper module and then create a reference in this one
moduleMethodInst = instModule->GetMethodInstance(typeInst, methodDef, methodGenericArguments, defFlags, foreignType);
tryModuleMethodLookup = true;
if ((mIsReified) && (!moduleMethodInst.mMethodInstance->mIsReified))
{
CheckHotMethod(moduleMethodInst.mMethodInstance, "");
}
}
}
if (tryModuleMethodLookup)
return ReferenceExternalMethodInstance(moduleMethodInst.mMethodInstance, flags);
if (((flags & BfGetMethodInstanceFlag_ForceInline) != 0) && (!methodDef->mAlwaysInline))
{
auto moduleMethodInstance = GetMethodInstance(typeInst, methodDef, methodGenericArguments, (BfGetMethodInstanceFlags)(flags & ~BfGetMethodInstanceFlag_ForceInline), foreignType);
if (moduleMethodInstance)
return ReferenceExternalMethodInstance(moduleMethodInstance.mMethodInstance, flags);
return moduleMethodInst;
}
bool isReified = mIsReified /* || ((flags & BfGetMethodInstanceFlag_ExplicitResolveOnlyPass) != 0)*/;
bool hadConcreteInterfaceGenericArgument = false;
if ((flags & BfGetMethodInstanceFlag_Unreified) != 0)
isReified = false;
if ((flags & BfGetMethodInstanceFlag_ExplicitResolveOnlyPass) != 0)
{
isReified = false;
}
BfTypeVector sanitizedMethodGenericArguments;
SizedArray<BfProject*, 4> projectList;
2019-08-23 11:56:54 -07:00
bool isUnspecializedPass = (flags & BfGetMethodInstanceFlag_UnspecializedPass) != 0;
if ((isUnspecializedPass) && (methodDef->mGenericParams.size() == 0))
isUnspecializedPass = false;
// Check for whether we are an extension method from a project that does not hold the root type, AND that isn't already the project for this type
bool isExternalExtensionMethod = false;
if ((!typeInst->IsUnspecializedType()) && (!isUnspecializedPass))
{
if (((flags & BfGetMethodInstanceFlag_ForeignMethodDef) == 0) && (methodDef->mDeclaringType->mProject != typeInst->mTypeDef->mProject))
{
auto specProject = methodDef->mDeclaringType->mProject;
isExternalExtensionMethod = true;
if (typeInst->IsGenericTypeInstance())
{
auto genericTypeInst = (BfTypeInstance*)typeInst;
if (genericTypeInst->mGenericTypeInfo->mProjectsReferenced.empty())
2019-08-23 11:56:54 -07:00
genericTypeInst->GenerateProjectsReferenced();
if (genericTypeInst->mGenericTypeInfo->mProjectsReferenced.Contains(specProject))
2019-08-23 11:56:54 -07:00
{
// This is a generic type where a generic param is already confined to the project in question
isExternalExtensionMethod = false;
}
}
if (isExternalExtensionMethod)
{
BF_ASSERT(typeInst->mTypeDef->mIsCombinedPartial);
projectList.push_back(methodDef->mDeclaringType->mProject);
}
}
}
if ((!isUnspecializedPass) && (methodGenericArguments.size() != 0))
{
int typeProjectsCounts = 0;
if (typeInst->IsGenericTypeInstance())
{
auto genericTypeInst = (BfTypeInstance*)typeInst;
if (genericTypeInst->mGenericTypeInfo->mProjectsReferenced.empty())
2019-08-23 11:56:54 -07:00
genericTypeInst->GenerateProjectsReferenced();
typeProjectsCounts = (int)genericTypeInst->mGenericTypeInfo->mProjectsReferenced.size();
projectList.Insert(0, &genericTypeInst->mGenericTypeInfo->mProjectsReferenced[0], genericTypeInst->mGenericTypeInfo->mProjectsReferenced.size());
2019-08-23 11:56:54 -07:00
}
else
{
typeProjectsCounts = 1;
projectList.Insert(0, typeInst->mTypeDef->mProject);
}
isUnspecializedPass = true;
for (int genericArgIdx = 0; genericArgIdx < (int) methodGenericArguments.size(); genericArgIdx++)
{
auto genericArgType = methodGenericArguments[genericArgIdx];
//BF_ASSERT(!genericArgType->IsRef());
if (genericArgType->IsConcreteInterfaceType())
{
hadConcreteInterfaceGenericArgument = true;
auto concreteInterfaceType = (BfConcreteInterfaceType*)genericArgType;
genericArgType = concreteInterfaceType->mInterface;
}
if (genericArgType->IsGenericParam())
{
auto genericParam = (BfGenericParamType*) genericArgType;
if ((genericParam->mGenericParamKind != BfGenericParamKind_Method) || (genericParam->mGenericParamIdx != genericArgIdx))
isUnspecializedPass = false;
}
else
{
BfTypeUtils::GetProjectList(genericArgType, &projectList, typeProjectsCounts);
isUnspecializedPass = false;
}
sanitizedMethodGenericArguments.push_back(genericArgType);
}
if ((int)projectList.size() > typeProjectsCounts)
{
// Just leave the new items
projectList.RemoveRange(0, typeProjectsCounts);
std::sort(projectList.begin(), projectList.end(), [](BfProject* lhs, BfProject*rhs)
{
return lhs->mName < rhs->mName;
});
2019-08-23 11:56:54 -07:00
}
else
{
projectList.Clear();
}
}
const BfTypeVector& lookupMethodGenericArguments = isUnspecializedPass ? BfTypeVector() : sanitizedMethodGenericArguments;
BfMethodInstanceGroup* methodInstGroup = NULL;
if ((flags & BfGetMethodInstanceFlag_ForeignMethodDef) != 0)
{
BF_ASSERT(!typeInst->IsInterface());
//for (auto& checkGroup : typeInst->mMethodInstanceGroups)
for (int methodIdx = (int)typeInst->mTypeDef->mMethods.size(); methodIdx < (int)typeInst->mMethodInstanceGroups.size(); methodIdx++)
{
auto& checkGroup = typeInst->mMethodInstanceGroups[methodIdx];
// if (checkGroup.mDefault == NULL)
// {
// // This should only be for when we explicitly add a spot so we can set mOnDemandKind
// BF_ASSERT(methodIdx == typeInst->mMethodInstanceGroups.size() - 1);
// methodInstGroup = &checkGroup;
// break;
// }
if ((checkGroup.mDefault != NULL) && (checkGroup.mDefault->mMethodDef == methodDef))
{
methodInstGroup = &checkGroup;
break;
}
}
if (methodInstGroup == NULL)
{
// Allocate a new entry
typeInst->mMethodInstanceGroups.Resize(typeInst->mMethodInstanceGroups.size() + 1);
methodInstGroup = &typeInst->mMethodInstanceGroups.back();
methodInstGroup->mMethodIdx = (int)typeInst->mMethodInstanceGroups.size() - 1;
methodInstGroup->mOwner = typeInst;
// if (methodInstGroup->mOnDemandKind == BfMethodOnDemandKind_NotSet)
// methodInstGroup->mOnDemandKind = BfMethodOnDemandKind_AlwaysInclude;
if (mCompiler->mOptions.mCompileOnDemandKind == BfCompileOnDemandKind_AlwaysInclude)
methodInstGroup->mOnDemandKind = BfMethodOnDemandKind_AlwaysInclude;
else
methodInstGroup->mOnDemandKind = BfMethodOnDemandKind_Decl_AwaitingDecl;
}
}
else
{
methodInstGroup = &typeInst->mMethodInstanceGroups[methodDef->mIdx];
}
if (methodInstGroup->mOnDemandKind == BfMethodOnDemandKind_NotSet)
{
BfLogSysM("Forcing BfMethodOnDemandKind_NotSet to BfMethodOnDemandKind_AlwaysInclude for Method:%s in Type:%p\n", methodDef->mName.c_str(), typeInst);
methodInstGroup->mOnDemandKind = BfMethodOnDemandKind_AlwaysInclude;
}
BfIRFunction prevIRFunc;
bool doingRedeclare = false;
BfMethodInstance* methodInstance = NULL;
auto _SetReified = [&]()
{
methodInstance->mIsReified = true;
};
if (lookupMethodGenericArguments.size() == 0)
{
methodInstance = methodInstGroup->mDefault;
2019-08-23 11:56:54 -07:00
if ((methodInstance != NULL) && (isReified) && (!methodInstance->mIsReified))
{
MarkDerivedDirty(typeInst);
if (mIsScratchModule)
{
BfLogSysM("Marking scratch module method instance as reified: %p\n", methodInstance);
_SetReified();
CheckHotMethod(methodInstance, "");
}
else if ((flags & BfGetMethodInstanceFlag_NoForceReification) != 0)
{
// We don't need to force reification if we're in an unspecialized generic- we can wait for
// the specializations to handle that for us
return BfModuleMethodInstance(methodInstance);
}
else if (methodInstGroup->mOnDemandKind == BfMethodOnDemandKind_Referenced)
{
if (methodDef->mIsAbstract)
{
BfLogSysM("Reifying abstract unreified method instance: %p IRFunction:%d\n", methodInstance, methodInstance->mIRFunction.mId);
_SetReified();
}
else
{
BfLogSysM("Re-declaring processed but unreified method instance: %p IRFunction:%d\n", methodInstance, methodInstance->mIRFunction.mId);
methodInstGroup->mOnDemandKind = BfMethodOnDemandKind_NoDecl_AwaitingReference;
methodInstance->UndoDeclaration(!methodInstance->mIRFunction.IsFake());
doingRedeclare = true;
mOnDemandMethodCount++;
}
}
else
{
BfLogSysM("Marking unprocessed method instance as reified: %p\n", methodInstance);
// We haven't processed it yet
_SetReified();
CheckHotMethod(methodInstance, "");
if (methodInstance->mDeclModule == this)
{
if (methodInstance->mMethodProcessRequest != NULL)
{
// Disconnect method process request
BF_ASSERT(methodInstance->mMethodProcessRequest->mFromModule == this);
}
}
else
{
if (methodInstance->mMethodProcessRequest != NULL)
{
// Disconnect method process request
BF_ASSERT(methodInstance->mMethodProcessRequest->mFromModule == mContext->mUnreifiedModule);
methodInstance->mMethodProcessRequest->mMethodInstance = NULL;
methodInstance->mMethodProcessRequest = NULL;
}
if (methodInstance->mDeclModule == mContext->mUnreifiedModule)
{
// Add new request in proper module
methodInstance->mDeclModule = this;
}
else
{
if (methodInstance->mDeclModule == NULL)
{
//
}
else if (methodInstance->mDeclModule != this)
{
// Is from specialized module?
BF_ASSERT(methodInstance->mDeclModule->mParentModule == this);
}
}
if ((!methodInstance->mIRFunction) || (methodInstance->mIRFunction.IsFake()))
{
methodInstance->mIRFunction = BfIRFunction();
if (!mIsModuleMutable)
StartExtension();
if ((!mBfIRBuilder->mIgnoreWrites) && (methodInstance->mDeclModule != NULL))
2019-08-23 11:56:54 -07:00
{
StringT<128> mangledName;
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), methodInstance);
bool isIntrinsic = false;
SetupIRFunction(methodInstance, mangledName, false, &isIntrinsic);
BfLogSysM("Creating new IRFunction %d\n", methodInstance->mIRFunction.mId);
}
}
AddMethodToWorkList(methodInstance);
}
}
}
}
else
{
BF_ASSERT(lookupMethodGenericArguments.size() != 0);
if (methodInstGroup->mMethodSpecializationMap == NULL)
methodInstGroup->mMethodSpecializationMap = new BfMethodInstanceGroup::MapType();
BfMethodInstance** methodInstancePtr = NULL;
if (methodInstGroup->mMethodSpecializationMap->TryGetValue(lookupMethodGenericArguments, &methodInstancePtr))
{
methodInstance = *methodInstancePtr;
if ((isReified) && (!methodInstance->mIsReified))
{
MarkDerivedDirty(typeInst);
if (methodInstance->mHasBeenProcessed)
{
BfLogSysM("Deleting processed but unreified specialized method instance %p\n", methodInstance);
delete methodInstance;
methodInstGroup->mMethodSpecializationMap->Remove(lookupMethodGenericArguments);
methodInstance = NULL;
}
else if ((!methodInstance->mIRFunction) || (methodInstance->mIRFunction.IsFake()))
{
BfLogSysM("Deleting declared but uncreated specialized method instance %p\n", methodInstance);
delete methodInstance;
methodInstGroup->mMethodSpecializationMap->Remove(lookupMethodGenericArguments);
methodInstance = NULL;
}
else
{
BfLogSysM("Reifying declared but unreified method instance %p\n", methodInstance);
// We haven't processed it yet
_SetReified();
CheckHotMethod(methodInstance, "");
}
}
}
}
if ((methodInstance != NULL) && (!doingRedeclare))
{
2019-08-23 11:56:54 -07:00
if (methodInstance->mMethodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingReference)
{
2019-08-23 11:56:54 -07:00
/*if ((!mCompiler->mIsResolveOnly) && (!isReified))
BF_ASSERT(!methodInstance->mIsReified);*/
if (methodInstance->mIsReified != isReified)
BfLogSysM("GetMethodInstance %p Decl_AwaitingReference setting reified to %d\n", methodInstance, isReified);
if ((!isReified) &&
((methodInstance->mDeclModule == NULL) || (!methodInstance->mDeclModule->mIsModuleMutable)))
{
methodInstance->mDeclModule = mContext->mUnreifiedModule;
methodInstance->mIRFunction = BfIRFunction();
}
methodInstance->mIsReified = isReified;
if ((methodInstance->mHotMethod != NULL) && (!mCompiler->IsHotCompile()) && (isReified))
{
methodInstance->mHotMethod->mFlags = (BfHotDepDataFlags)(methodInstance->mHotMethod->mFlags | BfHotDepDataFlag_IsOriginalBuild);
}
if (!methodInstance->mMethodDef->mIsAbstract)
{
AddMethodToWorkList(methodInstance);
}
else
{
methodInstance->mMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Referenced;
}
}
if (mExtensionCount > 0)
{
if ((mIsModuleMutable) && (!methodInstance->mIRFunction) &&
((projectList.IsEmpty() || ((flags & BfGetMethodInstanceFlag_ExplicitSpecializedModule) != 0))))
{
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, false);
if (mAwaitingInitFinish)
FinishInit();
// We need to refer to a function that was defined in a prior module
bool isInlined = (methodInstance->mAlwaysInline) || ((flags & BfGetMethodInstanceFlag_ForceInline) != 0);
methodInstance->mIRFunction = CreateFunctionFrom(methodInstance, false, isInlined);
BF_ASSERT((methodInstance->mDeclModule == this) || (methodInstance->mDeclModule == mContext->mUnreifiedModule) || (methodInstance->mDeclModule == NULL));
methodInstance->mDeclModule = this;
// Add this inlined def to ourselves
if ((methodInstance->mAlwaysInline) && (HasCompiledOutput()) && (!methodInstance->mIsUnspecialized))
{
mIncompleteMethodCount++;
BfInlineMethodRequest* inlineMethodRequest = mContext->mInlineMethodWorkList.Alloc();
inlineMethodRequest->mType = typeInst;
inlineMethodRequest->mFromModule = this;
inlineMethodRequest->mFunc = methodInstance->mIRFunction;
inlineMethodRequest->mFromModuleRevision = mRevision;
inlineMethodRequest->mMethodInstance = methodInstance;
BfLogSysM("mInlineMethodWorkList %p for method %p in module %p in GetMethodInstance\n", inlineMethodRequest, methodInstance, this);
BF_ASSERT(mIsModuleMutable);
}
}
}
2019-08-23 11:56:54 -07:00
if (mCompiler->IsSkippingExtraResolveChecks())
return BfModuleMethodInstance(methodInstance, BfIRFunction());
else
{
2019-08-23 11:56:54 -07:00
if (methodInstance->mDeclModule != this)
return ReferenceExternalMethodInstance(methodInstance, flags);
if ((!methodInstance->mIRFunction) && (mIsModuleMutable) && (!mBfIRBuilder->mIgnoreWrites))
{
auto importKind = methodInstance->GetImportCallKind();
if (importKind != BfImportCallKind_None)
{
BfLogSysM("DllImportGlobalVar creating %p\n", methodInstance);
methodInstance->mIRFunction = mBfIRBuilder->GetFakeVal();
mFuncReferences[methodInstance] = CreateDllImportGlobalVar(methodInstance, true);;
}
}
return BfModuleMethodInstance(methodInstance);
}
}
if (!doingRedeclare)
{
BfModule* specModule = NULL;
if ((!keepInCurrentModule) && (projectList.size() > 0) && (!isUnspecializedPass) && (HasCompiledOutput()) && (!mIsScratchModule))
{
specModule = GetSpecializedMethodModule(projectList);
}
if ((specModule != NULL) && (specModule != this))
{
auto specMethodInstance = specModule->GetMethodInstance(typeInst, methodDef, methodGenericArguments, (BfGetMethodInstanceFlags)(flags | BfGetMethodInstanceFlag_ExplicitSpecializedModule));
if (mAwaitingInitFinish)
return BfModuleMethodInstance(specMethodInstance.mMethodInstance, BfIRFunction());
return ReferenceExternalMethodInstance(specMethodInstance.mMethodInstance, flags);
}
if ((!isUnspecializedPass) && (methodGenericArguments.size() != 0) && (methodInstGroup->mDefault == NULL))
{
// We are attempting to specialize but we don't have the unspecialized method yet. Generate that first.
GetMethodInstance(typeInst, methodDef, BfTypeVector(), BfGetMethodInstanceFlag_UnspecializedPass);
}
}
if (methodInstance == NULL)
{
if (lookupMethodGenericArguments.size() == 0)
{
BF_ASSERT(methodInstGroup->mDefault == NULL);
methodInstance = new BfMethodInstance();
methodInstGroup->mDefault = methodInstance;
BfLogSysM("Created Default MethodInst: %p TypeInst: %p Group: %p\n", methodInstance, typeInst, methodInstGroup);
}
else
{
BfMethodInstance** methodInstancePtr = NULL;
bool added = methodInstGroup->mMethodSpecializationMap->TryAdd(lookupMethodGenericArguments, NULL, &methodInstancePtr);
BF_ASSERT(added);
methodInstance = new BfMethodInstance();
*methodInstancePtr = methodInstance;
BfLogSysM("Created Specialized MethodInst: %p TypeInst: %p\n", methodInstance, typeInst);
}
if ((prevIRFunc) && (!prevIRFunc.IsFake()))
methodInstance->mIRFunction = prevIRFunc; // Take it over
}
/*// 24 bits for typeid, 20 for method id, 20 for specialization index
methodInstance->mMethodId = ((int64)typeInst->mTypeId) + ((int64)methodInstGroup->mMethodIdx << 24);
if (methodInstGroup->mMethodSpecializationMap != NULL)
methodInstance->mMethodId += ((int64)methodInstGroup->mMethodSpecializationMap->size() << 44);*/
methodInstance->mMethodDef = methodDef;
methodInstance->mAlwaysInline = methodDef->mAlwaysInline;
methodInstance->mMethodInstanceGroup = methodInstGroup;
methodInstance->mIsReified = isReified;
SetupMethodIdHash(methodInstance);
if (hadConcreteInterfaceGenericArgument)
methodInstance->mIgnoreBody = true;
if ((flags & BfGetMethodInstanceFlag_ForeignMethodDef) != 0)
{
methodInstance->mIsForeignMethodDef = true;
BF_ASSERT(foreignType != NULL);
methodInstance->GetMethodInfoEx()->mForeignType = foreignType;
}
if ((typeInst->mTypeDef == mCompiler->mValueTypeTypeDef) && (methodDef->mName == BF_METHODNAME_EQUALS))
{
if (!lookupMethodGenericArguments.empty())
{
auto compareType = lookupMethodGenericArguments[0];
PopulateType(compareType, BfPopulateType_Data);
if (compareType->IsSplattable())
methodInstance->mAlwaysInline = true;
}
}
if (methodDef->mDeclaringType->mTypeDeclaration != typeInst->mTypeDef->mTypeDeclaration)
{
// With extensions we can have multiple definitions of the same method
//methodInstance->mMangleWithIdx = true;
}
BfModule* declareModule = GetOrCreateMethodModule(methodInstance);
BF_ASSERT(typeInst == methodInstance->GetOwner());
auto methodDeclaration = methodDef->GetMethodDeclaration();
if (isUnspecializedPass)
{
for (int genericParamIdx = 0; genericParamIdx < (int) methodDef->mGenericParams.size(); genericParamIdx++)
{
auto genericParamType = GetGenericParamType(BfGenericParamKind_Method, genericParamIdx);
methodInstance->GetMethodInfoEx()->mMethodGenericArguments.Add(genericParamType);
}
}
else if ((methodDeclaration != NULL) && (methodDeclaration->mGenericParams != NULL))
{
if (!sanitizedMethodGenericArguments.IsEmpty())
methodInstance->GetMethodInfoEx()->mMethodGenericArguments = sanitizedMethodGenericArguments;
if (methodDef->mGenericParams.size() != sanitizedMethodGenericArguments.size())
{
Fail("Internal error");
BFMODULE_FATAL(this, "Generic method argument counts mismatch");
2019-08-23 11:56:54 -07:00
return NULL;
}
for (auto genericArg : sanitizedMethodGenericArguments)
{
if (genericArg->IsPrimitiveType())
genericArg = GetWrappedStructType(genericArg);
AddDependency(genericArg, typeInst, BfDependencyMap::DependencyFlag_MethodGenericArg);
if (genericArg->IsMethodRef())
{
auto methodRefType = (BfMethodRefType*)genericArg;
//AddDependency(genericArg, typeInst, BfDependencyMap::DependencyFlag_);
}
}
}
// Generic constraints
for (int genericParamIdx = 0; genericParamIdx < (int)methodDef->mGenericParams.size(); genericParamIdx++)
{
auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, genericParamIdx);
methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);
}
for (int externConstraintIdx = 0; externConstraintIdx < (int)methodDef->mExternalConstraints.size(); externConstraintIdx++)
{
auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, externConstraintIdx + (int)methodDef->mGenericParams.size());
methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);
}
2019-08-23 11:56:54 -07:00
bool addToWorkList = !processNow;
if (mCompiler->GetAutoComplete() != NULL)
{
if (typeInst->IsSpecializedByAutoCompleteMethod())
addToWorkList = false;
}
BF_ASSERT(declareModule != NULL);
methodInstance->mDeclModule = declareModule;
if ((!methodInstance->mIsReified) && (mCompiler->mCompileState != BfCompiler::CompileState_Normal))
{
// We can be sure this method won't become reified later. Normally we can "go either way" with an unreified method
// of a reified module -
// If we declare it in the reified module then we can switch it to "reified" before actual processing without any extra work,
// BUT if we don't reify it then we have to remove the body after processing.
// But if we declare it in the unreified module module and then end up needing to reify it then we need to re-declare it in
// the proper reified module.
declareModule = mContext->mUnreifiedModule;
}
else if ((!declareModule->mIsModuleMutable) && (!mCompiler->mIsResolveOnly))
{
BF_ASSERT(!methodInstance->mIsReified);
declareModule = mContext->mUnreifiedModule;
}
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(declareModule->mCurMethodInstance, methodInstance);
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(declareModule->mCurTypeInstance, typeInst);
SetAndRestoreValue<BfFilePosition> prevFilePos(declareModule->mCurFilePosition);
declareModule->DoMethodDeclaration(methodDef->GetMethodDeclaration(), false, addToWorkList);
if (processNow)
{
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, NULL);
ProcessMethod(methodInstance);
}
if (mCompiler->IsSkippingExtraResolveChecks())
return BfModuleMethodInstance(methodInstance, BfIRFunction());
return BfModuleMethodInstance(methodInstance);
}
BfModuleMethodInstance BfModule::GetMethodInstance(BfMethodInstance* methodInstance, BfGetMethodInstanceFlags flags)
{
BF_ASSERT((methodInstance->mMethodInfoEx == NULL) || (methodInstance->mMethodInfoEx->mGenericParams.IsEmpty()));
if (methodInstance->mIsForeignMethodDef)
flags = (BfGetMethodInstanceFlags)(flags | BfGetMethodInstanceFlag_ForeignMethodDef);
return GetMethodInstance(methodInstance->GetOwner(), methodInstance->mMethodDef, BfTypeVector(), flags);
}
BfMethodInstance* BfModule::GetOuterMethodInstance(BfMethodInstance* methodInstance)
{
if (!methodInstance->mMethodDef->mIsLocalMethod)
return NULL;
auto outerLocal = methodInstance->mMethodInfoEx->mClosureInstanceInfo->mLocalMethod->mOuterLocalMethod;
if (outerLocal == NULL)
return NULL;
BfTypeVector genericArgs;
for (int i = 0; i < (int)outerLocal->mMethodDef->mGenericParams.size(); i++)
genericArgs.Add(methodInstance->mMethodInfoEx->mMethodGenericArguments[i]);
return GetLocalMethodInstance(outerLocal, genericArgs).mMethodInstance;
}
void BfModule::SetupMethodIdHash(BfMethodInstance* methodInstance)
{
HashContext hashCtx;
std::function<void(BfMethodInstance*)> _MixinMethodInstance = [&](BfMethodInstance* methodInstance)
{
hashCtx.Mixin(methodInstance->GetOwner()->mTypeId);
hashCtx.Mixin(methodInstance->mMethodDef->mIdx);
if (methodInstance->GetNumGenericArguments() != 0)
{
hashCtx.Mixin((int32)methodInstance->mMethodInfoEx->mMethodGenericArguments.size());
for (auto methodGenericArg : methodInstance->mMethodInfoEx->mMethodGenericArguments)
hashCtx.Mixin(methodGenericArg->mTypeId);
}
};
_MixinMethodInstance(methodInstance);
if (methodInstance->mMethodDef->mIsLocalMethod)
{
auto outmostMethodInstance = mCurMethodState->GetRootMethodState()->mMethodInstance;
BF_ASSERT((outmostMethodInstance->mIdHash != 0) || (outmostMethodInstance->mIsAutocompleteMethod));
hashCtx.Mixin(outmostMethodInstance->mIdHash);
}
methodInstance->mIdHash = (int64)hashCtx.Finish64();
}
BfIRValue BfModule::GetInterfaceSlotNum(BfTypeInstance* ifaceType)
{
BfIRValue globalValue;
BfIRValue* globalValuePtr = NULL;
if (mInterfaceSlotRefs.TryGetValue(ifaceType, &globalValuePtr))
{
globalValue = *globalValuePtr;
}
if (!globalValue)
{
// This is necessary to reify the interface type
PopulateType(ifaceType);
StringT<128> slotVarName;
BfMangler::MangleStaticFieldName(slotVarName, mCompiler->GetMangleKind(), ifaceType, "sBfSlotOfs");
BfType* intType = GetPrimitiveType(BfTypeCode_Int32);
BfIRValue value;
if ((mCompiler->mHotState != NULL) && (ifaceType->mSlotNum >= 0))
{
/*// If this interface already existed before this hot compile but we never indirectly called any of its methods
// then the sBfSlotOfs could have gotten its symbol stripped, so actually define it here.
int virtSlotIdx = ifaceType->mSlotNum + 1;
value = GetConstValue32(virtSlotIdx);*/
mCompiler->mHotState->mSlotDefineTypeIds.Add(ifaceType->mTypeId);
ifaceType->mDirty = true; // Makes sure we create interface in vdata
}
globalValue = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(intType), true, BfIRLinkageType_External, value, slotVarName);
mInterfaceSlotRefs[ifaceType] = globalValue;
}
return mBfIRBuilder->CreateLoad(globalValue/*, "slotOfs"*/);
}
void BfModule::HadSlotCountDependency()
{
if (mCompiler->mIsResolveOnly)
return;
BF_ASSERT(!mBfIRBuilder->mIgnoreWrites);
BF_ASSERT((mUsedSlotCount == BF_MAX(mCompiler->mMaxInterfaceSlots, 0)) || (mUsedSlotCount == -1));
mUsedSlotCount = BF_MAX(mCompiler->mMaxInterfaceSlots, 0);
}
BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance)
2019-11-25 06:47:41 -08:00
{
2019-08-23 11:56:54 -07:00
if (mIsScratchModule)
{
// Just fake it for the extern and unspecialized modules
auto ptrType = CreatePointerType(fieldInstance->GetResolvedType());
return BfTypedValue(mBfIRBuilder->GetFakeVal(), fieldInstance->GetResolvedType(), true);
2019-08-23 11:56:54 -07:00
}
BfIRValue globalValue;
auto fieldDef = fieldInstance->GetFieldDef();
if ((fieldDef->mIsConst) && (!fieldDef->mIsExtern))
{
if (fieldInstance->mConstIdx != -1)
{
auto constant = fieldInstance->mOwner->mConstHolder->GetConstantById(fieldInstance->mConstIdx);
return BfTypedValue(ConstantToCurrent(constant, fieldInstance->mOwner->mConstHolder, fieldInstance->GetResolvedType()), fieldInstance->GetResolvedType());
}
else
{
return GetDefaultTypedValue(fieldInstance->GetResolvedType());
}
}
BfIRValue* globalValuePtr = NULL;
if (mStaticFieldRefs.TryGetValue(fieldInstance, &globalValuePtr))
{
globalValue = *globalValuePtr;
BF_ASSERT(globalValue);
}
else
{
StringT<128> staticVarName;
BfMangler::Mangle(staticVarName, mCompiler->GetMangleKind(), fieldInstance);
auto typeType = fieldInstance->GetResolvedType();
if ((fieldDef->mIsExtern) && (fieldDef->mIsConst) && (typeType->IsPointer()))
{
typeType = typeType->GetUnderlyingType();
}
PopulateType(typeType);
if ((typeType != NULL) && (!typeType->IsValuelessType()))
{
globalValue = mBfIRBuilder->CreateGlobalVariable(
mBfIRBuilder->MapType(typeType),
false,
BfIRLinkageType_External,
BfIRValue(),
staticVarName,
IsThreadLocal(fieldInstance));
if (!mBfIRBuilder->mIgnoreWrites)
{
// Only store this if we actually did the creation
BF_ASSERT(globalValue);
mStaticFieldRefs[fieldInstance] = globalValue;
}
BfLogSysM("Mod:%p Type:%p ReferenceStaticField %p -> %p\n", this, fieldInstance->mOwner, fieldInstance, globalValue);
}
}
auto type = fieldInstance->GetResolvedType();
if (type->IsValuelessType())
return BfTypedValue(globalValue, type);
return BfTypedValue(globalValue, type, !fieldDef->mIsConst);
}
BfTypedValue BfModule::GetThis()
{
auto useMethodState = mCurMethodState;
while ((useMethodState != NULL) && (useMethodState->mClosureState != NULL) && (useMethodState->mClosureState->mCapturing))
{
useMethodState = useMethodState->mPrevMethodState;
}
if (useMethodState != NULL)
{
// Fake a 'this' for var field resolution
if (useMethodState->mTempKind == BfMethodState::TempKind_NonStatic)
{
auto thisType = mCurTypeInstance;
if (thisType->IsValueType())
return BfTypedValue(mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapTypeInstPtr(thisType)), thisType, BfTypedValueKind_ThisAddr);
else
return BfTypedValue(mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapTypeInst(thisType)), thisType, BfTypedValueKind_ThisValue);
}
else if (useMethodState->mTempKind == BfMethodState::TempKind_Static)
{
return BfTypedValue();
}
}
else
{
//TODO: Do we allow useMethodState to be NULL anymore?
return BfTypedValue();
}
// if (useMethodState->HasNonStaticMixin())
// {
// auto checkMethodState = useMethodState;
// while (checkMethodState != NULL)
// {
// for (int localIdx = (int)checkMethodState->mLocals.size() - 1; localIdx >= 0; localIdx--)
// {
// auto varDecl = checkMethodState->mLocals[localIdx];
// if (varDecl->mName == "this")
// {
// varDecl->mReadFromId = useMethodState->GetRootMethodState()->mCurAccessId++;
// if (varDecl->mIsSplat)
// {
// return BfTypedValue(varDecl->mValue, varDecl->mResolvedType, BfTypedValueKind_ThisSplatHead);
// }
// else if ((varDecl->mResolvedType->IsValueType()) && (varDecl->mAddr))
// {
// return BfTypedValue(varDecl->mAddr, varDecl->mResolvedType, BfTypedValueKind_ThisAddr);
// }
// return BfTypedValue(varDecl->mValue, varDecl->mResolvedType, varDecl->mResolvedType->IsValueType() ? BfTypedValueKind_ThisAddr : BfTypedValueKind_ThisValue);
// }
// }
//
// checkMethodState = checkMethodState->mPrevMethodState;
// }
// }
// Check mixin state for 'this'
{
auto checkMethodState = useMethodState;
while (checkMethodState != NULL)
{
if (checkMethodState->mMixinState != NULL)
{
BfTypedValue thisValue = checkMethodState->mMixinState->mTarget;
if (thisValue.HasType())
2019-08-23 11:56:54 -07:00
{
checkMethodState->mMixinState->mLastTargetAccessId = useMethodState->GetRootMethodState()->mCurAccessId++;
if (!thisValue.mType->IsValueType())
thisValue = LoadValue(thisValue);
return thisValue;
}
}
checkMethodState = checkMethodState->mPrevMethodState;
}
}
auto curMethodInstance = mCurMethodInstance;
if (useMethodState->mMethodInstance != NULL)
curMethodInstance = useMethodState->mMethodInstance;
if ((curMethodInstance == NULL) /*|| (!mCurMethodInstance->mIRFunction)*/ || (curMethodInstance->mMethodDef->mIsStatic))
{
if ((useMethodState->mClosureState != NULL) && (!useMethodState->mClosureState->mConstLocals.empty()))
{
auto& constLocal = useMethodState->mClosureState->mConstLocals[0];
if (constLocal.mIsThis)
{
BF_ASSERT(constLocal.mResolvedType->IsValuelessType());
return BfTypedValue(mBfIRBuilder->GetFakeVal(), constLocal.mResolvedType);
}
}
return BfTypedValue();
}
2019-08-23 11:56:54 -07:00
if (useMethodState->mLocals.IsEmpty())
{
// This can happen in rare non-capture cases, such as when we need to do a const expression resolve for a sized-array return type on a local method
BF_ASSERT(useMethodState->mPrevMethodState != NULL);
return BfTypedValue();
}
auto thisLocal = useMethodState->mLocals[0];
BF_ASSERT(thisLocal->mIsThis);
bool preferValue = !IsTargetingBeefBackend();
if (!thisLocal->mAddr)
preferValue = true;
bool usedVal = false;
2019-08-23 11:56:54 -07:00
BfIRValue thisValue;
if ((preferValue) && (!thisLocal->mIsLowered))
{
2019-08-23 11:56:54 -07:00
thisValue = thisLocal->mValue;
usedVal = true;
}
2019-08-23 11:56:54 -07:00
else if ((thisLocal->mIsSplat) || (thisLocal->mIsLowered))
thisValue = thisLocal->mAddr;
else
thisValue = mBfIRBuilder->CreateLoad(thisLocal->mAddr);
useMethodState->mLocals[0]->mReadFromId = useMethodState->GetRootMethodState()->mCurAccessId++;
if (useMethodState->mClosureState != NULL)
{
auto closureTypeInst = useMethodState->mClosureState->mClosureType;
if (closureTypeInst != NULL)
{
if (closureTypeInst->mFieldInstances.size() > 0)
{
auto& field = closureTypeInst->mFieldInstances[0];
auto fieldDef = field.GetFieldDef();
if (fieldDef->mName == "__this")
{
if (mCurMethodState->mClosureState->mCapturing)
mCurMethodState->mClosureState->mReferencedOuterClosureMembers.Add(&field);
// This is a captured 'this'
BfTypedValue result = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(thisValue, 0, field.mDataIdx), field.mResolvedType, true);
if (field.mResolvedType->IsRef())
{
auto refType = (BfRefType*)field.mResolvedType;
auto underlyingType = refType->GetUnderlyingType();
result = BfTypedValue(mBfIRBuilder->CreateLoad(result.mValue), underlyingType, true);
}
if (field.mResolvedType->IsObject())
{
result = LoadValue(result);
result.mKind = BfTypedValueKind_ThisValue;
}
else
{
if (fieldDef->mIsReadOnly)
result.mKind = BfTypedValueKind_ReadOnlyThisAddr;
else
result.mKind = BfTypedValueKind_ThisAddr;
}
return result;
}
}
return BfTypedValue();
}
if (useMethodState->mClosureState->mCapturing)
{
auto thisType = useMethodState->mLocals[0]->mResolvedType;
if (thisType->IsValueType())
return BfTypedValue(CreateAlloca(thisType), thisType, BfTypedValueKind_ThisAddr);
else
return BfTypedValue(GetDefaultValue(thisType), thisType, BfTypedValueKind_ThisValue);
}
}
auto localDef = useMethodState->mLocals[0];
auto curMethodOwner = mCurMethodInstance->mMethodInstanceGroup->mOwner;
if ((curMethodOwner->IsStruct()) || (curMethodOwner->IsTypedPrimitive()))
{
if ((localDef->mResolvedType->IsTypedPrimitive()) && (!mCurMethodInstance->mMethodDef->mIsMutating))
{
return BfTypedValue(thisValue, useMethodState->mLocals[0]->mResolvedType, BfTypedValueKind_ReadOnlyThisValue);
}
if (localDef->mIsSplat)
{
return BfTypedValue(thisValue, useMethodState->mLocals[0]->mResolvedType, BfTypedValueKind_ThisSplatHead);
}
return BfTypedValue(thisValue, useMethodState->mLocals[0]->mResolvedType, localDef->mIsReadOnly ? BfTypedValueKind_ReadOnlyThisAddr : BfTypedValueKind_ThisAddr);
}
return BfTypedValue(thisValue, mCurMethodInstance->mMethodInstanceGroup->mOwner, BfTypedValueKind_ThisValue);
}
BfLocalVariable* BfModule::GetThisVariable()
{
if (mCurMethodState == NULL)
return NULL;
auto methodState = mCurMethodState->GetNonCaptureState();
if ((methodState->mLocals.size() > 0) && (methodState->mLocals[0]->mIsThis))
return methodState->mLocals[0];
return NULL;
}
bool BfModule::IsInGeneric()
{
return (mCurMethodInstance->GetNumGenericArguments() != 0) || (mCurTypeInstance->IsGenericTypeInstance());
}
2020-04-28 07:30:45 -07:00
bool BfModule::InDefinitionSection()
2019-08-23 11:56:54 -07:00
{
2020-04-28 07:30:45 -07:00
if (mCurTypeInstance != NULL)
{
if (mCurTypeInstance->IsUnspecializedTypeVariation())
return false;
}
return !IsInSpecializedSection();
}
bool BfModule::IsInSpecializedGeneric()
{
2019-08-23 11:56:54 -07:00
if ((mCurMethodInstance == NULL) || (mCurMethodInstance->mIsUnspecialized))
return false;
return (mCurMethodInstance->GetNumGenericArguments() != 0) || (mCurTypeInstance->IsGenericTypeInstance());
}
bool BfModule::IsInSpecializedSection()
{
return IsInSpecializedGeneric() ||
((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL));
}
bool BfModule::IsInUnspecializedGeneric()
{
if (mCurMethodInstance != NULL)
return mCurMethodInstance->mIsUnspecialized;
return false;
}
//////////////////////////////////////////////////////////////////////////
BfIRValue BfModule::AllocLocalVariable(BfType* type, const StringImpl& name, bool doLifetimeEnd)
{
//if ((type->IsValuelessType()) || (type->IsMethodRef()))
if (type->IsValuelessType())
return mBfIRBuilder->GetFakeVal();
auto allocaInst = CreateAlloca(type, doLifetimeEnd, name.c_str());
if ((!doLifetimeEnd) && (WantsLifetimes()))
2019-08-23 11:56:54 -07:00
{
auto lifetimeStart = mBfIRBuilder->CreateLifetimeStart(allocaInst);
mBfIRBuilder->ClearDebugLocation(lifetimeStart);
}
bool initLocalVariables = mCompiler->mOptions.mInitLocalVariables;
auto typeOptions = GetTypeOptions();
if (typeOptions != NULL)
initLocalVariables = BfTypeOptions::Apply(initLocalVariables, typeOptions->mInitLocalVariables);
// Local variable inits are implicitly handled in the Beef Backend
if ((initLocalVariables) && (!IsTargetingBeefBackend()))
{
auto prevBlock = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRInitBlock);
auto storeInst = mBfIRBuilder->CreateAlignedStore(GetDefaultValue(type), allocaInst, type->mAlign);
mBfIRBuilder->ClearDebugLocation(storeInst);
mBfIRBuilder->SetInsertPoint(prevBlock);
}
return allocaInst;
}
void BfModule::DoAddLocalVariable(BfLocalVariable* localVar)
{
localVar->mLocalVarIdx = (int)mCurMethodState->mLocals.size();
mCurMethodState->mLocals.push_back(localVar);
BfLocalVarEntry* localVarEntryPtr;
if (!mCurMethodState->mLocalVarSet.TryAdd(BfLocalVarEntry(localVar), &localVarEntryPtr))
{
localVar->mShadowedLocal = localVarEntryPtr->mLocalVar;
localVarEntryPtr->mLocalVar = localVar;
}
}
BfLocalVariable* BfModule::AddLocalVariableDef(BfLocalVariable* localVarDef, bool addDebugInfo, bool doAliasValue, BfIRValue declareBefore, BfIRInitType initType)
{
2019-08-23 11:56:54 -07:00
if ((localVarDef->mValue) && (!localVarDef->mAddr) && (IsTargetingBeefBackend()))
{
if ((!localVarDef->mValue.IsConst()) && (!localVarDef->mValue.IsArg()) && (!localVarDef->mValue.IsFake()))
{
mBfIRBuilder->CreateValueScopeRetain(localVarDef->mValue);
mCurMethodState->mCurScope->mHadScopeValueRetain = true;
}
}
if ((addDebugInfo) && (mBfIRBuilder->DbgHasInfo()) &&
((mCurMethodInstance == NULL) || (!mCurMethodInstance->mIsUnspecialized)) &&
(mHasFullDebugInfo) &&
((mCurMethodState->mClosureState == NULL) || (!mCurMethodState->mClosureState->mCapturing)))
{
auto varType = localVarDef->mResolvedType;
if (localVarDef->mResolvedType->IsValuelessType())
{
}
else
{
bool isByAddr = false;
auto diValue = localVarDef->mValue;
if (localVarDef->mConstValue)
diValue = localVarDef->mConstValue;
else if (localVarDef->mAddr)
{
diValue = localVarDef->mAddr;
isByAddr = true;
}
auto diType = mBfIRBuilder->DbgGetType(localVarDef->mResolvedType);
bool didConstToMem = false;
bool isConstant = false;
if ((localVarDef->mIsReadOnly) && (localVarDef->mAddr))
diType = mBfIRBuilder->DbgCreateConstType(diType);
else if (localVarDef->mConstValue)
{
auto constant = mBfIRBuilder->GetConstant(localVarDef->mConstValue);
isConstant =
(constant->mConstType < BfConstType_GlobalVar) ||
(constant->mConstType == BfConstType_AggZero) ||
(constant->mConstType == BfConstType_Array);
if (isConstant)
{
if (localVarDef->mResolvedType->IsComposite())
{
mBfIRBuilder->PopulateType(localVarDef->mResolvedType);
auto constMem = mBfIRBuilder->ConstToMemory(localVarDef->mConstValue);
if (IsTargetingBeefBackend())
{
diValue = mBfIRBuilder->CreateAliasValue(constMem);
didConstToMem = true;
diType = mBfIRBuilder->DbgCreateReferenceType(diType);
}
else
{
isByAddr = true;
mBfIRBuilder->SaveDebugLocation();
auto prevBlock = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRInitBlock);
mBfIRBuilder->ClearDebugLocation();
auto ptrType = CreatePointerType(localVarDef->mResolvedType);
auto addrVal = CreateAlloca(ptrType);
mBfIRBuilder->CreateStore(constMem, addrVal);
diType = mBfIRBuilder->DbgCreateReferenceType(diType);
diValue = addrVal;
didConstToMem = true;
mBfIRBuilder->SetInsertPoint(prevBlock);
mBfIRBuilder->RestoreDebugLocation();
}
}
if (mCompiler->mOptions.IsCodeView())
diType = mBfIRBuilder->DbgCreateConstType(diType);
}
}
2020-06-03 05:22:29 -07:00
if (!mBfIRBuilder->mIgnoreWrites)
2019-08-23 11:56:54 -07:00
{
2020-06-03 05:22:29 -07:00
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope,
localVarDef->mName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType, initType);
localVarDef->mDbgVarInst = diVariable;
if (mBfIRBuilder->HasDebugLocation())
2019-08-23 11:56:54 -07:00
{
2020-06-03 05:22:29 -07:00
if ((isConstant) && (!didConstToMem))
{
localVarDef->mDbgDeclareInst = mBfIRBuilder->DbgInsertValueIntrinsic(localVarDef->mConstValue, diVariable);
2019-08-23 11:56:54 -07:00
}
2020-06-03 05:22:29 -07:00
else
2019-08-23 11:56:54 -07:00
{
2020-06-03 05:22:29 -07:00
if ((IsTargetingBeefBackend()) && (doAliasValue))
{
diValue = mBfIRBuilder->CreateAliasValue(diValue);
mCurMethodState->mCurScope->mDeferredLifetimeEnds.Add(diValue);
}
if (isByAddr)
localVarDef->mDbgDeclareInst = mBfIRBuilder->DbgInsertDeclare(diValue, diVariable, declareBefore);
else if (diValue)
{
localVarDef->mDbgDeclareInst = mBfIRBuilder->DbgInsertValueIntrinsic(diValue, diVariable);
}
else if (mCompiler->mOptions.mToolsetType != BfToolsetType_GNU) // DWARF chokes on this:
localVarDef->mDbgDeclareInst = mBfIRBuilder->DbgInsertValueIntrinsic(BfIRValue(), diVariable);
2019-08-23 11:56:54 -07:00
}
}
}
}
}
localVarDef->mDeclBlock = mBfIRBuilder->GetInsertBlock();
DoAddLocalVariable(localVarDef);
auto rootMethodState = mCurMethodState->GetRootMethodState();
if (localVarDef->mLocalVarId == -1)
{
BF_ASSERT(rootMethodState->mCurLocalVarId >= 0);
localVarDef->mLocalVarId = rootMethodState->mCurLocalVarId++;
}
if ((localVarDef->mNameNode != NULL) && (mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
mCompiler->mResolvePassData->mAutoComplete->CheckLocalDef(localVarDef->mNameNode, localVarDef);
if ((localVarDef->mNameNode != NULL) && (mCurMethodInstance != NULL))
{
bool isClosureProcessing = (mCurMethodState->mClosureState != NULL) && (!mCurMethodState->mClosureState->mCapturing);
if ((!isClosureProcessing) && (mCompiler->mResolvePassData != NULL) && (localVarDef->mNameNode != NULL))
mCompiler->mResolvePassData->HandleLocalReference(localVarDef->mNameNode, rootMethodState->mMethodInstance->GetOwner()->mTypeDef, rootMethodState->mMethodInstance->mMethodDef, localVarDef->mLocalVarId);
}
return localVarDef;
}
void BfModule::CreateDIRetVal()
{
if (!WantsDebugInfo())
return;
/*if (!mCurMethodState->mRetVal)
{
if (mCurMethodInstance->mMethodDef->mName != "Ziggle")
return;
}*/
if ((mCurMethodState->mRetVal) || (mCurMethodState->mRetValAddr))
2019-11-02 06:02:36 -07:00
{
2019-08-23 11:56:54 -07:00
BfType* dbgType = mCurMethodInstance->mReturnType;
BfIRValue dbgValue = mCurMethodState->mRetVal.mValue;
if (mCurMethodInstance->GetStructRetIdx() != -1)
2019-08-23 11:56:54 -07:00
{
BF_ASSERT(mCurMethodState->mRetValAddr);
dbgType = CreatePointerType(dbgType);
dbgValue = mCurMethodState->mRetValAddr;
}
else
{
BF_ASSERT(!mCurMethodState->mRetValAddr);
}
mCurMethodState->mDIRetVal = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope,
"__return", mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mBfIRBuilder->DbgGetType(dbgType));
auto declareCall = mBfIRBuilder->DbgInsertDeclare(dbgValue, mCurMethodState->mDIRetVal);
}
}
void BfModule::CheckVariableDef(BfLocalVariable* variableDef)
{
if (variableDef->mName.IsEmpty())
return;
2019-08-23 11:56:54 -07:00
BfLocalVarEntry* localVarEntryPtr = NULL;
if ((mCurMethodState != NULL) && (mCurMethodState->mLocalVarSet.TryGet(BfLocalVarEntry(variableDef), &localVarEntryPtr)))
{
auto checkLocal = localVarEntryPtr->mLocalVar;
if ((checkLocal->mLocalVarIdx >= mCurMethodState->GetLocalStartIdx()) && (!checkLocal->mIsShadow))
{
BfError* error;
if (checkLocal->mIsImplicitParam)
return; // Ignore 'redefinition'
if (checkLocal->IsParam())
error = Fail(StrFormat("A parameter named '%s' has already been declared.", variableDef->mName.c_str()), variableDef->mNameNode);
else if (checkLocal->mLocalVarIdx < mCurMethodState->mCurScope->mLocalVarStart)
error = Fail(StrFormat("A variable named '%s' has already been declared in this parent's scope.", variableDef->mName.c_str()), variableDef->mNameNode);
else
error = Fail(StrFormat("A variable named '%s' has already been declared in this scope.", variableDef->mName.c_str()), variableDef->mNameNode);
if ((checkLocal->mNameNode != NULL) && (error != NULL))
mCompiler->mPassInstance->MoreInfo("Previous declaration", checkLocal->mNameNode);
return;
}
}
}
BfScopeData* BfModule::FindScope(BfAstNode* scopeName, BfMixinState* fromMixinState, bool allowAcrossDeferredBlock)
2019-08-23 11:56:54 -07:00
{
bool inMixinDecl = (mCurMethodInstance != NULL) && (mCurMethodInstance->IsMixin());
if (auto tokenNode = BfNodeDynCast<BfTokenNode>(scopeName))
{
if (tokenNode->GetToken() == BfToken_Colon)
{
if ((!allowAcrossDeferredBlock) && (mCurMethodState->mInDeferredBlock))
{
Fail("Cannot access method scope across deferred block boundary", scopeName);
return NULL;
}
2019-08-23 11:56:54 -07:00
return &mCurMethodState->mHeadScope;
}
2019-08-23 11:56:54 -07:00
else if (tokenNode->GetToken() == BfToken_Mixin)
{
if (fromMixinState == NULL)
{
if (mCurMethodInstance->mMethodDef->mMethodType != BfMethodType_Mixin)
Fail("'mixin' scope specifier can only be used within a mixin declaration", scopeName);
return mCurMethodState->mCurScope;
}
fromMixinState->mUsedInvocationScope = true;
return fromMixinState->mTargetScope;
}
else if (tokenNode->GetToken() == BfToken_New)
return NULL;
}
if (auto identifier = BfNodeDynCast<BfIdentifierNode>(scopeName))
{
auto findLabel = scopeName->ToString();
bool crossedDeferredBlock = false;
2019-08-23 11:56:54 -07:00
auto checkScope = mCurMethodState->mCurScope;
while (checkScope != NULL)
{
if (checkScope->mIsDeferredBlock)
crossedDeferredBlock = true;
2019-08-23 11:56:54 -07:00
if (checkScope->mLabel == findLabel)
{
if ((crossedDeferredBlock) && (!allowAcrossDeferredBlock))
{
Fail(StrFormat("Cannot access scope '%s' across deferred block boundary", findLabel.c_str()), scopeName);
return NULL;
}
2019-08-23 11:56:54 -07:00
return checkScope;
}
2019-08-23 11:56:54 -07:00
checkScope = checkScope->mPrevScope;
}
if (!inMixinDecl)
Fail(StrFormat("Unable to locate label '%s'", findLabel.c_str()), scopeName);
}
if (auto scopeNode = BfNodeDynCast<BfScopeNode>(scopeName))
{
return FindScope(scopeNode->mTargetNode, allowAcrossDeferredBlock);
2019-08-23 11:56:54 -07:00
}
return mCurMethodState->mCurScope;
}
BfScopeData* BfModule::FindScope(BfAstNode* scopeName, bool allowAcrossDeferredBlock)
2019-08-23 11:56:54 -07:00
{
return FindScope(scopeName, mCurMethodState->mMixinState, allowAcrossDeferredBlock);
2019-08-23 11:56:54 -07:00
}
BfBreakData* BfModule::FindBreakData(BfAstNode* scopeName)
{
auto scopeData = FindScope(scopeName, false);
2019-08-23 11:56:54 -07:00
if (scopeData == NULL)
return NULL;
int scopeDepth = scopeData->GetDepth();
// Find the first break data that is at or below the depth of our requested scope
auto breakData = mCurMethodState->mBreakData;
while ((breakData != NULL) && (scopeDepth < breakData->mScope->GetDepth()))
{
if (breakData->mScope->mIsConditional)
return NULL;
2019-08-23 11:56:54 -07:00
breakData = breakData->mPrevBreakData;
}
return breakData;
}
void BfModule::EmitLifetimeEnds(BfScopeData* scopeData)
{
// LLVM is stricter about the placement of these (ie: they can't occur after a 'ret')
if (!IsTargetingBeefBackend())
return;
for (auto lifetimeEnd : scopeData->mDeferredLifetimeEnds)
{
mBfIRBuilder->CreateLifetimeEnd(lifetimeEnd);
}
}
void BfModule::ClearLifetimeEnds()
{
auto scopeData = mCurMethodState->mCurScope;
while (scopeData != NULL)
{
scopeData->mDeferredLifetimeEnds.Clear();
scopeData = scopeData->mPrevScope;
}
}
bool BfModule::WantsDebugInfo()
{
if ((mCurMethodInstance != NULL) &&
((mCurMethodInstance->mIsUnspecialized) || (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Mixin)))
return false;
return (mBfIRBuilder->DbgHasInfo()) && (mHasFullDebugInfo) &&
((mCurMethodState == NULL) || (mCurMethodState->mClosureState == NULL) || (!mCurMethodState->mClosureState->mCapturing));
}
BfTypeOptions* BfModule::GetTypeOptions()
{
if ((mCurMethodState != NULL) && (mCurMethodState->mMethodTypeOptions != NULL))
return mCurMethodState->mMethodTypeOptions;
return mSystem->GetTypeOptions(mCurTypeInstance->mTypeOptionsIdx);
}
bool BfModule::HasDeferredScopeCalls(BfScopeData* scope)
{
BfScopeData* checkScope = mCurMethodState->mCurScope;
while (checkScope != NULL)
{
if ((!checkScope->mDeferredCallEntries.IsEmpty()) || (!checkScope->mDeferredLifetimeEnds.empty()))
return true;
if (checkScope == scope)
break;
checkScope = checkScope->mPrevScope;
}
return false;
}
void BfModule::EmitDeferredScopeCalls(bool useSrcPositions, BfScopeData* scopeData, BfIRBlock doneBlock)
{
// Is there anything we need to do here?
if (mBfIRBuilder->mIgnoreWrites)
{
// Just visit deferred blocks
BfScopeData* checkScope = mCurMethodState->mCurScope;
while (checkScope != NULL)
{
BfDeferredCallEntry* deferredCallEntry = checkScope->mDeferredCallEntries.mHead;
while (deferredCallEntry != NULL)
{
if (deferredCallEntry->mDeferredBlock != NULL)
{
VisitEmbeddedStatement(deferredCallEntry->mDeferredBlock, NULL, BfEmbeddedStatementFlags_IsDeferredBlock);
2019-08-23 11:56:54 -07:00
}
deferredCallEntry = deferredCallEntry->mNext;
}
if (checkScope == scopeData)
break;
checkScope = checkScope->mPrevScope;
}
return;
}
// Why did we want to do SetIllegalSrcPos here?
// Don't we always want to step onto these instances?
// Find a case where we don't and perhaps only do it there.
// The downside was that 'EmitEnsureInstructionAt' on the end of block statements causes
// a (seemingly) unneeded NOP when we do SetIllegalSrcPos
//SetIllegalSrcPos();
bool setSrcPos = false;
bool wantsNop = true;
BfAstNode* deferCloseNode = NULL;
if (mCurMethodState->mInPostReturn)
{
// These won't get used, they are post-return
return;
}
SizedArray<BfIRValue, 8> deferredLifetimeEnds;
BfScopeData* scopeJumpBlock = NULL;
bool hadExtraDeferredLifetimeEnds = false;
auto _FlushLifetimeEnds = [&]()
{
for (auto lifetimeEnd : deferredLifetimeEnds)
{
mBfIRBuilder->CreateLifetimeEnd(lifetimeEnd);
mBfIRBuilder->ClearDebugLocation_Last();
}
deferredLifetimeEnds.clear();
};
BfScopeData* checkScope = mCurMethodState->mCurScope;
while (checkScope != NULL)
{
if (checkScope->mCloseNode != NULL)
deferCloseNode = checkScope->mCloseNode;
if (doneBlock)
{
// Try to find a match where we've already emitted these calls and then jumped to the correct block
for (auto& checkHandler : checkScope->mDeferredHandlers)
{
if (checkHandler.mDoneBlock == doneBlock)
{
scopeJumpBlock = checkScope;
mBfIRBuilder->CreateBr(checkHandler.mHandlerBlock);
mBfIRBuilder->ClearDebugLocation_Last();
_FlushLifetimeEnds();
break;
}
}
if (scopeJumpBlock != NULL)
break;
}
bool hasWork = (checkScope->mSavedStack) || (checkScope->mDeferredCallEntries.mHead != NULL);
if (checkScope != scopeData) // Only emit a block for deferred lifetimes if we're going back beyond this entry
hasWork |= (!deferredLifetimeEnds.IsEmpty());
if (hasWork)
{
SetAndRestoreValue<BfScopeData*> prevScope(mCurMethodState->mCurScope, checkScope);
if (deferCloseNode != NULL)
{
UpdateSrcPos(deferCloseNode);
}
if (doneBlock)
{
bool crossingMixin = mCurMethodState->mCurScope->mMixinDepth != checkScope->mMixinDepth;
String blockName = "deferredCalls";
//blockName += StrFormat("_%d", mBfIRBuilder->mBlockCount);
BfDeferredHandler deferredHandler;
if ((!crossingMixin) && (deferredLifetimeEnds.IsEmpty()))
{
mBfIRBuilder->SaveDebugLocation();
mBfIRBuilder->ClearDebugLocation();
deferredHandler.mHandlerBlock = mBfIRBuilder->MaybeChainNewBlock(blockName);
mBfIRBuilder->RestoreDebugLocation();
}
else
{
// Definitely chain
deferredHandler.mHandlerBlock = mBfIRBuilder->CreateBlock(blockName);
mBfIRBuilder->CreateBr(deferredHandler.mHandlerBlock);
mBfIRBuilder->ClearDebugLocation_Last();
_FlushLifetimeEnds();
mBfIRBuilder->AddBlock(deferredHandler.mHandlerBlock);
mBfIRBuilder->SetInsertPoint(deferredHandler.mHandlerBlock);
}
deferredHandler.mDoneBlock = doneBlock;
if (!mBfIRBuilder->mIgnoreWrites)
checkScope->mDeferredHandlers.push_back(deferredHandler);
if (checkScope == &mCurMethodState->mHeadScope)
{
if (!mCurMethodState->mDIRetVal)
{
// Weird case- if we have a return from a mixin, we need the DbgLoc to be for the mixin but we need the DIRetVal to
// be scoped to the physical method
/*if (deferCloseNode != NULL)
{
UpdateSrcPos(deferCloseNode);
}*/
CreateDIRetVal();
}
}
if (checkScope != mCurMethodState->mTailScope)
{
if (deferredHandler.mHandlerBlock.IsFake())
{
BF_ASSERT(mBfIRBuilder->mIgnoreWrites);
}
if (!mBfIRBuilder->mIgnoreWrites)
checkScope->mAtEndBlocks.push_back(deferredHandler.mHandlerBlock);
}
}
std::unordered_set<BfDeferredCallEntry*> handledSet;
BfDeferredCallEntry* deferredCallEntry = checkScope->mDeferredCallEntries.mHead;
while (deferredCallEntry != NULL)
{
BfDeferredCallEmitState deferredCallEmitState;
deferredCallEmitState.mCloseNode = deferCloseNode;
SetAndRestoreValue<BfDeferredCallEmitState*> prevDeferredCallEmitState(mCurMethodState->mDeferredCallEmitState, &deferredCallEmitState);
SetAndRestoreValue<bool> prevIgnoredWrites(mBfIRBuilder->mIgnoreWrites, deferredCallEntry->mIgnored);
if (deferCloseNode != NULL)
{
UpdateSrcPos(deferCloseNode);
}
if (wantsNop)
EmitEnsureInstructionAt();
wantsNop = false;
if (deferredCallEntry->mDeferredBlock != NULL)
{
auto itr = handledSet.insert(deferredCallEntry);
if (!itr.second)
{
// Already handled, can happen if we defer again within the block
deferredCallEntry = deferredCallEntry->mNext;
continue;
}
auto prevHead = checkScope->mDeferredCallEntries.mHead;
2020-05-18 08:30:42 -07:00
EmitDeferredCall(*deferredCallEntry, true);
2019-08-23 11:56:54 -07:00
if (prevHead != checkScope->mDeferredCallEntries.mHead)
{
// The list changed, start over and ignore anything we've already handled
deferredCallEntry = checkScope->mDeferredCallEntries.mHead;
}
else
deferredCallEntry = deferredCallEntry->mNext;
}
else
{
2020-05-18 08:30:42 -07:00
EmitDeferredCall(*deferredCallEntry, true);
2019-08-23 11:56:54 -07:00
deferredCallEntry = deferredCallEntry->mNext;
}
}
if (checkScope->mSavedStack)
{
checkScope->mSavedStackUses.Add(mBfIRBuilder->CreateStackRestore(checkScope->mSavedStack));
2019-08-23 11:56:54 -07:00
if (mCurMethodState->mDynStackRevIdx)
{
auto curValue = mBfIRBuilder->CreateLoad(mCurMethodState->mDynStackRevIdx);
auto newValue = mBfIRBuilder->CreateAdd(curValue, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1));
mBfIRBuilder->CreateStore(newValue, mCurMethodState->mDynStackRevIdx);
}
}
}
if (!checkScope->mIsScopeHead)
{
// We manually emit function-level lifetime ends after the 'ret' in ProcessMethod
if (!IsTargetingBeefBackend())
{
for (auto lifetimeEnd : checkScope->mDeferredLifetimeEnds)
{
mBfIRBuilder->CreateLifetimeEnd(lifetimeEnd);
}
}
else
{
for (auto lifetimeEnd : checkScope->mDeferredLifetimeEnds)
{
deferredLifetimeEnds.Add(lifetimeEnd);
}
}
}
if (checkScope == scopeData)
break;
checkScope = checkScope->mPrevScope;
}
if ((doneBlock) && (scopeJumpBlock == NULL))
{
mBfIRBuilder->CreateBr(doneBlock);
mBfIRBuilder->ClearDebugLocation_Last();
}
// Keeping CreateLifetimeEnds until the end messes up when we chain to old mDeferredHandlers that
// may have been created when other scope lifetimes were available
_FlushLifetimeEnds();
// Emit lifetimeEnds after the branch for Beef
/*if (IsTargetingBeefBackend())
{
bool needsEnsureInst = (!doneBlock) && (scopeJumpBlock == NULL);
BfScopeData* checkScope = mCurMethodState->mCurScope;
while (checkScope != NULL)
{
if (checkScope == scopeJumpBlock)
break;
if (!checkScope->mIsScopeHead)
{
for (auto lifetimeEnd : checkScope->mDeferredLifetimeEnds)
{
if (needsEnsureInst)
{
needsEnsureInst = false;
}
mBfIRBuilder->CreateLifetimeEnd(lifetimeEnd);
}
}
if (checkScope == scopeData)
break;
checkScope = checkScope->mPrevScope;
}
}*/
}
void BfModule::MarkScopeLeft(BfScopeData* scopeData)
{
if (mCurMethodState->mDeferredLocalAssignData != NULL)
{
auto deferredLocalAssignData = mCurMethodState->mDeferredLocalAssignData;
while (deferredLocalAssignData != NULL)
{
if ((deferredLocalAssignData->mScopeData == NULL) || (deferredLocalAssignData->mScopeData->mScopeDepth < scopeData->mScopeDepth))
break;
if ((deferredLocalAssignData->mScopeData != NULL) && (deferredLocalAssignData->mScopeData->mScopeDepth == scopeData->mScopeDepth))
deferredLocalAssignData->mIsUnconditional = false;
deferredLocalAssignData = deferredLocalAssignData->mChainedAssignData;
}
}
//for (int localIdx = scopeData->mLocalVarStart; localIdx < (int)mCurMethodState->mLocals.size(); localIdx++)
// We mark all unassigned variables as assigned now, for avoiding "may be unassigned" usage cases like:
// int b;
// if (cond) return;
// else b = 1;
// Use(b);
for (int localIdx = 0; localIdx < (int)mCurMethodState->mLocals.size(); localIdx++)
{
auto localDef = mCurMethodState->mLocals[localIdx];
if ((!localDef->mIsAssigned) && (!localDef->IsParam()))
mCurMethodState->LocalDefined(localDef);
}
}
void BfModule::CreateReturn(BfIRValue val)
{
if (mCurMethodInstance->GetStructRetIdx() != -1)
2019-08-23 11:56:54 -07:00
{
// Store to sret
BF_ASSERT(val);
mBfIRBuilder->CreateStore(val, mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx()));
2019-08-23 11:56:54 -07:00
mBfIRBuilder->CreateRetVoid();
}
else
{
if (mCurMethodInstance->mReturnType->IsValuelessType())
{
mBfIRBuilder->CreateRetVoid();
}
else if (mCurMethodInstance->mReturnType->IsStruct())
{
BfTypeCode loweredReturnType = BfTypeCode_None;
BfTypeCode loweredReturnType2 = BfTypeCode_None;
mCurMethodInstance->GetLoweredReturnType(&loweredReturnType, &loweredReturnType2);
auto retVal = CreateAlloca(mCurMethodInstance->mReturnType);
mBfIRBuilder->CreateStore(val, retVal);
auto irRetType = GetIRLoweredType(loweredReturnType, loweredReturnType2);
irRetType = mBfIRBuilder->GetPointerTo(irRetType);
auto ptrReturnValue = mBfIRBuilder->CreateBitCast(retVal, irRetType);
auto loadedReturnValue = mBfIRBuilder->CreateLoad(ptrReturnValue);
mBfIRBuilder->CreateRet(loadedReturnValue);
}
2019-08-23 11:56:54 -07:00
else
{
BF_ASSERT(val);
mBfIRBuilder->CreateRet(val);
}
}
}
void BfModule::EmitReturn(BfIRValue val)
{
if (mCurMethodState->mIRExitBlock)
{
if (!mCurMethodInstance->mReturnType->IsValuelessType())
{
if (val) // We allow for val to be empty if we know we've already written the value to mRetVal
{
BfIRValue retVal = mCurMethodState->mRetVal.mValue;
if (!mCurMethodState->mRetVal)
retVal = mBfIRBuilder->CreateLoad(mCurMethodState->mRetValAddr);
mBfIRBuilder->CreateStore(val, retVal);
}
}
EmitDeferredScopeCalls(true, NULL, mCurMethodState->mIRExitBlock);
}
else
{
EmitDeferredScopeCalls(false, NULL);
if (val)
{
if (mCurMethodInstance->mReturnType->IsValuelessType())
mBfIRBuilder->CreateRetVoid();
else
mBfIRBuilder->CreateRet(val);
}
2019-08-23 11:56:54 -07:00
}
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
}
void BfModule::EmitDefaultReturn()
{
if (mCurMethodState->mInDeferredBlock)
return;
2019-08-23 11:56:54 -07:00
if (mCurMethodState->mIRExitBlock)
{
EmitDeferredScopeCalls(true, NULL, mCurMethodState->mIRExitBlock);
}
else
{
if (mCurMethodInstance->mReturnType->IsVoid())
mBfIRBuilder->CreateRetVoid();
else if (mCurMethodInstance->GetStructRetIdx() == -1)
2019-08-23 11:56:54 -07:00
mBfIRBuilder->CreateRet(GetDefaultValue(mCurMethodInstance->mReturnType));
}
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
}
void BfModule::AssertErrorState()
{
2019-08-23 11:56:54 -07:00
if (mIgnoreErrors)
return;
if (mHadBuildError)
return;
if (mCurTypeInstance != NULL)
{
if (mCurTypeInstance->IsUnspecializedTypeVariation())
return;
}
if (mCurMethodInstance != NULL)
{
if (mCurMethodInstance->mIsUnspecializedVariation)
return;
if (mCurMethodInstance->mHasFailed)
return;
2019-08-23 11:56:54 -07:00
}
// We want the module to be marked as failed even if it's just an error in the parser
if (mCurMethodInstance != NULL)
mCurMethodInstance->mHasFailed = true;
mHadBuildError = true;
// We check for either the parsing to have failed (possibly on a previous run), or for the current instance to have failed
if (mCurTypeInstance != NULL)
{
if (mCurTypeInstance->mTypeFailed)
return;
if (mCurTypeInstance->mTypeDef->mSource->mParsingFailed)
return;
}
if (mCurMethodInstance != NULL)
{
if ((mCurMethodInstance->mMethodDef->mDeclaringType != NULL) && (mCurMethodInstance->mMethodDef->mDeclaringType->mSource->mParsingFailed))
return;
if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL) && (mCurMethodState->mMixinState->mMixinMethodInstance->mMethodDef->mDeclaringType->mSource->mParsingFailed))
return;
}
if (mCompiler->IsAutocomplete())
return;
2019-08-23 11:56:54 -07:00
BF_ASSERT(mCompiler->mPassInstance->HasFailed());
}
void BfModule::AssertParseErrorState()
{
if (mCompiler->mRevision == 1)
AssertErrorState();
}
BfType* BfModule::GetDelegateReturnType(BfType* delegateType)
{
BF_ASSERT(delegateType->IsDelegate());
auto typeInst = delegateType->ToTypeInstance();
PopulateType(typeInst, BfPopulateType_DataAndMethods);
BfMethodInstance* invokeMethodInstance = GetRawMethodInstanceAtIdx(typeInst->ToTypeInstance(), 0, "Invoke");
return invokeMethodInstance->mReturnType;
}
BfMethodInstance* BfModule::GetDelegateInvokeMethod(BfTypeInstance* typeInstance)
{
return GetRawMethodInstanceAtIdx(typeInstance, 0, "Invoke");
}
void BfModule::CreateDelegateInvokeMethod()
{
// Clear out debug loc - otherwise we'll single step onto the delegate type declaration
//mBfIRBuilder->ClearDebugLocation();
SetIllegalSrcPos();
2019-08-23 11:56:54 -07:00
auto typeInstance = mCurTypeInstance;
auto memberFuncType = mBfIRBuilder->MapMethod(mCurMethodInstance);
SizedArray<BfIRType, 4> staticParamTypes;
SizedArray<BfIRValue, 4> staticFuncArgs;
SizedArray<BfIRValue, 4> memberFuncArgs;
auto multicastDelegateType = typeInstance->mBaseType;
if (multicastDelegateType->mFieldInstances.size() != 2)
{
AssertErrorState();
return;
}
auto multicastDelegate = mBfIRBuilder->CreateBitCast(mCurMethodState->mLocals[0]->mValue, mBfIRBuilder->MapType(multicastDelegateType));
auto fieldPtr = mBfIRBuilder->CreateInBoundsGEP(multicastDelegate, 0, 2); // Load 'delegate.mTarget'
auto fieldVal = mBfIRBuilder->CreateLoad(fieldPtr);
BfExprEvaluator exprEvaluator(this);
SizedArray<BfIRType, 8> origParamTypes;
BfIRType origReturnType;
mCurMethodInstance->GetIRFunctionInfo(this, origReturnType, origParamTypes);
if (mCurMethodInstance->mReturnType->IsValueType())
mBfIRBuilder->PopulateType(mCurMethodInstance->mReturnType, BfIRPopulateType_Full);
if (mCurMethodInstance->GetStructRetIdx() != 0)
memberFuncArgs.push_back(BfIRValue()); // Push 'target'
2019-08-23 11:56:54 -07:00
int thisIdx = 0;
if (mCurMethodInstance->GetStructRetIdx() != -1)
{
thisIdx = mCurMethodInstance->GetStructRetIdx() ^ 1;
staticFuncArgs.push_back(mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx()));
memberFuncArgs.push_back(mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx()));
2019-08-23 11:56:54 -07:00
}
if (mCurMethodInstance->GetStructRetIdx() == 0)
memberFuncArgs.push_back(BfIRValue()); // Push 'target'
2019-08-23 11:56:54 -07:00
mCurMethodInstance->GetIRFunctionInfo(this, origReturnType, staticParamTypes, true);
for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++)
{
BfTypedValue localVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[i], true);
exprEvaluator.PushArg(localVal, staticFuncArgs);
exprEvaluator.PushArg(localVal, memberFuncArgs);
}
auto staticFunc = mBfIRBuilder->CreateFunctionType(origReturnType, staticParamTypes, false);
auto staticFuncPtr = mBfIRBuilder->GetPointerTo(staticFunc);
auto staticFuncPtrPtr = mBfIRBuilder->GetPointerTo(staticFuncPtr);
auto trueBB = mBfIRBuilder->CreateBlock("if.then", true);
auto falseBB = mBfIRBuilder->CreateBlock("if.else");
auto doneBB = mBfIRBuilder->CreateBlock("done");
auto checkTargetNull = mBfIRBuilder->CreateIsNotNull(fieldVal);
mBfIRBuilder->CreateCondBr(checkTargetNull, trueBB, falseBB);
BfIRValue nonStaticResult;
BfIRValue staticResult;
auto callingConv = GetIRCallingConvention(mCurMethodInstance);
2019-08-23 11:56:54 -07:00
/// Non-static invocation
{
auto memberFuncPtr = mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapMethod(mCurMethodInstance));
auto memberFuncPtrPtr = mBfIRBuilder->GetPointerTo(memberFuncPtr);
mBfIRBuilder->SetInsertPoint(trueBB);
memberFuncArgs[thisIdx] = mBfIRBuilder->CreateBitCast(fieldVal, mBfIRBuilder->MapType(mCurTypeInstance));
auto fieldPtr = mBfIRBuilder->CreateInBoundsGEP(multicastDelegate, 0, 1); // Load 'delegate.mFuncPtr'
auto funcPtrPtr = mBfIRBuilder->CreateBitCast(fieldPtr, memberFuncPtrPtr);
auto funcPtr = mBfIRBuilder->CreateLoad(funcPtrPtr);
nonStaticResult = mBfIRBuilder->CreateCall(funcPtr, memberFuncArgs);
if (mCurMethodInstance->GetStructRetIdx() != -1)
mBfIRBuilder->Call_AddAttribute(nonStaticResult, mCurMethodInstance->GetStructRetIdx() + 1, BfIRAttribute_StructRet);
2019-08-23 11:56:54 -07:00
if (callingConv != BfIRCallingConv_CDecl)
mBfIRBuilder->SetCallCallingConv(nonStaticResult, callingConv);
mCurMethodState->SetHadReturn(false);
mCurMethodState->mLeftBlockUncond = false;
mCurMethodState->mLeftBlockCond = false;
mBfIRBuilder->CreateBr(doneBB);
}
// Static invocation
{
mBfIRBuilder->AddBlock(falseBB);
mBfIRBuilder->SetInsertPoint(falseBB);
auto fieldPtr = mBfIRBuilder->CreateInBoundsGEP(multicastDelegate, 0, 1); // Load 'delegate.mFuncPtr'
auto funcPtrPtr = mBfIRBuilder->CreateBitCast(fieldPtr, staticFuncPtrPtr);
auto funcPtr = mBfIRBuilder->CreateLoad(funcPtrPtr);
staticResult = mBfIRBuilder->CreateCall(funcPtr, staticFuncArgs);
if (mCurMethodInstance->GetStructRetIdx() != -1)
{
// Note: since this is a forced static invocation, we know the sret will be the first parameter
mBfIRBuilder->Call_AddAttribute(staticResult, 0 + 1, BfIRAttribute_StructRet);
}
2019-08-23 11:56:54 -07:00
if (callingConv == BfIRCallingConv_ThisCall)
callingConv = BfIRCallingConv_CDecl;
if (callingConv != BfIRCallingConv_CDecl)
mBfIRBuilder->SetCallCallingConv(staticResult, callingConv);
mCurMethodState->SetHadReturn(false);
mCurMethodState->mLeftBlockUncond = false;
mCurMethodState->mLeftBlockCond = false;
mBfIRBuilder->CreateBr(doneBB);
}
mBfIRBuilder->AddBlock(doneBB);
mBfIRBuilder->SetInsertPoint(doneBB);
if ((mCurMethodInstance->mReturnType->IsValuelessType()) || (mCurMethodInstance->GetStructRetIdx() != -1))
2019-08-23 11:56:54 -07:00
{
mBfIRBuilder->CreateRetVoid();
}
else
{
BfIRType loweredIRReturnType;
BfTypeCode loweredTypeCode = BfTypeCode_None;
BfTypeCode loweredTypeCode2 = BfTypeCode_None;
if (mCurMethodInstance->GetLoweredReturnType(&loweredTypeCode, &loweredTypeCode2))
loweredIRReturnType = GetIRLoweredType(loweredTypeCode, loweredTypeCode2);
else
loweredIRReturnType = mBfIRBuilder->MapType(mCurMethodInstance->mReturnType);
auto phi = mBfIRBuilder->CreatePhi(loweredIRReturnType, 2);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->AddPhiIncoming(phi, nonStaticResult, trueBB);
mBfIRBuilder->AddPhiIncoming(phi, staticResult, falseBB);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->CreateRet(phi);
}
}
// "Interested" here means every method for a normal compile, and just the method the cursor is on for autocompletion
bool BfModule::IsInterestedInMethod(BfTypeInstance* typeInstance, BfMethodDef* methodDef)
{
auto typeDef = typeInstance->mTypeDef;
auto methodDeclaration = methodDef->mMethodDeclaration;
if (!mCompiler->mIsResolveOnly)
return true;
if (typeInstance->IsGenericTypeInstance())
{
// We only really want to process the unspecialized type for autocompletion
if ((!typeInstance->IsUnspecializedType()) || (typeInstance->IsUnspecializedTypeVariation()))
return false;
}
BfAstNode* checkNode = methodDeclaration;
if (methodDeclaration == NULL)
checkNode = methodDef->mBody;
if ((mCompiler->mResolvePassData->mParser != NULL) && (typeDef->mTypeDeclaration->IsFromParser(mCompiler->mResolvePassData->mParser)))
{
if (mCompiler->mResolvePassData->mAutoComplete == NULL)
return true;
}
return false;
}
void BfModule::CalcAppendAlign(BfMethodInstance* methodInst)
{
methodInst->mAppendAllocAlign = 1;
}
BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl<BfIRValue>& args)
{
BP_ZONE("BfModule::TryConstCalcAppend");
if (mCompiler->mIsResolveOnly)
return BfTypedValue();
// We want to regenerate all ctor calls when the method internals change
{
auto checkTypeInst = methodInst->GetOwner();
while (checkTypeInst->mTypeDef->mHasAppendCtor)
{
AddDependency(checkTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_InlinedCall);
checkTypeInst = GetBaseType(checkTypeInst);
}
}
if (!methodInst->mMayBeConst)
return BfTypedValue();
// Do we need to iterate in order to resolve mAppendAllocAlign?
bool isFirstRun = methodInst->mAppendAllocAlign < 0;
bool wasAllConst = true;
int argIdx = 0;
int paramIdx = 0;
while (true)
{
if (argIdx >= (int)args.size())
break;
auto paramType = methodInst->GetParamType(paramIdx);
PopulateType(paramType);
int argCount = 0;
if (!paramType->IsValuelessType())
{
if (methodInst->GetParamIsSplat(paramIdx))
argCount = paramType->GetSplatCount();
else
argCount = 1;
for (int argOfs = 0; argOfs < argCount; argOfs++)
{
auto arg = args[argIdx + argOfs];
if (!arg.IsConst())
{
wasAllConst = false;
if (!isFirstRun)
{
auto& param = methodInst->mParams[argIdx];
if (param.mReferencedInConstPass)
{
// If this param is required as part of the const calculation then
// we require that it be const...
return BfTypedValue();
}
}
}
}
}
paramIdx++;
argIdx += argCount;
}
auto methodDef = methodInst->mMethodDef;
auto methodDecl = methodDef->GetMethodDeclaration();
auto methodDeclBlock = BfNodeDynCast<BfBlock>(methodDecl->mBody);
if (methodDeclBlock == NULL)
2020-05-08 11:11:01 -07:00
return GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_IntPtr)); // Must not have an append alloc at all!
2019-08-23 11:56:54 -07:00
BfTypedValue constValue;
auto prevBlock = mBfIRBuilder->GetInsertBlock();
auto checkState = mCurMethodState->mConstResolveState;
while (checkState != NULL)
{
if (checkState->mMethodInstance == methodInst)
{
return BfTypedValue();
}
checkState = checkState->mPrevConstResolveState;
}
//auto accumValue = GetConstValue(0);
bool failed = false;
//
{
BfConstResolveState constResolveState;
constResolveState.mMethodInstance = methodInst;
constResolveState.mPrevConstResolveState = mCurMethodState->mConstResolveState;
SetAndRestoreValue<bool> ignoreWrites(mBfIRBuilder->mIgnoreWrites, true);
BfMethodState methodState;
SetAndRestoreValue<BfTypeInstance*> prevTypeInst(mCurTypeInstance, methodInst->GetOwner());
SetAndRestoreValue<BfMethodInstance*> prevMethodInst(mCurMethodInstance, methodInst);
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
methodState.mTempKind = BfMethodState::TempKind_NonStatic;
methodState.mConstResolveState = &constResolveState;
int argIdx = 0;
for (int paramIdx = 0; paramIdx < methodInst->GetParamCount(); paramIdx++)
{
if (methodInst->GetParamKind(paramIdx) == BfParamKind_AppendIdx)
continue;
auto paramType = methodInst->GetParamType(paramIdx);
// Fix this after we allow structs to be consts
//BF_ASSERT(!paramType->IsSplattable());
BfLocalVariable* localVar = new BfLocalVariable();
localVar->mName = methodInst->GetParamName(paramIdx);
localVar->mResolvedType = paramType;
localVar->mConstValue = args[argIdx];
localVar->mParamIdx = paramIdx;
AddLocalVariableDef(localVar);
argIdx++;
}
AppendAllocVisitor appendAllocVisitor;
appendAllocVisitor.mConstAccum = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_IntPtr));
appendAllocVisitor.mIsFirstConstPass = isFirstRun;
BfTypedValue baseCtorAppendValue = CallBaseCtorCalc(true);
if (baseCtorAppendValue)
{
if (baseCtorAppendValue.mValue.IsFake())
appendAllocVisitor.mFailed = true;
else
appendAllocVisitor.mConstAccum = baseCtorAppendValue;
}
appendAllocVisitor.mModule = this;
appendAllocVisitor.VisitChild(methodDecl->mBody);
if (!appendAllocVisitor.mFailed)
constValue = appendAllocVisitor.mConstAccum;
if (isFirstRun)
mCurMethodInstance->mEndingAppendAllocAlign = appendAllocVisitor.mCurAppendAlign;
if (isFirstRun)
{
for (int paramIdx = 0; paramIdx < methodInst->GetParamCount(); paramIdx++)
{
auto localVar = mCurMethodState->mLocals[paramIdx];
if (localVar->mReadFromId != -1)
{
auto& param = methodInst->mParams[paramIdx];
param.mReferencedInConstPass = true;
}
}
}
}
mBfIRBuilder->SetInsertPoint(prevBlock);
if (!constValue)
{
// If we did a 'force' then some params may not have been const -- only clear mMayBeConst if we had
// all-const args
if (wasAllConst)
{
methodInst->mMayBeConst = false;
}
}
return constValue;
}
BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly)
{
// Any errors should only be shown in the actual CTOR call
SetAndRestoreValue<bool> prevIgnoreWrites(mIgnoreErrors, true);
auto methodDef = mCurMethodInstance->mMethodDef;
BF_ASSERT((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_CtorCalcAppend));
auto ctorDeclaration = (BfConstructorDeclaration*)methodDef->mMethodDeclaration;
BfTypeInstance* targetType = NULL;
if (ctorDeclaration->mInitializer != NULL)
{
auto targetToken = BfNodeDynCast<BfTokenNode>(ctorDeclaration->mInitializer->mTarget);
targetType = (targetToken->GetToken() == BfToken_This) ? mCurTypeInstance : mCurTypeInstance->mBaseType;
}
else
targetType = mCurTypeInstance->mBaseType;
if ((targetType == NULL) || (targetType == mContext->mBfObjectType))
return NULL;
auto targetRefNode = methodDef->GetRefNode();
BfType* targetThisType = targetType;
BfTypedValue target(mBfIRBuilder->GetFakeVal(), targetThisType);
BfExprEvaluator exprEvaluator(this);
BfResolvedArgs argValues;
if ((ctorDeclaration != NULL) && (ctorDeclaration->mInitializer != NULL))
{
argValues.Init(&ctorDeclaration->mInitializer->mArguments);
}
//
{
}
BfFunctionBindResult bindResult;
bindResult.mSkipThis = true;
bindResult.mWantsArgs = true;
{
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
exprEvaluator.ResolveArgValues(argValues, BfResolveArgFlag_DeferParamEval);
SetAndRestoreValue<BfFunctionBindResult*> prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult);
exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, true);
}
if (bindResult.mMethodInstance == NULL)
{
AssertErrorState();
return BfTypedValue();
}
if (!bindResult.mMethodInstance->mMethodDef->mHasAppend)
{
return BfTypedValue();
}
BF_ASSERT(bindResult.mIRArgs[0].IsFake());
bindResult.mIRArgs.RemoveAt(0);
auto calcAppendMethodModule = GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND);
BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, bindResult.mIRArgs);
BF_ASSERT(calcAppendMethodModule.mMethodInstance->mAppendAllocAlign >= 0);
mCurMethodInstance->mAppendAllocAlign = BF_MAX((int)mCurMethodInstance->mAppendAllocAlign, calcAppendMethodModule.mMethodInstance->mAppendAllocAlign);
BF_ASSERT(calcAppendMethodModule.mMethodInstance->mEndingAppendAllocAlign > -1);
mCurMethodState->mCurAppendAlign = BF_MAX(calcAppendMethodModule.mMethodInstance->mEndingAppendAllocAlign, 0);
if (appendSizeTypedValue)
return appendSizeTypedValue;
if (constOnly)
return BfTypedValue(mBfIRBuilder->GetFakeVal(), GetPrimitiveType(BfTypeCode_IntPtr));
bool needsRecalc = false;
for (auto irArg : bindResult.mIRArgs)
{
if (irArg.IsFake())
needsRecalc = true;
}
auto calcAppendArgs = bindResult.mIRArgs;
if (needsRecalc)
{
// Do it again, but without mIgnoreWrites set
BfResolvedArgs argValues;
argValues.Init(&ctorDeclaration->mInitializer->mArguments);
exprEvaluator.ResolveArgValues(argValues, BfResolveArgFlag_DeferParamEval);
BfFunctionBindResult bindResult;
bindResult.mSkipThis = true;
bindResult.mWantsArgs = true;
SetAndRestoreValue<BfFunctionBindResult*> prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult);
exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, true);
BF_ASSERT(bindResult.mIRArgs[0].IsFake());
bindResult.mIRArgs.RemoveAt(0);
calcAppendArgs = bindResult.mIRArgs;
}
BF_ASSERT(calcAppendMethodModule.mFunc);
appendSizeTypedValue = exprEvaluator.CreateCall(calcAppendMethodModule.mMethodInstance, calcAppendMethodModule.mFunc, false, calcAppendArgs);
BF_ASSERT(appendSizeTypedValue.mType == GetPrimitiveType(BfTypeCode_IntPtr));
return appendSizeTypedValue;
}
// This method never throws errors - it relies on the proper ctor actually throwing the errors
void BfModule::EmitCtorCalcAppend()
{
if (mCompiler->mIsResolveOnly)
return;
auto methodDef = mCurMethodInstance->mMethodDef;
auto methodDecl = methodDef->GetMethodDeclaration();
Array<BfAstNode*> deferredNodeList;
auto methodDeclBlock = BfNodeDynCast<BfBlock>(methodDecl->mBody);
if (methodDeclBlock == NULL)
return;
AppendAllocVisitor appendAllocVisitor;
auto baseCalcAppend = CallBaseCtorCalc(false);
if (baseCalcAppend)
{
mBfIRBuilder->CreateStore(baseCalcAppend.mValue, mCurMethodState->mRetVal.mValue);
}
appendAllocVisitor.mModule = this;
appendAllocVisitor.VisitChild(methodDecl->mBody);
}
void BfModule::CreateStaticCtor()
{
auto typeDef = mCurTypeInstance->mTypeDef;
auto methodDef = mCurMethodInstance->mMethodDef;
BfIRBlock exitBB;
if ((HasCompiledOutput()) && (!mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mChainType != BfMethodChainType_ChainMember))
{
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
auto didStaticInitVarAddr = mBfIRBuilder->CreateGlobalVariable(
mBfIRBuilder->MapType(boolType),
false,
BfIRLinkageType_Internal,
GetDefaultValue(boolType),
"didStaticInit");
auto initBB = mBfIRBuilder->CreateBlock("init", true);
mCurMethodState->mIRExitBlock = mBfIRBuilder->CreateBlock("exit", true);
auto didStaticInitVar = mBfIRBuilder->CreateLoad(didStaticInitVarAddr);
mBfIRBuilder->CreateCondBr(didStaticInitVar, mCurMethodState->mIRExitBlock, initBB);
mBfIRBuilder->SetInsertPoint(initBB);
mBfIRBuilder->CreateStore(GetConstValue(1, boolType), didStaticInitVarAddr);
}
// Do DLL imports
if (!mDllImportEntries.empty())
{
auto internalType = ResolveTypeDef(mCompiler->mInternalTypeDef);
PopulateType(internalType);
auto getSharedProcAddressInstance = GetMethodByName(internalType->ToTypeInstance(), "GetSharedProcAddress");
if (!getSharedProcAddressInstance)
{
if (!mCompiler->mPassInstance->HasFailed())
Fail("Internal error: System.Internal doesn't contain LoadSharedLibrary method");
}
}
// Fill in initializer values
if ((!mCompiler->mIsResolveOnly) || (mCompiler->mResolvePassData->mAutoComplete == NULL))
{
for (auto fieldDef : typeDef->mFields)
{
if ((!fieldDef->mIsConst) && (fieldDef->mIsStatic) && (fieldDef->mInitializer != NULL))
{
// For extensions, only handle these fields in the appropriate extension
if ((fieldDef->mDeclaringType->mTypeDeclaration != methodDef->mDeclaringType->mTypeDeclaration))
continue;
2019-08-23 11:56:54 -07:00
UpdateSrcPos(fieldDef->mInitializer);
auto fieldInst = &mCurTypeInstance->mFieldInstances[fieldDef->mIdx];
if (!fieldInst->mFieldIncluded)
continue;
if (fieldInst->mResolvedType->IsVar())
2019-11-02 06:02:36 -07:00
{
continue;
}
GetFieldInitializerValue(fieldInst, NULL, NULL, NULL, true);
2019-08-23 11:56:54 -07:00
}
}
}
else
{
for (auto tempTypeDef : mCompiler->mResolvePassData->mAutoCompleteTempTypes)
{
if (tempTypeDef->mFullName == typeDef->mFullName)
{
for (auto fieldDef : tempTypeDef->mFields)
{
if ((fieldDef->mIsStatic) && (!fieldDef->mIsConst))
{
if (fieldDef->mInitializer != NULL)
{
if (mCompiler->mResolvePassData->mSourceClassifier != NULL)
{
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(fieldDef->mInitializer, BfSourceElementType_Normal);
mCompiler->mResolvePassData->mSourceClassifier->VisitChild(fieldDef->mInitializer);
}
BfType* wantType = NULL;
if ((!BfNodeIsA<BfVarTypeReference>(fieldDef->mTypeRef)) && (!BfNodeIsA<BfLetTypeReference>(fieldDef->mTypeRef)))
{
wantType = ResolveTypeRef(fieldDef->mTypeRef);
}
CreateValueFromExpression(fieldDef->mInitializer, wantType, BfEvalExprFlags_FieldInitializer);
}
}
}
}
}
}
if (mCurMethodInstance->mChainType == BfMethodChainType_ChainHead)
CallChainedMethods(mCurMethodInstance, false);
}
void BfModule::EmitDtorBody()
{
if (mCurTypeInstance->IsClosure())
{
BfFieldInstance* fieldInstance = &mCurTypeInstance->mFieldInstances.back();
BF_ASSERT(fieldInstance->GetFieldDef()->mName == "__dtorThunk");
auto thisVal = GetThis();
auto dtorThunkPtr = mBfIRBuilder->CreateInBoundsGEP(thisVal.mValue, 0, fieldInstance->mDataIdx);
auto dtorThunk = mBfIRBuilder->CreateLoad(dtorThunkPtr);
auto funcPtrType = mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapMethod(mCurMethodInstance));
auto dtorPtr = mBfIRBuilder->CreateBitCast(dtorThunk, funcPtrType);
SizedArray<BfIRValue, 1> args;
args.push_back(thisVal.mValue);
auto result = mBfIRBuilder->CreateCall(dtorPtr, args);
mBfIRBuilder->SetCallCallingConv(result, BfIRCallingConv_CDecl);
// Fall through to Object::~this call
auto dtorFunc = GetMethodByName(mContext->mBfObjectType, "~this");
auto basePtr = mBfIRBuilder->CreateBitCast(thisVal.mValue, mBfIRBuilder->MapTypeInstPtr(mContext->mBfObjectType));
SizedArray<BfIRValue, 1> vals = { basePtr };
result = mBfIRBuilder->CreateCall(dtorFunc.mFunc, vals);
mBfIRBuilder->SetCallCallingConv(result, GetIRCallingConvention(dtorFunc.mMethodInstance));
2019-08-23 11:56:54 -07:00
mBfIRBuilder->SetTailCall(result);
return;
}
auto typeDef = mCurTypeInstance->mTypeDef;
auto methodDef = mCurMethodInstance->mMethodDef;
auto methodDeclaration = methodDef->GetMethodDeclaration();
if (mCurMethodInstance->mChainType == BfMethodChainType_ChainHead)
CallChainedMethods(mCurMethodInstance, true);
if (auto bodyBlock = BfNodeDynCast<BfBlock>(methodDef->mBody))
{
VisitCodeBlock(bodyBlock);
if (bodyBlock->mCloseBrace != NULL)
{
UpdateSrcPos(bodyBlock->mCloseBrace);
}
}
else
{
if (methodDeclaration != NULL)
UpdateSrcPos(methodDeclaration);
else if (typeDef->mTypeDeclaration != NULL)
UpdateSrcPos(typeDef->mTypeDeclaration);
if ((methodDeclaration != NULL) && (methodDeclaration->mFatArrowToken != NULL))
{
Fail("Destructors cannot have expression bodies", methodDeclaration->mFatArrowToken, true);
2019-08-23 11:56:54 -07:00
}
}
if ((!mCompiler->mIsResolveOnly) || (mCompiler->mResolvePassData->mAutoComplete == NULL))
{
for (int fieldIdx = (int)mCurTypeInstance->mFieldInstances.size() - 1; fieldIdx >= 0; fieldIdx--)
{
auto fieldInst = &mCurTypeInstance->mFieldInstances[fieldIdx];
auto fieldDef = fieldInst->GetFieldDef();
if ((fieldDef != NULL) && (fieldDef->mIsStatic == methodDef->mIsStatic) && (fieldDef->mFieldDeclaration != NULL) && (fieldDef->mFieldDeclaration->mFieldDtor != NULL))
{
if ((!methodDef->mIsStatic) && (mCurTypeInstance->IsValueType()))
{
Fail("Structs cannot have field destructors", fieldDef->mFieldDeclaration->mFieldDtor->mTildeToken, true);
2019-08-23 11:56:54 -07:00
}
SetAndRestoreValue<BfFilePosition> prevFilePos(mCurFilePosition);
auto fieldDtor = fieldDef->mFieldDeclaration->mFieldDtor;
if ((fieldDef->mIsStatic) != (methodDef->mIsStatic))
continue;
UpdateSrcPos(fieldDtor);
BfScopeData scopeData;
mCurMethodState->AddScope(&scopeData);
NewScopeState();
UpdateSrcPos(fieldDtor);
bool hasDbgInfo = (!fieldDef->mIsConst);
if (hasDbgInfo)
{
mBfIRBuilder->SaveDebugLocation();
mBfIRBuilder->ClearDebugLocation();
}
BfIRValue value;
if (fieldDef->mIsStatic)
{
value = ReferenceStaticField(fieldInst).mValue;
}
else
{
if (!mCurTypeInstance->IsValueType())
{
auto thisValue = GetThis();
value = mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, fieldInst->mDataIdx);
}
else
{
AssertErrorState();
value = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(fieldInst->mResolvedType));
2019-08-23 11:56:54 -07:00
}
}
BfIRValue staticVal;
if (hasDbgInfo)
{
BfIRValue dbgShowValue = value;
BfLocalVariable* localDef = new BfLocalVariable();
localDef->mName = "_";
localDef->mResolvedType = fieldInst->mResolvedType;
localDef->mAddr = value;
if ((mBfIRBuilder->DbgHasInfo()) && (!IsTargetingBeefBackend()))
{
// Create another pointer indirection, a ref to the gep
auto refFieldType = CreateRefType(fieldInst->mResolvedType);
auto allocaInst = CreateAlloca(refFieldType);
auto storeResult = mBfIRBuilder->CreateStore(value, allocaInst);
localDef->mResolvedType = refFieldType;
localDef->mAddr = allocaInst;
}
mBfIRBuilder->RestoreDebugLocation();
auto defLocalVar = AddLocalVariableDef(localDef, true);
// Put back so we actually modify the correct value*/
defLocalVar->mResolvedType = fieldInst->mResolvedType;
defLocalVar->mAddr = value;
}
while (fieldDtor != NULL)
{
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mSourceClassifier != NULL))
{
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(fieldDtor, BfSourceElementType_Normal);
mCompiler->mResolvePassData->mSourceClassifier->VisitChild(fieldDtor);
}
UpdateSrcPos(fieldDtor);
VisitEmbeddedStatement(fieldDtor->mBody);
fieldDtor = fieldDtor->mNextFieldDtor;
}
RestoreScopeState();
}
}
if (!methodDef->mIsStatic)
{
auto checkBaseType = mCurTypeInstance->mBaseType;
while (checkBaseType != NULL)
{
if (auto bodyBlock = BfNodeDynCast<BfBlock>(methodDef->mBody))
{
if (bodyBlock->mCloseBrace != NULL)
UpdateSrcPos(bodyBlock->mCloseBrace);
}
else
{
UpdateSrcPos(typeDef->mTypeDeclaration->mNameNode);
}
if (checkBaseType->mTypeDef->mDtorDef != NULL)
{
auto dtorMethodInstance = GetMethodInstance(checkBaseType, checkBaseType->mTypeDef->mDtorDef, BfTypeVector());
if (mCompiler->IsSkippingExtraResolveChecks())
{
// Nothing
}
else if (dtorMethodInstance.mMethodInstance->GetParamCount() == 0)
{
if ((!mCompiler->IsSkippingExtraResolveChecks()) && (!checkBaseType->IsUnspecializedType()))
{
auto basePtr = mBfIRBuilder->CreateBitCast(mCurMethodState->mLocals[0]->mValue, mBfIRBuilder->MapTypeInstPtr(checkBaseType));
SizedArray<BfIRValue, 1> vals = { basePtr };
auto callInst = mBfIRBuilder->CreateCall(dtorMethodInstance.mFunc, vals);
mBfIRBuilder->SetCallCallingConv(callInst, GetIRCallingConvention(dtorMethodInstance.mMethodInstance));
2019-08-23 11:56:54 -07:00
}
}
else
{
// Invalid base dtor declaration
AssertErrorState();
}
break;
}
checkBaseType = checkBaseType->mBaseType;
}
}
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
}
else
{
// The reason we can't just do the 'normal' path for this is that the BfTypeInstance here is NOT the
// autocomplete type instance, so FieldInstance initializer values contain expressions from the full
// resolve pass, NOT the autocomplete expression
for (auto tempTypeDef : mCompiler->mResolvePassData->mAutoCompleteTempTypes)
{
if (tempTypeDef->mFullName == typeDef->mFullName)
{
for (auto fieldDef : tempTypeDef->mFields)
{
if ((fieldDef->mIsStatic == methodDef->mIsStatic) && (fieldDef->mFieldDeclaration != NULL) &&
(fieldDef->mFieldDeclaration->mFieldDtor != NULL) && (mCompiler->mResolvePassData->mSourceClassifier != NULL))
{
BfType* fieldType = NULL;
for (int curFieldIdx = 0; curFieldIdx < (int)mCurTypeInstance->mFieldInstances.size(); curFieldIdx++)
{
auto curFieldInstance = &mCurTypeInstance->mFieldInstances[curFieldIdx];
auto curFieldDef = curFieldInstance->GetFieldDef();
if ((curFieldDef != NULL) && (fieldDef->mName == curFieldDef->mName))
fieldType = curFieldInstance->GetResolvedType();
}
if (fieldType == NULL)
fieldType = GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
auto fieldDtor = fieldDef->mFieldDeclaration->mFieldDtor;
BfScopeData scopeData;
mCurMethodState->AddScope(&scopeData);
NewScopeState();
// This is just for autocomplete, it doesn't matter that mAddr is incorrect
if (fieldType != NULL)
{
BfLocalVariable* localDef = new BfLocalVariable();
localDef->mName = "_";
localDef->mResolvedType = fieldType;
2019-08-23 11:56:54 -07:00
localDef->mAddr = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(fieldType));
localDef->mIsAssigned = true;
AddLocalVariableDef(localDef);
}
while (fieldDtor != NULL)
{
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(fieldDtor, BfSourceElementType_Normal);
mCompiler->mResolvePassData->mSourceClassifier->VisitChild(fieldDtor);
UpdateSrcPos(fieldDtor);
VisitEmbeddedStatement(fieldDtor->mBody);
fieldDtor = fieldDtor->mNextFieldDtor;
}
RestoreScopeState();
}
}
}
}
}
}
BfIRValue BfModule::CreateDllImportGlobalVar(BfMethodInstance* methodInstance, bool define)
{
auto typeInstance = methodInstance->GetOwner();
bool foundDllImportAttr = false;
BfCallingConvention callingConvention = methodInstance->mCallingConvention;
2019-08-23 11:56:54 -07:00
for (auto customAttr : methodInstance->GetCustomAttributes()->mAttributes)
{
if (customAttr.mType->mTypeDef->mFullName.ToString() == "System.ImportAttribute")
{
foundDllImportAttr = true;
}
}
if (!foundDllImportAttr)
{
AssertErrorState();
return BfIRValue();
}
String name = "bf_hs_preserve@";
BfMangler::Mangle(name, mCompiler->GetMangleKind(), methodInstance);
name += "__imp";
BfIRType returnType;
SizedArray<BfIRType, 8> paramTypes;
methodInstance->GetIRFunctionInfo(this, returnType, paramTypes);
BfIRFunctionType externFunctionType = mBfIRBuilder->CreateFunctionType(returnType, paramTypes, methodInstance->IsVarArgs());
2019-08-23 11:56:54 -07:00
auto ptrType = mBfIRBuilder->GetPointerTo(externFunctionType);
BfIRValue initVal;
if (define)
{
if (methodInstance->GetImportCallKind() != BfImportCallKind_None)
initVal = mBfIRBuilder->CreateConstNull(ptrType);
}
auto globalVar = mBfIRBuilder->CreateGlobalVariable(ptrType, false, BfIRLinkageType_External, initVal, name);
if ((define) && (mBfIRBuilder->DbgHasInfo()))
{
auto voidType = GetPrimitiveType(BfTypeCode_None);
auto ptrType = CreatePointerType(voidType);
mBfIRBuilder->DbgCreateGlobalVariable(mDICompileUnit, name, name, BfIRMDNode(), 0, mBfIRBuilder->DbgGetType(ptrType), false, globalVar);
}
return globalVar;
}
void BfModule::CreateDllImportMethod()
{
if (mBfIRBuilder->mIgnoreWrites)
return;
auto globalVar = mFuncReferences[mCurMethodInstance];
if (!globalVar)
return;
bool allowTailCall = true;
mBfIRBuilder->ClearDebugLocation();
bool isHotCompile = mCompiler->IsHotCompile();
// If we are hot swapping, we need to have this stub because we may need to call the LoadSharedLibraries on demand
if (isHotCompile)
{
auto loadSharedLibsFunc = GetBuiltInFunc(BfBuiltInFuncType_LoadSharedLibraries);
mBfIRBuilder->CreateCall(loadSharedLibsFunc, SizedArray<BfIRValue, 0>());
}
auto callingConvention = GetIRCallingConvention(mCurMethodInstance);
2019-08-23 11:56:54 -07:00
BfIRType returnType;
SizedArray<BfIRType, 8> paramTypes;
mCurMethodInstance->GetIRFunctionInfo(this, returnType, paramTypes, mCurMethodInstance->IsVarArgs());
2019-08-23 11:56:54 -07:00
SizedArray<BfIRValue, 8> args;
for (int i = 0; i < (int)paramTypes.size(); i++)
args.push_back(mBfIRBuilder->GetArgument(i));
BfIRFunctionType externFunctionType = mBfIRBuilder->CreateFunctionType(returnType, paramTypes, mCurMethodInstance->IsVarArgs());
2019-08-23 11:56:54 -07:00
if (isHotCompile)
{
BfIRValue funcVal = mBfIRBuilder->CreateLoad(globalVar);
auto result = mBfIRBuilder->CreateCall(funcVal, args);
if (callingConvention == BfIRCallingConv_StdCall)
mBfIRBuilder->SetCallCallingConv(result, BfIRCallingConv_StdCall);
else if (callingConvention == BfIRCallingConv_FastCall)
mBfIRBuilder->SetCallCallingConv(result, BfIRCallingConv_FastCall);
2019-08-23 11:56:54 -07:00
else
mBfIRBuilder->SetCallCallingConv(result, BfIRCallingConv_CDecl);
if (allowTailCall)
mBfIRBuilder->SetTailCall(result);
CreateReturn(result);
mCurMethodState->mHadReturn = true;
}
if (HasCompiledOutput())
{
BfDllImportEntry dllImportEntry;
dllImportEntry.mFuncVar = globalVar;
dllImportEntry.mMethodInstance = mCurMethodInstance;
mDllImportEntries.push_back(dllImportEntry);
}
}
BfIRCallingConv BfModule::GetIRCallingConvention(BfMethodInstance* methodInstance)
2019-08-23 11:56:54 -07:00
{
auto methodDef = methodInstance->mMethodDef;
BfTypeInstance* owner = NULL;
if (!methodDef->mIsStatic)
owner = methodInstance->GetParamType(-1)->ToTypeInstance();
if (owner == NULL)
owner = methodInstance->GetOwner();
if ((mCompiler->mOptions.mMachineType != BfMachineType_x86) || (mCompiler->mOptions.mPlatformType != BfPlatformType_Windows))
return BfIRCallingConv_CDecl;
if (methodInstance->mCallingConvention == BfCallingConvention_Stdcall)
return BfIRCallingConv_StdCall;
if (methodInstance->mCallingConvention == BfCallingConvention_Fastcall)
return BfIRCallingConv_FastCall;
if ((!methodDef->mIsStatic) && (!owner->IsValuelessType()) &&
((!owner->IsSplattable()) || (methodDef->HasNoThisSplat())))
return BfIRCallingConv_ThisCall;
return BfIRCallingConv_CDecl;
//return GetIRCallingConvention(owner, methodInstance->mMethodDef);
2019-08-23 11:56:54 -07:00
}
void BfModule::SetupIRMethod(BfMethodInstance* methodInstance, BfIRFunction func, bool isInlined)
2019-08-23 11:56:54 -07:00
{
BfMethodDef* methodDef = NULL;
if (methodInstance != NULL)
methodDef = methodInstance->mMethodDef;
2019-08-23 11:56:54 -07:00
if (!func)
return;
if (mCompiler->mOptions.mNoFramePointerElim)
mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_NoFramePointerElim);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_NoUnwind);
if (mSystem->mPtrSize == 8) // We need unwind info for debugging
mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_UWTable);
if (methodInstance == NULL)
return;
if (methodDef->mImportKind == BfImportKind_Export)
mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_DllExport);
2019-08-23 11:56:54 -07:00
if (methodDef->mNoReturn)
mBfIRBuilder->Func_AddAttribute(func, -1, BfIRAttribute_NoReturn);
auto callingConv = GetIRCallingConvention(methodInstance);
2019-08-23 11:56:54 -07:00
if (callingConv != BfIRCallingConv_CDecl)
mBfIRBuilder->SetFuncCallingConv(func, callingConv);
if (isInlined)
{
mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_AlwaysInline);
}
2019-08-23 11:56:54 -07:00
int argIdx = 0;
int paramIdx = 0;
if (methodInstance->HasThis())
{
paramIdx = -1;
}
int argCount = methodInstance->GetIRFunctionParamCount(this);
2019-08-23 11:56:54 -07:00
while (argIdx < argCount)
{
if (argIdx == methodInstance->GetStructRetIdx())
{
mBfIRBuilder->Func_AddAttribute(func, argIdx + 1, BfIRAttribute_NoAlias);
mBfIRBuilder->Func_AddAttribute(func, argIdx + 1, BfIRAttribute_StructRet);
argIdx++;
continue;
}
2019-08-23 11:56:54 -07:00
while ((paramIdx != -1) && (methodInstance->IsParamSkipped(paramIdx)))
paramIdx++;
BfType* resolvedTypeRef = NULL;
BfType* resolvedTypeRef2 = NULL;
2019-08-23 11:56:54 -07:00
String paramName;
bool isSplattable = false;
bool tryLowering = true;
2019-08-23 11:56:54 -07:00
bool isThis = paramIdx == -1;
if (isThis)
{
paramName = "this";
if (methodInstance->mIsClosure)
resolvedTypeRef = mCurMethodState->mClosureState->mClosureType;
else
resolvedTypeRef = methodInstance->GetOwner();
isSplattable = (resolvedTypeRef->IsSplattable()) && (methodInstance->AllowsThisSplatting());
tryLowering = methodInstance->AllowsThisSplatting();
2019-08-23 11:56:54 -07:00
}
else
2019-08-23 11:56:54 -07:00
{
paramName = methodInstance->GetParamName(paramIdx);
resolvedTypeRef = methodInstance->GetParamType(paramIdx);
if (resolvedTypeRef->IsMethodRef())
isSplattable = true;
else if ((resolvedTypeRef->IsSplattable()) && (methodInstance->AllowsSplatting()))
2019-08-23 11:56:54 -07:00
{
auto resolvedTypeInst = resolvedTypeRef->ToTypeInstance();
if ((resolvedTypeInst != NULL) && (resolvedTypeInst->mIsCRepr))
{
// crepr splat is always splattable
isSplattable = true;
}
else if (resolvedTypeRef->GetSplatCount() + argIdx <= mCompiler->mOptions.mMaxSplatRegs)
2019-08-23 11:56:54 -07:00
isSplattable = true;
}
}
if (tryLowering)
2019-08-23 11:56:54 -07:00
{
BfTypeCode loweredTypeCode = BfTypeCode_None;
BfTypeCode loweredTypeCode2 = BfTypeCode_None;
if (resolvedTypeRef->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2))
{
mBfIRBuilder->Func_SetParamName(func, argIdx + 1, paramName + "__1");
argIdx++;
if (loweredTypeCode2 != BfTypeCode_None)
{
mBfIRBuilder->Func_SetParamName(func, argIdx + 1, paramName + "__2");
argIdx++;
}
paramIdx++;
continue;
}
2019-08-23 11:56:54 -07:00
}
auto _SetupParam = [&](const StringImpl& paramName, BfType* resolvedTypeRef)
{
mBfIRBuilder->Func_SetParamName(func, argIdx + 1, paramName);
int addDeref = -1;
if (resolvedTypeRef->IsRef())
{
auto refType = (BfRefType*)resolvedTypeRef;
auto elementType = refType->mElementType;
PopulateType(elementType, BfPopulateType_Data);
addDeref = elementType->mSize;
if ((addDeref <= 0) && (!elementType->IsValuelessType()))
AssertErrorState();
}
if ((resolvedTypeRef->IsComposite()) && (!resolvedTypeRef->IsTypedPrimitive()))
{
if (paramIdx == -1)
2019-08-23 11:56:54 -07:00
{
mBfIRBuilder->Func_AddAttribute(func, argIdx + 1, BfIRAttribute_NoCapture);
PopulateType(resolvedTypeRef, BfPopulateType_Data);
addDeref = resolvedTypeRef->mSize;
}
2020-06-10 09:39:16 -07:00
else if (methodInstance->WantsIRStructsByVal())
{
mBfIRBuilder->PopulateType(resolvedTypeRef);
mBfIRBuilder->Func_AddAttribute(func, argIdx + 1, BfIRAttribute_ByVal, mSystem->mPtrSize);
}
2019-08-23 11:56:54 -07:00
}
else if (resolvedTypeRef->IsPrimitiveType())
{
auto primType = (BfPrimitiveType*)resolvedTypeRef;
if (primType->mTypeDef->mTypeCode == BfTypeCode_Boolean)
mBfIRBuilder->Func_AddAttribute(func, argIdx + 1, BfIRAttribute_ZExt);
}
if (addDeref >= 0)
mBfIRBuilder->Func_AddAttribute(func, argIdx + 1, BfIRAttribute_Dereferencable, addDeref);
argIdx++;
};
if (isSplattable)
{
std::function<void(BfType*, const StringImpl&)> checkTypeLambda = [&](BfType* checkType, const StringImpl& curName)
{
if (checkType->IsStruct())
{
auto checkTypeInstance = checkType->ToTypeInstance();
if (checkTypeInstance->mBaseType != NULL)
checkTypeLambda(checkTypeInstance->mBaseType, curName);
if (checkTypeInstance->mIsUnion)
{
BfType* unionInnerType = checkTypeInstance->GetUnionInnerType();
checkTypeLambda(unionInnerType, curName + "_data");
}
else
{
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
auto fieldDef = fieldInstance->GetFieldDef();
if (fieldInstance->mDataIdx >= 0)
{
checkTypeLambda(fieldInstance->GetResolvedType(), curName + "_" + fieldInstance->GetFieldDef()->mName);
}
}
}
if (checkTypeInstance->IsEnum())
{
mBfIRBuilder->Func_SetParamName(func, argIdx + 1, curName + "_dscr");
argIdx++;
}
}
else if (checkType->IsMethodRef())
{
auto methodRefType = (BfMethodRefType*)checkType;
BfMethodInstance* methodRefMethodInst = methodRefType->mMethodRef;
for (int dataIdx = 0; dataIdx < (int)methodRefType->GetCaptureDataCount(); dataIdx++)
{
int methodRefParamIdx = methodRefType->GetParamIdxFromDataIdx(dataIdx);
if (methodRefType->WantsDataPassedAsSplat(dataIdx))
{
checkTypeLambda(methodRefType->GetCaptureType(dataIdx), curName + "_" + methodRefMethodInst->GetParamName(methodRefParamIdx));
}
else
{
_SetupParam(curName + "_" + methodRefMethodInst->GetParamName(methodRefParamIdx), methodRefType->GetCaptureType(dataIdx));
}
}
}
else if (!checkType->IsValuelessType())
{
_SetupParam(curName, checkType);
}
};
checkTypeLambda(resolvedTypeRef, paramName);
paramIdx++;
continue;
}
_SetupParam(paramName, resolvedTypeRef);
if (resolvedTypeRef2 != NULL)
_SetupParam(paramName, resolvedTypeRef2);
2019-08-23 11:56:54 -07:00
paramIdx++;
}
}
void BfModule::EmitCtorBody(bool& skipBody)
{
auto methodInstance = mCurMethodInstance;
auto methodDef = methodInstance->mMethodDef;
auto methodDeclaration = methodDef->mMethodDeclaration;
auto ctorDeclaration = (BfConstructorDeclaration*)methodDef->mMethodDeclaration;
auto typeDef = mCurTypeInstance->mTypeDef;
// Prologue
mBfIRBuilder->ClearDebugLocation();
bool hadThisInitializer = false;
if ((ctorDeclaration != NULL) && (ctorDeclaration->mInitializer != NULL))
{
auto targetToken = BfNodeDynCast<BfTokenNode>(ctorDeclaration->mInitializer->mTarget);
auto targetType = (targetToken->GetToken() == BfToken_This) ? mCurTypeInstance : mCurTypeInstance->mBaseType;
if (targetToken->GetToken() == BfToken_This)
{
hadThisInitializer = true;
// Since we call a 'this', it's that other ctor's responsibility to fully assign the fields
mCurMethodState->LocalDefined(GetThisVariable());
}
}
// Zero out memory for default ctor
if ((methodDeclaration == NULL) && (mCurTypeInstance->IsStruct()))
{
if (mCurTypeInstance->IsTypedPrimitive())
{
auto thisRef = mCurMethodState->mLocals[0]->mValue;
mBfIRBuilder->CreateStore(GetDefaultValue(mCurTypeInstance), thisRef);
}
else if (mCurTypeInstance->mInstSize > 0)
{
BfIRValue fillValue = GetConstValue8(0);
BfIRValue sizeValue = GetConstValue(mCurTypeInstance->mInstSize);
auto thisRef = mCurMethodState->mLocals[0]->mValue;
BfIRValue destBits = mBfIRBuilder->CreateBitCast(thisRef, mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr));
mBfIRBuilder->CreateMemSet(destBits, fillValue, sizeValue, mCurTypeInstance->mAlign);
}
}
BfAstNode* baseCtorNode = NULL;
if ((ctorDeclaration != NULL) && (ctorDeclaration->mInitializer != NULL) && (ctorDeclaration->mInitializer->mTarget != NULL))
baseCtorNode = ctorDeclaration->mInitializer->mTarget;
else if (methodDef->mBody != NULL)
baseCtorNode = methodDef->mBody;
else if (ctorDeclaration != NULL)
baseCtorNode = ctorDeclaration;
else if (mCurTypeInstance->mTypeDef->mTypeDeclaration != NULL)
baseCtorNode = mCurTypeInstance->mTypeDef->mTypeDeclaration->mNameNode;
else if ((mCurTypeInstance->mBaseType != NULL) && (mCurTypeInstance->mBaseType->mTypeDef->mTypeDeclaration != NULL))
baseCtorNode = mCurTypeInstance->mBaseType->mTypeDef->mTypeDeclaration;
else
baseCtorNode = mContext->mBfObjectType->mTypeDef->mTypeDeclaration;
if ((!mCurTypeInstance->IsBoxed()) && (methodDef->mMethodType == BfMethodType_Ctor))
{
// Call the root type's default ctor (with no body) to initialize its fields and call the chained ctors
if (methodDef->mDeclaringType->mTypeCode == BfTypeCode_Extension)
{
BfMethodDef* defaultCtor = NULL;
for (auto methodDef : mCurTypeInstance->mTypeDef->mMethods)
{
if ((methodDef->mMethodType == BfMethodType_CtorNoBody) && (methodDef->mDeclaringType->mTypeCode != BfTypeCode_Extension))
{
defaultCtor = methodDef;
}
}
if (defaultCtor != NULL)
{
UpdateSrcPos(mCurTypeInstance->mTypeDef->GetRefNode());
SetIllegalSrcPos();
auto moduleMethodInstance = GetMethodInstance(mCurTypeInstance, defaultCtor, BfTypeVector());
BfExprEvaluator exprEvaluator(this);
SizedArray<BfIRValue, 1> irArgs;
exprEvaluator.PushThis(NULL, GetThis(), moduleMethodInstance.mMethodInstance, irArgs);
exprEvaluator.CreateCall(moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, false, irArgs);
}
}
}
// Initialize fields (if applicable)
if (mCurTypeInstance->IsBoxed())
{
skipBody = true;
}
else if ((methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mDeclaringType->mTypeCode == BfTypeCode_Extension))
{
// Field initializers occur in CtorNoBody methods for extensions
}
else if (!hadThisInitializer)
{
// If we had a 'this' initializer, that other ctor will have initialized our fields
if ((!mCompiler->mIsResolveOnly) ||
(mCompiler->mResolvePassData->mAutoComplete == NULL) ||
(mCompiler->mResolvePassData->mAutoComplete->mResolveType == BfResolveType_ShowFileSymbolReferences))
{
bool hadInlineInitBlock = false;
BfScopeData scopeData;
for (auto fieldDef : typeDef->mFields)
{
// For extensions, only handle these fields in the appropriate extension
if ((fieldDef->mDeclaringType->mTypeDeclaration != methodDef->mDeclaringType->mTypeDeclaration))
continue;
if ((!fieldDef->mIsConst) && (!fieldDef->mIsStatic))
{
auto fieldInst = &mCurTypeInstance->mFieldInstances[fieldDef->mIdx];
if (!fieldInst->mFieldIncluded)
continue;
if (fieldInst->mDataIdx < 0)
continue;
if (fieldDef->mInitializer == NULL)
{
2020-04-10 07:53:56 -07:00
continue;
// if (fieldDef->mProtection != BfProtection_Hidden)
// continue;
// if (mCurTypeInstance->IsObject()) // Already zeroed out
// continue;
2019-08-23 11:56:54 -07:00
}
if (fieldInst->mResolvedType == NULL)
{
BF_ASSERT(mHadBuildError);
continue;
}
if (fieldDef->mInitializer != NULL)
{
if (!hadInlineInitBlock)
{
if ((mBfIRBuilder->DbgHasInfo()) && (!mCurTypeInstance->IsUnspecializedType()) && (mHasFullDebugInfo))
{
UpdateSrcPos(baseCtorNode);
if (methodDef->mBody == NULL)
SetIllegalSrcPos();
2019-08-23 11:56:54 -07:00
// NOP so we step onto the open paren of the method so we can choose to set into the field initializers or step over them
EmitEnsureInstructionAt();
BfType* thisType = mCurTypeInstance;
if (thisType->IsValueType())
thisType = CreateRefType(thisType);
SizedArray<BfIRMDNode, 1> diParamTypes;
BfIRMDNode diFuncType = mBfIRBuilder->DbgCreateSubroutineType(diParamTypes);
mCurMethodState->AddScope(&scopeData);
NewScopeState();
int flags = 0;
mCurMethodState->mCurScope->mDIInlinedAt = mBfIRBuilder->DbgGetCurrentLocation();
BF_ASSERT(mCurMethodState->mCurScope->mDIInlinedAt);
// mCurMethodState->mCurScope->mDIInlinedAt may still be null ifwe don't have an explicit ctor
mCurMethodState->mCurScope->mDIScope = mBfIRBuilder->DbgCreateFunction(mBfIRBuilder->DbgGetTypeInst(mCurTypeInstance), "this$initFields", "", mCurFilePosition.mFileInstance->mDIFile,
mCurFilePosition.mCurLine + 1, diFuncType, false, true, mCurFilePosition.mCurLine + 1, flags, false, BfIRValue());
UpdateSrcPos(fieldDef->mInitializer);
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope,
"this", mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mBfIRBuilder->DbgGetType(thisType));
//
{
auto loadedThis = GetThis();
// LLVM can't handle two variables pointing to the same value
BfIRValue copiedThisPtr;
copiedThisPtr = CreateAlloca(thisType);
auto storeInst = mBfIRBuilder->CreateStore(loadedThis.mValue, copiedThisPtr);
mBfIRBuilder->ClearDebugLocation(storeInst);
mBfIRBuilder->DbgInsertDeclare(copiedThisPtr, diVariable);
}
hadInlineInitBlock = true;
}
}
UpdateSrcPos(fieldDef->mInitializer);
}
BfIRValue fieldAddr;
2019-11-21 08:23:18 -08:00
if ((!mCurTypeInstance->IsTypedPrimitive()) && (!fieldInst->mResolvedType->IsVar()))
2019-08-23 11:56:54 -07:00
{
fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx /*, fieldDef->mName*/);
}
else
{
// Failed
2019-08-23 11:56:54 -07:00
}
auto assignValue = GetFieldInitializerValue(fieldInst);
if ((fieldAddr) && (assignValue))
mBfIRBuilder->CreateStore(assignValue.mValue, fieldAddr);
}
}
if (hadInlineInitBlock)
{
RestoreScopeState();
// This gets set to false because of AddScope but we actually are still in the head block
mCurMethodState->mInHeadScope = true;
}
}
else // Autocomplete case
{
// The reason we can't just do the 'normal' path for this is that the BfTypeInstance here is NOT the
// autocomplete type instance, so FieldInstance initializer values contain expressions from the full
// resolve pass, NOT the autocomplete expression
for (auto tempTypeDef : mCompiler->mResolvePassData->mAutoCompleteTempTypes)
{
if (tempTypeDef->mFullName == typeDef->mFullName)
{
for (auto fieldDef : tempTypeDef->mFields)
{
if ((!fieldDef->mIsStatic) && (fieldDef->mInitializer != NULL) && (mCompiler->mResolvePassData->mSourceClassifier != NULL))
{
mCompiler->mResolvePassData->mSourceClassifier->SetElementType(fieldDef->mInitializer, BfSourceElementType_Normal);
mCompiler->mResolvePassData->mSourceClassifier->VisitChild(fieldDef->mInitializer);
auto wantType = ResolveTypeRef(fieldDef->mTypeRef);
if ((wantType != NULL) &&
((wantType->IsVar()) || (wantType->IsLet()) || (wantType->IsRef())))
wantType = NULL;
CreateValueFromExpression(fieldDef->mInitializer, wantType, BfEvalExprFlags_FieldInitializer);
}
}
}
}
// Mark fields from full type with initializers as initialized, to give proper initialization errors within ctor
for (auto fieldDef : typeDef->mFields)
{
if ((!fieldDef->mIsConst) && (!fieldDef->mIsStatic) && (fieldDef->mInitializer != NULL))
{
auto fieldInst = &mCurTypeInstance->mFieldInstances[fieldDef->mIdx];
if (fieldDef->mInitializer != NULL)
MarkFieldInitialized(fieldInst);
}
}
}
}
// Call base ctor (if applicable)
BfTypeInstance* targetType = NULL;
BfAstNode* targetRefNode = NULL;
if (ctorDeclaration != NULL)
targetRefNode = ctorDeclaration->mInitializer;
if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(targetRefNode))
targetRefNode = invocationExpr->mTarget;
if (baseCtorNode != NULL)
{
UpdateSrcPos(baseCtorNode);
if (methodDef->mBody == NULL)
SetIllegalSrcPos();
2019-08-23 11:56:54 -07:00
}
if ((ctorDeclaration != NULL) && (ctorDeclaration->mInitializer != NULL))
{
auto targetToken = BfNodeDynCast<BfTokenNode>(ctorDeclaration->mInitializer->mTarget);
targetType = (targetToken->GetToken() == BfToken_This) ? mCurTypeInstance : mCurTypeInstance->mBaseType;
}
else if ((mCurTypeInstance->mBaseType != NULL) && (!mCurTypeInstance->IsUnspecializedType()))
{
auto baseType = mCurTypeInstance->mBaseType;
if ((!mCurTypeInstance->IsTypedPrimitive()) &&
(baseType->mTypeDef != mCompiler->mValueTypeTypeDef) &&
(baseType->mTypeDef != mCompiler->mBfObjectTypeDef))
{
// Try to find a ctor without any params first
2020-01-26 06:42:34 -08:00
BfMethodDef* matchedMethod = NULL;
bool hadCtorWithAllDefaults = false;
bool isHiddenGenerated = (methodDeclaration == NULL) && (methodDef->mProtection == BfProtection_Hidden);
2019-08-23 11:56:54 -07:00
for (int pass = 0; pass < 2; pass++)
{
2020-01-25 10:04:52 -08:00
baseType->mTypeDef->PopulateMemberSets();
BfMemberSetEntry* entry = NULL;
BfMethodDef* checkMethodDef = NULL;
baseType->mTypeDef->mMethodSet.TryGetWith(String("__BfCtor"), &entry);
if (entry != NULL)
checkMethodDef = (BfMethodDef*)entry->mMemberDef;
while (checkMethodDef != NULL)
2019-08-23 11:56:54 -07:00
{
2020-01-26 06:42:34 -08:00
bool allowMethod = checkMethodDef->mProtection > BfProtection_Private;
if (isHiddenGenerated)
2020-01-26 06:42:34 -08:00
{
// Allow calling of the default base ctor if it is implicitly defined
if ((checkMethodDef->mMethodDeclaration == NULL) && (pass == 1) && (!hadCtorWithAllDefaults))
allowMethod = true;
}
2020-01-25 10:04:52 -08:00
if ((checkMethodDef->mMethodType == BfMethodType_Ctor) && (!checkMethodDef->mIsStatic) && (allowMethod))
2019-08-23 11:56:54 -07:00
{
if (checkMethodDef->mParams.size() == 0)
2020-01-25 10:04:52 -08:00
{
matchedMethod = checkMethodDef;
2020-01-26 06:42:34 -08:00
}
else if (isHiddenGenerated)
2020-01-26 06:42:34 -08:00
{
if ((checkMethodDef->mParams[0]->mParamDeclaration != NULL) && (checkMethodDef->mParams[0]->mParamDeclaration->mInitializer != NULL))
hadCtorWithAllDefaults = true;
}
2019-08-23 11:56:54 -07:00
}
2020-01-25 10:04:52 -08:00
checkMethodDef = checkMethodDef->mNextWithSameName;
2019-08-23 11:56:54 -07:00
}
2020-01-25 10:04:52 -08:00
if (matchedMethod != NULL)
2019-08-23 11:56:54 -07:00
break;
}
2020-01-25 10:04:52 -08:00
if ((HasCompiledOutput()) && (matchedMethod != NULL))
{
SizedArray<BfIRValue, 1> args;
auto ctorBodyMethodInstance = GetMethodInstance(mCurTypeInstance->mBaseType, matchedMethod, BfTypeVector());
if (!mCurTypeInstance->mBaseType->IsValuelessType())
{
auto basePtr = mBfIRBuilder->CreateBitCast(mCurMethodState->mLocals[0]->mValue, mBfIRBuilder->MapTypeInstPtr(mCurTypeInstance->mBaseType));
args.push_back(basePtr);
}
AddCallDependency(ctorBodyMethodInstance.mMethodInstance, true);
auto callInst = mBfIRBuilder->CreateCall(ctorBodyMethodInstance.mFunc, args);
auto callingConv = GetIRCallingConvention(ctorBodyMethodInstance.mMethodInstance);
if (callingConv != BfIRCallingConv_CDecl)
mBfIRBuilder->SetCallCallingConv(callInst, callingConv);
}
if (matchedMethod == NULL)
2019-08-23 11:56:54 -07:00
{
targetType = mCurTypeInstance->mBaseType;
if (ctorDeclaration != NULL)
targetRefNode = ctorDeclaration->mThisToken;
else if (typeDef->mTypeDeclaration != NULL)
targetRefNode = typeDef->mTypeDeclaration->mNameNode;
}
}
}
if (methodDef->mHasAppend)
{
mCurMethodState->mCurAppendAlign = methodInstance->mAppendAllocAlign;
}
if ((ctorDeclaration != NULL) && (ctorDeclaration->mInitializer != NULL) && (ctorDeclaration->mInitializer->mArguments.size() == 1) && (targetType != NULL))
{
if (auto tokenNode = BfNodeDynCast<BfUninitializedExpression>(ctorDeclaration->mInitializer->mArguments[0]))
{
if (targetType == mCurTypeInstance)
{
auto thisVariable = GetThisVariable();
if (thisVariable != NULL)
{
thisVariable->mUnassignedFieldFlags = 0;
thisVariable->mIsAssigned = true;
}
}
targetType = NULL;
}
}
2019-08-23 11:56:54 -07:00
if (targetType != NULL)
{
BfAstNode* refNode = methodDeclaration;
if (refNode == NULL)
refNode = typeDef->mTypeDeclaration;
BfAutoParentNodeEntry autoParentNodeEntry(this, refNode);
2020-01-26 06:28:04 -08:00
2019-08-23 11:56:54 -07:00
auto autoComplete = mCompiler->GetAutoComplete();
auto wasCapturingMethodInfo = (autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo);
if ((autoComplete != NULL) && (ctorDeclaration != NULL) && (ctorDeclaration->mInitializer != NULL))
{
auto invocationExpr = ctorDeclaration->mInitializer;
autoComplete->CheckInvocation(invocationExpr, invocationExpr->mOpenParen, invocationExpr->mCloseParen, invocationExpr->mCommas);
}
BfType* targetThisType = targetType;
auto target = Cast(targetRefNode, GetThis(), targetThisType);
BfExprEvaluator exprEvaluator(this);
BfResolvedArgs argValues;
if ((ctorDeclaration != NULL) && (ctorDeclaration->mInitializer != NULL))
{
argValues.Init(&ctorDeclaration->mInitializer->mArguments);
if (gDebugStuff)
{
OutputDebugStrF("Expr: %@ %d\n", argValues.mArguments->mVals, argValues.mArguments->mSize);
}
}
exprEvaluator.ResolveArgValues(argValues, BfResolveArgFlag_DeferParamEval);
BfTypedValue appendIdxVal;
if (methodDef->mHasAppend)
{
auto localVar = mCurMethodState->GetRootMethodState()->mLocals[1];
BF_ASSERT(localVar->mName == "appendIdx");
auto intRefType = localVar->mResolvedType;
2019-08-23 11:56:54 -07:00
appendIdxVal = BfTypedValue(localVar->mValue, intRefType);
mCurMethodState->mCurAppendAlign = 1; // Don't make any assumptions about how the base leaves the alignment
}
exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, methodDef->mHasAppend, &appendIdxVal);
if (autoComplete != NULL)
{
if ((wasCapturingMethodInfo) && (!autoComplete->mIsCapturingMethodMatchInfo))
{
if (autoComplete->mMethodMatchInfo != NULL)
autoComplete->mIsCapturingMethodMatchInfo = true;
else
autoComplete->mIsCapturingMethodMatchInfo = false;
}
else
autoComplete->mIsCapturingMethodMatchInfo = false;
}
}
}
void BfModule::EmitEnumToStringBody()
{
auto stringType = ResolveTypeDef(mCompiler->mStringTypeDef);
auto strVal = CreateAlloca(stringType);
auto int64StructType = GetPrimitiveStructType(BfTypeCode_Int64);
BfExprEvaluator exprEvaluator(this);
auto stringDestAddr = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
BfIRBlock appendBlock = mBfIRBuilder->CreateBlock("append");
BfIRBlock noMatchBlock = mBfIRBuilder->CreateBlock("noMatch");
BfIRBlock endBlock = mBfIRBuilder->CreateBlock("end");
BfType* discriminatorType = NULL;
BfTypedValue rawPayload;
BfIRValue enumVal;
if (mCurTypeInstance->IsPayloadEnum())
{
discriminatorType = mCurTypeInstance->GetDiscriminatorType();
auto enumTypedValue = ExtractValue(GetThis(), NULL, 2);
enumTypedValue = LoadValue(enumTypedValue);
enumVal = enumTypedValue.mValue;
rawPayload = ExtractValue(GetThis(), NULL, 1);
}
else
enumVal = mBfIRBuilder->GetArgument(0);
Array<BfType*> paramTypes;
paramTypes.Add(stringType);
auto appendModuleMethodInstance = GetMethodByName(stringType->ToTypeInstance(), "Append", paramTypes);
auto switchVal = mBfIRBuilder->CreateSwitch(enumVal, noMatchBlock, (int)mCurTypeInstance->mFieldInstances.size());
HashSet<int64> handledCases;
for (auto& fieldInstance : mCurTypeInstance->mFieldInstances)
{
if (fieldInstance.mIsEnumPayloadCase)
{
int tagId = -fieldInstance.mDataIdx - 1;
BfIRBlock caseBlock = mBfIRBuilder->CreateBlock("case");
mBfIRBuilder->AddBlock(caseBlock);
mBfIRBuilder->SetInsertPoint(caseBlock);
BF_ASSERT(discriminatorType->IsPrimitiveType());
auto constVal = mBfIRBuilder->CreateConst(((BfPrimitiveType*)discriminatorType)->mTypeDef->mTypeCode, tagId);
mBfIRBuilder->AddSwitchCase(switchVal, constVal, caseBlock);
auto caseStr = GetStringObjectValue(fieldInstance.GetFieldDef()->mName);
auto stringDestVal = LoadValue(stringDestAddr);
SizedArray<BfIRValue, 2> args;
args.Add(stringDestVal.mValue);
args.Add(caseStr);
exprEvaluator.CreateCall(appendModuleMethodInstance.mMethodInstance, appendModuleMethodInstance.mFunc, false, args);
auto payloadType = fieldInstance.mResolvedType->ToTypeInstance();
BF_ASSERT(payloadType->IsTuple());
if (payloadType->mFieldInstances.size() != 0)
{
auto payload = rawPayload;
if (payload.mType != payloadType)
{
payload = Cast(NULL, payload, payloadType, BfCastFlags_Force);
}
auto toStringMethod = GetMethodByName(payloadType->ToTypeInstance(), "ToString");
SizedArray<BfIRValue, 2> irArgs;
exprEvaluator.PushThis(NULL, payload, toStringMethod.mMethodInstance, irArgs);
stringDestVal = LoadValue(stringDestAddr);
irArgs.Add(stringDestVal.mValue);
exprEvaluator.CreateCall(toStringMethod.mMethodInstance, toStringMethod.mFunc, true, irArgs);
}
mBfIRBuilder->CreateBr(endBlock);
continue;
}
if (fieldInstance.mConstIdx == -1)
continue;
auto constant = mCurTypeInstance->mConstHolder->GetConstantById(fieldInstance.mConstIdx);
if (!handledCases.TryAdd(constant->mInt64, NULL))
{
// Duplicate
continue;
}
BfIRBlock caseBlock = mBfIRBuilder->CreateBlock("case");
mBfIRBuilder->AddBlock(caseBlock);
mBfIRBuilder->SetInsertPoint(caseBlock);
BfIRValue constVal = ConstantToCurrent(constant, mCurTypeInstance->mConstHolder, mCurTypeInstance);
mBfIRBuilder->AddSwitchCase(switchVal, constVal, caseBlock);
auto caseStr = GetStringObjectValue(fieldInstance.GetFieldDef()->mName);
mBfIRBuilder->CreateStore(caseStr, strVal);
mBfIRBuilder->CreateBr(appendBlock);
}
mBfIRBuilder->AddBlock(appendBlock);
mBfIRBuilder->SetInsertPoint(appendBlock);
SizedArray<BfIRValue, 2> args;
auto stringDestVal = LoadValue(stringDestAddr);
args.Add(stringDestVal.mValue);
args.Add(mBfIRBuilder->CreateLoad(strVal));
exprEvaluator.CreateCall(appendModuleMethodInstance.mMethodInstance, appendModuleMethodInstance.mFunc, false, args);
mBfIRBuilder->CreateBr(endBlock);
mBfIRBuilder->AddBlock(noMatchBlock);
mBfIRBuilder->SetInsertPoint(noMatchBlock);
auto int64Val = mBfIRBuilder->CreateNumericCast(enumVal, false, BfTypeCode_Int64);
auto toStringModuleMethodInstance = GetMethodByName(int64StructType, "ToString", 1);
args.clear();
args.Add(int64Val);
stringDestVal = LoadValue(stringDestAddr);
args.Add(stringDestVal.mValue);
exprEvaluator.CreateCall(toStringModuleMethodInstance.mMethodInstance, toStringModuleMethodInstance.mFunc, false, args);
mBfIRBuilder->CreateBr(endBlock);
mBfIRBuilder->AddBlock(endBlock);
mBfIRBuilder->SetInsertPoint(endBlock);
}
void BfModule::EmitTupleToStringBody()
{
auto stringType = ResolveTypeDef(mCompiler->mStringTypeDef);
BfExprEvaluator exprEvaluator(this);
auto stringDestRef = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
Array<BfType*> paramTypes;
paramTypes.Add(GetPrimitiveType(BfTypeCode_Char8));
auto appendCharModuleMethodInstance = GetMethodByName(stringType->ToTypeInstance(), "Append", paramTypes);
paramTypes.Clear();
paramTypes.Add(stringType);
auto appendModuleMethodInstance = GetMethodByName(stringType->ToTypeInstance(), "Append", paramTypes);
auto _AppendChar = [&](char c)
{
SizedArray<BfIRValue, 2> args;
auto stringDestVal = LoadValue(stringDestRef);
args.Add(stringDestVal.mValue);
args.Add(mBfIRBuilder->CreateConst(BfTypeCode_Char8, (int)c));
exprEvaluator.CreateCall(appendCharModuleMethodInstance.mMethodInstance, appendCharModuleMethodInstance.mFunc, false, args);
};
int fieldIdx = 0;
auto thisValue = GetThis();
auto toStringModuleMethodInstance = GetMethodByName(mContext->mBfObjectType, "ToString", 1);
auto toStringSafeModuleMethodInstance = GetMethodByName(mContext->mBfObjectType, "ToString", 2);
BfIRValue commaStr;
auto iPrintableType = ResolveTypeDef(mCompiler->mIPrintableTypeDef)->ToTypeInstance();
auto printModuleMethodInstance = GetMethodByName(iPrintableType, "Print");
_AppendChar('(');
for (auto& fieldInstance : mCurTypeInstance->mFieldInstances)
{
if (fieldInstance.mDataIdx == -1)
continue;
if (fieldIdx > 0)
{
if (!commaStr)
commaStr = GetStringObjectValue(", ");
SizedArray<BfIRValue, 2> args;
auto stringDestVal = LoadValue(stringDestRef);
args.Add(stringDestVal.mValue);
args.Add(commaStr);
exprEvaluator.CreateCall(appendModuleMethodInstance.mMethodInstance, appendModuleMethodInstance.mFunc, false, args);
}
fieldIdx++;
if (fieldInstance.mResolvedType->IsValuelessType())
continue;
BfTypedValue fieldValue = ExtractValue(thisValue, &fieldInstance, fieldInstance.mDataIdx);
if (fieldValue.mType->IsPrimitiveType())
{
fieldValue.mType = GetPrimitiveStructType(((BfPrimitiveType*)fieldValue.mType)->mTypeDef->mTypeCode);
}
auto typeInstance = fieldValue.mType->ToTypeInstance();
if ((typeInstance != NULL) && (TypeIsSubTypeOf(typeInstance, iPrintableType, false)))
{
BfExprEvaluator exprEvaluator(this);
SizedArray<BfResolvedArg, 0> resolvedArgs;
BfMethodMatcher methodMatcher(NULL, this, printModuleMethodInstance.mMethodInstance, resolvedArgs);
methodMatcher.mBestMethodDef = printModuleMethodInstance.mMethodInstance->mMethodDef;
methodMatcher.mBestMethodTypeInstance = iPrintableType;
methodMatcher.TryDevirtualizeCall(fieldValue);
BfTypedValue callVal = Cast(NULL, fieldValue, methodMatcher.mBestMethodTypeInstance);
BfResolvedArg resolvedArg;
auto stringDestVal = LoadValue(stringDestRef);
resolvedArg.mTypedValue = stringDestVal;
resolvedArg.mResolvedType = stringDestVal.mType;
resolvedArgs.Add(resolvedArg);
exprEvaluator.CreateCall(&methodMatcher, callVal);
continue;
}
if (fieldValue.mType->IsObjectOrInterface())
{
BF_ASSERT(!fieldValue.IsAddr());
SizedArray<BfIRValue, 2> args;
args.Add(mBfIRBuilder->CreateBitCast(fieldValue.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType)));
auto stringDestVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[1]);
stringDestVal = LoadValue(stringDestVal);
args.Add(stringDestVal.mValue);
exprEvaluator.CreateCall(toStringSafeModuleMethodInstance.mMethodInstance, toStringSafeModuleMethodInstance.mFunc, false, args);
continue;
}
BfExprEvaluator exprEvaluator(this);
SizedArray<BfResolvedArg, 0> resolvedArgs;
BfMethodMatcher methodMatcher(NULL, this, toStringModuleMethodInstance.mMethodInstance, resolvedArgs);
methodMatcher.mBestMethodDef = toStringModuleMethodInstance.mMethodInstance->mMethodDef;
methodMatcher.mBestMethodTypeInstance = mContext->mBfObjectType;
methodMatcher.TryDevirtualizeCall(fieldValue);
if (methodMatcher.mBestMethodTypeInstance == mContext->mBfObjectType)
{
AddCallDependency(appendModuleMethodInstance.mMethodInstance);
AddDependency(fieldValue.mType, mCurTypeInstance, BfDependencyMap::DependencyFlag_Calls);
// Just append the type name
String typeName = TypeToString(fieldInstance.mResolvedType);
auto strVal = GetStringObjectValue(typeName);
SizedArray<BfIRValue, 2> args;
auto stringDestVal = LoadValue(stringDestRef);
args.Add(stringDestVal.mValue);
args.Add(strVal);
exprEvaluator.CreateCall(appendModuleMethodInstance.mMethodInstance, appendModuleMethodInstance.mFunc, false, args);
continue;
}
BfTypedValue callVal = Cast(NULL, fieldValue, methodMatcher.mBestMethodTypeInstance);
BfResolvedArg resolvedArg;
auto stringDestVal = LoadValue(stringDestRef);
resolvedArg.mTypedValue = stringDestVal;
resolvedArg.mResolvedType = stringDestVal.mType;
resolvedArgs.Add(resolvedArg);
exprEvaluator.CreateCall(&methodMatcher, callVal);
}
_AppendChar(')');
}
void BfModule::EmitIteratorBlock(bool& skipBody)
{
auto methodInstance = mCurMethodInstance;
auto methodDef = methodInstance->mMethodDef;
auto methodDeclaration = methodDef->GetMethodDeclaration();
auto typeDef = mCurTypeInstance->mTypeDef;
BfType* innerRetType = NULL;
BfTypeInstance* usingInterface = NULL;
auto retTypeInst = mCurMethodInstance->mReturnType->ToGenericTypeInstance();
if (retTypeInst != NULL)
{
if ((retTypeInst->mTypeDef == mCompiler->mGenericIEnumerableTypeDef) ||
(retTypeInst->mTypeDef == mCompiler->mGenericIEnumeratorTypeDef))
{
innerRetType = retTypeInst->mGenericTypeInfo->mTypeGenericArguments[0];
2019-08-23 11:56:54 -07:00
}
}
if (innerRetType == NULL)
{
Fail("Methods with yields must return either System.IEnumerable<T> or System.IEnumerator<T>", methodDeclaration->mReturnType);
return;
}
auto blockBody = BfNodeDynCast<BfBlock>(methodDeclaration->mBody);
if (blockBody == NULL)
return;
}
void BfModule::EmitGCMarkValue(BfTypedValue markVal, BfModuleMethodInstance markFromGCThreadMethodInstance)
{
auto fieldTypeInst = markVal.mType->ToTypeInstance();
if (fieldTypeInst == NULL)
return;
if (!markVal.mType->IsComposite())
markVal = LoadValue(markVal);
BfExprEvaluator exprEvaluator(this);
PopulateType(markVal.mType, BfPopulateType_Data);
if (markVal.mType->IsObjectOrInterface())
{
BfIRValue val = markVal.mValue;
val = mBfIRBuilder->CreateBitCast(val, mBfIRBuilder->MapType(mContext->mBfObjectType));
SizedArray<BfIRValue, 1> args;
args.push_back(val);
exprEvaluator.CreateCall(markFromGCThreadMethodInstance.mMethodInstance, markFromGCThreadMethodInstance.mFunc, false, args);
}
else if ((fieldTypeInst->IsComposite()) && (!fieldTypeInst->IsTypedPrimitive()))
{
auto markMemberMethodInstance = GetMethodByName(fieldTypeInst, BF_METHODNAME_MARKMEMBERS, 0, true);
if (markMemberMethodInstance)
{
SizedArray<BfIRValue, 1> args;
auto methodOwner = markMemberMethodInstance.mMethodInstance->GetOwner();
if (markVal.mType != methodOwner)
markVal = Cast(NULL, markVal, methodOwner);
2019-08-23 11:56:54 -07:00
exprEvaluator.PushThis(NULL, markVal, markMemberMethodInstance.mMethodInstance, args);
exprEvaluator.CreateCall(markMemberMethodInstance.mMethodInstance, markMemberMethodInstance.mFunc, false, args);
}
}
}
void BfModule::CallChainedMethods(BfMethodInstance* methodInstance, bool reverse)
{
Array<BfMethodInstance*> methodInstances;
for (int methodIdx = 0; methodIdx < (int)mCurTypeInstance->mMethodInstanceGroups.size(); methodIdx++)
{
auto& methodInstGroup = mCurTypeInstance->mMethodInstanceGroups[methodIdx];
auto chainedMethodInst = methodInstGroup.mDefault;
if ((chainedMethodInst != NULL) && (chainedMethodInst->mChainType == BfMethodChainType_ChainMember))
{
if ((chainedMethodInst->mMethodDef->mIsStatic == methodInstance->mMethodDef->mIsStatic) &&
(CompareMethodSignatures(methodInstance, chainedMethodInst)))
{
methodInstances.push_back(chainedMethodInst);
}
}
}
std::stable_sort(methodInstances.begin(), methodInstances.end(),
[&](BfMethodInstance* lhs, BfMethodInstance* rhs)
{
bool isBetter;
bool isWorse;
CompareDeclTypes(lhs->mMethodDef->mDeclaringType, rhs->mMethodDef->mDeclaringType, isBetter, isWorse);
if (isBetter == isWorse)
{
return false;
}
return isWorse;
});
if (reverse)
std::reverse(methodInstances.begin(), methodInstances.end());
for (auto chainedMethodInst : methodInstances)
{
auto declModule = this;
BfGetMethodInstanceFlags flags = BfGetMethodInstanceFlag_None;
auto moduleMethodInst = declModule->GetMethodInstance(mCurTypeInstance, chainedMethodInst->mMethodDef, BfTypeVector(), flags);
BfExprEvaluator exprEvaluator(this);
SizedArray<BfIRValue, 1> args;
exprEvaluator.PushThis(chainedMethodInst->mMethodDef->GetRefNode(), GetThis(), chainedMethodInst, args);
exprEvaluator.CreateCall(moduleMethodInst.mMethodInstance, moduleMethodInst.mFunc, true, args);
}
}
void BfModule::AddHotDataReferences(BfHotDataReferenceBuilder* builder)
{
2019-08-23 11:56:54 -07:00
BF_ASSERT(mCurMethodInstance->mIsReified);
if (mCurTypeInstance->mHotTypeData == NULL)
{
2019-08-23 11:56:54 -07:00
mCurTypeInstance->mHotTypeData = new BfHotTypeData();
BfLogSysM("Created HotTypeData %p created for type %p in AddHotDataReferences\n", mCurTypeInstance->mHotTypeData, mCurTypeInstance);
}
2019-08-23 11:56:54 -07:00
auto hotMethod = mCurMethodInstance->mHotMethod;
for (auto depData : hotMethod->mReferences)
{
// Only virtual decls are allowed to already be there
BF_ASSERT((depData->mDataKind == BfHotDepDataKind_VirtualDecl) || (depData->mDataKind == BfHotDepDataKind_DupMethod));
}
BF_ASSERT(hotMethod->mSrcTypeVersion != NULL);
int prevSize = (int)hotMethod->mReferences.size();
int refCount = (int)(prevSize + builder->mUsedData.size() + builder->mCalledMethods.size() + builder->mDevirtualizedCalledMethods.size());
if (!mCurMethodInstance->mMethodDef->mIsStatic)
refCount++;
hotMethod->mReferences.Reserve(refCount);
if (!mCurMethodInstance->mMethodDef->mIsStatic)
{
auto hotThis = mCompiler->mHotData->GetThisType(mCurMethodInstance->GetOwner()->mHotTypeData->GetLatestVersion());
hotThis->mRefCount++;
hotMethod->mReferences.Insert(0, hotThis);
prevSize++;
}
for (auto val : builder->mAllocatedData)
{
auto hotAllocation = mCompiler->mHotData->GetAllocation(val);
hotMethod->mReferences.Add(hotAllocation);
}
for (auto val : builder->mUsedData)
hotMethod->mReferences.Add(val);
for (auto val : builder->mCalledMethods)
hotMethod->mReferences.Add(val);
for (auto val : builder->mDevirtualizedCalledMethods)
{
auto devirtualizedMethod = mCompiler->mHotData->GetDevirtualizedMethod(val);
hotMethod->mReferences.Add(devirtualizedMethod);
}
for (auto val : builder->mFunctionPtrs)
{
auto funcRef = mCompiler->mHotData->GetFunctionReference(val);
hotMethod->mReferences.Add(funcRef);
}
for (auto val : builder->mInnerMethods)
{
auto innerMethod = mCompiler->mHotData->GetInnerMethod(val);
hotMethod->mReferences.Add(innerMethod);
}
for (int refIdx = prevSize; refIdx < (int)hotMethod->mReferences.size(); refIdx++)
{
auto depData = hotMethod->mReferences[refIdx];
BF_ASSERT(depData != NULL);
depData->mRefCount++;
}
2019-08-23 11:56:54 -07:00
}
void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfType* thisType, bool wantsDIData, SizedArrayImpl<BfIRMDNode>* diParams)
{
auto methodDef = methodInstance->mMethodDef;
auto methodDeclaration = methodDef->mMethodDeclaration;
bool isThisStruct = false;
if (thisType != NULL)
isThisStruct = thisType->IsStruct() && !thisType->IsTypedPrimitive();
int argIdx = 0;
if (argIdx == methodInstance->GetStructRetIdx())
2019-08-23 11:56:54 -07:00
argIdx++;
if (!methodDef->mIsStatic)
{
BfTypeCode loweredTypeCode = BfTypeCode_None;
BfTypeCode loweredTypeCode2 = BfTypeCode_None;
2019-08-23 11:56:54 -07:00
BfLocalVariable* paramVar = new BfLocalVariable();
paramVar->mResolvedType = thisType;
paramVar->mName = "this";
if (!thisType->IsValuelessType())
paramVar->mValue = mBfIRBuilder->GetArgument(argIdx);
else
paramVar->mValue = mBfIRBuilder->GetFakeVal();
if ((thisType->IsSplattable()) && (methodInstance->AllowsThisSplatting()))
2019-08-23 11:56:54 -07:00
{
if (!thisType->IsTypedPrimitive())
2019-08-23 11:56:54 -07:00
paramVar->mIsSplat = true;
}
else if ((!methodDef->mIsMutating) && (methodInstance->mCallingConvention == BfCallingConvention_Unspecified))
paramVar->mIsLowered = thisType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2) != BfTypeCode_None;
2019-08-23 11:56:54 -07:00
auto thisTypeInst = thisType->ToTypeInstance();
paramVar->mIsStruct = isThisStruct;
paramVar->mAddr = BfIRValue();
paramVar->mIsThis = true;
paramVar->mParamIdx = -1;
if ((methodDef->mMethodType == BfMethodType_Ctor) && (mCurTypeInstance->IsStruct()))
{
paramVar->mIsAssigned = false;
}
else
{
paramVar->mIsAssigned = true;
}
paramVar->mReadFromId = -1;
paramVar->Init();
if (methodDeclaration == NULL)
UseDefaultSrcPos();
AddLocalVariableDef(paramVar);
if (!thisType->IsValuelessType())
{
if (wantsDIData)
{
BfType* thisPtrType = thisType;
auto diType = mBfIRBuilder->DbgGetType(thisPtrType);
if (!thisType->IsValueType())
diType = mBfIRBuilder->DbgCreateArtificialType(diType);
diParams->push_back(diType);
}
if (paramVar->mIsSplat)
{
BfTypeUtils::SplatIterate([&](BfType* checkType) { argIdx++; }, paramVar->mResolvedType);
}
else
{
argIdx++;
}
}
if (loweredTypeCode2 != BfTypeCode_None)
argIdx++;
2019-08-23 11:56:54 -07:00
}
if (argIdx == methodInstance->GetStructRetIdx())
argIdx++;
2019-08-23 11:56:54 -07:00
bool hadParams = false;
bool hasDefault = false;
int compositeVariableIdx = -1;
int paramIdx = 0;
for (paramIdx = 0; paramIdx < methodInstance->GetParamCount(); paramIdx++)
{
2019-08-23 11:56:54 -07:00
// We already issues a type error for this param if we had one in declaration processing
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
BfLocalVariable* paramVar = new BfLocalVariable();
BfTypeCode loweredTypeCode = BfTypeCode_None;
BfTypeCode loweredTypeCode2 = BfTypeCode_None;
2019-08-23 11:56:54 -07:00
bool isParamSkipped = methodInstance->IsParamSkipped(paramIdx);
BfType* unresolvedType = methodInstance->GetParamType(paramIdx, false);
if (unresolvedType == NULL)
{
AssertErrorState();
unresolvedType = mContext->mBfObjectType;
paramVar->mParamFailed = true;
}
auto resolvedType = methodInstance->GetParamType(paramIdx, true);
prevIgnoreErrors.Restore();
PopulateType(resolvedType, BfPopulateType_Declaration);
paramVar->mResolvedType = resolvedType;
paramVar->mName = methodInstance->GetParamName(paramIdx);
paramVar->mNameNode = methodInstance->GetParamNameNode(paramIdx);
if (!isParamSkipped)
{
paramVar->mValue = mBfIRBuilder->GetArgument(argIdx);
if (resolvedType->IsMethodRef())
{
paramVar->mIsSplat = true;
}
else if (resolvedType->IsComposite() && resolvedType->IsSplattable())
{
auto resolvedTypeInst = resolvedType->ToTypeInstance();
if ((resolvedTypeInst != NULL) && (resolvedTypeInst->mIsCRepr))
{
// crepr splat is always splat
2019-08-23 11:56:54 -07:00
paramVar->mIsSplat = true;
}
else if (methodInstance->AllowsSplatting())
{
int splatCount = resolvedType->GetSplatCount();
if (argIdx + splatCount <= mCompiler->mOptions.mMaxSplatRegs)
paramVar->mIsSplat = true;
}
2019-08-23 11:56:54 -07:00
}
}
else
{
paramVar->mIsSplat = true; // Treat skipped (valueless) as a splat
paramVar->mValue = mBfIRBuilder->GetFakeVal();
}
paramVar->mIsLowered = resolvedType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2) != BfTypeCode_None;
2019-08-23 11:56:54 -07:00
paramVar->mIsStruct = resolvedType->IsComposite() && !resolvedType->IsTypedPrimitive();
paramVar->mParamIdx = paramIdx;
paramVar->mIsImplicitParam = methodInstance->IsImplicitCapture(paramIdx);
paramVar->mReadFromId = 0;
if (resolvedType->IsRef())
{
auto refType = (BfRefType*)resolvedType;
paramVar->mIsAssigned = refType->mRefKind != BfRefType::RefKind_Out;
}
else
{
paramVar->mIsAssigned = true;
if (methodDef->mMethodType != BfMethodType_Mixin)
paramVar->mIsReadOnly = true;
}
if (paramVar->mResolvedType->IsGenericParam())
{
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)paramVar->mResolvedType);
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Const) != 0)
{
auto typedVal = GetDefaultTypedValue(genericParamInst->mTypeConstraint, false, BfDefaultValueKind_Undef);
paramVar->mResolvedType = typedVal.mType;
paramVar->mConstValue = typedVal.mValue;
}
}
if (paramVar->mResolvedType->IsConstExprValue())
{
BfConstExprValueType* constExprValueType = (BfConstExprValueType*)paramVar->mResolvedType;
auto unspecializedMethodInstance = GetUnspecializedMethodInstance(methodInstance);
auto unspecParamType = unspecializedMethodInstance->GetParamType(paramIdx);
BF_ASSERT(unspecParamType->IsGenericParam());
if (unspecParamType->IsGenericParam())
{
BfGenericParamType* genericParamType = (BfGenericParamType*)unspecParamType;
if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
{
auto genericParamInst = unspecializedMethodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx];
if (genericParamInst->mTypeConstraint != NULL)
{
BfExprEvaluator exprEvaluator(this);
exprEvaluator.mExpectingType = genericParamInst->mTypeConstraint;
exprEvaluator.GetLiteral(NULL, constExprValueType->mValue);
auto checkType = genericParamInst->mTypeConstraint;
if (checkType->IsTypedPrimitive())
checkType = checkType->GetUnderlyingType();
if (exprEvaluator.mResult.mType == checkType)
{
paramVar->mResolvedType = genericParamInst->mTypeConstraint;
paramVar->mConstValue = exprEvaluator.mResult.mValue;
BF_ASSERT(paramVar->mConstValue.IsConst());
}
else
{
AssertErrorState();
paramVar->mResolvedType = genericParamInst->mTypeConstraint;
paramVar->mConstValue = GetDefaultValue(genericParamInst->mTypeConstraint);
}
}
}
}
//BF_ASSERT(!paramVar->mResolvedType->IsConstExprValue());
if (paramVar->mResolvedType->IsConstExprValue())
{
Fail("Invalid use of constant expression", methodInstance->GetParamTypeRef(paramIdx));
//AssertErrorState();
paramVar->mResolvedType = GetPrimitiveType(BfTypeCode_IntPtr);
paramVar->mConstValue = GetDefaultValue(paramVar->mResolvedType);
}
}
if (methodInstance->GetParamKind(paramIdx) == BfParamKind_DelegateParam)
{
if (compositeVariableIdx == -1)
{
compositeVariableIdx = (int)mCurMethodState->mLocals.size();
BfLocalVariable* localVar = new BfLocalVariable();
auto paramInst = &methodInstance->mParams[paramIdx];
auto paramDef = methodDef->mParams[paramInst->mParamDefIdx];
localVar->mName = paramDef->mName;
localVar->mResolvedType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam);
localVar->mCompositeCount = 0;
DoAddLocalVariable(localVar);
}
mCurMethodState->mLocals[compositeVariableIdx]->mCompositeCount++;
}
CheckVariableDef(paramVar);
paramVar->Init();
AddLocalVariableDef(paramVar);
if (!isParamSkipped)
{
if (wantsDIData)
diParams->push_back(mBfIRBuilder->DbgGetType(resolvedType));
if (paramVar->mIsSplat)
{
BfTypeUtils::SplatIterate([&](BfType* checkType) { argIdx++; }, paramVar->mResolvedType);
}
else
{
argIdx++;
}
if (loweredTypeCode2 != BfTypeCode_None)
argIdx++;
2019-08-23 11:56:54 -07:00
}
}
// Try to find an ending delegate params composite
if ((compositeVariableIdx == -1) && (methodDef->mParams.size() > 0))
{
auto paramDef = methodDef->mParams.back();
if (paramDef->mParamKind == BfParamKind_Params)
{
auto paramsType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam);
if (paramsType == NULL)
{
AssertErrorState();
}
else if (paramsType->IsGenericParam())
{
auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)paramsType);
if (genericParamInstance->mTypeConstraint != NULL)
{
auto typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance();
if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) ||
((typeInstConstraint != NULL) &&
((typeInstConstraint->mTypeDef == mCompiler->mDelegateTypeDef) || (typeInstConstraint->mTypeDef == mCompiler->mFunctionTypeDef))))
{
BfLocalVariable* localVar = new BfLocalVariable();
localVar->mName = paramDef->mName;
localVar->mResolvedType = paramsType;
localVar->mCompositeCount = 0;
DoAddLocalVariable(localVar);
}
}
}
else if (paramsType->IsDelegate())
{
BfMethodInstance* invokeMethodInstance = GetRawMethodInstanceAtIdx(paramsType->ToTypeInstance(), 0, "Invoke");
if (invokeMethodInstance != NULL)
{
// This is only for the case where the delegate didn't actually have any params so the composite is zero-sized
// and wasn't cleared by the preceding loop
BF_ASSERT(invokeMethodInstance->GetParamCount() == 0);
BfLocalVariable* localVar = new BfLocalVariable();
localVar->mName = paramDef->mName;
localVar->mResolvedType = paramsType;
localVar->mCompositeCount = 0;
DoAddLocalVariable(localVar);
}
}
}
}
}
void BfModule::ProcessMethod_ProcessDeferredLocals(int startIdx)
{
// if (mCurMethodState->mPrevMethodState != NULL) // Re-entry
// return;
auto _ClearState = [&]()
{
mCurMethodState->mLocalMethods.Clear();
for (auto& local : mCurMethodState->mLocals)
delete local;
mCurMethodState->mLocals.Clear();
mCurMethodState->mLocalVarSet.Clear();
};
// Don't process local methods if we had a build error - this isn't just an optimization, it keeps us from showing the same error twice since
// we show errors in the capture phase. If we somehow pass the capture phase without error then this method WILL show any errors here,
// however (compiler bug), so this is the safest way
if (!mCurMethodState->mDeferredLocalMethods.IsEmpty())
{
for (int deferredLocalMethodIdx = 0; deferredLocalMethodIdx < (int)mCurMethodState->mDeferredLocalMethods.size(); deferredLocalMethodIdx++)
{
auto deferredLocalMethod = mCurMethodState->mDeferredLocalMethods[deferredLocalMethodIdx];
BfLogSysM("Processing deferred local method %p\n", deferredLocalMethod);
if (!mHadBuildError)
{
// Process as a closure - that allows us to look back and see the const locals and stuff
BfClosureState closureState;
mCurMethodState->mClosureState = &closureState;
closureState.mConstLocals = deferredLocalMethod->mConstLocals;
closureState.mReturnType = deferredLocalMethod->mMethodInstance->mReturnType;
closureState.mActiveDeferredLocalMethod = deferredLocalMethod;
closureState.mLocalMethod = deferredLocalMethod->mLocalMethod;
if (deferredLocalMethod->mMethodInstance->mMethodInfoEx != NULL)
closureState.mClosureInstanceInfo = deferredLocalMethod->mMethodInstance->mMethodInfoEx->mClosureInstanceInfo;
mCurMethodState->mMixinState = deferredLocalMethod->mLocalMethod->mDeclMixinState;
_ClearState();
for (auto& constLocal : deferredLocalMethod->mConstLocals)
{
auto localVar = new BfLocalVariable();
*localVar = constLocal;
DoAddLocalVariable(localVar);
}
mCurMethodState->mLocalMethods = deferredLocalMethod->mLocalMethods;
bool doProcess = !mCompiler->mCanceling;
if (doProcess)
{
BP_ZONE_F("ProcessMethod local %s", deferredLocalMethod->mMethodInstance->mMethodDef->mName.c_str());
ProcessMethod(deferredLocalMethod->mMethodInstance);
}
}
delete deferredLocalMethod;
mSystem->CheckLockYield();
}
}
for (auto& kv : mCurMethodState->mLocalMethodCache)
{
BF_ASSERT((kv.mValue->mMethodDef == NULL) || (kv.mKey == kv.mValue->mMethodDef->mName));
}
if ((!mCurMethodState->mDeferredLambdaInstances.IsEmpty()) && (!mHadBuildError))
{
for (int deferredLambdaIdx = 0; deferredLambdaIdx < (int)mCurMethodState->mDeferredLambdaInstances.size(); deferredLambdaIdx++)
{
auto lambdaInstance = mCurMethodState->mDeferredLambdaInstances[deferredLambdaIdx];
BfLogSysM("Processing deferred lambdaInstance %p\n", lambdaInstance);
BfClosureState closureState;
mCurMethodState->mClosureState = &closureState;
//closureState.mConstLocals = deferredLocalMethod->mConstLocals;
closureState.mReturnType = lambdaInstance->mMethodInstance->mReturnType;
if (lambdaInstance->mClosureTypeInstance != NULL)
closureState.mClosureType = lambdaInstance->mClosureTypeInstance;
else
closureState.mClosureType = lambdaInstance->mDelegateTypeInstance;
2019-08-23 11:56:54 -07:00
closureState.mClosureInstanceInfo = lambdaInstance->mMethodInstance->mMethodInfoEx->mClosureInstanceInfo;
closureState.mDeclaringMethodIsMutating = lambdaInstance->mDeclaringMethodIsMutating;
2019-08-23 11:56:54 -07:00
mCurMethodState->mMixinState = lambdaInstance->mDeclMixinState;
_ClearState();
for (auto& constLocal : lambdaInstance->mConstLocals)
{
//if (constLocal.mIsThis)
{
// Valueless 'this'
closureState.mConstLocals.Add(constLocal);
}
/*else
{
auto localVar = new BfLocalVariable();
*localVar = constLocal;
DoAddLocalVariable(localVar);
}*/
}
BfMethodInstanceGroup methodInstanceGroup;
methodInstanceGroup.mOwner = mCurTypeInstance;
methodInstanceGroup.mOnDemandKind = BfMethodOnDemandKind_AlwaysInclude;
bool doProcess = !mCompiler->mCanceling;
if (doProcess)
{
BP_ZONE_F("ProcessMethod lambdaInstance %s", lambdaInstance->mMethodInstance->mMethodDef->mName.c_str());
lambdaInstance->mMethodInstance->mMethodInstanceGroup = &methodInstanceGroup;
ProcessMethod(lambdaInstance->mMethodInstance);
if (lambdaInstance->mDtorMethodInstance != NULL)
{
lambdaInstance->mDtorMethodInstance->mMethodInstanceGroup = &methodInstanceGroup;
ProcessMethod(lambdaInstance->mDtorMethodInstance);
}
}
}
mSystem->CheckLockYield();
}
}
void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet<int>& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance)
{
if (checkType->IsComposite())
PopulateType(checkType, BfPopulateType_Data);
if (!checkType->WantsGCMarking())
return;
auto typeInstance = checkType->ToTypeInstance();
bool callMarkMethod = false;
// Call the actual marking method
if (memberDepth == 0)
{
// Don't call marking method on self
}
else if (checkType->IsObjectOrInterface())
{
if (!objectOffsets.Add(curOffset))
{
return;
}
callMarkMethod = true;
}
else if (checkType->IsSizedArray())
{
callMarkMethod = true;
}
else if (typeInstance != NULL)
{
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())
{
callMarkMethod = true;
}
}
}
if (callMarkMethod)
{
BfTypedValue markValue;
auto memberType = checkType;
if (memberType->IsObjectOrInterface())
memberType = mContext->mBfObjectType; // Just to avoid that extra cast for the call itself
auto memberPtrType = CreatePointerType(memberType);
if (curOffset == 0)
{
markValue = BfTypedValue(mBfIRBuilder->CreateBitCast(thisValue.mValue, mBfIRBuilder->MapType(memberPtrType)), memberType, true);
}
else
{
if (!thisValue.mType->IsPointer())
{
auto int8Type = GetPrimitiveType(BfTypeCode_UInt8);
auto int8PtrType = CreatePointerType(int8Type);
thisValue = BfTypedValue(mBfIRBuilder->CreateBitCast(thisValue.mValue, mBfIRBuilder->MapType(int8PtrType)), int8PtrType);
}
auto offsetValue = mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, curOffset);
markValue = BfTypedValue(mBfIRBuilder->CreateBitCast(offsetValue, mBfIRBuilder->MapType(memberPtrType)), memberType, true);
}
EmitGCMarkValue(markValue, markFromGCThreadMethodInstance);
return;
}
auto methodDef = mCurMethodInstance->mMethodDef;
if (checkType->IsPayloadEnum())
{
for (auto& fieldInst : typeInstance->mFieldInstances)
{
if (!fieldInst.mIsEnumPayloadCase)
continue;
auto fieldDef = fieldInst.GetFieldDef();
EmitGCMarkValue(thisValue, fieldInst.mResolvedType, memberDepth + 1, curOffset, objectOffsets, markFromGCThreadMethodInstance);
}
return;
}
if (typeInstance == NULL)
return;
for (auto& fieldInst : typeInstance->mFieldInstances)
{
auto fieldDef = fieldInst.GetFieldDef();
if (fieldDef == NULL)
continue;
if (fieldDef->mIsStatic)
continue;
if (typeInstance == mCurTypeInstance)
{
// Note: we don't do method chaining when we are marking members of members. Theoretically this means
// we may be marking member values that can never contain a value anyway, but this is benign
if ((fieldDef->mDeclaringType->mTypeDeclaration != methodDef->mDeclaringType->mTypeDeclaration))
continue;
}
EmitGCMarkValue(thisValue, fieldInst.mResolvedType, memberDepth + 1, curOffset + fieldInst.mDataOffset, objectOffsets, markFromGCThreadMethodInstance);
}
if ((typeInstance->mBaseType != NULL) && (typeInstance->mBaseType != mContext->mBfObjectType))
{
EmitGCMarkValue(thisValue, typeInstance->mBaseType, memberDepth, curOffset, objectOffsets, markFromGCThreadMethodInstance);
}
}
void BfModule::EmitGCMarkMembers()
{
auto methodDef = mCurMethodInstance->mMethodDef;
auto gcType = ResolveTypeDef(mCompiler->mGCTypeDef, BfPopulateType_DataAndMethods);
BfModuleMethodInstance markFromGCThreadMethodInstance;
if (gcType != NULL)
markFromGCThreadMethodInstance = GetMethodByName(gcType->ToTypeInstance(), "Mark", 1);
if ((markFromGCThreadMethodInstance.mMethodInstance != NULL) && (!mCurMethodInstance->mIsUnspecialized))
{
if (mCurTypeInstance->IsBoxed())
{
// This already gets filled in by the normal boxed forwarding
}
else
{
auto thisValue = GetThis();
if (thisValue.IsSplat())
{
BfIRFunction func = mCurMethodInstance->mIRFunction;
int argIdx = 0;
std::function<void(BfType*)> checkTypeLambda = [&](BfType* checkType)
{
if (checkType->IsStruct())
{
auto checkTypeInstance = checkType->ToTypeInstance();
if (checkTypeInstance->mBaseType != NULL)
checkTypeLambda(checkTypeInstance->mBaseType);
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
if (fieldInstance->mDataIdx >= 0)
checkTypeLambda(fieldInstance->GetResolvedType());
}
}
else if (checkType->IsMethodRef())
{
BFMODULE_FATAL(this, "Not handled");
2019-08-23 11:56:54 -07:00
}
else if (checkType->WantsGCMarking())
{
BfTypedValue markValue(mBfIRBuilder->GetArgument(argIdx), checkType);
EmitGCMarkValue(markValue, markFromGCThreadMethodInstance);
argIdx++;
}
};
checkTypeLambda(thisValue.mType);
}
else if (!methodDef->mIsStatic)
{
if ((mCurTypeInstance->mBaseType != NULL) && (!mCurTypeInstance->IsTypedPrimitive()) && (mCurTypeInstance->mBaseType->WantsGCMarking()))
{
BfModuleMethodInstance moduleMethodInst = GetMethodByName(mCurTypeInstance->mBaseType, BF_METHODNAME_MARKMEMBERS, 0, true);
if (moduleMethodInst)
{
auto methodBaseType = moduleMethodInst.mMethodInstance->GetOwner();
if (methodBaseType != mContext->mBfObjectType)
{
auto thisValue = GetThis();
auto baseValue = Cast(NULL, thisValue, methodBaseType, BfCastFlags_Explicit);
SizedArray<BfIRValue, 1> args;
if (moduleMethodInst.mMethodInstance->GetParamIsSplat(-1))
{
BfExprEvaluator exprEvaluator(this);
exprEvaluator.SplatArgs(baseValue, args);
}
else
{
args.push_back(baseValue.mValue);
}
mBfIRBuilder->CreateCall(moduleMethodInst.mFunc, args);
}
}
}
}
if (!methodDef->mIsStatic)
{
HashSet<int> objectOffsets;
EmitGCMarkValue(thisValue, mCurTypeInstance, 0, 0, objectOffsets, markFromGCThreadMethodInstance);
}
else
{
for (auto& fieldInst : mCurTypeInstance->mFieldInstances)
{
if (thisValue.IsSplat())
break;
auto fieldDef = fieldInst.GetFieldDef();
if (fieldDef == NULL)
continue;
if (fieldInst.mIsThreadLocal)
continue;
if (fieldDef->mIsStatic == methodDef->mIsStatic)
{
// For extensions, only handle these fields in the appropriate extension
if ((fieldDef->mDeclaringType->mTypeDeclaration != methodDef->mDeclaringType->mTypeDeclaration))
continue;
if (!fieldInst.mFieldIncluded)
continue;
auto fieldTypeInst = fieldInst.mResolvedType->ToTypeInstance();
if (fieldTypeInst != NULL)
{
auto fieldDef = fieldInst.GetFieldDef();
BfTypedValue markVal;
if (fieldDef->mIsConst)
continue;
if ((!fieldTypeInst->IsObjectOrInterface()) && (!fieldTypeInst->IsComposite()))
continue;
mBfIRBuilder->PopulateType(fieldTypeInst);
if (fieldTypeInst->IsValuelessType())
continue;
if ((fieldDef->mIsStatic) && (!fieldDef->mIsConst))
{
markVal = ReferenceStaticField(&fieldInst);
}
else if (!fieldDef->mIsStatic)
{
markVal = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, fieldInst.mDataIdx/*, fieldDef->mName*/), fieldInst.mResolvedType, true);
}
if (markVal)
EmitGCMarkValue(markVal, markFromGCThreadMethodInstance);
}
}
}
}
if (mCurMethodInstance->mChainType == BfMethodChainType_ChainHead)
CallChainedMethods(mCurMethodInstance, false);
}
}
}
void BfModule::EmitGCFindTLSMembers()
{
auto methodDef = mCurMethodInstance->mMethodDef;
auto gcTypeInst = ResolveTypeDef(mCompiler->mGCTypeDef)->ToTypeInstance();
auto reportTLSMark = GetMethodByName(gcTypeInst, "ReportTLSMember", 2);
BF_ASSERT(reportTLSMark);
for (auto& fieldInst : mCurTypeInstance->mFieldInstances)
{
auto fieldDef = fieldInst.GetFieldDef();
if (fieldDef == NULL)
continue;
if (!fieldInst.mIsThreadLocal)
continue;
// For extensions, only handle these fields in the appropriate extension
if ((fieldDef->mDeclaringType->mTypeDeclaration != methodDef->mDeclaringType->mTypeDeclaration))
continue;
if (!fieldInst.mFieldIncluded)
continue;
if (fieldDef->mIsConst)
continue;
auto fieldType = fieldInst.mResolvedType;
if ((!fieldType->IsObjectOrInterface()) && (!fieldType->IsComposite()))
continue;
if (fieldType->IsValuelessType())
continue;
BfTypedValue markVal = ReferenceStaticField(&fieldInst);
if (markVal)
{
BfIRValue fieldRefPtr = mBfIRBuilder->CreateBitCast(markVal.mValue, mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_NullPtr)));
BfIRValue markFuncPtr = GetMarkFuncPtr(fieldType);
SizedArray<BfIRValue, 2> llvmArgs;
llvmArgs.push_back(fieldRefPtr);
llvmArgs.push_back(markFuncPtr);
mBfIRBuilder->CreateCall(reportTLSMark.mFunc, llvmArgs);
}
}
if (mCurMethodInstance->mChainType == BfMethodChainType_ChainHead)
CallChainedMethods(mCurMethodInstance, false);
}
void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup)
{
BP_ZONE_F("BfModule::ProcessMethod %s", BP_DYN_STR(methodInstance->mMethodDef->mName.c_str()));
if (mAwaitingInitFinish)
FinishInit();
if (!methodInstance->mIsReified)
BF_ASSERT(!mIsReified);
if (methodInstance->mMethodInfoEx != NULL)
{
for (auto methodGenericArg : methodInstance->mMethodInfoEx->mMethodGenericArguments)
{
if (methodGenericArg->IsMethodRef())
{
auto methodRefType = (BfMethodRefType*)methodGenericArg;
BF_ASSERT(methodRefType->mOwnerRevision == methodRefType->mOwner->mRevision);
}
}
}
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, mWantsIRIgnoreWrites || methodInstance->mIsUnspecialized);
bool ignoreWrites = mBfIRBuilder->mIgnoreWrites;
if ((HasCompiledOutput()) && (!mBfIRBuilder->mIgnoreWrites))
{
BF_ASSERT(!methodInstance->mIRFunction.IsFake() || (methodInstance->GetImportCallKind() != BfImportCallKind_None));
}
SetAndRestoreValue<BfSourceClassifier*> prevSourceClassifier;
if (((methodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend) || (methodInstance->mIsForeignMethodDef) || (methodInstance->IsSpecializedGenericMethod())) &&
(mCompiler->mResolvePassData != NULL))
{
// Don't classify on the CtorCalcAppend, just on the actual Ctor
prevSourceClassifier.Init(mCompiler->mResolvePassData->mSourceClassifier, NULL);
}
if (methodInstance->mHasBeenProcessed)
{
BfLogSysM("ProcessMethod %p HasBeenProcessed\n", methodInstance);
return;
}
if (methodInstance->mFailedConstraints)
{
// The only way this should be able to happen is if we fail our generic param check
BF_ASSERT(methodInstance->IsSpecializedGenericMethod());
BfLogSysM("Method instance marked as 'failed' at start of ProcessMethod %p\n", methodInstance);
methodInstance->mHasBeenProcessed = true;
return;
}
BfMethodInstance* defaultMethodInstance = methodInstance->mMethodInstanceGroup->mDefault;
BF_ASSERT(methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_NotSet);
if ((methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_AlwaysInclude) &&
(methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_Referenced))
{
methodInstance->mMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Referenced;
auto owningModule = methodInstance->GetOwner()->mModule;
if (owningModule->mIsScratchModule)
{
BF_ASSERT(owningModule->mOnDemandMethodCount == 0);
}
else
{
BF_ASSERT((owningModule->mOnDemandMethodCount > 0) || (!HasCompiledOutput()) || (owningModule->mExtensionCount > 0));
if (owningModule->mOnDemandMethodCount > 0)
owningModule->mOnDemandMethodCount--;
}
}
// We set mHasBeenProcessed to true immediately -- this helps avoid stack overflow during recursion for things like
// self-referencing append allocations in ctor@calcAppend
methodInstance->mHasBeenProcessed = true;
mIncompleteMethodCount--;
BF_ASSERT((mIsSpecialModule) || (mIncompleteMethodCount >= 0));
auto typeDef = methodInstance->mMethodInstanceGroup->mOwner->mTypeDef;
auto methodDef = methodInstance->mMethodDef;
auto methodDeclaration = methodDef->GetMethodDeclaration();
if ((methodInstance->mIsReified) && (methodInstance->mVirtualTableIdx != -1))
{
// If we reify a virtual method in a HotCompile then we need to make sure the type data gets written out
// so we get the new vdata for it
methodInstance->GetOwner()->mDirty = true;
}
int dependentGenericStartIdx = 0;
if (methodDef->mIsLocalMethod) // See DoMethodDeclaration for an explaination of dependentGenericStartIdx
dependentGenericStartIdx = (int)mCurMethodState->GetRootMethodState()->mMethodInstance->GetNumGenericArguments();
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, methodInstance);
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, methodInstance->mMethodInstanceGroup->mOwner);
SetAndRestoreValue<BfFilePosition> prevFilePos(mCurFilePosition);
SetAndRestoreValue<bool> prevHadBuildError(mHadBuildError, false);
SetAndRestoreValue<bool> prevHadWarning(mHadBuildWarning, false);
SetAndRestoreValue<bool> prevIgnoreWarnings(mIgnoreWarnings, false);
if ((methodInstance->mIsReified) &&
((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_CtorNoBody)))
{
mCurTypeInstance->mHasBeenInstantiated = true;
}
// Warnings are shown in the capture phase
if ((methodDef->mIsLocalMethod) || (mCurTypeInstance->IsClosure()))
mIgnoreWarnings = true;
if ((methodDeclaration == NULL) && (mCurMethodState == NULL))
UseDefaultSrcPos();
// We may not actually be populated in relatively rare autocompelte cases
PopulateType(mCurTypeInstance, BfPopulateType_DataAndMethods);
mBfIRBuilder->PopulateType(mCurTypeInstance, BfIRPopulateType_Full);
BfAstNode* nameNode = NULL;
if (methodDeclaration != NULL)
{
nameNode = methodDeclaration->mNameNode;
if (nameNode == NULL)
{
if (auto ctorDeclaration = BfNodeDynCast<BfConstructorDeclaration>(methodDeclaration))
nameNode = ctorDeclaration->mThisToken;
}
}
if ((mCompiler->mResolvePassData != NULL) && (methodDeclaration != NULL) && (nameNode != NULL) &&
(mCompiler->mResolvePassData->mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Method) && (methodDef->mIdx >= 0))
{
if (methodInstance->GetExplicitInterface() == NULL)
mCompiler->mResolvePassData->HandleMethodReference(nameNode, typeDef, methodDef);
else
{
for (auto& ifaceEntry : mCurTypeInstance->mInterfaces)
{
auto ifaceInst = ifaceEntry.mInterfaceType;
int startIdx = ifaceEntry.mStartInterfaceTableIdx;
int iMethodCount = (int)ifaceInst->mMethodInstanceGroups.size();
if (methodInstance->GetExplicitInterface() != ifaceInst)
continue;
ifaceInst->mTypeDef->PopulateMemberSets();
BfMethodDef* nextMethod = NULL;
BfMemberSetEntry* entry = NULL;
if (ifaceInst->mTypeDef->mMethodSet.TryGet(BfMemberSetEntry(methodDef), &entry))
nextMethod = (BfMethodDef*)entry->mMemberDef;
while (nextMethod != NULL)
{
auto checkMethod = nextMethod;
nextMethod = nextMethod->mNextWithSameName;
BfMethodInstance* implMethodInst = mCurTypeInstance->mInterfaceMethodTable[startIdx + checkMethod->mIdx].mMethodRef;
if (implMethodInst == methodInstance)
{
auto ifaceMethodDef = ifaceInst->mTypeDef->mMethods[checkMethod->mIdx];
mCompiler->mResolvePassData->HandleMethodReference(nameNode, ifaceInst->mTypeDef, ifaceMethodDef);
}
}
}
}
if (methodDef->mIsOverride)
{
for (int virtIdx = 0; virtIdx < (int)mCurTypeInstance->mVirtualMethodTable.size(); virtIdx++)
{
auto& ventry = mCurTypeInstance->mVirtualMethodTable[virtIdx];
if (ventry.mDeclaringMethod.mMethodNum == -1)
continue;
BfMethodInstance* virtMethod = ventry.mImplementingMethod;
if (virtMethod != NULL)
{
if (virtMethod == methodInstance)
{
// Is matching method
if (virtIdx < (int)mCurTypeInstance->mBaseType->mVirtualMethodTable.size())
{
auto baseType = mCurTypeInstance->mBaseType;
if (baseType != NULL)
{
while ((baseType->mBaseType != NULL) && (virtIdx < baseType->mBaseType->mVirtualMethodTableSize))
baseType = baseType->mBaseType;
BfMethodInstance* baseVirtMethod = baseType->mVirtualMethodTable[virtIdx].mImplementingMethod;
mCompiler->mResolvePassData->HandleMethodReference(nameNode, baseType->mTypeDef, baseVirtMethod->mMethodDef);
}
}
break;
}
}
}
}
}
BfLogSysM("ProcessMethod %p Unspecialized: %d Module: %p IRFunction: %d Reified: %d\n", methodInstance, mCurTypeInstance->IsUnspecializedType(), this, methodInstance->mIRFunction.mId, methodInstance->mIsReified);
if (methodInstance->GetImportCallKind() != BfImportCallKind_None)
{
if (mBfIRBuilder->mIgnoreWrites)
return;
BfLogSysM("DllImportGlobalVar processing %p\n", methodInstance);
if (!methodInstance->mIRFunction)
{
BfIRValue dllImportGlobalVar = CreateDllImportGlobalVar(methodInstance, true);
methodInstance->mIRFunction = mBfIRBuilder->GetFakeVal();
mFuncReferences[mCurMethodInstance] = dllImportGlobalVar;
}
if (HasCompiledOutput())
{
BfDllImportEntry dllImportEntry;
dllImportEntry.mFuncVar = methodInstance->mIRFunction;
dllImportEntry.mMethodInstance = mCurMethodInstance;
mDllImportEntries.push_back(dllImportEntry);
}
return;
}
StringT<128> mangledName;
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), mCurMethodInstance);
if (!methodInstance->mIRFunction)
{
bool isIntrinsic = false;
SetupIRFunction(methodInstance, mangledName, false, &isIntrinsic);
}
if (methodInstance->mIsIntrinsic)
return;
auto prevActiveFunction = mBfIRBuilder->GetActiveFunction();
mBfIRBuilder->SetActiveFunction(mCurMethodInstance->mIRFunction);
if (methodDef->mBody != NULL)
UpdateSrcPos(methodDef->mBody, BfSrcPosFlag_NoSetDebugLoc);
else if (methodDeclaration != NULL)
UpdateSrcPos(methodDeclaration, BfSrcPosFlag_NoSetDebugLoc);
else if (mCurTypeInstance->mTypeDef->mTypeDeclaration != NULL)
UpdateSrcPos(mCurTypeInstance->mTypeDef->mTypeDeclaration, BfSrcPosFlag_NoSetDebugLoc);
if (mCurMethodState == NULL) // Only do initial classify for the 'outer' method state, not any local methods or lambdas
{
if ((mCompiler->mIsResolveOnly) && (methodDef->mBody != NULL) && (!mCurTypeInstance->IsBoxed()) &&
(methodDef->mBody->IsFromParser(mCompiler->mResolvePassData->mParser)) && (mCompiler->mResolvePassData->mSourceClassifier != NULL))
{
mCompiler->mResolvePassData->mSourceClassifier->VisitChildNoRef(methodDef->mBody);
}
}
BfHotDataReferenceBuilder hotDataReferenceBuilder;
BfMethodState methodState;
if (mCurMethodState != NULL)
methodState.mClosureState = mCurMethodState->mClosureState;
if ((mCompiler->mOptions.mAllowHotSwapping) && (methodInstance->mIsReified) && (!methodInstance->mIsUnspecialized) && (!isInlineDup))
{
//BF_ASSERT(methodInstance->mHotMethod != NULL);
if (methodInstance->mHotMethod == NULL)
CheckHotMethod(methodInstance, mangledName);
methodState.mHotDataReferenceBuilder = &hotDataReferenceBuilder;
}
methodState.mPrevMethodState = mCurMethodState;
methodState.mMethodInstance = methodInstance;
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
if (methodInstance->GetCustomAttributes() != NULL)
{
int typeOptionsIdx = GenerateTypeOptions(methodInstance->GetCustomAttributes(), mCurTypeInstance, false);
if (typeOptionsIdx != -1)
methodState.mMethodTypeOptions = mSystem->GetTypeOptions(typeOptionsIdx);
}
BfTypeState typeState(mCurTypeInstance);
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
2019-08-23 11:56:54 -07:00
bool isGenericVariation = (methodInstance->mIsUnspecializedVariation) || (mCurTypeInstance->IsUnspecializedTypeVariation());
BfMethodInstance* unspecializedMethodInstance = NULL;
if ((prevMethodState.mPrevVal != NULL) &&
((methodState.mClosureState == NULL) || (methodState.mClosureState->mActiveDeferredLocalMethod == NULL)))
{
// This is 'inner' (probably a closure) - use binding from outer function
methodState.mGenericTypeBindings = prevMethodState.mPrevVal->mGenericTypeBindings;
}
else if ((methodInstance->mIsUnspecialized) ||
((mCurTypeInstance->IsUnspecializedType()) && (!isGenericVariation)))
2019-08-23 11:56:54 -07:00
{
methodState.mGenericTypeBindings = &methodInstance->GetMethodInfoEx()->mGenericTypeBindings;
}
else if ((((methodInstance->mMethodInfoEx != NULL) && ((int)methodInstance->mMethodInfoEx->mMethodGenericArguments.size() > dependentGenericStartIdx)) ||
((mCurTypeInstance->IsGenericTypeInstance()) && (!isGenericVariation))))
2019-08-23 11:56:54 -07:00
{
unspecializedMethodInstance = GetUnspecializedMethodInstance(methodInstance);
BF_ASSERT(unspecializedMethodInstance != methodInstance);
if (!unspecializedMethodInstance->mHasBeenProcessed)
{
// Make sure the unspecialized method is processed so we can take its bindings
// Clear mCurMethodState so we don't think we're in a local method
SetAndRestoreValue<BfMethodState*> prevMethodState_Unspec(mCurMethodState, prevMethodState.mPrevVal);
mContext->ProcessMethod(unspecializedMethodInstance);
}
methodState.mGenericTypeBindings = &unspecializedMethodInstance->GetMethodInfoEx()->mGenericTypeBindings;
}
if (methodDef->mMethodType == BfMethodType_Operator)
{
auto operatorDef = (BfOperatorDef*) methodDef;
BfAstNode* refNode = operatorDef->mOperatorDeclaration->mOperatorToken;
auto paramErrorRefNode = refNode;
if (operatorDef->mOperatorDeclaration->mOpenParen != NULL)
paramErrorRefNode = operatorDef->mOperatorDeclaration->mOpenParen;
if ((mCurMethodInstance->GetParamCount() > 0) && (mCurMethodInstance->GetParamTypeRef(0) != NULL))
paramErrorRefNode = mCurMethodInstance->GetParamTypeRef(0);
if (operatorDef->mOperatorDeclaration->mBinOp != BfBinaryOp_None)
{
if (methodDef->mParams.size() != 2)
{
Fail("Binary operators must declare two parameters", paramErrorRefNode);
}
else
{
if ((mCurMethodInstance->GetParamType(0) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(0)->IsSelf()) &&
(mCurMethodInstance->GetParamType(1) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(1)->IsSelf()))
{
Fail("At least one of the parameters of a binary operator must be the containing type", paramErrorRefNode);
}
}
BfType* wantType = NULL;
if (operatorDef->mOperatorDeclaration->mBinOp == BfBinaryOp_Compare)
{
wantType = GetPrimitiveType(BfTypeCode_IntPtr);
}
if ((wantType != NULL) && (methodInstance->mReturnType != wantType))
Fail(StrFormat("The return type for the '%s' operator must be '%s'", BfGetOpName(operatorDef->mOperatorDeclaration->mBinOp),
TypeToString(wantType).c_str()), operatorDef->mOperatorDeclaration->mReturnType);
}
else if (operatorDef->mOperatorDeclaration->mUnaryOp != BfUnaryOp_None)
{
if (methodDef->mParams.size() != 1)
{
Fail("Unary operators must declare one parameter", paramErrorRefNode);
}
else if ((mCurMethodInstance->GetParamType(0) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(0)->IsSelf()))
{
Fail("The parameter of a unary operator must be the containing type", paramErrorRefNode);
}
if (((operatorDef->mOperatorDeclaration->mUnaryOp == BfUnaryOp_Increment) ||
(operatorDef->mOperatorDeclaration->mUnaryOp == BfUnaryOp_Decrement)) &&
(!TypeIsSubTypeOf(mCurMethodInstance->mReturnType->ToTypeInstance(), mCurTypeInstance)))
{
Fail("The return type for the '++' or '--' operator must match the parameter type or be derived from the parameter type", operatorDef->mOperatorDeclaration->mReturnType);
}
}
else if (operatorDef->mOperatorDeclaration->mAssignOp != BfAssignmentOp_None)
{
if (methodDef->mParams.size() != 1)
{
Fail("Assignment operators must declare one parameter", paramErrorRefNode);
}
if (!mCurMethodInstance->mReturnType->IsVoid())
2019-08-23 11:56:54 -07:00
{
Fail("The return type for assignment operator must be 'void'", operatorDef->mOperatorDeclaration->mReturnType);
}
}
else // Conversion
{
if (!operatorDef->mOperatorDeclaration->mIsConvOperator)
{
if (operatorDef->mOperatorDeclaration->mReturnType != NULL)
Fail("Conversion operators must declare their target type after the 'operator' token, not before", operatorDef->mOperatorDeclaration->mReturnType);
}
if (methodDef->mParams.size() != 1)
{
auto refNode = paramErrorRefNode;
if (!operatorDef->mOperatorDeclaration->mCommas.empty())
refNode = operatorDef->mOperatorDeclaration->mCommas[0];
Fail("Conversion operators must declare one parameter", refNode);
}
else
{
if ((mCurMethodInstance->GetParamType(0) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(0)->IsSelf()) &&
(mCurMethodInstance->mReturnType != mCurTypeInstance) && (!mCurMethodInstance->mReturnType->IsSelf()))
Fail("User-defined conversion must convert to or from the enclosing type", paramErrorRefNode);
if (mCurMethodInstance->GetParamType(0) == mCurMethodInstance->mReturnType)
Fail("User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type", operatorDef->mOperatorDeclaration->mReturnType);
// On type lookup error we default to 'object', so don't do the 'base class' error if that may have
// happened here
if ((!mHadBuildError) || ((mCurMethodInstance->mReturnType != mContext->mBfObjectType) && (mCurMethodInstance->GetParamType(0) != mContext->mBfObjectType)))
{
auto isToBase = TypeIsSubTypeOf(mCurTypeInstance->mBaseType, mCurMethodInstance->mReturnType->ToTypeInstance());
bool isFromBase = TypeIsSubTypeOf(mCurTypeInstance->mBaseType, mCurMethodInstance->GetParamType(0)->ToTypeInstance());
if ((mCurTypeInstance->IsObject()) && (isToBase || isFromBase))
Fail("User-defined conversions to or from a base class are not allowed", paramErrorRefNode);
else if ((mCurTypeInstance->IsStruct()) && (isToBase))
Fail("User-defined conversions to a base struct are not allowed", paramErrorRefNode);
}
}
}
}
bool wantsDIData = (mBfIRBuilder->DbgHasInfo()) && (!methodDef->IsEmptyPartial()) && (!mCurMethodInstance->mIsUnspecialized) && (mHasFullDebugInfo);
if (methodDef->mMethodType == BfMethodType_Mixin)
wantsDIData = false;
if ((mCurTypeInstance->IsBoxed()) && (methodDef->mMethodType != BfMethodType_Ctor))
wantsDIData = false;
bool wantsDIVariables = wantsDIData;
if (methodDef->mIsExtern)
wantsDIVariables = false;
SizedArray<BfIRMDNode, 8> diParams;
diParams.push_back(mBfIRBuilder->DbgGetType(methodInstance->mReturnType));
bool isThisStruct = mCurTypeInstance->IsStruct() && !mCurTypeInstance->IsTypedPrimitive();
BfType* thisType = NULL;
if (!methodDef->mIsStatic)
{
if ((methodState.mClosureState != NULL) && (methodState.mClosureState->mClosureType != NULL))
thisType = methodState.mClosureState->mClosureType;
else
thisType = mCurTypeInstance;
}
PopulateType(methodInstance->mReturnType, BfPopulateType_Data);
ProcessMethod_SetupParams(methodInstance, thisType, wantsDIData, &diParams);
//////////////////////////////////////////////////////////////////////////
//
{
if (methodDeclaration != NULL)
UpdateSrcPos(methodDeclaration, BfSrcPosFlag_NoSetDebugLoc);
else if (methodDef->mBody != NULL)
UpdateSrcPos(methodDef->mBody, BfSrcPosFlag_NoSetDebugLoc);
else if (mCurTypeInstance->mTypeDef->mTypeDeclaration != NULL)
UpdateSrcPos(mCurTypeInstance->mTypeDef->mTypeDeclaration, BfSrcPosFlag_NoSetDebugLoc);
}
BfIRMDNode diFunction;
if (wantsDIData)
{
BP_ZONE("BfModule::BfMethodDeclaration.DISetup");
BfIRMDNode diFuncType = mBfIRBuilder->DbgCreateSubroutineType(diParams);
int defLine = mCurFilePosition.mCurLine;
if (mDICompileUnit)
{
int flags = 0;
BfIRMDNode funcScope = mBfIRBuilder->DbgGetTypeInst(mCurTypeInstance);
if (methodDef->mProtection == BfProtection_Public)
flags = llvm::DINode::FlagPublic;
else if (methodDef->mProtection == BfProtection_Protected)
flags = llvm::DINode::FlagProtected;
else
flags = llvm::DINode::FlagPrivate;
if (methodDef->mIsOverride)
{
//flags |= llvm::DINode::FlagVirtual;
}
else if (methodDef->mIsVirtual)
flags |= llvm::DINode::FlagIntroducedVirtual;
if ((methodDef->mIsStatic) || (methodDef->mMethodType == BfMethodType_Mixin))
flags |= llvm::DINode::FlagStaticMember;
else
{
if ((mCurTypeInstance->IsValuelessType()) || (mCurTypeInstance->IsSplattable()))
flags |= llvm::DINode::FlagStaticMember;
}
flags |= llvm::DINode::FlagPrototyped;
if (methodDef->mMethodType == BfMethodType_Ctor)
{
flags |= llvm::DINode::FlagArtificial;
}
auto llvmFunction = methodInstance->mIRFunction;
SizedArray<BfIRMDNode, 1> genericArgs;
SizedArray<BfIRValue, 1> genericConstValueArgs;
String methodName;
if (methodInstance->GetForeignType() != NULL)
{
methodName += TypeToString(methodInstance->GetForeignType());
methodName += ".";
}
if (methodInstance->GetExplicitInterface() != NULL)
{
methodName += TypeToString(methodInstance->GetExplicitInterface());
methodName += ".";
}
methodName += methodDef->mName;
if (methodInstance->GetNumGenericArguments() != 0)
{
for (auto genericArg : methodInstance->mMethodInfoEx->mMethodGenericArguments)
{
if (genericArg->IsConstExprValue())
{
BfConstExprValueType* constExprValueType = (BfConstExprValueType*)genericArg;
while (genericConstValueArgs.size() < genericArgs.size())
genericConstValueArgs.push_back(BfIRValue());
genericConstValueArgs.push_back(mBfIRBuilder->CreateConst(BfTypeCode_UInt64, constExprValueType->mValue.mUInt64));
genericArgs.push_back(mBfIRBuilder->DbgGetType(GetPrimitiveType(BfTypeCode_Int64)));
}
else
genericArgs.push_back(mBfIRBuilder->DbgGetType(genericArg));
}
methodName += "<";
for (int i = 0; i < (int)methodInstance->mMethodInfoEx->mMethodGenericArguments.size(); i++)
{
if (i > 0)
methodName += ", ";
BfType* type = methodInstance->mMethodInfoEx->mMethodGenericArguments[i];
methodName += TypeToString(type);
}
methodName += ">";
}
if ((methodName.empty()) && (methodDeclaration != NULL))
{
if (auto operatorDecl = BfNodeDynCast<BfOperatorDeclaration>(methodDeclaration))
{
methodName += "operator";
if (operatorDecl->mIsConvOperator)
{
methodName += " ";
methodName += TypeToString(methodInstance->mReturnType);
}
else if (operatorDecl->mOpTypeToken != NULL)
methodName += operatorDecl->mOpTypeToken->ToString();
}
}
if (methodDef->mCheckedKind == BfCheckedKind_Checked)
methodName += "$CHK";
else if (methodDef->mCheckedKind == BfCheckedKind_Unchecked)
methodName += "$UCHK";
2019-08-23 11:56:54 -07:00
methodState.mDIFile = mCurFilePosition.mFileInstance->mDIFile;
// diFunction = mBfIRBuilder->DbgCreateMethod(funcScope, methodName, mangledName, methodState.mDIFile,
// defLine + 1, diFuncType, false, true,
// (methodInstance->mVirtualTableIdx != -1) ? llvm::dwarf::DW_VIRTUALITY_virtual : llvm::dwarf::DW_VIRTUALITY_none,
// (methodInstance->mVirtualTableIdx != -1) ? methodInstance->DbgGetVirtualMethodNum() : 0,
// nullptr, flags, IsOptimized(), llvmFunction, genericArgs, genericConstValueArgs);
diFunction = mBfIRBuilder->DbgCreateMethod(funcScope, methodName, mangledName, methodState.mDIFile,
defLine + 1, diFuncType, false, true,
llvm::dwarf::DW_VIRTUALITY_none,
0,
nullptr, flags, IsOptimized(), llvmFunction, genericArgs, genericConstValueArgs);
}
}
//////////////////////////////////////////////////////////////////////////
// Head and Init get rolled into Entry afterwards.
methodState.mIRFunction = methodInstance->mIRFunction;
methodState.mIRHeadBlock = mBfIRBuilder->CreateBlock("head", true);
methodState.mIRInitBlock = mBfIRBuilder->CreateBlock("init", true);
methodState.mIREntryBlock = mBfIRBuilder->CreateBlock("entry", true);
methodState.mCurScope->mDIScope = diFunction;
auto llvmEntryBlock = methodState.mIREntryBlock;
mBfIRBuilder->SetInsertPoint(llvmEntryBlock);
2020-05-28 07:37:55 -07:00
if (methodDef->mName == "__MALFORMED")
{
auto newBlock = mBfIRBuilder->CreateBlock("malformed", true);
mBfIRBuilder->SetInsertPoint(newBlock);
}
2019-08-23 11:56:54 -07:00
if (methodDef->mBody != NULL)
{
UpdateSrcPos(methodDef->mBody, BfSrcPosFlag_NoSetDebugLoc);
}
else if ((mHasFullDebugInfo) && (diFunction) && (typeDef->mTypeDeclaration != NULL))
{
// We want to be able to step into delegate invokes -- we actually step over them
if (methodDef->mName != "Invoke")
{
UpdateSrcPos(typeDef->mTypeDeclaration);
mBfIRBuilder->DbgCreateAnnotation(diFunction, "StepOver", GetConstValue32(1));
}
}
// Clear out DebugLoc - to mark the ".addr" code as part of prologue
mBfIRBuilder->ClearDebugLocation();
bool isTypedPrimitiveFunc = mCurTypeInstance->IsTypedPrimitive() && (methodDef->mMethodType != BfMethodType_Ctor);
int irParamCount = methodInstance->GetIRFunctionParamCount(this);
if (methodInstance->GetImportKind() != BfImportKind_Import_Dynamic)
2019-08-23 11:56:54 -07:00
{
int localIdx = 0;
int argIdx = 0;
2019-08-23 11:56:54 -07:00
Array<BfIRValue> splatAddrValues;
for ( ; argIdx < irParamCount; localIdx++)
{
bool isThis = ((localIdx == 0) && (!mCurMethodInstance->mMethodDef->mIsStatic));
if ((isThis) && (thisType->IsValuelessType()))
isThis = false;
if (methodInstance->GetStructRetIdx() == argIdx)
{
argIdx++;
if (argIdx == irParamCount)
break;
}
2019-08-23 11:56:54 -07:00
BfLocalVariable* paramVar = NULL;
while (true)
{
BF_ASSERT(localIdx < methodState.mLocals.size());
paramVar = methodState.mLocals[localIdx];
if ((paramVar->mCompositeCount == -1) &&
((!paramVar->mResolvedType->IsValuelessType()) || (paramVar->mResolvedType->IsMethodRef())))
break;
localIdx++;
}
if ((isThis) && (mCurTypeInstance->IsValueType()) && (methodDef->mMethodType != BfMethodType_Ctor) && (!methodDef->HasNoThisSplat()))
{
paramVar->mIsReadOnly = true;
}
bool wantsAddr = (wantsDIVariables) || (!paramVar->mIsReadOnly) || (paramVar->mResolvedType->GetLoweredType(BfTypeUsage_Parameter));
2019-08-23 11:56:54 -07:00
if (paramVar->mResolvedType->IsMethodRef())
wantsAddr = false;
// if ((methodDef->mHasAppend) && (argIdx == 1))
// {
// BF_ASSERT(paramVar->mName == "appendIdx");
// wantsAddr = true;
// }
2020-03-12 13:41:14 -07:00
if (paramVar->mIsSplat)
2019-08-23 11:56:54 -07:00
{
auto prevInsert = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
BfTypeUtils::SplatIterate([&](BfType* checkType)
{
BfIRType splatIRType;
if (checkType->IsComposite())
splatIRType = mBfIRBuilder->MapType(CreatePointerType(checkType));
else
splatIRType = mBfIRBuilder->MapType(checkType);
auto allocaInst = mBfIRBuilder->CreateAlloca(splatIRType);
mBfIRBuilder->SetAllocaAlignment(allocaInst, checkType->mAlign);
if (!paramVar->mAddr)
paramVar->mAddr = allocaInst;
if (WantsLifetimes())
mCurMethodState->mCurScope->mDeferredLifetimeEnds.push_back(allocaInst);
2019-08-23 11:56:54 -07:00
splatAddrValues.push_back(allocaInst);
}, paramVar->mResolvedType);
mBfIRBuilder->SetInsertPoint(prevInsert);
}
else if (isThis)
{
if (wantsAddr)
{
auto prevInsert = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
BfIRType thisAddrType = mBfIRBuilder->MapType(thisType);
if (paramVar->mIsSplat)
{
2020-03-12 13:41:14 -07:00
//
2019-08-23 11:56:54 -07:00
}
else
{
bool wantPtr = (thisType->IsComposite()) && (!paramVar->mIsLowered);
if ((thisType->IsTypedPrimitive()) && (methodDef->HasNoThisSplat()))
wantPtr = true;
if (wantPtr)
{
thisAddrType = mBfIRBuilder->MapTypeInstPtr(thisType->ToTypeInstance());
}
auto allocaInst = mBfIRBuilder->CreateAlloca(thisAddrType);
mBfIRBuilder->SetName(allocaInst, paramVar->mName + ".addr");
mBfIRBuilder->SetAllocaAlignment(allocaInst, mSystem->mPtrSize);
paramVar->mAddr = allocaInst;
if (WantsLifetimes())
mCurMethodState->mCurScope->mDeferredLifetimeEnds.push_back(allocaInst);
2019-08-23 11:56:54 -07:00
}
mBfIRBuilder->SetInsertPoint(prevInsert);
}
}
else if ((!isThis) && ((paramVar->mIsStruct) || (paramVar->mResolvedType->IsRef())))
{
// Address doesn't change so we don't have to alloca it
BfIRType allocType;
int alignSize = mSystem->mPtrSize;
if (paramVar->mResolvedType->IsRef())
{
allocType = mBfIRBuilder->MapType(paramVar->mResolvedType);
}
else if ((paramVar->mIsSplat) || (paramVar->mIsLowered))
{
allocType = mBfIRBuilder->MapType(paramVar->mResolvedType);
alignSize = paramVar->mResolvedType->mAlign;
}
else
{
paramVar->mHasLocalStructBacking = true;
auto typeInst = paramVar->mResolvedType->ToTypeInstance();
if (typeInst != NULL)
allocType = mBfIRBuilder->MapTypeInstPtr(paramVar->mResolvedType->ToTypeInstance());
else
allocType = mBfIRBuilder->MapType(CreatePointerType(paramVar->mResolvedType));
}
if (wantsAddr)
{
auto prevInsert = mBfIRBuilder->GetInsertBlock();
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
mBfIRBuilder->PopulateType(paramVar->mResolvedType);
auto allocaInst = mBfIRBuilder->CreateAlloca(allocType);
mBfIRBuilder->SetName(allocaInst, paramVar->mName + ".addr");
mBfIRBuilder->SetAllocaAlignment(allocaInst, alignSize);
paramVar->mAddr = allocaInst;
mBfIRBuilder->SetInsertPoint(prevInsert);
if (WantsLifetimes())
mCurMethodState->mCurScope->mDeferredLifetimeEnds.push_back(allocaInst);
2019-08-23 11:56:54 -07:00
}
}
else if (wantsAddr)
{
auto allocaInst = CreateAlloca(paramVar->mResolvedType);
mBfIRBuilder->SetName(allocaInst, paramVar->mName + ".addr");
paramVar->mAddr = allocaInst;
}
if (paramVar->mIsSplat)
{
BfTypeUtils::SplatIterate([&](BfType* checkType) { argIdx++; }, paramVar->mResolvedType);
}
else if (paramVar->mIsLowered)
{
BfTypeCode loweredTypeCode = BfTypeCode_None;
BfTypeCode loweredTypeCode2 = BfTypeCode_None;
paramVar->mResolvedType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2);
argIdx++;
if (loweredTypeCode2 != BfTypeCode_None)
argIdx++;
}
2019-08-23 11:56:54 -07:00
else
{
argIdx++;
}
}
if (methodDef->mBody != NULL)
UpdateSrcPos(methodDef->mBody);
else if (mCurTypeInstance->mTypeDef->mTypeDeclaration != NULL)
UpdateSrcPos(mCurTypeInstance->mTypeDef->mTypeDeclaration);
int declArgIdx = 0;
localIdx = 0;
argIdx = 0;
2019-08-23 11:56:54 -07:00
int splatAddrIdx = 0;
while (localIdx < (int)methodState.mLocals.size())
{
if (argIdx == methodInstance->GetStructRetIdx())
argIdx++;
2019-08-23 11:56:54 -07:00
int curLocalIdx = localIdx++;
BfLocalVariable* paramVar = methodState.mLocals[curLocalIdx];
if (!paramVar->IsParam())
continue;
if (paramVar->mCompositeCount != -1)
continue;
bool isThis = ((curLocalIdx == 0) && (!mCurMethodInstance->mMethodDef->mIsStatic));
if ((isThis) && (thisType->IsValuelessType()))
isThis = false;
BfIRMDNode diVariable;
if (wantsDIData)
{
String paramName = paramVar->mName;
BfIRMDNode diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType);
if (isThis)
{
if ((mCurMethodState->mClosureState != NULL) && (mCurMethodState->mClosureState->mClosureType != NULL))
paramName = "__closure";
if ((paramVar->mResolvedType->IsValueType()) && (!paramVar->mIsSplat) && (!paramVar->mIsLowered))
{
diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType);
bool wantRef = paramVar->mResolvedType->IsComposite();
if ((paramVar->mResolvedType->IsTypedPrimitive()) && (methodDef->HasNoThisSplat()))
wantRef = true;
if (wantRef)
diType = mBfIRBuilder->DbgCreateReferenceType(diType);
}
else if (!paramVar->mResolvedType->IsValueType())
{
diType = mBfIRBuilder->DbgCreateArtificialType(diType);
}
}
else
{
if ((paramVar->mResolvedType->IsComposite()) && (!paramVar->mIsSplat) && (!paramVar->mIsLowered))
{
diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType);
diType = mBfIRBuilder->DbgCreateReferenceType(diType);
diType = mBfIRBuilder->DbgCreateConstType(diType);
}
}
2020-03-12 13:41:14 -07:00
if (!paramVar->mIsSplat)
2019-08-23 11:56:54 -07:00
{
if (paramVar->mResolvedType->IsValuelessType())
{
diVariable = mBfIRBuilder->DbgCreateAutoVariable(diFunction,
paramName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType);
}
else if (paramVar->mIsImplicitParam)
{
// Annotate this specially?
diVariable = mBfIRBuilder->DbgCreateParameterVariable(diFunction,
paramName, argIdx + 1, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType, true, 0);
}
else
{
diVariable = mBfIRBuilder->DbgCreateParameterVariable(diFunction,
paramName, argIdx + 1, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType, true, 0);
}
paramVar->mDbgVarInst = diVariable;
}
else if ((paramVar->mIsSplat) && (paramVar->mResolvedType->GetSplatCount() == 0))
{
// if ((mBfIRBuilder->HasDebugLocation()) && (wantsDIVariables))
// {
// // Only add this placeholder if we don't have any values
// auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope,
// paramName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType);
// mBfIRBuilder->DbgInsertValueIntrinsic(GetConstValue(0), diVariable);
// }
}
}
bool isTypedPrimCtor = mCurTypeInstance->IsTypedPrimitive() && (methodDef->mMethodType == BfMethodType_Ctor);
if ((!paramVar->mParamFailed) && (paramVar->mAddr))
{
// Write our argument value into the .addr
mBfIRBuilder->ClearDebugLocation();
if (paramVar->mIsSplat)
{
mBfIRBuilder->PopulateType(paramVar->mResolvedType);
2020-03-12 13:41:14 -07:00
//
2019-08-23 11:56:54 -07:00
}
else
{
bool handled = false;
2019-08-23 11:56:54 -07:00
if (paramVar->mIsLowered)
{
BfTypeCode loweredTypeCode = BfTypeCode_None;
BfTypeCode loweredTypeCode2 = BfTypeCode_None;
if (paramVar->mResolvedType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2))
2019-08-23 11:56:54 -07:00
{
// We have a lowered type coming in, so we have to cast the .addr before storing
auto primType = mBfIRBuilder->GetPrimitiveType(loweredTypeCode);
auto primPtrType = mBfIRBuilder->GetPointerTo(primType);
auto primPtrVal = mBfIRBuilder->CreateBitCast(paramVar->mAddr, primPtrType);
mBfIRBuilder->CreateStore(paramVar->mValue, primPtrVal);
if (loweredTypeCode2 != BfTypeCode_None)
{
declArgIdx++;
auto primType2 = mBfIRBuilder->GetPrimitiveType(loweredTypeCode2);
auto primPtrType2 = mBfIRBuilder->GetPointerTo(primType2);
auto primPtrVal2 = mBfIRBuilder->CreateBitCast(mBfIRBuilder->CreateInBoundsGEP(primPtrVal, 1), primPtrType2);
mBfIRBuilder->CreateStore(mBfIRBuilder->GetArgument(argIdx + 1), primPtrVal2);
}
2019-08-23 11:56:54 -07:00
// We don't want to allow directly using value
paramVar->mValue = BfIRValue();
handled = true;
}
else
{
BF_ASSERT("Expected lowered");
}
2019-08-23 11:56:54 -07:00
}
if (!handled)
mBfIRBuilder->CreateAlignedStore(paramVar->mValue, paramVar->mAddr, paramVar->mResolvedType->mAlign);
}
}
if (!isThis)
declArgIdx++;
if (methodDef->mBody != NULL)
UpdateSrcPos(methodDef->mBody);
else if (methodDef->mDeclaringType->mTypeDeclaration != NULL)
UpdateSrcPos(methodDef->mDeclaringType->mTypeDeclaration);
else if (methodDeclaration == NULL)
UseDefaultSrcPos();
// Write our argument value into the .addr
2020-03-12 13:41:14 -07:00
if (paramVar->mIsSplat)
2019-08-23 11:56:54 -07:00
{
int splatComponentIdx = 0;
auto curArgIdx = argIdx;
auto _FinishSplats = [&] (BfType* checkType, const StringImpl& name)
{
BfIRValue splatAddrValue = splatAddrValues[splatAddrIdx++];
methodState.mSplatDecompAddrs.push_back(splatAddrValue);
auto storeInst = mBfIRBuilder->CreateAlignedStore(mBfIRBuilder->GetArgument(curArgIdx), splatAddrValue, checkType->mAlign);
mBfIRBuilder->ClearDebugLocation(storeInst);
if (wantsDIData)
{
BfIRMDNode diType;
if (checkType->IsComposite())
diType = mBfIRBuilder->DbgGetType(CreateRefType(checkType));
else
diType = mBfIRBuilder->DbgGetType(checkType);
auto diVariable = mBfIRBuilder->DbgCreateParameterVariable(diFunction,
name, curArgIdx + 1, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType);
if (wantsDIVariables)
{
mBfIRBuilder->DbgInsertDeclare(splatAddrValue, diVariable);
}
}
curArgIdx++;
splatComponentIdx++;
};
std::function<void(BfType*, const StringImpl&)> _FinishSplatsIterate = [&](BfType* checkType, const StringImpl& name)
{
if (checkType->IsStruct())
{
auto checkTypeInstance = checkType->ToTypeInstance();
if (checkTypeInstance->mBaseType != NULL)
{
_FinishSplatsIterate(checkTypeInstance->mBaseType, name + "$b");
}
if (checkTypeInstance->mIsUnion)
{
auto unionInnerType = checkTypeInstance->GetUnionInnerType();
if (!unionInnerType->IsValuelessType())
{
_FinishSplatsIterate(unionInnerType, name + "$u");
}
if (checkTypeInstance->IsEnum())
{
auto dscrType = checkTypeInstance->GetDiscriminatorType();
_FinishSplatsIterate(dscrType, name + "$d");
}
}
else
{
for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++)
{
auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx];
auto fieldDef = fieldInstance->GetFieldDef();
if (fieldInstance->mDataIdx >= 0)
{
_FinishSplatsIterate(fieldInstance->GetResolvedType(), name + "$m$" + fieldDef->mName);
}
}
}
}
else if (checkType->IsMethodRef())
{
auto methodRefType = (BfMethodRefType*)checkType;
BfMethodInstance* methodInstance = methodRefType->mMethodRef;
// int implicitParamCount = methodInstance->GetImplicitParamCount();
// for (int implicitParamIdx = methodInstance->HasThis() ? -1 : 0; implicitParamIdx < implicitParamCount; implicitParamIdx++)
// {
// auto paramType = methodInstance->GetParamType(implicitParamIdx);
// if (!paramType->IsValuelessType())
// _FinishSplats(paramType, name + "$m$" + methodInstance->GetParamName(implicitParamIdx));
// }
for (int dataIdx = 0; dataIdx < methodRefType->GetCaptureDataCount(); dataIdx++)
{
int paramIdx = methodRefType->GetParamIdxFromDataIdx(dataIdx);
String paramName = methodInstance->GetParamName(paramIdx);
if (paramName == "this")
paramName = "__this";
if (methodRefType->WantsDataPassedAsSplat(dataIdx))
{
_FinishSplatsIterate(methodRefType->GetCaptureType(dataIdx), name + "$m$" + paramName);
}
else
{
_FinishSplats(methodRefType->GetCaptureType(dataIdx), name + "$m$" + paramName);
}
}
}
else if (!checkType->IsValuelessType())
{
_FinishSplats(checkType, name);
}
};
mBfIRBuilder->PopulateType(paramVar->mResolvedType);
if (!paramVar->mConstValue)
_FinishSplatsIterate(paramVar->mResolvedType, "$" + paramVar->mName);
}
BfIRValue declareCall;
if (diVariable)
{
if ((mBfIRBuilder->HasDebugLocation()) && (wantsDIVariables))
{
if (!paramVar->mAddr)
{
if ((!paramVar->mValue) || (paramVar->mValue.IsFake()))
{
if ((!paramVar->mIsThis) && (mCompiler->mOptions.mToolsetType != BfToolsetType_GNU)) // DWARF chokes on this:
{
// We don't need to set the location for this
mBfIRBuilder->DbgInsertValueIntrinsic(BfIRValue(), diVariable);
}
}
else
declareCall = mBfIRBuilder->DbgInsertDeclare(paramVar->mValue, diVariable);
}
else
{
declareCall = mBfIRBuilder->DbgInsertDeclare(paramVar->mAddr, diVariable);
}
}
}
if ((isThis) && (!paramVar->mIsSplat) && (paramVar->mAddr))
{
// We don't allow actually assignment to "this", so we just do a single load
// Keep in mind we don't use the ACTUAL mValue value because that's a register, but
// we need to store it in the stack frame for debugging purposes
auto loadedThis = mBfIRBuilder->CreateLoad(paramVar->mAddr/*, "this"*/);
mBfIRBuilder->ClearDebugLocation(loadedThis);
paramVar->mValue = loadedThis;
}
if ((wantsDIData) && (declareCall))
mBfIRBuilder->UpdateDebugLocation(declareCall);
if (paramVar->mIsSplat)
{
BfTypeUtils::SplatIterate([&](BfType* checkType) { argIdx++; }, paramVar->mResolvedType);
}
else if (paramVar->mIsLowered)
{
argIdx++;
BfTypeCode loweredTypeCode = BfTypeCode_None;
BfTypeCode loweredTypeCode2 = BfTypeCode_None;
paramVar->mResolvedType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2);
if (loweredTypeCode != BfTypeCode_None)
argIdx++;
}
2019-08-23 11:56:54 -07:00
else if (!paramVar->mResolvedType->IsValuelessType())
{
argIdx++;
}
}
if (wantsDIData)
{
BF_ASSERT(splatAddrIdx == (int)splatAddrValues.size());
}
}
for (int varIdx = 0; varIdx < (int)mCurMethodState->mLocals.size(); varIdx++)
{
auto paramVar = mCurMethodState->mLocals[varIdx];
// We don't need this because the debug func type records the type of the splat param, and then the same can be inferred from the first
// splat member. If the "splat" is valueless then we do create a placeholder, but further up in this method
// if (paramVar->mIsSplat)
// {
// if (wantsDIVariables)
// {
// auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(diFunction, paramVar->mName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mBfIRBuilder->DbgGetType(paramVar->mResolvedType));
// mBfIRBuilder->DbgInsertValueIntrinsic(mBfIRBuilder->CreateConst(BfTypeCode_Int64, 0), diVariable);
// }
// }
if (paramVar->mResolvedType->IsValuelessType())
{
if ((mBfIRBuilder->HasDebugLocation()) && (wantsDIVariables) && (mCompiler->mOptions.mToolsetType != BfToolsetType_GNU)) // DWARF chokes on this:
{
// Only add this placeholder if we don't have any values
auto diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType);
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope,
paramVar->mName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType);
mBfIRBuilder->DbgInsertValueIntrinsic(BfIRValue(), diVariable);
}
}
}
if (IsTargetingBeefBackend())
mCurMethodState->mCurScope->mValueScopeStart = mBfIRBuilder->CreateValueScopeStart();
if (methodState.mClosureState != NULL)
{
for (auto& constLocal : methodState.mClosureState->mConstLocals)
{
BfLocalVariable* localVar = new BfLocalVariable();
*localVar = constLocal;
AddLocalVariableDef(localVar, true);
}
}
bool wantsRemoveBody = false;
bool skipBody = false;
bool skipUpdateSrcPos = false;
bool skipEndChecks = false;
bool hasExternSpecifier = (methodDeclaration != NULL) && (methodDeclaration->mExternSpecifier != NULL) && (methodInstance->GetImportKind() != BfImportKind_Import_Dynamic);
2019-08-23 11:56:54 -07:00
auto propertyDeclaration = methodDef->GetPropertyDeclaration();
if ((methodDef != NULL) && (propertyDeclaration != NULL) && (propertyDeclaration->mExternSpecifier != NULL))
hasExternSpecifier = true;
// Allocate, clear, set classVData
if ((methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mIsStatic))
{
CreateStaticCtor();
}
else if ((mCurTypeInstance->IsBoxed()) && (methodDef->mMethodType != BfMethodType_Ctor) && (methodDef->mName != BF_METHODNAME_DYNAMICCAST) && (methodDef->mName != BF_METHODNAME_DYNAMICCAST_INTERFACE))
{
skipBody = true;
skipEndChecks = true;
if (HasCompiledOutput())
2020-04-28 07:30:45 -07:00
{
2019-08-23 11:56:54 -07:00
// Clear out DebugLoc - to mark the ".addr" code as part of prologue
mBfIRBuilder->ClearDebugLocation();
BfBoxedType* boxedType = (BfBoxedType*) mCurTypeInstance;
BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance();
2019-08-23 11:56:54 -07:00
PopulateType(innerType, BfPopulateType_DataAndMethods);
mBfIRBuilder->PopulateType(mCurTypeInstance);
BfGetMethodInstanceFlags flags = BfGetMethodInstanceFlag_None;
if (methodInstance->mIsForeignMethodDef)
{
flags = BfGetMethodInstanceFlag_ForeignMethodDef;
}
BfTypeVector methodGenericArguments;
if (methodInstance->mMethodInfoEx != NULL)
methodGenericArguments = methodInstance->mMethodInfoEx->mMethodGenericArguments;
auto innerMethodInstance = GetMethodInstance(innerType, methodDef, methodGenericArguments, flags, methodInstance->GetForeignType());
if (innerMethodInstance.mMethodInstance->IsSkipCall())
{
if (!methodInstance->mReturnType->IsValuelessType())
{
auto retVal = GetDefaultValue(methodInstance->mReturnType);
CreateReturn(retVal);
}
2020-04-28 07:30:45 -07:00
else
mBfIRBuilder->CreateRetVoid();
2019-08-23 11:56:54 -07:00
}
else
{
BF_ASSERT(innerMethodInstance.mMethodInstance->mMethodDef == methodDef);
SizedArray<BfIRValue, 8> innerParams;
BfExprEvaluator exprEvaluator(this);
if (!innerType->IsValuelessType())
{
BfIRValue thisValue = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, 1);
BfTypedValue innerVal(thisValue, innerType, true);
2019-10-09 16:16:01 -07:00
if (boxedType->IsBoxedStructPtr())
{
innerVal = LoadValue(innerVal);
innerVal = BfTypedValue(innerVal.mValue, innerType, true);
}
2019-08-23 11:56:54 -07:00
exprEvaluator.PushThis(NULL, innerVal, innerMethodInstance.mMethodInstance, innerParams);
}
for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++)
{
BfLocalVariable* localVar = mCurMethodState->mLocals[i];
BfTypedValue localVal = exprEvaluator.LoadLocal(localVar, true);
exprEvaluator.PushArg(localVal, innerParams);
}
if (!innerMethodInstance.mFunc)
{
BF_ASSERT(innerType->IsUnspecializedType());
}
else if (methodInstance->GetStructRetIdx() != -1)
2019-08-23 11:56:54 -07:00
{
mBfIRBuilder->PopulateType(methodInstance->mReturnType);
auto returnType = BfTypedValue(mBfIRBuilder->GetArgument(methodInstance->GetStructRetIdx()), methodInstance->mReturnType, true);
2019-08-23 11:56:54 -07:00
exprEvaluator.mReceivingValue = &returnType;
auto retVal = exprEvaluator.CreateCall(innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, true);
BF_ASSERT(exprEvaluator.mReceivingValue == NULL); // Ensure it was actually used
mBfIRBuilder->CreateRetVoid();
}
else
{
mBfIRBuilder->PopulateType(methodInstance->mReturnType);
auto retVal = exprEvaluator.CreateCall(innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, true);
if (mCurMethodInstance->mReturnType->IsValueType())
retVal = LoadValue(retVal);
CreateReturn(retVal.mValue);
}
}
}
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
}
else if (methodDef->mMethodType == BfMethodType_CtorClear)
{
SetIllegalSrcPos();
2019-08-23 11:56:54 -07:00
mBfIRBuilder->ClearDebugLocation();
PopulateType(mCurTypeInstance, BfPopulateType_Data);
auto thisVal = GetThis();
int prevSize = mContext->mBfObjectType->mInstSize;
int curSize = mCurTypeInstance->mInstSize;
if (curSize > prevSize)
{
auto int8PtrType = CreatePointerType(GetPrimitiveType(BfTypeCode_Int8));
auto int8PtrVal = mBfIRBuilder->CreateBitCast(thisVal.mValue, mBfIRBuilder->MapType(int8PtrType));
int8PtrVal = mBfIRBuilder->CreateInBoundsGEP(int8PtrVal, GetConstValue(prevSize));
mBfIRBuilder->CreateMemSet(int8PtrVal, GetConstValue8(0), GetConstValue(curSize - prevSize), GetConstValue(mCurTypeInstance->mInstAlign));
}
skipUpdateSrcPos = true;
}
else if (((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_CtorNoBody)) && (!hasExternSpecifier))
{
EmitCtorBody(skipBody);
}
else if ((methodDef->mMethodType == BfMethodType_Dtor) && (!hasExternSpecifier))
{
EmitDtorBody();
skipBody = true;
}
if ((!mCurTypeInstance->IsBoxed()) && (methodDeclaration != NULL) && (methodDeclaration->mHadYield) && (methodDef->mBody != NULL))
{
EmitIteratorBlock(skipBody);
}
if (methodDef->mName == BF_METHODNAME_MARKMEMBERS)
{
// We need to be able to mark deleted objects
mCurMethodState->mIgnoreObjectAccessCheck = true;
}
auto customAttributes = methodInstance->GetCustomAttributes();
if (customAttributes != NULL)
{
if (customAttributes->Contains(mCompiler->mDisableObjectAccessChecksAttributeTypeDef))
mCurMethodState->mIgnoreObjectAccessCheck = true;
if (customAttributes->Contains(mCompiler->mDisableChecksAttributeTypeDef))
mCurMethodState->mDisableChecks = true;
}
2019-08-23 11:56:54 -07:00
if ((methodDef->mMethodType == BfMethodType_CtorNoBody) && (!methodDef->mIsStatic) &&
((methodInstance->mChainType == BfMethodChainType_ChainHead) || (methodInstance->mChainType == BfMethodChainType_None)))
{
// We chain even non-default ctors to the default ctors
auto defaultCtor = methodInstance;
if (defaultCtor->mChainType == BfMethodChainType_ChainHead)
CallChainedMethods(defaultCtor, false);
}
auto bodyBlock = BfNodeDynCast<BfBlock>(methodDef->mBody);
if (methodDef->mBody == NULL)
{
if (methodDeclaration != NULL)
{
if (auto operatorDeclaration = BfNodeDynCast<BfOperatorDeclaration>(methodDeclaration))
{
if (operatorDeclaration->mIsConvOperator)
wantsRemoveBody = true;
}
}
bool isDllImport = false;
if (methodInstance->GetImportKind() == BfImportKind_Import_Static)
2019-08-23 11:56:54 -07:00
{
for (auto customAttr : methodInstance->GetCustomAttributes()->mAttributes)
{
if (customAttr.mType->mTypeDef->mFullName.ToString() == "System.ImportAttribute")
{
if (customAttr.mCtorArgs.size() == 1)
{
auto fileNameArg = customAttr.mCtorArgs[0];
int strNum = 0;
auto constant = mCurTypeInstance->mConstHolder->GetConstant(fileNameArg);
if (constant != NULL)
{
if (constant->IsNull())
continue; // Invalid
strNum = constant->mInt32;
}
else
{
strNum = GetStringPoolIdx(fileNameArg, mCurTypeInstance->mConstHolder);
}
if (!mImportFileNames.Contains(strNum))
mImportFileNames.Add(strNum);
}
}
}
//mImportFileNames
}
else if (methodInstance->GetImportKind() == BfImportKind_Import_Dynamic)
2019-08-23 11:56:54 -07:00
{
CreateDllImportMethod();
}
else if (((mCurTypeInstance->mTypeDef->mIsDelegate) || (mCurTypeInstance->mTypeDef->mIsFunction)) &&
(methodDef->mName == "Invoke") && (methodDef->mMethodType == BfMethodType_Normal))
{
if (mCurTypeInstance->mTypeDef->mIsFunction)
{
// Emit nothing
}
else
{
CreateDelegateInvokeMethod();
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
}
}
else if ((methodDef->mName == BF_METHODNAME_DYNAMICCAST) || (methodDef->mName == BF_METHODNAME_DYNAMICCAST_INTERFACE))
{
if (mCurTypeInstance->IsObject())
{
CreateDynamicCastMethod();
}
else
{
mBfIRBuilder->CreateRet(GetDefaultValue(methodInstance->mReturnType));
mCurMethodState->mHadReturn = true;
}
}
else if ((methodDef->mName == BF_METHODNAME_ENUM_HASFLAG) && (mCurTypeInstance->IsEnum()) && (!mCurTypeInstance->IsBoxed()))
{
BfIRValue ret;
if (mCurMethodState->mLocals[1]->mResolvedType != mCurTypeInstance)
{
// This can happen if we provide an invalid name
AssertErrorState();
ret = mBfIRBuilder->CreateRet(GetDefaultValue(GetPrimitiveType(BfTypeCode_Boolean)));
}
else
{
// Unfortunate DebugLoc shenanigans-
// Our params get removed if we don't have any DebugLocs, but we don't want to actually be able to step into this method,
// so we only set the loc on the CreateRet which gets inlined out
mBfIRBuilder->SaveDebugLocation();
mBfIRBuilder->ClearDebugLocation();
BfIRValue fromBool;
if (!mCurTypeInstance->IsTypedPrimitive())
{
fromBool = GetDefaultValue(methodInstance->mReturnType);
}
else
{
auto andResult = mBfIRBuilder->CreateAnd(mCurMethodState->mLocals[0]->mValue, mCurMethodState->mLocals[1]->mValue);
auto toBool = mBfIRBuilder->CreateCmpNE(andResult, GetDefaultValue(mCurMethodState->mLocals[0]->mResolvedType));
fromBool = mBfIRBuilder->CreateNumericCast(toBool, false, BfTypeCode_Boolean);
}
mBfIRBuilder->RestoreDebugLocation();
ret = mBfIRBuilder->CreateRet(fromBool);
//ExtendLocalLifetimes(0);
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
}
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
}
else if (((methodDef->mName == BF_METHODNAME_ENUM_GETUNDERLYINGREF) || (methodDef->mName == BF_METHODNAME_ENUM_GETUNDERLYING)) &&
(mCurTypeInstance->IsEnum()) && (!mCurTypeInstance->IsBoxed()))
{
BfIRValue ret;
// Unfortunate DebugLoc shenanigans-
// Our params get removed if we don't have any DebugLocs, but we don't want to actually be able to step into this method,
// so we only set the loc on the CreateRet which gets inlined out
mBfIRBuilder->SaveDebugLocation();
mBfIRBuilder->ClearDebugLocation();
BfIRValue fromBool;
mBfIRBuilder->RestoreDebugLocation();
ret = mBfIRBuilder->CreateRet(GetThis().mValue);
//ExtendLocalLifetimes(0);
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
}
else if ((mCurTypeInstance->IsEnum()) && (!mCurTypeInstance->IsBoxed()) && (methodDef->mName == BF_METHODNAME_TO_STRING))
{
auto enumType = ResolveTypeDef(mCompiler->mEnumTypeDef);
if (HasCompiledOutput())
{
EmitEnumToStringBody();
}
mBfIRBuilder->CreateRetVoid();
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
}
else if ((mCurTypeInstance->IsTuple()) && (!mCurTypeInstance->IsBoxed()) && (methodDef->mName == BF_METHODNAME_TO_STRING))
{
auto enumType = ResolveTypeDef(mCompiler->mEnumTypeDef);
if (HasCompiledOutput())
{
EmitTupleToStringBody();
}
mBfIRBuilder->CreateRetVoid();
mCurMethodState->SetHadReturn(true);
mCurMethodState->mLeftBlockUncond = true;
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
}
else if (methodDef->mName == BF_METHODNAME_DEFAULT_EQUALS)
{
CreateValueTypeEqualsMethod();
skipBody = true;
skipEndChecks = true;
}
else if ((methodDef->mName == BF_METHODNAME_EQUALS) && (typeDef == mCompiler->mValueTypeTypeDef))
{
CreateValueTypeEqualsMethod();
skipBody = true;
skipEndChecks = true;
}
2020-05-08 14:33:28 -07:00
else
{
2019-08-23 11:56:54 -07:00
auto propertyDeclaration = methodDef->GetPropertyDeclaration();
if ((propertyDeclaration != NULL) && (!typeDef->HasAutoProperty(propertyDeclaration)))
{
if ((!mCurTypeInstance->IsInterface()) && (!hasExternSpecifier))
Fail("Body expected", methodDef->mBody);
2020-05-08 14:33:28 -07:00
}
2019-08-23 11:56:54 -07:00
else if (methodDef->mMethodType == BfMethodType_PropertyGetter)
{
if (methodInstance->mReturnType->IsValuelessType())
{
mBfIRBuilder->CreateRetVoid();
}
else if (HasCompiledOutput())
{
String autoPropName = typeDef->GetAutoPropertyName(propertyDeclaration);
BfFieldInstance* fieldInstance = GetFieldByName(mCurTypeInstance, autoPropName);
BfFieldDef* fieldDef = NULL;
if (fieldInstance != NULL)
fieldDef = fieldInstance->GetFieldDef();
BfType* retType = methodInstance->mReturnType;
if (retType->IsRef())
retType = retType->GetUnderlyingType();
if ((fieldInstance != NULL) && (fieldInstance->GetResolvedType() == retType) &&
((fieldInstance->mDataIdx >= 0) || (fieldDef->IsNonConstStatic())))
{
BfTypedValue lookupValue;
if (fieldDef->IsNonConstStatic())
lookupValue = ReferenceStaticField(fieldInstance);
else if (mCurTypeInstance->IsObject())
lookupValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(GetThis().mValue, 0, fieldInstance->mDataIdx), fieldInstance->mResolvedType, true);
else
2020-05-19 13:19:26 -07:00
lookupValue = ExtractValue(GetThis(), fieldInstance, fieldInstance->mDataIdx);
2020-05-08 14:33:28 -07:00
lookupValue = LoadOrAggregateValue(lookupValue);
2020-04-10 07:53:56 -07:00
CreateReturn(lookupValue.mValue);
2019-08-23 11:56:54 -07:00
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
}
else
{
// This can happen if we have two properties with the same name but different types
2020-04-10 07:53:56 -07:00
CreateReturn(GetDefaultValue(mCurMethodInstance->mReturnType));
EmitDefaultReturn();
2019-08-23 11:56:54 -07:00
}
}
else
{
CreateReturn(GetDefaultValue(mCurMethodInstance->mReturnType));
EmitDefaultReturn();
2019-08-23 11:56:54 -07:00
}
mCurMethodState->SetHadReturn(true);
}
else if (methodDef->mMethodType == BfMethodType_PropertySetter)
{
2020-05-08 15:43:27 -07:00
if ((!methodDef->mIsMutating) && (mCurTypeInstance->IsValueType()))
2020-05-08 14:33:28 -07:00
{
Fail("Auto-setter must be marked as 'mut'", methodDef->GetRefNode(), true);
}
else if (!mCompiler->IsAutocomplete())
2019-08-23 11:56:54 -07:00
{
String autoPropName = typeDef->GetAutoPropertyName(propertyDeclaration);
BfFieldInstance* fieldInstance = GetFieldByName(mCurTypeInstance, autoPropName);
auto fieldDef = fieldInstance->GetFieldDef();
auto& lastParam = mCurMethodState->mLocals.back();
if ((fieldInstance != NULL) && (fieldInstance->GetResolvedType() == lastParam->mResolvedType) &&
((fieldInstance->mDataIdx >= 0) || (fieldDef->IsNonConstStatic())))
{
BfIRValue lookupAddr;
if (fieldDef->IsNonConstStatic())
lookupAddr = ReferenceStaticField(fieldInstance).mValue;
else
{
auto thisValue = GetThis();
if (thisValue.IsSplat())
{
BFMODULE_FATAL(this, "Should not happen");
2019-08-23 11:56:54 -07:00
}
if (mCurTypeInstance->IsObject())
2020-05-08 14:33:28 -07:00
thisValue = LoadValue(thisValue);
lookupAddr = mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, fieldInstance->mDataIdx);
2019-08-23 11:56:54 -07:00
}
2020-04-10 07:53:56 -07:00
BfExprEvaluator exprEvaluator(this);
auto localVal = exprEvaluator.LoadLocal(lastParam);
2020-05-08 14:33:28 -07:00
localVal = LoadOrAggregateValue(localVal);
2020-04-10 07:53:56 -07:00
mBfIRBuilder->CreateStore(localVal.mValue, lookupAddr);
2019-08-23 11:56:54 -07:00
}
else if (!fieldInstance->mResolvedType->IsValuelessType())
{
// This can happen if we have two properties with the same name but different types
AssertErrorState();
}
}
}
2020-05-08 14:33:28 -07:00
else if ((methodDef->mName == BF_METHODNAME_MARKMEMBERS) || (methodDef->mName == BF_METHODNAME_MARKMEMBERS_STATIC))
{
if (mCompiler->mOptions.mEnableRealtimeLeakCheck)
{
if (HasCompiledOutput())
EmitGCMarkMembers();
}
else if (!mCurTypeInstance->IsObject())
{
}
}
else if (methodDef->mName == BF_METHODNAME_FIND_TLS_MEMBERS)
{
if (mCompiler->mOptions.mEnableRealtimeLeakCheck)
{
if (HasCompiledOutput())
EmitGCFindTLSMembers();
}
}
2019-08-23 11:56:54 -07:00
}
2020-05-08 14:33:28 -07:00
}
else if (!skipBody)
{
bool isEmptyBodied = BfNodeDynCast<BfTokenNode>(methodDef->mBody) != NULL;
2019-08-23 11:56:54 -07:00
if ((!mCurMethodInstance->mReturnType->IsValuelessType()) && (!isEmptyBodied))
{
mBfIRBuilder->PopulateType(mCurMethodInstance->mReturnType);
if (mCurMethodInstance->GetStructRetIdx() != -1)
2019-08-23 11:56:54 -07:00
{
auto ptrType = CreatePointerType(mCurMethodInstance->mReturnType);
auto allocaInst = AllocLocalVariable(ptrType, "__return.addr", false);
auto storeInst = mBfIRBuilder->CreateStore(mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx()), allocaInst);
2019-08-23 11:56:54 -07:00
mBfIRBuilder->ClearDebugLocation(storeInst);
mCurMethodState->mRetValAddr = allocaInst;
}
else
{
auto allocaInst = AllocLocalVariable(mCurMethodInstance->mReturnType, "__return", false);
mCurMethodState->mRetVal = BfTypedValue(allocaInst, mCurMethodInstance->mReturnType, true);
}
}
if (methodDef->mMethodType == BfMethodType_CtorCalcAppend)
{
mBfIRBuilder->CreateStore(GetConstValue(0), mCurMethodState->mRetVal.mValue);
BfGetSymbolReferenceKind prevSymbolKind;
BfAutoComplete* prevAutoComplete;
if (mCompiler->mResolvePassData != NULL)
{
prevSymbolKind = mCompiler->mResolvePassData->mGetSymbolReferenceKind;
prevAutoComplete = mCompiler->mResolvePassData->mAutoComplete;
mCompiler->mResolvePassData->mGetSymbolReferenceKind = BfGetSymbolReferenceKind_None;
}
EmitCtorCalcAppend();
if (mCompiler->mResolvePassData != NULL)
{
mCompiler->mResolvePassData->mGetSymbolReferenceKind = prevSymbolKind;
mCompiler->mResolvePassData->mAutoComplete = prevAutoComplete;
}
}
else if (!isEmptyBodied)
{
if (!mCurMethodState->mIRExitBlock)
mCurMethodState->mIRExitBlock = mBfIRBuilder->CreateBlock("exit", true);
bool isExpressionBody = false;
if (methodDeclaration != NULL)
{
if (methodDeclaration->mFatArrowToken != NULL)
isExpressionBody = true;
}
2019-09-30 12:24:02 -07:00
else if (auto propertyDeclaration = methodDef->GetPropertyDeclaration())
2020-05-08 11:11:01 -07:00
{
auto propertyMethodDeclaration = methodDef->GetPropertyMethodDeclaration();
if ((propertyMethodDeclaration != NULL) && (propertyMethodDeclaration->mFatArrowToken != NULL))
isExpressionBody = true;
2019-09-30 12:24:02 -07:00
if (auto propBodyExpr = BfNodeDynCast<BfPropertyBodyExpression>(propertyDeclaration->mDefinitionBlock))
{
isExpressionBody = true;
}
2019-08-23 11:56:54 -07:00
}
else
{
if (auto block = BfNodeDynCast<BfBlock>(methodDef->mBody))
{
if (!block->mChildArr.IsEmpty())
{
if (auto exprStmt = BfNodeDynCast<BfExpressionStatement>(block->mChildArr.GetLast()))
{
if (exprStmt->mTrailingSemicolon == NULL)
isExpressionBody = true;
}
}
}
else
isExpressionBody = true;
}
if (auto fieldDtorBody = BfNodeDynCast<BfFieldDtorDeclaration>(methodDef->mBody))
{
while (fieldDtorBody != NULL)
{
VisitEmbeddedStatement(fieldDtorBody->mBody);
fieldDtorBody = fieldDtorBody->mNextFieldDtor;
}
}
else if (!isExpressionBody)
{
auto bodyBlock = BfNodeDynCast<BfBlock>(methodDef->mBody);
mCurMethodState->mHeadScope.mAstBlock = bodyBlock;
mCurMethodState->mHeadScope.mCloseNode = bodyBlock->mCloseBrace;
VisitCodeBlock(bodyBlock);
}
else if (auto expressionBody = BfNodeDynCast<BfExpression>(methodDef->mBody))
{
2020-05-08 11:11:01 -07:00
// if ((methodDef->mMethodType != BfMethodType_Normal) && (propertyDeclaration == NULL))
// {
// BF_ASSERT(methodDeclaration->mFatArrowToken != NULL);
// Fail("Only normal methods can have expression bodies", methodDeclaration->mFatArrowToken);
// }
2019-08-23 11:56:54 -07:00
auto expectingType = mCurMethodInstance->mReturnType;
2020-05-08 11:11:01 -07:00
// What was this error for?
2019-08-23 11:56:54 -07:00
// if ((expectingType->IsVoid()) && (IsInSpecializedSection()))
// {
// Warn(0, "Using a 'void' return with an expression-bodied method isn't needed. Consider removing '=>' token", methodDeclaration->mFatArrowToken);
// }
2020-05-08 11:11:01 -07:00
BfEvalExprFlags exprEvalFlags = BfEvalExprFlags_None;
if (expectingType->IsVoid())
{
exprEvalFlags = BfEvalExprFlags_NoCast;
bool wasReturnGenericParam = false;
if ((mCurMethodState->mClosureState != NULL) && (mCurMethodState->mClosureState->mReturnType != NULL))
{
wasReturnGenericParam = mCurMethodState->mClosureState->mReturnType->IsGenericParam();
}
else
{
auto unspecializedMethodInstance = GetUnspecializedMethodInstance(mCurMethodInstance);
if ((unspecializedMethodInstance != NULL) && (unspecializedMethodInstance->mReturnType->IsGenericParam()))
wasReturnGenericParam = true;
}
// If we the void return was from a generic specialization, allow us to return a void result,
// otherwise treat expression as though it must be a statement
bool isStatement = expressionBody->VerifyIsStatement(mCompiler->mPassInstance, wasReturnGenericParam);
if (isStatement)
expectingType = NULL;
}
2019-08-23 11:56:54 -07:00
UpdateSrcPos(expressionBody);
2020-05-08 11:11:01 -07:00
auto retVal = CreateValueFromExpression(expressionBody, expectingType, exprEvalFlags);
2019-08-23 11:56:54 -07:00
if ((retVal) && (expectingType != NULL))
{
mCurMethodState->mHadReturn = true;
retVal = LoadValue(retVal);
EmitReturn(retVal.mValue);
}
}
}
}
BF_ASSERT(mCurMethodState->mCurScope == &mCurMethodState->mHeadScope);
if (skipUpdateSrcPos)
{
// Skip
}
else if ((bodyBlock != NULL) && (bodyBlock->mCloseBrace != NULL))
UpdateSrcPos(bodyBlock->mCloseBrace);
else if (methodDef->mBody != NULL)
UpdateSrcPos(methodDef->mBody);
else if (methodDeclaration != NULL)
UpdateSrcPos(methodDeclaration);
else if (methodDef->mDeclaringType->mTypeDeclaration != NULL)
UpdateSrcPos(methodDef->mDeclaringType->mTypeDeclaration);
else
UseDefaultSrcPos();
if (methodDef->mMethodType == BfMethodType_CtorCalcAppend)
{
if (mCurMethodState->mRetVal)
{
mCurMethodState->SetHadReturn(true);
auto retVal = mBfIRBuilder->CreateLoad(mCurMethodState->mRetVal.mValue);
mBfIRBuilder->CreateRet(retVal);
}
else
AssertErrorState();
}
if (!mCurMethodState->mHadReturn)
{
// Clear off the stackallocs that have occurred after a scopeData break
EmitDeferredScopeCalls(false, &mCurMethodState->mHeadScope, mCurMethodState->mIRExitBlock);
}
if (mCurMethodState->mIRExitBlock)
{
for (auto preExitBlock : mCurMethodState->mHeadScope.mAtEndBlocks)
mBfIRBuilder->MoveBlockToEnd(preExitBlock);
mBfIRBuilder->MoveBlockToEnd(mCurMethodState->mIRExitBlock);
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRExitBlock);
if (!mCurMethodState->mDIRetVal)
CreateDIRetVal();
}
for (auto localVar : mCurMethodState->mLocals)
{
if ((skipEndChecks) || (bodyBlock == NULL))
break;
LocalVariableDone(localVar, true);
}
if (mCurMethodState->mIRExitBlock)
{
if ((mCurMethodState->mRetVal) && (mCurMethodInstance->GetStructRetIdx() == -1))
2019-08-23 11:56:54 -07:00
{
auto loadedVal = mBfIRBuilder->CreateLoad(mCurMethodState->mRetVal.mValue);
CreateReturn(loadedVal);
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
if (mCurMethodState->mDIRetVal)
mBfIRBuilder->DbgLifetimeEnd(mCurMethodState->mDIRetVal);
}
else
{
// Have something on the last line to step onto
if (mHasFullDebugInfo)
{
if ((bodyBlock != NULL) && (bodyBlock->mCloseBrace != NULL))
UpdateSrcPos(bodyBlock->mCloseBrace);
EmitEnsureInstructionAt();
if ((irParamCount == 0) && (!IsTargetingBeefBackend()) && (mCompiler->mOptions.mAllowHotSwapping))
{
// This may be a case where we only emit 4 bytes, whereas we need 5 for a hot replace jump
mBfIRBuilder->EnsureFunctionPatchable();
}
}
mBfIRBuilder->CreateRetVoid();
EmitLifetimeEnds(&mCurMethodState->mHeadScope);
if (mCurMethodState->mDIRetVal)
mBfIRBuilder->DbgLifetimeEnd(mCurMethodState->mDIRetVal);
}
}
if ((mCurMethodInstance->mReturnType == NULL) || (mCurMethodInstance->mReturnType->IsValuelessType()))
{
if ((!mCurMethodState->mHadReturn) && (!mCurMethodState->mIRExitBlock))
{
if ((irParamCount == 0) && (!IsTargetingBeefBackend()) && (mCompiler->mOptions.mAllowHotSwapping))
{
// This may be a case where we only emit 4 bytes, whereas we need 5 for a hot replace jump
mBfIRBuilder->EnsureFunctionPatchable();
}
mBfIRBuilder->CreateRetVoid();
}
}
else
{
if (!mCurMethodState->mHadReturn)
{
auto refNode = mCurMethodInstance->mMethodDef->GetRefNode();
if (bodyBlock != NULL)
{
if (bodyBlock->mCloseBrace != NULL)
{
BfAstNode* target = bodyBlock->mCloseBrace;
Fail("Method must return value", target);
}
else
{
// It's possible to not have a closing brace if the method ends in an EOF
AssertErrorState();
}
}
}
}
// Move 'init' into 'entry'
mBfIRBuilder->MergeBlockDown(mCurMethodState->mIRInitBlock, mCurMethodState->mIREntryBlock);
mBfIRBuilder->MergeBlockDown(mCurMethodState->mIRHeadBlock, mCurMethodState->mIREntryBlock);
if ((mCurMethodInstance->mIsUnspecialized) /*|| (typeDef->mIsFunction)*/ || (mCurTypeInstance->IsUnspecializedType()))
{
// Don't keep instructions for unspecialized types
mBfIRBuilder->Func_DeleteBody(mCurMethodInstance->mIRFunction);
}
if ((hasExternSpecifier) && (!skipBody))
{
// If we hot swap, we want to make sure at least one method refers to this extern method so it gets pulled in
// incase it gets called later by some hot-loaded coded
if (mCompiler->mOptions.mAllowHotSwapping)
CreateFakeCallerMethod(mangledName);
mBfIRBuilder->Func_DeleteBody(mCurMethodInstance->mIRFunction);
}
else if ((methodInstance->GetImportKind() == BfImportKind_Import_Dynamic) && (!mCompiler->IsHotCompile()))
2019-08-23 11:56:54 -07:00
{
// We only need the DLL stub when we may be hot swapping
mBfIRBuilder->Func_DeleteBody(mCurMethodInstance->mIRFunction);
}
else if (wantsRemoveBody)
mBfIRBuilder->Func_DeleteBody(mCurMethodInstance->mIRFunction);
// We don't want to hold on to pointers to LLVMFunctions of unspecialized types.
// This allows us to delete the mScratchModule LLVM module without rebuilding all
// unspecialized types
if ((mCurTypeInstance->IsUnspecializedType()) || (mCurTypeInstance->IsInterface()))
{
BfLogSysM("ProcessMethod Clearing IRFunction: %p\n", methodInstance);
methodInstance->mIRFunction = BfIRFunction();
}
CheckAddFailType();
if (mHadBuildError)
prevHadBuildError.CancelRestore();
if (mHadBuildWarning)
prevHadWarning.CancelRestore();
if (!methodDef->mIsLocalMethod)
ProcessMethod_ProcessDeferredLocals();
mBfIRBuilder->SetActiveFunction(prevActiveFunction);
if (methodState.mHotDataReferenceBuilder != NULL)
{
AddHotDataReferences(&hotDataReferenceBuilder);
}
else
{
if ((methodInstance->mHotMethod != NULL) && (!methodInstance->mIsReified))
{
// Remove the hot method data
auto hotMethod = methodInstance->mHotMethod;
auto prevMethod = hotMethod->mPrevVersion;
if (prevMethod != NULL)
{
// If there's a prev method then pull its data into the main HotMethod.
// This, in effect, removes the 'hotMethod' data and rebases 'prevMethod'
// over to the main HotMethod definition to keep the HotMethod address
// invariant
for (auto ref : hotMethod->mReferences)
ref->Deref();
hotMethod->mReferences.Clear();
BF_ASSERT(prevMethod->mRefCount == 1);
hotMethod->mReferences = prevMethod->mReferences;
if (hotMethod->mSrcTypeVersion != NULL)
hotMethod->mSrcTypeVersion->Deref();
hotMethod->mSrcTypeVersion = prevMethod->mSrcTypeVersion;
prevMethod->mSrcTypeVersion = NULL;
prevMethod->mReferences.Clear();
hotMethod->mPrevVersion = prevMethod->mPrevVersion;
prevMethod->mPrevVersion = NULL;
prevMethod->Deref();
}
hotMethod->Deref();
methodInstance->mHotMethod = NULL;
}
}
}
String BfModule::GetLocalMethodName(const StringImpl& baseName, BfAstNode* anchorNode, BfMethodState* declMethodState, BfMixinState* declMixinState)
{
String name;
bool found = false;
auto checkMethodState = mCurMethodState;
while (checkMethodState != NULL)
{
if (checkMethodState->mClosureState == NULL)
break;
if (checkMethodState->mClosureState->mClosureMethodDef != NULL)
{
found = true;
name += checkMethodState->mClosureState->mClosureMethodDef->mName;
break;
}
checkMethodState = checkMethodState->mPrevMethodState;
}
if (!found)
name += mCurMethodInstance->mMethodDef->mName;
int prevSepPos = (int)name.LastIndexOf('$');
if (prevSepPos != -1)
{
name.RemoveToEnd(prevSepPos);
}
name += "@";
name += baseName;
HashContext hashCtx;
if (anchorNode != NULL)
{
hashCtx.Mixin(anchorNode->GetStartCharId());
}
//
{
auto checkMethodState = declMethodState;
auto checkMixinState = declMixinState;
while (checkMethodState != NULL)
{
if (checkMixinState != NULL)
{
hashCtx.Mixin(checkMixinState->mSource->GetStartCharId());
}
checkMethodState = checkMethodState->mPrevMethodState;
if (checkMethodState != NULL)
checkMixinState = checkMethodState->mMixinState;
}
}
auto rootMethodState = mCurMethodState->GetRootMethodState();
if (rootMethodState->mMethodInstance->mMethodInfoEx != NULL)
{
for (auto methodGenericArg : rootMethodState->mMethodInstance->mMethodInfoEx->mMethodGenericArguments)
{
StringT<128> genericTypeName;
BfMangler::Mangle(genericTypeName, mCompiler->GetMangleKind(), methodGenericArg);
hashCtx.MixinStr(genericTypeName);
}
}
uint64 hashVal = hashCtx.Finish64();
name += "$";
name += BfTypeUtils::HashEncode64(hashVal);
return name;
}
BfMethodDef* BfModule::GetLocalMethodDef(BfLocalMethod* localMethod)
{
if (localMethod->mMethodDef != NULL)
return localMethod->mMethodDef;
auto rootMethodState = mCurMethodState->GetRootMethodState();
auto declMethodState = localMethod->mDeclMethodState;
auto declMixinState = localMethod->mDeclMixinState;
auto typeInst = mCurTypeInstance;
BfAstNode* body = NULL;
BfAstNode* anchorNode = NULL;
auto _AllocDirectTypeRef = [&](BfType* type)
{
BfDirectTypeReference* directTypeRef = localMethod->mDirectTypeRefs.Alloc();
directTypeRef->Init(type);
return directTypeRef;
};
auto methodDeclaration = localMethod->mMethodDeclaration;
if (methodDeclaration != NULL)
{
body = methodDeclaration->mBody;
anchorNode = methodDeclaration->mOpenParen;
}
else
{
body = localMethod->mLambdaBindExpr->mBody;
anchorNode = localMethod->mLambdaBindExpr->mFatArrowToken;
}
BF_ASSERT(rootMethodState->mCurLocalVarId >= 0);
int methodLocalIdx = rootMethodState->mCurLocalVarId++;
BfMethodDef* methodDef;
BfMethodDef* outerMethodDef = NULL;
if (localMethod->mOuterLocalMethod != NULL)
outerMethodDef = localMethod->mOuterLocalMethod->mMethodDef;
else
outerMethodDef = rootMethodState->mMethodInstance->mMethodDef;
2019-08-23 11:56:54 -07:00
if (methodDeclaration != NULL)
{
BfDefBuilder defBuilder(mCompiler->mSystem);
defBuilder.mPassInstance = mCompiler->mPassInstance;
defBuilder.mCurTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType;
2019-08-23 11:56:54 -07:00
methodDef = defBuilder.CreateMethodDef(methodDeclaration, outerMethodDef);
}
else
{
auto invokeMethod = localMethod->mLambdaInvokeMethodInstance;
methodDef = new BfMethodDef();
methodDef->mName = localMethod->mMethodName;
methodDef->mDeclaringType = mCurMethodInstance->mMethodDef->mDeclaringType;
methodDef->mReturnTypeRef = _AllocDirectTypeRef(invokeMethod->mReturnType);
methodDef->mBody = body;
for (int paramIdx = 0; paramIdx < invokeMethod->GetParamCount(); paramIdx++)
{
auto paramType = invokeMethod->GetParamType(paramIdx);
String paramName;
if (paramIdx < (int)localMethod->mLambdaBindExpr->mParams.size())
paramName = localMethod->mLambdaBindExpr->mParams[paramIdx]->ToString();
else
paramName = invokeMethod->GetParamName(paramIdx);
auto paramDef = new BfParameterDef();
paramDef->mTypeRef = _AllocDirectTypeRef(paramType);
paramDef->mName = paramName;
methodDef->mParams.Add(paramDef);
}
if (outerMethodDef != NULL)
{
for (auto genericParam : outerMethodDef->mGenericParams)
{
auto* copiedGenericParamDef = new BfGenericParamDef();
*copiedGenericParamDef = *genericParam;
methodDef->mGenericParams.Add(copiedGenericParamDef);
}
}
2019-08-23 11:56:54 -07:00
}
methodDef->mIdx = ~methodLocalIdx;
BF_ASSERT(!localMethod->mExpectedFullName.IsEmpty());
methodDef->mName = localMethod->mExpectedFullName;
// methodDef->mName = GetLocalMethodName(methodDef->mName, anchorNode, declMethodState, declMixinState);
//
// if (!localMethod->mExpectedFullName.IsEmpty())
// BF_ASSERT(methodDef->mName == localMethod->mExpectedFullName);
methodDef->mIsStatic = true;
methodDef->mIsLocalMethod = true;
methodDef->mIsVirtual = false;
localMethod->mMethodDef = methodDef;
auto methodInstanceGroup = new BfMethodInstanceGroup();
localMethod->mMethodInstanceGroup = methodInstanceGroup;
methodInstanceGroup->mMethodIdx = -1;
methodInstanceGroup->mOwner = mCurTypeInstance;
methodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_AlwaysInclude;
if ((declMethodState->mMixinState != NULL) && (declMethodState->mMixinState->mTarget))
{
methodInstanceGroup->mOwner = declMethodState->mMixinState->mTarget.mType->ToTypeInstance();
BF_ASSERT(methodInstanceGroup->mOwner != NULL);
}
if ((mCompiler->mResolvePassData != NULL) && (methodDeclaration != NULL))
{
mCompiler->mResolvePassData->HandleLocalReference(methodDeclaration->mNameNode, typeInst->mTypeDef, rootMethodState->mMethodInstance->mMethodDef, methodLocalIdx);
auto autoComplete = mCompiler->mResolvePassData->mAutoComplete;
if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(methodDeclaration->mNameNode)))
{
autoComplete->SetDefinitionLocation(methodDeclaration->mNameNode);
autoComplete->mInsertStartIdx = methodDeclaration->mNameNode->GetSrcStart();
autoComplete->mInsertEndIdx = methodDeclaration->mNameNode->GetSrcEnd();
autoComplete->mDefType = typeInst->mTypeDef;
autoComplete->mDefMethod = rootMethodState->mMethodInstance->mMethodDef;
autoComplete->mReplaceLocalId = methodLocalIdx;
}
}
return localMethod->mMethodDef;
}
BfModuleMethodInstance BfModule::GetLocalMethodInstance(BfLocalMethod* localMethod, const BfTypeVector& methodGenericArguments, BfMethodInstance* methodInstance, bool force)
{
BP_ZONE_F("GetLocalMethodInstance %s", localMethod->mMethodName.c_str());
BfLogSysM("GetLocalMethodInstance %p\n", localMethod);
auto rootMethodState = mCurMethodState->GetRootMethodState();
auto declMethodState = localMethod->mDeclMethodState;
auto declMixinState = localMethod->mDeclMixinState;
auto callerMethodState = mCurMethodState;
auto typeInst = mCurTypeInstance;
if (mCurMethodState->mMixinState != NULL)
{
typeInst = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner();
}
2019-08-23 11:56:54 -07:00
auto methodDef = GetLocalMethodDef(localMethod);
BfAstNode* body = NULL;
auto methodDeclaration = localMethod->mMethodDeclaration;
if (methodDeclaration != NULL)
{
body = methodDeclaration->mBody;
}
else
{
body = localMethod->mLambdaBindExpr->mBody;
}
bool hadConcreteInterfaceGenericArgument = false;
BfTypeVector sanitizedMethodGenericArguments;
// Ignore the outermost method's generic method arguments for the purpose of determining if we are the 'default' (ie: unspecialized)
// version of this method for this pass through the outermost method
int dependentGenericStartIdx = 0;
if (rootMethodState->mMethodInstance->mMethodInfoEx != NULL)
dependentGenericStartIdx = (int)rootMethodState->mMethodInstance->mMethodInfoEx->mMethodGenericArguments.size();
BfMethodInstance* outerMethodInstance = mCurMethodInstance;
if (methodGenericArguments.size() == 0)
{
if (rootMethodState->mMethodInstance->mMethodInfoEx != NULL)
sanitizedMethodGenericArguments = rootMethodState->mMethodInstance->mMethodInfoEx->mMethodGenericArguments;
}
else
{
int genericArgIdx = 0;
// for (; genericArgIdx < (int)outerMethodInstance->mMethodGenericArguments.size(); genericArgIdx++)
// {
// BF_ASSERT(methodGenericArguments[genericArgIdx] == outerMethodInstance->mMethodGenericArguments[genericArgIdx]);
// sanitizedMethodGenericArguments.push_back(methodGenericArguments[genericArgIdx]);
// }
for (; genericArgIdx < (int)methodGenericArguments.size(); genericArgIdx++)
{
auto genericArgType = methodGenericArguments[genericArgIdx];
BF_ASSERT(!genericArgType->IsRef());
if (genericArgType->IsConcreteInterfaceType())
{
hadConcreteInterfaceGenericArgument = true;
auto concreteInterfaceType = (BfConcreteInterfaceType*)genericArgType;
genericArgType = concreteInterfaceType->mInterface;
}
sanitizedMethodGenericArguments.push_back(genericArgType);
}
}
bool wantPrematureMethodInstance = false;
if (!force)
{
wantPrematureMethodInstance = (callerMethodState->mClosureState != NULL) && (callerMethodState->mClosureState->mCaptureVisitingBody);
}
auto methodInstGroup = localMethod->mMethodInstanceGroup;
bool isDefaultPass = true;
BfTypeVector lookupMethodGenericArguments;
for (int genericArgIdx = dependentGenericStartIdx; genericArgIdx < (int)methodGenericArguments.size(); genericArgIdx++)
{
auto genericArgType = methodGenericArguments[genericArgIdx];
BF_ASSERT(!genericArgType->IsRef());
lookupMethodGenericArguments.Add(genericArgType);
if (genericArgType->IsGenericParam())
{
auto genericParam = (BfGenericParamType*)genericArgType;
if ((genericParam->mGenericParamKind != BfGenericParamKind_Method) || (genericParam->mGenericParamIdx != genericArgIdx))
isDefaultPass = false;
}
else
{
isDefaultPass = false;
}
}
bool hadExistingMethodInstance = false;
if (methodInstance != NULL)
{
hadExistingMethodInstance = true;
}
else
{
if (isDefaultPass)
{
methodInstance = methodInstGroup->mDefault;
if (methodInstance != NULL)
{
hadExistingMethodInstance = true;
}
else
{
methodInstance = new BfMethodInstance();
methodInstGroup->mDefault = methodInstance;
}
}
else
{
BF_ASSERT(sanitizedMethodGenericArguments.size() != 0);
if (methodInstGroup->mMethodSpecializationMap == NULL)
methodInstGroup->mMethodSpecializationMap = new BfMethodInstanceGroup::MapType();
BfMethodInstance** methodInstancePtr = NULL;
if (!methodInstGroup->mMethodSpecializationMap->TryAdd(lookupMethodGenericArguments, NULL, &methodInstancePtr))
{
methodInstance = *methodInstancePtr;
hadExistingMethodInstance = true;
}
else
{
methodInstance = new BfMethodInstance();
*methodInstancePtr = methodInstance;
}
}
}
if (hadExistingMethodInstance)
{
if ((!methodInstance->mDisallowCalling) ||
((wantPrematureMethodInstance) && (methodInstance->mDisallowCalling)))
return BfModuleMethodInstance(methodInstance);
BF_ASSERT((methodInstance->mMethodInfoEx == NULL) || (methodInstance->mMethodInfoEx->mClosureInstanceInfo->mCaptureClosureState == NULL));
}
else
{
auto methodInfoEx = methodInstance->GetMethodInfoEx();
methodInfoEx->mClosureInstanceInfo = new BfClosureInstanceInfo();
methodInfoEx->mClosureInstanceInfo->mLocalMethod = localMethod;
methodInstance->mIsReified = mCurMethodInstance->mIsReified;
methodInstance->mIsAutocompleteMethod = mCurMethodInstance->mIsAutocompleteMethod;
methodInstance->mMethodDef = methodDef;
methodInstance->mMethodInstanceGroup = localMethod->mMethodInstanceGroup;
if (hadConcreteInterfaceGenericArgument)
methodInstance->mIgnoreBody = true;
methodInfoEx->mMethodGenericArguments = sanitizedMethodGenericArguments;
for (auto genericArg : sanitizedMethodGenericArguments)
{
if (genericArg->IsPrimitiveType())
genericArg = GetWrappedStructType(genericArg);
AddDependency(genericArg, typeInst, BfDependencyMap::DependencyFlag_MethodGenericArg);
}
for (int genericParamIdx = (int)sanitizedMethodGenericArguments.size(); genericParamIdx < (int)methodDef->mGenericParams.size(); genericParamIdx++)
{
auto genericParamType = GetGenericParamType(BfGenericParamKind_Method, genericParamIdx);
methodInfoEx->mMethodGenericArguments.push_back(genericParamType);
}
SetupMethodIdHash(methodInstance);
}
auto _SetupMethodInstance = [&]()
{
BF_ASSERT(methodInstance->GetNumGenericParams() == 0);
// Generic constraints
for (int genericParamIdx = 0; genericParamIdx < (int)methodDef->mGenericParams.size(); genericParamIdx++)
{
auto genericParamInstance = new BfGenericMethodParamInstance(methodDef, genericParamIdx);
methodInstance->GetMethodInfoEx()->mGenericParams.push_back(genericParamInstance);
}
};
//////////////////////////////////////////////////////////////////////////
auto _VisitLambdaBody = [&]()
{
if (localMethod->mDeclOnly)
return;
if (mCompiler->mCanceling)
return;
if (auto blockBody = BfNodeDynCast<BfBlock>(body))
{
VisitCodeBlock(blockBody);
}
else if (auto bodyExpr = BfNodeDynCast<BfExpression>(body))
CreateValueFromExpression(bodyExpr);
};
auto _SafeResolveTypeRef = [&](BfTypeReference* typeRef)
{
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
auto result = ResolveTypeRef(typeRef);
if (result == NULL)
result = mContext->mBfObjectType;
return result;
};
BfTypeInstance* outerClosure = NULL;
if ((declMethodState->mClosureState != NULL) && (!declMethodState->mClosureState->mCapturing))
outerClosure = declMethodState->mClosureState->mClosureType;
BfMethodState methodState;
methodState.mPrevMethodState = declMethodState;//mCurMethodState;
BfIRFunctionType funcType;
auto voidType = GetPrimitiveType(BfTypeCode_None);
SizedArray<BfIRType, 0> paramTypes;
funcType = mBfIRBuilder->CreateFunctionType(mBfIRBuilder->MapType(voidType), paramTypes, methodInstance->IsVarArgs());
2019-08-23 11:56:54 -07:00
mBfIRBuilder->SaveDebugLocation();
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
auto prevActiveFunction = mBfIRBuilder->GetActiveFunction();
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
BfDeferredLocalAssignData deferredLocalAssignData;
//TODO: Why did we do this? It can cause us to pull in local variables that don't belong to us...
/*if (declMethodState->mDeferredLocalAssignData != NULL)
deferredLocalAssignData.ExtendFrom(declMethodState->mDeferredLocalAssignData);*/
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
methodState.mIRHeadBlock = mBfIRBuilder->CreateBlock("head", true);
methodState.mIRInitBlock = mBfIRBuilder->CreateBlock("init", true);
methodState.mIREntryBlock = mBfIRBuilder->CreateBlock("entry", true);
methodState.mCurScope->mDIScope = localMethod->mDeclDIScope; //declMethodState->mCurScope->mDIScope ;
//methodState.mCurLocalVarId = declMethodState->mCurLocalVarId;
methodState.mIRFunction = declMethodState->mIRFunction;
methodState.mDeferredLocalAssignData = &deferredLocalAssignData;
2020-05-28 07:37:55 -07:00
2020-02-19 14:02:49 -08:00
if (auto blockBody = BfNodeDynCast<BfBlock>(body))
{
methodState.mCurScope->mAstBlock = blockBody;
methodState.mCurScope->mCloseNode = blockBody->mCloseBrace;
}
2019-08-23 11:56:54 -07:00
mBfIRBuilder->SetInsertPoint(methodState.mIREntryBlock);
BfClosureState closureState;
if (methodDef->mMethodType == BfMethodType_Mixin)
{
if (declMethodState != NULL)
{
if ((declMethodState->mClosureState != NULL) && (declMethodState->mClosureState->mReturnType != NULL))
closureState.mReturnType = declMethodState->mClosureState->mReturnType;
else if (declMethodState->mMethodInstance != NULL)
closureState.mReturnType = declMethodState->mMethodInstance->mReturnType;
}
}
if (closureState.mReturnType == NULL)
closureState.mReturnType = _SafeResolveTypeRef(methodDef->mReturnTypeRef);
closureState.mCapturing = true;
closureState.mLocalMethod = localMethod;
closureState.mClosureInstanceInfo = methodInstance->mMethodInfoEx->mClosureInstanceInfo;
closureState.mDeclaringMethodIsMutating = mCurMethodInstance->mMethodDef->mIsMutating;
methodState.mClosureState = &closureState;
closureState.mClosureType = outerClosure;
int outerLocalsCount = (int)methodState.mLocals.size();
if (!hadExistingMethodInstance)
{
BfLogSysM("LocalMethod %p premature DoMethodDeclaration %p (Source:%p)\n", localMethod, methodInstance, (methodDeclaration != NULL) ? methodDeclaration->GetSourceData() : NULL);
SetAndRestoreValue<bool> prevIgnoreWrites(mWantsIRIgnoreWrites, true);
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
// Prematurely do declaration. We will have to undo this later, so don't write this one and don't throw errors
auto declareModule = this;
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(declareModule->mCurMethodInstance, methodInstance);
SetAndRestoreValue<BfFilePosition> prevFilePos(declareModule->mCurFilePosition);
BF_ASSERT(declareModule != NULL);
methodInstance->mDeclModule = declareModule;
_SetupMethodInstance();
declareModule->DoMethodDeclaration(localMethod->mMethodDef->GetMethodDeclaration(), false, false);
methodInstance->mDisallowCalling = true; // Don't allow us to ACTUALLY call until we add the captures
}
if (wantPrematureMethodInstance)
{
// Return a premature methodInstance -- since we are partway through determining the capture of a local method, we can't rely on its
// capture information. This returned method instance can only be used for method matching, but we will visit it later to create
// its true method instance
//auto moduleMethodInstance = _CheckReturnedMethod(methodInstance);
//BF_ASSERT(moduleMethodInstance);
return BfModuleMethodInstance(methodInstance);
}
auto checkInsertBlock = mBfIRBuilder->GetInsertBlock();
BF_ASSERT(methodInstance->mMethodInfoEx->mClosureInstanceInfo->mCaptureClosureState == NULL);
closureState.mCaptureStartAccessId = mCurMethodState->GetRootMethodState()->mCurAccessId;
methodInstance->mMethodInfoEx->mClosureInstanceInfo->mCaptureClosureState = &closureState;
closureState.mClosureMethodDef = methodDef;
bool wantsVisitBody = true;
if ((methodDef->mMethodType == BfMethodType_Mixin) && (!methodGenericArguments.IsEmpty()))
wantsVisitBody = false;
if (methodInstance->mIsUnspecializedVariation)
wantsVisitBody = false;
if (wantsVisitBody)
{
BP_ZONE("VisitLambdaBody");
// For generic methods, we capture for each specialization, so make sure mIsStatic is set to 'true' for
// this capture stage for each of those times
SetAndRestoreValue<bool> prevWasStatis(methodDef->mIsStatic, true);
/*
// Only generate errors once. Also there are some errors that will occur during this scanning phase
// that will not occur during the actual lambda method generation, for example: returning a value
// when our current method is a 'void' method. This shouldn't affect capture scanning since all
// our AST nodes will still be visited
SetAndRestoreValue<bool> ignoreError(mIgnoreErrors, true);
*/
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, methodInstance);
//SetAndRestoreValue<bool> wantsIgnoreWrites(mWantsISIgnoreWrites, true);
mBfIRBuilder->SaveDebugLocation();
closureState.mCaptureVisitingBody = true;
BF_ASSERT(methodInstance->mMethodInfoEx != NULL);
methodState.mGenericTypeBindings = &methodInstance->mMethodInfoEx->mGenericTypeBindings;
methodState.mMethodInstance = methodInstance;
2019-08-23 11:56:54 -07:00
NewScopeState();
ProcessMethod_SetupParams(methodInstance, NULL, false, NULL);
if (methodInstance->mMethodDef->mMethodType == BfMethodType_Mixin)
{
// Allow assigning to arguments when we're capturing - which may or may not be legal at the injection site (we throw error there)
for (auto localDef : methodState.mLocals)
{
if (localDef->mResolvedType->IsVar())
continue;
localDef->mAddr = CreateAlloca(localDef->mResolvedType);
}
}
_VisitLambdaBody();
RestoreScopeState();
//ProcessMethod(methodInstance);
closureState.mCaptureVisitingBody = false;
mBfIRBuilder->RestoreDebugLocation();
}
// If we ended up being called by a method with a lower captureStartAccessId, propagate that to whoever is calling us, too...
if ((methodState.mPrevMethodState->mClosureState != NULL) && (methodState.mPrevMethodState->mClosureState->mCapturing))
{
auto prevClosureState = methodState.mPrevMethodState->mClosureState;
if (closureState.mCaptureStartAccessId < prevClosureState->mCaptureStartAccessId)
prevClosureState->mCaptureStartAccessId = closureState.mCaptureStartAccessId;
}
//RestoreScopeState();
// Actually get the referenced local methods. It's important we wait until after visiting the body, because then our own capture information
// can be used by these methods (which will be necessary if any of these methods call us directly or indirectly)
closureState.mClosureMethodDef = NULL;
for (auto methodInstance : closureState.mLocalMethodRefs)
{
GetLocalMethodInstance(methodInstance->mMethodInfoEx->mClosureInstanceInfo->mLocalMethod, BfTypeVector(), methodInstance);
}
methodInstance->mMethodInfoEx->mClosureInstanceInfo->mCaptureClosureState = NULL;
prevIgnoreWrites.Restore();
std::multiset<BfClosureCapturedEntry> capturedEntries;
//
{
auto varMethodState = declMethodState;
while (varMethodState != NULL)
{
if ((varMethodState->mMixinState != NULL) && (varMethodState->mMixinState->mLastTargetAccessId >= closureState.mCaptureStartAccessId))
{
BF_ASSERT(methodInstance->GetOwner() == varMethodState->mMixinState->mTarget.mType);
methodDef->mIsStatic = false;
if (rootMethodState->mMethodInstance->mMethodDef->mIsMutating)
methodDef->mIsMutating = true;
}
bool copyOuterCaptures = false;
for (int localIdx = 0; localIdx < varMethodState->mLocals.size(); localIdx++)
{
auto localVar = varMethodState->mLocals[localIdx];
if ((localVar->mReadFromId >= closureState.mCaptureStartAccessId) || (localVar->mWrittenToId >= closureState.mCaptureStartAccessId))
{
if (localVar->mIsThis)
{
// We can only set mutating if our owning type is mutating
if (localVar->mWrittenToId >= closureState.mCaptureStartAccessId)
{
if (typeInst->IsValueType())
2019-08-23 11:56:54 -07:00
{
if (rootMethodState->mMethodInstance->mMethodDef->mIsMutating)
methodDef->mIsMutating = true;
}
}
methodDef->mIsStatic = false;
continue;
}
if ((localVar->mIsThis) && (outerClosure != NULL))
{
continue;
}
if ((localVar->mConstValue) || (localVar->mResolvedType->IsValuelessType()))
{
closureState.mConstLocals.push_back(*localVar);
continue;
}
BfClosureCapturedEntry capturedEntry;
auto capturedType = localVar->mResolvedType;
bool captureByRef = false;
if (!capturedType->IsRef())
{
if (localVar->mIsThis)
{
if ((localVar->mResolvedType->IsValueType()) && (mCurMethodInstance->mMethodDef->HasNoThisSplat()))
captureByRef = true;
}
else if ((!localVar->mIsReadOnly) && (localVar->mWrittenToId >= closureState.mCaptureStartAccessId))
captureByRef = true;
}
else
{
if (localVar->mWrittenToId < closureState.mCaptureStartAccessId)
{
capturedType = ((BfRefType*)capturedType)->mElementType;
}
}
if (captureByRef)
{
//capturedEntry.mIsByReference = true;
capturedType = CreateRefType(capturedType);
}
else
{
//capturedEntry.mIsByReference = false;
}
capturedEntry.mNameNode = localVar->mNameNode;
capturedEntry.mType = capturedType;
capturedEntry.mName = localVar->mName;
//capturedEntry.mLocalVarDef = localVar;
capturedEntries.insert(capturedEntry);
}
}
varMethodState = varMethodState->mPrevMethodState;
if ((varMethodState == NULL) ||
(varMethodState->mMixinState != NULL) ||
((varMethodState->mClosureState != NULL) && (!varMethodState->mClosureState->mCapturing)))
break;
}
}
for (auto& copyField : closureState.mReferencedOuterClosureMembers)
{
auto fieldDef = copyField->GetFieldDef();
BfClosureCapturedEntry capturedEntry;
capturedEntry.mName = copyField->GetFieldDef()->mName;
capturedEntry.mType = copyField->mResolvedType;
if (capturedEntry.mType->IsRef())
{
// Keep by ref
}
else if (!fieldDef->mIsReadOnly)
{
capturedEntry.mType = CreateRefType(capturedEntry.mType);
}
capturedEntries.insert(capturedEntry);
}
int captureIdx = 0;
for (auto& capturedEntry : capturedEntries)
{
methodInstance->mMethodInfoEx->mClosureInstanceInfo->mCaptureEntries.Add(capturedEntry);
}
/*if (isDefaultPass)
{
// Insert captured members a params at the start. If we added at the end then it would screw up varargs methods
int captureIdx = 0;
for (auto& capturedEntry : capturedEntries)
{
methodInstance->mClosureInstanceInfo->mCaptureNodes.Add(BfNodeDynCast<BfIdentifierNode>(capturedEntry.mNameNode));
BfParameterDef* paramDef = new BfParameterDef();
paramDef->mName = capturedEntry.mName;
BfDirectTypeReference* directTypeRef = new BfDirectTypeReference();
directTypeRef->mType = capturedEntry.mType;
typeInst->mTypeDef->mDirectAllocNodes.push_back(directTypeRef);
paramDef->mTypeRef = directTypeRef;
paramDef->mParamKind = BfParamKind_ImplicitCapture;
methodDef->mParams.Insert(captureIdx, paramDef);
captureIdx++;
}
}
else
{
auto defaultMethodInstance = methodInstGroup->mDefault;
methodInstance->mClosureInstanceInfo->mCaptureNodes = defaultMethodInstance->mClosureInstanceInfo->mCaptureNodes;
}*/
methodState.Reset();
closureState.mCapturing = false;
//////////////////////////////////////////////////////////////////////////
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, false);
if (methodInstance->mReturnType != NULL)
{
BF_ASSERT(methodInstance->mDisallowCalling);
// We did a premature declaration (without captures)
methodInstance->UndoDeclaration();
methodInstance->mDisallowCalling = false;
BfLogSysM("LocalMethod %p UndoingDeclaration %p\n", localMethod, methodInstance);
}
auto declareModule = this;
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(declareModule->mCurMethodInstance, methodInstance);
SetAndRestoreValue<BfFilePosition> prevFilePos(declareModule->mCurFilePosition);
BF_ASSERT(declareModule != NULL);
methodInstance->mDeclModule = declareModule;
_SetupMethodInstance();
BfLogSysM("LocalMethod %p DoMethodDeclaration %p\n", localMethod, methodInstance);
declareModule->DoMethodDeclaration(localMethod->mMethodDef->GetMethodDeclaration(), false, false);
closureState.mReturnType = methodInstance->mReturnType;
mCompiler->mStats.mMethodsQueued++;
mCompiler->UpdateCompletion();
declareModule->mIncompleteMethodCount++;
mCompiler->mStats.mMethodsProcessed++;
if (!methodInstance->mIsReified)
mCompiler->mStats.mUnreifiedMethodsProcessed++;
auto deferMethodState = rootMethodState;
// Since we handle errors & warnings in the capture phase, we don't need to process any local methods for resolve-only (unless we're doing a mDbgVerifyCodeGen)
if ((!localMethod->mDeclOnly) && (!methodInstance->mIsUnspecializedVariation) &&
(!mWantsIRIgnoreWrites) && (methodDef->mMethodType != BfMethodType_Mixin))
{
BP_ZONE("BfDeferredLocalMethod:create");
BfDeferredLocalMethod* deferredLocalMethod = new BfDeferredLocalMethod();
deferredLocalMethod->mLocalMethod = localMethod;
deferredLocalMethod->mConstLocals = closureState.mConstLocals;
deferredLocalMethod->mMethodInstance = methodInstance;
auto checkMethodState = declMethodState;
while (checkMethodState != NULL)
{
if ((checkMethodState->mClosureState != NULL) && (checkMethodState->mClosureState->mActiveDeferredLocalMethod != NULL))
{
for (auto& mixinRecord : checkMethodState->mClosureState->mActiveDeferredLocalMethod->mMixinStateRecords)
deferredLocalMethod->mMixinStateRecords.Add(mixinRecord);
}
if (checkMethodState->mMixinState != NULL)
{
BfMixinRecord mixinRecord;
mixinRecord.mSource = checkMethodState->mMixinState->mSource;
deferredLocalMethod->mMixinStateRecords.Add(mixinRecord);
}
checkMethodState = checkMethodState->mPrevMethodState;
}
rootMethodState->mDeferredLocalMethods.Add(deferredLocalMethod);
}
mBfIRBuilder->SetActiveFunction(prevActiveFunction);
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
mBfIRBuilder->RestoreDebugLocation();
if (methodInstance == localMethod->mMethodInstanceGroup->mDefault)
{
auto checkMethodState = mCurMethodState;
while (checkMethodState != NULL)
{
BfLocalMethod* nextLocalMethod = NULL;
if (checkMethodState->mLocalMethodMap.TryGetValue(localMethod->mMethodName, &nextLocalMethod))
{
while (nextLocalMethod != NULL)
{
auto checkLocalMethod = nextLocalMethod;
nextLocalMethod = nextLocalMethod->mNextWithSameName;
if (checkLocalMethod == localMethod)
continue;
if (checkLocalMethod->mMethodInstanceGroup == NULL)
continue;
auto checkMethodInstance = checkLocalMethod->mMethodInstanceGroup->mDefault;
if (checkMethodInstance == NULL)
continue; // Not generated yet
if (CompareMethodSignatures(localMethod->mMethodInstanceGroup->mDefault, checkMethodInstance))
{
auto bfError = Fail("Method already declared with the same parameter types", methodInstance->mMethodDef->GetRefNode(), true);
if (bfError != NULL)
mCompiler->mPassInstance->MoreInfo("First declaration", checkMethodInstance->mMethodDef->GetRefNode());
}
}
}
checkMethodState = checkMethodState->mPrevMethodState;
}
}
return BfModuleMethodInstance(methodInstance);
}
int BfModule::GetLocalInferrableGenericArgCount(BfMethodDef* methodDef)
{
if ((!methodDef->mIsLocalMethod) || (methodDef->mGenericParams.size() == 0))
return 0;
auto rootMethodState = mCurMethodState->GetRootMethodState();
int rootMethodGenericParamCount = 0;
if (rootMethodState->mMethodInstance->mMethodInfoEx != NULL)
rootMethodGenericParamCount = (int)rootMethodState->mMethodInstance->mMethodInfoEx->mGenericParams.size();
if (mCurMethodInstance == NULL)
return rootMethodGenericParamCount;
if ((mCurMethodInstance->mMethodInfoEx == NULL) || (mCurMethodInstance->mMethodInfoEx->mClosureInstanceInfo == NULL))
return rootMethodGenericParamCount;
BfLocalMethod* callerLocalMethod = mCurMethodInstance->mMethodInfoEx->mClosureInstanceInfo->mLocalMethod;
if (callerLocalMethod == NULL)
return rootMethodGenericParamCount;
BfLocalMethod* calledLocalMethod = NULL;
rootMethodState->mLocalMethodCache.TryGetValue(methodDef->mName, &calledLocalMethod);
while ((calledLocalMethod != NULL) && (callerLocalMethod != NULL))
{
if (calledLocalMethod->mOuterLocalMethod == callerLocalMethod)
return (int)callerLocalMethod->mMethodDef->mGenericParams.size();
callerLocalMethod = calledLocalMethod->mOuterLocalMethod;
}
return rootMethodGenericParamCount;
}
void BfModule::GetMethodCustomAttributes(BfMethodInstance* methodInstance)
{
auto methodDef = methodInstance->mMethodDef;
auto customAttributes = methodInstance->GetCustomAttributes();
if (customAttributes != NULL)
2019-08-23 11:56:54 -07:00
return;
auto methodDeclaration = methodDef->GetMethodDeclaration();
auto propertyMethodDeclaration = methodDef->GetPropertyMethodDeclaration();
auto typeInstance = methodInstance->GetOwner();
BfTypeState typeState(typeInstance);
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
2019-08-23 11:56:54 -07:00
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(mCurTypeInstance, typeInstance);
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, methodInstance);
if ((methodDeclaration != NULL) && (methodDeclaration->mAttributes != NULL))
{
if (methodInstance->GetMethodInfoEx()->mMethodCustomAttributes == NULL)
methodInstance->mMethodInfoEx->mMethodCustomAttributes = new BfMethodCustomAttributes();
methodInstance->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes = GetCustomAttributes(methodDeclaration->mAttributes,
((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_CtorCalcAppend)) ? BfAttributeTargets_Constructor : BfAttributeTargets_Method);
}
else if ((propertyMethodDeclaration != NULL) && (propertyMethodDeclaration->mAttributes != NULL))
{
if (methodInstance->GetMethodInfoEx()->mMethodCustomAttributes == NULL)
methodInstance->mMethodInfoEx->mMethodCustomAttributes = new BfMethodCustomAttributes();
methodInstance->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes = GetCustomAttributes(propertyMethodDeclaration->mAttributes, BfAttributeTargets_Method);
}
customAttributes = methodInstance->GetCustomAttributes();
if (customAttributes == NULL)
{
auto owner = methodInstance->GetOwner();
if ((owner->IsDelegate()) || (owner->IsFunction()))
customAttributes = owner->mCustomAttributes;
}
methodInstance->mCallingConvention = methodDef->mCallingConvention;
if (customAttributes != NULL)
{
auto linkNameAttr = customAttributes->Get(typeInstance->mModule->mCompiler->mCallingConventionAttributeTypeDef);
if (linkNameAttr != NULL)
{
if (linkNameAttr->mCtorArgs.size() == 1)
{
auto constant = typeInstance->mConstHolder->GetConstant(linkNameAttr->mCtorArgs[0]);
if (constant != NULL)
methodInstance->mCallingConvention = (BfCallingConvention)constant->mInt32;
}
}
}
2019-08-23 11:56:54 -07:00
}
void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& mangledName, bool isTemporaryFunc, bool* outIsIntrinsic)
{
if (methodInstance->GetImportCallKind() == BfImportCallKind_GlobalVar)
{
// Don't create a func
return;
}
auto typeInstance = methodInstance->GetOwner();
auto methodDef = methodInstance->mMethodDef;
BfIRFunctionType funcType = mBfIRBuilder->MapMethod(methodInstance);
// Don't set these pointers during resolve pass because they may become invalid if it's just a temporary autocomplete method
if (mCompiler->mResolvePassData == NULL)
{
if ((methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mIsStatic))
{
typeInstance->mHasStaticInitMethod = true;
}
if ((methodDef->mMethodType == BfMethodType_Dtor) && (methodDef->mIsStatic))
{
typeInstance->mHasStaticDtorMethod = true;
}
if ((methodDef->mMethodType == BfMethodType_Normal) && (methodDef->mIsStatic) && (methodDef->mName == BF_METHODNAME_MARKMEMBERS_STATIC))
{
typeInstance->mHasStaticMarkMethod = true;
}
if ((methodDef->mMethodType == BfMethodType_Normal) && (methodDef->mIsStatic) && (methodDef->mName == BF_METHODNAME_FIND_TLS_MEMBERS))
{
typeInstance->mHasTLSFindMethod = true;
}
}
if (isTemporaryFunc)
{
BF_ASSERT(((mCompiler->mIsResolveOnly) && (mCompiler->mResolvePassData->mAutoComplete != NULL)) ||
(methodInstance->GetOwner()->mDefineState < BfTypeDefineState_Defined));
2019-08-23 11:56:54 -07:00
mangledName = "autocomplete_tmp";
}
if (!mBfIRBuilder->mIgnoreWrites)
{
BfIRFunction prevFunc;
// Remove old version (if we have one)
prevFunc = mBfIRBuilder->GetFunction(mangledName);
if (prevFunc)
{
if (methodDef->mIsExtern)
{
// Allow this
BfLogSysM("Function collision allowed multiple extern functions for %p: %d\n", methodInstance, prevFunc.mId);
}
else if (typeInstance->IsIRFuncUsed(prevFunc))
{
// We can have a collision of names when we have generic methods that differ only in
// their constraints, but they should only collide in their unspecialized form
// since only one will be chosen for a given concrete type
mCurMethodInstance->mMangleWithIdx = true;
mangledName.Clear();
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), mCurMethodInstance);
prevFunc = mBfIRBuilder->GetFunction(mangledName);
BfLogSysM("Function collision forced mangleWithIdx for %p: %d\n", methodInstance, prevFunc.mId);
}
else
{
BfLogSysM("Function collision erased prevFunc %p: %d\n", methodInstance, prevFunc.mId);
mBfIRBuilder->Func_EraseFromParent(prevFunc);
}
}
}
if (typeInstance != mContext->mBfObjectType)
{
// Only allow attributes on System.Object methods that can be handled inside the DefBuilder
GetMethodCustomAttributes(methodInstance);
}
// if (prevFunc)
// {
// if (methodDef->mIsExtern)
// {
// BfLogSysM("Reusing function for %p: %d\n", methodInstance, prevFunc.mId);
// methodInstance->mIRFunction = prevFunc;
// }
// else
// {
// if ((methodInstance->IsSpecializedByAutoCompleteMethod()) ||
// ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mResolveType >= BfResolveType_GoToDefinition)))
// {
// // If we're doing re-pass over the methods for something like BfResolveType_ShowFileSymbolReferences, then allow this collision
// BfLogSysM("Ignoring function name collision\n");
// }
// else
// {
// if (methodDef->mMethodDeclaration == NULL)
// {
// AssertErrorState();
// }
// else
// {
// BF_ASSERT(mIsSpecialModule);
// if ((mCompiler->mRevision == 1) && (HasCompiledOutput()))
// {
// // We shouldn't have name collisions on the first run!
// AssertErrorState();
// }
// }
// }
// BfLogSysM("Before erase\n");
// mBfIRBuilder->Func_EraseFromParent(prevFunc);
// }
// }
bool isIntrinsic = false;
if (!methodInstance->mIRFunction)
{
BfIRFunction func;
bool wantsLLVMFunc = ((!typeInstance->IsUnspecializedType()) && (!methodDef->IsEmptyPartial())) && (funcType);
/*if (mCurTypeInstance->mTypeDef->mName->ToString() == "ClassA")
{
if (!mIsReified)
wantsLLVMFunc = false;
}*/
if (wantsLLVMFunc)
{
func = GetIntrinsic(methodInstance, true);
if (func)
{
isIntrinsic = true;
}
else
{
func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_External, mangledName);
BfLogSysM("Creating FuncId:%d %s in module %p\n", func.mId, mangledName.c_str(), this);
if (methodInstance->mAlwaysInline)
mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_AlwaysInline);
// if (prevFunc)
// BfLogSysM("Removing Func %d, replacing with %d\n", prevFunc.mId, func.mId);
}
methodInstance->mIRFunction = func;
//if (!ignoreWrites)
//BF_ASSERT(!func.IsFake());
}
}
if (outIsIntrinsic != NULL)
*outIsIntrinsic = isIntrinsic;
if (!isIntrinsic)
SetupIRMethod(methodInstance, methodInstance->mIRFunction, methodInstance->mAlwaysInline);
2019-08-23 11:56:54 -07:00
}
void BfModule::CheckHotMethod(BfMethodInstance* methodInstance, const StringImpl& inMangledName)
{
if (methodInstance->mHotMethod != NULL)
return;
if ((mCompiler->mOptions.mAllowHotSwapping) && (!methodInstance->mIsUnspecialized))
{
auto srcTypeInst = methodInstance->GetOwner();
StringT<128> mangledName = inMangledName;
if (mangledName.IsEmpty())
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), methodInstance);
// We always keep the current primary method at the same address
BfHotMethod* hotMethod;
BfHotMethod** hotMethodPtr;
if (mCompiler->mHotData->mMethodMap.TryAdd(mangledName, NULL, &hotMethodPtr))
{
hotMethod = new BfHotMethod();
*hotMethodPtr = hotMethod;
hotMethod->mRefCount = 1;
hotMethod->mFlags = (BfHotDepDataFlags)(hotMethod->mFlags | BfHotDepDataFlag_IsBound);
#ifdef BF_DBG_HOTMETHOD_IDX
static int sMethodIdx = 0;
hotMethod->mMethodIdx = sMethodIdx++;
#endif
BfLogSysM("HotMethodData %p created for method %p %s - %s\n", hotMethod, methodInstance, MethodToString(methodInstance).c_str(), mangledName.c_str());
}
else
{
hotMethod = *hotMethodPtr;
if ((hotMethod->mFlags & BfHotDepDataFlag_IsBound) != 0)
{
// This is a duplicate mangled name - we link to this new entry via a 'BfHotDupMethod'
auto prevHotMethod = *hotMethodPtr;
hotMethod = new BfHotMethod();
hotMethod->mFlags = (BfHotDepDataFlags)(hotMethod->mFlags | BfHotDepDataFlag_IsBound);
BfHotDupMethod* hotDupMethod = new BfHotDupMethod(hotMethod);
hotDupMethod->mRefCount++;
prevHotMethod->mReferences.Add(hotDupMethod);
prevHotMethod->mFlags = (BfHotDepDataFlags)(hotMethod->mFlags | BfHotDepDataFlag_HasDup);
BfLogSysM("HotMethodData %p (duplicate of %p) created for method %p %s - %s\n", hotMethod, prevHotMethod, methodInstance, MethodToString(methodInstance).c_str(), mangledName.c_str());
}
else if (mCompiler->IsHotCompile())
{
// Link the previous version into the mPrevVersion
BfHotMethod* prevMethod = new BfHotMethod();
prevMethod->mRefCount = 1;
prevMethod->mPrevVersion = hotMethod->mPrevVersion;
hotMethod->mPrevVersion = prevMethod;
prevMethod->mSrcTypeVersion = hotMethod->mSrcTypeVersion;
hotMethod->mSrcTypeVersion = NULL;
prevMethod->mFlags = hotMethod->mFlags;
hotMethod->mFlags = BfHotDepDataFlag_IsBound;
hotMethod->mReferences.MoveTo(prevMethod->mReferences);
#ifdef BF_DBG_HOTMETHOD_NAME
prevMethod->mMangledName = hotMethod->mMangledName;
#endif
BfLogSysM("HotMethodData %p created for prevmethod of %p for method %p %s\n", prevMethod, hotMethod, methodInstance, MethodToString(methodInstance).c_str());
}
else
{
BfLogSysM("HotMethodData %p used for method %p %s - %s\n", hotMethod, methodInstance, MethodToString(methodInstance).c_str(), mangledName.c_str());
hotMethod->Clear(true);
hotMethod->mFlags = (BfHotDepDataFlags)(hotMethod->mFlags | BfHotDepDataFlag_IsBound);
}
}
if (methodInstance->mIsClosure)
{
hotMethod->mFlags = (BfHotDepDataFlags)(hotMethod->mFlags | BfHotDepDataFlag_RetainMethodWithoutBinding);
}
hotMethod->mSrcTypeVersion = srcTypeInst->mHotTypeData->GetLatestVersion();
hotMethod->mSrcTypeVersion->mRefCount++;
hotMethod->mRefCount++;
#ifdef BF_DBG_HOTMETHOD_NAME
hotMethod->mMangledName = mangledName;
#endif
2020-04-16 00:32:00 -07:00
if ((methodInstance->mMethodInstanceGroup->IsImplemented()) && (!mCompiler->IsHotCompile()))
hotMethod->mFlags = (BfHotDepDataFlags)(hotMethod->mFlags | BfHotDepDataFlag_IsOriginalBuild);
methodInstance->mHotMethod = hotMethod;
2019-08-23 11:56:54 -07:00
}
}
// methodDeclaration is NULL for default constructors
void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool isTemporaryFunc, bool addToWorkList)
{
BP_ZONE("BfModule::BfMethodDeclaration");
// If we are doing this then we may end up creating methods when var types are unknown still, failing on splat/zero-sized info
BF_ASSERT((!mContext->mResolvingVarField) || (mBfIRBuilder->mIgnoreWrites));
2019-08-23 11:56:54 -07:00
// We could trigger a DoMethodDeclaration from a const resolver or other location, so we reset it here
// to effectively make mIgnoreWrites method-scoped
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, mWantsIRIgnoreWrites || mCurMethodInstance->mIsUnspecialized);
SetAndRestoreValue<bool> prevIsCapturingMethodMatchInfo;
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, NULL);
if (mCompiler->IsAutocomplete())
prevIsCapturingMethodMatchInfo.Init(mCompiler->mResolvePassData->mAutoComplete->mIsCapturingMethodMatchInfo, false);
if (mCurMethodInstance->mMethodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_NoDecl_AwaitingReference)
mCurMethodInstance->mMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Decl_AwaitingReference;
bool ignoreWrites = mBfIRBuilder->mIgnoreWrites;
if ((!isTemporaryFunc) && (mCurTypeInstance->mDefineState < BfTypeDefineState_Defined))
{
BF_ASSERT(mContext->mResolvingVarField);
isTemporaryFunc = true;
}
2019-08-23 11:56:54 -07:00
if (mAwaitingInitFinish)
FinishInit();
auto typeInstance = mCurTypeInstance;
auto typeDef = typeInstance->mTypeDef;
auto methodDef = mCurMethodInstance->mMethodDef;
BF_ASSERT(methodDef->mName != "__ASSERTNAME");
if (methodDef->mName == "__FATALERRORNAME")
BFMODULE_FATAL(this, "__FATALERRORNAME");
2019-08-23 11:56:54 -07:00
if (typeInstance->IsClosure())
{
if (methodDef->mName == "Invoke")
return;
}
auto methodInstance = mCurMethodInstance;
if ((methodInstance->IsSpecializedByAutoCompleteMethod()) || (mCurTypeInstance->IsFunction()))
addToWorkList = false;
bool hasNonGenericParams = false;
bool hasGenericParams = false;
int dependentGenericStartIdx = 0;
if (methodDef->mIsLocalMethod)
{
// If we're a local generic method inside an outer generic method, we can still be considered unspecialized
// if our outer method's generic args are specialized but ours are unspecialized. This is because locals get
// instantiated uniquely for each specialized or unspecialized pass through the outer method, but the local
// method still 'inherits' the outer's generic arguments -- but we still need to make an unspecialized pass
// over the local method each time
auto rootMethodInstance = prevMethodState.mPrevVal->GetRootMethodState()->mMethodInstance;
dependentGenericStartIdx = 0;
if (rootMethodInstance->mMethodInfoEx != NULL)
dependentGenericStartIdx = (int)rootMethodInstance->mMethodInfoEx->mMethodGenericArguments.size();
methodInstance->mIsUnspecialized = rootMethodInstance->mIsUnspecialized;
methodInstance->mIsUnspecializedVariation = rootMethodInstance->mIsUnspecializedVariation;
}
for (int genericArgIdx = dependentGenericStartIdx; genericArgIdx < (int) methodInstance->GetNumGenericArguments(); genericArgIdx++)
{
auto genericArgType = methodInstance->mMethodInfoEx->mMethodGenericArguments[genericArgIdx];
if (genericArgType->IsGenericParam())
{
hasGenericParams = true;
auto genericParam = (BfGenericParamType*)genericArgType;
methodInstance->mIsUnspecialized = true;
if ((genericParam->mGenericParamKind != BfGenericParamKind_Method) || (genericParam->mGenericParamIdx != genericArgIdx))
methodInstance->mIsUnspecializedVariation = true;
}
else
{
hasNonGenericParams = true;
if (genericArgType->IsUnspecializedType())
{
methodInstance->mIsUnspecialized = true;
methodInstance->mIsUnspecializedVariation = true;
}
}
}
if ((hasGenericParams) && (hasNonGenericParams))
{
methodInstance->mIsUnspecializedVariation = true;
}
if (typeInstance->IsUnspecializedType())
{
// A specialized method within an unspecialized type is considered an unspecialized variation -- in the sense that we don't
// actually want to do method processing on it
if ((!methodInstance->mIsUnspecialized) && (methodInstance->GetNumGenericArguments() != 0))
methodInstance->mIsUnspecializedVariation = true;
methodInstance->mIsUnspecialized = true;
}
methodInstance->mIsUnspecializedVariation |= typeInstance->IsUnspecializedTypeVariation();
for (auto genericParamDef : methodDef->mGenericParams)
{
if (mCompiler->IsAutocomplete())
{
auto autoComplete = mCompiler->mResolvePassData->mAutoComplete;
//autoComplete->CheckTypeRef()
}
}
if (methodInstance->mIsUnspecializedVariation)
{
}
if (methodInstance->mIsUnspecializedVariation)
BF_ASSERT(methodInstance->mIsUnspecialized);
if (methodDef->mMethodType == BfMethodType_Mixin)
methodInstance->mIsUnspecialized = true;
if (methodInstance->mIsUnspecialized)
{
//BF_ASSERT(methodInstance->mDeclModule == methodInstance->GetOwner()->mModule);
}
2019-08-23 11:56:54 -07:00
BfAutoComplete* bfAutocomplete = NULL;
if (mCompiler->mResolvePassData != NULL)
bfAutocomplete = mCompiler->mResolvePassData->mAutoComplete;
if (methodInstance->mMethodInfoEx != NULL)
{
for (int genericParamIdx = 0; genericParamIdx < (int)methodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
2019-08-23 11:56:54 -07:00
{
auto genericParam = methodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
if (genericParamIdx < (int)methodDef->mGenericParams.size())
2019-08-23 11:56:54 -07:00
{
genericParam->mExternType = GetGenericParamType(BfGenericParamKind_Method, genericParamIdx);
}
else
{
auto externConstraintDef = genericParam->GetExternConstraintDef();
genericParam->mExternType = ResolveTypeRef(externConstraintDef->mTypeRef);
auto autoComplete = mCompiler->GetAutoComplete();
if (autoComplete != NULL)
autoComplete->CheckTypeRef(externConstraintDef->mTypeRef, false);
if (genericParam->mExternType != NULL)
{
//
}
else
genericParam->mExternType = GetPrimitiveType(BfTypeCode_Var);
}
ResolveGenericParamConstraints(genericParam, methodInstance->mIsUnspecialized);
if (genericParamIdx < (int)methodDef->mGenericParams.size())
{
auto genericParamDef = methodDef->mGenericParams[genericParamIdx];
if (bfAutocomplete != NULL)
{
for (auto nameNode : genericParamDef->mNameNodes)
{
HandleMethodGenericParamRef(nameNode, typeDef, methodDef, genericParamIdx);
}
}
2019-08-23 11:56:54 -07:00
}
}
for (auto genericParam : methodInstance->mMethodInfoEx->mGenericParams)
{
for (auto constraintTypeInst : genericParam->mInterfaceConstraints)
AddDependency(constraintTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint);
if (genericParam->mTypeConstraint != NULL)
AddDependency(genericParam->mTypeConstraint, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint);
}
}
if ((methodInstance->mIsAutocompleteMethod) && (methodDeclaration != NULL) && (!methodInstance->IsSpecializedGenericMethod()) && (methodDef->mIdx >= 0))
{
auto autoComplete = mCompiler->mResolvePassData->mAutoComplete;
BfAstNode* nameNode = methodDeclaration->mNameNode;
if (nameNode == NULL)
{
if (auto ctorDeclaration = BfNodeDynCast<BfConstructorDeclaration>(methodDeclaration))
nameNode = ctorDeclaration->mThisToken;
}
if (autoComplete->IsAutocompleteNode(nameNode))
{
autoComplete->mInsertStartIdx = nameNode->GetSrcStart();
autoComplete->mInsertEndIdx = nameNode->GetSrcEnd();
autoComplete->mDefType = typeDef;
autoComplete->mDefMethod = methodDef;
autoComplete->SetDefinitionLocation(nameNode);
if (methodDef->mIsOverride)
{
for (int virtIdx = 0; virtIdx < (int)typeInstance->mVirtualMethodTable.size(); virtIdx++)
{
auto& ventry = typeInstance->mVirtualMethodTable[virtIdx];
if (ventry.mDeclaringMethod.mMethodNum == -1)
continue;
BfMethodInstance* virtMethod = ventry.mImplementingMethod;
if (virtMethod != NULL)
{
auto virtMethodDeclaration = virtMethod->mMethodDef->GetMethodDeclaration();
if ((virtMethod->GetOwner() == typeInstance) && (virtMethodDeclaration != NULL) &&
(virtMethodDeclaration->GetSrcStart() == methodDeclaration->GetSrcStart()))
{
// Is matching method
if (virtIdx < (int)typeInstance->mBaseType->mVirtualMethodTable.size())
{
auto baseType = typeInstance->mBaseType;
if (baseType != NULL)
{
while ((baseType->mBaseType != NULL) && (virtIdx < baseType->mBaseType->mVirtualMethodTableSize))
baseType = baseType->mBaseType;
BfMethodInstance* baseVirtMethod = baseType->mVirtualMethodTable[virtIdx].mImplementingMethod;
autoComplete->SetDefinitionLocation(baseVirtMethod->mMethodDef->GetRefNode(), true);
autoComplete->mDefType = baseType->mTypeDef;
autoComplete->mDefMethod = baseVirtMethod->mMethodDef;
}
}
break;
}
}
}
}
}
}
bool reportErrors = true;
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
reportErrors = true;
// Only the defining contexts needs to report errors here
SetAndRestoreValue<bool> prevReporErrors(mReportErrors, reportErrors);
if (methodDef->mMethodType == BfMethodType_Dtor)
{
if (methodDef->mIsStatic)
{
}
else
{
if ((typeInstance->IsStruct()) || (typeInstance->IsTypedPrimitive()))
{
BfAstNode* refNode = methodDeclaration;
if (refNode == NULL)
{
// Whatever caused this ctor to be generated should have caused another failure
// But that failure might not be generated until the ctor is generated
}
else
{
auto dtorDecl = BfNodeDynCast<BfDestructorDeclaration>(methodDeclaration);
if (dtorDecl != NULL)
refNode = dtorDecl->mThisToken;
Fail("Structs cannot have destructors", refNode);
}
}
auto methodDeclaration = methodDef->GetMethodDeclaration();
if ((methodDeclaration != NULL) && (methodDeclaration->mVirtualSpecifier != NULL) &&
(mCurTypeInstance != mContext->mBfObjectType))
{
Fail("Virtual specifier is not required, all destructors are implicitly virtual.", methodDeclaration->mVirtualSpecifier);
}
}
}
BfType* resolvedReturnType = NULL;
2020-06-03 05:22:29 -07:00
if (((methodDef->mMethodType == BfMethodType_Normal) || (methodDef->mMethodType == BfMethodType_Extension) || (methodDef->mMethodType == BfMethodType_CtorCalcAppend) ||
(methodDef->mMethodType == BfMethodType_PropertyGetter) || (methodDef->mMethodType == BfMethodType_Operator)) &&
2019-08-23 11:56:54 -07:00
(methodDef->mReturnTypeRef != NULL))
{
SetAndRestoreValue<bool> prevIngoreErrors(mIgnoreErrors, mIgnoreErrors || (methodDef->GetPropertyDeclaration() != NULL));
resolvedReturnType = ResolveTypeRef(methodDef->mReturnTypeRef, BfPopulateType_Declaration,
(BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowRef | BfResolveTypeRefFlag_AllowRefGeneric));
if ((resolvedReturnType != NULL) && (resolvedReturnType->IsVar()) && (methodDef->mMethodType != BfMethodType_Mixin))
{
Fail("Cannot declare var return types", methodDef->mReturnTypeRef);
resolvedReturnType = GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
}
if (resolvedReturnType == NULL)
resolvedReturnType = GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
}
else
{
resolvedReturnType = ResolveTypeDef(mSystem->mTypeVoid);
}
BF_ASSERT(resolvedReturnType != NULL);
mCurMethodInstance->mReturnType = resolvedReturnType;
//TODO: We used to NOT add the return value dependency for specialized methods, but when we have types that are
// specialized based on the method's generic param then they can get deleted if no one else has referred to them (yet)
//if (!methodInstance->IsSpecializedGenericMethod())
AddDependency(resolvedReturnType, typeInstance, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
if (methodDef->mExplicitInterface != NULL)
{
2019-12-13 14:22:23 -08:00
auto autoComplete = mCompiler->GetAutoComplete();
if (autoComplete != NULL)
autoComplete->CheckTypeRef(methodDef->mExplicitInterface, false);
auto explicitType = ResolveTypeRef(methodDef->mExplicitInterface, BfPopulateType_Declaration);
BfTypeInstance* explicitInterface = NULL;
if (explicitType != NULL)
explicitInterface = explicitType->ToTypeInstance();
2019-08-23 11:56:54 -07:00
if (explicitInterface != NULL)
2019-12-13 14:22:23 -08:00
{
2019-08-23 11:56:54 -07:00
mCurMethodInstance->GetMethodInfoEx()->mExplicitInterface = explicitInterface->ToTypeInstance();
2019-12-13 14:22:23 -08:00
if (autoComplete != NULL)
{
BfTokenNode* dotToken = NULL;
BfAstNode* nameNode = NULL;
if (auto methodDeclaration = BfNodeDynCast<BfMethodDeclaration>(methodDef->mMethodDeclaration))
{
dotToken = methodDeclaration->mExplicitInterfaceDotToken;
nameNode = methodDeclaration->mNameNode;
}
autoComplete->CheckExplicitInterface(explicitInterface, dotToken, nameNode);
}
}
2019-08-23 11:56:54 -07:00
if ((mCurMethodInstance->mMethodInfoEx != NULL) && (mCurMethodInstance->mMethodInfoEx->mExplicitInterface != NULL))
{
bool interfaceFound = false;
for (auto ifaceInst : typeInstance->mInterfaces)
interfaceFound |= ifaceInst.mInterfaceType == mCurMethodInstance->mMethodInfoEx->mExplicitInterface;
if (!interfaceFound)
{
if (methodDef->mMethodDeclaration != NULL)
Fail("Containing class has not declared to implement this interface", methodDef->mMethodDeclaration);
else
{
// For property decls, we should have already given the error during type population
if (mCompiler->mRevision == 1)
AssertErrorState();
}
}
}
}
bool isThisStruct = mCurTypeInstance->IsStruct();
BfType* thisType = NULL;
if ((!methodDef->mIsStatic) && (!mCurTypeInstance->IsValuelessType()))
{
if ((isThisStruct) || (mCurTypeInstance->IsTypedPrimitive()))
{
thisType = mCurTypeInstance;
if (thisType == NULL)
return;
if ((thisType->IsSplattable()) && (!methodDef->HasNoThisSplat()))
{
BfTypeUtils::SplatIterate([&](BfType* checkType)
{
PopulateType(checkType, BfPopulateType_Data);
}, thisType);
}
}
else
{
thisType = mCurTypeInstance;
PopulateType(thisType, BfPopulateType_Declaration);
}
}
int implicitParamCount = 0;
if ((mCurMethodInstance->mMethodInfoEx != NULL) && (mCurMethodInstance->mMethodInfoEx->mClosureInstanceInfo != NULL))
implicitParamCount = (int)methodInstance->mMethodInfoEx->mClosureInstanceInfo->mCaptureEntries.size();
methodInstance->mMethodDef->mParams.Reserve((int)methodDef->mParams.size());
bool hadDelegateParams = false;
bool hadParams = false;
for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size() + implicitParamCount; paramIdx++)
{
BfClosureCapturedEntry* closureCaptureEntry = NULL;
BfParameterDef* paramDef = NULL;
int paramDefIdx = -1;
if (paramIdx < implicitParamCount)
closureCaptureEntry = &methodInstance->mMethodInfoEx->mClosureInstanceInfo->mCaptureEntries[paramIdx];
else
{
paramDefIdx = paramIdx - implicitParamCount;
paramDef = methodDef->mParams[paramDefIdx];
}
if (hadParams)
break;
if ((paramDef != NULL) && (paramDef->mParamKind == BfParamKind_VarArgs))
continue;
2019-08-23 11:56:54 -07:00
if ((paramDef != NULL) && (mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL) &&
(paramDef->mParamKind != BfParamKind_AppendIdx))
mCompiler->mResolvePassData->mAutoComplete->CheckTypeRef(paramDef->mTypeRef, false);
if ((paramDef != NULL) && (paramDef->mParamDeclaration != NULL) && (paramDef->mParamDeclaration->mAttributes != NULL))
{
if (methodInstance->GetMethodInfoEx()->mMethodCustomAttributes == NULL)
methodInstance->mMethodInfoEx->mMethodCustomAttributes = new BfMethodCustomAttributes();
while ((int)methodInstance->mMethodInfoEx->mMethodCustomAttributes->mParamCustomAttributes.size() < paramIdx)
methodInstance->mMethodInfoEx->mMethodCustomAttributes->mParamCustomAttributes.push_back(NULL);
auto customAttributes = GetCustomAttributes(paramDef->mParamDeclaration->mAttributes, BfAttributeTargets_Parameter);
methodInstance->mMethodInfoEx->mMethodCustomAttributes->mParamCustomAttributes.push_back(customAttributes);
}
BfType* resolvedParamType = NULL;
if (closureCaptureEntry != NULL)
resolvedParamType = closureCaptureEntry->mType;
else if (paramDef->mTypeRef->IsA<BfLetTypeReference>())
{
Fail("Cannot declare 'let' parameters", paramDef->mTypeRef);
resolvedParamType = mContext->mBfObjectType;
}
else if (paramDef->mTypeRef->IsA<BfVarTypeReference>())
{
if (methodDef->mMethodType != BfMethodType_Mixin)
{
Fail("Cannot declare var parameters", paramDef->mTypeRef);
resolvedParamType = mContext->mBfObjectType;
}
else
resolvedParamType = GetPrimitiveType(BfTypeCode_Var);
}
BfType* unresolvedParamType = resolvedParamType;
bool wasGenericParam = false;
if (resolvedParamType == NULL)
{
resolvedParamType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration,
(BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowRef | BfResolveTypeRefFlag_AllowRefGeneric | BfResolveTypeRefFlag_AllowGenericMethodParamConstValue));
}
if (resolvedParamType == NULL)
{
resolvedParamType = GetPrimitiveType(BfTypeCode_Var);
2019-08-23 11:56:54 -07:00
unresolvedParamType = resolvedParamType;
if (mCurTypeInstance->IsBoxed())
{
auto boxedType = (BfBoxedType*)mCurTypeInstance;
// If we failed a lookup here then we better have also failed it in the original type
BF_ASSERT(boxedType->mElementType->ToTypeInstance()->mModule->mHadBuildError || mContext->mFailTypes.Contains(boxedType->mElementType->ToTypeInstance()));
2019-08-23 11:56:54 -07:00
}
}
if (!methodInstance->IsSpecializedGenericMethod())
AddDependency(resolvedParamType, typeInstance, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
PopulateType(resolvedParamType, BfPopulateType_Declaration);
AddDependency(resolvedParamType, mCurTypeInstance, BfDependencyMap::DependencyFlag_LocalUsage);
if ((paramDef != NULL) && (paramDef->mParamDeclaration != NULL) && (paramDef->mParamDeclaration->mInitializer != NULL) &&
(!paramDef->mParamDeclaration->mInitializer->IsA<BfBlock>()))
{
BfMethodState methodState;
2019-08-23 11:56:54 -07:00
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
methodState.mTempKind = BfMethodState::TempKind_Static;
BfTypedValue defaultValue;
if (resolvedParamType->IsConstExprValue())
{
auto constExprType = (BfConstExprValueType*)resolvedParamType;
BfExprEvaluator exprEvaluator(this);
exprEvaluator.mExpectingType = constExprType->mType;
exprEvaluator.GetLiteral(NULL, constExprType->mValue);
defaultValue = exprEvaluator.GetResult();
}
else
{
BfConstResolver constResolver(this);
defaultValue = constResolver.Resolve(paramDef->mParamDeclaration->mInitializer, resolvedParamType, BfConstResolveFlag_NoCast);
if ((defaultValue) && (defaultValue.mType != resolvedParamType))
{
SetAndRestoreValue<bool> prevIgnoreWrite(mBfIRBuilder->mIgnoreWrites, true);
auto castedDefaultValue = Cast(paramDef->mParamDeclaration->mInitializer, defaultValue, resolvedParamType, (BfCastFlags)(BfCastFlags_SilentFail | BfCastFlags_NoConversionOperator));
if ((castedDefaultValue) && (castedDefaultValue.mValue.IsConst()))
{
defaultValue = castedDefaultValue; // Good!
}
else if (!CanCast(defaultValue, resolvedParamType))
2019-08-23 11:56:54 -07:00
{
// We only care that we get a constant value that can be implicitly casted at the callsite- even if that requires
// a conversion operator.
// This should throw an error
defaultValue = Cast(paramDef->mParamDeclaration->mInitializer, defaultValue, resolvedParamType);
AssertErrorState();
if (!defaultValue.mValue.IsConst())
defaultValue = BfTypedValue();
}
}
}
if (!defaultValue)
{
defaultValue = GetDefaultTypedValue(resolvedParamType);
if (!defaultValue.mValue.IsConst())
defaultValue = BfTypedValue();
}
if (defaultValue)
{
if (defaultValue.mType->IsVar())
{
AssertErrorState();
}
else
{
BF_ASSERT(defaultValue.mValue.IsConst());
while ((int)mCurMethodInstance->mDefaultValues.size() < paramDefIdx)
mCurMethodInstance->mDefaultValues.Add(BfTypedValue());
2019-08-23 11:56:54 -07:00
CurrentAddToConstHolder(defaultValue.mValue);
mCurMethodInstance->mDefaultValues.Add(defaultValue);
}
2019-08-23 11:56:54 -07:00
}
}
if ((paramDef != NULL) && (paramDef->mParamKind == BfParamKind_Params))
{
bool addParams = true;
bool isValid = false;
auto resolvedParamTypeInst = resolvedParamType->ToTypeInstance();
if ((resolvedParamTypeInst != NULL) && (resolvedParamTypeInst->mTypeDef == mCompiler->mSpanTypeDef))
{
// Span<T>
isValid = true;
}
else if (resolvedParamType->IsArray())
{
// Array is the 'normal' params type
isValid = true;
}
else if ((resolvedParamType->IsDelegate()) || (resolvedParamType->IsFunction()))
{
hadDelegateParams = true;
// This means we copy the params from a delegate
BfMethodParam methodParam;
methodParam.mResolvedType = resolvedParamType;
methodParam.mParamDefIdx = paramDefIdx;
methodParam.mDelegateParamIdx = 0;
auto invokeMethodInstance = methodParam.GetDelegateParamInvoke();
for (int delegateParamIdx = 0; delegateParamIdx < invokeMethodInstance->GetParamCount(); delegateParamIdx++)
{
methodParam.mDelegateParamIdx = delegateParamIdx;
mCurMethodInstance->mParams.Add(methodParam);
auto paramType = invokeMethodInstance->GetParamType(delegateParamIdx);
if (!methodInstance->IsSpecializedGenericMethod())
AddDependency(paramType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
}
isValid = true;
addParams = false;
}
else if (resolvedParamType->IsGenericParam())
{
auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType);
if (genericParamInstance->mTypeConstraint != NULL)
{
auto typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance();
if (genericParamInstance->mTypeConstraint->IsArray())
{
BfMethodParam methodParam;
methodParam.mResolvedType = resolvedParamType;
methodParam.mParamDefIdx = paramDefIdx;
mCurMethodInstance->mParams.Add(methodParam);
isValid = true;
}
else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) ||
((genericParamInstance != NULL) && (typeInstConstraint != NULL) &&
2019-08-23 11:56:54 -07:00
((typeInstConstraint->mTypeDef == mCompiler->mDelegateTypeDef) || (typeInstConstraint->mTypeDef == mCompiler->mFunctionTypeDef))))
{
mCurMethodInstance->mHadGenericDelegateParams = true;
isValid = true;
addParams = false;
}
}
}
else
{
auto paramTypeInst = resolvedParamType->ToTypeInstance();
if ((paramTypeInst != NULL) &&
((paramTypeInst->mTypeDef == mCompiler->mDelegateTypeDef) || (paramTypeInst->mTypeDef == mCompiler->mFunctionTypeDef)))
{
// If we have a 'params T' and 'T' gets specialized with actually 'Delegate' or 'Function' then just ignore it
isValid = true;
addParams = false;
}
}
if (!isValid)
{
Fail("Parameters with 'params' specifiers can only be used for array, span, delegate, or function types", paramDef->mParamDeclaration->mModToken);
// Failure case, make it an Object[]
resolvedParamType = CreateArrayType(mContext->mBfObjectType, 1);
}
if (addParams)
{
BfMethodParam methodParam;
methodParam.mResolvedType = resolvedParamType;
methodParam.mParamDefIdx = paramDefIdx;
mCurMethodInstance->mParams.push_back(methodParam);
}
if (paramDefIdx < (int)methodDef->mParams.size() - 1)
{
Fail("Only the last parameter can specify 'params'", paramDef->mParamDeclaration->mModToken);
}
}
else
{
BfMethodParam methodParam;
methodParam.mResolvedType = resolvedParamType;
methodParam.mParamDefIdx = paramDefIdx;
mCurMethodInstance->mParams.Add(methodParam);
}
}
if (hadDelegateParams)
{
HashSet<String> usedNames;
Dictionary<int, int> usedParamDefIdx;
for (auto methodParam : methodDef->mParams)
{
usedNames.Add(methodParam->mName);
}
for (auto& methodParam : mCurMethodInstance->mParams)
{
if ((methodParam.mParamDefIdx != -1) && (methodParam.mDelegateParamIdx == 0))
{
int* refCount = NULL;
usedParamDefIdx.TryAdd(methodParam.mParamDefIdx, NULL, &refCount);
(*refCount)++;
}
}
for (auto& methodParam : mCurMethodInstance->mParams)
{
if (methodParam.mDelegateParamIdx != -1)
{
if (usedParamDefIdx[methodParam.mParamDefIdx] > 1)
methodParam.mDelegateParamNameCombine = true;
BfMethodInstance* invokeMethodInstance = methodParam.GetDelegateParamInvoke();
String paramName = invokeMethodInstance->GetParamName(methodParam.mDelegateParamIdx);
if (usedNames.Contains(paramName))
methodParam.mDelegateParamNameCombine = true;
}
}
}
int argIdx = 0;
PopulateType(methodInstance->mReturnType, BfPopulateType_Data);
2019-08-23 11:56:54 -07:00
if (!methodDef->mIsStatic)
{
if (methodInstance->GetParamIsSplat(-1))
argIdx += methodInstance->GetParamType(-1)->GetSplatCount();
else if (!methodInstance->GetOwner()->IsValuelessType())
argIdx++;
}
if (methodInstance->GetStructRetIdx() != -1)
argIdx++;
2019-08-23 11:56:54 -07:00
for (auto& methodParam : mCurMethodInstance->mParams)
{
if (methodParam.mResolvedType->IsMethodRef())
{
methodParam.mIsSplat = true;
}
else if ((methodParam.mResolvedType->IsComposite()) && (methodInstance->AllowsSplatting()))
2019-08-23 11:56:54 -07:00
{
PopulateType(methodParam.mResolvedType, BfPopulateType_Data);
if (methodParam.mResolvedType->IsSplattable())
{
int splatCount = methodParam.mResolvedType->GetSplatCount();
if (argIdx + splatCount <= mCompiler->mOptions.mMaxSplatRegs)
{
methodParam.mIsSplat = true;
argIdx += splatCount;
continue;
}
}
}
argIdx++;
}
bool hasExternSpecifier = (methodDeclaration != NULL) && (methodDeclaration->mExternSpecifier != NULL);
if ((hasExternSpecifier) && (methodDeclaration->mBody != NULL))
{
Fail("Cannot 'extern' and declare a body", methodDeclaration->mExternSpecifier);
}
else if ((methodDeclaration != NULL) && (methodDeclaration->mBody == NULL) && (!hasExternSpecifier) &&
(!typeInstance->IsInterface()) && (!mCurTypeInstance->IsDelegate()) && (!mCurTypeInstance->IsFunction()) &&
(!methodDef->mIsPartial) && (!methodDef->mIsAbstract) &&
(!methodDef->mIsSkipCall))
{
if (methodDeclaration->mEndSemicolon == NULL)
{
AssertParseErrorState();
}
else
{
bool isError = true;
if (typeInstance->IsTypedPrimitive())
{
if (auto operatorDeclaration = BfNodeDynCast<BfOperatorDeclaration>(methodDeclaration))
{
if ((operatorDeclaration->mIsConvOperator) && (methodInstance->mParams.size() == 1))
{
if (((methodInstance->mReturnType == typeInstance) && (methodInstance->GetParamType(0) == typeInstance->GetUnderlyingType())) ||
((methodInstance->mReturnType == typeInstance->GetUnderlyingType()) && (methodInstance->GetParamType(0) == typeInstance)))
{
isError = false;
}
}
}
}
if (isError)
{
Fail(StrFormat("'%s' must declare a body because it is not marked abstract, extern, or partial",
MethodToString(methodInstance).c_str()),
methodDef->GetRefNode());
}
}
}
if (methodDef->IsEmptyPartial())
hasExternSpecifier = true;
if (!methodInstance->IsAutocompleteMethod())
{
auto defaultMethodInstance = methodInstance->mMethodInstanceGroup->mDefault;
int implicitParamCount = methodInstance->GetImplicitParamCount();
int defaultImplicitParamCount = defaultMethodInstance->GetImplicitParamCount();
int actualParamCount = (int)methodInstance->mParams.size() - implicitParamCount;
BF_ASSERT((actualParamCount == defaultMethodInstance->mParams.size() - defaultImplicitParamCount) || (defaultMethodInstance->mHadGenericDelegateParams));
mCurMethodInstance->mHadGenericDelegateParams = defaultMethodInstance->mHadGenericDelegateParams;
int paramIdx = 0;
int defaultParamIdx = 0;
while (true)
{
bool isDone = paramIdx + implicitParamCount >= (int)methodInstance->mParams.size();
bool isDefaultDone = defaultParamIdx + defaultImplicitParamCount >= (int)defaultMethodInstance->mParams.size();
if ((!isDone) && (methodInstance->mParams[paramIdx + implicitParamCount].mDelegateParamIdx >= 0))
{
paramIdx++;
continue;
}
if ((!isDefaultDone) && (defaultMethodInstance->mParams[defaultParamIdx + defaultImplicitParamCount].mDelegateParamIdx >= 0))
{
defaultParamIdx++;
continue;
}
if ((isDone) || (isDefaultDone))
{
// If we have generic delegate params, it's possible we will fail constraints later if we specialize with an invalid type, but we can't allow that
// to cause us to throw an assertion in the declaration here
if (!defaultMethodInstance->mHadGenericDelegateParams)
BF_ASSERT((isDone) && (isDefaultDone));
break;
}
2020-06-01 07:33:00 -07:00
BfType* paramType = defaultMethodInstance->mParams[defaultParamIdx + defaultImplicitParamCount].mResolvedType;
if (paramType->IsRef())
paramType = paramType->GetUnderlyingType();
methodInstance->mParams[paramIdx + implicitParamCount].mWasGenericParam = paramType->IsGenericParam();
2019-08-23 11:56:54 -07:00
paramIdx++;
defaultParamIdx++;
}
}
StringT<128> mangledName;
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), mCurMethodInstance);
for (int paramIdx = 0; paramIdx < methodInstance->GetParamCount(); paramIdx++)
{
auto paramType = methodInstance->GetParamType(paramIdx);
if (paramType->IsComposite())
PopulateType(paramType, BfPopulateType_Data);
if (!methodInstance->IsParamSkipped(paramIdx))
{
if (paramType->IsStruct())
{
//
}
else
{
PopulateType(paramType, BfPopulateType_Declaration);
}
}
}
// Only process method in default unspecialized mode, not in variations
if (methodInstance->mIsUnspecializedVariation)
return;
PopulateType(resolvedReturnType, BfPopulateType_Data);
auto retLLVMType = mBfIRBuilder->MapType(resolvedReturnType);
if (resolvedReturnType->IsValuelessType())
retLLVMType = mBfIRBuilder->GetPrimitiveType(BfTypeCode_None);
if (!mBfIRBuilder->mIgnoreWrites)
{
bool isIntrinsic = false;
if (!methodInstance->mIRFunction)
SetupIRFunction(methodInstance, mangledName, isTemporaryFunc, &isIntrinsic);
if (isIntrinsic)
{
addToWorkList = false;
}
}
auto func = methodInstance->mIRFunction;
// if (methodInstance->mIsReified)
// CheckHotMethod(methodInstance, mangledName);
BfLogSysM("DoMethodDeclaration %s Module: %p Type: %p MethodInst: %p Reified: %d Unspecialized: %d IRFunction: %d MethodId:%llx\n", mangledName.c_str(), this, mCurTypeInstance, methodInstance, methodInstance->mIsReified, mCurTypeInstance->IsUnspecializedType(), methodInstance->mIRFunction.mId, methodInstance->mIdHash);
2019-11-25 06:47:41 -08:00
2019-08-23 11:56:54 -07:00
SizedArray<BfIRMDNode, 8> diParams;
diParams.push_back(mBfIRBuilder->DbgGetType(resolvedReturnType));
if ((!methodDef->mIsStatic) && (typeDef->mIsStatic) && (methodDef->mMethodDeclaration != NULL))
{
//CS0708
Fail("Cannot declare instance members in a static class", methodDef->GetRefNode());
}
if ((methodDef->mIsVirtual) && (methodDef->mGenericParams.size() != 0))
{
Fail("Virtual generic methods not supported", methodDeclaration->mVirtualSpecifier);
methodDef->mIsVirtual = false;
}
if ((methodDeclaration != NULL) && (methodDeclaration->mMutSpecifier != NULL) && (!mCurTypeInstance->IsBoxed()) && (!methodInstance->mIsForeignMethodDef))
{
if (methodDef->mIsStatic)
Warn(0, "Unnecessary 'mut' specifier, static methods have no implicit 'this' target to mutate", methodDeclaration->mMutSpecifier);
else if ((!mCurTypeInstance->IsValueType()) && (!mCurTypeInstance->IsInterface()))
Warn(0, "Unnecessary 'mut' specifier, methods of reference types are implicitly mutating", methodDeclaration->mMutSpecifier);
else if (methodDef->mMethodType == BfMethodType_Ctor)
Warn(0, "Unnecessary 'mut' specifier, constructors are implicitly mutating", methodDeclaration->mMutSpecifier);
else if (methodDef->mMethodType == BfMethodType_Dtor)
Warn(0, "Unnecessary 'mut' specifier, destructors are implicitly mutating", methodDeclaration->mMutSpecifier);
}
if (isTemporaryFunc)
{
// This handles temporary methods for autocomplete types
//BF_ASSERT(mIsScratchModule);
//BF_ASSERT(mCompiler->IsAutocomplete());
BfLogSysM("DoMethodDeclaration isTemporaryFunc bailout\n");
2019-08-23 11:56:54 -07:00
return; // Bail out early for autocomplete pass
}
2020-04-16 00:32:00 -07:00
if ((methodInstance->GetImportCallKind() != BfImportCallKind_None) && (!mBfIRBuilder->mIgnoreWrites) && (!methodInstance->mIRFunction))
{
BfLogSysM("DllImportGlobalVar DoMethodDeclaration processing %p\n", methodInstance);
2020-04-17 10:17:02 -07:00
// If this is in an extension then we did create the global variable already in the original obj
bool doDefine = mExtensionCount == 0;
BfIRValue dllImportGlobalVar = CreateDllImportGlobalVar(methodInstance, doDefine);
2020-04-16 00:32:00 -07:00
methodInstance->mIRFunction = mBfIRBuilder->GetFakeVal();
mFuncReferences[mCurMethodInstance] = dllImportGlobalVar;
}
2019-08-23 11:56:54 -07:00
//TODO: We used to have this (this != mContext->mExternalFuncModule) check, but it caused us to keep around
// an invalid mFuncRefernce (which came from GetMethodInstanceAtIdx) which later got remapped by the
// autocompleter. Why did we have this check anyway?
/*if ((typeInstance->mContext != mContext) && (!methodDef->IsEmptyPartial()))
{
AddMethodReference(methodInstance);
mFuncReferences[methodInstance] = func;
BfLogSysM("Adding func reference (DoMethodDeclaration). Module:%p MethodInst:%p LLVMFunc:%p\n", this, methodInstance, func);
}*/
if (mParentModule != NULL)
{
// 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;
}
if (addToWorkList)
{
bool wasAwaitingDecl = methodInstance->mMethodInstanceGroup->mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingDecl;
if (wasAwaitingDecl)
methodInstance->mMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Decl_AwaitingReference;
if ((!methodDef->mIsAbstract) && (!methodInstance->mIgnoreBody))
{
if (!wasAwaitingDecl)
AddMethodToWorkList(methodInstance);
}
else
{
BfLogSysM("DoMethodDeclaration ignoring method body %d %d %d\n", hasExternSpecifier, methodDef->mIsAbstract, methodInstance->mIgnoreBody);
methodInstance->mIgnoreBody = true;
if (typeInstance->IsUnspecializedType())
{
// Don't hold on to actual Function for unspecialized types
methodInstance->mIRFunction = BfIRFunction();
}
}
}
if ((!methodInstance->IsSpecializedGenericMethodOrType()) && (!mCurTypeInstance->IsBoxed()) &&
(!methodDef->mIsLocalMethod) &&
(!CheckDefineMemberProtection(methodDef->mProtection, methodInstance->mReturnType)) &&
(!methodDef->mReturnTypeRef->IsTemporary()))
2019-08-23 11:56:54 -07:00
{
if (methodDef->mMethodType == BfMethodType_PropertyGetter)
{
//TODO:
//CS0053
Fail(StrFormat("Inconsistent accessibility: property type '%s' is less accessible than property '%s'",
TypeToString(methodInstance->mReturnType).c_str(), MethodToString(methodInstance).c_str()),
methodDef->mReturnTypeRef, true);
}
else
{
//CS0050
Fail(StrFormat("Inconsistent accessibility: return type '%s' is less accessible than method '%s'",
TypeToString(methodInstance->mReturnType).c_str(), MethodToString(methodInstance).c_str()),
methodDef->mReturnTypeRef, true);
}
}
if ((typeInstance->IsInterface()) && (methodDef->mMethodType == BfMethodType_Ctor))
{
Fail("Interfaces cannot contain constructors", methodDeclaration);
}
// Don't compare specialized generic methods against normal methods
if ((((mCurMethodInstance->mIsUnspecialized) || (mCurMethodInstance->mMethodDef->mGenericParams.size() == 0))) &&
(!methodDef->mIsLocalMethod))
{
if (!methodInstance->mIsForeignMethodDef)
{
typeDef->PopulateMemberSets();
BfMethodDef* nextMethod = NULL;
BfMemberSetEntry* entry = NULL;
if (typeDef->mMethodSet.TryGet(BfMemberSetEntry(methodDef), &entry))
nextMethod = (BfMethodDef*)entry->mMemberDef;
while (nextMethod != NULL)
{
auto checkMethod = nextMethod;
nextMethod = nextMethod->mNextWithSameName;
if ((checkMethod != methodDef) && (typeInstance->mMethodInstanceGroups[checkMethod->mIdx].mDefault != NULL))
{
auto checkMethodInstance = typeInstance->mMethodInstanceGroups[checkMethod->mIdx].mDefault;
if (((checkMethodInstance->mChainType == BfMethodChainType_None) || (checkMethodInstance->mChainType == BfMethodChainType_ChainHead)) &&
(checkMethodInstance->GetExplicitInterface() == methodInstance->GetExplicitInterface()) &&
(CompareMethodSignatures(checkMethodInstance, mCurMethodInstance)))
{
bool canChain = false;
if ((methodDef->mParams.empty()) &&
(checkMethodInstance->mMethodDef->mIsStatic == methodInstance->mMethodDef->mIsStatic))
{
if ((methodDef->mMethodType == BfMethodType_CtorNoBody) || (methodDef->mMethodType == BfMethodType_Dtor))
canChain = true;
else if (methodDef->mMethodType == BfMethodType_Normal)
{
if ((methodDef->mName == BF_METHODNAME_MARKMEMBERS) ||
(methodDef->mName == BF_METHODNAME_MARKMEMBERS_STATIC) ||
(methodDef->mName == BF_METHODNAME_FIND_TLS_MEMBERS))
canChain = true;
}
}
if (canChain)
{
bool isBetter;
bool isWorse;
CompareDeclTypes(checkMethodInstance->mMethodDef->mDeclaringType, methodInstance->mMethodDef->mDeclaringType, isBetter, isWorse);
if (isBetter && !isWorse)
{
methodInstance->mChainType = BfMethodChainType_ChainHead;
checkMethodInstance->mChainType = BfMethodChainType_ChainMember;
}
else
{
checkMethodInstance->mChainType = BfMethodChainType_ChainHead;
methodInstance->mChainType = BfMethodChainType_ChainMember;
}
}
else if ((checkMethod->mBody == NULL) && (methodDef->mBody != NULL) &&
(checkMethod->mDeclaringType != methodDef->mDeclaringType))
{
// We're allowed to override an empty-bodied method
checkMethodInstance->mChainType = BfMethodChainType_ChainSkip;
}
else
{
if (!typeInstance->IsTypeMemberAccessible(checkMethod->mDeclaringType, methodDef->mDeclaringType))
continue;
2019-08-23 11:56:54 -07:00
bool silentlyAllow = false;
if (checkMethod->mDeclaringType != methodDef->mDeclaringType)
{
if (typeInstance->IsInterface())
{
if (methodDef->mIsOverride)
silentlyAllow = true;
}
else
silentlyAllow = true;
}
if ((checkMethod->mCommutableKind == BfCommutableKind_Reverse) || (methodDef->mCommutableKind == BfCommutableKind_Reverse))
silentlyAllow = true;
2019-08-23 11:56:54 -07:00
if (!silentlyAllow)
{
if ((!methodDef->mName.IsEmpty()) || (checkMethodInstance->mMethodDef->mIsOperator))
2019-08-23 11:56:54 -07:00
{
auto refNode = methodDef->GetRefNode();
auto bfError = Fail("Method already declared with the same parameter types", refNode, true);
if (bfError != NULL)
mCompiler->mPassInstance->MoreInfo("First declaration", checkMethod->GetRefNode());
}
}
}
}
}
}
}
// Virtual methods give their error while slotting
if ((!typeInstance->IsBoxed()) && (!methodDef->mIsVirtual) && (methodDef->mProtection != BfProtection_Private) &&
(!methodDef->mIsLocalMethod) &&
(!methodInstance->mIsForeignMethodDef) && (typeInstance->mBaseType != NULL) &&
(methodDef->mMethodType == BfMethodType_Normal) && (methodDef->mMethodDeclaration != NULL))
{
bool foundHiddenMethod = false;
auto baseType = typeInstance->mBaseType;
while (baseType != NULL)
{
//for (int checkMethodIdx = 0; checkMethodIdx < (int) baseType->mTypeDef->mMethods.size(); checkMethodIdx++)
auto baseTypeDef = baseType->mTypeDef;
baseTypeDef->PopulateMemberSets();
BfMethodDef* checkMethod = NULL;
BfMemberSetEntry* entry = NULL;
if (baseTypeDef->mMethodSet.TryGet(BfMemberSetEntry(methodDef), &entry))
checkMethod = (BfMethodDef*)entry->mMemberDef;
while (checkMethod != NULL)
{
if (checkMethod->mMethodDeclaration == NULL)
continue;
if (baseType->mMethodInstanceGroups.size() == 0)
{
BF_ASSERT(baseType->IsIncomplete() && mCompiler->IsAutocomplete());
break;
}
if (checkMethod == methodDef)
continue;
if (checkMethod->mName != methodDef->mName)
continue;
auto checkMethodInstance = GetRawMethodInstanceAtIdx(baseType, checkMethod->mIdx);
if (checkMethodInstance != NULL)
{
if ((checkMethodInstance->GetExplicitInterface() == methodInstance->GetExplicitInterface()) &&
(checkMethod->mProtection != BfProtection_Private) &&
(CompareMethodSignatures(checkMethodInstance, mCurMethodInstance)))
{
if (!methodDef->mIsNew)
{
BfAstNode* refNode = methodInstance->mMethodDef->GetRefNode();
if (refNode != NULL)
{
BfError* bfError = Warn(BfWarning_CS0114_MethodHidesInherited, StrFormat("Method hides inherited member from '%s'. Use the 'new' keyword if the hiding was intentional.", TypeToString(baseType).c_str()), refNode); //CDH TODO should we mention override keyword in warning text?
if (bfError != NULL)
bfError->mIsPersistent = true;
}
}
foundHiddenMethod = true;
break;
}
}
checkMethod = checkMethod->mNextWithSameName;
}
if (foundHiddenMethod)
break;
baseType = baseType->mBaseType;
}
if ((methodDef->mIsNew) && (!foundHiddenMethod))
{
auto propertyDeclaration = methodDef->GetPropertyDeclaration();
auto tokenNode = (propertyDeclaration != NULL) ? propertyDeclaration->mNewSpecifier :
methodDeclaration->mNewSpecifier;
Fail("Method does not hide an inherited member. The 'new' keyword is not required", tokenNode, true);
}
}
}
mCompiler->mStats.mMethodDeclarations++;
mCompiler->UpdateCompletion();
}
void BfModule::UniqueSlotVirtualMethod(BfMethodInstance* methodInstance)
{
BF_ASSERT(methodInstance->GetOwner() == mCurTypeInstance);
auto typeInstance = mCurTypeInstance;
auto methodDef = methodInstance->mMethodDef;
int virtualMethodMatchIdx = -1;
if (typeInstance->mHotTypeData != NULL)
{
if (typeInstance->mHotTypeData->mVTableOrigLength != -1)
{
BF_ASSERT(mCompiler->IsHotCompile());
// In the 'normal' case we'd assert that mIsOverride is false, but if we can't find the declaring method then we
// may slot this override anyway (?)
int vTableStart = 0;
auto implBaseType = typeInstance->GetImplBaseType();
if (implBaseType != NULL)
vTableStart = implBaseType->mVirtualMethodTableSize;
2019-08-23 11:56:54 -07:00
StringT<128> mangledName;
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), methodInstance);
for (int checkIdxOfs = 0; checkIdxOfs < (int)typeInstance->mHotTypeData->mVTableEntries.size(); checkIdxOfs++)
{
// O(1) checking when virtual methods haven't changed
int checkIdx = (typeInstance->mVirtualMethodTableSize - vTableStart + checkIdxOfs) % (int)typeInstance->mHotTypeData->mVTableEntries.size();
auto& entry = typeInstance->mHotTypeData->mVTableEntries[checkIdx];
if (mangledName == entry.mFuncName)
{
virtualMethodMatchIdx = vTableStart + checkIdx;
break;
}
}
if (virtualMethodMatchIdx != -1)
{
methodInstance->mVirtualTableIdx = virtualMethodMatchIdx;
typeInstance->mVirtualMethodTable[virtualMethodMatchIdx].mDeclaringMethod = methodInstance;
typeInstance->mVirtualMethodTable[virtualMethodMatchIdx].mImplementingMethod = methodInstance;
}
typeInstance->mVirtualMethodTableSize = (int)typeInstance->mVirtualMethodTable.size();
}
}
if (virtualMethodMatchIdx == -1)
{
methodInstance->mVirtualTableIdx = typeInstance->mVirtualMethodTableSize++;
BfVirtualMethodEntry entry = { methodInstance, methodInstance };
typeInstance->mVirtualMethodTable.push_back(entry);
}
}
void BfModule::CompareDeclTypes(BfTypeDef* newDeclType, BfTypeDef* prevDeclType, bool& isBetter, bool& isWorse)
{
if ((!prevDeclType->IsExtension()) && (newDeclType->IsExtension()))
{
// When we provide an extension override in the same project a root type override
isBetter = true;
isWorse = false;
}
else
{
isBetter = newDeclType->mProject->ContainsReference(prevDeclType->mProject);
isWorse = prevDeclType->mProject->ContainsReference(newDeclType->mProject);
}
}
bool BfModule::SlotVirtualMethod(BfMethodInstance* methodInstance, BfAmbiguityContext* ambiguityContext)
{
BP_ZONE("BfModule::SlotVirtualMethod");
if (mCurTypeInstance->IsUnspecializedTypeVariation())
return false;
auto _AddVirtualDecl = [&](BfMethodInstance* declMethodInstance)
{
if (!mCompiler->mOptions.mAllowHotSwapping)
return;
if ((!methodInstance->mIsReified) || (!declMethodInstance->mIsReified))
return;
if (methodInstance->mHotMethod == NULL)
CheckHotMethod(methodInstance, "");
if (methodInstance->mHotMethod == NULL)
return;
//BF_ASSERT(declMethodInstance->mHotMethod != NULL);
if (declMethodInstance->mHotMethod == NULL)
CheckHotMethod(declMethodInstance, "");
auto virtualDecl = mCompiler->mHotData->GetVirtualDeclaration(declMethodInstance->mHotMethod);
virtualDecl->mRefCount++;
methodInstance->mHotMethod->mReferences.Add(virtualDecl);
};
auto typeInstance = mCurTypeInstance;
auto typeDef = typeInstance->mTypeDef;
auto methodDef = methodInstance->mMethodDef;
auto methodDeclaration = methodDef->GetMethodDeclaration();
auto propertyDeclaration = methodDef->GetPropertyDeclaration();
auto propertyMethodDeclaration = methodDef->GetPropertyMethodDeclaration();
BfAstNode* declaringNode = methodDeclaration;
if (propertyMethodDeclaration != NULL)
declaringNode = propertyMethodDeclaration->mNameNode;
BfMethodInstance* methodOverriden = NULL;
bool usedMethod = false;
BfTokenNode* virtualToken = NULL;
if (propertyDeclaration != NULL)
virtualToken = propertyDeclaration->mVirtualSpecifier;
else if (methodDeclaration != NULL)
virtualToken = methodDeclaration->mVirtualSpecifier;
if ((methodDef->mIsVirtual) && (methodDef->mIsStatic))
{
Fail("Static members cannot be marked as override, virtual, or abstract", virtualToken, true);
}
else if (methodDef->mIsVirtual)
{
if (mCompiler->IsHotCompile())
mContext->EnsureHotMangledVirtualMethodName(methodInstance);
if ((methodDef->mProtection == BfProtection_Private) && (virtualToken != NULL))
Fail("Virtual or abstract members cannot be private", virtualToken, true);
BfTypeInstance* checkBase = typeInstance;
// If we are in an extension, look in ourselves first
if (methodDef->mDeclaringType->mTypeDeclaration == typeInstance->mTypeDef->mTypeDeclaration)
checkBase = checkBase->mBaseType;
if (typeInstance->IsValueType())
{
if (typeInstance->mBaseType == NULL)
return false; // It's actually ValueType
// We allow structs to override object methods for when they get boxed, so just ignore 'override' keyword until it gets boxed
if (!methodDef->mIsOverride)
{
Fail("Structs cannot have virtual methods", virtualToken, true);
return false;
}
checkBase = mContext->mBfObjectType;
if (checkBase->mVirtualMethodTableSize == 0)
PopulateType(checkBase, BfPopulateType_Full);
if (checkBase->mDefineState < BfTypeDefineState_DefinedAndMethodsSlotted)
return false; // System circular ref, don't do struct checking on those
}
int virtualMethodMatchIdx = -1;
bool hadHidingError = false;
BfMethodInstance* bestOverrideMethodInstance = NULL;
BfMethodInstance* ambiguousOverrideMethodInstance = NULL;
int bestOverrideMethodIdx = -1;
int ambiguousOverrideMethodIdx = -1;
if (checkBase != NULL)
{
auto& baseVirtualMethodTable = checkBase->mVirtualMethodTable;
auto lookupType = checkBase;
while (lookupType != NULL)
{
lookupType->mTypeDef->PopulateMemberSets();
BfMethodDef* nextMethodDef = NULL;
BfMemberSetEntry* entry;
if (lookupType->mTypeDef->mMethodSet.TryGetWith(methodInstance->mMethodDef->mName, &entry))
nextMethodDef = (BfMethodDef*)entry->mMemberDef;
while (nextMethodDef != NULL)
{
auto checkMethodDef = nextMethodDef;
nextMethodDef = nextMethodDef->mNextWithSameName;
if ((!checkMethodDef->mIsVirtual) || (checkMethodDef->mIsOverride))
continue;
BfMethodInstance* lookupMethodInstance = lookupType->mMethodInstanceGroups[checkMethodDef->mIdx].mDefault;
if ((lookupMethodInstance == NULL) || (lookupMethodInstance->mVirtualTableIdx == -1))
continue;
int checkMethodIdx = lookupMethodInstance->mVirtualTableIdx;
auto& baseMethodRef = baseVirtualMethodTable[checkMethodIdx];
if (baseMethodRef.mDeclaringMethod.mMethodNum == -1)
{
BF_ASSERT(mCompiler->mOptions.mHasVDataExtender);
continue;
}
BfMethodInstance* baseMethodInstance = baseVirtualMethodTable[checkMethodIdx].mDeclaringMethod;
if (baseMethodInstance == NULL)
{
AssertErrorState();
continue;
}
if ((baseMethodInstance != NULL) && (CompareMethodSignatures(baseMethodInstance, methodInstance)))
{
if (methodDef->mIsOverride)
{
BfMethodInstance* checkMethodInstance;
if (typeInstance->IsValueType())
checkMethodInstance = checkBase->mVirtualMethodTable[checkMethodIdx].mDeclaringMethod;
else
checkMethodInstance = typeInstance->mVirtualMethodTable[checkMethodIdx].mDeclaringMethod;
auto newDeclType = checkMethodInstance->mMethodDef->mDeclaringType;
if (!typeInstance->IsTypeMemberAccessible(newDeclType, methodDef->mDeclaringType->mProject))
continue;
if (bestOverrideMethodInstance != NULL)
{
auto prevDeclType = bestOverrideMethodInstance->mMethodDef->mDeclaringType;
bool isBetter = newDeclType->mProject->ContainsReference(prevDeclType->mProject);
bool isWorse = prevDeclType->mProject->ContainsReference(newDeclType->mProject);
if (isBetter == isWorse)
{
ambiguousOverrideMethodInstance = bestOverrideMethodInstance;
}
else
{
if (isWorse)
continue;
ambiguousOverrideMethodInstance = NULL;
}
}
bestOverrideMethodInstance = checkMethodInstance;
bestOverrideMethodIdx = checkMethodIdx;
}
else
{
if ((baseMethodInstance->GetOwner() != methodInstance->GetOwner()) && (!methodDef->mIsNew))
{
if (!hadHidingError)
Warn(BfWarning_CS0114_MethodHidesInherited, StrFormat("Method hides inherited member from '%s'. Use either 'override' or 'new'.", TypeToString(checkBase).c_str()), declaringNode);
hadHidingError = true;
}
}
}
}
lookupType = lookupType->mBaseType;
}
}
//TODO:
if ((checkBase != NULL)
&& (false)
)
{
auto& baseVirtualMethodTable = checkBase->mVirtualMethodTable;
for (int checkMethodIdx = (int) baseVirtualMethodTable.size() - 1; checkMethodIdx >= 0; checkMethodIdx--)
{
auto& baseMethodRef = baseVirtualMethodTable[checkMethodIdx];
if (baseMethodRef.mDeclaringMethod.mMethodNum == -1)
{
BF_ASSERT(mCompiler->mOptions.mHasVDataExtender);
continue;
}
BfMethodInstance* baseMethodInstance = baseVirtualMethodTable[checkMethodIdx].mDeclaringMethod;
if (baseMethodInstance == NULL)
{
AssertErrorState();
continue;
}
if ((baseMethodInstance != NULL) && (CompareMethodSignatures(baseMethodInstance, methodInstance)))
{
if (methodDef->mIsOverride)
{
BfMethodInstance* checkMethodInstance;
if (typeInstance->IsValueType())
checkMethodInstance = checkBase->mVirtualMethodTable[checkMethodIdx].mDeclaringMethod;
else
checkMethodInstance = typeInstance->mVirtualMethodTable[checkMethodIdx].mDeclaringMethod;
auto newDeclType = checkMethodInstance->mMethodDef->mDeclaringType;
if (!typeInstance->IsTypeMemberAccessible(newDeclType, methodDef->mDeclaringType->mProject))
continue;
if (bestOverrideMethodInstance != NULL)
{
auto prevDeclType = bestOverrideMethodInstance->mMethodDef->mDeclaringType;
bool isBetter = newDeclType->mProject->ContainsReference(prevDeclType->mProject);
bool isWorse = prevDeclType->mProject->ContainsReference(newDeclType->mProject);
if (isBetter == isWorse)
{
ambiguousOverrideMethodInstance = bestOverrideMethodInstance;
}
else
{
if (isWorse)
continue;
ambiguousOverrideMethodInstance = NULL;
}
}
bestOverrideMethodInstance = checkMethodInstance;
bestOverrideMethodIdx = checkMethodIdx;
}
else
{
if ((baseMethodInstance->GetOwner() != methodInstance->GetOwner()) && (!methodDef->mIsNew))
{
if (!hadHidingError)
Warn(BfWarning_CS0114_MethodHidesInherited, StrFormat("Method hides inherited member from '%s'. Use either 'override' or 'new'.", TypeToString(checkBase).c_str()), declaringNode);
hadHidingError = true;
}
}
}
}
}
if (bestOverrideMethodInstance != NULL)
{
if (ambiguousOverrideMethodInstance != NULL)
{
bool allow = false;
// If neither of these declarations "include" each other then it's okay. This can happen when we have two extensions that create the same virtual method but with different constraints.
// When when specialize, the specialized type will determine wither it has illegally pulled in both declarations or not
bool canSeeEachOther = false;
//
{
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, ambiguousOverrideMethodInstance);
if (bestOverrideMethodInstance->GetOwner()->IsTypeMemberIncluded(bestOverrideMethodInstance->mMethodDef->mDeclaringType, ambiguousOverrideMethodInstance->mMethodDef->mDeclaringType, this))
canSeeEachOther = true;
}
//
{
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, bestOverrideMethodInstance);
if (ambiguousOverrideMethodInstance->GetOwner()->IsTypeMemberIncluded(ambiguousOverrideMethodInstance->mMethodDef->mDeclaringType, bestOverrideMethodInstance->mMethodDef->mDeclaringType, this))
canSeeEachOther = true;
}
if (!canSeeEachOther)
{
BF_ASSERT(bestOverrideMethodInstance->GetOwner()->IsUnspecializedType() && ambiguousOverrideMethodInstance->GetOwner()->IsUnspecializedType());
}
else
{
auto error = Fail(StrFormat("Method '%s' is an ambiguous override", MethodToString(methodInstance).c_str()), methodDef->GetRefNode());
if (error != NULL)
{
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' is a candidate", MethodToString(bestOverrideMethodInstance).c_str()), bestOverrideMethodInstance->mMethodDef->GetRefNode());
mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' is a candidate", MethodToString(ambiguousOverrideMethodInstance).c_str()), ambiguousOverrideMethodInstance->mMethodDef->GetRefNode());
}
}
}
auto& baseVirtualMethodTable = checkBase->mVirtualMethodTable;
BfMethodInstance* baseVirtualMethodInstance = baseVirtualMethodTable[bestOverrideMethodIdx].mDeclaringMethod;
if ((baseVirtualMethodInstance != methodInstance) && (methodDef->mIsOverride))
{
if (baseVirtualMethodInstance->mReturnType != methodInstance->mReturnType)
{
BfTypeReference* returnTypeRef;
if (auto propertyDeclaration = methodDef->GetPropertyDeclaration())
{
returnTypeRef = propertyDeclaration->mTypeRef;
Fail("Property differs from overridden property by type", returnTypeRef, true);
}
else if (auto methodDeclaration = methodDef->GetMethodDeclaration())
{
returnTypeRef = methodDeclaration->mReturnType;
Fail("Method differs from overridden method by return value", returnTypeRef, true);
}
else
{
Fail(StrFormat("Internal error with method '%s'", MethodToString(methodInstance).c_str()));
}
return usedMethod;
}
}
if (methodDef->mIsOverride)
{
// We can have multiple matches when a virtual method is used to implement interface methods
// It doesn't matter what we set mVirtualTableIdx, as all base classes will override all matching
// vtable entries for this method signature
virtualMethodMatchIdx = bestOverrideMethodIdx;
if (!typeInstance->IsValueType())
methodInstance->mVirtualTableIdx = virtualMethodMatchIdx;
if (typeInstance->IsValueType())
{
methodOverriden = checkBase->mVirtualMethodTable[virtualMethodMatchIdx].mImplementingMethod;
}
else
{
methodInstance->mVirtualTableIdx = virtualMethodMatchIdx;
auto& overridenRef = typeInstance->mVirtualMethodTable[virtualMethodMatchIdx].mImplementingMethod;
if (overridenRef.mKind != BfMethodRefKind_AmbiguousRef)
{
methodOverriden = overridenRef;
BfMethodInstance* setMethodInstance = methodInstance;
bool doOverride = true;
if (methodOverriden->GetOwner() == methodInstance->GetOwner())
{
bool isBetter = false;
bool isWorse = false;
CompareDeclTypes(methodInstance->mMethodDef->mDeclaringType, methodOverriden->mMethodDef->mDeclaringType, isBetter, isWorse);
if (isBetter == isWorse)
{
// We have to resolve later per-project
if ((ambiguityContext != NULL) && (ambiguityContext->mIsProjectSpecific))
{
auto declMethodInstance = checkBase->mVirtualMethodTable[virtualMethodMatchIdx].mDeclaringMethod;
ambiguityContext->Add(virtualMethodMatchIdx, NULL, -1, methodOverriden, methodInstance);
}
if ((ambiguityContext == NULL) || (!ambiguityContext->mIsProjectSpecific))
typeInstance->mVirtualMethodTable[virtualMethodMatchIdx].mImplementingMethod.mKind = BfMethodRefKind_AmbiguousRef;
doOverride = false;
}
else
{
if ((isBetter) && (ambiguityContext != NULL))
{
ambiguityContext->Remove(virtualMethodMatchIdx);
}
doOverride = isBetter;
}
}
if (doOverride)
{
auto declMethodInstance = (BfMethodInstance*)typeInstance->mVirtualMethodTable[virtualMethodMatchIdx].mDeclaringMethod;
_AddVirtualDecl(declMethodInstance);
setMethodInstance->mVirtualTableIdx = virtualMethodMatchIdx;
2019-08-23 11:56:54 -07:00
typeInstance->mVirtualMethodTable[virtualMethodMatchIdx].mImplementingMethod = setMethodInstance;
}
}
}
if (methodOverriden != NULL)
{
auto prevProtection = methodOverriden->mMethodDef->mProtection;
if ((methodDef->mProtection != prevProtection) && (methodDef->mMethodType != BfMethodType_Dtor))
{
const char* protectionNames[] = {"hidden", "private", "protected", "public"};
BfAstNode* protectionRefNode = NULL;
if (auto propertyMethodDeclaration = methodDef->GetPropertyMethodDeclaration())
{
protectionRefNode = propertyMethodDeclaration->mProtectionSpecifier;
if (protectionRefNode == NULL)
protectionRefNode = propertyMethodDeclaration->mPropertyDeclaration->mProtectionSpecifier;
if (protectionRefNode == NULL)
protectionRefNode = propertyMethodDeclaration->mPropertyDeclaration->mNameNode;
}
else if (auto methodDeclaration = methodDef->GetMethodDeclaration())
{
protectionRefNode = methodDeclaration->mProtectionSpecifier;
if (protectionRefNode == NULL)
protectionRefNode = methodDeclaration->mNameNode;
}
if (protectionRefNode != NULL)
Fail(StrFormat("Cannot change access modifiers when overriding '%s' inherited member", protectionNames[(int)prevProtection]), protectionRefNode, true);
}
}
}
}
if ((virtualMethodMatchIdx == -1) && ((ambiguityContext == NULL) || (!ambiguityContext->mIsReslotting)))
{
if (methodDef->mIsOverride)
{
BfTokenNode* overrideToken = NULL;
if (auto propertyMethodDeclaration = methodDef->GetPropertyMethodDeclaration())
overrideToken = propertyMethodDeclaration->mPropertyDeclaration->mVirtualSpecifier;
else if (auto methodDeclaration = methodDef->GetMethodDeclaration())
overrideToken = methodDeclaration->mVirtualSpecifier;
if (overrideToken != NULL)
2019-09-30 12:24:02 -07:00
{
if ((propertyDeclaration != NULL) && (propertyDeclaration->mNameNode != NULL) &&
((methodDef->mMethodType == BfMethodType_PropertyGetter) || (methodDef->mMethodType == BfMethodType_PropertySetter)))
Fail(StrFormat("No suitable method found to override for '%s.%s'",
propertyDeclaration->mNameNode->ToString().c_str(), (methodDef->mMethodType == BfMethodType_PropertyGetter) ? "get" : "set"), overrideToken, true);
else
Fail("No suitable method found to override", overrideToken, true);
2019-08-23 11:56:54 -07:00
}
return usedMethod;
}
UniqueSlotVirtualMethod(methodInstance);
}
else
usedMethod = true;
if (typeInstance->IsValueType())
return usedMethod;
}
bool foundInterface = false;
bool hadAnyMatch = false;
// See if this method matches interfaces
for (auto& ifaceTypeInst : typeInstance->mInterfaces)
{
auto ifaceInst = ifaceTypeInst.mInterfaceType;
if (ifaceInst->IsIncomplete())
PopulateType(ifaceInst, BfPopulateType_DataAndMethods);
int startIdx = ifaceTypeInst.mStartInterfaceTableIdx;
int iMethodCount = (int)ifaceInst->mMethodInstanceGroups.size();
// See "bidirectional" rules mentioned in DoTypeInstanceMethodProcessing
if ((!typeInstance->IsTypeMemberAccessible(methodDef->mDeclaringType, ifaceTypeInst.mDeclaringType)) &&
(!typeInstance->IsTypeMemberAccessible(ifaceTypeInst.mDeclaringType, methodDef->mDeclaringType)))
continue;
if (!typeInstance->IsTypeMemberIncluded(methodDef->mDeclaringType, ifaceTypeInst.mDeclaringType, this))
continue;
bool hadMatch = false;
2019-12-13 14:22:23 -08:00
BfMethodInstance* hadNameMatch = NULL;
2019-08-23 11:56:54 -07:00
BfType* expectedReturnType = NULL;
bool showedError = false;
// We go through our VTable looking for NULL entries within the interface sections
// The only instance of finding a "better" method is when we have explicitly interface-dotted method
// because a normal method declared in this type could have matched earlier
//for (int iMethodIdx = 0; iMethodIdx < iMethodCount; iMethodIdx++)
ifaceInst->mTypeDef->PopulateMemberSets();
BfMethodDef* checkMethodDef = NULL;
BfMemberSetEntry* entry;
if (ifaceInst->mTypeDef->mMethodSet.TryGetWith(methodInstance->mMethodDef->mName, &entry))
checkMethodDef = (BfMethodDef*)entry->mMemberDef;
while (checkMethodDef != NULL)
{
int iMethodIdx = checkMethodDef->mIdx;
int iTableIdx = iMethodIdx + startIdx;
BfTypeInterfaceMethodEntry* interfaceMethodEntry = &typeInstance->mInterfaceMethodTable[iTableIdx];
auto iMethodPtr = &interfaceMethodEntry->mMethodRef;
bool storeIFaceMethod = false;
if ((mCompiler->mPassInstance->HasFailed()) && (iMethodIdx >= (int)ifaceInst->mMethodInstanceGroups.size()))
continue;
auto& iMethodGroup = ifaceInst->mMethodInstanceGroups[iMethodIdx];
auto iMethodInst = iMethodGroup.mDefault;
if (iMethodInst == NULL)
{
if ((!ifaceInst->IsGenericTypeInstance()) || (!ifaceInst->mTypeDef->mIsCombinedPartial))
AssertErrorState();
continue;
}
if (iMethodInst->mMethodDef->mName == methodInstance->mMethodDef->mName)
2019-12-13 14:22:23 -08:00
hadNameMatch = iMethodInst;
2019-08-23 11:56:54 -07:00
bool doesMethodSignatureMatch = CompareMethodSignatures(iMethodInst, methodInstance);
if ((!doesMethodSignatureMatch) && (iMethodInst->GetNumGenericParams() == 0) && (interfaceMethodEntry->mMethodRef.IsNull()))
{
doesMethodSignatureMatch = IsCompatibleInterfaceMethod(iMethodInst, methodInstance);
}
if ((doesMethodSignatureMatch) && (methodInstance->GetOwner()->IsValueType()))
{
if ((!iMethodInst->mMethodDef->mIsMutating) && (methodInstance->mMethodDef->mIsMutating))
{
if ((methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface == ifaceInst))
{
auto error = mCompiler->mPassInstance->Fail(StrFormat("Implementation method '%s' cannot specify 'mut' because the interface method does not allow it",
MethodToString(methodInstance).c_str()), methodInstance->mMethodDef->GetMutNode());
if (error != NULL)
mCompiler->mPassInstance->MoreInfo(StrFormat("Declare the interface method as 'mut' to allow matching 'mut' implementations"), iMethodInst->mMethodDef->mMethodDeclaration);
showedError = true;
}
}
}
if (doesMethodSignatureMatch)
{
usedMethod = true;
hadMatch = true;
hadAnyMatch = true;
storeIFaceMethod = true;
if ((iMethodPtr->mKind != BfMethodRefKind_AmbiguousRef) && (iMethodPtr->mTypeInstance != NULL))
{
auto prevMethod = (BfMethodInstance*)*iMethodPtr;
if ((mCompiler->mIsResolveOnly) && (prevMethod == methodInstance))
{
// When autocompletion regenerates a single method body but not the whole type then
// we will see ourselves in the vtable already
return usedMethod;
}
bool isBetter = false;
bool isWorse = false;
isBetter = (methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface != NULL);
isWorse = (prevMethod->mMethodInfoEx != NULL) && (prevMethod->mMethodInfoEx->mExplicitInterface != NULL);
if (isBetter == isWorse)
CompareDeclTypes(methodInstance->mMethodDef->mDeclaringType, prevMethod->mMethodDef->mDeclaringType, isBetter, isWorse);
if (isBetter == isWorse)
{
if (ambiguityContext != NULL)
{
ambiguityContext->Add(~iTableIdx, &ifaceTypeInst, iMethodIdx, prevMethod, methodInstance);
}
iMethodPtr->mKind = BfMethodRefKind_AmbiguousRef;
storeIFaceMethod = true;
}
else
{
if ((isBetter) && (ambiguityContext != NULL))
ambiguityContext->Remove(~iTableIdx);
storeIFaceMethod = isBetter;
}
}
}
if (storeIFaceMethod)
{
if (methodInstance->GetNumGenericParams() != 0)
_AddVirtualDecl(iMethodInst);
2019-08-23 11:56:54 -07:00
*iMethodPtr = methodInstance;
}
checkMethodDef = checkMethodDef->mNextWithSameName;
2019-12-13 14:22:23 -08:00
}
2019-08-23 11:56:54 -07:00
if ((methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface == ifaceInst) && (!hadMatch) && (!showedError))
{
if (expectedReturnType != NULL)
Fail(StrFormat("Wrong return type, expected '%s'", TypeToString(expectedReturnType).c_str()), declaringNode, true);
2019-12-13 14:22:23 -08:00
else if (hadNameMatch != NULL)
{
auto error = Fail("Method parameters don't match interface method", declaringNode, true);
if (error != NULL)
mCompiler->mPassInstance->MoreInfo("See interface method declaration", hadNameMatch->mMethodDef->GetRefNode());
}
2019-08-23 11:56:54 -07:00
else
2019-12-13 14:22:23 -08:00
{
auto propertyDecl = methodDef->GetPropertyDeclaration();
if (propertyDecl != NULL)
{
auto propertyMethodDecl = methodDef->GetPropertyMethodDeclaration();
2020-05-08 11:11:01 -07:00
String name;
if (auto indexerDeclaration = BfNodeDynCast<BfIndexerDeclaration>(propertyDecl))
name = "this[]";
else if (propertyDecl->mNameNode != NULL)
propertyDecl->mNameNode->ToString(name);
Fail(StrFormat("Property '%s' %s accessor not defined in interface '%s'", name.c_str(),
2019-12-13 14:22:23 -08:00
(methodDef->mMethodType == BfMethodType_PropertyGetter) ? "get" : "set", TypeToString(ifaceInst).c_str()), methodDef->GetRefNode(), true);
}
else
Fail(StrFormat("Method '%s' not found in interface '%s'", methodDef->mName.c_str(), TypeToString(ifaceInst).c_str()), methodDef->GetRefNode(), true);
}
2019-08-23 11:56:54 -07:00
}
}
// Any overriden virtual methods that were used in interfaces also need to be replaced
if (methodOverriden != NULL)
{
for (auto& methodEntry : typeInstance->mInterfaceMethodTable)
{
if (methodEntry.mMethodRef == methodOverriden)
methodEntry.mMethodRef = methodInstance;
}
}
return usedMethod;
}
bool BfModule::SlotInterfaceMethod(BfMethodInstance* methodInstance)
{
auto typeInstance = mCurTypeInstance;
auto methodDef = methodInstance->mMethodDef;
if (methodDef->mMethodType == BfMethodType_Ctor)
return true;
bool foundOverride = false;
if ((methodDef->mBody == NULL) && (methodDef->mProtection == BfProtection_Private))
{
Fail("Private interface methods must provide a body", methodDef->GetMethodDeclaration()->mProtectionSpecifier);
}
if ((methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface != NULL) && (!methodDef->mIsOverride))
{
Fail("Explicit interfaces can only be specified for overrides in interface declarations", methodDef->GetMethodDeclaration()->mExplicitInterface);
}
BfAstNode* declaringNode = methodDef->GetRefNode();
for (auto& ifaceTypeInst : typeInstance->mInterfaces)
{
auto ifaceInst = ifaceTypeInst.mInterfaceType;
int startIdx = ifaceTypeInst.mStartInterfaceTableIdx;
int iMethodCount = (int)ifaceInst->mMethodInstanceGroups.size();
if ((methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface != ifaceInst))
continue;
ifaceInst->mTypeDef->PopulateMemberSets();
BfMethodDef* nextMethod = NULL;
BfMemberSetEntry* entry = NULL;
if (ifaceInst->mTypeDef->mMethodSet.TryGet(BfMemberSetEntry(methodDef), &entry))
nextMethod = (BfMethodDef*)entry->mMemberDef;
while (nextMethod != NULL)
{
auto checkMethod = nextMethod;
nextMethod = nextMethod->mNextWithSameName;
auto ifaceMethod = ifaceInst->mMethodInstanceGroups[checkMethod->mIdx].mDefault;
if (ifaceMethod == NULL)
continue;
if (CompareMethodSignatures(ifaceMethod, methodInstance))
{
if (methodDef->mIsOverride)
{
if (ifaceMethod->mMethodDef->mProtection == BfProtection_Private)
{
auto error = Fail(StrFormat("Interface method '%s' cannot override private interface method '%s'", MethodToString(methodInstance).c_str(), MethodToString(ifaceMethod).c_str()), declaringNode);
if (error != NULL)
{
mCompiler->mPassInstance->MoreInfo("See base interface method", ifaceMethod->mMethodDef->GetRefNode());
}
}
foundOverride = true;
}
else if (!methodDef->mIsNew)
{
Warn(BfWarning_CS0114_MethodHidesInherited, StrFormat("Method hides inherited member from '%s'. Use the 'new' keyword if hiding was intentional.", TypeToString(ifaceInst).c_str()), declaringNode);
}
}
}
}
if ((methodDef->mIsOverride) && (!foundOverride))
{
// This allows us to declare a method in the base definition or in an extension and then provide
// an implementation in a more-specific extension
typeInstance->mTypeDef->PopulateMemberSets();
BfMethodDef* nextMethod = NULL;
BfMemberSetEntry* entry = NULL;
if (typeInstance->mTypeDef->mMethodSet.TryGet(BfMemberSetEntry(methodDef), &entry))
nextMethod = (BfMethodDef*)entry->mMemberDef;
while (nextMethod != NULL)
{
auto checkMethod = nextMethod;
nextMethod = nextMethod->mNextWithSameName;
auto ifaceMethod = typeInstance->mMethodInstanceGroups[checkMethod->mIdx].mDefault;
if (ifaceMethod == NULL)
continue;
if (ifaceMethod->mMethodDef->mDeclaringType == methodInstance->mMethodDef->mDeclaringType)
continue;
if (CompareMethodSignatures(ifaceMethod, methodInstance))
{
foundOverride = true;
}
}
// for (int methodIdx = 0; methodIdx < typeInstance->mMethodInstanceGroups.size(); methodIdx++)
// {
// auto ifaceMethod = typeInstance->mMethodInstanceGroups[methodIdx].mDefault;
// if (ifaceMethod == NULL)
// continue;
// if (ifaceMethod->mMethodDef->mDeclaringType == methodInstance->mMethodDef->mDeclaringType)
// continue;
//
// if (CompareMethodSignatures(ifaceMethod, methodInstance))
// {
// foundOverride = true;
// }
// }
}
if (methodDef->mIsOverride)
{
if (!foundOverride)
{
BfTokenNode* overrideToken = NULL;
if (auto propertyDeclaration = methodDef->GetPropertyDeclaration())
overrideToken = propertyDeclaration->mVirtualSpecifier;
else if (auto methodDeclaration = methodDef->GetMethodDeclaration())
overrideToken = methodDeclaration->mVirtualSpecifier;
if (overrideToken != NULL)
{
Fail("No suitable method found to override", overrideToken, true);
}
else
{
// Possible cause - DTOR with params
AssertErrorState();
}
}
return true;
}
// Generic methods can't be called virtually
if (methodInstance->GetNumGenericParams() != 0)
return true;
// Only public methods can be called virtually
if (methodDef->mProtection != BfProtection_Public)
return true;
UniqueSlotVirtualMethod(methodInstance);
return true;
}
void BfModule::DbgFinish()
{
if ((mBfIRBuilder == NULL) || (mExtensionCount != 0))
return;
if (mAwaitingInitFinish)
FinishInit();
if (mHasForceLinkMarker)
return;
String markerName;
BfIRValue linkMarker;
if (mBfIRBuilder->DbgHasInfo())
{
bool needForceLinking = false;
for (auto& ownedType : mOwnedTypeInstances)
{
bool hasConfirmedReference = false;
for (auto& methodInstGroup : ownedType->mMethodInstanceGroups)
{
2020-04-02 10:08:13 -07:00
if ((methodInstGroup.IsImplemented()) && (methodInstGroup.mDefault != NULL) &&
(!methodInstGroup.mDefault->mMethodDef->mIsStatic) && (methodInstGroup.mDefault->mIsReified) && (!methodInstGroup.mDefault->mAlwaysInline) &&
2019-08-23 11:56:54 -07:00
((methodInstGroup.mOnDemandKind == BfMethodOnDemandKind_AlwaysInclude) || (methodInstGroup.mOnDemandKind == BfMethodOnDemandKind_Referenced)))
{
hasConfirmedReference = true;
}
}
if ((!hasConfirmedReference) || (ownedType->IsBoxed()))
needForceLinking = true;
}
if ((needForceLinking) && (mProject->mCodeGenOptions.mAsmKind == BfAsmKind_None))
2019-08-23 11:56:54 -07:00
{
BfMethodState methodState;
SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
methodState.mTempKind = BfMethodState::TempKind_Static;
mHasForceLinkMarker = true;
auto voidType = mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_None));
SizedArray<BfIRType, 0> paramTypes;
BfIRFunctionType funcType = mBfIRBuilder->CreateFunctionType(voidType, paramTypes);
String linkName = "FORCELINKMOD_" + mModuleName;
BfIRFunction func = mBfIRBuilder->CreateFunction(funcType, BfIRLinkageType_External, linkName);
mBfIRBuilder->SetActiveFunction(func);
auto entryBlock = mBfIRBuilder->CreateBlock("main", true);
mBfIRBuilder->SetInsertPoint(entryBlock);
auto firstType = mOwnedTypeInstances[0];
UpdateSrcPos(mContext->mBfObjectType->mTypeDef->GetRefNode());
SizedArray<BfIRMDNode, 1> diParamTypes;
diParamTypes.Add(mBfIRBuilder->DbgGetType(GetPrimitiveType(BfTypeCode_None)));
BfIRMDNode diFuncType = mBfIRBuilder->DbgCreateSubroutineType(diParamTypes);
auto diScope = mBfIRBuilder->DbgCreateFunction(mDICompileUnit, "FORCELINKMOD", linkName, mCurFilePosition.mFileInstance->mDIFile,
mCurFilePosition.mCurLine + 1, diFuncType, false, true, mCurFilePosition.mCurLine + 1, 0, false, func);
methodState.mCurScope->mDIScope = diScope;
UpdateSrcPos(mContext->mBfObjectType->mTypeDef->GetRefNode(), BfSrcPosFlag_Force);
SizedArray<BfIRValue, 8> args;
BfExprEvaluator exprEvaluator(this);
for (auto& ownedType : mOwnedTypeInstances)
{
auto alloca = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(GetPrimitiveType(BfTypeCode_Int8)));
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(diScope, "variable", mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mBfIRBuilder->DbgGetType(ownedType));
mBfIRBuilder->DbgInsertDeclare(alloca, diVariable);
}
mBfIRBuilder->CreateRetVoid();
mBfIRBuilder->SetActiveFunction(BfIRFunction());
}
}
}
bool BfModule::Finish()
{
BP_ZONE("BfModule::Finish");
BfLogSysM("BfModule finish: %p\n", this);
if (mHadBuildError)
{
// Don't AssertErrorState here, this current pass may not have failed but
// the module was still queued in mFinishedModuleWorkList
ClearModule();
return true;
}
if (mUsedSlotCount != -1)
{
BF_ASSERT(mCompiler->mMaxInterfaceSlots != -1);
mUsedSlotCount = mCompiler->mMaxInterfaceSlots;
}
BF_ASSERT(mAddedToCount);
mAddedToCount = false;
mAwaitingFinish = false;
mCompiler->mStats.mModulesFinished++;
if (HasCompiledOutput())
{
DbgFinish();
if (mAwaitingInitFinish)
FinishInit();
for (auto ownedTypeInst : mOwnedTypeInstances)
{
// Generate dbg info and add global variables if we haven't already
mBfIRBuilder->PopulateType(ownedTypeInst);
}
if (mBfIRBuilder->DbgHasInfo())
{
mBfIRBuilder->DbgFinalize();
}
String objOutputPath;
String irOutputPath;
mIsModuleMutable = false;
//mOutFileNames.Clear();
BF_ASSERT((int)mOutFileNames.size() >= mExtensionCount);
bool writeModule = mBfIRBuilder->HasExports();
String outputPath;
BfCodeGenOptions codeGenOptions = mProject->mCodeGenOptions;
auto& compilerOpts = mCompiler->mOptions;
codeGenOptions.mSIMDSetting = compilerOpts.mSIMDSetting;
auto moduleOptions = GetModuleOptions();
codeGenOptions.mOptLevel = moduleOptions.mOptLevel;
codeGenOptions.mSIMDSetting = moduleOptions.mSIMDSetting;
codeGenOptions.mWriteLLVMIR = mCompiler->mOptions.mWriteIR;
codeGenOptions.mWriteObj = mCompiler->mOptions.mGenerateObj;
codeGenOptions.mWriteBitcode = mCompiler->mOptions.mGenerateBitcode;
2019-08-23 11:56:54 -07:00
codeGenOptions.mVirtualMethodOfs = 1 + mCompiler->GetDynCastVDataCount() + mCompiler->mMaxInterfaceSlots;
codeGenOptions.mDynSlotOfs = mSystem->mPtrSize - mCompiler->GetDynCastVDataCount() * 4;
mCompiler->mStats.mIRBytes += mBfIRBuilder->mStream.GetSize();
mCompiler->mStats.mConstBytes += mBfIRBuilder->mTempAlloc.GetAllocSize();
bool allowWriteToLib = true;
if ((allowWriteToLib) && (codeGenOptions.mOptLevel == BfOptLevel_OgPlus) &&
(!mCompiler->IsHotCompile()) && (mModuleName != "vdata"))
2019-08-23 11:56:54 -07:00
{
codeGenOptions.mWriteToLib = true;
mWroteToLib = true;
}
else
{
mWroteToLib = false;
}
for (int fileIdx = 0; fileIdx <= mExtensionCount; fileIdx++)
{
outputPath = mModuleName;
outputPath = mCompiler->mOutputDirectory + "/" + mProject->mName + "/" + outputPath;
BfModuleFileName moduleFileName;
if (mParentModule != NULL)
{
for (auto&& checkPair : mParentModule->mSpecializedMethodModules)
{
if (checkPair.mValue == this)
moduleFileName.mProjects = checkPair.mKey;
}
}
moduleFileName.mProjects.Add(mProject);
if (fileIdx > 0)
outputPath += StrFormat("@%d", fileIdx + 1);
if (mCompiler->mOptions.mWriteIR)
irOutputPath = outputPath + ".ll";
if (mCompiler->mOptions.mGenerateObj)
{
objOutputPath = outputPath + BF_OBJ_EXT;
moduleFileName.mFileName = objOutputPath;
}
else if (mCompiler->mOptions.mWriteIR)
{
moduleFileName.mFileName = irOutputPath;
}
else if ((!mCompiler->mOptions.mGenerateObj) && (!mCompiler->mOptions.mGenerateBitcode) && (!mCompiler->mOptions.mWriteIR))
2019-08-23 11:56:54 -07:00
{
BFMODULE_FATAL(this, "Neither obj nor IR specified");
2019-08-23 11:56:54 -07:00
}
moduleFileName.mModuleWritten = writeModule;
moduleFileName.mWroteToLib = mWroteToLib;
if (!mOutFileNames.Contains(moduleFileName))
mOutFileNames.Add(moduleFileName);
}
if (mCompiler->IsHotCompile())
{
codeGenOptions.mIsHotCompile = true;
if (mParentModule != NULL)
mParentModule->mHadHotObjectWrites = true;
mHadHotObjectWrites = true;
2019-08-23 11:56:54 -07:00
}
//TODO: Testing VDATA
/*if (mModuleName == "vdata")
{
codeGenOptions.mOptLevel = 4;
}*/
codeGenOptions.GenerateHash();
BP_ZONE("BfModule::Finish.WriteObjectFile");
if ((writeModule) && (!mBfIRBuilder->mIgnoreWrites))
mCompiler->mCodeGen.WriteObjectFile(this, outputPath, codeGenOptions);
mLastModuleWrittenRevision = mCompiler->mRevision;
}
else
{
for (auto type : mOwnedTypeInstances)
{
BF_ASSERT(!type->IsIncomplete());
}
}
for (auto& specModulePair : mSpecializedMethodModules)
{
auto specModule = specModulePair.mValue;
if ((specModule->mIsModuleMutable) && (!specModule->mAwaitingFinish))
{
BfLogSysM(" Mutable spec module remaining: %p\n", specModule);
}
}
CleanupFileInstances();
// We can't ClearModuleData here because we need the mSpecializedMethodModules to remain valid until we Finish them too
return true;
}
void BfModule::ReportMemory(MemReporter* memReporter)
{
memReporter->Add(sizeof(BfModule));
if (mBfIRBuilder != NULL)
{
memReporter->BeginSection("IRBuilder_ConstData");
memReporter->Add("Used", mBfIRBuilder->mTempAlloc.GetAllocSize());
memReporter->Add("Unused", mBfIRBuilder->mTempAlloc.GetTotalAllocSize() - mBfIRBuilder->mTempAlloc.GetAllocSize());
memReporter->EndSection();
memReporter->BeginSection("IRBuilder_BFIR");
memReporter->Add("Used", (int)(mBfIRBuilder->mStream.GetSize()));
memReporter->Add("Unused", (int)(mBfIRBuilder->mStream.mPools.size() * ChunkedDataBuffer::ALLOC_SIZE) - mBfIRBuilder->mStream.GetSize());
memReporter->EndSection();
memReporter->Add(sizeof(BfIRBuilder));
}
memReporter->BeginSection("FileInstanceMap");
memReporter->AddMap(mFileInstanceMap);
memReporter->AddMap(mNamedFileInstanceMap);
memReporter->Add((int)mNamedFileInstanceMap.size() * sizeof(BfFileInstance));
memReporter->EndSection();
memReporter->AddVec(mOwnedTypeInstances, false);
memReporter->AddVec(mSpecializedMethodModules, false);
memReporter->AddVec(mOutFileNames, false);
memReporter->AddMap("FuncReferences", mFuncReferences, false);
memReporter->AddMap(mInterfaceSlotRefs, false);
memReporter->AddMap(mStaticFieldRefs, false);
memReporter->AddMap(mClassVDataRefs, false);
memReporter->AddMap("VDataExtMap", mClassVDataExtRefs, false);
memReporter->AddMap(mTypeDataRefs, false);
memReporter->AddMap(mDbgRawAllocDataRefs, false);
memReporter->AddMap(mDeferredMethodCallData, false);
memReporter->AddHashSet(mDeferredMethodIds, false);
memReporter->AddHashSet("ModuleRefs", mModuleRefs, false);
}
// ClearModuleData is called immediately after the module is compiled, so don't clear out any data that needs to
// be transient through the next compile
void BfModule::ClearModuleData()
{
BfLogSysM("ClearModuleData %p\n", this);
if (mAddedToCount)
{
mCompiler->mStats.mModulesFinished++;
mAddedToCount = false;
}
mDICompileUnit = BfIRMDNode();
mIsModuleMutable = false;
mIncompleteMethodCount = 0;
mHasGenericMethods = false;
// We don't want to clear these because we want it to persist through module extensions-
// otherwise we may think an extension succeeds even though the base module failed
//mHadBuildError = false;
//mHadBuildWarning = false;
mClassVDataRefs.Clear();
mClassVDataExtRefs.Clear();
for (auto& kv : mTypeDataRefs)
kv.mValue = BfIRValue();
mStringCharPtrPool.Clear();
mStringObjectPool.Clear();
mStaticFieldRefs.Clear();
BF_ASSERT(!mIgnoreErrors);
for (auto prevIRBuilder : mPrevIRBuilders)
delete prevIRBuilder;
mPrevIRBuilders.Clear();
for (auto& specPair : mSpecializedMethodModules)
{
auto specModule = specPair.mValue;
specModule->ClearModuleData();
}
for (int i = 0; i < BfBuiltInFuncType_Count; i++)
mBuiltInFuncs[i] = BfIRFunction();
if (mNextAltModule != NULL)
mNextAltModule->ClearModuleData();
BfLogSysM("ClearModuleData. Deleting IRBuilder: %p\n", mBfIRBuilder);
delete mBfIRBuilder;
mBfIRBuilder = NULL;
mWantsIRIgnoreWrites = false;
}
void BfModule::DisownMethods()
{
2019-08-23 11:56:54 -07:00
for (int i = 0; i < BfBuiltInFuncType_Count; i++)
mBuiltInFuncs[i] = BfIRFunction();
BfModule* methodModule = this;
if (mParentModule != NULL)
methodModule = mParentModule;
for (auto typeInst : methodModule->mOwnedTypeInstances)
{
for (auto& methodGroup : typeInst->mMethodInstanceGroups)
{
if (methodGroup.mDefault != NULL)
{
if (methodGroup.mDefault->mIRFunction)
{
BF_ASSERT(methodGroup.mDefault->mDeclModule != NULL);
if (methodGroup.mDefault->mDeclModule == this)
{
methodGroup.mDefault->mIRFunction = BfIRFunction();
}
}
2019-08-23 11:56:54 -07:00
}
if (methodGroup.mMethodSpecializationMap != NULL)
{
for (auto& mapPair : *methodGroup.mMethodSpecializationMap)
{
auto methodInstance = mapPair.mValue;
if (methodInstance->mDeclModule == this)
{
2019-08-23 11:56:54 -07:00
methodInstance->mIRFunction = BfIRFunction();
}
2019-08-23 11:56:54 -07:00
}
}
}
}
}
void BfModule::ClearModule()
{
ClearModuleData();
DisownMethods();
for (auto& specPair : mSpecializedMethodModules)
{
auto specModule = specPair.mValue;
specModule->ClearModule();
}
if (mNextAltModule != NULL)
mNextAltModule->ClearModule();
}