llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: None (Sirraide) <details> <summary>Changes</summary> [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: - (modified) clang/include/clang/Basic/DiagnosticCommonKinds.td (+4) - (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+11-2) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3-3) - (modified) clang/include/clang/Parse/Parser.h (+37-4) - (modified) clang/include/clang/Sema/Sema.h (+32-1) - (modified) clang/lib/Parse/ParseDecl.cpp (+6-31) - (modified) clang/lib/Parse/ParseExpr.cpp (+11-2) - (modified) clang/lib/Parse/ParseInit.cpp (+20) - (modified) clang/lib/Parse/ParseStmt.cpp (+128-15) - (modified) clang/lib/Sema/CMakeLists.txt (+1) - (modified) clang/lib/Sema/SemaDecl.cpp (+4-3) - (added) clang/lib/Sema/SemaExpand.cpp (+81) - (added) clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp (+5) - (added) clang/test/Parser/cxx2c-expansion-statements.cpp (+63) ``````````diff 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] `````````` </details> https://github.com/llvm/llvm-project/pull/169681 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
