-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[Clang] [C++26] Expansion Statements (Part 2: Parsing and Parser Tests) #169681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: users/Sirraide/expansion-stmts-1-ast
Are you sure you want to change the base?
[Clang] [C++26] Expansion Statements (Part 2: Parsing and Parser Tests) #169681
Conversation
You can test this locally with the following command:git-clang-format --diff origin/main HEAD --extensions cpp,h -- clang/lib/Sema/SemaExpand.cpp clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp clang/test/Parser/cxx2c-expansion-statements.cpp clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Sema.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseExpr.cpp clang/lib/Parse/ParseInit.cpp clang/lib/Parse/ParseStmt.cpp clang/lib/Sema/SemaDecl.cpp --diff_from_common_commit
View the diff from clang-format here.diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 96c0d9be9..f48e8ca77 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -24,7 +24,6 @@
using namespace clang;
using namespace sema;
-
CXXExpansionStmtDecl *
Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
SourceLocation TemplateKWLoc) {
|
🐧 Linux x64 Test Results
|
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-codegen Author: None (Sirraide) Changes[Clang] [C++26] Expansion Statements (Part 2) Add Sema for CXXExpansionStmtDecl Add parser tests Patch is 34.81 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169681.diff 14 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index 6e50e225a8cc1..0b9225980e826 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -22,6 +22,10 @@ def select_constexpr_spec_kind : TextSubstitution<
def fatal_too_many_errors
: Error<"too many errors emitted, stopping now">, DefaultFatal;
+// TODO: Remove this.
+def err_expansion_statements_todo : Error<
+ "TODO (expansion statements)">;
+
def warn_stack_exhausted : Warning<
"stack nearly exhausted; compilation time may suffer, and "
"crashes due to stack overflow are likely">,
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index aa0ccb0c05101..55234ebab3fe4 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -16,6 +16,10 @@ let CategoryName = "Parse Issue" in {
defm enum_fixed_underlying_type : CXX11Compat<
"enumeration types with a fixed underlying type are",
/*ext_warn=*/false>;
+
+// C++26 compatibility with C++23.
+defm expansion_statements : CXX26Compat<
+ "expansion statements are">;
}
def err_asm_qualifier_ignored : Error<
@@ -419,9 +423,10 @@ def warn_cxx98_compat_for_range : Warning<
"range-based for loop is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def err_for_range_identifier : Error<
- "range-based for loop requires type for loop variable">;
+ "%select{range-based for loop|expansion statement}0 requires "
+ "type for %select{loop|expansion}0 variable">;
def err_for_range_expected_decl : Error<
- "for range declaration must declare a variable">;
+ "%select{for range|expansion statement}0 declaration must declare a variable">;
def err_argument_required_after_attribute : Error<
"argument required after attribute">;
def err_missing_param : Error<"expected parameter declarator">;
@@ -448,6 +453,10 @@ def err_unspecified_size_with_static : Error<
"'static' may not be used without an array size">;
def err_expected_parentheses_around_typename : Error<
"expected parentheses around type name in %0 expression">;
+def err_expansion_stmt_requires_range : Error<
+ "expansion statement must be a range-based for loop">;
+def err_expansion_stmt_requires_cxx2c : Error<
+ "expansion statements are only supported in C++2c">;
def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 53aa86a7dabde..ca862316a2f27 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2900,10 +2900,10 @@ def note_which_delegates_to : Note<"which delegates to">;
// C++11 range-based for loop
def err_for_range_decl_must_be_var : Error<
- "for range declaration must declare a variable">;
+ "%select{for range|expansion statement}0 declaration must declare a variable">;
def err_for_range_storage_class : Error<
- "loop variable %0 may not be declared %select{'extern'|'static'|"
- "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}1">;
+ "%select{loop|expansion}0 variable %1 may not be declared %select{'extern'|'static'|"
+ "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}2">;
def err_type_defined_in_for_range : Error<
"types may not be defined in a for range declaration">;
def err_for_range_deduction_failure : Error<
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 58eb1c0a7c114..ff4f7b4e1dd2d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1700,11 +1700,13 @@ class Parser : public CodeCompletionHandler {
}
/// Information on a C++0x for-range-initializer found while parsing a
- /// declaration which turns out to be a for-range-declaration.
+ /// declaration which turns out to be a for-range-declaration. Also used
+ /// for C++26's expansion statements.
struct ForRangeInit {
SourceLocation ColonLoc;
ExprResult RangeExpr;
SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
+ bool ExpansionStmt = false;
bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
};
struct ForRangeInfo : ForRangeInit {
@@ -4186,7 +4188,8 @@ class Parser : public CodeCompletionHandler {
bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
llvm::function_ref<void()> ExpressionStarts =
llvm::function_ref<void()>(),
- bool FailImmediatelyOnInvalidExpr = false);
+ bool FailImmediatelyOnInvalidExpr = false,
+ bool StopAtRBraceAfterComma = false);
/// ParseSimpleExpressionList - A simple comma-separated list of expressions,
/// used for misc language extensions.
@@ -5246,6 +5249,16 @@ class Parser : public CodeCompletionHandler {
///
ExprResult ParseBraceInitializer();
+ /// ParseExpansionInitList - Called when the initializer of an expansion
+ /// statement starts with an open brace.
+ ///
+ /// \verbatim
+ /// expansion-init-list: [C++26 [stmt.expand]]
+ /// '{' expression-list ','[opt] '}'
+ /// '{' '}'
+ /// \endverbatim
+ ExprResult ParseExpansionInitList();
+
struct DesignatorCompletionInfo {
SmallVectorImpl<Expr *> &InitExprs;
QualType PreferredBaseType;
@@ -7452,8 +7465,12 @@ class Parser : public CodeCompletionHandler {
/// [C++0x] expression
/// [C++0x] braced-init-list [TODO]
/// \endverbatim
- StmtResult ParseForStatement(SourceLocation *TrailingElseLoc,
- LabelDecl *PrecedingLabel);
+ StmtResult ParseForStatement(
+ SourceLocation *TrailingElseLoc, LabelDecl *PrecedingLabel,
+ CXXExpansionStmtDecl *CXXExpansionStmtDeclaration = nullptr);
+
+ void ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
+ ParsingDeclSpec *VarDeclSpec);
/// ParseGotoStatement
/// \verbatim
@@ -7500,6 +7517,22 @@ class Parser : public CodeCompletionHandler {
StmtResult ParseBreakOrContinueStatement(bool IsContinue);
+ /// ParseExpansionStatement - Parse a C++26 expansion
+ /// statement ('template for').
+ ///
+ /// \verbatim
+ /// expansion-statement:
+ /// 'template' 'for' '(' init-statement[opt]
+ /// for-range-declaration ':' expansion-initializer ')'
+ /// compound-statement
+ ///
+ /// expansion-initializer:
+ /// expression
+ /// expansion-init-list
+ /// \endverbatim
+ StmtResult ParseExpansionStatement(SourceLocation *TrailingElseLoc,
+ LabelDecl *PrecedingLabel);
+
StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
SourceLocation *TrailingElseLoc,
ParsedAttributes &Attrs,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ae500139ee6f7..786e53a2e179a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -893,6 +893,7 @@ class Sema final : public SemaBase {
// 33. Types (SemaType.cpp)
// 34. FixIt Helpers (SemaFixItUtils.cpp)
// 35. Function Effects (SemaFunctionEffects.cpp)
+ // 36. C++ Expansion Statements (SemaExpand.cpp)
/// \name Semantic Analysis
/// Implementations are in Sema.cpp
@@ -4097,7 +4098,7 @@ class Sema final : public SemaBase {
/// complete.
void ActOnInitializerError(Decl *Dcl);
- void ActOnCXXForRangeDecl(Decl *D);
+ void ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt);
StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
IdentifierInfo *Ident,
ParsedAttributes &Attrs);
@@ -15613,6 +15614,36 @@ class Sema final : public SemaBase {
void performFunctionEffectAnalysis(TranslationUnitDecl *TU);
///@}
+
+ //
+ //
+ // -------------------------------------------------------------------------
+ //
+ //
+
+ /// \name Expansion Statements
+ /// Implementations are in SemaExpand.cpp
+ ///@{
+public:
+ CXXExpansionStmtDecl *ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
+ SourceLocation TemplateKWLoc);
+
+ CXXExpansionStmtDecl *
+ BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
+ NonTypeTemplateParmDecl *NTTP);
+
+ ExprResult ActOnCXXExpansionInitList(MultiExprArg SubExprs,
+ SourceLocation LBraceLoc,
+ SourceLocation RBraceLoc);
+
+ StmtResult ActOnCXXExpansionStmtPattern(
+ CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt,
+ Expr *ExpansionInitializer, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps);
+
+ StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
+ ///@}
};
DeductionFailureInfo
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8688ccf41acb5..5e1ff2be28f38 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2304,43 +2304,18 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
// Handle the Objective-C for-in loop variable similarly, although we
// don't need to parse the container in advance.
if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) {
- bool IsForRangeLoop = false;
+ bool IsForRangeLoopOrExpansionStmt = false;
if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
- IsForRangeLoop = true;
- EnterExpressionEvaluationContext ForRangeInitContext(
- Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
- /*LambdaContextDecl=*/nullptr,
- Sema::ExpressionEvaluationContextRecord::EK_Other,
- getLangOpts().CPlusPlus23);
-
- // P2718R0 - Lifetime extension in range-based for loops.
- if (getLangOpts().CPlusPlus23) {
- auto &LastRecord = Actions.currentEvaluationContext();
- LastRecord.InLifetimeExtendingContext = true;
- LastRecord.RebuildDefaultArgOrDefaultInit = true;
- }
-
- if (getLangOpts().OpenMP)
+ IsForRangeLoopOrExpansionStmt = true;
+ if (getLangOpts().OpenMP && !FRI->ExpansionStmt)
Actions.OpenMP().startOpenMPCXXRangeFor();
- if (Tok.is(tok::l_brace))
- FRI->RangeExpr = ParseBraceInitializer();
- else
- FRI->RangeExpr = ParseExpression();
-
- // Before c++23, ForRangeLifetimeExtendTemps should be empty.
- assert(
- getLangOpts().CPlusPlus23 ||
- Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
- // Move the collected materialized temporaries into ForRangeInit before
- // ForRangeInitContext exit.
- FRI->LifetimeExtendTemps = std::move(
- Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
+ ParseForRangeInitializerAfterColon(*FRI, &DS);
}
Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
- if (IsForRangeLoop) {
- Actions.ActOnCXXForRangeDecl(ThisDecl);
+ if (IsForRangeLoopOrExpansionStmt) {
+ Actions.ActOnCXXForRangeDecl(ThisDecl, FRI->ExpansionStmt);
} else {
// Obj-C for loop
if (auto *VD = dyn_cast_or_null<VarDecl>(ThisDecl))
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 3515343202de1..902afcaeed987 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -3166,7 +3166,8 @@ void Parser::injectEmbedTokens() {
bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
llvm::function_ref<void()> ExpressionStarts,
- bool FailImmediatelyOnInvalidExpr) {
+ bool FailImmediatelyOnInvalidExpr,
+ bool StopAtRBraceAfterComma) {
bool SawError = false;
while (true) {
if (ExpressionStarts)
@@ -3195,7 +3196,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
SawError = true;
if (FailImmediatelyOnInvalidExpr)
break;
- SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
+
+ if (StopAtRBraceAfterComma)
+ SkipUntil(tok::comma, tok::r_brace, StopAtSemi | StopBeforeMatch);
+ else
+ SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
} else {
Exprs.push_back(Expr.get());
}
@@ -3205,6 +3210,10 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
// Move to the next argument, remember where the comma was.
Token Comma = Tok;
ConsumeToken();
+
+ if (StopAtRBraceAfterComma && Tok.is(tok::r_brace))
+ break;
+
checkPotentialAngleBracketDelimiter(Comma);
}
return SawError;
diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index 0e86c4c48d5e4..11c4e983cdcd3 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -516,6 +516,26 @@ ExprResult Parser::ParseBraceInitializer() {
return ExprError(); // an error occurred.
}
+ExprResult Parser::ParseExpansionInitList() {
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+
+ ExprVector InitExprs;
+
+ // CWG 3061: Accept a trailing comma here.
+ if (!Tok.is(tok::r_brace) &&
+ ParseExpressionList(InitExprs, /*ExpressionStarts=*/{},
+ /*FailImmediatelyOnInvalidExpr=*/false,
+ /*StopAtRBraceAfterComma=*/true)) {
+ T.consumeClose();
+ return ExprError();
+ }
+
+ T.consumeClose();
+ return Actions.ActOnCXXExpansionInitList(InitExprs, T.getOpenLocation(),
+ T.getCloseLocation());
+}
+
bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
bool &InitExprsOk) {
bool trailingComma = false;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 7e73d89c2a18c..39751c79c6852 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -260,6 +260,20 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
}
case tok::kw_template: {
+ if (NextToken().is(tok::kw_for)) {
+ // Expansion statements are not backported for now.
+ if (!getLangOpts().CPlusPlus26) {
+ Diag(Tok.getLocation(), diag::err_expansion_stmt_requires_cxx2c);
+
+ // Trying to parse this as a regular 'for' statement instead yields
+ // better error recovery.
+ ConsumeToken();
+ return ParseForStatement(TrailingElseLoc, PrecedingLabel);
+ }
+
+ return ParseExpansionStatement(TrailingElseLoc, PrecedingLabel);
+ }
+
SourceLocation DeclEnd;
ParseTemplateDeclarationOrSpecialization(DeclaratorContext::Block, DeclEnd,
getAccessSpecifierIfPresent());
@@ -1884,8 +1898,55 @@ bool Parser::isForRangeIdentifier() {
return false;
}
-StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
- LabelDecl *PrecedingLabel) {
+void Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
+ ParsingDeclSpec *VarDeclSpec) {
+ // Use an immediate function context if this is the initializer for a
+ // constexpr variable in an expansion statement.
+ auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+ if (FRI.ExpansionStmt && VarDeclSpec && VarDeclSpec->hasConstexprSpecifier())
+ Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+
+ EnterExpressionEvaluationContext InitContext(
+ Actions, Ctx,
+ /*LambdaContextDecl=*/nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Other,
+ getLangOpts().CPlusPlus23);
+
+ // P2718R0 - Lifetime extension in range-based for loops.
+ if (getLangOpts().CPlusPlus23) {
+ auto &LastRecord = Actions.currentEvaluationContext();
+ LastRecord.InLifetimeExtendingContext = true;
+ LastRecord.RebuildDefaultArgOrDefaultInit = true;
+ }
+
+ if (FRI.ExpansionStmt) {
+ Sema::ContextRAII CtxGuard(
+ Actions, Actions.CurContext->getEnclosingNonExpansionStatementContext(),
+ /*NewThis=*/false);
+
+ FRI.RangeExpr =
+ Tok.is(tok::l_brace) ? ParseExpansionInitList() : ParseExpression();
+ FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(FRI.RangeExpr);
+ } else if (Tok.is(tok::l_brace)) {
+ FRI.RangeExpr = ParseBraceInitializer();
+ } else {
+ FRI.RangeExpr = ParseExpression();
+ }
+
+ // Before c++23, ForRangeLifetimeExtendTemps should be empty.
+ assert(getLangOpts().CPlusPlus23 ||
+ Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
+
+ // Move the collected materialized temporaries into ForRangeInit before
+ // ForRangeInitContext exit.
+ FRI.LifetimeExtendTemps =
+ std::move(Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
+}
+
+StmtResult
+Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
+ LabelDecl *PrecedingLabel,
+ CXXExpansionStmtDecl *CXXExpansionStmtDecl) {
assert(Tok.is(tok::kw_for) && "Not a for stmt!");
SourceLocation ForLoc = ConsumeToken(); // eat the 'for'.
@@ -1920,6 +1981,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
unsigned ScopeFlags = 0;
if (C99orCXXorObjC)
ScopeFlags = Scope::DeclScope | Scope::ControlScope;
+ if (CXXExpansionStmtDecl)
+ ScopeFlags |= Scope::TemplateParamScope;
ParseScope ForScope(this, ScopeFlags);
@@ -1934,6 +1997,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
ExprResult Collection;
ForRangeInfo ForRangeInfo;
FullExprArg ThirdPart(Actions);
+ ForRangeInfo.ExpansionStmt = CXXExpansionStmtDecl != nullptr;
if (Tok.is(tok::code_completion)) {
cutOffParsing();
@@ -1964,18 +2028,17 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
MaybeParseCXX11Attributes(attrs);
ForRangeInfo.ColonLoc = ConsumeToken();
- if (Tok.is(tok::l_brace))
- ForRangeInfo.RangeExpr = ParseBraceInitializer();
- else
- ForRangeInfo.RangeExpr = ParseExpression();
+ ParseForRangeInitializerAfterColon(ForRangeInfo, /*VarDeclSpec=*/nullptr);
Diag(Loc, diag::err_for_range_identifier)
- << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
- ? FixItHint::CreateInsertion(Loc, "auto &&")
- : FixItHint());
-
- ForRangeInfo.LoopVar =
- Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs);
+ << ForRangeInfo.ExpansionStmt
+ << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
+ ? FixItHint::CreateInsertion(Loc, "auto &&")
+ : FixItHint());
+
+ if (!ForRangeInfo.ExpansionStmt)
+ ForRangeInfo.LoopVar =
+ Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs);
} else if (isForInitDeclaration()) { // for (int X = 4;
ParenBraceBracketBalancer BalancerRAIIObj(*this);
@@ -2067,7 +2130,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// User tried to write the reasonable, but ill-formed, for-range-statement
// for (expr : expr) { ... }
Diag(Tok, diag::err_for_range_expected_decl)
- << FirstPart.get()->getSourceRange();
+ << (CXXExpansionStmtDecl != nullptr)
+ << FirstPart.get()->getSourceRange();
SkipUntil(tok::r_paren, StopBeforeMatch);
SecondPart = Sema::ConditionError();
} else {
@@ -2189,7 +2253,13 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
StmtResult ForRangeStmt;
StmtResult ForEachStmt;
- if (ForRangeInfo.ParsedForRangeDecl()) {
+ if (CXXExpansionStmtDecl) {
+ ForRangeStmt = Actions.ActOnCXXExpansionStmtPattern(
+ CXXExpansionStmt...
[truncated]
|
cd40ea9 to
c7cd02e
Compare
clang/include/clang/Parse/Parser.h
Outdated
| llvm::function_ref<void()>(), | ||
| bool FailImmediatelyOnInvalidExpr = false); | ||
| bool FailImmediatelyOnInvalidExpr = false, | ||
| bool StopAtRBraceAfterComma = false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems subtle, can we comment this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has something to do w/ trailing commas; I don’t remember exactly how this works so this definitely needs a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed this to ParsingExpansionInitList because that’s what it actually means, really.
clang/lib/Parse/ParseExpr.cpp
Outdated
| SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); | ||
|
|
||
| if (StopAtRBraceAfterComma) | ||
| SkipUntil(tok::comma, tok::r_brace, StopAtSemi | StopBeforeMatch); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain the WHY here? The name could perhaps use improvement to help explain what is happening.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because we expect the expression-list to be terminated by a } rather than a ) if we’re parsing an expansion-init-list; I added a comment that explains that.
| getLangOpts().CPlusPlus23); | ||
|
|
||
| // P2718R0 - Lifetime extension in range-based for loops. | ||
| if (getLangOpts().CPlusPlus23) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side note, Did we not DR this?
| if (!Exp || !Body) | ||
| return StmtError(); | ||
|
|
||
| llvm_unreachable("TODO"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather a diag here if this isn't filled in by later in the patch (nit: would be neat if we had something to mark these up in that case).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That’s also removed later; basically all the TODOs in the entire patch series are removed at some point (and if they aren’t, it’s because I missed one and we don’t have enough tests, but there shouldn’t be any left by the end)
c7cd02e to
2d43de2
Compare
| template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \ | ||
| expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}} | ||
| ; | ||
| template while (true) {} // expected-error {{expected '<' after 'template'}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a better error message here? Maybe something along the lines of "'template' cannot be followed by 'while'", potentially followed by a fixit.
I'd also like to see tests for for template and template do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, sure, we can add special handling for e.g. template while; we do the same thing for friend concept so why not; any other invalid keyword combinations I should test for (I guess template do?)
I'd also like to see tests for
for templateandtemplate do
I’ll add some
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added tests for this as well as a fixit:
test.cc:4:3: error: 'for template' is invalid; use 'template for' instead
4 | for template (int x : {}) {}
| ^~~~~~~~~~~~
| template forThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a test for template do, if you meant to publish it by this point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not yet as I’m still looking into something else; I was just replying to the comments as I was adding things because I’d probably forget about some of them if I waited until after I publish everything...
2d43de2 to
f92568a
Compare

This patch implements parsing of expansion statements and expansion initialiser lists. I’ve also had to implement sema for
CXXExpansionStmtDeclbecause parsing doesn’t really work without that node.