1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

Improvements to unassigned variable detection

This commit is contained in:
Brian Fiete 2020-09-21 13:58:00 -07:00
parent da563ee14b
commit 5b8d2ffee2
6 changed files with 283 additions and 134 deletions

View file

@ -3103,6 +3103,7 @@ namespace System {
AdjustmentRule [] adjustmentRules,
out bool adjustmentRulesSupportDst) {
adjustmentRulesSupportDst = false;
if (id.IsNull) {
//throw new ArgumentNullException("id");
return .Err;
@ -3125,8 +3126,6 @@ namespace System {
}
Contract.EndContractBlock();
adjustmentRulesSupportDst = false;
//
// "adjustmentRules" can either be null or a valid array of AdjustmentRule objects.
// A valid array is one that does not contain any null elements and all elements

View file

@ -6,6 +6,45 @@ namespace IDETest
{
class LocalVars
{
public void If1()
{
int a;
int b = 123;
if (b == 234)
{
a = 234;
return;
}
b = a; //FAIL
}
public void If2(out int a) //FAIL
{
int b = 123;
if (b == 234)
return;
a = 234;
b = a;
}
public void For1(out int a) //FAIL
{
for (int b < 2)
a = 9;
}
public void Do1(out int a) //FAIL
{
int b = 123;
do
{
if (b == 234)
break;
a = 9;
}
b = a;
}
public void Switch1()
{
int val;
@ -91,6 +130,20 @@ namespace IDETest
int c = b;
}
public void Switch6()
{
int val;
Result<int> iResult = .Ok(123);
switch (iResult)
{
case .Ok(out val):
case .Err: break;
}
int a = val; //FAIL
}
public void While1()
{
int a = 1;

View file

@ -6553,7 +6553,7 @@ BfTypedValue BfExprEvaluator::ResolveArgValue(BfResolvedArg& resolvedArg, BfType
localVar->mIsReadOnly = isLet;
localVar->mReadFromId = 0;
localVar->mWrittenToId = 0;
localVar->mIsAssigned = true;
localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
mModule->CheckVariableDef(localVar);
localVar->Init();
mModule->AddLocalVariableDef(localVar, true);
@ -10940,7 +10940,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
methodDef->mParams.push_back(paramDef);
localVar->mResolvedType = invokeMethodInstance->GetParamType(paramIdx);
localVar->mIsAssigned = true;
localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localVar->mReadFromId = 0;
auto rootMethodState = methodState.GetRootMethodState();
@ -13920,17 +13920,17 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
mModule->AddLocalVariableDef(newLocalVar, hasConstValue);
auto inLocalVar = exprEvaluator->mResultLocalVar;
newLocalVar->mIsAssigned = inLocalVar->mIsAssigned;
newLocalVar->mAssignedKind = inLocalVar->mAssignedKind;
newLocalVar->mUnassignedFieldFlags = inLocalVar->mUnassignedFieldFlags;
newLocalVar->mReadFromId = inLocalVar->mReadFromId;
newLocalVar->mIsReadOnly = inLocalVar->mIsReadOnly;
if ((!newLocalVar->mIsAssigned) && (mModule->mCurMethodState->mDeferredLocalAssignData != NULL))
if ((newLocalVar->mAssignedKind == BfLocalVarAssignKind_None) && (mModule->mCurMethodState->mDeferredLocalAssignData != NULL))
{
for (auto deferredAssign : mModule->mCurMethodState->mDeferredLocalAssignData->mAssignedLocals)
{
if (deferredAssign.mLocalVar == inLocalVar)
newLocalVar->mIsAssigned = true;
newLocalVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
}
}
@ -14095,7 +14095,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
argValue = target;
localVar->mName = "this";
localVar->mIsThis = true;
localVar->mIsAssigned = true;
localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
else
{
@ -14123,7 +14123,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
auto refType = (BfRefType*)localVar->mResolvedType;
localVar->mAddr = mModule->LoadValue(argValue).mValue;
localVar->mResolvedType = argValue.mType->GetUnderlyingType();
localVar->mIsAssigned = refType->mRefKind != BfRefType::RefKind_Out;
localVar->mAssignedKind = (refType->mRefKind != BfRefType::RefKind_Out) ? BfLocalVarAssignKind_Unconditional : BfLocalVarAssignKind_None;
}
else if (argValue.IsAddr())
localVar->mAddr = argValue.mValue;
@ -14202,8 +14202,8 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
if (mResultLocalVar != NULL)
{
auto inLocalVar = mResultLocalVar;
if (localVar->mIsAssigned)
inLocalVar->mIsAssigned = true;
if (localVar->mAssignedKind != BfLocalVarAssignKind_None)
inLocalVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
if (localVar->mReadFromId != -1)
inLocalVar->mReadFromId = mModule->mCurMethodState->GetRootMethodState()->mCurAccessId++;
}
@ -14220,8 +14220,8 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
if ((exprEvaluator != NULL) && (exprEvaluator->mResultLocalVar != NULL))
{
auto inLocalVar = exprEvaluator->mResultLocalVar;
if (localVar->mIsAssigned)
inLocalVar->mIsAssigned = true;
if (localVar->mAssignedKind != BfLocalVarAssignKind_None)
inLocalVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
if (localVar->mReadFromId != -1)
inLocalVar->mReadFromId = mModule->mCurMethodState->GetRootMethodState()->mCurAccessId++;
}
@ -15478,12 +15478,12 @@ void BfExprEvaluator::CheckResultForReading(BfTypedValue& typedValue)
return;
}
if (!localVar->mIsAssigned)
if (localVar->mAssignedKind == BfLocalVarAssignKind_None)
{
mModule->TryLocalVariableInit(localVar);
}
if (!localVar->mIsAssigned)
if (localVar->mAssignedKind == BfLocalVarAssignKind_None)
{
auto methodStateForLocal = mModule->mCurMethodState->GetMethodStateForLocal(localVar);
@ -15500,7 +15500,7 @@ void BfExprEvaluator::CheckResultForReading(BfTypedValue& typedValue)
auto assignedLocal = assignedVar.mLocalVar;
if (assignedLocal == localVar)
{
int assignedFieldIdx = (assignedVar.mLocalVarField) - 1;
int assignedFieldIdx = assignedVar.mLocalVarField;
if (assignedFieldIdx >= 0)
undefinedFieldFlags &= ~((int64)1 << assignedFieldIdx);
else

View file

@ -40,11 +40,11 @@ void BfLocalVariable::Init()
{
if (mResolvedType->IsValuelessType())
{
mIsAssigned = true;
mAssignedKind = BfLocalVarAssignKind_Unconditional;
return;
}
if (mIsAssigned)
if (mAssignedKind != BfLocalVarAssignKind_None)
return;
bool isStruct = mResolvedType->IsStruct();
@ -64,7 +64,7 @@ void BfLocalVariable::Init()
mUnassignedFieldFlags &= ~(((int64)1 << typeInstance->mBaseType->mMergedFieldDataCount) - 1);
}
if (mUnassignedFieldFlags == 0)
mIsAssigned = true;
mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
else
{
@ -117,20 +117,39 @@ void BfDeferredLocalAssignData::SetIntersection(const BfDeferredLocalAssignData&
{
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))
bool wantRemove = true;
bool foundOtherFields = false;
for (auto& otherLocalAssignData : otherLocalAssignData.mAssignedLocals)
{
if (otherLocalAssignData.mLocalVar == local.mLocalVar)
{
if ((otherLocalAssignData.mLocalVarField == local.mLocalVarField) || (otherLocalAssignData.mLocalVarField == -1))
{
if (otherLocalAssignData.mAssignKind == BfLocalVarAssignKind_Conditional)
local.mAssignKind = BfLocalVarAssignKind_Conditional;
wantRemove = false;
}
else
foundOtherFields = true;
}
}
if ((wantRemove) && (foundOtherFields))
{
for (auto& otherLocalAssignData : otherLocalAssignData.mAssignedLocals)
{
if (otherLocalAssignData.mLocalVar == local.mLocalVar)
{
mAssignedLocals.Add(otherLocalAssignData);
}
}
}
if (wantRemove)
{
mAssignedLocals.RemoveAt(i);
}
@ -175,7 +194,7 @@ BfMethodState::~BfMethodState()
BF_ASSERT(mCurAccessId == 1);
BF_ASSERT(mCurLocalVarId <= 0);
}
for (auto local : mLocals)
delete local;
@ -198,7 +217,7 @@ BfMethodState* BfMethodState::GetMethodStateForLocal(BfLocalVariable* localVar)
return NULL;
}
void BfMethodState::LocalDefined(BfLocalVariable* localVar, int fieldIdx)
void BfMethodState::LocalDefined(BfLocalVariable* localVar, int fieldIdx, BfLocalVarAssignKind assignKind, bool isFromDeferredAssignData)
{
auto localVarMethodState = GetMethodStateForLocal(localVar);
if (localVarMethodState != this)
@ -207,7 +226,13 @@ void BfMethodState::LocalDefined(BfLocalVariable* localVar, int fieldIdx)
}
//BF_ASSERT(localVarMethodState == this);
if (!localVar->mIsAssigned)
// if (assignKind == BfLocalVarAssignKind_None)
// assignKind = ((localVarMethodState->mLeftBlockCond) && ((mDeferredLocalAssignData != NULL) || isFromDeferredAssignData)) ? BfLocalVarAssignKind_Conditional : BfLocalVarAssignKind_Unconditional;
//
// //assignKind = BfLocalVarAssignKind_Unconditional;
if (localVar->mAssignedKind == BfLocalVarAssignKind_None)
{
BfDeferredLocalAssignData* ifDeferredLocalAssignData = NULL;
@ -226,7 +251,14 @@ void BfMethodState::LocalDefined(BfLocalVariable* localVar, int fieldIdx)
((deferredLocalAssignData->mIsChained) || (deferredLocalAssignData->mIsUnconditional)))
deferredLocalAssignData = deferredLocalAssignData->mChainedAssignData;
if ((deferredLocalAssignData == NULL) || (localVar->mLocalVarId >= deferredLocalAssignData->mVarIdBarrier))
if (assignKind == BfLocalVarAssignKind_None)
assignKind = ((deferredLocalAssignData != NULL) && (deferredLocalAssignData->mLeftBlock)) ? BfLocalVarAssignKind_Conditional : BfLocalVarAssignKind_Unconditional;
if (localVar->mAssignedKind == assignKind)
{
// Leave it alone
}
else if ((deferredLocalAssignData == NULL) || (localVar->mLocalVarId >= deferredLocalAssignData->mVarIdBarrier))
{
if (fieldIdx >= 0)
{
@ -236,18 +268,21 @@ void BfMethodState::LocalDefined(BfLocalVariable* localVar, int fieldIdx)
}*/
if (localVar->mUnassignedFieldFlags == 0)
localVar->mIsAssigned = true;
{
if (localVar->mAssignedKind == BfLocalVarAssignKind_None)
localVar->mAssignedKind = assignKind;
}
}
else
{
localVar->mIsAssigned = true;
{
localVar->mAssignedKind = assignKind;
}
}
else
{
BF_ASSERT(deferredLocalAssignData->mVarIdBarrier != -1);
BfAssignedLocal defineVal = {localVar, fieldIdx + 1};
BfAssignedLocal defineVal = {localVar, fieldIdx, assignKind};
auto& assignedLocals = deferredLocalAssignData->mAssignedLocals;
if (!assignedLocals.Contains(defineVal))
assignedLocals.push_back(defineVal);
@ -269,7 +304,7 @@ void BfMethodState::ApplyDeferredLocalAssignData(const BfDeferredLocalAssignData
for (auto& assignedLocal : deferredLocalAssignData.mAssignedLocals)
{
LocalDefined(assignedLocal.mLocalVar);
LocalDefined(assignedLocal.mLocalVar, assignedLocal.mLocalVarField, assignedLocal.mAssignKind, true);
}
}
@ -2029,14 +2064,14 @@ bool BfModule::TryLocalVariableInit(BfLocalVariable* localVar)
localVar->mUnassignedFieldFlags &= ~checkMask;
if (localVar->mUnassignedFieldFlags == 0)
localVar->mIsAssigned = true;
localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
}
}
checkTypeInstance = checkTypeInstance->mBaseType;
}
return localVar->mIsAssigned;
return localVar->mAssignedKind != BfLocalVarAssignKind_None;
}
void BfModule::LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit)
@ -2069,7 +2104,7 @@ void BfModule::LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit)
if ((localVar->mReadFromId == -1) || (isOut) || ((localVar->mIsThis) && (mCurTypeInstance->IsStruct())))
{
if ((!localVar->mIsAssigned) & (localVar->IsParam()))
if ((localVar->mAssignedKind != BfLocalVarAssignKind_Unconditional) & (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
@ -2077,7 +2112,8 @@ void BfModule::LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit)
//bool deferFullAnalysis = true;
bool deferUsageWarning = deferFullAnalysis && mCompiler->IsAutocomplete();
if ((!localVar->mIsAssigned) && (!localVar->mIsImplicitParam))
if (((localVar->mAssignedKind != BfLocalVarAssignKind_Unconditional) || (localVar->mHadExitBeforeAssign)) &&
(!localVar->mIsImplicitParam))
{
if (deferUsageWarning)
{
@ -4022,7 +4058,7 @@ void BfModule::CreateDynamicCastMethod()
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);
@ -4036,7 +4072,7 @@ void BfModule::CreateDynamicCastMethod()
genericArgs.push_back(GetGenericParamType(BfGenericParamKind_Type, i));
auto unboundType = ResolveTypeDef(mCurTypeInstance->mTypeDef, genericArgs, BfPopulateType_Declaration);
typeMatches.push_back(unboundType->mTypeId);
}
}
if (mCurTypeInstance->IsBoxed())
{
@ -13266,7 +13302,7 @@ BfLocalVariable* BfModule::AddLocalVariableDef(BfLocalVariable* localVarDef, boo
if ((!isClosureProcessing) && (mCompiler->mResolvePassData != NULL) && (localVarDef->mNameNode != NULL))
mCompiler->mResolvePassData->HandleLocalReference(localVarDef->mNameNode, rootMethodState->mMethodInstance->GetOwner()->mTypeDef, rootMethodState->mMethodInstance->mMethodDef, localVarDef->mLocalVarId);
}
return localVarDef;
}
@ -13851,20 +13887,27 @@ void BfModule::MarkScopeLeft(BfScopeData* scopeData)
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++)
// When we leave a scope, mark those as assigned for deferred assignment purposes
for (int localIdx = scopeData->mLocalVarStart; localIdx < (int)mCurMethodState->mLocals.size(); localIdx++)
{
auto localDef = mCurMethodState->mLocals[localIdx];
if ((!localDef->mIsAssigned) && (!localDef->IsParam()))
auto localDef = mCurMethodState->mLocals[localIdx];
if (localDef->mAssignedKind == BfLocalVarAssignKind_None)
{
bool hadAssignment = false;
if (mCurMethodState->mDeferredLocalAssignData != NULL)
{
for (auto& entry : mCurMethodState->mDeferredLocalAssignData->mAssignedLocals)
if (entry.mLocalVar == localDef)
hadAssignment = true;
}
if (!hadAssignment)
localDef->mHadExitBeforeAssign = true;
mCurMethodState->LocalDefined(localDef);
}
}
}
@ -14843,7 +14886,7 @@ void BfModule::EmitDtorBody()
localDef->mName = "_";
localDef->mResolvedType = fieldType;
localDef->mAddr = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(fieldType));
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
AddLocalVariableDef(localDef);
}
@ -15591,7 +15634,7 @@ void BfModule::EmitCtorBody(bool& skipBody)
if (thisVariable != NULL)
{
thisVariable->mUnassignedFieldFlags = 0;
thisVariable->mIsAssigned = true;
thisVariable->mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
}
@ -16142,11 +16185,11 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
paramVar->mParamIdx = -1;
if ((methodDef->mMethodType == BfMethodType_Ctor) && (mCurTypeInstance->IsStruct()))
{
paramVar->mIsAssigned = false;
paramVar->mAssignedKind = BfLocalVarAssignKind_None;
}
else
{
paramVar->mIsAssigned = true;
paramVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
paramVar->mReadFromId = -1;
@ -16242,11 +16285,11 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
if (resolvedType->IsRef())
{
auto refType = (BfRefType*)resolvedType;
paramVar->mIsAssigned = refType->mRefKind != BfRefType::RefKind_Out;
paramVar->mAssignedKind = (refType->mRefKind != BfRefType::RefKind_Out) ? BfLocalVarAssignKind_Unconditional : BfLocalVarAssignKind_None;
}
else
{
paramVar->mIsAssigned = true;
paramVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
if (methodDef->mMethodType != BfMethodType_Mixin)
paramVar->mIsReadOnly = true;
}

View file

@ -119,6 +119,13 @@ enum BfEmbeddedStatementFlags
BfEmbeddedStatementFlags_IsDeferredBlock = 2
};
enum BfLocalVarAssignKind
{
BfLocalVarAssignKind_None = 0,
BfLocalVarAssignKind_Conditional = 1,
BfLocalVarAssignKind_Unconditional = 2
};
class BfLocalVariable
{
public:
@ -143,7 +150,8 @@ public:
bool mIsStruct;
bool mIsImplicitParam;
bool mParamFailed;
bool mIsAssigned;
BfLocalVarAssignKind mAssignedKind;
bool mHadExitBeforeAssign;
bool mIsReadOnly;
bool mIsSplat;
bool mIsLowered;
@ -155,20 +163,21 @@ public:
public:
BfLocalVariable()
{
{
mUnassignedFieldFlags = 0;
mResolvedType = NULL;
mNameNode = NULL;
mNameNode = NULL;
mLocalVarIdx = -1;
mLocalVarId = -1;
mCompositeCount = -1;
mParamIdx = -2;
mIsThis = false;
mHasLocalStructBacking = false;
mIsStruct = false;
mIsStruct = false;
mIsImplicitParam = false;
mParamFailed = false;
mIsAssigned = false;
mAssignedKind = BfLocalVarAssignKind_None;
mHadExitBeforeAssign = false;
mWrittenToId = -1;
mReadFromId = -1;
mIsReadOnly = false;
@ -295,6 +304,59 @@ struct BfDeferredHandler
BfIRBlock mDoneBlock;
};
class BfScopeData;
struct BfAssignedLocal
{
BfLocalVariable* mLocalVar;
int mLocalVarField;
BfLocalVarAssignKind mAssignKind;
bool operator==(const BfAssignedLocal& second) const
{
return (mLocalVar == second.mLocalVar) && (mLocalVarField == second.mLocalVarField) && (mAssignKind == second.mAssignKind);
}
};
// We use this structure in the case where we have multiple execution paths, then we merge the assigned variables together
// So when we have "if (check) { a = 1; } else {a = 2; }" we can know that a IS definitely assigned afterwards
class BfDeferredLocalAssignData
{
public:
BfScopeData* mScopeData;
int mVarIdBarrier;
SizedArray<BfAssignedLocal, 4> mAssignedLocals;
bool mIsChained;
BfDeferredLocalAssignData* mChainedAssignData;
bool mHadFallthrough;
bool mHadReturn;
bool mIsUnconditional;
bool mIsIfCondition;
bool mIfMayBeSkipped;
bool mLeftBlock;
public:
BfDeferredLocalAssignData(BfScopeData* scopeData = NULL)
{
mScopeData = scopeData;
mVarIdBarrier = -1;
mHadFallthrough = false;
mHadReturn = false;
mChainedAssignData = NULL;
mIsChained = false;
mIsUnconditional = false;
mIsIfCondition = false;
mIfMayBeSkipped = false;
mLeftBlock = false;
}
void ExtendFrom(BfDeferredLocalAssignData* outerLocalAssignData, bool doChain = false);
void BreakExtendChain();
void SetIntersection(const BfDeferredLocalAssignData& otherLocalAssignData);
void Validate() const;
void SetUnion(const BfDeferredLocalAssignData& otherLocalAssignData);
};
// "Looped" means this scope will execute zero to many times, "Conditional" means zero or one.
// Looped and Conditional are mutually exclusive. "Dyn" means Looped OR Conditional.
class BfScopeData
@ -330,6 +392,7 @@ public:
Array<BfDeferredHandler> mDeferredHandlers; // These get cleared when us our a parent gets new entries added into mDeferredCallEntries
Array<BfIRBlock> mAtEndBlocks; // Move these to the end after we close scope
Array<BfIRValue> mDeferredLifetimeEnds;
BfDeferredLocalAssignData* mExitLocalAssignData;
BfIRMDNode mAltDIFile;
BfIRMDNode mAltDIScope;
@ -355,11 +418,13 @@ public:
mMixinDepth = 0;
mScopeDepth = 0;
mScopeLocalId = -1;
mExitLocalAssignData = NULL;
}
~BfScopeData()
{
mDeferredCallEntries.DeleteAll();
delete mExitLocalAssignData;
}
BfScopeData* GetHead()
@ -535,54 +600,6 @@ public:
}
};
struct BfAssignedLocal
{
BfLocalVariable* mLocalVar;
int mLocalVarField;
bool operator==(const BfAssignedLocal& second) const
{
return (mLocalVar == second.mLocalVar) && (mLocalVarField == second.mLocalVarField);
}
};
// We use this structure in the case where we have multiple execution paths, then we merge the assigned variables together
// So when we have "if (check) { a = 1; } else {a = 2; }" we can know that a IS definitely assigned afterwards
class BfDeferredLocalAssignData
{
public:
BfScopeData* mScopeData;
int mVarIdBarrier;
SizedArray<BfAssignedLocal, 4> mAssignedLocals;
bool mIsChained;
BfDeferredLocalAssignData* mChainedAssignData;
bool mHadFallthrough;
bool mHadReturn;
bool mIsUnconditional;
bool mIsIfCondition;
bool mIfMayBeSkipped;
public:
BfDeferredLocalAssignData(BfScopeData* scopeData = NULL)
{
mScopeData = scopeData;
mVarIdBarrier = -1;
mHadFallthrough = false;
mHadReturn = false;
mChainedAssignData = NULL;
mIsChained = false;
mIsUnconditional = false;
mIsIfCondition = false;
mIfMayBeSkipped = false;
}
void ExtendFrom(BfDeferredLocalAssignData* outerLocalAssignData, bool doChain = false);
void BreakExtendChain();
void SetIntersection(const BfDeferredLocalAssignData& otherLocalAssignData);
void Validate() const;
void SetUnion(const BfDeferredLocalAssignData& otherLocalAssignData);
};
class BfMixinRecord
{
public:
@ -910,7 +927,7 @@ public:
BfDeferredLocalAssignData* mDeferredLocalAssignData;
BfProjectSet mVisibleProjectSet;
int mDeferredLoopListCount;
int mDeferredLoopListEntryCount;
int mDeferredLoopListEntryCount;
HashSet<int> mSkipObjectAccessChecks; // Indexed by BfIRValue value id
Dictionary<int64, BfType*>* mGenericTypeBindings;
@ -995,7 +1012,7 @@ public:
mCurAccessId = 1;
mCurAppendAlign = 0;
mDeferredLoopListCount = 0;
mDeferredLoopListEntryCount = 0;
mDeferredLoopListEntryCount = 0;
mClosureState = NULL;
mDeferredCallEmitState = NULL;
mIteratorClassState = NULL;
@ -1120,7 +1137,7 @@ public:
return false;
}
void LocalDefined(BfLocalVariable* localVar, int fieldIdx = -1);
void LocalDefined(BfLocalVariable* localVar, int fieldIdx = -1, BfLocalVarAssignKind assignKind = BfLocalVarAssignKind_None, bool isFromDeferredAssignData = false);
void ApplyDeferredLocalAssignData(const BfDeferredLocalAssignData& deferredLocalAssignData);
void Reset();

