Skip to content

Commit e99d65e

Browse files
committed
[Clang] [C++26] Expansion Statements (Part 2)
1 parent 902410b commit e99d65e

File tree

11 files changed

+310
-61
lines changed

11 files changed

+310
-61
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ let CategoryName = "Parse Issue" in {
1616
defm enum_fixed_underlying_type : CXX11Compat<
1717
"enumeration types with a fixed underlying type are",
1818
/*ext_warn=*/false>;
19+
20+
// C++26 compatibility with C++23.
21+
defm expansion_statements : CXX26Compat<
22+
"expansion statements are">;
1923
}
2024

2125
def err_asm_qualifier_ignored : Error<
@@ -419,9 +423,10 @@ def warn_cxx98_compat_for_range : Warning<
419423
"range-based for loop is incompatible with C++98">,
420424
InGroup<CXX98Compat>, DefaultIgnore;
421425
def err_for_range_identifier : Error<
422-
"range-based for loop requires type for loop variable">;
426+
"%select{range-based for loop|expansion statement}0 requires "
427+
"type for %select{loop|expansion}0 variable">;
423428
def err_for_range_expected_decl : Error<
424-
"for range declaration must declare a variable">;
429+
"%select{for range|expansion statement}0 declaration must declare a variable">;
425430
def err_argument_required_after_attribute : Error<
426431
"argument required after attribute">;
427432
def err_missing_param : Error<"expected parameter declarator">;
@@ -448,6 +453,10 @@ def err_unspecified_size_with_static : Error<
448453
"'static' may not be used without an array size">;
449454
def err_expected_parentheses_around_typename : Error<
450455
"expected parentheses around type name in %0 expression">;
456+
def err_expansion_stmt_requires_range : Error<
457+
"expansion statement must be a range-based for loop">;
458+
def err_expansion_stmt_requires_cxx2c : Error<
459+
"expansion statements are only supported in C++2c">;
451460

452461
def err_expected_case_before_expression: Error<
453462
"expected 'case' keyword before expression">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2900,10 +2900,10 @@ def note_which_delegates_to : Note<"which delegates to">;
29002900

29012901
// C++11 range-based for loop
29022902
def err_for_range_decl_must_be_var : Error<
2903-
"for range declaration must declare a variable">;
2903+
"%select{for range|expansion statement}0 declaration must declare a variable">;
29042904
def err_for_range_storage_class : Error<
2905-
"loop variable %0 may not be declared %select{'extern'|'static'|"
2906-
"'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}1">;
2905+
"%select{loop|expansion}0 variable %1 may not be declared %select{'extern'|'static'|"
2906+
"'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}2">;
29072907
def err_type_defined_in_for_range : Error<
29082908
"types may not be defined in a for range declaration">;
29092909
def err_for_range_deduction_failure : Error<

clang/include/clang/Parse/Parser.h

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,11 +1700,13 @@ class Parser : public CodeCompletionHandler {
17001700
}
17011701

17021702
/// Information on a C++0x for-range-initializer found while parsing a
1703-
/// declaration which turns out to be a for-range-declaration.
1703+
/// declaration which turns out to be a for-range-declaration. Also used
1704+
/// for C++26's expansion statements.
17041705
struct ForRangeInit {
17051706
SourceLocation ColonLoc;
17061707
ExprResult RangeExpr;
17071708
SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
1709+
bool ExpansionStmt = false;
17081710
bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
17091711
};
17101712
struct ForRangeInfo : ForRangeInit {
@@ -4186,7 +4188,8 @@ class Parser : public CodeCompletionHandler {
41864188
bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
41874189
llvm::function_ref<void()> ExpressionStarts =
41884190
llvm::function_ref<void()>(),
4189-
bool FailImmediatelyOnInvalidExpr = false);
4191+
bool FailImmediatelyOnInvalidExpr = false,
4192+
bool StopAtRBraceAfterComma = false);
41904193

