Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -219,6 +219,12 @@ unsigned : NumStmtBits; + /// True if the ForStmt has storage for an init statement. + unsigned HasInit : 1; + + /// True if the ForStmt has storage for a condition variable. + unsigned HasVar : 1; + /// The location of the "for". SourceLocation ForLoc; }; @@ -1796,20 +1802,125 @@ /// ForStmt - This represents a 'for (init;cond;inc)' stmt. Note that any of /// the init/cond/inc parts of the ForStmt will be null if they were not /// specified in the source. -class ForStmt : public Stmt { - enum { INIT, CONDVAR, COND, INC, BODY, END_EXPR }; - Stmt* SubExprs[END_EXPR]; // SubExprs[INIT] is an expression or declstmt. +class ForStmt final : public Stmt, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// The location of the left and right parentheses. SourceLocation LParenLoc, RParenLoc; -public: - ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, + // ForStmt 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 affect children(). Note also that even though + // it is possible to have a for statement without a condition or increment, + // the condition and increment are unconditionally stored since these kind + // of for statement are quite uncommon. + // The trailing objects are in order: + // + // * A "Stmt *" for the init statement. + // Present if and only if hasInitStorage(). This is in fact either + // an "Expr *" or a "DeclStmt *" depending on whether the init statement + // is an expression statement or a simple declaration. + // + // * 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 increment. + // Always present. This is in fact an "Expr *". + // + // * A "Stmt *" for the body. + // Always present. + enum { InitOffset = 0, IncOffsetFromCond = 1, BodyOffsetFromCond = 2 }; + enum { NumMandatoryStmtPtr = 3 }; + + 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 incOffset() const { return condOffset() + IncOffsetFromCond; } + unsigned bodyOffset() const { return condOffset() + BodyOffsetFromCond; } + + /// Build a for statement. + ForStmt(const ASTContext &Ctx, Stmt *Init, Expr *Cond, VarDecl *CondVar, Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, SourceLocation RP); /// Build an empty for statement. - explicit ForStmt(EmptyShell Empty) : Stmt(ForStmtClass, Empty) {} + explicit ForStmt(EmptyShell Empty, bool HasInit, bool HasVar); + +public: + /// Create a for statement. + static ForStmt *Create(const ASTContext &Ctx, Stmt *Init, Expr *Cond, + VarDecl *CondVar, Expr *Inc, Stmt *Body, + SourceLocation FL, SourceLocation LP, + SourceLocation RP); + + /// Create an empty for statement optionally with storage for an init + /// statement and a condition variable. + static ForStmt *CreateEmpty(const ASTContext &Ctx, bool HasInit, bool HasVar); + + /// True if this ForStmt has storage for an init statement. + bool hasInitStorage() const { return ForStmtBits.HasInit; } + + /// True if this ForStmt has storage for a condition variable. + bool hasVarStorage() const { return ForStmtBits.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); + } + + Expr *getInc() { + return reinterpret_cast(getTrailingObjects()[incOffset()]); + } + + const Expr *getInc() const { + return reinterpret_cast(getTrailingObjects()[incOffset()]); + } + + void setInc(Expr *Inc) { + getTrailingObjects()[incOffset()] = reinterpret_cast(Inc); + } + + Stmt *getBody() { return getTrailingObjects()[bodyOffset()]; } + const Stmt *getBody() const { + return getTrailingObjects()[bodyOffset()]; + } + + void setBody(Stmt *Body) { + getTrailingObjects()[bodyOffset()] = Body; + } - Stmt *getInit() { return SubExprs[INIT]; } + Stmt *getInit() { + return hasInitStorage() ? getTrailingObjects()[initOffset()] + : nullptr; + } + + const Stmt *getInit() const { + return hasInitStorage() ? getTrailingObjects()[initOffset()] + : nullptr; + } + + void setInit(Stmt *Init) { + assert(hasInitStorage() && + "This for statement has no storage for an init statement!"); + getTrailingObjects()[initOffset()] = Init; + } /// Retrieve the variable declared in this "for" statement, if any. /// @@ -1819,28 +1930,28 @@ /// // ... /// } /// \endcode - VarDecl *getConditionVariable() const; + VarDecl *getConditionVariable(); + const VarDecl *getConditionVariable() const { + return const_cast(this)->getConditionVariable(); + } + + /// Set the condition variable declared in this for statement. + /// The for statement must have storage for this variable. void setConditionVariable(const ASTContext &C, VarDecl *V); /// If this ForStmt has a condition variable, return the faux DeclStmt /// associated with the creation of that condition variable. - const DeclStmt *getConditionVariableDeclStmt() const { - return reinterpret_cast(SubExprs[CONDVAR]); + DeclStmt *getConditionVariableDeclStmt() { + return hasVarStorage() ? static_cast( + getTrailingObjects()[varOffset()]) + : nullptr; } - Expr *getCond() { return reinterpret_cast(SubExprs[COND]); } - Expr *getInc() { return reinterpret_cast(SubExprs[INC]); } - Stmt *getBody() { return SubExprs[BODY]; } - - const Stmt *getInit() const { return SubExprs[INIT]; } - const Expr *getCond() const { return reinterpret_cast(SubExprs[COND]);} - const Expr *getInc() const { return reinterpret_cast(SubExprs[INC]); } - const Stmt *getBody() const { return SubExprs[BODY]; } - - void setInit(Stmt *S) { SubExprs[INIT] = S; } - void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast(E); } - void setInc(Expr *E) { SubExprs[INC] = reinterpret_cast(E); } - void setBody(Stmt *S) { SubExprs[BODY] = S; } + const DeclStmt *getConditionVariableDeclStmt() const { + return hasVarStorage() ? static_cast( + getTrailingObjects()[varOffset()]) + : nullptr; + } SourceLocation getForLoc() const { return ForStmtBits.ForLoc; } void setForLoc(SourceLocation L) { ForStmtBits.ForLoc = L; } @@ -1850,7 +1961,9 @@ void setRParenLoc(SourceLocation L) { RParenLoc = L; } SourceLocation getBeginLoc() const { return getForLoc(); } - SourceLocation getEndLoc() const { return getBody()->getEndLoc(); } + SourceLocation getEndLoc() const LLVM_READONLY { + return getBody()->getEndLoc(); + } static bool classof(const Stmt *T) { return T->getStmtClass() == ForStmtClass; @@ -1858,7 +1971,9 @@ // Iterators child_range children() { - return child_range(&SubExprs[0], &SubExprs[0]+END_EXPR); + return child_range(getTrailingObjects(), + getTrailingObjects() + + numTrailingObjects(OverloadToken())); } }; Index: lib/AST/ASTDumper.cpp =================================================================== --- lib/AST/ASTDumper.cpp +++ lib/AST/ASTDumper.cpp @@ -514,6 +514,7 @@ void VisitIfStmt(const IfStmt *Node); void VisitSwitchStmt(const SwitchStmt *Node); void VisitWhileStmt(const WhileStmt *Node); + void VisitForStmt(const ForStmt *Node); void VisitLabelStmt(const LabelStmt *Node); void VisitGotoStmt(const GotoStmt *Node); void VisitCXXCatchStmt(const CXXCatchStmt *Node); @@ -2048,6 +2049,14 @@ OS << " has_var"; } +void ASTDumper::VisitForStmt(const ForStmt *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: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -5860,10 +5860,9 @@ ToInit, ToCond, ToConditionVariable, ToInc, ToBody, ToForLoc, ToLParenLoc, ToRParenLoc) = *Imp; - return new (Importer.getToContext()) ForStmt( - Importer.getToContext(), - ToInit, ToCond, ToConditionVariable, ToInc, ToBody, ToForLoc, ToLParenLoc, - ToRParenLoc); + return ForStmt::Create(Importer.getToContext(), ToInit, ToCond, + ToConditionVariable, ToInc, ToBody, ToForLoc, + ToLParenLoc, ToRParenLoc); } ExpectedStmt ASTNodeImporter::VisitGotoStmt(GotoStmt *S) { Index: lib/AST/Stmt.cpp =================================================================== --- lib/AST/Stmt.cpp +++ lib/AST/Stmt.cpp @@ -881,36 +881,71 @@ return isa(getCond()); } -ForStmt::ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, - Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, - SourceLocation RP) - : Stmt(ForStmtClass), LParenLoc(LP), RParenLoc(RP) -{ - SubExprs[INIT] = Init; - setConditionVariable(C, condVar); - SubExprs[COND] = Cond; - SubExprs[INC] = Inc; - SubExprs[BODY] = Body; - ForStmtBits.ForLoc = FL; -} - -VarDecl *ForStmt::getConditionVariable() const { - if (!SubExprs[CONDVAR]) - return nullptr; +ForStmt::ForStmt(const ASTContext &Ctx, Stmt *Init, Expr *Cond, + VarDecl *CondVar, Expr *Inc, Stmt *Body, SourceLocation FL, + SourceLocation LP, SourceLocation RP) + : Stmt(ForStmtClass), LParenLoc(LP), RParenLoc(RP) { + bool HasInit = Init != nullptr; + bool HasVar = CondVar != nullptr; + ForStmtBits.HasInit = HasInit; + ForStmtBits.HasVar = HasVar; + + setCond(Cond); + setBody(Body); + setInc(Inc); + if (HasInit) + setInit(Init); + if (HasVar) + setConditionVariable(Ctx, CondVar); - auto *DS = cast(SubExprs[CONDVAR]); + setForLoc(FL); +} + +ForStmt::ForStmt(EmptyShell Empty, bool HasInit, bool HasVar) + : Stmt(ForStmtClass, Empty) { + ForStmtBits.HasInit = HasInit; + ForStmtBits.HasVar = HasVar; +} + +ForStmt *ForStmt::Create(const ASTContext &Ctx, Stmt *Init, Expr *Cond, + VarDecl *CondVar, Expr *Inc, Stmt *Body, + SourceLocation FL, SourceLocation LP, + SourceLocation RP) { + bool HasInit = Init != nullptr; + bool HasVar = CondVar != nullptr; + void *Mem = Ctx.Allocate( + totalSizeToAlloc(NumMandatoryStmtPtr + HasInit + HasVar), + alignof(ForStmt)); + return new (Mem) ForStmt(Ctx, Init, Cond, CondVar, Inc, Body, FL, LP, RP); +} + +ForStmt *ForStmt::CreateEmpty(const ASTContext &Ctx, bool HasInit, + bool HasVar) { + void *Mem = Ctx.Allocate( + totalSizeToAlloc(NumMandatoryStmtPtr + HasInit + HasVar), + alignof(ForStmt)); + return new (Mem) ForStmt(EmptyShell(), HasInit, HasVar); +} + +VarDecl *ForStmt::getConditionVariable() { + auto *DS = getConditionVariableDeclStmt(); + if (!DS) + return nullptr; return cast(DS->getSingleDecl()); } -void ForStmt::setConditionVariable(const ASTContext &C, VarDecl *V) { +void ForStmt::setConditionVariable(const ASTContext &Ctx, VarDecl *V) { + assert(hasVarStorage() && + "no storage for the condition variable in this ForStmt!"); + if (!V) { - SubExprs[CONDVAR] = nullptr; + getTrailingObjects()[varOffset()] = nullptr; return; } SourceRange VarRange = V->getSourceRange(); - SubExprs[CONDVAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), - VarRange.getEnd()); + getTrailingObjects()[varOffset()] = new (Ctx) + DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); } SwitchStmt::SwitchStmt(const ASTContext &Ctx, Stmt *Init, VarDecl *Var, Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -1780,9 +1780,9 @@ if (isa(Body)) getCurCompoundScope().setHasEmptyLoopBodies(); - return new (Context) - ForStmt(Context, First, Second.get().second, Second.get().first, Third, - Body, ForLoc, LParenLoc, RParenLoc); + return ForStmt::Create(Context, First, Second.get().second, + Second.get().first, Third, Body, ForLoc, LParenLoc, + RParenLoc); } /// In an Objective C collection iteration statement: Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -292,11 +292,18 @@ void ASTStmtReader::VisitForStmt(ForStmt *S) { VisitStmt(S); - S->setInit(Record.readSubStmt()); + + bool HasInit = Record.readInt(); + bool HasVar = Record.readInt(); + S->setCond(Record.readSubExpr()); - S->setConditionVariable(Record.getContext(), ReadDeclAs()); S->setInc(Record.readSubExpr()); S->setBody(Record.readSubStmt()); + if (HasInit) + S->setInit(Record.readSubStmt()); + if (HasVar) + S->setConditionVariable(Record.getContext(), ReadDeclAs()); + S->setForLoc(ReadSourceLocation()); S->setLParenLoc(ReadSourceLocation()); S->setRParenLoc(ReadSourceLocation()); @@ -2342,7 +2349,10 @@ break; case STMT_FOR: - S = new (Context) ForStmt(Empty); + S = ForStmt::CreateEmpty( + Context, + /* HasInit=*/Record[ASTStmtReader::NumStmtFields + 0], + /* HasVar=*/Record[ASTStmtReader::NumStmtFields + 1]); break; case STMT_GOTO: Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -208,11 +208,20 @@ void ASTStmtWriter::VisitForStmt(ForStmt *S) { VisitStmt(S); - Record.AddStmt(S->getInit()); + + bool HasInit = S->getInit() != nullptr; + bool HasVar = S->getConditionVariableDeclStmt() != nullptr; + Record.push_back(HasInit); + Record.push_back(HasVar); + Record.AddStmt(S->getCond()); - Record.AddDeclRef(S->getConditionVariable()); Record.AddStmt(S->getInc()); Record.AddStmt(S->getBody()); + if (HasInit) + Record.AddStmt(S->getInit()); + if (HasVar) + Record.AddDeclRef(S->getConditionVariable()); + Record.AddSourceLocation(S->getForLoc()); Record.AddSourceLocation(S->getLParenLoc()); Record.AddSourceLocation(S->getRParenLoc()); Index: test/Import/for-stmt/test.cpp =================================================================== --- test/Import/for-stmt/test.cpp +++ test/Import/for-stmt/test.cpp @@ -3,8 +3,6 @@ // CHECK: ForStmt // CHECK-NEXT: <> // CHECK-NEXT: <> -// CHECK-NEXT: <> -// CHECK-NEXT: <> // CHECK-NEXT: NullStmt // CHECK: ForStmt @@ -13,11 +11,9 @@ // CHECK-NEXT: IntegerLiteral // CHECK-NEXT: <> // CHECK-NEXT: <> -// CHECK-NEXT: <> // CHECK-NEXT: ContinueStmt // CHECK: ForStmt -// CHECK-NEXT: <> // CHECK-NEXT: DeclStmt // CHECK-NEXT: VarDecl // CHECK-NEXT: CXXBoolLiteralExpr @@ -32,7 +28,6 @@ // CHECK-NEXT: DeclStmt // CHECK-NEXT: VarDecl // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: BinaryOperator // CHECK-NEXT: ImplicitCastExpr