Index: cfe/trunk/include/clang/AST/Stmt.h =================================================================== --- cfe/trunk/include/clang/AST/Stmt.h +++ cfe/trunk/include/clang/AST/Stmt.h @@ -177,6 +177,17 @@ unsigned : NumStmtBits; + /// True if the SwitchStmt has storage for an init statement. + unsigned HasInit : 1; + + /// True if the SwitchStmt has storage for a condition variable. + unsigned HasVar : 1; + + /// If the SwitchStmt is a switch on an enum value, records whether all + /// the enum values were covered by CaseStmts. The coverage information + /// value is meant to be a hint for possible clients. + unsigned AllEnumCasesCovered : 1; + /// The location of the "switch". SourceLocation SwitchLoc; }; @@ -1427,21 +1438,102 @@ }; /// SwitchStmt - This represents a 'switch' stmt. -class SwitchStmt : public Stmt { - enum { INIT, VAR, COND, BODY, END_EXPR }; - Stmt* SubExprs[END_EXPR]; +class SwitchStmt final : public Stmt, + private llvm::TrailingObjects { + friend TrailingObjects; - // This points to a linked list of case and default statements and, if the - // SwitchStmt is a switch on an enum value, records whether all the enum - // values were covered by CaseStmts. The coverage information value is meant - // to be a hint for possible clients. - llvm::PointerIntPair FirstCase; + /// Points to a linked list of case and default statements. + SwitchCase *FirstCase; -public: - SwitchStmt(const ASTContext &C, Stmt *Init, VarDecl *Var, Expr *cond); + // SwitchStmt is followed by several trailing objects, + // some of which optional. Note that it would be more convenient to + // put the optional trailing objects at the end but this would change + // the order in children(). + // The trailing objects are in order: + // + // * A "Stmt *" for the init statement. + // Present if and only if hasInitStorage(). + // + // * A "Stmt *" for the condition variable. + // Present if and only if hasVarStorage(). This is in fact a "DeclStmt *". + // + // * A "Stmt *" for the condition. + // Always present. This is in fact an "Expr *". + // + // * A "Stmt *" for the body. + // Always present. + enum { InitOffset = 0, BodyOffsetFromCond = 1 }; + enum { NumMandatoryStmtPtr = 2 }; + + unsigned numTrailingObjects(OverloadToken) const { + return NumMandatoryStmtPtr + hasInitStorage() + hasVarStorage(); + } + + unsigned initOffset() const { return InitOffset; } + unsigned varOffset() const { return InitOffset + hasInitStorage(); } + unsigned condOffset() const { + return InitOffset + hasInitStorage() + hasVarStorage(); + } + unsigned bodyOffset() const { return condOffset() + BodyOffsetFromCond; } + + /// Build a switch statement. + SwitchStmt(const ASTContext &Ctx, Stmt *Init, VarDecl *Var, Expr *Cond); /// Build a empty switch statement. - explicit SwitchStmt(EmptyShell Empty) : Stmt(SwitchStmtClass, Empty) {} + explicit SwitchStmt(EmptyShell Empty, bool HasInit, bool HasVar); + +public: + /// Create a switch statement. + static SwitchStmt *Create(const ASTContext &Ctx, Stmt *Init, VarDecl *Var, + Expr *Cond); + + /// Create an empty switch statement optionally with storage for + /// an init expression and a condition variable. + static SwitchStmt *CreateEmpty(const ASTContext &Ctx, bool HasInit, + bool HasVar); + + /// True if this SwitchStmt has storage for an init statement. + bool hasInitStorage() const { return SwitchStmtBits.HasInit; } + + /// True if this SwitchStmt has storage for a condition variable. + bool hasVarStorage() const { return SwitchStmtBits.HasVar; } + + Expr *getCond() { + return reinterpret_cast(getTrailingObjects()[condOffset()]); + } + + const Expr *getCond() const { + return reinterpret_cast(getTrailingObjects()[condOffset()]); + } + + void setCond(Expr *Cond) { + getTrailingObjects()[condOffset()] = reinterpret_cast(Cond); + } + + Stmt *getBody() { return getTrailingObjects()[bodyOffset()]; } + const Stmt *getBody() const { + return getTrailingObjects()[bodyOffset()]; + } + + void setBody(Stmt *Body) { + getTrailingObjects()[bodyOffset()] = Body; + } + + Stmt *getInit() { + return hasInitStorage() ? getTrailingObjects()[initOffset()] + : nullptr; + } + + const Stmt *getInit() const { + return hasInitStorage() ? getTrailingObjects()[initOffset()] + : nullptr; + } + + void setInit(Stmt *Init) { + assert(hasInitStorage() && + "This switch statement has no storage for an init statement!"); + getTrailingObjects()[initOffset()] = Init; + } /// Retrieve the variable declared in this "switch" statement, if any. /// @@ -1452,64 +1544,69 @@ /// // ... /// } /// \endcode - VarDecl *getConditionVariable() const; - void setConditionVariable(const ASTContext &C, VarDecl *V); + VarDecl *getConditionVariable(); + const VarDecl *getConditionVariable() const { + return const_cast(this)->getConditionVariable(); + } + + /// Set the condition variable in this switch statement. + /// The switch statement must have storage for it. + void setConditionVariable(const ASTContext &Ctx, VarDecl *VD); /// If this SwitchStmt has a condition variable, return the faux DeclStmt /// associated with the creation of that condition variable. - const DeclStmt *getConditionVariableDeclStmt() const { - return reinterpret_cast(SubExprs[VAR]); + DeclStmt *getConditionVariableDeclStmt() { + return hasVarStorage() ? static_cast( + getTrailingObjects()[varOffset()]) + : nullptr; } - Stmt *getInit() { return SubExprs[INIT]; } - const Stmt *getInit() const { return SubExprs[INIT]; } - void setInit(Stmt *S) { SubExprs[INIT] = S; } - const Expr *getCond() const { return reinterpret_cast(SubExprs[COND]);} - const Stmt *getBody() const { return SubExprs[BODY]; } - const SwitchCase *getSwitchCaseList() const { return FirstCase.getPointer(); } - - Expr *getCond() { return reinterpret_cast(SubExprs[COND]);} - void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast(E); } - Stmt *getBody() { return SubExprs[BODY]; } - void setBody(Stmt *S) { SubExprs[BODY] = S; } - SwitchCase *getSwitchCaseList() { return FirstCase.getPointer(); } + const DeclStmt *getConditionVariableDeclStmt() const { + return hasVarStorage() ? static_cast( + getTrailingObjects()[varOffset()]) + : nullptr; + } - /// Set the case list for this switch statement. - void setSwitchCaseList(SwitchCase *SC) { FirstCase.setPointer(SC); } + SwitchCase *getSwitchCaseList() { return FirstCase; } + const SwitchCase *getSwitchCaseList() const { return FirstCase; } + void setSwitchCaseList(SwitchCase *SC) { FirstCase = SC; } SourceLocation getSwitchLoc() const { return SwitchStmtBits.SwitchLoc; } void setSwitchLoc(SourceLocation L) { SwitchStmtBits.SwitchLoc = L; } void setBody(Stmt *S, SourceLocation SL) { - SubExprs[BODY] = S; - SwitchStmtBits.SwitchLoc = SL; + setBody(S); + setSwitchLoc(SL); } void addSwitchCase(SwitchCase *SC) { - assert(!SC->getNextSwitchCase() - && "case/default already added to a switch"); - SC->setNextSwitchCase(FirstCase.getPointer()); - FirstCase.setPointer(SC); + assert(!SC->getNextSwitchCase() && + "case/default already added to a switch"); + SC->setNextSwitchCase(FirstCase); + FirstCase = SC; } /// Set a flag in the SwitchStmt indicating that if the 'switch (X)' is a /// switch over an enum value then all cases have been explicitly covered. - void setAllEnumCasesCovered() { FirstCase.setInt(true); } + void setAllEnumCasesCovered() { SwitchStmtBits.AllEnumCasesCovered = true; } /// Returns true if the SwitchStmt is a switch of an enum value and all cases /// have been explicitly covered. - bool isAllEnumCasesCovered() const { return FirstCase.getInt(); } + bool isAllEnumCasesCovered() const { + return SwitchStmtBits.AllEnumCasesCovered; + } SourceLocation getBeginLoc() const { return getSwitchLoc(); } - - SourceLocation getEndLoc() const { - return SubExprs[BODY] ? SubExprs[BODY]->getEndLoc() - : SubExprs[COND]->getEndLoc(); + SourceLocation getEndLoc() const LLVM_READONLY { + return getBody() ? getBody()->getEndLoc() + : reinterpret_cast(getCond())->getEndLoc(); } // Iterators child_range children() { - return child_range(&SubExprs[0], &SubExprs[0]+END_EXPR); + return child_range(getTrailingObjects(), + getTrailingObjects() + + numTrailingObjects(OverloadToken())); } static bool classof(const Stmt *T) { Index: cfe/trunk/lib/AST/ASTDumper.cpp =================================================================== --- cfe/trunk/lib/AST/ASTDumper.cpp +++ cfe/trunk/lib/AST/ASTDumper.cpp @@ -512,6 +512,7 @@ void VisitDeclStmt(const DeclStmt *Node); void VisitAttributedStmt(const AttributedStmt *Node); void VisitIfStmt(const IfStmt *Node); + void VisitSwitchStmt(const SwitchStmt *Node); void VisitLabelStmt(const LabelStmt *Node); void VisitGotoStmt(const GotoStmt *Node); void VisitCXXCatchStmt(const CXXCatchStmt *Node); @@ -2032,6 +2033,14 @@ OS << " has_else"; } +void ASTDumper::VisitSwitchStmt(const SwitchStmt *Node) { + VisitStmt(Node); + if (Node->hasInitStorage()) + OS << " has_init"; + if (Node->hasVarStorage()) + OS << " has_var"; +} + void ASTDumper::VisitLabelStmt(const LabelStmt *Node) { VisitStmt(Node); OS << " '" << Node->getName() << "'"; Index: cfe/trunk/lib/AST/ASTImporter.cpp =================================================================== --- cfe/trunk/lib/AST/ASTImporter.cpp +++ cfe/trunk/lib/AST/ASTImporter.cpp @@ -5790,8 +5790,8 @@ SourceLocation ToSwitchLoc; std::tie(ToInit, ToConditionVariable, ToCond, ToBody, ToSwitchLoc) = *Imp; - auto *ToStmt = new (Importer.getToContext()) SwitchStmt( - Importer.getToContext(), ToInit, ToConditionVariable, ToCond); + auto *ToStmt = SwitchStmt::Create(Importer.getToContext(), ToInit, + ToConditionVariable, ToCond); ToStmt->setBody(ToBody); ToStmt->setSwitchLoc(ToSwitchLoc); Index: cfe/trunk/lib/AST/Stmt.cpp =================================================================== --- cfe/trunk/lib/AST/Stmt.cpp +++ cfe/trunk/lib/AST/Stmt.cpp @@ -913,33 +913,69 @@ VarRange.getEnd()); } -SwitchStmt::SwitchStmt(const ASTContext &C, Stmt *init, VarDecl *Var, - Expr *cond) - : Stmt(SwitchStmtClass), FirstCase(nullptr, false) { - setConditionVariable(C, Var); - SubExprs[INIT] = init; - SubExprs[COND] = cond; - SubExprs[BODY] = nullptr; - SwitchStmtBits.SwitchLoc = SourceLocation{}; +SwitchStmt::SwitchStmt(const ASTContext &Ctx, Stmt *Init, VarDecl *Var, + Expr *Cond) + : Stmt(SwitchStmtClass), FirstCase(nullptr) { + bool HasInit = Init != nullptr; + bool HasVar = Var != nullptr; + SwitchStmtBits.HasInit = HasInit; + SwitchStmtBits.HasVar = HasVar; + SwitchStmtBits.AllEnumCasesCovered = false; + + setCond(Cond); + setBody(nullptr); + if (HasInit) + setInit(Init); + if (HasVar) + setConditionVariable(Ctx, Var); + + setSwitchLoc(SourceLocation{}); } -VarDecl *SwitchStmt::getConditionVariable() const { - if (!SubExprs[VAR]) - return nullptr; +SwitchStmt::SwitchStmt(EmptyShell Empty, bool HasInit, bool HasVar) + : Stmt(SwitchStmtClass, Empty) { + SwitchStmtBits.HasInit = HasInit; + SwitchStmtBits.HasVar = HasVar; + SwitchStmtBits.AllEnumCasesCovered = false; +} - auto *DS = cast(SubExprs[VAR]); +SwitchStmt *SwitchStmt::Create(const ASTContext &Ctx, Stmt *Init, VarDecl *Var, + Expr *Cond) { + bool HasInit = Init != nullptr; + bool HasVar = Var != nullptr; + void *Mem = Ctx.Allocate( + totalSizeToAlloc(NumMandatoryStmtPtr + HasInit + HasVar), + alignof(SwitchStmt)); + return new (Mem) SwitchStmt(Ctx, Init, Var, Cond); +} + +SwitchStmt *SwitchStmt::CreateEmpty(const ASTContext &Ctx, bool HasInit, + bool HasVar) { + void *Mem = Ctx.Allocate( + totalSizeToAlloc(NumMandatoryStmtPtr + HasInit + HasVar), + alignof(SwitchStmt)); + return new (Mem) SwitchStmt(EmptyShell(), HasInit, HasVar); +} + +VarDecl *SwitchStmt::getConditionVariable() { + auto *DS = getConditionVariableDeclStmt(); + if (!DS) + return nullptr; return cast(DS->getSingleDecl()); } -void SwitchStmt::setConditionVariable(const ASTContext &C, VarDecl *V) { +void SwitchStmt::setConditionVariable(const ASTContext &Ctx, VarDecl *V) { + assert(hasVarStorage() && + "This switch statement has no storage for a condition variable!"); + if (!V) { - SubExprs[VAR] = nullptr; + getTrailingObjects()[varOffset()] = nullptr; return; } SourceRange VarRange = V->getSourceRange(); - SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), - VarRange.getEnd()); + getTrailingObjects()[varOffset()] = new (Ctx) + DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); } WhileStmt::WhileStmt(const ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, Index: cfe/trunk/lib/Sema/SemaStmt.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaStmt.cpp +++ cfe/trunk/lib/Sema/SemaStmt.cpp @@ -727,8 +727,7 @@ setFunctionHasBranchIntoScope(); - SwitchStmt *SS = new (Context) - SwitchStmt(Context, InitStmt, Cond.get().first, CondExpr); + auto *SS = SwitchStmt::Create(Context, InitStmt, Cond.get().first, CondExpr); getCurFunction()->SwitchStack.push_back( FunctionScopeInfo::SwitchInfo(SS, false)); return SS; Index: cfe/trunk/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTReaderStmt.cpp +++ cfe/trunk/lib/Serialization/ASTReaderStmt.cpp @@ -240,13 +240,21 @@ void ASTStmtReader::VisitSwitchStmt(SwitchStmt *S) { VisitStmt(S); - S->setInit(Record.readSubStmt()); - S->setConditionVariable(Record.getContext(), ReadDeclAs()); + + bool HasInit = Record.readInt(); + bool HasVar = Record.readInt(); + bool AllEnumCasesCovered = Record.readInt(); + if (AllEnumCasesCovered) + S->setAllEnumCasesCovered(); + S->setCond(Record.readSubExpr()); S->setBody(Record.readSubStmt()); + if (HasInit) + S->setInit(Record.readSubStmt()); + if (HasVar) + S->setConditionVariable(Record.getContext(), ReadDeclAs()); + S->setSwitchLoc(ReadSourceLocation()); - if (Record.readInt()) - S->setAllEnumCasesCovered(); SwitchCase *PrevSC = nullptr; for (auto E = Record.size(); Record.getIdx() != E; ) { @@ -2310,7 +2318,10 @@ break; case STMT_SWITCH: - S = new (Context) SwitchStmt(Empty); + S = SwitchStmt::CreateEmpty( + Context, + /* HasInit=*/Record[ASTStmtReader::NumStmtFields + 0], + /* HasVar=*/Record[ASTStmtReader::NumStmtFields + 1]); break; case STMT_WHILE: Index: cfe/trunk/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTWriterStmt.cpp +++ cfe/trunk/lib/Serialization/ASTWriterStmt.cpp @@ -159,12 +159,22 @@ void ASTStmtWriter::VisitSwitchStmt(SwitchStmt *S) { VisitStmt(S); - Record.AddStmt(S->getInit()); - Record.AddDeclRef(S->getConditionVariable()); + + bool HasInit = S->getInit() != nullptr; + bool HasVar = S->getConditionVariableDeclStmt() != nullptr; + Record.push_back(HasInit); + Record.push_back(HasVar); + Record.push_back(S->isAllEnumCasesCovered()); + Record.AddStmt(S->getCond()); Record.AddStmt(S->getBody()); + if (HasInit) + Record.AddStmt(S->getInit()); + if (HasVar) + Record.AddDeclRef(S->getConditionVariable()); + Record.AddSourceLocation(S->getSwitchLoc()); - Record.push_back(S->isAllEnumCasesCovered()); + for (SwitchCase *SC = S->getSwitchCaseList(); SC; SC = SC->getNextSwitchCase()) Record.push_back(Writer.RecordSwitchCaseID(SC)); Index: cfe/trunk/test/Import/switch-stmt/test.cpp =================================================================== --- cfe/trunk/test/Import/switch-stmt/test.cpp +++ cfe/trunk/test/Import/switch-stmt/test.cpp @@ -1,8 +1,6 @@ // RUN: clang-import-test -dump-ast -import %S/Inputs/F.cpp -expression %s | FileCheck %s // CHECK: SwitchStmt -// CHECK-NEXT: <> -// CHECK-NEXT: <> // CHECK-NEXT: IntegerLiteral // CHECK-NEXT: CompoundStmt // CHECK-NEXT: CaseStmt @@ -22,7 +20,6 @@ // CHECK-NEXT: DeclStmt // CHECK-NEXT: VarDecl // CHECK-SAME: varname -// CHECK-NEXT: <> // CHECK-NEXT: IntegerLiteral // CHECK-NEXT: CompoundStmt // CHECK-NEXT: CaseStmt @@ -37,15 +34,11 @@ // CHECK-NEXT: BreakStmt // CHECK: SwitchStmt -// CHECK-NEXT: <> -// CHECK-NEXT: <> // CHECK-NEXT: IntegerLiteral // CHECK-NEXT: DefaultStmt // CHECK-NEXT: BreakStmt // CHECK: SwitchStmt -// CHECK-NEXT: <> -// CHECK-NEXT: <> // CHECK-NEXT: IntegerLiteral // CHECK-NEXT: NullStmt Index: cfe/trunk/test/Misc/ast-dump-color.cpp =================================================================== --- cfe/trunk/test/Misc/ast-dump-color.cpp +++ cfe/trunk/test/Misc/ast-dump-color.cpp @@ -46,7 +46,6 @@ //CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]FunctionDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:9:1[[RESET]], [[Yellow]]line:16:1[[RESET]]> [[Yellow]]line:9:6[[RESET]][[CYAN]] TestAttributedStmt[[RESET]] [[Green]]'void ()'[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| |-[[RESET]][[MAGENTA:.\[0;1;35m]]CompoundStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:27[[RESET]], [[Yellow]]line:16:1[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]SwitchStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:10:3[[RESET]], [[Yellow]]line:15:3[[RESET]]>{{$}} -//CHECK: {{^}}[[Blue]]| | |-[[RESET]][[Blue:.\[0;34m]]<<>>[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:10:11[[RESET]]> [[Green]]'int'[[RESET]][[Cyan:.\[0;36m]][[RESET]][[Cyan]][[RESET]][[CYAN]] 1[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]CompoundStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:14[[RESET]], [[Yellow]]line:15:3[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]CaseStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:3[[RESET]], [[Yellow]]line:12:27[[RESET]]>{{$}}