41914194
/// ParseSimpleExpressionList - A simple comma-separated list of expressions,
41924195
/// used for misc language extensions.
@@ -5246,6 +5249,16 @@ class Parser : public CodeCompletionHandler {
52465249
///
52475250
ExprResult ParseBraceInitializer();
52485251

5252+
/// ParseExpansionInitList - Called when the initializer of an expansion
5253+
/// statement starts with an open brace.
5254+
///
5255+
/// \verbatim
5256+
/// expansion-init-list: [C++26 [stmt.expand]]
5257+
/// '{' expression-list ','[opt] '}'
5258+
/// '{' '}'
5259+
/// \endverbatim
5260+
ExprResult ParseExpansionInitList();
5261+
52495262
struct DesignatorCompletionInfo {
52505263
SmallVectorImpl<Expr *> &InitExprs;
52515264
QualType PreferredBaseType;
@@ -7452,8 +7465,12 @@ class Parser : public CodeCompletionHandler {
74527465
/// [C++0x] expression
74537466
/// [C++0x] braced-init-list [TODO]
74547467
/// \endverbatim
7455-
StmtResult ParseForStatement(SourceLocation *TrailingElseLoc,
7456-
LabelDecl *PrecedingLabel);
7468+
StmtResult ParseForStatement(
7469+
SourceLocation *TrailingElseLoc, LabelDecl *PrecedingLabel,
7470+
CXXExpansionStmtDecl *CXXExpansionStmtDeclaration = nullptr);
7471+
7472+
void ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
7473+
ParsingDeclSpec *VarDeclSpec);
74577474

74587475
/// ParseGotoStatement
74597476
/// \verbatim
@@ -7500,6 +7517,22 @@ class Parser : public CodeCompletionHandler {
75007517

75017518
StmtResult ParseBreakOrContinueStatement(bool IsContinue);
75027519

7520+
/// ParseExpansionStatement - Parse a C++26 expansion
7521+
/// statement ('template for').
7522+
///
7523+
/// \verbatim
7524+
/// expansion-statement:
7525+
/// 'template' 'for' '(' init-statement[opt]
7526+
/// for-range-declaration ':' expansion-initializer ')'
7527+
/// compound-statement
7528+
///
7529+
/// expansion-initializer:
7530+
/// expression
7531+
/// expansion-init-list
7532+
/// \endverbatim
7533+
StmtResult ParseExpansionStatement(SourceLocation *TrailingElseLoc,
7534+
LabelDecl *PrecedingLabel);
7535+
75037536
StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
75047537
SourceLocation *TrailingElseLoc,
75057538
ParsedAttributes &Attrs,

clang/include/clang/Sema/Sema.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,7 @@ class Sema final : public SemaBase {
893893
// 33. Types (SemaType.cpp)
894894
// 34. FixIt Helpers (SemaFixItUtils.cpp)
895895
// 35. Function Effects (SemaFunctionEffects.cpp)
896+
// 36. C++ Expansion Statements (SemaExpand.cpp)
896897

897898
/// \name Semantic Analysis
898899
/// Implementations are in Sema.cpp
@@ -4097,7 +4098,7 @@ class Sema final : public SemaBase {
40974098
/// complete.
40984099
void ActOnInitializerError(Decl *Dcl);
40994100

4100-
void ActOnCXXForRangeDecl(Decl *D);
4101+
void ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt);
41014102
StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
41024103
IdentifierInfo *Ident,
41034104
ParsedAttributes &Attrs);
@@ -15613,6 +15614,36 @@ class Sema final : public SemaBase {
1561315614
void performFunctionEffectAnalysis(TranslationUnitDecl *TU);
1561415615

1561515616
///@}
15617+
15618+
//
15619+
//
15620+
// -------------------------------------------------------------------------
15621+
//
15622+
//
15623+
15624+
/// \name Expansion Statements
15625+
/// Implementations are in SemaExpand.cpp
15626+
///@{
15627+
public:
15628+
CXXExpansionStmtDecl *ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
15629+
SourceLocation TemplateKWLoc);
15630+
15631+
CXXExpansionStmtDecl *
15632+
BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
15633+
NonTypeTemplateParmDecl *NTTP);
15634+
15635+
ExprResult ActOnCXXExpansionInitList(MultiExprArg SubExprs,
15636+
SourceLocation LBraceLoc,
15637+
SourceLocation RBraceLoc);
15638+
15639+
StmtResult ActOnCXXExpansionStmtPattern(
15640+
CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt,
15641+
Expr *ExpansionInitializer, SourceLocation LParenLoc,
15642+
SourceLocation ColonLoc, SourceLocation RParenLoc,
15643+
ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps);
15644+
15645+
StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
15646+
///@}
1561615647
};
1561715648

1561815649
DeductionFailureInfo

clang/lib/Parse/ParseDecl.cpp

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,43 +2304,18 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
23042304
// Handle the Objective-C for-in loop variable similarly, although we
23052305
// don't need to parse the container in advance.
23062306
if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) {
2307-
bool IsForRangeLoop = false;
2307+
bool IsForRangeLoopOrExpansionStmt = false;
23082308
if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
2309-
IsForRangeLoop = true;
2310-
EnterExpressionEvaluationContext ForRangeInitContext(
2311-
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
2312-
/*LambdaContextDecl=*/nullptr,
2313-
Sema::ExpressionEvaluationContextRecord::EK_Other,
2314-
getLangOpts().CPlusPlus23);
2315-
2316-
// P2718R0 - Lifetime extension in range-based for loops.
2317-
if (getLangOpts().CPlusPlus23) {
2318-
auto &LastRecord = Actions.currentEvaluationContext();
2319-
LastRecord.InLifetimeExtendingContext = true;
2320-
LastRecord.RebuildDefaultArgOrDefaultInit = true;
2321-
}
2322-
2323-
if (getLangOpts().OpenMP)
2309+
IsForRangeLoopOrExpansionStmt = true;
2310+
if (getLangOpts().OpenMP && !FRI->ExpansionStmt)
23242311
Actions.OpenMP().startOpenMPCXXRangeFor();
2325-
if (Tok.is(tok::l_brace))
2326-
FRI->RangeExpr = ParseBraceInitializer();
2327-
else
2328-
FRI->RangeExpr = ParseExpression();
2329-
2330-
// Before c++23, ForRangeLifetimeExtendTemps should be empty.
2331-
assert(
2332-
getLangOpts().CPlusPlus23 ||
2333-
Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
23342312

2335-
// Move the collected materialized temporaries into ForRangeInit before
2336-
// ForRangeInitContext exit.
2337-
FRI->LifetimeExtendTemps = std::move(
2338-
Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
2313+
ParseForRangeInitializerAfterColon(*FRI, &DS);
23392314
}
23402315

23412316
Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
2342-
if (IsForRangeLoop) {
2343-
Actions.ActOnCXXForRangeDecl(ThisDecl);
2317+
if (IsForRangeLoopOrExpansionStmt) {
2318+
Actions.ActOnCXXForRangeDecl(ThisDecl, FRI->ExpansionStmt);
23442319
} else {
23452320
// Obj-C for loop
23462321
if (auto *VD = dyn_cast_or_null<VarDecl>(ThisDecl))

clang/lib/Parse/ParseExpr.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,7 +3166,8 @@ void Parser::injectEmbedTokens() {
31663166

31673167
bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
31683168
llvm::function_ref<void()> ExpressionStarts,
3169-
bool FailImmediatelyOnInvalidExpr) {
3169+
bool FailImmediatelyOnInvalidExpr,
3170+
bool StopAtRBraceAfterComma) {
31703171
bool SawError = false;
31713172
while (true) {
31723173
if (ExpressionStarts)
@@ -3195,7 +3196,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
31953196
SawError = true;
31963197
if (FailImmediatelyOnInvalidExpr)
31973198
break;
3198-
SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
3199+
3200+
if (StopAtRBraceAfterComma)
3201+
SkipUntil(tok::comma, tok::r_brace, StopAtSemi | StopBeforeMatch);
3202+
else
3203+
SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
31993204
} else {
32003205
Exprs.push_back(Expr.get());
32013206
}
@@ -3205,6 +3210,10 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
32053210
// Move to the next argument, remember where the comma was.
32063211
Token Comma = Tok;
32073212
ConsumeToken();
3213+
3214+
if (StopAtRBraceAfterComma && Tok.is(tok::r_brace))
3215+
break;
3216+
32083217
checkPotentialAngleBracketDelimiter(Comma);
32093218
}
32103219
return SawError;

clang/lib/Parse/ParseInit.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,26 @@ ExprResult Parser::ParseBraceInitializer() {
516516
return ExprError(); // an error occurred.
517517
}
518518

519+
ExprResult Parser::ParseExpansionInitList() {
520+
BalancedDelimiterTracker T(*this, tok::l_brace);
521+
T.consumeOpen();
522+
523+
ExprVector InitExprs;
524+
525+
// CWG 3061: Accept a trailing comma here.
526+
if (!Tok.is(tok::r_brace) &&
527+
ParseExpressionList(InitExprs, /*ExpressionStarts=*/{},
528+
/*FailImmediatelyOnInvalidExpr=*/false,
529+
/*StopAtRBraceAfterComma=*/true)) {
530+
T.consumeClose();
531+
return ExprError();
532+
}
533+
534+
T.consumeClose();
535+
return Actions.ActOnCXXExpansionInitList(InitExprs, T.getOpenLocation(),
536+
T.getCloseLocation());
537+
}
538+
519539
bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
520540
bool &InitExprsOk) {
521541
bool trailingComma = false;

0 commit comments

Comments
 (0)