Skip to content

Commit

Permalink
[cxx2a] P0614R1: Support init-statements in range-based for loops.
Browse files Browse the repository at this point in the history
We don't yet support this for the case where a range-based for loop is
implicitly rewritten to an ObjC for..in statement.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@343350 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
zygoloid committed Sep 28, 2018
1 parent ef72cd7 commit 250f67c
Show file tree
Hide file tree
Showing 33 changed files with 651 additions and 141 deletions.
2 changes: 2 additions & 0 deletions include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2180,6 +2180,8 @@ DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, {})

DEF_TRAVERSE_STMT(CXXForRangeStmt, {
if (!getDerived().shouldVisitImplicitCode()) {
if (S->getInit())
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInit());
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getLoopVarStmt());
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRangeInit());
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
Expand Down
17 changes: 10 additions & 7 deletions include/clang/AST/StmtCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,15 @@ class CXXTryStmt final : public Stmt,
};

/// CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for
/// statement, represented as 'for (range-declarator : range-expression)'.
/// statement, represented as 'for (range-declarator : range-expression)'
/// or 'for (init-statement range-declarator : range-expression)'.
///
/// This is stored in a partially-desugared form to allow full semantic
/// analysis of the constituent components. The original syntactic components
/// can be extracted using getLoopVariable and getRangeInit.
class CXXForRangeStmt : public Stmt {
SourceLocation ForLoc;
enum { RANGE, BEGINSTMT, ENDSTMT, COND, INC, LOOPVAR, BODY, END };
enum { INIT, RANGE, BEGINSTMT, ENDSTMT, COND, INC, LOOPVAR, BODY, END };
// SubExprs[RANGE] is an expression or declstmt.
// SubExprs[COND] and SubExprs[INC] are expressions.
Stmt *SubExprs[END];
Expand All @@ -135,16 +136,17 @@ class CXXForRangeStmt : public Stmt {

friend class ASTStmtReader;
public:
CXXForRangeStmt(DeclStmt *Range, DeclStmt *Begin, DeclStmt *End,
Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body,
SourceLocation FL, SourceLocation CAL, SourceLocation CL,
SourceLocation RPL);
CXXForRangeStmt(Stmt *InitStmt, DeclStmt *Range, DeclStmt *Begin,
DeclStmt *End, Expr *Cond, Expr *Inc, DeclStmt *LoopVar,
Stmt *Body, SourceLocation FL, SourceLocation CAL,
SourceLocation CL, SourceLocation RPL);
CXXForRangeStmt(EmptyShell Empty) : Stmt(CXXForRangeStmtClass, Empty) { }


Stmt *getInit() { return SubExprs[INIT]; }
VarDecl *getLoopVariable();
Expr *getRangeInit();

const Stmt *getInit() const { return SubExprs[INIT]; }
const VarDecl *getLoopVariable() const;
const Expr *getRangeInit() const;

Expand Down Expand Up @@ -179,6 +181,7 @@ class CXXForRangeStmt : public Stmt {
}
const Stmt *getBody() const { return SubExprs[BODY]; }

void setInit(Stmt *S) { SubExprs[INIT] = S; }
void setRangeInit(Expr *E) { SubExprs[RANGE] = reinterpret_cast<Stmt*>(E); }
void setRangeStmt(Stmt *S) { SubExprs[RANGE] = S; }
void setBeginStmt(Stmt *S) { SubExprs[BEGINSTMT] = S; }
Expand Down
6 changes: 6 additions & 0 deletions include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,12 @@ def ext_init_statement : ExtWarn<
def warn_cxx14_compat_init_statement : Warning<
"%select{if|switch}0 initialization statements are incompatible with "
"C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>;
def ext_for_range_init_stmt : ExtWarn<
"range-based for loop initialization statements are a C++2a extension">,
InGroup<CXX2a>;
def warn_cxx17_compat_for_range_init_stmt : Warning<
"range-based for loop initialization statements are incompatible with "
"C++ standards before C++2a">, DefaultIgnore, InGroup<CXXPre2aCompat>;

// C++ derived classes
def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">;
Expand Down
3 changes: 3 additions & 0 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2299,6 +2299,9 @@ def warn_for_range_copy : Warning<
"loop variable %0 of type %1 creates a copy from type %2">,
InGroup<RangeLoopAnalysis>, DefaultIgnore;
def note_use_reference_type : Note<"use reference type %0 to prevent copying">;
def err_objc_for_range_init_stmt : Error<
"initialization statement is not supported when iterating over Objective-C "
"collection">;

// C++11 constexpr
def warn_cxx98_compat_constexpr : Warning<
Expand Down
13 changes: 10 additions & 3 deletions include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1793,10 +1793,12 @@ class Parser : public CodeCompletionHandler {
SourceLocation Start);

//===--------------------------------------------------------------------===//
// C++ if/switch/while condition expression.
// C++ if/switch/while/for condition expression.
struct ForRangeInfo;
Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
SourceLocation Loc,
Sema::ConditionKind CK);
Sema::ConditionKind CK,
ForRangeInfo *FRI = nullptr);

