mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-08 09:03:18 +00:00
[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
This commit is contained in:
parent
8f6dd44056
commit
0f0af19b05
@ -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.
|
||||
|
@ -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<Expr*>(SubExprs[0]); }
|
||||
Expr *getRHS() const { return static_cast<Expr*>(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
|
||||
|
@ -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.
|
||||
|
@ -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<CXX1z>;
|
||||
def warn_cxx14_compat_fold_expression : Warning<
|
||||
"pack fold expression is incompatible with C++ standards before C++1z">,
|
||||
InGroup<CXXPre1zCompat>, 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">;
|
||||
|
||||
|
@ -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<
|
||||
|
@ -135,6 +135,7 @@ def SubstNonTypeTemplateParmPackExpr : DStmt<Expr>;
|
||||
def FunctionParmPackExpr : DStmt<Expr>;
|
||||
def MaterializeTemporaryExpr : DStmt<Expr>;
|
||||
def LambdaExpr : DStmt<Expr>;
|
||||
def CXXFoldExpr : DStmt<Expr>;
|
||||
|
||||
// Obj-C Expressions.
|
||||
def ObjCStringLiteral : DStmt<Expr>;
|
||||
|
@ -1392,6 +1392,8 @@ private:
|
||||
|
||||
ExprResult ParseObjCBoolLiteral();
|
||||
|
||||
ExprResult ParseFoldExpression(ExprResult LHS, BalancedDelimiterTracker &T);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++ Expressions
|
||||
ExprResult ParseCXXIdExpression(bool isAddressOfOperand = false);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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: {
|
||||
|
@ -3209,12 +3209,35 @@ recurse:
|
||||
mangleFunctionParam(cast<ParmVarDecl>(Pack));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case Expr::MaterializeTemporaryExprClass: {
|
||||
mangleExpression(cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case Expr::CXXFoldExprClass: {
|
||||
auto *FE = cast<CXXFoldExpr>(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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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!");
|
||||
|
@ -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<BinaryOperator>(E) || isa<AbstractConditionalOperator>(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);
|
||||
}
|
||||
|
@ -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<Derived>::TransformMaterializeTemporaryExpr(
|
||||
return getDerived().TransformExpr(E->GetTemporaryExpr());
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
ExprResult
|
||||
TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
|
||||
Expr *Pattern = E->getPattern();
|
||||
|
||||
SmallVector<UnexpandedParameterPack, 2> 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<unsigned> 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<typename Derived>
|
||||
ExprResult
|
||||
TreeTransform<Derived>::TransformCXXStdInitializerListExpr(
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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:
|
||||
|
45
clang/test/CodeGenCXX/cxx1z-fold-expression.cpp
Normal file
45
clang/test/CodeGenCXX/cxx1z-fold-expression.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
// RUN: %clang_cc1 -std=c++1z -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
template<int> struct A {};
|
||||
template<int ...N> void foldr(A<(N + ...)>);
|
||||
template<int ...N> void foldl(A<(... + N)>);
|
||||
template<int ...N> void foldr1(A<(N + ... + 1)>);
|
||||
template<int ...N> 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<int ...N> using Foldr = A<(N + ...)>;
|
||||
template<int ...N> using Foldl = A<(... + N)>;
|
||||
template<int ...N> using Foldr1 = A<(N + ... + 1)>;
|
||||
template<int ...N> using Foldl1 = A<(1 + ... + N)>;
|
||||
|
||||
template<int ...A> struct Partial {
|
||||
template<int ...B> void foldr(Foldr<A..., B..., A..., B...>);
|
||||
template<int ...B> void foldl(Foldr<A..., B..., A..., B...>);
|
||||
template<int ...B> void foldr1(Foldr1<A..., B..., A..., B...>);
|
||||
template<int ...B> void foldl1(Foldl1<A..., B..., A..., B...>);
|
||||
};
|
||||
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<int ...N> void f() {
|
||||
(n = ... = N);
|
||||
}
|
||||
template void f<>();
|
29
clang/test/Parser/cxx1z-fold-expressions.cpp
Normal file
29
clang/test/Parser/cxx1z-fold-expressions.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
// RUN: %clang_cc1 -std=c++1z -verify %s
|
||||
|
||||
template<typename ...T> constexpr auto sum(T ...t) { return (... + t); }
|
||||
template<typename ...T> constexpr auto product(T ...t) { return (t * ...); }
|
||||
template<typename ...T> constexpr auto all(T ...t) { return (true && ... && t); }
|
||||
template<typename ...T> 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<int ...N> void bad1() { (N + ... + N); } // expected-error {{unexpanded parameter packs in both operands}}
|
||||
// FIXME: it would be reasonable to support this as an extension.
|
||||
template<int ...N> void bad2() { (2 * N + ... + 1); } // expected-error {{expression not permitted as operand}}
|
||||
template<int ...N> void bad3() { (2 + N * ... * 1); } // expected-error {{expression not permitted as operand}}
|
||||
template<int ...N, int ...M> void bad4(int (&...x)[N]) { (N + M * ... * 1); } // expected-error {{expression not permitted as operand}}
|
||||
template<int ...N, int ...M> void fixed4(int (&...x)[N]) { ((N + M) * ... * 1); }
|
||||
|
||||
// Parens are mandatory.
|
||||
template<int ...N> void bad5() { N + ...; } // expected-error {{expected expression}} expected-error +{{}}
|
||||
template<int ...N> void bad6() { ... + N; } // expected-error {{expected expression}}
|
||||
template<int ...N> void bad7() { N + ... + N; } // expected-error {{expected expression}} expected-error +{{}}
|
||||
|
||||
// Must have a fold-operator in the relevant places.
|
||||
template<int ...N> int bad8() { return (N + ... * 3); } // expected-error {{operators in fold expression must be the same}}
|
||||
template<int ...N> int bad9() { return (3 + ... * N); } // expected-error {{operators in fold expression must be the same}}
|
||||
template<int ...N> int bad10() { return (3 ? ... : N); } // expected-error +{{}} expected-note {{to match}}
|
||||
template<int ...N> int bad11() { return (N + ... 0); } // expected-error {{expected a foldable binary operator}} expected-error {{expected expression}}
|
||||
template<int ...N> int bad12() { return (... N); } // expected-error {{expected expression}}
|
@ -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 ...N> 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 ...N> int f() { return (N + ...); } // expected-warning {{incompatible with C++ standards before C++1z}}
|
||||
|
||||
#endif
|
||||
|
78
clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
Normal file
78
clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
// RUN: %clang_cc1 -std=c++1z -verify %s
|
||||
|
||||
template<typename ...T> constexpr auto sum(T ...t) { return (... + t); }
|
||||
template<typename ...T> constexpr auto product(T ...t) { return (t * ...); }
|
||||
template<typename ...T> constexpr auto all(T ...t) { return (true && ... && t); }
|
||||
template<typename ...T> 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<typename ...T> 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<int ...N> 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<int ...N> 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<int ...N> 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<int ...N> 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<typename T, typename ...Ts> 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);
|
@ -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:
|
||||
|
@ -524,11 +524,13 @@ as the draft C++1z standard evolves.</p>
|
||||
<th>C++1z Proposal</th>
|
||||
<th>Available in Clang?</th>
|
||||
</tr>
|
||||
<!-- Issaquah papers -->
|
||||
<tr>
|
||||
<td><tt>static_assert</tt> with no message</td>
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3928.pdf">N3928</a></td>
|
||||
<td class="full" align="center">Clang 3.5</td>
|
||||
</tr>
|
||||
<!-- Rapperswil papers -->
|
||||
<tr>
|
||||
<td>Disabling trigraph expansion by default</td>
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3981.html">N3981</a></td>
|
||||
@ -544,6 +546,12 @@ as the draft C++1z standard evolves.</p>
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4051.html">N4051</a></td>
|
||||
<td class="full" align="center">Clang 3.5</td>
|
||||
</tr>
|
||||
<!-- Urbana papers -->
|
||||
<tr>
|
||||
<td>Fold expressions</td>
|
||||
<td><!--<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4051.html">-->N4295<!--</a>--></td>
|
||||
<td class="svn" align="center">SVN</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2 id="ts">Technical specifications and standing documents</h2>
|
||||
|
Loading…
Reference in New Issue
Block a user