From 0f0af19b058151690ec02d781d0039fb1fc38426 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 8 Nov 2014 05:07:16 +0000 Subject: [PATCH] [c++1z] N4295: fold-expressions. This is a new form of expression of the form: (expr op ... op expr) where one of the exprs is a parameter pack. It expands into (expr1 op (expr2onwards op ... op expr)) (and likewise if the pack is on the right). The non-pack operand can be omitted; in that case, an empty pack gives a fallback value or an error, depending on the operator. llvm-svn: 221573 --- .../clang/AST/DataRecursiveASTVisitor.h | 1 + clang/include/clang/AST/ExprCXX.h | 63 ++++++++ clang/include/clang/AST/RecursiveASTVisitor.h | 1 + .../clang/Basic/DiagnosticParseKinds.td | 13 +- .../clang/Basic/DiagnosticSemaKinds.td | 8 + clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Parse/Parser.h | 2 + clang/include/clang/Sema/Sema.h | 15 ++ .../include/clang/Serialization/ASTBitCodes.h | 3 +- clang/lib/AST/Expr.cpp | 1 + clang/lib/AST/ExprClassification.cpp | 1 + clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 27 +++- clang/lib/AST/StmtPrinter.cpp | 14 ++ clang/lib/AST/StmtProfile.cpp | 5 + clang/lib/Parse/ParseExpr.cpp | 83 +++++++++- clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Sema/SemaExpr.cpp | 3 +- clang/lib/Sema/SemaTemplateVariadic.cpp | 105 +++++++++++++ clang/lib/Sema/TreeTransform.h | 143 ++++++++++++++++++ clang/lib/Serialization/ASTReaderStmt.cpp | 16 +- clang/lib/Serialization/ASTWriterStmt.cpp | 11 ++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + .../test/CodeGenCXX/cxx1z-fold-expression.cpp | 45 ++++++ clang/test/Parser/cxx1z-fold-expressions.cpp | 29 ++++ clang/test/SemaCXX/cxx0x-compat.cpp | 6 + .../SemaTemplate/cxx1z-fold-expressions.cpp | 78 ++++++++++ clang/tools/libclang/CXCursor.cpp | 1 + clang/www/cxx_status.html | 8 + 29 files changed, 677 insertions(+), 9 deletions(-) create mode 100644 clang/test/CodeGenCXX/cxx1z-fold-expression.cpp create mode 100644 clang/test/Parser/cxx1z-fold-expressions.cpp create mode 100644 clang/test/SemaTemplate/cxx1z-fold-expressions.cpp diff --git a/clang/include/clang/AST/DataRecursiveASTVisitor.h b/clang/include/clang/AST/DataRecursiveASTVisitor.h index f528e1cabb8a..5cd3358c7eca 100644 --- a/clang/include/clang/AST/DataRecursiveASTVisitor.h +++ b/clang/include/clang/AST/DataRecursiveASTVisitor.h @@ -2257,6 +2257,7 @@ DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, {}) DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, {}) DEF_TRAVERSE_STMT(FunctionParmPackExpr, {}) DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {}) +DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) // These literals (all of them) do not need any action. diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index c3e47cafb348..588680e79df9 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3818,6 +3818,69 @@ public: } }; +/// \brief Represents a folding of a pack over an operator. +/// +/// This expression is always dependent and represents a pack expansion of the +/// forms: +/// +/// ( expr op ... ) +/// ( ... op expr ) +/// ( expr op ... op expr ) +class CXXFoldExpr : public Expr { + SourceLocation LParenLoc; + SourceLocation EllipsisLoc; + SourceLocation RParenLoc; + Stmt *SubExprs[2]; + BinaryOperatorKind Opcode; + + friend class ASTStmtReader; + friend class ASTStmtWriter; +public: + CXXFoldExpr(QualType T, SourceLocation LParenLoc, Expr *LHS, + BinaryOperatorKind Opcode, SourceLocation EllipsisLoc, Expr *RHS, + SourceLocation RParenLoc) + : Expr(CXXFoldExprClass, T, VK_RValue, OK_Ordinary, + /*Dependent*/ true, true, true, + /*ContainsUnexpandedParameterPack*/ false), + LParenLoc(LParenLoc), EllipsisLoc(EllipsisLoc), RParenLoc(RParenLoc), + Opcode(Opcode) { + SubExprs[0] = LHS; + SubExprs[1] = RHS; + } + CXXFoldExpr(EmptyShell Empty) : Expr(CXXFoldExprClass, Empty) {} + + Expr *getLHS() const { return static_cast(SubExprs[0]); } + Expr *getRHS() const { return static_cast(SubExprs[1]); } + + /// Does this produce a right-associated sequence of operators? + bool isRightFold() const { + return getLHS() && getLHS()->containsUnexpandedParameterPack(); + } + /// Does this produce a left-associated sequence of operators? + bool isLeftFold() const { return !isRightFold(); } + /// Get the pattern, that is, the operand that contains an unexpanded pack. + Expr *getPattern() const { return isLeftFold() ? getRHS() : getLHS(); } + /// Get the operand that doesn't contain a pack, for a binary fold. + Expr *getInit() const { return isLeftFold() ? getLHS() : getRHS(); } + + SourceLocation getEllipsisLoc() const { return EllipsisLoc; } + BinaryOperatorKind getOperator() const { return Opcode; } + + SourceLocation getLocStart() const LLVM_READONLY { + return LParenLoc; + } + SourceLocation getLocEnd() const LLVM_READONLY { + return RParenLoc; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXFoldExprClass; + } + + // Iterators + child_range children() { return child_range(SubExprs, SubExprs + 2); } +}; + } // end namespace clang #endif diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 4688beae8bfb..53eb503cab4d 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2279,6 +2279,7 @@ DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, {}) DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, {}) DEF_TRAVERSE_STMT(FunctionParmPackExpr, {}) DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {}) +DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) // These literals (all of them) do not need any action. diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 4510fdf157ec..7c9ee21d0c21 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -682,7 +682,18 @@ def err_explicit_spec_non_template : Error< def err_default_template_template_parameter_not_template : Error< "default template argument for a template template parameter must be a class " "template">; - + +def ext_fold_expression : ExtWarn< + "pack fold expression is a C++1z extension">, + InGroup; +def warn_cxx14_compat_fold_expression : Warning< + "pack fold expression is incompatible with C++ standards before C++1z">, + InGroup, DefaultIgnore; +def err_expected_fold_operator : Error< + "expected a foldable binary operator in fold expression">; +def err_fold_operator_mismatch : Error< + "operators in fold expression must be the same">; + def err_ctor_init_missing_comma : Error< "missing ',' between base or member initializers">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9f561164c5d9..33e28c7b8b65 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3784,6 +3784,14 @@ def err_ellipsis_in_declarator_not_parameter : Error< def err_sizeof_pack_no_pack_name : Error< "%0 does not refer to the name of a parameter pack">; +def err_fold_expression_packs_both_sides : Error< + "binary fold expression has unexpanded parameter packs in both operands">; +def err_fold_expression_empty : Error< + "unary fold expression has empty expansion for operator '%0' " + "with no fallback value">; +def err_fold_expression_bad_operand : Error< + "expression not permitted as operand of fold expression">; + def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; def err_unexpected_namespace : Error< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index ea2f931c1ab3..750108f39f9a 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -135,6 +135,7 @@ def SubstNonTypeTemplateParmPackExpr : DStmt; def FunctionParmPackExpr : DStmt; def MaterializeTemporaryExpr : DStmt; def LambdaExpr : DStmt; +def CXXFoldExpr : DStmt; // Obj-C Expressions. def ObjCStringLiteral : DStmt; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index f595b1f13268..8368f04d7a38 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1392,6 +1392,8 @@ private: ExprResult ParseObjCBoolLiteral(); + ExprResult ParseFoldExpression(ExprResult LHS, BalancedDelimiterTracker &T); + //===--------------------------------------------------------------------===// // C++ Expressions ExprResult ParseCXXIdExpression(bool isAddressOfOperand = false); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2ecaf53815ab..4ad7463767d6 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3714,6 +3714,10 @@ public: bool GNUSyntax, ExprResult Init); +private: + static BinaryOperatorKind ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind); + +public: ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, tok::TokenKind Kind, Expr *LHSExpr, Expr *RHSExpr); ExprResult BuildBinOp(Scope *S, SourceLocation OpLoc, @@ -4287,6 +4291,17 @@ public: void *TyOrExpr, SourceLocation RParenLoc); + /// \brief Handle a C++1z fold-expression: ( expr op ... op expr ). + ExprResult ActOnCXXFoldExpr(SourceLocation LParenLoc, Expr *LHS, + tok::TokenKind Operator, + SourceLocation EllipsisLoc, Expr *RHS, + SourceLocation RParenLoc); + ExprResult BuildCXXFoldExpr(SourceLocation LParenLoc, Expr *LHS, + BinaryOperatorKind Operator, + SourceLocation EllipsisLoc, Expr *RHS, + SourceLocation RParenLoc); + ExprResult BuildEmptyCXXFoldExpr(SourceLocation EllipsisLoc, + BinaryOperatorKind Operator); //// ActOnCXXThis - Parse 'this' pointer. ExprResult ActOnCXXThis(SourceLocation loc); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 1b70cfd0654b..85495839d32a 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1332,7 +1332,8 @@ namespace clang { EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr - + EXPR_CXX_FOLD, // CXXFoldExpr + // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index a76d50576aa6..ab37e7a1138b 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2887,6 +2887,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx) const { case SubstNonTypeTemplateParmPackExprClass: case FunctionParmPackExprClass: case TypoExprClass: + case CXXFoldExprClass: llvm_unreachable("shouldn't see dependent / unresolved nodes here"); case DeclRefExprClass: diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 4e2e3ea2ee89..915ca161c919 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -182,6 +182,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::AsTypeExprClass: case Expr::ObjCIndirectCopyRestoreExprClass: case Expr::AtomicExprClass: + case Expr::CXXFoldExprClass: return Cl::CL_PRValue; // Next come the complicated cases. diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d49d9b2c27c4..2ce5ac590bd7 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8661,6 +8661,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: case Expr::LambdaExprClass: + case Expr::CXXFoldExprClass: return ICEDiag(IK_NotICE, E->getLocStart()); case Expr::InitListExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index e41c664c8f23..d7b82eb56a73 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -3209,12 +3209,35 @@ recurse: mangleFunctionParam(cast(Pack)); break; } - + case Expr::MaterializeTemporaryExprClass: { mangleExpression(cast(E)->GetTemporaryExpr()); break; } - + + case Expr::CXXFoldExprClass: { + auto *FE = cast(E); + if (!FE->getLHS()) + Out << "fl"; + else if (!FE->getRHS()) + Out << "fr"; + else + Out << "fx"; + + if (FE->getOperator() == BO_PtrMemD) + Out << "ds"; + else + mangleOperatorName( + BinaryOperator::getOverloadedOperator(FE->getOperator()), + /*Arity=*/2); + + if (FE->getLHS()) + mangleExpression(FE->getLHS()); + if (FE->getRHS()) + mangleExpression(FE->getRHS()); + break; + } + case Expr::CXXThisExprClass: Out << "fpT"; break; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index b5441c830de0..927a679244b5 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2022,6 +2022,20 @@ void StmtPrinter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *Node){ PrintExpr(Node->GetTemporaryExpr()); } +void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) { + OS << "("; + if (E->getLHS()) { + PrintExpr(E->getLHS()); + OS << " " << BinaryOperator::getOpcodeStr(E->getOperator()) << " "; + } + OS << "..."; + if (E->getRHS()) { + OS << " " << BinaryOperator::getOpcodeStr(E->getOperator()) << " "; + PrintExpr(E->getRHS()); + } + OS << ")"; +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 15a867698620..647b643486c5 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1238,6 +1238,11 @@ void StmtProfiler::VisitMaterializeTemporaryExpr( VisitExpr(S); } +void StmtProfiler::VisitCXXFoldExpr(const CXXFoldExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getOperator()); +} + void StmtProfiler::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { VisitExpr(E); } diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 60840c6f510d..208ead86466d 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -216,6 +216,13 @@ bool Parser::isNotExpressionStart() { return isKnownToBeDeclarationSpecifier(); } +static bool isFoldOperator(prec::Level Level) { + return Level > prec::Unknown && Level != prec::Conditional; +} +static bool isFoldOperator(tok::TokenKind Kind) { + return isFoldOperator(getBinOpPrecedence(Kind, false, true)); +} + /// \brief Parse a binary expression that starts with \p LHS and has a /// precedence of at least \p MinPrec. ExprResult @@ -247,6 +254,16 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { return LHS; } + // If the next token is an ellipsis, then this is a fold-expression. Leave + // it alone so we can handle it in the paren expression. + if (isFoldOperator(NextTokPrec) && Tok.is(tok::ellipsis)) { + // FIXME: We can't check this via lookahead before we consume the token + // because that tickles a lexer bug. + PP.EnterToken(Tok); + Tok = OpToken; + return LHS; + } + // Special case handling for the ternary operator. ExprResult TernaryMiddle(true); if (NextTokPrec == prec::Conditional) { @@ -365,7 +382,6 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, getLangOpts().CPlusPlus11); } - assert(NextTokPrec <= ThisPrec && "Recursion didn't work!"); if (!RHS.isInvalid() && RHSIsInitList) { if (ThisPrec == prec::Assignment) { @@ -1989,11 +2005,15 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { /// cast-expression: [C99 6.5.4] /// '(' type-name ')' cast-expression /// [ARC] bridged-cast-expression -/// /// [ARC] bridged-cast-expression: /// (__bridge type-name) cast-expression /// (__bridge_transfer type-name) cast-expression /// (__bridge_retained type-name) cast-expression +/// fold-expression: [C++1z] +/// '(' cast-expression fold-operator '...' ')' +/// '(' '...' fold-operator cast-expression ')' +/// '(' cast-expression fold-operator '...' +/// fold-operator cast-expression ')' /// \endverbatim ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, @@ -2194,12 +2214,18 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, Result = Actions.ActOnParenListExpr(OpenLoc, Tok.getLocation(), ArgExprs); } + } else if (Tok.is(tok::ellipsis) && + isFoldOperator(NextToken().getKind())) { + return ParseFoldExpression(ExprResult(), T); } else { InMessageExpressionRAIIObject InMessage(*this, false); Result = ParseExpression(MaybeTypeCast); ExprType = SimpleExpr; + if (isFoldOperator(Tok.getKind()) && NextToken().is(tok::ellipsis)) + return ParseFoldExpression(Result, T); + // Don't build a paren expression unless we actually match a ')'. if (!Result.isInvalid() && Tok.is(tok::r_paren)) Result = @@ -2357,6 +2383,59 @@ ExprResult Parser::ParseGenericSelectionExpression() { Types, Exprs); } +/// \brief Parse A C++1z fold-expression after the opening paren and optional +/// left-hand-side expression. +/// +/// \verbatim +/// fold-expression: +/// ( cast-expression fold-operator ... ) +/// ( ... fold-operator cast-expression ) +/// ( cast-expression fold-operator ... fold-operator cast-expression ) +ExprResult Parser::ParseFoldExpression(ExprResult LHS, + BalancedDelimiterTracker &T) { + if (LHS.isInvalid()) { + T.skipToEnd(); + return true; + } + + tok::TokenKind Kind = tok::unknown; + SourceLocation FirstOpLoc; + if (LHS.isUsable()) { + Kind = Tok.getKind(); + assert(isFoldOperator(Kind) && "missing fold-operator"); + FirstOpLoc = ConsumeToken(); + } + + assert(Tok.is(tok::ellipsis) && "not a fold-expression"); + SourceLocation EllipsisLoc = ConsumeToken(); + + ExprResult RHS; + if (Tok.isNot(tok::r_paren)) { + if (!isFoldOperator(Tok.getKind())) + return Diag(Tok.getLocation(), diag::err_expected_fold_operator); + + if (Kind != tok::unknown && Tok.getKind() != Kind) + Diag(Tok.getLocation(), diag::err_fold_operator_mismatch) + << SourceRange(FirstOpLoc); + Kind = Tok.getKind(); + ConsumeToken(); + + RHS = ParseExpression(); + if (RHS.isInvalid()) { + T.skipToEnd(); + return true; + } + } + + Diag(EllipsisLoc, getLangOpts().CPlusPlus1z + ? diag::warn_cxx14_compat_fold_expression + : diag::ext_fold_expression); + + T.consumeClose(); + return Actions.ActOnCXXFoldExpr(T.getOpenLocation(), LHS.get(), Kind, + EllipsisLoc, RHS.get(), T.getCloseLocation()); +} + /// ParseExpressionList - Used for C/C++ (argument-)expression-list. /// /// \verbatim diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index ac0616df7408..07671b2f1d73 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1048,6 +1048,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::CXXDependentScopeMemberExprClass: case Expr::CXXUnresolvedConstructExprClass: case Expr::DependentScopeDeclRefExprClass: + case Expr::CXXFoldExprClass: return CT_Dependent; case Expr::AsTypeExprClass: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a8e4407f34fd..5a4c7e6b778c 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9164,8 +9164,7 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, return Result; } -static inline BinaryOperatorKind ConvertTokenKindToBinaryOpcode( - tok::TokenKind Kind) { +BinaryOperatorKind Sema::ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind) { BinaryOperatorKind Opc; switch (Kind) { default: llvm_unreachable("Unknown binop!"); diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 8a84e00a598b..52875465e68b 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -935,3 +935,108 @@ Sema::getTemplateArgumentPackExpansionPattern( llvm_unreachable("Invalid TemplateArgument Kind!"); } + +static void CheckFoldOperand(Sema &S, Expr *E) { + if (!E) + return; + + E = E->IgnoreImpCasts(); + if (isa(E) || isa(E)) { + S.Diag(E->getExprLoc(), diag::err_fold_expression_bad_operand) + << E->getSourceRange() + << FixItHint::CreateInsertion(E->getLocStart(), "(") + << FixItHint::CreateInsertion(E->getLocEnd(), ")"); + } +} + +ExprResult Sema::ActOnCXXFoldExpr(SourceLocation LParenLoc, Expr *LHS, + tok::TokenKind Operator, + SourceLocation EllipsisLoc, Expr *RHS, + SourceLocation RParenLoc) { + // LHS and RHS must be cast-expressions. We allow an arbitrary expression + // in the parser and reduce down to just cast-expressions here. + CheckFoldOperand(*this, LHS); + CheckFoldOperand(*this, RHS); + + // [expr.prim.fold]p3: + // In a binary fold, op1 and op2 shall be the same fold-operator, and + // either e1 shall contain an unexpanded parameter pack or e2 shall contain + // an unexpanded parameter pack, but not both. + if (LHS && RHS && + LHS->containsUnexpandedParameterPack() == + RHS->containsUnexpandedParameterPack()) { + return Diag(EllipsisLoc, + LHS->containsUnexpandedParameterPack() + ? diag::err_fold_expression_packs_both_sides + : diag::err_pack_expansion_without_parameter_packs) + << LHS->getSourceRange() << RHS->getSourceRange(); + } + + // [expr.prim.fold]p2: + // In a unary fold, the cast-expression shall contain an unexpanded + // parameter pack. + if (!LHS || !RHS) { + Expr *Pack = LHS ? LHS : RHS; + assert(Pack && "fold expression with neither LHS nor RHS"); + if (!Pack->containsUnexpandedParameterPack()) + return Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) + << Pack->getSourceRange(); + } + + BinaryOperatorKind Opc = ConvertTokenKindToBinaryOpcode(Operator); + return BuildCXXFoldExpr(LParenLoc, LHS, Opc, EllipsisLoc, RHS, RParenLoc); +} + +ExprResult Sema::BuildCXXFoldExpr(SourceLocation LParenLoc, Expr *LHS, + BinaryOperatorKind Operator, + SourceLocation EllipsisLoc, Expr *RHS, + SourceLocation RParenLoc) { + return new (Context) CXXFoldExpr(Context.DependentTy, LParenLoc, LHS, + Operator, EllipsisLoc, RHS, RParenLoc); +} + +ExprResult Sema::BuildEmptyCXXFoldExpr(SourceLocation EllipsisLoc, + BinaryOperatorKind Operator) { + // [temp.variadic]p9: + // If N is zero for a unary fold-expression, the value of the expression is + // * -> 1 + // + -> int() + // & -> -1 + // | -> int() + // && -> true + // || -> false + // , -> void() + // if the operator is not listed [above], the instantiation is ill-formed. + // + // Note that we need to use something like int() here, not merely 0, to + // prevent the result from being a null pointer constant. + QualType ScalarType; + switch (Operator) { + case BO_Add: + ScalarType = Context.IntTy; + break; + case BO_Mul: + return ActOnIntegerConstant(EllipsisLoc, 1); + case BO_Or: + ScalarType = Context.IntTy; + break; + case BO_And: + return CreateBuiltinUnaryOp(EllipsisLoc, UO_Minus, + ActOnIntegerConstant(EllipsisLoc, 1).get()); + case BO_LOr: + return ActOnCXXBoolLiteral(EllipsisLoc, tok::kw_false); + case BO_LAnd: + return ActOnCXXBoolLiteral(EllipsisLoc, tok::kw_true); + case BO_Comma: + ScalarType = Context.VoidTy; + break; + + default: + return Diag(EllipsisLoc, diag::err_fold_expression_empty) + << BinaryOperator::getOpcodeStr(Operator); + } + + return new (Context) CXXScalarValueInitExpr( + ScalarType, Context.getTrivialTypeSourceInfo(ScalarType, EllipsisLoc), + EllipsisLoc); +} diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 854ff52f52a1..9ddb6d842f42 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2784,6 +2784,27 @@ public: return getSema().CheckPackExpansion(Pattern, EllipsisLoc, NumExpansions); } + /// \brief Build a new C++1z fold-expression. + /// + /// By default, performs semantic analysis in order to build a new fold + /// expression. + ExprResult RebuildCXXFoldExpr(SourceLocation LParenLoc, Expr *LHS, + BinaryOperatorKind Operator, + SourceLocation EllipsisLoc, Expr *RHS, + SourceLocation RParenLoc) { + return getSema().BuildCXXFoldExpr(LParenLoc, LHS, Operator, EllipsisLoc, + RHS, RParenLoc); + } + + /// \brief Build an empty C++1z fold-expression with the given operator. + /// + /// By default, produces the fallback value for the fold-expression, or + /// produce an error if there is no fallback value. + ExprResult RebuildEmptyCXXFoldExpr(SourceLocation EllipsisLoc, + BinaryOperatorKind Operator) { + return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator); + } + /// \brief Build a new atomic operation expression. /// /// By default, performs semantic analysis to build the new expression. @@ -9564,6 +9585,128 @@ TreeTransform::TransformMaterializeTemporaryExpr( return getDerived().TransformExpr(E->GetTemporaryExpr()); } +template +ExprResult +TreeTransform::TransformCXXFoldExpr(CXXFoldExpr *E) { + Expr *Pattern = E->getPattern(); + + SmallVector Unexpanded; + getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + + // Determine whether the set of unexpanded parameter packs can and should + // be expanded. + bool Expand = true; + bool RetainExpansion = false; + Optional NumExpansions; + if (getDerived().TryExpandParameterPacks(E->getEllipsisLoc(), + Pattern->getSourceRange(), + Unexpanded, + Expand, RetainExpansion, + NumExpansions)) + return true; + + if (!Expand) { + // Do not expand any packs here, just transform and rebuild a fold + // expression. + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); + + ExprResult LHS = + E->getLHS() ? getDerived().TransformExpr(E->getLHS()) : ExprResult(); + if (LHS.isInvalid()) + return true; + + ExprResult RHS = + E->getRHS() ? getDerived().TransformExpr(E->getRHS()) : ExprResult(); + if (RHS.isInvalid()) + return true; + + if (!getDerived().AlwaysRebuild() && + LHS.get() == E->getLHS() && RHS.get() == E->getRHS()) + return E; + + return getDerived().RebuildCXXFoldExpr( + E->getLocStart(), LHS.get(), E->getOperator(), E->getEllipsisLoc(), + RHS.get(), E->getLocEnd()); + } + + // The transform has determined that we should perform an elementwise + // expansion of the pattern. Do so. + ExprResult Result = getDerived().TransformExpr(E->getInit()); + if (Result.isInvalid()) + return true; + bool LeftFold = E->isLeftFold(); + + // If we're retaining an expansion for a right fold, it is the innermost + // component and takes the init (if any). + if (!LeftFold && RetainExpansion) { + ForgetPartiallySubstitutedPackRAII Forget(getDerived()); + + ExprResult Out = getDerived().TransformExpr(Pattern); + if (Out.isInvalid()) + return true; + + Result = getDerived().RebuildCXXFoldExpr( + E->getLocStart(), Out.get(), E->getOperator(), E->getEllipsisLoc(), + Result.get(), E->getLocEnd()); + if (Result.isInvalid()) + return true; + } + + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex( + getSema(), LeftFold ? I : *NumExpansions - I - 1); + ExprResult Out = getDerived().TransformExpr(Pattern); + if (Out.isInvalid()) + return true; + + if (Out.get()->containsUnexpandedParameterPack()) { + // We still have a pack; retain a pack expansion for this slice. + Result = getDerived().RebuildCXXFoldExpr( + E->getLocStart(), + LeftFold ? Result.get() : Out.get(), + E->getOperator(), E->getEllipsisLoc(), + LeftFold ? Out.get() : Result.get(), + E->getLocEnd()); + } else if (Result.isUsable()) { + // We've got down to a single element; build a binary operator. + Result = getDerived().RebuildBinaryOperator( + E->getEllipsisLoc(), E->getOperator(), + LeftFold ? Result.get() : Out.get(), + LeftFold ? Out.get() : Result.get()); + } else + Result = Out; + + if (Result.isInvalid()) + return true; + } + + // If we're retaining an expansion for a left fold, it is the outermost + // component and takes the complete expansion so far as its init (if any). + if (LeftFold && RetainExpansion) { + ForgetPartiallySubstitutedPackRAII Forget(getDerived()); + + ExprResult Out = getDerived().TransformExpr(Pattern); + if (Out.isInvalid()) + return true; + + Result = getDerived().RebuildCXXFoldExpr( + E->getLocStart(), Result.get(), + E->getOperator(), E->getEllipsisLoc(), + Out.get(), E->getLocEnd()); + if (Result.isInvalid()) + return true; + } + + // If we had no init and an empty pack, and we're not retaining an expansion, + // then produce a fallback value or error. + if (Result.isUnset()) + return getDerived().RebuildEmptyCXXFoldExpr(E->getEllipsisLoc(), + E->getOperator()); + + return Result; +} + template ExprResult TreeTransform::TransformCXXStdInitializerListExpr( diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 9c669d7b2496..0e4d99472346 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1580,6 +1580,16 @@ void ASTStmtReader::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { E->setExtendingDecl(VD, ManglingNumber); } +void ASTStmtReader::VisitCXXFoldExpr(CXXFoldExpr *E) { + VisitExpr(E); + E->LParenLoc = ReadSourceLocation(Record, Idx); + E->EllipsisLoc = ReadSourceLocation(Record, Idx); + E->RParenLoc = ReadSourceLocation(Record, Idx); + E->SubExprs[0] = Reader.ReadSubExpr(); + E->SubExprs[1] = Reader.ReadSubExpr(); + E->Opcode = (BinaryOperatorKind)Record[Idx++]; +} + void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); E->SourceExpr = Reader.ReadSubExpr(); @@ -2908,7 +2918,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_MATERIALIZE_TEMPORARY: S = new (Context) MaterializeTemporaryExpr(Empty); break; - + + case EXPR_CXX_FOLD: + S = new (Context) CXXFoldExpr(Empty); + break; + case EXPR_OPAQUE_VALUE: S = new (Context) OpaqueValueExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 61ec6a058b1f..309e0a653291 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1579,6 +1579,17 @@ void ASTStmtWriter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { Code = serialization::EXPR_MATERIALIZE_TEMPORARY; } +void ASTStmtWriter::VisitCXXFoldExpr(CXXFoldExpr *E) { + VisitExpr(E); + Writer.AddSourceLocation(E->LParenLoc, Record); + Writer.AddSourceLocation(E->EllipsisLoc, Record); + Writer.AddSourceLocation(E->RParenLoc, Record); + Writer.AddStmt(E->SubExprs[0]); + Writer.AddStmt(E->SubExprs[1]); + Record.push_back(E->Opcode); + Code = serialization::EXPR_CXX_FOLD; +} + void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); Writer.AddStmt(E->getSourceExpr()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 7464b84c4a01..4699df8819bd 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -752,6 +752,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: + case Stmt::CXXFoldExprClass: case Stmt::MSPropertyRefExprClass: case Stmt::CXXUnresolvedConstructExprClass: case Stmt::DependentScopeDeclRefExprClass: diff --git a/clang/test/CodeGenCXX/cxx1z-fold-expression.cpp b/clang/test/CodeGenCXX/cxx1z-fold-expression.cpp new file mode 100644 index 000000000000..7677fc0e18f8 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx1z-fold-expression.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -std=c++1z -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s + +template struct A {}; +template void foldr(A<(N + ...)>); +template void foldl(A<(... + N)>); +template void foldr1(A<(N + ... + 1)>); +template void foldl1(A<(1 + ... + N)>); +void use() { + foldr<1, 2, 3>({}); + foldl<1, 2, 3>({}); + foldr1<1, 2, 3>({}); + foldl1<1, 2, 3>({}); + // CHECK-DAG: @_Z5foldrIJLi1ELi2ELi3EEEv1AIXfrplT_EE( + // CHECK-DAG: @_Z5foldlIJLi1ELi2ELi3EEEv1AIXflplT_EE( + // CHECK-DAG: @_Z6foldr1IJLi1ELi2ELi3EEEv1AIXfxplT_Li1EEE( + // CHECK-DAG: @_Z6foldl1IJLi1ELi2ELi3EEEv1AIXfxplLi1ET_EE( +} + +template using Foldr = A<(N + ...)>; +template using Foldl = A<(... + N)>; +template using Foldr1 = A<(N + ... + 1)>; +template using Foldl1 = A<(1 + ... + N)>; + +template struct Partial { + template void foldr(Foldr); + template void foldl(Foldr); + template void foldr1(Foldr1); + template void foldl1(Foldl1); +}; +void use(Partial<1, 2> p) { + p.foldr<3, 4>({}); + p.foldl<3, 4>({}); + p.foldr1<3, 4>({}); + p.foldl1<3, 4>({}); + // CHECK-DAG: @_ZN7PartialIJLi1ELi2EEE5foldrIJLi3ELi4EEEEv1AIXplLi1EplLi2EfxplT_plLi1EplLi2EfrplT_EE( + // CHECK-DAG: @_ZN7PartialIJLi1ELi2EEE5foldlIJLi3ELi4EEEEv1AIXplLi1EplLi2EfxplT_plLi1EplLi2EfrplT_EE( + // CHECK-DAG: @_ZN7PartialIJLi1ELi2EEE6foldr1IJLi3ELi4EEEEv1AIXplLi1EplLi2EfxplT_plLi1EplLi2EfxplT_Li1EEE( + // CHECK-DAG: @_ZN7PartialIJLi1ELi2EEE6foldl1IJLi3ELi4EEEEv1AIXfxplplplfxplplplLi1ELi1ELi2ET_Li1ELi2ET_EE( +} + +extern int n; +template void f() { + (n = ... = N); +} +template void f<>(); diff --git a/clang/test/Parser/cxx1z-fold-expressions.cpp b/clang/test/Parser/cxx1z-fold-expressions.cpp new file mode 100644 index 000000000000..a908311ea316 --- /dev/null +++ b/clang/test/Parser/cxx1z-fold-expressions.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +template constexpr auto sum(T ...t) { return (... + t); } +template constexpr auto product(T ...t) { return (t * ...); } +template constexpr auto all(T ...t) { return (true && ... && t); } +template constexpr auto all2(T ...t) { return (t && ... && true); } + +int k1 = (1 + ... + 2); // expected-error {{does not contain any unexpanded parameter packs}} +int k2 = (1 + ...); // expected-error {{does not contain any unexpanded parameter packs}} +int k3 = (... + 2); // expected-error {{does not contain any unexpanded parameter packs}} + +template void bad1() { (N + ... + N); } // expected-error {{unexpanded parameter packs in both operands}} +// FIXME: it would be reasonable to support this as an extension. +template void bad2() { (2 * N + ... + 1); } // expected-error {{expression not permitted as operand}} +template void bad3() { (2 + N * ... * 1); } // expected-error {{expression not permitted as operand}} +template void bad4(int (&...x)[N]) { (N + M * ... * 1); } // expected-error {{expression not permitted as operand}} +template void fixed4(int (&...x)[N]) { ((N + M) * ... * 1); } + +// Parens are mandatory. +template void bad5() { N + ...; } // expected-error {{expected expression}} expected-error +{{}} +template void bad6() { ... + N; } // expected-error {{expected expression}} +template void bad7() { N + ... + N; } // expected-error {{expected expression}} expected-error +{{}} + +// Must have a fold-operator in the relevant places. +template int bad8() { return (N + ... * 3); } // expected-error {{operators in fold expression must be the same}} +template int bad9() { return (3 + ... * N); } // expected-error {{operators in fold expression must be the same}} +template int bad10() { return (3 ? ... : N); } // expected-error +{{}} expected-note {{to match}} +template int bad11() { return (N + ... 0); } // expected-error {{expected a foldable binary operator}} expected-error {{expected expression}} +template int bad12() { return (... N); } // expected-error {{expected expression}} diff --git a/clang/test/SemaCXX/cxx0x-compat.cpp b/clang/test/SemaCXX/cxx0x-compat.cpp index ded51ab704b9..bcf0cf11dc18 100644 --- a/clang/test/SemaCXX/cxx0x-compat.cpp +++ b/clang/test/SemaCXX/cxx0x-compat.cpp @@ -41,9 +41,15 @@ void h(size_t foo, size_t bar) { #define _x + 1 char c = 'x'_x; // expected-warning {{will be treated as a user-defined literal suffix}} +template int f() { // expected-warning {{C++11 extension}} + return (N + ...); // expected-warning {{C++1z extension}} +} + #else auto init_capture = [a(0)] {}; // expected-warning {{initialized lambda captures are incompatible with C++ standards before C++14}} static_assert(true); // expected-warning {{incompatible with C++ standards before C++1z}} +template int f() { return (N + ...); } // expected-warning {{incompatible with C++ standards before C++1z}} + #endif diff --git a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp new file mode 100644 index 000000000000..3934f011a359 --- /dev/null +++ b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +template constexpr auto sum(T ...t) { return (... + t); } +template constexpr auto product(T ...t) { return (t * ...); } +template constexpr auto all(T ...t) { return (true && ... && t); } +template constexpr auto dumb(T ...t) { return (false && ... && t); } + +static_assert(sum(1, 2, 3, 4, 5) == 15); +static_assert(product(1, 2, 3, 4, 5) == 120); +static_assert(!all(true, true, false, true, false)); +static_assert(all(true, true, true, true, true)); +static_assert(!dumb(true, true, true, true, true)); + +struct S { + int a, b, c, d, e; +}; +template constexpr auto increment_all(T &...t) { + (++t, ...); +} +constexpr bool check() { + S s = { 1, 2, 3, 4, 5 }; + increment_all(s.a, s.b, s.c, s.d, s.e); + return s.a == 2 && s.b == 3 && s.c == 4 && s.d == 5 && s.e == 6; +} +static_assert(check()); + +template void empty() { + static_assert((N + ...) == 0); + static_assert((N * ...) == 1); + static_assert((N | ...) == 0); + static_assert((N & ...) == -1); + static_assert((N || ...) == false); + static_assert((N && ...) == true); + (N, ...); +} +template void empty<>(); + +// An empty fold-expression isn't a null pointer just because it's an integer +// with value 0. +template void null_ptr() { + void *p = (N + ...); // expected-error {{rvalue of type 'int'}} + void *q = (N | ...); // expected-error {{rvalue of type 'int'}} +} +template void null_ptr<>(); // expected-note {{in instantiation of}} + +template void bad_empty() { + (N - ...); // expected-error {{empty expansion for operator '-' with no fallback}} + (N / ...); // expected-error {{empty expansion for operator '/' with no fallback}} + (N % ...); // expected-error {{empty expansion for operator '%' with no fallback}} + (N = ...); // expected-error {{empty expansion for operator '=' with no fallback}} +} +template void bad_empty<>(); // expected-note {{in instantiation of}} + +template void empty_with_base() { + extern int k; + (k = ... = N); // expected-warning{{unused}} + + // FIXME: We misparse these. The first one looks a lot loke a declaration; + // it's not clear what's happening in the second one. + void (k = ... = N); // expected-error {{expected ')'}} expected-note {{to match}} + (void) (k = ... = N); // expected-error {{expected ')'}} expected-note {{to match}} +} +template void empty_with_base<>(); // expected-note {{in instantiation of}} +template void empty_with_base<1>(); + +struct A { + struct B { + struct C { + struct D { + int e; + } d; + } c; + } b; +} a; +template constexpr decltype(auto) apply(T &t, Ts ...ts) { + return (t.*....*ts); +} +static_assert(&apply(a, &A::b, &A::B::c, &A::B::C::d, &A::B::C::D::e) == &a.b.c.d.e); diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 2c83236ca914..c069781739c2 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -229,6 +229,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: + case Stmt::CXXFoldExprClass: case Stmt::CXXStdInitializerListExprClass: case Stmt::CXXScalarValueInitExprClass: case Stmt::CXXUuidofExprClass: diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index c5efbbc48bf5..565349ab029e 100644 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -524,11 +524,13 @@ as the draft C++1z standard evolves.

C++1z Proposal Available in Clang? + static_assert with no message N3928 Clang 3.5 + Disabling trigraph expansion by default N3981 @@ -544,6 +546,12 @@ as the draft C++1z standard evolves.

N4051 Clang 3.5 + + + Fold expressions + N4295 + SVN +

Technical specifications and standing documents