//===--------------------------------------------------------------------===//
// C++ Coroutines
Expand Down Expand Up @@ -2045,6 +2047,9 @@ class Parser : public CodeCompletionHandler {

bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
};
struct ForRangeInfo : ForRangeInit {
StmtResult LoopVar;
};

DeclGroupPtrTy ParseDeclaration(DeclaratorContext Context,
SourceLocation &DeclEnd,
Expand Down Expand Up @@ -2215,13 +2220,15 @@ class Parser : public CodeCompletionHandler {
Expression, ///< Disambiguated as an expression (either kind).
ConditionDecl, ///< Disambiguated as the declaration form of condition.
InitStmtDecl, ///< Disambiguated as a simple-declaration init-statement.
ForRangeDecl, ///< Disambiguated as a for-range declaration.
Error ///< Can't be any of the above!
};
/// Disambiguates between the different kinds of things that can happen
/// after 'if (' or 'switch ('. This could be one of two different kinds of
/// declaration (depending on whether there is a ';' later) or an expression.
ConditionOrInitStatement
isCXXConditionDeclarationOrInitStatement(bool CanBeInitStmt);
isCXXConditionDeclarationOrInitStatement(bool CanBeInitStmt,
bool CanBeForRangeDecl);

bool isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous);
bool isCXXTypeId(TentativeCXXTypeIdContext Context) {
Expand Down
2 changes: 2 additions & 0 deletions include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3777,12 +3777,14 @@ class Sema {

StmtResult ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
SourceLocation CoawaitLoc,
Stmt *InitStmt,
Stmt *LoopVar,
SourceLocation ColonLoc, Expr *Collection,
SourceLocation RParenLoc,
BuildForRangeKind Kind);
StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc,
SourceLocation CoawaitLoc,
Stmt *InitStmt,
SourceLocation ColonLoc,
Stmt *RangeDecl, Stmt *Begin, Stmt *End,
Expr *Cond, Expr *Inc,
Expand Down
11 changes: 6 additions & 5 deletions lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5394,6 +5394,9 @@ Stmt *ASTNodeImporter::VisitCXXTryStmt(CXXTryStmt *S) {
}

Stmt *ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
auto *ToInit = dyn_cast_or_null<Stmt>(Importer.Import(S->getInit()));
if (!ToInit && S->getInit())
return nullptr;
auto *ToRange =
dyn_cast_or_null<DeclStmt>(Importer.Import(S->getRangeStmt()));
if (!ToRange && S->getRangeStmt())
Expand Down Expand Up @@ -5423,11 +5426,9 @@ Stmt *ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
SourceLocation ToCoawaitLoc = Importer.Import(S->getCoawaitLoc());
SourceLocation ToColonLoc = Importer.Import(S->getColonLoc());
SourceLocation ToRParenLoc = Importer.Import(S->getRParenLoc());
return new (Importer.getToContext()) CXXForRangeStmt(ToRange, ToBegin, ToEnd,
ToCond, ToInc,
ToLoopVar, ToBody,
ToForLoc, ToCoawaitLoc,
ToColonLoc, ToRParenLoc);
return new (Importer.getToContext())
CXXForRangeStmt(ToInit, ToRange, ToBegin, ToEnd, ToCond, ToInc, ToLoopVar,
ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc);
}

Stmt *ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4217,6 +4217,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
const CXXForRangeStmt *FS = cast<CXXForRangeStmt>(S);
BlockScopeRAII Scope(Info);

// Evaluate the init-statement if present.
if (FS->getInit()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
if (ESR != ESR_Succeeded)
return ESR;
}

// Initialize the __range variable.
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt());
if (ESR != ESR_Succeeded)
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/StmtCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@ CXXTryStmt::CXXTryStmt(SourceLocation tryLoc, Stmt *tryBlock,
std::copy(handlers.begin(), handlers.end(), Stmts + 1);
}

CXXForRangeStmt::CXXForRangeStmt(DeclStmt *Range,
CXXForRangeStmt::CXXForRangeStmt(Stmt *Init, DeclStmt *Range,
DeclStmt *BeginStmt, DeclStmt *EndStmt,
Expr *Cond, Expr *Inc, DeclStmt *LoopVar,
Stmt *Body, SourceLocation FL,
SourceLocation CAL, SourceLocation CL,
SourceLocation RPL)
: Stmt(CXXForRangeStmtClass), ForLoc(FL), CoawaitLoc(CAL), ColonLoc(CL),
RParenLoc(RPL) {
SubExprs[INIT] = Init;
SubExprs[RANGE] = Range;
SubExprs[BEGINSTMT] = BeginStmt;
SubExprs[ENDSTMT] = EndStmt;
Expand Down
82 changes: 40 additions & 42 deletions lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,28 @@ namespace {
IndentLevel -= SubIndent;
}

void PrintInitStmt(Stmt *S, unsigned PrefixWidth) {
// FIXME: Cope better with odd prefix widths.
IndentLevel += (PrefixWidth + 1) / 2;
if (auto *DS = dyn_cast<DeclStmt>(S))
PrintRawDeclStmt(DS);
else
PrintExpr(cast<Expr>(S));
OS << "; ";
IndentLevel -= (PrefixWidth + 1) / 2;
}

void PrintControlledStmt(Stmt *S) {
if (auto *CS = dyn_cast<CompoundStmt>(S)) {
OS << " ";
PrintRawCompoundStmt(CS);
OS << NL;
} else {
OS << NL;
PrintStmt(S);
}
}

void PrintRawCompoundStmt(CompoundStmt *S);
void PrintRawDecl(Decl *D);
void PrintRawDeclStmt(const DeclStmt *S);
Expand Down Expand Up @@ -218,6 +240,8 @@ void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) {

void StmtPrinter::PrintRawIfStmt(IfStmt *If) {
OS << "if (";
if (If->getInit())
PrintInitStmt(If->getInit(), 4);
if (const DeclStmt *DS = If->getConditionVariableDeclStmt())
PrintRawDeclStmt(DS);
else
Expand Down Expand Up @@ -258,21 +282,14 @@ void StmtPrinter::VisitIfStmt(IfStmt *If) {

void StmtPrinter::VisitSwitchStmt(SwitchStmt *Node) {
Indent() << "switch (";
if (Node->getInit())
PrintInitStmt(Node->getInit(), 8);
if (const DeclStmt *DS = Node->getConditionVariableDeclStmt())
PrintRawDeclStmt(DS);
else
PrintExpr(Node->getCond());
OS << ")";

// Pretty print compoundstmt bodies (very common).
if (auto *CS = dyn_cast<CompoundStmt>(Node->getBody())) {
OS << " ";
PrintRawCompoundStmt(CS);
OS << NL;
} else {
OS << NL;
PrintStmt(Node->getBody());
}
PrintControlledStmt(Node->getBody());
}

void StmtPrinter::VisitWhileStmt(WhileStmt *Node) {
Expand Down Expand Up @@ -303,31 +320,19 @@ void StmtPrinter::VisitDoStmt(DoStmt *Node) {

void StmtPrinter::VisitForStmt(ForStmt *Node) {
Indent() << "for (";
if (Node->getInit()) {
if (auto *DS = dyn_cast<DeclStmt>(Node->getInit()))
PrintRawDeclStmt(DS);
else
PrintExpr(cast<Expr>(Node->getInit()));
}
OS << ";";
if (Node->getCond()) {
OS << " ";
if (Node->getInit())
PrintInitStmt(Node->getInit(), 5);
else
OS << (Node->getCond() ? "; " : ";");
if (Node->getCond())
PrintExpr(Node->getCond());
}
OS << ";";
if (Node->getInc()) {
OS << " ";
PrintExpr(Node->getInc());
}
OS << ") ";

if (auto *CS = dyn_cast<CompoundStmt>(Node->getBody())) {
PrintRawCompoundStmt(CS);
OS << NL;
} else {
OS << NL;
PrintStmt(Node->getBody());
}
OS << ")";
PrintControlledStmt(Node->getBody());
}

void StmtPrinter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *Node) {
Expand All @@ -338,28 +343,21 @@ void StmtPrinter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *Node) {
PrintExpr(cast<Expr>(Node->getElement()));
OS << " in ";
PrintExpr(Node->getCollection());
OS << ") ";

if (auto *CS = dyn_cast<CompoundStmt>(Node->getBody())) {
PrintRawCompoundStmt(CS);
OS << NL;
} else {
OS << NL;
PrintStmt(Node->getBody());
}
OS << ")";
PrintControlledStmt(Node->getBody());
}

void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) {
Indent() << "for (";
if (Node->getInit())
PrintInitStmt(Node->getInit(), 5);
PrintingPolicy SubPolicy(Policy);
SubPolicy.SuppressInitializers = true;
Node->getLoopVariable()->print(OS, SubPolicy, IndentLevel);
OS << " : ";
PrintExpr(Node->getRangeInit());
OS << ") {" << NL;
PrintStmt(Node->getBody());
Indent() << "}";
if (Policy.IncludeNewlines) OS << NL;
OS << ")";
PrintControlledStmt(Node->getBody());
}

void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) {
Expand Down
5 changes: 4 additions & 1 deletion lib/Analysis/CFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4255,7 +4255,10 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
Block = createBlock();
addStmt(S->getBeginStmt());
addStmt(S->getEndStmt());
return addStmt(S->getRangeStmt());
CFGBlock *Head = addStmt(S->getRangeStmt());
if (S->getInit())
Head = addStmt(S->getInit());
return Head;
}

CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
Expand Down
2 changes: 2 additions & 0 deletions lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,8 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S,
LexicalScope ForScope(*this, S.getSourceRange());

// Evaluate the first pieces before the loop.
if (S.getInit())
EmitStmt(S.getInit());
EmitStmt(S.getRangeStmt());
EmitStmt(S.getBeginStmt());
EmitStmt(S.getEndStmt());
Expand Down
2 changes: 2 additions & 0 deletions lib/CodeGen/CodeGenPGO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ struct ComputeRegionCounts : public ConstStmtVisitor<ComputeRegionCounts> {

void VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
RecordStmtCount(S);
if (S->getInit())
Visit(S->getInit());
Visit(S->getLoopVarStmt());
Visit(S->getRangeStmt());
Visit(S->getBeginStmt());
Expand Down
2 changes: 2 additions & 0 deletions lib/CodeGen/CoverageMappingGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,8 @@ struct CounterCoverageMappingBuilder

void VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
extendRegion(S);
if (S->getInit())
Visit(S->getInit());
Visit(S->getLoopVarStmt());
Visit(S->getRangeStmt());

Expand Down
Loading

0 comments on commit 250f67c

Please sign in to comment.