View file

@ -811,7 +811,7 @@ void BfModule::EmitDeferredCall(BfDeferredCallEntry& deferredCallEntry, bool mov
{
BfLocalVariable* localVar = new BfLocalVariable();
localVar->mIsReadOnly = true;
localVar->mIsAssigned = true;
localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localVar->mReadFromId = 0;
localVar->mName = capture.mName;
localVar->mValue = capture.mValue.mValue;
@ -1470,7 +1470,7 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD
resolvedType = initValue.mType;
unresolvedType = resolvedType;
localDef->mLocalVarId = prevLocal->mLocalVarId;
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localDef->mIsShadow = true;
exprEvaluator.mResultLocalVarRefNode = varDecl->mNameNode;
@ -1707,7 +1707,7 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD
}
}
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
else
{
@ -1927,7 +1927,7 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD
varDecl->mNameNode->ToString(localDef->mName);
localDef->mNameNode = BfNodeDynCast<BfIdentifierNode>(varDecl->mNameNode);
localDef->mResolvedType = type;
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localDef->mValue = val.mValue;
if (isLet)
{
@ -2104,7 +2104,7 @@ void BfModule::HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl, Bf
}
}
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
}
else if ((varDecl != NULL) && (varDecl->mInitializer == NULL))
{
@ -3624,7 +3624,8 @@ void BfModule::DoIfStatement(BfIfStatement* ifStmt, bool includeTrueStmt, bool i
bool falseHadReturn = false;
if (ifStmt->mFalseStatement != NULL)
{
BfDeferredLocalAssignData falseDeferredLocalAssignData(mCurMethodState->mCurScope);
BfDeferredLocalAssignData falseDeferredLocalAssignData(&newScope);
falseDeferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
if (falseBB)
{
mBfIRBuilder->AddBlock(falseBB);
@ -4104,7 +4105,7 @@ void BfModule::Visit(BfSwitchStatement* switchStmt)
localDef->mName = "_";
localDef->mResolvedType = switchValueAddr.mType;
localDef->mIsReadOnly = true;
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
if (switchValue.IsAddr())
{
localDef->mAddr = switchValue.mValue;
@ -4884,7 +4885,15 @@ void BfModule::Visit(BfReturnStatement* returnStmt)
return;
}
checkScope = checkScope->mPrevScope;
}
}
auto checkLocalAssignData = mCurMethodState->mDeferredLocalAssignData;
while (checkLocalAssignData != NULL)
{
if (checkLocalAssignData->mScopeData != NULL)
checkLocalAssignData->mLeftBlock = true;
checkLocalAssignData = checkLocalAssignData->mChainedAssignData;
}
if (retType == NULL)
{
@ -5041,6 +5050,14 @@ void BfModule::Visit(BfBreakStatement* breakStmt)
}
}
auto checkLocalAssignData = mCurMethodState->mDeferredLocalAssignData;
while (checkLocalAssignData != NULL)
{
if ((checkLocalAssignData->mScopeData != NULL) && (checkLocalAssignData->mScopeData->mScopeDepth >= breakData->mScope->mScopeDepth))
checkLocalAssignData->mLeftBlock = true;
checkLocalAssignData = checkLocalAssignData->mChainedAssignData;
}
if (HasDeferredScopeCalls(breakData->mScope))
{
EmitDeferredScopeCalls(true, breakData->mScope, breakData->mIRBreakBlock);
@ -5297,14 +5314,22 @@ void BfModule::Visit(BfDoStatement* doStmt)
breakData.mPrevBreakData = mCurMethodState->mBreakData;
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, false);
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
// We may have a call in the loop body
mCurMethodState->mMayNeedThisAccessCheck = true;
mBfIRBuilder->CreateBr(bodyBB);
mBfIRBuilder->SetInsertPoint(bodyBB);
VisitEmbeddedStatement(doStmt->mEmbeddedStatement);
prevDLA.Restore();
mCurMethodState->ApplyDeferredLocalAssignData(deferredLocalAssignData);
RestoreScopeState();
RestoreScopeState();
if (!mCurMethodState->mLeftBlockUncond)
mBfIRBuilder->CreateBr(endBB);
@ -5635,6 +5660,12 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt)
auto isLet = BfNodeDynCast<BfLetTypeReference>(forEachStmt->mVariableTypeRef) != 0;
auto isVar = BfNodeDynCast<BfVarTypeReference>(forEachStmt->mVariableTypeRef) != 0;
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
deferredLocalAssignData.mIsIfCondition = true;
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, true);
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
BfTypedValue target;
BfType* varType = NULL;
bool didInference = false;
@ -5661,6 +5692,12 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt)
if (varType == NULL)
varType = GetPrimitiveType(BfTypeCode_IntPtr);
deferredLocalAssignData.mIsIfCondition = false;
// The "extend chain" is only valid for the conditional -- since that expression may contain unconditionally executed and
// conditionally executed code (in the case of "(GetVal(out a) && GetVal(out b))" for example
mCurMethodState->mDeferredLocalAssignData->BreakExtendChain();
BfType* checkType = varType;
if (checkType->IsTypedPrimitive())
checkType = checkType->GetUnderlyingType();
@ -5683,7 +5720,7 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt)
varInst = CreateAlloca(varType);
}
localDef->mAddr = varInst;
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localDef->mReadFromId = 0;
localDef->mIsReadOnly = isLet || (forEachStmt->mReadOnlyToken != NULL);
@ -6146,7 +6183,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
localDef->mName = variableName;
localDef->mResolvedType = itr.mType;
localDef->mAddr = itr.mValue;
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localDef->mReadFromId = 0;
localDef->Init();
UpdateSrcPos(forEachStmt);
@ -6184,7 +6221,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
if (!needsValCopy)
localDef->mResolvedType = CreateRefType(localDef->mResolvedType);
localDef->mAddr = CreateAlloca(localDef->mResolvedType);
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localDef->mReadFromId = 0;
if ((isLet) || (forEachStmt->mReadOnlyToken != NULL))
localDef->mIsReadOnly = true;
@ -6233,7 +6270,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
localDef->mResolvedType = varType;
varInst = CreateAlloca(varType);
localDef->mAddr = varInst;
localDef->mIsAssigned = true;
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
localDef->mReadFromId = 0;
if ((isLet) || (forEachStmt->mReadOnlyToken != NULL))
localDef->mIsReadOnly = true;
@ -6935,7 +6972,7 @@ void BfModule::Visit(BfInlineAsmStatement* asmStmt)
{
// if you access a variable in asm, we suppress any warnings related to used or assigned, regardless of usage
checkLocal.mIsReadFrom = true;
checkLocal.mIsAssigned = true;
checkLocal.mAssignedKind = BfLocalVarAssignKind_Unconditional;
found = true;
break;
@ -7122,7 +7159,7 @@ void BfModule::Visit(BfInlineAsmStatement* asmStmt)
// if you access a variable in asm, we suppress any warnings related to used or assigned, regardless of usage
checkLocal.mIsReadFrom = true;
checkLocal.mIsAssigned = true;
checkLocal.mAssignedKind = BfLocalVarAssignKind_Unconditional;
found = true;
break;