Index: cfe/trunk/include/clang/AST/Stmt.h =================================================================== --- cfe/trunk/include/clang/AST/Stmt.h +++ cfe/trunk/include/clang/AST/Stmt.h @@ -193,10 +193,14 @@ }; class WhileStmtBitfields { + friend class ASTStmtReader; friend class WhileStmt; unsigned : NumStmtBits; + /// True if the WhileStmt has storage for a condition variable. + unsigned HasVar : 1; + /// The location of the "while". SourceLocation WhileLoc; }; @@ -1615,16 +1619,75 @@ }; /// WhileStmt - This represents a 'while' stmt. -class WhileStmt : public Stmt { - enum { VAR, COND, BODY, END_EXPR }; - Stmt* SubExprs[END_EXPR]; +class WhileStmt final : public Stmt, + private llvm::TrailingObjects { + friend TrailingObjects; -public: - WhileStmt(const ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, + // WhileStmt is followed by several trailing objects, + // some of which optional. Note that it would be more + // convenient to put the optional trailing object at the end + // but this would affect children(). + // The trailing objects are in order: + // + // * 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 { VarOffset = 0, BodyOffsetFromCond = 1 }; + enum { NumMandatoryStmtPtr = 2 }; + + unsigned varOffset() const { return VarOffset; } + unsigned condOffset() const { return VarOffset + hasVarStorage(); } + unsigned bodyOffset() const { return condOffset() + BodyOffsetFromCond; } + + unsigned numTrailingObjects(OverloadToken) const { + return NumMandatoryStmtPtr + hasVarStorage(); + } + + /// Build a while statement. + WhileStmt(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, Stmt *Body, SourceLocation WL); /// Build an empty while statement. - explicit WhileStmt(EmptyShell Empty) : Stmt(WhileStmtClass, Empty) {} + explicit WhileStmt(EmptyShell Empty, bool HasVar); + +public: + /// Create a while statement. + static WhileStmt *Create(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, + Stmt *Body, SourceLocation WL); + + /// Create an empty while statement optionally with storage for + /// a condition variable. + static WhileStmt *CreateEmpty(const ASTContext &Ctx, bool HasVar); + + /// True if this WhileStmt has storage for a condition variable. + bool hasVarStorage() const { return WhileStmtBits.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; + } /// Retrieve the variable declared in this "while" statement, if any. /// @@ -1634,28 +1697,36 @@ /// // ... /// } /// \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 of this while statement. + /// The while statement must have storage for it. + void setConditionVariable(const ASTContext &Ctx, VarDecl *V); /// If this WhileStmt 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; } - Expr *getCond() { return reinterpret_cast(SubExprs[COND]); } - const Expr *getCond() const { return reinterpret_cast(SubExprs[COND]);} - void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast(E); } - Stmt *getBody() { return SubExprs[BODY]; } - const Stmt *getBody() const { return SubExprs[BODY]; } - void setBody(Stmt *S) { SubExprs[BODY] = S; } + const DeclStmt *getConditionVariableDeclStmt() const { + return hasVarStorage() ? static_cast( + getTrailingObjects()[varOffset()]) + : nullptr; + } SourceLocation getWhileLoc() const { return WhileStmtBits.WhileLoc; } void setWhileLoc(SourceLocation L) { WhileStmtBits.WhileLoc = L; } SourceLocation getBeginLoc() const { return getWhileLoc(); } - - SourceLocation getEndLoc() const { return getBody()->getEndLoc(); } + SourceLocation getEndLoc() const LLVM_READONLY { + return getBody()->getEndLoc(); + } static bool classof(const Stmt *T) { return T->getStmtClass() == WhileStmtClass; @@ -1663,7 +1734,9 @@ // Iterators child_range children() { - return child_range(&SubExprs[0], &SubExprs[0]+END_EXPR); + return child_range(getTrailingObjects(), + getTrailingObjects() + + numTrailingObjects(OverloadToken())); } }; Index: cfe/trunk/lib/AST/ASTDumper.cpp =================================================================== --- cfe/trunk/lib/AST/ASTDumper.cpp +++ cfe/trunk/lib/AST/ASTDumper.cpp @@ -513,6 +513,7 @@ void VisitAttributedStmt(const AttributedStmt *Node); void VisitIfStmt(const IfStmt *Node); void VisitSwitchStmt(const SwitchStmt *Node); + void VisitWhileStmt(const WhileStmt *Node); void VisitLabelStmt(const LabelStmt *Node); void VisitGotoStmt(const GotoStmt *Node); void VisitCXXCatchStmt(const CXXCatchStmt *Node); @@ -2041,6 +2042,12 @@ OS << " has_var"; } +void ASTDumper::VisitWhileStmt(const WhileStmt *Node) { + VisitStmt(Node); + 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 @@ -5864,9 +5864,8 @@ SourceLocation ToWhileLoc; std::tie(ToConditionVariable, ToCond, ToBody, ToWhileLoc) = *Imp; - return new (Importer.getToContext()) WhileStmt( - Importer.getToContext(), - ToConditionVariable, ToCond, ToBody, ToWhileLoc); + return WhileStmt::Create(Importer.getToContext(), ToConditionVariable, ToCond, + ToBody, ToWhileLoc); } ExpectedStmt ASTNodeImporter::VisitDoStmt(DoStmt *S) { Index: cfe/trunk/lib/AST/Stmt.cpp =================================================================== --- cfe/trunk/lib/AST/Stmt.cpp +++ cfe/trunk/lib/AST/Stmt.cpp @@ -978,32 +978,60 @@ DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); } -WhileStmt::WhileStmt(const ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, - SourceLocation WL) - : Stmt(WhileStmtClass) { - setConditionVariable(C, Var); - SubExprs[COND] = cond; - SubExprs[BODY] = body; - WhileStmtBits.WhileLoc = WL; +WhileStmt::WhileStmt(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, + Stmt *Body, SourceLocation WL) + : Stmt(WhileStmtClass) { + bool HasVar = Var != nullptr; + WhileStmtBits.HasVar = HasVar; + + setCond(Cond); + setBody(Body); + if (HasVar) + setConditionVariable(Ctx, Var); + + setWhileLoc(WL); } -VarDecl *WhileStmt::getConditionVariable() const { - if (!SubExprs[VAR]) - return nullptr; +WhileStmt::WhileStmt(EmptyShell Empty, bool HasVar) + : Stmt(WhileStmtClass, Empty) { + WhileStmtBits.HasVar = HasVar; +} + +WhileStmt *WhileStmt::Create(const ASTContext &Ctx, VarDecl *Var, Expr *Cond, + Stmt *Body, SourceLocation WL) { + bool HasVar = Var != nullptr; + void *Mem = + Ctx.Allocate(totalSizeToAlloc(NumMandatoryStmtPtr + HasVar), + alignof(WhileStmt)); + return new (Mem) WhileStmt(Ctx, Var, Cond, Body, WL); +} - auto *DS = cast(SubExprs[VAR]); +WhileStmt *WhileStmt::CreateEmpty(const ASTContext &Ctx, bool HasVar) { + void *Mem = + Ctx.Allocate(totalSizeToAlloc(NumMandatoryStmtPtr + HasVar), + alignof(WhileStmt)); + return new (Mem) WhileStmt(EmptyShell(), HasVar); +} + +VarDecl *WhileStmt::getConditionVariable() { + auto *DS = getConditionVariableDeclStmt(); + if (!DS) + return nullptr; return cast(DS->getSingleDecl()); } -void WhileStmt::setConditionVariable(const ASTContext &C, VarDecl *V) { +void WhileStmt::setConditionVariable(const ASTContext &Ctx, VarDecl *V) { + assert(hasVarStorage() && + "This while 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()); } // IndirectGotoStmt Index: cfe/trunk/lib/Sema/SemaStmt.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaStmt.cpp +++ cfe/trunk/lib/Sema/SemaStmt.cpp @@ -1306,8 +1306,8 @@ if (isa(Body)) getCurCompoundScope().setHasEmptyLoopBodies(); - return new (Context) - WhileStmt(Context, CondVal.first, CondVal.second, Body, WhileLoc); + return WhileStmt::Create(Context, CondVal.first, CondVal.second, Body, + WhileLoc); } StmtResult Index: cfe/trunk/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTReaderStmt.cpp +++ cfe/trunk/lib/Serialization/ASTReaderStmt.cpp @@ -270,10 +270,14 @@ void ASTStmtReader::VisitWhileStmt(WhileStmt *S) { VisitStmt(S); - S->setConditionVariable(Record.getContext(), ReadDeclAs()); + + bool HasVar = Record.readInt(); S->setCond(Record.readSubExpr()); S->setBody(Record.readSubStmt()); + if (HasVar) + S->setConditionVariable(Record.getContext(), ReadDeclAs()); + S->setWhileLoc(ReadSourceLocation()); } @@ -2325,7 +2329,9 @@ break; case STMT_WHILE: - S = new (Context) WhileStmt(Empty); + S = WhileStmt::CreateEmpty( + Context, + /* HasVar=*/Record[ASTStmtReader::NumStmtFields + 0]); break; case STMT_DO: Index: cfe/trunk/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTWriterStmt.cpp +++ cfe/trunk/lib/Serialization/ASTWriterStmt.cpp @@ -183,9 +183,15 @@ void ASTStmtWriter::VisitWhileStmt(WhileStmt *S) { VisitStmt(S); - Record.AddDeclRef(S->getConditionVariable()); + + bool HasVar = S->getConditionVariableDeclStmt() != nullptr; + Record.push_back(HasVar); + Record.AddStmt(S->getCond()); Record.AddStmt(S->getBody()); + if (HasVar) + Record.AddDeclRef(S->getConditionVariable()); + Record.AddSourceLocation(S->getWhileLoc()); Code = serialization::STMT_WHILE; } Index: cfe/trunk/test/Import/while-stmt/test.cpp =================================================================== --- cfe/trunk/test/Import/while-stmt/test.cpp +++ cfe/trunk/test/Import/while-stmt/test.cpp @@ -1,12 +1,10 @@ // RUN: clang-import-test -dump-ast -import %S/Inputs/F.cpp -expression %s | FileCheck %s // CHECK: WhileStmt -// CHECK-NEXT: <> // CHECK-NEXT: CXXBoolLiteralExpr // CHECK-NEXT: NullStmt // CHECK: WhileStmt -// CHECK-NEXT: <> // CHECK-NEXT: CXXBoolLiteralExpr // CHECK-NEXT: CompoundStmt