diff options
author | Serge Pavlov <sepavloff@gmail.com> | 2014-01-23 15:05:00 +0000 |
---|---|---|
committer | Serge Pavlov <sepavloff@gmail.com> | 2014-01-23 15:05:00 +0000 |
commit | 09f9924acf1a8644e9458833d3c8ea40d7ddbc46 (patch) | |
tree | ab81b6c65a12f96059f2bed913314d8f138c7925 /clang/lib | |
parent | 5a68c4b28747fc5da182916b801de9a34e59a0dd (diff) | |
download | bcm5719-llvm-09f9924acf1a8644e9458833d3c8ea40d7ddbc46.tar.gz bcm5719-llvm-09f9924acf1a8644e9458833d3c8ea40d7ddbc46.zip |
Fix to PR8880 (clang dies processing a for loop)
Due to statement expressions supported as GCC extension, it is possible
to put 'break' or 'continue' into a loop/switch statement but outside
its body, for example:
for ( ; ({ if (first) { first = 0; continue; } 0; }); )
This code is rejected by GCC if compiled in C mode but is accepted in C++
code. GCC bug 44715 tracks this discrepancy. Clang used code generation
that differs from GCC in both modes: only statement of the third
expression of 'for' behaves as if it was inside loop body.
This change makes code generation more close to GCC, considering 'break'
or 'continue' statement in condition and increment expressions of a
loop as it was inside the loop body. It also adds error for the cases
when 'break'/'continue' appear outside loop due to this syntax. If
code generation differ from GCC, warning is issued.
Differential Revision: http://llvm-reviews.chandlerc.com/D2518
llvm-svn: 199897
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/CodeGen/CGStmt.cpp | 24 | ||||
-rw-r--r-- | clang/lib/Parse/ParseStmt.cpp | 13 | ||||
-rw-r--r-- | clang/lib/Sema/Scope.cpp | 15 | ||||
-rw-r--r-- | clang/lib/Sema/SemaStmt.cpp | 56 |
4 files changed, 78 insertions, 30 deletions
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 880e801189a..eba0c2dcac7 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -614,8 +614,6 @@ void CodeGenFunction::EmitDoStmt(const DoStmt &S) { } Cnt.adjustForControlFlow(); - BreakContinueStack.pop_back(); - EmitBlock(LoopCond.getBlock()); // C99 6.8.5.2: "The evaluation of the controlling expression takes place @@ -626,6 +624,8 @@ void CodeGenFunction::EmitDoStmt(const DoStmt &S) { // compares unequal to 0. The condition must be a scalar type. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + BreakContinueStack.pop_back(); + // "do {} while (0)" is common in macros, avoid extra blocks. Be sure // to correctly handle break/continue though. bool EmitBoolCondBranch = true; @@ -673,6 +673,16 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S) { llvm::BasicBlock *CondBlock = Continue.getBlock(); EmitBlock(CondBlock); + // If the for loop doesn't have an increment we can just use the + // condition as the continue block. Otherwise we'll need to create + // a block for it (in the current scope, i.e. in the scope of the + // condition), and that we will become our continue block. + if (S.getInc()) + Continue = getJumpDestInCurrentScope("for.inc"); + + // Store the blocks to use for break and continue. + BreakContinueStack.push_back(BreakContinue(LoopExit, Continue, &Cnt)); + // Create a cleanup scope for the condition variable cleanups. RunCleanupsScope ConditionScope(*this); @@ -710,16 +720,6 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S) { } Cnt.beginRegion(Builder); - // If the for loop doesn't have an increment we can just use the - // condition as the continue block. Otherwise we'll need to create - // a block for it (in the current scope, i.e. in the scope of the - // condition), and that we will become our continue block. - if (S.getInc()) - Continue = getJumpDestInCurrentScope("for.inc"); - - // Store the blocks to use for break and continue. - BreakContinueStack.push_back(BreakContinue(LoopExit, Continue, &Cnt)); - { // Create a separate cleanup scope for the body, in case it is not // a compound statement. diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index f131ad855f5..f2e4ad9f7b1 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1168,7 +1168,7 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) { // while, for, and switch statements are local to the if, while, for, or // switch statement (including the controlled statement). // - unsigned ScopeFlags = Scope::BreakScope | Scope::SwitchScope; + unsigned ScopeFlags = Scope::SwitchScope; if (C99orCXX) ScopeFlags |= Scope::DeclScope | Scope::ControlScope; ParseScope SwitchScope(this, ScopeFlags); @@ -1206,6 +1206,7 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) { // See comments in ParseIfStatement for why we create a scope for the // condition and a new scope for substatement in C++. // + getCurScope()->AddFlags(Scope::BreakScope); ParseScope InnerScope(this, Scope::DeclScope, C99orCXX && Tok.isNot(tok::l_brace)); @@ -1417,12 +1418,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { // Names declared in the for-init-statement are in the same declarative-region // as those declared in the condition. // - unsigned ScopeFlags; + unsigned ScopeFlags = 0; if (C99orCXXorObjC) - ScopeFlags = Scope::BreakScope | Scope::ContinueScope | - Scope::DeclScope | Scope::ControlScope; - else - ScopeFlags = Scope::BreakScope | Scope::ContinueScope; + ScopeFlags = Scope::DeclScope | Scope::ControlScope; ParseScope ForScope(this, ScopeFlags); @@ -1537,6 +1535,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { } } } + + // Parse the second part of the for specifier. + getCurScope()->AddFlags(Scope::BreakScope | Scope::ContinueScope); if (!ForEach && !ForRange) { assert(!SecondPart.get() && "Shouldn't have a second expression yet."); // Parse the second part of the for specifier. diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp index 10f12ce844f..eef55d31947 100644 --- a/clang/lib/Sema/Scope.cpp +++ b/clang/lib/Sema/Scope.cpp @@ -69,3 +69,18 @@ bool Scope::containedInPrototypeScope() const { } return false; } + +void Scope::AddFlags(unsigned FlagsToSet) { + assert((FlagsToSet & ~(BreakScope | ContinueScope)) == 0 && + "Unsupported scope flags"); + if (FlagsToSet & BreakScope) { + assert((Flags & BreakScope) == 0 && "Already set"); + BreakParent = this; + } + if (FlagsToSet & ContinueScope) { + assert((Flags & ContinueScope) == 0 && "Already set"); + ContinueParent = this; + } + Flags |= FlagsToSet; +} + diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 34a0f84e8f3..badd048bd87 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1205,6 +1205,7 @@ Sema::ActOnWhileStmt(SourceLocation WhileLoc, FullExprArg Cond, Expr *ConditionExpr = CondResult.take(); if (!ConditionExpr) return StmtError(); + CheckBreakContinueBinding(ConditionExpr); DiagnoseUnusedExprResult(Body); @@ -1221,6 +1222,7 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, Expr *Cond, SourceLocation CondRParen) { assert(Cond && "ActOnDoStmt(): missing expression"); + CheckBreakContinueBinding(Cond); ExprResult CondResult = CheckBooleanCondition(Cond, DoLoc); if (CondResult.isInvalid()) return StmtError(); @@ -1483,25 +1485,33 @@ namespace { return false; } - // A visitor to determine if a continue statement is a subexpression. - class ContinueFinder : public EvaluatedExprVisitor<ContinueFinder> { - bool Found; + // A visitor to determine if a continue or break statement is a + // subexpression. + class BreakContinueFinder : public EvaluatedExprVisitor<BreakContinueFinder> { + SourceLocation BreakLoc; + SourceLocation ContinueLoc; public: - ContinueFinder(Sema &S, Stmt* Body) : - Inherited(S.Context), - Found(false) { + BreakContinueFinder(Sema &S, Stmt* Body) : + Inherited(S.Context) { Visit(Body); } - typedef EvaluatedExprVisitor<ContinueFinder> Inherited; + typedef EvaluatedExprVisitor<BreakContinueFinder> Inherited; void VisitContinueStmt(ContinueStmt* E) { - Found = true; + ContinueLoc = E->getContinueLoc(); + } + + void VisitBreakStmt(BreakStmt* E) { + BreakLoc = E->getBreakLoc(); } - bool ContinueFound() { return Found; } + bool ContinueFound() { return ContinueLoc.isValid(); } + bool BreakFound() { return BreakLoc.isValid(); } + SourceLocation GetContinueLoc() { return ContinueLoc; } + SourceLocation GetBreakLoc() { return BreakLoc; } - }; // end class ContinueFinder + }; // end class BreakContinueFinder // Emit a warning when a loop increment/decrement appears twice per loop // iteration. The conditions which trigger this warning are: @@ -1530,11 +1540,11 @@ namespace { if (!ProcessIterationStmt(S, LastStmt, LastIncrement, LastDRE)) return; // Check that the two statements are both increments or both decrements - // on the same varaible. + // on the same variable. if (LoopIncrement != LastIncrement || LoopDRE->getDecl() != LastDRE->getDecl()) return; - if (ContinueFinder(S, Body).ContinueFound()) return; + if (BreakContinueFinder(S, Body).ContinueFound()) return; S.Diag(LastDRE->getLocation(), diag::warn_redundant_loop_iteration) << LastDRE->getDecl() << LastIncrement; @@ -1544,6 +1554,25 @@ namespace { } // end namespace + +void Sema::CheckBreakContinueBinding(Expr *E) { + if (!E || getLangOpts().CPlusPlus) + return; + BreakContinueFinder BCFinder(*this, E); + Scope *BreakParent = CurScope->getBreakParent(); + if (BCFinder.BreakFound() && BreakParent) { + if (BreakParent->getFlags() & Scope::SwitchScope) { + Diag(BCFinder.GetBreakLoc(), diag::warn_break_binds_to_switch); + } else { + Diag(BCFinder.GetBreakLoc(), diag::warn_loop_ctrl_binds_to_inner) + << "break"; + } + } else if (BCFinder.ContinueFound() && CurScope->getContinueParent()) { + Diag(BCFinder.GetContinueLoc(), diag::warn_loop_ctrl_binds_to_inner) + << "continue"; + } +} + StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, Stmt *First, FullExprArg second, Decl *secondVar, @@ -1567,6 +1596,9 @@ Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, } } + CheckBreakContinueBinding(second.get()); + CheckBreakContinueBinding(third.get()); + CheckForLoopConditionalStatement(*this, second.get(), third.get(), Body); CheckForRedundantIteration(*this, third.get(), Body); |