diff --git a/BeefLibs/corlib/src/TimeZoneInfo.bf b/BeefLibs/corlib/src/TimeZoneInfo.bf index f1edd494..46a0ad87 100644 --- a/BeefLibs/corlib/src/TimeZoneInfo.bf +++ b/BeefLibs/corlib/src/TimeZoneInfo.bf @@ -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 diff --git a/IDE/Tests/CompileFail001/src/LocalVars.bf b/IDE/Tests/CompileFail001/src/LocalVars.bf index f21ce83d..dc4e2e56 100644 --- a/IDE/Tests/CompileFail001/src/LocalVars.bf +++ b/IDE/Tests/CompileFail001/src/LocalVars.bf @@ -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 iResult = .Ok(123); + switch (iResult) + { + case .Ok(out val): + case .Err: break; + } + + int a = val; //FAIL + } + public void While1() { int a = 1; diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 3430f312..32d498a7 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -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 diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 2bf43df2..183f5754 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -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 typeMatches; SizedArray 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; } diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 53b845a9..27b4be18 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -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 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 mDeferredHandlers; // These get cleared when us our a parent gets new entries added into mDeferredCallEntries Array mAtEndBlocks; // Move these to the end after we close scope Array 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 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 mSkipObjectAccessChecks; // Indexed by BfIRValue value id Dictionary* 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(); diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index 3b6c0e0f..d329e16c 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -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(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 prevBreakData(mCurMethodState->mBreakData, &breakData); + BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope); + deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, false); + deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId; + SetAndRestoreValue 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(forEachStmt->mVariableTypeRef) != 0; auto isVar = BfNodeDynCast(forEachStmt->mVariableTypeRef) != 0; + BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope); + deferredLocalAssignData.mIsIfCondition = true; + deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, true); + deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId; + SetAndRestoreValue 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;