Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -245,6 +245,20 @@ SourceLocation RetLoc; }; + class SwitchCaseBitfields { + friend class SwitchCase; + friend class CaseStmt; + + unsigned : NumStmtBits; + + /// Used by CaseStmt to store whether it is a case statement + /// of the form case LHS ... RHS (a GNU extension). + unsigned CaseStmtIsGNURange : 1; + + /// The location of the "case" or "default" keyword. + SourceLocation KeywordLoc; + }; + //===--- Expression bitfields classes ---===// class ExprBitfields { @@ -461,6 +475,7 @@ ContinueStmtBitfields ContinueStmtBits; BreakStmtBitfields BreakStmtBits; ReturnStmtBitfields ReturnStmtBits; + SwitchCaseBitfields SwitchCaseBits; // Expressions ExprBitfields ExprBits; @@ -877,36 +892,40 @@ // SwitchCase is the base class for CaseStmt and DefaultStmt, class SwitchCase : public Stmt { protected: - // A pointer to the following CaseStmt or DefaultStmt class, - // used by SwitchStmt. - SwitchCase *NextSwitchCase = nullptr; - SourceLocation KeywordLoc; + /// The location of the ":". SourceLocation ColonLoc; + // The location of the "case" or "default" keyword. Stored in SwitchCaseBits. + // SourceLocation KeywordLoc; + + /// A pointer to the following CaseStmt or DefaultStmt class, + /// used by SwitchStmt. + SwitchCase *NextSwitchCase = nullptr; + SwitchCase(StmtClass SC, SourceLocation KWLoc, SourceLocation ColonLoc) - : Stmt(SC), KeywordLoc(KWLoc), ColonLoc(ColonLoc) {} + : Stmt(SC), ColonLoc(ColonLoc) { + setKeywordLoc(KWLoc); + } SwitchCase(StmtClass SC, EmptyShell) : Stmt(SC) {} public: const SwitchCase *getNextSwitchCase() const { return NextSwitchCase; } - SwitchCase *getNextSwitchCase() { return NextSwitchCase; } - void setNextSwitchCase(SwitchCase *SC) { NextSwitchCase = SC; } - SourceLocation getKeywordLoc() const { return KeywordLoc; } - void setKeywordLoc(SourceLocation L) { KeywordLoc = L; } + SourceLocation getKeywordLoc() const { return SwitchCaseBits.KeywordLoc; } + void setKeywordLoc(SourceLocation L) { SwitchCaseBits.KeywordLoc = L; } SourceLocation getColonLoc() const { return ColonLoc; } void setColonLoc(SourceLocation L) { ColonLoc = L; } - Stmt *getSubStmt(); + inline Stmt *getSubStmt(); const Stmt *getSubStmt() const { - return const_cast(this)->getSubStmt(); + return const_cast(this)->getSubStmt(); } - SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; } - SourceLocation getEndLoc() const LLVM_READONLY; + SourceLocation getBeginLoc() const { return getKeywordLoc(); } + inline SourceLocation getEndLoc() const LLVM_READONLY; static bool classof(const Stmt *T) { return T->getStmtClass() == CaseStmtClass || @@ -914,52 +933,137 @@ } }; -class CaseStmt : public SwitchCase { - SourceLocation EllipsisLoc; - enum { LHS, RHS, SUBSTMT, END_EXPR }; - Stmt* SubExprs[END_EXPR]; // The expression for the RHS is Non-null for - // GNU "case 1 ... 4" extension +/// CaseStmt - Represent a case statement. It can optionally be a GNU case +/// statement of the form LHS ... RHS representing a range of cases. +class CaseStmt final + : public SwitchCase, + private llvm::TrailingObjects { + friend TrailingObjects; + + // CaseStmt 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 impact children(). + // The trailing objects are in order: + // + // * A "Stmt *" for the LHS of the case statement. Always present. + // + // * A "Stmt *" for the RHS of the case statement. This is a GNU extension + // which allow ranges in cases statement of the form LHS ... RHS. + // Present if and only if caseStmtIsGNURange() is true. + // + // * A "Stmt *" for the substatement of the case statement. Always present. + // + // * A SourceLocation for the location of the ... if this is a case statement + // with a range. Present if and only if caseStmtIsGNURange() is true. + enum { LhsOffset = 0, SubStmtOffsetFromRhs = 1 }; + enum { NumMandatoryStmtPtr = 2 }; -public: + unsigned numTrailingObjects(OverloadToken) const { + return NumMandatoryStmtPtr + caseStmtIsGNURange(); + } + + unsigned numTrailingObjects(OverloadToken) const { + return caseStmtIsGNURange(); + } + + unsigned lhsOffset() const { return LhsOffset; } + unsigned rhsOffset() const { return LhsOffset + caseStmtIsGNURange(); } + unsigned subStmtOffset() const { return rhsOffset() + SubStmtOffsetFromRhs; } + + /// Build a case statement assuming that the storage for the + /// trailing objects has been properly allocated. CaseStmt(Expr *lhs, Expr *rhs, SourceLocation caseLoc, SourceLocation ellipsisLoc, SourceLocation colonLoc) - : SwitchCase(CaseStmtClass, caseLoc, colonLoc) { - SubExprs[SUBSTMT] = nullptr; - SubExprs[LHS] = reinterpret_cast(lhs); - SubExprs[RHS] = reinterpret_cast(rhs); - EllipsisLoc = ellipsisLoc; + : SwitchCase(CaseStmtClass, caseLoc, colonLoc) { + setLHS(lhs); + setSubStmt(nullptr); + // Handle GNU case statements of the form LHS ... RHS. + bool IsGNURange = rhs != nullptr; + SwitchCaseBits.CaseStmtIsGNURange = IsGNURange; + if (IsGNURange) { + setRHS(rhs); + setEllipsisLoc(ellipsisLoc); + } } /// Build an empty switch case statement. - explicit CaseStmt(EmptyShell Empty) : SwitchCase(CaseStmtClass, Empty) {} + explicit CaseStmt(EmptyShell Empty, bool CaseStmtIsGNURange) + : SwitchCase(CaseStmtClass, Empty) { + SwitchCaseBits.CaseStmtIsGNURange = CaseStmtIsGNURange; + } - SourceLocation getCaseLoc() const { return KeywordLoc; } - void setCaseLoc(SourceLocation L) { KeywordLoc = L; } - SourceLocation getEllipsisLoc() const { return EllipsisLoc; } - void setEllipsisLoc(SourceLocation L) { EllipsisLoc = L; } - SourceLocation getColonLoc() const { return ColonLoc; } - void setColonLoc(SourceLocation L) { ColonLoc = L; } +public: + /// Build a case statement. + static CaseStmt *Create(const ASTContext &Ctx, Expr *lhs, Expr *rhs, + SourceLocation caseLoc, SourceLocation ellipsisLoc, + SourceLocation colonLoc); + + /// Build an empty case statement. + static CaseStmt *CreateEmpty(const ASTContext &Ctx, bool CaseStmtIsGNURange); - Expr *getLHS() { return reinterpret_cast(SubExprs[LHS]); } - Expr *getRHS() { return reinterpret_cast(SubExprs[RHS]); } - Stmt *getSubStmt() { return SubExprs[SUBSTMT]; } + /// True if this case statement is of the form case LHS ... RHS, which + /// is a GNU extension. In this case the RHS can be obtained with getRHS() + /// and the location of the ellipsis can be obtained with getEllipsisLoc(). + bool caseStmtIsGNURange() const { return SwitchCaseBits.CaseStmtIsGNURange; } + + SourceLocation getCaseLoc() const { return getKeywordLoc(); } + void setCaseLoc(SourceLocation L) { setKeywordLoc(L); } + + /// Get the location of the ... in a case statement of the form LHS ... RHS. + SourceLocation getEllipsisLoc() const { + return caseStmtIsGNURange() ? *getTrailingObjects() + : SourceLocation(); + } + + /// Set the location of the ... in a case statement of the form LHS ... RHS. + /// Assert that this case statement is of this form. + void setEllipsisLoc(SourceLocation L) { + assert( + caseStmtIsGNURange() && + "setEllipsisLoc but this is not a case stmt of the form LHS ... RHS!"); + *getTrailingObjects() = L; + } + + Expr *getLHS() { + return reinterpret_cast(getTrailingObjects()[lhsOffset()]); + } const Expr *getLHS() const { - return reinterpret_cast(SubExprs[LHS]); + return reinterpret_cast(getTrailingObjects()[lhsOffset()]); + } + + void setLHS(Expr *Val) { + getTrailingObjects()[lhsOffset()] = reinterpret_cast(Val); + } + + Expr *getRHS() { + return caseStmtIsGNURange() ? reinterpret_cast( + getTrailingObjects()[rhsOffset()]) + : nullptr; } const Expr *getRHS() const { - return reinterpret_cast(SubExprs[RHS]); + return caseStmtIsGNURange() ? reinterpret_cast( + getTrailingObjects()[rhsOffset()]) + : nullptr; } - const Stmt *getSubStmt() const { return SubExprs[SUBSTMT]; } + void setRHS(Expr *Val) { + assert(caseStmtIsGNURange() && + "setRHS but this is not a case stmt of the form LHS ... RHS!"); + getTrailingObjects()[rhsOffset()] = reinterpret_cast(Val); + } - void setSubStmt(Stmt *S) { SubExprs[SUBSTMT] = S; } - void setLHS(Expr *Val) { SubExprs[LHS] = reinterpret_cast(Val); } - void setRHS(Expr *Val) { SubExprs[RHS] = reinterpret_cast(Val); } + Stmt *getSubStmt() { return getTrailingObjects()[subStmtOffset()]; } + const Stmt *getSubStmt() const { + return getTrailingObjects()[subStmtOffset()]; + } - SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; } + void setSubStmt(Stmt *S) { + getTrailingObjects()[subStmtOffset()] = S; + } + SourceLocation getBeginLoc() const { return getKeywordLoc(); } SourceLocation getEndLoc() const LLVM_READONLY { // Handle deeply nested case statements with iteration instead of recursion. const CaseStmt *CS = this; @@ -975,16 +1079,18 @@ // Iterators child_range children() { - return child_range(&SubExprs[0], &SubExprs[END_EXPR]); + return child_range(getTrailingObjects(), + getTrailingObjects() + + numTrailingObjects(OverloadToken())); } }; class DefaultStmt : public SwitchCase { - Stmt* SubStmt; + Stmt *SubStmt; public: - DefaultStmt(SourceLocation DL, SourceLocation CL, Stmt *substmt) : - SwitchCase(DefaultStmtClass, DL, CL), SubStmt(substmt) {} + DefaultStmt(SourceLocation DL, SourceLocation CL, Stmt *substmt) + : SwitchCase(DefaultStmtClass, DL, CL), SubStmt(substmt) {} /// Build an empty default statement. explicit DefaultStmt(EmptyShell Empty) @@ -994,12 +1100,10 @@ const Stmt *getSubStmt() const { return SubStmt; } void setSubStmt(Stmt *S) { SubStmt = S; } - SourceLocation getDefaultLoc() const { return KeywordLoc; } - void setDefaultLoc(SourceLocation L) { KeywordLoc = L; } - SourceLocation getColonLoc() const { return ColonLoc; } - void setColonLoc(SourceLocation L) { ColonLoc = L; } + SourceLocation getDefaultLoc() const { return getKeywordLoc(); } + void setDefaultLoc(SourceLocation L) { setKeywordLoc(L); } - SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; } + SourceLocation getBeginLoc() const { return getKeywordLoc(); } SourceLocation getEndLoc() const LLVM_READONLY { return SubStmt->getEndLoc(); } @@ -1009,13 +1113,23 @@ } // Iterators - child_range children() { return child_range(&SubStmt, &SubStmt+1); } + child_range children() { return child_range(&SubStmt, &SubStmt + 1); } }; -inline SourceLocation SwitchCase::getEndLoc() const { +SourceLocation SwitchCase::getEndLoc() const { if (const auto *CS = dyn_cast(this)) return CS->getEndLoc(); - return cast(this)->getEndLoc(); + else if (const auto *DS = dyn_cast(this)) + return DS->getEndLoc(); + llvm_unreachable("SwitchCase is neither a CaseStmt nor a DefaultStmt!"); +} + +Stmt *SwitchCase::getSubStmt() { + if (auto *CS = dyn_cast(this)) + return CS->getSubStmt(); + else if (auto *DS = dyn_cast(this)) + return DS->getSubStmt(); + llvm_unreachable("SwitchCase is neither a CaseStmt nor a DefaultStmt!"); } /// LabelStmt - Represents a label, which has a substatement. For example: Index: lib/AST/ASTDumper.cpp =================================================================== --- lib/AST/ASTDumper.cpp +++ lib/AST/ASTDumper.cpp @@ -515,6 +515,7 @@ void VisitLabelStmt(const LabelStmt *Node); void VisitGotoStmt(const GotoStmt *Node); void VisitCXXCatchStmt(const CXXCatchStmt *Node); + void VisitCaseStmt(const CaseStmt *Node); void VisitCapturedStmt(const CapturedStmt *Node); // OpenMP @@ -2047,6 +2048,12 @@ dumpDecl(Node->getExceptionDecl()); } +void ASTDumper::VisitCaseStmt(const CaseStmt *Node) { + VisitStmt(Node); + if (Node->caseStmtIsGNURange()) + OS << " gnu_range"; +} + void ASTDumper::VisitCapturedStmt(const CapturedStmt *Node) { VisitStmt(Node); dumpDecl(Node->getCapturedDecl()); Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -5707,8 +5707,8 @@ std::tie(ToLHS, ToRHS, ToSubStmt, ToCaseLoc, ToEllipsisLoc, ToColonLoc) = *Imp; - auto *ToStmt = new (Importer.getToContext()) CaseStmt( - ToLHS, ToRHS, ToCaseLoc, ToEllipsisLoc, ToColonLoc); + auto *ToStmt = CaseStmt::Create(Importer.getToContext(), ToLHS, ToRHS, + ToCaseLoc, ToEllipsisLoc, ToColonLoc); ToStmt->setSubStmt(ToSubStmt); return ToStmt; Index: lib/AST/Stmt.cpp =================================================================== --- lib/AST/Stmt.cpp +++ lib/AST/Stmt.cpp @@ -942,12 +942,6 @@ VarRange.getEnd()); } -Stmt *SwitchCase::getSubStmt() { - if (isa(this)) - return cast(this)->getSubStmt(); - return cast(this)->getSubStmt(); -} - WhileStmt::WhileStmt(const ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, SourceLocation WL) : Stmt(WhileStmtClass) { @@ -991,6 +985,27 @@ return cast_or_null(RetExpr); } +// CaseStmt +CaseStmt *CaseStmt::Create(const ASTContext &Ctx, Expr *lhs, Expr *rhs, + SourceLocation caseLoc, SourceLocation ellipsisLoc, + SourceLocation colonLoc) { + bool CaseStmtIsGNURange = rhs != nullptr; + void *Mem = Ctx.Allocate( + totalSizeToAlloc( + NumMandatoryStmtPtr + CaseStmtIsGNURange, CaseStmtIsGNURange), + alignof(CaseStmt)); + return new (Mem) CaseStmt(lhs, rhs, caseLoc, ellipsisLoc, colonLoc); +} + +CaseStmt *CaseStmt::CreateEmpty(const ASTContext &Ctx, + bool CaseStmtIsGNURange) { + void *Mem = Ctx.Allocate( + totalSizeToAlloc( + NumMandatoryStmtPtr + CaseStmtIsGNURange, CaseStmtIsGNURange), + alignof(CaseStmt)); + return new (Mem) CaseStmt(EmptyShell(), CaseStmtIsGNURange); +} + SEHTryStmt::SEHTryStmt(bool IsCXXTry, SourceLocation TryLoc, Stmt *TryBlock, Stmt *Handler) : Stmt(SEHTryStmtClass), IsCXXTry(IsCXXTry), TryLoc(TryLoc) { Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -462,8 +462,8 @@ return StmtError(); } - CaseStmt *CS = new (Context) - CaseStmt(LHSVal.get(), RHSVal.get(), CaseLoc, DotDotDotLoc, ColonLoc); + auto *CS = CaseStmt::Create(Context, LHSVal.get(), RHSVal.get(), + CaseLoc, DotDotDotLoc, ColonLoc); getCurFunction()->SwitchStack.back().getPointer()->addSwitchCase(CS); return CS; } @@ -472,7 +472,7 @@ void Sema::ActOnCaseStmtBody(Stmt *caseStmt, Stmt *SubStmt) { DiagnoseUnusedExprResult(SubStmt); - CaseStmt *CS = static_cast(caseStmt); + auto *CS = static_cast(caseStmt); CS->setSubStmt(SubStmt); } Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -177,10 +177,13 @@ void ASTStmtReader::VisitCaseStmt(CaseStmt *S) { VisitSwitchCase(S); + bool CaseStmtIsGNURange = Record.readInt(); S->setLHS(Record.readSubExpr()); - S->setRHS(Record.readSubExpr()); S->setSubStmt(Record.readSubStmt()); - S->setEllipsisLoc(ReadSourceLocation()); + if (CaseStmtIsGNURange) { + S->setRHS(Record.readSubExpr()); + S->setEllipsisLoc(ReadSourceLocation()); + } } void ASTStmtReader::VisitDefaultStmt(DefaultStmt *S) { @@ -2277,7 +2280,9 @@ break; case STMT_CASE: - S = new (Context) CaseStmt(Empty); + S = CaseStmt::CreateEmpty( + Context, + /*CaseStmtIsGNURange*/ Record[ASTStmtReader::NumStmtFields + 3]); break; case STMT_DEFAULT: Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -96,10 +96,13 @@ void ASTStmtWriter::VisitCaseStmt(CaseStmt *S) { VisitSwitchCase(S); + Record.push_back(S->caseStmtIsGNURange()); Record.AddStmt(S->getLHS()); - Record.AddStmt(S->getRHS()); Record.AddStmt(S->getSubStmt()); - Record.AddSourceLocation(S->getEllipsisLoc()); + if (S->caseStmtIsGNURange()) { + Record.AddStmt(S->getRHS()); + Record.AddSourceLocation(S->getEllipsisLoc()); + } Code = serialization::STMT_CASE; } Index: test/Import/switch-stmt/test.cpp =================================================================== --- test/Import/switch-stmt/test.cpp +++ test/Import/switch-stmt/test.cpp @@ -7,10 +7,8 @@ // CHECK-NEXT: CompoundStmt // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: BreakStmt // CHECK: SwitchStmt @@ -22,11 +20,9 @@ // CHECK-NEXT: CompoundStmt // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: BreakStmt // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: BreakStmt // CHECK: SwitchStmt Index: test/Misc/ast-dump-color.cpp =================================================================== --- test/Misc/ast-dump-color.cpp +++ test/Misc/ast-dump-color.cpp @@ -51,13 +51,11 @@ //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]]>{{$}} //CHECK: {{^}}[[Blue]]| | | |-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 1[[RESET]]{{$}} -//CHECK: {{^}}[[Blue]]| | | |-[[RESET]][[Blue]]<<>>[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | | `-[[RESET]][[MAGENTA]]AttributedStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:12:5[[RESET]], [[Yellow]]col:27[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | | |-[[RESET]][[BLUE]]FallThroughAttr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:7[[RESET]], [[Yellow]]col:14[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | | `-[[RESET]][[MAGENTA]]NullStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:27[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]CaseStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:13:3[[RESET]], [[Yellow]]line:14:5[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:13:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 2[[RESET]]{{$}} -//CHECK: {{^}}[[Blue]]| | |-[[RESET]][[Blue]]<<>>[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]NullStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:14:5[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| `-[[RESET]][[Blue]]FullComment[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:8:4[[RESET]], [[Yellow]]col:11[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| `-[[RESET]][[Blue]]ParagraphComment[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:4[[RESET]], [[Yellow]]col:11[[RESET]]>{{$}}