diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 4083e862..d8a33fb6 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -11174,7 +11174,7 @@ BfAllocTarget BfExprEvaluator::ResolveAllocTarget(BfAstNode* allocNode, BfTokenN if (auto scopeNode = BfNodeDynCast(allocNode)) { newToken = scopeNode->mScopeToken; - allocTarget.mScopeData = mModule->FindScope(scopeNode->mTargetNode); + allocTarget.mScopeData = mModule->FindScope(scopeNode->mTargetNode, true); if (autoComplete != NULL) { diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 9978c722..b1e18cc4 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -2613,7 +2613,7 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers bfError->mIsWhileSpecializing = isWhileSpecializing; if ((mCurMethodState != NULL) && (mCurMethodState->mDeferredCallEmitState != NULL) && (mCurMethodState->mDeferredCallEmitState->mCloseNode != NULL)) - mCompiler->mPassInstance->MoreInfo("Error during deferred code emission", mCurMethodState->mDeferredCallEmitState->mCloseNode); + mCompiler->mPassInstance->MoreInfo("Error during deferred statement handling", mCurMethodState->mDeferredCallEmitState->mCloseNode); } return bfError; @@ -12033,14 +12033,22 @@ void BfModule::CheckVariableDef(BfLocalVariable* variableDef) } } -BfScopeData* BfModule::FindScope(BfAstNode* scopeName, BfMixinState* fromMixinState) +BfScopeData* BfModule::FindScope(BfAstNode* scopeName, BfMixinState* fromMixinState, bool allowAcrossDeferredBlock) { bool inMixinDecl = (mCurMethodInstance != NULL) && (mCurMethodInstance->IsMixin()); if (auto tokenNode = BfNodeDynCast(scopeName)) { if (tokenNode->GetToken() == BfToken_Colon) + { + if ((!allowAcrossDeferredBlock) && (mCurMethodState->mInDeferredBlock)) + { + Fail("Cannot access method scope across deferred block boundary", scopeName); + return NULL; + } + return &mCurMethodState->mHeadScope; + } else if (tokenNode->GetToken() == BfToken_Mixin) { if (fromMixinState == NULL) @@ -12061,11 +12069,23 @@ BfScopeData* BfModule::FindScope(BfAstNode* scopeName, BfMixinState* fromMixinSt { auto findLabel = scopeName->ToString(); + bool crossedDeferredBlock = false; + auto checkScope = mCurMethodState->mCurScope; while (checkScope != NULL) { + if (checkScope->mIsDeferredBlock) + crossedDeferredBlock = true; + if (checkScope->mLabel == findLabel) + { + if ((crossedDeferredBlock) && (!allowAcrossDeferredBlock)) + { + Fail(StrFormat("Cannot access scope '%s' across deferred block boundary", findLabel.c_str()), scopeName); + return NULL; + } return checkScope; + } checkScope = checkScope->mPrevScope; } @@ -12075,20 +12095,20 @@ BfScopeData* BfModule::FindScope(BfAstNode* scopeName, BfMixinState* fromMixinSt if (auto scopeNode = BfNodeDynCast(scopeName)) { - return FindScope(scopeNode->mTargetNode); + return FindScope(scopeNode->mTargetNode, allowAcrossDeferredBlock); } return mCurMethodState->mCurScope; } -BfScopeData* BfModule::FindScope(BfAstNode* scopeName) +BfScopeData* BfModule::FindScope(BfAstNode* scopeName, bool allowAcrossDeferredBlock) { - return FindScope(scopeName, mCurMethodState->mMixinState); + return FindScope(scopeName, mCurMethodState->mMixinState, allowAcrossDeferredBlock); } BfBreakData* BfModule::FindBreakData(BfAstNode* scopeName) { - auto scopeData = FindScope(scopeName); + auto scopeData = FindScope(scopeName, false); if (scopeData == NULL) return NULL; int scopeDepth = scopeData->GetDepth(); @@ -12097,6 +12117,8 @@ BfBreakData* BfModule::FindBreakData(BfAstNode* scopeName) auto breakData = mCurMethodState->mBreakData; while ((breakData != NULL) && (scopeDepth < breakData->mScope->GetDepth())) { + if (breakData->mScope->mIsConditional) + return NULL; breakData = breakData->mPrevBreakData; } return breakData; @@ -12169,7 +12191,7 @@ void BfModule::EmitDeferredScopeCalls(bool useSrcPositions, BfScopeData* scopeDa { if (deferredCallEntry->mDeferredBlock != NULL) { - VisitChild(deferredCallEntry->mDeferredBlock); + VisitEmbeddedStatement(deferredCallEntry->mDeferredBlock, NULL, BfEmbeddedStatementFlags_IsDeferredBlock); } deferredCallEntry = deferredCallEntry->mNext; } @@ -12522,6 +12544,9 @@ void BfModule::EmitReturn(BfIRValue val) void BfModule::EmitDefaultReturn() { + if (mCurMethodState->mInDeferredBlock) + return; + if (mCurMethodState->mIRExitBlock) { EmitDeferredScopeCalls(true, NULL, mCurMethodState->mIRExitBlock); diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 1dda21fe..b07dab01 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -107,6 +107,13 @@ enum BfProtectionCheckFlags BfProtectionCheckFlag_AllowPrivate = 8, }; +enum BfEmbeddedStatementFlags +{ + BfEmbeddedStatementFlags_None = 0, + BfEmbeddedStatementFlags_IsConditional = 1, + BfEmbeddedStatementFlags_IsDeferredBlock = 2 +}; + class BfLocalVariable { public: @@ -299,7 +306,8 @@ public: bool mInnerIsConditional; bool mHadOuterDynStack; bool mAllowTargeting; - bool mHadScopeValueRetain; + bool mHadScopeValueRetain; + bool mIsDeferredBlock; BfBlock* mAstBlock; BfAstNode* mCloseNode; BfExprEvaluator* mExprEvaluator; @@ -329,6 +337,7 @@ public: mInnerIsConditional = false; mHadOuterDynStack = false; mHadScopeValueRetain = false; + mIsDeferredBlock = false; mAllowTargeting = true; mMixinDepth = 0; mScopeDepth = 0; @@ -870,12 +879,13 @@ public: BfScopeData* mCurScope; BfScopeData* mTailScope; // Usually equals mCurScope TempKind mTempKind; // Used for var inference, etc + bool mInDeferredBlock; bool mHadReturn; bool mHadContinue; bool mMayNeedThisAccessCheck; bool mLeftBlockUncond; // Definitely left block. mHadReturn also sets mLeftBlock bool mLeftBlockCond; // May have left block. - bool mInPostReturn; // Unreachable code + bool mInPostReturn; // Unreachable code bool mCrossingMixin; // ie: emitting dtors in response to a return in a mixin bool mNoBind; bool mInConditionalBlock; // IE: RHS of ((A) && (B)), indicates an allocation in 'B' won't be dominated by a dtor, for example @@ -904,7 +914,7 @@ public: mInHeadScope = true; mBreakData = NULL; mBlockNestLevel = 0; - mInPostReturn = false; + mInPostReturn = false; mCrossingMixin = false; mNoBind = false; mIsEmbedded = false; @@ -913,6 +923,7 @@ public: mAllowUinitReads = false; mCancelledDeferredCall = false; mNoObjectAccessChecks = false; + mInDeferredBlock = false; mDeferredLocalAssignData = NULL; mCurLocalVarId = 0; mCurAccessId = 1; @@ -1410,7 +1421,7 @@ public: void ValueScopeEnd(BfIRValue valueScopeStart); void AddBasicBlock(BfIRBlock bb, bool activate = true); - void VisitEmbeddedStatement(BfAstNode* stmt, BfExprEvaluator* exprEvaluator = NULL, bool isConditional = false); + void VisitEmbeddedStatement(BfAstNode* stmt, BfExprEvaluator* exprEvaluator = NULL, BfEmbeddedStatementFlags flags = BfEmbeddedStatementFlags_None); void VisitCodeBlock(BfBlock* block); void VisitCodeBlock(BfBlock* block, BfIRBlock continueBlock, BfIRBlock breakBlock, BfIRBlock fallthroughBlock, bool defaultBreak, bool* hadReturn = NULL, BfLabelNode* labelNode = NULL, bool closeScope = false); void DoForLess(BfForEachStatement* forEachStmt); @@ -1594,8 +1605,8 @@ public: BfLocalVariable* HandleVariableDeclaration(BfVariableDeclaration* varDecl, BfExprEvaluator* exprEvaluator = NULL); BfLocalVariable* HandleVariableDeclaration(BfVariableDeclaration* varDecl, BfTypedValue val, bool updateSrcLoc = true, bool forceAddr = false); void CheckVariableDef(BfLocalVariable* variableDef); - BfScopeData* FindScope(BfAstNode* scopeName, BfMixinState* curMixinState); - BfScopeData* FindScope(BfAstNode* scopeName); + BfScopeData* FindScope(BfAstNode* scopeName, BfMixinState* curMixinState, bool allowAcrossDeferredBlock); + BfScopeData* FindScope(BfAstNode* scopeName, bool allowAcrossDeferredBlock); BfBreakData* FindBreakData(BfAstNode* scopeName); void EmitLifetimeEnds(BfScopeData* scopeData); void ClearLifetimeEnds(); diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index b7768d18..ba220de1 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -661,7 +661,8 @@ void BfModule::EmitDeferredCall(BfDeferredCallEntry& deferredCallEntry) } AddLocalVariableDef(localVar, true); } - VisitEmbeddedStatement(deferredCallEntry.mDeferredBlock); + + VisitEmbeddedStatement(deferredCallEntry.mDeferredBlock, NULL, BfEmbeddedStatementFlags_IsDeferredBlock); RestoreScopeState(); return; } @@ -2744,7 +2745,7 @@ void BfModule::AddBasicBlock(BfIRBlock bb, bool activate) mBfIRBuilder->SetInsertPoint(bb); } -void BfModule::VisitEmbeddedStatement(BfAstNode* stmt, BfExprEvaluator* exprEvaluator, bool isConditional) +void BfModule::VisitEmbeddedStatement(BfAstNode* stmt, BfExprEvaluator* exprEvaluator, BfEmbeddedStatementFlags flags) { auto block = BfNodeDynCast(stmt); BfLabelNode* labelNode = NULL; @@ -2799,17 +2800,24 @@ void BfModule::VisitEmbeddedStatement(BfAstNode* stmt, BfExprEvaluator* exprEval if (labelNode != NULL) scopeData.mLabelNode = labelNode->mLabel; NewScopeState(block != NULL); - mCurMethodState->mCurScope->mOuterIsConditional = isConditional; + mCurMethodState->mCurScope->mOuterIsConditional = (flags & BfEmbeddedStatementFlags_IsConditional) != 0; + mCurMethodState->mCurScope->mIsDeferredBlock = (flags & BfEmbeddedStatementFlags_IsDeferredBlock) != 0; mCurMethodState->mCurScope->mExprEvaluator = exprEvaluator; - if (block != NULL) + + // { - if (labelNode != NULL) - VisitCodeBlock(block, BfIRBlock(), BfIRBlock(), BfIRBlock(), false, NULL, labelNode); + SetAndRestoreValue inDeferredBlock(mCurMethodState->mInDeferredBlock, mCurMethodState->mInDeferredBlock || mCurMethodState->mCurScope->mIsDeferredBlock); + if (block != NULL) + { + if (labelNode != NULL) + VisitCodeBlock(block, BfIRBlock(), BfIRBlock(), BfIRBlock(), false, NULL, labelNode); + else + VisitCodeBlock(block); + } else - VisitCodeBlock(block); + VisitChild(stmt); } - else - VisitChild(stmt); + if ((block != NULL) && (closeBrace != NULL)) { UpdateSrcPos(closeBrace); @@ -2942,9 +2950,9 @@ void BfModule::VisitCodeBlock(BfBlock* block) String* namePtr; if (!mCurMethodState->mLocalMethodMap.TryAdd(localMethod->mMethodName, &namePtr, &localMethodPtr)) - { - BF_ASSERT(localMethod->mNextWithSameName != *localMethodPtr); - localMethod->mNextWithSameName = *localMethodPtr; + { + BF_ASSERT(localMethod != *localMethodPtr); + localMethod->mNextWithSameName = *localMethodPtr; } *localMethodPtr = localMethod; } @@ -3334,7 +3342,7 @@ void BfModule::DoIfStatement(BfIfStatement* ifStmt, bool includeTrueStmt, bool i falseDeferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData); SetAndRestoreValue prevDLA(mCurMethodState->mDeferredLocalAssignData, &falseDeferredLocalAssignData); if (includeFalseStmt) - VisitEmbeddedStatement(ifStmt->mFalseStatement, NULL, true); + VisitEmbeddedStatement(ifStmt->mFalseStatement, NULL, BfEmbeddedStatementFlags_IsConditional); } if ((!mCurMethodState->mLeftBlockUncond) && (!ignoredLastBlock)) { @@ -4498,6 +4506,22 @@ void BfModule::Visit(BfReturnStatement* returnStmt) if (mCurMethodState->mClosureState != NULL) retType = mCurMethodState->mClosureState->mReturnType; + auto checkScope = mCurMethodState->mCurScope; + while (checkScope != NULL) + { + if (checkScope->mIsDeferredBlock) + { + Fail("Deferred blocks cannot contain 'return' statements", returnStmt); + if (returnStmt->mExpression != NULL) + { + BfExprEvaluator exprEvaluator(this); + CreateValueFromExpression(exprEvaluator, returnStmt->mExpression, GetPrimitiveType(BfTypeCode_Var), BfEvalExprFlags_None); + } + return; + } + checkScope = checkScope->mPrevScope; + } + if (retType == NULL) { if (returnStmt->mExpression != NULL) @@ -4616,7 +4640,7 @@ void BfModule::Visit(BfBreakStatement* breakStmt) if (breakData->mIRBreakBlock) break; breakData = breakData->mPrevBreakData; - } + } } if ((breakData == NULL) || (!breakData->mIRBreakBlock)) @@ -4630,6 +4654,22 @@ void BfModule::Visit(BfBreakStatement* breakStmt) return; } + if (mCurMethodState->mInDeferredBlock) + { + auto checkScope = mCurMethodState->mCurScope; + while (checkScope != NULL) + { + if (checkScope == breakData->mScope) + break; + if (checkScope->mIsDeferredBlock) + { + Fail("The break target crosses a deferred block boundary", breakStmt); + return; + } + checkScope = checkScope->mPrevScope; + } + } + if (HasDeferredScopeCalls(breakData->mScope)) { EmitDeferredScopeCalls(true, breakData->mScope, breakData->mIRBreakBlock); @@ -4724,6 +4764,22 @@ void BfModule::Visit(BfContinueStatement* continueStmt) if (breakData->mInnerValueScopeStart) earliestValueScopeStart = breakData->mInnerValueScopeStart; + if (mCurMethodState->mInDeferredBlock) + { + auto checkScope = mCurMethodState->mCurScope; + while (checkScope != NULL) + { + if (checkScope == breakData->mScope) + break; + if (checkScope->mIsDeferredBlock) + { + Fail("The continue target crosses a deferred block boundary", continueStmt); + return; + } + checkScope = checkScope->mPrevScope; + } + } + if ((nextScope != NULL) && (HasDeferredScopeCalls(nextScope))) { EmitDeferredScopeCalls(true, nextScope, breakData->mIRContinueBlock); @@ -6021,7 +6077,7 @@ void BfModule::Visit(BfDeferStatement* deferStmt) scope = &mCurMethodState->mHeadScope; } else if (deferStmt->mScopeName != NULL) - scope = FindScope(deferStmt->mScopeName); + scope = FindScope(deferStmt->mScopeName, true); else scope = mCurMethodState->mCurScope;