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:
parent
da563ee14b
commit
5b8d2ffee2
6 changed files with 283 additions and 134 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -13851,21 +13887,28 @@ 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()))
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BfModule::CreateReturn(BfIRValue val)
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -168,7 +176,8 @@ public:
|
|||
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:
|
||||
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -4886,6 +4887,14 @@ void BfModule::Visit(BfReturnStatement* returnStmt)
|
|||
checkScope = checkScope->mPrevScope;
|
||||
}
|
||||
|
||||
auto checkLocalAssignData = mCurMethodState->mDeferredLocalAssignData;
|
||||
while (checkLocalAssignData != NULL)
|
||||
{
|
||||
if (checkLocalAssignData->mScopeData != NULL)
|
||||
checkLocalAssignData->mLeftBlock = true;
|
||||
checkLocalAssignData = checkLocalAssignData->mChainedAssignData;
|
||||
}
|
||||
|
||||
if (retType == NULL)
|
||||
{
|
||||
if (returnStmt->mExpression != 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,6 +5314,11 @@ 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;
|
||||
|
||||
|
@ -5304,6 +5326,9 @@ void BfModule::Visit(BfDoStatement* doStmt)
|
|||
mBfIRBuilder->SetInsertPoint(bodyBB);
|
||||
VisitEmbeddedStatement(doStmt->mEmbeddedStatement);
|
||||
|
||||
prevDLA.Restore();
|
||||
mCurMethodState->ApplyDeferredLocalAssignData(deferredLocalAssignData);
|
||||
|
||||
RestoreScopeState();
|
||||
|
||||
if (!mCurMethodState->mLeftBlockUncond)
|
||||
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue