mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-26 11:25:27 +00:00
[cxx2a] P0614R1: Support init-statements in range-based for loops.
We don't yet support this for the case where a range-based for loop is implicitly rewritten to an ObjC for..in statement. llvm-svn: 343350
This commit is contained in:
parent
8e90bad63d
commit
8baa50013c
@ -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());
|
||||
|
@ -118,14 +118,15 @@ public:
|
||||
};
|
||||
|
||||
/// 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];
|
||||
@ -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;
|
||||
|
||||
@ -179,6 +181,7 @@ public:
|
||||
}
|
||||
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; }
|
||||
|
@ -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">;
|
||||
|
@ -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<
|
||||
|
@ -1793,10 +1793,12 @@ private:
|
||||
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
|
||||
@ -2045,6 +2047,9 @@ private:
|
||||
|
||||
bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
|
||||
};
|
||||
struct ForRangeInfo : ForRangeInit {
|
||||
StmtResult LoopVar;
|
||||
};
|
||||
|
||||
DeclGroupPtrTy ParseDeclaration(DeclaratorContext Context,
|
||||
SourceLocation &DeclEnd,
|
||||
@ -2215,13 +2220,15 @@ private:
|
||||
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) {
|
||||
|
@ -3777,12 +3777,14 @@ public:
|
||||
|
||||
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,
|
||||
|
@ -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())
|
||||
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -45,7 +45,7 @@ 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,
|
||||
@ -53,6 +53,7 @@ CXXForRangeStmt::CXXForRangeStmt(DeclStmt *Range,
|
||||
SourceLocation RPL)
|
||||
: Stmt(CXXForRangeStmtClass), ForLoc(FL), CoawaitLoc(CAL), ColonLoc(CL),
|
||||
RParenLoc(RPL) {
|
||||
SubExprs[INIT] = Init;
|
||||
SubExprs[RANGE] = Range;
|
||||
SubExprs[BEGINSTMT] = BeginStmt;
|
||||
SubExprs[ENDSTMT] = EndStmt;
|
||||
|
@ -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);
|
||||
@ -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
|
||||
@ -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) {
|
||||
@ -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) {
|
||||
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
||||
|
@ -1738,10 +1738,14 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
|
||||
/// \param Loc The location of the start of the statement that requires this
|
||||
/// condition, e.g., the "for" in a for loop.
|
||||
///
|
||||
/// \param FRI If non-null, a for range declaration is permitted, and if
|
||||
/// present will be parsed and stored here, and a null result will be returned.
|
||||
///
|
||||
/// \returns The parsed condition.
|
||||
Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
|
||||
SourceLocation Loc,
|
||||
Sema::ConditionKind CK) {
|
||||
Sema::ConditionKind CK,
|
||||
ForRangeInfo *FRI) {
|
||||
ParenBraceBracketBalancer BalancerRAIIObj(*this);
|
||||
|
||||
if (Tok.is(tok::code_completion)) {
|
||||
@ -1761,7 +1765,7 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
|
||||
};
|
||||
|
||||
// Determine what kind of thing we have.
|
||||
switch (isCXXConditionDeclarationOrInitStatement(InitStmt)) {
|
||||
switch (isCXXConditionDeclarationOrInitStatement(InitStmt, FRI)) {
|
||||
case ConditionOrInitStatement::Expression: {
|
||||
ProhibitAttributes(attrs);
|
||||
|
||||
@ -1799,6 +1803,15 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
|
||||
return ParseCXXCondition(nullptr, Loc, CK);
|
||||
}
|
||||
|
||||
case ConditionOrInitStatement::ForRangeDecl: {
|
||||
assert(FRI && "should not parse a for range declaration here");
|
||||
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
|
||||
DeclGroupPtrTy DG = ParseSimpleDeclaration(
|
||||
DeclaratorContext::ForContext, DeclEnd, attrs, false, FRI);
|
||||
FRI->LoopVar = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation());
|
||||
return Sema::ConditionResult();
|
||||
}
|
||||
|
||||
case ConditionOrInitStatement::ConditionDecl:
|
||||
case ConditionOrInitStatement::Error:
|
||||
break;
|
||||
|
@ -1570,11 +1570,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
|
||||
ExprResult Value;
|
||||
|
||||
bool ForEach = false, ForRange = false;
|
||||
bool ForEach = false;
|
||||
StmtResult FirstPart;
|
||||
Sema::ConditionResult SecondPart;
|
||||
ExprResult Collection;
|
||||
ForRangeInit ForRangeInit;
|
||||
ForRangeInfo ForRangeInfo;
|
||||
FullExprArg ThirdPart(Actions);
|
||||
|
||||
if (Tok.is(tok::code_completion)) {
|
||||
@ -1600,20 +1600,19 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
SourceLocation Loc = ConsumeToken();
|
||||
MaybeParseCXX11Attributes(attrs);
|
||||
|
||||
ForRangeInit.ColonLoc = ConsumeToken();
|
||||
ForRangeInfo.ColonLoc = ConsumeToken();
|
||||
if (Tok.is(tok::l_brace))
|
||||
ForRangeInit.RangeExpr = ParseBraceInitializer();
|
||||
ForRangeInfo.RangeExpr = ParseBraceInitializer();
|
||||
else
|
||||
ForRangeInit.RangeExpr = ParseExpression();
|
||||
ForRangeInfo.RangeExpr = ParseExpression();
|
||||
|
||||
Diag(Loc, diag::err_for_range_identifier)
|
||||
<< ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
|
||||
? FixItHint::CreateInsertion(Loc, "auto &&")
|
||||
: FixItHint());
|
||||
|
||||
FirstPart = Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name,
|
||||
attrs, attrs.Range.getEnd());
|
||||
ForRange = true;
|
||||
ForRangeInfo.LoopVar = Actions.ActOnCXXForRangeIdentifier(
|
||||
getCurScope(), Loc, Name, attrs, attrs.Range.getEnd());
|
||||
} else if (isForInitDeclaration()) { // for (int X = 4;
|
||||
ParenBraceBracketBalancer BalancerRAIIObj(*this);
|
||||
|
||||
@ -1630,13 +1629,13 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
|
||||
DeclGroupPtrTy DG = ParseSimpleDeclaration(
|
||||
DeclaratorContext::ForContext, DeclEnd, attrs, false,
|
||||
MightBeForRangeStmt ? &ForRangeInit : nullptr);
|
||||
MightBeForRangeStmt ? &ForRangeInfo : nullptr);
|
||||
FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation());
|
||||
if (ForRangeInit.ParsedForRangeDecl()) {
|
||||
Diag(ForRangeInit.ColonLoc, getLangOpts().CPlusPlus11 ?
|
||||
if (ForRangeInfo.ParsedForRangeDecl()) {
|
||||
Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11 ?
|
||||
diag::warn_cxx98_compat_for_range : diag::ext_for_range);
|
||||
|
||||
ForRange = true;
|
||||
ForRangeInfo.LoopVar = FirstPart;
|
||||
FirstPart = StmtResult();
|
||||
} else if (Tok.is(tok::semi)) { // for (int x = 4;
|
||||
ConsumeToken();
|
||||
} else if ((ForEach = isTokIdentifier_in())) {
|
||||
@ -1699,17 +1698,33 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
|
||||
// Parse the second part of the for specifier.
|
||||
getCurScope()->AddFlags(Scope::BreakScope | Scope::ContinueScope);
|
||||
if (!ForEach && !ForRange && !SecondPart.isInvalid()) {
|
||||
if (!ForEach && !ForRangeInfo.ParsedForRangeDecl() &&
|
||||
!SecondPart.isInvalid()) {
|
||||
// Parse the second part of the for specifier.
|
||||
if (Tok.is(tok::semi)) { // for (...;;
|
||||
// no second part.
|
||||
} else if (Tok.is(tok::r_paren)) {
|
||||
// missing both semicolons.
|
||||
} else {
|
||||
if (getLangOpts().CPlusPlus)
|
||||
if (getLangOpts().CPlusPlus) {
|
||||
// C++2a: We've parsed an init-statement; we might have a
|
||||
// for-range-declaration next.
|
||||
bool MightBeForRangeStmt = !ForRangeInfo.ParsedForRangeDecl();
|
||||
ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt);
|
||||
SecondPart =
|
||||
ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean);
|
||||
else {
|
||||
ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean,
|
||||
MightBeForRangeStmt ? &ForRangeInfo : nullptr);
|
||||
|
||||
if (ForRangeInfo.ParsedForRangeDecl()) {
|
||||
Diag(FirstPart.get() ? FirstPart.get()->getBeginLoc()
|
||||
: ForRangeInfo.ColonLoc,
|
||||
getLangOpts().CPlusPlus2a
|
||||
? diag::warn_cxx17_compat_for_range_init_stmt
|
||||
: diag::ext_for_range_init_stmt)
|
||||
<< (FirstPart.get() ? FirstPart.get()->getSourceRange()
|
||||
: SourceRange());
|
||||
}
|
||||
} else {
|
||||
ExprResult SecondExpr = ParseExpression();
|
||||
if (SecondExpr.isInvalid())
|
||||
SecondPart = Sema::ConditionError();
|
||||
@ -1719,7 +1734,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
Sema::ConditionKind::Boolean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the third part of the for statement.
|
||||
if (!ForEach && !ForRangeInfo.ParsedForRangeDecl()) {
|
||||
if (Tok.isNot(tok::semi)) {
|
||||
if (!SecondPart.isInvalid())
|
||||
Diag(Tok, diag::err_expected_semi_for);
|
||||
@ -1732,7 +1750,6 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
ConsumeToken();
|
||||
}
|
||||
|
||||
// Parse the third part of the for specifier.
|
||||
if (Tok.isNot(tok::r_paren)) { // for (...;...;)
|
||||
ExprResult Third = ParseExpression();
|
||||
// FIXME: The C++11 standard doesn't actually say that this is a
|
||||
@ -1745,7 +1762,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
|
||||
// C++ Coroutines [stmt.iter]:
|
||||
// 'co_await' can only be used for a range-based for statement.
|
||||
if (CoawaitLoc.isValid() && !ForRange) {
|
||||
if (CoawaitLoc.isValid() && !ForRangeInfo.ParsedForRangeDecl()) {
|
||||
Diag(CoawaitLoc, diag::err_for_co_await_not_range_for);
|
||||
CoawaitLoc = SourceLocation();
|
||||
}
|
||||
@ -1756,12 +1773,12 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
StmtResult ForRangeStmt;
|
||||
StmtResult ForEachStmt;
|
||||
|
||||
if (ForRange) {
|
||||
if (ForRangeInfo.ParsedForRangeDecl()) {
|
||||
ExprResult CorrectedRange =
|
||||
Actions.CorrectDelayedTyposInExpr(ForRangeInit.RangeExpr.get());
|
||||
Actions.CorrectDelayedTyposInExpr(ForRangeInfo.RangeExpr.get());
|
||||
ForRangeStmt = Actions.ActOnCXXForRangeStmt(
|
||||
getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(),
|
||||
ForRangeInit.ColonLoc, CorrectedRange.get(),
|
||||
ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc, CorrectedRange.get(),
|
||||
T.getCloseLocation(), Sema::BFRK_Build);
|
||||
|
||||
// Similarly, we need to do the semantic analysis for a for-range
|
||||
@ -1816,7 +1833,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
|
||||
return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(),
|
||||
Body.get());
|
||||
|
||||
if (ForRange)
|
||||
if (ForRangeInfo.ParsedForRangeDecl())
|
||||
return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get());
|
||||
|
||||
return Actions.ActOnForStmt(ForLoc, T.getOpenLocation(), FirstPart.get(),
|
||||
|
@ -345,22 +345,55 @@ struct Parser::ConditionDeclarationOrInitStatementState {
|
||||
bool CanBeExpression = true;
|
||||
bool CanBeCondition = true;
|
||||
bool CanBeInitStatement;
|
||||
bool CanBeForRangeDecl;
|
||||
|
||||
ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement)
|
||||
: P(P), CanBeInitStatement(CanBeInitStatement) {}
|
||||
ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement,
|
||||
bool CanBeForRangeDecl)
|
||||
: P(P), CanBeInitStatement(CanBeInitStatement),
|
||||
CanBeForRangeDecl(CanBeForRangeDecl) {}
|
||||
|
||||
bool resolved() {
|
||||
return CanBeExpression + CanBeCondition + CanBeInitStatement +
|
||||
CanBeForRangeDecl < 2;
|
||||
}
|
||||
|
||||
void markNotExpression() {
|
||||
CanBeExpression = false;
|
||||
|
||||
if (CanBeCondition && CanBeInitStatement) {
|
||||
if (!resolved()) {
|
||||
// FIXME: Unify the parsing codepaths for condition variables and
|
||||
// simple-declarations so that we don't need to eagerly figure out which
|
||||
// kind we have here. (Just parse init-declarators until we reach a
|
||||
// semicolon or right paren.)
|
||||
RevertingTentativeParsingAction PA(P);
|
||||
P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch);
|
||||
if (CanBeForRangeDecl) {
|
||||
// Skip until we hit a ')', ';', or a ':' with no matching '?'.
|
||||
// The final case is a for range declaration, the rest are not.
|
||||
while (true) {
|
||||
unsigned QuestionColonDepth = 0;
|
||||
P.SkipUntil({tok::r_paren, tok::semi, tok::question, tok::colon},
|
||||
StopBeforeMatch);
|
||||
if (P.Tok.is(tok::question))
|
||||
++QuestionColonDepth;
|
||||
else if (P.Tok.is(tok::colon)) {
|
||||
if (QuestionColonDepth)
|
||||
--QuestionColonDepth;
|
||||
else {
|
||||
CanBeCondition = CanBeInitStatement = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
CanBeForRangeDecl = false;
|
||||
break;
|
||||
}
|
||||
P.ConsumeToken();
|
||||
}
|
||||
} else {
|
||||
// Just skip until we hit a ')' or ';'.
|
||||
P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch);
|
||||
}
|
||||
if (P.Tok.isNot(tok::r_paren))
|
||||
CanBeCondition = false;
|
||||
CanBeCondition = CanBeForRangeDecl = false;
|
||||
if (P.Tok.isNot(tok::semi))
|
||||
CanBeInitStatement = false;
|
||||
}
|
||||
@ -368,28 +401,36 @@ struct Parser::ConditionDeclarationOrInitStatementState {
|
||||
|
||||
bool markNotCondition() {
|
||||
CanBeCondition = false;
|
||||
return !CanBeInitStatement || !CanBeExpression;
|
||||
return resolved();
|
||||
}
|
||||
|
||||
bool markNotForRangeDecl() {
|
||||
CanBeForRangeDecl = false;
|
||||
return resolved();
|
||||
}
|
||||
|
||||
bool update(TPResult IsDecl) {
|
||||
switch (IsDecl) {
|
||||
case TPResult::True:
|
||||
markNotExpression();
|
||||
return true;
|
||||
assert(resolved() && "can't continue after tentative parsing bails out");
|
||||
break;
|
||||
case TPResult::False:
|
||||
CanBeCondition = CanBeInitStatement = false;
|
||||
return true;
|
||||
CanBeCondition = CanBeInitStatement = CanBeForRangeDecl = false;
|
||||
break;
|
||||
case TPResult::Ambiguous:
|
||||
return false;
|
||||
break;
|
||||
case TPResult::Error:
|
||||
CanBeExpression = CanBeCondition = CanBeInitStatement = false;
|
||||
return true;
|
||||
CanBeExpression = CanBeCondition = CanBeInitStatement =
|
||||
CanBeForRangeDecl = false;
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("unknown tentative parse result");
|
||||
return resolved();
|
||||
}
|
||||
|
||||
ConditionOrInitStatement result() const {
|
||||
assert(CanBeExpression + CanBeCondition + CanBeInitStatement < 2 &&
|
||||
assert(CanBeExpression + CanBeCondition + CanBeInitStatement +
|
||||
CanBeForRangeDecl < 2 &&
|
||||
"result called but not yet resolved");
|
||||
if (CanBeExpression)
|
||||
return ConditionOrInitStatement::Expression;
|
||||
@ -397,6 +438,8 @@ struct Parser::ConditionDeclarationOrInitStatementState {
|
||||
return ConditionOrInitStatement::ConditionDecl;
|
||||
if (CanBeInitStatement)
|
||||
return ConditionOrInitStatement::InitStmtDecl;
|
||||
if (CanBeForRangeDecl)
|
||||
return ConditionOrInitStatement::ForRangeDecl;
|
||||
return ConditionOrInitStatement::Error;
|
||||
}
|
||||
};
|
||||
@ -419,8 +462,10 @@ struct Parser::ConditionDeclarationOrInitStatementState {
|
||||
/// to the ';' to disambiguate cases like 'int(x))' (an expression) from
|
||||
/// 'int(x);' (a simple-declaration in an init-statement).
|
||||
Parser::ConditionOrInitStatement
|
||||
Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement) {
|
||||
ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement);
|
||||
Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
|
||||
bool CanBeForRangeDecl) {
|
||||
ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement,
|
||||
CanBeForRangeDecl);
|
||||
|
||||
if (State.update(isCXXDeclarationSpecifier()))
|
||||
return State.result();
|
||||
@ -447,11 +492,19 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement) {
|
||||
return State.result();
|
||||
}
|
||||
|
||||
// A colon here identifies a for-range declaration.
|
||||
if (State.CanBeForRangeDecl && Tok.is(tok::colon))
|
||||
return ConditionOrInitStatement::ForRangeDecl;
|
||||
|
||||
// At this point, it can't be a condition any more, because a condition
|
||||
// must have a brace-or-equal-initializer.
|
||||
if (State.markNotCondition())
|
||||
return State.result();
|
||||
|
||||
// Likewise, it can't be a for-range declaration any more.
|
||||
if (State.markNotForRangeDecl())
|
||||
return State.result();
|
||||
|
||||
// A parenthesized initializer could be part of an expression or a
|
||||
// simple-declaration.
|
||||
if (Tok.is(tok::l_paren)) {
|
||||
|
@ -1647,6 +1647,8 @@ namespace {
|
||||
void VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
|
||||
// Only visit the initialization of a for loop; the body
|
||||
// has a different break/continue scope.
|
||||
if (const Stmt *Init = S->getInit())
|
||||
Visit(Init);
|
||||
if (const Stmt *Range = S->getRangeStmt())
|
||||
Visit(Range);
|
||||
if (const Stmt *Begin = S->getBeginStmt())
|
||||
@ -2065,15 +2067,20 @@ static bool ObjCEnumerationCollection(Expr *Collection) {
|
||||
/// The body of the loop is not available yet, since it cannot be analysed until
|
||||
/// we have determined the type of the for-range-declaration.
|
||||
StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
|
||||
SourceLocation CoawaitLoc, Stmt *First,
|
||||
SourceLocation ColonLoc, Expr *Range,
|
||||
SourceLocation RParenLoc,
|
||||
SourceLocation CoawaitLoc, Stmt *InitStmt,
|
||||
Stmt *First, SourceLocation ColonLoc,
|
||||
Expr *Range, SourceLocation RParenLoc,
|
||||
BuildForRangeKind Kind) {
|
||||
if (!First)
|
||||
return StmtError();
|
||||
|
||||
if (Range && ObjCEnumerationCollection(Range))
|
||||
if (Range && ObjCEnumerationCollection(Range)) {
|
||||
// FIXME: Support init-statements in Objective-C++20 ranged for statement.
|
||||
if (InitStmt)
|
||||
return Diag(InitStmt->getBeginLoc(), diag::err_objc_for_range_init_stmt)
|
||||
<< InitStmt->getSourceRange();
|
||||
return ActOnObjCForCollectionStmt(ForLoc, First, Range, RParenLoc);
|
||||
}
|
||||
|
||||
DeclStmt *DS = dyn_cast<DeclStmt>(First);
|
||||
assert(DS && "first part of for range not a decl stmt");
|
||||
@ -2119,10 +2126,10 @@ StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
|
||||
return StmtError();
|
||||
}
|
||||
|
||||
return BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc, RangeDecl.get(),
|
||||
/*BeginStmt=*/nullptr, /*EndStmt=*/nullptr,
|
||||
/*Cond=*/nullptr, /*Inc=*/nullptr,
|
||||
DS, RParenLoc, Kind);
|
||||
return BuildCXXForRangeStmt(
|
||||
ForLoc, CoawaitLoc, InitStmt, ColonLoc, RangeDecl.get(),
|
||||
/*BeginStmt=*/nullptr, /*EndStmt=*/nullptr,
|
||||
/*Cond=*/nullptr, /*Inc=*/nullptr, DS, RParenLoc, Kind);
|
||||
}
|
||||
|
||||
/// Create the initialization, compare, and increment steps for
|
||||
@ -2270,6 +2277,7 @@ BuildNonArrayForRange(Sema &SemaRef, Expr *BeginRange, Expr *EndRange,
|
||||
static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
|
||||
SourceLocation ForLoc,
|
||||
SourceLocation CoawaitLoc,
|
||||
Stmt *InitStmt,
|
||||
Stmt *LoopVarDecl,
|
||||
SourceLocation ColonLoc,
|
||||
Expr *Range,
|
||||
@ -2286,8 +2294,8 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
|
||||
return StmtResult();
|
||||
|
||||
StmtResult SR = SemaRef.ActOnCXXForRangeStmt(
|
||||
S, ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, AdjustedRange.get(),
|
||||
RParenLoc, Sema::BFRK_Check);
|
||||
S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc,
|
||||
AdjustedRange.get(), RParenLoc, Sema::BFRK_Check);
|
||||
if (SR.isInvalid())
|
||||
return StmtResult();
|
||||
}
|
||||
@ -2297,9 +2305,9 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
|
||||
// case there are any other (non-fatal) problems with it.
|
||||
SemaRef.Diag(RangeLoc, diag::err_for_range_dereference)
|
||||
<< Range->getType() << FixItHint::CreateInsertion(RangeLoc, "*");
|
||||
return SemaRef.ActOnCXXForRangeStmt(S, ForLoc, CoawaitLoc, LoopVarDecl,
|
||||
ColonLoc, AdjustedRange.get(), RParenLoc,
|
||||
Sema::BFRK_Rebuild);
|
||||
return SemaRef.ActOnCXXForRangeStmt(
|
||||
S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc,
|
||||
AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -2319,12 +2327,13 @@ struct InvalidateOnErrorScope {
|
||||
}
|
||||
|
||||
/// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement.
|
||||
StmtResult
|
||||
Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
|
||||
SourceLocation ColonLoc, Stmt *RangeDecl,
|
||||
Stmt *Begin, Stmt *End, Expr *Cond,
|
||||
Expr *Inc, Stmt *LoopVarDecl,
|
||||
SourceLocation RParenLoc, BuildForRangeKind Kind) {
|
||||
StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
|
||||
SourceLocation CoawaitLoc, Stmt *InitStmt,
|
||||
SourceLocation ColonLoc, Stmt *RangeDecl,
|
||||
Stmt *Begin, Stmt *End, Expr *Cond,
|
||||
Expr *Inc, Stmt *LoopVarDecl,
|
||||
SourceLocation RParenLoc,
|
||||
BuildForRangeKind Kind) {
|
||||
// FIXME: This should not be used during template instantiation. We should
|
||||
// pick up the set of unqualified lookup results for the != and + operators
|
||||
// in the initial parse.
|
||||
@ -2519,7 +2528,7 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
|
||||
// If building the range failed, try dereferencing the range expression
|
||||
// unless a diagnostic was issued or the end function is problematic.
|
||||
StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc,
|
||||
CoawaitLoc,
|
||||
CoawaitLoc, InitStmt,
|
||||
LoopVarDecl, ColonLoc,
|
||||
Range, RangeLoc,
|
||||
RParenLoc);
|
||||
@ -2636,7 +2645,7 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
|
||||
return StmtResult();
|
||||
|
||||
return new (Context) CXXForRangeStmt(
|
||||
RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()),
|
||||
InitStmt, RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()),
|
||||
cast_or_null<DeclStmt>(EndDeclStmt.get()), NotEqExpr.get(),
|
||||
IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, CoawaitLoc,
|
||||
ColonLoc, RParenLoc);
|
||||
|
@ -2021,11 +2021,10 @@ public:
|
||||
/// By default, performs semantic analysis to build the new statement.
|
||||
/// Subclasses may override this routine to provide different behavior.
|
||||
StmtResult RebuildCXXForRangeStmt(SourceLocation ForLoc,
|
||||
SourceLocation CoawaitLoc,
|
||||
SourceLocation ColonLoc,
|
||||
Stmt *Range, Stmt *Begin, Stmt *End,
|
||||
Expr *Cond, Expr *Inc,
|
||||
Stmt *LoopVar,
|
||||
SourceLocation CoawaitLoc, Stmt *Init,
|
||||
SourceLocation ColonLoc, Stmt *Range,
|
||||
Stmt *Begin, Stmt *End, Expr *Cond,
|
||||
Expr *Inc, Stmt *LoopVar,
|
||||
SourceLocation RParenLoc) {
|
||||
// If we've just learned that the range is actually an Objective-C
|
||||
// collection, treat this as an Objective-C fast enumeration loop.
|
||||
@ -2037,17 +2036,24 @@ public:
|
||||
|
||||
Expr *RangeExpr = RangeVar->getInit();
|
||||
if (!RangeExpr->isTypeDependent() &&
|
||||
RangeExpr->getType()->isObjCObjectPointerType())
|
||||
return getSema().ActOnObjCForCollectionStmt(ForLoc, LoopVar, RangeExpr,
|
||||
RParenLoc);
|
||||
RangeExpr->getType()->isObjCObjectPointerType()) {
|
||||
// FIXME: Support init-statements in Objective-C++20 ranged for
|
||||
// statement.
|
||||
if (Init) {
|
||||
return SemaRef.Diag(Init->getBeginLoc(),
|
||||
diag::err_objc_for_range_init_stmt)
|
||||
<< Init->getSourceRange();
|
||||
}
|
||||
return getSema().ActOnObjCForCollectionStmt(ForLoc, LoopVar,
|
||||
RangeExpr, RParenLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc,
|
||||
Range, Begin, End,
|
||||
Cond, Inc, LoopVar, RParenLoc,
|
||||
Sema::BFRK_Rebuild);
|
||||
return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, Init, ColonLoc,
|
||||
Range, Begin, End, Cond, Inc, LoopVar,
|
||||
RParenLoc, Sema::BFRK_Rebuild);
|
||||
}
|
||||
|
||||
/// Build a new C++0x range-based for statement.
|
||||
@ -7412,6 +7418,11 @@ StmtResult TreeTransform<Derived>::TransformCXXTryStmt(CXXTryStmt *S) {
|
||||
template<typename Derived>
|
||||
StmtResult
|
||||
TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
|
||||
StmtResult Init =
|
||||
S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
|
||||
if (Init.isInvalid())
|
||||
return StmtError();
|
||||
|
||||
StmtResult Range = getDerived().TransformStmt(S->getRangeStmt());
|
||||
if (Range.isInvalid())
|
||||
return StmtError();
|
||||
@ -7445,6 +7456,7 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
|
||||
|
||||
StmtResult NewStmt = S;
|
||||
if (getDerived().AlwaysRebuild() ||
|
||||
Init.get() != S->getInit() ||
|
||||
Range.get() != S->getRangeStmt() ||
|
||||
Begin.get() != S->getBeginStmt() ||
|
||||
End.get() != S->getEndStmt() ||
|
||||
@ -7452,7 +7464,7 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
|
||||
Inc.get() != S->getInc() ||
|
||||
LoopVar.get() != S->getLoopVarStmt()) {
|
||||
NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
|
||||
S->getCoawaitLoc(),
|
||||
S->getCoawaitLoc(), Init.get(),
|
||||
S->getColonLoc(), Range.get(),
|
||||
Begin.get(), End.get(),
|
||||
Cond.get(),
|
||||
@ -7470,7 +7482,7 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
|
||||
// it now so we have a new statement to attach the body to.
|
||||
if (Body.get() != S->getBody() && NewStmt.get() == S) {
|
||||
NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
|
||||
S->getCoawaitLoc(),
|
||||
S->getCoawaitLoc(), Init.get(),
|
||||
S->getColonLoc(), Range.get(),
|
||||
Begin.get(), End.get(),
|
||||
Cond.get(),
|
||||
|
@ -1270,6 +1270,7 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
|
||||
S->CoawaitLoc = ReadSourceLocation();
|
||||
S->ColonLoc = ReadSourceLocation();
|
||||
S->RParenLoc = ReadSourceLocation();
|
||||
S->setInit(Record.readSubStmt());
|
||||
S->setRangeStmt(Record.readSubStmt());
|
||||
S->setBeginStmt(Record.readSubStmt());
|
||||
S->setEndStmt(Record.readSubStmt());
|
||||
|
@ -1222,6 +1222,7 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
|
||||
Record.AddSourceLocation(S->getCoawaitLoc());
|
||||
Record.AddSourceLocation(S->getColonLoc());
|
||||
Record.AddSourceLocation(S->getRParenLoc());
|
||||
Record.AddStmt(S->getInit());
|
||||
Record.AddStmt(S->getRangeStmt());
|
||||
Record.AddStmt(S->getBeginStmt());
|
||||
Record.AddStmt(S->getEndStmt());
|
||||
|
@ -1670,6 +1670,287 @@
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>path</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>kind</key><string>control</string>
|
||||
<key>edges</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>start</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>3</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>5</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>end</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>10</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>event</string>
|
||||
<key>location</key>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<key>ranges</key>
|
||||
<array>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>15</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</array>
|
||||
<key>depth</key><integer>0</integer>
|
||||
<key>extended_message</key>
|
||||
<string>Initializing to a null pointer value</string>
|
||||
<key>message</key>
|
||||
<string>Initializing to a null pointer value</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>control</string>
|
||||
<key>edges</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>start</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>10</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>end</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>58</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>58</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>event</string>
|
||||
<key>location</key>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>58</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<key>ranges</key>
|
||||
<array>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>58</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>58</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</array>
|
||||
<key>depth</key><integer>0</integer>
|
||||
<key>extended_message</key>
|
||||
<string>Entering loop body</string>
|
||||
<key>message</key>
|
||||
<string>Entering loop body</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>control</string>
|
||||
<key>edges</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>start</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>58</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>58</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>end</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>51</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>53</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>event</string>
|
||||
<key>location</key>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>51</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<key>ranges</key>
|
||||
<array>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>51</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>56</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</array>
|
||||
<key>depth</key><integer>0</integer>
|
||||
<key>extended_message</key>
|
||||
<string>'p' initialized to a null pointer value</string>
|
||||
<key>message</key>
|
||||
<string>'p' initialized to a null pointer value</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>control</string>
|
||||
<key>edges</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>start</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>51</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>102</integer>
|
||||
<key>col</key><integer>53</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>end</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>103</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>103</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>event</string>
|
||||
<key>location</key>
|
||||
<dict>
|
||||
<key>line</key><integer>103</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<key>ranges</key>
|
||||
<array>
|
||||
<array>
|
||||
<dict>
|
||||
<key>line</key><integer>103</integer>
|
||||
<key>col</key><integer>6</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>line</key><integer>103</integer>
|
||||
<key>col</key><integer>6</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</array>
|
||||
<key>depth</key><integer>0</integer>
|
||||
<key>extended_message</key>
|
||||
<string>Dereference of null pointer (loaded from variable 'p')</string>
|
||||
<key>message</key>
|
||||
<string>Dereference of null pointer (loaded from variable 'p')</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>description</key><string>Dereference of null pointer (loaded from variable 'p')</string>
|
||||
<key>category</key><string>Logic error</string>
|
||||
<key>type</key><string>Dereference of null pointer</string>
|
||||
<key>check_name</key><string>core.NullDereference</string>
|
||||
<!-- This hash is experimental and going to change! -->
|
||||
<key>issue_hash_content_of_line_in_context</key><string>ad377f8d4510dfd77d6485c402d57a2d</string>
|
||||
<key>issue_context_kind</key><string>function</string>
|
||||
<key>issue_context</key><string>testForRangeInit</string>
|
||||
<key>issue_hash_function_offset</key><string>2</string>
|
||||
<key>location</key>
|
||||
<dict>
|
||||
<key>line</key><integer>103</integer>
|
||||
<key>col</key><integer>8</integer>
|
||||
<key>file</key><integer>0</integer>
|
||||
</dict>
|
||||
<key>ExecutedLines</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<array>
|
||||
<integer>101</integer>
|
||||
<integer>102</integer>
|
||||
<integer>103</integer>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>files</key>
|
||||
<array>
|
||||
|
@ -97,3 +97,8 @@ void testLoopErrorInRange() {
|
||||
|
||||
*(volatile int *)0 = 1; // no-warning
|
||||
}
|
||||
|
||||
void testForRangeInit() {
|
||||
for (int *arr[3] = {nullptr, nullptr, nullptr}; int *p : arr) // expected-warning {{extension}}
|
||||
*p = 1; // expected-warning {{Dereference of null pointer}}
|
||||
}
|
||||
|
@ -820,10 +820,9 @@ void test_for_compound_and_break() {
|
||||
// CHECK-NEXT: 3: __end1
|
||||
// CHECK-NEXT: 4: [B2.3] (ImplicitCastExpr, LValueToRValue, class A *)
|
||||
// CHECK-NEXT: 5: [B2.2] != [B2.4]
|
||||
// CHECK-NEXT: T: for (auto &i : [B5.4]) {
|
||||
// CHECK-NEXT: T: for (auto &i : [B5.4])
|
||||
// CHECK: [B4.11];
|
||||
// CHECK-NEXT:}
|
||||
// CHECK-NEXT: Preds (2): B3 B5
|
||||
// CHECK: Preds (2): B3 B5
|
||||
// CHECK-NEXT: Succs (2): B4 B1
|
||||
// CHECK: [B3]
|
||||
// CHECK-NEXT: 1: __begin1
|
||||
|
10
clang/test/CodeGenCXX/cxx2a-init-statement.cpp
Normal file
10
clang/test/CodeGenCXX/cxx2a-init-statement.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s -w | FileCheck %s
|
||||
|
||||
// CHECK: @_ZZ1fvE3arr = private unnamed_addr constant [3 x i32] [i32 1, i32 2, i32 3], align 4
|
||||
|
||||
void f() {
|
||||
// CHECK: %[[ARR:.*]] = alloca [3 x i32], align 4
|
||||
// CHECK: call void @llvm.memcpy{{.*}}({{.*}} @_ZZ1fvE3arr
|
||||
for (int arr[3] = {1, 2, 3}; int a : arr)
|
||||
;
|
||||
}
|
@ -4,8 +4,7 @@ struct Container {
|
||||
};
|
||||
|
||||
void f() {
|
||||
Container c;
|
||||
for (int varname : c) {
|
||||
for (Container c; int varname : c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,11 @@
|
||||
|
||||
// CHECK: CXXForRangeStmt
|
||||
|
||||
// CHECK-NEXT: DeclStmt
|
||||
// CHECK-NEXT: VarDecl
|
||||
// CHECK-SAME: c 'Container'
|
||||
// CHECK-NEXT: CXXConstructExpr
|
||||
|
||||
// CHECK-NEXT: DeclStmt
|
||||
// CHECK-NEXT: VarDecl
|
||||
// CHECK-NEXT: DeclRefExpr
|
||||
|
8
clang/test/PCH/cxx2a-for-init-statement.cpp
Normal file
8
clang/test/PCH/cxx2a-for-init-statement.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
// RUN: %clang_cc1 -emit-pch -std=c++2a -o %t %s
|
||||
// RUN: %clang_cc1 -std=c++2a -x ast -ast-print %t | FileCheck %s
|
||||
|
||||
void f() {
|
||||
// CHECK: for (int arr[3]; int n : arr) {
|
||||
// CHECK-NEXT: }
|
||||
for (int arr[3]; int n : arr) {}
|
||||
}
|
34
clang/test/Parser/cxx2a-init-statement.cpp
Normal file
34
clang/test/Parser/cxx2a-init-statement.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -verify %s
|
||||
|
||||
template<int N> struct A {};
|
||||
|
||||
using F = bool(*)(int);
|
||||
extern F *p;
|
||||
extern int m;
|
||||
|
||||
struct Convertible { template<typename T> operator T(); };
|
||||
|
||||
void f() {
|
||||
int arr1[3];
|
||||
for (int n = 5; int x : arr1) {}
|
||||
|
||||
int A<0>::*arr2[3];
|
||||
for (int n = 5; int A<true ? 0 : 1>::*x : arr2) {}
|
||||
|
||||
F (*arr3[3])(int);
|
||||
for (int n = 5; F (*p)(int n) : arr3) {}
|
||||
for (int n = 5; F (*p)(int (n)) : arr3) {}
|
||||
|
||||
// Here, we have a declaration rather than an expression.
|
||||
for (int n = 5; F (*p)(int (n)); ++n) {}
|
||||
|
||||
// We detect whether we have a for-range-declaration before parsing so that
|
||||
// we can give different diagnostics for for-range-declarations versus
|
||||
// conditions (even though the rules are currently identical).
|
||||
Convertible arr4[3];
|
||||
for (int n = 0; struct { operator bool(); } x = {}; ++n) {} // expected-error {{cannot be defined in a condition}}
|
||||
for (int n = 0; struct { operator bool(); } x : arr4) {} // expected-error {{may not be defined in a for range declaration}}
|
||||
|
||||
for (int n = 0; static int m = 0; ++n) {} // expected-error {{type name does not allow storage class}}
|
||||
for (int n = 0; static int m : arr1) {} // expected-error {{loop variable 'm' may not be declared 'static'}}
|
||||
}
|
@ -204,3 +204,10 @@ static_assert(complex_test(makeComplex<_Complex int>(0, 0),
|
||||
std::strong_equality::nonequal));
|
||||
// TODO: defaulted operator <=>
|
||||
} // namespace ThreeWayComparison
|
||||
|
||||
constexpr bool for_range_init() {
|
||||
int k = 0;
|
||||
for (int arr[3] = {1, 2, 3}; int n : arr) k += n;
|
||||
return k == 6;
|
||||
}
|
||||
static_assert(for_range_init());
|
||||
|
@ -54,3 +54,12 @@ struct DefaultDeleteWrongType : DefaultDeleteWrongTypeBase {
|
||||
// expected-warning@-4 {{explicitly defaulting this copy constructor with a type different from the implicit type is incompatible with C++ standards before C++2a}}
|
||||
#endif
|
||||
};
|
||||
|
||||
void ForRangeInit() {
|
||||
for (int arr[3] = {1, 2, 3}; int n : arr) {}
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-warning@-2 {{range-based for loop initialization statements are a C++2a extension}}
|
||||
#else
|
||||
// expected-warning@-4 {{range-based for loop initialization statements are incompatible with C++ standards before C++2a}}
|
||||
#endif
|
||||
}
|
||||
|
@ -14,6 +14,14 @@ void f(NSArray *a) {
|
||||
for (auto thisKey : keys) { } // expected-warning{{'auto' deduced as 'id' in declaration of 'thisKey'}}
|
||||
}
|
||||
|
||||
void for_init_stmt() {
|
||||
for (id keys; id key : keys) {} // expected-warning{{extension}} expected-error{{not supported}}
|
||||
}
|
||||
template<typename T> void for_init_stmt_tmpl() {
|
||||
for (T keys; id key : keys) {} // expected-warning{{extension}} expected-error{{not supported}}
|
||||
}
|
||||
template void for_init_stmt_tmpl<id>(); // expected-note {{in instantiation of}}
|
||||
|
||||
template<typename Collection>
|
||||
void ft(Collection col) {
|
||||
for (id x : col) { }
|
||||
|
@ -874,7 +874,7 @@ as the draft C++2a standard evolves.
|
||||
<tr>
|
||||
<td>Range-based for statements with initializer</td>
|
||||
<td><a href="http://wg21.link/p0614r1">P0614R1</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="svn" align="center">SVN</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ADL and function templates that are not visible</td>
|
||||
|
Loading…
x
Reference in New Issue
Block a user