]> granicus.if.org Git - clang/commitdiff
When creating the CFGBlocks for a switch statement, we now have the "default"
authorTed Kremenek <kremenek@apple.com>
Wed, 13 Feb 2008 22:05:39 +0000 (22:05 +0000)
committerTed Kremenek <kremenek@apple.com>
Wed, 13 Feb 2008 22:05:39 +0000 (22:05 +0000)
branch ALWAYS be the last successor for a switch-terminated block. This allows
clients to distinguish cases like the following:

switch(...)
  case XXX:
    switch(...) {
      case YYY: ...
    }

  case ZZZ: ..
}

In this case, the block with "case ZZZ:" is the default block for the inner
switch statement, but that case is associated with the outer switch statement,
and not the inner one. Clients can test for this behavior by checking if a
successor block is the last one (and thus just assume that this is the "default"
case).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@47088 91177308-0d34-0410-b5e6-96231b3b80d8

AST/CFG.cpp

index eb39fdfc01d19538c126b1f7ab0e0b0d4c90216e..bc450989f7e975b4123bb964302dec30ed615bc7 100644 (file)
@@ -65,7 +65,7 @@ class VISIBILITY_HIDDEN CFGBuilder : public StmtVisitor<CFGBuilder,CFGBlock*> {
   CFGBlock* ContinueTargetBlock;
   CFGBlock* BreakTargetBlock;
   CFGBlock* SwitchTerminatedBlock;
-  bool      SwitchHasDefaultCase;
+  CFGBlock* DefaultCaseBlock;
   
   // LabelMap records the mapping from Label expressions to their blocks.
   typedef llvm::DenseMap<LabelStmt*,CFGBlock*> LabelMapTy;
@@ -83,7 +83,7 @@ class VISIBILITY_HIDDEN CFGBuilder : public StmtVisitor<CFGBuilder,CFGBlock*> {
 public:  
   explicit CFGBuilder() : cfg(NULL), Block(NULL), Succ(NULL),
                           ContinueTargetBlock(NULL), BreakTargetBlock(NULL),
-                          SwitchTerminatedBlock(NULL) {
+                          SwitchTerminatedBlock(NULL), DefaultCaseBlock(NULL) {
     // Create an empty CFG.
     cfg = new CFG();                        
   }
@@ -109,7 +109,7 @@ public:
   CFGBlock* VisitContinueStmt(ContinueStmt* C);
   CFGBlock* VisitBreakStmt(BreakStmt* B);
   CFGBlock* VisitSwitchStmt(SwitchStmt* S);
-  CFGBlock* VisitSwitchCase(SwitchCase* S);
+  CFGBlock* VisitCaseStmt(CaseStmt* S);
   CFGBlock* VisitDefaultStmt(DefaultStmt* D);
   CFGBlock* VisitIndirectGotoStmt(IndirectGotoStmt* I);
   
@@ -884,10 +884,13 @@ CFGBlock* CFGBuilder::VisitSwitchStmt(SwitchStmt* S) {
 
   // Save the current "switch" context.
   SaveAndRestore<CFGBlock*> save_switch(SwitchTerminatedBlock),
-                            save_break(BreakTargetBlock);
-  
-  SaveAndRestore<bool> save_has_default_case(SwitchHasDefaultCase);
-  SwitchHasDefaultCase = false;
+                            save_break(BreakTargetBlock),
+                            save_default(DefaultCaseBlock);
+
+  // Set the "default" case to be the block after the switch statement.
+  // If the switch statement contains a "default:", this value will
+  // be overwritten with the block for that code.
+  DefaultCaseBlock = SwitchSuccessor;
   
   // Create a new block that will contain the switch statement.
   SwitchTerminatedBlock = createBlock(false);
@@ -907,8 +910,7 @@ CFGBlock* CFGBuilder::VisitSwitchStmt(SwitchStmt* S) {
 
   // If we have no "default:" case, the default transition is to the
   // code following the switch body.
-  if (!SwitchHasDefaultCase)
-    SwitchTerminatedBlock->addSuccessor(SwitchSuccessor);
+  SwitchTerminatedBlock->addSuccessor(DefaultCaseBlock);
   
   // Add the terminator and condition in the switch block.
   SwitchTerminatedBlock->setTerminator(S);
@@ -918,23 +920,23 @@ CFGBlock* CFGBuilder::VisitSwitchStmt(SwitchStmt* S) {
   return addStmt(S->getCond());
 }
 
-CFGBlock* CFGBuilder::VisitSwitchCase(SwitchCase* S) {
-  // A SwitchCase is either a "default" or "case" statement.  We handle
-  // both in the same way.  They are essentially labels, so they are the
+CFGBlock* CFGBuilder::VisitCaseStmt(CaseStmt* S) {
+  // CaseStmts are essentially labels, so they are the
   // first statement in a block.      
 
   if (S->getSubStmt()) Visit(S->getSubStmt());
   CFGBlock* CaseBlock = Block;
   if (!CaseBlock) CaseBlock = createBlock();  
     
-  // Cases/Default statements partition block, so this is the top of
-  // the basic block we were processing (the case/default is the label).
+  // Cases statements partition blocks, so this is the top of
+  // the basic block we were processing (the "case XXX:" is the label).
   CaseBlock->setLabel(S);
   FinishBlock(CaseBlock);
   
   // Add this block to the list of successors for the block with the
   // switch statement.
-  if (SwitchTerminatedBlock) SwitchTerminatedBlock->addSuccessor(CaseBlock);
+  assert (SwitchTerminatedBlock);
+  SwitchTerminatedBlock->addSuccessor(CaseBlock);
   
   // We set Block to NULL to allow lazy creation of a new block (if necessary)
   Block = NULL;
@@ -945,9 +947,30 @@ CFGBlock* CFGBuilder::VisitSwitchCase(SwitchCase* S) {
   return CaseBlock;    
 }
   
-CFGBlock* CFGBuilder::VisitDefaultStmt(DefaultStmt* D) {
-  SwitchHasDefaultCase = true;
-  return VisitSwitchCase(D);
+CFGBlock* CFGBuilder::VisitDefaultStmt(DefaultStmt* S) {
+  if (S->getSubStmt()) Visit(S->getSubStmt());
+  DefaultCaseBlock = Block;
+  if (!DefaultCaseBlock) DefaultCaseBlock = createBlock();  
+  
+  // Default statements partition blocks, so this is the top of
+  // the basic block we were processing (the "default:" is the label).
+  DefaultCaseBlock->setLabel(S);
+  FinishBlock(DefaultCaseBlock);
+
+  // Unlike case statements, we don't add the default block to the
+  // successors for the switch statement immediately.  This is done
+  // when we finish processing the switch statement.  This allows for
+  // the default case (including a fall-through to the code after the
+  // switch statement) to always be the last successor of a switch-terminated
+  // block.
+  
+  // We set Block to NULL to allow lazy creation of a new block (if necessary)
+  Block = NULL;
+  
+  // This block is now the implicit successor of other blocks.
+  Succ = DefaultCaseBlock;
+  
+  return DefaultCaseBlock;  
 }
 
 CFGBlock* CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt* I) {